19 de març de 2024
StatefulSets, gestió optimitzada d'aplicacions amb estat
Què és un StatefulSet?
Podem definir-lo com l'objecte API de càrrega de treball i administració d'aplicacions amb estat o també conegudes com a “stateful applications”. Aquest gestiona la implementació i l'escalat d'un conjunt de pods. Oferint així garanties sobre l'ordre i singularitat dels pods que gestiona.
A semblança amb els deployments, un StatefulSet administra pods basats en especificacions de contenidor idèntiques entre si, però a diferència dels deployments un StatefulSet manté una identitat fixa per a cadascun dels seus pods. És a dir, aquests pods encara que s'hagin creat a partir de les mateixes especificacions no són intercanviables entre ells, posseeixen un identificador persistent que es manté davant qualsevol reprogramació ((re)scheduling).
Si desitgem utilitzar volums d'emmagatzematge per aconseguir persistència en la nostra càrrega de treball, els StatefulSets poden formar part d'una bona solució. Encara que aquests pods que pertanyen a un StatefulSet són susceptibles a fallades, els seus identificadors persistents ens facilitaran la unió dels volums ja existents amb els nous pods que substituiran els pods fallits.
Quan utilitzar un StatefulSet?
Els StatefulSets són especialment útils en els següents casos o requeriments.
- Identificadors de xarxa únics i estables.
- Emmagatzematge estable i persistent.
- Desplegaments i escalats ordenats i sense afectació.
- Actualitzacions automàtiques, contínues i ordenades.
Quan ens referim a aplicacions que no requereixen d'un identificador persistent, implementació ordenada, eliminacions o escalats.
Hem, en aquest cas, implementar la nostra aplicació mitjançant objectes de càrrega de treball que proporcioni un conjunt de rèpliques sense estat. Per exemple, un ReplicaSet o Deployment que seria més eficaç donat tal cas.
Limitacions a tenir en compte
- L'emmagatzematge per a un pod determinat ha de ser aprovisionat mitjançant un “PersistentVolume” o volum persistent en funció de la classe d'emmagatzematge sol·licitada, o bé, prèviament de mà de l'administrador.
- Eliminar i/o reduir un StatefulSet no eliminarà els volums associats a aquest. A causa que sempre tractem de garantir la seguretat i persistència de les dades, normalment més valuoses que una eliminació automàtica de tots els recursos associats al StatefulSet.
- Actualment, els StatefulSets necessiten un servei “Headless” o sense cap que ha de ser el responsable de la identitat de xarxa dels pods desplegats. En aquest cas hem de crear aquest servei pel nostre costat.
- Els StatefulSets no ofereixen cap garantia en termes de finalització dels pods a causa de l'eliminació del StatefulSet. Per poder garantir una finalització ordenada i sense afectació dels pods hem d'utilitzar la possibilitat d'escalar el StatefulSet a 0 abans d'eliminar-lo.
- Quan treballem amb actualitzacions contínues (rolling updates) sobre les polítiques d'administració de pods predeterminades (OrderedReady), és possible rebre un estat d'interrupció o trencament que requereixi intervenció manual per a la seva reparació.
Components d'un Kubernetes StatefulSet
Anem a analitzar el següent exemple d'un StatefulSet.
El següent exemple ha estat extret de documentació oficial de Kubernetes i per tant no revela cap tipus de configuració o dada sensible.
Observacions:
- Línia 1-13: El servei “headless“ o sense cap, anomenat “nginx” i utilitzat per controlar el domini de la xarxa. Podem obrir un port específic i assignar-li un àlies.
- Línia 15-49: El StatefulSet anomenat “web” té una “spec“ (especificació) a la línia 24 que ens indica el nombre de rèpliques del contenidor de “nginx” que seran llançades en pods únics.
- Línia 41-49: El “volumeClaimTemplate” ens facilitarà l'emmagatzematge estable(persistent) utilitzant un “PersistentVolume” aprovisionat pel “PersistentVolume Provisioner”.
El nom de l'objecte “StatefulSet” ha de ser un nom vàlid per al subdomini especificat en el DNS.
- Línia 20-22: Hem d'especificar una etiqueta, però tenint en compte que haurà de ser la mateixa que l'etiqueta especificada en el camp “app” a la línia 29. No etiquetar correctament el camp del “Pod selector” resultarà en un error de validació durant el procés de creació del StatefulSet.
- Línia 25: Podem especificar “minReadySeconds“ dins de “specs” (especificacions) el qual és un camp opcional i determina els segons que han de passar des de la creació d'un nou pod corrent i en estat llest o “ready” sense que cap dels seus contenidors es trenquin per ser considerat en estat disponible o “available”. Aquesta opció és especialment útil quan apliquem actualitzacions contínues (rolling updates) per comprovar el progrés d'aquesta actualització.
Identitat del pod
Els pods que pertanyen a un StatefulSet posseeixen un identificador únic compost per un ordinal (número ordinal), una identitat de xarxa persistent i emmagatzematge persistent també. La identitat o identificador s'associa al pod sense importar en quin node es programi((re)schedule).
Índex ordinal
Per a un StatefulSet amb n rèpliques, a cada pod li serà assignat un número enter ordinal des de 0 fins a n-1, únic en el seu conjunt.
Identificador Persistent de Xarxa
Cada pod d'un StatefulSet deriva el seu nom del nom del StatefulSet i de l'ordinal del pod. El patró per al nom del host(pod) construït és $(statefulset name)-$(ordinal).
El StatefulSet pot utilitzar un servei "headless" o sense cap per controlar el domini dels seus pods. Aquest domini té la següent nomenclatura $(nom del servei).$(namespace).svc.cluster.local, on “cluster.local” és el domini del clúster.
A mesura que es crea cada pod, obtenim un subdomini DNS coincident, amb la següent forma; $(podname).$(domini del servei), on el domini del servei es defineix mitjançant el camp “serviceName” en el StatefulSet.
Depenent de la configuració del DNS en el seu clúster, és possible que no pugui cercar el nom de DNS per a un pod recentment executat. Aquest comportament ocorre quan altres clients en el clúster ja van enviar consultes a aquest nom d'host del pod abans que es creés. L'emmagatzematge en memòria cau en negatiu significa que els resultats de cerques fallides anteriors es recorden i es reutilitzen, fins i tot després d'haver executat el pod.
Per tal que aquest problema no ocorri i no tenir uns segons de caiguda o d'errors després de crear un pod podem utilitzar alguna de les solucions descrites a continuació.
- Llançar les consultes directament a través de l'API de Kubernetes (per exemple, utilitzant un rellotge) en lloc de confiar en les cerques de DNS.
- Reduir el temps d'emmagatzematge en memòria cau en el seu proveïdor de DNS en Kubernetes (normalment configurar el “configmap” per a CoreDNS, normalment amb valor en 30s).
"Recordar que com s'ha esmentat a l'apartat de limitacions, nosaltres mateixos som responsables de crear el servei headless (sense cap) responsable de la identitat de xarxa dels pods."
A continuació, alguns exemples de com afecta el Domini del clúster, el nom del servei i el nom del StatefulSet al DNS dels pods.
Emmagatzematge persistent
Per a cada entrada “VolumeClaimTemplate” definida en un StatefulSet, cada pod rep un “PersistentVolumeClaim”. En l'exemple de cada “nginx” plantejat anteriorment, cada pod rep un sol PersistentVolume amb StorageClass “my-storage-class“ i un 1Gib d'emmagatzematge aprovisionat.
Quan un pod es reprograma ((re)schedule) en un node, els seus “VolumeMounts” munten els “PersistentVolumes” associats amb els seus respectius “PersistentVolumesClaims”.
Etiqueta de nom del pod
Quan el “Controller” del StatefulSet crea un pod, afegeix una etiqueta, “statefulset.kubernetes.io/pod-name”, aquesta s'estableix en el nom del pod. Aquesta mateixa etiqueta ens permet associar un servei a un pod específic en el StatefulSet.
Garanties de Desplegament i Escalat
Si tenim un StatefulSet amb n rèpliques, quan els pods estan sent desplegats, ho fan de forma seqüencial en el següent ordre {0..N-1}.
Quan els pods estan sent eliminats, ho fan de forma inversa és a dir del {N-1..0}.
Abans que una operació d'escalat s'apliqui a un pod, tots els predecessors d'aquest han d'estar en estat "Running" i "Ready".
Abans que un pod sigui acabat tots els seus successors han de tancar-se completament.
Mai hem d'especificar a “pod.Spec.TerminationGracePeriodSeconds” un valor de 0. Aquesta pràctica és insegura i mai recomanada.
Estrategies d'Actualització
El camp .spec.updateStrategy ens permet configurar o desactivar actualitzacions contínues (rolling updates) automàtiques per als contenidors, etiquetes, recursos requests i limits i també annotations per als pods en un StatefulSet. Tenim dos possibles valors.
OnDelete: Quan establim el tipus de “updateStrategy” a “OnDelete”, el controlador del StatefulSet automàticament no actualitzarà els pods que pertanyin als pods. Els usuaris haurem d'eliminar manualment els pods per forçar el controlador a crear nous pods que reflecteixin les modificacions realitzades al template del StatefulSet.
RollingUpdate: Aquest valor implementa les actualitzacions automàtiques, per defecte aquest és el valor establert per a l'estratègia d'actualització.
Actualitzacions contínues
Quan l'estratègia d'actualitzacions (spec.updateStrategy.type) d'un StatefulSet s'estableix en RollingUpdate, el controlador (controller) de StatefulSet eliminarà i tornarà a crear cada pod en el StatefulSet. Es procedeix en el mateix ordre que quan acabem pods (d'ordinal més gran al més petit). Actualitzant cada pod d'un en un.
El controlplane de Kubernetes espera fins que un pod actualitzat estigui en execució i llest abans d'actualitzar el seu predecessor. El controlplane respecta el valor que li hàgim assignat a “spec.minReadySeconds” des que el pod canvia el seu estat a “llest” abans de continuar actualitzant la resta de pods.
Particions
La estratègia d'actualització “rolling update” ens permet fer l'actualització de forma parcial, si especifiquem una partició (spec.updaterStrategy.rollingUpdate.partition) tots els pods que tinguin un ordinal més gran que el valor de la partició seran actualitzats quan el template (spec.template) del StatefulSet es modifiqui.
Els pods que tinguin un ordinal menor al valor de la partició no seran actualitzats i encara que s'eliminen i es tornin a desplegar ho faran amb la versió anterior del template del StatefulSet. Si el valor de la partició és més gran que el nombre de rèpliques (spec.replicas) cap pod serà actualitzat.
Retrocediment forçat
Quan utilitzem l'estratègia d'actualització per defecte “OrderedReady”, és possible entrar en un estat de fallada en què es necessiti intervenció manual per reparar-lo. Si actualitzem el template d'un pod i la nostra configuració mai converteix el seu estat en “running” o “ready” el StatefulSet deixarà d'actualitzar-se i quedarà a l'espera.
Aquest estat no és suficient per poder revertir el pod template a una bona configuració. A causa d'un problema conegut, el StatefulSet continuarà esperant al pod trencat que el seu estat canviï a “ready“, cosa que mai passarà, abans d'intentar revertir cap a la configuració funcional.
Un cop revertida la plantilla, hem d'eliminar els pods del StatefulSet que s'hagin intentat desplegar amb la configuració errònia. Un cop fet això, el mateix StatefulSet començarà a recrear els pods utilitzant la plantilla tirada enrere cap a la configuració correcta.
Rèpliques
El camp opcional “spec.replicas” especifica el número desitjat de pods, el valor per defecte és 1.
Si hem d'escalar automàticament un desplegament podem utilitzar directament el comandament kubectl o bé modificar el fitxer “.yml” i tornar a desplegar el StatefulSet. Amb el comandament kubectl:
"kubectl scale statefulset statefulset –replicas=X"
Si tenim un “HorizontalPodAutoscaler” o una API similar per escalar automàticament no hem d'especificar aquest camp ja que el control plane de Kubernetes actuarà per nosaltres.
Share