Preparar um aplicativo para o Anthos Service Mesh


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

Objetivos

Neste tutorial, você aprenderá a:

  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 Anthos 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. Make sure that billing is enabled for your Google Cloud project.

Siga este tutorial usando o Cloud Shell, que provisiona uma máquina virtual (VM, na sigla em inglês) g1-small do Google Compute Engine que executa um sistema operacional Linux baseado em Debian ou seu próprio computador com Linux ou macOS.

Opção A: usar 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.

Para usar o Cloud Shell:

  1. Acesse o Console do Google Cloud.
  2. Clique no botão Ativar Cloud Shell na parte superior da janela do Console do Google Cloud.

    Console do Google Cloud Platform

    Uma sessão do Cloud Shell é aberta dentro de um novo frame na parte inferior do console do Google Cloud, e exibe um prompt de linha de comando.

    Sessão do Cloud Shell

Opção B: usar ferramentas de linha de comando localmente

Se você prefere seguir este tutorial no seu computador Linux ou macOS, será necessário configurar e instalar o seguinte:

  1. Configure um ambiente de desenvolvimento Python 3 e Python 2.
  2. Instale a CLI do Google Cloud.
  3. Instale o kubectl, uma linha de comando que também é usada para se comunicar com o Kubernetes.

    gcloud components install kubectl
  4. Instale o Docker Community Edition (CE). Use a ferramenta de linha de comando docker para criar imagens de contêiner do aplicativo de amostra.

  5. Instale a ferramenta de controle de origem do Git para que você possa buscar o aplicativo de amostra no GitHub.

Faça o download do código de amostra

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

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

    cd istio-samples/sample-apps/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 ou no seu computador.

  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. Se você estiver usando o Cloud Shell, 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/istio-samples/sample-apps/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 agora está sendo executado no mesmo host (o computador local ou a VM do Cloud Shell). 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 containers. 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.9-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.9/ /usr/local/lib/python3.9/

# 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 projeto do Google 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 do 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 projeto do Google 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/istio-samples/sample-apps/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

Crie um cluster do GKE

É possível executar esses contêineres na VM do Cloud Shell ou na estação de trabalho 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 zona do Google Cloud definidos anteriormente. Para executar o Anthos 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. Quando você executa os serviços em uma VM do Cloud Shell ou no computador, é possível enviar 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 ou no computador.

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/istio-samples/sample-apps/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 projeto do Google Cloud.

  4. Salve e feche server.yaml.

  5. Implante o arquivo YAML no Kubernetes:

    kubectl apply -f server.yaml
    

    Em caso de sucesso, o comando responde 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 projeto do 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
    

    Em caso de sucesso, o comando responde 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 Anthos 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 Anthos Service Mesh pode responder a essas perguntas. O Anthos Service Mesh é uma versão gerenciada pelo Google Cloud do projeto de código aberto do Istio. O Anthos 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 Anthos 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 Anthos 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 Anthos 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 Anthos Service Mesh, como dividir o tráfego entre as versões do mesmo serviço.

Instale o Anthos Service Mesh

Acesse o guia de instalação do Anthos Service Mesh e siga as instruções para instalar o Anthos 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