# How to deploy a Kubernetes cluster
> HERMOSILLA Nicolas
> M2 Infra Cloud Sécurité
# Infrastructure provisionning
> ⚠ Ajouter la clé SSH sur scaleway avant de faire toute manipulation.
> Cela nous permettra d'ajouter la clé SSH aux instances et de s'y connecter
- Récupérer le terraform permettant de faire le déploiement
git clone git@github.com:Arcahub/kube-install-tuto.git
- Créer une API key pour l'utilisateur en sélectionnant le projet M1-M2 Conteneur et Orchestration. Ajouter ensuite la clé d'accès et la clé secrète à l'aide de la commande suivante:
```
scw init
```
- Modifier les fichiers variables.yaml kube-install-tuto/terraform/modules/k8s/modules:
```
type : DEV1-M
```
- Aller dans le dossier terraform et déployer le projet
```
cd kube-install-tuto/terraform
terraform init
terraform plan
terraform apply -var="project_name=nicolas-kube"
```
- Récupérer les IPs
```
terraform output
control_plane_ip = "10.74.62.113"
public_gateway_ip = "212.47.253.167"
worker_ips = [
"10.71.96.47",
"10.70.152.77",
]
```
# Kubernetes installation with kubeadm
```
ssh -J bastion@212.47.253.167:61000 root@10.74.62.113
ssh -J bastion@212.47.253.167:61000 root@10.71.96.47
ssh -J bastion@212.47.253.167:61000 root@10.70.152.77
```
- Préparer les nodes. Lancer les commandes sur les 3 instances
```
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# sysctl params required by setup, params persist across reboots
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# Apply sysctl params without reboot
sudo sysctl --system
```
- Installer containerd
```
# Install required packages for https repository
sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl
# Add Docker’s official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# Add Docker repository
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# Update package manager index
sudo apt-get update
```
```
sudo apt-get install -y containerd.io
```
```
sudo mkdir -p /etc/containerd
sudo containerd config default > /etc/containerd/config.toml
```
- Modifier le fichier /etc/containerd/config.toml en mettant la valeur du SystemdCgroup à `true`. Redémarrer ensuite le service containerd
```
vim /etc/containerd/config.toml
```

```
sudo systemctl restart containerd
```
- Tester le bon fonctionnement du service containerd
```
ctr images pull docker.io/library/hello-world:latest
sudo ctr run --rm docker.io/library/hello-world:latest hello-world
ctr images rm docker.io/library/hello-world:latest
```
- Installer les paquets kubeadm, kubelet et kubectl
```
# Install required packages for https repository
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
# Add Kubernetes’s official GPG key
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
# Add Kubernetes repository
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
# Update package manager index
sudo apt-get update
# Install kubeadm, kubelet and kubectl with the exact same version or else components could be incompatible
sudo apt-get install -y kubelet=1.25.0-00 kubeadm=1.25.0-00 kubectl=1.25.0-00
# Hold the version of the packages
sudo apt-mark hold kubelet kubeadm kubectl
```
# Setting up control plane node
- Se connecter au node control plane et initier le nœud avec kubeadm
```
ssh -J bastion@212.47.253.167:61000 root@10.74.62.113
kubeadm init
```

- Modifier le fichier kubeconfig pour pouvoir utiliser kubectl et utiliser le cluster
```
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
```
- Vérifier les nodes et les pods. Nous remarquons que le node control-plane n'est pas encore prêt
```
kubectl get nodes
NAME STATUS ROLES AGE VERSION
nicolas-kube-controlplane NotReady control-plane 4m54s v1.25.0
```
```
kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-565d847f94-8g78h 0/1 Pending 0 5m17s
kube-system coredns-565d847f94-dxdqc 0/1 Pending 0 5m17s
kube-system etcd-nicolas-kube-controlplane 1/1 Running 0 5m20s
kube-system kube-apiserver-nicolas-kube-controlplane 1/1 Running 0 5m20s
kube-system kube-controller-manager-nicolas-kube-controlplane 1/1 Running 0 5m20s
kube-system kube-proxy-857mc 1/1 Running 0 5m17s
kube-system kube-scheduler-nicolas-kube-controlplane 1/1 Running 0 5m22s
```
- Installer le plugin CNI (Container Network Interface) pour que les pods puissent se communiquer entre eux
```
kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml
```
- Attendre que le pod weave-net soit prêt
```
kubectl -n kube-system wait pod -l name=weave-net --for=condition=Ready --timeout=-1s
pod/weave-net-s6v44 condition met
kubectl get pods -l name=weave-net -n kube-system
NAME READY STATUS RESTARTS AGE
weave-net-s6v44 2/2 Running 1 (96s ago) 103s
```
- Vérifier que le node control plane est maintenant prêt
```
kubectl get nodes
NAME STATUS ROLES AGE VERSION
n-kube-controlplane Ready control-plane 10m v1.25.0
```
```
kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-565d847f94-8g78h 1/1 Running 0 12m
kube-system coredns-565d847f94-dxdqc 1/1 Running 0 12m
kube-system etcd-nicolas-kube-controlplane 1/1 Running 0 12m
kube-system kube-apiserver-nicolas-kube-controlplane 1/1 Running 0 12m
kube-system kube-controller-manager-nicolas-kube-controlplane 1/1 Running 0 12m
kube-system kube-proxy-857mc 1/1 Running 0 12m
kube-system kube-scheduler-nicolas-kube-controlplane 1/1 Running 0 12m
kube-system weave-net-s6v44 2/2 Running 1 (4m26s ago) 4m33s
```
# Setting up worker node
- Toujours sur le control plane, créer un token pour relier les 2 workers au cluster
```
kubeadm token create --print-join-command
kubeadm join 192.168.1.55:6443 --token iuqy4s.sqv2yfibfdfw746p --discovery-token-ca-cert-hash sha256:2908618fb5d9039b23230718031e0bc74dfcef35e7739137eb3980b2ef3eefa6
```
Se connecter aux 2 nodes puis lancer la commande générée pour ajouter les ajouter au cluster
```
ssh -J bastion@212.47.253.167:61000 root@10.71.96.47
ssh -J bastion@212.47.253.167:61000 root@10.70.152.77
kubeadm join 192.168.1.55:6443 --token iuqy4s.sqv2yfibfdfw746p --discovery-token-ca-cert-hash sha256:2908618fb5d9039b23230718031e0bc74dfcef35e7739137eb3980b2ef3eefa6
```

- Vérifier sur le control plane que les workers fonctionnent correctement et ajouter un label pour chaque worker
```
ssh -J bastion@212.47.253.167:61000 root@10.74.62.113
kubectl get nodes
NAME STATUS ROLES AGE VERSION
nicolas-kube-controlplane Ready control-plane 18m v1.25.0
nicolas-kube-node-1 Ready <none> 2m29s v1.25.0
nicolas-kube-node-2 Ready <none> 89s v1.25.0
```
```
kubectl label node nicolas-kube-node-1 node-role.kubernetes.io/worker=worker
node/nicolas-kube-node-1 labeled
kubectl label node nicolas-kube-node-2 node-role.kubernetes.io/worker=worker
node/nicolas-kube-node-2 labeled
```
# Understanding what we have done
## Les pods statiques
- Verifier sur le control plane que les fichiers dans /etc/kubernetes/manifests correspondent bien aux pods avec comme namespace `kube-system`
```
sudo ls /etc/kubernetes/manifests
kubectl get pods --namespace=kube-system
```
- Supprimer le pod kube-apiserver pour voir si le pod se recréer automatiquement. Kubelet vérifiera en permanence le dossier du static pod et recréera le pod s'il est détruit.
```
kubectl delete pod kube-apiserver-nicolas-kube-controlplane --namespace=kube-system
pod "kube-apiserver-nicolas-kube-controlplane" deleted
```
```
kubectl get pods --namespace=kube-system
```
- Simuler la suppression du fichier du pod kube-apiserver. On obtient une erreur car kubectl n'arrive pas à contacter le server API.
```
sudo mv /etc/kubernetes/manifests/kube-apiserver.yaml ~/kube-apiserver.yaml
kubectl get pods --namespace=kube-system
```
- Remettons le fichier dans le bon dossier. Le pod est de nouveau présent.
```
sudo mv ~/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml
kubectl get pods --namespace=kube-system
```
- Nous pouvons également ajouter un nouveau fichier pour créer un pod nginx. Le pod nginx sera directement en état running grâce à kubelet.
```
sudo tee /etc/kubernetes/manifests/nginx.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: nginx-test
namespace: default
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
EOF
```
```
kubectl get pods --namespace=default
```
- Supprimer le fichier nginx pour supprimer le pod et vérifier que le pod soit bien supprimé.
```
sudo rm /etc/kubernetes/manifests/nginx.yaml
kubectl get pods --namespace=default
No resources found in default namespace.
```
## Les certificats
- Vérifier l'expiration des certificats avec `kubeadm`
```
kubeadm certs check-expiration
```
- Il est possible de renouveler les certificats. Il faudra ensuite redémarrer le kubelet.
```
kubeadm certs renew all
sudo systemctl daemon-reload
sudo systemctl restart kubelet
```
# Creating our first deployment
- Déployer une première application nginx avec 3 replicas.
```
kubectl create deployment nginx --image=nginx --replicas=3
kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 9s
```
# Playing with the scheduler
## Manual scheduling
- Déplacer le fichier kube-scheduler.yaml afin de s'assurer qu'aucune modification ne soit effectuée par ce dernier. Vérifier que le pod ne tourne plus.
```
sudo mv /etc/kubernetes/manifests/kube-scheduler.yaml /tmp
kubectl get pods -n kube-system
```
- Créer un pod nginx en créant un fichier nginx.yaml dans manifest pour pouvoir y apporter des modifications.
```
kubectl run nginx --image=nginx --dry-run=client -o yaml > ~/nginx.yaml
```
- Créer le pod nginx
```
kubectl apply -f ~/nginx.yaml
pod/nginx created
```
- Vérifier que le pod s'est bien créé. Le pod est en état pending.
```
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 2m29s
```
- Déployer un nouveau pod nginx.
```
vim nginx.yaml
```

```
kubectl apply -f ~/nginx.yaml
pod/nginx2 created
```
- Vérifier que le pod est bien dans la liste. Le pod nginx2 est bien en état running.
```
kubectl get pods
```

- Redéplacer le fichier scheduler et vérifier que le pod nginx fonctionne correctement
```
sudo mv /tmp/kube-scheduler.yaml /etc/kubernetes/manifests
kubectl get pods
```

- Supprimer les 2 pods nginx et nginx2
```
kubectl delete pod nginx nginx2
```
## Node selector
- Utiliser un label pour scheduler un pod. Ajouter un label à un node existant
```
kubectl label node nicolas-kube-node-1 node-type=worker
node/nicolas-kube-node-1 labeled
```
- Créer un pod avec le node selector
```
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
node-type: worker
EOF
pod/nginx created
```
- Vérifier que le pod fonctionne correctement
```
kubectl get pods -o wide
```
- Supprimer le pod
```
kubectl delete pod nginx
pod "nginx" deleted
```
## Taints and tolerations
- Le node control plane a par défaut un taint.
```
kubectl describe node nicolas-kube-controlplane | grep Taints
Taints: node-role.kubernetes.io/control-plane:NoSchedule
```
- Créer un pod qui ne sera lié à aucun worder mais fonctionnera sur le node control plane
```
cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: node-role.kubernetes.io/master
operator: Equal
value: ""
- key: node-role.kubernetes.io/control-plane
operator: Equal
value: ""
nodeSelector:
node-role.kubernetes.io/control-plane: ""
EOF
pod/nginx created
```
- Vérifier que le pod fonctionne correctement
```
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 36s
```
- Supprimer le pod
```
kubectl delete pod nginx
pod "nginx" deleted
```
- Recréer le pod sur le control plane et vérifier son comportement. Le pod se créer bien mais reste en état pending car il n'y a pas de tolérance.
```
cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
node-role.kubernetes.io/control-plane: ""
EOF
pod/nginx created
```
```
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 48s
```
- Supprimer le pod
```
kubectl delete pod nginx
pod "nginx" deleted
```
# Upgrading cluster version
## Upgrade control plane
- Upgrade kubeadm
```
sudo apt-mark unhold kubeadm && \
sudo apt update && apt install -y kubeadm=1.26.0-00 && \
sudo apt-mark hold kubeadm
```
- Verifier que le upgrade est disponible
```
kubeadm upgrade plan
```
- Upgrader le cluster avec la bonne version
```
kubeadm upgrade apply v1.26.0
```

- Drainer le node control plane et upgrader kubelet
```
kubectl drain nicolas-kube-controlplane --ignore-daemonsets
node/nicolas-kube-controlplane cordoned
Warning: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-65tmx, kube-system/weave-net-s6v44
sudo apt-mark unhold kubelet && \
sudo apt update && apt install -y kubelet=1.26.0-00 && \
sudo apt-mark hold kubelet
```
- Redémarrer le service kubelet
```
sudo systemctl daemon-reload
sudo systemctl restart kubelet
```
- Déconnecter le node control plane
```
kubectl uncordon nicolas-kube-controlplane
node/nicolas-kube-controlplane uncordoned
```
## Upgrade worker nodes
- Se connecter aux workers
```
ssh -J bastion@212.47.253.167:61000 root@10.71.96.47
ssh -J bastion@212.47.253.167:61000 root@10.70.152.77
```
- Upgrade kubeadm sur chaque worker
```
sudo apt-mark unhold kubeadm && \
sudo apt update && apt install -y kubeadm=1.26.0-00 && \
sudo apt-mark hold kubeadm
```
- Upgrader le node sur chaque worker
```
kubeadm upgrade node
```

- Drainer les workers sur le control plane puis upgrade kubelet sur chaque worker
```
kubectl drain nicolas-kube-node-1 --ignore-daemonsets
kubectl drain nicolas-kube-node-2 --ignore-daemonsets
```
```
sudo apt-mark unhold kubelet && \
sudo apt update && apt install -y kubelet=1.26.0-00 && \
sudo apt-mark hold kubelet
```
- Redémarrer kubelet sur chaque worker
```
sudo systemctl daemon-reload
sudo systemctl restart kubelet
```
- Déconnecter les workers depuis le control plane
```
kubectl uncordon nicolas-kube-node-1
node/nicolas-kube-node-1 uncordoned
kubectl uncordon nicolas-kube-node-2
node/nicolas-kube-node-2 uncordoned
```
- Vérifier que le cluster fonctionne et que les workers ont la bonne version
```
kubectl get nodes
```
```
kubectl get nodes
NAME STATUS ROLES AGE VERSION
nicolas-kube-controlplane Ready control-plane 156m v1.26.0
nicolas-kube-node-1 Ready worker 137m v1.26.0
nicolas-kube-node-2 Ready worker 136m v1.26.0
```