Come Dailymotion utilizza Kubernetes: distribuzione delle applicazioni
Noi di Dailymotion abbiamo iniziato a utilizzare Kubernetes in produzione 3 anni fa. Tuttavia, distribuire applicazioni su più cluster non è un compito semplice, perciò negli ultimi anni abbiamo cercato di migliorare i nostri strumenti e i nostri processi.
Da dove è iniziato
Qui spiegheremo come distribuiamo le nostre applicazioni su più cluster Kubernetes in tutto il mondo.
Per distribuire più oggetti Kubernetes contemporaneamente, utilizziamo , e tutti i nostri chart sono conservati in un unico repository git. Per distribuire l'intero stack dell'applicazione composto da più servizi, utilizziamo quello che chiamiamo un chart aggregatore. Fondamentalmente, si tratta di un chart che dichiara le dipendenze e consente di inizializzare l'API e i suoi servizi con un solo comando.
Inoltre, abbiamo scritto un piccolo script Python sopra Helm per effettuare controlli, creare chart, aggiungere segreti e distribuire applicazioni. Tutte queste operazioni vengono eseguite su una piattaforma CI centrale utilizzando un'immagine Docker.
Passiamo al sodo.
Nota. Quando leggi questo, la prima release candidate di Helm 3 è già stata annunciata. La versione principale contiene un insieme di miglioramenti progettati per affrontare alcuni problemi che abbiamo affrontato in passato.
Flusso di lavoro per lo sviluppo dei chart
Per le applicazioni utilizziamo il branching e abbiamo deciso di applicare lo stesso approccio ai chart.
- Ramo dev è utilizzato per creare i chart che verranno testati nei cluster di sviluppo.
- Quando la pull request viene inviata a master, vengono controllati in staging.
- Infine, creiamo una pull request per applicare le modifiche al ramo prod e implementarle in produzione.
Ogni ambiente ha il proprio repository privato che memorizza i nostri chart, e utilizziamo con API molto utili. In questo modo garantiamo un rigoroso isolamento tra gli ambienti e testiamo i chart in condizioni reali prima di utilizzarli in produzione.
Repository di chart in diversi ambienti
È importante notare che quando gli sviluppatori inviano un ramo dev, la versione del loro chart viene automaticamente inviata in dev Chartmuseum. In questo modo, tutti gli sviluppatori utilizzano un unico repository dev, e sarà necessario specificare attentamente la propria versione del chart per non utilizzare accidentalmente le modifiche di qualcun altro.
Inoltre, il nostro piccolo script Python verifica gli oggetti Kubernetes rispetto alle specifiche Kubernetes OpenAPI tramite , prima di pubblicarli in Chartmuseum.
Panoramica del flusso di lavoro di sviluppo del chart
- Configurazione delle attività della pipeline secondo le specifiche per il controllo qualità (lint, unit-test).
- Invio dell'immagine docker con strumenti Python che distribuiscono le nostre applicazioni.
- Impostazione dell'ambiente in base al nome del ramo.
- Verifica dei file yaml di Kubernetes utilizzando Kubeval.
- Incremento automatico della versione del chart e dei suoi chart parent (charts che dipendono dal chart modificabile).
- Invio del chart a Chartmuseum, che corrisponde al suo ambiente
Gestione delle differenze nei cluster
Federazione dei cluster
C'è stato un tempo in cui utilizzavamo , dove era possibile dichiarare oggetti Kubernetes da un unico endpoint API. Tuttavia, si sono presentati dei problemi. Ad esempio, alcuni oggetti Kubernetes non potevano essere creati nell'endpoint della federazione, rendendo difficile gestire gli oggetti combinati e altri oggetti per cluster separati.
Per risolvere il problema, abbiamo iniziato a gestire i cluster in modo indipendente, il che ha semplificato notevolmente il processo (abbiamo utilizzato la prima versione della federazione; nella seconda potrebbe essere cambiato qualcosa).
Piattaforma geodistribuita
Attualmente, la nostra piattaforma è distribuita in 6 regioni — 3 localmente e 3 nel cloud.
Distribuzione distribuita
Valori globali Helm
4 valori globali Helm permettono di definire differenze tra cluster. Per tutti i nostri chart ci sono valori minimi predefiniti.
global:
cloud: True
env: staging
region: us-central1
clusterName: staging-us-central1Valori globali
Questi valori aiutano a definire il contesto per le nostre applicazioni e sono utilizzati per vari compiti: monitoraggio, tracciamento, logging, effettuazione di chiamate esterne, scalabilità, ecc.
- «cloud»: abbiamo una piattaforma ibrida Kubernetes. Ad esempio, la nostra API è distribuita nelle zone GCP e nei nostri data center.
- «env»: alcuni valori possono cambiare per gli ambienti non di produzione. Ad esempio, le definizioni delle risorse e la configurazione del ridimensionamento automatico.
- «region»: queste informazioni aiutano a determinare la posizione del cluster e possono essere utilizzate per identificare i punti finali più vicini per i servizi esterni.
- «clusterName»: se e quando vogliamo definire un valore per un cluster specifico.
Ecco un esempio specifico:
{{/* Restituisce le repliche dell'Horizontal Pod Autoscaler per GraphQL*/}}
{{- definisci "graphql.hpaReplicas" -}}
{{- se eq .Values.global.env "prod" }}
{{- se eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- altrimenti }}
minReplicas: 150
{{- fine }}
maxReplicas: 1400
{{- altrimenti }}
minReplicas: 4
maxReplicas: 20
{{- fine }}
{{- fine -}}Esempio di template Helm
Questa logica è definita in un template ausiliario per evitare di affollare il Kubernetes YAML.
Dichiarazione dell'applicazione
I nostri strumenti di distribuzione si basano su diversi file YAML. Di seguito è riportato un esempio di come dichiariamo il servizio e la sua topologia di scalabilità (numero di repliche) nel cluster.
rilascia:
- foo.world
foo.world: # Nome del rilascio
servizi: # Elenco delle app/progetti di dailymotion
foobar:
chart_name: foo-foobar
repo: git@github.com:dailymotion/foobar
contesti:
prod-europe-west1:
distribuzioni:
- nome: foo-bar-baz
repliche: 18
- nome: altra-distribuzione
repliche: 3Definizione del servizio
Questa è la schemi di tutti i passaggi che definiscono il nostro processo di distribuzione. L'ultimo passaggio distribuisce l'applicazione simultaneamente su più cluster di lavoro.
Passaggi di distribuzione in Jenkins
E i segreti?
Per quanto riguarda la sicurezza, tracciamo tutti i segreti da diverse fonti e li conserviamo in un archivio unico a Parigi.
I nostri strumenti di distribuzione estraggono i valori dei segreti da Vault e, quando arriva il momento della distribuzione, li inseriscono in Helm.
Per questo motivo abbiamo definito una mappatura tra i segreti in Vault e i segreti necessari alle 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 delle regole generali che devono essere seguite quando si registrano i segreti nel Vault.
- Se il segreto è associato a un contesto o a un cluster specifico, è necessario aggiungere una registrazione specifica. (Qui il contesto cluster1 ha un valore proprio per il segreto stack-app1-password).
- In caso contrario, viene utilizzato il valore predefinito.
- Per ogni voce in questo elenco in segreto Kubernetes viene inserito un coppia chiave-valore. Pertanto, il modello del segreto nei nostri chart è 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: OpaqueProblemi e limitazioni
Lavorare con più repository
Attualmente stiamo separando lo sviluppo dei chart e delle applicazioni. Questo significa che gli sviluppatori devono lavorare in due repository git: uno per l'applicazione e il secondo per definire il suo deployment in Kubernetes. 2 repository git equivalgono a 2 flussi di lavoro, ed è facile per un principiante confondersi.
Gestire chart generici è complicato
Come abbiamo già detto, i chart generici sono molto utili per definire le dipendenze e per il rapido deployment di più applicazioni. Ma noi utilizziamo --reuse-values, per evitare di passare tutti i valori ogni volta che deployiamo un'applicazione che fa parte di questo chart generico.
Nel processo di delivery continuo abbiamo solo due valori che cambiano regolarmente: il numero di repliche e il tag dell'immagine (versione). Altri valori, più stabili, vengono modificati manualmente, e questo è piuttosto complicato. Inoltre, un errore nel rilascio di un chart generico può portare a gravi malfunzionamenti, come abbiamo sperimentato in prima persona.
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, aggiungere l'applicazione alle dipendenze se fa parte del chart generico.
I permessi di Jenkins sono troppo estesi in Vault
Attualmente abbiamo uno , che legge tutti i segreti da Vault.
Il processo di rollback non è automatizzato
Per un rollback è necessario eseguire comandi su più cluster, il che porta a potenziali errori. Effettuiamo questa operazione manualmente per assicurarci di indicare il corretto identificativo della versione.
Stiamo andando verso GitOps
Il nostro obiettivo
Vogliamo riportare il chart nel repository dell'applicazione che distribuisce.
Il processo di lavoro sarà lo stesso di quello per lo sviluppo. Ad esempio, quando un ramo viene inviato nel master, il deployment verrà avviato automaticamente. La principale differenza tra questo approccio e l'attuale processo di lavoro è che tutto sarà gestito in git (l'applicazione stessa e il modo in cui viene distribuita in Kubernetes).
Ci sono diversi vantaggi:
- Molto più chiaro per lo sviluppatore. È più facile imparare ad applicare le modifiche nel chart locale.
- La definizione del deployment del servizio può essere specificata lì dove si trova il codice del servizio.
- Gestione della cancellazione dei chart generali. Il servizio avrà la sua release Helm. Questo permetterà di gestire il ciclo di vita dell'applicazione (rollback, upgrade) a un livello molto dettagliato, senza coinvolgere altri servizi.
- Vantaggi di git per la gestione dei chart: annullamento delle modifiche, registro delle audit, ecc. Se è necessario annullare una modifica al chart, è possibile farlo tramite git. Il deployment viene avviato automaticamente.
- Si può pensare a migliorare il processo di sviluppo utilizzando strumenti come Skaffold, con cui gli sviluppatori possono testare le modifiche in un contesto simile a quello di produzione.
Migrazione a due fasi
I nostri sviluppatori utilizzano questo flusso di lavoro da 2 anni, quindi abbiamo bisogno di una migrazione il più indolore possibile. Per questo motivo, abbiamo deciso di aggiungere una fase intermedia verso l'obiettivo.
La prima fase è semplice:
- Manteniamo una struttura simile per la configurazione del deployment delle applicazioni, 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 rilascio per applicazione (senza chart generici).
- Chart 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. Presto scriverò un altro post sulla seconda fase: come siamo passati al flusso di lavoro GitOps con . Vi racconterò come abbiamo configurato tutto e quali difficoltà abbiamo affrontato (diversi repository, segreti, ecc.). Rimanete sintonizzati per gli aggiornamenti.
Qui abbiamo cercato di descrivere il nostro progresso nel processo di distribuzione delle applicazioni negli ultimi anni, che ci ha portati a riflettere sull'approccio GitOps. Non abbiamo ancora raggiunto l'obiettivo e comunicheremo i risultati, ma siamo già convinti di aver fatto la scelta giusta semplificando tutto e avvicinandolo alle abitudini degli sviluppatori.
Fonte: habr.com
