Implementer applikationer på tværs af flere Kubernetes-klynger med Helm

Sådan bruger Dailymotion Kubernetes: Application Deployment

Vi hos Dailymotion begyndte at bruge Kubernetes i produktionen for 3 år siden. Men det er sjovt at implementere applikationer på tværs af flere klynger, så i løbet af de sidste par år har vi forsøgt at forbedre vores værktøjer og arbejdsgange.

Hvor startede det

Her vil vi dække, hvordan vi implementerer vores applikationer på tværs af flere Kubernetes-klynger rundt om i verden.

For at implementere flere Kubernetes-objekter på én gang, bruger vi Helm, og alle vores diagrammer er gemt i ét git-lager. For at implementere en fuld applikationsstak fra flere tjenester bruger vi det såkaldte summary chart. I det væsentlige er dette et diagram, der erklærer afhængigheder og giver dig mulighed for at initialisere API'en og dens tjenester med én kommando.

Vi skrev også et lille Python-script oven på Helm for at udføre kontrol, oprette diagrammer, tilføje hemmeligheder og implementere applikationer. Alle disse opgaver udføres på en central CI-platform ved hjælp af et docker-image.

Lad os komme til sagen.

Bemærk. Mens du læser dette, er den første udgivelseskandidat til Helm 3 allerede blevet annonceret. Hovedversionen indeholder en lang række forbedringer for at løse nogle af de problemer, vi tidligere har stødt på.

Workflow for diagramudvikling

Vi bruger forgrening til applikationer, og vi besluttede at anvende den samme tilgang til diagrammer.

  • Afdeling dev bruges til at lave diagrammer, der vil blive testet på udviklingsklynger.
  • Når en pull-anmodning indsendes til Master, er de tjekket i iscenesættelse.
  • Til sidst opretter vi en pull-anmodning for at forpligte ændringerne til filialen prod og anvende dem i produktionen.

Hvert miljø har sit eget private depot, der gemmer vores diagrammer, og vi bruger Kortmuseum med meget nyttige API'er. På denne måde sikrer vi streng isolation mellem miljøer og test af diagrammer i den virkelige verden, før de bruges i produktionen.

Kortlagre i forskellige miljøer

Det er værd at bemærke, at når udviklere pusher en dev-gren, bliver en version af deres diagram automatisk skubbet til dev Chartmuseum. Således bruger alle udviklere det samme dev-lager, og du skal nøje specificere din version af diagrammet for ikke at bruge andres ændringer ved et uheld.

Desuden validerer vores lille Python-script Kubernetes-objekter mod Kubernetes OpenAPI-specifikationer ved hjælp af Kubeval, før de offentliggøres på Chartmusem.

Generel beskrivelse af diagramudviklingens arbejdsgang

  1. Opsætning af pipeline opgaver efter specifikation gazr.io til kvalitetskontrol (fnug, enhedstest).
  2. Skub et docker-billede med Python-værktøjer, der implementerer vores applikationer.
  3. Opsætning af miljøet efter filialnavn.
  4. Validering af Kubernetes yaml-filer ved hjælp af Kubeval.
  5. Øg automatisk versionen af ​​et diagram og dets overordnede diagrammer (diagrammer, der afhænger af det diagram, der ændres).
  6. Indsende et søkort til et søkortmuseum, der matcher dets miljø

Håndtering af forskelle på tværs af klynger

Sammenslutning af Klynger

Der var engang, hvor vi brugte sammenslutning af Kubernetes-klynger, hvor Kubernetes-objekter kunne erklæres fra et enkelt API-slutpunkt. Men der opstod problemer. For eksempel kunne nogle Kubernetes-objekter ikke oprettes i føderationens slutpunkt, hvilket gør det vanskeligt at vedligeholde fødererede objekter og andre objekter for individuelle klynger.

For at løse problemet begyndte vi at administrere klyngerne uafhængigt, hvilket i høj grad forenklede processen (vi brugte den første version af føderation; noget kunne have ændret sig i den anden).

Geo-distribueret platform

Vores platform er i øjeblikket fordelt på tværs af 6 regioner - 3 lokalt og 3 i skyen.


Distribueret implementering

Globale Helm-værdier

4 globale Helm-værdier giver dig mulighed for at identificere forskelle mellem klynger. Alle vores diagrammer har standard minimumværdier.

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

Globale værdier

Disse værdier hjælper med at definere konteksten for vores applikationer og bruges til forskellige formål: overvågning, sporing, logning, foretage eksterne opkald, skalering osv.

  • "cloud": Vi har en hybrid Kubernetes-platform. For eksempel er vores API implementeret i GCP-zoner og i vores datacentre.
  • "env": Nogle værdier kan ændre sig for ikke-produktionsmiljøer. For eksempel ressourcedefinitioner og autoskaleringskonfigurationer.
  • "region": Disse oplysninger hjælper med at bestemme placeringen af ​​klyngen og kan bruges til at bestemme nærliggende endepunkter for eksterne tjenester.
  • "clusterName": hvis og hvornår vi ønsker at definere en værdi for en individuel klynge.

Her er et specifikt eksempel:

{{/* 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 -}}

Hjelm skabelon eksempel

Denne logik er defineret i en hjælperskabelon for at undgå rod i Kubernetes YAML.

Ansøgningsmeddelelse

Vores implementeringsværktøjer er baseret på flere YAML-filer. Nedenfor er et eksempel på, hvordan vi erklærer en tjeneste og dens skaleringstopologi (antal replikaer) i en klynge.

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

Service Definition

Dette er en oversigt over alle de trin, der definerer vores implementeringsarbejdsgang. Det sidste trin implementerer applikationen til flere arbejdsklynger samtidigt.


Jenkins implementeringstrin

Hvad med hemmeligheder?

Med hensyn til sikkerhed sporer vi alle hemmeligheder fra forskellige steder og gemmer dem i en unik boks Vault i Paris.

Vores implementeringsværktøjer udtrækker hemmelige værdier fra Vault, og indsæt dem i Helm, når implementeringstidspunktet kommer.

For at gøre dette definerede vi en kortlægning mellem hemmelighederne i Vault og de hemmeligheder, som vores applikationer har brug for:

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"

  • Vi har defineret generelle regler, der skal følges, når du optager hemmeligheder i Vault.
  • Hvis hemmeligheden gælder til en bestemt kontekst eller klynge, skal du tilføje en specifik post. (Her har kontekstcluster1 sin egen værdi for det hemmelige stack-app1-adgangskode).
  • Ellers bruges værdien по умолчанию.
  • For hvert punkt på denne liste i Kubernetes hemmelighed et nøgleværdi-par indsættes. Derfor er den hemmelige skabelon i vores diagrammer meget enkel.

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

Problemer og begrænsninger

Arbejder med flere depoter

Nu adskiller vi udviklingen af ​​diagrammer og applikationer. Dette betyder, at udviklere skal arbejde i to git-repositories: et til applikationen og et til at definere dens udrulning til Kubernetes. 2 git repositories betyder 2 arbejdsgange, og det er nemt for en nybegynder at blive forvirret.

Det er besværligt at administrere generaliserede diagrammer

Som vi allerede har sagt, er generiske diagrammer meget nyttige til at identificere afhængigheder og hurtigt implementere flere applikationer. Men vi bruger --reuse-valuesfor at undgå at passere alle værdierne, hver gang vi implementerer en applikation, der er en del af dette generaliserede diagram.

I en kontinuerlig leveringsarbejdsgang har vi kun to værdier, der ændres regelmæssigt: antallet af replikaer og billedtagget (version). Andre, mere stabile værdier ændres manuelt, og det er ret svært. Desuden kan en fejl ved at implementere et generaliseret diagram føre til alvorlige fejl, som vi har set fra vores egen erfaring.

Opdatering af flere konfigurationsfiler

Når en udvikler tilføjer en ny applikation, skal han ændre flere filer: applikationserklæringen, listen over hemmeligheder, tilføjelse af applikationen som en afhængighed, hvis den er inkluderet i det generelle diagram.

Jenkins tilladelser er for udvidede i Vault

Nu har vi en AppRole, som læser alle hemmelighederne fra Vault.

Tilbageføringsprocessen er ikke automatiseret

For at rulle tilbage skal du køre kommandoen på flere klynger, og dette er fyldt med fejl. Vi udfører denne handling manuelt for at sikre, at det korrekte versions-id er angivet.

Vi bevæger os mod GitOps

Vores mål

Vi ønsker at returnere diagrammet til arkivet for det program, det implementerer.

Arbejdsgangen vil være den samme som for udvikling. For eksempel, når en gren skubbes til master, udløses implementeringen automatisk. Den største forskel mellem denne tilgang og den nuværende arbejdsgang ville være det alt vil blive administreret i git (selve applikationen og den måde, den er implementeret i Kubernetes).

Der er flere fordele:

  • Meget klarere for udvikleren. Det er nemmere at lære, hvordan man anvender ændringer i et lokalt diagram.
  • Serviceimplementeringsdefinitionen kan specificeres samme sted som koden service.
  • Håndtering af fjernelse af generaliserede diagrammer. Tjenesten vil have sin egen Helm-udgivelse. Dette giver dig mulighed for at administrere applikationens livscyklus (tilbageføring, opgradering) på det mindste niveau, for ikke at påvirke andre tjenester.
  • Fordele ved git til diagramstyring: fortryd ændringer, revisionslog osv. Hvis du har brug for at fortryde en ændring af et diagram, kan du gøre dette ved hjælp af git. Implementeringen starter automatisk.
  • Du kan overveje at forbedre din udviklingsarbejdsgang med værktøjer som f.eks Skaffold, hvormed udviklere kan teste ændringer i en sammenhæng tæt på produktion.

To-trins migration

Vores udviklere har brugt denne arbejdsgang i 2 år nu, så vi ønsker, at migreringen skal være så smertefri som muligt. Derfor besluttede vi at tilføje et mellemtrin på vejen mod målet.
Den første fase er enkel:

  • Vi beholder en lignende struktur til opsætning af applikationsimplementering, men i et enkelt objekt kaldet 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 udgivelse pr. applikation (uden generaliserede diagrammer).
  • Diagrammer i applikationens git-lager.

Vi har talt med alle udviklerne, så migreringsprocessen er allerede begyndt. Det første trin styres stadig ved hjælp af CI-platformen. Jeg vil snart skrive endnu et indlæg om fase to: hvordan vi flyttede til en GitOps workflow med Flux. Jeg vil fortælle dig, hvordan vi sætter alt op, og hvilke vanskeligheder vi stødte på (flere depoter, hemmeligheder osv.). Følg nyhederne.

Her har vi forsøgt at beskrive vores fremskridt i applikationsimplementerings-workflowet over de seneste år, hvilket førte til tanker om GitOps-tilgangen. Vi har endnu ikke nået målet og vil rapportere om resultaterne, men nu er vi overbeviste om, at vi gjorde det rigtige, da vi besluttede at forenkle alt og bringe det tættere på udviklernes vaner.

Kilde: www.habr.com

Tilføj en kommentar