3、Docker基础:Docker的安装与配置、Dockerfile编写、镜像构建与仓库管理

聊到云原生CI/CD,Docker绝对是绕不开的基石。说实话,没有容器化,后面那些Kubernetes、微服务什么的,玩起来都会很别扭。我最早接触Docker是在2016年,那时候还在用笨重的虚拟机做环境隔离,每次部署都像开盲盒——开发环境跑得好好的,一到测试环境就崩。后来切到Docker,整个世界清净了。

这一章,咱们就踏踏实实把Docker的基础打牢。从安装配置开始,到写Dockerfile,再到构建镜像和推送到仓库。嗯,这些都是你后面搭建流水线时,每天都要打交道的活儿。

3.1 Docker的安装与配置

安装Docker其实没什么难度,但不同操作系统有些小差异。我个人习惯在Linux上玩,毕竟生产环境基本都是Linux。

3.1.1 在Ubuntu上安装

官方推荐用apt安装,但别直接用系统自带的旧版本。我曾经踩过这个坑——系统源里的Docker版本太老,导致后面很多新特性用不了。

# 卸载旧版本
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引擎
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

装完之后,记得把当前用户加到docker组里,不然每次都得sudo,烦得很。

sudo usermod -aG docker $USER
newgrp docker
注意: 加完组要重新登录才能生效。我曾经以为newgrp就够了,结果换个终端窗口又报权限错误,折腾了半天才发现是没重新登录。

3.1.2 配置镜像加速器

国内拉取镜像,你懂的,速度感人。我建议第一时间配置镜像加速器。这里以阿里云为例,你也可以用腾讯云、华为云的。

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://你的加速器地址.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

配置完跑一下 docker info,看看Registry Mirrors那行有没有生效。嗯,这一步很多人会忘,结果后面拉镜像慢到怀疑人生。

3.2 Dockerfile编写

Dockerfile说白了就是一份镜像的「菜谱」。你告诉Docker要用什么基础镜像、装什么软件、复制什么文件、启动什么命令。我见过不少新手把Dockerfile写得又大又乱,一个镜像几个G,部署起来慢得要命。

3.2.1 核心指令

指令 作用 使用频率
FROM 指定基础镜像 必用
RUN 执行命令(构建时) 高频
COPY 复制文件到镜像 高频
ADD 复制文件(支持URL和解压) 中频
CMD 容器启动时的默认命令 高频
ENTRYPOINT 容器启动入口(不可覆盖) 中频
ENV 设置环境变量 高频
WORKDIR 设置工作目录 高频
EXPOSE 声明容器端口 中频

3.2.2 一个实战例子

假设我们要打包一个Node.js应用。你想想看,如果直接把整个项目复制进去,然后npm install,那镜像里会塞满node_modules,体积大得吓人。我建议用多阶段构建来瘦身。

# 第一阶段:构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# 第二阶段:运行
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]
核心思路: 第一阶段装依赖、编译,第二阶段只拿产物。这样最终镜像只有运行时需要的文件,体积能小一半以上。我在一个项目中,用这个方法把镜像从1.2GB砍到了180MB。

3.2.3 编写技巧与避坑

  • 层数要少: 每个RUN、COPY都会产生一层。我习惯把多个命令用 && 串起来,减少层数。
  • 注意缓存: Docker会缓存每一层。把变化少的指令放前面,比如先COPY package.json,再COPY源码。这样改代码时不用重新装依赖。
  • 别用latest: 基础镜像指定具体版本,比如 node:18-alpine 而不是 node:latest。否则哪天latest更新了,你的构建可能就炸了。
小技巧: 调试Dockerfile时,可以只构建到某一步:docker build --target builder -t myapp:build .。这样能快速定位问题,不用每次都从头构建。

3.3 镜像构建与仓库管理

Dockerfile写好了,下一步就是构建镜像。构建本身很简单,但怎么管理镜像、怎么打标签、怎么推送到仓库,这里面有不少门道。

3.3.1 构建镜像

# 基本构建
docker build -t myapp:v1.0 .

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

# 带构建参数
docker build --build-arg NODE_ENV=production -t myapp:latest .

构建的时候,. 表示上下文路径。注意,Docker会把上下文里所有文件都发给守护进程。如果你的项目里有node_modules、.git这些大目录,记得写个 .dockerignore 文件。

# .dockerignore
node_modules
.git
*.log
dist
.cache

我曾经见过一个同事没写.dockerignore,构建上下文有2个多G,每次构建都要等半天。后来加上忽略文件,构建时间从5分钟降到了30秒。

3.3.2 镜像标签与版本管理

标签这东西,看似简单,但团队里经常因为标签不规范出问题。我建议用语义化版本号,再加一些元信息。

标签格式 示例 适用场景
语义版本 v1.2.3 正式发布
分支+提交 feature-abc123 开发测试
构建编号 build-456 CI流水线
latest latest 仅用于开发环境
# 打标签
docker tag myapp:v1.0 registry.example.com/myapp:v1.0
docker tag myapp:v1.0 registry.example.com/myapp:latest

# 查看本地镜像
docker images
注意: 别在生产环境用latest标签。你想想看,哪天有人不小心push了一个新镜像覆盖了latest,所有拉取latest的服务都会跟着变,后果不堪设想。我曾经在一个项目里吃过这个亏,凌晨三点被叫起来回滚。

3.3.3 镜像仓库管理

镜像仓库分两种:公有和私有。公有仓库最常用的是Docker Hub,但国内访问慢。私有仓库可以用Harbor、Nexus,或者各大云厂商的镜像服务。

推送到仓库的流程很简单:

# 登录
docker login registry.example.com -u username -p password

# 推送
docker push registry.example.com/myapp:v1.0

# 拉取
docker pull registry.example.com/myapp:v1.0

在CI/CD流水线里,我一般会把镜像推送到私有仓库,然后部署时从私有仓库拉取。这样有几个好处:一是速度快,内网传输;二是安全,镜像不会暴露到公网;三是可以配合镜像扫描工具做安全检测。

3.3.4 镜像清理与维护

时间一长,本地会堆积大量无用的镜像和容器。我习惯定期清理:

# 清理所有停止的容器
docker container prune

# 清理所有未使用的镜像
docker image prune -a

# 一键清理(慎用)
docker system prune -a --volumes
建议: 在CI流水线里,每次构建完都清理一下构建机上的旧镜像。否则磁盘很快就会被撑爆。我一般保留最近5个版本的镜像,其他的自动删除。

好了,Docker基础这部分就聊到这儿。安装配置、Dockerfile编写、镜像构建与仓库管理,这三块是你在云原生CI/CD路上必须掌握的硬功夫。下一章咱们会把这些知识串起来,搭建一条完整的流水线。到时候你会发现,今天打下的基础,全都能用上。