Introdução: Por Que Um Container Registry Privado?
Quando você trabalha com containerização em produção, armazenar imagens Docker em repositórios públicos (como Docker Hub) pode ser uma prática arriscada. Dados sensíveis, propriedade intelectual e conformidade regulatória são questões críticas. Um container registry privado é exatamente uma solução que permite manter suas imagens de forma segura, com controle total sobre acesso, versionamento e distribuição.
Existem três principais opções no mercado atualmente: Harbor (open-source, self-hosted), GitHub Container Registry — GHCR (integrado ao GitHub, cloud-based) e AWS ECR (serviço gerenciado da Amazon). Cada uma tem seu lugar na arquitetura de uma empresa. Neste artigo, você aprenderá não apenas como configurar cada uma, mas também quando usá-las e como integrá-las ao seu pipeline de CI/CD.
Harbor: Container Registry Privado Self-Hosted
O Que é Harbor e Por Que Escolhê-lo
Harbor é um container registry de código aberto, desenvolvido pela Cloud Native Computing Foundation (CNCF). Ele oferece controle total sobre sua infraestrutura, sem depender de provedores cloud. Com features como replicação de imagens, verificação de vulnerabilidades (Trivy integrado), RBAC (Role-Based Access Control) e suporte a múltiplos protocolos, Harbor é ideal para empresas que precisam de customização completa.
A principal vantagem é a autonomia: você não paga por requisições ou armazenamento, apenas mantém a infraestrutura. A desvantagem é que você é responsável por backup, segurança e escalabilidade. Para organizações médias a grandes, especialmente em ambientes on-premise, Harbor é praticamente obrigatório.
Instalando Harbor com Docker Compose
Vou mostrar como instalar Harbor de forma prática usando Docker Compose. Primeiro, baixe o arquivo de configuração oficial:
wget https://github.com/goharbor/harbor/releases/download/v2.8.2/harbor-offline-installer-v2.8.2.tgz
tar xzvf harbor-offline-installer-v2.8.2.tgz
cd harbor
Agora, edite o arquivo harbor.yml. Este é o arquivo de configuração central:
# harbor.yml
hostname: registry.seudominio.com.br
http:
port: 80
https:
port: 443
certificate: /path/to/certificate.crt
private_key: /path/to/private.key
harbor_admin_password: SenhaForte123!@#
database:
password: DbPassword123!@#
data_volume: /data
trivy:
ignore_unfixed: false
severity: HIGH,CRITICAL
Para gerar certificados SSL autoassinados (apenas para desenvolvimento):
openssl req -x509 -newkey rsa:4096 -keyout private.key -out certificate.crt -days 365 -nodes
Agora execute a instalação:
./prepare
docker-compose up -d
Verifique se todos os containers estão rodando:
docker-compose ps
Acesse via navegador em https://registry.seudominio.com.br (aceite o aviso de certificado auto-assinado). Login padrão: admin / Harbor12345.
Integrando Harbor com Docker CLI
Para fazer push de imagens ao Harbor, primeiro você precisa fazer login:
docker login registry.seudominio.com.br
# Username: seu_usuario
# Password: sua_senha
Agora crie uma imagem de exemplo e faça push:
# Crie um Dockerfile simples
cat > Dockerfile << 'EOF'
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask
EXPOSE 5000
CMD ["python", "app.py"]
EOF
# Build da imagem
docker build -t registry.seudominio.com.br/meu-projeto/app:v1.0 .
# Push para Harbor
docker push registry.seudominio.com.br/meu-projeto/app:v1.0
Acesse o dashboard do Harbor e você verá o projeto meu-projeto com a imagem app armazenada. Harbor automaticamente verifica vulnerabilidades usando Trivy — você pode consultar relatórios de segurança diretamente na interface.
Replicação de Imagens
Harbor permite replicar imagens automaticamente entre registries. Vá em Administration → Registries e adicione um novo registry de destino. Depois, crie uma regra de replicação que sincronize suas imagens com um segundo Harbor ou até com ECR como backup.
GitHub Container Registry (GHCR): Integração Nativa com GitHub
Entendendo GHCR e Quando Usá-lo
GitHub Container Registry é a solução de registro de containers do GitHub. Diferentemente do Docker Hub, ele está intimamente integrado com repositórios GitHub e ações CI/CD. É a escolha natural se você já hospeda seu código no GitHub e quer minimizar configurações extras.
Vantagens incluem: não requer setup adicional, integração nativa com GitHub Actions, suporte automático a OIDC (OpenID Connect) para deployments seguros e preços razoáveis. A desvantagem é que você fica amarrado ao ecossistema GitHub. Se sua organização usa GitLab ou Gitea, essa não é a melhor opção.
Autenticação e Push com GitHub Actions
Para usar GHCR, você precisa de um personal access token (PAT) do GitHub. Acesse Settings → Developer settings → Personal access tokens → Tokens (classic) e crie um token com permissão write:packages e delete:packages.
Agora vou mostrar um workflow GitHub Actions que faz build e push de imagem automaticamente:
# .github/workflows/docker-publish.yml
name: Docker Publish
on:
push:
branches:
- main
tags:
- 'v*'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
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 Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Este workflow faz build e push automaticamente quando você faz push na branch main ou cria uma tag. O GITHUB_TOKEN é injetado automaticamente pelo GitHub, sem necessidade de configuração manual.
Usando Imagens do GHCR
Para usar uma imagem hospedada no GHCR em um deployment, você precisa se autenticar primeiro. Em um cluster Kubernetes:
apiVersion: v1
kind: Secret
metadata:
name: ghcr-secret
type: kubernetes.io/dockercfg
data:
.dockercfg: <BASE64_ENCODED_DOCKER_CONFIG>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
imagePullSecrets:
- name: ghcr-secret
containers:
- name: app
image: ghcr.io/seuusername/seu-repo:latest
ports:
- containerPort: 5000
Para gerar o secret, você pode usar:
kubectl create secret docker-registry ghcr-secret \
--docker-server=ghcr.io \
--docker-username=seu_username \
--docker-password=seu_pat_token \
--docker-email=seu_email@example.com \
-o yaml > ghcr-secret.yaml
AWS ECR: Container Registry Gerenciado
Arquitetura e Benefícios do ECR
AWS Elastic Container Registry é o serviço gerenciado de container registry da Amazon. Integra perfeitamente com EC2, ECS, EKS e Lambda, oferecendo escalabilidade automática, criptografia nativa e suporte completo a IAM para controle de acesso granular.
A principal vantagem é simplicidade operacional: AWS cuida de patching, backups, redundância e escalabilidade. Você paga por armazenamento (USD 0,07 por GB/mês no início de 2024) e requisições. Para empresas já na AWS, ECR é praticamente transparente — não requer setup dedicado além de permissões IAM.
Criando Repositório e Fazendo Push
Primeiro, crie um repositório ECR via AWS CLI:
aws ecr create-repository \
--repository-name meu-projeto/app \
--region us-east-1 \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES
Agora, faça login no ECR. Note que o token de login expira em 12 horas:
# Obter URI do ECR
ECR_URI=$(aws ecr describe-repositories \
--repository-names meu-projeto/app \
--region us-east-1 \
--query 'repositories[0].repositoryUri' \
--output text)
# Fazer login
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin $ECR_URI
Build e push da imagem:
# Build
docker build -t $ECR_URI:v1.0 .
# Push
docker push $ECR_URI:v1.0
# Verificar imagens no repositório
aws ecr describe-images \
--repository-name meu-projeto/app \
--region us-east-1
Integração com ECS e EKS
Para usar imagens ECR em ECS, a configuração é direta. Aqui está uma task definition exemplo:
{
"family": "meu-app-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"containerDefinitions": [
{
"name": "meu-app",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/meu-projeto/app:v1.0",
"portMappings": [
{
"containerPort": 5000,
"hostPort": 5000,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/meu-app",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole"
}
Para EKS, você precisará configurar um ImagePullSecret com credenciais ECR:
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: ecr-secret
namespace: default
type: kubernetes.io/dockercfg
data:
.dockercfg: <BASE64_DOCKER_AUTH>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
serviceAccountName: default
imagePullSecrets:
- name: ecr-secret
containers:
- name: app
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/meu-projeto/app:v1.0
ports:
- containerPort: 5000
Policies IAM para Segurança
Configure políticas IAM granulares para seus usuários e roles. Aqui está um exemplo que permite push/pull apenas para repositórios específicos:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": "arn:aws:ecr:us-east-1:123456789012:repository/meu-projeto/*"
},
{
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
}
]
}
Comparação Prática e Seleção da Melhor Opção
Agora que você conhece os três, precisamos entender como escolher. A decisão não é sobre qual é "melhor", mas qual se adequa melhor ao seu contexto.
Harbor é sua escolha se: você precisa de máxima autonomia, trabalha em ambiente on-premise, tem dados regulados (LGPD, HIPAA), ou quer replicação distribuída entre datacenters. Desvantagem: você gerencia a infraestrutura e é responsável por patches de segurança.
GHCR é sua escolha se: você já usa GitHub como repositório principal, prefere não configurar coisas extras e quer CI/CD integrada. Desvantagem: você fica preso ao ecossistema GitHub e não tem as features avançadas de Harbor como replicação pull-based.
AWS ECR é sua escolha se: você já está investido na AWS, usa ECS ou EKS, e quer gerenciamento mínimo. Desvantagem: custa dinheiro por armazenamento/requisições e você fica amarrado à AWS.
Uma estratégia comum em grandes organizações é usar dois registries: ECR como sistema primário em produção (gerenciado, confiável, integrado) e Harbor como backup/desenvolvimento (controle total, sem custos variáveis). Imagens são replicadas do Harbor para ECR antes do deploy.
Conclusão
Você aprendeu que não existe uma solução única para todos: Harbor oferece controle máximo em troca de responsabilidade operacional; GHCR integra perfeitamente com GitHub eliminando complexidade; ECR fornece gerenciabilidade em ambientes AWS. A segunda lição crítica é que container registries privados não são apenas armazenamento — eles são pontos críticos de segurança que precisam de autenticação forte, RBAC bem definido, e verificação de vulnerabilidades ativa. A terceira é que a escolha influencia seu pipeline inteiro: um bom registry economiza horas em troubleshooting de pulls falhos, autenticação quebrada e imagens obsoletas.