Exploitation de Kubernetes

Exploiter Kubernetes

Types de services

Un service est une abstraction qui définit un ensemble logique de Pods exposé à travers un endpoint.

Il existe différents types de services :

  • ClusterIP expose l’IP interne du cluster.
  • NodePort expose le Service sur l’IP de chaque nœud sur un port statique.
  • LoadBalancer expose le Service à l’extérieur en utilisant l’équilibreur de charge d’un fournisseur de cloud computing.
  • ExternalName associe le Service à un nom externe, tel que abc.toto.com.

Le service de type ClusterIP est le service par défaut de Kubernetes. Il donne un service à l’intérieur du cluster. Les pods à l’intérieur du même cluster peuvent utiliser ce service pour y accéder. Le service de type ClusterIP ne propose pas d’accès externe.

Spec pour un service de type ClusterIP:

apiVersion: v1
kind: Service
metadata: 
 name: my-service
spec:
 selector:   
   app: toto
 type: ClusterIP
 ports: 
 - name: http
   port: 80
   targetPort: 8080
   protocol: TCP

Tous les pods à l’intérieur du cluster peuvent atteindre le pod "toto" sur leur port 8080 via http://my-service:80. Il est aussi possible d’atteindre le pod_b en passant par l’adresse IP du clusterIP, mais ceci n’est pas conseillé. Si il y a plusieurs pod qui ont un label "app" égale à "toto", my-service distribue les requêtes selon une approche aléatoire.

Un service NodePort est le moyen le plus simple d’aiguiller du trafic externe directement vers un Pod. NodePort, comme son nom l’indique, ouvre un port spécifique sur tous les nodes, et tout trafic envoyé vers ce port est transféré vers le service.

Spec pour un service de NodePort:

apiVersion: v1
kind: Service
metadata:
 name: my-service
spec:
 ports:
 - port: 80
   protocol: TCP
   targetPort: 8080
   nodePort: 32016
 selector:
   app: toto
 type: NodePort

Un service LoadBalancer est le moyen standard d’exposer un service via un load balancer. Ce type de service est particulièrement adapté aux clusters Kubernetes managés par un fournisseur cloud. Par exemple sur l’infrastructure Cloud de Google, dans un cluster GKE, cela fera tourner un loadbalancer qui donnera une adresse IP unique qui transférera tout le trafic vers votre service.

Spec pour un service de type LoadBalancer:

apiVersion: v1
kind: Service
metadata:
 name: my-service
spec:
 ports:
 - port: 80
   protocol: TCP
   targetPort: 8080
 selector:
   run: toto
 type: LoadBalancer

Labels et choix d'un node pour le déploiement

Pour nous intéresser plus tard à l'affinité nous devons auparavant nous intéresser au nodeSelector.

Le nodeSelector est une des formes de contrainte de sélection de node disponible dans Kubernetes. C'est un champ qu'on rajoute dans la spec des pods, et où on spécifie une paire de clé-valeur correspondant au label du node sur lequel on souhaite recevoir notre pod. Passons en revue un exemple d'utilisation de nodeSelector.

Avant toute chose, pour assigner un label à un node:

kubectl label node <NODE_NAME> <key>=<value>

Dans cet exemple je vais affecter le label ntype:toto pour mon node worker-1.

kubectl label node worker-1 ntype=toto

Vous pouvez vérifier que cela a fonctionné en exécutant la commande suivante:

kubectl get nodes worker-1 --show-labels

# Confirmons que l'on retrouve bien notre label
NAME     STATUS   ROLES    AGE   VERSION   LABELS
worker-1   Ready    <none>   82m   v1.14.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker-1,kubernetes.io/os=linux,ntype=toto

L'étape suivante sera d'ajouter le champ nodeSelector à la configuration YAML de votre pod.

apiVersion: v1
kind: pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    ntype: toto

Crééons notre pod avec la commande suivante:

kubectl create -f pod.yaml

Une fois la commande lancée, le pod est alors planifié sur le node auquel vous avez attaché le label. Constatons cela avec la commande suivante:

kubectl get pods -o wide

NAME    READY   STATUS    RESTARTS   AGE     IP          NODE     NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          2m52s   10.44.0.1   worker-1   <node>           <node>

Vous pouvez supprimer le label de votre node en rajoutant le signe '-' (moins) à la fin de la commande kubectl label sans spécifier la valeur de la clé. Par exemple si je souhaite supprimer le label crée précédemment, je vais utiliser la commande suivante:

kubectl label node worker-1 ntype-

Nous pouvons également ordonnancer un pod sur un node spécifique via le paramètre nodeName.

Par exemple:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeName: foo-node # schedule pod to specific node
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

Nous pourrions par exemple souhaiter run un pod sur plusieurs nodes avec différents labels ou encore run un pod sur tous les nodes qui ne contiennent pas de tel ou tel label.

Néanmoins le nodeSelector ou le nodeName possèdent quelques limites. Dans notre exemple ne nous n'avions utilisé qu'un seul label de selection pour atteindre notre objectif.

Mais que ce passe-til si notre exigence est beaucoup plus complexe ?

Pour résoudre ces problématiques complexes, nous utiliserons une autre fonctionnalité proposée par kubernetes qui est l'affinité ou anti-affinité de node, que nous aborderons dans la section suivante.

Affinité et anti-affinité

La fonctionnalité d'affinité de node nous fournit des fonctionnalités avancées pour limiter le placement de pods sur des nodes spécifiques. Dans Kubernetes, les directives relatives aux "Affinités" contrôlent comment les Pods sont programmés - plus regroupés ou plus dispersés.

Pour PodAffinity, vous pouvez essayer de regrouper un certain nombre de Pods dans des domaines de topologie qualifiés

Pour PodAntiAffinity, seulement un Pod peut être programmé dans un domaine de topologie unique.

La fonctionnalité "EvenPodsSpread" fournit des options flexibles pour distribuer des Pods uniformément sur différents domaines de topologie - pour mettre en place de la haute disponibilité ou réduire les coûts. Cela peut aussi aider au rolling update des charges de travail et à la mise à l'échelle de réplicas.

Il existe actuellement deux types d'affinité de node, appelés:

requiredDuringSchedulingIgnoredDuringExecution preferredDuringSchedulingIgnoredDuringExecution

On peut distinguer ces types avec les états suivants:

During Scheduling 	During Execution
required 	ignored
preferred 	ignored

Nous avons ainsi:

During Scheduling: c'est l'état où un pod n'existe pas encore et qu'il est créé pour la première fois.

Cet état peut bénéficier de deux valeurs:

required: exige que le pod soit placé sur un node respectant les règles d'affinité. S'il ne parvient pas à en trouver un, le pod ne sera alors pas planifié.

preferred: quand aucun node correspondant n'est trouvé. Le scheduler ignorera simplement les règles d’affinité du pod et le placera sur n'importe quel node disponible.

C’est une façon de dire au scheduler:

"Fais de ton mieux pour placer le pod sur la correspondance d’affinité mais si tu ne peux vraiment pas en trouver un, place-le sur n’importe quel autre pod."

During Execution: c'est l'état lorsqu'un pod est déjà exécuté et qu'un changement a été apporté à l'environnement qui affecte l'affinité du node, tel qu'un changement dans le label du node.

Comme vous pouvez le constater, les deux types d’affinité disponibles aujourd’hui sont à l'état ingored, ce qui signifie que les pods continueront à fonctionner et que toute modification de l'affinité des nodes, n'aura aucun impact une fois qu'ils sont planifiés.

L'affinité de node est spécifiée dans le champ nodeAffinity du champ affinity dans la spécification du pod. Voici un exemple d'un pod utilisant une affinité de node:

apiVersion: v1
kind: pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: ntype
            operator: NotIn
            values:
            - virus
            - malware
  containers:
  - name: nginx
    image: nginx

Cette règle d'affinité de node indique que le pod ne peut pas être placé (grâce à l'opérateur NotIn) sur un node portant un label dont la clé est ntype et dont la valeur est virus ou malware.

Voici les différents opérateurs avec leur description pris en charge par les règles d'affinité de node:

  • In - utiliser les nodes avec les mêmes clés et valeurs à ceux qui sont définies dans le spec des pods.

  • NotIn - ignorer les nodes avec les mêmes clés et valeurs à ceux qui sont définies dans le spec des pods.

  • Exists - utiliser les nodes avec les mêmes clés à ceux qui sont définies dans le spec des pods.

  • DoesNotExist - ignorer les nodes avec les mêmes clés à ceux qui sont définies dans le spec des pods.

  • Gt - utiliser les nodes avec des valeurs numériques supérieur à ceux qui sont définies dans le spec du pod.

  • Lt - utiliser les nodes avec des valeurs numériques inférieur à ceux qui sont définies dans le spec du pod.

Les opérateurs NotIn et DoesNotExist provoquent ce que l'on appelle un comportement anti-affinité de node afin de respawn les pods des nodes que vous avez spécifiés.

Sources

Assigning Pods to Nodes

Manipuler le scheduler Kubernetes

Daemons set, health check, config map et secrets

DaemonSet

PersistenceDaemonSet

DaemonSet - Un DeamonSet est un contrôleur qui va s'assurer qu'un seul et unique pod s'exécute sur un node. C'est utile pour faire du monitoring serveur ou collecter des logs par exemple. Ainsi quand un node est ajouté au cluster, le DaemonSet va lancer lui même le pod qu'il définit.

Vous pouvez décrire un DaemonSet dans un fichier YAML. Par exemple, le fichier daemonset.yaml ci-dessous décrit un DaemonSet qui exécute l'image Docker fluentd-elasticsearch:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # this toleration is to have the daemonset runnable on master nodes
      # remove it if your masters can't run pods
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Puis, appliquer la spec:

kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml

Pour détruire le DaemonSet:

kubectl delete -f https://k8s.io/examples/controllers/daemonset.yaml

HealthCheck

Afin de vérifier si un conteneur dans un pod est sain et prêt à servir le trafic, Kubernetes prévoit une série de mécanismes de contrôle de santé. Les contrôles de santé, ou sondes comme on les appelle dans Kubernetes, sont effectués par la kubelet pour déterminer quand redémarrer un conteneur (sondes de lividité) et utilisés par les services et les déploiements pour déterminer si un pod doit recevoir du trafic (sondes de disponibilité). Dans ce qui suit, nous nous concentrerons sur les contrôles de santé HTTP.

Notez qu'il est de la responsabilité du développeur de l'application d'exposer une URL que le kubelet peut utiliser pour déterminer si le conteneur est sain (et potentiellement prêt).

Créons un pod qui expose un endpoint /health, répondant avec un code d'état HTTP 200:

kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/healthz/pod.yaml

Dans la spec du pod nous avons défini ce qui suit:

livenessProbe:
initialDelaySeconds: 2
periodSeconds: 5
httpGet:
path: /health
port: 9876

La configuration ci-dessus indique à Kubernetes de commencer à vérifier le point de terminaison /health, après avoir initialement attendu 2 secondes, toutes les 5 secondes.

Si nous regardons maintenant le pod, nous pouvons voir qu'il est considéré comme sain:

kubectl describe pod hc

Name:         hc
Namespace:    default
Priority:     0
Node:         minikube/192.168.39.51
...
Containers:
  sise:
    Container ID:   docker://2cfe4187808a89ae4731abfe242ac42611e1f658505691f540ac31ca8f6ce86f
    Image:          quay.io/openshiftlabs/simpleservice:0.5.0
    ...
    Ready:          True
    Restart Count:  0
    Liveness:       http-get http://:9876/health delay=2s timeout=1s period=5s #success=1 #failure=3
    Environment:    <none>
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
...

Source HealthChecks Kube By Example

ConfigMap

Une ConfigMap est un objet d'API utilisé pour stocker des données non confidentielles dans des paires clé-valeur. Les pods peuvent utiliser les ConfigMaps comme variables d'environnement, arguments de ligne de commande ou fichiers de configuration dans un volume. Un ConfigMap vous permet de découpler la configuration spécifique à l'environnement de vos images de conteneur, afin que vos applications soient facilement portables.

Typiquement, nous pourrions renseigner en tant que ConfigMap les fichiers de configuration de nos bases de données.

Utilisez une ConfigMap pour définir les données de configuration séparément du code de l'application.

Par exemple, imaginez que vous développez une application que vous pouvez exécuter sur votre propre ordinateur (pour le développement) et dans le cloud (pour gérer le trafic réel). Vous écrivez le code pour chercher dans une variable d'environnement appelée DATABASE_HOST. Localement, vous attribuez à cette variable la valeur localhost. Dans le nuage, vous la définissez pour faire référence à un service Kubernetes qui expose le composant de base de données à votre cluster. Cela vous permet de récupérer une image de conteneur exécutée dans le nuage et de déboguer le même code localement si nécessaire.

Une ConfigMap n'est pas conçue pour contenir de grandes quantités de données. Les données stockées dans une ConfigMap ne peuvent pas dépasser 1 Mo. Si vous devez stocker des paramètres dont la taille dépasse cette limite, vous pouvez envisager de monter un volume ou d'utiliser une base de données ou un service de fichiers distinct.

Créer un objet ConfigMap et définissez la spec d'un pod utilisant cette ConfigMap:

# cm.yaml
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # property-like keys; each key maps to a simple value
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # file-like keys
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
---
# Pod
apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # Define the environment variable
        - name: PLAYER_INITIAL_LIVES # Notice that the case is different here
                                     # from the key name in the ConfigMap.
          valueFrom:
            configMapKeyRef:
              name: game-demo           # The ConfigMap this value comes from.
              key: player_initial_lives # The key to fetch.
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
    # You set volumes at the Pod level, then mount them into containers inside that Pod
    - name: config
      configMap:
        # Provide the name of the ConfigMap you want to mount.
        name: game-demo
        # An array of keys from the ConfigMap to create as files
        items:
        - key: "game.properties"
          path: "game.properties"
        - key: "user-interface.properties"
          path: "user-interface.properties"
kubectl apply -f cm.yaml

Pour détruire les ressources:

kubectl delete -f cm.yaml

Secrets

Les objets secret de Kubernetes vous permettent de stocker et de gérer des informations sensibles, telles que les mots de passe, les tokens OAuth et les clés ssh. Mettre ces informations dans un secret est plus sûr et plus flexible que de le mettre en dur dans la définition d'un Pod ou dans une image de container.

Un secret est un objet qui contient une petite quantité de données sensibles telles qu'un mot de passe, un token ou une clé. De telles informations pourraient autrement être placées dans une spécification de pod ou dans une image; le placer dans un objet secret permet de mieux contrôler la façon dont il est utilisé et réduit le risque d'exposition accidentelle.

Les utilisateurs peuvent créer des secrets et le système crée également des secrets.

Pour utiliser un secret, un pod doit référencer le secret. Un secret peut être utilisé avec un pod de deux manières: sous forme de fichiers dans un volume monté sur un ou plusieurs de ses conteneurs, ou utilisé par kubelet lorsque vous récupérez des images pour le pod.

Vous pouvez également créer un secret dans un fichier d'abord, au format json ou yaml, puis créer cet objet. Le secret contient deux table de hachage: data et stringData. Le champ data est utilisé pour stocker des données arbitraires, encodées en base64. Le champ stringData est fourni pour plus de commodité et vous permet de fournir des données secrètes sous forme de chaînes non codées.

Par exemple, pour stocker deux chaînes dans un secret à l'aide du champ data, convertissez-les en base64 comme suit:

echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

Écrivez un secret qui ressemble à ceci:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

Maintenant, créez le secret en utilisant kubectl apply:

kubectl apply -f ./secret.yaml

secret "mysecret" created

Pour certains scénarios, vous pouvez utiliser le champ stringData à la place. Ce champ vous permet de mettre une chaîne non codée en base64 directement dans le secret, et la chaîne sera codée pour vous lorsque le secret sera créé ou mis à jour.

Un exemple pratique de cela pourrait être le suivant: vous déployez une application qui utilise un secret pour stocker un fichier de configuration. Vous souhaitez remplir des parties de ce fichier de configuration pendant votre processus de déploiement.

Si votre application utilise le fichier de configuration suivant:

apiUrl: "https://my.api.com/api/v1"
username: "user"
password: "password"

Vous pouvez stocker cela dans un secret en utilisant ce qui suit:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |-
    apiUrl: "https://my.api.com/api/v1"
    username: {{username}}
    password: {{password}}    

Votre outil de déploiement pourrait alors remplacer les variables de modèle {{username}} et {{password}} avant d'exécuter kubectl apply.

stringData est un champ de commodité en écriture seule. Il n'est jamais affiché lors de la récupération des secrets. Par exemple, si vous exécutez la commande suivante:

kubectl get secret mysecret -o yaml

L'output généré sera alors:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2021-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29y

Pour décoder un secret nous aurons besoin de récupérer le secret créé via la command kubectl get secret.

kubectl get secret mysecret -o yaml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2021-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

Décodez le champ du mot de passe:

echo 'MWYyZDFlMmU2N2Rm' | base64 --decode

1f2d1e2e67df

Persistent Volumes et Persistent Volumes Claim

La gestion du stockage est un problème distinct de la gestion du compute.

Le sous-système PersistentVolume fournit aux utilisateurs et aux administrateurs une API qui fait abstraction des détails sur la façon dont le stockage est fourni et sur la façon dont il est consommé.

Pour ce faire, nous introduisons deux nouvelles ressources API: PersistentVolume et PersistentVolumeClaim.

Un PersistentVolume (PV) est un élément de stockage en réseau dans le cluster qui a été provisionné par un administrateur. Il s'agit d'une ressource du cluster, tout comme un nœud est une ressource du cluster. Les PV sont des plugins de volume comme les volumes, mais ont un cycle de vie indépendant de tout pod individuel qui utilise le PV. Cet objet API capture les détails de l'implémentation du stockage, qu'il s'agisse de NFS, d'iSCSI ou d'un système de stockage spécifique au fournisseur de cloud.

Une PersistentVolumeClaim (PVC) est une demande de stockage par un utilisateur. Elle est similaire à un pod. Les pods consomment des ressources de nœuds et les PVC des ressources de PV. Les pods peuvent demander des niveaux spécifiques de ressources (CPU et mémoire). Les revendications peuvent demander une taille et des modes d'accès spécifiques (par exemple, peuvent être montées une fois en lecture/écriture ou plusieurs fois en lecture seule).

Alors que les PersistentVolumeClaims permettent à un utilisateur de consommer des ressources de stockage abstraites, il est courant que les utilisateurs aient besoin de PersistentVolumes avec des propriétés variables, telles que la performance, pour différents problèmes.

Les administrateurs de clusters doivent être en mesure d'offrir une variété de PersistentVolumes qui diffèrent en plus de la taille et des modes d'accès, sans exposer les utilisateurs aux détails de l'implémentation de ces volumes.

Pour ces besoins, il existe la ressource StorageClass.

Une StorageClass permet aux administrateurs de décrire les "classes" de stockage qu'ils proposent. Les différentes classes peuvent correspondre à des niveaux de qualité de service, à des politiques de sauvegarde ou à des politiques arbitraires déterminées par les administrateurs du cluster. Kubernetes lui-même ne se prononce pas sur ce que représentent les classes. Ce concept est parfois appelé "profils" dans d'autres systèmes de stockage.

Quelques types de plugins pour persistent volumes:

  • GCEPersistentDisk
  • AWSElasticBlockStore
  • AzureFile
  • AzureDisk
  • FC (Fibre Channel)
  • Flocker
  • NFS
  • iSCSI
  • RBD (Ceph Block Device)
  • CephFS
  • Cinder (OpenStack block storage)
  • Glusterfs
  • VsphereVolume
  • Quobyte Volumes
  • HostPath (tester sur un seul nœud uniquement - le stockage local n'est en aucun cas pris en charge et ne fonctionnera pas dans un cluster multi-nœuds)
  • VMware Photon
  • Portworx Volumes
  • ScaleIO Volumes

Les plug-ins de volume suivants prennent en charge les volumes de blocs bruts, y compris l'approvisionnement dynamique, le cas échéant:

  • AWSElasticBlockStore
  • AzureDisk
  • FC (Fibre Channel)
  • GCEPersistentDisk
  • iSCSI
  • Local volume
  • RBD (Ceph Block Device)
  • VsphereVolume (alpha)

Seuls les volumes FC et iSCSI prennent en charge les volumes de blocs bruts dans Kubernetes 1.9. La prise en charge des plugins supplémentaires a été ajoutée dans 1.10.

Se référer à la documentation spécifique a chaque plugin pour déterminer la spec permettant leur uitlisation.

Pour des volumes persistants utilisant un volume de bloc brut:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: block-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  persistentVolumeReclaimPolicy: Retain
  fc:
    targetWWNs: ["50060e801049cfd1"]
    lun: 0
    readOnly: false

Revendication de volume persistant demandant un volume de bloc brut:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 10Gi

Spécification de pod ajoutant le chemin du périphérique de bloc brut dans le conteneur:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-block-volume
spec:
  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: [ "tail -f /dev/null" ]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: block-pvc

N.B. Lorsque vous ajoutez un périphérique de bloc brut pour un pod, vous spécifiez le chemin de périphérique dans le conteneur au lieu d'un chemin de montage.

Sources

Unoffical K8s: persistent volumes

Concepts: Persistent Volumes

Déploiement d'une base de données et d'une application (TP)

Vos disposez de la spec suivante qui définit respectivement:

  • un PVC qui crée automatiquement le PV associé via la storage-class local-path

Vous devrez définir:

  • un service mysql
  • une configmap pour un fichier de configuration de votre choix
  • un secret pour notre db
  • le deployment de notre db

Vous aurez à charge d'écrire la spec du deployment du service et du secret mysql correspondant. N'hésitez pas pour ce faire à vous repportez à la section "Syntaxe YAML".

mysql-full-resources.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-storage-class
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: '10'
  fsType: xfs
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: ebs-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: ebs-storage-class
---
apiVersion: apps/v1
kind: Deployment
# Completer ce qui suit
---
apiVersion: v1
kind: ConfigMap
# Completer ce qui suit
---
apiVersion: v1
kind: Service
# Completer ce qui suit
---
apiVersion: v1
kind: Secret
# Completer ce qui suit
kubectl apply mysql-full-resources.yaml

Facultatif: Réécrivez les specs en utilisant une autre classe de storage que celle définie précédemment (kubernetes.io/aws-ebs) et créer le PV et le PVC en conséquence.

TP Version 2 (on K3s instead of AWS)

Vos disposez de la spec suivante qui définit respectivement:

  • un PVC qui crée automatiquement le PV associé via la storage-class local-path

Vous devrez définir:

  • un service mysql
  • un secret pour notre db
  • le deployment de notre db

Vous aurez à charge d'écrire la spec du deployment du service et du secret mysql correspondant. N'hésitez pas pour ce faire à vous repportez à la section "Syntaxe YAML".

mysql-full-resources.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-path-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 2Gi
---
apiVersion: apps/v1
kind: Deployment
# Completer ce qui suit
---
apiVersion: v1
kind: ConfigMap
# Completer ce qui suit
---
apiVersion: v1
kind: Service
# Completer ce qui suit
---
apiVersion: v1
kind: Secret
# Completer ce qui suit
kubectl apply mysql-full-resources.yaml

Facultatif: Réécrivez les specs en utilisant une autre classe de storage que celle définie précédemment (local-path) et créer le PV et le PVC en conséquence.