4. Docker容器技术:从安装到生产实践

Docker 这东西,说白了就是把你的应用连同环境一起打包。我刚开始接触时也觉得不就是个轻量级虚拟机吗?后来线上踩过几次坑才明白——容器和虚拟机的思维模式完全不同。这一章,我把这几年在生产环境折腾 Docker 的经验梳理出来,从安装到镜像管理、Dockerfile 编写、私有仓库搭建,一条线讲清楚。

4.1 Docker安装与架构

安装 Docker 其实不难,但不同发行版有细微差别。我个人习惯用官方脚本,干净利落:

# Ubuntu / Debian
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# CentOS / RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io

装完后记得把当前用户加入 docker 组,不然每次都要 sudo:

sudo usermod -aG docker $USER
newgrp docker
注意: 加入 docker 组相当于给了 root 权限。生产环境要严格控制谁有 docker 组权限,我曾经见过因为容器挂载了宿主机目录导致被提权的案例。

Docker 的架构其实就三块:客户端、守护进程、镜像仓库。客户端发指令,守护进程干活,仓库存镜像。我画了张图帮你理解:

Docker 核心架构 Docker Client docker build / run / push Docker Host Docker Daemon containerd / runc Container 1 Container 2 Container N Local Images Cache Registry Docker Hub / Private 图例说明 客户端 守护进程 容器实例 本地镜像缓存

4.2 镜像与容器生命周期管理

镜像和容器的关系,你可以理解成程序和进程。镜像是静态的只读模板,容器是运行中的实例。我经常看到新手把镜像当容器用,直接在镜像里改东西——这是个大坑。

镜像操作

拉取、查看、删除,三板斧:

# 拉取镜像
docker pull nginx:1.25-alpine

# 列出本地镜像
docker images

# 删除镜像(先删依赖的容器)
docker rmi nginx:1.25-alpine

# 查看镜像分层
docker history nginx:1.25-alpine
小技巧: 我习惯用 alpine 版本,镜像小、漏洞少。比如 nginx:alpine 只有 20MB 出头,而完整版要 180MB。生产环境能省则省。

容器生命周期

容器的状态流转其实就几个:Created → Running → Paused → Stopped → Deleted。我整理了一张表:

命令 作用 常用参数
docker run 创建并启动容器 -d 后台运行,-p 端口映射,-v 挂载卷
docker start/stop 启动/停止已有容器 --time 等待时间(默认10秒)
docker pause/unpause 暂停/恢复进程 使用 cgroups freezer
docker rm 删除已停止的容器 -f 强制删除运行中的
docker exec 进入运行中的容器 -it 交互模式

举个例子,启动一个 Nginx 并挂载配置:

docker run -d \
  --name my-nginx \
  -p 8080:80 \
  -v /data/nginx/html:/usr/share/nginx/html:ro \
  -v /data/nginx/conf:/etc/nginx/conf.d:ro \
  --restart always \
  nginx:1.25-alpine
踩坑提醒: 我曾经在生产环境用 --restart=always 配合 -d,结果容器因为配置错误不断重启,日志刷爆了磁盘。建议先用 --rm 调试,确认没问题再改成 --restart。

4.3 Dockerfile编写最佳实践

Dockerfile 写得好不好,直接影响镜像大小和构建速度。我总结了几个核心原则:

  • 选择合适的基础镜像:能用 alpine 就别用 ubuntu,能指定版本就别用 latest
  • 减少层数:每个 RUN 指令都会产生一层,能用 && 合并就合并
  • 利用构建缓存:把不常变的指令放前面,经常变的放后面
  • 多阶段构建:编译环境和运行环境分离

看一个实际例子,构建一个 Go 应用:

# 第一阶段:编译
FROM golang:1.21-alpine 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.19
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/myapp /usr/local/bin/
EXPOSE 8080
USER nobody
ENTRYPOINT ["myapp"]

这个 Dockerfile 有几个好处:最终镜像只有 15MB 左右,没有 Go 编译工具链;用 nobody 用户运行,安全;时区和证书都配好了。

多阶段构建的好处: 我有个 Java 项目,单阶段构建镜像 800MB,改成多阶段后只有 180MB。部署快了,传输也快了,安全风险面也小了。

Dockerfile 指令速查

指令 用途 注意事项
FROM 指定基础镜像 尽量用官方镜像,指定精确版本
RUN 执行命令 用 && 合并,清理缓存
COPY 复制文件 优先于 ADD,ADD 有自动解压行为
CMD 默认启动命令 可以被 docker run 参数覆盖
ENTRYPOINT 入口点 与 CMD 配合使用更灵活
EXPOSE 声明端口 只是文档作用,实际映射靠 -p
USER 指定运行用户 不要用 root,创建专用用户

4.4 私有仓库搭建

生产环境不可能每次都从 Docker Hub 拉镜像,太慢了,也不安全。私有仓库是必需品。我推荐用 Harbor,功能全、有 Web UI、支持镜像复制和漏洞扫描。

快速部署 Harbor

# 下载离线安装包
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzf harbor-offline-installer-v2.10.0.tgz
cd harbor

# 复制配置模板
cp harbor.yml.tmpl harbor.yml

# 修改配置(关键项)
# hostname: registry.yourcompany.com
# harbor_admin_password: YourStrongPassword
# 如果使用 HTTP,需要配置 insecure-registries

# 安装
sudo ./install.sh

安装完成后,访问 http://registry.yourcompany.com 就能看到 Web 界面。默认管理员账号 admin,密码在 harbor.yml 里配置。

推送和拉取镜像

# 登录
docker login registry.yourcompany.com

# 打标签
docker tag nginx:1.25-alpine registry.yourcompany.com/library/nginx:1.25-alpine

# 推送
docker push registry.yourcompany.com/library/nginx:1.25-alpine

# 拉取
docker pull registry.yourcompany.com/library/nginx:1.25-alpine
镜像命名规范: 我建议用 /: 的格式。比如 infra/nginx:1.25、app/user-service:v2.1.0。项目多了你就知道规范有多重要。

配置 HTTPS 和清理策略

生产环境必须配 HTTPS,不然密码明文传输。可以用 Let's Encrypt 免费证书:

# 在 harbor.yml 中配置
certificate: /data/cert/server.crt
private_key: /data/cert/server.key

# 重启 Harbor
sudo docker-compose down
sudo ./install.sh

镜像多了会占磁盘,我习惯设置自动清理:

  • 保留最近 30 天的版本
  • 每个项目最多保留 10 个标签
  • 未被任何容器引用的镜像自动删除
磁盘告警: 我有一次 Harbor 磁盘写满,导致所有 CI/CD 构建失败。后来加了定时清理策略和磁盘告警,再没出过问题。Harbor 的垃圾回收(GC)也要定期执行,不然会有 dangling 镜像占空间。

嗯,Docker 这块内容不少,但核心就这些:装好环境、管好镜像和容器、写好 Dockerfile、搭好私有仓库。把这些基础打牢,后面讲编排和监控才能站得住脚。