Se você acompanhou a série
até este ponto, nosso aplicativo Linux de amostra foi migrado para a
nuvem e configuramos alguns recursos de confiabilidade básicos.
Neste artigo, o terceiro da série sobre migração de um
aplicativo para a nuvem Amazon, é o momento de aproveitar a natureza
dinâmica da nuvem fazendo crescer e reduzindo a infraestrutura em
resposta à carga e empurrar alguns dos recursos estáticos para a borda
da nuvem.
A Parte 1 desta série começou observando uma oferta software como serviço chamada
SmallPayroll.ca. O aplicativo foi escolhido e movido para o Amazon Elastic Compute Cloud (Amazon EC2). Depois, na Parte 2, o aplicativo foi tornado mais robusto com a inclusão de redundância, backups e discos mais confiáveis.
Agora é hora de olhar além da estabilidade e em direção à
escalabilidade. Como os servidores são alugados por hora, faria mais
sentido executar servidores extras somente quando necessário? E quanto
às tarefas processadas de maneira assíncrona?
As redes de distribuição de conteúdo (CDNs), que
armazenam em cache conteúdo estático na borda da rede, tinham alto
custo. A computação em nuvem tornou as CDNs acessíveis até mesmo para o
menor dos sites. Ocorre um grande impulso no desempenho quando é usada
uma CDN, por isso vamos configurar uma também.
Implementação automática
Um elemento principal ao se fazer a infraestrutura crescer
dinamicamente é que uma nova instância precisará ser anexada ao ambiente
de produção sem intervenção humana.
Seria possível reconfigurar os
pacotes de uma Amazon Machine Instance (AMI) continuamente após cada
mudança, como uma implementação de código ou mudança do banco de dados.
No entanto, um pouco de script significa que um servidor pode
implementar a si próprio sem muito trabalho.
SmallPayroll.ca tem um processo de implementação semelhante à maioria dos outros aplicativos:
- Puxe o código do repositório, como Subversion ou git.
- Inicie os servidores de aplicativos.
- Verifique se o aplicativo iniciou corretamente.
- Inclua o novo servidor no conjunto do balanceador de carga.
A instância terá também de configurar um servidor de banco de dados em
/etc/hosts.
Puxando o código do repositório
O código base SmallPayroll.ca está armazenado em um repositório
Subversion.
Subversion é um sistema de controle de código base que
controla as mudanças no código base e permite aos desenvolvedores
repartir e mesclar código para trabalhar em recursos em um ambiente
separado.
No nível mais simples, um aplicativo Rails pode executar
diretamente de uma cópia verificada do código de origem. Quando forem
feitas mudanças, o servidor de produção executa uma atualização e é
reiniciado.
Uma Ruby Gem chamada
Capistrano gerencia essas implementações de
uma maneira que permite controle centralizado de múltiplos servidores e
retrocesso fácil se forem encontrados problemas.
Em vez de verificar o código manualmente, um novo servidor autoinicializa o
Capistrano. Isso é mais trabalho diretamente, mas significa que Capistrano pode mais tarde gerenciar o servidor com muita facilidade.
A Listagem 1 mostra o conteúdo do
Capfile inicial, que será colocado no diretório inicial do usuário da folha de pagamento.
Listagem 1. Um Capfile para autoinicializar a implementação do Capistrano
load 'deploy' <br /><br /># Where is the code? This will need to be customized!<br />set :repository, "http://svn.smallpayroll.ca/svn/payroll/trunk/"<br />set :scm_username, "DEPLOY_USERNAME"<br />set :scm_password, "DEPLOY_PASSWORD"<br /><br /># Deploy to the local server<br />server "localhost", :app, :web, :db, :primary => true<br /><br /># By default Capistrano tries to do some things as root through sudo - disable this<br />set :use_sudo, false<br /><br /># Define where everything goes<br />set :home, '/home/payroll'<br />set :deploy_to, "#{home}"<br />set :rails_env, "production"O Capfile é executado pelo cap. Capistrano espera que estará usando o
Secure Shell (SSH) para conectar a um servidor, mesmo se
for o host local.
A Listagem 2, que deve ser executada como o usuário do
aplicativo, prepara o servidor de modo que o usuário do aplicativo pode
usar SSH para conectar ao servidor local como ele próprio e as tarefas
do Capistrano podem ser executadas.
Listagem 2. Preparando o ambiente
# Create the SSH directory and generate a secure key pair<br />mkdir .ssh<br />chmod 700 .ssh<br />ssh-keygen -b 1024 -t dsa -q -f .ssh/id_dsa -N ""<br /><br /># Allow the user to SSH in to the server without a password<br />cat .ssh/id_dsa.pub >> .ssh/authorized_keys<br /># SSH is very fussy about permissions! Nothing should be readable<br /># by other users<br />chmod 600 .ssh/*<br /><br /># SSH in once, ignoring host keys, so that the server will save the host key<br />ssh 127.0.0.1 -o StrictHostKeyChecking=false /bin/falseA Listagem 2 está dividida em três seções:
- Os dois primeiros comandos criam um diretório SSH
que pode ser lido somente pelo usuário do aplicativo. O terceiro comando
cria uma chave Digital
Signature Algorithm (DSA) de 1024 bits
(-b 1024 -t dsa), desliga a saída
(-q) e especifica o nome da chave e que não há senha. - Copia a chave pública do usuário para o arquivo,
o que permite ao usuário que possui a chave privada correspondente
efetuar login sem uma senha. Certifique-se de que ninguém mais pode ler
arquivos no diretório. - Sem uma chave de host salva, SSH e prompt do
Capistrano para verificar a chave, o
que quebra a implementação automatizada. O último comando efetua login
no servidor local, ignorando chaves de host e executa
/bin/false. Isso é suficiente para salvar a chave de host.
A etapa final do processo cria o ambiente e implementa a versão atual do aplicativo. A Listagem 3 mostra como usar o
Capistrano para cuidar dessas tarefas.
Listagem 3. Implementando o aplicativo com Capistrano
cap deploy:setup<br />chmod 700 /home/payroll<br />cap deployA Listagem 3 executa a tarefa deploy:setup, que cria a estrutura de diretório debaixo do diretório inicial do aplicativo que consiste nos diretórios denominados shared e releases.
Cada implementação vai em seu próprio diretório na pasta
de releases. O diretório compartilhado é usado para logs e outros
elementos que podem ser compartilhados entre implementações separadas.
Antes de implementar o aplicativo, o segundo comando altera as permissões no diretório inicial para ser 700.
Esse é um artefato de implementar a para o diretório
inicial em vez de um subdiretório, pois a tarefa de configuração abre
as permissões do diretório de implementação para permitir gravações de
grupo. O daemon SSH não permite autenticação baseada em chave do
diretório inicial se pessoas diferentes do usuário puderem gravar nele.
A tarefa
deploy é finalmente executada, o que
verifica o código fora de Subversion, coloca-o em um diretório de
release e faz um link simbólico (symlink) chamado current. Com
esse symlink estabelecido e cada implementação em um diretório separado,
é fácil rolar de volta para versões anteriores do aplicativo.
Ativando servidores de aplicativos automaticamente
Embora seja possível construir um sistema que inclui automaticamente
mais servidores da Web quando a carga fica alta, não é sempre uma grande
ideia. O tráfego da Web flutua consideravelmente, por isso é necessário
ter certeza de que o tráfego irá permanecer antes de ativar um outro
servidor.
Serão necessários aproximadamente 10 minutos para determinar
se a carga que estiver sendo observada merece um novo servidor, depois
outros 5 a 10 minutos para ativar uma instância e ter um novo servidor
da Web no pool. Esse processo pode não ser efetivo para um servidor da
Web, mas pode funcionar bem para servidores de tarefas.
Ainda é possível fazer crescer e reduzir os aplicativos
da Web em um planejamento. Por exemplo, a maioria dos usuários do
aplicativo SmallPayroll.ca faz isso entre 8h e 21h nos dias úteis.
Para
melhorar o tempo de resposta para os usuários registrados e reduzir os
custos de servidor, três servidores serão executados nos dias úteis das
8h às 21h e dois servidores durante o resto do horário.
Os scripts de implementação já estão escritos: tudo que é
necessário fazer é iniciar o servidor e incluí-lo no balanceador de
carga.
Ativando servidores no cron
A facilidade cron parece ser o lugar natural para ligar e desligar a instância extra em um planejamento predefinido. O crontab ideal teria aparência semelhante à Listagem 4.
Listagem 4. Um crontab que inicia e para servidores automaticamente
0 8 * * 1-5 $BASEDIR/servercontrol launch app<br />0 21 * * 1-5 $BASEDIR/servercontrol terminate appO código na Listagem 4 é inserido no crontab de um usuário em uma máquina fora do ambiente virtual. Às 8h nos dias 1 a 5 (segunda a sexta), um script denominado servercontrol é executado com os parâmetros launch app. Às 21h, o mesmo comando é executado com parâmetros de terminate app. O script entenderá esses parâmetros para ser uma operação e uma função de servidor. Por enquanto, as funções serão sempre app.
Escrevendo o script de controle
A próxima etapa é escrever o script de controle de servidor, que é executado no cron para iniciar servidores on demand. A Listagem 5 mostra o script.
Listagem 5. O script de controle de servidor
#!/bin/bash<br /><br />AMI=ami-ad7e95c4 OP=$1<br />ROLE=$2<br /><br />if [ "$OP" == "" -o "$ROLE" == "" ]; then<br /> echo You\'re doing it wrong.<br /> echo $0 operation number role<br /> exit<br />fi<br /><br />case "$OP" in<br /> "launch")<br /><br /> # Ative a instância e analise os dados para obter o ID da instância<br /> DATA=`ec2-run-instances -k main -d "DB=10.126.17.1;ROLE=$ROLE" $AMI`<br /> INSTANCE=`echo $DATA | grep INSTANCE | cut -f 6 -d ' '`<br /> echo $INSTANCE is your instance<br /><br /> # Continue verificando até o estado ficar "running"<br /> STATE=""<br /> while [ "$STATE" != "running" ]; do<br /> STATE=`ec2-describe-instances $INSTANCE |awk '/^INSTANCE/ {print $6}'`<br /> echo the state is $STATE<br /> sleep 10<br /> done<br /> # Controle quais instâncias foram iniciadas por esse método<br /> echo $INSTANCE >> $HOME/.ec2-$ROLE<br /><br /> # Agora que a instância está em execução, capture o endereço IP<br /> IP=`ec2-describe-instances $INSTANCE |awk '/^INSTANCE/ {print $13}'`<br /><br /> # Se for para ser um servidor de aplicativos...<br /> if [ "$ROLE" == "app" ]; then<br /> # Verifique o servidor da Web para garantir que ele retorna nosso conteúdo<br /> UP=0<br /> while [ $UP -eq 0 ]; do<br /> OUTPUT=`curl -s -m 5 http://$IP`<br /> if [ $? -eq 0 ]; then # curl was successful<br /> echo $OUTPUT | grep -qi 'welcome'<br /> if [ $? -eq 0 ]; then<br /> UP=1<br /> else<br /> sleep 5<br /> fi<br /> fi<br /> done<br /><br /> # Registre com o balanceador de carga<br /> elb-register-instances-with-lb smallpayroll-http --instances $INSTANCE<br /> fi<br /><br /> ;;<br /><br /> "terminate")<br /> # Capture o ID da instância. É a última linha do arquivo<br /> FILE=.ec2-$ROLE<br /><br /> # Presumindo que o arquivo existe, naturalmente<br /> if [ ! -f $FILE ]; then<br /> echo No dynamic instances have been started for the $ROLE role<br /> exit<br /> fi<br /><br /> # A última instância iniciada é a última linha do arquivo<br /> INSTANCE=`tail -1 $HOME/.ec2-$ROLE`<br /><br /> # Presumindo que não há nada nesse arquivo, ou seja<br /> if [ "$INSTANCE" == "" ]; then<br /> echo No dynamic instances have been started for the $ROLE role<br /> exit<br /> fi<br /><br /> # Finalize a instância<br /> ec2-terminate-instances $INSTANCE<br /><br /> # Tire a instância do balanceador de carga<br /> elb-deregister-instances-from-lb smallpayroll-http --instances $INSTANCE<br /><br /> # Exclua a última linha do arquivo<br /> sed -i '$d' $FILE<br /> ;;<br /><br /> *) echo "You may only launch or terminate"<br /> exit<br /> ;;<br />esacEsse script pode parecer longo, mas uma boa parte é verificação de
erro. O script inicia analisando parâmetros e quebrando as funções
launch eterminate
em uma declaração de caso.
Para a ativação, o script
inicia a instância e aguarda até a instância entrar no estado
running. Em seguida, o script obtém o
endereço IP da instância e aguarda até um pedido da Web ao servidor
retornar com sucesso e conter a palavra welcome.
Parar a instância é muito mais fácil. Os IDs são gravados
em um dotfile no diretório inicial do usuário, com instâncias mais
novas sendo anexadas ao arquivo. Para parar a instância, o script lê a
última linha do arquivo para obter o ID da instância da instância
iniciada mais recentemente, emite um comando de encerramento, remove a
instância do balanceador de carga e exclui a última linha do arquivo que
contém as instâncias.
Observe que cada função tem seu próprio arquivo. Neste momento há somente a função da Web, mas isso será expandido mais tarde.
Um item curioso da Listagem 5 é o parâmetro -d
que foi passado para ec2-run-instances.
Esse parâmetro contém informações que a instância pode
ler visitando um URI especial que é acessível somente à instância. Essas
informações estão na forma de uma cadeia de caractere.
Na Listagem 5, a
função do servidor e o servidor de banco de dados são passados para a
instância.
Escrevendo o script inicial
O script de inicialização é executado durante o boot do sistema e
configura a instância com uma versão atual do aplicativo, junto com o
início da configuração apropriada do aplicativo.
A Listagem 6 usa as
informações passadas do script de controle para tomar essas decisões de
configuração. O código nessa listagem pode ser parte do script de
inicialização SYSV ou pode ir no arquivo rc.local para ser executado uma
vez na inicialização.
Listagem 6. O script de inicialização do aplicativo
USER=payroll<br />HOME=/home/payroll<br />SRC=/etc/smallpayroll<br /><br /># If this is a fresh AMI, set up the application directories<br />if [ ! -d $HOME/releases ]; then<br />echo "Setting up environment"<br />cp $SRC/Capfile $HOME<br /># Listing 2<br />su - $USER -c "cd $HOME && sh $SRC/setup_environment"<br />fi<br /><br />echo "Deploying the application"<br />su - $USER -c "cd $HOME && /opt/ree/bin/cap deploy"<br /><br /># Grab the user supplied data. 169.254.169.254 returns data unique to the instance.<br />USER_DATA=`/usr/bin/curl -s http://169.254.169.254/2007-01-19/user-data`<br />DBHOST=`echo $USER_DATA | sed 's/.*DB=\([0-9\.]*\).*/\1/'`<br />ROLE=`echo $USER_DATA | sed 's/.*ROLE=\([a-zA-Z]*\).*/\1/'`<br />logger "Starting application with DBHOST=$DBHOST and ROLE=$ROLE"<br /><br /># If available, put the dbhost in /etc/hosts<br />if [ "$DBHOST" != "" ]; then<br />sed -i '/dbhost/d' /etc/hosts<br />echo "$DBHOST dbhost" >> /etc/hosts<br />fi<br /><br /># Depending on the role...<br />case "$ROLE" in<br />'app')<br /># Web server... start up mongrel<br />su - $USER -c "mongrel_rails cluster::start \<br />-C $HOME/current/config/mongrel_cluster.yml"<br />;;<br />*)<br />logger "$ROLE doesn't make sense to me"<br />;;<br />esac<br /> A Listagem 6 começa inicializando algumas variáveis. Em seguida, o
diretório inicial do usuário do aplicativo é verificado para ver se a
tarefa
cap deploy:setup foi executada antes; se
não foi, a tarefa é executada. Em seguida, uma implementação é executada
para que o código mais recente esteja disponível.
Agora que o código está disponível, o script verifica os metadados que foram passados para a instância e, com uma certa mágica sed, extrai os componentes para as variáveis.
Se a variável DBHOST estiver
configurada, esse valor é colocado no /etc/hosts para que o aplicativo
saiba onde localizar o banco de dados. A função é verificada; se o
servidor estiver destinado a ser um servidor de aplicativos, os
servidores mongrel são iniciados.
Juntas, a Listagem 5 e a Listagem 6 constituem um bit de
código justo, mas definem os fundamentos para automatizar o início e a
parada de qualquer tipo de servidor.
Com o crontab da Listagem 4 estabelecido, o
servidor extra entrará on-line durante os períodos de pico e desligará
quando o site estiver menos ocupado. Em seguida, essa estrutura é
estendida para ativar diferentes tipos de servidores.
Processamento de tarefa assíncrona
Uma técnica comum para tornar os sites da Web dinâmicos mais
eficientes é mover os pedidos de longa execução para um processo em
segundo plano. Essas tarefas de longa execução geralmente não são tão
sensíveis ao tempo quanto um pedido real.
Um aplicativo poderá enviar um
pedido para executar um relatório em um sistema de processamento de
tarefa e, usando Asynchronous JavaScript + Extensible Markup Language
(Ajax), pesquisar a conclusão do segundo plano.
O usuário vê algum tipo
de spinner que indica que o aplicativo está operando, mas o usuário não
está amarrando um processo mongrel que poderia entregar mais pedidos
interativos.
Essa abordagem não remove a necessidade de ter recursos
amplos disponíveis para processar as tarefas. Se a fila de tarefas ficar
muito longa, os usuários ficarão cansados de aguardar seus relatórios.
Esse parece ser um caso de uso ideal para ativação dinâmica de servidor.
Alguma coisa monitora a lista não processada de tarefas; se a lista não
processada ultrapassar um limite determinado, um novo servidor será
ativado para ajudar. Após algum tempo o servidor será desativado.
O processamento de segundo plano é fornecido pelo excelente gem
delayed_job – especificamente, a bifurcação
collectiveidea.
Esse gem permite disparar tarefas em uma linha de código e implementa
uma fila de prioridade para as tarefas importantes não aguardarem atrás
de tarefas de rotina.
O daemon de processamento de tarefa executa no
diretório de aplicativos do Rails e usa o banco de dados para pedir
trabalho. Isso significa que é possível estender os scripts atuais para
tratar os daemons
delayed_job.
Não há ativações grátis
Executar novos servidores não é grátis, por isso há
algumas considerações econômicas a se fazer ao decidir quando ativar
novos servidores.
A instância m1.small que esta série tem
usado custa aproximadamente US$ 8,5 centavos por hora ou parte de uma
hora. Assim, ao lançar uma instância, mesmo durante um minuto, você está
comprando uma hora.
Se decidir usar instâncias maiores para fazer mais
trabalho, o custo por hora irá subir.
Certifique-se de que entende os custos de ativação de
um novo servidor e os custos de não fazê-lo. Serviço mais lento
significa clientes insatisfeitos. Serviço mais rápido significa clientes
mais satisfeitos, mas custos mais altos.
Atualizando o script de inicialização para suportar servidores de processamento de tarefa
Rechamada de A listagem 6 ROLE que o script verifica seus metadados de instância para ver o que foi passado do script de controle de servidor.
O parâmetro declara a tarefa do servidor, com app
significando um servidor de aplicativos.
As instruções para cada tipo
de servidor são quebradas em uma instrução de caso. A Listagem 7 estende
essa instrução de caso para tratar funções delayed_job.
Listagem 7. Tratando a reinicialização de um servidor delayed_job
case "$ROLE" in<br /> 'app')<br /> # Para um servidor de aplicativos, inicie os<br /> su - payroll -c "/opt/ree/bin/mongrel_rails cluster::start \<br /> -C /home/payroll/current/config/mongrel_cluster.yml"<br /> ;;<br /> 'job')<br /> # Para um servidor de tarefas, descubra que tipo de máquina é execute<br /> # um número apropriado de daemons de processamento de tarefa<br /> TYPE=`curl -s http://169.254.169.254/2007-08-29/meta-data/instance-type`<br /> case "$TYPE" in<br /> 'm1.small') NUM=2 ;; # 2 per ECU * 1 ECU<br /> 'm1.large') NUM=8 ;; # 2 per ECU * 4 ECUs<br /> *) NUM=2 ;;<br /> esac<br /> su - payroll -c "RAILS_ENV=production $HOME/current/script/delayed_job -n $NUM start"<br /> ;;<br /> *)<br /> logger "$ROLE doesn't make sense to me"<br /> ;;<br />esacO script verifica a função do servidor. Se o servidor for um servidor
de aplicativos, mongrels são iniciados. Se o servidor for um servidor
de tarefa, os scripts verificam para descobrir em que tipo de instância
ele está executando.
Essas informações estão disponíveis de outra URL no
host virtual 169.254.169.254. Como estimativa grosseira, o script ativa
dois trabalhadores
delayed_job por Elastic Compute Unit
(ECU). A sua carga de trabalho pode ser diferente.
Neste ponto, o script servercontrol pode ativar um novo servidor de tarefa passando launch job
na linha de comando.
Monitorando a fila
Existem várias maneiras de monitorar a lista não processada da fila.
Seria possível incluir uma tarefa e medir o tempo que leva para ser
processada, iniciando um novo servidor se o tempo estiver fora de um
limite.
O lado negativo desse método é que se realmente foi feito backup
da fila, levará um longo tempo para determinar que foi feito backup. A
solução mais simples é consultar o banco de dados para saber o número de
pedidos pendentes e entregar isso em um controlador. A Listagem
8 mostra um controlador.
Listagem 8. Um controlador que mostra o comprimento da fila
class QueueController < ApplicationController<br /> def length<br /> render :text => Delayed::Job.count(<br /> :conditions => "priority > 0 AND failed_at IS NULL").to_s<br /> end<br />endA Listagem 8 simplesmente mostra o comprimento da fila – especificamente,
tarefas com uma prioridade maior que 0 e as que não foram processadas
(ou seja, não falharam) – e renderiza esse número diretamente em vez de
passá-lo para um modelo. Navegando até /queue/length pode-se obter a
lista não processada da fila atual.
Ativando novos servidores de tarefa em resposta à demanda
Agora que é possível determinar com facilidade o comprimento da fila, é
necessário um script para agir sobre esses dados. A Listagem 9 mostra
esse script.
Listagem 9. Ativando mais servidores de tarefa, se necessário
#!/bin/bash<br /><br /># Qual é o comprimento da fila<br />QUEUE=`curl -s http://app.smallpayroll.ca/queue/length`<br /># Quantos servidores estão em execução agora? (zero se a fila não existir)<br />SERVERS=`wc -l $HOME/.ec2-job`<br />if [ "$SERVERS" == "" ]; then SERVERS=0; fi<br /><br /># lançar até dois servidores enquanto a fila estiver acima de 20<br />if [ $SERVERS -le 2 -a $QUEUE -gt 20 ]; then<br /> servercontrol launch job<br />fi<br /><br /># Finalizar uma instância se a fila estiver abaixo de 5<br />if [ $SERVERS -gt 0 -a $QUEUE -lt 5 ]; then<br /> export TZ=/usr/share/zoneinfo/UTC<br /> LAST=`tail -1 $HOME/.ec2-job`<br /> # But only if the server has run for at least 45 minutes<br /> UPTIME=`ec2-describe-instances $LAST | \<br /> awk '/INSTANCE/ {t=$10; gsub(/[\-:T\+]/, " ", t); print systime() - mktime(t) }'`<br /> if [ $UPTIME -gt 2700 ]; then<br /> servercontrol terminate job<br /> fi<br />fiO código na Listagem 9 deverá ser executado do cron a cada 5 minutos. O código obtém primeiro o comprimento da fila e o
número de servidores de tarefa atualmente em execução.
O número de
servidores de tarefa é obtido do comprimento do arquivo .ec2-job que
contém os IDs das instâncias dos servidores executados dinamicamente.
Se
o comprimento da fila for mais que 20 e houver menos que dois
servidores de tarefa extras em execução, um servidor é ativado. Se
houver mais que um servidor em execução e a fila for menor que 5, o
script faz uma verificação para ver se deveria finalizar uma instância.
O script primeiro determina o fuso horário da Hora Universal
Coordenada (UTC) definindo a variável de ambiente TZ. Em seguida, obtém o
ID da instância do servidor de tarefa executado por último e consulta
para obter o horário da inicialização.
Esse horário é alimentado em
alguma substituição awk para chegar ao tempo, em segundos,
que o servidor está ativo. Se o servidor estiver ativo há mais de 45
minutos, a instância pode ser desligada. Caso contrário, o servidor
permanece ativo.
A barreira de 45 minutos está estabelecida para impedir o
desligamento prematuro de um servidor. Se a fila diminuir e depois
aumentar novamente, o servidor ainda estará lá.
Lógica de negócios
A Listagem 9 implementa um algoritmo simples. É suficiente demonstrar os
princípios que estão por trás da ativação dinâmica de servidores, mas
poderiam ter mais inteligência. Por exemplo, o script poderia observar o
comprimento da fila ao longo do tempo e desligar servidores após um
período de estabilidade em vez da versão simples que existe hoje.
Usando uma rede de distribuição de conteúdo
Quando alguém acessa seu Web site, está também carregando imagens,
Cascading Style Sheets (CSS) e código JavaScript. Serviços chamados
CDNs armazenam em cache os recursos
estáticos e os distribuem por muitos servidores na Internet.
Como
resultado, pode ser fornecido acesso mais rápido dos seus usuários a
esses recursos e pode-se permitir que transfiram por download múltiplos
arquivos em paralelo. A Amazon fornece um serviço CDN chamado Amazon
CloudFront?uma oferta “pague pelo uso” como seus outros
serviços.
Os recursos que devem ser entregues de um CDN são pedidos
de um servidor diferente, assim a URL mudará. Como exemplo,
http://app.smallpayroll.ca/stylesheets/style.css irá
carregar a folha de estilo do servidor de aplicativos, mas
http://cdn0.smallpayroll.ca/stylesheets/style.css
carrega do CDN. Se o CDN não tiver o recurso, puxa o recurso de
origin antes de armazenar em cache e passar o recurso para o usuário.
CloudFront é ligeiramente diferente de outros CDNs porque
a origem é um depósito Amazon Simple Storage Service (Amazon S3). Para
usar CloudFront, é necessário primeiro preencher um depósito Amazon S3
com os recursos estáticos e, em seguida, regravar as URLs para usar
hosts CloudFront.
Configurando o CloudFront
Na página principal do CloudFront (ao final do artigo, consulte Recursos para obter um link), clique no link para inscrever-se. Será necessário aguardar um e-mail de ativação antes de continuar.
Ao receber a confirmação da ativação, acesse a página do console Amazon
Web Services e clique em Amazon CloudFront
Você verá a página mostrada na Figura 1.

Observe que nenhuma distribuição foi criada. Uma distribution é simplesmente um depósito de arquivos que será ligado de volta a um depósito Amazon S3.
Clique em Create Distribution para obter a página mostrada na Figura 2.
Figura 2. Criando uma distribuição

Execute as seguintes tarefas:
- Para Delivery Method, selecione
Download. - Na lista suspensa Origin selecione o nome de um depósito Amazon S3 ou elabore um que criará com sua ferramenta de opção no Amazon S3.
- Para Logging, selecione =Off a menos que planeje usar esses logs.
- No campo CNAMEs digite quatro nomes abaixo do seu domínio, como cdn0.smallpayroll.ca
a cdn3.smallpayroll.ca. - Em Comments, insira o texto que desejar.
- Para Distribution Status, selecione
Enabled.
Ao clicar em Create, será levado de volta à guia
CloudFront , onde verá a distribuição que acabou de criar (consulte a Figura 3).
Figura 3. Uma distribuição configurada

Nessa página há um nome de domínio, como
djdzxdmb99068.cloudfront.net. Agora é necessário acessar o servidor DNS,
configurar os quatro CNAMEs e fazê-los apontar para o nome de domínio
da distribuição. Amazon Elastic Load
Balancing funciona de maneira semelhante, exceto que são
criados quatro nomes numerados.
Coloque um arquivo de teste no depósito Amazon S3 que
associou à distribuição. Deverá ser possível ver esse documento
navegando até o nome de domínio da distribuição, como
http://djdzxdmb99068.cloudfront.net/test.htm, para visualizar um arquivo
chamado
test.htm dentro do depósito.
Se você
obtiver um erro de acesso negado, certifique-se de ter o acesso público
ativado nos arquivos (as instruções variam dependendo da ferramenta
usada para gerenciar o depósito Amazon S3). Se o teste anterior
funcionou, agora é possível tentar usar o CNAMEs que foi criado
anteriormente, como http://cdn1.smallpayroll.ca/test.htm.
Sincronizando os arquivos
O conteúdo do diretório público deve ser copiado para o depósito Amazon
S3 de origem. A maneira mais simples de fazer isso é executar:
s3sync.rb -r -p public/ smallpayroll-cdn:no diretório-raiz do aplicativo Rails. A opção
-r significa que a cópia deve ser recursiva.
A opção
-p torna todos os arquivos legíveis publicamente.
Para automatizar esse procedimento, verifique Recursos para obter um link para um gem que trate a tarefa.
Atualizando seu aplicativo
Em um nível simples é possível alterar todos os links de imagem,
JavaScript e CSS para apontarem para um dos links CDN em vez de para o
servidor da Web. Se foram usados auxiliares de URL do Rails como image_tag, é possível fazer o Rails executar o trabalho para você. Inclua a seguinte linha em
config/environments/production.rb:
ActionController::Base.asset_host = "cdn%d.smallpayroll.ca"Esse código inclui uma única linha à configuração de
produção – especificamente, quais recursos estáticos devem ser entregues
do host definido como
asset_hosts. A opção
%d
é, por padrão, expandida aos números 0 a 3, assim está
sendo dito ao Rails para girar entre cdn0.smallpayroll.ca,
cdn1.smallpayroll.ca,
cdn2.smallpayroll.ca e cdn3.smallpayroll.ca.
Esses são
os mesmos hosts para os quais o CloudFront foi configurado para
responder. Com quatro hosts, pode-se esperar que os navegadores
transfiram por download até oito recursos de cada vez, pois os
navegadores geralmente são limitados a duas conexões por host.
Agora o aplicativo usará o CDN onde possível. Desfrute a velocidade melhorada!
Conclusão
O seu aplicativo usa um Amazon CloudFront como um CDN, o que irá
acelerar os carregamentos de páginas tornando os downloads mais rápidos e
permitindo download paralelo pelo cliente.
O aplicativo também foi
atualizado para fazer crescer e reduzir dinamicamente os recursos de
computação. Parte da ativação e do encerramento é feita em um
planejamento e parte é feita em resposta à carga.
Existem alguns scripts que podem ser estendidos para
tratar muitos outros casos também.
Um item que falta é gerenciamento. Em qualquer momento
determinado, provavelmente não se sabe exatamente quantos computadores
estão em execução.
As implementações não estão automatizadas ainda
porque a lista de servidores está sempre em fluxo. Realmente não se sabe
como os próprios servidores estão executando. Procure o artigo final
desta série para tratar desses problemas.
Recursos
Aprender
-
AssetTagHelper: Verifique a documentação do Ruby para o
AssetTagHelper . Se essas funções forem usadas para gerar links e imagens, você descobrirá que mudar para um CDN é um muito fácil. -
Preparing a Rails Application for SVN
mostra como gerenciar qualquer aplicativo Rails por meio do Subversion.
É mais complicado do que parece devido aos vários arquivos de log. -
Performance tuning considerations in your application server
environment (Sean Walberg, developerWorks,
janeiro de 2009) discute maneiras de tornar o aplicativo mais rápido,
inclusive usando servidores de tarefa. - Uma instância do Amazon EC2 contém várias partes de metadados de instância
que podem ajudar a instância a aprender sobre o ambiente. Navegue por
este capítulo da documentação do Amazon EC2 juntamente com a lista de categorias de metadados e observe algumas ideias do que pode ser feito. -
Blog do Alfa
Jango: Leve o CloudFront uma etapa adiante e
saiba como tratar recursos compactados e seletivamente entregar recursos
do aplicativo ou do CDN. -
Participe de instruções em Live! grátis no
developerWorks para atualizar-se rapidamente em relação aos produtos e ferramentas IBM e às tendências do segmento de mercado de TI. -
Veja Demos on-demand do developerWorks
que abrangem desde demos de instalação e configuração de produtos para
iniciantes até funcionalidade avançada para desenvolvedores experientes.
Obter produtos e tecnologias
- Agora que você obteve múltiplos AMIs no Amazon S3, poderá desejar remover alguns antigos.
A opção
Amazon S3 File Manager
é um gerenciador de arquivos que rivaliza os recursos de muito
aplicativos independentes ou plug-ins do navegador. Se excluir um AMI,
não se esqueça de
ec2-deregister . - S3Sync é uma ferramenta útil para copiar arquivos do e para o Amazon S3 e também para manipular seus depósitos.
- Capistrano é um pacote de implementação popular que age de maneira semelhante ao Rake.
-
delayed_job é um servidor de tarefa em segundo plano que integra bem com Rails e ActiveRecord. Esse link é para a bifurcação
collectiveidea do projeto, que parece ser o fluxo mantido atualmente. - synch_s3_asset_host é um gem que torna muito fácil a sincronização do depósito Amazon S3 de origem e os arquivos estáticos do aplicativo.
***
artigo publicado originalmente no developerWorks Brasil, por Sean Walberg
Sean Walberg trabalha com Linux e UNIX desde 1994 em ambientes
acadêmicos, corporativos e de provedores de serviço de Internet.
Escreveu muito sobre administração de sistemas nos últimos anos. É
possível entrar em contato com ele pelo e-mail sean@ertw.com.







