En esta página se explica cómo mutar recursos con Policy Controller. Esto resulta útil para
hacer cosas como definir valores predeterminados. Por ejemplo, puede que quieras insertar una etiqueta para todos los recursos de un espacio de nombres específico o asignar el valor Always
a imagePullPolicy
de un pod si aún no se ha definido.
Habilitar mutación
Consola
Para habilitar la mutación, sigue estos pasos:
- En la consola, vaya a la página Política de la sección Gestión de postura. Google Cloud
- En la pestaña Configuración, en la tabla de clústeres, seleccione Editar edit en la columna Editar configuración.
- Despliega el menú Editar configuración de Policy Controller.
- Selecciona la casilla Habilitar webhook de mutación.
- Selecciona Guardar cambios.
gcloud
Para habilitar la mutación, ejecuta el siguiente comando:
gcloud container fleet policycontroller update \
--memberships=MEMBERSHIP_NAME \
--mutation
Sustituye MEMBERSHIP_NAME
por el nombre de miembro del clúster registrado para habilitar la mutación. Puedes especificar varias suscripciones separadas por comas.
Definiciones
- Mutador: recurso de Kubernetes que ayuda a configurar el comportamiento de mutación de Policy Controller.
- Sistema: una disposición de varios mutadores
Ejemplo de mutación
En el siguiente ejemplo se muestra un mutador que asigna el valor Always
a imagePullPolicy
de todos los contenedores de todos los pods:
# set-image-pull-policy.yaml
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
name: always-pull-image
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
location: "spec.containers[name: *].imagePullPolicy"
parameters:
assign:
value: "Always"
En este ejemplo, se incluyen los campos de metadatos estándar de Kubernetes (apiVersion
, kind
y metadata.name
), pero spec
es donde se configura el comportamiento del mutador.
spec.applyTo
vincula el mutador a los recursos especificados. Como vamos a cambiar campos específicos de un objeto, estamos definiendo implícitamente el esquema de ese objeto. Por ejemplo, el mutador actual no tendría sentido si se aplicara a un recurso Namespace
. Por este motivo, este campo es obligatorio para que Policy Controller sepa para qué esquemas es relevante este mutador.
GroupVersionKinds
que falten en esta lista no se mutan.
spec.location
nos indica qué campo se va a modificar. En este caso, usamos un glob (*
) para indicar que queremos modificar todas las entradas de la lista de contenedores. Ten en cuenta que los únicos tipos de listas que se pueden recorrer con el campo location
son las listas de tipo mapa, y se debe especificar el campo de clave del mapa. Las listas de tipo mapa son una estructura de Kubernetes. Puedes consultar más detalles sobre ellos en la documentación de Kubernetes.
spec.parameters.assign.value
es el valor que se asignará a location
.
Este campo no tiene tipo y puede adoptar cualquier valor. Sin embargo, ten en cuenta que Kubernetes sigue validando la solicitud después de la mutación, por lo que, si insertas valores con un esquema incorrecto para el objeto que se está modificando, la solicitud se rechazará.
Usar mutadores para Jobs
Si vas a configurar un trabajo o un CronJob, debes especificar la versión y el grupo por separado, tal como se muestra en el siguiente ejemplo:
applyTo:
- groups: ["batch"]
kind: ["Job"]
versions: ["v1"]
Cuando usas un mutador para Jobs, los Jobs no se mutan a menos que se modifiquen. Si se modifica un trabajo, se activa una solicitud al webhook de mutación y se muta.
Flujo de ejecución
Quizá el concepto más importante que debes conocer sobre los webhooks de mutación de Kubernetes es su política de reinvocación, ya que el resultado de un mutador puede cambiar el comportamiento de otro. Por ejemplo, si añades un nuevo contenedor sidecar, un mutador que defina la política de extracción de imágenes para todos los contenedores tendrá un nuevo contenedor que mutar.
En la práctica, esto significa que, para cualquier solicitud, es posible que se llame al webhook de mutación de Policy Controller más de una vez.
Para reducir la latencia, Policy Controller vuelve a invocarse a sí mismo para evitar solicitudes HTTP adicionales. Esto significa que la inyección de sidecar y la política de extracción de imágenes tienen el resultado esperado.
La rutina de mutación de Policy Controller seguirá invocándose a sí misma hasta que el recurso "converja", lo que significa que las iteraciones adicionales no tendrán ningún efecto.
Sintaxis de ubicación
La sintaxis de ubicación usa el descriptor de acceso de punto (.
) para desplazarse por los campos. En el caso de las listas con claves, los usuarios pueden hacer referencia a objetos concretos de una lista mediante la sintaxis [<key>: <value>]
o a todos los objetos de la lista mediante [<key>: *]
.
Los valores y los campos se pueden entrecomillar con comillas simples ('
) o dobles ("
). Esto es necesario cuando tienen caracteres especiales, como puntos o espacios.
En los valores entre comillas, los caracteres especiales se pueden escapar añadiendo el prefijo \
. "Use \" to escape and \\\" to escape"
se convierte en Use " to escape and \" to escape
.
Algunos ejemplos del recurso v1/Pod
:
spec.priority
referenciasspec.priority
spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly
hace referencia al camporeadOnly
del montaje/my/mount
del contenedorfoo
.spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly
hace referencia al camporeadOnly
del/my/mount
de todos los contenedores.
Si haces referencia a una ubicación que no existe en un recurso, se creará de forma predeterminada. Este comportamiento se puede configurar mediante pruebas de ruta.
Pruebas de rutas
¿Cómo podemos realizar la asignación de valores predeterminados sin modificar un valor que ya existe? Quizá queramos definir /secure-mount
como de solo lectura para todos los contenedores, pero no queremos crear un /secure-mount
si aún no existe. Podemos hacer cualquiera de estas dos cosas mediante pruebas de ruta.
Aquí tienes un ejemplo que evita mutar imagePullPolicy
si ya se ha definido:
# set-image-pull-policy.yaml
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
name: always-pull-image
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
location: "spec.containers[name: *].imagePullPolicy"
parameters:
assign:
value: "Always"
pathTests:
- subPath: "spec.containers[name: *].imagePullPolicy"
condition: "MustNotExist"
Aquí tienes otro ejemplo en el que se evita crear un contenedor sidecar
vacío si aún no existe:
# set-image-pull-policy.yaml
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
name: always-pull-image
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
location: 'spec.containers[name: "sidecar"].imagePullPolicy'
parameters:
assign:
value: "Always"
pathTests:
- subPath: 'spec.containers[name: "sidecar"]'
condition: "MustExist"
Si es necesario, se pueden especificar varias pruebas de ruta.
subPath
debe ser un prefijo de location
(o igual a él).
Los únicos valores válidos para condition
son MustExist
y MustNotExist
.
Concordancia
Los mutadores también permiten la coincidencia, utilizando los mismos criterios que las restricciones.
Mutadores
Actualmente hay dos tipos de mutadores: Assign
y AssignMetadata
.
Asignad
Assign
puede cambiar cualquier valor fuera del campo metadata
de un recurso.
Como todos los GroupVersionKinds
tienen un esquema único, deben estar vinculados a un conjunto de GroupVersionKinds
concretos.
Tiene el siguiente esquema:
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
name: always-pull-image
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
kinds: # redundant because of `applyTo`, but left in for consistency
- apiGroups: ["*"]
kinds: ["*"]
namespaces: ["my-namespace"]
scope: "Namespaced" # or "Cluster"
excludedNamespaces: ["some-other-ns"]
labelSelector:
matchLabels:
mutate: "yes"
matchExpressions:
- key: "my-label"
operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
values: ["my-value"]
namespaceSelector:
matchLabels:
mutate: "yes"
matchExpressions:
- key: "my-label"
operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
values: ["my-value"]
location: "spec.containers[name: *].imagePullPolicy"
parameters:
pathTests:
- subPath: 'spec.containers[name: "sidecar"]' # must be a prefix of `location`
condition: "MustExist" # or "MustNotExist"
- subPath: "spec.containers[name: *].imagePullPolicy"
condition: "MustNotExist"
assign:
value: "Always" # any type can go here, not just a string
AssignMetadata
AssignMetadata
puede añadir nuevas etiquetas de metadatos. No puede cambiar el valor de las etiquetas de metadatos que ya existen. De lo contrario, sería posible escribir un sistema de mutadores que se repitiera indefinidamente, lo que provocaría que las solicitudes se agotasen.
Como todos los recursos comparten el mismo esquema metadata
, no es necesario especificar a qué recurso se aplica un AssignMetadata
.
Además, como AssignMetadata
no puede hacer tantas cosas, su esquema es un poco más sencillo.
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: AssignMetadata
metadata:
name: set-team-name
spec:
match:
kinds:
- apiGroups: ["*"]
kinds: ["*"]
namespaces: ["my-namespace"]
scope: "Namespaced" # or "Cluster"
excludedNamespaces: ["some-other-ns"]
labelSelector:
matchLabels:
mutate: "yes"
matchExpressions:
- key: "my-label"
operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
values: ["my-value"]
namespaceSelector:
matchLabels:
mutate: "yes"
matchExpressions:
- key: "my-label"
operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
values: ["my-value"]
location: "metadata.labels.team" # must start with `metadata.labels`
parameters:
assign:
value: "Always" # any type can go here, not just a string
Prácticas recomendadas
Advertencias de Kubernetes
En la documentación de Kubernetes se indican algunas consideraciones importantes sobre el uso de webhooks de mutación. Como Policy Controller funciona como un webhook de admisión de Kubernetes, estos consejos también se aplican en este caso.
La sintaxis de mutación de Policy Controller se ha diseñado para que sea más fácil cumplir los requisitos operativos de los webhooks de mutación, incluida la idempotencia.
Escribir mutadores
Atomicidad
Es recomendable que cada mutador sea lo más autosuficiente posible. Como Kubernetes es coherente en última instancia, un mutador no debe depender de que se haya reconocido un segundo mutador para hacer su trabajo correctamente. Por ejemplo, al añadir un sidecar, añada todo el sidecar, no lo construya poco a poco usando varios mutadores.
Validación
Si quieres aplicar una condición, es recomendable que el mutador tenga una restricción coincidente. De esta forma, nos aseguramos de que se rechacen las solicitudes infractoras y de que se detecten las infracciones preexistentes en la auditoría.
Recuperación de emergencia
La mutación se implementa como un webhook de mutación de Kubernetes. Se puede detener de la misma forma que el webhook de validación, pero el recurso pertinente es un MutatingWebhookConfiguration
llamado gatekeeper-mutating-webhook-configuration
.
Siguientes pasos
- Para ver explicaciones y ejemplos de mutaciones de Gatekeeper, consulta la documentación de código abierto.