Docker Compose 的编写
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的强大工具。在现代软件开发中,应用程序往往由多个互相关联的服务组成,例如数据库、Web 服务器和后端服务等。Docker Compose 通过一个简单的 YAML 文件来描述这些服务,并提供了一键式启动、停止和管理整个应用的能力,使得开发、测试和部署变得更加简便和高效。
让我们开始这段 Docker Compose 的探索之旅,了解如何利用这一工具简化多容器应用的管理,提升效率和协作能力。🚀
常用内容
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多容器堆栈
示例
name: brother
services:
web:
image: nginx:latest
ports:
- "80:80"
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:软件
services:
my-service:
image: lscr.io/linuxserver/syncthing:latest # 镜像image 用于确定要使用的软件和来源。
格式:仓库/作者/软件:版本。其中,软件是必要的,其它都是可选项目。要指定镜像仓库,则需要指定作者。
image: redis # 镜像名
image: ubuntu:14.04 # 镜像名:版本
image: estrellaxd/auto_bangumi # 作者/镜像名
image: my-registry.com:4000/postgresql # 仓库/作者/镜像名
image: a4bc65fd # 镜像id支持的格式。
环境变量
它是一种存储配置的方式。
services:
my-service:
environment: # 环境变量
- PUID=3000 # 用户ID
- PGID=950 # 组ID
- TZ=Asia/Shanghai # 时区另一种写法
services:
my-service:
environment:
PUID: 3000 # 用户ID
PGID: 950 # 组ID
TZ: Asia/Shanghai # 时区存储
Docker 的卷(Volume)是用于在容器之间共享和持久化数据的一种机制。它们提供了一种独立于容器生命周期的数据管理方式,使得数据在容器停止或删除后仍然能够保留。
背景
默认情况下,容器的数据不会被保存。这使得,在这些情况下,数据丢失:容器停止、重启系统、软件升级
为什么使用卷?
- 持久化数据:卷允许你持久化数据,即使容器被删除,数据也不会丢失。
- 共享数据:不同的容器可以共享同一个卷,方便实现数据共享和协作。
- 数据隔离:卷可以隔离数据,使得不同的应用和环境可以使用独立的数据存储。
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时,外部不能主动访问容器内。这个功能可以解决此问题。通过此功能,还能使用自定义的端口,而不是软件默认监听的的端口。
services:
my-service:
network_mode: bridge # 网络模式
ports: # 端口映射
# 格式 宿主机端口:容器端口/协议
- 8384:8384
- 22000:22000/tcp
- 22000:22000/udp网络模式
services:
my-service:
network_mode: host # 网络模式区别
bridge: Docker 的默认网络模式,每个容器在启动时都会连接到一个桥接网络。容器之间可以通过容器名相互通信,但默认情况下,外部不能直接访问容器。它的本质是NAT,就像小区内的快递柜。host: 容器与宿主机共享网络栈。网络方面,效果和直接在宿主机运行相同。适用于需要高性能网络的场景,但会减少网络隔离。none: 容器不配置任何网络。容器内没有网络接口,只适用于不需要网络通信的场景。
网络相关
services:
my-service:
hostname: my-service # 主机名。没什么用。重启策略
services:
my-service:
restart: alwaysno:默认值,表示不会自动重启容器。如果容器停止或退出,它将保持停止状态。always:无论容器如何停止,都会自动重启。这意味着即使容器崩溃或手动停止,Compose 也会尝试重新启动它。on-failure:仅在容器以非零退出码退出时重启。你可以指定最大重启次数,例如on-failure:5表示容器最多重启五次。unless-stopped:除非容器被手动停止,否则会自动重启。这意味着如果容器崩溃,Compose 会尝试重新启动它,但如果你手动停止了容器,它将保持停止状态。
参数与入口点
services:
Watchtower:
command: --schedule "0 5 * * *" aria2 AriaNg yacd-meta Peer-Ban-Helper Home_Assistant AutoBangumi DDNS-Gocommand 的功能是传递参数
services:
web:
entrypoint: ["sh", "-c", 'apk add openssh && exec /app/ddns-go && "$@"']entrypoint 指容器开启时,启动软件的命令。它被指定时,效果是覆盖原有的entrypoint,而不是追加。
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没有中文字体,导致中文显示为方框
交互式终端
services:
web:
stdin_open: true # 开启标准输入
tty: true # 分配为伪终端docker exec -it 容器名称 sh # 连接容器的 shell这两个选项通常一起使用,以确保容器能够提供一个交互式的终端环境,适合调试或运行需要用户输入的应用程序。通常使用场景:需要在容器启动后,立刻在其中执行命令。
构建
简洁语法
services:
webapp:
build: . # 构建上下文详细语法(对象模式)
services:
app:
build:
context: ./src
dockerfile: Dockerfile-dev
args:
buildno: 1
gitcommithash: cdc3b19
# 或者使用数组写法:
# - buildno=1
# - gitcommithash=cdc3b19高级语法(依赖 BuildKit 的现代特性)
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_rsacontext: 构建上下文。构建镜像时的根目录,包含构建所需的所有文件。可以是相对路径、绝对路径或 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: macOSfreebsd/amd64/freebsd/arm64
避坑指南
- 基础镜像支持: Dockerfile 里
FROM的镜像必须提供对应的平台架构版本。如果未提供,构建时会提示找不到 manifest。
SSH
选项
default: 使用默认的 ssh-agentmy_key=~/.ssh/id_rsa: 或者指定具体密钥路径
有2种模式: SSH Agent, 密钥文件
| 特性对比 | SSH Agent (default) | 密钥文件 (id=path) |
|---|---|---|
| 底层机制 | 映射 SSH Socket 通信通道 | 将文件内容临时挂载到内存 |
| 私钥实体是否进入容器 | ❌ 否(0接触私钥文件) | ✅ 是(在内存中,阅后即焚) |
| 是否支持带密码的私钥 | ✅ 支持(在宿主机解密即可) | ❌ 不支持(会导致构建卡死) |
| 宿主机前置要求 | 必须运行 ssh-agent 并 ssh-add | 无要求,有文件就行 |
| 多密钥精确控制 | 较弱(Agent 自动尝试管理的密钥) | ✅ 极强(通过 ID 精准指定对应密钥) |
| 最佳使用场景 | 本地开发者电脑、强安全要求环境 | CI/CD 自动化流水线、服务器自动构建 |
使用示例
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模式,请先用此命令在宿主机加载密钥:
eval "$(ssh-agent -s)" # 启动 ssh-agent
ssh-add ~/.ssh/id_rsa # 添加私钥资源限制
Docker 允许用户对容器,限制、预留CPU、内存。因此,可以更容易地管理资源。
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如何处理镜像拉取:
services:
my-service:
pull_policy: alwayspull_policy的选项:
- always: 每次启动服务时都会拉取镜像。
- missing: 如果服务启动时本地没有该镜像,则会拉取镜像。
- never: 强制使用本地存在的镜像,不会进行任何拉取操作。
- if_not_present: 如果镜像本地不存在则会拉取,但如果本地已经有这个镜像,即使不是最新的也不会进行更新。
注意事项
- 使用
--pull always或pull_policy: always可能会导致更新时由于镜像变化过大而产生兼容性问题或服务中断,所以生产环境中要慎用,确保更新的镜像与现有应用兼容。 - 使用
pull_policy是一种更细粒度的控制,可以为每个服务独立设置更新策略。 - 确保你的环境变量以及其它依赖的配置是与新的镜像更新兼容的。
GPU
简洁语法
services:
my-service:
gpus: all正式语法
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。count或device_ids(二选一):count: 指定需要使用几张显卡。all, 或者具体的数字(如1,2) 。device_ids: 指定使用具体的显卡。值为显卡编号的列表,必须是字符串格式,例如['0'],['0', '2']。all: 所有显卡0,1: 编号为 0 和 1 的显卡GPU-xxxx-xxxx...: 显卡 UUIDnone: 无显卡被容器可见(常用于临时禁用)
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 位应用。
微调器(可选)
services:
my-service:
environment:
NVIDIA_VISIBLE_DEVICES: all
NVIDIA_DRIVER_CAPABILITIES: allNVIDIA_VISIBLE_DEVICES:
意义:决定容器内部能看到并使用宿主机上的哪些 GPUNVIDIA_DRIVER_CAPABILITIES:
意义: 能使用显卡的哪些功能”。显卡有很多不同的功能模块(如计算、视频解码、图形渲染等),这个变量决定将宿主机的哪些驱动库文件挂载到容器中。
选项:
INFO
Q: 环境变量NVIDIA_*与 deploy / gpus 的关系?
A: deploy 是主开关,环境变量是微调旋钮
deploy/gpus:负责分配硬件(给容器进 GPU 房间的钥匙)。- 环境变量:负责分配权限和功能(告诉容器在房间里能用哪些工具,比如是做数学计算
compute,还是做视频压制video)。
扩展功能
secrets
定义可以被服务使用的全局机密(仅适用于Compose v3.1+):
secrets:
my-secret:
file: ./my_secret.txtconfigs
定义可以在服务之间共享的配置(仅适用于Compose v3.1+):
configs:
my-config:
file: ./my_config.txt扩展标签
docker-compose.yml可以包含一些全局设置,允许你为整个文件应用某些配置:
- x-default-env-file: 指定一个环境文件,所有的服务都将默认读取这个文件中的环境变量
x-default-env-file:
- .envx-default-pull_policy: 拉取策略
x-default-pull_policy: always- x-default-logging: 设置一个全局的日志记录配置,这是被所有服务继承,除非单个服务有自己的特殊设置:
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: 在某些版本中,你可以定义一个默认的端口映射策略,它将自动应用于所有适用的服务:
x-default-ports:
- 'target: 80, published: 8080'- x-default-networks: 为所有服务添加默认网络:
x-default-networks:
- default- x-default-volumes: 定义可以在服务中使用的全局卷:
x-default-volumes:
- data:/data
volumes:
data:
...WARNING
需要注意的是,像x-*这样的用户定义的扩展字段都是非标准的,所以它们在不同的Docker Compose版本中可能有不同的支持方式或被忽略。在使用这些扩展功能时,请检查你的Docker Compose版本和官方支持的功能,确保你使用的标签是有效的。