Como implantar o Memcached no Kubernetes Engine

Neste tutorial, você aprenderá a implantar um cluster de servidores distribuídos do Memcached no Kubernetes Engine usando Kubernetes, Helm e Mcrouter. Memcached é um dos mais utilizados sistemas multifunções de código aberto para armazenamento em cache. Ele normalmente funciona como um armazenamento temporário para os dados mais usados a fim de agilizar aplicativos da Web e diminuir cargas do banco de dados.

Características do Memcached

O Memcached tem duas metas de projeto principais:

  • Simplicidade: o Memcached funciona como uma grande tabela de hash e oferece uma API simples para armazenar e recuperar objetos de maneira arbitrária por chave.
  • Velocidade: o Memcached mantém dados de cache de maneira exclusiva em memória de acesso aleatório (RAM, na sigla em inglês), acelerando muito o acesso a dados.

Memcached é um sistema distribuído que permite que a capacidade da tabela de hash seja escalada horizontalmente em um grupo de servidores. Cada servidor do Memcached opera em completo isolamento dos outros servidores no grupo. Portanto, o roteamento e o balanceamento de carga entre os servidores precisam ser feitos no nível do cliente. Os clientes do Memcached aplicam um esquema de hash consistente para selecionar corretamente os servidores de destino. Esse esquema garante as seguintes condições:

  • O mesmo servidor sempre é selecionado para a mesma chave.
  • O uso da memória é equilibrado de maneira uniforme entre os servidores.
  • Um número mínimo de chaves é realocado quando o grupo de servidores é reduzido ou expandido.

O diagrama a seguir ilustra detalhadamente a interação entre um cliente do Memcached e um grupo distribuído de servidores do Memcached.

interação entre memcached e um grupo de servidores do memcached
Figura 1: interação detalhada entre um cliente do Memcached e um grupo distribuído de servidores do Memcached.

Objetivos

  • Saiba mais sobre algumas características da arquitetura distribuída do Memcached.
  • Implante um serviço do Memcached no Kubernetes Engine usando Kubernetes e Helm.
  • Implante o Mcrouter, um proxy do Memcached de código aberto, para melhorar o desempenho do sistema.

Custos

Neste tutorial, usamos os seguintes componentes faturáveis do Google Cloud Platform:

  • Compute Engine

Use a calculadora de preços para gerar uma estimativa de custo com base no uso do projeto. Novos usuários do GCP são qualificados para uma avaliação gratuita.

Antes de começar

  1. Faça login na sua Conta do Google.

    Se você ainda não tiver uma, inscreva-se.

  2. Selecione ou crie um projeto do Google Cloud Platform.

    Acessar a página Gerenciar recursos

  3. Verifique se o faturamento foi ativado no projeto do Google Cloud Platform.

    Saiba como ativar o faturamento

  4. Ativar Compute Engine API.

    Ativar a API

  5. Inicie uma instância do Cloud Shell.
    ABRIR O CLOUD SHELL

Como implantar um serviço do Memcached

Uma maneira simples de implantar um serviço do Memcached no Kubernetes Engine é usar um gráfico do Helm. Para continuar a implantação, siga estas etapas no Cloud Shell:

  1. Crie um novo cluster do Kubernetes Engine de três nodes:

    gcloud container clusters create demo-cluster --num-nodes 3 --zone us-central1-f
    
  2. Faça o download do arquivo binário helm:

    cd ~
    wget https://kubernetes-helm.storage.googleapis.com/helm-v2.6.0-linux-amd64.tar.gz
    
  3. Descompacte o arquivo no sistema local:

    mkdir helm-v2.6.0
    tar zxfv helm-v2.6.0-linux-amd64.tar.gz -C helm-v2.6.0
    
  4. Adicione o diretório do binário helm à variável de ambiente PATH:

    export PATH="$(echo ~)/helm-v2.6.0/linux-amd64:$PATH"
    

    Este comando torna o binário helm detectável em qualquer diretório durante a sessão atual do Cloud Shell. Para tornar essa configuração persistente em várias sessões, adicione o comando ao arquivo ~/.bashrc do usuário do Cloud Shell.

  5. Crie uma conta de serviço com papel de administrador do cluster do Tiller, o servidor do Helm:

    kubectl create serviceaccount --namespace kube-system tiller
    kubectl create clusterrolebinding tiller --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
    
  6. Inicialize o Tiller no cluster e atualize as informações dos gráficos disponíveis:

    helm init --service-account tiller
    helm repo update
    
  7. Instale um novo gráfico do Memcached Helm com três réplicas, uma para cada node:

    helm install stable/memcached --name mycache --set replicaCount=3
    

    O gráfico do Memcached Helm usa um controlador StatefulSet. Uma vantagem de usar um controlador StatefulSet é que os nomes dos pods são ordenados e previsíveis. Neste caso, os nomes são mycache-memcached-{0..2}. Essa ordem facilita a consulta aos servidores pelos clientes do Memchached.

  8. Execute o seguinte comando para ver os pods em execução:

    kubectl get pods
    

    Saída resultante:

    NAME                  READY     STATUS    RESTARTS   AGE
    mycache-memcached-0   1/1       Running   0          45s
    mycache-memcached-1   1/1       Running   0          35s
    mycache-memcached-2   1/1       Running   0          25s

Como descobrir pontos de extremidade do serviço Memcached

O gráfico do Memcached Helm usa um serviço sem cabeça. Um serviço sem cabeça expõe endereços IP de todos os pods, de maneira que eles possam ser detectados individualmente.

  1. Verifique se o serviço implantado está sem cabeça:

    kubectl get service mycache-memcached -o jsonpath="{.spec.clusterIP}"
    

    A saída None confirma que o serviço não tem clusterIP e que, portanto, está sem cabeça.

    O serviço cria um registro DNS para um nome do host do formulário:

    [SERVICE_NAME].[NAMESPACE].svc.cluster.local
    

    Neste tutorial, o nome do serviço é mycache-memcached. Como um namespace não foi definido explicitamente, o namespace padrão é usado e, portanto, o nome completo do host é mycache-memcached.default.svc.cluster.local. Esse nome de host é resolvido para um conjunto de endereços IP e domínios para todos os três pods expostos pelo serviço. Se futuramente algum pod for adicionado ao pool ou um anterior for removido, kube-dns atualizará automaticamente o registro DNS.

    É responsabilidade do cliente descobrir os pontos de extremidade do serviço Memcached, conforme descrito nas próximas etapas.

  2. Recupere os endereços IP dos pontos de extremidade:

    kubectl get endpoints mycache-memcached
    

    A saída é semelhante à seguinte:

    NAME                ENDPOINTS                                            AGE
    mycache-memcached   10.36.0.32:11211,10.36.0.33:11211,10.36.1.25:11211   3m
    

    Cada pod do Memcached tem um endereço IP separado, respectivamente 10.36.0.32, 10.36.0.33 e 10.36.1.25. Esses endereços IP podem ser diferentes para as próprias instâncias de servidor. Cada pod ouve a porta 11211, que é a porta padrão do Memcached.

  3. Para uma alternativa à etapa 2, recupere esses mesmos registros usando uma consulta DNS padrão com o comando nslookup:

    kubectl run -it --rm alpine --image=alpine:3.6 --restart=Never nslookup mycache-memcached.default.svc.cluster.local
    

    A saída é semelhante à seguinte:

    Name:      mycache-memcached.default.svc.cluster.local
    Address 1: 10.36.0.32 mycache-memcached-0.mycache-memcached.default.svc.cluster.local
    Address 2: 10.36.0.33 mycache-memcached-2.mycache-memcached.default.svc.cluster.local
    Address 3: 10.36.1.25 mycache-memcached-1.mycache-memcached.default.svc.cluster.local
    

    Cada servidor tem o próprio nome de domínio da seguinte forma:

    [POD_NAME].[SERVICE_NAME].[NAMESPACE].svc.cluster.local
    

    Por exemplo, o domínio do pod mycache-memcached-0 é:

    mycache-memcached-0.mycache-memcached.default.svc.cluster.local
    
  4. Para outra alternativa à etapa 2, realize a mesma inspeção de DNS usando uma linguagem de programação como Python:

    1. Inicie um console interativo Python dentro do cluster:

      kubectl run -it --rm python --image=python:3.6-alpine --restart=Never python
      
    2. No console Python, execute estes comandos:

      import socket
      print(socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local'))
      exit()
      

      A saída é semelhante à seguinte:

      ('mycache-memcached.default.svc.cluster.local', [], ['10.36.0.32', '10.36.0.33', '10.36.1.25'])

  5. Teste a implantação abrindo uma sessão telnet com um dos servidores Memcached em execução na porta 11211:

    kubectl run -it --rm alpine --image=alpine:3.6 --restart=Never telnet mycache-memcached-0.mycache-memcached.default.svc.cluster.local 11211
    

    No prompt telnet, execute estes comandos usando o protocolo Memcached ASCII:

    set mykey 0 0 5
    hello
    get mykey
    quit

    A saída resultante é mostrada aqui em negrito:

    set mykey 0 0 5
    hello
    STORED
    get mykey
    VALUE mykey 0 5
    hello
    END
    quit

Como implementar a lógica de descoberta do serviço

Agora está tudo pronto para você implementar a lógica de descoberta do serviço básico mostrada no diagrama a seguir.

lógica da descoberta de serviço
Figura 2: lógica de descoberta do serviço.

Em um nível mais detalhado, a lógica de descoberta do serviço consiste nas seguintes etapas:

  1. O aplicativo consulta kube-dns sobre o registro DNS de mycache-memcached.default.svc.cluster.local.
  2. O aplicativo recupera os endereços IP associados a esse registro.
  3. O aplicativo instancia um novo cliente do Memcached e fornece a ele os endereços IP recuperados.
  4. O balanceador de carga integrado do cliente do Memcached se conecta aos servidores do Memcached nos endereços IP fornecidos.

Você agora implementa essa lógica de descoberta de serviço usando o Python:

  1. Implante um novo pod ativado para Python no cluster e inicie uma sessão de shell dentro do pod:

    kubectl run -it --rm python --image=python:3.6-alpine --restart=Never sh
    
  2. Instale a biblioteca pymemcache:

    pip install pymemcache
    
  3. Inicie um console interativo Python executando o comando python.

  4. No console Python, execute estes comandos:

    import socket
    from pymemcache.client.hash import HashClient
    _, _, ips = socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local')
    servers = [(ip, 11211) for ip in ips]
    client = HashClient(servers, use_pooling=True)
    client.set('mykey', 'hello')
    client.get('mykey')
    

    A saída resultante do último comando é a seguinte:

    b'hello'

    O prefixo b indica um literal de bytes, que é o formato em que o Memcached armazena dados.

  5. Saia do console Python:

    exit()
    
  6. Saia da sessão de shell do pod pressionando Control + D.

Como ativar o agrupamento de conexões

À medida que as necessidades de armazenamento em cache crescem e o grupo chega a dezenas, centenas ou milhares de servidores Memcached, você pode enfrentar algumas limitações. Em especial, o grande número de conexões abertas de clientes do Memcached pode colocar uma carga pesada sobre os servidores, conforme mostra o diagrama a seguir.

número elevado de conexões abertas quando todos os clientes do Memcached acessam todos os servidores Memcached diretamente
Figura 3: número elevado de conexões abertas quando todos os clientes do Memcached acessam todos os servidores Memcached diretamente.

Para reduzir o número de conexões abertas, você precisa introduzir um proxy para ativar o agrupamento de conexões, como no diagrama a seguir.

Proxy para ativar o agrupamento de conexões.
Figura 4: como usar um proxy para reduzir o número de conexões abertas.

O Mcrouter (pronuncia-se "mick router"), um proxy Memcached de código aberto eficiente, permite o agrupamento de conexões. A integração do Mcrouter é perfeita porque ele usa o protocolo padrão Memcached ASCII. Para um cliente do Memcached, o Mcrouter se comporta como um servidor do Memcached normal. Para um servidor do Memcached, o Mcrouter se comporta como um cliente do Memcached normal.

Para implantar o Mcrouter, execute os comandos a seguir no Cloud Shell.

  1. Exclua a versão do gráfico do Helm mycache instalada anteriormente:

    helm delete mycache --purge
    
  2. Implante novos pods do Memcached e do Mcrouter instalando uma nova versão do gráfico do Mcrouter Helm:

    helm install stable/mcrouter --name=mycache --set memcached.replicaCount=3
    

    Os pods de proxy agora estão prontos para aceitar solicitações de aplicativos de cliente.

  3. Teste essa configuração se conectando a um dos pods de proxy. Use o comando telnet na porta 5000, que é a porta padrão do Mcrouter.

    MCROUTER_POD_IP=$(kubectl get pods -l app=mycache-mcrouter -o jsonpath="{.items[0].status.podIP}")
    
    kubectl run -it --rm alpine --image=alpine:3.6 --restart=Never telnet $MCROUTER_POD_IP 5000
    

    No prompt telnet, execute estes comandos:

    set anotherkey 0 0 15
    Mcrouter is fun
    get anotherkey
    quit

    Os comandos definem e reproduzem o valor da chave.

Você já implantou um proxy que permite o agrupamento de conexões.

Como reduzir a latência

Para aumentar a resiliência, é comum usar um cluster com vários nós. Este tutorial usa um cluster com três nós. No entanto, usar vários nós também oferece o risco de latência aumentada causada pelo tráfego de rede mais pesado entre nós.

Como colocar pods de proxy

Você pode reduzir esse risco conectando pods de aplicativo do cliente apenas a um pod de proxy do Memcached que esteja no mesmo nó. O diagrama a seguir ilustra essa configuração.

topologia para interações entre pods
Figura 5: topologia das interações entre os pods de aplicativo, os pods do Mcrouter e os pods do Memcached em um cluster de três nós.

Realize essa configuração da seguinte maneira:

  1. Verifique se cada nó contém um pod de proxy em execução. Uma abordagem comum é implantar os pods de proxy com um controlador DaemonSet. À medida que nós são adicionados ao cluster, novos pods de proxy são adicionados automaticamente a eles. À medida que nós são removidos do cluster, o lixo desses pods é coletado. Neste tutorial, o gráfico do Mcrouter Helm que você implantou anteriormente usa um controlador DaemonSet por padrão. Dessa forma, esta etapa já está completa.
  2. Defina um valor hostPort nos parâmetros do Kubernetes do contêiner de proxy para fazer o nó escutar essa porta e redirecionar o tráfego para o proxy. Neste tutorial, o gráfico do Mcrouter Helm usa esse parâmetro por padrão para a porta 5000. Assim, esta etapa também já está completa.
  3. Exponha o nome do nó como uma variável de ambiente dentro dos pods do aplicativo usando a entrada spec.env e selecionando o valor spec.nodeName fieldRef. Saiba mais sobre esse método na documentação de Kubernetes.

    1. Implante alguns pods de aplicativos de amostra:

      cat <<EOF | kubectl create -f -
      apiVersion: extensions/v1beta1
      kind: Deployment
      metadata:
        name: sample-application
      spec:
        replicas: 9
        template:
          metadata:
            labels:
              app: sample-application
          spec:
            containers:
              - name: alpine
                image: alpine:3.6
                command: [ "sh", "-c"]
                args:
                - while true; do sleep 10; done;
                env:
                  - name: NODE_NAME
                    valueFrom:
                      fieldRef:
                        fieldPath: spec.nodeName
      EOF
      
  4. Verifique se o nome do nó está exposto, observando dentro de um dos pods de aplicativos de amostra:

    POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}")
    
    kubectl exec -it $POD -- sh -c 'echo $NODE_NAME'
    

    Este comando produz o nome do nó na seguinte forma:

    gke-demo-cluster-default-pool-XXXXXXXX-XXXX

Como conectar os pods

Os pods de aplicativos de amostra agora estão prontos para se conectar ao pod do Mcrouter executado nos respectivos nós mútuos na porta 5000, que é a porta padrão do Mcrouter.

  1. Inicie uma conexão para um dos pods abrindo uma sessão telnet:

    POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}")
    
    kubectl exec -it $POD -- sh -c 'telnet $NODE_NAME 5000'
    
  2. No prompt telnet, execute estes comandos:

    get anotherkey
    quit
    

    Saída resultante:

    Mcrouter is fun

Por fim, como ilustração, o seguinte código Python é um programa de amostra que executa essa conexão recuperando a variável NODE_NAME do ambiente e usando a biblioteca pymemcache:

import os
from pymemcache.client.base import Client

NODE_NAME = os.environ['NODE_NAME']
client = Client((NODE_NAME, 5000))
client.set('some_key', 'some_value')
result = client.get('some_key')

Como limpar

Para evitar cobranças dos recursos usados neste tutorial na conta do Google Cloud Platform, siga as etapas a seguir:

  1. Execute o comando abaixo para excluir o cluster do Kubernetes Engine:

    gcloud container clusters delete demo-cluster --zone us-central1-f
    
  2. Se quiser, exclua o binário do Helm:

    cd ~
    rm -rf helm-v2.6.0
    rm helm-v2.6.0-linux-amd64.tar.gz
    

Próximas etapas

  • Explore os muitos outros recursos que o Mcrouter oferece além do agrupamento de conexões simples, como réplicas de failover, streams de exclusão confiáveis, aquecimento de cache a frio, transmissão multicluster.
  • Explore os arquivos de origem do gráfico do Memcached e do gráfico do Mcrouter para mais detalhes sobre as respectivas configurações do Kubernetes.
  • Leia mais sobre técnicas efetivas de como usar o Memcached no App Engine. Algumas delas se aplicam a outras plataformas, como o Kubernetes Engine.
  • Teste outros recursos do Google Cloud Platform. Veja nossos tutoriais.
Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…