前言
Docker Compose是一种用于定义和共享多容器应用程序的工具。本文主要介绍 Compose 项目的具体情况,以及如何进行安装和使用。
一、Compose 简介
Compose 项目是 Docker 官方的开源项目,负责实现对基于 Docker器的多应用服务的快速编排。从功能上看,跟 OpenStack 中的 Heat 十分类似。其代码目前在 https://github.com/docker/compose上开源。Compose 定位是“定义和运行多个 Docker 容器的应用”,其前身是开源项目 Fig, 目前仍然兼容 Fig 格式的模板文件。
通过前面的学习我们已经知道使用一个 Dockerfile 模板文件,可以让用户很方便地定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括前端的负载均衡容器等。
Compose 恰好满足了这样的需求。它允许用户通过一个单独的 docker-compose.yml模板文件 (YAML 格式)来定义一组相关联的应用容器为一个服务栈 (stack)。
Compose 中有两个重要的概念:
服务 (service) :一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
Compose 的默认管理对象是项⽬,通过子命令对项⽬中的多个服务进行便捷的生命周期管理。
Compose 项目由 Python 编写,实现上调用了 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持 Docker API, 就可以在其上利用 Compose 来进行编排管理。
二、Compose示例
以下示例通过一个具体的示例应用程序说明了 Compose 规范概念。
将应用程序拆分为前端 Web 应用程序和后端服务。
前端在运行时使用由基础设施管理的 HTTP 配置文件进行配置,提供外部域名和由平台的安全机密存储注入的 HTTPS 服务器证书。
后端将数据存储在持久卷中。
两个服务在隔离的后端网络上相互通信,而前端也连接到前端网络并公开端口 443 以供外部使用。
(External user) --> 443 [frontend network]
|
+--------------------+
| frontend service |...ro...<HTTP configuration>
| "webapp" |...ro...<server certificate> #secured
+--------------------+
|
[backend network]
|
+--------------------+
| backend service | r+w ___________________
| "database" |=======( persistent volume )
+--------------------+ \_________________/
示例应用程序由以下部分组成:
2 个服务,由 Docker 镜像支持:webapp和database
1 个密钥(HTTPS 证书),注入前端
1个配置(HTTP),注入前端
1 个持久卷,附加到后端
2 个网络
services:
frontend:
image: awesome/webapp
ports:
- "443:8043"
networks:
- front-tier
- back-tier
configs:
- httpd-config
secrets:
- server-certificate
backend:
image: awesome/database
volumes:
- db-data:/etc/data
networks:
- back-tier
volumes:
db-data:
driver: flocker
driver_opts:
size: "10GiB"
configs:
httpd-config:
external: true
secrets:
server-certificate:
external: true
networks:
# The presence of these objects is sufficient to define them
front-tier: {}
back-tier: {}
三、安装
Compose ⽀持 Linux、macOS、Windows 10 三⼤平台。Compose 可以通过 Python 的包管理⼯ 具 pip 进⾏安装,也可以直接下载编译好的⼆进制⽂件使⽤,甚⾄能够直接在 Docker 容器中运⾏。 前两种⽅式是传统⽅式,适合本地环境下安装使⽤;最后⼀种⽅式则不破坏系统环境,更适合云计算场景。
3.1、pip 安装
这种⽅式是将 Compose 当作⼀个 Python 应⽤来从 pip 源中安装。执⾏安装命令:
sudo pip install -U docker-compose
[root@iZhp33j6fklnmhbf0lz2obZ ~]# sudo pip3 install -U docker-compose
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Looking in indexes: http://mirrors.cloud.aliyuncs.com/pypi/simple/
Collecting docker-compose
Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/f3/3e/ca05e486d44e38eb495ca60b8ca526b192071717387346ed1031ecf78966/docker_compose-1.29.2-py2.py3-none-any.whl (114 kB)
|████████████████████████████████| 114 kB 51.5 MB/s
Collecting distro<2,>=1.5.0
Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/e1/54/d08d1ad53788515392bec14d2d6e8c410bffdc127780a9a4aa8e6854d502/distro-1.7.0-py3-none-any.whl (20 kB)
Collecting python-dotenv<1,>=0.13.0
...
3.2、二进制安装
在 Linux 上的也安装⼗分简单,从 官⽅ GitHub Release 处直接下载编译好的⼆进制⽂件即可。例 如,在 Linux 64 位系统上直接下载对应的⼆进制包。
sudo curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-` uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
3.3、版本查看
docker-compose -v
[root@iZhp33j6fklnmhbf0lz2obZ ~]# docker-compose -v
/usr/local/lib/python3.6/site-packages/paramiko/transport.py:33: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography and will be removed in a future release.
from cryptography.hazmat.backends import default_backend
docker-compose version 1.29.2, build unknown
可以添加 bash 补全命令:
curl -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/d ocker-compose > /etc/bash_completion.d/docker-compose
[root@iZhp33j6fklnmhbf0lz2obZ ~]# curl -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/d ocker-compose > /etc/bash_completion.d/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:34 --:--:-- 0
四、容器中执行
Compose 既然是一个 Python 应用,自然也可以直接用容器来执行它:
curl -L https://github.com/docker/compose/releases/download/1.29.2/run.sh > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
[root@iZhp33j6fklnmhbf0lz2obZ ~]# curl -L https://github.com/docker/compose/releases/download/1.29.2/run.sh > /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 2585 100 2585 0 0 1729 0 0:00:01 0:00:01 --:--:-- 3801
[root@iZhp33j6fklnmhbf0lz2obZ ~]# chmod +x /usr/local/bin/docker-compose
实际上,查看下载的 run. sh 脚本内容,如下:
set -e
VERSION="1.29.2"
IMAGE="docker/compose:$VERSION"
# Setup options for connecting to docker host
if [ -z "$DOCKER_HOST" ]; then
DOCKER_HOST='unix:///var/run/docker.sock'
fi
if [ -S "${DOCKER_HOST#unix://}" ]; then
DOCKER_ADDR="-v ${DOCKER_HOST#unix://}:${DOCKER_HOST#unix://} -e DOCKER_HOST"
else
DOCKER_ADDR="-e DOCKER_HOST -e DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH"
fi
# Setup volume mounts for compose config and context
if [ "$(pwd)" != '/' ]; then
VOLUMES="-v $(pwd):$(pwd)"
fi
if [ -n "$COMPOSE_FILE" ]; then
COMPOSE_OPTIONS="$COMPOSE_OPTIONS -e COMPOSE_FILE=$COMPOSE_FILE"
compose_dir="$(dirname "$COMPOSE_FILE")"
# canonicalize dir, do not use realpath or readlink -f
# since they are not available in some systems (e.g. macOS).
compose_dir="$(cd "$compose_dir" && pwd)"
fi
if [ -n "$COMPOSE_PROJECT_NAME" ]; then
COMPOSE_OPTIONS="-e COMPOSE_PROJECT_NAME $COMPOSE_OPTIONS"
fi
if [ -n "$compose_dir" ]; then
VOLUMES="$VOLUMES -v $compose_dir:$compose_dir"
fi
if [ -n "$HOME" ]; then
VOLUMES="$VOLUMES -v $HOME:$HOME -e HOME" # Pass in HOME to share docker.config and allow ~/-relative paths to work.
fi
i=$#
while [ $i -gt 0 ]; do
arg=$1
i=$((i - 1))
shift
case "$arg" in
-f|--file)
value=$1
i=$((i - 1))
shift
set -- "$@" "$arg" "$value"
file_dir=$(realpath "$(dirname "$value")")
VOLUMES="$VOLUMES -v $file_dir:$file_dir"
;;
*) set -- "$@" "$arg" ;;
esac
done
# Setup environment variables for compose config and context
ENV_OPTIONS=$(printenv | sed -E "/^PATH=.*/d; s/^/-e /g; s/=.*//g; s/\n/ /g")
# Only allocate tty if we detect one
if [ -t 0 ] && [ -t 1 ]; then
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -t"
fi
# Always set -i to support piped and terminal input in run/exec
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -i"
# Handle userns security
if docker info --format '{{json .SecurityOptions}}' 2>/dev/null | grep -q 'name=userns'; then
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS --userns=host"
fi
# shellcheck disable=SC2086
exec docker run --rm $DOCKER_RUN_OPTIONS $DOCKER_ADDR $COMPOSE_OPTIONS $ENV_OPTIONS $VOLUMES -w "$(pwd)" $IMAGE "$@"
可以看到,它其实是下载了 docker/compose 镜像并运行。
五、卸载
如果是二进制包方式安装的,删除二进制文件即可:
sudo rm /usr/local/bin/docker-compose
如果是通过 Python pip 工具安装的,则可以执行如下命令删除:
sudo pip uninstall docker-compose
六、使用
1.⽤ Python 编写记录访问web网站技术的app.py文件。
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
count = redis.incr('hits')
return 'Hello World! 该页面已被访问 {} 次。\n'.format(count)
if __name__ == "__main__":
# 设置运行信息
app.run(host="127.0.0.1", debug=True)
2.编写 Dockerfile ⽂件。
FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]
3.编写 docker-compose.yml ⽂件。
version: '3'
services:
web:
build: .
ports:
- "8000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
4.运⾏ compose 项⽬
docker-compose up
[root@iZhp33j6fklnmhbf0lz2obZ admin]# docker-compose up
Starting admin_web_1 ... done
Starting admin_redis_1 ... done
Attaching to admin_web_1, admin_redis_1
redis_1 | 1:C 17 Aug 2022 06:49:50.124 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1 | 1:C 17 Aug 2022 06:49:50.124 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1 | 1:C 17 Aug 2022 06:49:50.124 # 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 17 Aug 2022 06:49:50.125 * monotonic clock: POSIX clock_gettime
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Running mode=standalone, port=6379.
redis_1 | 1:M 17 Aug 2022 06:49:50.126 # 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 17 Aug 2022 06:49:50.126 # Server initialized
redis_1 | 1:M 17 Aug 2022 06:49:50.126 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1'
for this to take effect.redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Loading RDB produced by version 7.0.4
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * RDB age 1576 seconds
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * RDB memory usage when created 0.82 Mb
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Done loading RDB, keys loaded: 0, keys expired: 0.
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * DB loaded from disk: 0.000 seconds
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Ready to accept connections
5.访问
此时访问本地 8000 端⼝,每访问一次,计数加 1。
[root@iZhp33j6fklnmhbf0lz2obZ admin]# curl -v localhost:8000
* Rebuilt URL to: localhost:8000/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.61.1
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 44
< Server: Werkzeug/2.0.3 Python/3.6.15
< Date: Wed, 17 Aug 2022 06:56:07 GMT
<
Hello World! 该页面已被访问 8 次。
* Closing connection 0