Artigos

Como criar uma configuração básica de Kubernetes

Kubernetes é uma plataforma robusta de orquestração de contêineres. Pode gerenciar seus contêineres, programá-los em diferentes nós conforme os recursos disponíveis, escalar automaticamente sua infraestrutura em resposta à demanda e fazer muitas outras coisas.

Diante da tarefa de implantar seus aplicativos, a maioria dos desenvolvedores costuma procurar a solução com menos obstáculos, seja recorrendo a um provedor totalmente gerenciado (por exemplo, Heroku) ou criando seus próprios scripts simples de implantação (ou seja, conectar ao servidor por ssh, baixar imagens Docker e iniciar contêineres).

À medida que seu projeto cresce, você passa cada vez mais tempo ajustando seus scripts de implantação, adiciona mais servidores, precisa executar comandos antes ou depois da implantação, quer poder voltar a uma versão anterior do software se algo falhar e depois precisa de um sistema de verificação de integridade para todos esses serviços... Em algum momento, começa a perceber que seu script está se tornando uma plataforma de orquestração de contêineres por si só e que provavelmente deveria parar de trabalhar nele.

Talvez você ache que usar Kubernetes para um projeto de um ou dois contêineres seja exagerado ou desnecessário, mas não se pode subestimar a flexibilidade que ele oferece. Você terá uma forma padrão de gerenciar seu sistema, poderá ampliar facilmente seu conjunto de tecnologias se necessário e também poderá migrar de um provedor de nuvem para outro com mais facilidade.

Hoje, a maioria dos provedores de servidores em nuvem tem sua própria oferta de Kubernetes. Isso significa que colocar seus clusters Kubernetes em funcionamento é muito fácil. Usaremos DigitalOcean nos exemplos, mas você deve conseguir fazer o mesmo com Amazon Web Services e outros provedores.

Configuração

Vamos criar um novo cluster Kubernetes no DigitalOcean e configurar a ferramenta kubectl para se comunicar com ele.

  1. Entre na sua conta do DigitalOcean e clique em Kubernetes no painel à esquerda,
  2. Clique em "Create a Kubernetes Cluster"
  3. Selecione uma região e reduza o número de nós para 1, pois não precisaremos de mais do que isso por enquanto.
  4. Clique em "Create Cluster". Você verá uma barra de progresso no topo. Seu cluster estará pronto quando essa barra desaparecer.
  5. Instale Kubectl. Você precisará dessa ferramenta para gerenciar seu cluster Kubernetes.
  6. Siga este guia para configurar a ferramenta doctl e conectar kubectl ao seu cluster. Outra opção é clicar em "Download Config File" e passar esse arquivo ao kubectl para se conectar ao cluster:
$ kubectl --kubeconfig=path/to/config-file.yaml <commands>

Agora devemos conseguir usar o comando kubectl para nos comunicar com nosso cluster.

$ kubectl get nodes
NAME                    STATUS   ROLES    AGE     VERSION
pool-dsayjvr1j-33ipn    Ready    <none>   8m44s   v1.16.6

Se usamos a abordagem do arquivo de configuração, o comando anterior deve se parecer com isto:

$ kubectl --kubeconfig=path/to/k8s...kubeconfig.yaml get nodes
NAME                    STATUS   ROLES   AGE   VERSION
pool-dsayjvr1j-33ipn    Ready    <none>  12m   v1.16.6

Podemos ver que nosso cluster consiste em um nó (ou seja, uma máquina de trabalho do Kubernetes).

Se usamos o método do arquivo kubeconfig, não podemos esquecer de passar a opção --kubeconfig ao kubectl toda vez. De agora em diante, todas as chamadas de kubectl aparecerão sem a opção --kubeconfig, então devemos ajustá-las conforme necessário.

Pods e Deployments

Kubernetes define pods como «as menores unidades de computação implantáveis que podem ser criadas e gerenciadas no Kubernetes». Um Pod pode ser composto de um ou mais contêineres. Nossos Pods neste exemplo, como costuma acontecer, serão compostos de apenas um contêiner.

Os Deployments, por outro lado, descrevem o estado desejado do sistema em relação aos Pods; por exemplo, podemos criar um deployment que solicite duas instâncias de determinado Pod, e o Kubernetes tentará atender a esse requisito da melhor maneira possível conforme os recursos disponíveis.

Agora vamos fazer o Kubernetes implantar nossa aplicação frontend. Vamos criar um novo arquivo chamado "frontend.yaml" com o seguinte conteúdo:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
    labels:
        app: frontend
    spec:
      containers:
    - name: frontend
        image: sophilabs/k8s-demo-frontend
        ports:
        - name: http
          containerPort: 80

As primeiras duas linhas informam ao Kubernetes que estamos definindo um Deployment usando a versão apps/v1 do esquema. Depois vem a seção "metadata", na qual damos um nome ao Deployment que estamos criando, neste caso “frontend”.

A seção "spec" é onde realmente definimos o estado desejado do nosso Deployment. Primeiro declaramos que queremos pelo menos uma réplica dos Pods que correspondam ao “selector” indicado. A seção “selector” especifica como o Deployment deve selecionar os Pods sobre os quais atua; neste caso, corresponderá a qualquer Pod rotulado como “app: frontend”.

A seção "template" é onde definimos nosso Pod. Assim como o próprio Deployment, contém uma seção “metadata”, na qual adicionamos o rótulo “app: frontend” ao Pod. Depois vem a seção “spec”, na qual definimos os contêineres que ficarão dentro desse Pod.

Neste caso, temos um contêiner chamado "frontend" usando a imagem Docker “sophilabs/k8s-demo-frontend”. A seção ports define as portas expostas pelo contêiner e lhes dá um nome; neste caso, “http” e a porta 80. Usaremos o nome da porta mais adiante.

Agora é hora de dizer ao Kubernetes para aplicar esse deployment.

$ kubectl apply -f frontend.yaml
deployment.apps/frontend created

Agora podemos inspecionar nosso cluster Kubernetes.

$ kubectl get deployments
NAME      READY  UP-TO-DATE  AVAILABLE  AGE
frontend  1/1    1           1          5s

Aqui podemos ver que nosso deployment "frontend" foi criado e está pronto. Vamos ver se nosso Pod frontend foi criado:

$ kubectl get pods
NAME                       READY    STATUS  RESTARTS   AGE
frontend-84865ff7bd-d5w4w  1/1      Running     0          8s

Ótimo, nosso pod também está em funcionamento. Lembre-se de que esse processo leva alguns segundos. Continue executando os subcomandos "get deployments" e “get pods” até a coluna “ready” mostrar “1/1.”

Agora podemos executar comandos no pod frontend e anotar o nome do Pod que obtivemos com o comando "get pods".

$ kubectl exec frontend-84865ff7bd-d5w4w -- ls
bin
boot
dev
...
var

E assim executamos o comando "ls" no contêiner frontend sem precisar configurar chaves SSH, nomes de host ou qualquer outra coisa. Ótimo!

Se quisermos abrir um shell em um contêiner em execução, podemos fazer isso com:

$ kubectl exec -ti frontend-84865ff7bd-d5w4w -- sh
/ # ls
bin dev etc home  lib media mnt opt
proc  root  run sbin  srv sys tmp usr
var
/ #

Como faremos uma requisição a esse Pod agora? Antes de ver como fazer isso corretamente, vamos conhecer outro ótimo recurso do kubectl: "port-forward"

$ kubectl port-forward frontend-84865ff7bd-d5w4w 9080:80
Forwarding from 127.0.0.1:9080 -> 80
Forwarding from [::1]:9080 -> 80

Agora todas as requisições que fizermos a localhost:9080 serão enviadas à porta 80 do Pod!

Abra um navegador e acesse http://localhost:9080

Não poderia ser mais fácil. Que tal verificar os logs do contêiner?

$ kubectl logs frontend-84865ff7bd-d5w4w

127.0.0.1 - - [23/Apr/2020:18:50:08 +0000] "GET / HTTP/1.1" 200 77 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0" "-"

2020/04/23 18:50:08 [error] 6#6: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost:9080"

127.0.0.1 - - [23/Apr/2020:18:50:08 +0000] "GET /favicon.ico HTTP/1.1" 404 154 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0" "-"

Agora você deve ver os logs que o nginx gerou quando visualizou a página!

Serviços

Agora temos um servidor frontend em execução no nosso cluster Kubernetes, mas não podemos acessá-lo sem executar o comando port-forward. Para expor nossa aplicação frontend ao mundo externo, precisamos criar um "Service"

A documentação do Kubernetes define um "Service" como «Uma forma abstrata de expor uma aplicação em execução em um conjunto de Pods como um serviço de rede».

Há várias maneiras de configurar Services no Kubernetes. Agora vamos nos concentrar nos serviços "NodePort". Um “NodePort Service” configura uma porta nos nós que encaminhará o tráfego de entrada ao Service.

Criamos um arquivo frontend-service.yaml com o seguinte conteúdo:

kind: Service
apiVersion: v1
metadata:
  name: frontend
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: http

Aqui definimos um Service chamado "frontend" do tipo “NodePort” para todos os Pods rotulados como “app: frontend”. Ele encaminha o tráfego da porta 80 à porta do contêiner chamada “http” (lembre-se de que demos o nome http à porta 80 do contêiner frontend).

Precisamos aplicar esse arquivo yaml:

$ kubectl apply -f frontend-service.yaml
service/frontend created

Vamos inspecionar o Service que acabamos de criar.

$ kubectl describe service frontend
Name:                   frontend
Namespace:              default
Labels:                 <none>
Annotations:            Selector:  app=frontend
Type:                   NodePort
IP:                     10.245.4.91
Port:                   http  80/TCP
TargetPort:             http/TCP
NodePort:               http  30695/TCP
Endpoints:              10.244.0.31:80
Session Affinity:       None
External Traffic Policy:  Cluster
Events:                 <none>

A saída mostra que o "NodePort" atribuído a esse Service é 30695. Agora precisamos do endereço ip externo do nosso nó.

$ kubectl describe nodes | grep ExternalIP
ExternalIP:  159.89.140.126

Agora você deve conseguir acessar o servidor frontend visitando: http://159.89.140.120:30695 (É claro, substitua esses números pelos que o kubectl retornar no seu caso).

Há outro aspecto importante de termos criado esse Service frontend. Agora o servidor frontend está acessível pelo nome frontend em todo o cluster; ou seja, podemos enviar uma requisição a [&lt;http://frontend&gt;](&lt;http://frontend&gt;) de dentro do cluster, e ela chegará a um dos Pods frontend em execução (por enquanto, apenas um). Vamos testar:

Abrimos um shell no contêiner frontend e executamos curl http://frontend.

$ kubectl exec -ti frontend-84865ff7bd-d5w4w -- sh
# curl http://frontend
<!DOCTYPE html>
<html>
  <body>
      <h1>Hello</h1>
  </body>
</html>

Agora vamos excluir o serviço que acabamos de criar.

$ kubectl delete -f frontend-service.yaml
service "frontend" deleted

Vamos tentar executar o comando curl novamente:

$ kubectl exec -ti frontend-84865ff7bd-d5w4w -- sh
# curl <http://frontend>
curl: (6) Could not resolve host: frontend

Podemos ver que, sem a definição do serviço "frontend", já não conseguimos acessar o Pod frontend usando o nome de domínio “frontend”.

Controlador Ingress

Nossa aplicação frontend tem pouca utilidade se não podemos acessá-la facilmente por um nome de domínio legível. A forma habitual de fazer isso no DigitalOcean e na maioria dos outros provedores de nuvem é criar um balanceador de carga e deixar que o provedor gerencie nossas regras DNS. Kubernetes oferece Services do tipo Load Balancer, que podem criar um balanceador de carga usando a API do seu provedor de nuvem e configurá-lo para apontar para o Service dentro do cluster. Esse tipo de serviço funcionaria no nosso caso porque temos apenas um Service frontend, mas o que fazemos se precisamos de controle detalhado sobre como o tráfego é roteado dentro do cluster? Para isso, podemos usar um Ingress-Controller.

Controladores Ingress são servidores HTTP (como Nginx) modificados para saber que estão em execução dentro de um cluster Kubernetes. Isso permite que respondam a eventos do cluster, como a criação e exclusão de recursos Ingress; esses recursos são usados para configurar o controlador Ingress para implementar nossos requisitos de roteamento.

Usaremos a ferramenta Helm para instalar nosso controlador ingress.

Acesse o site do Helm e instale o programa.

Depois execute estes comandos:

$ helm repo add stable [<https://kubernetes-charts.storage.googleapis.com/>](<https://kubernetes-charts.storage.googleapis.com/>)

$ helm install nginx-ingress stable/nginx-ingress

Se você usa o parâmetro --kubeconfig para kubectl, adicione --kubeconfig=path/to/k8s-....-.yaml depois do comando helm, por exemplo, helm --kubeconfig=path/to/file.yaml install &lt;etc,...&gt;

Vamos ver o que mudou no nosso cluster:

$ kubectl get pods
NAME                                           READY  STATUS  (...)
frontend-84865ff7bd-d5w4w                      1/1    Running (...)
nginx-ingress-controller-676d7fcf55-5cwtq      1/1    Running (...)
nginx-ingress-default-backend-5b967cf596-w6kd4 1/1    Running (...)

Helm adicionou mais dois pods ao nosso cluster.

Vamos verificar nossos serviços:

$ kubectl get services
NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                     AGE
kubernetes                     ClusterIP      10.245.0.1      <none>          443/TCP                     27h 
nginx-ingress-controller       LoadBalancer   10.245.108.229  167.172.10.106  80:31122/TCP,443:31442/TCP  15m
nginx-ingress-default-backend  ClusterIP      10.245.17.126   <none>          80/TCP                      15m

Agora temos um serviço nginx-ingress-controller do tipo “LoadBalancer”, o que significa que o Kubernetes criou um LoadBalancer do DigitalOcean apontado para nosso novo controlador Ingress!

Entre na sua conta do DigitalOcean, clique no link Networking (painel à esquerda), depois na aba "Load balancers", e você deve ver seu novo balanceador de carga ali.

Observe que o endereço IP do balanceador de carga é o mesmo que obtivemos com kubectl: “167.172.10.106” no nosso caso.

Só falta reinstalar nosso serviço frontend e adicionar uma rota Ingress apontada para ele.

Abra o arquivo frontend-service.yaml que criamos antes e remova a linha type: NodePort. Isso tornará nosso serviço frontend acessível apenas dentro do cluster.

Aplique o arquivo do serviço:

$ kubectl apply -f frontend-service.yaml

Em seguida, crie um arquivo frontend-ingress.yaml com o seguinte conteúdo:

kind: Ingress
apiVersion: extensions/v1beta1
metadata:
  name: frontend
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - http:
    paths:
    - path: /
        backend:
          serviceName: frontend
          servicePort: http

Você pode ver aqui que estamos definindo uma nova regra de roteamento. Toda requisição com o caminho / será encaminhada ao nosso serviço frontend na porta "http" (ou seja, a porta 80 definida em frontend-service.yaml).

Copiamos o endereço IP do nosso balanceador de carga e visitamos o site: http://167.172.10.106/ Devemos ver a página "Hello" novamente!

E é isso! Só falta apontar um nome de domínio para nosso novo balanceador de carga; podemos ler como fazer isso na documentação do nosso provedor de nuvem.

Leitura adicional

Este foi um pequeno guia para ajudar você a obter uma configuração básica, mas completa, de Kubernetes. Tenho certeza de que ainda há muitas perguntas sem resposta; você pode encontrar muito mais informações e guias em kubernetes.io. Recomendo começar pelos tutoriais interativos e avançar a partir daí.

“Como criar uma configuração básica de Kubernetes” por Juan Cabrera está sob a licença CC BY SA. Os exemplos de código-fonte estão sob a licença MIT.

Foto de sophilabs.

Classificado em Pesquisa e aprendizado.

Leituras relacionadas