Docker Compose
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文件
1 | 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 |
添加可执行权限
1 | sudo chmod +x /usr/local/bin/docker-compose |
测试安装结果
1 | docker-compose --version |
pip安装
安装python-pip
1 | yum -y install epel-release |
安装docker-compose
1 | pip install docker-compose |
测试安装结果
1 | docker-compose --version |
快速入门
打包项目
打包项目,获得 jar 包 docker-demo-0.0.1-SNAPSHOT.jar
创建Dockerfile
在 jar 包所在路径创建 Dockerfile 文件,添加以下内容
1 | FROM openjdk:8-jdk-alpine |
创建 docker-compose 配置文件
在 jar 包所在路径创建文件 docker-compose.yml,添加以下内容
1 | version: '2' # 表示该 Docker-Compose 文件使用的是 Version 2 file |
启动服务
在 docker-compose.yml 所在路径下执行该命令 Compose 就会自动构建镜像并使用镜像启动容器
1 | docker-compose up |
工程实战
基本使用
接下来我们使用 docker-compose 构建我们的微服务以及mysql,并逐步讲解其使用。
准备工作
在项目文件夹下创建 docker-compose.yml文件
1 | cd /usr/local/docker-learn/ && touch docker-compose.yml && ll |
编写配置文件
先在 docker-compose.yml 文件里添加如下代码,构建我们的项目
1 | version: '2' |
运行测试
在项目的文件中执行
docker-compose up -d
命令就可以启动了
1 | docker-compose up -d |
微服务访问测试
通过网关地址访问测试微服务
1 | curl http://192.168.64.153:8888/employeapi/find/10001| python -m json.tool |
扩缩容
nacos查看集群情况
我们可以查看nacos,查看当服务器集群的一个部署情况
扩容节点
我们现在对
learn-docker-storage
节点进行扩容语法格式:
docker-compose up -d --scale 服务名=节点数
1 | docker-compose up -d --scale learn-docker-storage=2 |
启动后查看nacos节点信息
缩容节点
和扩容一样指定节点数量就可以的
1 | docker-compose up -d --scale learn-docker-storage=1 |
停止后后查看nacos节点信息
工程、服务、容器
Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container)
Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例
常见命令
docker-compose常用命令可以通过以下命令大致看一下:
1 | docker-compose --help |
1 | Define and run multi-container applications with Docker. |
下面会针对一些比较常用的命令进行简单介绍
ps
列出所有运行容器
显示所有容器信息,默认显示name、command、state、ports。
1 | docker-compose ps |
logs
打印服务日志
1 | docker-compose logs |
语法
1 | logs [options] [SERVICE...] |
参数
1 | --no-color Produce monochrome output. |
port
打印绑定的公共端口,下面命令可以输出 eureka 服务 8761 端口所绑定的公共端口
1 | docker-compose port eureka 8761 |
build
当修改dockerfile或者docker-compose时,运行docker-compose build 重建镜像。 生成镜像后,可使用docker-compose up启动
1 | docker-compose build |
create
为服务创建容器.只是单纯的create,还需要使用start启动compose
语法
1 | create [options] [SERVICE...] |
参数
1 | --force-recreate 重新创建容器,即使他的配置和镜像没有改变,不兼容--no-recreate参数 |
start
启动指定服务已存在的容器
1 | docker-compose start eureka |
stop
停止已运行的服务的容器
1 | docker-compose stop eureka |
pause
暂停容器服务,
docker-compose pause
暂停所有服务。docker-compose pause web
,暂停web服务的容器。
1 | docker-compose pause eureka |
unpause
恢复容器服务,
docker-compose unpause
恢复所有服务,docker-compose unpause web
,恢复web服务的容器。
1 | docker-compose unpause eureka |
restart
重启docker-compose.yml中定义的所有的已停止的和正在运行的服务。
1 | docker-compose restart |
run
在一个服务上执行一个命令
1 | docker-compose run web bash |
语法
1 | run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] |
参数
1 | -d 后台运行,输出容器名. |
rm
删除已经停止的容器,如果服务在运行,需要先
docker-compose stop
停止容器,默认情况下,已挂载的volume中的数据不会被删除,可以使用docker volume ls
查看所有的volume情况。所有在容器中并未在volume中的数据将被删除。
1 | 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
。如果需要的话,这样将会启动已经停止的容器。
1 | docker-compose up |
语法
1 | up [options] [--scale SERVICE=NUM...] [SERVICE...] |
参数
1 | -d 后台运行,输出容器的名字. |
down
停止和删除容器、网络、卷、镜像,这些内容是通过
docker-compose up
命令创建的. 默认值删除容器网络,可以通过指定rmi volumes
参数删除镜像和卷。
1 | docker-compose down |
语法
1 | down [options] |
参数
1 | --rmi type 删除镜像,类型必须是: |
kill
通过发送 SIGKILL 信号来停止指定服务的容器
强制停止某个正在运行的服务
1 | docker-compose kill eureka |
pull
下载服务镜像
1 | docker-compose pull eureka |
scale
设置指定服务运气容器的个数,以 service=num 形式指定
1 | docker-compose scale user=3 movie=3 |
常用配置
image
image是指定服务的镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。
例如下面这些格式都是可以的
1 | image: redis |
buld
服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。Compose 将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器。
1 | build: /path/to/build/dir |
也可以是相对路径,只要上下文确定就可以读取到 Dockerfile。
1 | build: ./dir |
设定上下文根目录,然后以该目录为准指定 Dockerfile。
1 | build: |
注意 build 都是一个目录,如果你要指定 Dockerfile 文件需要在 build 标签的子级标签中使用 dockerfile 标签指定,如上面的例子。
如果你同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。
既然可以在 docker-compose.yml 中定义构建任务,那么一定少不了 arg 这个标签,就像 Dockerfile 中的 ARG 指令,它可以在构建过程中指定环境变量,但是在构建成功后取消,在 docker-compose.yml 文件中也支持这样的写法:
1 | build: |
下面这种写法也是支持的,一般来说下面的写法更适合阅读。
1 | build: |
与 ENV 不同的是,ARG 是允许空值的。例如:
1 | args: |
这样构建过程可以向它们赋值。
注意:YAML 的布尔值(true, false, yes, no, on, off)必须要使用引号引起来(单引号、双引号均可),否则会当成字符串解析。
command
使用 command 可以覆盖容器启动后默认执行的命令。
1 | command: bundle exec thin -p 3000 |
也可以写成类似 Dockerfile 中的格式:
1 | command: [bundle, exec, thin, -p, 3000] |
container_name
Compose 的容器名称格式是:<项目名称><服务名称><序号>
虽然可以自定义项目名称、服务名称,但是如果你想完全控制容器的命名,可以使用这个标签指定:
1 | container_name: app |
这样容器的名字就指定为 app 了。
depends_on
在使用 Compose 时,最大的好处就是少打启动命令,但是一般项目容器启动的顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。
例如在没启动数据库容器的时候启动了应用容器,这时候应用容器会因为找不到数据库而退出,为了避免这种情况我们需要加入一个标签,就是 depends_on,这个标签解决了容器的依赖、启动先后的问题。
例如下面容器会先启动 redis 和 db 两个服务,最后才启动 web 服务:
1 | version: '2' |
注意的是,默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系。
dns
和 –dns 参数一样用途,格式如下:
1 | dns: 8.8.8.8 |
也可以是一个列表:
1 | dns: |
dns_search
配置 DNS 搜索域,可以是一个值或列表
1 | dns_search: example.com |
environment
和 arg 有几分类似,这个标签的作用是设置镜像变量,它可以保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置,这是与 arg 最大的不同。
一般 arg 标签的变量仅用在构建过程中。而 environment 和 Dockerfile 中的 ENV 指令一样会把变量一直保存在镜像、容器中,类似 docker run -e 的效果。
环境变量配置,可以用数组或字典两种方式
1 | environment: |
extra_hosts
添加主机名的标签,就是往/etc/hosts文件中添加一些记录,与Docker client的–add-host类似:
1 | extra_hosts: |
启动之后查看容器内部hosts:
1 | 162.242.195.82 somehost |
labels
向容器添加元数据,和Dockerfile的LABEL指令一个意思,格式如下:
1 | labels: |
links
将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况
还记得上面的depends_on吧,那个标签解决的是启动顺序问题,这个标签解决的是容器连接问题,与Docker client的–link一样效果,会连接到其它服务中的容器。
格式如下:
1 | links: |
使用的别名将会自动在服务容器中的/etc/hosts里创建。例如:
1 | 172.12.2.186 db |
相应的环境变量也将被创建。
env_file
从文件中获取环境变量,可以指定一个文件路径或路径列表,其优先级低于 environment 指定的环境变量
1 | env_file: .env |
expose
暴露端口,只将端口暴露给连接的服务,而不暴露给主机
1 | expose: |
networks
加入指定网络,格式如下:
1 | services: |
关于这个标签还有一个特别的子标签aliases,这是一个用来设置服务别名的标签,例如:
1 | services: |
相同的服务可以在不同的网络有不同的别名。
network_mode
设置网络模式,与Docker client的–net参数类似,只是相对多了一个service:[service name] 的格式。
1 | network_mode: "bridge" |
可以指定使用服务或者容器的网络。
ports
映射端口的标签。
使用HOST:CONTAINER格式或者只是指定容器的端口,宿主机会随机映射端口
1 | # 暴露端口信息 - "宿主机端口:容器暴露端口" |
注意:当使用HOST:CONTAINER格式来映射端口时,如果你使用的容器端口小于60你可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。
volumes
挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER] 这样的格式,或者使用 [HOST:CONTAINER:ro] 这样的格式,后者对于容器来说,数据卷是只读的,这样可以有效保护宿主机的文件系统。
Compose的数据卷指定路径可以是相对路径,使用 . 或者 .. 来指定相对目录。
数据卷的格式可以是下面多种形式:
1 | volumes: |
如果你不使用宿主机的路径,你可以指定一个volume_driver。
1 | volume_driver: mydriver |
logs
日志输出信息
1 | --no-color 单色输出,不显示其他颜. |
其他操作
更新容器
当服务的配置发生更改时,可使用 docker-compose up 命令更新配置
此时,Compose 会删除旧容器并创建新容器,新容器会以不同的 IP 地址加入网络,名称保持不变,任何指向旧容起的连接都会被关闭,重新找到新容器并连接上去。
links
服务之间可以使用服务名称相互访问,links 允许定义一个别名,从而使用该别名访问其它服务
1 | version: '2' |
这样 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端口
1 | !/bin/bash |
在docker-compose.yaml各个服务的command中的配置如下:
1 | command: ["/wait-it-for.sh", "host:8080", "/usr/bin/supervisord"] |
该脚本的缺点
- 只支持http的方式来判断;
- 只支持依赖一个服务
通过dockerize 工具来判断
dockerize可以做以下几件事:
- 可以用来在容器启动时以模板和环境变量生成配置文件;
- 将任意的日志文件转到标准输出和标准错误中;
- 等待TCP、HTTP(S)服务可用之后启动主进程
- dockerize文档 、dockerize GIT地址
在解决服务依赖问题上使用dockerize:
在docker-compose.yaml各个服务的command中的配置如下:
1 | command: ["dockerize", "-wait", "tcp://zookeeper:2181", "-wait", "tcp://mysql:3306", "-wait", "http://apphost:8080", "-timeout", "1800s", "/usr/bin/supervisord"] |
选项说明
wait
-wait 参数可以添加多个,代表依赖多个服务
1 | -wait <protocol://host:port> |
timeout
-timeout 后面跟持续等待的秒数,默认是10秒
1 | -timeout 1800s |
最后一个命令是各依赖服务准备好之后要执行的命令,也就是当前应用启动的命令
支持的协议有:file, tcp, tcp4, tcp6, http, https and unix