BLUF
Nella community Kubernetes vige una convenzione: i componenti integrati in Kubernetes richiamano kubernetes.io nel proprio nome, mentre gli add-on no. Questi ultimi seguono in genere una convenzione autoesplicativa come ebs.csi.aws.com, dalla quale si capisce a colpo d'occhio che si tratta di un add-on. L'add-on aws-load-balancer-controller infrange questa convenzione.
A chi è rivolto
Questo articolo si rivolge a tre tipi di lettori, con altrettanti obiettivi:
1. Amministratori Kubernetes che gestiscono EKS o distribuzioni Kubernetes generiche su AWS: non è strettamente necessario leggerlo, ma consiglio di salvarlo tra i preferiti e di tenerlo a mente come materiale di riferimento, da riprendere in mano quando vi capiterà di fare il debug del provisioning di AWS Load Balancer tramite servizi Kubernetes con annotazioni del tipo service.beta.kubernetes.io/aws-load-balancer-*.
2. Appassionati di Kubernetes a cui piacciono gli articoli di approfondimento per capire meglio la piattaforma.
3. Maintainer di EKS, aws-cloud-controller-manager, aws-load-balancer-controller e della documentazione AWS: con questo articolo intendo accendere i riflettori su un'incongruenza, fonte di confusione, che riguarda esclusivamente il provisioning dei Load Balancer Kubernetes su AWS. La speranza è di spingere verso una maggiore coerenza con il resto della community Kubernetes. (Il problema attraversa diversi repository git e siti di documentazione, quindi non si risolve con qualche merge request.)
Introduzione
Di recente ho avviato una proof of concept che prevedeva il provisioning di AWS Load Balancer per un cluster EKS (Elastic Kubernetes Service) tramite servizi Kubernetes di tipo LoadBalancer con annotazioni nella forma service.beta.kubernetes.io/aws-load-balancer-*, elencate in questa pagina.
All'inizio alcune annotazioni non si comportavano come previsto. Sono riuscito a risolvere i problemi, ma vale la pena sottolineare che per farlo ho dovuto capire perché non funzionavano. La documentazione della pagina linkata spiega le cose in modo discreto, ma le spiegazioni non sono certo amichevoli per chi inizia.
Questo articolo, quindi, vuole essere un approfondimento su diversi aspetti:
- Perché aws-load-balancer-controller genera confusione
- Le informazioni di contesto utili a mettere al passo chi parte da zero
- I dettagli sul perché le cose funzionano in un certo modo
- Qualche consiglio di base, ma utile, per il troubleshooting
- Idee su come i vari gruppi di maintainer potrebbero ridurre la confusione in futuro
La sfida: un caso di scambio di identità
Come dicevo, la pagina linkata sopra non la trovo affatto adatta ai principianti. Per essere chiari: il progetto mi è sembrato così confuso che mi ci è voluto un giorno per capirci qualcosa. Un brutto segno, considerato che mi occupo di Kubernetes da SME dal luglio 2018. Una volta capito, il primo pensiero è stato: "Ah, ecco di cosa si tratta. Oggi ho scoperto che esistono due controller Kubernetes specifici per AWS che possono usare le annotazioni associate ai servizi Kubernetes di tipo Load Balancer per fare il provisioning e gestire gli AWS LB." Il secondo: "Wow… come si suppone che gli altri ci arrivino senza un esperto al fianco? Devo scriverci un articolo."
Ecco gli aspetti che rendono confuso il progetto AWS Load Balancer Controller:
1.) Sembra una funzionalità ufficiale integrata, ma non lo è!
Cercando su Google "aws service of type lb":
Primo risultato: docs.aws.amazon.com/eks/latest/userguide/network-load-balancing.html
Secondo risultato: kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/service/annotations/
E la sezione pertinente della documentazione ufficiale di Kubernetes, primo risultato cercando "kubernetes service of type loadbalancer":
https://kubernetes.io/docs/concepts/services-networking/service/
Tutte e tre le pagine sembrano documentazione ufficiale e sembrano trattare lo stesso argomento, perché le stesse annotazioni compaiono ovunque.
Se in tutte e tre le pagine si fa Ctrl+F cercando:
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags
La stessa risorsa appare in tutte e tre. A prima vista verrebbe da pensare che i tre siti parlino della stessa cosa.
In realtà, aws-load-balancer-controller è un add-on di Kubernetes che estende le funzionalità integrate. Quindi due dei link riguardano funzionalità integrate, mentre il terzo è un add-on pensato per offrire funzionalità aggiuntive.
2.) La convenzione di denominazione adottata dal sito del progetto e dalle annotazioni è davvero poco intuitiva e induce a pensare che le annotazioni rimandino a funzionalità integrate:
2A.) Il dominio DNS che ospita la documentazione del progetto è kubernetes-sigs.github.io, il che lo fa sembrare una funzionalità integrata di Kubernetes.
2B.) Le annotazioni dell'add-on richiamano service.beta.kubernetes.io, prefisso solitamente riservato alle funzionalità integrate.
3.) La home page del sito di documentazione del progetto non rende immediatamente chiaro che si tratta di un add-on Kubernetes pensato per estendere le funzionalità integrate.
Solo arrivando verso il fondo della seconda pagina della documentazione
e notando helm install aws-load-balancer-controller, scatta finalmente: "Ah! È un add-on, ok, ora torna tutto."
4.) Il pattern usato dall'API è incoerente con quello adottato dalla community Kubernetes nel suo complesso: prendiamo come esempio Cert Manager e il driver EBS Volume CSI (Container Storage Interface):
- Nella documentazione di cert-manager, il 95% delle annotazioni usa
cert-manager.io. A colpo d'occhio è quindi evidente che le annotazioni si riferiscono a un add-on di terze parti. - La prima frase della landing page della documentazione recita "cert-manager adds certificates and certificate issuers as resource types in Kubernetes clusters…", chiarendo da subito che il progetto è un'estensione che aggiunge funzionalità.
- Nel caso del driver EBS Volume CSI, la convenzione di denominazione adottata permette di distinguere chiaramente, a prima vista, ciò che è un add-on da ciò che è funzionalità integrata.
La Storage Class AWS integrata usa provisioner: kubernetes.io/aws-ebs
La Storage Class dell'add-on EBS usa provisioner:ebs.csi.aws.com
- È pratica diffusa che le landing page dei vari siti di documentazione di EBS CSI ribadiscano subito che si tratta di un add-on non installato di default. La documentazione di questo progetto arriva persino a inserire deliberatamente la parola chiave "driver" per renderlo più intuitivo: "The Amazon EBS CSI driver isn't installed when you first create a cluster. To use the driver, you must add it as an Amazon EKS add-on or as a self-managed add-on." E nel caso si arrivi sulla pagina GitHub anziché sulla documentazione, anche la landing page GitHub del progetto riporta un link a "Driver Installation".
5.) Anche l'interfaccia e la documentazione di AWS sono incoerenti per quanto riguarda aws-load-balancer-controller: AWS Load Balancer Controller ha una guida utente su docs.aws.amazon.com intitolata "Installing the AWS Load Balancer Controller add-on".
Ma nella GUI non compare come add-on ufficiale, neppure cliccando su Get more add-ons dove invece compaiono Amazon EBS CSI Driver e AWS Distro for OpenTelemetry.
6.) Al progetto mancano informazioni di contesto
La pagina della documentazione dedicata alle annotazioni dei servizi accenna a due argomenti utili da padroneggiare (perché capirli aiuta nel troubleshooting) ma quasi impossibili da assimilare per un nuovo arrivato senza spiegazioni aggiuntive. Cercando su Google si trovano informazioni vaghe, frammentarie e in apparenza contraddittorie, a meno di saper cogliere parecchie sfumature.
I due argomenti citati di sfuggita nella documentazione sono:
1. "in-tree": "the k8s in-tree kube-controller-manager"
2. "Legacy AWS Cloud Provider": "The AWS Load Balancer Controller manages Kubernetes Services in a compatible way with the legacy aws cloud provider."
In un mondo ideale ci sarebbe una definizione sintetica di queste espressioni e un link a letture di approfondimento per chi è interessato. Ecco come le riassumerei: tecnicamente sono due cose diverse, ma di fatto coincidono. Il Legacy AWS Cloud Provider è ciò che fornisce la funzionalità integrata predefinita per il provisioning di servizi di tipo Load Balancer nei cluster Kubernetes in esecuzione su AWS.
Informazioni di contesto
Quando faccio troubleshooting, la prima cosa che faccio è cercare di capire il problema. Per inquadrare a fondo il provisioning degli AWS LB servono molte informazioni di contesto. Purtroppo, fino a oggi queste informazioni erano sparpagliate, mai approfondite e ricche di sfumature che ne complicavano la comprensione. Questo è il mio tentativo di consolidare alcune nozioni chiave e renderle digeribili.
Contesto degli argomenti chiave:
1.) kube-controller-manager vs cloud-controller-manager vs aws-cloud-controller-manager Le sfumature di "in-tree" e "Legacy AWS Cloud Provider" sono difficili da cogliere senza il contesto degli argomenti correlati: partiamo quindi da qui.
In passato Kubernetes aveva quattro componenti del control plane: controller-manager, scheduler, api-server ed etcd. Si eseguiva o kube-controller-manager o cloud-controller-manager, mai entrambi.
Il cloud-controller-manager era l'opzione utilizzata nel 95% dei casi. Aveva le stesse funzionalità di kube-controller-manager, più il supporto integrato per dialogare con le API di più CSP (Cloud Service Provider). Sapendo come parlare con le API dei CSP, era in grado, ad esempio, di fare il provisioning di volumi AWS EBS e AWS Load Balancer.
kube-controller-manager è un'implementazione cloud-agnostic e quindi non ha la capacità integrata di fare cose come il provisioning automatico di storage o Load Balancer presso i CSP.
Oggi Kubernetes ha tipicamente cinque componenti del control plane:
etcd, kube-api-server, kube-scheduler, kube-controller-manager e un controller manager specifico per CSP, come aws-cloud-controller-manager. I diagrammi della documentazione ufficiale di Kubernetes sono stati aggiornati per riflettere questa nuova architettura. Lo si può verificare anche con metodi di deployment come kops o kubeadm, che permettono di vedere il control plane. Il cambiamento principale è che ora si hanno cloud controller manager dedicati a un singolo cloud service provider, da eseguire affiancati a kube-controller-manager. Il cloud-controller-manager originale, invece, era una soluzione universale e onnicomprensiva.
2.) "In-tree" si riferisce al periodo in cui le librerie Go in grado di interfacciarsi con le API di più CSP risiedevano nel repo Kubernetes per costruire un'immagine container universale di Kubernetes Cloud Controller Manager:
In-tree: codice che risiede nel repository core di Kubernetes k8s.io/kubernetes.
Out-of-tree: codice che risiede in un repository esterno al repo git k8s.io/kubernetes.
La code base di Kubernetes 1.14 includeva integrate le API di 8 diversi CSP. Si volevano aggiungerne altri, ma il fatto che fossero tutti raggruppati implicava che dovessero raggiungere la stabilità contemporaneamente per ogni release. Un pattern insostenibile sul piano della manutenibilità. Per affrontare il problema è stato approvato un KEP (Kubernetes Enhancement Proposal) che proponeva di disaccoppiare le funzionalità CSP da Kubernetes e di rifattorizzarle in add-on.
Questo disaccoppiamento avrebbe semplificato il processo di release di Kubernetes e avrebbe permesso ai cloud provider di rilasciare funzionalità e bug fix in modo indipendente dal ciclo di release di Kubernetes. Una scelta per ripagare il debito tecnico e garantire la salute a lungo termine del progetto.
3.) Provando a documentarsi in autonomia su questo tema, è facile imbattersi in informazioni che a prima vista sembrano contraddittorie, finché non si colgono diverse sfumature. Eccone alcune, in ordine sparso:
- Sembra che la migrazione da in-tree a out-of-tree stia richiedendo anni. Si trovano anche informazioni che la danno avviata e conclusa già da tempo, oppure avviata e conclusa in momenti diversi, oppure ancora in corso.
- Prima ho ricordato che la code base di Kubernetes 1.14 aveva le API di otto diversi CSP. Guardando la code base di Kubernetes 1.26, si vedono ancora quattro CSP in-tree. AWS compare ancora nell'elenco dei CSP in-tree. Eppure esiste un repo git out-of-tree che fa riferimento ad aws-cloud-provider e che lascia intendere che AWS abbia completato la migrazione già con la 1.20.
Mentre il KEP suggerisce che la migrazione si concluderà attorno alla versione 1.27?
È proprio per situazioni come questa che ho preferito scrivere un articolo piuttosto che contribuire a sistemare un problema della documentazione. Anche se AWS e altri CSP esistono ancora in-tree/in una versione universale del cloud controller manager, quella versione non è ciò che si usa nella pratica. AWS e gli altri CSP usano la propria implementazione specifica, come aws-cloud-controller-manager, al posto del vecchio cloud-controller-manager universale e onnicomprensivo in-tree.
I vari CSP hanno completato la migrazione dall'in-tree universale all'out-of-tree specifico a ritmi diversi. Un altro elemento che genera confusione è che questo lavoro complesso è stato suddiviso in due parti. La funzionalità integrata in-tree originaria includeva la logica per il provisioning di storage CSP e LB CSP. Non solo è stata portata out-of-tree, ma è stata ulteriormente disaccoppiata: i driver CSI per lo storage CSP sono diventati un componente isolato, con tempistiche di migrazione proprie.
- Se si comprende la portata del refactor, alcune cose iniziano a tornare. Primo: non c'è da stupirsi che il refactor abbia richiesto oltre 4 anni. Le implementazioni out-of-tree dei cloud provider hanno raggiunto lo stato beta nella versione 1.11, a maggio 2019. Secondo: sapendo che sono stati introdotti diversi feature freeze per supportare la migrazione, si capisce perché bug come quelli legati a
ExternalTrafficPolicy: Localsugli AWS LB abbiano richiesto anni per essere risolti. Spiega anche perché per anni la logica integrata di provisioning degli AWS LB non abbia ricevuto granché in termini di nuove funzionalità.
4.) "Legacy AWS Cloud Provider" indica, sostanzialmente, la funzionalità predefinita di provisioning dei load balancer disponibile in un'installazione standard di EKS o di Kubernetes su AWS.
Alcune sfumature impediscono a questa affermazione di essere vera al 100%, ma si avvicina abbastanza alla realtà da risultare utile.
Le sfumature sono queste:
- Legacy AWS Cloud Provider può riferirsi alla logica specifica per AWS che esisteva in-tree nel cloud-controller-manager (universale CSP), e che permetteva di fare il provisioning di volumi AWS EBS e AWS LB.
- Legacy AWS Cloud Provider può anche riferirsi alla logica predefinita di provisioning dei load balancer presente in aws-cloud-controller-manager.
- aws-cloud-controller-manager è spesso invisibile agli utenti.
– Con EKS, aws-cloud-controller-manager gira sui master node gestiti, che non si vedono.
– Con kops o kubeadm, che usano master node self-hosted, lo si vede.
– Con RKE2 non lo si vede, per via di alcune scelte di sicurezza defense-in-depth che fanno girare alcuni componenti del control plane come processi isolati da Kubernetes.
- Legacy AWS Cloud Provider può quindi riferirsi a due cose diverse, ma nel contesto degli AWS LB sono di fatto la stessa cosa, perché supportano le medesime annotazioni. Le 22 annotazioni originali si trovano cercando in questa pagina la stringa
const ServiceAnnotationLoadBalancer. Si noti che, al momento in cui scrivo, cercando la stringaservice.beta.kubernetes.io/aws-load-balancernella pagina della documentazione si vedono solo 21 annotazioni: è solo un problema della documentazione, non una modifica del codice.
Ecco l'elenco delle 22 annotazioni disponibili di default:
service.beta.kubernetes.io/aws-load-balancer-type
service.beta.kubernetes.io/aws-load-balancer-internal
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled
service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout
service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled
service.beta.kubernetes.io/aws-load-balancer-extra-security-groups
service.beta.kubernetes.io/aws-load-balancer-security-groups
service.beta.kubernetes.io/aws-load-balancer-ssl-cert
service.beta.kubernetes.io/aws-load-balancer-ssl-ports
service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy
service.beta.kubernetes.io/aws-load-balancer-backend-protocol
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold
service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval
5.) Il progetto AWS Load Balancer Controller è concettualmente simile al progetto AWS EBS CSI Driver: entrambi rappresentano il disaccoppiamento di una logica di controller Kubernetes che risiedeva in-tree nel cloud-controller-manager universale e onnicomprensivo. Entrambi avevano l'obiettivo comune di garantire la retrocompatibilità. Una parte di me si chiede se questo abbia influito sulla scelta del progetto di riutilizzare alcune annotazioni.
6.) I progetti dell'add-on EBS CSI Driver e dell'add-on AWS LB Controller sono nati da gruppi diversi.
Il progetto dell'add-on EBS CSI Driver è stato avviato da AWS. Quello dell'add-on AWS LB Controller è stato originariamente avviato da Ticketmaster e CoreOS ed era noto come "AWS ALB Ingress Controller". Nel 2018 è stato donato a Kubernetes SIG-AWS. In origine ALB Ingress Controller era a tutti gli effetti un Application Load Balancer Controller. Con il tempo lo scope del progetto si è ampliato fino a includere anche il controllo degli NLB. A luglio 2021 il progetto è stato quindi rinominato "AWS Load Balancer Controller".
Perché le cose stanno così?
Se la logica del cloud-controller-manager universale e onnicomprensivo non fosse stata migrata out-of-tree in add-on esterni e disaccoppiati, il debito tecnico avrebbe rallentato in modo permanente lo sviluppo di nuove funzionalità e i bug fix. Ricordo un periodo in cui i bug legati a externalTrafficPolicy: local continuavano a ripresentarsi e ci sono voluti anni per risolverli a dovere. Ora che la logica è disaccoppiata dal codice, dal processo di release e dal testing del progetto Kubernetes ufficiale, i bug si risolvono in mesi anziché in anni e si torna a poter prendere in considerazione le richieste di nuove funzionalità.
Uno sviluppo più rapido ha portato a miglioramenti notevoli: ad esempio, rendere più facile da implementare il concetto di Software Defined Perimeter tramite Authn/z Proxy, grazie alla nuova integrazione di AWS Cognito con la logica di provisioning degli ALB. Anche il progetto AWS LB Controller offre più opzioni per il provisioning degli NLB tramite annotazioni. Una nuova funzionalità che apprezzo particolarmente è l'annotazione service.beta.kubernetes.io/aws-load-balancer-nlb-target-type:, che permette di scegliere come instradare il traffico verso i pod di backend. Il valore predefinito "instance" fa sì che il traffico vada da nELB -> NodePort -> servizio Kube -> Pod. Il valore "ip", di recente introduzione, consente al traffico di andare da nELB -> Pod, in modo simile a come funziona aELB.
Riferimento abbreviato:
nELB = network Elastic Load Balancer (LB L4 as a service)
aELB/ALB = application Elastic Load Balancer (LB L7 as a service)
cELB = classic Elastic Load Balancer (LB L4/L7 as a service)
Sospetto che lo scope creep che ha portato la gestione degli nELB nel progetto un tempo noto come ALB Ingress Controller sia avvenuto perché quel progetto era probabilmente più ricettivo a modifiche e nuove funzionalità in un periodo in cui aws-cloud-controller-manager era sotto feature freeze per agevolare la migrazione in-tree.
Quanto al perché AWS Load Balancer Controller non somigli di più all'add-on AWS EBS CSI Driver, credo dipenda dal fatto che AWS ha avuto il controllo del progetto EBS CSI dall'inizio alla fine. Il progetto LB Controller, invece, è stato adottato in seguito, e le grandi organizzazioni tendono a introdurre i cambiamenti con lentezza per via della legge di Brooks. (Nelle grandi organizzazioni l'overhead di comunicazione rallenta in modo significativo i cambiamenti.)
Consigli per il troubleshooting di AWS LB Controller
Questi consigli per il troubleshooting non vogliono essere esaustivi. Mirano solo a essere abbastanza utili da sbloccarvi e indirizzarvi verso argomenti su cui approfondire la ricerca.
1.) Verificare se AWS LB Controller è installato kubectl get deployments --namespace=kube-system
È un controllo importante perché può cambiare di molto il comportamento del cluster. Se si hanno due cluster con il 99% degli stessi workloads YAML e delle stesse configurazioni, e l'unica differenza è la presenza o meno di AWS LB Controller, lo stesso YAML può produrre risultati diversi.
2.) Valutare seriamente l'aggiornamento alla versione più recente
Per vedere quale versione si sta usando si può eseguire questo comando:
kubectl get deploy aws-load-balancer-controller -n=kube-system -o yaml | grep image:
image: public.ecr.aws/eks/aws-load-balancer-controller:v2.4.6
Ecco un caso reale recente in cui mantenersi aggiornati ha fatto la differenza:
EKS 1.21 è andato in End of Life il 15 febbraio 2023. Alcuni utenti di AWS LB Controller hanno subito disservizi, finché non sono passati dalla 2.3.x → 2.4.x. I disservizi erano dovuti al fatto che l'aggiornamento da Kubernetes 1.21 a 1.22 ha rimosso molte API deprecate. AWS LB Controller 2.4.x supporta la più recente API Ingress networking.k8s.io/v1, mentre la 2.3.x supporta solo la vecchia networking.k8s.io/v1beta1, rimossa con Kubernetes 1.22. I ticket di issue del progetto aws-lb-controller hanno previsto il problema e hanno reso le versioni 2.4.x compatibili con Kubernetes 1.19++, dando alle organizzazioni il tempo di migrare. Le organizzazioni che seguono la best practice di tenere aggiornati gli add-on hanno evitato i disservizi. Quelle che fanno solo manutenzione minima, presumibilmente, sono incappate nel problema solo dopo aver aggiornato Kubernetes per restare su una release supportata.
3.) Leggere con attenzione tutta la documentazione di installazione, dall'inizio alla fine: è facile sfuggirsi qualche requisito:
Oltre a installare aws-load-balancer-controller nel namespace kube-system e a configurare correttamente i ruoli IAM, è necessario anche taggare correttamente le subnet del VPC.
# Snippet di configurazione VPC Terraform come codice
module vpc {
...
public_subnet_tags = {
"kubernetes.io/role/elb" = "1"
}
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = "1"
}
}
/*
Informazioni di contesto aggiuntive:
Guardando la documentazione EKS
https://aws.amazon.com/premiumsupport/knowledge-center/eks-load-balancer-controller-subnets/
si vedrà un riferimento a
"kubernetes.io/cluster/${local.cluster_name}" = "shared"
Quel tag era richiesto dalle vecchie versioni di aws-load-balancer-controller
*/
4.) Per fare il debug di un oggetto ingress, controllare quali Ingressclass sono installate
kubectl get ingressclass
Se ne compare più di una, eseguire kubectl describe ingressclass e verificare se una delle ingress class ha un'annotazione che la marca come default.
Se la ingress class alb non è marcata come default, conviene specificare in modo esplicito la ingress class desiderata nell'oggetto ingress di cui si sta facendo il debug.
5. Per fare il debug delle annotazioni di provisioning LB specifiche degli oggetti service le cose si complicano un po'…
Perché si complica:
- Si avrebbero, di fatto, due controller diversi in esecuzione contemporaneamente nello stesso cluster.
- Nel caso di EKS, trattandosi di un servizio gestito con master node Kubernetes gestiti, si vede solo uno dei due controller. (aws-cloud-controller-manager non si vede, perché gira sui master node gestiti.)
- I due controller hanno molte sovrapposizioni in termini di funzionalità e oggetti di cui si occupano:
– aws-cloud-controller-manager: provisiona cELB e nELB
– aws-load-balancer-controller: provisiona aELB e nELB
5A.) Determinare quale controller sta effettivamente gestendo il proprio oggetto service Kubernetes. Secondo questo documento, se si annota un service Kubernetes con una di queste opzioni:
service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip service.beta.kubernetes.io/aws-load-balancer-type: external
il controller aws-cloud-controller-manager ignorerà l'oggetto, lasciando ad aws-load-balancer-controller la possibilità di gestirlo.
5B.) Quando aws-load-balancer-controller gestisce un service, kubectl describe service diventa utile! Chi ha provato opzioni LB avanzate si è probabilmente imbattuto in scenari in cui il LB rimane in stato pending e si rifiuta di completare il provisioning. Mettiamo che si esegua kubectl describe service $NAME per fare il debug. Se a gestire il service è il Legacy AWS Controller Manager, è probabile ottenere un messaggio di errore inutile. Se invece è aws-load-balancer-controller, si otterranno messaggi di errore davvero utili per il debug. (Sono ottimi per evidenziare se è stato sbagliato un passaggio dell'installazione, ad esempio diritti IAM mancanti o subnet non taggate.)
5C.) Sfogliare le release notes per individuare eventuali modifiche di cui tenere conto:
- aws-cloud-controller-manager, di default, fa il provisioning di LB con IP pubblici. Per ottenere IP privati va aggiunta della configurazione. Per aws-load-balancer-controller vale il contrario. (dalla v2.2.0)
- aws-cloud-controller-manager, di default, provisiona cELB. aws-load-balancer-controller, di default, provisiona nELB (per qualsiasi service annotato con
service.beta.kubernetes.io/aws-load-balancer-type: external, dato che è ciò che determina quale controller è responsabile.) - Le immagini container di aws-cloud-controller-manager erano disponibili su Docker Hub, ma a partire dalla v2.4.6 saranno ospitate solo sul container registry public.ecr.aws
5D.) La maggior parte degli oggetti Kubernetes supporta i loop di riconciliazione che portano lo stato attuale verso quello desiderato. I controller dei Load Balancer presentano invece alcuni casi limite in cui, per applicare modifiche iterative, è necessario eliminare e ricreare la risorsa.
Di solito non serve, ma a volte vale la pena provarci: l'annotazione del punto 5A è un esempio in cui la documentazione raccomanda la ricreazione invece della modifica. Va anche sottolineato che i valori nlb-ip ed external sono specifici di AWS LB Controller, mentre il Legacy Controller usa i valori nlb e (vuoto, che provisionerà un cELB). Questa insidia può essere rilevante per chi usa un controller GitOps come ArgoCD o Flux per le modifiche iterative, dato che questi strumenti tendono ad aggiornare i manifest invece di eliminare e ricreare le risorse. Quindi un utente di ArgoCD o Flux che stesse testando modifiche iterative in un ambiente di dev potrebbe dover intervenire manualmente, in questo caso limite, per vedere applicate le proprie modifiche.
Avevo due motivi principali per scrivere questo articolo. Il primo era condividere conoscenza, e questa parte è fatta. Il secondo era contribuire a spingere cambiamenti che possano ridurre la confusione. Ci sono tre interventi che i maintainer del progetto e della documentazione potrebbero fare e che farebbero molta strada nel chiarire le idee. Riguardano tutti il rendere aws-load-balancer-controller più simile al progetto EBS-CSI:
- Aggiungere il progetto all'elenco EKS degli add-on ufficiali installabili tramite la GUI della Console AWS.
- Aggiornare la documentazione in più punti per rendere immediatamente evidente che aws-load-balancer-controller è un add-on.
- Aggiornare le annotazioni in modo che, anziché richiamare
kubernetes.io, seguano uno schema più simile aebs.csi.aws.com. Questo perché renderebbe evidente a colpo d'occhio lo status di add-on e avrebbe un effetto SEO (search engine optimization) che migliorerebbe l'esperienza utente nella ricerca della documentazione pertinente.