A gestão eficiente de cache é um dos pilares fundamentais para o desempenho de aplicações web modernas. Quando você atualiza conteúdo estático — como imagens, CSS, JavaScript ou documentos HTML — em sua origem (S3, EC2, Elastic Load Balancer), o CloudFront pode servir versões obsoletas aos usuários finais se as distribuições não forem invalidadas corretamente. Embora a CDN ofereça mecanismos de expiração baseados em TTL (Time to Live), forçar uma invalidation é frequentemente necessário para correções urgentes ou atualizações críticas.
A abordagem manual pela AWS Console é viável para cenários esporádicos, mas torna-se um gargalo operacional em ambientes DevOps onde a entrega contínua exige consistência e velocidade. Neste tutorial técnico, demonstraremos como automatizar o processo de invalidação do CloudFront utilizando Python e a biblioteca boto3. Esta solução permite que scripts de CI/CD, Lambda Functions ou servidores de build executem a limpeza de cache de forma programática, garantindo que seus usuários recebam sempre a versão mais recente dos ativos.
Pré-requisitos e Configuração do Ambiente
Antes de escrever o código, é essencial garantir que seu ambiente de desenvolvimento esteja preparado com as dependências corretas e as credenciais de acesso adequadas. O boto3 é a AWS SDK para Python, e ela interage com a API do CloudFront para criar e gerenciar invalidações.
Inicie instalando a biblioteca em seu ambiente virtual ou sistema local. Utilize o gerenciador de pacotes pip para garantir que a versão mais estável seja baixada:
pip install boto3
Além da instalação da biblioteca, a autenticação é um passo crítico. O boto3 utiliza o padrão de credenciais da AWS. Certifique-se de que suas variáveis de ambiente ou arquivo de configuração local (~/.aws/credentials) contenham uma AWS_ACCESS_KEY_ID e uma AWS_SECRET_ACCESS_KEY válidas. Essas credenciais devem pertencer a um usuário IAM com permissões específicas para o CloudFront.
O política IAM mínima necessária deve incluir as ações cloudfront:CreateInvalidation e cloudfront:GetInvalidation. Restringir o acesso apenas à distribuição específica melhora a segurança da sua infraestrutura. Sem essas permissões, seu script retornará erros de AccessDenied, interrompendo o fluxo de automação.
Estrutura do Script Python
A lógica central reside na criação de uma instância do cliente CloudFront e na construção da estrutura de dados exigida pela API da AWS. O código abaixo serve como base robusta para integração em pipelines de deploy.
import boto3
import time
from botocore.exceptions import ClientError
def create_cloudfront_invalidation(distribution_id, paths, comment=""):
"""
Cria uma invalidação no CloudFront e aguarda sua conclusão.
:param distribution_id: ID da distribuição do CloudFront
:param paths: Lista de caminhos a serem invalidados (ex: ['/index.html', '/*'])
:param comment: Comentário opcional para rastreamento
:return: ID da invalidação criada
"""
client = boto3.client('cloudfront')
# Estrutura do pedido exigida pela API
invalidation_request = {
'DistributionId': distribution_id,
'InvalidationBatch': {
'Paths': {
'Quantity': len(paths),
'Items': paths,
'IsTruncated': False
},
'CallerReference': str(time.time()).replace('.', '') # Referência única obrigatória
}
}
if comment:
invalidation_request['InvalidationBatch']['Comment'] = comment
try:
response = client.create_invalidation(**invalidation_request)
invalidation_id = response['Invalidation']['Id']
print(f"Invalidação {invalidation_id} iniciada para a distribuição {distribution_id}")
return invalidation_id
except ClientError as e:
print(f"Erro ao criar invalidação: {e.response['Error']['Message']}")
raise
Neste script, observamos dois pontos técnicos cruciais. Primeiro, o parâmetro CallerReference. A API do CloudFront exige que cada requisição de criação tenha uma string única que identifique a operação durante um período mínimo de 5 minutos. Isso previne a criação acidental de duplicatas se o script for reexecutado por engano. Utilizamos o timestamp atual como fonte dessa unicidade.
Segundo, a estrutura InvalidationBatch. Ela define quais caminhos serão afetados. O CloudFront suporta invalidações parciais (caminhos específicos) ou totais (todos os objetos). Definir IsTruncated como False indica que a lista de paths é completa e não há mais itens ocultos, o que é o padrão para a maioria dos casos de uso.
Executando a Invalidação e Verificando o Status
Apenas criar a invalidação não garante que o cache foi limpo no momento da chamada. O processo é assíncrono. Para garantir que seu deploy só prossiga quando o conteúdo estiver atualizado globalmente, você deve implementar uma função de espera (waiter) ou verificar o status manualmente.
Abaixo, apresentamos a implementação completa que combina a criação com a verificação de status até que a invalidação atinja o estado Completed.
def wait_for_invalidation_completion(client, distribution_id, invalidation_id):
"""
Aguarda até que a invalidação seja concluída.
:param client: Instância do cliente boto3 cloudfront
:param distribution_id: ID da distribuição
:param invalidation_id: ID da invalidação
"""
waiter = client.get_waiter('invalidation_completed')
try:
waiter.wait(DistributionId=distribution_id, Id=invalidation_id)
print(f"Invalidação {invalidation_id} concluída com sucesso.")
except Exception as e:
print(f"Falha ao aguardar conclusão da invalidação: {e}")
# Exemplo de uso integrado
def main():
distribution_id = 'E1234567890ABC' # Substitua pelo seu ID real
paths_to_invalidate = ['/images/logo.png', '/css/style.css']
try:
inv_id = create_cloudfront_invalidation(
distribution_id=distribution_id,
paths=paths_to_invalidate,
comment="Deploy v1.2.3"
)
# Aguarda a propagação global da limpeza de cache
client = boto3.client('cloudfront')
wait_for_invalidation_completion(client, distribution_id, inv_id)
except Exception as e:
print(f"Operação falhou: {e}")
if __name__ == "__main__":
main()
A função get_waiter é uma abstração poderosa do boto3. Ela gerencia o loop de polling internamente, verificando o status da invalidação a intervalos regulares até que o estado mude para Completed ou ocorra um erro. Isso elimina a necessidade de escrever loops manuais com time.sleep(), tornando o código mais limpo e eficiente.
Estratégias de Paths e Boas Práticas
A escolha dos caminhos na lista paths impacta diretamente o custo e a latência da operação. O CloudFront cobra por quantidade de objetos invalidados, não pelo tamanho dos arquivos. Portanto, invalidar paths muito amplos pode gerar custos elevados.
Invalide apenas o necessário: Se você atualizou apenas um arquivo CSS, invalide apenas esse caminho específico em vez de usar /*. Isso preserva o cache para os demais ativos que não foram alterados, mantendo a performance da CDN para usuários que acessam outras partes do site.
Evite invalidações frequentes: A AWS impõe limites de taxa (rate limits) para operações de invalidação. Geralmente, você pode criar até 1000 invalidações por dia por distribuição. Se seu pipeline de CI/CD for muito agressivo, considere ajustar a estratégia de versionamento dos arquivos (ex: hash no nome do arquivo style.v2.css) para evitar a necessidade de invalidação contínua.
Uso de Wildcards: O CloudFront suporta o caractere curinga * no final do caminho. Por exemplo, /assets/* invalidará todos os objetos dentro da pasta assets. Isso é útil para limpar diretórios inteiros sem precisar listar cada arquivo individualmente.
Integração com CI/CD e Automação
Com o script Python validado, a integração em ambientes de produção torna-se trivial. Em pipelines como Jenkins, GitLab CI ou GitHub Actions, você pode salvar este código como um módulo executável e chamá-lo após o upload dos novos ativos para o bucket S3 ou servidor de origem.
No ambiente GitHub Actions, por exemplo, a configuração seria:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: pip install boto3
- name: Invalidate CloudFront Cache
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
run: python invalidate_cloudfront.py
Note a importância das variáveis de ambiente. Nunca hardcode credenciais no script. Utilize segredos gerenciados pela plataforma de CI/CD para injetar as chaves de acesso durante a execução. Isso mantém sua infraestrutura segura e auditável.
Troubleshooting Comum
Ao implementar essa automação, você pode encontrar erros recorrentes. O mais comum é o DistributionNotDisabled, que ocorre se você tentar invalidar uma distribuição em estado incorreto (raro para invalidações, mas possível em configurações de borda). Outro erro frequente é o InvalidArgument, geralmente causado por paths mal formatados. Lembre-se: os paths devem começar com barra (/) e não conter espaços desnecessários.
Se a invalidação ficar presa no estado In Progress por um tempo anormalmente longo, verifique se há muitos objetos sendo servidos simultaneamente ou se o limite de taxa diária foi atingido. Em casos extremos, entre em contato com o suporte da AWS, mas na vasta maioria das situações, a paciência e a revisão dos logs do CloudTrail são suficientes para diagnosticar o problema.
Conclusão
A automação de invalidações no CloudFront via boto3 é uma ferramenta poderosa para engenheiros de infraestrutura e desenvolvedores. Ela transforma um processo manual propenso a erros em um passo confiável do pipeline de entrega, garantindo que as atualizações cheguem aos usuários finais sem atrasos indesejados causados por cache antigo.
Ao seguir as práticas recomendadas — validar permissões IAM, usar CallerReference únicos e invalidar apenas os recursos alterados — você otimiza custos e mantém a integridade da performance da sua aplicação. Implemente este script hoje e eleve o nível de maturidade DevOps do seu ambiente AWS.