Docker 进阶实战:Dockerfile、网络、数据卷与 Compose

Docker 进阶实战:Dockerfile、网络、数据卷与 Compose

本篇目标:把自己的应用容器化。覆盖从写 Dockerfile 到多容器编排的完整链路,例子以一个 FastAPI + PostgreSQL 的 Web 应用为主线(FastBlog 项目同款架构)。

一、Dockerfile:把应用打成镜像

Dockerfile 是镜像的"配方",逐条指令构建分层:

FROM python:3.11-slim          # 基础镜像(slim 变体更小)

WORKDIR /app                   # 后续指令的工作目录
COPY requirements.txt .        # 先只拷依赖清单!
RUN pip install --no-cache-dir -r requirements.txt \
    -i https://mirrors.ustc.edu.cn/pypi/simple

COPY . .                       # 再拷业务代码(改动频繁)

EXPOSE 8000                    # 声明端口(文档作用)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
docker build -t myapp:1.0 .
docker run -d -p 8000:8000 myapp:1.0

分层缓存:构建速度的关键

每条指令生成一层,某层的输入没变,重建时直接用缓存。上面的写法故意"先 COPY requirements 再 COPY 代码":改代码时依赖层缓存依然命中,无需重装依赖。如果反过来一行 COPY . . 全量拷贝,改一个字符就要重新 pip install——这是新手最常犯的构建性能错误。

常用指令速查

指令 说明 要点
RUN 构建期执行命令 同类操作用 && 合并成一层,减少层数与体积
COPY / ADD 拷贝文件 优先 COPY(ADD 有解压等隐式行为)
ENV 环境变量 构建期与运行期都可见
ARG 构建参数 仅构建期,--build-arg 传入
ENTRYPOINT / CMD 启动命令 ENTRYPOINT 固定主程序,CMD 提供默认参数
VOLUME 声明挂载点 匿名卷声明

二、容器网络:四种模式

docker network ls              # 查看网络
docker run --network host ...  # 指定模式
模式 说明 场景
bridge(默认) 独立网段,通过端口映射对外 绝大多数场景
host 共享宿主机网络栈,无隔离 追求极致性能
none 无网络 离线任务/安全隔离
自定义 bridge 容器间可用容器名互相访问 多容器协作(重点)

自定义网络是多容器通信的正解:

docker network create appnet
docker run -d --name db --network appnet postgres:15
docker run -d --name app --network appnet -e DB_HOST=db myapp:1.0
#                                ↑ 容器内直接用容器名 db 当主机名,自带 DNS 解析

三、数据卷:让数据活过容器生命周期

# 命名卷:docker 管理,推荐用于数据库数据
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:15

# 绑定挂载:宿主机目录直通容器,推荐用于配置文件、开发热更新
docker run -d -v /opt/myapp/conf:/app/conf:ro myapp:1.0
#                                                        │ro 只读

选型口诀:数据库数据用命名卷(可移植、好备份);配置和代码用绑定挂载(直观可控)。删除容器 docker rm 不会删命名卷,需 docker rm -v 或 docker volume rm,这是很多人"删了容器数据还在/没了"困惑的来源。

四、Docker Compose:多容器编排

真实应用从来不止一个容器。Compose 用一个 YAML 描述整套应用:

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}      # 从 .env 读取,别硬编码
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:                             # 健康检查,配合 depends_on
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10
    restart: unless-stopped

  backend:
    build: .                                 # 用 Dockerfile 构建
    environment:
      DB_HOST: db                            # 直接用服务名当主机名
    depends_on:
      db:
        condition: service_healthy           # 等 db 健康再启动
    restart: unless-stopped

  web:
    image: nginx:latest
    ports: ["80:80"]
    volumes:
      - ./deploy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on: [backend]

volumes:
  pgdata:
docker compose up -d           # 一键起全套
docker compose logs -f backend # 看某服务日志
docker compose ps              # 整套状态
docker compose down            # 停止并删除(加 -v 连数据卷一起删,高危!)
docker compose up -d --build   # 改了代码重建镜像再启动

depends_on + healthcheck 的组合解决了"后端先于数据库启动导致连库失败"的经典问题,务必掌握。.env 文件与 compose 同目录时自动加载,密钥类配置全部走 .env 并加入 .gitignore。

五、动手练习

  1. 给自己的任意项目写 Dockerfile,用 time docker build 对比"先 COPY 依赖清单"和"全量 COPY"两种写法的二次构建耗时。
  2. 复刻本文的 Compose 例子,故意把 healthcheck 删掉重启,观察 backend 首次启动的连库报错,理解 condition 的价值。
  3. 用 docker volume 和 docker cp 两种方式把容器里的文件拿到宿主机。

下一篇预告

镜像构建好了、编排跑起来了,生产环境还有一堆现实问题:镜像太大、容器占资源失控、以 root 运行的安全隐患、日志把磁盘写满。最后一篇《Docker 精通之路》逐一击破。

评论(0)

0 / 2000
还没有评论,来抢沙发