Propuesta Kubernetes Hard Way
La idea del proyecto es conocer Kubernetes bajo el capó y poner en práctica todo el conocimiento adquirido a lo largo del camino. Esto nos capacitará para resolver problemas relacionados con la instalación y en el futuro realizar un tuning.
Vamos a intentar hacer todo de la forma más difícil y manual, aislando cada componente cuando sea posible. Vamos a intentar crear un clúster de manera hard.
Para crear este entorno vamos a necesitar 6 máquinas.
3 masters 2 workers 1 load balancer (Lo ideal es al menos 2, pero aquí vamos a dar un descuento)
Entorno
Requisitos
Ten estas dos herramientas instaladas en tu sistema.
No hay paso a paso para la instalación de estas herramientas en este proyecto. Si es para ser hard es para correr detrás.
- Vagrant - Versión 2.4.1 o superior - Vamos a simular un entorno localmente utilizando Vagrant para levantar nuestras máquinas.
- VirtualBox Será utilizado por Vagrant para virtualizar las máquinas.
Define 6 IPs en tu red local que puedan ser utilizadas. Las máquinas no estarán en NAT sino en bridge.
Verifica cuál es la interfaz de red que tu máquina local está utilizando. Filtra por la IP de tu máquina y busca esta información. En mi caso es la 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
Crea esta carpeta de la siguiente forma.
tree -L 2
.
├── environment # Contendrá archivos relacionados con Vagrant
└── shared_files # Contendrá todos los archivos y scripts que vamos a crear
Crea un archivo en la carpeta environment llamado Vagrantfile con el siguiente contenido. Ya ajusta para las IPs que elegiste. Observa los comentarios.
# -*- mode: ruby -*-
# vi:set ft=ruby sw=2 ts=2 sts=2:
Vagrant.require_version ">= 2.4.1"
NETWORK_INTERFACE = "enx00e04c6810f3" # Cambia a tu interfaz de red
NETWORK_PREFIX = "10.0.0." # Cambia al prefijo de tu red
# Cambia las IPs abajo de acuerdo con las IPs que separaste en tu red.
# -*- 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" # Generando todos los archivos necesarios
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 y crea ClusterRoles para los nodos.
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
La propuesta es que todos nuestros archivos necesarios (certificados, kubeconfigs, yamls) estén concentrados en una única carpeta para ser copiados en los lugares correctos en lugar de usar scp para todo.
Podemos ver en el código arriba tenemos bloques que ejecutan loops para cada conjunto de máquinas.
Cuando la máquina esté provisionada cada línea con machine.vm.provision ejecutará algún script en secuencia. No todos los scripts necesitan ejecutarse en todas las máquinas. Vamos a colocar los scripts dentro de la carpeta shared_files que será montada dentro de cada una de las VMs creadas.
Todas las máquinas necesitan ejecutar un update, definir los hosts y ajustar el DNS. Vamos a crear un script para eso.
Todas las máquinas necesitan conocer todas las máquinas entonces vamos a actualizar el hosts. En un entorno productivo tendríamos un nameserver para resolver este problema, pero como no tenemos, vamos a definir todo dentro del hosts.
Esté dentro de la carpeta shared_files para ejecutar el comando abajo.
# Ajusta las IPs de acuerdo con las elegidas
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##### Actualizando el Sistema Operativo #####"
sudo apt-get update
sudo apt-get upgrade -y
echo -e "\n##### Definiendo /etc/hosts #####"
sudo cp /vagrant/shared_files/hosts /etc/hosts
echo -e "\n##### Cambiando el DNS resolv para DNS de Google #####"
sed -i -e 's/#DNS=/DNS=8.8.8.8/' /etc/systemd/resolved.conf
service systemd-resolved restart
echo -e "\n##### Instalando Chrony #####"
sudo apt-get install chrony -y
EOF
Vamos a ajustar el acceso SSH en todas las máquinas. Podríamos usar la propia clave generada por Vagrant, pero vamos a abstenernos de eso. Vamos a posibilitar que todas las máquinas consigan hacer un acceso SSH a las otras aunque no sea necesario. Puede facilitar tu estudio.
Vamos a crear un par de claves SSH y el script que colocará este par de claves disponible para todos los usuarios en todas las máquinas.
ssh-keygen -t rsa -b 4096 -f id_rsa -N ""
Como existen 3 usuarios vagrant, ubuntu y root vamos a hacer para todos ellos. Dejé bien explícito aquí.
cat <<EOF >> ssh.sh
#!/bin/bash
echo -e "\n##### Agregar el par de claves SSH en el usuario 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##### Agregar el par de claves SSH en el usuario 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##### Agregar el par de claves SSH en el usuario 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