惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

博客园 - xiashengwang

Rocky Linux 9 安装docker Rocky Linux 9 与Centos区别,以及软件安装dnf命令 《Linux 基础点滴》:(17)SSH 密钥生成与管理 – 免密登录的利器 Linux服务器安全登录:从入门到实践 putty 基础 《Linux 基础点滴》:(16)firewalld 防火墙管理命令 《Linux 基础点滴》:(15)用户管理 – 创建、删除、权限与 sudo 《Linux 基础点滴》:(14)定时任务 – 用 crontab 自动化 《Linux 基础点滴》:(13)文本编辑入门 – 使用 Vim 《Linux 基础点滴》:(12)tldr – 简化版命令手册 《Linux 基础点滴》:(11)系统信息与资源监控 – 掌握服务器状态 《Linux 基础点滴》:(10)压缩与打包 – tar 的艺术 《Linux 基础点滴》:(9)查找与过滤 – find, grep, 管道 《Linux 基础点滴》:(8)网络基础 – 查看 IP、连通性与端口 《Linux 基础点滴》:(7)进程管理 – 查看与控制运行中的程序 《Linux 基础点滴》:(6)文件权限与所有者 – 保护你的文件 《Linux 基础点滴》:(5)文件与目录操作 – 增删改查必会命令 《Linux 基础点滴》:(4)磁盘空间查看 – df 和 du 的妙用 《Linux 基础点滴》:(3)Linux 文件系统概览 – 一切皆文件 《Linux 基础点滴》:(2)SSH 连接与基础命令 – 从登录到第一次操作 《Linux 基础点滴》:(1)初识 Linux 大家族 – 发行版分类与选择 docker学习(11)- 容器的日志(log) docker学习(10)-利用Github Action 自动化部署 docker学习(9)-Docker Swarm + Nginx 跨机器负载均衡 docker学习(8)-从 Windows 开发到 Linux 部署 docker学习(6)-数据持久化与网络通信 学会 Git Tag,让版本管理不再混乱 docker学习(5)-Dockerfile docker学习(4)-Docker常用命令 docker学习(3)-一个简单的例子 docker学习(2)-安装 docker学习(1)-基本概念 Github 使用 Self-hosted Runner 实现服务器自动化部署 github创建分支 + Pull Request 合并 Git Rebase vs Git Merge:深入理解与实战选择 Git 系统学习教程:从零到团队协作 AI学习笔记(2)-OpenClaw安装 windbg分析dmp文件 docker desktop 和 Vmware冲突的解决办法
docker学习(7)-docker compose 文件
xiashengwang · 2026-04-16 · via 博客园 - xiashengwang

一、学习目标

  • 掌握 docker-compose.yml 的完整语法(版本、服务、网络、卷、环境变量、健康检查、依赖控制等)
  • 理解 Compose 中的环境管理(.env 文件、多文件覆盖、环境变量替换)
  • 学会使用 Compose 扩展(extends)和锚点(YAML 特性)复用配置
  • 能够为应用配置健康检查、资源限制、重启策略
  • 实现服务的水平扩展(scale)

二、核心知识点

1, Compose 文件版本
当前推荐使用 版本 3.8(兼容 Swarm)或 3.9(最新)。在文件开头声明:

version: '3.8'

2, 服务定义进阶
a) 构建配置

services:
  web:
    build:
      context: ./web          # 构建上下文路径
      dockerfile: Dockerfile  # 指定 Dockerfile 文件名(可选)
      args:                   # 构建参数(ARG)
        - ENV=production
    image: myapp:latest       # 构建后镜像名称

b) 环境变量注入

  • 直接定义:
environment:
  - NODE_ENV=production
  - DB_HOST=mysql
  • 从文件加载:
env_file:
  - ./config/common.env
  - ./config/prod.env
  • 使用 .env 文件(与 docker-compose.yml 同目录,自动加载):
# .env 文件内容
VERSION=1.0
PORT=8080

在 Compose 中引用:"${VERSION}""${PORT:-80}"(带默认值)

c) 依赖控制

  • depends_on:控制启动顺序(但不等待就绪)
depends_on:
  - db
  - redis
  • 条件等待(v3 已支持部分条件):可使用 condition(需要 healthcheck)

v3: Docker Compose 文件格式的第 3 代版本,如:version:3.8

depends_on:
  db:
    condition: service_healthy

d) 健康检查

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

e) 资源限制
也就是给容器限定使用cpu和内存的上限和下限。但是deploy模式只在集群模式(Swarm)才有效,单机会被忽略,但最好写,这样直接迁移到集群可以通用。

services:
  web:
    # 单机生效(资源限制)
    cpus: '0.5'     # CPU 上限
    mem_limit: 512M # 内存上限
    
    # 集群生效(兼容 Swarm)
    deploy:
      resources:
        limits: # 上限:绝对不能超过
          cpus: '0.5'
          memory: 512M
        reservations: # 保底:至少保证这么多
          cpus: '0.25'
          memory: 256M

注意:deploy 配置仅在 Swarm 模式下生效,在单机 Compose 中可使用 cpus 和 mem_limit(旧版语法)。推荐使用 deploy 保持与 Swarm 兼容。

f) 重启策略

restart: always       # always, no, on-failure, unless-stopped
  • no(默认):
    不重启。容器退出后,无论何种原因都不会自动重启。
  • on-failure:
    仅异常时重启。只有当容器以非零退出码(即程序报错/崩溃)退出时,才会重启。如果程序正常退出(exit 0),则不重启。
  • always:
    总是重启。无论容器是崩溃还是正常退出,都会自动重启。
    重点:即使你手动执行 docker stop 停止了容器,当 Docker 服务本身重启(如服务器重启)时,该容器也会跟着自动启动。
  • unless-stopped(最常用):
    除非手动停止,否则一直重启。行为与 always 类似,都会自动重启。
    核心区别:如果你手动执行了 docker stop,即使 Docker 服务重启,它也不会自动启动。这尊重了用户的主动停止操作。

g) 端口映射

ports:
  - "8080:80"           # 主机:容器
  - "8081:80"           # 简写
  - "0.0.0.0:8082:80"   # 绑定到指定主机 IP
  - "8083-8085:80-82"   # 范围映射

h) 卷挂载

volumes:
  # 1. Docker托管卷 → 存持久数据
  - type: volume
    source: mydata
    target: /data
  # 2. 挂载本机配置 → 改外面生效
  - type: bind
    source: ./config
    target: /app/config
    read_only: true
  # 3. 临时文件 → 放内存
  - type: tmpfs
    target: /tmp

3, 网络定义

networks:
  frontend:
    driver: bridge
    #ip address management
    ipam:
      config:
        - subnet: 172.20.0.0/16
  backend:
    driver: bridge

services:
  web:
    networks:
      - frontend
  db:
    networks:
      - backend
  app:
    networks:
      - frontend
      - backend

划分网络,可以做app直接的隔离,比如上面web和db是不通的,app可以访问db和web。

4, 卷定义
权限继承机制
Bind mount直接挂载宿主机目录,容器内进程访问文件时沿用宿主机的UID/GID和权限位(如0644),易因用户不一致导致权限拒绝。而named volume由Docker管理,初始化时会根据镜像设定正确属主,避免权限错配。
比如:在service下直接写:/host/data:/container/data,那么宿主机的/host/data目录属主可能是root,而/container/data的属主可能是mysql,就会出现容器无权限操作挂载目录的问题。解决方法就是用下面的这种named volume,这种方式,docker内容会自动处理权限兼容性问题,会将宿主/host/data目录改为和容器目录一致的属主(如:mysql),这样就解决了权限问题。因此,生产环境,建议都要用卷定义的方式来挂载外部数据

volumes:
  db_data:
    driver: local
    driver_opts:
      type: none
      device: /data/mysql
      o: bind
  cache:
    external: true   # 使用外部已创建的卷,不会再创建新的卷

这是 Docker 顶级卷定义(写在文件最外层,不是服务里)。作用:提前定义好数据卷,让所有容器共用、管理更清晰。
每个字段意思:

  • driver: local:本地卷(最常用)
  • type: none:我要手动绑定目录
  • device: /data/mysql:宿主机真实文件夹(不是容器里!)
  • o: bind:开启绑定模式

5, 环境管理

  • 多文件覆盖:使用 -f 指定多个 Compose 文件,后面的覆盖前面的。
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

常见命名:

  • docker-compose.yml:基础配置
  • docker-compose.override.yml:自动覆盖(如果存在,up 时会自动加载)
  • docker-compose.prod.yml、docker-compose.test.yml:手动指定

大部分项目的结构:

你的项目/
├── docker-compose.yml          # 基础公共配置(不变)
├── docker-compose.override.yml # 开发专用(自动加载)
├── docker-compose.prod.yml     # 生产专用(手动加载)
└── .env                        # 环境变量

开发环境命令:自动加载docker-compose.yml和docker-compose.override.yml
docker-compose up -d

生产环境命令:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

这条命令可以查看合并后的配置,确认是否合并正确:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml config

  • extends 扩展(单文件复用)
# common.yml
services:
  base:
    image: alpine
    restart: always

# docker-compose.yml
services:
  web:
    extends:
      file: common.yml
      service: base
    command: /app/web
  • YAML 锚点与别名(更灵活):
x-logging: &default-logging
  driver: json-file
  options:
    max-size: "10m"

services:
  web:
    logging: *default-logging
  db:
    logging: *default-logging
  • x-logging:只是一个名字(随便写,x- 开头是 Compose 扩展字段)

  • &default-logging:创建一个锚点,名字叫 default-logging

  • 作用:把下面这段日志配置存起来,以后随时用

  • *default-logging:调用刚才定义的锚点

  • 等于把上面那段 logging 配置原封不动复制过来

6, 常用 Compose 命令

命令 说明
docker-compose up -d 后台启动所有服务
docker-compose down 停止并删除容器、网络(保留卷)
docker-compose down -v 同时删除卷
docker-compose build 构建或重新构建服务镜像
docker-compose pull 拉取服务镜像
docker-compose push 推送镜像到仓库
docker-compose logs -f 查看日志
docker-compose ps 列出容器状态
docker-compose exec <service> <cmd> 在运行中的容器执行命令
docker-compose run <service> <cmd> 运行一次性命令(新容器)
docker-compose stop/start/restart 管理服务
docker-compose scale <service>=<num> 扩展服务实例数(需要 Swarm 或 Compose v2)
docker-compose top 显示容器内进程
docker-compose config 验证并查看解析后的配置

7, 水平扩展(Scale)
在 Compose v2 中,可以使用 scale 命令(或 --scale 选项)运行多个容器实例。需要服务无状态且可能配置负载均衡。

docker-compose up -d --scale web=3

一次运行多个实例

docker-compose up -d --scale web=3 --scale mysql=2

需要在 docker-compose.yml 中开放端口时注意端口冲突,通常结合负载均衡器(如 Nginx)或使用随机端口映射。

三、实践任务

任务 1:使用环境变量和 .env 文件
目标:创建一个 Web 应用,根据环境变量显示不同内容。

  • 创建项目目录 :\docker-compose-demo

  • 编写 app.py(Flask 示例):

import os
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    message = os.environ.get('MESSAGE', 'Hello World')
    return f'<h1>{message}</h1>'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
  • 编写 requirements.txt:
flask
  • 编写 Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]
  • 编写 docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - MESSAGE=Hello from Compose
  • 启动:docker-compose up -d,访问 http://localhost:5000 看到消息。

  • 创建 .env 文件,内容:PORT=8080,并修改 Compose 文件:
    docker-compose.yml会自动读取.env里面的内容。

ports:
  - "${PORT}:5000"
  • 重新启动:docker-compose down && docker-compose up -d,访问 http://localhost:8080

  • 尝试修改 .env 中的 PORT 值,再次启动验证。

任务 2:多文件环境配置(开发 vs 生产)
目标:使用 docker-compose.override.yml 实现开发环境的热加载。

  • 沿用任务 1 的项目,创建 docker-compose.override.yml
version: '3.8'
services:
  web:
    environment:
      - MESSAGE=Development Mode
    volumes:
      - ./app.py:/app/app.py   # 挂载代码实现热加载
    command: python app.py --debug   # 开启 debug 模式
  • 执行 docker-compose up -d,此时会自动合并 docker-compose.ymldocker-compose.override.yml。访问页面看到 "Development Mode"。

  • 修改本地的 app.py 中的消息内容,保存后刷新页面,看是否立即生效(取决于 Flask 是否开启 debug 或重启)。

  • 创建生产环境文件 docker-compose.prod.yml

version: '3.8'
services:
  web:
    environment:
      - MESSAGE=Production Mode
    restart: always
  • 使用 -f 启动生产配置:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
  • 清理:docker-compose down

任务 3:健康检查与依赖控制
目标: 模拟一个依赖数据库的应用,确保数据库健康后才启动应用。

  • 创建 docker-compose.yml
version: '3.8'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: example
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 3
  app:
    image: alpine:latest
    command: sh -c "while true; do echo waiting for db... && sleep 5; done"
    depends_on:
      db:
        condition: service_healthy
  • 启动:docker-compose up -d,观察 app 容器会等待 db 健康检查通过后才启动。

  • 查看日志:docker-compose logs app,确认在 db 健康后启动。

任务 4:使用外部卷和网络
目标: 创建一个自定义网络和卷,在 Compose 中引用外部资源。

  • 预先创建卷和网络:
docker volume create shared-data
docker network create mynet
  • 编写 docker-compose.yml
version: '3.8'
services:
  writer:
    image: alpine
    command: sh -c "echo hello > /data/msg && tail -f /dev/null"
    volumes:
      - shared-data:/data
    networks:
      - mynet
  reader:
    image: alpine
    command: sh -c "sleep 10 && cat /data/msg && tail -f /dev/null"
    volumes:
      - shared-data:/data
    networks:
      - mynet
volumes:
  shared-data:
    external: true
networks:
  mynet:
    external: true
  • 启动:docker-compose up -d,查看 reader 日志应该能读取到 hello。

  • 清理:docker-compose down,手动删除卷和网络:docker volume rm shared-datadocker network rm mynet

任务 5:水平扩展与负载均衡(结合 Nginx)
目标: 扩展 Web 服务到 3 个实例,并通过 Nginx 负载均衡访问。

  • 创建简单 Web 应用(返回实例主机名)。编写 app.py:
import os
from flask import Flask
app = Flask(__name__)
hostname = os.environ.get('HOSTNAME', 'unknown')

@app.route('/')
def index():
    return f'Hello from {hostname}\n'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Dockerfile 同上(Flask)。

FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]

requirements.txt

flask

docker-compose.yml:

version: '3.8'
services:
  web:
    build: .
    # 下面的必须注释,容器启动时,会自动注入容器id到HOSTNAME
    #environment:
    # - HOSTNAME=${HOSTNAME}
    # 不暴露端口,让 Nginx 代理
  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "8080:80"
    depends_on:
      - web
  • 编写 nginx.conf(简单负载均衡):
events { worker_connections 1024; }

http {
  upstream web_servers {
    server web:5000;
  }

  server {
    listen 80;
    location / {
      proxy_pass http://web_servers;
      proxy_set_header Host $host;
    }
  }
}
  • 启动服务并扩展 web 到 3 个实例:
docker-compose up -d --scale web=3

多次访问 http://localhost:8080 ,观察返回的主机名应该在不同容器之间轮询(Nginx 默认轮询)。

  • 查看容器状态:docker-compose ps。

  • 清理:docker-compose down