Aws sao paulo summit 2015 elasti cache avancado

Post on 25-Jul-2015

358 views 5 download

Transcript of Aws sao paulo summit 2015 elasti cache avancado

São Paulo

Amazon ElastiCache Avançado

Fábio Aragão da Silva, Solutions Architect at AWS

Willy Barro, Chief Technology Officer at Kanui

Fernando Cabral, Lead SysAdmin at Kanui

28 de Maio de 2015

Agenda• Revisão Rápida

• Lançando

• Conectando

• Distribuindo as Chaves (Sharding)

• Monitorando

• Kanui

• Casos de Uso

• Juntando tudo

• Melhores Práticas e Lições Aprendidas

Revisão Rápida

Por que estamos aqui?

ms μs

ElastiCache

Memcached

• Serviço gerenciado da AWS

que permite criar, utilizar e

escalar rapidamente clusters

de cache distribuido na

nuvem

Disponível em duas opções:

Redis

Similar a um banco no

NoSQLhttp://redis.io/commands

Cache em memória

do tipo chave,valor

Suporte a tipos de dadosstrings, listas, hashes, sets, sets ordenados,

bitmaps & HyperLogLogs

Single-threaded

Operações AtômicasSuporte a transações

com propriedades ACID

Ridiculamente rápido!

Réplicas de Leitura

PersistênciaSnapshots ou log append-only

Funcionalidade de

publicação/subscrição

Memcached

Alocação Slab

Cache em memória do tipo

chave,valor

Suporte a String e

Objetos

Multi-threaded

Absurdamente rápido!

Consolidado

Sem persistência

Padrões de Sharding

Lançando

Lançando

1Escolha:

• Versão

• Porta

• Parametros

• Multi-AZ & replicação*

• Nome do cluster

• Tipo de nó

• # de Nós (ou réplicas)

• Local de backup no S3*

2Choose:

• Grupo de subnet

• Zonas de Disponibilidade

• Security group

• Habilite backups*

• Janela de Manutenção

• Tópico do SNS

3

+ +

*opção no Redis

Escolha o motor:

A partir do console da AWS:

Ou use a AWS CLI:aws elasticache create-cache-cluster

--cache-cluster-id mycache

--engine redis

--cache-node-type cache.m3.medium

--num-cache-nodes 1

ou o AWS CloudFormation:

"Resources" : {

"CacheCluster" : {

"Type": "AWS::ElastiCache::CacheCluster",

"Properties": {

"CacheNodeType" : { "Ref" : "CacheNodeType" },

"CacheSecurityGroupNames" : [ { "Ref" : "CacheSecurityGroup" } ],

"Engine" : "memcached",

"NumCacheNodes" : { "Ref" : "NumberOfCacheNodes" }

}

}

Mas ao final das contas, o que interessa

mesmo são os endpoints

Endpoint: Refere-se a um nó em particular do cluster

mycache-002.hnou5c.0001.usw2.cache.amazonaws.com:6379 (redis)

mycache.hnou5c.0002.usw2.cache.amazonaws.com:11211 (memcached)

Endpoint de Configuração: Para o memcached, é um alias de DNS para

consultar a lista atual de nós participantes de um cluster

mycache.hnou5c.cfg.usw2.cache.amazonaws.com:11211

Endpoint: Primário: Pra grupos de replicação no redis, é um alias de DNS que

se refere ao nó onde devem ser feitas as escritas

mycacherepgroup.hnou5c.ng.0001.usw2.cache.amazonaws.com:6379

Redis Multi-AZ Auto-Failover

Escolhe a réplica com

menor atraso

Não muda o DNS

Availability Zone #1 Availability Zone #2

escritasUse o endpoint

primário

leiturasUse os endpoints de

réplica (ou o primário

também)

Conectando

Conectando ao Redis

Language Library

Ruby Redis-rb, Redis objects

Python Redis-py

Node.js node-redis

C#/.NET ServiceStack.Redis

PHP phpredis

Java Jedis

Bibliotecas Cliente:

Suporta os mesmos comandos

+ histórico de comandos

+ teste de latência

+ backups

+ vários outros

$telnet {primary-endpoint} 6379

>HSET hash mykey "mydata"

:1

>HGET hash mykey

$6

mydata

#from redis.io download:

$redis-cli -h {primary-endpoint}

// Exempoo em Java – requer http://aws.amazon.com/sdk-for-javaAmazonElastiCache ec = new AmazonElastiCacheClient();

String replicationGroupName = "mycache”; // mude para o nome do seu grupo de replicação

String metadataURL = "http://169.254.169.254/latest/meta-data/placement/availability-zone";

String myAZ = new Scanner(new URL(metadataURL).openStream(), "UTF-8").

useDelimiter("\\A").next();

ec.setRegion(Region.getRegion(Regions.US_WEST_2));

DescribeReplicationGroupsRequest rgrequest = new

DescribeReplicationGroupsRequest().withReplicationGroupId(replicationGroupName);

DescribeReplicationGroupsResult rgresult = ec.describeReplicationGroups(rgrequest);

for (ReplicationGroup rg : rgresult.getReplicationGroups()) {

for (NodeGroup ng : rg.getNodeGroups()) {

for (NodeGroupMember ngm : ng.getNodeGroupMembers()) {

if (ngm.getCurrentRole().equals("replica") &&

ngm.getPreferredAvailabilityZone().equalsIgnoreCase(myAZ)) {

System.out.println(ngm.getReadEndpoint().getAddress() + ":" +

ngm.getReadEndpoint().getPort());

}

}

}

}

Quais réplicas estão na minha AZ?

En

co

ntr

ar

AZ

Ch

am

ad

aa

AP

I d

o

Ela

sti

Ca

ch

e

Conectando no Memcached

Útil para algumas informações &

manutenção, mas normalmente vamos nos

conectar usando alguma biblioteca cliente

Language Library

Ruby Dalli, Dalli:ElastiCache

Python Memcache Ring, django-elasticache

Node.js node-memcached

C#/.NET ElastiCache Auto Discovery Client

PHP ElastiCache Auto Discovery Client

Java ElastiCache Auto Discovery Client

(based on spymemcached)

Bibliotecas Cliente:

As bibliotecas oficiais de ElastiCache

para PHP, Java e .NET suportam Auto

Discovery se nós de Memcached

forem adicionados ou removidos

$telnet {cfg-endpoint} 11211

>config get cluster

$telnet {node1} 11211

>set mykey 0 60 6

>mydata

STORED

>get mykey

VALUE mykey 0 6

mydata

END

Clientes para o Amazon ElastiCache

• Bibliotecas oficiais para download:– Java: baseada em spymemcached

– PHP: (várias versões)

– .NET

• Benefícios:– Provê hashing consistente

– Realiza Auto Discovery (a cada 60s) para detectar nós adicionados

ou removidos

– Não requer reconfiguração ou dar HUP em instâncias em execução

# PHP

$server_endpoint =

"mycache.z2vq55.cfg.usw2.cache.amazonaws.com";

$server_port = 11211;

$cache = new Memcached();

$cache->setOption(

Memcached::OPT_CLIENT_MODE, Memcached::DYNAMIC_CLIENT_MODE);

# Use o endpoint de configuração como o único servidor

$cache->addServer($server_endpoint, $server_port);

# A biblioteca localiza os nós automaticamente

$cache->set("key", "value");

Descoberta automática de nós

Sempre use o endpoint

de configuração

Distribuindo as Chaves (Sharding)

Quem tem as chaves?

Com múltiplos servidores de Memcached, onde armazenar as chaves?

Primeira abordagem:

Baseada no módulo do número de

servidores:

Desvantagem:

Ao adicionar novo cluster,

grande número de chaves

precisa ser reassociado:

Se você for de

3 servidores 4

servidores,

¾ = 75% das chaves

serão impactadas

num_nós_antigos

num_nós novos= ( )server_list = [

'mycache.0001.usw2.cache.amazonaws.com:11211',

'mycache.0002.usw2.cache.amazonaws.com:11211'

]

server_index = hash(key) % server_list.length

server = server_list[server_index]

Melhor ainda: hashing consistente

Uma explicação bem simplificada de algo que pode gerar algumas horas de discussão

# número de chaves impactadas com +/-

nós :

aproximadamente ( 1 – método via módulo)

Ex: se for de 3 servidores 4 servidores,

(1 - ¾) ~ 25% das chaves serão impactadas

①Imagine o anelas vezes chamado de ‘continuum’

②Divida-o em partições

(um número fixo “N”)Nesse caso, 24, em geral 2(32 or 160)

③Servidores são “mapeados” nas partiçõesspread throughout the ring, not even like this

12

3

4

5

6789

1011

12

13

14

1516

Nó-A

Nó-B

Nó-C

Nó-C

Nó-B

Nó-A

④ Bibliotecas cliente fazem o hash da chave e

usam % N para determinar onde elas devema maior partição mais próxima no anel

“uma estratégia de sharding

para a todos governar

Monitorando

Memcached internamente

SEM

MEMÓRIA

Slab Classe 42

Tam. Chunk: 1MB

Chunks/Pg: 1

> stats slabsSlab Classe 27

Tam. Chunk: 42KB

Chunks/Pg: 24

Slab Classe 15

Tam. Chunk: 1800B

Chunks/Pg: 582

Slab Classe 1

Tam. Chunk: 96B

Chunks/Pg: 10922

Memory pool

>stats cachedump 1 100

ITEM mykey3 [4 b; 1414372065 s]

>stats slabs

STAT 1:used_chunks 1

>get mykey3

END

>stats cachedump 1 100

END

>stats slabs

STAT 1:used_chunks 0

Gerenciamento da memória

• Não existe processo ‘reaper’ (anjo da morte) pra limpeza da memória

• Eviction no Memcached limpa a memória com base em LRU

• LRU = Menos recentemente usado

• Tempo de expiração não é ‘mantenha até’, mas sim ‘não válido após’

• Por padrão, páginas não se movem

• Use ‘parameter groups’ para alterar comportamento padrão

Exemplo

Monitoramento

Source:http://blog.elijaa.org/index.php?pages/phpMemcachedAdmin-Download

phpMemcachedAdminAmazon

CloudWatch

Alarmes

“Os serviços da AWS nos ajudaram a fazer da Black

Friday um dia comum, ao invés de um pesadelo para o

time de engenharia”

• A Kanui é um e-commerce

especializado em artigos

esportivos e um dos líderes em

seu segmento

• Como o site é a base de todo o

nosso negócio, alta

disponibilidade é imprescindível.

“Com alta

escalabilidade,

interoperabilidade com

ferramentas open source,

suporte eficaz e visibilidade de

custos detalhada, a AWS se

tornou uma escolha natural”

- Willy Barro, CTO

• Disponibilidade - Zero Downtime. Um minuto fora do ar é um minuto sem vendas

• Escalabilidade - Suportar a Black Friday, mobile push notifications e promoções relâmpago

• Custos - Visibilidade detalhada de todos os custos

• Tools - Ferramentas e bibliotecas para gerenciar e monitorar a infraestrutura

O Desafio

A Solução

A Solução

Casos de Uso

Revisando o cenário tipico de web 2.0

ELB Aplicação

APIs Externas

fácil de adicionar

Começe com “cache preguiçoso” (‘lazy’)

• Muito benéfico para padrões

de acesso de intensa leitura

– Informação de perfil de usuário

– Dados sumarizados

• Muitas bibliotecas encapsulam

esse padrão

# Pseudocódigo em Python:

def get_user(user_id):

# Verifica o Cache

record = cache.get(user_id)

if record is None:

# Consulta a Base de Dados

record = db.query(

”select * from users where id = ?”,

user_id)

# Popula o Cache

cache.set(user_id, record)

return record

# Código da Aplicação

user = get_user(17)

E como ficam as escritas e leituras?

• Cache atualizado em

tempo real

• Trata atualizações no perfil

dos usuários

• Pode também escolher

remover a chave e deixar o

cache “preguiçoso” agir

# Pseudocódigo em Python:

def save_user(user_id, values):

# Salva na Base de Dados

record = db.query(

"update users ... where id = ?",

user_id, values)

# Grava no Cache

cache.set(user_id, record)

return record

# Código da Aplicação

user = save_user(17, {"name": ”Sauron"})

Persistência de Sessão1) Instale o“memcache”

‘yum install php-pecl-memcache’

2) Configure o “php.ini”

session.save_handler = memcache

session.save_path =

"tcp://node1:11211, tcp://node2:11211"

3) Configure o “php.d/memcache.ini”

memcache.hash_strategy = consistent

memcache.allow_failover = 1

memcache.session_redundancy = 3

4) Re-inicie o httpd

5) Comece a usar dados da sessão:

<?php

session_start();

$_SESSION[”REQUEST_TIME"] = time()];

?>

• Para situações onde você

precisa armazenar a sessão

externamente

– Em especial quando se usa

ASG (auto scaling groups)

– Cache é otimizado para

altos volumes de leitura

Reference:

http://php.net/manual/en/book.memcache.php *strange memcache bug needs n+1

Exemplo em PHP

Fonte em https://github.com/martinrusev/django-redis-sessions

1) Instale o django-redis-sessions:

’pip install django-redis-sessions’

2) Altere o ‘settings.py’:

SESSION_ENGINE = 'redis_sessions.session'

SESSION_REDIS_HOST = 'mycache.hnou5c.ng.0001.usw2.cache.amazonaws.com'

SESSION_REDIS_PREFIX = 'djangosession’

3) Confirme que as sessões estão sendo persistidas no Redis:

mycache.hnou5c.ng.0001.usw2.cache.amazonaws.com:6379> keys "djangosession*"

1) "djangosession:rm6az4eesd7ruc5sibbmf6rlhrwinevs"

Exemplo com Django

Persistência de Sessão

Taxa Limite

• Caso ideal caso você queira porexemplo limitar a quantidade de request por segundo a uma API

• Usa o comando INCR

ELB

API

Externa Referência: http://redis.io/commands/INCR

FUNCTION LIMIT_API_CALL(APIaccesskey)limit = HGET(APIaccesskey, “limit”)time = CURRENT_UNIX_TIME()keyname = APIaccesskey + ":”+timecount = GET(keyname)IF current != NULL && count > limit THEN

ERROR ”API request limit exceeded"ELSE

MULTIINCR(keyname)EXPIRE(keyname,10)

EXECPERFORM_API_CALL()

END

Fila de Tarefas

• Basicamente, qualquer coisa pode

ser feita de forma assíncrona for a

da experiência imediata do

usuário:

– Envio de email

– Processamento de imagem ou

video

– Conversão de documentos

– Geração de relatórios

– Limpeza de cache

– Interação com API’s externas

– search indexing

Baseada em Ruby Baseada em Python

http://python-rq.orghttp://github.com/resque

Redis-Queue

Publicação/Subscrição

• Casos de Uso:

• Mensagens dentro do

aplicativo

• Janelas de web chat

• Chat/invite para jogos

online

• Não é persistente

• Mais detalhes

http://www.rediscookbook.org• Usando Pub/Sub para

comunicação assíncrona

SUBSCRIBE “mordor:chat”SUBSCRIBE “mordor:chat”

SUBSCRIBE “mordor:chat”

SUBSCRIBE “mordor:chat”

PUBLISH “mordor:chat” “Estou de olho em você!”

Estou de olho em você!Estou de olho em você!

Estou de olho em você!

Estou de olho em você!

(integer) 4

>>

>

>

>

var clients = [];

var echo = sockjs.createServer();

echo.on('connection', function(conn) {

clients.push(conn);

conn.on('data', function(message) {

for (var i=0; i<clients.length; i++) {

clients[i].write(message);

}

});

});

WebSockets

http://goldfirestudios.com/blog/136/Horizontally-Scaling-Node.js-and-WebSockets-with-Redis

1

4

3

2

// configure o redis e pub/sub, subscreva ao tópico

// ao receber uma msg, publique pra todos os clientes

// para enviar uma mensagem, publique no redis

Comece com um esqueleto:

Exemplo com Node.js

sub.on('message', function(channel, msg) {

for (var i=0; i<clients.length; i++) {

clients[i].write(msg);

}

});

pub.publish('websocket', message);

var redis = require('node-redid');

var pub = redis.createClient(port, host);

var sub = redis.createClient(port, host);

sub.subscribe('websocket');

npm install node-redis

E o favorito de todos: leaderboard!

Não se eu

destruir primeiroÉ meu!

• Fácil de implementar usando Sorted

Sets

• Garante simultaneamente:– unicidade and ordenação

ZADD "leaderboard" 1201 "Gollum”

ZADD "leaderboard" 963 "Sauron"

ZADD "leaderboard" 1092 "Bilbo"

ZADD "leaderboard" 1383 "Frodo”

ZREVRANGE "leaderboard" 0 -1

1) "Frodo"

2) "Gollum"

3) "Bilbo"

4) "Sauron”

ZREVRANK "leaderboard" "Sauron"

(integer) 3

Example

def save_score(user, score):

redis.zadd("leaderboard", score, user)

def get_rank(user)

return redis.zrevrank(user) + 1

Integração com NGINX

/foo

http {

...

include includes/memc-backend.conf

...

server {

# GET /foo?cmd=get&key=bar

location /foo {

set $memc_cmd $arg_cmd;

set $memc_key $arg_key;

memc_cmds_allowed get;

memc_connect_timeout 5s;

memc_pass backend;

}

}

- includes/memc-backend.conf

upstream backend {

server 172.16.1.1:11211;

server 172.16.1.2:11211;

}

1 Comece com nginx

2 Compile no

memc-nginx-module

3 Configure o segmento

de URI para usar o Memcached

Juntando Tudo

auto discovery com Serf

ElastiCache

NODE+

−SNS

TOPICO

SQSMENSAGEM

API de Auto

Scaling

Au

to S

ca

lin

g G

rou

p

Protocolo Gossip

S S S S SS

S

S

S

SS

S

S

S

S

S

?) Mudança no Cache

?) Mudança no ASG> Notifica Agente Serf

Script Python:

auto discovery com Serf

Usage: percolator <region-name> <app-autoscale-group> <elasticache-cluster> <sqs-queue> <serf-role>

# Reconfigure o serf se ocorrer qualquer mudança no grupo de autoscaling

appautoscalegroup_ips = getAppAutoscalegroupIps(arg_region, arg_appautoscalegroup)

if len(appautoscalegroup_ips) > 0:

...

if ((hashlib.md5(open(serfconfig_file, 'rb').read()).hexdigest()) !=

(hashlib.md5(serfconfig).hexdigest())):

...

sysLog(”Grupo de autoscaling \"%s\" mudou – atualizando o cluster serf" % arg_appautoscalegroup)

text_file.write(serfconfig)

os.system(serfdaemon_reload)

# Mande uma mensagem de atualização pelo serf caso encontre uma mensagem de atualização do clusterde elasticache na fila do SQS

if checkSqsQueue(arg_region, arg_sqsqueue, debug):

elasticachenode_addresses = getElasticacheClusterAddresses(arg_region, arg_elasticachecluster)

...

os.system(serfdaemon_event % nodelist[1:])

sysLog(”Despachando evento de atualização de aplicação no serf: %s" % nodelist[1:])

E por fim

Lembre-se:

• Nenhum dos dois motores de cache tem qualquer noçãosignificativa sobre autenticação oucriptografia

• Inicie seus clusters de cache dentro de subnets privadas da suaVPC

• Utilize regras apropriadas de security group para controlar o acesso aos seus nós de cache

E o que fazer a partir daqui?

• Automatize a atividade do seu

cluster

• Tire vantagem da infra de Multi-AZ

• Identifique novos casos de uso que

se beneficiariam com cache

• Use o Amazon SNS e

monitoramento

Segurança

Melhores Práticas

Considerações gerais de planejamentoElastiCache para Memcached

• Tamanho dos nós

– http://docs.aws.amazon.com/AmazonElastiC

ache/latest/UserGuide/CacheNode.Memcach

ed.html

• Otimização

– http://docs.aws.amazon.com/AmazonElastiC

ache/latest/UserGuide/CacheParameterGrou

ps.Memcached.html -

CacheParameterGroups.Memcached.Connec

tionOverhead

• Distribuição em Multi-AZs

– Distribua os nós do Memcached entre

múltiplas AZ’s

ElastiCache para Redis

• Tamanho dos nós

– http://docs.aws.amazon.com/AmazonElastiCa

che/latest/UserGuide/CacheNode.Redis.html

• Otimização

– Redis é single threaded, então escolher um

nó com processador mais rápido vai permitir

melhor performance / maior throughput

• Distribuição em Multi-AZs

– ElastiCache para Redis suporta cluster de nó

único apenas

– Certifique-se de que a(s) replica(s) de leitura

está(ão) em uma AZ diferente do cluster

primário

Mudar o tamanho do nó causa impacto significativo na

aplicação, então escolha o tamanho do nó cuidadosamente

Melhores Práticas e Lições Aprendidas

• ElastiCache para Memcached– Encapsule o acesso ao Memcached em comandos Hystrix para

permitir controle de timeouts e atuar como “circuit breaker”

• Para saber mais sobre o Hystrix, veja https://github.com/Netflix/Hystrix

– Use chamadas de API assíncronas no lugar de chamadassíncronas

• No passado, tivemos situações de time out de operações

– Diagnosticamos a causa no SDK de ElastiCache para Java e realizamosa correção

• Mudamos a implementação para executar chamadas assíncronascom timeout pequeno e re-tentativas

– Estamos trabalhando para reduzir ainda mais o limite de timeout

Melhores Práticas e Lições Aprendidas

• ElastiCache para Memcached (continuação)– Métricas do Amazon CloudWatch:

• CPUUtilization, GetHills, GetMisses, IncrementHits, IncrementMisses,

DeleteHits, NewItems, UnusedMemory, FreeableMemory

Melhores Práticas e Lições Aprendidas

• ElastiCache para Redis– Failover automático do cluster primário*

• AWS cuida da recuperação automática do nó principal, porém essaoperação pode levar até 10 minutos

• A promoção de uma réplica de leitura qualquer a nó principal é umaoperação manual

– Considere implementar um cache de 2 camadas com múltiplasréplicas de leitura

• Requer implementação em código para gerenciar nó“principal/preferencial” versus replicas de leitura “alternativas”

• Provê grau adicional de proteção no caso de falha de AZ e indisponibilidade do cluster principal.

** Baseado em testes feitos antes do anúncio da funcionalidade. Vejam o artigo do blog da AWS chamado“Multi-AZ Support/Auto Failover for ElastiCache for Redis”.

Melhores Práticas e Lições Aprendidas

• ElastiCache para Redis (continuação)– Métricas do Amazon CloudWatch

• Métricas de nível do nó: CPUUtilization, SwapUsage,

FreeableMemory, NetworkBytesIn, NetworkBytesOut

• Métricas de nível do ElastiCache: Replication Lag,

CurrConnections, CacheHits, Cachemisses

Leitura

• aws.amazon.com/documentation/elasticache/

Comunidade e suporte:

• awshub.com.br

• forums.aws.amazon.com

• aws.amazon.com/support

Treinamento

• https://qwiklabs.com/focuses/preview/1380

Próximos Passos

Fábio Aragão da Silva, Solutions Architect – AWS

São Paulo