第2章 容器化基础:Docker安装、Dockerfile编写、镜像构建与优化、Docker Compose
好,咱们进入容器化这一章。说实话,Docker 这东西,现在已经是云原生开发的标配了。你出去面试,要是说自己没用过 Docker,面试官可能都会愣一下。我最早接触 Docker 是在 2016 年,那时候公司还在用虚拟机部署,每次发版都要折腾半天。后来切到 Docker,整个团队的幸福感直线上升。
2.1 Docker 安装与环境配置
安装 Docker 其实不复杂,但不同系统有些小坑。我个人的习惯是,不管用什么系统,都优先用官方推荐的安装方式。
Linux 环境安装
以 Ubuntu 为例,官方推荐用 apt 仓库安装。别去官网下载 .deb 包手动装,那样后续更新很麻烦。
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
# 设置仓库
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 设置稳定版仓库
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER,然后退出重新登录。
macOS 与 Windows
这两个系统就简单多了。直接下载 Docker Desktop 安装就行。不过我要提醒一句,Docker Desktop 在 macOS 上挺吃资源的,尤其是老款 Intel 芯片的 Mac。我有个同事用 2017 款的 MacBook Pro,开了 Docker 之后风扇就没停过。
安装完跑一下 hello-world 验证:
docker run hello-world
看到 "Hello from Docker!" 就说明搞定了。
2.2 Dockerfile 编写实战
Dockerfile 说白了就是一份构建说明书。你告诉 Docker 怎么一步步把你的应用打包成镜像。我见过很多新手把 Dockerfile 写成了一锅粥,什么都在里面装,最后镜像体积大得吓人。
基础语法
先看一个最简单的例子,一个 Node.js 应用的 Dockerfile:
# 指定基础镜像
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
# 安装依赖
RUN npm install --production
# 复制源代码
COPY . .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "app.js"]
这里有几个关键点:
- FROM:基础镜像的选择很重要。alpine 版本体积小,但有些包可能不兼容。我一般优先用 slim 版本,平衡体积和兼容性。
- WORKDIR:设置工作目录,后续的 COPY、RUN、CMD 都会在这个目录下执行。
- COPY:注意顺序。把不常变动的文件(如 package.json)放在前面,利用 Docker 的缓存机制。源代码放在后面。
- RUN:每个 RUN 指令都会创建一个新的镜像层。所以尽量合并命令,减少层数。
多阶段构建
这个技巧我强烈推荐。尤其是编译型语言,比如 Go、Java、C++。多阶段构建可以让你在第一个阶段编译,第二个阶段只保留运行时的产物,体积能小好几倍。
# 第一阶段:编译
FROM golang:1.20 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .
# 第二阶段:运行
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
你看,最终镜像只有 alpine 加上一个二进制文件,体积从 1GB 降到了 20MB 左右。我在项目中用这个技巧,把 CI/CD 的构建时间从 5 分钟降到了 40 秒。
2.3 镜像构建与优化
镜像构建不只是写个 Dockerfile 然后 docker build 就完事了。优化镜像体积和构建速度,是每个云原生工程师的必修课。
构建命令
# 基本构建
docker build -t myapp:1.0 .
# 指定 Dockerfile 路径
docker build -f ./deploy/Dockerfile -t myapp:1.0 .
# 带构建参数
docker build --build-arg VERSION=1.0 -t myapp:1.0 .
镜像优化技巧
| 优化项 | 说明 | 效果 |
|---|---|---|
| 选择合适的基础镜像 | 优先用 alpine、slim 版本 | 体积减少 50%-80% |
| 合并 RUN 指令 | 用 && 连接多个命令 | 减少镜像层数 |
| 清理缓存 | apt clean、npm cache clean | 减少无用文件 |
| 多阶段构建 | 分离编译和运行环境 | 体积减少 90% 以上 |
| 利用 .dockerignore | 排除不需要的文件 | 加快构建速度 |
我的经验:有一次线上服务频繁 OOM,排查了半天发现是镜像里装了个调试工具包,占了几百兆内存。从那以后,我要求团队所有生产镜像必须用多阶段构建,运行时镜像里只保留最小依赖。
.dockerignore 文件
这个文件的作用和 .gitignore 类似。把 node_modules、.git、日志文件这些不需要的东西排除掉,构建速度能快不少。
node_modules
.git
*.log
.env
dist
.cache
2.4 Docker Compose 编排
单机环境下,Docker Compose 是管理多容器应用的神器。你想想看,一个微服务项目可能包含前端、后端、数据库、缓存、消息队列...要是每个都手动 docker run,那不得累死。
docker-compose.yml 示例
version: '3.8'
services:
web:
build: ./web
ports:
- "3000:3000"
depends_on:
- api
environment:
- API_URL=http://api:8080
api:
build: ./api
ports:
- "8080:8080"
depends_on:
- db
- redis
environment:
- DB_HOST=db
- REDIS_HOST=redis
db:
image: postgres:15-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
常用命令
# 启动所有服务
docker compose up -d
# 查看日志
docker compose logs -f
# 重新构建并启动
docker compose up -d --build
# 停止并删除容器
docker compose down
# 查看服务状态
docker compose ps
docker compose up 前台运行,方便看日志。生产环境用 docker compose up -d 后台运行。另外,docker compose down -v 会删除数据卷,小心使用!
Compose 文件的最佳实践
- 环境变量分离:敏感信息用 .env 文件管理,不要硬编码在 yml 里。
- 健康检查:给关键服务加上 healthcheck,确保依赖服务就绪后再启动。
- 资源限制:用
deploy.resources限制 CPU 和内存,防止某个服务把资源吃光。 - 网络隔离:默认 Compose 会创建一个网络,服务之间用服务名通信。也可以自定义网络,实现更精细的隔离。
嗯,容器化这块内容其实挺多的。但掌握了 Dockerfile 编写、镜像优化和 Compose 编排,你已经能应付大部分日常工作了。下一章我们会聊 Kubernetes,那才是云原生的重头戏。
对了,有个事我得提醒你。Docker 虽然好用,但别什么应用都往容器里塞。有状态的服务(比如数据库)在容器里跑,数据持久化和网络性能都是挑战。我建议数据库这类服务还是用云服务商提供的托管服务,省心很多。