Prepare uma aplicação para o Cloud Service Mesh

O Cloud Service Mesh é uma ferramenta poderosa para gerir e monitorizar aplicações distribuídas. Para tirar o máximo partido do Cloud Service Mesh, é útil compreender as suas abstrações subjacentes, incluindo contentores e o Kubernetes. Este tutorial explica como preparar uma aplicação para a Cloud Service Mesh a partir do código fonte para um contentor em execução no GKE, até ao ponto imediatamente antes de instalar a Cloud Service Mesh.

Se já conhece os conceitos do Kubernetes e da malha de serviços, pode ignorar este tutorial e aceder diretamente ao guia de instalação da malha de serviços da Google Cloud.

Objetivos

  1. Explore uma aplicação "hello world" simples com vários serviços.
  2. Execute a aplicação a partir da origem
  3. Contentorize a aplicação.
  4. Crie um cluster do Kubernetes.
  5. Implemente os contentores no cluster.

Antes de começar

Siga os passos abaixo para ativar a API Cloud Service Mesh:
  1. Visite a página do Kubernetes Engine na Google Cloud consola.
  2. Crie ou selecione um projeto.
  3. Aguarde até que a API e os serviços relacionados sejam ativados. Esta ação pode demorar vários minutos.
  4. Verify that billing is enabled for your Google Cloud project.

Este tutorial usa o Cloud Shell, que aprovisiona uma máquina virtual g1-small do Compute Engine que executa um sistema operativo Linux baseado em Debian.

Prepare o Cloud Shell

As vantagens da utilização do Cloud Shell são:

  • Os ambientes de desenvolvimento do Python 2 e Python 3 (incluindo virtualenv) estão todos configurados.
  • As ferramentas de linhas de comando gcloud, docker, git e kubectl usadas neste tutorial já estão instaladas.
  • Tem à sua escolha editores de texto:

    • Editor de código, ao qual acede clicando em na parte superior da janela do Cloud Shell.

    • Emacs, Vim ou Nano, aos quais acede a partir da linha de comandos no Cloud Shell.

In the Google Cloud console, activate Cloud Shell.

Activate Cloud Shell

Transfira o exemplo de código

  1. Transfira o código-fonte helloserver:

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

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

Explore a aplicação multiserviços

A aplicação de exemplo está escrita em Python e tem dois componentes que comunicam através de REST:

  • server: Um servidor simples com um ponto final GET, / , que imprime "hello world" na consola.
  • loadgen: um script que envia tráfego para o server, com um número configurável de pedidos por segundo (RPS).

aplicação de exemplo

Execute a aplicação a partir da origem

Para se familiarizar com a aplicação de exemplo, execute-a no Cloud Shell.

  1. A partir do diretório sample-apps/helloserver, execute o comando server:

    python3 server/server.py
    

    No arranque, o server apresenta o seguinte:

    INFO:root:Starting server...
    
  2. Abra outra janela de terminal para poder enviar pedidos para server. Clique em para abrir outra sessão.

  3. Envie um pedido ao server:

    curl http://localhost:8080
    

    O server responde:

    Hello World!
    
  4. No diretório onde transferiu o exemplo de código, mude para o diretório que contém o ficheiro loadgen:

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

    export SERVER_ADDR=http://localhost:8080
    export REQUESTS_PER_SECOND=5
    
  6. Início 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 comando loadgen:

    python3 loadgen.py
    

    No arranque, o loadgen produz uma mensagem semelhante à seguinte:

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

    Na outra janela do terminal, o server escreve mensagens na consola semelhantes às seguintes:

    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, toda a aplicação está agora a ser executada no mesmo anfitrião. Por este motivo, pode usar o localhost para enviar pedidos para o server.

  10. Para parar o loadgen e o server, introduza Ctrl-c em cada janela do terminal.

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

    deactivate
    

Contentorize a aplicação

Para executar a aplicação no GKE, tem de empacotar a aplicação de exemplo, tanto server como loadgen, em contentores. Um contentor é uma forma de embalar uma aplicação de modo que fique isolada do ambiente subjacente.

Para colocar a aplicação num contentor, precisa de um Dockerfile. Um Dockerfile é um ficheiro de texto que define os comandos necessários para montar o código-fonte da aplicação e as respetivas dependências numa imagem Docker. Depois de criar a imagem, carregue-a para um registo de contentores, como o Docker Hub ou o registo de contentores.

O exemplo inclui um Dockerfile para o server e o loadgen com todos os comandos necessários para criar as imagens. Seguem-se os Dockerfile para o 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 indica ao Docker que use a imagem do Python 3 mais recente como imagem de base.
  • O comando COPY . . copia os ficheiros de origem no diretório de trabalho atual (neste caso, apenas server.py) para o sistema de ficheiros do contentor.
  • O elemento ENTRYPOINT define o comando que é usado para executar o contentor. Neste caso, o comando é quase igual ao que usou para executar server.py a partir do código fonte.
  • O comando EXPOSE especifica que o server é responsável pela deteção na porta 8080. Este comando não expõe nenhuma porta, mas serve como documentação de que tem de abrir a porta 8080 quando executar o contentor.

Prepare-se para colocar a aplicação em contentores

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

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

    Usa o valor de PROJECT_ID e GCR_REPO para etiquetar a imagem do Docker quando a cria e, em seguida, a envia para o seu registo de contentores privado.

  2. Defina o projeto Google Cloud predefinido para a CLI do Google Cloud.

    gcloud config set project $PROJECT_ID
  3. Defina a zona predefinida para a CLI gcloud.

    gcloud config set compute/zone us-central1-b
    
  4. Certifique-se de que o serviço Container Registry está ativado no seu Google Cloud projeto.

    gcloud services enable containerregistry.googleapis.com
    

Coloque o server num contentor

  1. Mude para o diretório onde se encontra o exemplo server:

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

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

    A flag -t representa a etiqueta do Docker. Este é o nome da imagem que usa quando implementa o contentor.

  3. Envie a imagem para o Container Registry:

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

Coloque o loadgen num contentor

  1. Mude para o diretório onde se encontra o exemplo loadgen:

    cd ../loadgen
    
  2. Crie a imagem:

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

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

Indica as imagens

Obtenha uma lista das imagens no repositório para confirmar que as imagens foram enviadas:

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

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

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

Crie um cluster do GKE

Pode executar estes contentores na VM do Cloud Shell usando o comando docker run. No entanto, na produção, tem de orquestrar os contentores de uma forma mais unificada. Por exemplo, precisa de um sistema que garanta que os contentores estão sempre em execução e de uma forma de aumentar e iniciar instâncias adicionais de um contentor para processar aumentos de tráfego.

Pode usar o GKE para executar aplicações contentorizadas. O GKE é uma plataforma de orquestração de contentores que funciona através da ligação de VMs a um cluster. Cada MV é referida como um . Os clusters do GKE são baseados no sistema de gestão de clusters de código aberto Kubernetes. O Kubernetes fornece os mecanismos através dos quais interage com o seu 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 Google Cloud projeto e na zona que definiu anteriormente. Para executar o Cloud Service Mesh, recomendamos, pelo menos, 4 nós e o tipo de máquina n1-standard-4.

    O comando para criar o cluster demora alguns minutos a ser concluído. Quando o cluster estiver pronto, o comando produz uma mensagem semelhante à seguinte:

    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 comandos kubectl para que a possa usar para gerir o cluster:

    gcloud container clusters get-credentials asm-ready
    
  3. Agora, pode usar o kubectl para comunicar com o Kubernetes. Por exemplo, pode executar o seguinte comando para obter o estado 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
    

Compreenda os principais conceitos do Kubernetes

O diagrama seguinte representa a aplicação em execução no GKE:

aplicação contentorizada

Antes de implementar os contentores no GKE, recomendamos que reveja alguns conceitos-chave do Kubernetes. No final deste tutorial, são disponibilizados links para que possa saber mais sobre cada conceito.

  • Nós e clusters: no GKE, um nó é uma VM. Noutras plataformas Kubernetes, um nó pode ser uma máquina física ou virtual. Um cluster é um conjunto de nós que podem ser tratados em conjunto como uma única máquina, na qual implementa uma aplicação contentorizada.

  • Pods: no Kubernetes, os contentores são executados num pod. Um pod é a unidade atómica no Kubernetes. Um pod contém um ou mais contentores. Implementa os contentores server e loadgen, cada um no seu próprio pod. Quando um Pod executa vários contentores (por exemplo, um servidor de aplicações e um servidor proxy), os contentores são geridos como uma única entidade e partilham os recursos do Pod.

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

  • Serviço Kubernetes: a execução do código da aplicação no GKE altera a rede entre o loadgen e o server. Quando executou os serviços numa VM do Cloud Shell, podia enviar pedidos para o server através do endereço localhost:8080. Depois de implementar no GKE, os pods são agendados para serem executados nos nós disponíveis. Por predefinição, não pode controlar em que nó o pod está a ser executado, pelo que os pods não têm endereços IP estáveis.

    Para obter um endereço IP para o server, tem de definir uma abstração de rede acima dos pods denominada serviço Kubernetes. Um serviço do Kubernetes fornece um ponto final 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 possa alcançar o server a partir do exterior do cluster.

    O Kubernetes também tem um sistema DNS incorporado, que atribui nomes DNS (por exemplo, helloserver.default.cluster.local) aos serviços. Isto permite que os pods no cluster alcancem outros pods no cluster com um endereço estável. Não pode usar este nome DNS fora do cluster, como a partir do Cloud Shell.

Manifestos do Kubernetes

Quando executou a aplicação a partir do código-fonte, usou um comando imperativo: python3 server.py

Imperativo significa orientado por verbos: "faz isto".

Por outro lado, o Kubernetes opera num modelo declarativo. Isto significa que, em vez de dizer exatamente ao Kubernetes o que fazer, fornece ao Kubernetes um estado desejado. Por exemplo, o Kubernetes inicia e termina os pods conforme necessário para que o estado real do sistema corresponda ao estado desejado.

Especifica o estado pretendido num conjunto de manifestos ou ficheiros YAML. Um ficheiro YAML contém a especificação de um ou mais objetos do Kubernetes.

O exemplo contém um ficheiro YAML para o server e o loadgen. Cada ficheiro YAML especifica o estado pretendido para o objeto de implementação do Kubernetes e o serviço.

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 implementação.
  • O primeiro campo spec contém uma descrição do estado pretendido.
  • spec.replicas especifica o número de pods desejados.
  • A secção spec.template define um modelo de agrupamento. A especificação dos pods inclui o campo image, que é o nome da imagem a extrair do registo de contentores.

O Serviço é definido da seguinte forma:

apiVersion: v1
kind: Service
metadata:
  name: hellosvc
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: helloserver
  type: LoadBalancer
  • LoadBalancer: os clientes enviam pedidos para o endereço IP de um balanceador de carga de rede, que tem um endereço IP estável e é acessível fora do cluster.
  • targetPort: lembre-se de que o comando EXPOSE 8080 em Dockerfile não expõe realmente nenhuma porta. Expõe a porta 8080 para poder aceder ao contentor server fora do cluster. Neste caso, hellosvc.default.cluster.local:80 (nome abreviado: hellosvc) é mapeado para a porta 8080 do IP do agrupamento helloserver.
  • port: este é o número da porta que outros serviços no cluster usam quando enviam pedidos.

Gerador de carga

O objeto de implementação em loadgen.yaml é semelhante a server.yaml. Uma diferença notável é que o objeto Deployment contém uma secção denominada env. Esta secção define as variáveis de ambiente necessárias para o loadgen, que definiu anteriormente quando executou a aplicação 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

Uma vez que o loadgen não aceita pedidos recebidos, o campo type está definido como ClusterIP. Este tipo fornece um endereço IP estável que os serviços no cluster podem usar, mas o endereço IP não é exposto a clientes externos.

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

Implemente os contentores no GKE

  1. Mude para o diretório onde se encontra o exemplo server:

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

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

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

    Substitua PROJECT_ID pelo ID do seu Google Cloud projeto.

  4. Guardar e fechar server.yaml.

  5. Implemente o ficheiro YAML no Kubernetes:

    kubectl apply -f server.yaml
    

    Em caso de êxito, o comando responde com o seguinte:

    deployment.apps/helloserver created
    service/hellosvc created
    

  6. Altere para o diretório onde se encontra loadgen.

    cd ../loadgen
    
  7. Abra loadgen.yaml num editor de texto.

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

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

    Substitua PROJECT_ID pelo ID do seu Google Cloud projeto.

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

  10. Implemente o ficheiro YAML no Kubernetes:

    kubectl apply -f loadgen.yaml
    

    Em caso de êxito, o comando responde com o seguinte:

    deployment.apps/loadgenerator created
    service/loadgensvc created
    

  11. Verifique o estado dos Pods:

    kubectl get pods
    

    O comando responde com um estado 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. Obtenha os registos da aplicação do loadgen Pod. Substitua POD_ID pelo identificador da saída anterior.

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

    kubectl get service
    

    A resposta do comando é semelhante à 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 um pedido para hellosvc. Substitua EXTERNAL_IP pelo endereço IP externo do seu hellosvc.

    curl http://EXTERNAL_IP
    

Tudo pronto para o Cloud Service Mesh

Agora, tem a aplicação implementada no GKE. O loadgen pode usar o DNS do Kubernetes (hellosvc:80) para enviar pedidos para oserver e pode enviar pedidos para o server com um endereço IP externo. Embora o Kubernetes lhe ofereça muitas funcionalidades, faltam algumas informações sobre os serviços:

  • Como é que os serviços interagem? Qual é a relação entre os serviços? Como flui o tráfego entre os serviços? Sabe que o loadgen envia pedidos para o server, mas imagine que não conhece a aplicação. Não pode responder a estas perguntas consultando a lista de pods em execução no GKE.
  • Métricas: quanto tempo demora o server a responder a pedidos recebidos? Quantos pedidos por segundo (RPS) são recebidos no server? Existem respostas de erro?
  • Informações de segurança: o tráfego entre loadgen e server é simples HTTP ou mTLS?

O Cloud Service Mesh pode fornecer respostas a estas perguntas. O Cloud Service Mesh é uma versão Google Cloudgerida do projeto Istio de código aberto. O Cloud Service Mesh funciona através da colocação de um proxy sidecar do Envoy em cada Pod. O proxy Envoy interceta todo o tráfego de entrada e saída para os contentores de aplicações. Isto significa que o server e o loadgen recebem cada um um proxy sidecar do Envoy e todo o tráfego do loadgen para o server é mediado pelos proxies do Envoy. As ligações entre estes proxies Envoy formam a malha de serviços. Esta arquitetura de malha de serviço fornece uma camada de controlo acima do Kubernetes.

malha de serviço

Uma vez que os proxies Envoy são executados nos respetivos contentores, pode instalar o Cloud Service Mesh sobre um cluster do GKE sem alterações substanciais ao código da aplicação. No entanto, existem algumas formas importantes de preparar a aplicação para ser instrumentada com a Cloud Service Mesh:

  • Serviços para todos os contentores: as implementações server e loadgen têm um serviço Kubernetes anexado. Mesmo o loadgen, que não recebe pedidos de entrada, tem um serviço.
  • As portas nos serviços têm de ter um nome: embora o GKE lhe permita definir portas de serviço sem nome, o Cloud Service Mesh requer que forneça um nome para uma porta que corresponda ao protocolo da porta. No ficheiro YAML, a porta para o server tem o nome http porque o server usa o protocolo de comunicação HTTP. Se o service usado for gRPC, deve dar o nome grpc à porta.
  • As implementações estão etiquetadas: isto permite-lhe usar funcionalidades de gestão de tráfego da Cloud Service Mesh, como dividir o tráfego entre versões do mesmo serviço.

Instale o Cloud Service Mesh

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

Limpar

Para evitar incorrer em custos na sua conta do Google Cloud pelos recursos usados neste tutorial, elimine o projeto que contém os recursos ou mantenha o projeto e elimine os recursos individuais.

Para limpar, elimine o cluster do GKE. A eliminação do cluster elimina todos os recursos que compõem o cluster de contentores, como as instâncias de computação, os discos e os recursos de rede.

gcloud container clusters delete asm-ready

O que se segue?