Implementarea aplicațiilor în mai multe clustere Kubernetes cu Helm

Cum folosește Dailymotion Kubernetes: Implementarea aplicației

Noi, cei de la Dailymotion, am început să folosim Kubernetes în producție acum 3 ani. Dar implementarea aplicațiilor în mai multe clustere este distractivă, așa că în ultimii ani am încercat să ne îmbunătățim instrumentele și fluxurile de lucru.

De unde a început

Aici vom acoperi modul în care implementăm aplicațiile noastre în mai multe clustere Kubernetes din întreaga lume.

Pentru a implementa mai multe obiecte Kubernetes simultan, folosim Cârmă, iar toate diagramele noastre sunt stocate într-un singur depozit git. Pentru a implementa o stivă completă de aplicații din mai multe servicii, folosim așa-numita diagramă rezumat. În esență, aceasta este o diagramă care declară dependențe și vă permite să inițializați API-ul și serviciile sale cu o singură comandă.

De asemenea, am scris un mic script Python deasupra lui Helm pentru a face verificări, a crea diagrame, a adăuga secrete și a implementa aplicații. Toate aceste sarcini sunt efectuate pe o platformă centrală CI folosind o imagine docker.

Să trecem la subiect.

Notă. Pe măsură ce citiți acest lucru, primul candidat pentru lansare pentru Helm 3 a fost deja anunțat. Versiunea principală conține o serie întreagă de îmbunătățiri pentru a rezolva unele dintre problemele pe care le-am întâlnit în trecut.

Flux de lucru pentru dezvoltarea graficelor

Folosim ramificarea pentru aplicații și am decis să aplicăm aceeași abordare diagramelor.

  • Ramura dev folosit pentru a crea diagrame care vor fi testate pe clustere de dezvoltare.
  • Când o cerere de extragere este trimisă către maestru, sunt verificate în staging.
  • În cele din urmă, creăm o cerere de extragere pentru a comite modificările în ramură prod și să le aplici în producție.

Fiecare mediu are propriul său depozit privat care stochează diagramele noastre și le folosim Chartmuseum cu API-uri foarte utile. În acest fel, asigurăm izolarea strictă între medii și testarea diagramelor în lumea reală înainte de a le folosi în producție.

Arhivele de diagrame în diferite medii

Este demn de remarcat faptul că atunci când dezvoltatorii împing o ramură către dev, o versiune a diagramei lor este trimisă automat în dev Chartmuseum. Astfel, toți dezvoltatorii folosesc același depozit de dezvoltare și trebuie să specificați cu atenție versiunea diagramei pentru a nu folosi accidental modificările altcuiva.

Mai mult, micul nostru script Python validează obiectele Kubernetes cu specificațiile Kubernetes OpenAPI folosind Kubeval, înainte de a le publica pe Chartmusem.

Descrierea generală a fluxului de lucru pentru dezvoltarea graficelor

  1. Configurarea sarcinilor pipeline conform specificațiilor gazr.io pentru controlul calității (scame, test unitar).
  2. Impingerea unei imagini docker cu instrumente Python care implementează aplicațiile noastre.
  3. Configurarea mediului după numele sucursalei.
  4. Validarea fișierelor Yaml Kubernetes folosind Kubeval.
  5. Creșteți automat versiunea unei diagrame și a diagramelor părinte ale acesteia (diagrame care depind de diagrama care este schimbată).
  6. Trimiterea unei diagrame la un Chartmuseum care se potrivește cu mediul său

Gestionarea diferențelor între clustere

Federația Clusterelor

A fost o vreme când am folosit federație de clustere Kubernetes, unde obiectele Kubernetes ar putea fi declarate de la un singur punct final API. Dar au apărut probleme. De exemplu, unele obiecte Kubernetes nu au putut fi create în punctul final de federație, ceea ce face dificilă întreținerea obiectelor federate și a altor obiecte pentru clustere individuale.

Pentru a rezolva problema, am început să gestionăm clusterele în mod independent, ceea ce a simplificat foarte mult procesul (am folosit prima versiune de federație; s-ar putea să se fi schimbat ceva în a doua).

Platformă geo-distribuită

Platforma noastră este distribuită în prezent în 6 regiuni - 3 local și 3 în cloud.


Implementare distribuită

Valorile globale Helm

Cele 4 valori globale Helm vă permit să identificați diferențele dintre clustere. Toate graficele noastre au valori minime implicite.

global:
  cloud: True
  env: staging
  region: us-central1
  clusterName: staging-us-central1

Valori globale

Aceste valori ajută la definirea contextului aplicațiilor noastre și sunt utilizate în diverse scopuri: monitorizare, urmărire, înregistrare în jurnal, efectuarea de apeluri externe, scalare etc.

  • „cloud”: avem o platformă hibridă Kubernetes. De exemplu, API-ul nostru este implementat în zonele GCP și în centrele noastre de date.
  • „env”: Unele valori se pot schimba pentru mediile care nu sunt de producție. De exemplu, definiții de resurse și configurații de autoscaling.
  • „regiune”: aceste informații ajută la determinarea locației clusterului și pot fi utilizate pentru a determina punctele finale din apropiere pentru servicii externe.
  • „clusterName”: dacă și când vrem să definim o valoare pentru un cluster individual.

Iată un exemplu concret:

{{/* Returns Horizontal Pod Autoscaler replicas for 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 -}}

Exemplu de șablon Helm

Această logică este definită într-un șablon de ajutor pentru a evita aglomerarea Kubernetes YAML.

Anunțul de aplicare

Instrumentele noastre de implementare se bazează pe mai multe fișiere YAML. Mai jos este un exemplu despre modul în care declarăm un serviciu și topologia lui de scalare (numărul de replici) într-un cluster.

releases:
  - foo.world

foo.world:                # Release name
  services:               # List of dailymotion's apps/projects
    foobar:
      chart_name: foo-foobar
      repo: [email protected]:dailymotion/foobar
      contexts:
        prod-europe-west1:
          deployments:
            - name: foo-bar-baz
              replicas: 18
            - name: another-deployment
              replicas: 3

Definiția serviciului

Aceasta este o schiță a tuturor pașilor care definesc fluxul nostru de lucru de implementare. Ultimul pas implementează aplicația în mai multe clustere de lucrători simultan.


Pașii de implementare Jenkins

Dar secrete?

În ceea ce privește securitatea, urmărim toate secretele din diferite locuri și le stocăm într-un seif unic Boltă în Paris.

Instrumentele noastre de implementare extrag valori secrete din Vault și, când vine timpul de implementare, le introduc în Helm.

Pentru a face acest lucru, am definit o mapare între secretele din Vault și secretele de care au nevoie aplicațiile noastre:

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"

  • Am definit reguli generale de urmat la înregistrarea secretelor în Vault.
  • Dacă secretul se aplică la un anumit context sau cluster, trebuie să adăugați o anumită intrare. (Aici, clusterul context1 are propria sa valoare pentru parola secretă stack-app1-parola).
  • În caz contrar, se utilizează valoarea implicit.
  • Pentru fiecare articol din această listă în Secretul Kubernetes este inserată o pereche cheie-valoare. Prin urmare, șablonul secret din diagramele noastre este foarte simplu.

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: Opaque

Probleme și limitări

Lucrul cu mai multe depozite

Acum separăm dezvoltarea diagramelor și a aplicațiilor. Aceasta înseamnă că dezvoltatorii trebuie să lucreze în două depozite git: unul pentru aplicație și unul pentru definirea implementării acesteia în Kubernetes. 2 depozite git înseamnă 2 fluxuri de lucru și este ușor pentru un începător să se încurce.

Gestionarea diagramelor generalizate este o bătaie de cap

După cum am spus deja, diagramele generice sunt foarte utile pentru identificarea dependențelor și implementarea rapidă a mai multor aplicații. Dar folosim --reuse-valuespentru a evita trecerea tuturor valorilor de fiecare dată când implementăm o aplicație care face parte din această diagramă generalizată.

Într-un flux de lucru de livrare continuă, avem doar două valori care se schimbă în mod regulat: numărul de replici și eticheta de imagine (versiune). Alte valori, mai stabile, sunt modificate manual, iar acest lucru este destul de dificil. Mai mult, o greșeală în implementarea unei diagrame generalizate poate duce la eșecuri grave, așa cum am văzut din propria noastră experiență.

Actualizarea mai multor fișiere de configurare

Când un dezvoltator adaugă o nouă aplicație, trebuie să schimbe mai multe fișiere: declarația aplicației, lista de secrete, adăugarea aplicației ca dependență dacă aceasta este inclusă în diagrama generalizată.

Permisiunile Jenkins sunt prea extinse în Vault

Acum avem unul AppRole, care citește toate secretele din Seif.

Procesul de rollback nu este automatizat

Pentru a derula înapoi, trebuie să rulați comanda pe mai multe clustere, iar acest lucru este plin de erori. Efectuăm această operație manual pentru a ne asigura că este specificat ID-ul corect al versiunii.

Ne îndreptăm către GitOps

Scopul nostru

Dorim să returnăm diagrama în depozitul aplicației pe care o implementează.

Fluxul de lucru va fi același ca pentru dezvoltare. De exemplu, atunci când o ramură este împinsă la master, implementarea va fi declanșată automat. Principala diferență între această abordare și fluxul de lucru actual ar fi aceea totul va fi gestionat în git (aplicația în sine și modul în care este implementată în Kubernetes).

Există mai multe avantaje:

  • Mult mai clar pentru dezvoltator. Este mai ușor să înveți cum să aplici modificările într-o diagramă locală.
  • Definiția de implementare a serviciului poate fi specificată chiar acolo unde este codul serviciu.
  • Gestionarea eliminării diagramelor generalizate. Serviciul va avea propria versiune Helm. Acest lucru vă va permite să gestionați ciclul de viață al aplicației (rollback, upgrade) la cel mai mic nivel, pentru a nu afecta alte servicii.
  • Beneficiile git pentru managementul diagramelor: anulați modificările, jurnalul de audit etc. Dacă trebuie să anulați o modificare a unei diagrame, puteți face acest lucru folosind git. Implementarea începe automat.
  • Ați putea lua în considerare îmbunătățirea fluxului de lucru de dezvoltare cu instrumente precum Scaffold, cu care dezvoltatorii pot testa modificările într-un context apropiat de producție.

Migrație în doi pași

Dezvoltatorii noștri folosesc acest flux de lucru de 2 ani, așa că dorim ca migrarea să fie cât mai nedureroasă posibil. Prin urmare, am decis să adăugăm un pas intermediar pe drumul către obiectiv.
Prima etapă este simplă:

  • Păstrăm o structură similară pentru configurarea implementării aplicației, dar într-un singur obiect numit 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 lansare per aplicație (fără diagrame generalizate).
  • Diagrame din depozitul git al aplicației.

Am vorbit cu toți dezvoltatorii, așa că procesul de migrare a început deja. Prima etapă este încă controlată folosind platforma CI. Voi scrie o altă postare în curând despre faza a doua: cum am trecut la un flux de lucru GitOps Flux. Vă voi spune cum am configurat totul și ce dificultăți am întâmpinat (mai multe depozite, secrete etc.). Urmăriți știrile.

Aici am încercat să descriem progresul nostru în fluxul de lucru de implementare a aplicațiilor în ultimii ani, ceea ce a condus la gânduri despre abordarea GitOps. Încă nu am atins obiectivul și vom raporta rezultatele, dar acum suntem convinși că am procedat corect atunci când am decis să simplificăm totul și să-l apropiem de obiceiurile dezvoltatorilor.

Sursa: www.habr.com

Adauga un comentariu