Implementacija aplikacija na više Kubernetes klastera pomoću Helma

Kako Dailymotion koristi Kubernetes: implementacija aplikacija

U Dailymotionu smo počeli koristiti Kubernetes u produkciji prije 3 godine. No, implementacija aplikacija na više klastera predstavlja izazov, pa smo u posljednjih nekoliko godina nastojali poboljšati naše alate i radne procese.

Kako je sve počelo

Ovdje ćemo objasniti kako implementiramo naše aplikacije na više klastera Kubernetes širom svijeta.

Za implementaciju više Kubernetes objekata odjednom koristimo Helm, a svi naši chartovi pohranjuju se u jednom git repozitoriju. Da bismo implementirali puni softverski paket iz više usluga, koristimo takozvani sveobuhvatni chart. U suštini, to je chart koji deklarira ovisnosti i omogućava inicijalizaciju API-ja i njegovih usluga jednim naredbom.

Također smo napisali mali Python skript preko Helma kako bismo obavljali provjere, kreirali chartove, dodavali tajne i implementirali aplikacije. Sve te zadatke obavljamo na centralnoj CI platformi koristeći docker sliku.

Pređimo na stvar.

Napomena. Kada ovo čitate, prvi kandidat za izdanja Helma 3 je već objavljen. Glavna verzija sadrži čitav niz poboljšanja koja su osmišljena da riješe neke probleme s kojima smo se suočavali u prošlosti.

Radni proces razvoja chartova

Za aplikacije koristimo grane, i ovaj pristup smo odlučili primijeniti i na chartove.

  • Grana dev se koristi za izradu chartova koji će se testirati na razvojnim klasterima.
  • Kada se pull request pošalje u master, oni se provjeravaju na stagingu.
  • Na kraju, kreiramo pull request kako bismo prenijeli promjene u granu prod i primijenili ih u produkciji.

Svako okruženje ima svoj privatni repozitorij koji pohranjuje naše chartove, a koristimo Chartmuseum s vrlo korisnim API-jima. Tako jamčimo strogu izolaciju između okruženja i provjeru chartova u stvarnim uvjetima prije nego što ih upotrijebimo u produkciji.

Repozitoriji chartova u različitim okruženjima

Važno je napomenuti da kada developeri šalju granu dev, verzija njihovog charta automatski se šalje u dev Chartmuseum. Tako svi developeri koriste jedan dev repozitorij i treba pažljivo odrediti svoju verziju charta kako ne bi slučajno koristili nečije izmjene.

Osim toga, naš mali Python skript provjerava Kubernetes objekte prema Kubernetes OpenAPI specifikacijama koristeći Kubeval, prije nego što ih objavimo u Chartmusu.

Opći opis radnog toka razvoja charta

  1. Konfiguracija zadataka cjevovoda prema specifikacijama gazr.io za kontrolu kvalitete (lint, unit-test).
  2. Slanje docker slike s alatima Python koji raspoređuju naše aplikacije.
  3. Konfiguracija okruženja prema nazivu grane.
  4. Provjera yaml datoteka Kubernetes pomoću Kubevala.
  5. Automatsko povećanje verzije charta i njegovih roditeljskih chartova (chartova koji ovise o promjenjivom chartu).
  6. Slanje charta u Chartmuseum, koji odgovara njegovom okruženju

Upravljanje razlikama u klasterima

Federacija klastera

Bilo je vremena kada smo koristili federaciju klastera Kubernetes, gdje se mogli objavljivati Kubernetes objekti s jedne API točke. Ali su se pojavili problemi. Na primjer, neki Kubernetes objekti nisu se mogli kreirati na federacijskoj točki, pa je bilo teško održavati objedinjene objekte i druge objekte za pojedinačne klastere.

Da bismo riješili problem, počeli smo upravljati klasterima neovisno, što je značajno pojednostavilo proces (koristili smo prvu verziju federacije; u drugoj je nešto moglo biti promijenjeno).

Geografski distribuirana platforma

Sada je naša platforma distribuirana u 6 regija — 3 lokalno i 3 u oblaku.


Distribuirano raspoređivanje

Globalne Helm vrijednosti

4 globalne Helm vrijednosti omogućuju definiranje razlika među klasterima. Za sve naše chartove postoje minimalne pretpostavljene vrijednosti.

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

Globalne vrijednosti

Te vrijednosti pomažu u određivanju konteksta za naše aplikacije i koriste se za različite zadatke: praćenje, traganje, evidentiranje, izvršavanje vanjskih poziva, skaliranje itd.

  • «cloud»: imamo hibridnu platformu Kubernetes. Na primjer, naš API se raspoređuje u GCP zonama i u našim podatkovnim centrima.
  • «env»: neke vrijednosti mogu se mijenjati za neproduktivna okruženja. Na primjer, definicije resursa i konfiguracije automatskog skaliranja.
  • «region»: ove informacije pomažu u određivanju lokacije klastera i mogu se koristiti za određivanje najbližih krajnjih točaka za vanjske usluge.
  • «clusterName»: ako i kada želimo odrediti vrijednost za pojedinačni klaster.

Evo konkretnog primjera:

{{
/* Vraća Horizontal Pod Autoscaler replike za 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 -}}

Primjer Helm predloška

Ova logika je definirana u pomoćnom predlošku kako bi se izbjeglo preopterećenje Kubernetes YAML.

Objava aplikacije

Naši alati za implementaciju temelje se na nekoliko YAML datoteka. Ispod je primjer kako objavljujemo uslugu i njenu topologiju skaliranja (broj replika) u klasteru.

releases:
  - foo.world

foo.world:                # Ime izdanja
  services:               # Lista dailymotion aplikacija/projekata
    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: 3

Definicija usluge

Ovo je skica svih koraka koji definiraju naš radni proces implementacije. Zadnji korak implementira aplikaciju istovremeno na nekoliko radnih klastera.


Koraci implementacije u Jenkinsu

A tajne?

Što se tiče sigurnosti, pratimo sve tajne iz različitih izvora i čuvamo ih u jedinstvenom skladištu Vault u Parizu.

Naši alati za implementaciju izvlače vrijednosti tajni iz Vault-a i, kada dođe vrijeme za implementaciju, umetnu ih u Helm.

Za to smo definirali mapiranje između tajni u Vault-u i tajni potrebnih našim aplikacijama:

tajne:                                                                                                                                                                                                        
     - secret_id: "stack1-app1-password"                                                                                                                                                                                  
       konteksti:                                                                                                                                                                                                   
         - ime: "default"                                                                                                                                                                                         
           vaultPath: "\/kv\/dev\/stack1\/app1\/test"                                                                                                                                                               
           vaultKey: "password"                                                                                                                                                                                    
         - ime: "cluster1"                                                                                                                                                                           
           vaultPath: "\/kv\/dev\/stack1\/app1\/test"                                                                                                                                                               
           vaultKey: "password"

  • Odredili smo opća pravila koja je potrebno slijediti prilikom zapisivanja tajni u Vault.
  • Ako tajna pripada određenom kontekstu ili klasteru, potrebno je dodati specifičnu stavku. (U ovom slučaju, kontekst cluster1 ima vlastitu vrijednost za tajnu stack-app1-password).
  • U suprotnom se koristi vrijednost po defaultu.
  • Za svaku stavku na ovom popisu u tajni Kubernetes ubacuje se par ključ-vrijednost. Stoga je predložak tajne u našim chartovima vrlo jednostavan.

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

Problemi i ograničenja

Rad s više repozitorija

Trenutno odvajamo razvoj chartova i aplikacija. To znači da programeri moraju raditi u dva git repozitorija: jedan za aplikaciju, a drugi za definiciju njenog implementiranja u Kubernetes. 2 git repozitorija predstavljaju 2 radna procesa, i početnicima lako može biti zbunjujuće.

Upravljanje generičkim chartovima je zamoran posao

Kao što smo već spomenuli, generički chartovi su vrlo korisni za definiranje ovisnosti i brzo implementiranje više aplikacija. No, koristimo --reuse-values, kako bismo izbjegli prijenos svih vrijednosti svaki put kada implementiramo aplikaciju koja ulazi u ovaj generički chart.

U procesu neprekidne isporuke imamo samo dvije vrijednosti koje se redovito mijenjaju: broj replika i oznaku slike (verzija). Ostale, stabilnije vrijednosti, mijenjaju se ručno, što je prilično izazovno. Osim toga, jedna greška u rasporedu generičkog charta može dovesti do ozbiljnih kvarova, što smo sami iskusili.

Ažuriranje više konfiguracijskih datoteka

Kada programer dodaje novu aplikaciju, mora promijeniti nekoliko datoteka: deklaraciju aplikacije, popis tajni, dodavanje aplikacije u ovisnosti, ako je uključena u generički chart.

Dozvole Jenkins-a su preširoke u Vaultu

Trenutno imamo jedan AppRole, koji čita sve tajne iz Vaulta.

Postupak vraćanja nije automatiziran

Za vraćanje je potrebno izvršiti naredbu na nekoliko klastera, što može dovesti do grešaka. Ovu operaciju radimo ručno kako bismo osigurali da zadajemo ispravni identifikator verzije.

Krećemo se prema GitOps-u

Naš cilj

Želimo vratiti chart u repozitorij aplikacije koju on raspoređuje.

Radni proces će biti isti kao za razvoj. Na primjer, kada se grana pošalje na master, raspored će se pokretati automatski. Glavna razlika između ovakvog pristupa i trenutnog radnog procesa će biti u tome što sve će se upravljati u gitu (sama aplikacija i način njenog raspoređivanja u Kubernetes-u).

Postoji nekoliko prednosti:

  • Mnogo jasnije za programera. Lakše je naučiti kako primijeniti promjene u lokalnom chartu.
  • Definiciju rasporeda usluge može se odrediti isto tako, gdje je kod usluge.
  • Upravljanje brisanjem generičkih chartova. Usluga će imati svoju Helm verziju. To će omogućiti upravljanje životnim ciklusom aplikacije (vraćanje, nadogradnja) na najsitnijem nivou, kako ne bi utjecalo na druge usluge.
  • Prednosti gita za upravljanje chartovima: opoziv promjena, dnevnik revizije itd. Ako je potrebno opozvati promjenu charta, to se može učiniti pomoću gita. Raspored se pokreće automatski.
  • Može se razmisliti o unapređenju radnog procesa razvoja pomoću alata kao što su Skaffold, s kojima programeri mogu testirati promjene u kontekstu bliskom produkciji.

Dvofazna migracija

Naši developeri koriste ovaj radni proces već 2 godine, stoga nam je potrebna što bezbolnija migracija. Stoga smo odlučili dodati intermedijarnu fazu na putu do cilja.
Prva faza je jednostavna:

  • Zadržavamo sličnu strukturu za konfiguraciju implementacije aplikacija, ali u jednom objektu pod nazivom 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 izdanje po aplikaciji (bez generaliziranih chartova).
  • Chartovi u git repozitoriju aplikacije.

Razgovarali smo sa svim developerima, tako da je proces migracije već počeo. Prva faza se još uvijek kontrolira pomoću CI platforme. Uskoro ću napisati još jedan post o drugoj fazi: kako smo prešli na GitOps radni proces s Flux. Ispričat ću kako smo sve postavili i s kakvim smo se izazovima susreli (nekoliko repozitorija, tajne itd.). Pratite vijesti.

Ovdje smo pokušali opisati naš napredak u radnom procesu implementacije aplikacija tijekom posljednjih godina, što je dovelo do razmišljanja o GitOps pristupu. Još nismo postigli cilj i redovito ćemo obavještavati o rezultatima, ali sada smo uvjereni da smo ispravno postupili kada smo odlučili sve pojednostaviti i približiti navikama developera.

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster