前边篇章我们已经介绍了
- Docker 基础概念和安装
- Docker 常用命令实践
- Docker 网络机制详解
- Docker 数据卷和挂载
- Dockerfile 编写和镜像构建
- Docker Compose 多容器编排
本篇为系列最后一章,介绍 Docker 的镜像管理和仓库操作。
本教程侧重于命令实践和理解,提供可在本地环境测试的实例,每章结束都有总结要点。
7.1 Docker 镜像管理基础
我们之前介绍过一期自建镜像站的若干方案: Docker 管理 | 代理配置、内网共享和 Harbor 部署。
本篇侧重展开介绍 docker 镜像相关的概念,以及原生支持的镜像站方案。
镜像的生命周期
1# 镜像生命周期概览 2构建 -> 标记 -> 推送 -> 拉取 -> 运行 -> 删除 3 4# 查看本地镜像 5docker images 6docker image ls 7 8# 查看镜像详细信息 9docker image inspect nginx:alpine 10 11# 查看镜像历史 12docker image history nginx:alpine 13 14# 查看镜像大小 15docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" 16
镜像标记和命名
1# 镜像命名规范 2[registry_host[:port]/]username/repository[:tag] 3 4# 示例 5docker.io/library/nginx:latest # 官方镜像,前缀 docker.io/library/ 可以省略 6docker.io/username/myapp:v1.0 # 用户镜像,前缀 docker.io/ 可以省略 7registry.example.com/team/app:latest # 私有仓库镜像 8 9# 为镜像添加标记 10docker tag nginx:alpine myregistry.com/nginx:v1.0 11docker tag myapp:latest myapp:v1.0 12docker tag myapp:latest myapp:production 13 14# 查看同一镜像的多个标记,需匹配到 username/repository 15docker images myapp 16
镜像清理和优化
1# 删除单个镜像 2docker rmi nginx:alpine 3docker image rm nginx:alpine 4 5# 删除多个镜像 6docker rmi $(docker images -q ubuntu) 7 8# 删除悬空镜像(dangling images) 9docker image prune 10 11# 删除所有未使用的镜像 12docker image prune -a 13 14# 删除指定时间前的镜像 15docker image prune -a --filter "until=24h" 16 17# 查看镜像占用空间 18docker system df 19 20# 全面清理系统 21docker system prune -a --volumes 22
7.2 Docker Hub 操作
Docker Hub 基本操作
1# 登录 Docker Hub 2docker login # -u username -p password 3 4# 登录指定仓库 5docker login registry.example.com 6 7# 查看登录状态 8cat ~/.docker/config.json 9
注意,这里用户名的大小写敏感。一般地,建议在设置页面创建 token,通过 token 登录。
1# 搜索镜像 2docker search nginx 3docker search --limit 5 --filter stars=100 nginx 4 5# 拉取镜像 6docker pull nginx 7docker pull nginx:1.21-alpine 8docker pull ubuntu:20.04 9 10# 推送镜像到 Docker Hub 11# 1. 构建镜像 12docker build -t username/myapp:v1.0 . 13 14# 2. 推送镜像 15docker push username/myapp:v1.0 16 17# 登出 18docker logout 19
上边命令中的 push 镜像,以及 pull 私人镜像需要登录。
实践:发布自己的镜像
1# 创建示例应用 2mkdir -p /tmp/docker-tutorial/my-web-app 3cd /tmp/docker-tutorial/my-web-app 4 5# 创建简单的 Web 应用 6cat > app.py << 'EOF' 7from flask import Flask, jsonify 8import os 9import socket 10 11app = Flask(__name__) 12 13@app.route('/') 14def hello(): 15 return jsonify({ 16 'message': 'Hello from my Docker app!', 17 'hostname': socket.gethostname(), 18 'version': os.environ.get('APP_VERSION', '1.0.0') 19 }) 20 21@app.route('/health') 22def health(): 23 return jsonify({'status': 'healthy'}) 24 25if __name__ == '__main__': 26 app.run(host='0.0.0.0', port=5000) 27EOF 28 29# 创建 requirements.txt 30cat > requirements.txt << 'EOF' 31Flask==2.3.3 32EOF 33 34# 创建 Dockerfile 35cat > Dockerfile << 'EOF' 36FROM python:3.11-alpine 37 38WORKDIR /app 39 40COPY requirements.txt . 41RUN pip install --no-cache-dir -r requirements.txt 42 43COPY app.py . 44 45EXPOSE 5000 46 47ENV APP_VERSION=1.0.0 48 49CMD ["python", "app.py"] 50EOF 51 52# 构建镜像(替换 username 为你的 Docker Hub 用户名) 53docker build -t username/my-web-app:1.0.0 . 54docker build -t username/my-web-app:latest . 55 56# 测试镜像 57docker run -d -p 5000:5000 --name test-app username/my-web-app:1.0.0 58 59# 测试应用 60curl http://localhost:5000 61curl http://localhost:5000/health 62 63# 停止测试容器 64docker stop test-app 65docker rm test-app 66 67# 推送到 Docker Hub(需要先登录) 68# docker login 69# docker push username/my-web-app:1.0.0 70# docker push username/my-web-app:latest 71
7.3 私有镜像仓库
搭建本地 Registry
关于镜像站,我们之前详细讲过一期,可以参考 『{%post_link server/docker/docker-registry }』。
这里仅介绍自带 Registry 的方法。
1# 启动本地 Registry 2docker run -d \ 3 -p 5000:5000 \ 4 --restart=always \ 5 --name registry \ 6 -v registry_data:/var/lib/registry \ 7 registry:2 8 9# 验证 Registry 运行状态 10curl http://localhost:5000/v2/ 11 12# 配置 Docker 信任本地仓库(仅用于测试) 13# 编辑 /etc/docker/daemon.json (Linux) 或 Docker Desktop 设置 14{ 15 "insecure-registries": ["localhost:5000"] 16} 17 18# 重启 Docker 服务 19# sudo systemctl restart docker # Linux 20# 或重启 Docker Desktop 21 22# 推送镜像到本地仓库 23docker tag username/my-web-app:1.0.0 localhost:5000/my-web-app:1.0.0 24docker push localhost:5000/my-web-app:1.0.0 25 26# 从本地仓库拉取镜像 27docker pull localhost:5000/my-web-app:1.0.0 28 29# 查看仓库中的镜像 30curl http://localhost:5000/v2/_catalog 31curl http://localhost:5000/v2/my-web-app/tags/list 32
带认证的私有仓库
1# 创建认证配置目录 2mkdir -p /tmp/docker-tutorial/registry-auth 3cd /tmp/docker-tutorial/registry-auth 4 5# 创建用户认证文件 6mkdir auth 7docker run --rm \ 8 --entrypoint htpasswd \ 9 httpd:2 -Bbn testuser testpass > auth/htpasswd 10 11# 创建 SSL 证书(自签名,仅用于测试) 12mkdir certs 13openssl req -newkey rsa:4096 -nodes -sha256 \ 14 -keyout certs/domain.key -x509 -days 365 \ 15 -out certs/domain.crt \ 16 -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/CN=localhost" 17 18# 启动带认证的 Registry 19docker run -d \ 20 -p 5443:5000 \ 21 --restart=always \ 22 --name secure-registry \ 23 -v $(pwd)/auth:/auth \ 24 -v $(pwd)/certs:/certs \ 25 -v registry_secure_data:/var/lib/registry \ 26 -e "REGISTRY_AUTH=htpasswd" \ 27 -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ 28 -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \ 29 -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt" \ 30 -e "REGISTRY_HTTP_TLS_KEY=/certs/domain.key" \ 31 registry:2 32 33# 登录到私有仓库 34docker login localhost:5443 35# 用户名: testuser 36# 密码: testpass 37 38# 推送镜像到私有仓库 39docker tag username/my-web-app:1.0.0 localhost:5443/my-web-app:1.0.0 40docker push localhost:5443/my-web-app:1.0.0 41 42# 清理 43docker stop registry secure-registry 44docker rm registry secure-registry 45
使用 Docker Compose 搭建完整的私有仓库
可以通过 config.yml + docker-compose.yml 搭建完整的私有仓库。
1# 创建私有仓库项目 2mkdir -p /tmp/docker-tutorial/private-registry 3cd /tmp/docker-tutorial/private-registry 4 5# 创建目录结构 6mkdir -p auth certs data 7 8# 创建用户认证文件 9docker run --rm \ 10 --entrypoint htpasswd \ 11 httpd:2 -Bbn admin adminpass > auth/htpasswd 12 13# 创建 Registry 配置 14cat > config.yml << 'EOF' 15version: 0.1 16log: 17 fields: 18 service: registry 19storage: 20 cache: 21 blobdescriptor: inmemory 22 filesystem: 23 rootdirectory: /var/lib/registry 24http: 25 addr: :5000 26 headers: 27 X-Content-Type-Options: [nosniff] 28health: 29 storagedriver: 30 enabled: true 31 interval: 10s 32 threshold: 3 33EOF 34 35# 创建 docker-compose.yml 36cat > docker-compose.yml << 'EOF' 37version: '3.8' 38 39services: 40 registry: 41 image: registry:2 42 ports: 43 - "5000:5000" 44 environment: 45 REGISTRY_AUTH: htpasswd 46 REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm 47 REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd 48 REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data 49 volumes: 50 - ./auth:/auth 51 - ./data:/data 52 - ./config.yml:/etc/docker/registry/config.yml 53 restart: unless-stopped 54 networks: 55 - registry-network 56 57 registry-ui: 58 image: joxit/docker-registry-ui:latest 59 ports: 60 - "8080:80" 61 environment: 62 REGISTRY_TITLE: Private Docker Registry 63 REGISTRY_URL: http://registry:5000 64 DELETE_IMAGES: true 65 SHOW_CONTENT_DIGEST: true 66 depends_on: 67 - registry 68 networks: 69 - registry-network 70 71networks: 72 registry-network: 73 driver: bridge 74 75volumes: 76 registry-data: 77EOF 78 79# 启动私有仓库 80docker-compose up -d 81 82# 查看服务状态 83docker-compose ps 84 85# 登录私有仓库 86docker login localhost:5000 87# 用户名: admin 88# 密码: adminpass 89 90# 推送镜像 91docker tag username/my-web-app:1.0.0 localhost:5000/my-web-app:1.0.0 92docker push localhost:5000/my-web-app:1.0.0 93 94# 访问 Web UI 95echo "访问 http://localhost:8080 查看仓库 UI" 96 97# 清理 98docker-compose down -v 99
7.4 镜像安全和最佳实践
镜像安全扫描
1# 使用 Docker Scout 扫描镜像(需要 Docker Desktop) 2docker scout cves nginx:latest 3 4# 使用 Trivy 扫描镜像 5# 安装 Trivy 6brew install trivy # macOS 7# 或 8curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin 9 10# 扫描镜像漏洞 11trivy image nginx:latest 12trivy image --severity HIGH,CRITICAL nginx:latest 13 14# 扫描本地镜像 15trivy image username/my-web-app:1.0.0 16 17# 生成报告 18trivy image --format json --output report.json nginx:latest 19
镜像签名和验证
1# 启用 Docker Content Trust 2export DOCKER_CONTENT_TRUST=1 3 4# 生成签名密钥 5docker trust key generate mykey 6 7# 为仓库添加签名者 8docker trust signer add --key mykey.pub myuser username/myapp 9 10# 推送签名镜像 11docker push username/myapp:signed 12 13# 验证镜像签名 14docker trust inspect username/myapp:signed 15 16# 禁用 Content Trust 17export DOCKER_CONTENT_TRUST=0 18
镜像优化最佳实践
创建优化示例
1mkdir -p /tmp/docker-tutorial/image-optimization 2cd /tmp/docker-tutorial/image-optimization 3
创建未优化的 Dockerfile.bad 文件:
1cat > Dockerfile.bad << 'EOF' 2FROM ubuntu:20.04 3 4RUN apt-get update 5RUN apt-get install -y python3 6RUN apt-get install -y python3-pip 7RUN apt-get install -y curl 8RUN apt-get install -y vim 9 10COPY . /app 11WORKDIR /app 12 13RUN pip3 install flask 14RUN pip3 install requests 15 16EXPOSE 5000 17 18CMD ["python3", "app.py"] 19EOF 20
创建优化的 Dockerfile.good 文件:
1cat > Dockerfile.good << 'EOF' 2# 使用更小的基础镜像 3FROM python:3.11-alpine 4 5# 设置工作目录 6WORKDIR /app 7 8# 只复制需要的文件 9COPY requirements.txt . 10 11# 合并 RUN 指令,清理缓存 12RUN pip install --no-cache-dir -r requirements.txt 13 14# 复制应用代码 15COPY app.py . 16 17# 创建非 root 用户 18RUN addgroup -g 1001 -S appgroup && \ 19 adduser -u 1001 -S appuser -G appgroup 20 21# 切换到非 root 用户 22USER appuser 23 24EXPOSE 5000 25 26# 使用 exec 形式的 CMD 27CMD ["python", "app.py"] 28EOF 29
创建其他文件:
1# 创建 .dockerignore 文件 2cat > .dockerignore << 'EOF' 3.git 4.gitignore 5README.md 6Dockerfile* 7.dockerignore 8node_modules 9npm-debug.log 10coverage/ 11.nyc_output 12*.log 13.DS_Store 14EOF 15 16# 创建应用文件 17cat > app.py << 'EOF' 18from flask import Flask 19app = Flask(__name__) 20 21@app.route('/') 22def hello(): 23 return 'Hello from optimized Docker image!' 24 25if __name__ == '__main__': 26 app.run(host='0.0.0.0', port=5000) 27EOF 28 29cat > requirements.txt << 'EOF' 30Flask==2.3.3 31EOF 32
1# 构建并比较镜像大小 2docker build -f Dockerfile.bad -t myapp:bad . 3docker build -f Dockerfile.good -t myapp:good . 4 5# 比较镜像大小 6docker images | grep myapp 7 8# 分析镜像层 9docker history myapp:bad 10docker history myapp:good 11
7.5 多架构镜像构建
使用 buildx 构建多架构镜像
1# 启用 buildx 2docker buildx version 3 4# 创建新的构建器 5docker buildx create --name multiarch --driver docker-container --use 6docker buildx inspect --bootstrap 7 8# 查看支持的平台 9docker buildx ls 10 11# 构建多架构镜像 12mkdir -p /tmp/docker-tutorial/multiarch 13cd /tmp/docker-tutorial/multiarch 14 15cat > Dockerfile << 'EOF' 16FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder 17 18ARG TARGETPLATFORM 19ARG BUILDPLATFORM 20ARG TARGETOS 21ARG TARGETARCH 22 23WORKDIR /app 24COPY main.go . 25 26RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o app main.go 27 28FROM alpine:latest 29RUN apk --no-cache add ca-certificates 30WORKDIR /root/ 31COPY --from=builder /app/app . 32EXPOSE 8080 33CMD ["./app"] 34EOF 35 36cat > main.go << 'EOF' 37package main 38 39import ( 40 "fmt" 41 "net/http" 42 "runtime" 43) 44 45func handler(w http.ResponseWriter, r *http.Request) { 46 fmt.Fprintf(w, "Hello from %s/%s!", runtime.GOOS, runtime.GOARCH) 47} 48 49func main() { 50 http.HandleFunc("/", handler) 51 fmt.Println("Server starting on :8080") 52 http.ListenAndServe(":8080", nil) 53} 54EOF 55 56# 构建多架构镜像并推送 57docker buildx build \ 58 --platform linux/amd64,linux/arm64,linux/arm/v7 \ 59 -t username/multiarch-app:latest \ 60 --push . 61 62# 查看镜像清单 63docker buildx imagetools inspect username/multiarch-app:latest 64
7.6 镜像仓库集成和 CI/CD
GitHub Actions 集成
更一般地模板可以从高星项目(比如 Open-WebUI, One-API 等)中摘取参考。
1# .github/workflows/docker.yml 2name: Build and Push Docker Image 3 4on: 5 push: 6 branches: [ main ] 7 tags: [ 'v*' ] 8 pull_request: 9 branches: [ main ] 10 11env: 12 REGISTRY: docker.io 13 IMAGE_NAME: username/myapp 14 15jobs: 16 build: 17 runs-on: ubuntu-latest 18 permissions: 19 contents: read 20 packages: write 21 22 steps: 23 - name: Checkout repository 24 uses: actions/checkout@v4 25 26 - name: Set up Docker Buildx 27 uses: docker/setup-buildx-action@v3 28 29 - name: Log in to Docker Hub 30 if: github.event_name != 'pull_request' 31 uses: docker/login-action@v3 32 with: 33 registry: ${{ env.REGISTRY }} 34 username: ${{ secrets.DOCKER_USERNAME }} 35 password: ${{ secrets.DOCKER_PASSWORD }} 36 37 - name: Extract metadata 38 id: meta 39 uses: docker/metadata-action@v5 40 with: 41 images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 42 tags: | 43 type=ref,event=branch 44 type=ref,event=pr 45 type=semver,pattern={{version}} 46 type=semver,pattern={{major}}.{{minor}} 47 48 - name: Build and push Docker image 49 uses: docker/build-push-action@v5 50 with: 51 context: . 52 platforms: linux/amd64,linux/arm64 53 push: ${{ github.event_name != 'pull_request' }} 54 tags: ${{ steps.meta.outputs.tags }} 55 labels: ${{ steps.meta.outputs.labels }} 56 cache-from: type=gha 57 cache-to: type=gha,mode=max 58
镜像版本管理策略
1# 语义化版本标记 2docker tag myapp:latest myapp:1.0.0 3docker tag myapp:latest myapp:1.0 4docker tag myapp:latest myapp:1 5 6# 基于 Git 提交的标记 7GIT_COMMIT=$(git rev-parse --short HEAD) 8docker tag myapp:latest myapp:${GIT_COMMIT} 9 10# 基于构建时间的标记 11BUILD_DATE=$(date +%Y%m%d-%H%M%S) 12docker tag myapp:latest myapp:${BUILD_DATE} 13 14# 环境特定标记 15docker tag myapp:latest myapp:dev 16docker tag myapp:latest myapp:staging 17docker tag myapp:latest myapp:production 18
本章总结
在本章中,我们深入学习了 Docker 镜像管理和仓库操作:
- 镜像管理基础:掌握了镜像的生命周期、命名规范和清理优化
- Docker Hub 操作:学会了镜像的搜索、拉取、推送和发布流程
- 私有仓库搭建:实践了本地 Registry 和带认证的私有仓库部署
- 镜像安全:了解了安全扫描、签名验证和最佳实践
- 多架构构建:掌握了使用 buildx 构建跨平台镜像
- CI/CD 集成:学习了镜像构建的自动化和版本管理策略
安全考虑:
- 不在镜像中包含敏感信息
- 使用非 root 用户运行应用
- 定期更新基础镜像
- 启用镜像签名和验证
- 实施访问控制和审计
镜像优化技巧:
- 选择合适的基础镜像
- 合并 RUN 指令减少层数
- 使用 .dockerignore 排除不必要文件
- 利用构建缓存提高构建速度
- 实施多阶段构建分离构建和运行环境
至此,我们已经完成了 Docker 教程的所有核心章节。从基础概念到高级应用,涵盖了 Docker 的完整生态系统。这些知识将帮助你在实际项目中有效地使用 Docker 技术,构建、部署和管理容器化应用。
《Docker 实战教程(7) | 镜像管理和仓库操作》 是转载文章,点击查看原文。
