Preparar um aplicativo para o Cloud Service Mesh


O Cloud Service Mesh é uma ferramenta poderosa para gerenciar e monitorar aplicativos distribuídos. Para aproveitar ao máximo o Cloud Service Mesh, é importante entender as abstrações subjacentes, incluindo contêineres e Kubernetes. Neste tutorial, explicamos como preparar um aplicativo para o Cloud Service Mesh com o código-fonte para um contêiner em execução no GKE, até o momento antes de instalar o Cloud Service Mesh.

Se você já tiver familiaridade com os conceitos de Kubernetes e malha de serviço, pule este tutorial e acesse o guia de instalação do Cloud Service Mesh.

Objetivos

  1. Explorar um aplicativo "hello world" de vários serviços
  2. Executar o aplicativo a partir do código-fonte
  3. Colocar o aplicativo em um contêiner
  4. Criar um cluster do Kubernetes
  5. Implantar os contêineres no cluster

Antes de começar

Siga estas etapas para ativar a API Cloud Service Mesh:
  1. Acesse a página do Kubernetes Engine no console do Google Cloud .
  2. Crie ou selecione um projeto.
  3. Aguarde a ativação da API e dos serviços relacionados. Isso pode levar alguns minutos.
  4. Verify that billing is enabled for your Google Cloud project.

Neste tutorial, usamos o Cloud Shell, que provisiona uma máquina virtual (VM) g1-small do Compute Engine executando um sistema operacional Linux baseado em Debian.

Preparar o Cloud Shell

As vantagens de usar o Cloud Shell são:

  • Os ambientes de desenvolvimento Python 2 e Python 3 (incluindo virtualenv) estão configurados.
  • As ferramentas de linha de comando gcloud, docker, git e kubectl usadas neste tutorial já estão instaladas.
  • É possível escolher os editores de texto:

    • Editor de código, que você acessa clicando em na parte superior da janela do Cloud Shell.

    • Emacs, Vim ou Nano, que você acessa na linha de comando do Cloud Shell.

  • In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    Fazer o download do exemplo de código

    1. Faça o download do código-fonte helloserver:

      git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
      
    2. Mude para o diretório do código de amostra:

      cd anthos-service-mesh-samples/docs/helloserver
      

    Explorar o aplicativo multisserviço

    O aplicativo de amostra é escrito em Python e tem dois componentes que se comunicam usando REST:

    • server: um servidor simples com um endpoint GET, /, que imprime "hello world" no console.
    • loadgen: um script que envia tráfego para server, com um número configurável de solicitações por segundo (RPS, na sigla em inglês).

    amostra de aplicativo

    Executar o aplicativo a partir do código-fonte

    Para se familiarizar com o aplicativo de amostra, execute-o no Cloud Shell.

    1. No diretório sample-apps/helloserver, execute o server:

      python3 server/server.py
      

      Na inicialização, o server exibe o seguinte:

      INFO:root:Starting server...
      
    2. Abra outra janela de terminal para enviar solicitações ao server. Clique em para abrir outra sessão.

    3. Envie uma solicitação para server:

      curl http://localhost:8080
      

      O server responde:

      Hello World!
      
    4. No diretório em que você fez o download do código de amostra, mude para o diretório que contém loadgen:

      cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/loadgen
    5. Crie as variáveis de ambiente a seguir:

      export SERVER_ADDR=http://localhost:8080
      export REQUESTS_PER_SECOND=5
      
    6. Inicialize virtualenv:

      virtualenv --python python3 env
      
    7. Ative o ambiente virtual:

      source env/bin/activate
      
    8. Instale os requisitos para loadgen:

      pip3 install -r requirements.txt
      
    9. Execute o loadgen:

      python3 loadgen.py
      

      Na inicialização, o loadgen gera uma mensagem semelhante a esta:

      Starting loadgen: 2019-05-20 10:44:12.448415
      5 request(s) complete to http://localhost:8080
      

      Na outra janela do terminal, o server grava mensagens no console semelhante a esta:

      127.0.0.1 - - [21/Jun/2019 14:22:01] "GET / HTTP/1.1" 200 -
      INFO:root:GET request,
      Path: /
      Headers:
      Host: localhost:8080
      User-Agent: python-requests/2.22.0
      Accept-Encoding: gzip, deflate
      Accept: */*
      

      Do ponto de vista da rede, todo o aplicativo está em execução no mesmo host. Por isso, use localhost para enviar solicitações para server.

    10. Para interromper loadgen e server, insira Ctrl-c em cada janela de terminal.

    11. Na janela de terminal loadgen, desative o ambiente virtual:

      deactivate
      

    Colocar o aplicativo em um contêiner

    Para executar o aplicativo no GKE, você precisa empacotar o aplicativo de amostra, server e loadgen, em contêineres. Um contêiner é uma forma de empacotar um aplicativo de modo que ele fique isolado do ambiente subjacente.

    Para colocar o aplicativo em um contêiner, você precisa de um Dockerfile. Um Dockerfile é um arquivo de texto que define os comandos necessários para juntar o código-fonte do aplicativo e as dependências em uma imagem do Docker. Depois de criar a imagem, faça o upload dela para um registro de contêiner, como o Docker Hub ou o Container Registry.

    A amostra vem com um Dockerfile para o server e o loadgen com todos os comandos necessários para criar as imagens. Veja a seguir o Dockerfile para server:

    FROM python:3.13-slim as base
    FROM base as builder
    RUN apt-get -qq update \
        && apt-get install -y --no-install-recommends \
            g++ \
        && rm -rf /var/lib/apt/lists/*
    
    # Enable unbuffered logging
    FROM base as final
    ENV PYTHONUNBUFFERED=1
    
    RUN apt-get -qq update \
        && apt-get install -y --no-install-recommends \
            wget
    
    WORKDIR /helloserver
    
    # Grab packages from builder
    COPY --from=builder /usr/local/lib/python3.* /usr/local/lib/
    
    # Add the application
    COPY . .
    
    EXPOSE 8080
    ENTRYPOINT [ "python", "server.py" ]
    
    • O comando FROM python:3-slim as base manda o Docker usar a imagem mais recente do Python 3 como a imagem de base.
    • O comando COPY . . copia os arquivos de origem no diretório de trabalho atual (neste caso, apenas server.py) para o sistema de arquivos do contêiner.
    • O ENTRYPOINT define o comando usado para executar o contêiner. Nesse caso, o comando é quase igual ao que você usou para executar o server.py a partir do código-fonte.
    • O comando EXPOSE especifica que o server escute na porta 8080. Esse comando não expõe nenhuma porta, mas serve como documentação necessária para abrir a porta 8080 ao executar o contêiner.

    Preparar para colocar o aplicativo em um contêiner

    1. Defina as seguintes variáveis de ambiente. Substitua PROJECT_ID pelo ID do seu projetoGoogle Cloud .

      export PROJECT_ID="PROJECT_ID"
      export GCR_REPO="asm-ready"

      Você usa o valor de PROJECT_ID e GCR_REPO para incluir uma tag na imagem do Docker ao criar e enviá-la ao seu Container Registry particular.

    2. Defina o projeto Google Cloud padrão para a Google Cloud CLI.

      gcloud config set project $PROJECT_ID
    3. Defina a zona padrão da Google Cloud CLI.

      gcloud config set compute/zone us-central1-b
      
    4. Verifique se o serviço do Container Registry está ativado no seu projetoGoogle Cloud .

      gcloud services enable containerregistry.googleapis.com
      

    Colocar o server em um contêiner

    1. Mude para o diretório em que o server de amostra está localizado:

      cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/server/
    2. Crie a imagem usando o Dockerfile e as variáveis de ambiente definidas anteriormente:

      docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1 .
      

      A sinalização -t representa a tag do Docker. Esse é o nome da imagem que você usa ao implantar o contêiner.

    3. Enviar a imagem ao Container Registry

      docker push gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1
      

    Colocar o loadgen em um contêiner

    1. Mude para o diretório em que o loadgen de amostra está localizado:

      cd ../loadgen
      
    2. Crie a imagem:

      docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1 .
      
    3. Enviar a imagem ao Container Registry

      docker push gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1
      

    Listar as imagens

    Gere uma lista das imagens no repositório para confirmar se as imagens foram enviadas:

    gcloud container images list --repository gcr.io/$PROJECT_ID/asm-ready
    

    O comando responde com os nomes das imagens que você acabou de enviar:

    NAME
    gcr.io/PROJECT_ID/asm-ready/helloserver
    gcr.io/PROJECT_ID/asm-ready/loadgen
    

    Criar um cluster do GKE

    É possível executar esses contêineres na VM do Cloud Shell usando o comando docker run. Mas, na produção, você precisa orquestrar contêineres de maneira mais unificada. Por exemplo, você precisa de um sistema que garanta que os contêineres estejam sempre em execução e também precisa de uma maneira de escalonar e iniciar instâncias extras de um contêiner para lidar com aumentos de tráfego.

    Use o GKE para executar aplicativos em contêineres. O GKE é uma plataforma de orquestração de contêineres que funciona ao conectar VMs cluster. Cada VM é chamada de . Os clusters do GKE são fornecidos pelo sistema de gerenciamento de clusters de código aberto Kubernetes. O Kubernetes contém os mecanismos necessários para interagir com o cluster.

    Para criar um cluster do GKE:

    1. Crie o cluster:

      gcloud container clusters create asm-ready \
        --cluster-version latest \
        --machine-type=n1-standard-4 \
        --num-nodes 4
      

      O comando gcloud cria um cluster no projeto e na zonaGoogle Cloud definidos anteriormente. Para executar o Cloud Service Mesh, recomendamos pelo menos quatro nós e o tipo de máquina n1-standard-4.

      O comando para criar o cluster leva alguns minutos para ser concluído. Quando o cluster estiver pronto, o comando gerará uma mensagem semelhante a esta:

      NAME        LOCATION       MASTER_VERSION  MASTER_IP      MACHINE_TYPE   NODE_VERSION   NUM_NODES  STATUS
      asm-ready  us-central1-b  1.13.5-gke.10   203.0.113.1    n1-standard-2  1.13.5-gke.10  4          RUNNING
      
    2. Forneça credenciais à ferramenta de linha de comando kubectl para que você possa usá-la para gerenciar o cluster:

      gcloud container clusters get-credentials asm-ready
      
    3. Agora é possível usar kubectl para se comunicar com o Kubernetes. Por exemplo, execute o comando a seguir para ver o status dos nós:

      kubectl get nodes
      

      O comando responde com uma lista dos nós, semelhante à seguinte:

      NAME                                       STATUS   ROLES    AGE    VERSION
      gke-asm-ready-default-pool-dbeb23dc-1vg0   Ready    <none>   99s    v1.13.6-gke.13
      gke-asm-ready-default-pool-dbeb23dc-36z5   Ready    <none>   100s   v1.13.6-gke.13
      gke-asm-ready-default-pool-dbeb23dc-fj7s   Ready    <none>   99s    v1.13.6-gke.13
      gke-asm-ready-default-pool-dbeb23dc-wbjw   Ready    <none>   99s    v1.13.6-gke.13
      

    Entender os principais conceitos do Kubernetes

    O diagrama a seguir descreve o aplicativo em execução no GKE:

    aplicativo em contêiner

    Antes de implantar os contêineres no GKE, convém analisar alguns dos principais conceitos do Kubernetes. No final deste tutorial, fornecemos links para que você possa saber mais sobre cada conceito.

    • Nós e clusters: no GKE, um nó é uma VM. Em outras plataformas do Kubernetes, um nó pode ser uma máquina física ou virtual. Um cluster é um conjunto de nós que podem ser tratados como uma única máquina, em que você implanta um aplicativo em contêiner.

    • Pods: no Kubernetes, os contêineres são executados em um pod. Um pod é a unidade atômica no Kubernetes. Um pod tem um ou mais contêineres. Implante os contêineres server e loadgen no próprio pod. Quando um pod executa vários contêineres (por exemplo, um servidor de aplicativos e um servidor proxy), eles são gerenciados como uma única entidade e compartilham os recursos do pod.

    • Implantações: uma implantação é um objeto do Kubernetes que representa um conjunto de pods idênticos. Uma implantação executa várias réplicas dos pods distribuídos entre os nós de um cluster. Uma implantação substitui automaticamente todos os pods com falha ou que não respondem.

    • Serviço do Kubernetes: a execução do código do aplicativo no GKE altera a rede entre loadgen e server. Ao executar os serviços em uma VM do Cloud Shell, envie solicitações para o server usando o endereço localhost:8080. Depois que você implantar no GKE, os pods serão programados para execução nos nós disponíveis. Por padrão, não é possível controlar em que nó o pod está sendo executado. Portanto, os pods não têm endereços IP estáveis.

      Para receber um endereço IP para server, defina uma abstração de rede sobre os pods, chamado de Serviço do Kubernetes. Um Serviço do Kubernetes fornece um endpoint de rede estável para um conjunto de pods. Existem vários tipos de serviços. O server usa um LoadBalancer, que expõe um endereço IP externo para que você possa acessar o server de fora do cluster.

      O Kubernetes também tem um sistema DNS integrado, que atribui nomes DNS (por exemplo, helloserver.default.cluster.local) aos serviços. Isso permite que pods dentro do cluster alcancem outros pods no cluster com um endereço estável. Não é possível usar esse nome DNS fora do cluster, como no Cloud Shell.

    Manifestos do Kubernetes

    Ao executar o aplicativo a partir do código-fonte, você usou um comando imperativo: python3 server.py

    Imperativo significa que ele está orientado por verbos: "faça isso".

    Por outro lado, o Kubernetes opera em um modelo declarativo. Isso significa que, em vez de dizer ao Kubernetes exatamente o que fazer, você informa ao Kubernetes o estado pretendido. Por exemplo, o Kubernetes inicia e encerra os pods conforme necessário para que o estado real do sistema corresponda ao estado desejado.

    Especifique o estado pretendido em um conjunto de manifestos ou arquivos YAML. Um arquivo YAML contém a especificação para um ou mais objetos do Kubernetes.

    A amostra contém um arquivo YAML para server e loadgen. Cada arquivo YAML especifica o estado desejado para o objeto de implantação e o serviço do Kubernetes.

    Servidor

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloserver
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: helloserver
      template:
        metadata:
          labels:
            app: helloserver
        spec:
          containers:
          - image: gcr.io/google-samples/istio/helloserver:v0.0.1
            imagePullPolicy: Always
            name: main
          restartPolicy: Always
          terminationGracePeriodSeconds: 5
    • kind indica o tipo de objeto.
    • metadata.name especifica o nome da implantação.
    • O primeiro campo spec contém uma descrição do estado desejado.
    • spec.replicas especifica o número de pods desejado.
    • A seção spec.template define um modelo de pod. Incluído na especificação para os pods está no campo image, que é o nome da imagem a ser extraída do Container Registry.

    O serviço é definido assim:

    apiVersion: v1
    kind: Service
    metadata:
      name: hellosvc
    spec:
      ports:
      - name: http
        port: 80
        targetPort: 8080
      selector:
        app: helloserver
      type: LoadBalancer
    • LoadBalancer: os clientes enviam solicitações para o endereço IP de um balanceador de carga de rede, que tem um endereço IP estável e pode ser acessado fora do cluster.
    • targetPort: lembre-se de que o comando EXPOSE 8080 em Dockerfile não expõe portas. Você expõe a porta 8080 para alcançar o contêiner server fora do cluster. Nesse caso, hellosvc.default.cluster.local:80 (nome abreviado: hellosvc) é mapeado para a porta 8080 do IP do pod helloserver.
    • port: é o número da porta que outros serviços no cluster usam ao enviar solicitações.

    Gerador de carga

    O objeto de implantação em loadgen.yaml é semelhante a server.yaml. Uma diferença significativa é que o objeto de implantação contém uma seção chamada env. Esta seção define as variáveis de ambiente exigidas por loadgen, que você definiu anteriormente quando executou o aplicativo a partir da origem.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: loadgenerator
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: loadgenerator
      template:
        metadata:
          labels:
            app: loadgenerator
        spec:
          containers:
          - env:
            - name: SERVER_ADDR
              value: http://hellosvc:80/
            - name: REQUESTS_PER_SECOND
              value: '10'
            image: gcr.io/google-samples/istio/loadgen:v0.0.1
            imagePullPolicy: Always
            name: main
            resources:
              limits:
                cpu: 500m
                memory: 512Mi
              requests:
                cpu: 300m
                memory: 256Mi
          restartPolicy: Always
          terminationGracePeriodSeconds: 5

    Como loadgen não aceita solicitações de entrada, o campo type é definido como ClusterIP. Esse tipo fornece um endereço IP estável que os serviços no cluster podem usar, mas o endereço IP não é exposto aos clientes externos.

    apiVersion: v1
    kind: Service
    metadata:
      name: loadgensvc
    spec:
      ports:
      - name: http
        port: 80
        targetPort: 8080
      selector:
        app: loadgenerator
      type: ClusterIP

    Implantar os contêineres no GKE

    1. Mude para o diretório em que o server de amostra está localizado:

      cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/server/
    2. Abra server.yaml em um editor de texto.

    3. Substitua o nome no campo image pelo nome da imagem do Docker.

      image: gcr.io/PROJECT_ID/asm-ready/helloserver:v0.0.1
      

      Substitua PROJECT_ID pelo ID do seu projeto Google Cloud .

    4. Salve e feche server.yaml.

    5. Implante o arquivo YAML no Kubernetes:

      kubectl apply -f server.yaml
      

      Se o procedimento for bem-sucedido, o comando responderá com o seguinte:

      deployment.apps/helloserver created
      service/hellosvc created
      

    6. Mude para o diretório em que loadgen está localizado.

      cd ../loadgen
      
    7. Abra loadgen.yaml em um editor de texto.

    8. Substitua o nome no campo image pelo nome da imagem do Docker.

      image: gcr.io/PROJECT_ID/asm-ready/loadgen:v0.0.1
      

      Substitua PROJECT_ID pelo ID do seu projeto Google Cloud .

    9. Salve e feche loadgen.yaml e o editor de texto.

    10. Implante o arquivo YAML no Kubernetes:

      kubectl apply -f loadgen.yaml
      

      Se o procedimento for bem-sucedido, o comando responderá com o seguinte:

      deployment.apps/loadgenerator created
      service/loadgensvc created
      

    11. Verifique o status dos pods:

      kubectl get pods
      

      O comando responde com o status semelhante ao seguinte:

      NAME                             READY   STATUS    RESTARTS   AGE
      helloserver-69b9576d96-mwtcj     1/1     Running   0          58s
      loadgenerator-774dbc46fb-gpbrz   1/1     Running   0          57s
      
    12. Encontre os registros do aplicativo do pod loadgen. Substitua POD_ID pelo identificador da saída anterior.

      kubectl logs loadgenerator-POD_ID
      
    13. Encontre os endereços IP externos de hellosvc:

      kubectl get service
      

      O resultado do comando é semelhante ao seguinte:

      NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
      hellosvc     LoadBalancer   10.81.15.158   192.0.2.1       80:31127/TCP   33m
      kubernetes   ClusterIP      10.81.0.1      <none>          443/TCP        93m
      loadgensvc   ClusterIP      10.81.15.155   <none>          80/TCP         4m52s
      
    14. Envie uma solicitação para hellosvc. Substitua EXTERNAL_IP pelo endereço IP externo do hellosvc.

      curl http://EXTERNAL_IP
      

    Preparação para o Cloud Service Mesh

    Agora o aplicativo foi implantado no GKE. O loadgen pode usar o DNS do Kubernetes (hellosvc:80) para enviar solicitações para server e para server com um endereço IP externo. Embora o Kubernetes ofereça muitos recursos, algumas informações sobre os serviços estão faltando:

    • Como os serviços interagem? Qual é a relação entre os serviços? Como o tráfego flui entre os serviços? Você sabe que loadgen envia solicitações para server, mas imagine que não está familiarizado com o aplicativo. Não é possível responder a essas perguntas na lista de pods do GKE no GKE.
    • Métricas: quanto tempo o server leva para responder às solicitações recebidas? Quantas solicitações por segundo (RPS) são enviadas ao server? Há respostas de erro?
    • Informações de segurança: o tráfego entre loadgen e o server de HTTP simples ou mTLS ?

    O Cloud Service Mesh pode responder a essas perguntas. O Cloud Service Mesh é uma versão gerenciada pelo Google Clouddo projeto de código aberto do Istio. O Cloud Service Mesh coloca um proxy sidecar do Envoy em cada pod. O proxy Envoy intercepta todo o tráfego de entrada e saída para os contêineres de aplicativos. Isso significa que server e loadgen recebem um proxy sidecar do Envoy e todo o tráfego de loadgen para server é mediado pelos proxies do Envoy. As conexões entre esses proxies do Envoy formam a malha de serviço. Essa arquitetura de serviço fornece uma camada de controle sobre o Kubernetes.

    malha de serviço

    Como os proxies do Envoy são executados nos próprios contêineres, é possível instalar o Cloud Service Mesh sobre um cluster do GKE sem causar alterações significativas no código do aplicativo. No entanto, existem algumas maneiras principais de preparar o aplicativo para ser instrumentado com o Cloud Service Mesh:

    • Serviços para todos os contêineres: as implantações server e loadgen têm um serviço do Kubernetes anexado. Mesmo o loadgen, que não recebe solicitações de entrada, tem um serviço.
    • As portas nos serviços precisam ser nomeadas: embora o GKE permita definir portas de serviço sem nome, o Cloud Service Mesh exige o nome de uma porta que corresponda ao protocolo da porta. No arquivo YAML, a porta para server é denominada http porque server usa o protocolo de comunicação HTTP. Se service usou gRPC, você nomeia a porta grpc.
    • As implantações são rotuladas: isso permite usar os recursos de gerenciamento de tráfego do Cloud Service Mesh, como dividir o tráfego entre as versões do mesmo serviço.

    Instalar o Cloud Service Mesh

    Acesse o guia de instalação do Cloud Service Mesh e siga as instruções para instalar o Cloud Service Mesh no cluster.

    Limpar

    Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

    Para limpar, exclua o cluster do GKE. A exclusão do cluster exclui todos os recursos que compõem o cluster de contêiner, como instâncias de computação, discos e recursos de rede.

    gcloud container clusters delete asm-ready

    A seguir