Kubernetes : Deployment e Services


 Hoje vamos falar de Deployment e Services no Kubernetes.

No artigo Kubernetes : Pod, ReplicaSet e Deployment eu apresentei o conceito de Pod, ReplicaSet e Deployment e hoje vamos focar no Deployment.


O Kubernetes possui 4 objetos muito importantes:

  1. Pods - Executa ou ou mais de um container;
  2. Services -  Configura a rede expondo as portas no cluster;
  3. Deployment - Mantêm um conjunto de Pods idênticos, assegurando que eles possuem a configuração correta e o número correto deles existe;

Quando executamos aplicativos no cluster Kubernetes usamos um Deployment ou seja uma implantação.

O que é um Deployment ?

Um Deployment é um objeto no Kubernetes que permite gerenciar um conjunto de Pods identificados por uma label.

Sem usar um deployment, você precisaria criar, atualizar e excluir vários Pods manualmente o que seria tedioso e inviável para um número muito grande de Pods.

Com um deployment, você declara um único objeto em um arquivo YAML e este objeto é responsável por criar os Pods, garantir que eles permaneçam atualizados e garantir que haja um número suficiente deles em execução.

Você também pode dimensionar automaticamente seus aplicativos com facilidade usando um deployment no Kubernetes. Assim, um deployment é usado para dimensionar, implantar e reverter versões de seus aplicativos nos Pods.

Com um deployment informamos ao Kubernetes quantas cópias de um Pod desejamos executar e o deployment  cuida de todo o resto.

Vamos definir o seguinte arquivo YAML para deployment com o nome novo_deployment.yaml :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment

spec:
  # 1. Quantas copias de cada pod ?
  replicas: 3

 # 3. Quais pods são gerenciados por este deployment?
  selector:
    # Precisa conferir com as labels definidas no Pod
    matchLabels:
      deploy: exemplo
  # Este modelo é configuração regular de pod
  # dentero da spec do deployment

  template:
    metadata:
      # Define as labels no pod.
      # usado no selector do deployment

       labels:
         deploy: exemplo
    spec:
      containers:
        - name: nginx
          image: nginx:1.20.2
 

Nota: No VS Code você pode instalar a extensão Kubernetes que vai te ajudar a criar arquivos YAML a partir de templates.

No arquivo após definir a versão da API do Kubernetes, o tipo de objeto que estamos criando e o nome do deployment temos a seção spec.

Nesta seção, primeiro, definimos a chave replicas que indica o número de instâncias do Pod que a implantação deve manter ativa.

Em seguida, usamos um rótulo selector para informar ao deployment quais Pods fazem parte da implantação Para isso usamos o rótulo deploy: exemplo , e,  isso basicamente diz que "todos os pods que correspondem a esses rótulos são agrupados em nossa implantação".

Depois disso, temos o objeto template.

Temos aqui um modelo de Pod dentro de nossa especificação de deployment. Quando o deployment cria Pods, ela os cria usando este template ou modelo.

Portanto, tudo sob a chave template é uma especificação de um Pod regular.

Nesse caso, a implantação criará Pods que executam a imagem do nginx e com os rótulos configurados.

Para testar vamos usar o ambiente do Windows 10 com o Docker Desktop instalado e com o Kubernetes, o minikube e o kubectl também instalados.

Como Instalar ?

Minikube -   https://minikube.sigs.k8s.io/docs/start/ 

Kubectl - https://kubernetes.io/docs/tasks/tools/
 

Primeiro vamos verificar o ambiente e ver se o minikube esta ok emitindo o comando:

minikube status

Tudo ok. Vemos que temos o Control Plane, o Kubelet e a Api Server em execução.

A seguir vamos verificar o ambiente usando o comando:  kubectl get all



Temos assim o cluster kubernetes em execução e pronto para ser usado.

Criando um Deployment

Existem dois métodos que podemos usar para criar um deployment do Kubernetes:

  1. Imperativo : Usa um comando CLI para criar um deployment;
     
  2. Declarativo : Descreve um estado de deployment desejado em um arquivo YAML (ou JSON) e aplica;

Embora o método imperativo possa ser mais rápido inicialmente, ele definitivamente tem algumas desvantagens. É difícil ver o que mudou quando você gerencia implantações usando comandos imperativos.

O método declarativo, por outro lado, é auto-documentado. Cada configuração está em um arquivo e esse arquivo pode ser gerenciado no Git. Muitos desenvolvedores podem trabalhar nas mesmas implantações e há um histórico claro de quem mudou o quê.

Para usar o método imperativo basta emitir o comando abaixo :

kubectl create deployment nginx-deployment --image nginx  --port=80

A seguir temos o comando que usa o método declarativo e o arquivo yaml novo_deployment.yaml :

kubectl apply -f novo_deployment.yaml
kubectl get all

Após alguns segundos vamos emitir novamente o comando para obter o status: kubectl get all

Observe que temos os Pods criados , o deployment e também um Replicaset. Assim um Deployment sempre cria e gerencia um Replicaset.

Vamos agora emitir o comando para emitir uma descrição do deployment

kubectl describe deployment nginx-deployment

Temos uma descrição completa do deployment feito, onde destacamos o tipo de estratégia usada para criar/reconstruir os pods quando uma atualização for feita que foi definida como RollingUpdate.

A estratégia RollingUpdate permite uma migração ordenada de uma versão de um aplicativo para uma versão mais recente. É a estratégia padrão usada no Kubernetes.

Além dessa temos também as seguintes estratégias:

Vamos agora escalar os Pods para 10 e alterar a tag da imagem do nginx para latest.

kubectl scale deployment nginx-deployment --replicas=10
kubectl get all

Observe que temos 5 containers sendo criados, e que de um total de 10 pods temos 5 disponíveis.

Após alguns segundos emitindo do comando : kubectl get all



Agora note que todos os Pods foram criados e os contêineres estão todos em execução.

Services

Os deployments garantem que seus aplicativos permaneçam disponíveis, mantendo o número desejado de Pods em execução e substituindo Pods não íntegros por novos.

Sabendo que um Pod tem um IP que pode mudar quando é substituído por um novo Pod, você pode antecipar que o IP do seu aplicativo muda com frequência.

Se você tiver vários Pods executando, seu aplicativo, terá vários IPs alterados com frequência para seu aplicativo. Isso torna difícil estabelecer uma comunicação estável entre o mundo externo e seu aplicativo e entre vários aplicativos dentro de seu cluster.

Para resolver este problema o Kubernetes oferece o objeto Service.

Um Service é uma abstração lógica para um grupo implantado de pods em um cluster e permite que um grupo de Pods receba um nome e um endereço IP exclusivo (clusterIP) e estável

Assim, um Service consiste em um conjunto de regras iptables dentro de seu cluster que o transformam em um componente virtual. Como ele não consome memória e não é uma instância em execução, não pode ser desativado.

Um Service pode expor um Deployment do Kubernetes oferecendo um IP estático na frente dele e, em vez de conversar com os pods, você conversaria com o serviço, que roteia o tráfego para os pods. O DNS interno em um cluster Kubernetes possibilita até mesmo conversar com um serviço usando um FQDN em vez de um IP.

Por fim, não importa em qual nó um Pod esta implantado, o Service escolherá um Pod usando o algoritmo round-robin.

Existem 4 tipos de serviços disponíveis:

  1. ClusterIP É o tipo de serviço padrão e cria um serviço interno na frente de sua implantação. O serviço não pode ser acessado de fora do cluster. Esse tipo de serviço é recomendado para estabelecer comunicação intracluster entre aplicativos.
  1. NodePort O tipo de serviço NodePort pode ser usado para expor um serviço ao público sem um balanceador de carga. Isso geralmente é usado durante o teste ou com um cluster local como o minikube. O serviço expõe o deployment e cria uma rota para cada nó no cluster Kubernetes, escolhendo automaticamente uma porta no intervalo 30000-32768.
     
  2. Load Balancer -  O tipo de serviço LoadBalancer é a solução recomendada para expor um deployment do Kubernetes porque cria um balanceador de carga na frente de seus nós e roteia o tráfego para eles. Se um nó ficar inativo, ele o encaminhará para um nó diferente. A partir daí, o serviço interno roteará o tráfego para um pod. Embora seja importante observar, esse pod não deve ser implantado no nó que recebeu o tráfego do balanceador de carga.
     
  3. ExternalName - Expõe o serviço usando um nome arbitrário (especificado através de externalName na especificação) retornando um registro de CNAME com o nome.

A seguir temos um exemplo de um Service definido usando um arquivo YAML:

Para aplicar este serviço podemos usar o comando:  kubectl apply -f service1.yaml

Como não foi definido explicitamente o serviço é do tipo ClusterIP,  não é acessível a partir do exterior e mapeia o IP de serviço XX.XX.XX.XXX:8080 para nossos pods na porta 80.

Este serviço será aplicado ao deployment definido com a Label app:nginx-deployment.

Assim um deployment é usado para manter um conjunto de Pods em execução criando Pods a partir de um modelo e um serviço é usado para permitir o acesso à rede a um conjunto de pods.

Tanto os Services quanto os Deployments escolhem em quais pods eles operam usando as labels ou rótulos e seletores(selector) de rótulo.

Você pode usar Deployments independentes de Services. Você pode usar serviços independentes de Deployments. Eles simplesmente combinam muito bem!

Em outro artigo veremos mais detalhes sobre os Services, aguarde...

"Bom é ter esperança, e aguardar em silêncio a salvação do Senhor."
Lamentações 3:26

Referências:


José Carlos Macoratti