2026/07/22

Docker 从入门到生产实践:容器边界、Compose 部署与运维清单

一篇面向开发者的 Docker 生产实践参考:镜像、容器、卷、网络、Dockerfile、Compose、健康检查、日志、备份、回滚与安全边界。

DockerDocker ComposeDockerfile容器化DevOps

Docker 的生产价值不是“命令更酷”,而是把运行环境、启动入口、网络、数据、配置和发布动作变成可复查的交付契约。这篇更适合作为综合参考:先建立边界,再落到服务器部署和运维清单。如果只会 docker run,却没有处理卷、健康检查、日志、资源边界、镜像标签和回滚,容器化只是把故障换了一个壳。

本文按 2026-07-22 的项目日期整理,结论以 Docker 官方文档为准。不要把容器当成完整虚拟机,也不要把 Compose 当成小型 Kubernetes;单机项目用 Compose 往往足够,关键是边界清楚、数据可恢复、更新可回退。

先划清四个对象

对象结论生产边界
镜像只读模板,包含应用与运行依赖固定明确标签,必要时固定 digest
容器镜像运行实例,生命周期由主进程决定容器可删可重建,不存唯一数据
仓库分发镜像的位置只拉可信镜像,私有镜像要控制权限
Volume独立于容器的数据层数据库优先用 volume,并单独备份

容器和虚拟机的差别要说清:容器共享宿主机内核,隔离的是进程、文件系统、网络和资源;虚拟机运行独立内核。很多生产服务器是在云 VM 里跑 Docker,VM 负责基础设施隔离,Docker 负责应用交付。

安装与第一条验收命令

Linux 服务器建议从 Docker 官方软件源安装 Docker Engine、CLI、containerd、Buildx 和 Compose 插件。不要长期依赖来历不明的一键脚本。安装后先验收版本和 hello-world,再考虑把用户加入 docker 组。能访问 Docker daemon 的用户接近拥有宿主机 root 能力,多人服务器要谨慎。

docker version
docker compose version
sudo docker run --rm hello-world

docker run 只适合学习边界

docker run -d \
  --name web-demo \
  -p 8080:80 \
  nginx:alpine

docker ps
docker logs -f --tail 100 web-demo
docker exec -it web-demo sh

端口映射顺序永远是宿主机端口:容器端口。容器里的 localhost 指向容器自己,不是宿主机,也不是数据库容器。应用连接 PostgreSQL 应写 db:5432 这样的服务名,并把两个服务放在同一个自定义网络里。

数据持久化:Volume 和 Bind Mount 分工

方式适合风险
VolumePostgreSQL、MySQL、上传文件等持久数据不是备份,仍需导出或快照
Bind Mount宿主机直接维护的配置、证书、日志目录路径和权限依赖宿主机,可能遮住镜像内文件
readonly mount只读配置减少被容器内进程篡改宿主机文件的风险
docker volume create postgres-data
docker run -d --name db \
  --mount type=volume,source=postgres-data,target=/var/lib/postgresql/data \
  -e POSTGRES_DB=app -e POSTGRES_USER=app -e POSTGRES_PASSWORD=change-me \
  postgres:17

Dockerfile 要让构建可重复

生产镜像的底线是:基础镜像可信,依赖层尽量可缓存,最终镜像只保留运行所需文件,应用以前台主进程启动,日志输出到标准输出,默认不以 root 运行。

FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /workspace
COPY pom.xml .
RUN --mount=type=cache,target=/root/.m2 mvn -B dependency:go-offline
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn -B clean package -DskipTests

FROM eclipse-temurin:21-jre
WORKDIR /app
RUN apt-get update  && apt-get install -y --no-install-recommends curl  && rm -rf /var/lib/apt/lists/*  && useradd --system --uid 10001 appuser
COPY --from=builder /workspace/target/*.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

.dockerignore 不是装饰。构建上下文会发送给构建器,.git、IDE 文件、日志、.env 和临时产物不应进入上下文。注意不要把 Dockerfile 需要 COPY 的文件也忽略掉。

.git
.idea
.vscode
*.log
.env
compose.override.yaml

Compose 是部署契约

现代 Compose 遵循 Compose Specification,通常不再需要顶层 version 字段。生产 Compose 文件应该同时说明镜像、端口、网络、卷、健康检查、重启策略、资源边界和密钥来源。

name: demo-app
services:
  app:
    image: registry.example.com/demo-app:2026-07-22
    restart: unless-stopped
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/app
      SPRING_DATASOURCE_USERNAME: app
      SPRING_DATASOURCE_PASSWORD_FILE: /run/secrets/db_password
      JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=70
    secrets:
      - db_password
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 40s
    mem_limit: 1g
    cpus: 1.5
    networks: [backend]
  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks: [backend]
secrets:
  db_password:
    file: ./secrets/db_password.txt
volumes:
  postgres-data:
networks:
  backend:

depends_on 的短格式只表达启动顺序,不代表数据库已经可用。使用 service_healthy 可以避免很多首启失败,但应用自身仍要有连接重试,因为数据库可能在运行中重启。

生产边界清单

  • 只把 Web 入口暴露给反向代理;数据库和 Redis 不发布到公网。
  • 应用端口优先绑定 127.0.0.1,再由 Nginx、Caddy 或 Compose 内部代理转发。
  • 设置 logging max-size/max-file,避免容器日志吃满磁盘。
  • 设置 mem_limit、cpus,并给 Java 堆外内存留空间。
  • 用 secrets 文件或宿主机密钥系统,不把密码写进镜像和公开仓库。
  • 更新时构建新镜像,修改部署版本,docker compose up -d,不进容器 git pull。

备份、恢复与回滚

docker compose exec -T db pg_dump -U app -d app -Fc > app-$(date +%F).dump
docker compose exec -T db pg_restore -U app -d app --clean --if-exists < app-2026-07-22.dump

docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail 200 app

数据卷不是备份。备份要有多份历史、至少一份离开本机、定期恢复演练、加密和权限控制。回滚也不是 down 再 up;优先切回上一镜像版本并重新 up -d,避免删除网络和数据卷。

故障排查顺序

故障先查常见根因
容器立即退出docker ps -a / logs / inspect启动命令、环境变量、权限、OOM、主进程结束
端口访问不了端口映射、监听地址、防火墙、安全组、DNS应用只监听 127.0.0.1 或端口没发布
连不上数据库连接串、网络、healthcheck、密码、卷初始化状态把 localhost 当成 db,或改密码后旧卷未变化
磁盘满df -h / docker system df -v日志未轮转、旧镜像、构建缓存、未清理容器

本文依据的 Docker 官方文档