Docker 镜像管理

核心知识点

1. 镜像基础概念

1.1 镜像结构

Docker 镜像采用分层存储结构,每一层都是只读的:

┌─────────────────────────────────────┐
│        可写容器层                │
├─────────────────────────────────────┤
│        镜像层 4 (应用层)        │
├─────────────────────────────────────┤
│        镜像层 3 (依赖层)        │
├─────────────────────────────────────┤
│        镜像层 2 (运行时层)       │
├─────────────────────────────────────┤
│        镜像层 1 (基础层)        │
├─────────────────────────────────────┤
│        镜像层 0 (Bootfs)       │
└─────────────────────────────────────┘

1.2 镜像标签

镜像标签用于标识镜像的不同版本:

# 格式:仓库名:标签
nginx:latest          # 最新版本
nginx:1.21           # 指定主版本
nginx:1.21.6         # 指定完整版本
nginx:alpine         # 使用 Alpine 基础镜像
myregistry.com/myapp:v1.0  # 私有仓库镜像

2. 镜像搜索与拉取

2.1 搜索镜像

# 搜索 Nginx 镜像
docker search nginx

# 搜索并限制结果数量
docker search --limit 5 nginx

# 搜索官方镜像
docker search --filter is-official=true nginx

# 搜索自动化构建的镜像
docker search --filter is-automated=true nginx

# 搜索特定星数的镜像
docker search --filter stars=100 nginx

2.2 拉取镜像

# 拉取最新版本
docker pull nginx

# 拉取指定版本
docker pull nginx:1.21.6

# 拉取所有标签
docker pull --all-tags nginx

# 拉取私有仓库镜像
docker pull myregistry.com/myapp:v1.0

3. 镜像查看与管理

3.1 查看镜像列表

# 查看所有镜像
docker images

# 查看镜像摘要
docker images --digests

# 查看镜像详细信息
docker image inspect nginx:latest

# 格式化输出
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

3.2 镜像历史与层级

# 查看镜像构建历史
docker history nginx:latest

# 查看镜像层级
docker image inspect nginx:latest | jq '.[0].RootFS.Layers'

4. 镜像构建

4.1 Dockerfile 基础

Dockerfile 是用于构建 Docker 镜像的文本文件:

# 基础镜像
FROM ubuntu:20.04

# 维护者信息
LABEL maintainer="your-email@example.com"

# 设置工作目录
WORKDIR /app

# 复制文件
COPY . /app

# 安装依赖
RUN apt-get update && \
    apt-get install -y python3 python3-pip && \
    pip3 install flask

# 暴露端口
EXPOSE 5000

# 设置环境变量
ENV FLASK_APP=app.py
ENV FLASK_ENV=production

# 运行命令
CMD ["python3", "app.py"]

4.2 常用 Dockerfile 指令

# FROM: 指定基础镜像
FROM ubuntu:20.04

# LABEL: 添加元数据
LABEL version="1.0" \
      description="My custom image"

# RUN: 执行命令
RUN apt-get update && apt-get install -y nginx

# CMD: 容器启动时执行的命令
CMD ["nginx", "-g", "daemon off;"]

# ENTRYPOINT: 容器启动入口点
ENTRYPOINT ["/entrypoint.sh"]

# COPY: 复制文件到镜像
COPY index.html /usr/share/nginx/html/

# ADD: 复制文件并支持自动解压
ADD app.tar.gz /app/

# ENV: 设置环境变量
ENV APP_ENV=production

# ARG: 构建参数
ARG VERSION=1.0
ENV APP_VERSION=$VERSION

# EXPOSE: 声明暴露端口
EXPOSE 80 443

# VOLUME: 创建挂载点
VOLUME ["/data"]

# USER: 指定运行用户
USER nginx

# WORKDIR: 设置工作目录
WORKDIR /app

# HEALTHCHECK: 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost/ || exit 1

# ONBUILD: 触发器指令
ONBUILD COPY . /app

5. 镜像优化

5.1 多阶段构建

多阶段构建可以减小最终镜像大小:

# 构建阶段
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

5.2 镜像层优化

# 不好的写法:创建多个层
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean

# 好的写法:合并为一个层
RUN apt-get update && \
    apt-get install -y nginx && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

5.3 使用 Alpine 基础镜像

# 使用 Ubuntu 基础镜像(约 70MB)
FROM ubuntu:20.04

# 使用 Alpine 基础镜像(约 5MB)
FROM alpine:3.14

6. 镜像导出与导入

6.1 导出镜像

# 导出镜像为 tar 文件
docker save -o myapp.tar myapp:v1.0

# 导出多个镜像
docker save -o images.tar nginx:latest redis:latest

# 使用 gzip 压缩
docker save myapp:v1.0 | gzip > myapp.tar.gz

6.2 导入镜像

# 从 tar 文件导入
docker load -i myapp.tar

# 从压缩文件导入
docker load < myapp.tar.gz

# 从标准输入导入
docker load < images.tar

7. 镜像删除

7.1 删除单个镜像

# 删除指定镜像
docker rmi nginx:latest

# 强制删除(即使有容器在使用)
docker rmi -f nginx:latest

# 删除多个镜像
docker rmi nginx:latest redis:latest

7.2 批量删除镜像

# 删除所有未使用的镜像
docker image prune

# 删除所有未使用的镜像(包括未标记的)
docker image prune -a

# 删除所有悬空镜像
docker rmi $(docker images -f "dangling=true" -q)

# 按模式删除镜像
docker rmi $(docker images | grep "myapp" | awk '{print $3}')

实用案例分析

案例 1:构建 Node.js 应用镜像

场景描述

将一个简单的 Node.js Web 应用打包为 Docker 镜像。

项目结构

my-node-app/
├── app.js
├── package.json
└── Dockerfile

操作步骤

# 1. 创建项目目录
mkdir ~/my-node-app
cd ~/my-node-app

# 2. 创建 package.json
cat > package.json << 'EOF'
{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "Simple Node.js app",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.0"
  }
}
EOF

# 3. 创建 app.js
cat > app.js << 'EOF'
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
    res.send('Hello from Docker!');
});

app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});
EOF

# 4. 创建 Dockerfile
cat > Dockerfile << 'EOF'
FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install --production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
EOF

# 5. 构建镜像
docker build -t my-node-app:v1.0 .

# 6. 查看构建的镜像
docker images my-node-app

# 7. 运行容器
docker run -d -p 3000:3000 --name my-node-app my-node-app:v1.0

# 8. 测试访问
curl http://localhost:3000

# 9. 查看容器日志
docker logs my-node-app

# 10. 清理
docker stop my-node-app
docker rm my-node-app

案例 2:多阶段构建优化

场景描述

使用多阶段构建优化 React 应用的镜像大小。

操作步骤

# 1. 创建 React 应用
npx create-react-app my-react-app
cd my-react-app

# 2. 创建多阶段 Dockerfile
cat > Dockerfile << 'EOF'
# 构建阶段
FROM node:16-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

# 生产阶段
FROM nginx:alpine

# 复制构建产物
COPY --from=builder /app/build /usr/share/nginx/html

# 复制自定义 nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
EOF

# 3. 创建 nginx 配置
cat > nginx.conf << 'EOF'
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
EOF

# 4. 构建镜像
docker build -t my-react-app:v1.0 .

# 5. 查看镜像大小
docker images my-react-app

# 6. 运行容器
docker run -d -p 80:80 --name my-react-app my-react-app:v1.0

# 7. 测试访问
curl http://localhost

# 8. 清理
docker stop my-react-app
docker rm my-react-app

案例 3:镜像版本管理

场景描述

管理应用的不同版本镜像,支持版本回滚。

操作步骤

# 1. 构建不同版本的镜像
docker build -t myapp:1.0.0 .
docker build -t myapp:1.1.0 .
docker build -t myapp:latest .

# 2. 为镜像添加标签
docker tag myapp:latest myapp:stable
docker tag myapp:latest myapp:production

# 3. 查看所有标签
docker images myapp

# 4. 导出镜像备份
docker save -o myapp-backup.tar myapp:1.0.0 myapp:1.1.0 myapp:stable

# 5. 模拟镜像丢失
docker rmi myapp:1.0.0 myapp:1.1.0 myapp:stable

# 6. 从备份恢复
docker load -i myapp-backup.tar

# 7. 验证恢复
docker images myapp

# 8. 清理
rm myapp-backup.tar

最佳实践

  1. 使用具体版本标签:避免使用 latest 标签,确保构建的可重复性。

  2. 优化镜像大小

    • 使用 Alpine 基础镜像
    • 合并 RUN 指令
    • 使用多阶段构建
    • 清理不必要的文件
  3. 合理利用缓存:将变化少的指令放在前面,提高构建速度。

  4. 使用 .dockerignore:排除不必要的文件,减小构建上下文大小。

# .dockerignore 文件示例
node_modules
npm-debug.log
.git
.env
*.md
  1. 安全扫描:定期扫描镜像漏洞。
# 使用 Docker Scout 扫描
docker scout quickview myapp:v1.0

# 使用 Trivy 扫描
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    aquasec/trivy image myapp:v1.0

总结

本教程详细介绍了 Docker 镜像的管理方法,包括搜索、拉取、构建、查看、删除等操作。通过实际案例,我们学习了如何构建 Node.js 和 React 应用镜像、使用多阶段构建优化镜像大小以及管理镜像版本。掌握这些技能后,可以高效地管理和分发 Docker 镜像。

« 上一篇 Docker 基础 下一篇 » Docker 容器管理