Distribuisci applicazioni su più cluster Kubernetes con Helm

Come Dailymotion utilizza Kubernetes: distribuzione delle applicazioni

Noi di Dailymotion abbiamo iniziato a utilizzare Kubernetes in produzione 3 anni fa. Ma distribuire applicazioni su più cluster è divertente, quindi negli ultimi anni abbiamo cercato di migliorare i nostri strumenti e flussi di lavoro.

Da dove è iniziato?

Qui tratteremo come distribuiamo le nostre applicazioni su più cluster Kubernetes in tutto il mondo.

Per distribuire più oggetti Kubernetes contemporaneamente, utilizziamo Cascoe tutti i nostri grafici sono archiviati in un repository git. Per distribuire uno stack completo di applicazioni da diversi servizi, utilizziamo la cosiddetta tabella di riepilogo. Essenzialmente, si tratta di un grafico che dichiara le dipendenze e consente di inizializzare l'API e i suoi servizi con un comando.

Abbiamo anche scritto un piccolo script Python su Helm per eseguire controlli, creare grafici, aggiungere segreti e distribuire applicazioni. Tutte queste attività vengono eseguite su una piattaforma CI centrale utilizzando un'immagine docker.

Andiamo al punto.

Nota. Mentre leggi questo, la prima release candidate per Helm 3 è già stata annunciata. La versione principale contiene tutta una serie di miglioramenti per risolvere alcuni dei problemi riscontrati in passato.

Flusso di lavoro di sviluppo del grafico

Utilizziamo la ramificazione per le applicazioni e abbiamo deciso di applicare lo stesso approccio ai grafici.

  • Ramo dev utilizzato per creare grafici che verranno testati sui cluster di sviluppo.
  • Quando viene inviata una richiesta pull a Mastercard, vengono controllati in stadiazione.
  • Infine, creiamo una richiesta pull per confermare le modifiche al ramo pungolo e applicarli in produzione.

Ogni ambiente ha il proprio repository privato che memorizza i nostri grafici e noi li utilizziamo Museo delle carte con API molto utili. In questo modo garantiamo un rigoroso isolamento tra gli ambienti e il test dei grafici nel mondo reale prima di utilizzarli in produzione.

Repository di grafici in diversi ambienti

Vale la pena notare che quando gli sviluppatori inviano un ramo dev, una versione del loro grafico viene automaticamente inviata al dev Chartmuseum. Pertanto, tutti gli sviluppatori utilizzano lo stesso repository di sviluppo ed è necessario specificare attentamente la propria versione del grafico in modo da non utilizzare accidentalmente le modifiche di qualcun altro.

Inoltre, il nostro piccolo script Python convalida gli oggetti Kubernetes rispetto alle specifiche Kubernetes OpenAPI utilizzando Kubeval, prima di pubblicarli su Chartmusem.

Descrizione generale del flusso di lavoro di sviluppo del grafico

  1. Impostazione delle attività della pipeline in base alle specifiche gazr.io per il controllo qualità (lanugine, test unitario).
  2. Invio di un'immagine docker con strumenti Python che distribuiscono le nostre applicazioni.
  3. Configurazione dell'ambiente in base al nome del ramo.
  4. Convalida dei file yaml Kubernetes utilizzando Kubeval.
  5. Aumenta automaticamente la versione di un grafico e dei relativi grafici principali (grafici che dipendono dalla modifica del grafico).
  6. Invio di una carta a un Chartmuseum che corrisponde al suo ambiente

Gestire le differenze tra i cluster

Federazione dei cluster

C'è stato un tempo in cui usavamo federazione di cluster Kubernetes, dove gli oggetti Kubernetes potrebbero essere dichiarati da un singolo endpoint API. Ma sono sorti dei problemi. Ad esempio, non è stato possibile creare alcuni oggetti Kubernetes nell'endpoint della federazione, rendendo difficile la gestione degli oggetti federati e di altri oggetti per i singoli cluster.

Per risolvere il problema abbiamo iniziato a gestire i cluster in modo indipendente, il che ha semplificato molto il processo (abbiamo utilizzato la prima versione della federazione, nella seconda potrebbe essere cambiato qualcosa).

Piattaforma geograficamente distribuita

La nostra piattaforma è attualmente distribuita in 6 regioni: 3 localmente e 3 nel cloud.


Distribuzione distribuita

Valori globali del timone

4 valori Helm globali consentono di identificare le differenze tra i cluster. Tutti i nostri grafici hanno valori minimi predefiniti.

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

Valori globali

Questi valori aiutano a definire il contesto per le nostre applicazioni e vengono utilizzati per vari scopi: monitoraggio, tracciamento, registrazione, esecuzione di chiamate esterne, ridimensionamento, ecc.

  • "cloud": disponiamo di una piattaforma Kubernetes ibrida. Ad esempio, la nostra API viene distribuita nelle zone GCP e nei nostri data center.
  • "env": alcuni valori potrebbero cambiare per ambienti non di produzione. Ad esempio, definizioni di risorse e configurazioni di scalabilità automatica.
  • "region": queste informazioni aiutano a determinare la posizione del cluster e possono essere utilizzate per determinare gli endpoint vicini per servizi esterni.
  • "clusterName": se e quando vogliamo definire un valore per un singolo cluster.

Ecco un esempio specifico:

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

Esempio di modello di timone

Questa logica è definita in un modello di supporto per evitare di ingombrare Kubernetes YAML.

Annuncio della domanda

I nostri strumenti di distribuzione si basano su più file YAML. Di seguito è riportato un esempio di come dichiariamo un servizio e la sua topologia di scalabilità (numero di repliche) in 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

Definizione del servizio

Questa è una descrizione di tutti i passaggi che definiscono il nostro flusso di lavoro di distribuzione. L'ultimo passaggio distribuisce l'applicazione su più cluster di lavoro contemporaneamente.


Passaggi di distribuzione Jenkins

E i segreti?

Per quanto riguarda la sicurezza, tracciamo tutti i segreti da luoghi diversi e li conserviamo in un caveau unico Volta a Parigi.

I nostri strumenti di distribuzione estraggono i valori segreti da Vault e, quando arriva il momento della distribuzione, li inseriscono in Helm.

Per fare ciò, abbiamo definito una mappatura tra i segreti di Vault e i segreti di cui hanno bisogno le nostre applicazioni:

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"

  • Abbiamo definito le regole generali da seguire durante la registrazione dei segreti in Vault.
  • Se il segreto si applica a un contesto o cluster specifico, è necessario aggiungere una voce specifica. (Qui il contesto cluster1 ha il proprio valore per il segreto stack-app1-password).
  • Altrimenti viene utilizzato il valore per impostazione predefinita.
  • Per ogni elemento di questo elenco in Segreto di Kubernetes viene inserita una coppia chiave-valore. Pertanto, il modello segreto nei nostri grafici è molto semplice.

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

Sfide e limitazioni

Lavorare con più repository

Ora separiamo lo sviluppo di grafici e applicazioni. Ciò significa che gli sviluppatori devono lavorare in due repository git: uno per l'applicazione e uno per definirne la distribuzione su Kubernetes. 2 repository git significano 2 flussi di lavoro ed è facile per un principiante confondersi.

Gestire i grafici generalizzati è una seccatura

Come abbiamo già detto, i grafici generici sono molto utili per identificare le dipendenze e distribuire rapidamente più applicazioni. Ma usiamo --reuse-valuesper evitare di passare tutti i valori ogni volta che distribuiamo un'applicazione che fa parte di questo grafico generalizzato.

In un flusso di lavoro di consegna continua, abbiamo solo due valori che cambiano regolarmente: il numero di repliche e il tag immagine (versione). Altri valori più stabili vengono modificati manualmente e questo è piuttosto difficile. Inoltre, un errore nell'implementazione di un grafico generalizzato può portare a gravi fallimenti, come abbiamo visto dalla nostra esperienza.

Aggiornamento di più file di configurazione

Quando uno sviluppatore aggiunge una nuova applicazione, deve modificare diversi file: la dichiarazione dell'applicazione, l'elenco dei segreti, aggiungendo l'applicazione come dipendenza se è inclusa nella tabella generalizzata.

Le autorizzazioni Jenkins sono troppo estese in Vault

Ora ne abbiamo uno AppRole, che legge tutti i segreti del Vault.

Il processo di ripristino non è automatizzato

Per eseguire il rollback, è necessario eseguire il comando su diversi cluster e questo è pieno di errori. Eseguiamo questa operazione manualmente per garantire che sia specificato l'ID di versione corretto.

Stiamo andando verso GitOps

Il nostro obbiettivo

Vogliamo restituire il grafico al repository dell'applicazione che distribuisce.

Il flusso di lavoro sarà lo stesso dello sviluppo. Ad esempio, quando un ramo viene inviato al master, la distribuzione verrà attivata automaticamente. La differenza principale tra questo approccio e il flusso di lavoro attuale sarebbe questa tutto sarà gestito in git (l'applicazione stessa e il modo in cui viene distribuita in Kubernetes).

Ci sono diversi vantaggi:

  • più più chiaro per lo sviluppatore. È più semplice imparare come applicare le modifiche in un grafico locale.
  • È possibile specificare la definizione di distribuzione del servizio stesso posto del codice servizio.
  • Gestire la rimozione dei grafici generalizzati. Il servizio avrà una propria versione Helm. Ciò ti consentirà di gestire il ciclo di vita dell'applicazione (rollback, aggiornamento) al livello più piccolo, in modo da non influenzare altri servizi.
  • Vantaggi di Git per la gestione dei grafici: annullamento delle modifiche, registro di controllo, ecc. Se è necessario annullare una modifica a un grafico, è possibile farlo utilizzando git. La distribuzione viene avviata automaticamente.
  • Potresti prendere in considerazione l'idea di migliorare il flusso di lavoro di sviluppo con strumenti come Scaffold, con cui gli sviluppatori possono testare le modifiche in un contesto vicino alla produzione.

Migrazione in due passaggi

I nostri sviluppatori utilizzano questo flusso di lavoro ormai da 2 anni, quindi vogliamo che la migrazione sia il più semplice possibile. Pertanto, abbiamo deciso di aggiungere un passaggio intermedio nel percorso verso l'obiettivo.
La prima fase è semplice:

  • Manteniamo una struttura simile per impostare la distribuzione dell'applicazione, ma in un unico oggetto chiamato 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 liberatoria per domanda (senza schemi generalizzati).
  • Grafici nel repository git dell'applicazione.

Abbiamo parlato con tutti gli sviluppatori, quindi il processo di migrazione è già iniziato. La prima fase è ancora controllata utilizzando la piattaforma CI. Scriverò presto un altro post sulla fase due: come siamo passati a un flusso di lavoro GitOps con Flusso. Ti dirò come abbiamo impostato il tutto e quali difficoltà abbiamo incontrato (repository multipli, segreti, ecc.). Segui le notizie.

Qui abbiamo cercato di descrivere i nostri progressi nel flusso di lavoro di distribuzione delle applicazioni negli ultimi anni, che ci hanno portato a riflettere sull'approccio GitOps. Non abbiamo ancora raggiunto l'obiettivo e riporteremo i risultati, ma ora siamo convinti di aver fatto la cosa giusta quando abbiamo deciso di semplificare tutto e avvicinarlo alle abitudini degli sviluppatori.

Fonte: habr.com

Aggiungi un commento