No todo esto es nuevo en 1.37.
Kubernetes v1.37 se publicó el 26 de agosto de 2026. La mayoría de los cambios que se le atribuyen aterrizaron en la v1.34 y la v1.35, uno todavía está a una versión de distancia en la v1.38, y el único que puede dejar pods atascados de verdad en ContainerCreating apenas se menciona. Aquí está el recuento, la auditoría que hay que hacer antes de actualizar y el runbook.
- Kubernetes
- SELinux
- Actualizaciones
- Plataforma
Kubernetes v1.37 se publicó el 26 de agosto de 2026, y la cobertura a su alrededor tiene una forma que conviene notar. Se le atribuyen a esta versión varios cambios que no le pertenecen: el kubelet negándose a arrancar sobre cgroup v1 (ese valor por defecto cambió en la v1.35), los pods estáticos perdiendo sus referencias a Secrets y ConfigMaps (activado por defecto desde la v1.34) y la eliminación del modo ipvs de kube-proxy (arrastra un aviso de obsolescencia desde la v1.35, y la eliminación apunta a la v1.43). Y hay un cambio que se cuenta mal en el sentido contrario: containerd 1.x sigue funcionando contra un kubelet v1.37, y el precipicio es la v1.38. Mientras tanto, el único cambio de esta versión que sí puede dejar pods atascados en ContainerCreating el día de la actualización —SELinuxMount pasando a GA y activándose por defecto— se lleva un párrafo, porque es invisible para cualquiera cuyos clústeres no usen SELinux.

La distinción no es pedantería, porque cada columna implica un trabajo distinto. Si el cambio de cgroup te sorprende en la 1.37, ya llevabas dos versiones de retraso y el arreglo es un reinicio por nodo en lugar de una vuelta atrás. Si reconstruyes el runtime de contenedores a la carrera porque alguien te dijo que containerd 1.x había dejado de funcionar, te habrás gastado una ventana de mantenimiento en una fecha límite que todavía está a una versión de distancia: real, pero no hoy. Y si el cambio de SELinux te sorprende en la 1.37, tienes pods que no arrancan y la auditoría que los habría encontrado es bastante más difícil de hacer una vez pasada la actualización. Así que este artículo hace dos cosas: separa el recuento con honestidad y luego entra a fondo en las partes que dan trabajo — la auditoría de SELinux y la salida completa, los calendarios reales de ipvs y de containerd, y un runbook que pone los pasos irreversibles en el orden correcto.
Las formas de fallar, y a qué versión pertenece cada una
Empieza por lo que verías en realidad, porque ninguno de estos fallos anuncia su propia causa. Un pod se queda en ContainerCreating para siempre en un nodo mientras un pod idéntico funciona bien en otro: eso es un conflicto de etiquetas de SELinux, y lo que decide es qué pod llegó primero al volumen. Un nodo vuelve de una actualización y su kubelet no arranca en absoluto: eso es casi con seguridad cgroup v1, y es así desde la v1.35. Un agente de monitorización que lleva corriendo como pod estático desde 2021 falla exactamente en un nodo, el que acabas de actualizar: esa es la referencia a un Secret que nunca debió haber podido usar, y el valor por defecto que se la quitó aterrizó ya en la v1.34.[sneak]
| Lo que ves | Lo que suele significar | Dónde se trata |
|---|---|---|
Un pod atascado en ContainerCreating en un nodo; un pod idéntico funciona en otro | Un conflicto de etiquetas de SELinux en un volumen compartido. El pod que llegó primero al volumen sostiene el único contexto del montaje | SELinuxMount |
| El kubelet no arranca en absoluto tras una actualización | cgroup v1 sin el override failCgroupV1: false. Esto es así desde la v1.35 | cgroup v1 |
| Un agente de monitorización que lleva años siendo pod estático falla solo en el nodo actualizado | Un secretRef o un configMapRef en el manifiesto de un pod estático. Prohibido por defecto desde la v1.34, así que muerde a los clústeres que se saltaron versiones | Pods estáticos |
| kube-proxy escribe un aviso de obsolescencia y todo sigue funcionando | Modo ipvs, obsoleto desde la v1.35. La v1.37 añade la puerta KubeProxyIPVS; la eliminación apunta a la v1.43 | ipvs |
Los pods corren, y la métrica cri_losing_support es distinta de cero en algunos nodos | containerd 1.x detrás del fallback de driver de cgroups de CRI. Sigue soportado en la v1.37; el fallback desaparece en la v1.38 | containerd |
El servidor de API escribe un aviso cada vez que se aplica un Service con externalIPs | No es la v1.37 en absoluto — los ExternalIPs de Service quedaron obsoletos en la v1.36. Todavía no se ha eliminado nada y nada deja de funcionar | El recuento |
kubectl top y el HPA siguen funcionando después de la actualización | Correcto. metrics.k8s.io gradúa a v1 y se siguen sirviendo las dos versiones | metrics.k8s.io |
El patrón que conviene interiorizar es que una actualización de versión saca a la luz todos los cambios desde la última versión sobre la que tenías confianza, no solo los cambios de la versión a la que te mueves. En la práctica los clústeres no se actualizan de una menor en una menor; se quedan en la 1.34 un año y entonces se mueven. Los ExternalIPs de Service son el ejemplo del momento: quedaron obsoletos en la v1.36, donde el servidor de API empezó a avisar en cada uso, y la gente que llega a la v1.37 desde antes se encuentra ese aviso por primera vez. Todavía no se ha eliminado nada —se espera que el soporte en kube-proxy pase a apagado por defecto no antes de la v1.40, con la eliminación no antes de la v1.43—, que es exactamente cómo un cambio a tres versiones vista se convierte en una caída: el aviso llega en una versión cuyas notas no leyó nadie. Por eso la segunda sección de este artículo es un recuento y no un resumen de las notas de la versión.[extip]
El recuento: nuevo en la 1.37, frente a lo que ya había aterrizado
Aquí está la cuenta honesta. La columna de la izquierda es lo que el propio adelanto del equipo de la versión lista para la v1.37; las de la derecha son de dónde viene el cambio en realidad, o hacia dónde sigue yendo. Todo lo que está en las filas «anterior» es algo de lo que ya deberías haberte ocupado, y si no lo has hecho, la ventana de actualización es cuando te vas a enterar; todo lo que está en las filas «posterior» es una fecha que apuntar en el calendario, no trabajo para esta semana.[sneak]
| Cambio | Se le suele atribuir a | Dónde pasa en realidad | Qué hace el día de la actualización a 1.37 |
|---|---|---|---|
SELinuxMount en GA, activado por defecto | 1.37 | 1.37 | Puede dejar pods en ContainerCreating donde dos pods con etiquetas distintas comparten un volumen y el driver de CSI se apuntó. Este es el que da trabajo |
Se añade la puerta de característica KubeProxyIPVS, marcada como obsoleta | 1.37 | 1.37 | Nada. La puerta existe para poder apagar ipvs por defecto más adelante |
kubectl run --filename/-f obsoleto | 1.37 | 1.37 | Solo un aviso, y sobre un flag que ya se ignoraba. Afecta a scripts, no a clústeres |
metrics.k8s.io gradúa a v1 | 1.37 | 1.37 | No se rompe nada. v1 y v1beta1 se sirven las dos durante la transición |
| Los pods estáticos no pueden referenciar Secrets ni ConfigMaps | 1.37 | 1.34 | Nada nuevo, salvo que te hayas saltado versiones, en cuyo caso rompe de plano esos pods estáticos en el nodo que acabas de actualizar |
| El kubelet se niega a arrancar sobre cgroup v1 | 1.37 | 1.35 | Nada nuevo. Si te muerde aquí, el nodo lleva arrastrando failCgroupV1: false desde la v1.35 |
Modo ipvs de kube-proxy obsoleto | «eliminado en 1.37» | 1.35 (solo aviso) | Una línea de log al arrancar, como lleva dos versiones. La puerta pasa a false por defecto en 1.40; eliminación en 1.43 |
ExternalIPs de Service obsoletos | «eliminados en 1.36» | 1.36 (solo aviso) | Un aviso del servidor de API. Apagado por defecto no antes de 1.40; eliminación no antes de 1.43 |
| Desaparece el fallback de CRI de containerd 1.x | 1.36, a veces 1.37 | 1.38 | Nada todavía. containerd 1.x sigue funcionando en 1.37 detrás del fallback, y cri_losing_support cuenta los nodos que dependen de él |
La frase más útil de todo el artículo: si tus nodos no ejecutan SELinux en modo enforcing, la sección más larga de aquí no te aplica en absoluto. El kubelet se salta todo el camino de código de SELinux cuando SELinux no está disponible o está deshabilitado en el kernel. Comprueba eso primero —es un solo comando— porque determina si esto es media jornada de auditoría o una lectura de diez minutos.
Dos de estos merecen una nota sobre cómo verificarlos por tu cuenta en vez de creerte la palabra de nadie. La referencia de puertas de características publica el estado por versión de SELinuxMount, SELinuxChangePolicy y KubeProxyIPVS, que es la forma más rápida de comprobar una afirmación sobre qué versión cambió qué valor por defecto. Y el blog del equipo de cada versión trae su propia lista de obsolescencias; leer tres de ellos lleva veinte minutos y es mejor uso de una ventana de mantenimiento que la mayoría de lo que se mete en una.[gates]
Dónde estás parado en realidad
Antes de que nada de esto sea accionable necesitas cuatro datos, y son independientes entre sí: la versión del kubelet por nodo, si SELinux está en enforcing en ese nodo, qué versión de cgroup ejecuta y en qué modo está kube-proxy. En una flota de cualquier tamaño las respuestas no son uniformes: los pools de nodos avanzan a su propio ritmo, y la política de desfase de versiones permite explícitamente que un kubelet vaya por detrás del servidor de API, así que una flota mezclada es una configuración soportada y no una señal de dejadez.[skew]
# Four numbers decide how much of this article applies to you, and they are
# independent of each other. Ask for all four rather than assuming.
# 1. Control plane and kubelet versions. Node pools drift; this is normal.
kubectl get nodes -o custom-columns=\
'NODE:.metadata.name,KUBELET:.status.nodeInfo.kubeletVersion,'\
'RUNTIME:.status.nodeInfo.containerRuntimeVersion,'\
'KERNEL:.status.nodeInfo.kernelVersion,OS:.status.nodeInfo.osImage'
# NODE KUBELET RUNTIME KERNEL OS
# node-01 v1.36.4 containerd://2.3.2 6.8.0-51 Ubuntu 24.04.3 LTS
# node-02 v1.35.9 containerd://1.7.28 5.15.0-118 Ubuntu 22.04.5 LTS <- two problems
# 2. Is SELinux actually in play? If the answer is "no" on every node, the
# largest section of this article does not apply to you at all.
for n in $(kubectl get nodes -o name); do
printf '%-22s ' "${n#node/}"
kubectl debug "$n" -q -it --image=busybox --profile=general -- \
chroot /host sh -c 'getenforce 2>/dev/null || echo "not installed"' 2>/dev/null
done
# node-01 Enforcing <- section "SELinux" applies
# node-02 not installed <- it does not
# 3. Which cgroup version. v2 shows cgroup2fs; v1 shows tmpfs.
kubectl debug node/node-01 -q -it --image=busybox --profile=general -- \
chroot /host stat -fc %T /sys/fs/cgroup
# cgroup2fs
# 4. Which kube-proxy mode. This is the one people are most often wrong about,
# because the answer usually predates everyone currently on the team.
kubectl -n kube-system get configmap kube-proxy \
-o jsonpath='{.data.config\.conf}' | grep -E '^\s*mode:'
# mode: "ipvs"
# Clean up the debug pods. `kubectl debug node/...` names them
# node-debugger-<node>-<suffix> and applies no label, so there is nothing to
# select on - match the name, or they accumulate silently.
kubectl get pods -o name | grep '^pod/node-debugger-' | xargs -r kubectl deleteEl otro dato que importa es cuánta pista le queda de verdad a la versión en la que estás. Kubernetes soporta las tres versiones menores más recientes, unos catorce meses cada una, con los dos últimos meses en modo mantenimiento, donde solo entran arreglos críticos de seguridad. Ese calendario es la razón de que las actualizaciones no sean opcionales, y conviene tenerlo delante cuando alguien proponga aplazar esta.[k8srel]
| Versión | Publicada | Modo mantenimiento desde | Fin de vida |
|---|---|---|---|
| 1.34 | 27 de agosto de 2025 | 27 de agosto de 2026 | 27 de octubre de 2026 — quedan dos meses |
| 1.35 | 17 de diciembre de 2025 | 28 de diciembre de 2026 | 28 de febrero de 2027 |
| 1.36 | 22 de abril de 2026 | 28 de abril de 2027 | 28 de junio de 2027 |
| 1.37 | 26 de agosto de 2026 | ≈ agosto de 2027 | ≈ octubre de 2027 |
SELinuxMount pasa a GA: el cambio que para pods
Esta es la sección que justifica el artículo. SELinuxMount llega a GA en la v1.37 y viene activado por defecto. El cambio es una mejora de rendimiento y el mecanismo es elegante: en lugar de que el runtime de contenedores recorra un volumen y reetiquete cada inodo —cosa que en un sistema de ficheros grande o remoto es genuinamente lento—, el kubelet monta el volumen con -o context=<etiqueta> y el kernel aplica la etiqueta a todos los inodos de ese montaje en tiempo constante. El problema es consecuencia directa del mecanismo. Un montaje solo puede sostener un contexto de SELinux. Con reetiquetado recursivo, dos pods con etiquetas distintas podían compartir un volumen; con un montaje con contexto no pueden, y uno de los dos se quedará en ContainerCreating hasta que el otro desaparezca.[selblog][kep1710]
| Condición | Dónde comprobarlo | Si no se cumple |
|---|---|---|
| El sistema operativo del nodo soporta SELinux y está en enforcing | getenforce en el nodo | No cambia nada. El kubelet se salta todo el camino de SELinux |
SELinuxMountReadWriteOncePod está activado | En GA e incondicional desde la v1.36 | No aplica en una versión soportada |
SELinuxMount y SELinuxChangePolicy están activados | Puertas de características. SELinuxMount es beta y está apagado en 1.36, en GA y encendido en 1.37 | Las etiquetas las aplica el runtime de forma recursiva, como antes |
El pod expone al menos seLinuxOptions.level | El securityContext del pod o del contenedor | El runtime asigna un nivel aleatorio tras el montaje y reetiqueta recursivamente igualmente |
El driver de CSI pone seLinuxMount: true | kubectl get csidrivers -o custom-columns=… | Sin cambios. Sin definir no es true. Entre los integrados, solo fc, iscsi y rbd soportan la opción |
spec.securityContext.seLinuxChangePolicy está sin definir o en MountOption | La especificación del Pod | Recursive es la salida explícita y conserva el comportamiento antiguo |
El radio de impacto es más estrecho de lo que suena, y merece la pena calcularlo con precisión en vez de suponer lo peor. Tienen que cumplirse cinco condiciones antes de que un solo volumen cambie de comportamiento, y la que se le escapa a la gente es el driver de CSI: el kubelet solo usa un montaje con contexto cuando el driver ha declarado que puede recibirlo, poniendo seLinuxMount: true en su objeto CSIDriver. Un driver que deja el campo sin definir —y sin definir no es true— conserva el comportamiento recursivo de siempre y no le afecta el cambio en absoluto. Los tipos de volumen integrados que soportan la opción de montaje son fc, iscsi y rbd; todo lo demás integrado reetiqueta recursivamente igualmente.[csidriver]
# The blast radius of the SELinuxMount change is not "clusters with SELinux".
# It is the intersection of three things, and all three have to be true before
# a single pod is at risk.
# (a) SELinux enforcing on the node. Checked above. If not, stop here.
# (b) A CSI driver that has opted in. The kubelet only uses the mount option
# when the driver declares it can take one. Drivers that do not set this
# keep the old recursive relabel and are unaffected by the flip.
kubectl get csidrivers \
-o custom-columns='DRIVER:.metadata.name,SELINUXMOUNT:.spec.seLinuxMount'
# DRIVER SELINUXMOUNT
# ebs.csi.aws.com true <- volumes on this driver change behaviour
# efs.csi.aws.com false <- unchanged
# csi.trident.netapp.io <none> <- unset is not true; unchanged
# The in-tree volume types that support the mount option are fc, iscsi and rbd.
# Everything else in tree relabels recursively regardless.
# (c) Two pods with different SELinux labels sharing one volume. This is the
# part you cannot infer from a manifest, because the label is often
# assigned by the runtime rather than written down. The cheapest proxy is
# to find the volumes that more than one workload mounts at all:
kubectl get pods -A \
-o jsonpath='{range .items[*]}{range .spec.volumes[?(@.persistentVolumeClaim)]}'\
'{.persistentVolumeClaim.claimName}{"\t"}{end}{.metadata.namespace}{"\n"}{end}' \
| awk -F'\t' 'NF>1' | sort | uniq -c | sort -rn | awk '$1>1'
# 3 shared-media default
# 2 build-cache ci
# That list is a starting point, not an answer. The answer comes from the
# controller below, which knows the labels.
# One more thing worth knowing before you panic: a pod that mounts a volume
# through different subPaths used to be able to share it across labels too.
# That case also stops working, and it is rare enough that upstream says it has
# never been seen in practice.Se rompen dos patrones de compartición, y solo uno de ellos aparece en la vida real. El primero es dos pods compartiendo un volumen a través de subPaths distintos con etiquetas distintas, que upstream describe como muy de nicho y dice no haber visto nunca en la práctica. El segundo es un pod privilegiado y uno no privilegiado compartiendo un volumen: sigue siendo poco frecuente, pero se ha observado en aplicaciones reales, y es el que hay que ir a buscar. La historia de actualización del propio KEP merece una lectura si eres quien tiene que dar el visto bueno a esto, porque es lo más parecido a un runbook oficial que existe.[kepstory3]
La auditoría que tiene que ocurrir antes de la actualización
Kubernetes v1.36 trajo un controlador para exactamente este problema y casi nadie lo ha activado, porque hay que pedirlo y porque aquello de lo que avisa todavía no había pasado. selinux-warning-controller corre dentro de kube-controller-manager detrás de --controllers=*,selinux-warning-controller, vigila todos los pods del clúster y reporta cada pareja de pods que comparte un volumen de una forma que SELinuxMount no va a permitir. Los reporta incluso cuando los pods están en nodos distintos, con el razonamiento correcto de que el planificador puede juntarlos mañana.[k8s136]
# Kubernetes v1.36 shipped a controller whose entire job is to find these
# conflicts before the upgrade turns them into stuck pods. It is off by
# default and it is the single most useful thing in this article.
#
# It runs inside kube-controller-manager. `*` keeps every default controller
# and adds this one; listing it alone would disable all the others.
# kubeadm clusters: edit the static pod manifest on each control plane node.
sudo vi /etc/kubernetes/manifests/kube-controller-manager.yaml
# spec:
# containers:
# - command:
# - kube-controller-manager
# - --controllers=*,bootstrapsigner,tokencleaner,selinux-warning-controller
# ^^^^^^^^^^^^^^^^^^^^^^^^^
# The kubelet restarts the pod when the file changes. Give it a minute.
# Note the second requirement, which is easy to miss: you must NOT have
# explicitly disabled the SELinuxChangePolicy feature gate. It is GA and on by
# default, so this only bites clusters carrying an old --feature-gates line.
kubectl -n kube-system get pod -l component=kube-controller-manager \
-o jsonpath='{.items[*].spec.containers[*].command}' | tr ',' '\n' \
| grep -E 'feature-gates|controllers='
# Confirm it is running before you trust its silence:
kubectl -n kube-system logs -l component=kube-controller-manager --tail=200 \
| grep -i 'selinux'
# ... "Starting controller" controller="selinux-warning-controller"
# Enabling it has one privacy consequence worth stating: the metric it emits
# carries namespace names as labels, so it can leak namespace names to anyone
# who can read kube-controller-manager metrics. Upstream's assumption is that
# only cluster administrators can.Luego lee las dos métricas, porque responden a preguntas distintas y ninguna basta por sí sola. La del controlador, selinux_warning_controller_selinux_volume_conflict, lleva como etiquetas los nombres y los espacios de nombres de los pods en conflicto: esa es tu lista de trabajo. La del kubelet, volume_manager_selinux_volume_context_mismatch_warnings_total, no tiene ninguna etiqueta con el nombre del pod, pero es la cuenta honesta de los pods que fallarían de verdad, emitida mientras SELinuxMount sigue desactivado. Esa última frase es todo el sentido de hacer esto en la v1.36: el contador de avisos es distinto de cero mientras todo sigue funcionando. Después de la actualización la misma medida aparece como ..._errors_total, y para entonces los pods están atascados en lugar de en riesgo.[selblog]
# There are two metrics and they answer two different questions. You need both:
# one tells you WHICH pods, the other tells you HOW MANY will actually fail.
# --- (1) From kube-controller-manager: which pods conflict, by name ---------
# Reported even when the pods are on different nodes, because the scheduler
# may put them together tomorrow.
kubectl get --raw /metrics 2>/dev/null | true # via your metrics stack, or:
kubectl -n kube-system exec -it \
"$(kubectl -n kube-system get pod -l component=kube-controller-manager \
-o name | head -1)" -- \
curl -sk https://127.0.0.1:10257/metrics \
| grep '^selinux_warning_controller_selinux_volume_conflict'
# selinux_warning_controller_selinux_volume_conflict{
# pod1_name="my-other-pod",pod1_namespace="default",
# pod1_value="system_u:object_r:container_file_t:s0:c0,c1",
# pod2_name="my-pod",pod2_namespace="default",
# pod2_value="system_u:object_r:container_file_t:s0:c0,c2",
# property="SELinuxLabel"} 1
# --- (2) From the kubelet: how many pods would actually fail ---------------
# Emitted while SELinuxMount is still DISABLED, which is exactly the window
# you are in before the upgrade. It has no pod-name label - that is what the
# controller above is for - but it is the honest count.
kubectl get --raw "/api/v1/nodes/node-01/proxy/metrics" \
| grep '^volume_manager_selinux_volume_context_mismatch_warnings_total'
# volume_manager_selinux_volume_context_mismatch_warnings_total{...} 2
# After the upgrade the same measurement lives under a different name, and by
# then the pods are already stuck rather than merely at risk:
# volume_manager_selinux_volume_context_mismatch_errors_total
# The whole point of doing this on v1.36 is that the warnings metric is
# non-zero while everything still works. Read it while it is still cheap.Para cada carga que nombren las métricas tienes dos opciones: arreglar la compartición o sacar a ese pod del cambio. La salida es un campo del Pod, spec.securityContext.seLinuxChangePolicy, y es API estable desde la v1.36 — lo que significa que puedes aplicarlo ya, en tu versión actual, sin ningún cambio de comportamiento, y hará lo correcto en el momento en que aterrice la actualización. Es el seguro más barato de toda esta versión. Ponerlo a Recursive conserva el comportamiento antiguo para ese pod y te cuesta la mejora de rendimiento, que para una carga que ya compartía un volumen entre etiquetas es un intercambio que llevabas haciendo desde siempre.[seccontext][mutadm]
# For every workload the metrics named, you have two options: fix the sharing,
# or opt that pod out of the mount-option path. The opt-out is a Pod field and
# it is stable API as of Kubernetes v1.36.
apiVersion: apps/v1
kind: Deployment
metadata:
name: legacy-shared-cache
spec:
template:
spec:
securityContext:
# Three values:
# unset - follow the cluster default. On v1.37 with SELinuxMount
# enabled, that means mount options.
# MountOption - use mount options explicitly. Only valid while the
# SELinuxMount feature gate is on.
# Recursive - the pre-1.37 behaviour: the runtime relabels every
# file. Slower on large volumes, and it is what lets two
# differently labelled pods share one volume.
seLinuxChangePolicy: Recursive
containers:
- name: app
image: registry.internal.example.com/app:1.4.2
---
# Applying this to every affected workload by hand does not scale, and both
# the SELinux blog and the KEP say so. Prefer a policy. In-tree:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingAdmissionPolicy
metadata:
name: selinux-recursive-optout
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
# Scope this to the namespaces the metrics actually named. A cluster-wide
# opt-out works, but it also throws away the performance win for every
# workload that was never at risk.
matchConditions:
- name: only-flagged-namespaces
expression: "request.namespace in ['default', 'ci']"
failurePolicy: Fail
reinvocationPolicy: IfNeeded
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{ spec: Object.spec{
securityContext: Object.spec.securityContext{
seLinuxChangePolicy: "Recursive" } } }seLinuxChangePolicy | Comportamiento en 1.36 | Comportamiento en 1.37 | Cuándo usarlo |
|---|---|---|---|
| sin definir (por defecto) | Reetiquetado recursivo — SELinuxMount está apagado por defecto | Opción de montaje, si se cumplen las demás condiciones | El valor por defecto para todo lo que no comparta volúmenes entre etiquetas |
MountOption | Solo válido con la puerta de característica activada | Opción de montaje, explícitamente | Rara vez hace falta. En 1.37 el valor por defecto ya hace esto |
Recursive | Reetiquetado recursivo | Reetiquetado recursivo — la salida | El arreglo. Aplícalo a cada carga que nombren las métricas de conflicto, antes de la actualización |
Aplicar ese campo a mano a cada Deployment y StatefulSet afectado no escala más allá de un clúster pequeño, y el blog de SELinux lo dice directamente, nombrando MutatingAdmissionPolicy, webhooks de mutación, Kyverno y Gatekeeper como las formas de hacerlo en bloque. Un consejo sobre el alcance: resiste la tentación de aplicar Recursive a todo el clúster como precaución general. Funciona, y tira a la basura la mejora de rendimiento de todas las cargas que nunca estuvieron en riesgo, que en la mayoría de los clústeres son casi todas. Acótalo a los espacios de nombres que las métricas hayan nombrado de verdad.[kyverno]
Los pods estáticos y sus referencias a Secrets: esto pasó en la 1.34
Este es el fallo que con más probabilidad le va a echar la culpa a la 1.37 alguien que venía de la 1.33. Los pods estáticos los gestiona el kubelet desde un directorio en disco en lugar de crearse a través del servidor de API, así que nunca debieron poder leer objetos de la API; un defecto les permitía referenciar Secrets y ConfigMaps mediante campos como configMapRef y secretRef. La restricción que lo cierra es la puerta de característica PreventStaticPodAPIReferences, y está activada por defecto desde la v1.34, así que en cualquier clúster que haya pasado de verdad por la 1.34, la 1.35 y la 1.36 esto ya ocurrió y los pods afectados ya se rompieron. El adelanto de la v1.37 anunció que se eliminaba la propia puerta, llevándose con ella la salida; la referencia de puertas de características publicada con la v1.37 sigue listando PreventStaticPodAPIReferences como puerta beta con valor true por defecto, así que puede que técnicamente la escapatoria siga ahí. Planifica como si no lo estuviera. Era un defecto y no una funcionalidad, no va a volver, y buscar en los directorios de pods estáticos cuesta menos que descubrir esto nodo a nodo.[staticpod][iss140226]
# Static pods are managed by the kubelet from a directory on disk, not by the
# API server. They were never supposed to be able to read API objects; a bug
# let them, through envFrom.configMapRef, envFrom.secretRef, valueFrom and
# volume references. The gate that closes it - PreventStaticPodAPIReferences -
# has defaulted to true since v1.34, so this is only "new in 1.37" for a
# cluster that skipped releases. The v1.37 sneak peek says the gate was
# removed in this release; the shipped v1.37 feature-gates reference still
# lists it as Beta/true. Plan as if the opt-out is gone: it closed a defect,
# and it is not coming back. Find the references before the upgrade.
# Where the manifests live. Do not assume /etc/kubernetes/manifests: read it
# from the kubelet's own configuration.
sudo grep -E '^staticPodPath:' /var/lib/kubelet/config.yaml
# staticPodPath: /etc/kubernetes/manifests
# The audit, per node. Any hit is a pod that will fail to start on v1.37.
sudo grep -rnE 'configMapRef|secretRef|configMapKeyRef|secretKeyRef|(configMap|secret):' \
/etc/kubernetes/manifests/
# /etc/kubernetes/manifests/node-exporter.yaml:24: secretRef:
# /etc/kubernetes/manifests/node-exporter.yaml:25: name: scrape-creds
# Fleet-wide, without logging into every box. Note that the control plane's
# own static pods (kube-apiserver, etcd, kube-scheduler,
# kube-controller-manager) are generated by kubeadm and do not use these
# references, so a clean result there is expected rather than reassuring.
for n in $(kubectl get nodes -o name); do
printf '%-22s ' "${n#node/}"
kubectl debug "$n" -q -it --image=busybox --profile=general -- \
chroot /host sh -c \
'grep -rlE "configMapRef|secretRef|configMapKeyRef|secretKeyRef" \
/etc/kubernetes/manifests/ 2>/dev/null | tr "\n" " " || true' 2>/dev/null
echo
done
# The fix is to stop being a static pod, or stop needing the reference:
# * A DaemonSet is the right answer for almost everything that is a static
# pod today for historical reasons. It can read Secrets normally.
# * If it has to stay static, put the value in the manifest, or bind-mount a
# file from the host and read it from there. Both are worse than a
# DaemonSet, and both work.El arreglo casi siempre es dejar de ser un pod estático. Muchísimos pods estáticos existen por razones históricas —eran la forma de ejecutar un agente de nivel de nodo antes de que los DaemonSets fueran tan capaces como ahora—, y un DaemonSet puede leer Secrets con normalidad, tiene actualizaciones progresivas y aparece en los sitios donde la gente mira. Donde algo tenga que seguir siendo estático de verdad, las opciones son poner el valor en línea en el manifiesto o montar por bind un fichero del host y leerlo de ahí. Las dos son peores que un DaemonSet y las dos funcionan. Aparte, ten en cuenta que kubectl run --filename/-f también queda obsoleto en esta versión, con el argumento de que el pod que producía se construía siempre exclusivamente a partir de los argumentos de línea de comandos; eso afecta a scripts, no a clústeres.[iss138671]
kube-proxy ipvs: obsoleto desde la 1.35, no eliminado
Ahora el cambio que más se cuenta de más. El modo ipvs de kube-proxy arrastra un aviso de obsolescencia desde la v1.35, no desde la v1.37. Lo que añade la v1.37 es la puerta de característica KubeProxyIPVS, marcada ella misma como obsoleta, junto al aviso que kube-proxy escribe en el log al arrancar. Nada deja de funcionar, no cambia ninguna puerta de característica y no se ve afectado ningún tráfico. El razonamiento detrás de la obsolescencia merece entenderse porque explica por qué nunca hubo arreglo: la API de ipvs del kernel no puede expresar todo lo que necesita un Service de Kubernetes, así que el modo ipvs siempre ha recurrido a iptables por debajo para parte del trabajo. KEP-3866 lo dice sin rodeos en un título de sección: el modo ipvs de kube-proxy no nos va a salvar.[kep5495][kep3866]
| Versión | Qué le pasa al modo ipvs | Qué tienes que hacer |
|---|---|---|
| 1.35 | Queda obsoleto. kube-proxy escribe un aviso al arrancar | Nada, pero aquí es cuando arrancó el reloj |
| 1.37 | Se añade la puerta de característica KubeProxyIPVS, marcada ella misma como obsoleta | Nada. Planifica la migración; no la corras |
| 1.38 – 1.39 | Sigue funcionando, sigue avisando | Migra al modo nftables en la ventana que elijas |
| 1.40 | Se espera que la puerta KubeProxyIPVS pase a false por defecto | Vuelve a activarla con la puerta, o tenerlo ya terminado |
| 1.43 | Soporte eliminado por completo | Ya no queda nada que hacer: esta es la fecha límite |
El destino es el modo nftables, que está en GA desde la v1.33 y es el modo recomendado para nodos Linux. Los clústeres no migran solos; hay que poner mode: "nftables" explícitamente. El requisito de kernel es real pero no oneroso —todo kernel demasiado antiguo para soportar el modo nftables sale de LTS antes de que acabe 2026— y los arreglos de nftables se retroportaron a las ramas 1.33 y 1.34 precisamente para que los usuarios de ipvs en versiones anteriores pudieran migrar sin actualizar Kubernetes primero.[nftblog]
# What actually happens on v1.37 if you run ipvs mode: kube-proxy logs the
# same deprecation warning it has logged since v1.35. That is all. Nothing
# stops working, no feature gate flips, and no traffic is affected. What v1.37
# adds is the KubeProxyIPVS gate, itself marked deprecated - the switch that
# will later be used to turn the mode off by default.
kubectl -n kube-system logs -l k8s-app=kube-proxy --tail=50 | grep -i deprecat
# W0826 ... "ipvs mode of kube-proxy is deprecated and will be removed in a
# future release; see KEP-5495"
# The reason is worth knowing, because it explains why there is no fixing it:
# the kernel's ipvs API cannot express everything a Kubernetes Service needs,
# so ipvs mode has always fallen back to iptables underneath for parts of the
# job. It was never the clean escape from iptables it was sold as.
# The destination is nftables mode, GA since v1.33. Clusters never migrate on
# their own - you have to set it.
# --- migrating, on a kubeadm cluster ---------------------------------------
# 1. Check the kernel. nftables mode wants a reasonably modern kernel; every
# kernel too old for it leaves LTS by the end of 2026.
kubectl get nodes -o jsonpath='{range .items[*]}{.status.nodeInfo.kernelVersion}{"\n"}{end}' \
| sort -u
# 2. Change the mode in the ConfigMap.
kubectl -n kube-system get configmap kube-proxy -o yaml > /tmp/kube-proxy.bak.yaml
kubectl -n kube-system patch configmap kube-proxy --type merge -p \
"$(printf '{"data":{"config.conf":%s}}' \
"$(kubectl -n kube-system get cm kube-proxy -o jsonpath='{.data.config\.conf}' \
| sed 's/^\(\s*mode:\).*/\1 "nftables"/' | jq -Rs .)")"
# 3. Roll the DaemonSet one node at a time and watch, rather than all at once.
kubectl -n kube-system rollout restart daemonset/kube-proxy
kubectl -n kube-system rollout status daemonset/kube-proxy --timeout=10m
# 4. Verify from the data plane, not the control plane. A Service that
# resolves but does not connect is the failure mode here.
kubectl run nft-check --rm -it --restart=Never --image=busybox -- \
sh -c 'wget -qO- --timeout=5 http://kubernetes.default.svc/healthz || echo FAILED'
# Rolling back is the same edit in reverse; keep /tmp/kube-proxy.bak.yaml.
# Do this as its own change, on its own day. Bundling a proxy-mode migration
# into a version upgrade means that when connectivity breaks you will not know
# which one did it.Un consejo de secuencia, dicho con sentimiento: haz la migración de modo de proxy en su propia ventana de mantenimiento, en su propio día, no metida dentro de la actualización de versión. Los dos cambios tocan el plano de datos, y cuando se rompa la conectividad en una ventana que contenía los dos, te vas a pasar la caída averiguando cuál de ellos fue en lugar de arreglándola. Aquí no hay ninguna presión de calendario que justifique combinarlos: la política de obsolescencia garantiza una pista larga a una funcionalidad en beta o mejor, y los criterios de graduación del propio KEP ponen la puerta de característica en false por defecto en la v1.40 y la eliminación en la v1.43.[kubeproxycfg][deprecpolicy]
cgroup v1: esto pasó en la 1.35
La historia de cgroup v1 es la que peor se atribuye, y acertar con ella cambia lo que haces al respecto. El ajuste failCgroupV1 del kubelet está en true por defecto desde Kubernetes v1.35. Un nodo que siga en cgroup v1 lleva por tanto negándose a arrancar su kubelet desde entonces, salvo que alguien añadiera failCgroupV1: false, cosa que mucha gente hizo, a toda prisa, durante la actualización a la v1.35, y luego nunca revisó. Así que la pregunta útil camino de la v1.37 no es «¿se va a romper esto?» sino «¿quién sigue arrastrando el override, y por cuánto tiempo más?».[kep5573][kubeletcfg]
# This is the change most often misattributed to v1.37. The kubelet setting
# `failCgroupV1` has defaulted to TRUE since Kubernetes v1.35. A node still on
# cgroup v1 has therefore been refusing to start its kubelet since v1.35,
# unless somebody added the override - which many people did, in a hurry, and
# then forgot.
# So the useful question on the way to v1.37 is not "will this break" but
# "who is still carrying the override?"
for n in $(kubectl get nodes -o name); do
printf '%-22s ' "${n#node/}"
kubectl debug "$n" -q -it --image=busybox --profile=general -- \
chroot /host sh -c \
'printf "cgroup=%s override=%s\n" \
"$(stat -fc %T /sys/fs/cgroup)" \
"$(grep -c failCgroupV1 /var/lib/kubelet/config.yaml 2>/dev/null)"' 2>/dev/null
done
# node-01 cgroup=cgroup2fs override=0 <- fine
# node-02 cgroup=tmpfs override=1 <- v1, running on borrowed time
# The override itself, for reference. It is a stopgap and upstream says so:
# apiVersion: kubelet.config.k8s.io/v1beta1
# kind: KubeletConfiguration
# failCgroupV1: false
# v1.37 still honours it. KEP-5573 removes cgroup v1 support outright in a
# later release, and no date has been committed to, so the honest planning
# assumption is "the next one that suits SIG Node" rather than a fixed month.
# What you lose in the meantime is not theoretical. In-place pod resizing and
# tiered memory protection - both features people are actively asking for -
# depend on cgroup v2 and simply do not work on a v1 node.
# Switching the node is a kernel command line change and a reboot:
# systemd.unified_cgroup_hierarchy=1
# ...and then the container runtime's cgroup driver has to agree with the
# kubelet's. That is a longer job than it looks, which is why it has its own
# article.La v1.37 sigue respetando el override, y KEP-5573 elimina el soporte de cgroup v1 por completo en una versión posterior sin ninguna fecha comprometida, así que la hipótesis honesta de planificación es «la versión que le venga bien a SIG Node» y no un mes que puedas poner en un plan. Lo que renuncias mientras tanto no es teórico: el redimensionado de pods en caliente y la protección de memoria por niveles dependen ambos de cgroup v2 y sencillamente no funcionan en un nodo con v1, y son funcionalidades que los equipos están pidiendo activamente. El cambio en sí es una modificación de la línea de comandos del kernel y un reinicio, más hacer que el driver de cgroups del runtime de contenedores coincida con el del kubelet, que es más trabajo del que suena y tiene su propio artículo.[cgroups]
containerd 1.x: el precipicio es la 1.38, no ahora
Este se cuenta mal en el sentido contrario, y equivocarse aquí te cuesta una ventana de mantenimiento que no hacía falta gastar. El soporte de containerd 1.x no se eliminó en la v1.36, y tampoco se elimina en la v1.37. Un kubelet v1.37 sigue funcionando contra él: con KubeletCgroupDriverFromCRI activado, el kubelet le pide al runtime su driver de cgroups por la RPC de CRI RuntimeConfig, containerd 1.y no implementa esa RPC, y el kubelet recurre calladamente a su propio valor de --cgroup-driver mientras incrementa la métrica cri_losing_support. Ese fallback estaba previsto originalmente para desaparecer en la v1.37 y se aplazó una versión, explícitamente para alinearlo con la ventana de soporte de containerd v1.7. La documentación de runtimes de Kubernetes ya lo dice sin rodeos: en la v1.38 el fallback desaparece, y las versiones antiguas de containerd fallan contra kubelets más nuevos.[k8s134][ctrrel]
# The change most often stated backwards. containerd 1.x was NOT removed in
# v1.36, and it still runs against a v1.37 kubelet. What it runs on is a
# fallback: the kubelet asks the runtime for its cgroup driver over the
# RuntimeConfig CRI RPC, containerd 1.y does not implement that RPC, and the
# kubelet falls back to its own --cgroup-driver value. That fallback was
# scheduled to go in v1.37 and was deferred one release to align with
# containerd v1.7's support window, so it disappears in v1.38.
# The audit is a metric, not a spreadsheet. Every node relying on the fallback
# increments this, so scrape it rather than walking nodes by hand.
kubectl get --raw /metrics | grep '^cri_losing_support'
# cri_losing_support{version="1.38.0"} 1
# Then confirm which nodes, and with what:
kubectl get nodes -o custom-columns=\
'NODE:.metadata.name,KUBELET:.status.nodeInfo.kubeletVersion,'\
'RUNTIME:.status.nodeInfo.containerRuntimeVersion' | sort -k3
# NODE KUBELET RUNTIME
# node-02 v1.37.0 containerd://1.7.28 <- works today, fails on 1.38
# node-01 v1.37.0 containerd://2.3.2
crictl version
# RuntimeName: containerd
# RuntimeVersion: v2.3.2
# RuntimeApiVersion: v1
# Two things make this less comfortable than "one release of runway" sounds:
# containerd 1.7's own extended support ends in September 2026, and
# containerd's published Kubernetes support matrix has no row for 1.37 yet -
# the last row is 1.36 (2.3.0+, 2.2.0+). Book the migration before 1.38, in
# its own window. Landing two runtime-level changes together means that when a
# node comes back wrong you will be bisecting instead of fixing.Así que la fecha límite es real y está a exactamente una versión de distancia: mejor posición de lo que sugiere el pánico, y peor de lo que sugiere no hacer nada. Hay dos cosas que la aprietan. El reloj del lado del runtime se agota antes: containerd 1.7 es la rama LTS de la que depende esto, y su soporte extendido termina en septiembre de 2026, que es ahora. Y la propia matriz de soporte de Kubernetes de containerd no tiene hoy ninguna fila para Kubernetes 1.37: la última fila publicada es la 1.36, que lista 2.3.0+ y 2.2.0+, así que quien te dé una versión de containerd bendecida para la 1.37 está extrapolando, no citando. En la práctica: recoge cri_losing_support, que ya la está emitiendo cada nodo que necesita el fallback y es mejor auditoría que recorrer nodos a mano, y luego confirma con containerRuntimeVersion. Haz la migración del runtime antes de la 1.38 y en su propia ventana. Meter dos cambios de nivel de runtime juntos significa que cuando un nodo vuelva mal estarás bisecando en lugar de arreglando.[runtimes]
metrics.k8s.io llega por fin a v1
La buena noticia de esta versión, y es buena de verdad. metrics.k8s.io gradúa a v1 después de casi nueve años en beta. Esta es la API que hay detrás de kubectl top y detrás de las métricas de CPU y memoria del HorizontalPodAutoscaler, lo que la convierte en una de las interfaces más usadas de Kubernetes y en una cosa rara de haber dejado en beta tanto tiempo. La graduación reconoce estabilidad en lugar de introducir cambios: no se esperan diferencias funcionales, y tanto v1 como v1beta1 se siguen sirviendo durante la transición.[kep5207][metricspipe]
# The good news in this release. metrics.k8s.io graduates to v1 after nearly
# nine years in beta. Both versions stay served during the transition, so
# there is nothing to do on upgrade day - this is a thing you can adopt on
# your own schedule rather than a thing that happens to you.
kubectl get --raw /apis/metrics.k8s.io | jq -r '.versions[].groupVersion'
# metrics.k8s.io/v1
# metrics.k8s.io/v1beta1
# What consumes it: `kubectl top`, and the HorizontalPodAutoscaler's cpu and
# memory metrics. Both keep working without changes.
kubectl top nodes
kubectl top pods -A --sort-by=memory | head
# Where it matters is code you own. Anything that talks to the API directly -
# a custom autoscaler, a capacity report, a dashboard backend - should move
# off v1beta1 while both are available rather than after one is not.
kubectl get --raw /apis/metrics.k8s.io/v1/nodes | jq '.items[0]'
# Find the clients still on the beta path, from the API server's own counters:
kubectl get --raw /metrics \
| grep 'apiserver_requested_deprecated_apis\|metrics.k8s.io.*v1beta1' | head
# There is no removal date for v1beta1 yet. Kubernetes' deprecation policy
# guarantees a beta API at least nine months or three releases after
# deprecation, so this is a housekeeping item, not a deadline.Así que no hay nada que hacer el día de la actualización, que es justamente el punto: esto es algo que adoptas a tu propio ritmo y no algo que te pasa. Donde sí importa es en el código que es tuyo: un autoescalador propio, un informe de capacidad, el backend de un panel, cualquier cosa que hable directamente con la API debería salirse del camino beta mientras estén las dos disponibles y no cuando ya no lo esté una. Todavía no hay fecha de eliminación para v1beta1, y la política de obsolescencia garantiza a una API beta al menos nueve meses o tres versiones, así que trátalo como mantenimiento y no como una fecha límite.[hpa]
El resto de la versión, en corto
Hay tres cosas más en esta versión que merece la pena conocer aunque ninguna vaya a afectar a una actualización. Las tres son graduaciones y no eliminaciones, y la primera es la que hay que vigilar.[kep4960]
- El kubelet dentro de un espacio de nombres de usuario —modo rootless— llega a beta. Los componentes de nodo han corrido tradicionalmente como root en el host. Esto les permite correr como un usuario no privilegiado en el host mientras siguen apareciendo como root dentro de un espacio de nombres de usuario de Linux, lo que acota el radio de impacto de una vulnerabilidad en un componente de nodo. Beta aquí significa que la puerta viene activada por defecto, pero activarla no hace por sí sola que el kubelet corra dentro de un espacio de nombres de usuario —hay configuración del host detrás— y el cambio ni siquiera entró en el anuncio de la versión. Trátalo como algo que probar en un nodo de pruebas y no como algo que te haya pasado. Tampoco es la misma funcionalidad que los espacios de nombres de usuario para pods, que llegó a beta en la v1.35 y a GA en la v1.36.
- El reetiquetado SELinux de volúmenes ReadWriteOncePod ya estaba en GA en la v1.36. Eso es
SELinuxMountReadWriteOncePod, una puerta más estrecha que elSELinuxMountdel que trata este artículo, y es la razón de que algunos clústeres lleven un tiempo montando con opción de contexto sin que se rompa nada: los volúmenes RWOP no se pueden compartir por definición, así que el conflicto que describe este artículo no puede darse ahí. - La monitorización de salud de volúmenes vuelve a empezar en alfa. Una implementación inicial aterrizó en la v1.21 y nunca graduó; KEP-1432 la reinicia detrás de la puerta
CSIVolumeHealthe introduce cuatro RPC de CSI —ControllerListVolumeHealth,ControllerGetVolumeHealth,NodeGetVolumeHealthyNodeGetStorageHealth— que reportan aPersistentVolumeClaim.status.healthStatus,Pod.status.volumeHealthyCSINode.status.storageHealth. El vocabulario es deliberadamente pequeño y legible por máquina:Inaccessible,DataLossyDegraded, conreasonymessageen la condición para el detalle propio de cada driver.
Alfa significa apagado por defecto y no apto para producción, pero el trabajo de monitorización de salud merece seguimiento si alguna vez has tenido que cruzar un montaje colgado con el panel de un proveedor de almacenamiento para averiguar qué iba mal. Es la primera respuesta legible por máquina que Kubernetes ha ofrecido a esa pregunta.[kep1432][userns]
La actualización, en orden
El orden importa más que los comandos. Todo lo genuinamente difícil de esta actualización es difícil antes de ella, y el único paso que se vuelve bastante más caro después es la auditoría de SELinux, así que va primero, con semanas de margen y no con minutos.[kubeadmup]
# The order matters more than the commands, and the SELinux audit has to come
# first because it is the only step that is materially harder after the
# upgrade than before it.
# --- WEEKS BEFORE, on v1.36 ------------------------------------------------
# 1. Turn on selinux-warning-controller, read both metrics, apply the opt-outs.
# 2. Audit static pods for Secret and ConfigMap references. Move them to
# DaemonSets where you can.
# 3. Get every node onto containerd 2.x and cgroup v2, if any are not.
# Both of these are already overdue rather than upcoming.
# 4. Decide about ipvs - and then do it in a DIFFERENT window.
# --- THE DAY ---------------------------------------------------------------
# Control plane first, one node at a time. Nothing here is 1.37-specific;
# it is the standard kubeadm sequence and it is standard because it works.
sudo apt-mark unhold kubeadm && sudo apt-get update \
&& sudo apt-get install -y kubeadm='1.37.0-*' && sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.37.0 # first control plane node
# sudo kubeadm upgrade node # every other control plane node
# Then the kubelet and kubectl on that same node:
sudo apt-mark unhold kubelet kubectl && sudo apt-get update \
&& sudo apt-get install -y kubelet='1.37.0-*' kubectl='1.37.0-*' \
&& sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
# --- WORKER NODES, ONE AT A TIME -------------------------------------------
NODE=node-02
kubectl drain "$NODE" --ignore-daemonsets --delete-emptydir-data --timeout=15m
# ... upgrade kubeadm, run `kubeadm upgrade node`, upgrade kubelet, restart ...
kubectl uncordon "$NODE"
# Then STOP and look, before the next node. The failure this release can
# produce is a pod stuck in ContainerCreating, and it is per-node:
kubectl get pods -A --field-selector spec.nodeName="$NODE" \
-o wide | grep -v Running | grep -v Completed
# The version skew policy is what makes the staged rollout legal: a kubelet
# may be up to three minor versions behind the API server, so a fleet halfway
# through this is a supported configuration rather than a risk in itself.Una tranquilidad sobre hacer esto de forma gradual: la política de desfase de versiones permite que un kubelet vaya hasta tres versiones menores por detrás del servidor de API, así que una flota a medio desplegar es una configuración soportada y no un riesgo en sí misma. Tómate el tiempo. Actualiza un nodo, míralo bien, y solo entonces sigue, porque el fallo que puede producir esta versión es por nodo y aparece como un pod que nunca arranca y no como un error por el que alguien te llame.[skew]
Volver atrás, y lo que no vuelve atrás
La vuelta atrás merece una respuesta directa y no una tranquilizadora, y en esta actualización la respuesta tiene tres partes. Los cambios reversibles son genuinamente reversibles: el modo de kube-proxy es un ConfigMap y un reinicio de DaemonSet, y seLinuxChangePolicy es un campo del Pod que se comporta igual en la v1.36 y en la v1.37, que es precisamente por lo que aplicarlo antes de la actualización no cuesta nada y te compra toda la historia de vuelta atrás.[kubeadmup]
# Rollback deserves a straight answer. Most of this release rolls back; one
# part of it does not roll back in the way people assume.
# --- What rolls back cleanly ----------------------------------------------
# The kube-proxy mode change: it is a ConfigMap and a DaemonSet restart.
kubectl -n kube-system apply -f /tmp/kube-proxy.bak.yaml
kubectl -n kube-system rollout restart daemonset/kube-proxy
# The seLinuxChangePolicy opt-out: it is a Pod field. Setting it to Recursive
# is safe on v1.36 and v1.37 alike, which is why applying it BEFORE the
# upgrade costs nothing and buys the whole rollback story.
# --- What does not ---------------------------------------------------------
# The control plane. `kubeadm upgrade` has no downgrade path: going back means
# restoring the etcd snapshot you took before you started, which means losing
# everything written to the cluster since. If you did not take one, you do not
# have a rollback - you have a forward fix.
sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save "/var/backups/etcd-pre-1.37-$(date +%F).db"
# The static pod references. If a static pod was relying on a secretRef, the
# reference is gone on 1.37 and comes back on a downgrade - but the downgrade
# is the control plane operation above, so in practice the fix is forward:
# move it to a DaemonSet.
# --- The one that surprises people ----------------------------------------
# Downgrading the kubelet does NOT un-stick a pod that failed to mount because
# of an SELinux label conflict, because the pod that WON the volume is still
# holding it with its own context. Terminate one of the two, or set
# seLinuxChangePolicy: Recursive on both and let them share again. Version
# numbers are not the lever here; the Pod field is.El plano de control es la parte que no vuelve atrás. kubeadm upgrade no tiene camino de bajada; ir hacia atrás significa restaurar la instantánea de etcd que tomaste antes de empezar y perder todo lo escrito en el clúster desde entonces. Si no tomaste una, no tienes vuelta atrás: tienes un arreglo hacia delante, que es una conversación distinta para tener a las dos de la mañana. Y hay un fallo que sorprende a la gente: bajar la versión del kubelet no desatasca un pod que falló al montar por un conflicto de etiquetas de SELinux, porque el pod que ganó el volumen lo sigue sosteniendo con su propio contexto. La palanca ahí es el campo del Pod, no el número de versión.[selblog]
Verificar, en lugar de confiar
La verificación en esta actualización tiene una forma concreta, porque el fallo característico lo deja todo en verde. El nodo está Ready. El kubelet está sano. El plano de control está bien. Simplemente hay un pod que nunca termina de crearse, en un nodo, perteneciente a un equipo. Así que comprobar que el clúster está arriba no demuestra absolutamente nada: hay que comprobar las cosas que pueden estar mal en silencio.[chlog]
#!/usr/bin/env bash
# Post-upgrade verification. Exits non-zero when something is wrong, so it can
# run between nodes in a pipeline rather than being read by a person at 3am.
set -uo pipefail
rc=0
fail() { printf ' FAIL %s\n' "$*"; rc=1; }
pass() { printf ' ok %s\n' "$*"; }
echo '== versions =='
kubectl version -o json | jq -r '.serverVersion.gitVersion'
echo '== every node Ready and on the expected version =='
bad=$(kubectl get nodes --no-headers | awk '$2!="Ready"{print $1}')
[ -z "$bad" ] && pass 'all nodes Ready' || fail "not Ready: $bad"
echo '== no pod stuck creating (the SELinux failure mode) =='
stuck=$(kubectl get pods -A --no-headers \
| awk '$4=="ContainerCreating"{print $1"/"$2}')
[ -z "$stuck" ] && pass 'nothing in ContainerCreating' || fail "stuck: $stuck"
echo '== SELinux context mismatches that became real failures =='
for n in $(kubectl get nodes -o name); do
v=$(kubectl get --raw "/api/v1/nodes/${n#node/}/proxy/metrics" 2>/dev/null \
| awk '/^volume_manager_selinux_volume_context_mismatch_errors_total/{s+=$2} END{print s+0}')
[ "$v" = "0" ] && pass "${n#node/}: 0 mismatch errors" \
|| fail "${n#node/}: $v SELinux mismatch errors"
done
echo '== static pods all running =='
sp=$(kubectl get pods -A -o json \
| jq -r '.items[] | select(.metadata.annotations["kubernetes.io/config.source"]=="file")
| select(.status.phase!="Running") | .metadata.namespace+"/"+.metadata.name')
[ -z "$sp" ] && pass 'static pods Running' || fail "static pods not Running: $sp"
echo '== the resource metrics API answers on both versions =='
kubectl get --raw /apis/metrics.k8s.io | jq -e \
'[.versions[].groupVersion] | index("metrics.k8s.io/v1")' >/dev/null \
&& pass 'metrics.k8s.io/v1 served' || fail 'metrics.k8s.io/v1 missing'
kubectl top nodes >/dev/null 2>&1 && pass 'kubectl top works' || fail 'kubectl top broken'
echo '== service traffic actually flows =='
kubectl run verify-net --rm -i --restart=Never --image=busybox --timeout=60s -- \
sh -c 'wget -qO- --timeout=5 http://kubernetes.default.svc/healthz' >/dev/null 2>&1 \
&& pass 'in-cluster Service reachable' || fail 'in-cluster Service unreachable'
exit $rcEjecútalo entre nodos y no al final. El script devuelve un código distinto de cero, lo que significa que puede vivir en un paso de pipeline en lugar de que lo lea una persona a las tres de la mañana, y las dos comprobaciones que merece la pena conservar incluso cuando ya tengas la actualización detrás son el barrido de ContainerCreating y el contador de discrepancias de SELinux. Las dos son baratas, y las dos pillan una clase de problema que si no espera a que alguien se dé cuenta de que su carga nunca volvió.[k8spatch]
El orden en el que hacer esto
Comprimida, esta es una actualización más pequeña de lo que sugiere el artículo, siempre que hicieras el trabajo que correspondía a las dos versiones anteriores. La tabla de decisión de abajo es en realidad una pregunta sobre cuál de esas te saltaste.[sneak]
| Si tu situación es… | Entonces la 1.37 es… | Y el trabajo es… |
|---|---|---|
| En 1.36, sin SELinux en ninguna parte, containerd 2.x, cgroup v2 | Una actualización rutinaria | La secuencia estándar de kubeadm. Busca en los manifiestos de pods estáticos y adelante |
En 1.36, SELinux en enforcing, drivers de CSI con seLinuxMount: true | La que necesita una auditoría | Activa ya el controlador de avisos, lee las dos métricas, aplica Recursive donde apunten y luego actualiza |
| En 1.34 o 1.35, con plan de saltar directamente a 1.37 | Dos o tres versiones de cambios de golpe | Léete las notas de cada versión que te saltas. La restricción de los pods estáticos, el valor por defecto de cgroup y el aviso de ExternalIPs están todos ahí dentro |
| Algún nodo todavía en cgroup v1 | No es tu problema más urgente | Ese nodo lleva arrastrando un override desde la 1.35. Conviértelo, por separado, antes de actualizar |
| Algún nodo todavía en containerd 1.x | Bien hoy, roto en 1.38 | No es trabajo del día de la actualización. Recoge cri_losing_support y luego reserva la migración del runtime, sola, antes de coger la 1.38 |
kube-proxy corriendo en modo ipvs | Una línea de log, nada más | Migra a nftables otro día. Tienes hasta la 1.43, y juntarlo esconde la causa de cualquier caída |
| Un servicio gestionado (GKE, EKS, AKS) | Lo que programe el proveedor | La auditoría de SELinux sigue siendo tuya: las cargas y los drivers de CSI son tuyos aunque los nodos no lo sean |
- Responde primero a la pregunta de SELinux, con un solo comando. Si ningún nodo ejecuta SELinux en modo enforcing, sáltate entera la sección más larga de aquí y trata la 1.37 como una actualización rutinaria. Si algún nodo lo hace, aplica todo lo de abajo.
- Activa
selinux-warning-controlleren la v1.36 y lee las dos métricas. El controlador nombra los pods en conflicto; el kubelet cuenta los que van a fallar de verdad. Hazlo con semanas de antelación, porque es bastante más difícil después de la actualización y para entonces los pods están atascados en lugar de simplemente en riesgo. - Aplica
seLinuxChangePolicy: Recursivea las cargas que nombraron las métricas, por política y no a mano. Es API estable en la v1.36, hoy no cambia nada y hace lo correcto en el momento en que aterrice la actualización. Acótalo a los espacios de nombres implicados y no a todo el clúster. - Busca
secretRefyconfigMapRefen el directorio de pods estáticos de cada nodo. Esto es el valor por defecto desde la v1.34, así que en un clúster que fue pasando por cada versión ya ocurrió; en uno que dio el salto, está esperando. Lleva lo que encuentres a un DaemonSet, que es casi siempre lo que debió haber sido. - Salda la deuda de cgroup v1 de la 1.35, y mete la migración de containerd en el calendario antes de la 1.38. Un nodo con cgroup v1 lleva dos versiones arrastrando un override. Un nodo con containerd 1.x funciona hoy y deja de funcionar dentro de una versión. Cada uno se lleva su propia ventana, y deja la migración de ipvs a nftables para otro día completamente distinto.
Dos de las deudas de arriba tienen su propio runbook, porque ninguna es cosa de cinco minutos: migrar containerd 1.7 a 2.x, que es la eliminación de containerd 1.x vista desde el lado del runtime, y migrar de cgroup v1 a cgroup v2, que es la auditoría y la conversión que hay detrás del override de failCgroupV1. Si la misma ventana de mantenimiento está reconstruyendo además tu ingress, pasar de ingress-nginx a la Gateway API cubre esa migración. Y si este es el momento en el que alguien en la sala pregunta si todo esto merece la pena, cuándo no usar Kubernetes es la otra cara de ese argumento, contada con honestidad.
Preguntas frecuentes
¿Cuándo sale Kubernetes 1.37 y qué se rompe de verdad?
La v1.37 se publicó el 26 de agosto de 2026. Solo un cambio de los que trae puede parar una carga: SELinuxMount llega a GA y viene activado por defecto, lo que puede dejar pods en ContainerCreating donde dos pods con etiquetas distintas comparten un volumen en un nodo con SELinux en enforcing y un driver de CSI que se apuntó. El resto de lo genuinamente nuevo es silencioso: se añade la puerta de característica KubeProxyIPVS y se marca obsoleta al momento, metrics.k8s.io gradúa a v1 con las dos versiones todavía servidas, y kubectl run --filename/-f queda obsoleto, lo que afecta a scripts y no a clústeres. Todo lo demás que se le atribuye a esta versión aterrizó antes o todavía no ha aterrizado: el fallo del kubelet con cgroup v1 es de la v1.35, la restricción de Secrets en pods estáticos es de la v1.34, el modo ipvs está obsoleto desde la v1.35 y se elimina en la v1.43, y containerd 1.x sigue funcionando: su fallback de CRI desaparece en la v1.38.
¿Elimina Kubernetes 1.37 el modo ipvs de kube-proxy?
No, y tampoco lo dejó obsoleto: eso pasó en la v1.35, y kube-proxy lleva desde entonces escribiendo un aviso al arrancar. Lo que añade la v1.37 es la puerta de característica KubeProxyIPVS, marcada ella misma como obsoleta, que es el interruptor con el que más adelante se apagará el modo. Nada deja de funcionar y no se ve afectado ningún tráfico. KEP-5495 establece el calendario: se espera que la puerta pase a false por defecto en la v1.40, con el soporte eliminado por completo en la v1.43. El motivo de la obsolescencia es que la API de ipvs del kernel no puede expresar todo lo que necesita un Service de Kubernetes, así que el modo ipvs siempre recurrió a iptables por debajo para parte del trabajo. El destino de la migración es el modo nftables, en GA desde la v1.33, y los clústeres nunca cambian solos, así que tienes que poner tú mode: "nftables". Hazlo en una ventana de mantenimiento distinta de la de la actualización de versión.
¿Por qué se me quedan los pods en ContainerCreating tras actualizar a 1.37?
Si el nodo ejecuta SELinux en modo enforcing, la causa probable es un conflicto de compartición de volumen introducido por el paso a GA de SELinuxMount. Los volúmenes se montan ahora con -o context=<etiqueta> en lugar de reetiquetarse recursivamente, y un montaje solo puede sostener un contexto de SELinux, así que dos pods con etiquetas distintas compartiendo un volumen en un nodo ya no pueden coexistir. Uno se queda en ContainerCreating hasta que el otro termina. El caso clásico es un pod privilegiado y uno no privilegiado compartiendo un volumen. Confírmalo leyendo la métrica del kubelet volume_manager_selinux_volume_context_mismatch_errors_total en ese nodo. El arreglo es poner spec.securityContext.seLinuxChangePolicy: Recursive en los pods afectados; ojo, porque bajar la versión del kubelet no va a liberar el volumen, porque el pod que lo ganó sigue sosteniendo el contexto.
¿Cómo encuentro los conflictos de volúmenes de SELinux antes de actualizar?
Activa el selinux-warning-controller que llegó en la v1.36 pasándole --controllers=*,selinux-warning-controller a kube-controller-manager, y asegúrate de no haber deshabilitado explícitamente la puerta de característica SELinuxChangePolicy. Después lee dos métricas. selinux_warning_controller_selinux_volume_conflict lleva como etiquetas los nombres y los espacios de nombres de los pods en conflicto: esa es tu lista de trabajo, y reporta conflictos incluso cuando los pods están en nodos distintos, porque el planificador puede juntarlos más adelante. volume_manager_selinux_volume_context_mismatch_warnings_total, que emite el kubelet mientras SELinuxMount sigue desactivado, no tiene etiqueta con el nombre del pod pero da la cuenta honesta de los pods que fallarían de verdad. Necesitas las dos. Hazlo en la v1.36, mientras el contador de avisos es distinto de cero y todo sigue funcionando.
¿Qué hace realmente seLinuxChangePolicy: Recursive, y es seguro ponerlo ya?
Saca a ese pod del camino de la opción de montaje y conserva el comportamiento anterior a la 1.37: el runtime de contenedores recorre el volumen y reetiqueta todos los ficheros. El coste es la mejora de rendimiento —en un volumen grande o remoto, el reetiquetado recursivo es genuinamente lento— y el beneficio es que dos pods con etiquetas distintas pueden volver a compartir el volumen. Es API estable del Pod desde la v1.36, así que ponerlo hoy no cambia nada de cómo se comporta tu clúster ahora mismo y hace lo correcto en el momento en que aterrice la actualización. Eso lo convierte en el seguro más barato de esta versión. Aplícalo mediante una MutatingAdmissionPolicy o un motor de políticas en lugar de editando cada Deployment, y acótalo a los espacios de nombres que nombraron de verdad las métricas de conflicto y no a todo el clúster: un Recursive generalizado tira a la basura la mejora de todas las cargas que nunca estuvieron en riesgo.
Mi clúster no usa SELinux. ¿Me aplica algo de esto?
Casi nada. Cuando SELinux no está disponible o está deshabilitado en el kernel, el kubelet se salta todo el camino de código de SELinux, así que el cambio de SELinuxMount no hace nada en tu caso y puedes ignorar la sección más larga de este artículo. Lo que sí aplica es la restricción de los pods estáticos —busca secretRef y configMapRef en el directorio de pods estáticos de cada nodo, que es el valor por defecto desde la v1.34 y por tanto solo muerde a los clústeres que se saltaron versiones—, además de la deuda de cgroup v2 de la v1.35 si no la has saldado, y de la migración a containerd 2.x, que no es urgente en la 1.37 pero hay que hacerla antes de la 1.38. Comprueba getenforce en todos los nodos en vez de suponerlo, eso sí: una flota mezclada donde un pool de nodos trae una imagen con SELinux activado es más común de lo que se espera.
¿Impide la 1.37 que el kubelet arranque sobre cgroup v1?
Ese cambio no es de la 1.37. El ajuste failCgroupV1 del kubelet está en true por defecto desde la v1.35, así que un nodo con cgroup v1 lleva desde entonces sin poder arrancar su kubelet salvo que alguien añadiera failCgroupV1: false. La v1.37 sigue respetando ese override. KEP-5573 elimina el soporte de cgroup v1 por completo en una versión posterior, pero no hay ninguna fecha comprometida, así que la hipótesis de planificación debería ser «la versión que le venga bien a SIG Node» y no un mes. La razón para no quedarse sentado sobre el override es que el redimensionado de pods en caliente y la protección de memoria por niveles requieren ambos cgroup v2 y sencillamente no funcionan sin él. Cambiar un nodo significa una modificación de la línea de comandos del kernel (systemd.unified_cgroup_hierarchy=1), un reinicio y hacer que el driver de cgroups del runtime coincida con el del kubelet.
¿Necesito containerd 2.0 para Kubernetes 1.37?
No, y esta es la afirmación que más veces se cuenta del revés. containerd 1.x sigue funcionando contra un kubelet v1.37. Lo hace a través de un fallback: el kubelet le pide al runtime su driver de cgroups por la RPC de CRI RuntimeConfig, containerd 1.y no implementa esa RPC, y el kubelet recurre a su propio valor de --cgroup-driver mientras incrementa la métrica cri_losing_support. Ese fallback estaba previsto para eliminarse en la v1.37 y se aplazó una versión para alinearlo con la ventana de soporte de containerd v1.7, así que desaparece en la v1.38, y ahí sí es donde las versiones antiguas de containerd fallan contra kubelets más nuevos. Dos matices que lo hacen menos cómodo de lo que suena: el soporte extendido del propio containerd 1.7 termina en septiembre de 2026, y la matriz de soporte de Kubernetes publicada por containerd todavía no tiene fila para la 1.37, así que ahora mismo nadie puede citar una combinación oficialmente bendecida. Audítalo con cri_losing_support y containerRuntimeVersion, y reserva la migración antes de coger la 1.38, en su propia ventana y no metida en una actualización de versión.
¿Qué sustituye a un pod estático que lee un Secret?
Un DaemonSet, en casi todos los casos. Los pods estáticos los gestiona el kubelet desde un directorio en disco en lugar de crearse a través del servidor de API, así que leer objetos de la API nunca debió funcionar: un defecto lo permitía mediante campos como configMapRef y secretRef. La puerta PreventStaticPodAPIReferences que cierra el defecto está activada por defecto desde la v1.34, así que esto no es nuevo en la 1.37: solo se lo parece a los clústeres que se saltaron varias versiones. El adelanto de la v1.37 decía que la puerta se eliminaba del todo; la referencia de puertas de características publicada con la v1.37 la sigue listando como puerta beta con valor true por defecto. En cualquier caso, no planifiques contando con la salida. La mayoría de los pods estáticos existen por razones históricas, de antes de que los DaemonSets fueran tan capaces como ahora; un DaemonSet lee Secrets con normalidad, tiene actualizaciones progresivas y aparece donde la gente lo busca. Si algo tiene que seguir siendo estático de verdad, pon el valor en línea en el manifiesto o monta por bind un fichero del host y léelo de ahí. Las dos son peores que un DaemonSet y las dos funcionan. Encuéntralos antes de la actualización, en lugar de nodo a nodo.
¿Debería actualizar de la 1.34 o la 1.35 directamente a la 1.37?
Puedes —la política de desfase de versiones permite un kubelet hasta tres versiones menores por detrás del servidor de API, y kubeadm lleva el plano de control de versión en versión—, pero el riesgo no es la mecánica, es la lectura. Saltarse versiones significa que todas las obsolescencias de las versiones que te saltaste llegan de golpe, y están documentadas por versión y no de forma acumulada. Viniendo de la 1.35 heredas además los cambios de la v1.36: el aviso de obsolescencia de los ExternalIPs de Service, SELinuxMountReadWriteOncePod y SELinuxChangePolicy llegando a GA, y los espacios de nombres de usuario para pods pasando a estable. Viniendo de la 1.34 heredas encima el cambio del valor por defecto de failCgroupV1 de la v1.35, que va a parar el kubelet de plano en un nodo con cgroup v1. Léete la entrada del blog del equipo de la versión para cada versión que te estés saltando. Tres de ellas se leen en unos veinte minutos y son mejor uso de una ventana de mantenimiento que la mayoría de lo que se mete en una.
¿Puedo volver atrás de una actualización a 1.37?
En parte, y las partes importan. El cambio de modo de kube-proxy vuelve atrás limpiamente: es un ConfigMap y un reinicio de DaemonSet. seLinuxChangePolicy es un campo del Pod que se comporta igual en la v1.36 y en la v1.37, que es por lo que aplicarlo de antemano no cuesta nada. El plano de control no vuelve atrás: kubeadm upgrade no tiene camino de bajada, así que ir hacia atrás significa restaurar la instantánea de etcd que tomaste antes de empezar y perder todo lo escrito desde entonces. Toma esa instantánea. Y hay un fallo que sorprende: bajar la versión del kubelet no desatasca un pod que falló al montar por un conflicto de SELinux, porque el pod que ganó el volumen sigue sosteniendo el contexto del montaje. La palanca es seLinuxChangePolicy en los dos pods, no el número de versión.
¿Va a desaparecer metrics.k8s.io v1beta1?
Todavía no, y no sin aviso. metrics.k8s.io gradúa a v1 en la v1.37 tras casi nueve años en beta, y tanto v1 como v1beta1 se siguen sirviendo durante la transición precisamente para que la adopción pueda ir a tu ritmo. No se esperan cambios funcionales: la graduación reconoce estabilidad, no la introduce. kubectl top y el HorizontalPodAutoscaler siguen funcionando sin que hagas nada. Lo que sí merece la pena hacer es sacar el código que es tuyo —un autoescalador propio, un informe de capacidad, el backend de un panel— del camino beta mientras estén los dos disponibles. La política de obsolescencia de Kubernetes garantiza a una API beta al menos nueve meses o tres versiones tras la obsolescencia, y a v1beta1 no le han puesto fecha de eliminación en absoluto, así que esto es mantenimiento y no una fecha límite.
Fuentes
Fuentes primarias primero. El adelanto del equipo de la versión y el changelog de la v1.37 son las únicas declaraciones autorizadas sobre qué hay en esta versión; los KEP son el único sitio donde están escritos los calendarios de varias versiones, que es lo que los convierte en el antídoto contra un resumen que dice «eliminado en la 1.37» de algo que se elimina en la 1.43. Donde este artículo corrige algo —que el fallo de cgroup es un cambio de la v1.35, que la restricción de los pods estáticos es un cambio de la v1.34, que ipvs está obsoleto desde la v1.35 y no eliminado en la v1.37, y que el precipicio de containerd lo tienes por delante en la v1.38 y no por detrás en la v1.36— el desacuerdo es con la cobertura secundaria, no con el proyecto. Hay un desacuerdo que es interno al proyecto y merece nombrarse: el adelanto dice que la puerta PreventStaticPodAPIReferences se eliminó en esta versión, y la referencia de puertas de características publicada con la v1.37 la sigue listando. La página de referencia es la que hay que creer sobre lo que se publicó de verdad.
- Kubernetes v1.37 Sneak Peek - the release team's own list of what is deprecated, removed and breaking in this release, published 31 July 2026. This is the document that separates "new in 1.37" from "still in progress", and it carries its own caveat that the information reflects the state of the release before the release date
- Kubernetes CHANGELOG-1.37.md - the authoritative record once the release is cut on 26 August 2026. Where this article and the changelog disagree after that date, the changelog is right and this page is a snapshot of the plan
- SELinux Volume Label Changes goes GA (and likely implications in v1.37) - the pre-announcement by the feature's own authors. It contains the five conditions for a mount-option relabel, the two conflict scenarios, the seLinuxChangePolicy opt-out, the selinux-warning-controller and the recommended upgrade path. This is the single most important source for this article
- KEP-1710: Speed up SELinux volume relabeling using mounts. The enhancement proposal behind SELinuxMount, including why a mount can hold only one context and therefore why volume sharing across differently labelled pods stops working
- KEP-1710, "Story 3: cluster upgrade" - the upgrade scenario written by the authors, which is the closest thing to an official runbook for this change
- Kubernetes - Configure a Security Context for a Pod or Container: seLinuxOptions, the seLinuxChangePolicy field, efficient SELinux volume relabeling and the selinux-warning-controller
- Kubernetes API reference - CSIDriver: the seLinuxMount field a driver has to set to true before the kubelet will mount its volumes with a context option. Drivers that do not set it keep the old recursive behaviour, which is why the blast radius of this change is driver-specific
- Kubernetes - Feature Gates: the per-release state of SELinuxMount, SELinuxChangePolicy, SELinuxMountReadWriteOncePod and KubeProxyIPVS. The table is the fastest way to check a claim about which release flipped which default
- KEP-5495: Deprecate ipvs mode in kube-proxy. The deprecation timeline this article quotes - warning now, feature gate defaulting to false by v1.40, removal by v1.43 - comes from its graduation criteria and nowhere else
- KEP-5495 README on GitHub: the same document at its source, including the graduation criteria table with the release numbers
- KEP-3866: nftables kube-proxy backend, including the section titled "The ipvs mode of kube-proxy will not save us" - the technical argument that ipvs mode never stopped depending on iptables underneath, which is the reason for the deprecation
- NFTables mode for kube-proxy - the introduction to the mode that replaces both iptables and ipvs, and the migration notes for moving to it
- Kubernetes - kube-proxy configuration (v1alpha1) reference: the `mode` field this article tells you to read and change, and the rest of the KubeProxyConfiguration surface
- KEP-5573: Remove CGroup v1 support. The staged removal plan behind the kubelet's failCgroupV1 setting, and the statement that the override is temporary
- Kubernetes - About cgroup v2: how to check which version a node is on, the requirements for cgroup v2, and the features that depend on it
- Kubernetes - kubelet configuration (v1beta1) reference: failCgroupV1 and the rest of the KubeletConfiguration fields this article edits
- Kubernetes - Static Pods: what they are, why the kubelet manages them directly rather than through the API server, and therefore why referencing API objects from one was never supposed to work
- kubernetes/kubernetes issue 140226 - the discussion behind prohibiting Secret and ConfigMap references from static pods, and the fate of the PreventStaticPodAPIReferences feature gate that allowed an opt-out. The gate has defaulted to on since v1.34; the v1.37 sneak peek says it was removed in this release while the shipped v1.37 feature-gates reference still lists it, so the reference page is the one to trust
- kubernetes/kubernetes issue 138671 - the deprecation of `kubectl run --filename/-f`, on the grounds that the pod it produces is always built purely from the command-line arguments
- KEP-5207: metrics.k8s.io API definition. The enhancement that graduates the resource metrics API to stable after nearly nine years in beta, and the statement that v1 and v1beta1 both remain available during the transition
- Kubernetes - Resource metrics pipeline: what metrics.k8s.io actually serves, who serves it, and how `kubectl top` and the HorizontalPodAutoscaler consume it
- Kubernetes - Horizontal Pod Autoscaling: the largest consumer of the resource metrics API, and the reason its graduation matters beyond `kubectl top`
- KEP-2033 / KEP-4960: Kubelet in UserNS, also known as rootless mode. Graduating to beta in v1.37, which lets node components run as an unprivileged user on the host while still appearing as root inside the namespace
- Kubernetes - User namespaces for pods: the workload-level feature that reached GA in v1.36, distinct from the rootless kubelet but built on the same kernel mechanism
- KEP-1432: Volume health monitoring. Reset to alpha in v1.37 with four new CSI RPCs and three new status fields, after an initial implementation in v1.21 that never graduated
- Kubernetes v1.34: Of Wind & Will - the release that opened the containerd 1.x end-of-support discussion and the CRI cgroup-driver work behind it. Read alongside the container runtimes page, which records where that timeline actually ended up: the fallback is dropped in v1.38, not v1.36
- Kubernetes v1.36 release announcement - the release that made user namespaces GA, graduated SELinuxMountReadWriteOncePod, and shipped the selinux-warning-controller that this article tells you to switch on
- Kubernetes v1.36: Deprecation and removal of Service ExternalIPs - note that despite the title, v1.36 only deprecates the field and starts emitting warnings; kube-proxy support goes off by default no earlier than v1.40 and removal is no earlier than v1.43. A good illustration both of why reading one release's notes is not enough, and of how a headline turns into a rumour
- Kubernetes - Releases: the supported branches and their end-of-life dates. The source for the support window this article uses to argue about how much runway a cluster actually has
- Kubernetes - Patch releases: the cadence, the support period and the maintenance-mode window at the end of each minor release's life
- Kubernetes - Version skew policy: how far the kubelet may lag the API server, which is what makes a staged node upgrade legal in the first place
- Kubernetes - Upgrading kubeadm clusters: the control plane first, then one node at a time, with drain and uncordon around each. The sequence the runbook in this article slots into
- Kubernetes - Deprecation policy: the rules that govern how long a deprecated feature must survive before removal, which is why an ipvs deprecation in v1.37 cannot become a removal before v1.43
- Kubernetes - Container runtimes: installing and configuring containerd or CRI-O, including the cgroup driver requirement that ties this article to the cgroup v2 migration. It is also the page that settles the containerd question, stating that older containerd versions still work today through the kubelet's cgroup-driver fallback and that in Kubernetes 1.38 that fallback is dropped and they will fail with newer kubelets
- containerd - Versioning and release: the release-status table and the Kubernetes/containerd support matrix. As of September 2026 the matrix stops at Kubernetes 1.36 (2.3.0+, 2.2.0+) and has no row for 1.37, and containerd 1.7's extended LTS support ends in September 2026
- Kubernetes - Releases: the release and support dates quoted in this article, including v1.35 on 17 December 2025, v1.36 on 22 April 2026 and v1.37 on 26 August 2026
- Kubernetes - MutatingAdmissionPolicy: the in-tree way to apply the seLinuxChangePolicy opt-out across a namespace or a cluster without editing every workload by hand
- Kyverno: a policy engine the SELinux blog names explicitly as a way to apply the opt-out fleet-wide. Listed because the alternative - patching every Deployment and StatefulSet individually - does not scale past a small cluster
- Gateway API v1.6: TCPRoute and UDPRoute graduate to Standard. Out of scope for this article but on the same upgrade window for most clusters, and the reason the ingress migration keeps appearing in the same maintenance plan
¿Te ha resultado útil?