10 buone pratiche per Argo CD che seguo
Ti mostrerò come convalidare le risorse personalizzate rispetto a queste best practice Argo per gli ingegneri DevOps.
Il mio viaggio DevOps è iniziato quando ho iniziato a sviluppare Datree, un comando open source che mira ad aiutare gli ingegneri DevOps a impedire che errori di configurazione di Kubernetes raggiungano la produzione. Un anno dopo, la ricerca delle migliori pratiche e di ulteriori modi per prevenire configurazioni errate è diventata il mio stile di vita.
Questo è il motivo per cui quando ho saputo di Argo CD per la prima volta, il pensiero di usare Argo senza conoscerne le insidie e le complicazioni semplicemente non aveva senso per me. Dopotutto, è probabile che una configurazione errata possa facilmente causare la prossima interruzione della produzione.
In questo articolo esplorerò alcune delle migliori pratiche di Argo che ho trovato e ti mostrerò come convalidare le risorse personalizzate rispetto a queste migliori pratiche.
Non consentire di fornire una retryStrategy vuota
Progetto: Argo Workflows
Best practice: un utente può specificare una retryStrategy
che determina il modo in cui gli errori e gli errori vengono ripetuti in un flusso di lavoro. Fornendo un retryStrategy (retryStrategy: {})
vuoto, il contenitore riproverà fino al completamento e alla fine causerà problemi di memoria insufficiente (OOM).
Assicurati che i pod del flusso di lavoro non siano configurati per utilizzare l'account di servizio predefinito
Progetto: Argo Workflows
Best practice: tutti i pod in un flusso di lavoro vengono eseguiti con un account di servizio, che può essere specificato in workflow.spec.serviceAccountName
. Se omesso, Argo utilizza l'account di servizio default
dello spazio dei nomi del flusso di lavoro. Ciò fornisce al flusso di lavoro (il pod) la possibilità di interagire con il server API Kubernetes. Ciò consente agli aggressori con accesso a un singolo contenitore di abusare di Kubernetes utilizzando AutomountServiceAccountToken
. Se per caso l'opzione per AutomountServiceAccountToken
è stata disabilitata, l'account di servizio predefinito utilizzato da Argo non avrà alcuna autorizzazione e il flusso di lavoro fallisce.
Si consiglia di creare account di servizio dedicati gestiti dall'utente con i ruoli appropriati.
Imposta l'etichetta 'parte di: argocd' in ConfigMaps
Progetto: CD Argo
Quando si installa Argo CD, la sua configurazione atomica contiene alcuni servizi e configMaps
. Per ogni tipo specifico di risorsa ConfigMap e segreta, esiste un solo nome di risorsa supportato (come elencato nella tabella precedente). Se hai bisogno di unire le cose, fallo prima di crearle. È importante annotare le tue risorse ConfigMap utilizzando l'etichetta app.kubernetes.io/part-of: argocd
, altrimenti Argo CD non sarà in grado di utilizzarle.
Disabilitare "FailFast=false" nel DAG
Progetto: Argo Workflows
Best practice: come alternativa alla specifica delle sequenze di passaggi nel flusso di lavoro, puoi definire il flusso di lavoro come un grafico aciclico diretto (DAG) specificando le dipendenze di ciascuna attività. La logica DAG dispone di una funzionalità fail fast incorporata per interrompere la pianificazione di nuovi passaggi non appena rileva che uno dei nodi DAG non è riuscito. Quindi attende finché tutti i nodi DAG non vengono completati prima di guastare il DAG stesso. Il flag predefinito FailFast è true
. Se impostato su false
, consente a un DAG di eseguire tutti i rami del DAG fino al completamento (successo o fallimento), indipendentemente dai risultati non riusciti dei rami nel DAG.
Assicurati che il passaggio di pausa dell'implementazione abbia una durata configurata
Progetto: Implementazioni di Argo
Best practice: per ogni implementazione puoi definire un elenco di passaggi. Ogni passaggio può avere uno di due campi: setWeight
e pause
. Il campo setWeight
determina la percentuale di traffico che dovrebbe essere inviata al canary e pause indica letteralmente la pausa del rollout.
Dietro le quinte, il controller Argo utilizza questi passaggi per manipolare i ReplicaSet durante il lancio. Quando il controller raggiunge un passaggio pausa per un'implementazione, aggiunge una struttura PauseCondition
al file .status.PauseConditions
campo. Se è impostato il campo duration
all'interno della struttura pause, il rollout non procede al passaggio successivo finché non ha atteso il valore di duration
campo. Tuttavia, se il campo duration
è stato omesso, l'implementazione potrebbe attendere indefinitamente fino alla rimozione della condizione di pausa aggiunta.
Specificare il revisionHistoryLimit dell'implementazione
Progetto: Implementazioni di Argo
Best practice: .spec.revisionHistoryLimit
è un campo facoltativo che indica il numero di vecchi ReplicaSet, che dovrebbero essere conservati per consentire il rollback. Questi vecchi ReplicaSet consumano risorse in etcd
e affollano l'output di kubectl get rs
. La configurazione di ciascuna revisione del Deployment è archiviata nei relativi ReplicaSet; pertanto, una volta eliminato un vecchio ReplicaSet, si perde la possibilità di eseguire il rollback a quella revisione di distribuzione.
Per impostazione predefinita, vengono mantenuti 10 vecchi ReplicaSet. Tuttavia, il suo valore ideale dipende dalla frequenza e dalla stabilità delle nuove distribuzioni. Più specificamente, impostare questo campo su zero significa che tutti i vecchi ReplicaSet con 0 repliche verranno rimossi. In questo caso, non è possibile annullare l'implementazione di una nuova distribuzione perché la cronologia delle revisioni viene rimossa.
Imposta scaleDownDelaySeconds su 30 secondi
Progetto: Implementazioni di Argo
Best practice: quando l'implementazione modifica il selettore sul servizio, si verifica un ritardo di propagazione prima che tutti i nodi aggiornino le proprie tabelle IP per inviare il traffico ai nuovi pod anziché al vecchio. Il traffico viene indirizzato ai vecchi pod se i nodi non sono stati ancora aggiornati durante questo ritardo. Per evitare che i pacchetti vengano inviati a un nodo che ha ucciso il vecchio pod, l'implementazione utilizza il campo scaleDownDelaySeconds
per dare ai nodi abbastanza tempo per trasmettere le modifiche alla tabella IP. Se omesso, il Rollout attende 30 secondi prima di ridurre il ReplicaSet precedente.
Si consiglia di impostare scaleDownDelaySeconds
su un minimo di 30 secondi per garantire che la tabella IP si propaghi attraverso i nodi in un cluster. Il motivo è che Kubernetes attende un periodo di tempo specificato chiamato periodo di grazia per la terminazione. Per impostazione predefinita, questo è 30 secondi.
Assicurati di riprovare sia in caso di Errore che di TransientError
Progetto: Argo Workflows
Best practice: retryStrategy
è un campo facoltativo del CRD Workflow, che fornisce controlli per riprovare un passaggio del flusso di lavoro. Uno dei campi di retryStrategy
è _retryPolicy
, che definisce la politica degli stati di NodePhase da ritentare (NodePhase è la condizione di un nodo al momento corrente). Le opzioni per retryPolicy
possono essere: Sempre
, OnError
o OnTransientError
. Inoltre, l'utente può utilizzare un'espressione per controllare più tentativi.
Qual è il problema?
- retryPolicy=Sempre è troppo: consentire all'utente di riprovare in caso di errori a livello di sistema (ad esempio, il nodo muore o viene interrotto), ma non in caso di errori che si verificano nel codice a livello di utente poiché questi errori indicano un insetto. Inoltre, questa opzione è più adatta ai contenitori di lunga durata rispetto ai flussi di lavoro che sono lavori.
- retryPolicy=OnError non gestisce le prelazioni: l'utilizzo di
retryPolicy=OnError
gestisce alcuni errori a livello di sistema come la scomparsa del nodo o l'eliminazione del pod. Tuttavia, durante la terminazione regolare del pod, kubelet assegna uno statoFailed
e un motivoShutdown
ai pod terminati. Di conseguenza, le prelazioni dei nodi restituiscono lo stato del nodoFailure
anzichéError
, quindi le prelazioni non vengono ritentate. - retryPolicy=OnError non gestisce gli errori temporanei: è consentito classificare un messaggio di errore di prelazione come errore temporaneo. Tuttavia, ciò richiede
retryPolicy=OnTransientError
. (vedi ancheTRANSIENT_ERROR_PATTERN
).
Consiglio di impostare retryPolicy: "Always"
e di utilizzare la seguente espressione:
lastRetry.status == "Error" or (lastRetry.status == "Failed" and asInt(lastRetry.exitCode) not in [0])
Assicurati che progressDeadlineAbort sia impostato su true
Progetto: Implementazioni di Argo
Best practice: un utente può impostare progressDeadlineSeconds
, che indica il tempo massimo in secondi in cui un'implementazione deve avanzare durante un aggiornamento prima che venga considerato fallito.
Se i pod di implementazione rimangono bloccati in uno stato di errore (ad esempio, pull back off dell'immagine), l'implementazione peggiora una volta superata la scadenza per l'avanzamento ma il set di repliche o i pod errati non vengono ridimensionati. I pod continuerebbero a riprovare e alla fine il messaggio di implementazione leggerebbe ProgressDeadlineExceeded: The replicaset has timeout progressing
. Per interrompere l'implementazione, imposta sia progressDeadlineSeconds
che progressDeadlineAbort
, con progressDeadlineAbort: true
.
Assicurati che le risorse personalizzate corrispondano allo spazio dei nomi dell'istanza ArgoCD
Progetto: CD Argo
Best practice: in ciascun repository, tutti i manifest di Application e AppProject devono corrispondere allo stesso metadata.namespace. Se hai distribuito Argo CD utilizzando la distribuzione tipica, Argo CD crea due ClusterRoles
e ClusterRoleBinding
, che fanno riferimento allo spazio dei nomi argocd
per impostazione predefinita. In questo caso, è consigliabile non solo assicurarsi che tutte le risorse del CD Argo corrispondano allo spazio dei nomi dell'istanza del CD Argo, ma anche utilizzare lo spazio dei nomi argocd
. Altrimenti, devi assicurarti di aggiornare il riferimento allo spazio dei nomi in tutte le risorse interne del CD Argo.
Tuttavia, se hai distribuito Argo CD per cluster esterni (in modalità di isolamento dello spazio dei nomi), invece di ClusterRole
e ClusterRoleBinding
, Argo crea Ruoli
e RoleBindings
associati nello spazio dei nomi in cui è stato distribuito Argo CD. All'account di servizio creato viene concesso un livello limitato di accesso per la gestione, quindi affinché Argo CD possa funzionare come desiderato, l'accesso allo spazio dei nomi deve essere concesso esplicitamente. In questo caso, dovresti assicurarti che tutte le risorse, inclusi Application
e AppProject
, utilizzino lo spazio dei nomi corretto dell'istanza ArgoCD.
E adesso?
Sono un sostenitore di GitOps e credo che ogni risorsa Kubernetes debba essere gestita esattamente come il codice sorgente, soprattutto se utilizzi Helm o Kustomize. Quindi, per come la vedo io, dovresti controllare automaticamente le tue risorse ad ogni modifica del codice.
Puoi scrivere le tue policy utilizzando linguaggi come Rego o JSONSchema e utilizzare strumenti come OPA ConfTest o diversi validatori per scansionare e convalidare le nostre risorse ad ogni modifica. Inoltre, se disponi di un repository GitOps, Argo svolge un ruolo importante nel fornire un repository centralizzato per sviluppare e controllare la versione delle tue policy.
[Scarica l'eBook: Getting GitOps: una piattaforma pratica con OpenShift, Argo CD e Tekton]
Come funziona Datree
La CLI Datree esegue controlli automatici su ogni risorsa esistente in un determinato percorso. Una volta completato il controllo, Datree visualizza un output dettagliato di qualsiasi violazione o configurazione errata rilevata, con linee guida su come risolverlo:
Scansiona il tuo cluster con Datree
$ kubectl datree test -- -n argocd
Puoi utilizzare il plug-in Datree kubectl per convalidare le tue risorse dopo le distribuzioni, prepararti per futuri aggiornamenti delle versioni e monitorare la conformità generale del tuo cluster.
Scansiona i tuoi manifest nel CI
In generale, Datree può essere utilizzato nel CI, come libreria di test locale o anche come hook di pre-commit. Per utilizzare datree
, devi prima installare il comando sul tuo computer, quindi eseguirlo con il seguente comando:
$ datree test -.datree/k8s-demo.yaml >> File: .datree/k8s-demo.yaml
[V] YAML Validation
[V] Kubernetes schema validation
[X] Policy check
X Ensure each container image has a pinned (tag) version [1 occurrence]
- metadata.name: rss-site (kind: Deployment)
!! Incorrect value for key 'image' - specify an image version
X Ensure each container has a configured memory limit [1 occurrence]
- metadata.name: rss-site (kind: Deployment)
!! Missing property object 'limits.memory' - value should be within the accepter
X Ensure workload has valid Label values [1 occurrence]
- metadata.name: rss-site (kind: Deployment)
!! Incorrect value for key(s) under 'labels' - the vales syntax is not valid
X Ensure each container has a configured liveness probe [1 occurrence]
- metadata.name: rss-site (kind: Deployment)
!! Missing property object 'livenessProbe' - add a properly configured livenessP:
[...]
Come accennato in precedenza, il modo in cui funziona la CLI è che esegue controlli automatici su ogni risorsa esistente nel percorso specificato. Ogni controllo automatico comprende tre passaggi:
- Convalida YAML: verifica che il file sia un file YAML valido.
- Convalida dello schema Kubernetes: verifica che il file sia una risorsa Kubernetes/Argo valida.
- Controllo policy: verifica che il file sia conforme alla policy Kubernetes (regole integrate Datree per impostazione predefinita).
Riepilogo
A mio avviso, le policy di governo sono solo l'inizio del raggiungimento dell'affidabilità, della sicurezza e della stabilità del tuo cluster Kubernetes. Sono stato sorpreso di scoprire che la gestione centralizzata delle policy potrebbe anche essere una soluzione chiave per risolvere una volta per tutte lo stallo di DevOps e sviluppo.
Dai un'occhiata al progetto open source Datree. Ti incoraggio vivamente a rivedere il codice e inviare un PR e non esitare a contattarci.
Questo articolo è apparso originariamente sul blog Datree ed è stato ripubblicato con il permesso.