| name | container-registry-management |
| description | Manage container registries (Docker Hub, ECR, GCR) with image scanning, retention policies, and access control. |
Container Registry Management
Overview
Implement comprehensive container registry management including image scanning, vulnerability detection, retention policies, access control, and multi-region replication.
When to Use
- Container image storage and distribution
- Security scanning and compliance
- Image retention and cleanup
- Registry access control
- Multi-region deployments
- Image signing and verification
- Cost optimization
Implementation Examples
1. AWS ECR Setup and Management
# 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 "Setting up ECR repository..."
# Create ECR repository
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 "Repository: $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REGISTRY_NAME"
# Set lifecycle policy
aws ecr put-lifecycle-policy \
--repository-name "$REGISTRY_NAME" \
--region "$REGION" \
--lifecycle-policy-text '{
"rules": [
{
"rulePriority": 1,
"description": "Keep last 20 images tagged with release",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["release"],
"countType": "imageCountMoreThan",
"countNumber": 20
},
"action": {
"type": "expire"
}
},
{
"rulePriority": 2,
"description": "Remove untagged images older than 7 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 7
},
"action": {
"type": "expire"
}
},
{
"rulePriority": 3,
"description": "Keep all development images for 30 days",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["dev"],
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 30
},
"action": {
"type": "expire"
}
}
]
}'
# Enable cross-region replication
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 setup complete"
scan-images.sh: |
#!/bin/bash
set -euo pipefail
REGISTRY_NAME="myapp"
REGION="us-east-1"
echo "Scanning all images in $REGISTRY_NAME"
# Get all image IDs
IMAGE_IDS=$(aws ecr list-images \
--repository-name "$REGISTRY_NAME" \
--region "$REGION" \
--query 'imageIds[*]' \
--output json)
# Scan each image
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 "Scanning image: $IMAGE ($DIGEST)"
# Start scan
aws ecr start-image-scan \
--repository-name "$REGISTRY_NAME" \
--image-id imageTag="$IMAGE" \
--region "$REGION" || true
# Get scan results
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 "WARNING: Image has $CRITICAL critical vulnerabilities"
fi
if [ "$HIGH" -gt 0 ]; then
echo "WARNING: Image has $HIGH high vulnerabilities"
fi
done
echo "Image scanning complete"
---
# Terraform ECR configuration
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 = "Keep last 20 production images"
selection = {
tagStatus = "tagged"
tagPrefixList = ["release"]
countType = "imageCountMoreThan"
countNumber = 20
}
action = {
type = "expire"
}
},
{
rulePriority = 2
description = "Remove untagged images after 7 days"
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. Container Image Build and Push
#!/bin/bash
# build-and-push.sh - Build and push container images
set -euo pipefail
REGISTRY="${1:-123456789012.dkr.ecr.us-east-1.amazonaws.com}"
IMAGE_NAME="${2:-myapp}"
VERSION="${3:-latest}"
DOCKERFILE="${4:-Dockerfile}"
echo "Building and pushing container image..."
# Set full image path
FULL_IMAGE="$REGISTRY/$IMAGE_NAME:$VERSION"
# Login to ECR
echo "Authenticating to ECR..."
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin "$REGISTRY"
# Build image
echo "Building image: $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" \
.
# Scan with trivy before push
echo "Scanning image with Trivy..."
trivy image --severity HIGH,CRITICAL "$FULL_IMAGE"
# Push image
echo "Pushing image to ECR..."
docker push "$FULL_IMAGE"
docker push "$REGISTRY/$IMAGE_NAME:latest"
# Get image digest
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$FULL_IMAGE" | cut -d@ -f2)
echo "Image pushed successfully"
echo "Image: $FULL_IMAGE"
echo "Digest: $DIGEST"
3. Image Signing with Notary
#!/bin/bash
# sign-image.sh - Sign container images with Notary
set -euo pipefail
IMAGE="${1}"
NOTATION_KEY="${2:-mykey}"
echo "Signing image: $IMAGE"
# Initialize Notary
notary key list
# Sign image
notation sign \
--key "$NOTATION_KEY" \
--allow-missing \
"$IMAGE"
echo "Image signed successfully"
# Verify signature
notation verify "$IMAGE"
4. Registry Access Control
# 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 policy for ECR access
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
# registry-monitoring.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-monitoring
namespace: monitoring
data:
dashboards.json: |
{
"dashboard": {
"title": "Container Registry",
"panels": [
{
"title": "Images by Repository",
"targets": [
{
"expr": "count by (repository) (aws_ecr_repository_images)"
}
]
},
{
"title": "Images with Vulnerabilities",
"targets": [
{
"expr": "sum(aws_ecr_image_scan_findings{severity=~\"HIGH|CRITICAL\"})"
}
]
},
{
"title": "Registry Storage",
"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: "Image has critical vulnerabilities"
- alert: ImagePushFailure
expr: aws_ecr_push_failures_total > 0
for: 1m
labels:
severity: warning
annotations:
summary: "Image push failed"
- alert: RegistryStorageHigh
expr: aws_ecr_repository_size_bytes / 1024 / 1024 / 1024 > 100
labels:
severity: warning
annotations:
summary: "Registry storage usage is high"
Best Practices
✅ DO
- Scan images before deployment
- Use image tag immutability
- Implement retention policies
- Control registry access with IAM
- Sign images for verification
- Replicate across regions
- Monitor registry storage
- Use private registries
❌ DON'T
- Push to public registries
- Use
latesttag in production - Allow anonymous pulls
- Store secrets in images
- Keep old images indefinitely
- Push without scanning
- Use default credentials
- Share registry credentials
Registry Options
- Docker Hub: Public registry
- AWS ECR: AWS-managed
- Google GCR: Google Cloud
- Azure ACR: Azure-managed
- Artifactory: Self-hosted
- Harbor: Open-source
Image Naming Convention
[registry]/[organization]/[repository]:[tag]
123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.2.3