2026/07/22
Docker 从入门到生产实践:容器边界、Compose 部署与运维清单
一篇面向开发者的 Docker 生产实践参考:镜像、容器、卷、网络、Dockerfile、Compose、健康检查、日志、备份、回滚与安全边界。
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 分工
| 方式 | 适合 | 风险 |
|---|---|---|
| Volume | PostgreSQL、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 | 日志未轮转、旧镜像、构建缓存、未清理容器 |