Docker-Compose 详解

docker-compose

1. 介绍

一个项目一般会由多个应用服务组成。例如一个 Django 的 Web 项目,由 Django 后端服务,数据库,uwsgi 服务,nginx 反向代理服务等组成。如果使用容器技术当然不会将所有的服务都部署到一个容器中,一般是一个独立的服务一个容器。因此上面的项目需要一个 python 环境的容器用来运行 django 后端服务和 uwsgi 服务,一个数据库容器,一个 nginx 容器。单独部署时,需要一个个手动部署,构建镜像,添加网络,以及各容器之间的依赖和通信等问题,非常繁琐。

docker-compose 就是批量部署容器,并自动构建镜像,处理依赖和通信的工具。只需要在一个 YAML 文件中配置好应用程序的服务,然后通过 docker-compose 命令就可以创建并启动配置中的所有服务。

使用 docker-compose 有三个基本步骤:

  1. 在 Dockerfile 中定义好你的应用程序的环境,以便在任何地方它都可以复制。
  2. docker-compose.ymal 中定义组成应用程序的服务,以便它们可以在一个独立的环境中一起运行。
  3. 运行 docker-compose up, docker-compose 命令会启动并运行整个应用程序。

一个 docker-compose.Yml 看起来是这样的:

version: "3.9"  # optional since v1.27.0
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

2. 安装

Docker Compose 依赖于 Docker Engine 来完成任何有意义的工作,所以请确保在本地或远程安装 Docker Engine。

2.1 在 windows 和 mac 上安装

Docker Desktop for Mac 和 Windows 默认包括 Compose 命令。因此,Windows 和 Mac 用户不需要单独安装 Compose V2。

2.2 在 liunx 系统上安装

在 Linux 上,您可以从 GitHub 上的 Compose 存储库发布页面下载 Docker Compose 二进制文件,然后按照下面的步骤来安装。

注意:

对于 alpine,需要以下依赖包:

py-pip, python3-dev, libffi-dev, openssl-dev, gcc, libc-dev, rust, cargo and make.

  1. 运行此命令下载 Docker Compose 的当前稳定版本:

     sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    

    要安装不同版本的 Compose,请将 1.29.2 替换为您想要使用的 Compose 版本。

  2. 添加执行权限

     sudo chmod +x /usr/local/bin/docker-compose
    
  3. 测试安装是否成功

    $ docker-compose --version
    docker-compose version 1.25.0
    

上面这种手动安装容易出现问题,推荐使用下面的 pip 安装。

通过 pip 安装

Compose 可以使用 pip 从 pypi 安装。如果您使用 pip 安装,我们建议您使用 virtualenv,因为许多操作系统有与 docker-compose 依赖项冲突的 python 系统包。

 pip install docker-compose

3. 基本使用

接下来我们通过运行一个简单的 python Web 应用来学习 docker-compose 命令。

3.1 第一步: 编写 python 程序

  1. 创建一个项目文件夹

     mkdir composetest
     cd composetest
    
  2. 在项目目录下创建 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)
    

    在上面的程序中,Redis 的主机名为 'redis',并使用默认端口 6379

  3. 在你的项目目录中创建另一个名为 requirements.txt 的文件,并粘贴如下内容:

    flask
    redis
    

3.2 第二步:创建一个 Dockerfile

在这个步骤中,我们会构建一个镜像。该映像包含 Python 应用程序所需的所有依赖项,包括 Python 本身。

在项目根目录创建文件 Dockerfile,内容如下:

# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]

这告诉 Docker:

3.3 第三步:在 compose file 中定义服务

在项目根目录中创建文件 docker-compose.yml,并在其中编写如下内容:

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

这个 compose file 定义了两个服务 webredis

Web service

Web 服务会使用项目当前目录下的 dockerfile 构建的镜像。 然后它将容器的 5000 端口绑定到主机的 5000 端口上。

Redis service

Redis 服务使用从 Docker Hub 下载的公共 Redis 镜像。

3.4 第四步:使用 compose 命令构建和运行应用

  1. 从项目目录,通过运行 docker-compose up 启动应用程序。

    $ docker-compose up
    Creating composetest_web_1   ... done
    Creating composetest_redis_1 ... done
    Attaching to composetest_redis_1, composetest_web_1
    redis_1  | 1:C 14 Oct 2021 11:34:42.291 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
    redis_1  | 1:C 14 Oct 2021 11:34:42.291 # Redis version=6.2.1, bits=64, commit=00000000, modified=0, pid=1, just started
    redis_1  | 1:C 14 Oct 2021 11:34:42.291 # 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 14 Oct 2021 11:34:42.291 * monotonic clock: POSIX clock_gettime
    redis_1  | 1:M 14 Oct 2021 11:34:42.292 * Running mode=standalone, port=6379.
    redis_1  | 1:M 14 Oct 2021 11:34:42.292 # Server initialized
    redis_1  | 1:M 14 Oct 2021 11:34:42.294 * Ready to accept connections
    web_1    |  * Serving Flask app 'app.py' (lazy loading)
    web_1    |  * Environment: production
    web_1    |    WARNING: This is a development server. Do not use it in a production deployment.
    web_1    |    Use a production WSGI server instead.
    web_1    |  * Debug mode: off
    web_1    |  * Running on all addresses.
    web_1    |    WARNING: This is a development server. Do not use it in a production deployment.
    web_1    |  * Running on http://192.168.80.3:5000/ (Press CTRL+C to quit)
    

    compose 下载了一个 Redis 镜像,为代码构建了一个镜像,并启动定义好的服务。在本例中,代码在构建时静态地复制到镜像中。

  2. 访问服务程序

    在浏览器中输入 http://localhost:5000/ 或者 http://127.0.0.1:5000/ 访问我们运行的 Web 服务程序。

    image.png

  3. 刷新页面

    数字应该增加。

    image.png

  4. 切换到另一个终端并输入 docker image ls 查询所有本地镜像

    image.png

  5. 停止应用

    直接在当前运行终端 Ctrl + C,或者是再开启一个终端在项目目录下运行 docker-compose down 关闭运行的应用。

3.5 第五步:添加挂载点

编辑 docker-compose.yml,给 Web 服务添加挂载点。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_ENV: development
  redis:
    image: "redis:alpine"

volumes 键将主机上的项目目录(当前目录)挂载到容器内的/code 中,并允许动态地修改代码,而不必重新构建映像。

environment 键设置 FLASK_ENV 环境变量,该变量告诉 flask 以开发模式运行并在代码发送更改时重新加载。这种模式只能在开发中使用。

3.6 第六步:通过 compose 重新构建并运行应用

在项目目录下运行 docker-compose up 使用更新后的 compose 文件重新构建并运行应用。

image.png

3.7 第七步:更新应用

因为现在使用卷将应用程序代码挂载到容器中,所以您可以对其代码进行更改并立即查看更改,而无需重新构建镜像。

修改 app.py 中的返回问候语,例如:

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

在浏览器中刷新应用程序。欢迎语应该更新,计数器应该仍然在增加。

image.png

3.8 第八步:实验一下其他命令

如果你想在后台运行你的服务,你可以将-d 标志(用于“分离”模式)传递给 docker-compose up,并使用 docker-compose ps 来查看当前正在运行的服务:

image.png

docker-compose run 命令允许您为您的服务运行一次性命令。例如,要查看 Web 服务可用的环境变量:

docker-compose run web env
Creating composetest_web_run ... done
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=7929330f0f39
TERM=xterm
FLASK_ENV=development
LANG=C.UTF-8
GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
PYTHON_VERSION=3.7.10
PYTHON_PIP_VERSION=21.0.1
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/b60e2320d9e8d02348525bd74e871e466afdf77c/get-pip.py
PYTHON_GET_PIP_SHA256=c3b81e5d06371e135fb3156dc7d8fd6270735088428c4a9a5ec1f342e2024565
FLASK_APP=app.py
FLASK_RUN_HOST=0.0.0.0
HOME=/root

如果使用 docker-compose up -d 命令启动服务,停止服务可以使用命令

docker-compose stop

也可以使用 down 命令删除所有,完全删除容器。通过 ——volumes 来删除 Redis 容器使用的数据卷:

docker-compose down --volumes

这些就是 docker-compose 的基本使用方法。

4. Compose file

Compose file 是一个定义 servicesnetworksvolumes 的 YAML 文件。Compose file 的默认路径是 ./docker-compose.yml。YAML 文件的后缀 ymlyaml 都支持。

下面介绍 Compose file 中的常用指令。

version

指定 compose file 的版本。

networks

创建网络。

services

定义服务程序,每个服务对应会创建一个容器。

build

配置构建。

  1. 设置为一个字符串,指定构建时的上下文路径:

    version: "3.9"
    services:
      webapp:
        build: ./dir
    
  2. 设置为一个对象,在其中设置 context,Dockerfile,args

    version: "3.9"
    services:
      webapp:
        build:
          context: ./dir
          dockerfile: Dockerfile-alternate
          args:
            buildno: 1
    

当同时设置 buildimage 配置项时,Compose 会使用 image 中指定的 webapptag 来命名构建的图像:

build: ./dir
image: webapp:tag

command

覆盖容器默认的启动命令。

command: bundle exec thin -p 3000

也可以是一个列表

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

container_name

指定启动容器的名字

container_name: my-web-container

depends_on

表示服务之间的依赖关系。 服务依赖会导致以下行为:

version: "3.9"
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

在使用 depends_on 时需要注意:

entrypoint

覆盖容器默认的 entrypoint

entrypoint: /code/entrypoint.sh

也可以写成列表的形式

entrypoint: ["php", "-d", "memory_limit=-1", "vendor/bin/phpunit"]

注意设置了 entrypoint 后,不仅会覆盖服务镜像 Dockerfile 中的 ENTRYPOINT 指令还会忽略其中的 CMD 指令。

environment

设置容器运行时的环境变量。你可以使用数组,也可以使用字典。 任何布尔值(true, false, yes, no)都需要用引号括起来,以确保它们不会被 YML 解析器转换为 TrueFalse

environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:

expose

暴露端口,但不映射到宿主机,只被连接的服务访问。只能指定内部端口。

expose:
  - "3000"
  - "8000"

image

指定容器启动的镜像。可以是镜像名,也可以是镜像 ID 的部分。

image: redis
image: ubuntu:18.04
image: a4bc65fd

如果镜像不存在 compose 会 pull 它。如果还制定了 build 指令,将作为构建后的镜像的名字。

network_mode

设置网络模式。与 docker 客户端的 --network 参数的取值一样。

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

networks

配置容器连接的网络,引用顶级 networks 下的条目 。

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

ports

端口映射。

有三种格式:

ports:
  - "3000"
  - "3000-3005"
  - "8000:8000"
  - "9090-9091:8080-8081"
  - "49100:22"
  - "127.0.0.1:8001:8001"
  - "127.0.0.1:5000-5010:5000-5010"
  - "127.0.0.1::5000"
  - "6060:6060/udp"
  - "12400-12500:1240"

restart

设置重启策略。

no 是默认的重新启动策略,在任何情况下都不会重新启动容器。

当指定 always 时,容器总是重新启动。

如果退出码指示一个 on-failure 错误,on-failure 策略将重新启动容器。

设置为 unless-stop 则除非容器被停止(手动或其他方式),否则该容器总是会重新启动。

restart: "no"
restart: always
restart: on-failure
restart: unless-stopped

volumes

挂着一个主机路径或者绑定一个命名卷到服务。

可以将主机路径挂载为单个服务的定义的一部分,不需要在顶级 volumes 中定义它。

但是,如果您想跨多个服务重用卷,那么在顶级卷键中定义一个命名卷。

简单语法格式是:[SOURCE:]TARGET[:MODE]

volumes:
  # Just specify a path and let the Engine create a volume
  - /var/lib/mysql

  # Specify an absolute path mapping
  - /opt/data:/var/lib/mysql

  # Path on the host, relative to the Compose file
  - ./cache:/tmp/cache

  # User-relative path
  - ~/configs:/etc/configs/:ro

  # Named volume
  - datavolume:/var/lib/mysql

更多信息详见官方文档

5. compose v2

新的 compose V2 版本支持将 compose 命令作为 docker cli 的一部分,也即是可以去掉 docker-compose 命令中的 -,直接使用 docker compose 命令。

v2 版本的做了很多更新和优化,未来会取代 v1 版本。

1. 安装

1.1 在 windows 和 mac 上安装

Docker Desktop for Mac 和 Windows 3.2.1 及以上版本包括新的 Compose 命令和 Docker CLI。因此,Windows 和 Mac 用户不需要单独安装 Compose V2。

1.2 在 Linux 上安装

直接通过从项目发布页面下载适合您系统的二进制文件并将其复制为 $HOME/.docker/cli-plugins 目录下的 docker-compose 文件。

  1. 运行以下命令下载 Docker Compose 的当前稳定版本:
 mkdir -p ~/.docker/cli-plugins/
 curl -SL https://github.com/docker/compose/releases/download/v2.0.1/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose

这个命令将为当前活动用户安装 Compose V2 到 $HOME 目录下。为系统上的所有用户安装 Docker Compose,替换 ~/.docker/cli-plugins//usr/local/lib/docker/cli-plugins

  1. 添加可执行权限

     chmod +x ~/.docker/cli-plugins/docker-compose
    
  2. 测试是否安装成功

    $ docker compose version
    Docker Compose version v2.0.1
    
回帖
请输入回帖内容 ...