Kubernetes prend en charge plusieurs clusters virtuels présents sur le même cluster physique. Ces clusters virtuels sont appelés namespaces.
Les objets Kubernetes comme eles pods et les containers sont dans un namespace. Un namespace est un moyen de séparer et organiser les objets dans un cluster.
Afin d'afficher tous les namespaces existants, on effectue la commande suivante :
$ kubectl get namespace
NAME STATUS AGE
default Active 42h
kube-node-lease Active 42h
kube-public Active 42h
kube-system Active 42h
De base on a toujours un namespace default
. Kubeadm lui, créer les namespaces kube-*
Lors de l'utilisation de kubectl on peut spécifier un namespace via le paramètre --namespace.
Si aucun namespace n'est spécifié, le namespace par default
sera sélectionné.
$ kubectl get pods --namespace my-namespace
# Soit :
$ kubectl get pods --namespace kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-78d6f96c7b-tqk4m 1/1 Running 1 42h
calico-node-5hzhz 1/1 Running 1 42h
calico-node-7k2jb 1/1 Running 1 42h
calico-node-wdmxr 1/1 Running 1 42h
coredns-558bd4d5db-2t4d9 1/1 Running 1 42h
coredns-558bd4d5db-2x58z 1/1 Running 1 42h
etcd-k8s-control 1/1 Running 1 43h
kube-apiserver-k8s-control 1/1 Running 1 43h
kube-controller-manager-k8s-control 1/1 Running 1 43h
kube-proxy-98vh2 1/1 Running 1 42h
kube-proxy-dr9jb 1/1 Running 1 42h
kube-proxy-jsmr9 1/1 Running 1 42h
kube-scheduler-k8s-control 1/1 Running 1 43h
On peut spécifier tous les namespaces :
$ kubectl get pods --all-namespace
On peut évidemment créer un namespace :
$ kubectl create namespace my-namespace
namespace/my-namespace created
K8S permet de rendre les applications hautement disponible. Mais il est aussi conçu pour être lui même hautement disponible. Pour cela il faut ajouter des "control plane". Il faut un nombre impair de noeud afin de faire un cluster hautement disponible.
On aura donc une architecture du style :
Il existe 2 manière d'inclure etcd (permet le stockage des clés/valeurs pour tout le cluster) dans un cluster :
Il existe différents outils afin de gérer kubernetes, il est intéressant de les connaitre :
Outil | Utilité |
---|---|
kubectl | Outil officiel de kubernetes permettant d'exécuter des commandes Kubernetes. |
kubeadm | Outil permettant la mise en place facile et rapide d'un cluster Kubernetes |
minikube | Permet de créer un cluster Kubernetes sur une seule machine. Pratique pour développer. |
helm | Fournit un système de templating et de gestion des paquets (package manager) pour les objets k8s. Grâce à helm on peut créer nos propres templates (charte) ou les télécharger (wordpress chart par exemple). |
kompose | Permet de transformer un fichier docker compose en objets kubernetes. |
Kustomize | Gestion de la configuration des objets kubernetes (comme helm). Permet de partager et ré-utiliser des configurations Kubernetes via un template |
Qu'est-ce que "drainer un noeud" ?
Lorsque l'on fait fait une maintenance sur un noeud, on peut parfois avoir besoin de retirer ce noeud du cluster (redémarrage nécessaire, mise à jour de packages etc...). Il faut donc "drainer" ce noeud afin de retirer tous les pods présents. Cette méthode permet de retirer les pods correctement et les reschedule sur un autre noeud.
Afin de drainer un noeud, on va utiliser la commande suivante :
$ kubectl drain <nom noeud>
Si l'on a des DaemonSets sur le noeud (Un DaemonSet garantit que tous les nœuds (ou certains d'entre eux) exécutent une copie d'un pod) il va falloir les ignorer grâce au paramètre --ignore-daemonsets. Sans ça, la commande retournera une erreur car un DaemonSet ne peut pas être envoyé sur un autre noeud.
$ kubectl drain <nom noeud> --ignore-daemonsets
Une fois la maintenance finie, on veut remettre le noeud en service. Pour se faire :
$ kubectl uncordon <nom noeud>
Nous allons créer un pod simple (nginx) et un déploiement simple (nginx aussi) :
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
restartPolicy: OnFailure
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-deployment
spec:
replicas: 2
selector:
matchLabels:
app: my-deployment
template:
metadata:
labels:
app: my-deployment
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
On va maintenant appliquer ces 2 configurations :
$ kubectl apply -f pod.yml
pod/my-pod created
$ kubectl apply -f deployment.yml
deployment.apps/my-deployment created
On affiche maintenant les pods et on applique le paramètre -o wide afin de voir sur quel noeud tourne les pods :
# Sans précision de namespace, on regarde donc "default"
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-deployment-5f85c44867-8shmp 1/1 Running 0 7s 192.168.126.3 k8s-worker2 <none> <none>
my-deployment-5f85c44867-k86nl 1/1 Running 0 7s 192.168.126.2 k8s-worker2 <none> <none>
my-pod 1/1 Running 0 2m51s 192.168.126.1 k8s-worker2 <none> <none>
On voit que tous les pods tournent sur "k8s-worker2" (parfait), on va drain ce noeud afin de voir ce qu'il se passe avec le pod et le déploiement :
$ kubectl drain k8s-worker2
node/k8s-worker2 cordoned
error: unable to drain node "k8s-worker2", aborting command...
There are pending nodes to be drained:
k8s-worker2
cannot delete Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet (use --force to override): default/my-pod
cannot delete DaemonSet-managed Pods (use --ignore-daemonsets to ignore): kube-system/calico-node-5hzhz, kube-system/kube-proxy-dr9jb
Comme dit plus haut, nous devons ignorer les DaemonSets. Calico étant un DaemonSet nous allons passer le paramètre --ignore-daemonsets.
Concernant le pod, nous ne pouvons pas supprimer un pod qui n'est pas géré par un ReplicationController, ReplicaSet, Job, DaemonSet ou StatefulSet, on va donc utiliser le paramètre --force afin de supprimer le pod :
$ kubectl drain k8s-worker2 --ignore-daemonsets --force
node/k8s-worker2 already cordoned
WARNING: deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet: default/my-pod; ignoring DaemonSet-managed Pods: kube-system/calico-node-5hzhz, kube-system/kube-proxy-dr9jb
evicting pod default/my-pod
evicting pod default/my-deployment-5f85c44867-8shmp
evicting pod default/my-deployment-5f85c44867-k86nl
pod/my-deployment-5f85c44867-k86nl evicted
pod/my-pod evicted
pod/my-deployment-5f85c44867-8shmp evicted
node/k8s-worker2 evicted
On va maintenant voir ou sont les pods :
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-deployment-5f85c44867-sgzcg 1/1 Running 0 78s 192.168.194.68 k8s-worker1 <none> <none>
my-deployment-5f85c44867-zfmt8 1/1 Running 0 78s 192.168.194.67 k8s-worker1 <none> <none>
Le déploiement a bien été bougé de worker. Le pod à lui été supprimé.
Si on regarde l'état des noeuds :
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-control Ready control-plane,master 5d13h v1.21.0
k8s-worker1 Ready <none> 5d13h v1.21.0
k8s-worker2 Ready,SchedulingDisabled <none> 5d13h v1.21.0
On voit que le "k8s-worker2" est dans un état ou il ne peut recevoir de pod "SchedulingDisabled".
On a fini la maintenance, on va maintenant rendre disponible le noeud :
$ kubectl uncordon k8s-worker2
node/k8s-worker2 uncordoned
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-control Ready control-plane,master 5d13h v1.21.0
k8s-worker1 Ready <none> 5d13h v1.21.0
k8s-worker2 Ready <none> 5d13h v1.21.0
Tout est rentré dans l'ordre. Les pods restent cependant sur le worker1, ils ne sont pas reschedule.
On peut supprimer le déploiement :
$ kubectl delete -f deployment.yml
deployment.apps "my-deployment" deleted
# ou
$ kubectl delete deployment my-deployment
Il est toujours préférable de garder à jour Kubernetes. Kubeadm facilite la tâche.
$ kubectl drain k8s-control --ignore-daemonsets
# --allow-change-held-packages car on avait lock la version auparavant
sudo apt-get update && \
sudo apt-get install -y --allow-change-held-packages kubeadm=1.21.1-00
$ sudo kubeadm upgrade plan v1.21.1
$ sudo kubeadm upgrade apply v1.21.1
# --allow-change-held-packages car on avait lock la version auparavant
sudo apt-get update && \
sudo apt-get install -y --allow-change-held-packages kubelet=1.21.1-00 kubectl=1.21.1-00
$ sudo systemctl daemon-reload
$ sudo systemctl restart kubelet
$ kubectl uncordon k8s-control
node/k8s-control uncordoned
$ kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-control Ready control-plane,master 5d16h v1.21.1
k8s-worker1 Ready <none> 5d16h v1.21.0
k8s-worker2 Ready <none> 5d16h v1.21.0
$ kubectl drain k8s-worker1 --ignore-daemonsets --force
$ sudo apt-get update && \
sudo apt-get install -y --allow-change-held-packages kubeadm=1.21.1-00
$ sudo kubeadm upgrade node
$ sudo apt-get update && \
sudo apt-get install -y --allow-change-held-packages kubelet=1.21.1-00 kubectl=1.21.1-00
$ sudo systemctl daemon-reload
$ sudo systemctl restart kubelet
$ kubectl uncordon k8s-worker1
node/k8s-worker1 uncordoned
Pourquoi ?
etcd contient le stockage backend de kubernetes. C'est-à-dire tous les objets Kubernetes, les applications, les configurations.
Il est donc essentiel de sauvegarder etcd.
On peut backup etcd grâce à la CLI etcdctl :
$ ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save etcd.bak
Pour le restaurer :
$ ETCDCTL_API=3 etcdctl snapshot restore etcd.bak