Kubernetes 2: O Retorno
Tags: kubernetes, docker, devops, cloud
Published: 2023-10-09 15:44:23

## Relembrando o'que é o Kubernetes?

O Kubernetes, frequentemente abreviado como K8s, é uma plataforma de código aberto desenvolvida pela Google que se tornou uma ferramenta amplamente adotada para orquestrar contêineres em ambientes de computação em nuvem e infraestruturas locais. Ele foi projetado para resolver os desafios de implantação, gerenciamento e escalabilidade de aplicativos em contêineres, como aqueles criados com Docker.

Aqui estão os principais conceitos e componentes do Kubernetes:

1. **Contêineres:** Os contêineres são unidades leves e isoladas que empacotam um aplicativo e todas as suas dependências, garantindo que ele seja executado de maneira consistente em diferentes ambientes. O Kubernetes é altamente compatível com contêineres Docker, mas também suporta outros formatos de contêineres.

2. **Clusters:** No Kubernetes, você cria um cluster que é composto por vários nós (ou máquinas) que executam o software do Kubernetes. Esses nós podem ser máquinas físicas ou virtuais em seu data center ou instâncias de máquinas virtuais em provedores de nuvem.

3. **Pods:** O pod é a unidade mais básica no Kubernetes. Ele contém um ou mais contêineres que compartilham o mesmo espaço de rede e armazenamento. Os contêineres em um pod geralmente estão intimamente relacionados e precisam se comunicar entre si.

4. **Service:** Os serviços são recursos do Kubernetes que permitem que os pods se comuniquem uns com os outros e com o mundo externo. Eles fornecem balanceamento de carga e uma maneira consistente de acessar os pods, independentemente de sua localização ou número de réplicas.

5. **Deployment:** Os deployments permitem que você defina como os aplicativos são implantados e escalados. Eles garantem que um número especificado de réplicas de um aplicativo esteja sempre em execução e facilitam a atualização de aplicativos sem tempo de inatividade.

6. **Namespace:** Os namespaces fornecem isolamento lógico dentro de um cluster Kubernetes. Eles são usados para segmentar recursos e aplicativos em diferentes ambientes ou equipes.

7. **Kubectl:** O kubectl é a ferramenta de linha de comando que permite interagir com clusters Kubernetes. Com o kubectl, você pode criar, modificar e gerenciar recursos no cluster.

8. **Control Plane:** O control plane é o "cérebro" do Kubernetes. Ele consiste em vários componentes, como o kube-apiserver, kube-scheduler e kube-controller-manager, que gerenciam o estado do cluster e tomam decisões sobre como a implantação deve ser mantida.

9. **Etcd:** O etcd é um banco de dados distribuído usado pelo Kubernetes para armazenar informações de configuração do cluster e o estado desejado dos recursos.

10. **Ingress Controller:** O ingress controller permite que o tráfego externo seja roteado para serviços dentro do cluster, possibilitando a exposição de aplicativos para a Internet de maneira controlada.

## Arquitetura do Kubernetes

A arquitetura do Kubernetes é fundamental para entender como essa plataforma orquestra contêineres e gerenciar aplicativos em escala. Vamos explorar os principais componentes da arquitetura do Kubernetes em detalhes:

1. **Master Node:**

O nó mestre é o componente central do cluster Kubernetes e é responsável por coordenar todas as operações no cluster. Ele consiste em vários componentes principais:

- **kube-apiserver:** É o ponto de entrada para a API do Kubernetes. Todas as interações com o cluster, como criar, atualizar ou excluir recursos, são realizadas por meio do kube-apiserver. Ele valida e processa as solicitações da API, interage com o etcd para armazenamento persistente e encaminha comandos para os componentes apropriados.

- **etcd:** É um banco de dados de chave-valor consistente usado para armazenar o estado do cluster e as configurações desejadas. O etcd é altamente disponível e garante a consistência dos dados em todo o cluster.

- **kube-scheduler:** Este componente é responsável por determinar em qual nó um novo pod deve ser implantado. Ele considera fatores como recursos disponíveis, requisitos de recursos do pod e políticas de escalabilidade ao tomar essa decisão.

- **kube-controller-manager:** Inclui vários controladores que monitoram o estado dos recursos do Kubernetes e trabalham para manter o estado desejado. Por exemplo, há controladores para pods, serviços e replicaset, garantindo que o cluster esteja sempre convergindo para o estado desejado.

- **cloud-controller-manager (opcional):** Este componente interage com a infraestrutura da nuvem subjacente (se aplicável) para gerenciar recursos específicos da nuvem, como balanceadores de carga e discos persistentes. É opcional e só é necessário em ambientes de nuvem.

2. **Node (ou Worker Node):**

Os nós (nodes) são as máquinas onde os contêineres são realmente executados. Cada nó é responsável por executar os pods e relatar seu status ao mestre. Um cluster Kubernetes pode ter vários nós, criando assim a capacidade de escalabilidade horizontal. Cada nó consiste em:

- **Kubelet:** O kubelet é o agente que roda em cada nó e se comunica com o kube-apiserver. Ele garante que os pods estejam em execução nos Nodes e que seu estado seja correspondido ao estado desejado.

- **Kube-proxy:** O kube-proxy é responsável por encaminhar o tráfego de rede para os pods. Ele configura as regras de rede para permitir a comunicação entre os pods e com o mundo externo.

- **Container Runtime:** É o software responsável por executar os contêineres em um nó. Docker é a runtime mais comum, mas o Kubernetes também suporta outras, como containerd ou CRI-O.

3. **Pods:**

Os pods são a unidade básica de implantação no Kubernetes (é aqui que o nosso container da aplicação irá ficar). Eles podem conter um ou mais contêineres que compartilham o mesmo espaço de rede e armazenamento, tornando-os ideais para aplicativos intimamente relacionados. Os pods são agendados para execução em nós pelo kube-scheduler e podem ser escalados horizontalmente (podemos fazer esses agendamentos utilizando o kubectl ou até mesmo uma interface, como a do minikube).

4. **Serviços e Ingress:**

Os serviços são recursos que permitem a comunicação entre os pods. Eles fornecem uma abstração de rede estável e um balanceamento de carga para garantir que os aplicativos sejam acessíveis e escaláveis. O Ingress, por outro lado, permite que o tráfego externo seja roteado para serviços específicos dentro do cluster. Nesse ponto é muito importante configurarmos o controle se sessões para múltiplos pods, uma prática comum e usar uma configuração de sessão permanente, na qual irá garantir que um usuário sempre utilize a mesma sessão para se conectar com os pods, essa configuração normalmente é necessária quando temos múltiplos pods do mesmo sistema, que é uma amostrar na escalabilidade que o k8s nos fornece.

5. **Namespace:**

Os namespaces são usados para segmentar e isolar recursos e aplicativos dentro do mesmo cluster Kubernetes. Eles ajudam a organizar e gerenciar recursos em ambientes compartilhados ou em várias equipes.

Esses são os principais componentes da arquitetura do Kubernetes. Eles trabalham juntos para fornecer orquestração, gerenciamento, escalabilidade e alta disponibilidade para aplicativos em contêineres em ambientes de nuvem e locais. Entender essa arquitetura é fundamental para usar eficazmente o Kubernetes e garantir que seus aplicativos sejam executados de forma confiável e eficiente.

## Implementando o Kubernetes no projeto

### Ingress

O ingress é o componente do Kubernetes que é responsável por expor nosso sistema publicamente, sendo um meio campo entre o Serviço que está nosso pod com o projeto e o público, ele serve como um balanceador de carga no cluster que joga o trafego para seus determinados pods.

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: k8s-ingress-testing
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/proxy-body-size: "240m"
    nginx.ingress.kubernetes.io/affinity: "cookie"
spec:
  tls:
  - hosts:
    - fernando.com.br
    secretName: k8s-testing-tls
  rules:
  - host: fernando.com.br
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: meu-sistema
            port:
              number: 80

```
### Dissecção

- `apiVersion: networking.k8s.io/v1`: Isso especifica a versão da API do recurso Ingress que está sendo usada. No caso, é a versão 1 da API de rede do Kubernetes.

- `kind: Ingress`: Define o tipo de recurso como Ingress, que é usado para controlar o tráfego de entrada para os serviços dentro do cluster Kubernetes.

- `metadata`: Aqui estão metadados associados ao recurso Ingress. Estes incluem:

  - `name`: O nome do recurso Ingress, que é "k8s-ingress-testing".

  - `annotations`: Anotações adicionais que fornecem informações e configurações adicionais para o Ingress. Algumas anotações específicas estão sendo usadas aqui, como a definição da classe de Ingress, reescrita de destino, emissão de certificado, tamanho do corpo do proxy e afinidade.

- `spec`: Esta seção descreve as especificações do Ingress. Aqui estão algumas partes importantes:

  - `tls`: Configuração para suporte a TLS (SSL) no Ingress. Isso indica que a rota para o host "[fernando.rosa.com.br](http://fernando.rosa.com.br/)" deve ser acessada via TLS (HTTPS) usando o certificado armazenado em "k8s-testing-tls". Isso permite a criptografia de dados entre o cliente e o servidor.

  - `rules`: Define as regras de roteamento do tráfego. No exemplo, há apenas uma regra:

  - `host`: O host pelo qual o tráfego deve ser roteado, que é "[fernando.rosa.com.br](http://fernando.rosa.com.br/)".

  - `http`: Define as regras HTTP para esse host.

  - `paths`: Define os caminhos sob o host que correspondem a esse Ingress. No exemplo, há uma única regra de caminho:

  - `pathType`: O tipo de caminho, que é "Prefix". Isso significa que qualquer URL que comece com "/" (ou seja, todas as URLs) corresponderão a esta regra.

  - `path`: O caminho em si, que é "/" neste caso, ou seja, todas as URLs.

  - `backend`: Define o serviço de destino para encaminhar o tráfego. Neste caso, o tráfego será encaminhado para o serviço chamado "meu-sistema" na porta 80.

### Configmap para o Nginx

Umas das práticas que iremos adotar em nossos cluster e pods de projeto, é a separação e dinamicidade das configurações do nginx, para isso iremos utilizar o componente ConfigMap, que permite desacoplar as configurações de qualquer tipo de arquivo. Nesse caso, nosso arquivo do ConfigMap será o default.conf, que fica dentro do nginx.

```yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
data:
  default.conf: |-
      server {
        listen [::]:8080 default_server;
        listen 8080 default_server;
        server_name _;

        client_max_body_size 240m;

        sendfile off;
        tcp_nodelay on;
        absolute_redirect off;

        root /var/www/public;
        index index.php index.html;

        location / {
            try_files $uri $uri/ /index.php?q=$uri&$args;
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root /var/lib/nginx/html;
        }

        location ~ \\.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\\.php)(/.+)$;
            fastcgi_pass unix:/run/php-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param SCRIPT_NAME $fastcgi_script_name;
            fastcgi_index index.php;
            fastcgi_param HTTPS 'on';
            include fastcgi_params;
        }

        location ~* \\.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
            expires 5d;
        }

        location ~ /\\. {
            log_not_found off;
            deny all;
        }

        location ~ ^/(fpm-status|fpm-ping)$ {
            access_log off;
            allow 127.0.0.1;
            deny all;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
            fastcgi_pass unix:/run/php-fpm.sock;
        }
      }

```
### Dissecção

- `kind: ConfigMap`: Define o tipo de recurso como ConfigMap. Um ConfigMap é usado para armazenar dados de configuração em um formato chave-valor que pode ser consumido por outros recursos dentro do cluster Kubernetes.

- `apiVersion: v1`: Isso especifica a versão da API do recurso ConfigMap que está sendo usada. Neste caso, é a versão 1 da API principal do Kubernetes.

- `metadata`: Esta seção contém metadados associados ao recurso ConfigMap. Aqui, estão os seguintes metadados:

  - `name`: O nome do ConfigMap, que é "nginx-config". Isso é o identificador exclusivo do ConfigMap dentro do namespace onde está sendo criado.

- `data`: Esta seção é onde os dados de configuração são definidos no formato chave-valor. No exemplo fornecido, há uma única chave chamada "default.conf", seguida por ":-" para indicar que o valor associado a essa chave será fornecido na próxima linha.

  - `default.conf`: Este é o nome da chave no ConfigMap. Esta chave é usada para armazenar configurações específicas do Nginx, que provavelmente são arquivos de configuração ou fragmentos de configuração em texto.

### Service

O Service padrão do Kubernetes quando não especificamos nada, e o ClusterIP, que é basicamente uma exposição da porta configurada para que nossos pods possam expor os serviços necessários. Controlamos as associação de pods x service com a configuração `app: meu-sistema`, o qual deve ser configurada posteriormente nos componentes de Deployment no Kubernetes.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: meu-sistema
spec:
  selector:
    app: meu-sistema
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

```
### Dissecção

- `apiVersion: v1`: Isso especifica a versão da API do recurso Service que está sendo usada. Neste caso, é a versão 1 da API principal do Kubernetes.

- `kind: Service`: Define o tipo de recurso como Service. Um Service no Kubernetes é usado para expor aplicativos em execução dentro do cluster para comunicação com outros recursos dentro ou fora do cluster.

- `metadata`: Esta seção contém metadados associados ao recurso Service. Aqui, está o seguinte metadado:

  - `name`: O nome do Service, que é "meu-sistema". Este é o identificador exclusivo do Service dentro do namespace onde está sendo criado.

- `spec`: Esta seção contém as especificações para o Service. As partes relevantes incluem:

  - `selector`: Esta seção especifica um conjunto de rótulos que são usados para selecionar os pods de destino para os quais o tráfego será direcionado. No caso, os pods de destino são selecionados com base no rótulo "app" com o valor "meu-sistema". Isso significa que este Service encaminhará o tráfego para todos os pods que têm o rótulo "app" definido como "meu-sistema".

  - `ports`: Esta seção especifica as portas nas quais o Service estará disponível e para onde o tráfego será encaminhado. No exemplo:

  - `protocol`: Define o protocolo de rede a ser usado, que é "TCP". Isso indica que o Service lida com tráfego TCP.

  - `port`: A porta no Service onde o tráfego será recebido, que é a porta 80.

  - `targetPort`: A porta no pod de destino para onde o tráfego será encaminhado, que é a porta 8080.

### Deployment

Esse componente é oque de fato irá realizar o deploy da nossa aplicação, contanto que ela já esteja no Registry e seja acessível (Consulte Docker para mais informações). Aqui é onde realizamos a implementação de configMaps, réplicas e quais imagem e containers nosso pod irá ter. Um ponto importante a se notar é a configuração `matchLabels:` que é onde será vinculado nossos pod a um determinado service.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: meu-sistema
spec:
  replicas: 1
  selector:
    matchLabels:
      app: meu-sistema
  template:
    metadata:
      labels:
        app: meu-sistema
    spec:
      volumes:
        - name: nginx-config-volume
          configMap:
            name: nginx-config
      containers:
        - image: registry.digitalocean.com/fernando/meu-sistema:alpine-php-nginx-2.0
          name: meu-sistema
          volumeMounts:
            - name: nginx-config-volume
              mountPath: /etc/nginx/conf.d
          envFrom:
            - configMapRef:
                name: env

```

### Dissecção

- `apiVersion: apps/v1`: Isso especifica a versão da API do recurso Deployment que está sendo usada. Neste caso, é a versão 1 da API de aplicativos do Kubernetes.

- `kind: Deployment`: Define o tipo de recurso como Deployment. Um Deployment no Kubernetes é usado para gerenciar a implantação de réplicas de pods.

- `metadata`: Esta seção contém metadados associados ao recurso Deployment. Aqui, está o seguinte metadado:

  - `name`: O nome do Deployment, que é "meu-sistema". Este é o identificador exclusivo do Deployment dentro do namespace onde está sendo criado.

- `spec`: Esta seção contém as especificações para o Deployment. As partes relevantes incluem:

  - `replicas`: Define o número desejado de réplicas do pod que o Deployment manterá em execução. Neste caso, está definido como 1, o que significa que deve haver uma única réplica do pod.

  - `selector`: Esta seção especifica um conjunto de rótulos que são usados para selecionar os pods de destino controlados pelo Deployment. No caso, os pods de destino são selecionados com base no rótulo "app" com o valor "meu-sistema".

  - `template`: Define o modelo para a criação de pods controlados pelo Deployment.

  - `metadata`: Esta seção contém rótulos associados aos pods criados pelo Deployment. No caso, o rótulo "app" com o valor "meu-sistema" é adicionado aos pods.

  - `spec`: Esta seção descreve as especificações dos pods criados pelo Deployment. As partes relevantes incluem:

  - `volumes`: Esta seção especifica os volumes que podem ser montados nos pods. No exemplo, há um volume chamado "nginx-config-volume" que é um ConfigMap chamado "nginx-config". Isso permite que os pods acessem os dados de configuração do ConfigMap "nginx-config".

  - `containers`: Aqui, é definido o container que será executado nos pods. As partes relevantes do container incluem:

  - `image`: A imagem do container que será usada. Neste caso, é uma imagem específica do Docker hospedada em "[registry.digitalocean.com/fernando/meu-sistema:alpine-php-nginx-2.0](http://registry.digitalocean.com/fernando/meu-sistema:alpine-php-nginx-2.0)".

  - `name`: O nome do container, que é "meu-sistema".

  - `volumeMounts`: Esta seção especifica como os volumes definidos anteriormente são montados no container. Neste caso, o volume "nginx-config-volume" é montado em "/etc/nginx/conf.d" no container.

  - `envFrom`: Esta seção permite que o container obtenha variáveis de ambiente de um ConfigMap chamado "env".

## Considerações finais

Essa é uma implementação basica de um ambiente Kubernetes, configurações como o uso de Values e Secrets para dados sensíveis não foram utilizadas, por conta desse tutorial seguir uma linha do iniciante para o avançado.

Mas, assim como o primeiro, vamos ter a parte 3 deste tutorial, onde iremos implementar monitoramento utilizando o Prometheus e o Grafana para análise de desempenho de cada ingress e cada pod do nosso ambiente, juntamente com a utilização de secrets e de como utilizados da forma mais segura e correta para controle dinâmico de variáveis de ambiente.