简单来说, docker compose就是一键启动/关闭多个容器的工具, 它能够帮你解决容器之间依赖的问题, 哪个先启动, 依赖哪个容器等.
当开发的系统越来越复杂, 开发环境和部署都没那么简单的时候, 可以试试docker compose.
下面会把我实际经验中的一个例子简化出来,一步步教大家如何搭建docker compose,对踩过的坑进行总结,希望对docker能有更深的了解.
1. 搭建环境背景
以我目前在做的一个用python开发的web应用为例, 需要搭建一个依赖mysql, localstack, presto, celery, python flask等多项docker容器.
2. 配置文件
通过官网的介绍, 创建docker-compose.yml
, 配置上面提到的几个services.
version: '3'
services:
mysql:
# mysql 镜像
image: registry.docker-cn.com/library/mysql:latest
environment:
# 初始化mysql环境变量
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: "123456"
# 暴露端口号
ports:
- "3306:3306"
localstack:
image: atlassianlabs/localstack:latest
environment:
# localstack主要是为了模拟aws s3, 方便单元测试
AWS_ACCESS_KEY_ID: unit-test-user
AWS_SECRET_ACCESS_KEY: unit-test-user
AWS_DEFAULT_REGION: cn-north-1
AWS_DEFAULT_OUTPUT: text
SERVICES: s3
ports:
- "4572:4572"
presto:
# presto在我的另外一篇文章中有写如何制作镜像 //www.greatytc.com/p/bb5181008cd7
image: presto:v0.180
ports:
- "8888:8888"
web:
# 这个镜像可以根据代码中依赖的包进行build, 节省每次安装的时间
image: python-web-base:v0.1
command: bash /base/sbin/docker_compose_web_entrypoint.sh
# 暴露端口号, 成功启动之后可以通过 http://localhost:8081 进行访问
ports:
- "8081:8081"
# 环境变量的设置
environment:
S3_PORT: 4572
S3_HOST: localstack
AWS_ACCESS_KEY_ID: unit-test-user
AWS_SECRET_ACCESS_KEY: unit-test-user
AWS_DEFAULT_REGION: cn-north-1
AWS_DEFAULT_OUTPUT: text
# 在docker-compose中, 想要在web service中访问
# mysql, localstack or presto, 需要给一个hostname,
# hostname跟service name一致.
PRESTO_HOST: presto
MYSQL_HOST: mysql
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: "123456"
depends_on:
- mysql
- localstack
- presto
volumes:
# code base
- .:/base
# query result shared volume: /tmp/
- /tmp:/tmp
celery:
image: python-web-base:v0.1
command: bash /base/sbin/docker_compose_celery_entrypoint.sh
environment:
C_FORCE_ROOT: "true"
PRESTO_HOST: presto
MYSQL_HOST: mysql
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: "123456"
AWS_ACCESS_KEY_ID: unit-test-user
AWS_SECRET_ACCESS_KEY: unit-test-user
AWS_DEFAULT_REGION: cn-north-1
AWS_DEFAULT_OUTPUT: text
depends_on:
- mysql
- presto
- localstack
volumes:
# code base
- .:/base
# query result shared volume: /tmp/
- /tmp:/tmp
完整的代码我放到了github中 https://github.com/yamyamyuo/docker/tree/master/docker-compose
3. 遇到的坑
3.1 host name问题
配置mysql的时候由于不知道docker-compose中网络通信是怎么样的, 就用localhost:3306 或者127.0.0.1:3306去连mysql, 总是报错, 无法连接该mysql. 发现原来在docker-compose环境下, 不管是mysql还是其他servers如presto, 想要连接这些服务, 都要用这些服务的名字进行连接. 如下所示
version: '3'
services:
mysql:
...
presto:
...
localstack:
...
可以连接的服务的名称分别为mysql
, presto
和localstack
. 于是我在环境变量中export这些HOST name, 方便我在程序中去判断是否存在这些环境变量, 如果有的话就连接这个hostname.
3.2 服务启动的先后顺序
当mysql还没有成功启动, celery会一直报错, 并不断地重复连接mysql, 于是我想设置这些服务的启动先后顺序, 用了docker compose的 depens_on, 但是depens_on只是表达服务之间的依赖关系, 并不会按照次序来启动service.
depends_on
does not wait fordb
andredis
to be “ready” before startingweb
- only until they have been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it.
看来如果想让web
或者celery
服务等mysql
启动完毕后再启动, 需要一个wait-for-it
脚本的帮忙. 这个人写的脚本还比较清晰简单的: https://github.com/yamyamyuo/wait-for-it
可以在command
添加一个脚本, 脚本中使用wait-for-it
去轮询依赖的services, 一旦services启动成功, web 和 celery就可以继续进行启动了.
3.3 暴露端口号
服务对外暴露的端口号不要忘记填写, 否则其他docker container无法找到该服务. 注意事项:
当通过HOST:CONTAINER
格式来映射端口号的时候, 低于60的端口号会有错误提示, 因为YAML解析格式例如 xx:yy
的数字是基于base-60的. 因此强烈建议用双引号把 "HOST:CONTAINER" 括起来.
3.4 volumes
为了能够持久化和共享容器中的数据, Docker提出了volume的概念. Volume可以让容器中的联合文件系统, 以目录或文件的形式存于宿主机上.
我最初遇到的一个问题是celery
service 通过异步的方式执行一些任务, 任务结束后会把结果写到本地文件, web
service 查询的时候会到本地文件中查看是否有相关结果. 这个时候volume排上用场. 只要把容器内用到的文件地址映射到宿主机, 同时让web
和celery
service 都共享该volume即可.
以上就是本次docker compose遇到的一些问题, 如有问题可以留言~