Docker 实战教程(7) | 镜像管理和仓库操作

前边篇章我们已经介绍了

  1. Docker 基础概念和安装
  2. Docker 常用命令实践
  3. Docker 网络机制详解
  4. Docker 数据卷和挂载
  5. Dockerfile 编写和镜像构建
  6. Docker Compose 多容器编排

本篇为系列最后一章,介绍 Docker 的镜像管理和仓库操作。

本教程侧重于命令实践和理解,提供可在本地环境测试的实例,每章结束都有总结要点。

7.1 Docker 镜像管理基础

我们之前介绍过一期自建镜像站的若干方案: Docker 管理 | 代理配置、内网共享和 Harbor 部署

本篇侧重展开介绍 docker 镜像相关的概念,以及原生支持的镜像站方案。

镜像的生命周期

# 镜像生命周期概览
构建 -> 标记 -> 推送 -> 拉取 -> 运行 -> 删除

# 查看本地镜像
docker images
docker image ls

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

# 查看镜像历史
docker image history nginx:alpine

# 查看镜像大小
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

镜像标记和命名

# 镜像命名规范
[registry_host[:port]/]username/repository[:tag]

# 示例
docker.io/library/nginx:latest          # 官方镜像,前缀 docker.io/library/ 可以省略
docker.io/username/myapp:v1.0           # 用户镜像,前缀 docker.io/ 可以省略
registry.example.com/team/app:latest    # 私有仓库镜像

# 为镜像添加标记
docker tag nginx:alpine myregistry.com/nginx:v1.0
docker tag myapp:latest myapp:v1.0
docker tag myapp:latest myapp:production

# 查看同一镜像的多个标记,需匹配到 username/repository
docker images myapp

镜像清理和优化

# 删除单个镜像
docker rmi nginx:alpine
docker image rm nginx:alpine

# 删除多个镜像
docker rmi $(docker images -q ubuntu)

# 删除悬空镜像(dangling images)
docker image prune

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

# 删除指定时间前的镜像
docker image prune -a --filter "until=24h"

# 查看镜像占用空间
docker system df

# 全面清理系统
docker system prune -a --volumes

7.2 Docker Hub 操作

Docker Hub 基本操作

# 登录 Docker Hub
docker login # -u username -p password

# 登录指定仓库
docker login registry.example.com

# 查看登录状态
cat ~/.docker/config.json

注意,这里用户名的大小写敏感。一般地,建议在设置页面创建 token,通过 token 登录。

# 搜索镜像
docker search nginx
docker search --limit 5 --filter stars=100 nginx

# 拉取镜像
docker pull nginx
docker pull nginx:1.21-alpine
docker pull ubuntu:20.04

# 推送镜像到 Docker Hub
# 1. 构建镜像
docker build -t username/myapp:v1.0 .

# 2. 推送镜像
docker push username/myapp:v1.0

# 登出
docker logout

上边命令中的 push 镜像,以及 pull 私人镜像需要登录。

实践:发布自己的镜像

# 创建示例应用
mkdir -p /tmp/docker-tutorial/my-web-app
cd /tmp/docker-tutorial/my-web-app

# 创建简单的 Web 应用
cat > app.py << 'EOF'
from flask import Flask, jsonify
import os
import socket

app = Flask(__name__)

@app.route('/')
def hello():
    return jsonify({
        'message': 'Hello from my Docker app!',
        'hostname': socket.gethostname(),
        'version': os.environ.get('APP_VERSION', '1.0.0')
    })

@app.route('/health')
def health():
    return jsonify({'status': 'healthy'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
EOF

# 创建 requirements.txt
cat > requirements.txt << 'EOF'
Flask==2.3.3
EOF

# 创建 Dockerfile
cat > Dockerfile << 'EOF'
FROM python:3.11-alpine

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

EXPOSE 5000

ENV APP_VERSION=1.0.0

CMD ["python", "app.py"]
EOF

# 构建镜像(替换 username 为你的 Docker Hub 用户名)
docker build -t username/my-web-app:1.0.0 .
docker build -t username/my-web-app:latest .

# 测试镜像
docker run -d -p 5000:5000 --name test-app username/my-web-app:1.0.0

# 测试应用
curl http://localhost:5000
curl http://localhost:5000/health

# 停止测试容器
docker stop test-app
docker rm test-app

# 推送到 Docker Hub(需要先登录)
# docker login
# docker push username/my-web-app:1.0.0
# docker push username/my-web-app:latest

7.3 私有镜像仓库

搭建本地 Registry

关于镜像站,我们之前详细讲过一期,可以参考 『{%post_link server/docker/docker-registry }』。

这里仅介绍自带 Registry 的方法。

# 启动本地 Registry
docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v registry_data:/var/lib/registry \
  registry:2

# 验证 Registry 运行状态
curl http://localhost:5000/v2/

# 配置 Docker 信任本地仓库(仅用于测试)
# 编辑 /etc/docker/daemon.json (Linux) 或 Docker Desktop 设置
{
  "insecure-registries": ["localhost:5000"]
}

# 重启 Docker 服务
# sudo systemctl restart docker  # Linux
# 或重启 Docker Desktop

# 推送镜像到本地仓库
docker tag username/my-web-app:1.0.0 localhost:5000/my-web-app:1.0.0
docker push localhost:5000/my-web-app:1.0.0

# 从本地仓库拉取镜像
docker pull localhost:5000/my-web-app:1.0.0

# 查看仓库中的镜像
curl http://localhost:5000/v2/_catalog
curl http://localhost:5000/v2/my-web-app/tags/list

带认证的私有仓库

# 创建认证配置目录
mkdir -p /tmp/docker-tutorial/registry-auth
cd /tmp/docker-tutorial/registry-auth

# 创建用户认证文件
mkdir auth
docker run --rm \
  --entrypoint htpasswd \
  httpd:2 -Bbn testuser testpass > auth/htpasswd

# 创建 SSL 证书(自签名,仅用于测试)
mkdir certs
openssl req -newkey rsa:4096 -nodes -sha256 \
  -keyout certs/domain.key -x509 -days 365 \
  -out certs/domain.crt \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/CN=localhost"

# 启动带认证的 Registry
docker run -d \
  -p 5443:5000 \
  --restart=always \
  --name secure-registry \
  -v $(pwd)/auth:/auth \
  -v $(pwd)/certs:/certs \
  -v registry_secure_data:/var/lib/registry \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt" \
  -e "REGISTRY_HTTP_TLS_KEY=/certs/domain.key" \
  registry:2

# 登录到私有仓库
docker login localhost:5443
# 用户名: testuser
# 密码: testpass

# 推送镜像到私有仓库
docker tag username/my-web-app:1.0.0 localhost:5443/my-web-app:1.0.0
docker push localhost:5443/my-web-app:1.0.0

# 清理
docker stop registry secure-registry
docker rm registry secure-registry

使用 Docker Compose 搭建完整的私有仓库

可以通过 config.yml + docker-compose.yml 搭建完整的私有仓库。

# 创建私有仓库项目
mkdir -p /tmp/docker-tutorial/private-registry
cd /tmp/docker-tutorial/private-registry

# 创建目录结构
mkdir -p auth certs data

# 创建用户认证文件
docker run --rm \
  --entrypoint htpasswd \
  httpd:2 -Bbn admin adminpass > auth/htpasswd

# 创建 Registry 配置
cat > config.yml << 'EOF'
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
EOF

# 创建 docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  registry:
    image: registry:2
    ports:
      - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./auth:/auth
      - ./data:/data
      - ./config.yml:/etc/docker/registry/config.yml
    restart: unless-stopped
    networks:
      - registry-network

  registry-ui:
    image: joxit/docker-registry-ui:latest
    ports:
      - "8080:80"
    environment:
      REGISTRY_TITLE: Private Docker Registry
      REGISTRY_URL: http://registry:5000
      DELETE_IMAGES: true
      SHOW_CONTENT_DIGEST: true
    depends_on:
      - registry
    networks:
      - registry-network

networks:
  registry-network:
    driver: bridge

volumes:
  registry-data:
EOF

# 启动私有仓库
docker-compose up -d

# 查看服务状态
docker-compose ps

# 登录私有仓库
docker login localhost:5000
# 用户名: admin
# 密码: adminpass

# 推送镜像
docker tag username/my-web-app:1.0.0 localhost:5000/my-web-app:1.0.0
docker push localhost:5000/my-web-app:1.0.0

# 访问 Web UI
echo "访问 http://localhost:8080 查看仓库 UI"

# 清理
docker-compose down -v

7.4 镜像安全和最佳实践

镜像安全扫描

# 使用 Docker Scout 扫描镜像(需要 Docker Desktop)
docker scout cves nginx:latest

# 使用 Trivy 扫描镜像
# 安装 Trivy
brew install trivy  # macOS
# 或
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# 扫描镜像漏洞
trivy image nginx:latest
trivy image --severity HIGH,CRITICAL nginx:latest

# 扫描本地镜像
trivy image username/my-web-app:1.0.0

# 生成报告
trivy image --format json --output report.json nginx:latest

镜像签名和验证

# 启用 Docker Content Trust
export DOCKER_CONTENT_TRUST=1

# 生成签名密钥
docker trust key generate mykey

# 为仓库添加签名者
docker trust signer add --key mykey.pub myuser username/myapp

# 推送签名镜像
docker push username/myapp:signed

# 验证镜像签名
docker trust inspect username/myapp:signed

# 禁用 Content Trust
export DOCKER_CONTENT_TRUST=0

镜像优化最佳实践

创建优化示例

mkdir -p /tmp/docker-tutorial/image-optimization
cd /tmp/docker-tutorial/image-optimization

创建未优化的 Dockerfile.bad 文件:

cat > Dockerfile.bad << 'EOF'
FROM ubuntu:20.04

RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y python3-pip
RUN apt-get install -y curl
RUN apt-get install -y vim

COPY . /app
WORKDIR /app

RUN pip3 install flask
RUN pip3 install requests

EXPOSE 5000

CMD ["python3", "app.py"]
EOF

创建优化的 Dockerfile.good 文件:

cat > Dockerfile.good << 'EOF'
# 使用更小的基础镜像
FROM python:3.11-alpine

# 设置工作目录
WORKDIR /app

# 只复制需要的文件
COPY requirements.txt .

# 合并 RUN 指令,清理缓存
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY app.py .

# 创建非 root 用户
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

# 切换到非 root 用户
USER appuser

EXPOSE 5000

# 使用 exec 形式的 CMD
CMD ["python", "app.py"]
EOF

创建其他文件:

# 创建 .dockerignore 文件
cat > .dockerignore << 'EOF'
.git
.gitignore
README.md
Dockerfile*
.dockerignore
node_modules
npm-debug.log
coverage/
.nyc_output
*.log
.DS_Store
EOF

# 创建应用文件
cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello from optimized Docker image!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
EOF

cat > requirements.txt << 'EOF'
Flask==2.3.3
EOF
# 构建并比较镜像大小
docker build -f Dockerfile.bad -t myapp:bad .
docker build -f Dockerfile.good -t myapp:good .

# 比较镜像大小
docker images | grep myapp

# 分析镜像层
docker history myapp:bad
docker history myapp:good

7.5 多架构镜像构建

使用 buildx 构建多架构镜像

# 启用 buildx
docker buildx version

# 创建新的构建器
docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap

# 查看支持的平台
docker buildx ls

# 构建多架构镜像
mkdir -p /tmp/docker-tutorial/multiarch
cd /tmp/docker-tutorial/multiarch

cat > Dockerfile << 'EOF'
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder

ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH

WORKDIR /app
COPY main.go .

RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o app main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .
EXPOSE 8080
CMD ["./app"]
EOF

cat > main.go << 'EOF'
package main

import (
    "fmt"
    "net/http"
    "runtime"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello from %s/%s!", runtime.GOOS, runtime.GOARCH)
}

func main() {
    http.HandleFunc("/", handler)
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", nil)
}
EOF

# 构建多架构镜像并推送
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t username/multiarch-app:latest \
  --push .

# 查看镜像清单
docker buildx imagetools inspect username/multiarch-app:latest

7.6 镜像仓库集成和 CI/CD

GitHub Actions 集成

更一般地模板可以从高星项目(比如 Open-WebUI, One-API 等)中摘取参考。

# .github/workflows/docker.yml
name: Build and Push Docker Image

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: docker.io
  IMAGE_NAME: username/myapp

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Log in to Docker Hub
      if: github.event_name != 'pull_request'
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}

    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        platforms: linux/amd64,linux/arm64
        push: ${{ github.event_name != 'pull_request' }}
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

镜像版本管理策略

# 语义化版本标记
docker tag myapp:latest myapp:1.0.0
docker tag myapp:latest myapp:1.0
docker tag myapp:latest myapp:1

# 基于 Git 提交的标记
GIT_COMMIT=$(git rev-parse --short HEAD)
docker tag myapp:latest myapp:${GIT_COMMIT}

# 基于构建时间的标记
BUILD_DATE=$(date +%Y%m%d-%H%M%S)
docker tag myapp:latest myapp:${BUILD_DATE}

# 环境特定标记
docker tag myapp:latest myapp:dev
docker tag myapp:latest myapp:staging
docker tag myapp:latest myapp:production

本章总结

在本章中,我们深入学习了 Docker 镜像管理和仓库操作:

  1. 镜像管理基础:掌握了镜像的生命周期、命名规范和清理优化
  2. Docker Hub 操作:学会了镜像的搜索、拉取、推送和发布流程
  3. 私有仓库搭建:实践了本地 Registry 和带认证的私有仓库部署
  4. 镜像安全:了解了安全扫描、签名验证和最佳实践
  5. 多架构构建:掌握了使用 buildx 构建跨平台镜像
  6. CI/CD 集成:学习了镜像构建的自动化和版本管理策略

安全考虑

  • 不在镜像中包含敏感信息
  • 使用非 root 用户运行应用
  • 定期更新基础镜像
  • 启用镜像签名和验证
  • 实施访问控制和审计

镜像优化技巧

  • 选择合适的基础镜像
  • 合并 RUN 指令减少层数
  • 使用 .dockerignore 排除不必要文件
  • 利用构建缓存提高构建速度
  • 实施多阶段构建分离构建和运行环境

至此,我们已经完成了 Docker 教程的所有核心章节。从基础概念到高级应用,涵盖了 Docker 的完整生态系统。这些知识将帮助你在实际项目中有效地使用 Docker 技术,构建、部署和管理容器化应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

致宏Rex

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值