Tenir le Cap avec les Meilleures Pratiques pour les Charts Helm

Kubernetes, le célèbre outil d’orchestration pour les applications de conteneurs, tire son nom du mot grec pour « pilote », ou celui qui gouverne le navire. Mais comme dans tout voyage, le succès du navigateur dépend de la carte dont il dispose.

Le chartHelmd’une application est cette carte. Une collection de fichiers qui peuvent être déployés à partir d’undépôt de charts Helmqui décrivent un ensemble connexe de ressources K8s. La confection la plus efficace possible de vos charts Helm permet à Kubernetes de naviguer entre les écueils lors du déploiement des conteneurs dans votre environnement de production.

Mais il existe d'autres façons de partir à la dérive, comme je l'ai constaté en développant des charts K8s accessibles au public pour déployer des produits. Avec chaque demande d'extraction, les commentaires de la communauté Helm m'ont aidé à m'orienter vers lesmeilleures pratiques des charts Helmqui offraient les meilleurs résultats pour l'exploitation et la mise à jour des conteneurs.

Voici quelques éléments à prendre en compte lors de l'écriture de charts K8s qui seront utilisés par la communauté ou les clients en production. Les questions à se poser sont notamment les suivantes :

  • Quelles dépendances devez-vous définir ?
  • Votre application aura-t-elle besoin d’un état persistant pour fonctionner ?
  • Comment allez-vous gérer la sécurité via les secrets et les autorisations ?
  • Comment allez-vous contrôler l’exécution des conteneurs kubelet ?
  • Comment vous assurer que vos applications sont en cours d’exécution et en mesure de recevoir des appels ?
  • Comment allez-vous présenter au monde les services de l’application ?
  • Comment allez-vous tester votre chart ?

Ce guide propose quelques bonnes pratiques pour structurer et spécifier vos charts Helm qui aideront K8s à faire accoster délicatement vos applications conteneurisées.

Démarrer

Avant de commencer, assurez-vous que vous êtes familiarisé avec les procédures essentielles pour ledéveloppement de charts Helm.

Dans ce guide, nous allons créer un chart Helm selon les meilleures pratiques que nous recommandons pour déployer une application decréation, lecture, mise à jour et suppression (CRUD) à deux niveaux pour la base de données Mongo à l’aide d’Express.js.

Vous trouverez le code source de notre exemple d’application dansexpress-crud dans GitHub.

Création et remplissage du chart Helm

Créons notre modèle de chart Helm à l’aide de lacommande de créationdu client Helm :

$ helm create express-crud

Nous créerons ainsi une structure de répertoires pour un chart Helmexpress-crud.

Pour commencer, mettez à jour les métadonnées du chart dans le fichierChart.yamlqui vient d’être créé. Veillez à ajouter des informations appropriées pour l'appVersion(la version de l’application à utiliser comme balise d'image docker), ladescription, laversion(une chaîne de versionSemVer 2), lessources, lesresponsableset l'icône.

apiVersion: v1 appVersion: "1.0.0" description: Un chart Helm pour une application express-crud name: express-crud version: 0.1.0 sources: - https://github.com/jainishshah17/express-mongo-crud maintainers: - name: myaccount email: myacount@mycompany.com icon: https://github.com/mycompany17/mycompany.com/blob/master/app/public/images/logo.jpg home: https://mycompany.com/

Définition des Dépendances

Si votre application a desdépendances, vous devez créer un fichierrequirements.yamldans la structure de répertoires du chart Helm qui les spécifie. Étant donné que notre application a besoin de la base de donnéesmongodb, nous devons le spécifier dans la liste desdépendancesdu fichierrequirements.yamlque nous créons.

Un fichierrequirements.yamlpour cet exemple contient:

dependencies: - name: mongodb version: 3.0.4 repository: https://kubernetes-charts.storage.googleapis.com/ condition: mongodb.enabled

Une fois qu’un fichierrequirements.yamlest créé, vous devez exécuter la commande demise à jour des dépendancesdans le client Helm :

$ helm dep update

Création de fichiers de déploiement

Les fichiers de déploiement de votre chart Helm résident dans le sous-répertoire\templateset spécifient la façon dont K8s déploiera l’application conteneur.

Le développement de vos fichiers de déploiement implique la prise de certaines décisions clés.

Objet de Déploiement ou Objet StatefulSet

Le fichier de déploiement que vous créez dépendra du fait que l'application nécessite que K8s la gère comme un Objet de Déploiement ou unObjet StatefulSet.

Un Objet de Déploiement est une application sans état qui est déclarée dans le fichierdeployment.yamlet spécifie le paramètrekindcommedeployment.

Un Objet Stateful est destiné aux applications avec état et utilisées avec des systèmes distribués. Ils sont déclarés dans le fichierstateless.yamlet spécifient le paramètrekindcommestateful.

Deployment StatefulSet
Les Deployments sont destinés à une utilisation sans état et sont plutôt légers. Les StatefulSets sont utilisés lorsque l'état doit être rendu persistant. Par conséquent, il utilisevolumeClaimTemplatessur les volumes persistants pour s’assurer qu’ils peuvent conserver l’état entre les redémarrages des composants.
Si votre application est sans état ou si l’état peut être créé à partir de systèmes backend au démarrage, utilisez Deployments. Si votre application est avec état ou si vous souhaitez déployer un stockage avec état en plus de Kubernetes, utilisez un StatefulSet.

Cette application n’ayant pas besoin d’état pour être rendue persistante, j’utilise un objet de déploiement.
Le fichierdeployment.yamla déjà été créé par la commandehelm create.

Nous utiliserons AppVersion comme balise d’image Docker pour notre application. Cela nous permet de mettre à niveau le chart Helm avec la nouvelle version de l’application en changeant simplement la valeur dansChart.yaml

image: "{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}"

Secret Ou ConfigMap

Vous devrez déterminer quels sont les identifiants ou données de configuration qu'il convient de stocker en tant quesecretset celles qui peuvent se trouver dans uneConfigMap.

Les secrets sont destinés aux informations sensibles telles que les mots de passe que K8s stockera dans un format crypté.

Une ConfigMap est un fichier qui contient des informations de configuration qui peuvent être partagées par les applications. Les informations contenues dans une ConfigMap ne sont pas chiffrées, elle ne doit donc pas contenir d’informations sensibles.

Secret ConfigMap
Le fait de placer ces informations dans un secret est plus sûr et plus souple que de les placer textuellement dans une définition de pod ou dans une image docker; Une ConfigMap vous permet de découpler les artefacts de configuration du contenu de l'image afin de conserver les applications conteneurisées portables
Utilisation pour les données confidentielles Utilisation pour les données non confidentielles
Exemples d'utilisation : Clés API, mot de passe, jetons et clés ssh Exemples d'utilisation : Rotateurs de journaux, Configuration sans données confidentielles

Dans cet exemple, nous allons autoriser Helm à extraire des images docker à partir deregistres docker privésl 'aide de秘密'extraction d形象。

Cette procédure s'appuie sur le fait que le cluster Kubernetes dispose d'un secret qui spécifie les informations d'identification du gestionnaire de dépôts. Ce secret peut être créé par une ligne de commande kubectl telle que :

$ kubectl create secret docker-registry regsecret --docker-server=$DOCKER_REGISTRY_RUL --docker-username=$USERNAME --docker-password=$PASSWORD --docker-email=$EMAIL

Dans le fichiervalues.yamlde votre chart Helm, vous pouvez ensuite passer le nom du secret à une valeur :

imagePullSecrets: regsecret

Vous pouvez ensuite utiliser le secret pour permettre à Helm d’accéder au registre docker via ces lignes dansdeployment.yaml:

{{- if .Values.imagePullSecrets }} imagePullSecrets: - name: {{ .Values.imagePullSecrets }} {{- end }}

Pour les secrets disponibles pour l’application, vous devez ajouter ces informations directement àvalues.yaml.

Par exemple, pour configurer notre application pour accéder à mongodb avec un utilisateur et une base de données pré-créés, ajoutez ces informations dansvalues.yaml

mongodb: enabled: true mongodbRootPassword: mongodbUsername: admin mongodbPassword: mongodbDatabase: test

Notez qu’ici, nous ne codons pas en dur les identifiants par défaut dans notre chart Helm. Au lieu de cela, nous utilisons une logique pour générer aléatoirement le mot de passe quand il n'est pas fourni via –set flag ouvalues.yaml

Nous utiliserons un secret pour transmettre les identifiants mongodb à notre application, via ces lignes dansdeployment.yaml.

env: - name: DATABASE_PASSWORD valueFrom: secretKeyRef: name: {{ .Release.Name }}-mongodb key: mongodb-password

Vous pouvez contrôler l’exécution des conteneurs kubelet via desInit Containersspécialisés ou desContainer Lifecycle Hooks.

InitContainers Conteneur Lifecycle Hooks
Les InitContainerssont des conteneurs spécialisés qui s’exécutent avant les conteneurs d’application et peuvent contenir des utilitaires ou des scripts de configuration qui ne sont pas présents dans une image d’application. Les conteneurs peuvent utiliser le framework Container lifecycle hook pour exécuter du code déclenché par des événements pendant leur cycle de vie de gestion.
Un Pod peut avoir un ou plusieurs Init Containers, qui sont exécutés avant le démarrage des conteneurs d'applications.

Un Pod ne peut avoir qu’un seul hookPostStartouPreStop
Le hookPostStarts’exécute immédiatement après la création d’un conteneur. Toutefois, il n’y a aucune garantie que le hook s’exécutera avant l'ENTRYPOINT du conteneur. Aucun paramètre n'est passé au handler.
par ex., déplacement de fichiers montés à l’aide de ConfigMap/Secrets vers un emplacement différent.
Le hookPreStopest appelé immédiatement avant qu'un conteneur se termine. Il est bloquant, ce qui veut dire qu'il est synchrone, et doit donc se terminer avant que l'appel pour supprimer le conteneur soit envoyé.

ex., Arrêt progressif de l'application

Vous pouvez utiliser desinitContainerspour ajouter des attentes, afin de vérifier que les microservices dépendants sont fonctionnels avant de continuer. Vous pouvez utiliser le hookPostStartpour mettre à jour le fichier dans le même pod, par exemple pour mettre à jour les fichiers de configuration avec l'IP Service

Dans notre exemple, ajoutez ces spécificationsinitContainersau fichierdeployments.yamlpour suspendre le démarrage de notre application jusqu’à ce que la base de données soit opérationnelle.

initContainers: - name: wait-for-db image: "{{ .Values.initContainerImage }}" command: - 'sh' - '-c' - > until nc -z -w 2 {{ .Release.Name }}-mongodb 27017 && echo mongodb ok; do sleep 2; done

Ajout de sondes de disponibilité et d'activité (Readiness et Liveness)

它可是有用的d 'ajouter de dispon一个探头ibilité et une sonde d'activité pour vérifier la santé de l'application. Si vous ne le faites pas, l'application peut échouer de telle sorte qu'elle semble fonctionner, mais ne répond pas aux appels ou aux requêtes.

Ces lignes dans le fichierdeployment.yamlajouteront ces sondes pour effectuer des vérifications périodiques :

livenessProbe: httpGet: path: '/health' port: http initialDelaySeconds: 60 periodSeconds: 10 failureThreshold: 10 readinessProbe: httpGet: path: '/health' port: http initialDelaySeconds: 60 periodSeconds: 10 failureThreshold: 10

Ajout de la prise en charge d'un RBAC

Ces procédures ajouteront la prise en charge d'uncontrôle d’accès basé sur les rôles (RBAC)à notre chart, lorsqu’une application l’exige.

Étape 1 : Créez unRôle enajoutant le contenu suivant dans un fichierrole.yaml:
Un rôle peut uniquement être utilisé pour accorder l’accès aux ressources dans un espace de noms unique.

{{- if .Values.rbac.create }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: app: {{ template "express-crud.name" . }} chart: {{ template "express-crud.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} name: {{ template "express-crud.fullname" . }} rules: {{ toYaml .Values.rbac.role.rules }} {{- end }}

Étape 2 : Créez unRoleBindingen ajoutant le contenu suivant dans un fichierrolebinding.yaml:
Un ClusterRole peut être utilisé pour accorder les mêmes autorisations qu'un Rôle, mais étant à l'échelle du cluster, ils peuvent également être utilisés pour accorder l'accès à :

  • ressources à l'échelle du cluster (comme des nœuds)
  • points de terminaison hors ressources (comme "/healthz")
  • ressources avec espace de noms (comme des pods) dans tous les espaces de noms
{{- if .Values.rbac.create }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: app: {{ template "express-crud.name" . }} chart: {{ template "express-crud.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} name: {{ template "express-crud.fullname" . }} subjects: - kind: ServiceAccount name: {{ template "express-crud.serviceAccountName" . }} roleRef: kind: Role apiGroup: rbac.authorization.k8s.io name: {{ template "express-crud.fullname" . }} {{- end }}

Étape 3 : Créez unServiceAccountenajoutant le contenu suivant dans un fichierserviceaccount.yaml:

Un compte de service fournit une identité pour les processus qui s'exécutent dans un Pod.

{{- if .Values.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: labels: app: {{ template "express-crud.name" . }} chart: {{ template "express-crud.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} name: {{ template "express-crud.serviceAccountName" . }} {{- end }}

Étape 4 : Utilisez le modèle d’assistance pour définir le nom du ServiceAccount.
Pour ce faire, ajoutez le contenu suivant dans le fichier_helpers.tpl

{{/* Créez le nom du compte de service à utiliser */}} {{- define "express-crud.serviceAccountName" -}} {{- if .Values.serviceAccount.create -}} {{ default (include "express-crud.fullname" .) .Values.serviceAccount.name }} {{- else -}} {{ default "default" .Values.serviceAccount.name }} {{- end -}} {{- end -}}

Ajout d’un service

Il est maintenant temps de présenter notre application au mondeà travers un service.

联合国de recevoir服务允许的应用程序du trafic via une adresse IP. Les services peuvent être présentés de différentes manières en spécifiant untype:

ClusterIP Le service est uniquement accessible par une adresse IP interne depuis le cluster.
NodePort Le service est accessible depuis l’extérieur du cluster via NodeIP et NodePort.
LoadBalancer Le service est accessible depuis l’extérieur du cluster via un équilibreur de charge externe. PeutEntrerdans l’application..


Pour ce faire, ajouter le contenu suivant àservice.yaml:

apiVersion: v1 kind: Service metadata: name: {{ template "express-crud.fullname" . }} labels: app: {{ template "express-crud.name" . }} chart: {{ template "express-crud.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.externalPort }} targetPort: http protocol: TCP name: http selector: app: {{ template "express-crud.name" . }} release: {{ .Release.Name }}

Il convient de noter que dans ce qui précède, pour notretypede service nous faisons référence à un paramètre dans notrevalues.yaml:

service: type: LoadBalancer internalPort: 3000 externalPort: 80

Résumé Values.yaml

La définition d'un grand nombre de nos paramètres dans un fichiervalues.yamlest une bonne pratique pour aider à maintenir vos charts Helm.

Voici comment le fichiervalues.yamlde notre exemple s'affiche, montrant la variété des paramètres que nous définissons pour de nombreuses fonctionnalités décrites ci-dessus :

#值defaut倒express-mongo-crud不相上下。# Ils’agit d’un fichier au format YAML. # Déclarez les variables à passer dans vos modèles. ## Contrôle d’accès basé sur un rôle ## Réf : https://kubernetes.io/docs/admin/authorization/rbac/ rbac: create: true role: ## Règles à créer. Selon la spécification de rôle rules: - apiGroups: - '' resources: - services - endpoints - pods verbs: - get - watch - list ## Compte de service ## Réf : https://kubernetes.io/docs/admin/service-accounts-admin/ ## serviceAccount: create: true ## Nom du ServiceAccount à utiliser. ## Si non défini et que la valeur de create est true, un nom est généré à l’aide du modèle fullname name: ## Valeurs de configuration pour la dépendance mongodb ## réf. : https://github.com/kubernetes/charts/blob/master/stable/mongodb/README.md ## mongodb: enabled: true image: tag: 3.6.3 pullPolicy: IfNotPresent persistence: size: 50Gi # resources: # requests: # memory: "12Gi" # cpu: "200m" # limits: # memory: "12Gi" # cpu: "2" ## Assurez-vous que la valeur de --wiredTigerCacheSizeGB n’est pas supérieure à la moitié de la limite de mémoire ! ## C’est essentiel pour se protéger contre OOMKill par Kubernetes ! mongodbExtraFlags: - "--wiredTigerCacheSizeGB=1" mongodbRootPassword: mongodbUsername: admin mongodbPassword: mongodbDatabase: test # livenessProbe: # initialDelaySeconds: 60 # periodSeconds: 10 # readinessProbe: # initialDelaySeconds: 30 # periodSeconds: 30 ingress: enabled: faux annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" path: / hosts: - chart-example.local tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local initContainerImage: "alpine:3.6" imagePullSecrets: replicaCount: 1 image: repository: jainishshah17/express-mongo-crud # tag: 1.0.1 pullPolicy: IfNotPresent service: type: LoadBalancer internalPort: 3000 externalPort: 80 resources: {} # Nous recommandons généralement de ne pas spécifier les ressources par défaut et de laisser cela comme un # choix conscient de l’utilisateur. Cela augmente également les chances que les charts s’exécutent dans des environnements avec peu # de ressources, par ex. Minikube. Si vous souhaitez spécifier des ressources, décommentez les # lignes suivantes, modifiez-les si nécessaire, et supprimez les accolades après 'resources:'. # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi nodeSelector: {} tolerations: [] affinity: {}

Tests et installations du chart Helm

Il est essentiel de tester notre chart Helm, ce que nous ferons avec la commandehelm lint.

$ helm lint ./ ## Output ==> Linting ./ Lint OK 1 chart testé, pas d’échecs

Utilisez la commandehelminstallpour déployer notre application à l'aide d'un chart helm sur Kubernetes.

$ helm install --name test1 ./ ## Output NAME: test1 LAST DEPLOYED: Sat Sep 15 09:36:23 2018 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE test1-mongodb 1 1 1 0 0s ==> v1beta2/Deployment test1-express-crud 1 1 1 0 0s ==> v1/Secret NAME TYPE DATA AGE test1-mongodb Opaque 2 0s ==> v1/PersistentVolumeClaim NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test1-mongodb Pending standard 0s ==> v1/ServiceAccount NAME SECRETS AGE test1-express-crud 1 0s ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test1-mongodb ClusterIP 10.19.248.205 27017/TCP 0s test1-express-crud LoadBalancer 10.19.254.169 80:31994/TCP 0s ==> v1/Role NAME AGE test1-express-crud 0s ==> v1/RoleBinding NAME AGE test1-express-crud 0s ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE test1-mongodb-67b6697449-tppk5 0/1 Pending 0 0s test1-express-crud-dfdbd55dc-rdk2c 0/1 Init:0/1 0 0s NOTES: 1. Get the application URL by running these commands: NOTE: It may take a few minutes for the LoadBalancer IP to be available. You can watch the status of by running 'kubectl get svc -w test1-express-crud' export SERVICE_IP=$(kubectl get svc --namespace default test1-express-crud -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo https://$SERVICE_IP:80

L 'execution de la commande helm install ci-dessus produira une IP_Externe pour l’équilibreur de charge. Vous pouvez utiliser cette adresse IP pour exécuter l’application.

Voici comment notre application apparaît lors de l’exécution :

Result

Conclusion

Comme vous pouvez le voir dans cet exemple, Helm est un système extrêmement polyvalent qui vous offre une grande flexibilité dans la manière de structurer et de développer un chart. En procédant conformément aux conventions de la communauté Helm vous pourrez soumettre plus facilement vos charts Helm pour une utilisation publique, et vous pourrez les gérer beaucoup plus facilement lors de la mise à jour de votre application.

Les charts Helm achevés pour cet exemple de projet se trouvent dans le dépôtexpress-crudsur GitHub, et vous pouvez examiner ces fichiers fonctionnels pour vous aider à mieux comprendre leur structure.

Pour explorer d’autres exemples, vous pouvez consulter mon dépôt d'exemples decharts Helmpour le développement de produits sur Kubernetes.