name: container-registry-management description: 管理容器注册表(Docker Hub、ECR、GCR)包括镜像扫描、漏洞检测、保留策略、访问控制和多区域复制。
容器注册表管理
概览
实现全面的容器注册表管理,包括镜像扫描、漏洞检测、保留策略、访问控制和多区域复制。
何时使用
- 容器镜像存储和分发
- 安全扫描和合规性
- 镜像保留和清理
- 注册表访问控制
- 多区域部署
- 镜像签名和验证
- 成本优化
实施示例
1. AWS ECR 设置和管理
# ecr-setup.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ecr-management
namespace: operations
data:
setup-ecr.sh: |
#!/bin/bash
set -euo pipefail
REGISTRY_NAME="myapp"
REGION="us-east-1"
ACCOUNT_ID="123456789012"
echo "设置ECR仓库..."
# 创建ECR仓库
aws ecr create-repository \
--repository-name "$REGISTRY_NAME" \
--region "$REGION" \
--encryption-configuration encryptionType=KMS,kmsKey=arn:aws:kms:$REGION:$ACCOUNT_ID:key/12345678-1234-1234-1234-123456789012 \
--image-tag-mutability IMMUTABLE \
--image-scanning-configuration scanOnPush=true || true
echo "仓库:$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REGISTRY_NAME"
# 设置生命周期策略
aws ecr put-lifecycle-policy \
--repository-name "$REGISTRY_NAME" \
--region "$REGION" \
--lifecycle-policy-text '{
"rules": [
{
"rulePriority": 1,
"description": "保留最后20个标记为发布的镜像",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["release"],
"countType": "imageCountMoreThan",
"countNumber": 20
},
"action": {
"type": "expire"
}
},
{
"rulePriority": 2,
"description": "移除7天前未标记的镜像",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 7
},
"action": {
"type": "expire"
}
},
{
"rulePriority": 3,
"description": "保留所有开发镜像30天",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["dev"],
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 30
},
"action": {
"type": "expire"
}
}
]
}'
# 启用跨区域复制
aws ecr create-registry \
--region "$REGION" \
--replication-configuration '{
"rules": [
{
"destinations": [
{
"region": "eu-west-1",
"registryId": "'$ACCOUNT_ID'"
},
{
"region": "ap-northeast-1",
"registryId": "'$ACCOUNT_ID'"
}
],
"repositoryFilters": [
{
"filter": "'$REGISTRY_NAME'",
"filterType": "PREFIX_MATCH"
}
]
}
]
}' || true
echo "ECR设置完成"
scan-images.sh: |
#!/bin/bash
set -euo pipefail
REGISTRY_NAME="myapp"
REGION="us-east-1"
echo "扫描$REGISTRY_NAME中的所有镜像"
# 获取所有镜像ID
IMAGE_IDS=$(aws ecr list-images \
--repository-name "$REGISTRY_NAME" \
--region "$REGION" \
--query 'imageIds[*]' \
--output json)
# 扫描每个镜像
echo "$IMAGE_IDS" | jq -r '.[] | @base64' | while read image; do
IMAGE=$(echo "$image" | base64 -d | jq -r '.imageTag')
DIGEST=$(echo "$image" | base64 -d | jq -r '.imageDigest')
echo "扫描镜像:$IMAGE ($DIGEST)"
# 开始扫描
aws ecr start-image-scan \
--repository-name "$REGISTRY_NAME" \
--image-id imageTag="$IMAGE" \
--region "$REGION" || true
# 获取扫描结果
sleep 5
RESULTS=$(aws ecr describe-image-scan-findings \
--repository-name "$REGISTRY_NAME" \
--image-id imageTag="$IMAGE" \
--region "$REGION")
CRITICAL=$(echo "$RESULTS" | jq '.imageScanFindings.findingSeverityCounts.CRITICAL // 0')
HIGH=$(echo "$RESULTS" | jq '.imageScanFindings.findingSeverityCounts.HIGH // 0')
if [ "$CRITICAL" -gt 0 ]; then
echo "警告:镜像有$CRITICAL个严重漏洞"
fi
if [ "$HIGH" -gt 0 ]; then
echo "警告:镜像有$HIGH个高漏洞"
fi
done
echo "镜像扫描完成"
---
# Terraform ECR配置
resource "aws_ecr_repository" "myapp" {
name = "myapp"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
encryption_configuration {
encryption_type = "KMS"
kms_key = aws_kms_key.ecr.arn
}
tags = {
Name = "myapp-registry"
}
}
resource "aws_ecr_lifecycle_policy" "myapp" {
repository = aws_ecr_repository.myapp.name
policy = jsonencode({
rules = [
{
rulePriority = 1
description = "保留最后20个生产镜像"
selection = {
tagStatus = "tagged"
tagPrefixList = ["release"]
countType = "imageCountMoreThan"
countNumber = 20
}
action = {
type = "expire"
}
},
{
rulePriority = 2
description = "7天后移除未标记的镜像"
selection = {
tagStatus = "untagged"
countType = "sinceImagePushed"
countUnit = "days"
countNumber = 7
}
action = {
type = "expire"
}
}
]
})
}
resource "aws_ecr_repository_policy" "myapp" {
repository = aws_ecr_repository.myapp.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/EcsTaskExecutionRole"
}
Action = [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:GetImage"
]
}
]
})
}
2. 容器镜像构建和推送
#!/bin/bash
# build-and-push.sh - 构建和推送容器镜像
set -euo pipefail
REGISTRY="${1:-123456789012.dkr.ecr.us-east-1.amazonaws.com}"
IMAGE_NAME="${2:-myapp}"
VERSION="${3:-latest}"
DOCKERFILE="${4:-Dockerfile}"
echo "构建和推送容器镜像..."
# 设置完整镜像路径
FULL_IMAGE="$REGISTRY/$IMAGE_NAME:$VERSION"
# 登录到ECR
echo "认证到ECR..."
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin "$REGISTRY"
# 构建镜像
echo "构建镜像:$FULL_IMAGE"
docker build \
-f "$DOCKERFILE" \
-t "$FULL_IMAGE" \
-t "$REGISTRY/$IMAGE_NAME:latest" \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
--build-arg VCS_REF="$(git rev-parse --short HEAD)" \
--build-arg VERSION="$VERSION" \
.
# 推送前用trivy扫描
echo "用Trivy扫描镜像..."
trivy image --severity HIGH,CRITICAL "$FULL_IMAGE"
# 推送镜像
echo "推送镜像到ECR..."
docker push "$FULL_IMAGE"
docker push "$REGISTRY/$IMAGE_NAME:latest"
# 获取镜像摘要
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$FULL_IMAGE" | cut -d@ -f2)
echo "镜像推送成功"
echo "镜像:$FULL_IMAGE"
echo "摘要:$DIGEST"
3. 用Notary签名镜像
#!/bin/bash
# sign-image.sh - 用Notary签名容器镜像
set -euo pipefail
IMAGE="${1}"
NOTATION_KEY="${2:-mykey}"
echo "签名镜像:$IMAGE"
# 初始化Notary
notary key list
# 签名镜像
notation sign \
--key "$NOTATION_KEY" \
--allow-missing \
"$IMAGE"
echo "镜像签名成功"
# 验证签名
notation verify "$IMAGE"
4. 注册表访问控制
# registry-access-control.yaml
apiVersion: v1
kind: Secret
metadata:
name: ecr-pull-secret
namespace: production
type: kubernetes.io/dockercfg
stringData:
.dockercfg: |
{
"123456789012.dkr.ecr.us-east-1.amazonaws.com": {
"auth": "base64-encoded-credentials",
"email": "service-account@mycompany.com"
}
}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: ecr-pull-sa
namespace: production
imagePullSecrets:
- name: ecr-pull-secret
---
apiVersion: v1
kind: Pod
metadata:
name: myapp
namespace: production
spec:
serviceAccountName: ecr-pull-sa
containers:
- name: app
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
imagePullPolicy: Always
---
# IAM策略用于ECR访问
apiVersion: iam.aws.amazon.com/v1
kind: IAMPolicy
metadata:
name: ecr-read-only
spec:
policyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:GetImage
- ecr:DescribeImages
Resource: arn:aws:ecr:*:123456789012:repository/myapp
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: '*'
5. 注册表监控
# registry-monitoring.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-monitoring
namespace: monitoring
data:
dashboards.json: |
{
"dashboard": {
"title": "容器注册表",
"panels": [
{
"title": "按仓库的镜像",
"targets": [
{
"expr": "count by (repository) (aws_ecr_repository_images)"
}
]
},
{
"title": "有漏洞的镜像",
"targets": [
{
"expr": "sum(aws_ecr_image_scan_findings{severity=~\"HIGH|CRITICAL\"})"
}
]
},
{
"title": "注册表存储",
"targets": [
{
"expr": "aws_ecr_repository_size_bytes"
}
]
}
]
}
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-alerts
namespace: monitoring
data:
alerts.yaml: |
groups:
- name: registry_alerts
rules:
- alert: ImageWithCriticalVulnerabilities
expr: aws_ecr_image_scan_findings{severity="CRITICAL"} > 0
for: 5m
labels:
severity: critical
annotations:
summary: "镜像有严重漏洞"
- alert: ImagePushFailure
expr: aws_ecr_push_failures_total > 0
for: 1m
labels:
severity: warning
annotations:
summary: "镜像推送失败"
- alert: RegistryStorageHigh
expr: aws_ecr_repository_size_bytes / 1024 / 1024 / 1024 > 100
labels:
severity: warning
annotations:
summary: "注册表存储使用率高"
最佳实践
✅ DO
- 部署前扫描镜像
- 使用镜像标签不变性
- 实施保留策略
- 用IAM控制注册表访问
- 签名镜像以供验证
- 跨区域复制
- 监控注册表存储
- 使用私有注册表
❌ DON’T
- 推送到公共注册表
- 在生产中使用
latest标签 - 允许匿名拉取
- 在镜像中存储秘密
- 无限期保留旧镜像
- 未扫描即推送
- 使用默认凭据
- 共享注册表凭据
注册表选项
- Docker Hub: 公共注册表
- AWS ECR: AWS管理
- Google GCR: Google云
- Azure ACR: Azure管理
- Artifactory: 自托管
- Harbor: 开源
镜像命名约定
[registry]/[organization]/[repository]:[tag]
123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.2.3