docker-run vs docker-compose

通常我们启动一个容器的时候会使用 docker run 命令,例如启动一个 PostgreSQL 数据库容器:

docker run \
  --datach \
  --publish 5432:5432 \
  --name postgis \
  --restart unless-stopped \
  --volume $(pwd)/db/data:/var/lib/postgresql/data \
  beginor/postgis:9.3

在日常运维的时候为了保存这些命令行参数,通常会将这个 docker run 命令保存成 shell 脚本, 需要的时候运行 shell 脚本即可。 这对于只有单个镜像的简单应用, 基本上满足需要的,只要保存对应的 shell 文件, 备份好卷的内容,当容器出现问题或者需要迁移活着需要重新部署时执行 shell 文件就可以快速完成。

docker 容器编排问题

然而容器作为打包、部署、运行 Linux 进程技术,它更多的应用场景是在微服务架构中,从而来解决越来越庞大的单体应用碰到的研发、修复、运维的问题,详情可以参考 AWS 的《什么是微服务》。根据微服务架构的理念,我们在使用 docker 启动应用程序的时候,很多场景下我们不会只启动一个容器,可能会启动两个甚至更多的容器并且它们之间可能还存在先后依赖关系。举例来说,如果我想启动一个 Web 应用服务时,它不仅有 web app 本体程序,还有依赖的数据库服务,所以在启动这个 Web 应用程序的时候我首先需要启动数据库服务,然后再启动 web app 本体,所以如果使用 docker run 命令的话,具体参数如下所示(注意 --link 参数):

# 启动 PostreSQL 数据库
docker run \
  --datach \
  --publish 5432:5432 \
  --name postgis \
  --restart unless-stopped \
  --volume $(pwd)/db/data:/var/lib/postgresql/data \
  beginor/postgis:9.3

# 启动 Geo Server 服务应用
docker run \
  --detach \
  --publish 8080:8080 \
  --name geoserver \
  --restart unless-stopped \
  --volume $(pwd)/geoserver/data_dir:/geoserver/data_dir \
  --volume $(pwd)/geoserver/logs:/geoserver/logs \
  --hostname geoserver \
  --link postgis:postgis \
  beginor/geoserver:2.11.0

通过上面这个简单的 Web 应用启动例子我们可以发现,如果一个应用程序是由多个容器启动的,相互之间还存在诸如启动顺序、共享卷等问题的时候,在一个 shell 脚本中维护这些事情会变得越来越复杂,这个问题被称为 docker 容器的编排问题

docker compose

其实针对 docker 容器编排的问题还有一个办法那就是就是把几个依赖的应用全部都塞进一个容器中,这样就没有编排的问题了,但是这就与微服务的理念不符合即每个容器功能独立、无状态,塞进一个容器里面避免了编排的问题,但是又会碰到单体应用需要解决的问题了,所以这种办法显然不够好。

如果我们可以用一个配置文件来描述 docker 容器编排岂不是非常方便,就是 docker compose 诞生的由来。

docker compose使用 YAML 文件来配置说明应用服务,然后只需一个 docker compose 命令即可从配置中创建并启动所有服务容器,下面通过一个应用程序样例来说明:

  • 部署架构如图所示:
  • 该应用程序由以下部分组成:
    1. 2 个服务,由 Docker 映像提供支持: webapp 和 database
    2. 1 个密钥(HTTPS 证书),注入前端
    3. 1 个配置 (HTTP),注入到前端
    4. 1 个持久卷,附加到后端
    5. 2个网络(networks)
  • YAML 文件描述上述应用程序:
version: 1.0.0
services:
  frontend:
    image: example/webapp
    ports:
      - "443:8043"
    networks:
      - front-tier
      - back-tier
    configs:
      - httpd-config
    secrets:
      - server-certificate

  backend:
    image: example/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: {}
  • docker compose up -d -f app.yml 一个命令即可启动上述应用程序

除此之外,docker compose 会为这个应用创建一个独立的网络便于和其它应用进行隔离,能够自动创建网络、启动响应的容器实例,也可以根据配置文件删除停止和删除容器实例, 并删除对应的网络等,这对于搭建独立的测试环境非常有用。

References

  1. 什么是微服务?| AWS
  2. 微服务是什么?微服务架构的优缺点
  3. docker run | Docker Docs
  4. 使用 docker-compose 替代 docker run