Skip to content

Docker Compose 的编写

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的强大工具。在现代软件开发中,应用程序往往由多个互相关联的服务组成,例如数据库、Web 服务器和后端服务等。Docker Compose 通过一个简单的 YAML 文件来描述这些服务,并提供了一键式启动、停止和管理整个应用的能力,使得开发、测试和部署变得更加简便和高效。

让我们开始这段 Docker Compose 的探索之旅,了解如何利用这一工具简化多容器应用的管理,提升效率和协作能力。🚀

常用内容

yaml
name: syncthing # 编排的名称

services: # 要使用的镜像。固定格式。
  Syncthing: # 服务的名称。可以随便填。
    image: lscr.io/linuxserver/syncthing # 镜像。指定了作者与软件
    container_name: syncthing # 容器的名称
    network_mode: host # 网络模式。
    
    environment: # 环境变量。一种设定配置的方式。
      - PUID=3000
      - PGID=950
      - TZ=Asia/Shanghai
    
    volumes: # 卷的映射。将宿主机的文件或目录映射到容器中。
      # 格式:外部卷:容器内路径
      - /app/syncthing/config:/config
      - mynfs:/mnt/nfs

    ports: # 端口映射
      # 格式 宿主机端口:容器端口/协议
      - 8384:8384

    restart: always # 重启策略

volumes: # 卷的另一种定义方式。可以实现更多功能。
  mynfs:
    external: true

多容器堆栈

示例

yaml
name: brother
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"

  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

软件

yaml
services:
  my-service:
    image: lscr.io/linuxserver/syncthing:latest # 镜像

image 用于确定要使用的软件和来源。
格式:仓库/作者/软件:版本。其中,软件是必要的,其它都是可选项目。要指定镜像仓库,则需要指定作者。

yaml
image: redis # 镜像名
image: ubuntu:14.04 # 镜像名:版本
image: estrellaxd/auto_bangumi # 作者/镜像名
image: my-registry.com:4000/postgresql # 仓库/作者/镜像名
image: a4bc65fd # 镜像id

支持的格式。

环境变量

它是一种存储配置的方式。

yaml
services:
  my-service:
    environment: # 环境变量
      - PUID=3000 # 用户ID
      - PGID=950 # 组ID
      - TZ=Asia/Shanghai # 时区

另一种写法

yaml
services:
  my-service:
    environment:
      PUID: 3000 # 用户ID
      PGID: 950 # 组ID
      TZ: Asia/Shanghai # 时区

存储

Docker 的卷(Volume)是用于在容器之间共享和持久化数据的一种机制。它们提供了一种独立于容器生命周期的数据管理方式,使得数据在容器停止或删除后仍然能够保留。

背景

默认情况下,容器的数据不会被保存。这使得,在这些情况下,数据丢失:容器停止、重启系统、软件升级

为什么使用卷?

  • 持久化数据:卷允许你持久化数据,即使容器被删除,数据也不会丢失。
  • 共享数据:不同的容器可以共享同一个卷,方便实现数据共享和协作。
  • 数据隔离:卷可以隔离数据,使得不同的应用和环境可以使用独立的数据存储。
yaml
services:
  my-service:
    volumes: # 卷的映射。将宿主机的文件或目录映射到容器中。
      - /path/to/host:/config # 宿主机路径:容器内路径
      - mynfs:/mnt/nfs # 卷的名称:容器内路径

      - web_data:/usr/share/nginx/html
      - db_data:/var/lib/mysql

      - /path2:/config:ro # 只允许容器读取数据,不能写入
      - /path3:/config:rw # 允许容器读取、写入数据


volumes: # 卷的另一种定义方式。可以实现更多功能。
  web_data: # 简单定义

  # 更多可选项
  db_data:
    name: custom_db_data # 卷的名称

  volumes_3:
    driver: local
    driver_opts:
      o: bind
      device: /path/on/host

  # 网络卷
  mynfs:
    driver_opts:
      type: "nfs"
      o: "addr=192.168.5.25,vers=4,nolock,soft,rw"
      device: ":/mnt/pool_1/Main/"

  my_smb:
    driver_opts:
      type: "cifs"
      o: "addr=192.168.5.25,username=用户,password=密码,vers=3.0"
      device: "//192.168.5.25/pool_1"

  wai_bu: # 外部卷
    external: true

端口映射

功能:映射容器内的端口,到宿主机的端口
在网络模式模式为bridge时,外部不能主动访问容器内。这个功能可以解决此问题。通过此功能,还能使用自定义的端口,而不是软件默认监听的的端口。

yaml
services:
  my-service:
    network_mode: bridge # 网络模式
    ports: # 端口映射
      # 格式 宿主机端口:容器端口/协议
      - 8384:8384
      - 22000:22000/tcp
      - 22000:22000/udp

网络模式

yaml
services:
  my-service:
    network_mode: host # 网络模式

区别

  • bridge : Docker 的默认网络模式,每个容器在启动时都会连接到一个桥接网络。容器之间可以通过容器名相互通信,但默认情况下,外部不能直接访问容器。它的本质是NAT,就像小区内的快递柜。
  • host: 容器与宿主机共享网络栈。网络方面,效果和直接在宿主机运行相同。适用于需要高性能网络的场景,但会减少网络隔离。
  • none : 容器不配置任何网络。容器内没有网络接口,只适用于不需要网络通信的场景。

网络相关

yaml
services:
  my-service:
    hostname: my-service # 主机名。没什么用。

重启策略

yaml
services:
  my-service:
    restart: always
  • no:默认值,表示不会自动重启容器。如果容器停止或退出,它将保持停止状态。
  • always:无论容器如何停止,都会自动重启。这意味着即使容器崩溃或手动停止,Compose 也会尝试重新启动它。
  • on-failure:仅在容器以非零退出码退出时重启。你可以指定最大重启次数,例如 on-failure:5 表示容器最多重启五次。
  • unless-stopped:除非容器被手动停止,否则会自动重启。这意味着如果容器崩溃,Compose 会尝试重新启动它,但如果你手动停止了容器,它将保持停止状态。

参数与入口点

yaml
services:
  Watchtower:
    command: --schedule "0 5 * * *"    aria2 AriaNg yacd-meta Peer-Ban-Helper Home_Assistant AutoBangumi DDNS-Go

command 的功能是传递参数

yaml
services:
  web:
    entrypoint: ["sh", "-c", 'apk add openssh && exec /app/ddns-go && "$@"']

entrypoint 指容器开启时,启动软件的命令。它被指定时,效果是覆盖原有的entrypoint,而不是追加。

yaml
services:
  jellyfin:
    image: jellyfin/jellyfin
    entrypoint: ["sh", "-c", 'apt update && apt install -y fonts-noto-cjk-extra && exec /jellyfin/jellyfin && "$@"']

覆盖entrypoint,可以在启动软件前,安装其他的依赖,或做一些定制。
在这个示例中,先安装代号为fonts-noto-cjk-extra的字体,再启动软件。解决了问题:Jellyfin没有中文字体,导致中文显示为方框

交互式终端

yaml
services:
    web:
      stdin_open: true # 开启标准输入
      tty: true # 分配为伪终端
shell
docker exec -it 容器名称 sh # 连接容器的 shell

这两个选项通常一起使用,以确保容器能够提供一个交互式的终端环境,适合调试或运行需要用户输入的应用程序。通常使用场景:需要在容器启动后,立刻在其中执行命令。

构建

简洁语法

yaml
services:
  webapp:
    build: . # 构建上下文

详细语法(对象模式)

yaml
services:
  app:
    build:
      context: ./src
      dockerfile: Dockerfile-dev

      args:
        buildno: 1
        gitcommithash: cdc3b19
        # 或者使用数组写法:
        # - buildno=1
        # - gitcommithash=cdc3b19

高级语法(依赖 BuildKit 的现代特性)

yaml
services:
  app:
    build:
      network: host
      tags:
        - "myregistry.com/webapp:1.0.0"
        - "myregistry.com/webapp:latest"
      ssh:
        - default
        - my_key=~/.ssh/id_rsa
      platforms:
        - "linux/amd64"
        - "linux/arm64"
      cache_from:
        - myregistry/myimage:cache
      cache_to:
        - type=registry,ref=myregistry/myimage:cache,mode=max
      secrets:
        - npm_token
        - ssh_key

secrets:
  npm_token:
    file: ./secrets/npm_token.txt
  my_key:
    file: ~/.ssh/id_rsa
  • context: 构建上下文。构建镜像时的根目录,包含构建所需的所有文件。可以是相对路径、绝对路径或 Git 仓库的 URL。
  • dockerfile:Dockerfile 的位置
  • tags: 额外标签。除了默认的标签外,为镜像指定额外的 Tag 列表。
  • args: 构建参数。向 Dockerfile 传递参数。格式可以是字典或数组。

    💡 提示:如果只写参数名(如 buildno:- buildno)而不赋值,Docker Compose 会自动去宿主机的环境变量中寻找同名变量并传入。

  • target: 多阶段构建目标。用于多阶段构建。可以指定只构建到哪一个阶段(stage)就停止。这在区分开发环境和生产环境时极为常用。
  • platforms: 跨平台多架构构建。构建支持不同 CPU 架构的镜像(如 ARM 和 x86)。
  • cache_from,cache_to: 构建缓存。用于指定拉取哪里的缓存,以及将缓存推送到哪里,极大提升 CI/CD 中的构建速度。
  • network: 网络模式。执行 RUN 指令时的网络连接类型。通常设为 host/none
  • secrets: 机密。敏感数据(如密码、私钥、Token)被安全地传递给构建过程,而不会保留在最终的镜像层中
  • ssh: SSH 代理。构建过程借用宿主机的 SSH 私钥(比如拉取私有的仓库,避免限流)

platforms

选项

常用选项

  • linux/amd64: 基于 64 位 Intel/AMD 处理器的 Linux 环境。
  • linux/arm64 (linux/arm64/v8):基于 64 位 ARM 处理器的 Linux 环境。
  • linux/arm/v7:基于 32 位 ARM 处理器的 Linux 环境(ARMv7 架构)。

其他选项

  • linux/386:32 位的 x86 架构(非常古老的 PC)。
  • linux/arm/v6:更老旧的 ARM 设备(如第一代树莓派 Raspberry Pi 1 或 Zero)。
  • linux/ppc64le:IBM Power 架构的小端模式(企业级服务器)。
  • linux/s390x:IBM Z 系列大型机架构。
  • linux/riscv64:新兴的开源 RISC-V 64位架构。
  • windows/amd64 / windows/arm64
    (注意:构建 Windows 容器不能在 Linux 宿主机上进行,且目前 Compose 对多平台 Windows 容器构建的支持相对有限。)
  • darwin/amd64 / darwin/arm64: macOS
  • freebsd/amd64 / freebsd/arm64

避坑指南

  • 基础镜像支持: Dockerfile 里 FROM 的镜像必须提供对应的平台架构版本。如果未提供,构建时会提示找不到 manifest。

SSH

选项

  • default: 使用默认的 ssh-agent
  • my_key=~/.ssh/id_rsa: 或者指定具体密钥路径

有2种模式: SSH Agent, 密钥文件

特性对比SSH Agent (default)密钥文件 (id=path)
底层机制映射 SSH Socket 通信通道将文件内容临时挂载到内存
私钥实体是否进入容器❌ 否(0接触私钥文件)✅ 是(在内存中,阅后即焚)
是否支持带密码的私钥支持(在宿主机解密即可)不支持(会导致构建卡死)
宿主机前置要求必须运行 ssh-agentssh-add无要求,有文件就行
多密钥精确控制较弱(Agent 自动尝试管理的密钥)✅ 极强(通过 ID 精准指定对应密钥)
最佳使用场景本地开发者电脑、强安全要求环境CI/CD 自动化流水线、服务器自动构建

使用示例

dockerfile
FROM alpine/git:latest

# 添加目标服务器的公钥到 known_hosts
# 否则在 SSH 连接时会因为要求手动确认 "Are you sure you want to continue connecting (yes/no)?" 导致构建卡死。
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts

# 执行需要鉴权的命令时使用 --mount=type=ssh
RUN --mount=type=ssh git clone [email protected]:YourOrg/repo1.git /app/
RUN --mount=type=ssh,id=my_github_key git clone [email protected]:YourOrg/repo1.git /app/

如果使用Agent模式,请先用此命令在宿主机加载密钥:

bash
eval "$(ssh-agent -s)" # 启动 ssh-agent
ssh-add ~/.ssh/id_rsa  # 添加私钥

资源限制

Docker 允许用户对容器,限制、预留CPU、内存。因此,可以更容易地管理资源。

yaml
services:
  web:
    deploy:
      resources:
        limits: # 资源限制
          cpus: "0.50"      # 多可以使用 0.5 个CPU核心
          memory: "512M"    # 最多可以使用 512MB 的内存。
        reservations: # 资源保留
          cpus: "0.25"      # 保留 0.25 个CPU核心
          memory: "256M"    # 保留 256MB 的内存。

cpus单位:1 CPU核心。例子:宿主的 CPU 有 6 核,要使用全部资源的50%,则值应为3;它的最大值为6。

拉取策略

此策略告诉Compose如何处理镜像拉取:

yaml
services:
  my-service:
    pull_policy: always

pull_policy的选项:

  • always: 每次启动服务时都会拉取镜像。
  • missing: 如果服务启动时本地没有该镜像,则会拉取镜像。
  • never: 强制使用本地存在的镜像,不会进行任何拉取操作。
  • if_not_present: 如果镜像本地不存在则会拉取,但如果本地已经有这个镜像,即使不是最新的也不会进行更新。

注意事项

  • 使用--pull alwayspull_policy: always可能会导致更新时由于镜像变化过大而产生兼容性问题或服务中断,所以生产环境中要慎用,确保更新的镜像与现有应用兼容。
  • 使用pull_policy是一种更细粒度的控制,可以为每个服务独立设置更新策略。
  • 确保你的环境变量以及其它依赖的配置是与新的镜像更新兼容的。

GPU

简洁语法

yaml
services:
  my-service:
    gpus: all

正式语法

yaml
services:
  my-service:
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
  • deploy: 资源部署配置块。用来管理容器的资源配额(如限制 CPU、内存、分配 GPU)。

  • resources: 定义该容器的资源需求。

  • reservations: 资源预留。GPU 不能像 CPU 那样按百分比随意切分,容器必须“独占”或“明确预留”指定的物理 GPU 设备。

  • devices: 声明需要映射到容器内的宿主机设备列表。

  • driver: nvidia: 使用 NVIDIA 的底层驱动(由 NVIDIA Container Toolkit 提供)来挂载设备。如果是 AMD 显卡或某些新架构,这里可能会变成 cdi

  • countdevice_ids (二选一):

    • count: 指定需要使用几张显卡。all, 或者具体的数字(如 1, 2) 。
    • device_ids: 指定使用具体的显卡。值为显卡编号的列表,必须是字符串格式,例如 ['0'], ['0', '2']
      • all: 所有显卡
      • 0,1: 编号为 0 和 1 的显卡
      • GPU-xxxx-xxxx...: 显卡 UUID
      • none: 无显卡被容器可见(常用于临时禁用)
  • capabilities: 暴露的 GPU 功能。
    Docker Compose 只是把这个数组透传给底层的显卡驱动(即 NVIDIA Container Toolkit)。

    • all:开启所有的功能。
    • gpu:挂载基础的计算和工具能力。 这是 Docker 提供的简写,相当于 [compute, utility]。对于大部分日常的深度学习、大语言模型部署,[gpu] 就足够。
    • compute: CUDA 和 OpenCL(用于 AI 跑图、大模型训练/推理等计算任务)。
    • utility: 实用工具。nvidia-smi 和 NVML 库(用于查看显卡温度、显存占用等状态)。
    • video: Video Codec SDK(用于视频硬件编解码,如 NVENC/NVDEC)。
    • graphics: OpenGL 和 Vulkan(用于 3D 图形渲染、云游戏)。
    • display: X11 物理显示输出。
    • compat32:兼容 32 位应用。

微调器(可选)

yaml
services:
  my-service:
    environment:
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: all
  • NVIDIA_VISIBLE_DEVICES:
    意义:决定容器内部能看到并使用宿主机上的哪些 GPU

  • NVIDIA_DRIVER_CAPABILITIES:
    意义: 能使用显卡的哪些功能”。显卡有很多不同的功能模块(如计算、视频解码、图形渲染等),这个变量决定将宿主机的哪些驱动库文件挂载到容器中。
    选项:

INFO

Q: 环境变量NVIDIA_*deploy / gpus 的关系?

A: deploy 是主开关,环境变量是微调旋钮

  • deploy / gpus:负责分配硬件(给容器进 GPU 房间的钥匙)。
  • 环境变量:负责分配权限和功能(告诉容器在房间里能用哪些工具,比如是做数学计算 compute,还是做视频压制 video)。

扩展功能

secrets

定义可以被服务使用的全局机密(仅适用于Compose v3.1+):

yaml
secrets:
  my-secret:
    file: ./my_secret.txt

configs

定义可以在服务之间共享的配置(仅适用于Compose v3.1+):

yaml
configs:
  my-config:
    file: ./my_config.txt

扩展标签

docker-compose.yml可以包含一些全局设置,允许你为整个文件应用某些配置:

  • x-default-env-file: 指定一个环境文件,所有的服务都将默认读取这个文件中的环境变量
yaml
x-default-env-file:
  - .env
  • x-default-pull_policy: 拉取策略
yaml
x-default-pull_policy: always
  • x-default-logging: 设置一个全局的日志记录配置,这是被所有服务继承,除非单个服务有自己的特殊设置:
yaml
x-default-logging: &global-logging
  driver: syslog
  options:
    syslog-address: "udp://127.0.0.1:123"
    tag: 'something'
services:
  web:
    image: myimage
    logging: *global-logging
  • x-default-ports: 在某些版本中,你可以定义一个默认的端口映射策略,它将自动应用于所有适用的服务:
yaml
x-default-ports:
  - 'target: 80, published: 8080'
  • x-default-networks: 为所有服务添加默认网络:
yaml
x-default-networks:
  - default
  • x-default-volumes: 定义可以在服务中使用的全局卷:
yaml
x-default-volumes:
  - data:/data
volumes:
  data:
...

WARNING

需要注意的是,像x-*这样的用户定义的扩展字段都是非标准的,所以它们在不同的Docker Compose版本中可能有不同的支持方式或被忽略。在使用这些扩展功能时,请检查你的Docker Compose版本和官方支持的功能,确保你使用的标签是有效的。