1. Introduction
Les relations inégales entre les instances et les applications où les instances ont des dépendances sur des données externes sont appelées "applications avec état".
La relation dite inégale entre les instances signifie que pour les applications distribuées, il existe souvent des dépendances relativement importantes entre chaque instance et chaque application. Par exemple, une application doit être démarrée avant les autres applications, sinon les autres applications ne seront pas démarrées.
L'application la plus importante qui dépend des données externes est l'application de base de données. Pour les applications de base de données, nous devons stocker ses données de manière permanente. S'il s'agit d'une application sans état, le redémarrage des données dans la base de données et l'application perdra le contact, ce qui est manifestement une violation Notre intention initiale n'était pas de la mettre en production.
Par conséquent, afin de résoudre le support efficace des applications avec état dans Kubernetes, Kubernetes utilise StatefulSet pour orchestrer et gérer les applications avec état.
StatefulSet est similaire à ReplicaSet. La différence est qu'il peut contrôler la séquence de démarrage des pods et qu'il définit un identifiant unique pour chaque pod. Il a les fonctions suivantes:
-
Identifiant réseau unique et stable
-
Stockage stable et persistant
-
Déploiement et mise à l'échelle ordonnés et élégants
- Mise à jour progressive et ordonnée
La conception de StatefulSet est facile à comprendre et résume le monde réel dans les deux situations suivantes:
(1) État topologique. Cela signifie qu'il existe une relation asymétrique entre les applications. Les applications doivent être démarrées dans un certain ordre. Même si l'application est redémarrée, elle doit être redémarrée dans l'ordre spécifié et son identification réseau doit être la même que celle d'origine après le redémarrage, afin que l'accès d'origine puisse être garanti L'utilisateur peut accéder au nouveau Pod via la même méthode;
(2), état de stockage. Cela signifie que l'application est liée aux données stockées, peu importe quand, quelle que soit la situation, pour l'application, tant que les données dans le stockage ne changent pas, les données lues doivent être les mêmes;
La fonction principale de StatefulSet est donc d'enregistrer l'état du pod d'une certaine manière, puis de restaurer son état d'une certaine manière lorsque le pod est recréé.
二 、 Service sans tête
Avant d'introduire StatefulSet, comprenons d'abord ce qu'est le service sans tête.
Nous savons que dans Kubernetes, le service est un moyen de fournir un accès externe à un groupe de pods. Habituellement, nous utilisons le service pour accéder au pod des deux manières suivantes:
(1) via l'adresse IP du cluster, cette adresse IP du cluster est équivalente à VIP. Lorsque nous visitons cette adresse IP, la demande sera transmise au pod backend;
(2) via DNS méthode, de cette manière, vous devez d'abord vous assurer qu'il existe un service DNS dans le cluster Kubernetes. À ce stade, tant que nous accédons à "mon-service.my-namespace.svc, cluster.local", nous pouvons accéder au pod backend mandaté par le service nommé my-service;
Pour la deuxième méthode, il existe les deux méthodes de traitement suivantes:
(1) Service normal, c'est-à-dire résoudre le nom de domaine, obtenir l'adresse IP du cluster, puis accéder selon la première méthode;
(2), Service sans tête, c'est-à-dire , résolvez le nom de domaine pour obtenir Est l'adresse IP d'un pod dans le backend, afin qu'il soit accessible directement;
Pour la deuxième méthode de traitement, nous pouvons voir qu'il n'y a pas besoin d'IP de cluster, mais l'adresse IP du pod backend est directement résolue via le nom de domaine pour l'accès.
Ce qui suit est une simple définition de fichier YAML de Headless Service:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
Le fichier YAML ci-dessus montre qu'il n'y a pas beaucoup de différence avec notre définition de service ordinaire. La seule différence est que le clusterIP est défini sur None, c'est-à-dire qu'aucun cluster n'est nécessaire. Nous créons ce service via kubectl apply -f , puis vérifiez que l'adresse IP du cluster du service est Aucune.
Le service créé de cette manière est un service sans tête, il n'a pas de VIP, il exposera donc le pod qu'il a mandaté sous la forme d'enregistrements DNS.
Troisièmement, utilisez StatefulSet
Créez le PV avant de créer le StatefulSet, comme suit:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01
labels:
release: stable
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
hostPath:
path: /tmp/data
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02
labels:
release: stable
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
hostPath:
path: /tmp/data
Puis exécutez kubectl apply -f pour démarrer PV, comme suit:
[root@master statefulset]# kubectl apply -f pv.yaml
persistentvolume/pv01 created
persistentvolume/pv02 created
[root@master statefulset]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv01 1Gi RWO Recycle Available 10s
pv02 1Gi RWO Recycle Available 9s
Vous pouvez voir que les états des deux PV sont tous deux disponibles, puis écrire le fichier YAML du StatefulSet:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
role: stateful
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
role: stateful
spec:
containers:
- name: nginx
image: cnych/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
Notez que le fichier YAML ci-dessus associé à volumeMounts est un nouvel attribut: volumeClaimTemplates, cet attribut déclarera automatiquement un objet pvc et pv pour la gestion, et serviceName: "nginx" signifie que le headless de nginx est utilisé lorsque la boucle de contrôle est exécutée. Service pour enregistrer l'identité résoluble du pod.
Ensuite, nous ouvrons ici deux fenêtres de terminal. Dans le premier terminal, utilisez kubectl get pour visualiser la création des pods du StatefulSet.
$ kubectl get pods -w -l role=stateful
Dans un autre terminal, utilisez kubectl create pour créer le service Headless et StatefulSet définis dans statefulset-demo.yaml.
$ kubectl create -f statefulset-demo.yaml
service "nginx" created
statefulset.apps "web" created
Ensuite, nous observons la séquence de démarrage du Pod:
[root@master ~]# kubectl get pods -w -l role=stateful
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 3m12s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 1s
web-1 0/1 ContainerCreating 0 1s
web-1 1/1 Running 0 5s
Grâce au processus de création ci-dessus, nous pouvons voir que StatefulSet numérote les pods qu'il gère. Sa règle de dénomination est [statefulset-name] - [index] et son index commence à 0, ce qui est le même que chaque instance de pod de StatefulSet. Une correspondance, ne jamais répéter. Plus important encore, le processus de création du pod est séquentiel. Comme ci-dessus, web-1 entre dans l'état en attente après que web-0 entre dans l'état en cours d'exécution.
Nous utilisons la commande pour visualiser les résultats de sa création:
[root@master statefulset]# kubectl get statefulset web
NAME READY AGE
web 2/2 17m
[root@master statefulset]# kubectl get pods -l role=stateful
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 17m
web-1 1/1 Running 0 14m
[root@master statefulset]# kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 17m
Lorsque les deux pods entrent en état d'exécution, vous pouvez afficher leurs identités réseau respectives. Nous pouvons les afficher via kubectl exec, comme suit:
[root@master statefulset]# kubectl exec web-0 -- sh -c 'hostname'
web-0
[root@master statefulset]# kubectl exec web-1 -- sh -c 'hostname'
web-1
Vous pouvez voir que le nom d'hôte et le nom de pod des deux pods sont identiques et qu'ils reçoivent tous des numéros correspondants. Ensuite, nous utilisons DNS pour accéder au service Headless.
Nous commençons par démarrer un pod avec la commande suivante:
kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh
Ensuite, utilisez nslookup pour analyser le service sans tête correspondant à un pod dans ce pod:
kubectl run -i --tty --image centos dns-test --restart=Never --rm /bin/sh
sh-4.2# yum install bind-utils -y
sh-4.2# nslookup web-0.nginx
Server: 10.68.0.2
Address: 10.68.0.2#53
Name: web-0.nginx.default.svc.cluster.local
Address: 172.20.2.63
sh-4.2# nslookup web-1.nginx
Server: 10.68.0.2
Address: 10.68.0.2#53
Name: web-1.nginx.default.svc.cluster.local
Address: 172.20.2.64
A partir de l'analyse des résultats de nslookup, lors de l'accès à web-0.nginx, l'IP du Pod web-0 est analysée, et l'autre est la même.
À ce stade, si nous supprimons les deux pods, observez le processus de redémarrage du pod:
[root@master statefulset]# kubectl delete pod -l role=stateful
pod "web-0" deleted
pod "web-1" deleted
Vérifiez ensuite la séquence de redémarrage comme suit:
[root@master ~]# kubectl get pods -w -l role=stateful
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 2s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 2s
Nous pouvons voir qu'après avoir supprimé le Pod, sa séquence de redémarrage est toujours redémarrée selon le numéro d'origine, et son identification réseau reste la même qu'auparavant.
Grâce à cette règle de correspondance stricte, StatefulSet garantit la stabilité de l'identité réseau du Pod. Grâce à cette méthode, l'état topologique du Pod peut être fixé en fonction du nom + du numéro du Pod. En outre, Kubernetes fournit également une entrée d'accès fixe et unique pour chaque pod, c'est-à-dire l'enregistrement DNS du pod.
Nous pouvons également vérifier la liaison du PV et du PVC:
[root@master statefulset]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv01 1Gi RWO Recycle Bound default/www-web-0 129m
pv02 1Gi RWO Recycle Bound default/www-web-1 129m
[root@master statefulset]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pv01 1Gi RWO 124m
www-web-1 Bound pv02 1Gi RWO 116m
Par conséquent, nous trions StatefulSet comme suit:
(1), StatefulSet gère directement Pod. En effet, les instances de pod dans le StatefulSet ne sont pas exactement les mêmes que les instances de pod dans le ReplicaSet. Elles sont légèrement différentes. Par exemple, le nom et le nom d'hôte de chaque pod sont différents, et la façon dont StatefulSet distingue ces instances est pour ajouter le numéro de pod;
(2), Kubernetes génère un enregistrement avec le même numéro dans le serveur DNS pour ce pod numéroté via le service sans tête. Tant que le StatefulSet peut garantir que le numéro de ce pod reste inchangé, l'enregistrement DNS similaire à web-0.nginx.default.svc.cluster.local dans le service ne changera pas et l'adresse IP du pod résolue par cet enregistrement Il est automatiquement mis à jour avec la recréation du Pod;
(3) StatefulSet peut également allouer et créer un PVC avec le même numéro que le Pod pour chaque Pod. De cette manière, Kubernetes peut lier le PV correspondant à ce PVC via le mécanisme Persitent Volume, afin de garantir que chaque Pod a un Volume indépendant. Dans ce cas, même si le pod est supprimé, son PVC et son PV correspondants seront toujours conservés, donc lorsque le pod est recréé, Kubernetes trouvera le PVC avec le même numéro pour lui, et montera le volume correspondant au PVC. les données précédentes du volume précédent;
Quatre, résumé
L'une des principales fonctions du contrôleur StatefulSet est de les numéroter lors de la création de pods à l'aide du modèle de pod, et d'effectuer les tâches dans l'ordre des numéros. Lorsque la boucle de contrôle du StatefulSet constate que l'état réel du pod est incompatible avec l'état attendu, il fera également fonctionner le pod dans l'ordre.
Bien sûr, StatefulSet a également d'autres fonctionnalités. Dans les projets réels, nous retournons rarement en arrière et déployons nos services avec état directement via StatefulSet, à moins que vous ne puissiez les conserver vous-même. Pour certains services spécifiques, nous pouvons utiliser davantage d'opérateurs avancés, tels que comme etcd-operator, prometheus-operator, etc. Ces applications peuvent très bien gérer les services avec état, au lieu d'utiliser simplement un StatefulSet pour déployer un Pod, car pour les applications avec état, le plus important est la récupération de données, le basculement, etc.
Finir