Deploy de containers Docker na AWS: ECR, Portainer e SSL
Atualizado em:
Recentemente precisei estruturar um fluxo de deploy para um serviço containerizado rodando em uma instância EC2 na AWS, já com Docker e Portainer instalados e um domínio apontado via Route 53. Neste texto documento o processo completo: da criação do repositório de imagens até o HTTPS funcionando.
O cenário
- EC2 Ubuntu com Docker e Portainer já rodando
- Domínio já configurado no Route 53
- Objetivo: subir uma imagem Docker para o Amazon ECR (Elastic Container Registry), fazer o Portainer consumir essa imagem e expor o serviço via HTTPS
1. Autenticação AWS
O primeiro passo é garantir que o AWS CLI local tenha credenciais válidas:
aws sts get-caller-identity
Se o comando falhar (erros como InvalidClientTokenId ou pendências de configuração SSO), o caminho mais simples para uso individual é criar um usuário IAM dedicado, sem acesso ao console, com uma access key gerada especificamente para linha de comando:
aws configure
Informando Access Key ID, Secret Access Key, região e formato de saída.
2. Criando o repositório no ECR
Com as credenciais configuradas:
aws ecr create-repository --repository-name meu-servico --region sa-east-1
A URL do repositório segue o padrão:
<account-id>.dkr.ecr.<regiao>.amazonaws.com/meu-servico
3. Build e push da imagem
Na pasta do projeto, com o Dockerfile já pronto:
# autenticação no registry
aws ecr get-login-password --region sa-east-1 | \
docker login --username AWS --password-stdin <account-id>.dkr.ecr.sa-east-1.amazonaws.com
# build
docker build -t meu-servico .
# tag apontando para o ECR
docker tag meu-servico:latest <account-id>.dkr.ecr.sa-east-1.amazonaws.com/meu-servico:latest
# push
docker push <account-id>.dkr.ecr.sa-east-1.amazonaws.com/meu-servico:latest
Um detalhe que vale mencionar: depender só da tag latest dificulta rastrear qual versão está em produção e complica rollback. Tags mais específicas (SHA do commit, número de versão) resolvem isso sem custo adicional.
4. Um usuário IAM só de leitura para o Portainer
Para o Portainer puxar imagens do ECR, não é necessário dar permissão de escrita. A AWS já tem uma policy gerenciada pronta para isso:
AmazonEC2ContainerRegistryReadOnly
Ela cobre exatamente o necessário — GetAuthorizationToken, BatchGetImage, DescribeRepositories, entre outras — sem permitir push, delete ou alteração de repositórios. Vale criar um usuário IAM exclusivo com essa policy só para as credenciais que o Portainer vai usar, seguindo o princípio de menor privilégio.
5. Registrando o ECR no Portainer
No Portainer:
- Registries → Add registry, tipo AWS ECR
- Informar as credenciais do usuário read-only e a região
- Salvar
O Portainer se encarrega de renovar o token de autenticação do ECR automaticamente (ele expira a cada 12h).
Subindo o container pela primeira vez
Em Containers → Add container, seleciona-se o registry cadastrado, o repositório e a tag desejada. Um ponto de atenção aqui: o campo de imagem espera repositorio:tag — a URL do registry já é preenchida automaticamente pelo Portainer a partir do registry selecionado. Tentar digitar a tag isoladamente (ex: só latest) gera erro de referência inválida, porque o Portainer concatena o valor ao path do repositório.
Configurações do container:
- Port mapping: porta interna do host (ex:
3000) → porta exposta pela aplicação (ex:8080) - Restart policy:
unless-stopped
Essa política reinicia o container automaticamente após falhas ou reboot da instância, mas não o reinicia se ele for parado manualmente — diferente de always, que reinicia mesmo após parada intencional.
Atualizando o container em deploys futuros
Após um novo docker push, no Portainer: seleciona-se o container → Recreate → marca-se “Pull latest image”. Isso refaz o container com a imagem mais recente, mantendo porta, nome e demais configurações.
Container webhooks (que permitiriam disparar esse recreate via uma chamada HTTP simples, útil em pipelines de CI/CD) fazem parte da Business Edition do Portainer — que, vale registrar, é gratuita para até 3 nós, sem limite de tempo. Uma alternativa sem depender disso é rodar o pull/stop/rm/run diretamente via SSH a partir do pipeline de CI.
6. HTTPS com Let’s Encrypt
Como o container passa a escutar numa porta interna (não mais diretamente na 80), um reverse proxy na própria instância cuida do SSL.
Instalação:
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx -y
Configuração do site (/etc/nginx/sites-available/meu-servico):
server {
listen 80;
server_name meuservico.exemplo.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
sudo ln -s /etc/nginx/sites-available/meu-servico /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Emissão do certificado:
sudo certbot --nginx -d meuservico.exemplo.com
O Certbot detecta a configuração do Nginx, emite o certificado e já configura o redirecionamento HTTP → HTTPS. A renovação automática é instalada junto (via cron ou systemd timer), e pode ser validada com:
sudo certbot renew --dry-run
Considerações finais
O fluxo inteiro — ECR para armazenar imagens, Portainer como camada de gerenciamento visual dos containers, Nginx + Certbot para TLS — é uma combinação simples de montar e barata de manter, especialmente para quem já tem uma única instância EC2 e não quer (ainda) a complexidade de um orquestrador completo como ECS ou Kubernetes. Para cenários com múltiplos serviços ou necessidade de deploy automatizado via webhook, vale considerar migrar para a Business Edition do Portainer ou integrar o pull/redeploy diretamente no pipeline de CI/CD via SSH.
Deixe um comentário