Skip to content

Instantly share code, notes, and snippets.

@tapajos
Created February 12, 2012 23:05
Show Gist options
  • Save tapajos/1811460 to your computer and use it in GitHub Desktop.
Save tapajos/1811460 to your computer and use it in GitHub Desktop.
Migrando do Heroku para a Amazon Web Services

Migrando do Heroku para a Amazon Web Services

Recentemente eu comentei sobre a migração que fizemos no Myfinance saindo do Heroku e indo para a Amazon Web Services e diversas pessoas me perguntaram as razões pela qual fizemos essa mudança. O objetivo desse post é apenas explicar a decisão que a nossa equipe tomou, como foi e as conseguências dessa migração.

Antes de começar eu gostaria de deixar claro que o objetivo desse texto não é comparar hospedagens e nem dizer que uma é melhor do que a outra. Trata-se apenas do relato de uma experiência.

A motivação

Antes de explicar a motivação por trás da migração é interessante mostrar como era a nossa estrutura.

  • Rails 3.0.X
  • Postgres (Heroku shared)
  • Heroku
  • DelayedJob com alguns workers
  • Memcached (Heroku add-on)
  • Websolr (Heroku add-on)
  • Cron
  • Airbrake (Heroku add-on)

Em um dado momento começamos a receber um número significativo de SIGTERM sem nenhum explicação. Após abrir um ticket no suporte do Heroku descobrimos que a razão pela qual nossos processos estavam sendo terminados era um excesso de carga no Postgres compartilhado.

A tentativa de contornar o problema

Embora nossa aplicação realmente tenha crescido em número de usuários nós não estavamos confortaveis com a solução de contratar um banco de dados dedicado por duas razões. A primeira delas era o custo extra de $200,00 (sim, o banco de dados do Heroku é excessivamente caro) e em segundo lugar porque a quantidade de usuários ainda não era algo que justificasse essa carga tão elevada no banco.

Após analisar detalhes específicos da nossa aplicação constatamos que essa carga excessiva no banco de dados tinha como origem nossa fila de processos em background. O DelayedJob utiliza o mesmo banco de dados da aplicação e nossa aplicação faz um uso muito grande de filas.

Não nos restava outra alternativa a não ser utilizar um armazenamento diferente para nossa fila e a opção mais lógica era a dupla Redis + Resque.

Realmente em um primeiro momento essa opção de mostrou acertada e imediatemente após um deploy com a nova estrutura de fila a carga no nosso banco caiu absurdamente e ao que tudo indicava o problema estava solucionado.

O novo problema

Como diz o ditado, "alegria de pobre dura pouco". A ilusão do problema resolvido e de um fim de semana tranquilo não durou nem 24 horas.

Existe um problema com o uso do Resque no Heroku. Não vou me preocupar em relatar esse problema pois exitem dois tickets bastante explicativos: Veja em Heroku sends SIGTERM to Resque e prune_dead_workers doesn't work on heroku cedar.

Não preciso nem dizer que esse novo problema se manifestou exatamente em uma sexta feira e levou eu e o Vanderson a passar um final de semana inteiro de "babá de software". Passamos o final de semana todo monitorando e reiniciando os nossos workers.

Sabendo que o problema não era simples e que a solução para ele não dependia apenas da nossa equipe eu acabei tendo que apelar para os meus amigos Pedro Belo e Chris Stolt(o Chris era o único cara na EngineYard que resolvia os meus problemas).

Após algumas conversas com eles eu cheguei em uma situção onde eu tinha uma solução que dependia de uma informação que a API deles não tinha como me fornecer naquele momento. Com o sistema em produção nós não podíamos esperar e tivemos que partir para o plano B.

Quero deixar aqui registrado um MUITO OBRIGADO ao Pedro Belo e Chris Stolt que foram sensacionais.

Porque a Amazon Web Services?

A resposta é simples, porque lá na Myfreecomm já estávamos usando a Amazon Web Services (em outros projetos) e porque tinhamos toda uma infraestrutura já pronta que já resolvia os nossos principais problemas(deploy, setup de maquina e etc.).

O processo de migração

Migrar um projeto em produção nunca é simples e sabendo disso nós tentamos elaborar um checklist com tudo necessário para minimizar o impacto dessa migração.

O primeiro passo foi migrar todos os add ons que a gente tinha contratado no Heroku para contas diretamente com o prestador do serviço. Esse passo foi super tranquilo.

Nosso principal desafio era (sempre é) o banco de dados. Como nós não queríamos ter que nos preocupar em criar uma maquina de banco de dados e cuidar de todos os problemas que iriam surgir (backup, escalabilidade, redundância) decidimos por usar o RDS. Porém para usar RDS era necessário migrar nossa aplicação para utilizar o Mysql.

Nesse momento eu tenho que confessar que eu estava desejando estar no lugar do Rodrigo(enchendo a cara na Oktoberfest lá em Munich). Mas infelizmente eu não tinha essa opção então me juntei ao Vanderson e o DX7 para fazer nossa aplicação rodar com o Mysql.

Para a minha supresa nossa aplicação funcionou 100% com o Mysql! As únicas coisas que quebraram foram alguns testes porque nós fomos IDIOTAS de olhar o SQL gerado por alguns scopos.

Pode parecer estranho tudo ter funcionado na mudança de banco mas existe uma explicação para isso. Mesmo a nossa aplicação tendo algumas queries bastante complexas elas são geradas utilizando a o ActiveRecord e o Arel e isso se mostrou uma sábia decisão. Se eu puder deixar uma única dica para você que está lendo isso ela é: Não escreva em SQL, gere o SQL!

Resolvido o problema da aplicação o passo seguinte era a migração dos dados já existentes. Para essa tarefa contamos com o apoio do Vitor Mazzi que com meia dúzia de comandos e expressões regulares conseguiu migrar nossa base.

Foi nesse momento que eu e o Vitor Mazzi percebemos algo que estava na nossa cara desde o início. A migração poderia ser fatiada em partes e a primeira dela seria por no ar uma versão (ainda no Heroku) usando o Mysql do RDS. Após alguns testes em sandbox colocamos no ar essa versão e tivemos um downtime de apenas 15 minutos.

Nesse momento também migramos para usar o ElasticCache mas esse passo nem merece destaque já que foi apenas trocar uma URL.

O passo seguinte foi migrar os nossos workers para uma instância do EC2. Com o auxilio da equipe do Vitor Mazzi fizemos as modificações necessárias no nosso gerenciador de deploy. Após algumas idas e vindas conseguimos subir uma maquina para processar nossos jobs e desligamos os workers do Heroku.

Após esse passo nosso problema principal estava resolvido. Agora nossos workers funcionavam perfeitamente na Amazon e nossa parte WEB no Heroku. Agora com um peso muito menor nas costas trabalhamos para subir as maquinas WEB na Amazon e trocamos nosso DNS. A parte legal é que como era a mesma versão no Heroku e na Amazon (ambas usando o RDS) o downtime dessa etapa foi zero.

Como passo final tivemos que ajustar algumas tarefas de manutenção e alguns scripts mas nada que mereça destaque.

Conclusão

O processo de migração foi trabalhoso e cheio de desafios porém nada de outro mundo. O maior desafio foi manter a aplicação no ar o maior tempo possível e acho que dado ao tamanho deste desafio saimos vitoriosos com um downtime de apenas 15 minutos.

A maior surpresa que nós timemos é que nosso response time caiu pela metada após a migração. Não vou discutir esse ponto pois ele envolve diversos aspectos técnicos.

Da data da migração até hoje nossa estrutura mudou um pouco, se estabilizou e a decisão de migrar para a Amazon se mostrou acertada. Desde então não tivemos nenhum problema com os nossos servidores e estamos mantendo um excelente SLA.

Como tudo na vida essa migração deixou bem claro para a gente os lados posítivos e negativos de cada uma das duas arquiteturas e isso irá balisar nossas decisões futuras.

@adrianoalmeida7
Copy link

Bem interessante o relato, Tapajos. Só pra complementar a leitura, tem um post do ano passado no blog da Engine Yard que os caras falam de alguns problemas em usar banco como fila. Vale uma lida. http://www.engineyard.com/blog/2011/5-subtle-ways-youre-using-mysql-as-a-queue-and-why-itll-bite-you/

@tapajos
Copy link
Author

tapajos commented Feb 13, 2012

@adrianoalmeida7 Acabei de ler o texto que você indicou.

Os problemas relatados pelo cara vão além de usar banco como fila, são problemas de design da aplicação. Acho que ele misturou bem as coisas.

@fnando
Copy link

fnando commented Feb 13, 2012

perguntem ao @gleicon o que ele acha sobre db como fila.

@tapajos
Copy link
Author

tapajos commented Feb 13, 2012

@gleicon, o que você acha de db como fila?

PS: Se quiser xingar alguém faça isso com o @fnando. Foi ele quem mandou eu perguntar.

@tapajos
Copy link
Author

tapajos commented Feb 13, 2012

@fnando

Com certeza a melhor opção não é usar o db como fila mas em alguns momentos algumas concessões precisam ser feitas em pro de validar o produto no ar.

Para muitos casos o uso de sistema de fila em banco, como DelayedJob, é suficiente e pode sobreviver por muito tempo(o GitHub usou por muito tempo), porém esse não foi o nosso caso.

@brunogh
Copy link

brunogh commented Feb 16, 2012

Bacana o post! Usamos uma infra parecida... estavas recebendo o SIGTERM pelo Airbrake?

@tapajos
Copy link
Author

tapajos commented Feb 16, 2012 via email

@gilsondev
Copy link

@tapajos a quanto tempo ficou usando a Heroku? Quando vai aumentando o acesso de usuários em um determinado sistema web, não fica muito caro não? Essa é a minha dúvida.

@tapajos
Copy link
Author

tapajos commented Jun 19, 2012

@gilsondev Não sei te dizer com certeza absoluta mas provavelmente uns 6 meses. A questão de aumentar e ficar caro é complicado de avaliar, cada sistema é um sistema e você tem que ver os recursos que você está consumindo e como eles crescem com o número de usuários.

@brunogh
Copy link

brunogh commented Jun 19, 2012

Estou usando há 1 ano e já colocaria alguns custos iniciais de uma aplicação web em produção:

+1 web dyno - $36
1 worker (background) - $36
SSL - $20
production database - $50

A partir disto teria que ver como a sua aplicação escala para questão dos dynos/workers e os serviços adicionais necessários (ex: email, new relic, notificação de bugs, etc). Mesmo com este custo, para muitos ainda vale a pena, daí também depende o modelo de negócios.

@gilsondev
Copy link

Tudo bem, eu entendo isso. E mesmo assim com os valores que o @brunogh colocou, dá para ter uma noção do que se paga.

No seu caso @brunogh você tem quando acessos mensais em média? Se puder falar eu agradeço.

@brunogh
Copy link

brunogh commented Jun 20, 2012

@gilsondev isto sem falar em duplicar o banco de produção para ter redundância, o que dobraria o valor. Esta escala no banco também aumenta com a necessidade de mais cache (https://postgres.heroku.com/pricing), por aí vai.

Com relação aos dynos, muito também depende de ter um tempo de resposta rápido, já que um timeout no Heroku é de até 30 segundos por padrão. Ou seja, a demora para responder impacta em travar o dyno e piorar a experiência com o usuário. Usamos o New Relic para investigar gargalos e otimizar, seja no banco, código, etc.

Existe um case bem legal no Heroku que é o Rapportive (http://success.heroku.com/rapportive), recentemente comprado pelo LinkedIn. Em fevereiro de 2012 eles serviam 65 milhões de requisições para a sua siderbar e processavam 5 bilhões de mensagens por mês com 50 dynos e 15 workers.

A quantidade de dynos vai depender de ter um bom tempo de resposta e a quantidade de usuários da tua app, por exemplo, neste caso se o próprio trial/cadastro tem algum controle ou se é aberto.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment