Comment Dailymotion utilise Kubernetes : déploiement d'applications
Chez Dailymotion, nous avons commencé à utiliser Kubernetes en production il y a 3 ans. Cependant, déployer des applications sur plusieurs clusters peut être un véritable défi, c'est pourquoi ces dernières années, nous avons cherché à améliorer nos outils et nos processus.
Le début de tout
Ici, nous expliquons comment nous déployons nos applications sur plusieurs clusters Kubernetes à travers le monde.
Pour déployer plusieurs objets Kubernetes en même temps, nous utilisons , et tous nos charts sont stockés dans un seul dépôt git. Pour déployer l'ensemble de la pile applicative composée de plusieurs services, nous utilisons ce que l'on appelle un chart aggrégateur. En gros, c'est un chart qui déclare les dépendances et permet d'initialiser l'API et ses services en une seule commande.
Nous avons également écrit un petit script Python au-dessus de Helm pour effectuer des vérifications, créer des charts, ajouter des secrets et déployer des applications. Toutes ces tâches sont réalisées sur une plateforme CI centrale à l'aide d'une image docker.
Passons aux choses sérieuses.
Note. Quand vous lisez ceci, la première version candidate de Helm 3 a déjà été annoncée. La version principale contient un ensemble d'améliorations visant à résoudre certains problèmes auxquels nous avons été confrontés dans le passé.
Workflow de développement des charts
Pour les applications, nous utilisons la ramification, et nous avons décidé d'appliquer cette même approche aux charts.
- La branche dev est utilisée pour créer des charts qui seront testés sur des clusters de développement.
- Lorsque la demande de tirage est soumise à master, elle est vérifiée en staging.
- Enfin, nous créons une demande de tirage pour transmettre les modifications à la branche prod et les appliquer en production.
Chaque environnement a son propre dépôt privé qui stocke nos charts, et nous utilisons avec des API très utiles. Ainsi, nous garantissons une stricte isolation entre les environnements et un test des charts dans des conditions réelles avant de les utiliser en production.
Dépôts de charts dans différents environnements
Il est à noter que lorsque les développeurs envoient une branche dev, la version de leur chart est automatiquement envoyée à dev Chartmuseum. Ainsi, tous les développeurs utilisent un seul dépôt dev et doivent prêter attention à la version de leur chart pour ne pas utiliser accidentellement les modifications de quelqu'un d'autre.
De plus, notre petit script Python vérifie les objets Kubernetes selon les spécifications de Kubernetes OpenAPI en utilisant , avant de les publier dans Chartmuseum.
Description générale du flux de travail de développement de chartes
- Configuration des tâches de pipeline selon les spécifications pour le contrôle qualité (lint, test unitaire).
- Envoi de l'image docker avec des outils Python qui déploient nos applications.
- Configuration de l'environnement par nom de branche.
- Vérification des fichiers yaml Kubernetes avec Kubeval.
- Augmentation automatique de la version du chart et de ses charts parents (charts qui dépendent du chart modifié).
- Envoi du chart dans Chartmuseum, qui correspond à son environnement
Gestion des différences dans les clusters
Fédération de clusters
Il fut un temps où nous utilisions , où il était possible de déclarer des objets Kubernetes à partir d'un seul point d'extrémité API. Mais des problèmes sont survenus. Par exemple, certains objets Kubernetes ne pouvaient pas être créés à l'point d'extrémité de fédération, ce qui compliquait la gestion des objets agrégés et d'autres objets pour des clusters individuels.
Pour résoudre le problème, nous avons commencé à gérer les clusters de manière indépendante, ce qui a considérablement simplifié le processus (nous avons utilisé la première version de la fédération ; quelque chose a pu changer dans la deuxième version).
Plateforme géo-distribuée
Maintenant, notre plateforme est répartie sur 6 régions — 3 localement et 3 dans le cloud.
Déploiement distribué
Valeurs globales Helm
4 valeurs globales Helm permettent de définir les différences entre les clusters. Pour tous nos charts, il existe des valeurs minimales par défaut.
global:
cloud: True
env: staging
region: us-central1
clusterName: staging-us-central1Valeurs globales
Ces valeurs aident à définir le contexte de nos applications et sont utilisées pour différentes tâches : surveillance, traçage, journalisation, appels externes, mise à l'échelle, etc.
- "cloud": nous avons une plateforme Kubernetes hybride. Par exemple, notre API est déployée dans des zones GCP et dans nos centres de données.
- "env": certaines valeurs peuvent changer pour les environnements non opérationnels. Par exemple, les définitions de ressources et la configuration de l'autoscaling.
- "region": cette information aide à déterminer l'emplacement du cluster et peut être utilisée pour définir les points d'extrémité les plus proches pour les services externes.
- "clusterName": si et quand nous voulons définir une valeur pour un cluster spécifique.
Voici un exemple concret :
{{
/* Retourne le nombre de réplicas du Horizontal Pod Autoscaler pour GraphQL */}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}Exemple de modèle Helm
Cette logique est définie dans un modèle auxiliaire afin de ne pas encombrer le YAML de Kubernetes.
Déclaration de l'application
Nos outils de déploiement reposent sur plusieurs fichiers YAML. Voici un exemple de la façon dont nous déclarons le service et sa topologie de mise à l'échelle (nombre de réplicas) dans le cluster.
releases:
- foo.world
foo.world: # Nom de la version
services: # Liste des applications/projets de dailymotion
foobar:
chart_name: foo-foobar
repo: git@github.com:dailymotion/foobar
contexts:
prod-europe-west1:
deployments:
- name: foo-bar-baz
replicas: 18
- name: another-deployment
replicas: 3Définition du service
C'est le schéma de toutes les étapes qui définissent notre flux de travail de déploiement. La dernière étape déploie l'application simultanément sur plusieurs clusters de travail.
Étapes de déploiement dans Jenkins
Et les secrets ?
En ce qui concerne la sécurité, nous suivons tous les secrets provenant de différents endroits et les stockons dans un dépôt unique à Paris.
Nos outils de déploiement extraient les valeurs des secrets à partir de Vault et, lorsque vient le temps de déployer, les insèrent dans Helm.
Pour cela, nous avons défini une correspondance entre les secrets dans Vault et les secrets dont nos applications ont besoin :
secrets:
- secret_id: "stack1-app1-password"
contexts:
- name: "default"
vaultPath: "\/kv\/dev\/stack1\/app1\/test"
vaultKey: "password"
- name: "cluster1"
vaultPath: "\/kv\/dev\/stack1\/app1\/test"
vaultKey: "password"- Nous avons défini des règles générales à suivre lors de l'enregistrement des secrets dans Vault.
- Si un secret se rapporte à un contexte ou un cluster spécifique, il est nécessaire d'ajouter une entrée spécifique. (Ici, le contexte cluster1 a sa propre valeur pour le secret stack-app1-password).
- Dans le cas contraire, la valeur par défaut est utilisée par défaut.
- Pour chaque élément de cette liste, un secret Kubernetes est inséré avec une paire clé-valeur. C'est pourquoi le modèle de secret dans nos charts est très simple.
apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
{{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
name: "{{ .Chart.Name }}"
labels:
chartVersion: "{{ .Chart.Version }}"
tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: OpaqueProblèmes et limitations
Travail avec plusieurs dépôts
Actuellement, nous séparons le développement des charts et des applications. Cela signifie que les développeurs doivent travailler dans deux dépôts git : un pour l'application et un autre pour définir son déploiement dans Kubernetes. 2 dépôts git signifient 2 flux de travail, et un nouveau peut facilement se perdre.
Gérer des charts génériques est pénible
Comme nous l'avons déjà dit, les charts génériques sont très pratiques pour définir les dépendances et déployer rapidement plusieurs applications. Mais nous utilisons --reuse-values, pour éviter de transférer toutes les valeurs à chaque fois que nous déployons une application faisant partie de ce chart générique.
Dans le processus de livraison continue, nous n’avons que deux valeurs qui changent régulièrement : le nombre de répliques et l'étiquette de l'image (version). D'autres valeurs, plus stables, sont modifiées manuellement, et cela est assez compliqué. De plus, une erreur dans le déploiement d’un chart généralisé peut entraîner de graves pannes, comme nous l'avons constaté par expérience.
Mise à jour de plusieurs fichiers de configuration
Lorsqu'un développeur ajoute une nouvelle application, il doit modifier plusieurs fichiers : la déclaration de l'application, la liste des secrets, l'ajout de l'application en tant que dépendance si elle est incluse dans le chart généralisé.
Les autorisations Jenkins sont trop étendues dans Vault
Nous avons actuellement un , qui lit tous les secrets de Vault.
Le processus de rollback n'est pas automatisé
Pour le rollback, il faut exécuter une commande sur plusieurs clusters, ce qui est sujet aux erreurs. Nous effectuons cette opération manuellement pour garantir que le bon identifiant de version est spécifié.
Nous nous orientons vers GitOps
Notre objectif
Nous souhaitons ramener le chart dans le dépôt de l'application qu'il déploie.
Le processus de travail sera le même que pour le développement. Par exemple, lorsque la branche est envoyée vers la master, le déploiement se déclenchera automatiquement. La principale différence entre cette approche et le processus de travail actuel est que tout sera géré dans git (l'application elle-même et la manière dont elle est déployée dans Kubernetes).
Il y a plusieurs avantages :
- C'est beaucoup plus clair pour le développeur. Il est plus facile d'apprendre à appliquer des modifications dans le chart local.
- La définition du déploiement du service peut être indiquée au même endroit que le code du service.
- Gestion de la suppression des charts généralisés. Le service aura sa propre version Helm. Cela permettra de gérer le cycle de vie de l'application (rollback, mise à jour) à un niveau très fin, sans affecter les autres services.
- Les avantages de git pour la gestion des charts : annulation des modifications, journal d'audit, etc. Si un changement de chart doit être annulé, cela peut être fait à l'aide de git. Le déploiement se déclenche automatiquement.
- On peut envisager d'améliorer le processus de développement avec des outils comme Skaffold, avec lesquels les développeurs peuvent tester des modifications dans un contexte proche de la production.
Migration en deux étapes
Nos développeurs utilisent ce workflow depuis 2 ans, donc nous avons besoin d'une migration aussi fluide que possible. C'est pourquoi nous avons décidé d'ajouter une étape intermédiaire vers notre objectif.
La première étape est simple :
- Nous conservons une structure similaire pour la configuration du déploiement des applications, mais dans un seul objet nommé DailymotionRelease.
apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
name: "app1.ns1"
environment: "dev"
branch: "mybranch"
spec:
slack_channel: "#admin"
chart_name: "app1"
scaling:
- context: "dev-us-central1-0"
replicas:
- name: "hermes"
count: 2
- context: "dev-europe-west1-0"
replicas:
- name: "app1-deploy"
count: 2
secrets:
- secret_id: "app1"
contexts:
- name: "default"
vaultPath: "\/kv\/dev\/ns1\/app1\/test"
vaultKey: "password"
- name: "dev-europe-west1-0"
vaultPath: "\/kv\/dev\/ns1\/app1\/test"
vaultKey: "password"- 1 release par application (sans charts généralisés).
- Charts dans le référentiel git de l'application.
Nous avons parlé à tous les développeurs, donc le processus de migration a déjà commencé. La première étape est toujours contrôlée à l'aide de la plateforme CI. Je publierai bientôt un autre article sur la deuxième étape : comment nous avons évolué vers un workflow GitOps avec . Je parlerai de la façon dont nous avons tout configuré et des difficultés que nous avons rencontrées (plusieurs dépôts, secrets, etc.). Restez à l'écoute.
Ici, nous avons tenté de décrire nos progrès dans le workflow de déploiement des applications au cours des dernières années, ce qui nous a amené à réfléchir à l'approche GitOps. Nous n'avons pas encore atteint notre objectif et nous rendrons compte des résultats, mais nous sommes convaincus qu'il était juste d'avoir simplifié les choses et de les rapprocher des habitudes des développeurs.
Source : habr.com
