Klaster Kubernetesa na Virtualbox

Maszyny wirtualne pod to idealne środowisko dla domowego laboratorium i edukacji. Posiadając w naszym komputerze odpowiednie zasoby sprzętowe możemy postawić niemalże dowolny system operacyjny a na nim eksperymentować i uczyć się nowych rzeczy. A teraz przyszła pora na naukę i ćwiczenia z Kubernesem. Wspólnie przejdziemy przez proces, gdzie zainstalujemy prosty klaster Kubernetesa składający sie z jednego mastera i paru workerów. Ten setup pozwala przećwiczyć użycie obiektów kubernetesa, które działają na wielu nodach.  

Całość w 7 krokach. Zaczynamy!

1. Plan działania

Potrzebujemy planu działania – rozpisany w szczegółach a najlepiej rozrysowany.
A Plan mamy taki, żeby przynajmniej utworzyć 3 maszyny wirtualne gdzie 1 będzie masterem i dwie pozostałe to workery. Oczywiście w tej konfiguracji,w razie potrzeby możemy utworzyć więcej workerów.

Adresacja maszyn wirtualnych będzie wyglądać następująco”

10.0.8.5 master

10.0.8.10 worker1

System operacyjny: Ubuntu Server

Ustawienie sieci Virtualbox: NatNetwork

2. Przygotowanie i instalacja maszyny mastera

Na początek utworzymy taką sieć w Virtualboxie. W tym celu przechodzimy do menu File/Preferences/Network i dodajemy mową sieć, wchodzimy w jej ustawienia i nadajemy odpowiednią nazwę,  na przykład NatNetwork-k8s, dalej CIDR 10.0.8.0/24, DHCP mozemy włączyc, choć zaleca się aby node’y miały ustawione stały adres IP a nie dynamiczny pobierany z serwera DHCP, to też i tak wszystkie adresy IP na maszynach ustawimy na sztywno. 

Dalej, klikamy w Port Forwarding i ustawimy regułę dla połączeń SSH tak aby łatwo komunikować się z maszyny hosta z maszyna wirtualną. Oznacza to, że każde zapytanie na lokalny port 2205 będzie przekierowane na port 22 maszyny o adresie 10.0.8.5, czyli naszego mastera.

Zacznijmy od systemu operacyjnego, który będzie bazą dla naszych wirtualnych maszyn. Ja bardzo polubiłem się z Ubuntu dlatego i tu skorzystam z tego systemu operacyjnego w wersji Server 22.04.1 LTS. Pobieramy obraz ISO do oficjalnej strony i zapisujemy go gdzieś na naszym lokalnym dysku. Przyda się nam to zaraz.

Możemy wrócić do Virtualboxa i utworzyć nową wirtualną maszyną dla pierwszego noda Kubernetesa. Będzie to nasz master node.
Ta maszyna będzie miała przydzielone więcej zasobów ponieważ, według oficjalnej strony wymagania minimalne dla mastera to 16GB RAM i 4 rdzenie procesora. Natomiast dla maszyny workera są mniejsze – wystarczy 8GB RAM i 2 rdzenie procesora. W przypadku kiedy to jedna z kilku maszyn wirtualnych równocześnie na naszym hoście to zaczyna robić się gorąco jeśli chodzi o ram i cpu – przydaje się wtedy naprawdę mocna jednostka z kilkudziesięcioma gigabajtów pamięci operacyjnej i z kilkudziesięcioma rdzeniami procesora.
Jednak, dla naszych laboratoryjnych potrzeb i master i worker może zdziałać z mniejszą ilością pamięci i cpu, może będzie wtedy trochę wolniej ale nadal wystarczająco do naszych celów szkoleniowych.  

Wracając do Virtulaboxa utwórz nowa wirtualną maszynę, ustaw nazwę (Master) , ustaw pamięć 4096MB i przynajmniej 2 CPU a na koniec dysk około 60 GB, może być dynamiczny.

Choć domyślne ustawione opcje dotyczące wirtualizacji są poprawne to warto zwrócić na nie uwagę w ustawieniach   System/Procesor i Acceleration czy mamy ustawione Hardware virtualization: V Enable Nested Pagging i opcje Interface paravirtualization na Default lub KVM.

W ustawieniach sieciowych adapter ustawiamy na NAT Network i wybieramy wcześniej utworzona sieć NatNetwork-k8s. 


Przejdźmy już do instalacji systemu operacyjnego. Uruchamiamy maszynę wirtualna i wskazujemy jako nośnik wcześniej pobrany dysk ISO z systemem Ubuntu Server. 

Tu przechodzimy przez standardowy proces instalacji Try or install Ubuntu Server.
Rodzaj instalacji x Ubuntu Server.

Podczas instalacji możemy ustawić manualnie interfejs sieciowy. Wchodzimy w konfiguracje interfejsu enp0s3 i ustawiamy parametry sieci dla naszego klastra. Niech to bedzie podsieć (Subnet) 10.0.8.0/24, adres IP (Address) 10.0.8.5, brama (Gateway) 10.0.8.1, serwery nazw (names saver) możemy podać dowolne publiczne adres DNS, jak na przykład naszego lokalnego operatora lub nawet serwery googla 8.8.8.8

W kolejnym kroku ustawiamy nazwę dla naszego serwera na master i nazwę naszego użytkownika. Ja ustawiam takiego samego użytkownika jakiego mam na maszynie hosta i na każdym kolejny systemie wirtualnym – wygodniej tak działać.

Koniecznie zaznaczyć pole Install OpenSSH server – będzie to nam potrzebne do połączenia się terminalem do wirtualnego systemu.

Na koniec wybieramy Reboot Now i czasem ponownie trzeba potwierdzić klawiszem Enter.


Po zakończonej instalacji i uruchomieniu systemu, logujemy się na użytkownika którego podaliśmy w jednym z poprzednich kroków i sprawdzamy czy mamy poprawnie ustawiony adres IP. 

Tradycyjnie możemy też przy okazji zaktualizować system 

sudo apt update
sudo apt upgrade

Łączymy się terminalem z komputera hosta do maszyny wirtualnej poprzez protokół SSH dzięki przekierowaniu portu, które na początku ustawiliśmy . 

ssh -p 2205 nazwaUżytkownika@localhost

3. Konfiguracja Mastera

Dodajemy adresy w pliku hosts

sudo nano /etc/hosts

tak aby dodatkowo zawierał pozycje

10.0.8.5 master
10.0.8.10 worker1
10.0.8.20 worker2

Dodaj kolejne jeśli planujesz więcej workerów.

Maszyny kubernetesa muszą mieć wyłaczoną pamieć SWAP więc wykonaj polecenie

sudo swapoff -a

i dalej zakomentowujemy wpis z swapem poprzez dodanie # na początku linii.

sudo nano /etc/fstab 

Możemy sprawdzić czy mamy swap jest wyzerowany poleceniem 

free -h 

Włączamy moduły jądra Linuxa

Do pliku

sudo nano /etc/modules-load.d/containerd.conf

dodajemy następujące wpisy

overlay
br_netfilter

i uruchamiamy

sudo modprobe overlay
sudo modprobe br_netfilter
Natomiast pliku
sudo nano /etc/sysctl.d/kubernetes.conf

dopisujemy 

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

i uruchamiamy polecenie

sudo sysctl --system

Teraz zainstalujemy środowisko uruchomieniowe kontenerów. Zaczniemy od instalacji niezbędnych dodatków.

sudo apt install software-properties-common apt-transport-https ca-certificates gnupg2 curl 

Dodajemy klucz gpg i repozytorium Dockera

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Aktualizujemy repozytoria i instalujemy środowisko uruchomieniowe kontenerów.

sudo apt update
sudo apt install containerd.io

Konfigurujemy ContainerD dla cgroup.

containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml

Restartujemy usługę, aby zastosować nowe ustawienia. Dodatkowo ustawiamy tak aby startowała razem z systemem.

sudo systemctl stop containerd
sudo systemctl start containerd
sudo systemctl enable containerd

Teraz już pora na instalacje składników samego Kubernetesa. Na początek dodajemy klucz gpg i repozytoria

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"

Tradycyjnie po dodaniu nowego repozytorium aktualizujemy i możemy instalować składniki. 

sudo apt update
sudo apt install kubeadm kubectl kubelet

Dodatkowo zabezpieczamy je przed przypadkową aktualizacją czy usunięciem 

sudo apt-mark hold kubeadm kubectl kubelet

Inicjalizujemy klaster Kubernetesa na masterze poleceniem:

sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=10.0.8.5

Gdzie pierwszy adres oznacza podsieć dla wewnętrznej sieci podów kubernetesa. Natomiast drugi adres oznacza adres IP serwera na maszynie wirtualnej po której będą komunikować się node’y.

Po kilku chwilach i pozytywnym zakończeniu procesu.

Your Kubernetes control-plane has initialized successfully!

A jako wynik dostaniemy dalsze instrukcje do wykonania. 

mkdir -p $HOME/.kube

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

można jeszcze wykonać te polecenie 

sudo chown $(id -u):$(id -g) /etc/kubernetes/admin.conf

W dalszej części komunikatu mamy wypisane jakie polecenie trzeba użyć, aby dołączyć kolejne maszyny jako workery do naszego klastra (twoje polecenie będzie różniło się tokenem i sha). W moim przykładzie wygląda ono tak:

kubeadm join 10.0.8.5:6443 --token d4g8v2.8uh9h6d3b45mfl6x --discovery-token-ca-cert-hash sha256:710c6b71a67754fc7d3bf4ad21339d902f43723d2537cd0537df163fefa1f4a6

Warto zapisać sobie gdzieś te polecenie, ponieważ później się nam przyda.


Sprawdźmy czy nasz kubernetes na masterze już działa wykonujac proste polecenie: 

kubectl get nodes

i jako wynik powinniśmy dostać listę z jednym rekordem bo na razie mamy tylko jeden node. Na przykład taki wynik 

NAME  STATUS ROLES       AGE   VERSION

master NotReady   control-plane   1m   v1.26.1


Teraz ażeby ładnie nam wszystko zadziałało sieciowo potrzebujemy jeszcze plugin sieciowy na maszynie mastera. Na przykład Callico 

Pobieramy go 

wget https://docs.projectcalico.org/manifests/calico.yaml

i stosujemy w naszym kubernetesie

kubectl apply -f calico.yaml

Nawiasem mówiąc, często brak pluginu sieciowego może powodować to, że nasze node’y są w statusie NotReady zamiast Ready gdy wykonujemy polecenie kubectl get nodes

Jesli potrzebujez jedno-nodowego Kubernetesa to juz wszystko. Możesz już zacząć działać i tworzyć dla testów pierwsze obiekty w Kubernetsie. Jednak jeśli chcesz mieć wielonodowe środowisko to czytaj dalej. 

4. Instalacja i konfiguracja Workera.

Wracamy do virtualboxa i standardowo zaczynamy, tworzymy nową maszynę wirtualną. Nazywamy ją Worker1 i wybieramy system operacyjny jako Ubuntu 64-bit. Dalej przydzielamy pamięć, wystarczy już 2 GB. Tworzymy nowy dynamiczny dysk o rozmiarze powiedzmy  40 GB. Gdy zniknie okienko kreatora maszyny to przechodzimy jeszcze do jej ustawień (System/Acceleration) i upewniamy się, że zaznaczona jest opcja Hardware Virtuzlizacion: Enable Nestes Pagging a także wybieramy sieć Network/Adapter1 na NatNetwork-k8s. Nie potrzebujemy tu drugiego interfejsu sieciowego ani przekierowania portów ponieważ będziemy mogli się połączyć się przez SSH z maszyny mastera po wewnętrznym adresie do workera. Resztę ustawień możemy pozostawić domyślne.

Generalnie proces wygląda niemalże identycznie jak w przypadku mastera

Uruchamiamy maszynę, wskazujemy jej dysk ISO z Ubuntu Server i przechodzimy przez standardowy proces instalacji. Parametry interfejsu sieciowego enp0s3 dla IPv4 ustawiamy manualnie: podsieć 10.0.8.0/24, adres IP podajmy 10.0.8.10, brama 10.0.8.1, serwery nazw możemy podać dowolne publiczne adres DNS, jak na przykład naszego lokalnego operatora lub nawet serwery googla 8.8.8.8. Tworzymy też użytkownika dla wygody takiego samego i z tym samym hasłem jak utworzyliśmy na masterze.  Teraz chwila na proces instalacji.

Gdy nowa maszyna uruchomi się po instalacji, jeszcze w okienku maszyny wirtualboxa, sprawdźmy dla pewności że sieć działa poprawnie wykonując polecenie ping 10.0.8.5 i aktualizujemy repozytoria sudo apt update i sudo apt upgrade. 

Połączmy się teraz do worker1 poprzez ssh. W tym celu wracamy sesji terminala z masterem lub ustawiony nową, dla przypomnienia

ssh -p 2205 nazwaUżytkownika@localhost

i dalej polecenie

ssh nazwaUżytkownika@10.0.8.10

lub 

ssh nazwaUżytkownika@worker1

ten drugi sposób też zadziała jeśli poprawnie dodaliśmy wpisy do pliku /etc/hosts na masterze

Teraz kilka czynności konfiguracyjnych, przygotowujących workera do dołączenia do klastra Kubernetesa. Właściwie w większości identycznie jak w przypadku przygotowania mastera.

Dodajemy adresem w pliku hosts

sudo nano /etc/hosts

tak aby dodatkowo zawierał pozycje

10.0.8.5 master
10.0.8.10 worker1
10.0.8.20 worker2

Maszyny workera muszą mieć wyłączoną pamięć SWAP. 

sudo swapoff -a
sudo nano /etc/fstab 

i zakomentujemy wpis z napisem swap poprzez dodanie #

Możemy sprawdzić czy mamy swap jest wyzerowany poleceniem 

free -h 

Włączamy moduły kernela Linuxa

Dodając poniższe wpisy w pliku  sudo nano /etc/modules-load.d/containerd.conf

overlay
br_netfilter

i uruchamiamy polecenia

sudo modprobe overlay
sudo modprobe br_netfilter

Natomiast w pliku sudo nano /etc/sysctl.d/kubernetes.conf

dopisujemy 

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

i uruchamiamy polecenie

sudo sysctl --system

Teraz zainstalujemy środowisko uruchomieniowe kontenerów. Zaczniemy od instalacji niezbędnych dodatków.

sudo apt install software-properties-common apt-transport-https ca-certificates gnupg2 curl 

Dodajemy repozytorium Dockera

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Aktualizujemy repozytoria i instalujemy środowisko uruchomieniowe kontenerów.

sudo apt update
sudo apt install containerd.io

Konfigurujemy ContainerD  dla cgroup

containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml

Restartujemy usługę aby zastosować nowe ustawienia. Dodatkowo włączamy aby startowała razem z systemem.

sudo systemctl stop containerd
sudo systemctl start containerd
sudo systemctl enable containerd

Teraz pora już na instalacje składników samego Kubernetesa. Na początek dodajemy repozytoria

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"

Tradycyjnie po dodaniu nowego repozytorium aktualizujemy i możemy instalować składniki. Dodatkowo zabezpieczamy je przed przypadkową aktualizacją czy usunięciem 

sudo apt update
sudo apt install kubeadm kubectl kubelet
sudo apt-mark hold kubeadm kubectl kubelet

Już prawie wszystko. Jeśli chcesz w łatwy sposób utworzyć więcej workerów to na razie pomiń następny krok i przejdź do punktu 5 do klonowania maszyny.
Natomiast jeśli jeden worker Ci wystarczy to już tu korzystamy z polecenia, które dostaliśmy na masterze jako wyniki inicjalizacji klastra. Po dodaniu jednego workera przejdź już do punktu 7.

Następny krok. Mamy już przygotowaną konfigurację systemu i możemy dołączyć ją do klastra. 

W moim przypadku wygląda ono tak:

kubeadm join 10.0.8.5:6443 --token hak75j.vuxuaom0ozaj6h64 --discovery-token-ca-cert-hash sha256:afbf747e8a411f0efc2473543054bde40157c42b55fae40c78c6a6d91043fd0a

Gdybyś jednak zapomniał zachować wcześniejsze polecenie kubeadm join to możesz je odzyskać tym poleceniem wykonanym na masterze

kubeadm token create --print-join-command

Pamiętaj potem tylko jeszcze o dodaniu sudo na początku.

5. Klonowanie maszyny – Worker2, Worker3…

Wyłączamy maszynę wirtualną workera. Postaw maszynę wyłączoną na  każdy proces klonowania. 

Klikamy prawym przyciskiem myszy na naszego workera1 na liście w oknie Virtualboxa i wybieramy opcje Colne (CTRL+o). Tu przechodzimy przez prosty kreator gdzie podajemy nazwę worker2 i w MAC Address Policy wybieramy opcję Generate New MAC addresses for all network adapters. Pozostałe opcje pozostawiamy nie zaznaczone. Dalej wybieramy Linked clone i klikamy klonuj.  

Teraz uruchamiamy tylko nową maszynę worker2 i ustawimy jej właściwy hostname 

sudo nano /etc/hostname 

wpisujemy worker2

ewentualnie, to samo tylko jednym poleceniem 

sudo hostnamectl set-hostname worker2

Aktualizujemy adres IP w pliku 

sudo nano /etc/netplan/00-installer-config 

na 

10.0.8.20

Na koniec restartujemy maszynę reboot i po ponownym uruchomieniu sprawdzamy czy poprawnie weszły nasze zmiany – sprawdzamy nazwę  poleceniem 

hostname 

i adres IP poleceniem

ip a

Dla pewności możemy jeszcze wpisać ping do internetu

ping www.google.pl 

i mastera 

ping 10.8.0.5

Wyłączamy maszynę worker2

Jesli potrzebujesz wiecej workerów możesz analogicznie je teraz utworzyć ale sądzę że trzy to jest max czego potrzeba do domowego laba. Pamiętaj zawsze o ustawieniu poprawnego adresu IP dla maszyny i nazwy hostname.

Warto wspomnieć, że w przypadku opcji Linked clone nasza maszyna worker2 i worker3, itd. korzysta ze wspólnych danych (nie z plików) z maszyną worker1 (bo z niej klonujemy) – każda zmiana zapisuje się do oddzielnego pliku różnicowego dysku. Dlatego całkowite usunięcie dysku worker1 spowoduje, że przestanie działać worker2 i worker3.  Choć w tym przypadku na pewno  nie będziemy rozdzielać tych maszyn, ponieważ będziemy tu potrzebować ich razem. Jednak miej na uwadze ta zależność jak będziesz dokonywać innych własnych modyfikacji po klonowaniu maszyn wirtualnych w Virtualboksie. Opcja full Clone sprawa, że nie ma tej zależności między maszynami ale zajmuje to w więcej miejsca na dysku i czasu na proces klonowania. 

6. Podłączenie workerów do klastra

Teraz gdy mamy już gotowe wszystko to włączamy maszynę worker1

Logujemy się na mastera. dla mojego użytkonika bedzie wygladać to tak

 ssh -p 2205 dast@localhost

a dalej w tej samej sesji mastera logujemy się do workera1

ssh dast@woker1 

tam wykonujemy polecenie 

kubeadm join 10.0.8.5:6443 --token d4g8v2.8uh9h6d3b45mfl6x --discovery-token-ca-cert-hash sha256:710c6b71a67754fc7d3bf4ad21339d902f43723d2537cd0537df163fefa1f4a6

    

Jeśli wszystko przebiegło poprawnie to wychodzimy z sesji terminalowej worker1 poleceniem

exit która wraca nas do mastera i teraz włączamy worker2 i logujemy się 

ssh dast@woker2

Identycznie postępujemy dla maszyny worker3 i kolejnych jeśli chcesz.

i ponownie wykonujemy pełne polecenie kubedm join tym razem na worker2

Po tym procesie powinniśmy mieć włączone i połączone w klaster wszystkie maszyny (master, worker1, worker2, …).
Wiatrak w komputerze pewnie juz głośno pracują 🙂

7. Przetestowanie działania klastra

Teraz pora na test naszego workera. Wykonaj polecenie: 

kubectl get nodes

jeśli jako odpowiedź dostaniesz listę nodów to mamy sukces.



Natomiast jeśli dostaniesz komunikat błędu typu connect: connection refused co się często dzieje gdy mamy klaster lokalnie. Oznacza to, że mamy problem z konfiguracja kontextu narzędzia kubectl. Wpisz polecenie na workerze:

kubectl config view

tu masz wybrakowane informacje. Wpisz to samo polecenie na mastrze i zobaczysz jak wygląda pełna, poprawna konfiguracja. 

Teraz aby naprawić konfiguracje wystarczy ze przekopiujesz z mastera zawartość pliku. Najwygodniej za pomocą polecenia 

scp /home/dast/.kube/config dast@worker1:/home/dast/.kube/config

Teraz weryfikujemy  poprawność poleceniem z workera

kubectl get nodes 

albo 

kubectl cluster-info

Utwórzmy więc  jakiś obiekt Kubernetesa. 

Logujemy się po SSH do mastera.

ssh -p 2205 twojLogin@localhost

i tworzymy obiekt deployment poleceniem

kubectl create deployment webserver --image=nginx --port 80 --replicas=5

Poprawność sprawdzamy poleceniem

kubectl get deploy

A teraz utworzymy obiekt Kubernetesa DeamonSet,  który rozłoży pody na wszystkie dostępne workery. Do testów wykorzystamy lekki obraz alpine który odpali polecenie sleep w nieskończoności. 

Ma masterze odpalamy nasz ulubiony edytor i tworzymy plik yaml dla DaemonSeta

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: sleep
spec:
  selector:
    matchLabels:
      name: sleep
  template:
    metadata:
      labels:
        name: sleep
    spec:
      containers:
         - image: alpine:latest
           name: sleep
           command: [ "sleep", "inf" ]

Zapisujemy plik pod nazwa dowolną, na przykład sleep.yaml i uruchamiamy poleceniem 

kubectl apply -f sleep.yaml

i sprawdzamy jego status poleceniem

kubectl get ds

Sprawdźmy status podów poleceniem

kubectl get pod

tu powinniśmy zobaczyć tyle podów ile mamy nodów. W tym przykładzie nasz master też służy do uruchamiania podów.

To tyle. Prawda, że proste 😉 Takie środowisko daje już nam szerokie możliwości testowa i nauki Kubernetesa dla obiektów dziadzinych na wielu nodach, które równoważą ruch i obciążenie. Udanej zabawy z k8s.

Jeśli interesujesz się tematem Virtualboxa to zapraszam Cię do kursu Virtualbox w praktyce – tam poznasz szczegółowo wszystkie najważniejsze zagadnienia programu.
Natomiast jeśli chcesz zautomatyzować swoje maszyny wirtualne to kurs Vagrant w praktyce jest dla Ciebie.