« Kubernetes » : différence entre les versions
Aucun résumé des modifications |
Aucun résumé des modifications |
||
Ligne 51 : | Ligne 51 : | ||
Pour un worker: | Pour un worker: | ||
/usr/local/bin/k3s-agent-uninstall.sh | /usr/local/bin/k3s-agent-uninstall.sh | ||
= Déploiement d'une application (manuellement) = | |||
Je vais tenter de déployer mon premier service, ma propre application ([https://hub.docker.com/repository/docker/squi/sqnotes sqnotes]). Celle-ci requiert idéalement un accès en http et un volume. | |||
Tout va se faire en utilisant la commande kubectl depuis le master. | |||
== Distinction pod / deployment / service == | |||
* [https://kubernetes.io/docs/tutorials/hello-minikube/ Doc] | |||
Un pod est un groupe de conteneurs, que l'on met ensemble pour des raisons d'administration. | |||
Un deployment est l'expression d'un état désiré pour un pod. Schématiquement, "Je veux que mon pod untel soit actif". | |||
Un service est une façon abstraite d'exposer une application tournant sur un ensemble de pods en tant que service réseau. | |||
== Déploiement == | |||
=== kubectl === | |||
C'est la commande principale servant à gérer un cluster kub. | |||
Sa doc est dispo [https://kubernetes.io/docs/reference/kubectl/ ici] | |||
=== Un deployment === | |||
Déployer mon conteneur en tant que simple déploiement est... plutôt simple: | |||
kubectl create deployment sqnotes --image=squi/sqnotes:1.0 | |||
=== Informations sur mon deployment === | |||
Je peux ensuite le voir: | |||
<nowiki> | |||
$ kubectl get pods | |||
NAME READY STATUS RESTARTS AGE | |||
sqnotes-85dd78574f-p27wb 0/1 ContainerCreating 0 5s | |||
$ kubectl get deployments | |||
NAME READY UP-TO-DATE AVAILABLE AGE | |||
sqnotes 1/1 1 1 41s | |||
</nowiki> | |||
...ainsi que ses évènements | |||
kubectl get events | |||
...avoir une description détaillée de mes deploiements | |||
kubectl describe deployments | |||
=== Supprimer mon déployment === | |||
kubectl delete deployment sqnotes | |||
=== Un service === | |||
==== Parlons un peu de réseau... ==== | |||
Par défaut, mon pod n'est accessible que depuis son IP interne, depuis l'intérieur du cluster. Pour que mon pod soit accessible de l'extérieur, il faut en faire un service. | |||
=== Faire un service === | |||
La commande suivante va donc exposer le port 8080 de mon deployment: | |||
kubectl expose deployment sqnotes --type=LoadBalancer --port=8080 | |||
* --type=LoadBalancer signifie que le service doit être accessible de l'extérieur. | |||
J'expose le port 8080 car celui utilisé par mon image docker. | |||
=== Voir un service === | |||
kubectl get services | |||
Me donne: | |||
<nowiki> | |||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE | |||
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 54m | |||
sqnotes LoadBalancer 10.43.138.228 192.168.1.208,192.168.1.209 8080:31349/TCP 104s | |||
</nowiki> | |||
=== Delete un service === | |||
Supprimer le deployment ne suffit pas, il faut aussi supprimer le service. | |||
kubectl delete service sqnotes | |||
== Stockage == | |||
=== Volumes === | |||
* [https://cloud.netapp.com/blog/kubernetes-shared-storage-the-basics-and-a-quick-tutorial Source] | |||
K8s se base sur des volumes. Ceux-ci sont provisionnés sans se soucier de l'infrastructure sous-jacente; ainsi, un partage réseau par exemple peut servir à stocker les données des conteneurs et à les partager entre eux, comme par exemple avec un cluster [[Ceph]] ! | |||
Un volume est donc "une unité de stockage abstraite". K8s propose en réalité un tas de plugins de stockage qui peuvent fonctionne sur AWX, Azure, VMWare, d'autres choses... | |||
==== Volumes et Volumes persistants ==== | |||
Les volumes normaux sont éphémères. Les volumes persistants, déployés de la même façon que les pods, fournissent du stockage à long terme (même si les pods sont arrêtés). | |||
Les nodes peuvent provisionner des volumes en faisant des claim, et en précisant le type de stockage demandé. On peut définir des objets StorageClass qui spécifient quels stockages sont sont dispo. Le cluster va, lors d'un claim, chercher un stockage adapté en fonction de sa StorageClass et faire le lien entre un claim et un volume. | |||
==== Provisionnement Statique / dynamique ==== | |||
Les volumes peuvent être créés de façon statique ou dynamique. Si c'est statique, le cluster dispose d'un ensemble fixe de volumes; les claims sont rattachés à l'un deux si cela correspond aux critères demandés (débit, prix, etc). | |||
Le provisionnement dynamique fait que les volumes sont créés automatiquement en réponse à un claim. K8s ne fait qu'allouer le stockage, pas les backups ou la HA sur celui-ci. | |||
= Déploiement d'une application (avec un fichier) = | |||
* [https://kubernetes.io/fr/docs/concepts/workloads/controllers/deployment/ Doc] | |||
Le passage par des fichiers va nous permettre de nous y retrouver un peu plus et de faire des parallèles avec docker et docker-compose. Notamment, cela va faciliter la gestion des replicas et la gestion du stockage. | |||
== ReplicaSet == | |||
* [https://kubernetes.io/fr/docs/concepts/workloads/controllers/replicaset/ Doc] | |||
Un ReplicaSet (ensemble de réplicas en français) a pour but de maintenir un ensemble stable de Pods à un moment donné. Cet objet est souvent utilisé pour garantir la disponibilité d'un certain nombre identique de Pods. | |||
C'est en gros un objet qui va gérer la replication des pods. Il est défini par plusieurs choses: | |||
* un sélecteur qui précise comment savoir quels pods il peut posséder | |||
* le nombre de replicas qu'il doit maintenir en vie | |||
* un template de pod qu'il va pouvoir utiliser pour en créer de nouveaux. | |||
Le replicaSet se base sur un champ de métadonnée (metadata.ownerReferences) du pod qui précise son replicaSet. Il peut identifier des nouveaux pods à acquérir avec son sélecteur, si celui-ci n'as pas de "ownerReferences". | |||
Exemple de fichier de replicaSet: | |||
<nowiki> | |||
apiVersion: apps/v1 | |||
kind: ReplicaSet | |||
metadata: | |||
name: frontend | |||
labels: | |||
app: guestbook | |||
tier: frontend | |||
spec: | |||
# modify replicas according to your case | |||
replicas: 3 | |||
selector: | |||
matchLabels: | |||
tier: frontend | |||
template: | |||
metadata: | |||
labels: | |||
tier: frontend | |||
spec: | |||
containers: | |||
- name: php-redis | |||
image: gcr.io/google_samples/gb-frontend:v3 | |||
</nowiki> | |||
== Exemple de deployment == | |||
* [https://kubernetes.io/fr/docs/concepts/workloads/controllers/deployment/ Doc assez complète] | |||
Exemple de deployment commenté: | |||
<nowiki> | |||
#Version de l'API k8s utilisée pour ce déploiement | |||
#visible avec kubectl api-versions | |||
apiVersion: apps/v1 | |||
#Ce fichier représente un déploiement (complet donc) | |||
kind: Deployment | |||
#Section définissant les métadonnées du deploiement | |||
metadata: | |||
#Son nom | |||
name: nginx-deployment | |||
labels: | |||
#Ce label est réutilisé plus bas pour le replicaSet | |||
app: nginx | |||
#Sepcifications du déploiement | |||
spec: | |||
#3 replicas... | |||
replicas: 3 | |||
#...identifié par le label nginx | |||
selector: | |||
matchLabels: | |||
app: nginx | |||
#Template de pod pour le replicaSet: | |||
#c'est en fait ici qu'on va dire quel conteneur on veut. | |||
template: | |||
metadata: | |||
labels: | |||
#Ne pas oublier le label | |||
app: nginx | |||
spec: | |||
containers: | |||
#Spécification du conteneur | |||
- name: nginx | |||
image: nginx:1.7.9 | |||
ports: | |||
- containerPort: 80 | |||
</nowiki> | |||
J'écris donc un déploiement pour mon application de prise de notes. | |||
<nowiki> | |||
apiVersion: apps/v1 | |||
kind: Deployment | |||
metadata: | |||
name: sqnotes | |||
labels: | |||
app: sqnotes | |||
spec: | |||
replicas: 3 | |||
selector: | |||
matchLabels: | |||
app: sqnotes | |||
template: | |||
metadata: | |||
labels: | |||
app: sqnotes | |||
spec: | |||
containers: | |||
- name: sqnotes | |||
image: squi/sqnotes:1.0 | |||
ports: | |||
- containerPort: 8080 | |||
</nowiki> | |||
et je le lance avec: | |||
kubectl apply -f sqnotes.yml | |||
Ça fonctionne... sauf que je n'ai pas le service. | |||
== Exemple de service == | |||
Puisqu'un service est un objet (comme un pod, comme un replicaSet, etc...) on peut le décrire dans un fichier yaml avant de le balancer à l'API. | |||
Exemple: | |||
<nowiki> | |||
#version de l'API... toujours pas compris ce champ | |||
apiVersion: v1 | |||
#c'est un service | |||
kind: Service | |||
#avec un nom | |||
metadata: | |||
name: my-service | |||
spec: | |||
#il s'applique en fonction d'un label | |||
selector: | |||
app: MyApp | |||
ports: | |||
#le port 80 du node donnera sur le port 9376 du conteneur. | |||
- protocol: TCP | |||
port: 80 | |||
targetPort: 9376 | |||
</nowiki> | |||
Je vais donc écrire un service pour mon appli. |
Version du 22 avril 2022 à 15:05
Présentation vite fait
Kubernetes (communément appelé « K8s2 ») est un système open source qui vise à fournir une « plate-forme permettant d'automatiser le déploiement, la montée en charge et la mise en œuvre de conteneurs d'application sur des clusters de serveurs »3. Il fonctionne avec toute une série de technologies de conteneurisation, et est souvent utilisé avec Docker. Il a été conçu à l'origine par Google, puis offert à la Cloud Native Computing Foundation.
Un cluster k8s contient deux types de machines (ou nodes):
- Un Master qui coordonne le cluster: scheduling, maintien de leur état recherché, scaling, application des mises à jour...
- Des workers, où tournent les applications: Cela peut être une VM ou une machine physique, utilisé comme worker et managée par le Master. Typiquement, un worker contient des outils pour gérer les opérations sur le conteneur (Docker, rkt) et Kubelet, un agent servant à manager le noeud. Un cluster de prod devrait avoir au moins trois noeuds.
Mise en oeuvre
Je pars d'un cluster à 2 machines sous Debian 11.
Je vais utiliser k3s pour la mise en place, une distribution légère de k8s très rapide à installer.
MEP
Prérequis
- ici
- 2 nodes ne peuvent pas avoir le même hostname
- 512Mo de RAm et 1 CPU
- SSD recommandé
Ports:
- 6443 pour tous les nodes (protocole ?)
- 8472/udp si utilisation de VXLAN Flannel
- Port 10250 pour le serveur de métriques
- Ports 2379 et 2380 si utilisation de etcd
Install
Mon premier node sera master, le second un simple node. Tout se fait en root.
Sur le master:
curl -sfL https://get.k3s.io | sh - kubectl get node
Le script installe kubernetes et je vois que mon serveur est désormais noeud du cluster.
Mon cluster est identifié par un token présent sur le master. Je vais le récupérer:
/var/lib/rancher/k3s/server/node-token
Sur le worker, c'est un peu la même chose, mais on ajoute l'adresse du master et le token:
curl -sfL https://get.k3s.io | K3S_URL=https://myserver:6443 K3S_TOKEN=mynodetoken sh -
Je peux voir mes 2 machines sur le worker:
kubectl get node
Désinstallation si besoin
Pour un master:
/usr/local/bin/k3s-uninstall.sh
Pour un worker:
/usr/local/bin/k3s-agent-uninstall.sh
Déploiement d'une application (manuellement)
Je vais tenter de déployer mon premier service, ma propre application (sqnotes). Celle-ci requiert idéalement un accès en http et un volume.
Tout va se faire en utilisant la commande kubectl depuis le master.
Distinction pod / deployment / service
Un pod est un groupe de conteneurs, que l'on met ensemble pour des raisons d'administration.
Un deployment est l'expression d'un état désiré pour un pod. Schématiquement, "Je veux que mon pod untel soit actif".
Un service est une façon abstraite d'exposer une application tournant sur un ensemble de pods en tant que service réseau.
Déploiement
kubectl
C'est la commande principale servant à gérer un cluster kub.
Sa doc est dispo ici
Un deployment
Déployer mon conteneur en tant que simple déploiement est... plutôt simple:
kubectl create deployment sqnotes --image=squi/sqnotes:1.0
Informations sur mon deployment
Je peux ensuite le voir:
$ kubectl get pods NAME READY STATUS RESTARTS AGE sqnotes-85dd78574f-p27wb 0/1 ContainerCreating 0 5s $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE sqnotes 1/1 1 1 41s
...ainsi que ses évènements
kubectl get events
...avoir une description détaillée de mes deploiements
kubectl describe deployments
Supprimer mon déployment
kubectl delete deployment sqnotes
Un service
Parlons un peu de réseau...
Par défaut, mon pod n'est accessible que depuis son IP interne, depuis l'intérieur du cluster. Pour que mon pod soit accessible de l'extérieur, il faut en faire un service.
Faire un service
La commande suivante va donc exposer le port 8080 de mon deployment:
kubectl expose deployment sqnotes --type=LoadBalancer --port=8080
- --type=LoadBalancer signifie que le service doit être accessible de l'extérieur.
J'expose le port 8080 car celui utilisé par mon image docker.
Voir un service
kubectl get services
Me donne:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 54m sqnotes LoadBalancer 10.43.138.228 192.168.1.208,192.168.1.209 8080:31349/TCP 104s
Delete un service
Supprimer le deployment ne suffit pas, il faut aussi supprimer le service.
kubectl delete service sqnotes
Stockage
Volumes
K8s se base sur des volumes. Ceux-ci sont provisionnés sans se soucier de l'infrastructure sous-jacente; ainsi, un partage réseau par exemple peut servir à stocker les données des conteneurs et à les partager entre eux, comme par exemple avec un cluster Ceph !
Un volume est donc "une unité de stockage abstraite". K8s propose en réalité un tas de plugins de stockage qui peuvent fonctionne sur AWX, Azure, VMWare, d'autres choses...
Volumes et Volumes persistants
Les volumes normaux sont éphémères. Les volumes persistants, déployés de la même façon que les pods, fournissent du stockage à long terme (même si les pods sont arrêtés).
Les nodes peuvent provisionner des volumes en faisant des claim, et en précisant le type de stockage demandé. On peut définir des objets StorageClass qui spécifient quels stockages sont sont dispo. Le cluster va, lors d'un claim, chercher un stockage adapté en fonction de sa StorageClass et faire le lien entre un claim et un volume.
Provisionnement Statique / dynamique
Les volumes peuvent être créés de façon statique ou dynamique. Si c'est statique, le cluster dispose d'un ensemble fixe de volumes; les claims sont rattachés à l'un deux si cela correspond aux critères demandés (débit, prix, etc).
Le provisionnement dynamique fait que les volumes sont créés automatiquement en réponse à un claim. K8s ne fait qu'allouer le stockage, pas les backups ou la HA sur celui-ci.
Déploiement d'une application (avec un fichier)
Le passage par des fichiers va nous permettre de nous y retrouver un peu plus et de faire des parallèles avec docker et docker-compose. Notamment, cela va faciliter la gestion des replicas et la gestion du stockage.
ReplicaSet
Un ReplicaSet (ensemble de réplicas en français) a pour but de maintenir un ensemble stable de Pods à un moment donné. Cet objet est souvent utilisé pour garantir la disponibilité d'un certain nombre identique de Pods.
C'est en gros un objet qui va gérer la replication des pods. Il est défini par plusieurs choses:
- un sélecteur qui précise comment savoir quels pods il peut posséder
- le nombre de replicas qu'il doit maintenir en vie
- un template de pod qu'il va pouvoir utiliser pour en créer de nouveaux.
Le replicaSet se base sur un champ de métadonnée (metadata.ownerReferences) du pod qui précise son replicaSet. Il peut identifier des nouveaux pods à acquérir avec son sélecteur, si celui-ci n'as pas de "ownerReferences".
Exemple de fichier de replicaSet:
apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: guestbook tier: frontend spec: # modify replicas according to your case replicas: 3 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: php-redis image: gcr.io/google_samples/gb-frontend:v3
Exemple de deployment
Exemple de deployment commenté:
#Version de l'API k8s utilisée pour ce déploiement #visible avec kubectl api-versions apiVersion: apps/v1 #Ce fichier représente un déploiement (complet donc) kind: Deployment #Section définissant les métadonnées du deploiement metadata: #Son nom name: nginx-deployment labels: #Ce label est réutilisé plus bas pour le replicaSet app: nginx #Sepcifications du déploiement spec: #3 replicas... replicas: 3 #...identifié par le label nginx selector: matchLabels: app: nginx #Template de pod pour le replicaSet: #c'est en fait ici qu'on va dire quel conteneur on veut. template: metadata: labels: #Ne pas oublier le label app: nginx spec: containers: #Spécification du conteneur - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
J'écris donc un déploiement pour mon application de prise de notes.
apiVersion: apps/v1 kind: Deployment metadata: name: sqnotes labels: app: sqnotes spec: replicas: 3 selector: matchLabels: app: sqnotes template: metadata: labels: app: sqnotes spec: containers: - name: sqnotes image: squi/sqnotes:1.0 ports: - containerPort: 8080
et je le lance avec:
kubectl apply -f sqnotes.yml
Ça fonctionne... sauf que je n'ai pas le service.
Exemple de service
Puisqu'un service est un objet (comme un pod, comme un replicaSet, etc...) on peut le décrire dans un fichier yaml avant de le balancer à l'API.
Exemple:
#version de l'API... toujours pas compris ce champ apiVersion: v1 #c'est un service kind: Service #avec un nom metadata: name: my-service spec: #il s'applique en fonction d'un label selector: app: MyApp ports: #le port 80 du node donnera sur le port 9376 du conteneur. - protocol: TCP port: 80 targetPort: 9376
Je vais donc écrire un service pour mon appli.