Como usar o Istio Multicluster para "Burst" cargas de trabalho entre clusters

Como usar o Istio Multicluster para "explodir" cargas de trabalho entre clusters

Sobre este codelab

subjectÚltimo mar. 30, 2021 atualizado
account_circleEscrito por um Googler

1. Olá!

Agradecemos por participar do codelab do Istio Multi Cloud Burst do Google.Este codelab exige experiência prática de nível iniciante com Kubernetes, Node e Go.

O que você vai precisar

  • Conta do Google Cloud Platform (use uma já existente ou vamos fornecer contas sem custo financeiro)
  • No seu laptop (instale "kubectl", "gcloud" etc.) ou use o Google Cloud Shell.

  • Como criar um cluster do Kubernetes no GKE
  • Como instalar o Istio em um cluster do Kubernetes com o Helm
  • Como instalar o Istio Multicluster com o Helm
  • Implantar um aplicativo da Web da origem para o Kubernetes
  • Como gravar e aplicar regras de roteamento de tráfego ao Istio
  • Métricas do Prometheus
  • Criar e enviar imagens de contêineres em um cluster do Kubernetes

2. Etapas da configuração

É possível seguir este codelab em:

  • Google Cloud Shell (recomendado): shell no navegador, vem com ferramentas instaladas
  • seu laptop (siga as instruções abaixo)

Começar a usar o Google Cloud Platform

  1. Peça ao instrutor o cartão da conta de usuário sem custo financeiro se você não tiver uma conta do GCP.
  2. Acesse o console do Google Cloud e clique em "Selecionar um projeto": 5c2d9bf74c78f7e4.png
  3. Anote o "ID" do projeto em algum lugar e clique nele para fazer a seleção: ecc5e8e97bfa6559.png

O Cloud Shell fornece um shell de linha de comando no navegador com as ferramentas necessárias instaladas e autenticadas automaticamente na sua conta do Google Cloud Platform. Se você não quiser executar este exercício no Cloud Shell, pule para a próxima seção.

Acesse o console do Cloud e clique em "Ativar o Cloud Shell" na barra de ferramentas no canto superior direito:

68a17b036ce24ccb.png

Adicionar ferramentas ao Cloud Shell

  1. Instale kubectx****: faça o download dos scripts do Bash aqui e salve em um local em $PATH.
  2. Instale helm****: siga estas instruções.

Como alternativa, execute estes comandos para instalar os dois em ~/.bin e adicionar ao $PATH:

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

Confira algumas dicas rápidas para facilitar o uso do Cloud Shell:

1. Desconecte o shell em uma nova janela:

2. Usar o editor de arquivos : clique no ícone de lápis no canto superior direito para abrir um editor de arquivos no navegador. Isso será útil porque vamos copiar snippets de código para arquivos.

3. Iniciar novas guias:se você precisar de mais de uma solicitação de terminal.

4. Aumente o tamanho do texto : o tamanho padrão da fonte no Cloud Shell pode ser muito pequeno para ler.

Ctrl-+ no Linux/Windows⌘-+ no macOS.

Se você se sentir mais confortável usando seu próprio ambiente de estação de trabalho em vez do Cloud Shell, configure as seguintes ferramentas:

  1. Instalar gcloud: (pré-instalado no Cloud Shell). Siga as instruções para instalar o gcloud na sua plataforma. Vamos usar isso para criar um cluster do Kubernetes.
  2. Instalar kubectl:(pré-instalado no Cloud Shell). Execute o comando a seguir para instalar:
gcloud components install kubectl

Execute o comando a seguir para autenticar o gcloud. Você vai precisar fazer login com sua Conta do Google. Em seguida, escolha o projeto pré-criado (mostrado acima) como padrão. Você pode pular a configuração de uma zona do Compute:

gcloud init
  1. Instalar curl: Pré-instalado na maioria dos sistemas Linux/macOS. Você provavelmente já tem. Caso contrário, pesquise na Internet como fazer a instalação.
  2. Instale kubectx****: faça o download dos scripts do Bash aqui e salve-os em um local em $PATH.
  3. Instale helm****: siga estas instruções.

3. Configurar o projeto do GCP

Ative as APIs do Google Kubernetes Engine (GKE), do Google Container Registry (GCR) e do Google Cloud Build (GCB) no seu projeto:

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

Configurar as variáveis de ambiente

Vamos trabalhar bastante com nosso projeto do Google Cloud durante a configuração. Vamos definir uma variável de ambiente para referência rápida

export GCLOUD_PROJECT=$(gcloud config get-value project)

Vamos criar alguns arquivos de código e configuração durante este workshop. Crie um diretório de projeto e mude para ele.

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. Criar o cluster "principal" do Kubernetes

É possível criar clusters gerenciados do Kubernetes com facilidade usando o Google Kubernetes Engine (GKE).

O comando a seguir vai criar um cluster do Kubernetes:

  • chamado "primary",
  • na zona us-west1-a,
  • A versão mais recente do Kubernetes disponível,
  • com quatro nós iniciais
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

Isso pode levar cerca de cinco minutos. É possível acompanhar a criação do cluster no Console do Cloud.

Depois que o cluster do Kubernetes é criado, o gcloud configura o kubectl com as credenciais que apontam para o cluster.

gcloud container clusters get-credentials $cluster --zone=$zone

Agora você pode usar kubectl com seu novo cluster.

Execute o seguinte comando para listar os nós do Kubernetes do cluster (eles devem mostrar o status "Pronto"):

kubectl get nodes

Modificar os nomes do Kubeconfig para facilitar o uso

Vamos alternar entre os contextos com frequência, então é útil ter um alias curto para nossos clusters.

Esse comando renomeia a entrada kubeconfig que você acabou de criar para primary.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

Definir permissões:

Para implantar o Istio, você precisa ser um administrador do cluster. Esse comando define o e-mail associado à sua conta do Google Cloud como o administrador do cluster.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. Criar cluster "burst"

O comando a seguir vai criar um cluster do Kubernetes:

  • chamado "burst",
  • na zona us-west1-a,
  • A versão mais recente do Kubernetes disponível,
  • Com 1 nó inicial
  • Escalonamento automático ativado para até cinco nós
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

Isso pode levar cerca de cinco minutos. É possível acompanhar a criação do cluster no Console do Cloud.

Depois que o cluster do Kubernetes é criado, o gcloud configura o kubectl com as credenciais que apontam para o cluster.

gcloud container clusters get-credentials $cluster --zone=$zone

Agora você pode usar kubectl com seu novo cluster.

Execute o seguinte comando para listar os nós do Kubernetes do cluster (eles devem mostrar o status "Pronto"):

kubectl get nodes

Modificar os nomes do Kubeconfig para facilitar o uso

Esse comando vai modificar a entrada kubeconfig que você acabou de criar para burst.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

Definir permissões:

Para implantar o Istio Remote, você precisa ser um administrador do cluster. Esse comando define o e-mail associado à sua conta do Google Cloud como o administrador do cluster.

kubectl create clusterrolebinding cluster-admin-binding \
   
--clusterrole=cluster-admin \
   
--user=$(gcloud config get-value core/account)

6. Aplicar regras de firewall

Para que os dois clusters se comuniquem, precisamos criar uma regra de firewall.

Execute os comandos a seguir para criar uma regra de firewall na Google Cloud Platform que permita a comunicação dos clusters

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

Os dois clusters estão configurados e prontos para implantar o aplicativo e o Istio neles.

7. Introdução ao Istio

O que é o Istio?

O Istio é um plano de controle de malha de serviço que tem como objetivo "conectar, proteger, controlar e observar serviços". Isso é feito de várias maneiras, mas principalmente sidecarizando um contêiner de proxy ( Envoy) em cada um dos pods do Kubernetes implantados. O contêiner de proxy controla toda a comunicação de rede entre microsserviços em conjunto com um hub de política e telemetria de uso geral ( Mixer).

a25613cd581825da.png

Essas políticas podem ser aplicadas independentemente das implantações e dos serviços do Kubernetes, o que significa que o operador de rede pode observar a atividade de rede, restringir, redirecionar ou reescrever políticas de rede sem reinstalar os aplicativos associados.

Alguns dos recursos de gerenciamento de tráfego compatíveis com o Istio são:

  • Disjuntores
  • Divisão de tráfego baseada em porcentagem
  • Regravação de URL
  • término de TLS
  • Verificações de integridade
  • Balanceamento de carga

Para este workshop, vamos nos concentrar na divisão de tráfego com base em porcentagem.

Termos do Istio com que vamos trabalhar

VirtualService

Um VirtualService define um conjunto de regras de roteamento de tráfego a serem aplicadas quando um host é resolvido.

Gateway

Um gateway é um balanceador de carga que opera na borda da malha e recebe conexões HTTP/TCP de entrada ou saída. Os gateways podem especificar portas, configurações de SNI etc.

DestinationRule (em inglês)

Uma DestinationRule define políticas que se aplicam ao tráfego destinado a um serviço após o roteamento. Eles especificam a configuração para balanceamento de carga, o tamanho do pool de conexões do sidecar e as configurações de detecção de valores fora da curva.

Istio multicluster

Você pode ter notado, ao criar os dois clusters, que o cluster primary tinha quatro nós sem escalonamento automático, e o cluster burst tinha um nó com escalonamento automático de até cinco nós.

Há dois motivos para essa configuração.

Primeiro, queremos simular um cenário "on-prem" para a nuvem. Em um ambiente local, você não tem acesso a clusters de escalonamento automático, porque tem infraestrutura fixa.

Em segundo lugar, uma configuração de quatro nós (conforme definido acima) é o requisito mínimo para executar o Istio. Isso levanta a questão: se o Istio exige pelo menos quatro nós, como o cluster burst pode executar o Istio com um nó? A resposta é que o Istio Multicluster instala um conjunto muito menor dos serviços do Istio e se comunica com a instalação do Istio no cluster principal para recuperar as regras de política e publicar informações de telemetria.

8. Visão geral da arquitetura do aplicativo

Visão geral dos componentes

Vamos implantar um aplicativo de três camadas usando NodeJS e Redis.

Worker

O aplicativo worker é escrito em NodeJS e detecta solicitações POST HTTP de entrada, realiza uma operação de hash nelas e, se uma variável de ambiente chamada PREFIX estiver definida, ele vai inserir o hash com esse valor. Depois que o hash é calculado, o aplicativo envia o resultado no canal "calculation" no servidor Redis especificado.

Usaremos a variável de ambiente PREFIX mais adiante para demonstrar a funcionalidade de vários clusters.

Para referência: estes são os pacotes usados pelo aplicativo.

  • body-parser: Permite analisar nossas solicitações HTTP.
  • cors: Permite o uso do compartilhamento de recursos entre origens
  • dotenv: Análise fácil de variáveis de ambiente
  • express: Hospedagem de sites fácil
  • ioredis: Biblioteca de cliente para se comunicar com bancos de dados do Redis
  • morgan: Oferece um bom registro estruturado

Front-end

Nosso front-end também é um aplicativo NodeJS que hospeda uma página da Web usando express. Ele usa uma frequência inserida pelo usuário e envia solicitações para nosso aplicativo worker nessa taxa. Esse aplicativo também se inscreve em mensagens em um canal Redis chamado "calculation" e mostra os resultados em uma página da Web.

O aplicativo usa as seguintes dependências.

  • body-parser: Permite analisar nossas solicitações HTTP.
  • dotenv: Análise fácil de variáveis de ambiente
  • express: Hospedagem de sites fácil
  • ioredis: Biblioteca de cliente para se comunicar com bancos de dados do Redis
  • morgan: Oferece registros estruturados.
  • request: Permite fazer solicitações HTTP
  • socket.io: Permite a comunicação bidirecional da página da Web para o servidor

Esta página da Web usa o Bootstrap para estilizar e, quando executada, tem a seguinte aparência:

e5e3b9cbede4cac4.png

Diagrama de arquitetura

7ae4bc22a58f80a6.png

Diagrama de implantação

Vamos implantar nosso aplicativo final nos dois clusters criados. O cluster primary terá todos os componentes (frontend, worker e Redis) implantados, mas o cluster burst terá apenas o aplicativo worker implantado.

Confira um diagrama que descreve os dois clusters. As caixas em vermelho são serviços do Kubernetes, e as azuis são implantações do Kubernetes. As caixas amarelas indicam a instalação do Istio.

561db37c510944bd.png

Observe como o cluster burst ainda tem um serviço para Redis implantado, mesmo que não haja uma implantação para Redis no cluster. Precisamos ter esse serviço no cluster para que o DNS do Kubernetes possa resolver a solicitação, mas, quando a solicitação for realmente feita, o proxy Istio vai redirecionar a solicitação para a implantação do Redis no cluster primary.

O aplicativo final terá uma implantação adicional em execução no cluster primary com o nome istiowatcher.. Isso nos permitirá redirecionar dinamicamente o tráfego para o burst automaticamente quando o tráfego ultrapassar um determinado limite.

8f6183bdfc3f813c.png

9. Criar arquivos de implantação do aplicativo

Precisamos criar um conjunto de manifestos do Kubernetes para implantar nosso aplicativo.

Mude para o diretório raiz do projeto e crie uma nova pasta chamada kubernetes.

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

Escrever o frontend.yaml

Isso vai criar uma implantação e um serviço do Kubernetes para acessar nossa imagem de front-end.

Insira o seguinte em frontend.yaml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Pontos importantes a observar no Deployment

  • Especificamos a porta em que o aplicativo será executado como 8080
  • Definimos o endereço do worker como "http://worker-service" e vamos usar o recurso DNS integrado do Kubernetes para resolver o serviço resultante.
  • Definimos o endereço do REDIS_URL como "redis-cache-service:6379" e vamos usar o recurso DNS integrado do Kubernetes para resolver os endereços IP resultantes.
  • Também definimos as sondagens liveness e readiness para o contêiner para ajudar a informar o Kubernetes quando o contêiner estiver ativo.

Escrever worker-service.yaml

Estamos escrevendo a definição do serviço do Kubernetes em um arquivo separado da definição de implantação, porque vamos reutilizar esse serviço em vários clusters, mas vamos escrever uma implantação diferente para cada cluster.

Insira o seguinte em worker-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

Escrever worker-primary.yaml

Essa será a implantação de worker que vamos enviar para o cluster principal.

Insira o seguinte em worker-primary.yaml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

Observe que estamos seguindo o mesmo padrão de fornecer sondas liveness e readiness, além de especificar as variáveis de ambiente PORT e REDIS_URL para uso do nosso aplicativo.

Outra coisa a notar nesta implantação é a falta da variável de ambiente PREFIX. Isso significa que os resultados do cálculo serão hashes brutos (sem prefixo).

O ponto principal final dessa implantação é o rótulo cluster-type: primary-cluster. Vamos usar isso mais tarde, quando fizermos o roteamento de tráfego no Istio Multicluster.

Escrever redis.yaml

A comunicação do worker para o front-end é feita por um canal Redis. Portanto, precisamos implantar um aplicativo Redis no cluster.

Insira o seguinte em redis.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

Esta é uma implantação semipadrão de um aplicativo Redis. Ele cria um contêiner com base na imagem redis:alpine, expõe as portas adequadas e define limites de recursos razoáveis.

Escrever redis-service.yaml

Precisamos de um serviço do Kubernetes para nos comunicarmos com nosso aplicativo Redis.

Insira o seguinte em redis-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

Isso fornece o serviço chamado redis-cache-service para acessar nossa implantação do Redis.

10. Implantar o aplicativo

Com as imagens enviadas para o GCR e os manifestos do Kubernetes escritos, é um bom momento para implantar o aplicativo e conferir como ele funciona.

Execute os comandos abaixo para implantar o aplicativo.

  1. Verificar se estamos no cluster certo
kubectx primary
  1. Implantar o cache do Redis
kubectl apply -f redis.yaml
  1. Implantar o serviço Redis
kubectl apply -f redis-service.yaml
  1. Implantar o front-end
kubectl apply -f frontend.yaml
  1. Implantar o worker
kubectl apply -f worker-primary.yaml
  1. Implantar o serviço do worker
kubectl apply -f worker-service.yaml

Implantamos nosso aplicativo no GKE. Parabéns!

Teste

Aguarde os pods ficarem on-line

kubectl get pods -w

Quando todos os pods estiverem "Running", pressione Ctrl + C.

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

Percebemos que não expomos nosso front-end por um LoadBalancer. Isso porque vamos acessar o aplicativo pelo Istio mais tarde. Para testar se tudo está funcionando, vamos usar kubectl port-forward.. Execute o comando a seguir para encaminhar a porta 8080 na sua máquina local (ou no Cloud Shell) para a porta 8080 que executa a implantação frontend.

kubectl port-forward \
$
(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

Se estiver executando localmente: abra um navegador da Web e acesse http://localhost:8080.

Se você estiver executando no Cloud Shell:clique no botão "Visualização da Web" e selecione "Visualizar na porta 8080".

bdb5dc75f415be11.png

O front-end vai aparecer. Se você inserir um número na caixa "Frequência", os hashes vão começar a aparecer.

1caafaffab26897a.png

Parabéns! Tudo está pronto.

Pressione Ctrl+C para interromper o encaminhamento da porta.

11. Limpar o aplicativo implantado

Vamos aplicar o Istio ao cluster e reimplantar o aplicativo. Primeiro, vamos limpar o aplicativo atual.

Execute os comandos a seguir para excluir todas as implantações e serviços que você acabou de criar.

  1. Excluir redis-cache-service
kubectl delete -f redis-service.yaml
  1. Excluir redis
kubectl delete -f redis.yaml
  1. Excluir frontend
kubectl delete -f frontend.yaml
  1. Excluir worker
kubectl delete -f worker-primary.yaml
  1. Excluir worker-service
kubectl delete -f worker-service.yaml

12. Instalar o Istio no cluster primário

Instalar o Istio

As versões do Istio são hospedadas no GitHub. Os comandos a seguir vão fazer o download e descompactar a versão 1.0.0 do Istio.

  1. Mude para a raiz do projeto
cd ${proj}
  1. Fazer o download do arquivo
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. Extrair e remover o arquivo
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Criar modelo do Istio

A execução do comando Helm a seguir cria o modelo para instalar o Istio no cluster.

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

Isso cria um arquivo chamado istio-primary.yaml no diretório atual, que contém todas as definições e especificações necessárias para implantar e executar o Istio.

Observe os dois parâmetros --set. Eles adicionam suporte ao Prometheus e ao ServiceGraph ao sistema Istio. Vamos usar o serviço Prometheus mais tarde no laboratório.

Implantar o Istio

Para implantar o Istio, primeiro precisamos criar um namespace chamado istio-system em que as implantações e os serviços do Istio possam ser executados.

kubectl create namespace istio-system

E, por fim, aplique o arquivo istio-primary.yaml que criamos com o Helm.

kubectl apply -f istio-primary.yaml

Marcar o namespace padrão

O Istio funciona injetando um serviço de proxy sidecar em cada uma das implantações. Isso é feito com base na ativação, então precisamos rotular o namespace default com istio-injection=enabled para que o Istio possa injetar automaticamente o sidecar.

kubectl label namespace default istio-injection=enabled

Parabéns! Temos um cluster em execução com o Istio pronto para implantar nosso aplicativo.

13. Implantar nosso aplicativo com o gerenciamento de tráfego do Istio

Criar arquivos de configuração do Istio Traffic Management

O Istio funciona de maneira semelhante ao Kubernetes, já que usa arquivos yaml para configuração. Nesse sentido, precisamos criar um conjunto de arquivos para informar ao Istio como expor e encaminhar nosso tráfego.

Crie um diretório com o nome istio-manifests e mude para ele

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

Escrever frontend-gateway.yaml

Esse arquivo vai expor nosso cluster do Kubernetes de maneira semelhante a um LoadBalancer do GKE e rotear todo o tráfego de entrada para nosso serviço de front-end.

Crie um arquivo chamado frontend-gateway.yaml e insira o seguinte.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

Escrever redis-virtualservice.yaml

Crie um arquivo chamado redis-virtualservice.yaml e insira o seguinte:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

Escrever worker-virtualservice.yaml

Crie um arquivo chamado worker-virtualservice.yaml e insira o seguinte:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local  
       port:
         number: 80

Implantar políticas de gerenciamento de tráfego do Istio

A implantação das políticas do Istio é feita da mesma maneira que outros recursos do Kubernetes, com kubectl apply

  1. Aplicar nosso gateway
kubectl apply -f frontend-gateway.yaml
  1. Aplicar nosso VirtualService do Redis
kubectl apply -f redis-virtualservice.yaml
  1. Aplicar nosso VirtualService do worker
kubectl apply -f worker-virtualservice.yaml

Implantar o aplicativo

  1. Volte para o diretório kubernetes.
cd ${proj}/kubernetes
  1. Implantar o cache do Redis
kubectl apply -f redis.yaml
  1. Implantar o serviço Redis
kubectl apply -f redis-service.yaml
  1. Implantar o front-end
kubectl apply -f frontend.yaml
  1. Implantar o worker
kubectl apply -f worker-primary.yaml
  1. Implantar o serviço do worker
kubectl apply -f worker-service.yaml

Verificar

Neste ponto, reimplantamos nosso aplicativo em um cluster com o Istio e políticas de gerenciamento de tráfego.

Vamos aguardar todas as cargas de trabalho ficarem on-line

Quando todos estiverem on-line, acesse o IngressGateway que configuramos em frontend-ingressgateway.yaml.

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

Acesse o endereço <EXTERNAL-IP> ou faça um curl nele e o frontend vai aparecer.

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. Instalar o Istio no cluster &quot;burst&quot;

Estamos gastando muito tempo configurando e implantando no cluster primary, mas temos outro cluster inteiro para implantar.

Nesta seção, vamos precisar coletar variáveis de configuração nos dois clusters. Portanto, preste atenção ao cluster para cada comando.

Criar o manifesto remoto do Istio

Assim como fizemos com a implantação do Istio no cluster primary, vamos usar o Helm para criar um modelo de implantação do Istio remoto no cluster burst. No entanto, antes disso, precisamos de algumas informações sobre o cluster primary.

Coletar informações do cluster principal

Mudar para o cluster primary

kubectx primary

Os comandos a seguir recuperam os endereços IP de vários pods no cluster principal. Eles são usados pelo Istio Remote para se comunicar com o cluster principal.

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

Criar modelo remoto

Agora vamos usar helm para criar um arquivo chamado istio-remote-burst.yaml, que pode ser implantado no cluster burst.

Mudar para a raiz do projeto

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

Instalar o Istio Remote no cluster burst

Para instalar o Istio no cluster burst, precisamos seguir as mesmas etapas da instalação no cluster primary, mas usar o arquivo istio-remote-burst.yaml.

Mudar o kubecontext para burst

kubectx burst

Criar o namespace istio-system

kubectl create ns istio-system

Aplicar istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

Namespace padrão do rótulo

Mais uma vez, precisamos rotular o namespace default para que o proxy possa ser injetado automaticamente.

kubectl label namespace default istio-injection=enabled

Parabéns! Neste ponto, configuramos o Istio Remote no cluster burst. No entanto, os clusters ainda não conseguem se comunicar. Precisamos gerar um arquivo kubeconfig para o cluster burst que possa ser implantado no cluster primary para vinculá-los.

Criar kubeconfig para cluster "burst"

Mudar para cluster de burst

kubectx burst

Configurar o ambiente

Precisamos coletar algumas informações sobre o cluster para criar um arquivo kubeconfig.

  1. Conseguir o nome do cluster
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. Conseguir o nome do servidor do cluster
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Receber o nome do secret da autoridade certificadora da conta de serviço istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Receber os dados da autoridade certificadora armazenados no segredo anterior
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Receber o token armazenado no secret anterior
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

Criar o arquivo kubeconfig

Com todas essas variáveis de ambiente definidas, precisamos criar o arquivo kubeconfig.

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

Isso vai criar um novo arquivo chamado burst-kubeconfig no diretório atual, que pode ser usado pelo cluster primary para autenticar e gerenciar o cluster burst.

Mudar de volta para o cluster principal

kubectx primary

Aplicar o kubeconfig para "burst", criando um secret e o rotulando

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

Rotule o segredo para que o Istio saiba usá-lo na autenticação de vários clusters

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

Parabéns! Os dois clusters são autenticados e se comunicam entre si pelo Istio Multicluster. Vamos implantar o aplicativo entre clusters

15. Implantar um aplicativo entre clusters

Criar implantações

Altere para o diretório kubernetes.

cd ${proj}/kubernetes

Criar a implantação do worker para o cluster "burst": worker-burst.yaml

Crie um arquivo chamado worker-burst.yaml e insira o seguinte nele:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

Observe que ele é quase idêntico ao worker-primary.yaml que criamos anteriormente. Há duas diferenças principais.

A primeira diferença importante é que adicionamos a variável de ambiente PREFIX com o valor "bursty-".

env:
- name: PORT
  value
: "8081"
- name: REDIS_URL
  value
: "redis-cache-service:6379"
- name: PREFIX
  value
: "bursty-"

Isso significa que o worker no cluster burst vai prefixar todos os hashes enviados com "bursty-". Podemos usar isso para saber se o aplicativo é realmente entre clusters.

A segunda diferença importante é que mudamos o rótulo cluster-type dessa implantação de primary-cluster para burst-cluster.

labels:
  app: worker
  cluster-type: burst-cluster

Vamos usar esse rótulo mais tarde, quando atualizarmos o VirtualService.

Modificar os serviços do Istio

No momento, nossos serviços do Istio não estão aproveitando as duas implantações. 100% do nosso tráfego está sendo roteado para o cluster "principal". Vamos mudar isso.

Alterar para o diretório istio-manifests

cd ${proj}/istio-manifests

Editar worker-virtualservice.yaml para incluir DestinationRules

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

Adicionamos um segundo destino ao nosso VirtualService. Ele ainda faz referência ao mesmo host (worker-service.default.svc.cluster.local)), mas 50% do tráfego está sendo roteado para o subconjunto primary e os outros 50% para o subconjunto burst.

Definimos o subconjunto primary como implantações com o rótulo cluster-type: primary-cluster e o subconjunto burst como as implantações com o rótulo cluster-type: burst-cluster.

Isso divide o tráfego 50/50 entre os dois clusters.

Implantar no cluster

Implantar redis-service.yaml no cluster de pico

Mudar para o kubeconfig burst

kubectx burst

Mudar para a raiz do projeto

cd ${proj}

Em seguida, implante

Implantar o redis-service.yaml no cluster de pico

kubectl apply -f kubernetes/redis-service.yaml

Implantar worker-burst.yaml no cluster de burst

kubectl apply -f kubernetes/worker-burst.yaml

Implantar worker-service.yaml no cluster de pico

kubectl apply -f kubernetes/worker-service.yaml

Aplicar serviços virtuais do Istio

Mudar para o kubeconfig primary

kubectx primary

Em seguida, implantar

kubectl apply -f istio-manifests/worker-virtualservice.yaml

Verificar se funciona

Para verificar se está funcionando, acesse o ponto de entrada do Istio e observe que cerca de 50% dos hashes têm o prefixo "burst-".

78fb6e235e9f4a07.png

Isso significa que estamos conversando entre clusters. Tente mudar os pesos nos diferentes serviços e aplicar o arquivo worker-virtualservice.yaml. Essa é uma ótima maneira de equilibrar o tráfego entre clusters, mas e se pudéssemos fazer isso automaticamente?

16. Como aproveitar as métricas do Prometheus

Introdução ao Prometheus

O Prometheus é um kit de ferramentas de monitoramento e alerta de sistemas de código aberto criado originalmente no SoundCloud. Ele mantém um modelo de dados multidimensional com dados de série temporal identificados pelo nome da métrica e pares de chave-valor.

Confira o diagrama da arquitetura do Prometheus:

601e1155a825e0c2.png

Quando implantado com o Prometheus, o Istio informa automaticamente várias métricas para o servidor do Prometheus. Podemos usar essas métricas para gerenciar nossos clusters em tempo real.

Como usar as métricas do Prometheus

Para começar, precisamos expor a implantação do Prometheus.

Acesse a guia "Cargas de trabalho" no GKE e navegue até a carga de trabalho "prometheus".

b4a7a3cd67db05b3.png

Quando você estiver vendo os detalhes da implantação, acesse "Ações" -> "Expor".

c04a482e55bdfd41.png

Escolha encaminhar para a porta 9090 e digite "Balanceador de carga".

d5af3ba22a7a6ebb.png

E escolha "Exposição".

Isso vai criar um serviço em um endereço IP acessível publicamente que pode ser usado para analisar as métricas do Prometheus.

Aguarde o endpoint ficar operacional e clique no endereço IP ao lado de "Endpoints externos". b1e40ad90851da29.png

Agora você deve estar vendo a interface do Prometheus.

ed273552270337ec.png

O Prometheus fornece métricas suficientes para ser um workshop. Por enquanto, vamos começar analisando a métrica istio_requests_total.

A execução dessa consulta retorna muitos dados. São métricas de todas as solicitações que passam pela malha de serviço do Istio, e são muitas. Vamos mudar a expressão para filtrar o que realmente nos interessa:

Solicitações em que o serviço de destino é worker-service.default.svc.cluster.local e a origem é frontend-deployment limitada aos últimos 15 segundos

Essa consulta é semelhante a esta:

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

E nos fornece um conjunto de dados muito mais gerenciável para trabalhar

19d551fd5eac3785.png

Mas ainda é um pouco densa. Queremos saber as solicitações por segundo, não todas as solicitações.

Para isso, podemos usar a função rate integrada.

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

Isso está nos aproximando, mas precisamos reduzir essas métricas um pouco mais em um grupo lógico.

Para isso, podemos usar as palavras-chave sum e by para agrupar e somar nossos resultados.

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

Perfeito! Podemos extrair as métricas exatas que precisamos do Prometheus.

Nossa consulta final do Prometheus

Com tudo o que aprendemos, a consulta final que precisamos fazer ao Prometheus é

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

Agora podemos usar a API HTTP para acessar a métrica.

Podemos consultar a API com nossa consulta fazendo uma solicitação GET HTTP. Substitua <prometheus-ip-here>

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

Confira um exemplo de resposta:

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

Agora, podemos extrair o valor da métrica do JSON.

Limpeza

Precisamos excluir o serviço que acabamos de usar para expor o Prometheus. No Console do Google Cloud, acesse a parte de cima do serviço que acabamos de criar e clique em "Excluir".

d58cb51b4c922751.png

Próximas etapas:

Depois de encontrar uma maneira de descobrir como o tráfego está se movendo pelo cluster e a que taxa, a próxima etapa é escrever um binário pequeno que consulta o Prometheus periodicamente. Se as solicitações por segundo para worker excederem um determinado limite, aplique diferentes pesos de destino no serviço virtual do worker para enviar todo o tráfego ao cluster burst. Quando as solicitações por segundo ficarem abaixo de um limite inferior, envie todo o tráfego de volta para primary.

17. Criar um burst entre clusters

Configuração

Definir todo o tráfego do serviço do worker para o cluster principal

Vamos considerar que todo o tráfego destinado a worker-service e roteado para o cluster primary é o estado "padrão" do nosso aplicativo.

Edite $proj/istio-manifests/worker-virtualservice.yaml para ficar assim:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

Verifique se você está conectado ao cluster primary

kubectx primary

Aplicar istio-manifests/worker-virtualservice.yaml

kubectl apply -f istio-manifests/worker-virtualservice.yaml

Escrever o daemon istiowatcher

Vamos usar o Go para criar esse serviço por causa da velocidade e da portabilidade. O fluxo geral do aplicativo será inicializar e, a cada segundo, consultar o Prometheus.

Crie um novo diretório em src com o nome istiowatcher

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

Vamos chamar istioctl no contêiner para manipular o plano de controle do Istio no cluster.

Escrever istiowatcher.go

Crie um arquivo nesse diretório chamado istiowatcher.go e insira o seguinte nele

package main

import (
       
"github.com/tidwall/gjson"
       
"io/ioutil"
       
"log"
       
"net/http"
       
"os/exec"
       
"time"
)

func main() {
       
//These are in requests per second
       
var targetLow float64 = 10
       
var targetHigh float64 = 15
       
// This is for the ticker in milliseconds
       
ticker := time.NewTicker(1000 * time.Millisecond)

       
isBurst := false

       
// Our prometheus query
       
reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

       
for t := range ticker.C {
               
log.Printf("Checking Prometheus at %v", t)

               
// Check prometheus
               
// Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
               
// If we wanted this to be a little "snappier" we can scale it down to say 30s
               
resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
               
if err != nil {
                       
log.Printf("Error: %v", err)
                       
continue
               
}
               
defer resp.Body.Close()
               
body, _ := ioutil.ReadAll(resp.Body)

               
val := gjson.Get(string(body), "data.result.0.value.1")
               
log.Printf("Value: %v", val)

               
currentReqPerSecond := val.Float()
               
log.Printf("Reqs per second %f", currentReqPerSecond)

               
if currentReqPerSecond > targetHigh && !isBurst {
                       
applyIstio("burst.yaml")
                       
log.Println("Entering burst mode")
                       
isBurst = true
               
} else if currentReqPerSecond < targetLow && isBurst {
                       
applyIstio("natural.yaml")
                       
log.Println("Returning to natural state.")
                       
isBurst = false
               
}
       
}
}

func applyIstio(filename string) {
       
cmd := exec.Command("istioctl", "replace", "-f", filename)
       
if err := cmd.Run(); err != nil {
               
log.Printf("Error hit applying istio manifests: %v", err)
       
}
}

Escrever o Dockerfile

Crie um novo arquivo chamado Dockerfile e insira o seguinte nele.

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

Esse Dockerfile com vários estágios faz o download e extrai a versão 1.0.0 do Istio no primeiro estágio. A segunda etapa copia tudo do nosso diretório para a imagem, depois copia istioctl do estágio de build para /usr/local/bin (para que possa ser chamado pelo nosso aplicativo), recebe as dependências, compila o código e define CMD como "istiowatcher".

Gravar burst.yaml

Esse é o arquivo que o istiowatcher vai aplicar quando as solicitações/segundo para worker de frontend excederem 15.

Crie um novo arquivo chamado burst.yaml e insira o seguinte nele.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local  
       subset: primary
       port:
         number: 80      
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst
       port:
         number: 80      
     weight:  100

Escrever natural.yaml

Consideraremos esse o estado "natural" para o qual voltamos quando as solicitações/segundo de frontend para worker caírem abaixo de 10. Nesse estado, 100% do tráfego está sendo roteado para o cluster primary.

Crie um novo arquivo chamado natural.yaml e insira o seguinte nele

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local  
       subset: primary
       port:
         number: 80      
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst
       port:
         number: 80      
     weight: 0

Build and Push istiowatcher

Execute o comando a seguir para enviar o diretório atual ao Google Cloud Build (GCB), que vai criar e marcar a imagem no GCR.

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

Implantar o istiowatcher

Mude para o diretório kubernetes

cd ${proj}/kubernetes/

Escrever um arquivo de implantação: istiowatcher.yaml

Crie um arquivo chamado istiowatcher.yaml e insira o seguinte (substitua <your-project-id>).

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

Implantar

Executar no cluster principal

kubectx primary

Implante istiowatcher.yaml no namespace istio-system

kubectl apply -n istio-system -f istiowatcher.yaml

É importante observar as diretivas serviceAccountName e automountServiceAccountToken no yaml. Isso nos dá as credenciais necessárias para executar istioctl no cluster.

Também precisamos implantar isso no namespace istio-system para garantir que temos as credenciais para o istio-pilot-service-account. Ele não existe no namespace default.

Observe o tráfego mudar automaticamente.

Agora, o momento mágico! Vamos ao front-end e aumentar a taxa de req/segundo para 20

Ele leva alguns segundos, mas aumenta e todos os hashes têm o prefixo "bursty-".

Isso ocorre porque estamos amostrando o Prometheus em um intervalo de 15s, o que faz com que o tempo de resposta demore um pouco. Se quiséssemos uma faixa muito mais estreita, poderíamos mudar nossa consulta para prometheus para ser 5s.

18. A seguir

Limpeza

Não é necessário limpar se você estiver usando uma conta temporária fornecida para este workshop.

Você pode excluir seus clusters do Kubernetes, a regra de firewall e as imagens no GCR

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

O futuro