Para acompanhar a palestra, siga os passos a seguir:
$ git clone --recursive \
git@github.com:vcampitelli/workshop-kubernetes.git
docs/index.html
em seu navegador
scripts
e execute:
$ minikube start --kubernetes-version=v1.26.3
A variant of the SOA structural style – arranges an application as a collection of loosely-coupled services. In a microservices architecture, services are fine-grained and the protocols are lightweight.Wikipedia
Containers are an abstraction at the app layer that packages code and dependencies together. Multiple containers can run on the same machine and share the OS kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are typically tens of MBs in size), can handle more applications and require fewer VMs and Operating systems.Docker
Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications.kubernetes.io
Anunciado em 2014 pelo Google e mantido desde 2016 pela Cloud Native Computing Foundation , suporta diversos runtimes de execução de containers, como containerd, CRI-O e qualquer implementação do CRI
kubernetes.ioPods are the smaller deployable units of computing that you can create and manage in Kubernetes.
Pod is a group of one or more containers, with shared storage and network resources, and a specification for how to run the containers. A Pod's contents are always co-located and co-scheduled, and run in a shared context.kubernetes.io
A Deployment provides declarative updates for Pods and ReplicaSets.kubernetes.io
You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new ReplicaSets, or to remove existing Deployments and adopt all their resources with new Deployments.
An abstract way to expose an application running on a set of Pods as a network service.
In Kubernetes, a Service is an abstraction which defines a logical set of Pods and a policy by which to access them (sometimes this pattern is called a micro-service).kubernetes.io
Conjunto de servidores de processamento, chamados nós, que executam aplicações containerizadas.
Todo cluster possui ao menos um servidor de processamento (worker node), que hospeda os Pods.
Em ambientes de produção, o ambiente de gerenciamento geralmente executa em múltiplos computadores e um cluster geralmente executa em múltiplos nós (nodes) , provendo tolerância a falhas e alta disponibilidade.kubernetes.io
Siga a documentação oficial em
minikube.sigs.k8s.io
para instalar o minikube
de acordo com seu sistema operacional
Para ambientes Linux com arquitetura amd64, os comandos são:
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube
Clone ou atualize este repositório:
$ git clone --recursive \
git@github.com:vcampitelli/workshop-kubernetes.git
Entre na pasta scripts
e inicialize o cluster com:
$ minikube start --kubernetes-version=v1.26.3
O minikube cria um serviço separado do Docker, então sempre que iniciarmos um terminal e quisermos interagir diretamente com ele, devemos especificar que queremos executar os comandos nesse ambiente:
$ eval $(minikube -p minikube docker-env)
Execute os comandos abaixo para fazer o build das imagens do Docker:
$ docker build -t auth:0.0.1 auth
$ docker build -t posts:0.0.1 posts
$ docker build -t comments:0.0.1 comments
$ docker build -t composition:0.0.1 composition
Agora, vamos começar a configurar as nossas aplicações:
$ minikube kubectl -- create \
-f k8s/00-jwt.yml \
-f k8s/01-auth.yml \
-f k8s/01-posts.yml \
-f k8s/01-comments.yml \
-f k8s/01-composition.yml
Para acompanhar tudo que foi configurado:
$ minikube kubectl -- get pods,services,deployments
Como não há um serviço de Load Balancing no minikube
, devemos expor os
IPs dos services para eles serem acessados localmente
Execute o comando abaixo em um novo terminal e mantenha-o rodando:
$ minikube tunnel --cleanup
Volte ao terminal anterior, espere alguns segundos até que os três
EXTERNAL-IP
s estejam alocados e execute o comando a seguir:
$ minikube kubectl -- get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
auth-service LoadBalancer 10.106.141.164 <pending> 80:32110/TCP 5m
comments-service LoadBalancer 10.102.133.17 <pending> 80:30921/TCP 5m
composition-service LoadBalancer 10.99.218.84 <pending> 80:32527/TCP 5m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m
posts-service LoadBalancer 10.106.220.106 <pending> 80:32532/TCP 5m
Volte ao terminal anterior, espere alguns segundos até que os três
EXTERNAL-IP
s estejam alocados e execute o comando a seguir:
$ minikube kubectl -- get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
auth-service LoadBalancer 10.106.141.164 10.106.141.164 80:32110/TCP 5m
comments-service LoadBalancer 10.102.133.17 10.102.133.17 80:30921/TCP 5m
composition-service LoadBalancer 10.99.218.84 10.99.218.84 80:32527/TCP 5m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m
posts-service LoadBalancer 10.106.220.106 10.106.220.106 80:32532/TCP 5m
Para facilitar, vamos guardar os IPs com as portas em variáveis:
$ export URL_AUTH=http://10.106.141.164
$ export URL_COMMENTS=http://10.102.133.17
$ export URL_COMPOSITION=http://10.99.218.84
$ export URL_POSTS=http://10.106.220.106
Dica: você pode usar o script urls.sh
para fazer isso automaticamente:
$ eval $(./urls.sh)
Para se autenticar na aplicação, execute o comando abaixo:
$ curl -X POST -u "admin:admin" "${URL_AUTH}/auth"
Resultado:
{
"access_token": "eyJhbGciOiJFUzI1NiJ9.eyJzY29wZXMiOlsidXNlcnMiLCJwb3N0cyIsImNvbW...",
"expiration": "..."
}
Salve o access_token
em uma variável para facilitar as próximas chamadas:
$ TOKEN="eyJhbGciOiJFUzI1NiJ9.eyJzY29wZXMiOlsidXNlcnMiLCJwb3N0cyIsImNvbW..."
Dica: execute o script token.sh
para fazer isso automaticamente:
$ eval $(./token.sh)
Teste os serviços:
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_POSTS}/posts"
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_COMMENTS}/comments"
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_COMPOSITION}"
Acompanhe os logs:
$ minikube kubectl -- logs -f -l app
Ative o plugin de Metrics para coletar dados do uso de recursos:
$ minikube addons enable metrics-server
Abra um novo terminal e aplique a regra de Autoscaling:
$ minikube kubectl -- apply -f k8s/02-hpa.yml
kubernetes.io/docs/tasks
Abra um novo terminal e execute o comando abaixo para acompanhar o status:
$ watch -n 1 "minikube kubectl -- get hpa"
Também é possível rodar este comando para visualizar o histórico de ações:
$ minikube kubectl -- describe hpa posts
Baixe o wrk para realizar requisições simultâneas e execute:
$ wrk -t4 -c300 -d30s \
-H "Authorization: Bearer ${TOKEN}" \
"${URL_POSTS}/posts"
PS: esse comando irá abrir 300 conexões distribuídas em 4 threads durante 30 segundos.
Ajuste esses números para atender às especificações de sua máquina.
É uma camada dedicada de infraestrutura para facilitar a comunicação entre serviços usando um proxyWikipedia
Propõe uma gestão mais eficaz das comunicações entre serviços, maior controle operacional e também o fornecimento de informações comportamentaisWikipedia
Is an open source Service Mesh that layers transparently onto existing distributed applicationsistio.io
Seguindo a documentação em istio.io:
$ curl -L https://istio.io/downloadIstio | sh -
$ cd istio-1.17.2
$ export PATH=$PWD/bin:$PATH
Isso irá adicionar a pasta istio-1.17.2/bin
em seu $PATH
temporariamente
Para fazer de forma permanente, você pode colocar o último comando
(export ...
) em algum arquivo de seu sistema que seja sempre carregado,
como /etc/profile
, ~/.bashrc
, ~/.zshrc
, entre
outros
Adicione a label istio-injection=enabled
no namespace
default, indicando que queremos injetá-lo automaticamente em todos os recursos da nossa
aplicação, e instale o perfil demo
:
$ minikube kubectl -- label namespace default istio-injection=enabled
$ istioctl install --set profile=demo -y
Saiba mais sobre os outros perfis na documentação
Vamos apagar os pods existentes para que quando o Kubernetes suba novos, o Istio possa injetar seus proxies:
$ minikube kubectl -- delete pods -l app
Vamos criar um API Gateway para fazer o roteamento dos nossos serviços:
$ minikube kubectl -- create -f k8s/03-gateway.yml
istio.io
Descubra o IP do istio-ingressgateway
:
$ minikube kubectl -- get service istio-ingressgateway -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 10.101.22.8 10.101.22.8 15021:32023/TCP, ... 1h
E mais uma vez, vamos salvar em uma variável para facilitar o uso:
$ export URL_GATEWAY=http://10.101.22.8
Vamos testar o roteamento:
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_GATEWAY}/posts"
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_GATEWAY}/comments"
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_GATEWAY}/composition"
Abra um novo terminal para aplicar as regras e acompanhar os logs:
$ minikube kubectl -- apply -f k8s/04-retry.yml
$ minikube kubectl -- logs -f -l "app=posts"
Volte ao terminal anterior e faça consultas simulando falhas intermitentes:
$ curl -H "Authorization: Bearer ${TOKEN}" -H "X-Fail: 1" "${URL_GATEWAY}/posts"
Veja nos logs que o próprio Service Mesh refaz a consulta ao serviço
istio.ioAltere algo no sistema de postagens e gere uma nova versão do Docker e aplique as regras:
$ docker build -t posts:0.0.2 posts
$ minikube kubectl -- apply -f k8s/05-shifting.yml
Faça requisições ao serviço e veja algumas indo para a nova versão:
$ curl -s -D- -H "Authorization: Bearer ${TOKEN}" -o /dev/null \
"${URL_GATEWAY}/posts"
istio.io
Processo de testar um sistema para garantir que ele pode resistir a interrupções inesperadasTech Target
The best way to avoid failure is to fail constantlyNetflix
Quebrar um sistema com a proposta de coletar informações que vão ajudar a melhorar a resiliência do sistema
É uma abordagem de teste para software e garantia de qualidade, sendo adequado a sistemas distribuídos e processos modernos
Tech TargetQuanto maior e mais complexo o sistema, mais imprevisível e caótico será seu comportamento
Estes experimentos geram intencionalmente condições turbulentas em um sistema distribuído para testá-lo e encontrar suas fraquezas
Tech TargetIdentificar fraquezas em um sistema através de experimentos controlados, que introduzem comportamentos randômicos e imprevisíveis
Tech TargetAbra um novo terminal para acompanhar os logs:
$ minikube kubectl -- logs -f service/istio-ingressgateway -n istio-system
Volte ao terminal anterior, aplique a regra para adicionar delay e abortar requisições aleatoriamente e teste os comentários:
$ minikube kubectl -- apply -f k8s/06-fault.yml
$ curl -H "Authorization: Bearer ${TOKEN}" "${URL_GATEWAY}/comments"
Nota: no Istio, não é possível injetar falha e utilizar retry / timeout no mesmo serviço (ver documentação)
Reaplique a regra padrão do API Gateway para desconsiderar as falhas:
$ minikube kubectl -- apply -f k8s/03-gateway.yml
Design pattern para criar microsserviços resilientes limitando o impacto de falhas e latências nos serviços
Um de seus objetivos básicos é lidar com falhas de forma graciosa para que não ocorram o cascateamento para outros serviços
Em um ambiente de microsserviços, falhar rapidamente é críticodzone.com
Aplique a regra que irá evitar que o serviço de composição fique lento por causa de falhas na postagem:
$ minikube kubectl -- apply -f k8s/07-circuit-breaker.yml
$ curl -D- -H "X-Sleep-Fail: 1" -H "Authorization: Bearer ${TOKEN}" \
"${URL_GATEWAY}/composition"
Acompanhe em um outro terminal:
$ minikube kubectl -- logs -f -l app
Processo que usa ferramentas de software para detectar problemas, observando as entradas e saídas da pilha de tecnologia. As entradas incluem pilhas de aplicação e infraestrutura, enquanto as saídas incluem transações de negócios, experiências de usuário e desempenho de aplicações.cisco.com
Instalando o Kiali (dashboard), Prometheus (métricas), Grafana (monitoramento) e Jaeger (tracing distribuído):
$ minikube kubectl -- create -f <caminho-para-o-istio>/samples/addons
serviceaccount/grafana configured
configmap/grafana configured
service/grafana configured
deployment.apps/grafana configured
...
Para abrir o Kiali no navegador, execute o comando abaixo e acesse o endereço informado:
$ istioctl dashboard kiali
http://localhost:20001/kiali
Dentre os diversos provedores de cloud existentes (sendo os três principais Amazon Web Services, Google Cloud Platform e Microsoft Azure), escolhi a AWS por sua menor complexidade e ampla aceitação no mercado
O Kubernetes foi criado inicialmente pelo Google, então o seu serviço Google Kubernetes Engine (GKE) é bastante recomendado
Entretanto, eu acho o painel do Google Cloud um pouco confuso e, por isso, vou demonstrar como usar Kubernetes na Amazon Web Services
Existem dois principais serviços na AWS para lidar com clusters:
Iremos utilizar o EKS, mas para saber mais sobre a diferença entre eles e qual utilizar, leia o artigo Amazon ECS vs Amazon EKS: making sense of AWS container services
Usaremos o
AWS CloudFormation
para provisionar as nossas instâncias e a biblioteca eksctl
para criar as
stacks necessárias
kubeadm
, kubelet
e kubectl
para gerenciar o cluster em
kubernetes.io/docs/setup
aws
para comunicação com os serviços da Amazon em
aws.amazon.com/cli
eksctl
para manuseio do cluster em
eksctl.io
Para se comunicar com a AWS, crie o arquivo ~/.aws/credentials
e configure-o
com as informações recebidas para seu usuário, como visto abaixo:
[default]
aws_access_key_id=Access Key recebida
aws_secret_access_key=Secret Key recebida
region=us-east-1
Os usuários foram criados seguindo a recomendação
Minimum IAM Policies
do eksctl
$ eksctl create cluster \
--region us-east-1 \
--zones "us-east-1a,us-east-1b,us-east-1c,us-east-1d,us-east-1f" \
--node-type t2.medium \
--nodes-min 1 \
--nodes-max 3 \
--with-oidc \
--asg-access \
--name workshop-<USERNAME>
Essa operação demora até 30 minutos para completar, por isso já foi feita!
$ eksctl create cluster # ...
2022-07-15 06:52:04 [ℹ] eksctl version 0.105.0
2022-07-15 06:52:04 [ℹ] using region us-east-1
2022-07-15 06:52:04 [ℹ] subnets for us-east-1a - public:192.168.0.0/20 private:192.168.80.0/20
2022-07-15 06:52:04 [ℹ] subnets for us-east-1b - public:192.168.16.0/20 private:192.168.96.0/20
2022-07-15 06:52:04 [ℹ] subnets for us-east-1c - public:192.168.32.0/20 private:192.168.112.0/20
2022-07-15 06:52:04 [ℹ] subnets for us-east-1d - public:192.168.48.0/20 private:192.168.128.0/20
2022-07-15 06:52:04 [ℹ] subnets for us-east-1f - public:192.168.64.0/20 private:192.168.144.0/20
2022-07-15 06:52:04 [ℹ] nodegroup "ng-333b9ef5" will use "" [AmazonLinux2/1.22]
2022-07-15 06:52:04 [ℹ] using Kubernetes version 1.22
2022-07-15 06:52:04 [ℹ] creating EKS cluster "workshop-1" in "us-east-1" region with managed nodes
2022-07-15 06:52:04 [ℹ] will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
2022-07-15 06:52:04 [ℹ] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-east-1 --cluster=workshop-1'
2022-07-15 06:52:04 [ℹ] Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "workshop-1" in "us-east-1"
2022-07-15 06:52:04 [ℹ] CloudWatch logging will not be enabled for cluster "workshop-1" in "us-east-1"
2022-07-15 06:52:04 [ℹ] you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=us-east-1 --cluster=workshop-1'
2022-07-15 06:52:04 [ℹ]
2 sequential tasks: { create cluster control plane "workshop-1",
2 sequential sub-tasks: {
4 sequential sub-tasks: {
wait for control plane to become ready,
associate IAM OIDC provider,
2 sequential sub-tasks: {
create IAM role for serviceaccount "kube-system/aws-node",
create serviceaccount "kube-system/aws-node",
},
restart daemonset "kube-system/aws-node",
},
create managed nodegroup "ng-333b9ef5",
}
}
2022-07-15 06:52:04 [ℹ] building cluster stack "eksctl-workshop-1-cluster"
2022-07-15 06:52:07 [ℹ] deploying stack "eksctl-workshop-1-cluster"
2022-07-15 06:52:37 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-cluster"
2022-07-15 07:05:21 [ℹ] building iamserviceaccount stack "eksctl-workshop-1-addon-iamserviceaccount-kube-system-aws-node"
2022-07-15 07:05:22 [ℹ] deploying stack "eksctl-workshop-1-addon-iamserviceaccount-kube-system-aws-node"
2022-07-15 07:05:22 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-addon-iamserviceaccount-kube-system-aws-node"
2022-07-15 07:05:53 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-addon-iamserviceaccount-kube-system-aws-node"
2022-07-15 07:05:53 [ℹ] serviceaccount "kube-system/aws-node" already exists
2022-07-15 07:05:53 [ℹ] updated serviceaccount "kube-system/aws-node"
2022-07-15 07:05:54 [ℹ] daemonset "kube-system/aws-node" restarted
2022-07-15 07:05:54 [ℹ] building managed nodegroup stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:05:55 [ℹ] deploying stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:05:55 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:06:26 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:07:05 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:08:36 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:09:32 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:10:58 [ℹ] waiting for CloudFormation stack "eksctl-workshop-1-nodegroup-ng-333b9ef5"
2022-07-15 07:10:58 [ℹ] waiting for the control plane availability...
2022-07-15 07:10:58 [✔] saved kubeconfig as "/home/vcampitelli/.kube/config"
2022-07-15 07:10:58 [ℹ] no tasks
2022-07-15 07:10:58 [✔] all EKS cluster resources for "workshop-1" have been created
2022-07-15 07:10:59 [ℹ] nodegroup "ng-333b9ef5" has 1 node(s)
2022-07-15 07:10:59 [ℹ] node "ip-192-168-55-234.ec2.internal" is ready
2022-07-15 07:10:59 [ℹ] waiting for at least 1 node(s) to become ready in "ng-333b9ef5"
2022-07-15 07:10:59 [ℹ] nodegroup "ng-333b9ef5" has 1 node(s)
2022-07-15 07:10:59 [ℹ] node "ip-192-168-55-234.ec2.internal" is ready
2022-07-15 07:11:00 [ℹ] kubectl command should work with "/home/vcampitelli/.kube/config", try 'kubectl get nodes'
2022-07-15 07:11:00 [✔] EKS cluster "workshop-1" in "us-east-1" region is ready
kubectl
Ao criarmos o cluster no passo anterior, o eksctl
irá automaticamente
gerar o arquivo ~/.kube/config
(responsável por fornecer as configurações
ao kubectl
)
Se você sobrescreveu este arquivo ou se o cluster não foi criado a partir da
máquina atual que você irá executar o kubectl
, você pode gerá-lo com:
$ eksctl utils write-kubeconfig --cluster <nome>
Para verificar os nós que foram criados, execute:
$ kubectl get nodes
Iremos utilizar o Amazon Key Management Service (KMS) para criptografar as imagens do Docker
Para criar uma chave de criptografia, podemos utilizar o comando
aws kms create
, mas ele é um pouco complexo, então geralmente faremos isso via
interface
Você irá receber o ARN da chave que foi criada para seu usuário
Devemos fazer o build build das imagens do Docker necessárias e o push para o Amazon Elastic Container Registry (ECR), que é o serviço de registro de imagens
$ export W_KMS=<ARN-RECEBIDA>
$ export W_USERNAME=<USERNAME>
$ export W_ACCOUNT=<CONTA>
$ aws ecr create-repository \
--repository-name workshop-${W_USERNAME} \
--encryption-configuration encryptionType=KMS,kmsKey=${W_KMS}
$ aws ecr get-login-password --region us-east-1 | \
docker login --username AWS \
--password-stdin ${W_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com
$ docker build -t workshop-${W_USERNAME} ${W_APP}/
$ docker tag workshop-${W_USERNAME}:latest \
${W_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com/workshop-${W_USERNAME}:latest
$ docker push \
${W_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com/workshop-${W_USERNAME}:latest
Amazon ECR: Pushing a Docker image
k8s/aws/posts.yml
e:
push
"1234567890.dkr.ecr.us-east-1.amazonaws.com/workshop-vinicius"
kubectl
:
$ kubectl create -f k8s/aws/posts.yml
Para verificar os serviços e deployments criados, execute:
$ kubectl get services
$ kubectl get deployments
Caso queira atualizar a aplicação, um novo build de imagens deve ser feito e um
kubectl rollout restart deployment
deve ser executado
$ docker build -t workshop-${W_USERNAME} posts/
$ docker tag workshop-${W_USERNAME}:latest \
${W_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com/workshop-${W_USERNAME}:latest
$ docker push \
${W_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com/workshop-${W_USERNAME}:latest
$ kubectl rollout restart deployment ${W_APP}-deployment
Kubernetes: Updating a Deployment
eksctl
não cria mais uma role
de AutoScaling, mas sim acopla uma policy inline com sufixo
PolicyAutoScaling
$ kubectl create -f k8s/cluster-autoscaler.yml
$ kubectl scale --replicas=10 deployment/nginx
$ kubectl -n kube-system logs -f deployment/cluster-autoscaler
$ kubectl get pods -l app=nginx -o wide --watch
Obrigado!