一文掌握Docker Compose

一、Docker Compose介绍

Docker Compose是一个定义和运行多容器应用的单机编排工具。通过Docker Compose你可以使用一个单一的YAML文件来配置多个应用服务,通过一条命令,就可以将所有配置的服务全部启动起来。

使用Docker Compose的三个步骤:

  • 使用Dockerfile定义环境,这样可以确保其在任意地方运行

  • 使用docker-compose.yml文件定义服务,这样它们就可以在独立环境中一起运行

  • 运行docker-compose up使用docker-compose启动所有应用

Docker Compose可以管理应用的整个生命周期:

  • 启动、停止、重建服务

  • 查看服务的运行状态

  • 流式输出服务日志

  • 对服务执行一次性命令

二、Docker Compose安装

二进制安装:

下载地址:https://github.com/docker/compose/releases

pip安装:

pip install docker-compose

三、Docker Compose基本示例

1、基本文件及目录设置

创建一个目录:

mkdir composetest
cd composetest

在上面的目录中创建一个app.py文件,内容如下:

import time

import redis
from flask import Flask


app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)


def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)


@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.n'.format(count)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

再创建一个pip.conf文件,内容如下:

[global]
index-url = https://mirrors.aliyun.com/pypi/simple/

[install]
trusted-host=mirrors.aliyun.com

2、创建一个Dockerfile

仍然在composetest目录中创建一个Dockerfile,内容如下:

FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

3、通过docker-compose.yml定义服务

docker-compose.yml内容如下:

version: '3'
services:
  web:
    build: .
    ports:
     - "5000:5000"
  redis:
    image: "redis:alpine"

这个文件定义了两个服务:web和redis。

  • web服务使用当前目录的Dockerfile进行构建,并且映射web服务的5000端口到宿主机5000端口。
  • redis服务使用一个公共的redis镜像。

4、通过Docker Compose构建并启动服务

docker-compose up

这个时候可以通过http://127.0.0.1:5000来访问这个web服务。

5、修改Compse文件,添加一个挂载点

修改docker-compose.yml,内容如下:

version: '3'
services:
  web:
    build: .
    ports:
     - "5000:5000"
    volumes:
     - .:/code
  redis:
    image: "redis:alpine"

添加了一个volumes配置项,将当前目录挂载至web容器的/code目录下。

然后我们通过docker-compose up重新构建应用,再次访问,会发现结果与上面完全相同。

6、 更新应用

我们修改本地的app.py,修改Hello World!为Hello from Dokcer,如下:

return 'Hello from Docker! I have been seen {} times.n'.format(count)

这个时候再次访问http://127.0.0.1:5000,发现访问内容也随之修改。

这是因为在上面我们将本地目录挂载进了容器,我们修改本地的app.py就相当于修改了容器内的文件。

四、Docker Compose常用命令说明

在上面的一个简单示例中,我们已经使用了docker-compose up来启动一个docker-compose.yml文件定义的服务。我们刚刚通过docker-compose up虽然启动了服务,当是docker-compose指令却在前台执行,如果需要将其放入后台运行,可以使用-d参数:

docker-compose up -d

docker-compose up还可以使用–scale参数实现服务的扩缩容:

[root@app composetest]# docker-compose up -d --scale web=2
Recreating composetest_web_1 ... 
Recreating composetest_web_1 ... done
Creating composetest_web_2   ... done

还可以通过-f选项指定compose文件:

[root@app tranning]# docker-compose -f test-compose.yml up -d
Creating network "tranning_default" with the default driver
Creating network "tranning_frontend" with the default driver
Creating network "tranning_backend" with the default driver
Creating tranning_visualizer_1 ... done
Creating tranning_redis_1      ... done
Creating tranning_worker_1     ... done
Creating tranning_db_1         ... done
Creating tranning_result_1     ... done
Creating tranning_vote_1       ... done

需要说明的是,如果使用自动扩容,则web服务不能做端口映射,否则会出现端口冲突的情况

下面我们说一说其他常用的docker-compose命令:

docker-compose ps

[root@app composetest]# docker-compose ps 
       Name                      Command               State           Ports         
-------------------------------------------------------------------------------------
composetest_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp              
composetest_web_1     python app.py    

docker-compose stop

[root@app composetest]# docker-compose stop
Stopping composetest_redis_1 ... done
Stopping composetest_web_1   ... done

[root@app composetest]# docker-compose stop web
Stopping composetest_web_1 ... done
docker-compose start
[root@app composetest]# docker-compose start
Starting web   ... done
Starting redis ... done

[root@app composetest]# docker-compose start web
Starting web ... done

docker-compose restart

[root@app composetest]# docker-compose restart
Restarting composetest_web_1   ... done
Restarting composetest_redis_1 ... done

docker-compose run

[root@app composetest]# docker-compose run web env
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=72ad8a0682b7
TERM=xterm
LANG=C.UTF-8
GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D
PYTHON_VERSION=3.4.9
PYTHON_PIP_VERSION=18.0
HOME=/root

docker-compose down

[root@app composetest]# docker-compose down
Stopping composetest_redis_1 ... done
Stopping composetest_web_1   ... done
Removing composetest_redis_1 ... done
Removing composetest_web_1   ... done
Removing network composetest_default

通过–volumes还要以删除自动挂载的容器卷

docker-compose build

默认情况下,我们写好了Dockerfile,第一次通过docker-compose启动的时候,会自动完成构建,但如果随后Dockerfile发生了改动,再次通过docker-compose来启动实现更新的话,docker-compose不会再次自动构建镜像,而是复用第一次生成的镜像,如果希望镜像能够被重新构建,需要单独执行docker-compose build

docker-compose top

[root@app composetest]# docker-compose top
composetest_redis_1
UID    PID    PPID    C   STIME   TTY     TIME         CMD     
---------------------------------------------------------------
100   89653   89634   0   23:26   ?     00:00:00   redis-server

composetest_web_1
UID     PID    PPID    C   STIME   TTY     TIME                 CMD             
--------------------------------------------------------------------------------
root   89635   89619   0   23:26   ?     00:00:00   python app.py               
root   89742   89635   0   23:26   ?     00:00:00   /usr/local/bin/python app.py

其它

docker-compose rm       # 通过这种方式也能删除指定服务,但不会删除网络和volumes
docker-compose kill     # 强制杀死一个服务
docker-compose logs     # 用于查看日志

五、Docker Compose文件详解

通过之前的示例,其实我们可以看到,所有服务的管理,都是依靠docker-compose.yml文件来实现的。那么我们接下来就详细说一说docker-compose.yml文件中的常用指令。

compose文件使用yml格式,docker规定了一些指令,使用它们可以去设置对应的东西,主要分为了四个区域:

  • version:用于指定当前docker-compose.yml语法遵循哪个版本
  • services:服务,在它下面可以定义应用需要的一些服务,每个服务都有自己的名字、使用的镜像、挂载的数据卷、所属的网络、依赖哪些其他服务等等。
  • networks:应用的网络,在它下面可以定义应用的名字、使用的网络类型等。
  • volumes:数据卷,在它下面可以定义数据卷,然后挂载到不同的服务下去使用。

1、version

用于指定当前compose文件语法遵循哪个版本,下面这张表是不同的Compose文件版本兼容的Docker版本:

未分类

2、services

我们上面所说的所有服务的定义都是定义在services区域中,接下来,我们学习下services下常用的配置项

image

标明image的ID,这个image ID可以是本地也可以是远程的,如果本地不存在,compose会尝试pull下来
示例:

image: ubuntu
image: hub.dz11.com/library/tomcat:8

build

该参数指定Dockerfile文件的路径,compose会通过Dockerfile构建并生成镜像,然后使用该镜像
示例:

build: /path/to/build/dir

command

重写默认的命令,或者说指定启动容器的命令
示例:

command: "/run.sh"

links

链接到其他服务中的容器,可以指定服务名称和这个链接的别名,或者只指定服务名称
示例:

links:
  - db
  - db:database
  - redis

此时,在容器内部,会在/etc/hosts文件中用别名创建几个条目,如下:

172.17.2.100 db
172.17.2.100 database
172.17.2.100 redis

external_links

链接到compose外部启动的容器,特别是对于提供共享和公共服务的容器。在指定容器名称和别名时,external_links遵循着和links相同的语义用法
示例:

external_links:
  - redis_1
  - project_db_1: mysql
  - project_db_2: postgresql

ports

暴露端口,指定宿主机到容器的端口映射,或者只指定容器的端口,则表示映射到主机上的随机端口
注:当以 主机:容器 的形式来映射端口时,如果使容器的端口小于60,那可能会出现错误,因为YAML会将 xx:yy这样格式的数据解析为六十进制的数据,基于这个原因,时刻记得要将端口映射明确指定为字符串
示例:

ports:  
 - "3000"  
 - "8000:8000"  
 - "49100:22"  
 - "127.0.0.1:8001:8001" 

expose

暴露端口,但不需要建立与宿主机的映射,只是会向链接的服务提供
示例:

expose:
  - "3000"
  - "8000"

environment

加入环境变量,可以使用数组或者字典,只有一个key的环境变量可以在运行compose的机器上找到对应的值
示例:

environment:
  RACK_ENV: development
  SESSION_SECRET:

environment:
  - RACK_ENV: development
  - SESSION_SECRET:

env_file

从一个文件中引入环境变量,该文件可以是一个单独的值或者一个列表,如果同时定义了environment,则environment中的环境变量会重写这些值
示例:

env_file:
  - env
cat env
RACK_ENV: development

depends_on

定义当前服务启动时,依赖的服务,当前服务会在依赖的服务启动后启动

depends_on:
  - redis

deploy

该配置项在version 3里才引入,用于指定服务部署和运行时相关的参数

version: '3'
services:
  redis:
    image: redis:alpine
    deploy:
      replicas: 6
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

下面是deploy中常用参数的说明

replicas

指定副本数:

version: '3'
services:
  worker:
    image: dockersamples/examplevotingapp_worker
    deploy:
      replicas: 6

restart_policy

指定重启策略:

version: "3"
services:
  redis:
    image: redis:alpine
    deploy:
      restart_policy:
        condition: on-failure   #重启条件:on-failure, none, any
        delay: 5s   # 等待多长时间尝试重启
        max_attempts: 3 #尝试的次数
        window: 120s    # 在决定重启是否成功之前等待多长时间

update_config

定义更新服务的方式,常用于滚动更新

version: '3.4'
services:
  vote:
    image: dockersamples/examplevotingapp_vote:before
    depends_on:
      - redis
    deploy:
      replicas: 2
      update_config:
        parallelism: 2  # 一次更新2个容器
        delay: 10s  # 开始下一组更新之前,等待的时间
        failure_action:pause  # 如果更新失败,执行的动作:continue, rollback, pause,默认为pause
        max_failure_ratio: 20 # 在更新过程中容忍的失败率
        order: stop-first   # 更新时的操作顺序,停止优先(stop-first,先停止旧容器,再启动新容器)还是开始优先(start-first,先启动新容器,再停止旧容器),默认为停止优先,从version 3.4才引入该配置项

resources

限制服务资源:

version: '3'
services:
  redis:
    image: redis:alpine
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M

healthcheck

执行健康检查

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"]   # 用于健康检查的指令
  interval: 1m30s   # 间隔时间
  timeout: 10s  # 超时时间
  retries: 3    # 重试次数
  start_period: 40s # 启动多久后开始检查

network_mode

网络类型,可指定容器运行的网络类型
示例:

network_mode: "bridge"
network_mode: "host"
network_mode: "none"

dns

自定义dns服务
示例:

dns: 8.8.8

dns:
  - 223.5.5.5
  - 223.6.6.6

3、networks

网络决定了服务之间以及服务和外界之间如何去通信,在执行docker-compose up的时候,docker会默认创建一个默认网络,创建的服务也会默认的使用这个默认网络。服务和服务之间,可以使用服务的名字进行通信,也可以自己创建网络,并将服务加入到这个网络之中,这样服务之间可以相互通信,而外界不能够与这个网络中的服务通信,可以保持隔离性。

在networks中定义一个名为app_net,类型为driver的网络:

version: '2'

services:
  app:
    image: busybox
    command: ifconfig
    networks:
      app_net:
        ipv4_address: 172.16.238.10

networks:
  app_net:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
      - subnet: 172.16.238.0/24
        gateway: 172.168.238.254

4、volumes

version: "3.2"
services:
  web:
    image: nginx:alpine
    volumes:
      - type: volume
        source: mydata
        target: /data
        volume:
          nocopy: true
      - type: bind
        source: ./static
        target: /opt/app/static

  db:
    image: postgres:latest
    volumes:
      - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
      - "dbdata:/var/lib/postgresql/data"

volumes:
  mydata:
  dbdata:

六、Docker Compose案例实践

1、部署一个web集群

项目说明

这是一个典型的web项目,由一个haproxy容器加三个web容器组成。haproxy在前端充当负载均衡器,反向代理到后台三个服务服务。

基本目录结构

首先创建一个compose-haproxy-web的目录,然后在目录里面,创建两个子目录:haproxy和web。

在web目录里包含三个文件: Dockerfile、index.py、index.html

在haproxy目录里包含一个文件: haproxy.cfg

目录结构如下:

compose-haproxy-web/
├── docker-compose.yml
├── haproxy
│   └── haproxy.cfg
└── web
    ├── Dockerfile
    ├── index.html
    └── index.py

2 directories, 5 files

web目录下的index.py提供一个简单的http服务,打印出访问者的ip和实际的本地IP,内容如下:

#!/usr/bin/python
#authors: yeasy.github.com
#date: 2013-07-05
import sys
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import socket
import fcntl
import struct
import pickle
from datetime import datetime
from collections import OrderedDict

class HandlerClass(SimpleHTTPRequestHandler):
  def get_ip_address(self,ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
      s.fileno(),
      0x8915, # SIOCGIFADDR
      struct.pack('256s', ifname[:15])
    )[20:24])
  def log_message(self, format, *args):
    if len(args) < 3 or "200" not in args[1]:
      return
    try:
      request = pickle.load(open("pickle_data.txt","r"))
    except:
      request=OrderedDict()
    time_now = datetime.now()
    ts = time_now.strftime('%Y-%m-%d %H:%M:%S')
    server = self.get_ip_address('eth0')
    host=self.address_string()
    addr_pair = (host,server)
    if addr_pair not in request:
      request[addr_pair]=[1,ts]
    else:
      num = request[addr_pair][0]+1
      del request[addr_pair]
      request[addr_pair]=[num,ts]
    file=open("index.html", "w")
    file.write("<!DOCTYPE html> <html> <body><center><h1><font color="blue" face="Georgia, Arial" size=8><em>HA</em></font> Webpage Visit Results</h1></center>");
    for pair in request:
      if pair[0] == host:
        guest = "LOCAL: "+pair[0]
      else:
        guest = pair[0]
      if (time_now-datetime.strptime(request[pair][1],'%Y-%m-%d %H:%M:%S')).seconds < 3:
        file.write("<p style="font-size:150%" >#"+ str(request[pair][1]) +": <font color="red">"+str(request[pair][0])+ "</font> requests " + "from &lt<font color="blue">"+guest+"</font>&gt to WebServer &lt<font color="blue">"+pair[1]+"</font>&gt</p>")
      else:
        file.write("<p style="font-size:150%" >#"+ str(request[pair][1]) +": <font color="maroon">"+str(request[pair][0])+ "</font> requests " + "from &lt<font color="navy">"+guest+"</font>&gt to WebServer &lt<font color="navy">"+pair[1]+"</font>&gt</p>")
    file.write("</body> </html>");
    file.close()
    pickle.dump(request,open("pickle_data.txt","w"))

if __name__ == '__main__':
  try:
    ServerClass = BaseHTTPServer.HTTPServer
    Protocol = "HTTP/1.0"
    addr = len(sys.argv) < 2 and "0.0.0.0" or sys.argv[1]
    port = len(sys.argv) < 3 and 80 or int(sys.argv[2])
    HandlerClass.protocol_version = Protocol
    httpd = ServerClass((addr, port), HandlerClass)
    sa = httpd.socket.getsockname()
    print "Serving HTTP on", sa[0], "port", sa[1], "..."
    httpd.serve_forever()
  except:
    exit()

web目录下index.html文件是一个空文件,在程序启动之后会用到。

web目录下Dockerfile内容如下:

FROM python:2.7
WORKDIR /code
ADD . /code
EXPOSE 80
CMD python index.py

haproxy目录下的haproxy.cfg内容如下:

global
    log 127.0.0.1 local0
    log 127.0.0.1 local1 notice

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

listen stats
    bind 0.0.0.0:70
    stats enable
    stats uri /

frontend balancer
    bind 0.0.0.0:80
    mode http
    default_backend web_backends

backend web_backends
    mode http
    option forwardfor
    balance roundrobin
    server weba weba:80 check
    server webb webb:80 check
    server webc webc:80 check
    option httpchk GET /
    http-check expect status 200

配置docker-compose

docker-compose.yml内容如下:

version: '3'
services:
  weba:
    build: ./web
    expose:
    - 80

  webb:
    build: ./web
    expose:
    - 80

  webc:
    build: ./web
    expose:
    - 80

  haproxy:
    image: haproxy:latest
    volumes:
    - ./haproxy:/haproxy-override
    - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    links:
    - weba
    - webb
    - webc
    ports:
    - "80:80"
    - "70:70"
    expose:
    - "80"
    - "70"

启动docker-compose:

docker-compose up -d

通过验证,我们可以看到服务正常部署,访问http://127.0.0.1:80也可以完成服务的正常访问。但是在前面,我们讲过,weba和webb以及webc都是由同一个image创建而来,那么我们是否可以使用deploy配置项,直接配置三副本呢?

我们验证一下,修改docker-compose.yml内容如下:

version: '3'
services:
  web:
    build: ./web
    expose:
    - 80
    deploy:
      replicas: 3

  haproxy:
    image: haproxy:latest
    volumes:
    - ./haproxy:/haproxy-override
    - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    external_links:
    - compose-haproxy-web_web_1
    - compose-haproxy-web_web_2
    - compose-haproxy-web_web_3
    ports:
    - "80:80"
    - "70:70"
    expose:
    - "80"
    - "70"

执行docker-compose up -d:

root@ubuntu:~/trainning/compose-haproxy-web# docker-compose up -d
WARNING: Some services (web) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
compose-haproxy-web_web_1 is up-to-date
compose-haproxy-web_haproxy_1 is up-to-date

上面提示deploy不被支持,然后web也只启动了一个。这是为什么呢?

因为deploy在使用的时候,有一些限制,但你的compose文件中出现如下配置项时,deploy就无法使用:

  • build
  • cgroup_parent
  • container_name
  • devices
  • tmpfs
  • external_links
  • links
  • network_mode
  • restart
  • security_opt
  • stop_signal
  • sysctls
  • userns_mode

2、使用Docker Compose一键部署zabbix

version: '3'
services:
  mysql-server:
    hostname: mysql-server
    container_name: mysql-server
    image: mysql:5.7
    network_mode: host
    volumes:
      - /data/mysql5721/data:/var/lib/mysql
    command: --character-set-server=utf8
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: zabbix
      MYSQL_USER: zabbix
      MYSQL_PASSWORD: zabbix

  zabbix-web-nginx-mysql:
    hostname: zabbix-web-nginx-mysql
    container_name: zabbix-web-nginx-mysql
    image: zabbix/zabbix-web-nginx-mysql:alpine-3.4.11
    network_mode: host
    depends_on:
      - mysql-server
      - zabbix-server
    ports:
      - 80:80
    environment:
      DB_SERVER_HOST: 127.0.0.1
      MYSQL_DATABASE: zabbix
      MYSQL_USER: zabbix
      MYSQL_PASSWORD: zabbix
      MYSQL_ROOT_PASSWORD: 123456
      ZBX_SERVER_HOST: 127.0.0.1
      PHP_TZ: Asia/Shanghai

  zabbix-server:
    hostname: zabbix-server-mysql 
    container_name: zabbix-server-mysql 
    image: zabbix/zabbix-server-mysql:alpine-3.4.11
    depends_on:
      - mysql-server
    network_mode: host
    ports:
      - 10051:10051
    environment:
      DB_SERVER_HOST: 127.0.0.1
      MYSQL_DATABASE: zabbix
      MYSQL_USER: zabbix
      MYSQL_PASSWORD: zabbix
      MYSQL_ROOT_PASSWORD: 123456

  zabbix-agent:
    hostname: zabbix-agent
    container_name: zabbix-agent
    image: zabbix/zabbix-agent:alpine-3.4.11
    network_mode: host
    environment:
      ZBX_HOSTNAME: monitor
      ZBX_SERVER_HOST: 127.0.0.1

docker-compose up 时提示挂载目录open permission denied

docker-compose up 时提示挂载目录open permission denied

[root@localhost nginx-php]# docker-compose up
Creating php-fpm ... 
Creating php-fpm ... done
Creating nginx ... 
Creating nginx ... done
Attaching to php-fpm, nginx
php-fpm    | [15-Dec-2017 06:53:36] NOTICE: fpm is running, pid 1
php-fpm    | [15-Dec-2017 06:53:36] NOTICE: ready to handle connections
nginx      | nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
nginx      | 2017/12/15 06:53:36 [emerg] 1#1: open() "/var/log/nginx/error.log" failed (13: Permission denied)
nginx exited with code 1
^CGracefully stopping... (press Ctrl+C again to force)
Stopping php-fpm ... done
[root@localhost nginx-php]#

原因是SELinux搞的鬼,禁用就好了

#查看SELinux状态(如果SELinux status参数为enabled即为开启状态)
/usr/sbin/sestatus -v 

#临时关闭
setenforce 0

#修改配置文件重启机器禁用(将SELINUX=enforcing改为SELINUX=disabled)
vim /etc/selinux/config

docker-compose方式启动etcd

网上几乎所有启动etcd容器方式都是以 dokcer run的形式,但是由于生产上采用docker-compose的写法会更方便维护,于是通过不断尝试后,最终测试出了以dokcer-compose启动etcd的方式

1. 以docker run形式启动etcd

# 设置HostIP
export HostIP=192.168.1.102
# 执行etcd安装启动命令
docker run -d -v /usr/share/ca-certificates/:/etc/ssl/certs -p 4001:4001 -p 2380:2380 -p 2379:2379 
 --restart=always 
 --name etcd registry.cn-hangzhou.aliyuncs.com/coreos_etcd/etcd:v3 
 /usr/local/bin/etcd 
 -name etcd0 
 -advertise-client-urls http://${HostIP}:2379,http://${HostIP}:4001 
 -listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 
 -initial-advertise-peer-urls http://${HostIP}:2380 
 -listen-peer-urls http://0.0.0.0:2380 
 -initial-cluster-token etcd-cluster-1 
 -initial-cluster etcd0=http://${HostIP}:2380 
 -initial-cluster-state new

2. 以docker-compose启动的模式如下

etcd:
    container_name: etcd0
    image: registry.cn-hangzhou.aliyuncs.com/coreos_etcd/etcd:v3
    ports:
      - "2379:2379"
      - "4001:4001"
      - "2380:2380"
    environment:
      - TZ=CST-8
      - LANG=zh_CN.UTF-8
    command: 
      /usr/local/bin/etcd
      -name etcd0 
      -data-dir /etcd-data
      -advertise-client-urls http://${host_ip}:2379,http://${host_ip}:4001
      -listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001
      -initial-advertise-peer-urls http://${host_ip}:2380
      -listen-peer-urls http://0.0.0.0:2380 
      -initial-cluster-token docker-etcd 
      -initial-cluster etcd0=http://${host_ip}:2380
      -initial-cluster-state new 
    volumes:
      - "/data/conf/etcd/data:/etcd-data"
      # - "/data/config/etcd/ca-certificates/:/etc/ssl/certs"
    labels:
      - project.source=
      - project.extra=public-image
      - project.depends=
      - project.owner=LHZ

使用 Docker Compose 快速构建集群

本文档介绍如何在单机上通过 Docker Compose 快速一键部署一套 TiDB 测试集群。

Docker Compose 可以通过一个 YAML 文件定义多个容器的应用服务,然后一键启动或停止。

准备环境

确保你的机器上已安装:

  • Docker(17.06.0 及以上版本)
  • Docker Compose
  • Git

快速部署

1.下载 tidb-docker-compose

git clone https://github.com/pingcap/tidb-docker-compose.git

2.创建并启动集群

cd tidb-docker-compose && docker-compose pull # Get the latest Docker images
docker-compose up -d
  1. 访问集群
mysql -h 127.0.0.1 -P 4000 -u root

访问集群 Grafana 监控页面:http://localhost:3000 默认用户名和密码均为 admin。

集群数据可视化:http://localhost:8010

自定义集群

在完成快速部署后,以下组件已默认部署:3 个 PD,3 个 TiKV,1 个 TiDB 和监控组件 Prometheus,Pushgateway,Grafana 以及 tidb-vision。

如果想自定义集群,可以直接修改 docker-compose.yml,但是手动修改比较繁琐而且容易出错,强烈建议使用 Helm 模板引擎生成 docker-compose.yml 文件。

1.安装 Helm

Helm 可以用作模板渲染引擎,只需要下载其 binary 文件即可以使用。

curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash

如果是 Mac 系统,也可以通过 Homebrew 安装:

brew install kubernetes-helm

2.下载 tidb-docker-compose

git clone https://github.com/pingcap/tidb-docker-compose.git

3.自定义集群

cd tidb-docker-compose
cp compose/values.yaml values.yaml
vim values.yaml

修改 values.yaml 里面的配置,例如集群规模,TiDB 镜像版本等。

tidb-vision 是 TiDB 集群可视化页面,可以可视化地显示 PD 对 TiKV 数据的调度。如果不想部署该组件,可以将 tidbVision 项留空。

PD,TiKV,TiDB 和 tidb-vision 支持从 GitHub 源码或本地文件构建 Docker 镜像,供开发测试使用。

  • 如果希望从 GitHub 源码构建某个组件的镜像,需要将其 image 字段留空,然后设置其 buildFrom 为 remote。

  • 如果希望从本地已编译好的 binary 文件构建 PD,TiKV 或 TiDB 镜像,需要将其 image 字段留空,然后设置其 buildFrom 为 local,并将已编译好的 binary 拷贝到对应的 pd/bin/pd-server,tikv/bin/tikv-server,tidb/bin/tidb-server。

  • 如果希望从本地构建 tidb-vision 镜像,需要将其 image 字段留空,然后设置其 buildFrom 为 local,并将 tidb-vision 项目拷贝到 tidb-vision/tidb-vision。

4.生成 docker-compose.yml 文件

helm template -f values.yaml compose > generated-docker-compose.yml

5.使用生成的 docker-compose.yml 创建并启动集群

docker-compose -f generated-docker-compose.yaml pull # Get the latest Docker images
docker-compose -f generated-docker-compose.yml up -d

6.访问集群

mysql -h 127.0.0.1 -P 4000 -u root

访问集群 Grafana 监控页面:http://localhost:3000 默认用户名和密码均为 admin。

如果启用了 tidb-vision,可以通过 http://localhost:8010 查看。

访问 Spark shell 并加载 TiSpark

向 TiDB 集群中插入一些样本数据:

$ docker-compose exec tispark-master bash
$ cd /opt/spark/data/tispark-sample-data
$ mysql -h tidb -P 4000 -u root < dss.ddl

当样本数据加载到 TiDB 集群之后,可以使用 docker-compose exec tispark-master /opt/spark/bin/spark-shell 来访问 Spark shell。

$ docker-compose exec tispark-master /opt/spark/bin/spark-shell
...
Spark context available as 'sc' (master = local[*], app id = local-1527045927617).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _ / _ / _ `/ __/  '_/
   /___/ .__/_,_/_/ /_/_   version 2.1.1
      /_/

Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_172)
Type in expressions to have them evaluated.
Type :help for more information.

scala> import org.apache.spark.sql.TiContext
...
scala> val ti = new TiContext(spark)
...
scala> ti.tidbMapDatabase("TPCH_001")
...
scala> spark.sql("select count(*) from lineitem").show
+--------+
|count(1)|
+--------+
|   60175|
+--------+

docker-compose运行jenkins集成sonarqube代码质量检测

  • 192.168.0.141 jenkins

  • 192.168.0.142 sonarqube

本实例用到2台机器,一台机器里面跑着jenkins服务,一台机器为sonarqube服务

jenkins服务部署这里就不做多的讲解,可以采用docker-compose部署,也可以单独部署,博主采用docker-compose部署的jenkins,我们在另外一台机器部署一个sonarqube服务,这里我们用mysql5.6的镜像和sonarqube的最新版的镜像镜像compose编排部署。

这里编排的是3个服务在一起了,有需要可以自行进行拆分

请参考博主开源项目:https://github.com/lizibin/docker-jenkins-sonarqube

1、拉取博主的项目,即可成功运行jenkins和sonarqube的代码质量检测

我们可以访问主机ip+9000端口访问到页面

未分类

2、这里我们还需要再jenkins进行配置一下插件,才可以自动分析代码质量

未分类

找到这个插件安装好这个插件就开始配置

3、配置jenkins 进入系统配置

未分类

继续配置Global Tool Configuration

未分类

然后我们就可以建立一个自由风格的项目来分析代码了

4、创建项目,需要勾上

未分类

未分类

修改Analysis properties

未分类

就可以开始构建了

5、构建完毕我们可以去9000端口查看页面详情

未分类

使用Docker Compose部署python应用

这篇文章我们通过Docker Compose来运行部署一个简单的Python web应用,我们将使用Flask框架和Redis。

预备的东西

  • 安装好Docker
  • 安装好Docker Compose

定义应用的依赖

1、创建项目目录

mkdir composetest
cd composetest

2、创建应用文件app.py

在项目根目录composetest下创建并打开app.py文件,编写如下代码:

import time

import redis
from flask import Flask


app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)


def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)


@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.n'.format(count)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

这里,redis是应用网络上edis容器的主机名,我们使用Redis的默认端口:6379。

3、创建应用requirements.txt文件

在项目根目录composetest下创建requirements.txt文件,编辑文件:

flask
redis

创建Dockerfile文件

我们写个Dockerfile文件来构建Docker镜像,该镜像会包含该Python应用的所有依赖,包括Python本身。

在项目根目录composetest下创建Dockerfile文件并编辑文件:

FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

该文件告诉Docker完成以下工作:

  • 构建一个以Python 3.4镜像为基础镜像的镜像.
  • 把当前目录 . 复制到镜像的 /code 路径下.
  • 设置工作目录为 /code .
  • 安装Python依赖.
  • 设置容器的默认命令.

在Compose文件定义服务

在项目根目录composetest下创建docker-compose.yml文件并编辑:

version: '3'
services:
  web:
    build: .
    ports:
     - "5000:5000"
  redis:
    image: "redis:alpine"

该Compose文件定义两个服务:web和redis。该web服务将

  • 使用从Dockerfile文件构建的镜像.
  • 暴露容器的5000端口映射到宿主机的5000端口。我们使用Flask的web服务器默认端口 5000.

redis服务使用从Docker Hub registry仓库拉取的公有Redis镜像。

使用Compose构建和运行app

在项目根目录composetest下启动运行应用:

$ docker-compose up
Starting composetest_web_1   ... done
Starting composetest_redis_1 ... done
Attaching to composetest_redis_1, composetest_web_1
redis_1  | 1:C 05 May 02:38:07.354 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1  | 1:C 05 May 02:38:07.354 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1  | 1:C 05 May 02:38:07.354 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis_1  | 1:M 05 May 02:38:07.355 * Running mode=standalone, port=6379.
redis_1  | 1:M 05 May 02:38:07.355 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1  | 1:M 05 May 02:38:07.355 # Server initialized
redis_1  | 1:M 05 May 02:38:07.355 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
redis_1  | 1:M 05 May 02:38:07.355 * DB loaded from disk: 0.000 seconds
redis_1  | 1:M 05 May 02:38:07.355 * Ready to accept connections
web_1    |  * Serving Flask app "app" (lazy loading)
web_1    |  * Environment: production
web_1    |    WARNING: Do not use the development server in a production environment.
web_1    |    Use a production WSGI server instead.
web_1    |  * Debug mode: on
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
web_1    |  * Restarting with stat
web_1    |  * Debugger is active!
web_1    |  * Debugger PIN: 320-444-115
web_1    | 172.18.0.1 - - [05/May/2018 02:38:11] "GET / HTTP/1.1" 200 -

Compose拉取Redis镜像,为我们的应用构建一个镜像,并开始我们定义的服务。这种情况,代码在构建时是被静态地复制到镜像里。

打开浏览器,输入http://0.0.0.0:5000/, 会看到页面输出:

Hello World! I have been seen 1 times.

刷新页面,这里的数字会顺序递增

Hello World! I have been seen 2 times.

打开另一个terminal窗口,输入docker image ls可以看到本地的镜像列表,列表中会看到redis和web:

$ docker image ls
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
composetest_web                  latest              88cd35029579        12 hours ago        94.9MB
springio/gs-spring-boot-docker   latest              6c173fe17a59        26 hours ago        118MB
python                           3.4-alpine          42756b6e26a0        2 weeks ago         83.6MB
hello-world                      latest              e38bc07ac18e        3 weeks ago         1.85kB
redis                            alpine              98bd7cfc43b8        5 weeks ago         27.8MB
openjdk                          8-jdk-alpine        224765a6bdbe        3 months ago        102MB

要停止应用,可以输入docker-compose down命令或者按CTRL + C。

修改Compose文件

编辑docker-compose.yml,为web服务添加一个bind mount:

version: '3'
services:
  web:
    build: .
    ports:
     - "5000:5000"
    volumes:
     - .:/code
  redis:
    image: "redis:alpine"

新添加的 volume 键将宿主机的项目目录挂载到容器里的 /code 目录,这样我们可以即时地修改代码,而不用重新构建镜像。

重新构建和运行应用

项目根目录下输入 docker-compose up 重新构建并运行修改Compose文件后的应用

$ docker-compose up
Creating network "composetest_default" with the default driver
Creating composetest_web_1 ...
Creating composetest_redis_1 ...
Creating composetest_web_1
Creating composetest_redis_1 ... done
Attaching to composetest_web_1, composetest_redis_1
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
...

刷新浏览器,会看到计数依然会递增。

更新应用

现在项目代码已经通过 volume 被挂载到容器,我们可以修改代码并即时看到变化,而不用重新构建镜像。我们编辑修改下 app.py文件,比如将 Hello World! 修改为 Hello from Docker!:

return 'Hello from Docker! I have been seen {} times.n'.format(count)

刷新浏览器,页面会相应地更新

未分类

docke-compose其他命令

在后台运行服务:

$ docker-compose up -d

查看当前运行服务:

$ docker-compose ps

查看web服务在使用的环境变量:

$ docker-compose run web env

停止服务:

$ docker-compose stop

停止并删除容器、网络、镜像和volumes卷

$ docker-compose down --volumes

使用Docker compose配置WordPress运行环境并支持https

最近把 WordPress 迁移到了腾讯云,为了配置方便使用了 docker 来运行,这里记录下配置过程

准备 compose 文件

WordPress 的 docker compose 文件网上有很多,需要一个 mysql 的镜像,还有 WordPress 的镜像,大概长这样:

version: '3'
services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

定制 Dockerfile 添加 https 支持

借助于 letsencrypt 这个项目, 给个人网站添加 letsencrypt 变得十分容易,详细见这篇文章:

如何免费的让网站启用HTTPS: (https://coolshell.cn/articles/18094.html)

大概流程就是安装一个软件包 letsencrypt ,然后配置你的网站信息即可,但是我们的 WordPress 是安装在 docker 里面,所以我们要想办法把这个软件包打进镜像里面。

接下来我们要对 WordPress 这个镜像进行自定义,参考这篇文章:

docker + wordpress + letsencrypt: (https://breeto.id.au/2017/03/docker-wordpress-letsencrypt/)

先定制 Dockerfile,集成 letsencrypt

新建文件夹 wordpress_tls 添加 Dockerfile

FROM wordpress:php7.1
RUN echo "export TERM=xterm LANG=en_US.UTF-8" >> ~/.bashrc 
    && apt-get update && apt-get -y install git 
    && rm -rf "/opt/letsencrypt" 
    && git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt 
    && cd /opt/letsencrypt 
    && ./letsencrypt-auto --version

wordpress 官方镜像使用的 ubuntu 源是国外源,打包镜像的速度会让你怀疑人生。可以把宿主机的 ubuntu 源放进 docker 镜像里。

$cp /etc/apt/sources.list ./

修改 Dockerfile

FROM wordpress:php7.1
ADD sources.list /etc/apt/sources.list
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 
    && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 // 改成你的 key
RUN echo "export TERM=xterm LANG=en_US.UTF-8" >> ~/.bashrc 
    && apt-get update && apt-get -y install git 
    && rm -rf "/opt/letsencrypt" 
    && git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt 
    && cd /opt/letsencrypt 
    && ./letsencrypt-auto --version

添加新的源会有认证的问题,可以参考 http://naveenubuntu.blogspot.com/2011/08/fixing-gpg-keys-in-ubuntu.html 解决

配置 https

启动容器:

$docker-compose up -d

然后配置 https

$docker-compose exec wordpress_tls /opt/letsencrypt/certbot-auto --apache -d your.domain.com --agree-tos -n -m you@your.domain.com

Let’s Encrypt 的证书90天就过期了,过期后执行

$ docker-compose exec wordpress_tls /opt/letsencrypt/certbot-auto renew

来更新,可以把更新脚本写进 crontab
$crontab -e

0 0 1 * * docker-compose exec wordpress_tls /opt/letsencrypt/certbot-auto renew

完整示例

https://github.com/myfjdthink/docker-wordpress

使用docker-compose实现容器编排

创建一个容器,我们可以通过Dockerfile模板文件对镜像进行创建并按照配置要求启动。然而,一般项目往往需要多个容器相互配合才能完成某项任务,比如说在一个web项目中,除了web服务容器,往往还需要后端的数据库服务容器,甚至还需要负载均衡容器等。如何有效地做好容器之间的编排,是Docker Compose要做的内容。

Docker Compose是定义和运行多个Docker容器的工具,它主要管理一个项目(project),这个项目是由一组关联的应用容器组成的一个完整业务单元,而每个应用容器则对应一个服务(service),当然服务可能只包含一个容器(container)实例,也可能包括若干运行相同镜像的容器(container)实例。

Docker Compose的核心就在于“一个文件”和“一条命令”。所谓“一个文件”,是指docker-compose.yml,在这个文件中我们可以进行项目的配置,包括服务的定义。而“一条命令”则指,我们只需要类似docker-compose up这样简单的命令即可管理项目。其他帮助和命令,我们可以通过docker-compose -h进行查询。

未分类

未分类

为了更好地理解docker-compose如何进行容器编排,下面运行一个python web应用,其中web框架基于flask,计数器基于redis。

我们要实现的效果就是,每访问一次web服务网址,计数器对应加1。

创建项目目录

如果在启动项目时,如果没有指定具体的项目名称,则项目所在的目录名作为默认的项目名称。

mkdir composetest
cd composetest

使用Dockerfile定义镜像配置

在composetest目录下,创建3个文件,内容依次为:

app.py

#coding: utf-8
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379) # ''中的redis对应服务名
@app.route('/')
def hello():
    count = redis.incr('hits')
    return 'Hello World! I have been seen {} times.n'.format(count)
if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

requirements.txt

flask
redis

Dockefile

FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

这样,我们定义了web服务镜像的配置。在第一次启动项目的时候,docker-compose会使用Dockerfile创建一个名为composetest_web的镜像。

使用Docker Compose来管理项目

定义项目配置文件docker-compose.yml
在composetest目录下,创建docker-compose.yml,内容为:

version: '3'
services:
 web:
  build: .
  ports:
   - "5000:5000"
  volumes:
   - .:/code
 redis:
  image: redis

docker-compose.yml中定义了web和redis服务,对于web服务:

build    在当前目录从Dockerfile创建一个镜像
ports    端口映射,开放容器5000端口映射到宿主机5000端口
volumes    挂载数据卷,挂载容器目录/code对应到宿主机当前目录

未分类

此时我们需要的文件都准备好了,开始启动项目:

docker-compose up

未分类

此时我们可以通过浏览器访问对应的 http://ip:5000 地址,如果在服务本地访问,则对应 http://localhost:5000 。

未分类

可以看到每访问一次web服务地址,计数器对应加1。如果要更新应用,因为挂载了数据卷,只需修改app.py保存即可。

使用容器互联

除了使用docker-compose来进行容器编排,如果情况不太复杂,我们也可以使用容器互联的手段实现项目需求。容器互联要求针对多个容器,分别按顺序启动并指明依赖关系,也就是利用好“–link”这个参数。

同样,在composetest目录下,先启动redis服务容器:

docker run -d --name redis redis

创建web服务镜像:

docker build -t dockerfile_web .

通过查看镜像,可以看到生成的dockerfile_web镜像,在docker-compose.yml中定义的服务依赖的镜像也已经具备了,如composetest_web。由于dockerfile_web和composetest_web镜像都是由同一个Dockerfile模板文件生成的,本质上它们是一样的。

未分类

启动web服务容器,并关联redis服务容器:

docker run --name web -p 5000:5000 -v $(pwd):/code --link redis:webredis dockerfile_web

这样也能得到同样的效果,但是,对于复杂的容器编排情况,还是乖乖地使用docker-compose等利器吧。

docker compose出现ERROR: Version in “./docker-compose.yml” is unsupported错误

问题

以下是我的docker-compose.yml文件

version: “2”
services:
  web:
   build: .
   environment:
    MONGO_URI="mongodb://ravimongo:27017"
   ports:
    — “3000:3000”
   links:
    — ravimongo
   depends_on:
    — ravimongo
  ravimongo:
   image: mongo:3.2.6
   ports:
     — “27017:27017”

下面是错误信息:

ERROR: Version in "./docker-compose.yml" is unsupported. You might be seeing this error because you're using the wrong Compose file version. Either specify a supported version ("2.0", "2.1", "3.0") and place your service definitions under the `services` key, or omit the `version` key and place your service definitions at the root of the file to use version 1.
For more on the Compose file format versions, see https://docs.docker.com/compose/compose-file/

docker-compose的版本信息:

docker-compose version 1.11.2, build dfed245
docker-py version: 2.1.0
CPython version: 2.7.12
OpenSSL version: OpenSSL 1.0.2j  26 Sep 2016

docker版本信息:

Client:
 Version:      17.03.1-ce
 API version:  1.27
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Tue Mar 28 00:40:02 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      17.03.1-ce
 API version:  1.27 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Fri Mar 24 00:00:50 2017
 OS/Arch:      linux/amd64
 Experimental: true

我使用了 http://www.yamllint.com/ 和 https://codebeautify.org/yaml-validator验证这个yaml文件,没发现有什么问题。

最佳答案

YAML是有效的。不过你使用了一个左双引号,如:

version: “2”

基于错误显示,看来docker compose无法解析正确的版本。如果你使用了一个左双引号,而不是正常的双引号,docker compose将解析版本为“2”,而不是2。应该改为:

version: "2"

利用docker-compose安装lnmp(Nginx mariadb php7.0 )

对于Docker来说,最大的便利就是能快速的搭建起一个个的容器,容器之间可以通过网络和文件来进行通信。

之前我已经将自己的博客使用docker搭建起来了,这里简单记录一下docker-compose文件内容。

我的博客的架构为lnmp,依赖的容器有:

  • Nginx(Port:80)

  • mariadb(Port:3306)

  • wordpress+php7.0-fpm(Port:9000)

  • phpmyadmin(Port:8009)

docker-compose.yml文件内容如下

nginx:
    image: nginx:latest
    ports:
        - '80:80'
    volumes:
        - ./nginx:/etc/nginx/conf.d
        - ./logs/nginx:/var/log/nginx
        - ./jialeens:/var/www/html
    links:
        - wordpress
    restart: always

mysql:
    image: mariadb
    ports:
        - '3306:3306'
    volumes:
        - ./db-data:/var/lib/mysql
    environment:
        - MYSQL_ROOT_PASSWORD=******
    restart: always

wordpress:
    image: wordpress:4.8.0-php7.0-fpm
    ports:
        - '9000:9000'
    volumes:
        - ./jialeens:/var/www/html
    environment:
        - WORDPRESS_DB_NAME=***
        - WORDPRESS_TABLE_PREFIX=wp_
        - WORDPRESS_DB_HOST=mysql
        - WORDPRESS_DB_PASSWORD=*****
    links:
        - mysql
    restart: always
phpmyadmin:
  image: phpmyadmin/phpmyadmin
  links:
    - mysql
  environment:
    PMA_HOST: mysql
    PMA_PORT: 3306
  ports:
    - '8009:80'

Nginx配置文件:

jialeens.com.conf

server {
    listen 80;
    server_name jialeens.com www.jialeens.com;

    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;
    client_max_body_size 100m;
    root /var/www/html;
    index index.php;

    access_log /var/log/nginx/jialeens-access-http.log;
    error_log /var/log/nginx/jialeens-error-http.log;

    if ($host = 'jialeens.com') {
        return 301 http://www.jialeens.com$request_uri;
    }
    location ~* ^.+.(js|ico|gif|jpg|jpeg|png|html|htm)$ {
       log_not_found off;
       access_log off;
       expires 7d;
    }
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ .php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PHP_VALUE "upload_max_filesize=128M n post_max_size=128M";
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

因为流量不大,所以没做fastcgi的缓存,以后有空再弄吧。