Compose介绍

Docker Compose是一个用来定义和运行复杂应用的Docker工具。一个使用Docker容器的应用,通常由多个容器组成。使用Docker Compose不再需要使用shell脚本来启动容器。
​ Compose 通过一个配置文件来管理多个Docker容器,在配置文件中,所有的容器通过services来定义,然后使用docker-compose脚本来启动,停止和重启应用,和应用中的服务以及所有依赖服务的容器,非常适合组合使用多个容器进行开发的场景。

Compose和Docker兼容性

compose文件格式版本 docker版本
3.4 17.09.0+
3.3 17.06.0+
3.2 17.04.0+
3.1 1.13.1+
3 1.13.0+
2.3 17.06.0+
2.2 1.13.0+
2.1 1.12.0+
2 1.10.0+
1 1.9.1.+

Docker版本变化说明

Docker从1.13.x版本开始,版本分为企业版EE和社区版CE,版本号也改为按照时间线来发布,比如17.03就是2017年3月。

Docker的linux发行版的软件仓库从以前的https://apt.dockerproject.org和https://yum.dockerproject.org变更为目前的https://download.docker.com, 软件包名字改为docker-ce和docker-ee。

安装docker-compose

这里简单介绍下两种安装docker-compose的方式,第一种方式相对简单,但是由于网络问题,常常安装不上,并且经常会断开,第二种方式略微麻烦,但是安装过程比较稳定

二进制文件安装

https://github.com/docker/compose/releases/ 地址查找最新的docker-compose版本

下载最新版的docker-compose文件
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
添加可执行权限
sudo chmod +x /usr/local/bin/docker-compose
测试安装结果
docker-compose --version

pip安装

安装python-pip
yum -y install epel-release
yum -y install python-pip
安装docker-compose
pip install docker-compose
测试安装结果
docker-compose --version

快速入门

打包项目

打包项目,获得 jar 包 docker-demo-0.0.1-SNAPSHOT.jar

创建Dockerfile

在 jar 包所在路径创建 Dockerfile 文件,添加以下内容

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD docker-demo-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

创建 docker-compose 配置文件

在 jar 包所在路径创建文件 docker-compose.yml,添加以下内容

version: '2' # 表示该 Docker-Compose 文件使用的是 Version 2 file
services:
  docker-demo:  # 指定服务名称
    build: .  # 指定 Dockerfile 所在路径
    ports:    # 指定端口映射
      - "8080:8080"

启动服务

在 docker-compose.yml 所在路径下执行该命令 Compose 就会自动构建镜像并使用镜像启动容器

docker-compose up
docker-compose up -d  // 后台启动并运行容器

工程实战

基本使用

接下来我们使用 docker-compose 构建我们的微服务以及mysql,并逐步讲解其使用。

准备工作

在项目文件夹下创建 docker-compose.yml文件

cd /usr/local/docker-learn/ && touch docker-compose.yml && ll

image-20210331172718485

编写配置文件

先在 docker-compose.yml 文件里添加如下代码,构建我们的项目

version: '2'   
services:
    mysql:
        image: mysql:5.7.33
        hostname: mysql
        container_name: mysql
        restart: always
        networks:
            - learn-docker-network
        volumes:
            - "/tmp/etc/mysql:/etc/mysql/mysql.conf.d/"
            - "/tmp/data/mysql:/var/lib/mysql"
        environment:
            MYSQL_ROOT_PASSWORD: 'root'
    nacos:
        image: nacos/nacos-server
        restart: always
        ports:
            - "8848:8848"
        networks:
            - learn-docker-network
        environment:
            MODE: 'standalone'
            JVM_XMS: '128m'
            JVM_XMX: '128m'
    
    learn-docker-web:
        image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-web:1.0-SNAPSHOT
        restart: always
        networks:
            - learn-docker-network
        depends_on:
            - nacos
            - mysql
        volumes:
            - "/tmp/data/logs:/logs"
    learn-docker-storage:
        image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT
        hostname: learn-docker-storage
        container_name: learn-docker-storage
        restart: always
        networks:
            - learn-docker-network
        depends_on:
            - nacos
            - mysql
        volumes:
            - "/tmp/data/logs:/logs"
    learn-docker-gateway:
        image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-gateway:1.0-SNAPSHOT
        restart: always
        ports:
            - "8888:8888"
        networks:
            - learn-docker-network
        depends_on:
            - nacos
            - mysql
        volumes:
            - "/tmp/data/logs:/logs"
networks:
    learn-docker-network:
        driver: bridge
运行测试

在项目的文件中执行docker-compose up -d命令就可以启动了

docker-compose up -d

image-20210331180513570

微服务访问测试

通过网关地址访问测试微服务

curl http://192.168.64.153:8888/employeapi/find/10001| python -m json.tool

image-20210331182422618

扩缩容

nacos查看集群情况

我们可以查看nacos,查看当服务器集群的一个部署情况

image-20210326165255998

扩容节点

我们现在对learn-docker-storage节点进行扩容

语法格式:docker-compose up -d --scale 服务名=节点数

docker-compose up -d --scale learn-docker-storage=2

image-20210326171225129

启动后查看nacos节点信息

image-20210326171252719

缩容节点

和扩容一样指定节点数量就可以的

docker-compose up -d --scale learn-docker-storage=1

image-20210326171346870

停止后后查看nacos节点信息

image-20210326171432650

工程、服务、容器

Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container)

Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例

常见命令

docker-compose常用命令可以通过以下命令大致看一下:

docker-compose --help
Define and run multi-container applications with Docker.

Usage:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file
                              (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name
                              (default: directory name)
  -c, --context NAME          Specify a context name
  --verbose                   Show more output
  --log-level LEVEL           Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  --no-ansi                   Do not print ANSI control characters
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the
                              name specified in the client certificate
  --project-directory PATH    Specify an alternate working directory
                              (default: the path of the Compose file)
  --compatibility             If set, Compose will attempt to convert keys
                              in v3 files to their non-Swarm equivalent
  --env-file PATH             Specify an alternate environment file

Commands:
  build              Build or rebuild services
  config             Validate and view the Compose file
  create             Create services
  down               Stop and remove containers, networks, images, and volumes
  events             Receive real time events from containers
  exec               Execute a command in a running container
  help               Get help on a command
  images             List images
  kill               Kill containers
  logs               View output from containers
  pause              Pause services
  port               Print the public port for a port binding
  ps                 List containers
  pull               Pull service images
  push               Push service images
  restart            Restart services
  rm                 Remove stopped containers
  run                Run a one-off command
  scale              Set number of containers for a service
  start              Start services
  stop               Stop services
  top                Display the running processes
  unpause            Unpause services
  up                 Create and start containers
  version            Show the Docker-Compose version information

下面会针对一些比较常用的命令进行简单介绍

ps

列出所有运行容器

显示所有容器信息,默认显示name、command、state、ports。

docker-compose ps

logs

打印服务日志

docker-compose logs
语法
logs [options] [SERVICE...]
参数
--no-color          Produce monochrome output.
-f, --follow        Follow log output
-t, --timestamps    Show timestamps
--tail="all"        Number of lines to show from the end of the logs
                    for each container.

port

打印绑定的公共端口,下面命令可以输出 eureka 服务 8761 端口所绑定的公共端口

docker-compose port eureka 8761

build

当修改dockerfile或者docker-compose时,运行docker-compose build 重建镜像。 生成镜像后,可使用docker-compose up启动

docker-compose build

create

为服务创建容器.只是单纯的create,还需要使用start启动compose

语法
create [options] [SERVICE...]
参数
--force-recreate       重新创建容器,即使他的配置和镜像没有改变,不兼容--no-recreate参数
--no-recreate          如果容器已经存在,不需要重新创建. 不兼容--force-recreate参数
--no-build             不创建镜像,即使缺失.
--build                创建容器前,生成镜像

start

启动指定服务已存在的容器

docker-compose start eureka

stop

停止已运行的服务的容器

docker-compose stop eureka

pause

暂停容器服务,docker-compose pause 暂停所有服务。docker-compose pause web,暂停web服务的容器。

docker-compose pause eureka

unpause

恢复容器服务,docker-compose unpause 恢复所有服务,docker-compose unpause web,恢复web服务的容器。

docker-compose unpause eureka

restart

重启docker-compose.yml中定义的所有的已停止的和正在运行的服务。

docker-compose restart

run

在一个服务上执行一个命令

docker-compose run web bash
语法
run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...]
        SERVICE [COMMAND] [ARGS...]
参数
-d                    后台运行,输出容器名.
-e KEY=VAL            设置环境变量参数,可以使用多次
-u, --user=""         指定运行的用户
--no-deps             不启动link服务,只启动run的服务.
--rm                  运行后删除容器,后台运行模式除外(-d).
-p, --publish=[]      开放端口
--service-ports       compose文件中配置什么端口,就映射什么端口.
-T                    禁用TTY.
-w, --workdir=""      设置工作目录

rm

删除已经停止的容器,如果服务在运行,需要先docker-compose stop 停止容器,默认情况下,已挂载的volume中的数据不会被删除,可以使用docker volume ls查看所有的volume情况。所有在容器中并未在volume中的数据将被删除。

docker-compose rm eureka

up

构建,(重新)创建,启动,链接一个服务相关的容器。链接的服务都将会启动,除非他们已经运行。默认情况, docker-compose up 将会整合所有容器的输出,并且退出时,所有容器将会停止。如果使用 docker-compose up -d ,将会在后台启动并运行所有的容器。

默认情况,如果该服务的容器已经存在, docker-compose up 将会停止并尝试重新创建他们(保持使用 volumes-from 挂载的卷),以保证 docker-compose.yml的修改生效。如果你不想容器被停止并重新创建,可以使用 docker-compose up --no-recreate。如果需要的话,这样将会启动已经停止的容器。

docker-compose up
语法
up [options] [--scale SERVICE=NUM...] [SERVICE...]
参数
-d                         后台运行,输出容器的名字.
                           Incompatible with --abort-on-container-exit.
--no-color                  单色输出.
--no-deps                  不启动link服务.
--force-recreate           强制重新创建compose服务,即使没有任何改变。重新创建后启动容器
                           Incompatible with --no-recreate.
--no-recreate               如果容器已经存在,不重新创建.
                           Incompatible with --force-recreate.
--no-build                 不创建重启,即使镜像不存在.
--build                    重新创建镜像,然后生成容器.
--abort-on-container-exit  任何容器停止,自动停止所有容器.
                           Incompatible with -d.
-t, --timeout TIMEOUT      超时时间. (default: 10)
--remove-orphans           移除compose文件中未定义服务的容器

down

停止和删除容器、网络、卷、镜像,这些内容是通过docker-compose up命令创建的. 默认值删除容器网络,可以通过指定 rmi volumes参数删除镜像和卷。

docker-compose down
语法
down [options]
参数
--rmi type          删除镜像,类型必须是:
                        'all': 删除compose文件中定义的所以镜像.
                        'local': 删除镜像名为空的镜像
-v, --volumes       删除卷
                        attached to containers.
--remove-orphans    Remove containers for services not defined in the
                        Compose file

kill

通过发送 SIGKILL 信号来停止指定服务的容器

强制停止某个正在运行的服务

docker-compose kill eureka

pull

下载服务镜像

docker-compose pull eureka

scale

设置指定服务运气容器的个数,以 service=num 形式指定

docker-compose scale user=3 movie=3

常用配置

image

image是指定服务的镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。

例如下面这些格式都是可以的

image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd

buld

服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。Compose 将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器。

build: /path/to/build/dir

也可以是相对路径,只要上下文确定就可以读取到 Dockerfile。

build: ./dir

设定上下文根目录,然后以该目录为准指定 Dockerfile。

build:
  context: ../
  dockerfile: path/of/Dockerfile

注意 build 都是一个目录,如果你要指定 Dockerfile 文件需要在 build 标签的子级标签中使用 dockerfile 标签指定,如上面的例子。
如果你同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。

既然可以在 docker-compose.yml 中定义构建任务,那么一定少不了 arg 这个标签,就像 Dockerfile 中的 ARG 指令,它可以在构建过程中指定环境变量,但是在构建成功后取消,在 docker-compose.yml 文件中也支持这样的写法:

build:
  context: .
  args:
    buildno: 1
    password: secret

下面这种写法也是支持的,一般来说下面的写法更适合阅读。

build:
  context: .
  args:
    - buildno=1
    - password=secret

与 ENV 不同的是,ARG 是允许空值的。例如:

args:
  - buildno
  - password

这样构建过程可以向它们赋值。

注意:YAML 的布尔值(true, false, yes, no, on, off)必须要使用引号引起来(单引号、双引号均可),否则会当成字符串解析。

command

使用 command 可以覆盖容器启动后默认执行的命令。

command: bundle exec thin -p 3000

也可以写成类似 Dockerfile 中的格式:

command: [bundle, exec, thin, -p, 3000]

container_name

Compose 的容器名称格式是:<项目名称><服务名称><序号>
虽然可以自定义项目名称、服务名称,但是如果你想完全控制容器的命名,可以使用这个标签指定:

container_name: app

这样容器的名字就指定为 app 了。

depends_on

在使用 Compose 时,最大的好处就是少打启动命令,但是一般项目容器启动的顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。

例如在没启动数据库容器的时候启动了应用容器,这时候应用容器会因为找不到数据库而退出,为了避免这种情况我们需要加入一个标签,就是 depends_on,这个标签解决了容器的依赖、启动先后的问题。

例如下面容器会先启动 redis 和 db 两个服务,最后才启动 web 服务:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

注意的是,默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系。

dns

和 –dns 参数一样用途,格式如下:

dns: 8.8.8.8

也可以是一个列表:

dns:
  - 8.8.8.8
  - 9.9.9.9

配置 DNS 搜索域,可以是一个值或列表

dns_search: example.com
------------------------
dns_search:
    - dc1.example.com
    - dc2.example.com

environment

和 arg 有几分类似,这个标签的作用是设置镜像变量,它可以保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置,这是与 arg 最大的不同。

一般 arg 标签的变量仅用在构建过程中。而 environment 和 Dockerfile 中的 ENV 指令一样会把变量一直保存在镜像、容器中,类似 docker run -e 的效果。

环境变量配置,可以用数组或字典两种方式

environment:
    RACK_ENV: development
    SHOW: 'ture'
-------------------------
environment:
    - RACK_ENV=development
    - SHOW=ture

extra_hosts

添加主机名的标签,就是往/etc/hosts文件中添加一些记录,与Docker client的–add-host类似:

extra_hosts:
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"

启动之后查看容器内部hosts:

162.242.195.82  somehost
50.31.209.229   otherhost

labels

向容器添加元数据,和Dockerfile的LABEL指令一个意思,格式如下:

labels:
  com.example.description: "Accounting webapp"
  com.example.department: "Finance"
  com.example.label-with-empty-value: ""
labels:
  - "com.example.description=Accounting webapp"
  - "com.example.department=Finance"
  - "com.example.label-with-empty-value"

将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况

还记得上面的depends_on吧,那个标签解决的是启动顺序问题,这个标签解决的是容器连接问题,与Docker client的–link一样效果,会连接到其它服务中的容器。

格式如下:

links:
 - db
 - db:database
 - redis

使用的别名将会自动在服务容器中的/etc/hosts里创建。例如:

172.12.2.186  db
172.12.2.186  database
172.12.2.187  redis

相应的环境变量也将被创建。

env_file

从文件中获取环境变量,可以指定一个文件路径或路径列表,其优先级低于 environment 指定的环境变量

env_file: .env
---------------
env_file:
    - ./common.env

expose

暴露端口,只将端口暴露给连接的服务,而不暴露给主机

expose:
    - "3000"
    - "8000"

networks

加入指定网络,格式如下:

services:
  some-service:
    networks:
     - some-network
     - other-network

关于这个标签还有一个特别的子标签aliases,这是一个用来设置服务别名的标签,例如:

services:
  some-service:
    networks:
      some-network:
        aliases:
         - alias1
         - alias3
      other-network:
        aliases:
         - alias2

相同的服务可以在不同的网络有不同的别名。

network_mode

设置网络模式,与Docker client的–net参数类似,只是相对多了一个service:[service name] 的格式。

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"

可以指定使用服务或者容器的网络。

ports

映射端口的标签。
使用HOST:CONTAINER格式或者只是指定容器的端口,宿主机会随机映射端口

# 暴露端口信息  - "宿主机端口:容器暴露端口"
ports:
 - "3000"
 - "8000:8000"
 - "49100:22"
 - "127.0.0.1:8001:8001"

注意:当使用HOST:CONTAINER格式来映射端口时,如果你使用的容器端口小于60你可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。

volumes

挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER] 这样的格式,或者使用 [HOST:CONTAINER:ro] 这样的格式,后者对于容器来说,数据卷是只读的,这样可以有效保护宿主机的文件系统。

Compose的数据卷指定路径可以是相对路径,使用 . 或者 .. 来指定相对目录。
数据卷的格式可以是下面多种形式:

volumes:
  // 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。
  - /var/lib/mysql
 
  // 使用绝对路径挂载数据卷
  - /opt/data:/var/lib/mysql
 
  // 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。
  - ./cache:/tmp/cache
 
  // 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。
  - ~/configs:/etc/configs/:ro
 
  // 已经存在的命名的数据卷。
  - datavolume:/var/lib/mysql

如果你不使用宿主机的路径,你可以指定一个volume_driver。

volume_driver: mydriver

logs

日志输出信息

--no-color          单色输出,不显示其他颜.
-f, --follow        跟踪日志输出,就是可以实时查看日志
-t, --timestamps    显示时间戳
--tail              从日志的结尾显示,--tail=200

其他操作

更新容器

当服务的配置发生更改时,可使用 docker-compose up 命令更新配置

此时,Compose 会删除旧容器并创建新容器,新容器会以不同的 IP 地址加入网络,名称保持不变,任何指向旧容起的连接都会被关闭,重新找到新容器并连接上去。

服务之间可以使用服务名称相互访问,links 允许定义一个别名,从而使用该别名访问其它服务

version: '2'
services:
    web:
        build: .
        links:
            - "db:database"
    db:
        image: postgres

这样 Web 服务就可以使用 db 或 database 作为 hostname 访问 db 服务了

多服务依赖问题解决

一个稍微复杂点的项目,肯定不是一个单独的程序可以解决的,必定是许多程序组合起来的一个整体,它们之间存在一定的依赖关系,比如:nginx依赖web应用服务器tomcat,tomcat中的应用依赖mysql数据库和zookeeper配置,tomcat中的应用可能互相依赖等。本篇文章为了解决多个程序/服务部署在docker容器中如何根据依赖关系按顺序启动的问题。

普通的部署方式(直接在服务器安装各个应用:tomcat、mysql、nginx、zookeeper等)

如果人工去启动多个应用,肯定是等被依赖的服务准备好之后,再去启动下一个应用/服务;但是想要服务器自动启动就要靠脚本来检测被依赖的服务是否准备好,然后按顺序启动之后的应用/服务。

docker 容器部署方式(将各个应用部署到单独的容器中)

docker容器通过服务编排(docker-compose)的方式启动时,可以通过==depends_on==来配置被依赖容器,待被依赖的容器启动之后,才启动当前的容器,但隐含的问题是:容器启动之后,容器中的应用不一定可以准备好,就比如电脑启动之后,自启动应用不一定启动了,这中间会有时间间隔。为解决这个问题,我们必需判断具体的应用是否启动好了。

wait-for-it.sh脚本

比较low,通过写wait-for-it.sh脚本

因为项目里面多个tomcat应用存在依赖关系,应用的启动顺序无法保证,所以写了一个wait-for-it.sh 脚本,通过curl tomcat的页面来判断tomcat就否启动好。

注:tomcat启动时,当所有应用启动完成后才会将开启HTTP端口

#!/bin/bash
# wait-for-it.sh
 
set -e
 
host="$1"
shift
cmd="$@"
curl -I "${host}" > a.txt
status = cat a.txt|head -1|awk '{print $2}'
until $status="200"; do
  >&2 echo "${host} is unavailable - sleeping"
  sleep 1
done
 
>&2 echo "${host} is up - executing command"
exec $cmd

在docker-compose.yaml各个服务的command中的配置如下:

command: ["/wait-it-for.sh", "host:8080", "/usr/bin/supervisord"]
该脚本的缺点
  1. 只支持http的方式来判断;
  2. 只支持依赖一个服务

通过dockerize 工具来判断

dockerize可以做以下几件事:

  1. 可以用来在容器启动时以模板和环境变量生成配置文件;
  2. 将任意的日志文件转到标准输出和标准错误中;
  3. 等待TCP、HTTP(S)服务可用之后启动主进程
  4. dockerize文档dockerize GIT地址

在解决服务依赖问题上使用dockerize:
在docker-compose.yaml各个服务的command中的配置如下:

command: ["dockerize", "-wait", "tcp://zookeeper:2181", "-wait", "tcp://mysql:3306", "-wait", "http://apphost:8080", "-timeout", "1800s", "/usr/bin/supervisord"]
选项说明
wait

-wait 参数可以添加多个,代表依赖多个服务

-wait <protocol://host:port>
timeout

-timeout 后面跟持续等待的秒数,默认是10秒

-timeout 1800s

最后一个命令是各依赖服务准备好之后要执行的命令,也就是当前应用启动的命令

支持的协议有:file, tcp, tcp4, tcp6, http, https and unix