概述

Docker Compose 是一种工具,用于定义和运行多容器 Docker 应用程序。通过使用 Compose,你可以使用 YAML 文件来配置你的应用服务。然后,只需一个简单的命令,就能创建并启动你配置中的所有服务。

Docker Compose 允许你使用 YAML 文件来定义多个容器的应用服务,包括网络、卷等其他资源。这种方法可以让你将整个应用的配置集中管理,极大地简化了容器管理过程。

  • 简化配置:使用 YAML 文件定义服务,使得配置过程更加简单明了。
  • 一键部署:多容器应用可以通过一个命令同时启动,无需分别操作每个容器。
  • 易于维护和扩展:服务的更新和扩展可以通过修改配置文件实现,易于管理。

安装

Docker Compose 的安装过程取决于你的操作系统。在大多数情况下,它可以作为 Docker Desktop 的一部分自动安装,或者可以单独安装。

在 Linux 上安装

curl -L "https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

基本命令

命令描述
docker-compose up构建、(重新)创建、启动和连接到服务的容器。使用 -d 参数以后台模式运行。
docker-compose down停止并移除容器、网络、卷和镜像。
docker-compose build构建或重新构建服务中定义的镜像。
docker-compose logs查看服务的日志输出。
docker-compose pull拉取服务依赖的镜像。
docker-compose push将服务镜像推送到 Docker Hub 或其他镜像仓库。
docker-compose restart重启服务。
docker-compose start启动已经存在的服务容器。
docker-compose stop停止运行中的容器,不移除它们。
docker-compose pause暂停服务中的容器。
docker-compose unpause恢复服务中已暂停的容器。
docker-compose rm删除所有(停止状态的)服务容器。
docker-compose run在一个服务上运行一次性命令。
docker-compose exec在服务的容器中执行命令。
docker-compose scale设置服务的容器数量。(注:在 3.x 版本中已被 docker-compose up --scale 代替)
docker-compose config验证并查看 Compose 文件的配置。
docker-compose top显示运行中的容器的进程。
docker-compose port打印绑定的公开端口。
docker-compose ps列出项目中目前的所有容器。
docker-compose version显示 Docker Compose 的版本信息。

如果你想操作特定的一个 Docker Compose 编排,你应该在该编排文件所在的目录下执行相应的 docker-compose 命令,并使用 -f 参数指定你的编排文件(如果不是使用默认的 docker-compose.yml 文件名)。

管理应用

让我们通过一个简单的示例来展示如何使用 Docker Compose 管理多容器应用。

示例应用

下面创建一个 docker-compose.yml 文件,定义一个简单的 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="0.0.0.0", debug=True)

Dockerfile

FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]

docker-compose.yml

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

启动应用

使用 docker-compose up 命令来启动应用,Docker Compose 会自动启动定义的所有服务。

docker-compose up -d

停止应用

当你完成工作后,可以使用 docker-compose down 命令来停止并清理应用服务。

docker-compose down

Compose 文件

Docker Compose 文件是 Docker Compose 的核心,它使用 YAML 文件格式定义了多容器 Docker 应用的所有服务、网络和卷。

文件结构

一个基本的 docker-compose.yml 文件包含三个主要部分:services(服务)、networks(网络)和 volumes(卷)。

下面是一个简单的示例,展示了这些组件如何被定义和关联:

version: '3'
services:
  web:
    image: nginx
    ports:
      - "80:80"
    depends_on:
      - db
    networks:
      - backend
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
 
networks:
  backend:
 
volumes:
  db-data:

服务(Services)

services 部分,你定义了应用中的各个服务,每个服务可以是一个容器,在上面的例子中,有两个服务:webdb

  • image: 指定服务使用的镜像。
  • ports: 映射端口到宿主机。
  • depends_on: 表示服务之间的依赖关系。
  • networks: 指定服务连接的网络。
  • environment: 设置环境变量。

网络(Networks)

networks 部分,你可以定义一个或多个网络,服务可以连接到这些网络。在上例中,定义了一个名为 backend 的网络,webdb 服务都连接到了这个网络,使得它们可以相互通信。

卷(Volumes)

volumes 部分,你定义了数据卷用于数据持久化。在上例中,db-data 卷被挂载到了 db 服务的容器中,用于存储数据库数据。

语法

graph LR
    A[Docker Compose] --> B[Services]
    A --> C[Volumes]
    A --> D[Networks]
    A --> E[Configs]
    A --> F[Secrets]

    B --> G[Service 1]
    B --> H[Service 2]
    B --> I[Service N]
    
    G --> J[Image/Build]
    G --> K[Environment]
    G --> L[Ports]
    G --> M[Depends On]
    G --> N[Volumes]
    G --> O[Networks]

    C --> P[Volume 1]
    C --> Q[Volume 2]
    C --> R[Volume N]

    D --> S[Network 1]
    D --> T[Network 2]
    D --> U[Network N]

    E --> V[Config 1]
    E --> W[Config 2]

    F --> X[Secret 1]
    F --> Y[Secret 2]

    J --> J1[Type: Image or Build Path]
    K --> K1[Variables]
    L --> L1[Container:Host]
    M --> M1[Service Dependencies]
    N --> N1[Service:Volume Mapping]
    O --> O1[Service Networks]

    P --> P1[Driver]
    Q --> Q1[Driver Options]

    S --> S1[Driver]
    T --> T1[Driver Options]

    V --> V1[File or External]
    W --> W1[File or External]

    X --> X1[File or External]
    Y --> Y1[File or External]

关键字

配置项描述
build定义了构建服务的配置,可以是一个构建上下文的路径,或者一个包含 contextdockerfileargs 的对象。
cap_add, cap_drop添加或删除容器的能力。
cgroup_parent指定容器的父 cgroup。
command覆盖容器的默认命令。
configs为服务提供对配置的访问。
container_name指定自定义容器名称。
credential_spec配置管理服务帐户的凭据规范。
depends_on表达服务之间的依赖关系。
deploy指定与服务部署和运行相关的配置。
devices设备映射列表。
dns, dns_search自定义 DNS 服务器和搜索域。
entrypoint覆盖容器的默认入口点。
env_file从文件中加载环境变量。
environment设置环境变量。
expose暴露端口而不发布到宿主机。
external_links链接到 Docker Compose 外部的容器。
extra_hosts添加主机名映射。
healthcheck配置容器的健康检查。
image指定服务使用的镜像。
init使用 Docker 的 init 进程。
labels添加标签到容器。
links链接到其他服务的容器。
logging配置日志记录。
network_mode网络模式。
networks配置网络。
pidPID 模式。
ports发布端口。
secrets配置访问秘密。
security_opt安全选项。
stop_grace_period设置停止前的等待时间。
stop_signal设置停止容器的信号。
sysctls内核参数设置。
tmpfs挂载临时文件系统。
ulimits用户限制。
user指定运行用户。
userns_mode用户命名空间模式。
volumes, volume_driver配置卷。
volumes_from从其他服务或容器挂载卷。
working_dir工作目录。

服务配置详解

构建选项

如果你不是使用现有的镜像,而是需要构建自定义镜像,可以使用 build 选项:

version: '3'
services:
  webapp:
    build: ./dir

环境变量

你可以直接在 docker-compose.yml 文件中为服务设置环境变量,或者使用 .env 文件来管理:

version: '3'
services:
  web:
    image: nginx
    environment:
      - NGINX_PORT=80

依赖关系

使用 depends_on 选项可以定义服务启动的先后顺序:

version: '3'
services:
  web:
    image: nginx
    depends_on:
      - db
  db:
    image: postgres

端口映射

通过 ports 选项,可以将容器内的端口映射到宿主机的端口:

version: '3'
services:
  web:
    image: nginx
    ports:
      - "80:80"

环境变量与 .env 文件的使用

管理配置和敏感信息时,推荐使用 .env 文件来外部定义环境变量,然后在 docker-compose.yml 文件中引用这些变量:

.env 文件:

DB_PASSWORD=mysecretpassword

docker-compose.yml 文件:

version: '3'
services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}

通过这种方式,你可以避免将敏感信息直接硬编码在 docker-compose.yml 文件中,而是将其存储在外部的 .env 文件中,这有助于保持你的配置的安全性和灵活性。

多容器应用管理

在 Docker Compose 的使用中,管理多容器应用是核心任务之一。这包括了如何定义和运行多容器应用、如何管理容器间的网络连接以及如何实现数据的持久化和共享。

定义与运行多容器应用

利用 docker-compose.yml 文件,开发者可以定义涵盖多个服务(容器)的完整应用架构,实现一键部署和管理。

考虑到一个典型的三层应用架构,包含前端、后端及数据库层:

version: '3.8'
services:
  frontend:
    image: nginx:latest
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - app-network
 
  backend:
    image: node:14
    environment:
      DB_HOST: db
    ports:
      - "3000:3000"
    depends_on:
      - db
    networks:
      - app-network
 
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network
 
volumes:
  db-data:
 
networks:
  app-network:

在这个例子中,我们指定了使用的镜像、环境变量、端口映射、依赖关系、网络和数据卷。通过这样的配置,可以确保应用的各个部分能够正确连接和交互,同时数据也得到了持久化。

网络管理

Docker Compose 默认创建一个网络,使得同一 docker-compose.yml 文件中定义的所有服务都能够在这个网络中相互通信。然而,复杂应用可能需要更精细的网络配置来满足不同的安全和隔离需求。

自定义网络配置允许服务根据实际需求分配到不同的网络中,实现更细致的网络隔离和通信策略。

networks:
  app-network:
    driver: bridge
  internal-network:
    driver: bridge
    internal: true

在上述配置中,app-network 用于暴露外部可访问的服务(如前端),而 internal-network 则用于内部服务间的通信,不对外部暴露,增强了安全性。

数据卷与持久化

数据持久化对于任何生产级应用都至关重要,Docker Compose 通过卷(volumes)提供了数据持久化的能力。

volumes:
  db-data:
    driver: local

这里定义了一个名为 db-data 的卷,用于 PostgreSQL 数据库的数据持久化存储。通过指定卷,即使容器重新创建,数据也不会丢失。

Docker Compose 也支持定义卷来实现服务之间的数据共享:

services:
  service1:
    volumes:
      - shared-data:/path/to/data
  service2:
    volumes:
      - shared-data:/path/to/data
 
volumes:
  shared-data:

在此配置中,service1service2 共享了同一个卷 shared-data,允许它们访问和修改相同的数据集,这在需要数据共享的应用场景中非常有用。

高级功能

Docker Compose 不仅仅是一个多容器部署工具,它还提供了一系列高级功能和最佳实践,帮助开发者和运维人员优化应用配置和管理。本章节将深入探讨服务的健康检查、如何使用 extends 特性以及如何通过覆盖文件分离环境配置。

服务的健康检查

健康检查是监控服务状态和健康状况的重要手段。通过配置健康检查,Docker 可以自动检测服务是否正常运行。

docker-compose.yml 文件中,可以为服务配置 healthcheck 指令:

services:
  web:
    image: my-web-app
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

这个配置定义了一个健康检查,每 30 秒执行一次 curl -f http://localhost 命令来检查 web 服务的健康状态,如果命令在 10 秒内没有成功执行(即返回状态码非 0),则认为是一次失败。如果连续 3 次检查失败,则服务被认为是不健康的。

扩展与重写服务

extends 特性允许在一个服务中重用另一个服务的配置。这对于不同环境下的配置共享非常有用。

假设有一个基础服务配置 base-service.yml

version: '3.8'
services:
  app_base:
    image: my-app
    environment:
      - DEBUG=false

可以在 docker-compose.yml 中扩展这个服务:

version: '3.8'
services:
  app:
    extends:
      file: base-service.yml
      service: app_base
    ports:
      - "80:80"

通过这种方式,app 服务继承了 app_base 的所有配置,并添加了端口映射。

使用覆盖文件来分离环境配置

Docker Compose 允许使用多个文件来定义项目配置,这使得可以针对不同环境(如开发、测试、生产)使用不同的配置。

基础 docker-compose.yml 文件定义了所有环境共有的配置:

version: '3.8'
services:
  web:
    image: my-web-app
    environment:
      - LOG_LEVEL=info

针对开发环境的 docker-compose.override.yml

version: '3.8'
services:
  web:
    environment:
      - DEBUG=true
    volumes:
      - .:/code

在生产环境的 docker-compose.prod.yml

version: '3.8'
services:
  web:
    ports:
      - "80:80"
    environment:
      - LOG_LEVEL=warning

通过指定 -f 参数来使用不同的配置文件:

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

这种方法使得基础配置与环境特定配置分离,便于管理和维护。

实战项目

本实战项目将引导你构建一个简单的 Web 应用,该应用包含三个主要组件:前端(使用 Nginx 静态文件服务),后端(一个简单的 Node.js API),以及数据库(PostgreSQL)。此外,我们还将探讨如何调试这些服务并进行基本的性能优化。

Web 实战项目

项目概述

  • 前端:使用 Nginx 服务静态文件。
  • 后端:Node.js 应用提供 RESTful API。
  • 数据库:PostgreSQL 存储数据。

docker-compose.yml

version: '3.8'
services:
  frontend:
    image: nginx:alpine
    volumes:
      - ./frontend:/usr/share/nginx/html
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - app-network
 
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgres://user:password@db:5432/mydb
    ports:
      - "3000:3000"
    depends_on:
      - db
    networks:
      - app-network
 
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network
 
volumes:
  db-data:
 
networks:
  app-network:

前端

前端使用 Nginx 静态文件服务。你需要在 ./frontend 目录下放置你的静态文件(HTML、CSS、JavaScript 文件等)。

后端

后端是一个简单的 Node.js 应用,提供 RESTful API。你需要在 ./backend 目录下创建一个 Dockerfile 和你的 Node.js 应用代码。

./backend/Dockerfile

FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Node.js 服务器

// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
 
app.get('/api', (req, res) => {
  res.json({ message: "Hello from the backend!" });
});
 
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

数据库

使用 PostgreSQL 作为数据库服务。docker-compose.yml 文件中已经配置了必要的环境变量。

调试与优化

容器日志查看

使用 Docker Compose 查看服务日志的命令:

docker-compose logs -f backend

这将跟踪并显示后端服务的实时日志输出。

资源监控与性能优化

Docker Desktop 和其他第三方工具如 Portainer 或 cAdvisor 可用于监控容器的资源使用情况。基于监控数据,你可以对服务进行必要的调整,比如调整容器的 CPU 和内存限制:

services:
  backend:
    build: ./backend
    mem_limit: 500m
    cpus: '0.5'

docker-compose.yml 文件中,为后端服务设置了内存限制为 500MB,CPU 使用限制为 50%。