Pular para o conteúdo principal

Proposta Kubernetes Hard Way

A ideia do projeto é conhecer o Kubernetes por baixo do capô e colocar em prática todo conhecimento adquirido ao longo do caminho. Isso nos capacitará a resolver problemas relacionados à instalação e no futuro partir para um tuning.

Vamos tentar fazer tudo da forma mais difícil e manual, isolando cada componente quando possível. Vamos tentar criar um cluster de maneira hard.

Para criar esse ambiente vamos precisar de 6 Máquinas.

3 masters 2 workers 1 load balancer (O ideal é pelo menos 2, mas aqui vamos dar um desconto)

Ambiente

Requisitos

Tenha essas duas ferramentas instaladas no seu sistema.

Não tem passo a passo para a instalação dessas ferramentas nesse projeto. Se é para ser hard é para correr atrás.

  • Vagrant - Versão 2.4.1 ou superior - Vamos simular um ambiente localmente utilizando o Vagrant para subir nossas máquinas.
  • VirtualBox Será utilizado pelo Vagrant para virtualizar as máquinas.

Defina 6 IPs na sua rede local que podem ser utilizados. As máquinas não estarão em NAT mas em bridge.

Veja qual a interface de rede sua máquina local está utilizando. Filtre pelo IP da sua máquina e procure essa informação. No meu caso é a enx00e04c6810f3.

ip addr show | grep 10.0.0.105
inet 10.0.0.105/24 brd 10.0.0.255 scope global dynamic noprefixroute enx00e04c6810f3

Crie essa pasta da seguinte forma.

tree -L 2
.
├── environment # Conterá arquivos relacionados ao Vagrant
└── shared_files # Conterá todos os arquivos e scripts que vamos criar

Crie um arquivo na pasta environment chamado Vagrantfile com o seguinte conteúdo. Já ajuste para os ips que você escolheu. Observe os comentários.

# -*- mode: ruby -*-
# vi:set ft=ruby sw=2 ts=2 sts=2:
Vagrant.require_version ">= 2.4.1"

NETWORK_INTERFACE = "enx00e04c6810f3" # Altere para a sua interface de rede
NETWORK_PREFIX = "10.0.0." # Altere para o prefixo da sua rede

# Altere os IPs abaixo de acordo com os IPs que você separou na sua rede.
# -*- mode: ruby -*-
# vi:set ft=ruby sw=2 ts=2 sts=2:

Vagrant.require_version ">= 2.4.1"

haproxy_machines = {
"loadbalancer" => {"memory" => "1024", "cpu" => "1", "disk" => "40", "ip" => "200", "image" => "ubuntu/jammy64"},
}

masters_machines = {
"master1" => {"memory" => "2048", "cpu" => "1", "disk" => "40", "ip" => "201", "image" => "ubuntu/jammy64"},
"master2" => {"memory" => "2048", "cpu" => "1", "disk" => "40", "ip" => "202", "image" => "ubuntu/jammy64"},
"master3" => {"memory" => "2048", "cpu" => "1", "disk" => "40", "ip" => "203", "image" => "ubuntu/jammy64"},
}

workers_machines = {
"worker1" => {"memory" => "2048", "cpu" => "1", "disk" => "40", "ip" => "207", "image" => "ubuntu/jammy64"},
"worker2" => {"memory" => "2048", "cpu" => "1", "disk" => "40", "ip" => "208", "image" => "ubuntu/jammy64"},
}

NETWORK_INTERFACE = "enx00e04c6810f3"
NETWORK_PREFIX = "10.0.0."

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

haproxy_machines.each do |name, conf|
config.vm.define "#{name}" do |machine|
machine.vm.synced_folder "../shared_files", "/vagrant/shared_files"
machine.vm.box = "#{conf["image"]}"
machine.vm.hostname = "#{name}"
machine.vm.network "public_network", bridge: "#{NETWORK_INTERFACE}", ip: "#{NETWORK_PREFIX}#{conf["ip"]}"
machine.vagrant.plugins = ["vagrant-disksize"]
machine.disksize.size = "#{conf["disk"]}GB"
machine.vm.provider "virtualbox" do |vb|
vb.name = "#{name}"
vb.memory = conf["memory"]
vb.cpus = conf["cpu"]
vb.customize ["modifyvm", :id, "--groups", "/kubernetes", "--nicpromisc2", "allow-all"]
end
machine.vm.provision "shell", path: "../shared_files/update_vm.sh"
machine.vm.provision "shell", path: "../shared_files/ssh.sh"
machine.vm.provision "shell", path: "../shared_files/bootstrap_loadbalancer.sh"
end
end

masters_machines.each do |name, conf|
config.vm.define "#{name}" do |machine|
machine.vm.synced_folder "../shared_files", "/vagrant/shared_files"
machine.vm.box = "#{conf["image"]}"
machine.vm.hostname = "#{name}"
machine.vm.network "public_network", bridge: "#{NETWORK_INTERFACE}", ip: "#{NETWORK_PREFIX}#{conf["ip"]}"
machine.vagrant.plugins = ["vagrant-disksize"]
machine.disksize.size = "#{conf["disk"]}GB"
machine.vm.provider "virtualbox" do |vb|
vb.name = "#{name}"
vb.memory = conf["memory"]
vb.cpus = conf["cpu"]
vb.customize ["modifyvm", :id, "--groups", "/kubernetes", "--nicpromisc2", "allow-all"]
end
machine.vm.provision "shell", path: "../shared_files/update_vm.sh"
machine.vm.provision "shell", path: "../shared_files/ssh.sh"
machine.vm.provision "shell", path: "../shared_files/bootstrap_nodes.sh"

if name == "master1" # Gerando todos os arquivos necessários
machine.vm.provision "shell", inline: "cd /vagrant/shared_files/ && ./1_generate_certificate_control_plane.sh"
machine.vm.provision "shell", inline: "cd /vagrant/shared_files/ && ./2_generate_certificate_workers.sh"
machine.vm.provision "shell", inline: "cd /vagrant/shared_files/ && ./3_generate_kubeconfigs_control_plane.sh"
machine.vm.provision "shell", inline: "cd /vagrant/shared_files/ && ./4_generate_kubeconfigs_workers.sh"
machine.vm.provision "shell", inline: "cd /vagrant/shared_files/ && ./5_create_encription_method.sh"
end
machine.vm.provision "shell", path: "../shared_files/boostratp_etcd.sh"
machine.vm.provision "shell", path: "../shared_files/boostrap_control_plane.sh"
if name == "master3" # Instala CNI e cria ClusterRoles para os nodes.
machine.vm.provision "shell", path: "../shared_files/configure_cluster.sh"
end
end
end

workers_machines.each do |name, conf|
config.vm.define "#{name}" do |machine|
machine.vm.synced_folder "../shared_files", "/vagrant/shared_files"
machine.vm.box = "#{conf["image"]}"
machine.vm.hostname = "#{name}"
machine.vm.network "public_network", bridge: "#{NETWORK_INTERFACE}", ip: "#{NETWORK_PREFIX}#{conf["ip"]}"
machine.vagrant.plugins = ["vagrant-disksize"]
machine.disksize.size = "#{conf["disk"]}GB"
machine.vm.provider "virtualbox" do |vb|
vb.name = "#{name}"
vb.memory = conf["memory"]
vb.cpus = conf["cpu"]
vb.customize ["modifyvm", :id, "--groups", "/kubernetes", "--nicpromisc2", "allow-all"]
end
machine.vm.provision "shell", path: "../shared_files/update_vm.sh"
machine.vm.provision "shell", path: "../shared_files/ssh.sh"
machine.vm.provision "shell", path: "../shared_files/bootstrap_nodes.sh"
machine.vm.provision "shell", path: "../shared_files/boostrap_workers.sh"
end
end
end

A proposta é que todos os nossos arquivos necessários (certificados, kubeconfigs, yamls) estejam concentrados em uma única pasta para ser copiado nos lugares corretos ao invés de usar scp para tudo.

ambiente

Podemos ver no código acima temos blocos que executam loops para cada conjunto de máquinas. Quando a máquina tiver provisionada cada linha com machine.vm.provision irá rodar algum script na sequência. Nem todos os scripts precisam rodar em todas as máquinas. Vamos colocar os scripts dentro da pasta shared_files que será montada dentro de cada um das vms criadas.

Todas as máquinas precisam executar um update, definir os hosts e acertar o DNS. Vamos criar um script para isso.

Todas as máquinas precisam conhecer todas as máquinas então vamos atualizar o hosts. Em um ambiente produtivo teríamos um nameserver para resolver esse problema, mas como não temos, vamos definir tudo dentro do hosts.

Esteja dentro da pasta shared_files para executar o comando abaixo.

# Ajuste os IPs de acordo com os escolhidos
cat <<EOF >> hosts
10.0.0.200 loadbalancer
10.0.0.201 master1
10.0.0.202 master2
10.0.0.203 master3
10.0.0.207 worker1
10.0.0.208 worker2
EOF

cat <<EOF >> update_vm.sh
#!/bin/bash
echo -e "\n##### Atualizando o Sistema Operacional #####"
sudo apt-get update
sudo apt-get upgrade -y

echo -e "\n##### Definindo /etc/hosts #####"
sudo cp /vagrant/shared_files/hosts /etc/hosts

echo -e "\n##### Mudando o DNS resolv para DNS do Google #####"
sed -i -e 's/#DNS=/DNS=8.8.8.8/' /etc/systemd/resolved.conf
service systemd-resolved restart

echo -e "\n##### Instalando o Chrony #####"
sudo apt-get install chrony -y
EOF

Vamos acertar o acesso SSH em todas as máquinas. Poderíamos usar a própria chave gerada pelo Vagrant, mas vamos nos abster disso. Vamos possibilitar que todas as máquinas consigam fazer um acesso SSH às outras mesmo não sendo necessário. Pode facilitar o seu estudo.

Vamos criar um par de chaves SSH e o script que colocará este par de chaves disponível para todos os usuários em todas as máquinas.

ssh-keygen -t rsa -b 4096 -f id_rsa -N ""

Como existem 3 usuários vagrant, ubuntu e root vamos fazer para todos eles. Deixei bem explícito aqui.

cat <<EOF >> ssh.sh
#!/bin/bash

echo -e "\n##### Adicionar o par de chaves SSH no usuário vagrant #####"
tee -a /home/vagrant/.ssh/authorized_keys < /vagrant/shared_files/id_rsa.pub
cp /vagrant/shared_files/id_rsa /home/vagrant/.ssh/id_rsa
chmod 400 /home/vagrant/.ssh/id_rsa

echo -e "\n##### Adicionar o par de chaves SSH no usuário ubuntu #####"
sudo tee -a /home/ubuntu/.ssh/authorized_keys < /vagrant/shared_files/id_rsa.pub
sudo cp /vagrant/shared_files/id_rsa /home/ubuntu/.ssh/id_rsa
sudo chown ubuntu:ubuntu /home/ubuntu/.ssh/id_rsa
sudo chmod 400 /home/ubuntu/.ssh/id_rsa

echo -e "\n##### Adicionar o par de chaves SSH no usuário root #####"
sudo tee -a /root/.ssh/authorized_keys < /vagrant/shared_files/id_rsa.pub
sudo cp /vagrant/shared_files/id_rsa /root/.ssh/id_rsa
sudo chown root:root /root/.ssh/id_rsa
sudo chmod 400 /root/.ssh/id_rsa
EOF