2、容器化基础:Docker核心概念、Dockerfile编写、镜像构建与优化

聊到云原生中间件的高可用,容器化是绕不开的第一步。说白了,Docker 就是那个让应用“打包即运行”的神器。我刚开始接触时也觉得它就是个轻量级虚拟机,后来踩了坑才明白——它跟虚拟机完全是两码事。

2.1 Docker 核心概念:镜像、容器、仓库

这三个概念,你搞懂了,Docker 就入门了一半。

  • 镜像(Image):一个只读的模板。里面装了操作系统、依赖库、应用代码。你可以把它理解成“安装包”。
  • 容器(Container):镜像跑起来后的实例。有读写层,可以启动、停止、删除。说白了,就是“运行中的程序”。
  • 仓库(Registry):存镜像的地方。Docker Hub 是公共的,企业内部一般用 Harbor 或 Nexus。

关键区别:镜像是一个静态文件,容器是一个动态进程。我见过不少新手把容器当虚拟机用,进去装软件、改配置,结果一重启全丢了。记住,容器是“用完即走”的,数据要挂载卷。

2.2 Dockerfile 编写:从零到一

Dockerfile 就是构建镜像的“配方”。我习惯把它写得像一份清晰的说明书,每一行都有明确目的。

2.2.1 基础指令

指令 作用 示例
FROM 指定基础镜像 FROM openjdk:11-jre-slim
WORKDIR 设置工作目录 WORKDIR /app
COPY 复制本地文件到镜像 COPY target/app.jar .
RUN 构建时执行命令 RUN apt-get update && apt-get install -y curl
CMD 容器启动时的默认命令 CMD ["java", "-jar", "app.jar"]
EXPOSE 声明容器监听端口 EXPOSE 8080

2.2.2 实战:一个 Java 应用的 Dockerfile

# 第一阶段:构建
FROM maven:3.8.4-openjdk-11 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests

# 第二阶段:运行
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

我的习惯:用多阶段构建。第一阶段装 Maven、编译代码,第二阶段只留 JRE 和 jar 包。这样最终镜像能小一半以上。我在项目中遇到过,一个 800MB 的镜像优化后只有 200MB,部署速度快了不止一点。

2.3 镜像构建与优化:让镜像“瘦”下来

镜像太大,拉取慢、启动慢、磁盘占用高。你想想看,生产环境几百个 Pod,每个都 1GB,那得多浪费资源。

2.3.1 优化原则

  • 选择最小基础镜像:alpine、slim 版本优先。比如 openjdk:11-jre-slimopenjdk:11 小 300MB。
  • 减少层数:每个 RUN、COPY 都会产生一层。用 && 合并命令。
  • 清理缓存:安装完依赖后,顺手删掉 /var/cache/apt/tmp 等临时文件。
  • 利用 .dockerignore:排除本地无关文件,比如 .gitnode_modulestarget

2.3.2 构建命令与技巧

# 基础构建
docker build -t myapp:1.0 .

# 指定 Dockerfile 路径
docker build -f docker/Dockerfile.prod -t myapp:prod .

# 利用构建缓存(关键)
# 把变化少的指令放前面,比如 COPY pom.xml 放 COPY src 前面

我曾经踩过的坑:构建时没有利用缓存,每次改一行代码都要重新下载所有 Maven 依赖。后来我把 pom.xml 单独 COPY 并执行 mvn dependency:go-offline,这样只要 pom.xml 不变,依赖层就能复用。构建时间从 5 分钟降到了 30 秒。

2.3.3 镜像大小对比

优化阶段 镜像大小 说明
未优化 ~800MB 使用完整 JDK,单阶段构建
使用 slim 基础镜像 ~400MB 换成 jre-slim
多阶段构建 ~200MB 分离构建环境和运行环境
进一步清理 ~150MB 删除临时文件,使用 alpine

2.4 容器化最佳实践

嗯,这里要注意几个点,都是我在生产环境里摸爬滚打总结出来的。

  • 一个容器一个进程:别在容器里跑 systemd 或 supervisord。需要多个进程就用多个容器,配合 docker-compose 或 Kubernetes。
  • 日志输出到 stdout/stderr:Docker 会自动收集标准输出。别写日志文件,否则日志轮转、磁盘爆满都是麻烦事。
  • 健康检查:加上 HEALTHCHECK 指令,让 Docker 知道你的应用是否活着。
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

核心总结:容器化的本质不是“把虚拟机变小”,而是“把应用及其依赖打包成一个不可变单元”。镜像要小、构建要快、运行要稳。做到这三点,你的中间件容器化就成功了一大半。

下一章我们会聊 Docker Compose 和网络存储,到时候再深入讲讲多容器编排的那些事儿。