Kubernetes – Configurando um Cluster Multi-Master

Kubernetes – Configurando um Cluster Multi-Master

Neste post vamos configurar um cluster Kuberentes Multi-Master apenas com a sua máquina. Mas antes de falarmos sobre um cluster em Kubernetes trabalhando em modo Multi-Master…

Uma palavra sobre containers…

Hoje em dia fica difícil imaginar o desenvolvimento sem containers, mais precisamente sem Docker. Daqui a algum tempo desenvolver sem essa tecnologia será como hoje é desenvolver sem um versionador como o git do nosso lado, no mínimo duvidoso.

Existe alguns casos em que isso não é necessariamente verdadeiro, mas na maioria dos casos podemos assumir que sim.

Quando a arquitetura de nossas aplicações começam a crescer e as dependências entre serviços, alta disponibilidade e balanceamento passam a ser uma necessidade, precisamos de uma ferramenta capaz de gerenciar todos esses serviços, precisamos de um orquestrador. E não há hoje no mercado um orquestrador de containers tão conhecido e estável como o Kubernetes – mas se você nunca mexeu com orquestradores de containers eu aconselho fortemente a utilizar o Docker Swarm para começar, a Rankdone possuí uma infraestrutura sólida e bastante confiável baseada em microserviços dentro do Swarm.

Mas vamos voltar ao assunto deste post, o Kubernetes.

Kubernetes

Kubernetes – ou K8S – é um orquestrador de containers que facilita todo o trabalho de balanceamento de carga, descoberta de serviço, gerenciamento de estado e etc. Resumindo, o Kubernetes é uma das ferramentas que devemos procurar quando falamos de microserviços, multi-cloud ou mesmo vendor lock-in – porque não?

Se você nunca mexeu com o Kubernetes, você pode começar utilizando o minikube ou o k3s, eles são suficientes para fornecer uma base sólida para que você entenda seu funcionamento.

Mas este post é para aqueles que já conhecem e desejam criar uma infraestrutura em Kubernetes tolerante à falhas, a esta estrutura damos o nome de multi-master, pois existirão três máquinas responsáveis pela orquestração, cada uma com uma cópia do banco de dados, caso uma das máquinas caia, o funcionamento do cluster continua o mesmo.

Preciso de cloud?!

Esqueça a cloud, esqueça AWS, Azure, Googlevocê não precisa disso! é possível subir um cluster de Kubernetes na sua máquina ou mesmo na sua empresa. O Kubernetes não precisa funcionar exclusivamente dentro de uma nuvem.

Essa é uma vantagem do software livre e open source, com exclusão da sua própria máquina, você não precisa de mais nada a não ser vontade de aprender, mas lembre-se de contribuir com a comunidade, tudo bem?

Para nosso laboratório precisaremos de 3 coisas – uma delas é opcional:

  1. 4GB de memória RAM livres para as 4 máquinas
  2. Virtualbox
  3. Vagrant

Virtualbox

Precisamos de um hypervisor para criar as máquinas virtuais em nosso computador. O VirtualBox é mais do que o suficiente e funciona sem problemas nos mais variados sistemas operacionais.

Vagrant

Se você acompanha a 4Linux, já sabe que utilizamos o Vagrant  para criar a infraestrutura que utilizamos para fazer os nossos testes e os nossos estudos.

Você não precisa necessariamente do Vagrant, pode-se baixar as imagens do Debian no próprio site https://www.debian.org/ e instalar as máquinas uma a uma, o resultado será similar, mas para acelerar o processo para os mais apressados vamos utilizar o Vagrant para criar a nossa infraestrutura dentro do Virtualbox.

Três Máquinas?

Um cluster multi-master pode ser provisionado de duas formas, mas o ponto mais importante é que precisarão existir três instâncias do etcd – o banco de dados “chave/valor” que guarda todo o estado do Kubernetes.

Escrevi duas formas porque podemos provisionar o etcd como um container dentro de cada master – a forma mais simples e justamente a que iremos utilizar, ou provisionar o etcd separado dos masters, e neste caso, poderemos utilizar apenas dois masters, pois as instâncias do etcd estarão em outro lugar.

Arquitetura Multi-Master

Existem várias formas de instalar o Kubernetes:

Precisaremos de um balanceador – HAProxy – para centralizar o acesso aos masters:

Requisições -> Load Balancer -> Masters

É importante dizer que logo no início da configuração, diremos aos masters que o endereço “geral” do cluster é o próprio endereço do load balancer. Isso significa que as requisições tanto de clientes, como do cluster ou mesmo dos próprios componentes de inicialização deverão passar pelo HA-Proxy.

Outro ponto importante é que começaremos a instalação pelo Master1. Somente o Master1 iniciará o cluster, os demais masters utilizarão as configurações existentes.

Vamos começar!

Instalação

Os próximos passos abordam a instalação do cluster.

1 – Criando o ambiente

Crie uma pasta em qualquer lugar do seu computador, dentro desta pasta colocaremos o arquivo do Vagrant responsável por configurar a infraestrutura necessária.

Como aqui na 4Linux sempre utilizamos Linux, os exemplos de comandos básicos de terminal serão baseados no Linux, tudo bem?

A pasta se chamará k8s-ha:

cd ~/
mkdir k8s-ha

Dentro desta pasta vamos criar um arquivo chamado Vagrantfile com a definição das nossas 4 máquinas:

cd k8s-ha
cat > Vagrantfile <<'EOF'
# -*- mode: ruby -*-
# vi: set ft=ruby :

vms = {
  'balancer' => {'memory' => '256', 'cpus' => 1, 'ip' => '200'},
  'master3' => {'memory' => '2048', 'cpus' => 2, 'ip' => '30'},
  'master2' => {'memory' => '2048', 'cpus' => 2, 'ip' => '20'},
  'master1' => {'memory' => '2048', 'cpus' => 2, 'ip' => '10'}
}

Vagrant.configure('2') do |config|

  config.vm.box = 'debian/buster64'
  config.vm.box_check_update = false
  
  vms.each do |name, conf|
    config.vm.define "#{name}" do |k|
      k.vm.hostname = "#{name}.example.com"
      k.vm.network 'private_network', ip: "172.27.11.#{conf['ip']}"
      k.vm.provider 'virtualbox' do |vb|
        vb.memory = conf['memory']
        vb.cpus = conf['cpus']
      end
    end
  end
end
EOF

Pronto! Vamos levantar nossas máquinas virtuais:

vagrant up

É muito importante ressaltar que todas as máquinas do cluster deverão possuir hostnames diferentes.

2 – Acessando o Ambiente

Uma vez que as máquinas estejam funcionando, poderemos acessá-las executando um simples comando do Vagrant dentro do diretório k8s-ha:

vagrant ssh master1

Os nomes das máquinas estão descritos no arquivo Vagrantfile mas também é possível verificá-los com o comando:

vagrant status

3 – Docker, binários do Kubernetes e ajustes

Os passos a seguir precisarão ser executados em todos os masters.

Como o Debian 10 modificou o comportamento do iptables que agora utiliza por padrão o nftables através de uma camada de compatibilidade. O kube-proxy – responsável pelo roteamento – não suporta esse tipo de configuração e acaba duplicando as rotas causando problemas.

Dessa forma, antes de tudo precisaremos modificar o iptables para se comportar do modo “legacy”:

update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
update-alternatives --set arptables /usr/sbin/arptables-legacy
update-alternatives --set ebtables /usr/sbin/ebtables-legacy

Como o foco da instalação é a configuração do ambiente, executaremos um pequeno script que faz a instalação toda de uma vez.

Este script instalará o docker juntamente com os componentes do kubernetes kubelet, kubeadm e kubectl.

sudo su
bash <<EOF
# Lembre-se de executar nos outros masters!
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common vim
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb https://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
EOF

Além da instalação dos componentes, precisaremos desabilitar o swap – existe uma issue a respeito do swap já faz um bom tempo, mas ainda não foi corrigido. Vou lhe ensinar um comando para desabilitar o swap de uma só vez e que persista durante as reinicializações:

sed -Ei 's/(.*swap.*)/#\1/g' /etc/fstab
swapoff -a

O Docker instalado vem por padrão configurado com o cgroupdriver como cgroupfs e o Kubernetes recomanda o systemd, vamos alterá-lo:

cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "journald"
}
EOF
systemctl restart docker

Feito isso, precisaremos fazer mais um pequeno ajuste no serviço do kubelet – responsável por levantar os containers base do cluster – pois estamos utilizando o Vagrant e existe mais de uma interface de rede em nossa máquina. Vamos configurar o kubelet para utilizar um endereço IP específico:

echo "KUBELET_EXTRA_ARGS='--node-ip=172.27.11.10'" > /etc/default/kubelet

Obs: Você precisará executar estes comandos em todos os masters, e lembre-se de trocar o final do IP para 20 e 30 ao executar este último comando.

Pronto! Vamos para o load balancer!

4 – Configurando o Load Balancer

A configuração do load balancer felizmente é muito simples graças a própria simplicidade do HAProxy. Apenas por curiosidade, o idealizador e desenvolvedor do HAProxy foi também um dos desenvolvedores do kernel Linux por muito tempo.

Vamos acessar o balancer, lembre-se de sair das outras máquinas virtuais e voltar para o diretório onde está o Vagrantfile:

cd ~/k8s-ha
vagrant ssh balancer

Instale os pacotes do HAProxy, e você notará que ele é incrivelmente pequeno:

sudo su
apt-get update
apt-get install -y haproxy vim

Pronto! Vamos configurá-lo – para simplificar faremos em um único comando:

cat > /etc/haproxy/haproxy.cfg <<EOF
global
user haproxy
group haproxy

defaults
mode http
log global
retries 2
timeout connect 3000ms
timeout server 5000ms
timeout client 5000ms

frontend kubernetes
bind 172.27.11.200:6443
option tcplog
mode tcp
default_backend kubernetes-master-nodes

backend kubernetes-master-nodes
mode tcp
balance roundrobin
option tcp-check
server k8s-master-0 172.27.11.10:6443 check fall 3 rise 2
server k8s-master-1 172.27.11.20:6443 check fall 3 rise 2
server k8s-master-2 172.27.11.30:6443 check fall 3 rise 2
EOF

O arquivo parece um pouco assustador, mas é simples de entender:

  • O HAProxy precisa de um frontend responsável por receber as conexões e um backend responsável por receber essas conexões.
  • A interface de rede, o ip e a porta em que o HAProxy estará anexado é definido pelo bind.
  • O modo de conexão deverá ser tcp e não http como fazemos com balanceadores para webservers.
  • balance define o modo de balanceamento, neste caso round-robin – um diferente a cada conexão, existem outros como por exemplo leastconn, o menos carregado.
  • tpc-check define que haverá verificação do status dos serviços dos servidores definidos
  • server define cada máquina para onde o HAProxy redirecionará a conexão, precisarão de 3 falhas para sair da lista e 2 conexões bem sucedidades para voltar para a lista.

Vamos reiniciar o HAProxy e verificar se a porta está exposta – o que significa que o serviço está funcionando:

systemctl restart haproxy # Reinicia o serviço
systemctl status haproxy # Verifica status do serviço
ss -nltp | grep 6443 # Procura pelas portas TCP abertas

Pronto! Agora vamos focar na instalação do cluster!

5 – Configurando o primeiro Master

Vamos voltar para os masters. O primeiro master é o responsável por criar a configuração inicial juntamente com os certificados. É sua responsabilidade popular o primeiro etcd que será a base para os demais.

Para iniciar o cluster através do kubeadm é preciso criar um arquivo que indicará onde está o load balancer bem como qual o endereço de rede utilizaremos tanto para a máquina como para os containers.

Vamos criar este arquivo em /root/kubeadm-config.yml:

cat > /root/kubeadm-config.yml <<EOF
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: stable
controlPlaneEndpoint: "172.27.11.200:6443"
networking:
  podSubnet: "10.244.0.0/16"
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: "172.27.11.10"
  bindPort: 6443
EOF

Com o arquivo pronto, vamos finalmente iniciar o primeiro master do cluster:

kubeadm init --config '/root/kubeadm-config.yml' --upload-certs

Ao final da configuração a saída no console será semelhante a seguinte:

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of the control-plane node running the following command on each as root:

kubeadm join 172.27.11.200:6443 --token 2vjyq3.aaepezlc9szigtcp \
--discovery-token-ca-cert-hash sha256:fabea16da44390b45c1749e6fb4949ced6c82a1abd97cebe46db9bb175fa8566 \
--control-plane --certificate-key f936242f98d7d56a3b48963555eea669020716f81788414edf84044addaa7814

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use 
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 172.27.11.200:6443 --token 2vjyq3.aaepezlc9szigtcp \
--discovery-token-ca-cert-hash sha256:fabea16da44390b45c1749e6fb4949ced6c82a1abd97cebe46db9bb175fa8566

A primeira parte lhe explica como copiar o arquivo utilizado para a comunicação do kubectl com o cluster.

As linha seguintes explicam como adicionar outros masters – ou control planes – e outros nodes, respectivamente. Anote a linha de adição dos masters e dos nodes, vamos utilizar a dos masters nas outras máquinas.

Vamos aproveitar e copiar o arquivo de conexão para o usuário root:

mkdir -p /root/.kube
cp /etc/kubernetes/admin.conf $HOME/.kube/config

Teste se a conexão com o cluster funciona:

kubectl get nodes

Deverá aparecer algo semelhante:

NAME     STATUS    ROLES   AGE    VERSION
master1  NotReady  master  4m51s  v1.16.1

Não tenha receio, as máquinas ficarão Not Ready enquanto não configurarmos a rede, que é o nosso próximo passo!

6 – Configurando a rede no primeiro Master

Para que a comunicação entre os pods funcione, você precisa instalar um dos plugins de rede disponíveis, existem diversos. O Kubernetes não define um plugin de rede padrão, você precisa escolher, e neste caso vamos instalar o plugin do Calico. Caso você não entenda essa passo, sugiro que comece com um cluster simples, faça alguns testes e então volte para cá, você sempre será bem vindo.

Ainda na máquina master1 execute os seguintes comandos para baixar as definições do Calico, modificar a rede para 10.244.0.0/16 utilizada e adicioná-lo ao cluster. O endereço 10.244.0.0/16 foi o que definimos em nosso arquivo de inicialização do cluster:

cd /root
wget https://docs.projectcalico.org/v3.9/manifests/calico.yaml
sed -i 's?192.168.0.0/16?10.244.0.0/16?g' /root/calico.yaml
kubectl apply -f /root/calico.yaml

7 – Configurando os outros Masters

Os passos que deverão ser executados antes de colarmos o comando fornecido pelo primeiro master são os seguintes:

  1. Instalar o Docker
  2. Instalar os binários do Kubernetes
  3. Desativar o swap
  4. Modificar o docker para utilizar o driver de cgroups para systemd
  5. Alterar o arquivo /etc/default/kubelet para utilizar o endereço ip correto

O comando que deveremos executar é exatamente o mesmo que o primeiro master nos mostrou com apenas uma modificação:

Deveremos especificar aqui também em qual interface este master estará escutando com a opção –apiserver-advertise-address=ip.da.maquina, neste caso vou exemplificar os comandos a partir da master2:

kubeadm join 172.27.11.200:6443 --token m2bj3h.fjf2tjccajgldqi2 \
--discovery-token-ca-cert-hash sha256:534305ca1b14a916c59f3d413f75158a6479a607ab97e9daf1212603c1683491 \
--control-plane --certificate-key c15d7c091fbbef93b3665b30d16e587fb37c1481f301bc11dc05cb27442c8181 --apiserver-advertise-address=172.27.11.20

Basta esperar um pouquinho… e pronto! Temos agora dois masters!

Execute a mesma sequência no master3 e lembre-se de modificar os endereços!

8 – Testando o Load Balancer e o cluster Multi-Master

Nosso cluster está pronto, vamos verificar! Vá para a máquina master1 pois lá configuramos o usuário root para acessar o cluster:

vagrant ssh master1
sudo su -
kubectl get nodes

A saída deverá mostrar os três masters prontos:

NAME      STATUS   ROLES    AGE     VERSION
master1   Ready    master   21m     v1.16.1
master2   Ready    master   16m     v1.16.1
master3   Ready    master   4m58s   v1.16.1

Seria pedir muito adicionar um outro node, muita gente não terá memória RAM suficiente, neste caso, vamos configurar os masters para aceitarem pods de aplicações:

kubectl taint nodes --all node-role.kubernetes.io/master-

E adicionar um pequeno DaemonSet para criar um pod em cada uma das máquinas:

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: sh-cgi
  labels:
    name: sh-cgi
spec:
  selector:
    matchLabels:
      name: sh-cgi
  template:
    metadata:
      labels:
        name: sh-cgi
    spec:
      containers:
      - name: sh-cgi
        image: hectorvido/sh-cgi
EOF

E veja que existe um pod em cada máquina:

kubectl get pods -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP              NODE   
sh-cgi-46xn6   1/1     Running   0          80s   10.244.137.65   master1
sh-cgi-btmq2   1/1     Running   0          80s   10.244.180.4    master2
sh-cgi-fmtjl   1/1     Running   0          80s   10.244.136.1    master3

Pronto! Seu cluster multi-master está funcionando! Para adicionar nodes comuns nessa infraestrutura, basta seguir os passos de instalação e modificação do docker e do /etc/kubelet/default. Porém, ao ingressar a máquina no cluster, executar o outro comando.

Caso você não tenha anotado, poderá pegar outro através do seguinte comando:

kubeadm token create --print-join-command

Obrigado!

E caso tenha mais dúvidas, venha fazer o nosso curso!

Entre em contato agora mesmo com nossos consultores!

 

CURSOSCONSULTORIA    CONTATO

 

Anterior Dicas para Reduzir o Tamanho das Imagens do Docker e Melhorar seu Desempenho
Próxima 4Linux patrocina Rails Girls: evento de TI para mulheres iniciantes

About author

Hector Silva
Hector Silva 11 posts

Hector Vido Silva atua como Consultor de Infraestrutura em Software Livre, cursando Engenharia da Computação, formado em Análise e Desenvolvimento de Sistemas com MBA em Cibersegurança. Atua desde 2006 com desenvolvimento, implantação, arquitetura e resolução de problemas em ambientes envolvendo diferentes plataformas. Na 4Linux também ministra os cursos de desenvolvimento, banco de dados e infraestrutura para ambientes ágeis e automatizados com forte utilização de contêineres. Sim, é um generalista. Possuí certificações CKA, Openshift, LPIC-2, LPI DevOps, GCP Engineer, MongoDB, Blockchain e ZCE.

View all posts by this author →

Você pode gostar também

Monitoramento TI

Entenda a importância da observabilidade em suas aplicações

Saber o comportamento de nossas aplicações tornou-se primordial, mas o comportamento não se resume apenas a saber se a aplicação está funcionando ou se a aplicação está com todas as

DevOps

Descubra como explorar vulnerabilidades em diferentes plataformas com nosso guia passo a passo

Na continuação dos artigos da série, vamos recriar os passos utilizados para se descobrir uma vulnerabilidade real (CVE-2010-0083) encontrada pelo autor, bem como definir a forma de explorar tal vulnerabilidade.

DevOps

Desvendando os Anti-Padrões DevOps: Como otimizar a entrega de software

Introdução Um dos maiores problemas que (acredito eu!) deve afeta grande parte das equipe de desenvolvedores de software, não é “como transformar uma ideia em um bom sistema”, mas sim