Utiliser Kubernetes consiste à déclarer des objets grâce à l’API Kubernetes pour décrire l’état souhaité d’un cluster : quelles applications ou autres processus exécuter, quelles images elles utilisent, le nombre de replicas, les ressources réseau et disque que vous mettez à disposition, etc.
On définit des objets généralement via l’interface en ligne de commande et kubectl
de deux façons :
kubectl run <conteneur> ...
, kubectl expose ...
kubectl apply -f monpod.yml
Vous pouvez également écrire des programmes qui utilisent directement l’API Kubernetes pour interagir avec le cluster et définir ou modifier l’état souhaité. Kubernetes est complètement automatisable !
apply
Kubernetes encourage le principe de l’infrastructure-as-code : il est recommandé d’utiliser une description YAML et versionnée des objets et configurations Kubernetes plutôt que la CLI.
Pour cela la commande de base est kubectl apply -f object.yaml
.
La commande inverse kubectl delete -f object.yaml
permet de détruire un objet précédement appliqué dans le cluster à partir de sa description.
Lorsqu’on vient d’appliquer une description on peut l’afficher dans le terminal avec kubectl apply -f myobj.yaml view-last-applied
Globalement Kubernetes garde un historique de toutes les transformations des objets : on peut explorer, par exemple avec la commande kubectl rollout history deployment
.
Les description YAML permettent de décrire de façon lisible et manipulable de nombreuses caractéristiques des ressources Kubernetes (un peu comme un Compose file par rapport à la CLI Docker).
Création d’un service simple :
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
selector:
k8s-app: kubernetes-dashboard
type: NodePort
Création d’un “compte utiliseur” ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
Remarques de syntaxe :
Toutes les descriptions doivent commencer par spécifier la version d’API (minimale) selon laquelle les objets sont censés être créés
Il faut également préciser le type d’objet avec kind
Le nom dans metadata:\n name: value
est également obligatoire.
On rajoute généralement une description longue démarrant par spec:
On peut mettre plusieurs ressources à la suite dans un fichier k8s : cela permet de décrire une installation complexe en un seul fichier
L’ordre n’importe pas car les ressources sont décrites déclarativement c’est-à-dire que:
On peut sauter des lignes dans le YAML et rendre plus lisible les descriptions
On sépare les différents objets par ---
Tous les objets Kubernetes sont rangés dans différents espaces de travail isolés appelés namespaces
.
Cette isolation permet 3 choses :
Lorsqu’on lit ou créé des objets sans préciser le namespace, ces objets sont liés au namespace default
.
Pour utiliser un namespace autre que default
avec kubectl
il faut :
-n
: kubectl get pods -n kube-system
Kubernetes gère lui-même ses composants internes sous forme de pods et services.
-A
ou --all-namespaces
Un Pod est l’unité d’exécution de base d’une application Kubernetes que vous créez ou déployez. Un Pod représente des process en cours d’exécution dans votre Cluster.
Un Pod encapsule un conteneur (ou souvent plusieurs conteneurs), des ressources de stockage, une IP réseau unique, et des options qui contrôlent comment le ou les conteneurs doivent s’exécuter (ex: restart policy). Cette collection de conteneurs et volumes tournent dans le même environnement d’exécution mais les processus sont isolés.
Un Pod représente une unité de déploiement : un petit nombre de conteneurs qui sont étroitement liés et qui partagent :
Chaque Pod est destiné à exécuter une instance unique d’un workload donné. Si vous désirez mettre à l’échelle votre workload, vous devez multiplier le nombre de Pods.
Pour plus de détail sur la philosophie des pods, vous pouvez consulter ce bon article.
Kubernetes fournit un ensemble de commande pour débugger des conteneurs :
kubectl logs <pod-name> -c <conteneur_name>
(le nom du conteneur est inutile si un seul)kubectl exec -it <pod-name> -c <conteneur_name> -- bash
kubectl attach -it <pod-name>
Enfin, pour debugger la sortie réseau d’un programme on peut rapidement forwarder un port depuis un pods vers l’extérieur du cluster :
kubectl port-forward <pod-name> <port_interne>:<port_externe>
NodePort
.Pour copier un fichier dans un pod on peut utiliser: kubectl cp <pod-name>:</path/to/remote/file> </path/to/local/file>
Pour monitorer rapidement les ressources consommées par un ensemble de processus il existe les commande kubectl top nodes
et kubectl top pods
kuard-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nom_pod
spec:
containers:
- image: tecpi/pod_image:0.1
name: nom_conteneur
ports:
- containerPort: 8080
name: http
protocol: TCP
Un ReplicaSet ou rs
est une ressource qui permet de spécifier finement le nombre de réplication d’un pod à un moment donné.
kubectl get rs
pour afficher la liste des replicas.En général on ne les manipule pas directement.
Plutôt que d’utiliser les replicasets il est recommander d’utiliser un objet de plus haut niveau : les deployments.
De la même façon que les ReplicaSets gèrent les pods, les Deployments gèrent les ReplicaSet.
Un déploiement sert surtout à gérer le déploiement d’une nouvelle version d’un pod.
Un deployment est un peu l’équivalent d’un service docker : il demande la création d’un ensemble de Pods désignés par une étiquette label
.
Exemple :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
strategy:
type: Recreate
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
Pour les afficher : kubectl get deployments
La commande kubectl run
sert à créer un deployment à partir d’un modèle. Il vaut mieux utilisez apply -f
.
Les poupées russes Kubernetes : un Deployment contient un ReplicaSet, qui contient des Pods, qui contiennent des conteneurs
Dans Kubernetes, un service est un objet qui :
L’ensemble des pods ciblés par un service est déterminé par un selector
.
Par exemple, considérons un backend de traitement d’image (stateless, c’est-à-dire ici sans base de données) qui s’exécute avec 3 replicas. Ces replicas sont interchangeables et les frontends ne se soucient pas du backend qu’ils utilisent. Bien que les pods réels qui composent l’ensemble backend
puissent changer, les clients frontends ne devraient pas avoir besoin de le savoir, pas plus qu’ils ne doivent suivre eux-mêmes l’état de l’ensemble des backends.
L’abstraction du service permet ce découplage : les clients frontend s’addressent à une seule IP avec un seul port dès qu’ils ont besoin d’avoir recours à un backend. Les backends vont recevoir la requête du frontend aléatoirement.
Les Services sont de trois types principaux :
ClusterIP
: expose le service sur une IP interne au cluster appelée ClusterIP. Les autres pods peuvent alors accéder au service mais pas l’extérieur.
NodePort
: expose le service depuis l’IP publique de chacun des noeuds du cluster en ouvrant port directement sur le nœud, entre 30000 et 32767. Cela permet d’accéder aux pods internes répliqués. Comme l’IP est stable on peut faire pointer un DNS ou Loadbalancer classique dessus.
LoadBalancer
: expose le service en externe à l’aide d’un Loadbalancer de fournisseur de cloud. Les services NodePort et ClusterIP, vers lesquels le Loadbalancer est dirigé sont automatiquement créés.