Docker 容器化实战:从 Dockerfile 到 Compose 编排的完整指南
Docker 容器化实战:从 Dockerfile 到 Compose 编排的完整指南
Docker 已经成为现代应用交付的事实标准。从"在我机器上能跑"到"在任何机器上都能跑",容器化解决了环境一致性这一根本痛点。本文从镜像构建、容器运行到 Compose 编排,给出生产可用的实战方法。
一、理解镜像与容器的关系
镜像(Image) 是只读的模板,包含应用代码、运行时、依赖和配置;容器(Container) 是镜像的运行实例,可启动、停止、删除。二者关系类似"类"与"对象"。
镜像采用分层存储:每一层只记录与上一层的差异。分层带来的两个直接结论:
- 镜像复用层时只需拉取差异,传输速度快;
- 每一条
RUN指令都会产生新层,指令越多镜像越大——这就是合并指令、使用多阶段构建的原因。
二、写出规范的 Dockerfile
2.1 多阶段构建:把"构建环境"和"运行环境"分离
以 Go 应用为例,构建阶段需要编译器,运行阶段只需要可执行文件:
# 阶段一:构建
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o server ./cmd/server
# 阶段二:运行(更小的最终镜像)
FROM alpine:3.20
RUN adduser -D appuser
COPY --from=builder /app/server /usr/local/bin/server
USER appuser
EXPOSE 8080
ENTRYPOINT ["server"]
收益:最终镜像从几百 MB 瘦身到几十 MB,同时不携带源码和构建工具,攻击面更小。
2.2 指令顺序的艺术
Docker 构建会缓存每一层,把不易变化的指令放前面,易变化的放后面:
# 先拷贝依赖清单(变化频率低)→ 命中缓存
COPY go.mod go.sum ./
RUN go mod download
# 最后拷贝源码(每次构建都变化)
COPY . .
2.3 安全基线
- 使用官方镜像并锁定版本标签(
alpine:3.20而非alpine:latest); - 尽量以非 root 用户运行(
USER appuser); - 不把密钥写进镜像:用
--secret或运行时环境变量注入; - 定期扫描镜像漏洞(
docker scout/ Trivy)。
三、容器网络与数据持久化
3.1 三种网络模式
| 模式 | 特点 | 适用场景 | |------|------|----------| | bridge(默认) | 容器间通过自定义网桥互相访问 | 单机多容器互联 | | host | 直接使用宿主机网络 | 性能敏感、端口映射复杂时 | | none | 无网络 | 离线计算任务 |
自定义 bridge 网络让容器可用服务名互相访问,这是 Compose 服务发现的基石:
docker network create app-net
docker run --network app-net --name api myapp
docker run --network app-net --name nginx nginx # nginx 里可以直接 ping api
3.2 数据持久化的三种方式
- Volume(推荐):
docker volume create data,由 Docker 管理,跨容器共享,备份方便; - Bind Mount:
-v /host/path:/container/path,直接映射宿主机目录,适合开发热更新; - tmpfs:内存文件系统,适合存放敏感临时数据。
# 数据库容器使用 volume 持久化
docker run -d --name mysql8 \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8.0
四、Compose 编排:一套命令拉起整个栈
docker-compose.yml 用声明式配置描述多容器应用。一个典型的后端栈:
version: "3.8"
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
MYSQL_DATABASE: app
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
retries: 10
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
api:
build: .
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
environment:
DB_DSN: "root:${DB_ROOT_PASSWORD}@tcp(mysql:3306)/app"
REDIS_ADDR: "redis:6379"
ports:
- "8080:8080"
nginx:
image: nginx:1.26-alpine
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
ports:
- "80:80"
depends_on:
- api
volumes:
mysql-data:
redis-data:
几个值得注意的点:
depends_on配合 healthcheck:condition: service_healthy确保数据库就绪后再启动 API;- 环境变量用
${VAR}引用 .env 文件:敏感配置不写死在 yaml 里; - 服务名即 DNS 名:API 容器内直接连
mysql:3306。
常用命令:
docker compose up -d # 后台启动整套栈
docker compose logs -f api # 跟踪某个服务日志
docker compose down -v # 停止并删除数据卷(慎用!)
docker compose exec api sh # 进入容器调试
五、镜像仓库与 CI/CD 集成
5.1 命名与推送
docker tag myapp registry.example.com/team/myapp:v1.2.0
docker push registry.example.com/team/myapp:v1.2.0
5.2 流水线中的镜像策略
- 不可变标签:
v1.2.0、<sha>用于生产部署,不用latest做生产发布; - 构建缓存复用:CI 中开启 BuildKit(
DOCKER_BUILDKIT=1),多阶段缓存能大幅提速; - 制品签名:生产环境启用 Notary/cosign 校验镜像签名,防供应链攻击。
六、容器化常见坑
- 时区问题:基础镜像多为 UTC,
ENV TZ=Asia/Shanghai并在需要时安装 tzdata; - PID 1 与僵尸进程:容器内 PID 1 需要处理信号与回收子进程,Go 应用直接作 PID 1 通常没问题,脚本类应用建议用 tini;
- 日志不能写文件:容器日志应输出到 stdout/stderr,由 Docker 收集(
docker logs、日志驱动); - 健康检查缺失:没有 healthcheck 的容器无法被编排系统准确判断就绪状态;
- 磁盘爆满:定期清理
<none>悬空镜像与构建缓存:docker system prune -af。
结语
Docker 的核心价值不是"跑起来",而是把交付物变成标准化的、可审计的制品。写好 Dockerfile、用好 Compose、接入可靠的镜像仓库,你的应用就获得了"一次构建,处处运行"的能力。建议从把现有服务容器化开始,逐步建立镜像规范与 CI 流水线。
读完了?给作者一点支持
🔗 相关文章
常见错误及解决方案
Docker 在开发和生产环境中广泛应用,它为开发人员提供了快速构建、测试、部署和扩展应用程序的能力。然而,在使用 Docker 的过程中,可能会遇到各种各样的问题,如容器启动失败、镜像构建失败等。了解这些常见错误及其解决方案对于提高开发效率、减少故障恢复时间至关重要。 本文将列出 Docker 中常见的错误及其解决方案,并提供详细的示例和方法,帮助开发者更好地调试和解决问题。 1. 容器启动失败
调试与日志分析
Docker 是一个强大的容器化平台,能够帮助开发者构建、发布和运行应用程序。然而,在实际使用过程中,容器可能会遇到一些问题,导致容器无法正常启动或运行。为了快速定位并解决问题,Docker 提供了多个调试与日志分析工具,如 docker logs、docker inspect 等命令。通过合理使用这些工具,我们能够高效地进行容器的故障排除。 本文将详细介绍 Docker 中常用的调试与日志分析工
容器与虚拟机的区别
随着云计算和微服务架构的兴起,容器化技术越来越受到开发者和运维工程师的青睐。Docker是当前最流行的容器平台,它提供了一种轻量级、高效且灵活的方式来部署、运行和管理应用程序。然而,在理解Docker的优势之前,我们需要先了解容器与传统虚拟机(VM)的根本区别。本文将深入对比容器和虚拟机,帮助你了解容器如何在性能、效率、资源利用等方面展现其优势。 一、虚拟机(VM)的基本概念 虚拟机(VM)是通
评论区加载中…