前边篇章我们已经介绍了
- Docker 基础概念和安装
- Docker 常用命令实践
- Docker 网络机制详解
- Docker 数据卷和挂载
- Dockerfile 编写和镜像构建
- 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 镜像管理和仓库操作:
- 镜像管理基础:掌握了镜像的生命周期、命名规范和清理优化
- Docker Hub 操作:学会了镜像的搜索、拉取、推送和发布流程
- 私有仓库搭建:实践了本地 Registry 和带认证的私有仓库部署
- 镜像安全:了解了安全扫描、签名验证和最佳实践
- 多架构构建:掌握了使用 buildx 构建跨平台镜像
- CI/CD 集成:学习了镜像构建的自动化和版本管理策略
安全考虑:
- 不在镜像中包含敏感信息
- 使用非 root 用户运行应用
- 定期更新基础镜像
- 启用镜像签名和验证
- 实施访问控制和审计
镜像优化技巧:
- 选择合适的基础镜像
- 合并 RUN 指令减少层数
- 使用 .dockerignore 排除不必要文件
- 利用构建缓存提高构建速度
- 实施多阶段构建分离构建和运行环境
至此,我们已经完成了 Docker 教程的所有核心章节。从基础概念到高级应用,涵盖了 Docker 的完整生态系统。这些知识将帮助你在实际项目中有效地使用 Docker 技术,构建、部署和管理容器化应用。