Como instalar um Cluster Multi-Node Kubernetes



Este é um guia passo-a-passo onde vamos instalar e configurar um cluster multi-node Kubernetes do zero, porém, este não é só mais guia de instalação, este é o primeiro de uma série de artigos que estou preparando para que você possa aprender e explorar o Kubernetes na prática.

Kubernetes - The hard way será uma série mão na massa, com linguagem simples e no estilo de faça você mesmo.

Para provar o que estou dizendo, vamos começar a meter a mão na massa e instalar o nosso cluster Kubernetes de Lab o qual usaremos por toda essa jornada.

Antes de começar...

Para acompanhar essa série sobre Kubernetes você vai precisar de:

  • No mínimo 2 hosts com 2GB de RAM e 10GB de disco **.
  • Sistema operacional CentOS 7 instalado (pode ser a versão minimal) *.
  • Todos os hosts devem estar na mesma rede e com acesso à internet.

⚠️
* Nos próximos artigos iremos instalar novos recursos no cluster Kubernetes que precisam de mais recursos computacionais, se você tem interesse em acompanhar os próximos artigos recomendo > 4GB de RAM e > 20GB de Disco para os hosts.
⚠️
** Não vamos abordar a instalação do S.O CentOS 7 neste guia. Espero que já tenha o S.O instalado para iniciar, caso ainda não tenha, a ISO de instalação pode ser baixada em http://ftp.unicamp.br/pub/centos/7.9.2009/isos/x86_64/

Configurações iniciais do CentOS

Antes de mais nada precisamos configurar o nosso Sistema Operacional CentOS para atender os requisitos necessários para instalação do cluster Kubernetes.

⚠️
Muita atenção aos passos a seguir, as configurações aqui descritas são essenciais e devem ser executadas em todos os hosts que irão fazer parte do nosso cluster.

Desabilitando o SELinux

O Kubernetes não tem compatibilidade com o SELinux em modo "enforcing", geralmente o padrão do CentOS. Então, devemos desabilita-lo.

Para desativar o SELinux, execute:

sudo setenforce 0 && sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config

Carregando os módulos necessários

É necessário que o módulo br_filter esteja ativo no S.O, então vamos checar se módulo já está carregado.

Execute:

sudo lsmod | grep br_netfilter

Se o comando não teve resultado, o módulo não está ativo, então, vamos ativa-lo. Execute:

sudo modprobe br_netfilter

Vamos checar novamente com o lsmod. Desta vez o resultado deve ser similar a este:

$ sudo lsmod | grep br_netfilter
br_netfilter           22248  0
bridge                107106  1 br_netfilter

Desabilitando o firewall

A instalação do Kubernetes irá configurar as regras de firewall para nós, então, neste momento, vamos desativar o firewall para que não ocorram problemas com as regras que já estão ativas.

Para desabilitar o Iptables, execute:

sudo systemctl disable firewalld --now

Vamos aproveitar e garantir que algumas flags do nosso Iptables estejam devidamente configuradas para quando ele for reativado na instalação do Kubernetes.

Execute:

sudo echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables &&
sudo echo 1 > /proc/sys/net/bridge/bridge-nf-call-ip6tables &&
sudo echo 1 > /proc/sys/net/ipv4/ip_forward

Desabilitando SWAP

O Kubernetes não pode ser inicializado se o SWAP estiver ativo. Então, precisamos desativa-lo.

Para desativar o SWAP execute:

sudo swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab

Para verificar se o SWAP foi desativado com sucesso vamos checar o arquivo /proc/swaps. Execute:

cat /proc/swaps

O comando não deve retornar resultado, assim como no exemplo a seguir:

$ cat /proc/swaps
Filename                                Type            Size    Used    Priority

Configurando a rede

Agora chegou a hora de configurar a interface de rede do host e para garantir que não tenhamos problemas em nosso cluster devemos seguir estes passos com atenção.

Primeiro, vamos configurar o endereço IP do host como estático, isso vai garantir que o DHCP não tenha ação sobre nossa interface de rede e não irá alterar o endereço IP. Podemos fazer assim:

Vamos identificar qual interface de rede está conectada em nossa rede local, execute:

nmcli c show --active

O resultado deve ser algo parecido com esse:

$ nmcli c show --active
NAME   UUID                                  TYPE      DEVICE
ens33  6a62f935-8cdd-4f51-890e-98f3dc64b0dc  ethernet  ens33

No meu host a interface se chama ens33. Agora vamos consultar algumas informações desta interface.

Execute (não se esqueça de alterar o nome interface):

nmcli c show ens33 |grep -E "ipv4.method|IP4.ADDRESS|IP4.DNS|IP4.GATEWAY"

Com o comando anterior conseguimos confirmar que a nossa interface está usando DHCP, o endereço IP, o endereço IP do gateway e o endereço IP do DNS da nossa interface, como no exemplo a seguir:

nmcli c show ens33 |grep -E "ipv4.method|IP4.ADDRESS|IP4.DNS|IP4.GATEWAY"
ipv4.method:                            auto
IP4.ADDRESS[1]:                         192.168.130.138/24
IP4.GATEWAY:                            192.168.130.2
IP4.DNS[1]:                             192.168.130.2

Agora vamos alterar nossa interface para o modo estático, mas vamos usar as mesmas configurações de endereço IP.

Para o meu host o comando a ser executado fica assim (não esqueça de mudar o nome da interface):

sudo nmcli c mod ens33 connection.autoconnect yes \
ipv4.method "manual" \
ipv4.addresses $(nmcli c show ens33 |grep -E "IP4.ADDRESS" | awk '{print $2}') \
ipv4.gateway $(nmcli c show ens33 |grep -E "IP4.GATEWAY" | awk '{print $2}') \
ipv4.dns $(nmcli c show ens33 |grep -E "IP4.DNS" | awk '{print $2}') \
ipv4.dns-search "k8scluster" 

Os campos desse comando representam o seguinte:

  • connection.autoconnect: Configura a interface para iniciar automaticamente com o S.O.
  • ipv4.method: Configura a interface para endereço IP manual (estático).
  • ipv4.addresses: O endereço IP da interface.
  • ipv4.dns: Endereço IP para consulta DNS.
  • ipv4.dns-search: O nome do domínio local do nosso cluster.

Agora, se consultarmos nossa interface novamente, teremos um resultado similar a esse:

$ nmcli c show ens33 |grep -E "ipv4.method|IP4.ADDRESS|IP4.DNS|IP4.GATEWAY"
ipv4.method:                            manual
IP4.ADDRESS[1]:                         192.168.130.138/24
IP4.GATEWAY:                            192.168.130.2
IP4.DNS[1]:                             192.168.130.2

Configurando os repositórios

Vamos configurar o CentOS para ter os repositórios de instalação e os pacotes necessários.

Primeiro vamos atualizar nosso CentOS. Execute:

sudo yum update -y && sudo yum install -y yum-utils

Agora vamos configurar o repositório do Docker. Execute:

yum-config-manager --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

E por fim o repositório do Kubernetes. Execute:

cat << EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF

Pronto, agora já temos nosso host com o CentOS configurado para seguirmos com a instalação do Kubernetes e a configuração do nosso cluster.

Instalando o Kubernetes

Depois que todas as configurações do CentOS foram realizadas, está na hora de iniciar a instalação do Kubernetes.

⚠️
Os passos a seguir para instalação do Kubernetes e suas dependências devem ser executados em todos os hosts que irão fazer parte do cluster.

Para iniciar, precisamos instalar alguns pré-requisitos para o nosso CentOS. Execute:

sudo yum install -y device-mapper-persistent-data lvm2

Agora podemos seguir com a instalação de um serviço de containers. Em nosso cluster vamos usar o Docker. Execute:

sudo yum install -y docker-ce-19.03.13-3.el7

Por último podemos instalar o Kubernetes.

sudo yum install -y kubelet-1.19.4-0 kubeadm-1.19.4-0 kubectl-1.19.4-0 --disableexcludes=kubernetes
🗒️
Versões dos pacotes de instalação do Kubernetes Em nosso cluster vamos instalar a versão 19.4 do Kubernetes. Para saber quais versões estão disponíveis para instalação através do YUM você pode usar o seguinte comando: yum -v list kubelet --disableexcludes=kubernetes --show-duplicates

Iniciando os serviços do Kubernetes

Vamos inicializar o Docker e o Kubernetes e também habilitá-los para iniciar automaticamente no boot do S.O.

sudo systemctl enable docker kubelet --now

Se tudo ocorreu bem até aqui, agora temos nossos hosts devidamente configurados e prontos para seguir com a instalação do nosso cluster Kubernetes.

A seguir vamos iniciar o nosso cluster configurando o host Master e o nodes Workers.

Iniciando um Cluster Kubernetes

Um cluster Kubernetes deve ter no mínimo um host Master e outro Worker, em nosso cluster Lab vamos iniciar com esses dois hosts.

Master

O host master é o principal componente do nosso cluster Kubernetes, então ele deve ser configurado primeiro.

⚠️
Antes de seguir com os próximos passos é necessário ter feito todas as etapas de configuração descritas anteriormente.

Configurando o hostname

Para mantermos um padrão daqui em diante vamos dar o nome aos nossos hosts seguindo o modelo k8s-ROLE, onde ROLE será a função do host em nosso cluster Kubernetes.

Para o host master vamos nomea-lo de k8s-master. Para isso, execute:

sudo hostnamectl set-hostname k8s-master

Configurando a resolução de nomes

Precisamos garantir que os hosts do nosso cluster possam resolver seus nomes na rede, e como não temos uma infraestrutura de DNS para a rede local, vamos configurar o arquivo /etc/hosts em todos os hosts para conseguirmos resolver os nomes em nosso cluster.

No arquivo hosts vamos adicionar o endereço IP e o nome que demos ao nosso host.

Execute (não se esqueça de alterar o endereço IP para o endereço da sua interface):

echo "192.168.130.138 k8s-master k8s-master.k8scluster" >> /etc/hosts
💡
Confirmando o endereço IP da interface Você pode obter o endereço IP da interface de várias formas. - nmcli - ip a

Inicializando o Cluster Kubernetes

Agora já podemos começar a instalação do nosso cluster Kubernetes, vamos inicializar o nosso host Master.

⚠️
Sem entrar em muitos detalhes neste momento, preciso esclarece esse passo. Quando inicializarmos o cluster Kubernetes ele irá configurar novas redes, e para que não ocorram conflitos com o endereçamento IP da rede local (no meu caso a rede da interface ens33), precisamos configurar corretamente alguns parâmetros de inicialização do cluster Kubernetes.

Já sabemos o endereço IP do nosso host Master, mas vamos confirmar novamente:

$ nmcli c show ens33 |grep -E "IP4.ADDRESS"
IP4.ADDRESS[1]:                         192.168.130.138/24

Aqui podemos confirmar que o endereço IP da minha interface ens33 é 192.168.130.138 e a minha rede local é 192.168.130.0/24 (máscara 255.255.255.0).

Agora vamos ajudar os parâmetros de inicialização do cluster Kubernetes. Usaremos dois parâmetros.

  • apiserver-advertise-address: Será endereço IP do host.
  • pod-network-cid: Deve ser uma rede que não conflite com bloco de da interface local do host. No meu caso tem que ser diferente de 192.168.130.0/24.
⚠️
Por padrão o parâmetro pod-network-cidr usa o endereço 192.168.0.0/16. Por isso devemos ter atenção em configurar esse parâmetro corretamente antes deinicializar nosso cluster.

Com os parâmetros ajustados. Execute:

kubeadm init --apiserver-advertise-address=192.168.130.138 --pod-network-cidr=172.20.0.0/16

Após a conclusão da instalação, copie o comando com o Token, iremos utilizar esse comando para ingressar os nossos nodes Worker posteriormente.

kubeadm join 192.168.130.138:6443 --token 6krn26.htncib0efp5qwcuv \
    --discovery-token-ca-cert-hash sha256:e9e8a0e93f6f3a4be78644bd2b6e49561281e0fcc737f239791474981abaf26c

Configurando a console

Para que possamos executar comandos em nosso cluster Kubernetes sem problemas, devemos configurar a nossa console. Execute:

export KUBECONFIG=/etc/kubernetes/admin.conf
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile

Agora tudo pronto! Vamos executar o nosso primeiro comando no cluster para ver seu status, execute:

kubectl cluster-info

Se tudo estiver OK o resultado deve ser algo assim:

$ kubectl cluster-info
Kubernetes master is running at https://192.168.130.138:6443
KubeDNS is running at https://192.168.130.138:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Adicionando nodes Worker ao cluster

Sem Workers nosso cluster ainda não está completo e para isso, primeiro vamos configurar nossos hosts e depois vamos adicioná-los ao cluster com a função Worker.

⚠️
Antes de seguir com os próximos passos é necessário ter feito as etapas de configuração do CentOS descritas anteriormente em Configurações iniciais do CentOS e Instalando o Kubernetes.

Configurando o hostname

Seguindo o padrão de nomenclatura que já definimos, vamos nomear nossos hosts Workers como k8s-worker1, k8s-worker2 etc...

Para nosso primeiro Worker, execute:

sudo hostnamectl set-hostname k8s-worker1

Configurando a resolução de nomes

Quando configuramos o host Master adicionamos ao arquivo /etc/hosts apenas o endereço IP e o nome do host Master. Agora estamos configurando a resolução de nomes em nosso Worker, então, precisamos adicionar além das informações do Worker as informações do Master também.

Vamos seguir basicamente os mesmos passos que fizemos no Master, vamos descobrir o endereço IP do Worker e configurar o arquivo /etc/hosts

Para confirmar o IP do host Worker, execute:

nmcli c show ens33 |grep -E "IP4.ADDRESS"

Meu Worker tem o IP 192.168.130.139:

$ nmcli c show ens33 |grep -E "IP4.ADDRESS"
IP4.ADDRESS[1]:                         192.168.130.139/24

Agora vamos adicionar as informações do Worker e também do Master no arquivo /etc/hosts do nosso host Worker.

Execute (não esqueça de alterar os endereços IPs):

cat << EOF >> /etc/hosts
192.168.130.138 k8s-master k8s-master.k8scluster
192.168.130.139 k8s-worker1 k8s-worker1.k8scluster
EOF

Para finalizar também temos que atualizar o arquivo /etc/hosts do host Master com as informações do nosso Worker.

No host Master, execute (altere o endereço IP para o endereço do seu host Worker):

echo "192.168.130.139 k8s-worker1 k8s-worker1.k8scluster" >> /etc/hosts
⚠️
Todos os hosts do cluster Kubernetes devem ter o arquivo /etc/hosts sempre atualizados com os endereços IPs e os hostnames de todos os hosts que pertencem ao cluster. Quando adicionar novos Workers ao cluster, não se esqueça de sempre atualizar o arquivo /etc/hosts em todos os outros hosts dos cluster, assim como acabamos de fazer.
⚠️
Você pode testar as configurações executando o ping. Depois de configurar o arquivo /etc/hosts use o ping com o nome do host para obter resposta. Ex: ping k8s-master

Incluindo o Worker no cluster

No host Worker vamos executar o kubeadm join com as informações de Token (o Token, assim como o comando completo foram informados após a instalação do Master).

Execute (execute o comando que pegamos após a instalação do Master):

kubeadm join 192.168.130.138:6443 --token 6krn26.htncib0efp5qwcuv \
    --discovery-token-ca-cert-hash sha256:e9e8a0e93f6f3a4be78644bd2b6e49561281e0fcc737f239791474981abaf26c

Ao concluir você deve receber a mensagem que o node foi incluído ao cluster, com no exemplo:

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Configurando a rede do Cluster Kubernetes

Agora temos um cluster Kubernetes completo, Master e Workers rodando e tudo pronto. Nem tudo...

Anteriormente executamos o comando o kubectl cluster-info para verificar o status do nosso cluster, agora vamos ver como estão os nodes do nosso cluster. No host Master, execute:

kubectl get node

Repare o status, nossos hosts ainda não estão Ready.

$ kubectl get node
NAME          STATUS     ROLES    AGE     VERSION
k8s-master    NotReady   master   44m     v1.19.4
k8s-worker1   NotReady      2m47s   v1.19.4

Vamos dar uma olhada nos Pods do nosso cluster. Execute:

kubectl get pod --all-namespaces

Repare que alguns Pods coredns-* estão com o status Pending. Isso não é bom.

kubectl get pod --all-namespaces
NAMESPACE     NAME                                 READY   STATUS    RESTARTS   AGE
kube-system   coredns-f9fd979d6-7p46w              0/1     Pending   0          4m37s
kube-system   coredns-f9fd979d6-sjlcz              0/1     Pending   0          4m37s
kube-system   etcd-k8s-master                      1/1     Running   0          4m37s
kube-system   kube-apiserver-k8s-master            1/1     Running   0          4m37s
kube-system   kube-controller-manager-k8s-master   1/1     Running   0          4m37s
kube-system   kube-proxy-dxvtn                     1/1     Running   0          4m37s
kube-system   kube-scheduler-k8s-master            1/1     Running   0          4m37s

O que está acontecendo é devido a ainda não termos um componente de rede configurado em nosso cluster.

Precisamos instalar um Cluster Network Interface ou CNI. Este componente é responsável por gerenciar a comunicação entre os nodes, os pods e containers do nosso cluster Kubernetes.

Existem diversas alternativas de CNI compatíveis com o Kubernetes que podem ser instalados, em nosso cluster vamos utilizar o Calico.

Instalando o Calico no Cluster Kubernetes

Instalar um CNI no Kubernetes geralmente é uma tarefa simples, em nosso cluster, para instalar o Calico, vamos apenas baixar os arquivos YAML.

No host Master, execute:

curl https://docs.projectcalico.org/manifests/tigera-operator.yaml -o tigera-operator.yaml &&
curl https://docs.projectcalico.org/manifests/custom-resources.yaml -o custom-resources.yaml

Vamos conferir o arquivo de instalação do Calico e confirmar se as configurações do Calico estão de acordo com as configurações do nosso cluster. Se não, vamos fazer os ajustes se necessários:

cat custom-resources.yaml

Repare o item de configuração cidr, o endereço no arquivo é 192.168.0.0/16.

$ cat custom-resources.yaml
# This section includes base Calico installation configuration.
# For more information, see: https://docs.projectcalico.org/v3.18/reference/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 192.168.0.0/16
      encapsulation: VXLANCrossSubnet
      natOutgoing: Enabled
      nodeSelector: all()

Na instalação do Master customizamos a configuração do nosso endereço de rede de Pods para 172.20.0.0/16 usando o parâmetro --pod-network-cidr. Devemos alterar a configuração do Calico para corresponder ao mesmo endereço que estamos usando em nosso cluster.

⚠️
O endereço em cidr deve ser exatamente o mesmo usado no parâmetro --pod-network-cidr usado na inicialização do cluster. O endereço 192.168.0.0/16 é o valor padrão do Kubernetes quando o parâmetro --pod-network-cidr não é customizado. Por este motivo o arquivo de configuração do Calico mantém este endereço como padrão.

Vamos alterar o cidr do nosso arquivo. Execute:

sed -i 's/192.168.0.0/172.20.0.0/' custom-resources.yaml

Com o endereço da rede de Pods configurado, podemos seguir com a instalação do Calico. Execute:

kubectl create -f tigera-operator.yaml &&
kubectl create -f custom-resources.yaml

Agora vamos observar se os Pods do coredns alteram o status de Pending para Running. Execute:

watch kubectl get pods -n kube-system

Pode levar alguns segundos (ou minutos) para que os Pods fiquem Running.

Every 2.0s: kubectl get pod -n kube-system                                                                          Sat Feb  6 17:35:11 2021

NAME                                 READY   STATUS    RESTARTS   AGE
coredns-f9fd979d6-79vq7              1/1     Running   0          6m57s
coredns-f9fd979d6-q9ms5              1/1     Running   0          6m57s
etcd-k8s-master                      1/1     Running   0          7m6s
kube-apiserver-k8s-master            1/1     Running   0          7m6s
kube-controller-manager-k8s-master   1/1     Running   0          7m6s
kube-proxy-lct2l                     1/1     Running   0          6m57s
kube-scheduler-k8s-master            1/1     Running   0          7m6s

Com todos os Pods Running, vamos checar novamente como estão nossos nodes. Execute:

kubectl get node

Desta vez o status deve ser Ready.

$ kubectl get node
NAME          STATUS   ROLES    AGE   VERSION
k8s-master    Ready    master   72m   v1.19.4
k8s-worker1   Ready       31m   v1.19.4

Agora sim! Tudo pronto com nosso cluster Kubernetes de lab.

Que tal já começarmos a fazer algumas coisinhas?

Primeiros passos no Kubernetes

Agora que já temos nosso cluster Kubernetes rodando perfeitamente, está na hora de começarmos a soltar as asinhas e explorar nosso Lab.

Para começar, vamos criar nosso primeiro Pod, um servidor Web com uma página Web.

No host Master, execute:

kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-hello
data:
  index.html: |
    <h1>Hello World! o/</h1>
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
  labels:
    env: demo
spec:
  containers:
  - name: nginx-demo
    image: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: nginx-hello
  volumes:
  - name: nginx-hello
    configMap:
      name: nginx-hello
EOF

Vamos ver como está o nosso primeiro Pod:

kubectl get pod

Parece que temos nossa primeira aplicação rodando em nosso cluster Kubernetes.

$ kubectl get pod
NAME         READY   STATUS    RESTARTS   AGE
nginx-demo   1/1     Running   0          66s

Mas agora você deve estar se perguntando. Legal, tá rodando, mas como acesso a página Web do meu Pod?

Para isso precisamos "expor" nosso Pod para que as conexões de fora do nosso cluster cheguem até ele.

Para expor o Pod, execute:

kubectl expose pod nginx-demo --port=80 --type=NodePort

Nosso Pod já está acessível, mas em qual endereço?

Vamos ver qual a URL para acessar nossa página. Execute:

echo http://$(kubectl get pod nginx-demo -o jsonpath='{.status.hostIP}'):$(kubectl get service nginx-demo -o jsonpath='{.spec.ports[].nodePort}')

Agora é só acessar a URL pelo navegador!

Conclusão

Chegamos ao fim do primeiro artigo da série que irá lhe ajudar a entender melhor o Kubernetes e como ele funciona.

Sem muito blá blá blá, hoje colocamos a mão na massa e instalamos o nosso Cluster Kubernetes de Lab. Se você ficou com dúvidas, não se preocupe, o objetivo do primeiro artigo era apenas preparar o terreno e não detalhar as configurações. Os detalhes veremos a partir daqui.

No próximo artigo vamos entrar na arquitetura do Kubernetes e ver alguns dos principais componentes do nosso cluster, vamos entender o que são os Pods e como podemos planejar melhor nossas aplicações usando Deployments. Também vamos aprender como escalar nossas aplicações e publicar "expor" as aplicações para acesso externo de forma muito mais eficiente. Por fim, também instalar uma interface Web para gerenciamento do Kubernetes, o Kubernetes Dashboard.


Espero que você tenha gostado deste primeiro artigo. Se achou legal ou não, deixe um comentário, a sua mensagem pode me ajudar a trazer um conteúdo cada vez melhor!

Comentários

Postagens mais visitadas deste blog

Gestão de Serviços de TI com Open Source - OTRS