Orquestrando e configurando containers docker com o ECS na AWS.

Bruno Alves
Huia
Published in
8 min readNov 4, 2018

Ao longo dos últimos anos tenho desenvolvido aqui na @Huia projetos web em que a performance e a capacidade de atender uma quantidade muito grande de usuários simultâneos é requisito principal em termos de infraestrutura. Com isso vem o desafio de alinhar esforços de aplicação e infra para suportar e dar uma boa experiência para muitos usuários, mais de 90k simultâneos no nosso caso.

Analytics dos primeiros 15d de projeto

Nos últimos 4 projetos onde a performance era requisito principal usamos o ECS da AWS para fazer a osquestração e o controle de autoscaling dos containers. O resultado foi bem legal, e surgiu a ideia de dividir um pouco da experiência.

Daqui pra frente o assunto é mais técnico, e tento da um overview dos produtos e features da AWS que precisaram ser estudados e implementados pra que o objetivo fosse alcançado.

Repositório de containers

O ECR (Elastic Container Registry) é o repositório dos containers docker da AWS, pra utilizar o ECS precisamos manter nossas imagens docker no ECR.

Nessa etapa já entra o primeiro learning: É preciso cuidar para que a imagem do container da aplicação e todos os pacotes rodando seja sempre o menor possível em MB.

Imagens armazenadas no ECR — média de 57/58MB

Nossa imagem de container de uma das camadas da aplicação, que é a que mais escala tem 57–58MB em média. Usamos a dist do linux Alpine para esse container. O ECS sobe uma nova task usando essa dist cerca de 25% mais rápido(em 15–20s) do que nossa antiga versão de container que usava Ubuntu e tinha aproximadamente 290MB.

Não estou criticanto ou apoiando as dists, mas o tamanho final faz diferença quando você precisa escalar muito rápido, em segundos.

Task Definition

Uma task é o paralelo a uma aplicação, ou a uma parte dela no caso de um microserviço por exemplo. Ela pode ser composta de 1 ou mais containers.

Cada Task Definition possui N revisões, que pode ser tratado como cada versão ou deploy novo da aplicação.

Dentro do lifecycle da nossa aplicação, para cada release uma nova revisão de Task é criada. A revision mantem o estado do container vinculado a ela naquele momento. Então na prática a gente pode ter a mesma aplicação/serviço rodando com versões diferentes se quisermos.

Fargate ou EC2

A primeira escolha quando estamos criando a definição de uma task, é o tipo de instância que vamos executar. Task compatíveis com EC2 ou no modo Fargate.

Escolha do tipo de instância — Fargate ou EC2

Nós optamos pelo Fargate, que abstrai a camada infraestrutura. Foi essencial para o sucesso do projeto não termos de planejar e escalar outra camada de Infraestrutura. Explicando: quando optamos pelo Fargate paramos de nos preocupar em que tipo e tamanho de instancia nossos containers serão executados, nós apenas definimos a quantidade de CPU e Memory que nossa task possui, e a AWS escala dentro da infraestrutura dela sem que precisemos apontar em qual instância do EC2 nossos containers serão executados. Nesse cenário não contratamos instâncias T2, M3, R4, etc, isso é transparente.

Cluster e Service

Depois de configuradas as tasks e de registrados os containers, a administração da Infra passa a ser a administração do Serviço. A lógica é mais ou menos assim: Um cluster pode conter N Services que é encarado como uma camada da nossa aplicação/microserviço, e um Service pode conter N instancias da mesma task definition. É possível rodar versões diferentes de Task dentro do cluster sem ser servida pelo seu Service mas não é uma prática legal para ambiente produtivo, ao menos não seria um padrão. Isso porquê quando automatizamos o deploy da aplicação é o Service que recebe uma atualização com a nova revisão da Task, e ai ele se atualiza sozinho (com base nas nossas definições). Se a task roda fora do Service ele não atualizará para a nova versão apenas atualizando o serviço. Pode ser feito, mas não é prático.

Diagrama dos objetos do ECS

Quando a definição de um Service é criada, duas configurações se destacam:

Load Balancer e Target Group, que é como a aplicação será exposta na Internet, e as regras de Health Check e Auto Scaling.

A configuração do Load Balancer na AWS é bem intuitiva e não oferece desafios para configurar, o mais importante no nosso caso foi ter definido antes as regras do Security Group e VPC para garantir a segurança da aplicação.

Esse é um learning, antes de sair mexendo no ECS, crie e configure uma nova VPC com seus acessos bem definidos e redes separadas, caso contrário, você precisará recriar seus Cluster e Services para utilizar uma nova VPC.

Health Check

Durante a criação do Service, uma das etapas é configurar o path da sua aplicação que será acessado para verificar se tudo está funcionando bem. No primeiro projeto que publicamos criamos uma rota “/ping” que retornava um HTTP200 apenas para garantir que a aplicação tinha subido numa boa e estava funcionando. Com o tempo fomos melhorando e implementando alguns checks internos.

Ainda nessa etapa de configuração é necessário configurar um item muito importante: “Health check grace period”, que é o tempo que o ECS deve ignorar o Health Check do balanceador quando uma nova task é startada. Essa configuração vai mudar de projeto para projeto. Você que está configurando precisa monitorar o tempo médio que o ECS leva para startar a sua task mais o tempo que sua aplicação ou serviço leva para ficar disponível para uso.

Algumas outras configurações são muito interessantes no Service do ECS:

“Number of Tasks” — É a quantidade de tasks que queremos rodando no nosso Cluster. Bem intuitivo :)

“Minimum healthy percent” — É o percentual mínimo de Tasks rodando durante um novo deploy no seu Serviço. Isso é muito legal, basicamente o ECS automatiza o deploy de uma nova versão de task, e esse percentual vai dizer o número de tasks a serem mantidas durante a criação das novas. Na prática: Se você tem 4 tasks rodando e setou 50% como minimun healthy, quando iniciar um deploy o ECS vai derrubar 2 tasks da versão antiga, desregistrar do Load Balancer, criar novas 2 Tasks da nova versão, registrar no load balancer e repetir o processo para as demais tasks até que todas as tasks rodando do seu Service sejam da nova versão.

“Maximum percent” — Limite máximo de tasks rodando durante um deploy. Ele funciona junto do minimum healthy percent. Usando o exemplo acima do serviço com 4 tasks rodando e o maximum percent setado para 200% na prática as novas 4 tasks serão criadas sem derrubar as antigas, e a medida que as novas são registradas no balanceador as antigas vão sendo desregistradas e paradas até que todas as tasks rodando do seu Service sejam da nova versão.

Configuração de healthy das tasks

Auto Scaling

Escalar uma aplicação ou serviço depende bastante do comportamento dos usuários ou dos horários de maior pico de acessos na aplicação, enfim, depende bastante do negócio. Técnicamente é necessário identificar previamente qual o seu gargalo em termos de recurso de infraestrutura quando a quantidade de acessos aumenta, se é CPU, memória, banda, etc.

A configuração de AutoScaling disponível na AWS é bastante robusta, e no nosso caso a quantidade de tasks escala conforme uso da CPU que é o principal recurso da nossa aplicação.

A melhor configuração que chegamos é de escalar em 2 tasks adicionais quando o uso médio de CPU a cada 3 minutos é superior a 60%.

Também é possível configurar regras para Scale Out, e no nosso caso configuramos para que uma task seja removida a cada 5 minutos desde que a CPU esteja operando em 30% ou menos em média.

Logs

Fazer troubleshooting em ambiente de produção só é possível com uma captura de logs planejada.

Da aplicação: Na configuração dos containers dentro da criação da Task Definition é possível enviar a saída de Logs do docker para o driver de logs do CloudWatch. A AWS automatiza a captura, mas é necessário a aplicação estar configurada para tal ;)

Output de log no CloudWatch

Do Load Balancer: Nas configurações do Load Balancer é possível configurar o output de logs e escolher um Bucket no S3 para ser armazenado. Por padrão essa opção não está ativada, pois a escrita de log vai sempre reduzir um pouco da performance, mas está alí para quando for necessário utilizar.

Output de logs do LoadBalancer

Do Service no ECS: Na aba Events do Service tem uma lista em ordem decrescente do que está acontecendo. Novas tasks, tasks que foram paradas (automaticamente pelo Scale Out ou por problemas), etc.

Service Events

Das Tasks no Service: Se uma task é marcada com o Status Stopped é possível ver o último status dela, nesse caso é possível ter uma ideia do Erro. Eu digo ter uma ideia pois nesses logs das tasks individuais é onde acredito que tem muito pra AWS melhorar ainda. Você tem uma ideia pelo log do último status mas não dá pra saber exatamente o que aconteceu. Seria legal ter uma opção como um Verbose ou Troubleshooting Logs para ser ativada e ver no detalhe o que ocorreu.

Log das Tasks com status stopped

Por fim, é mais do que necessário manter alguns dashboards para monitorar o que está acontecendo em termos de recursos, e a experiência com o CloudWatch é muito boa, esse é um ponto bem positivo. A captura de métricas, possibilidade de dashboards e a granularidade da captura dos logs são excelentes.

Dashboard do CloudWatch

Números em 15 dias de projeto:

Page views: 58Milhões

Max. usuários simultâneos: 94Mil

Total de tasks escaladas: 48

--

--