mutation des ressources

Cette page vous explique comment effectuer la mutation des ressources à l'aide de Policy Controller.

Cette fonctionnalité est utile pour définir des valeurs par défaut, par exemple. Par exemple, vous pouvez injecter un libellé pour toutes les ressources d'un espace de noms spécifique, ou définir le paramètre imagePullPolicy d'un pod sur Always si ce n'est pas déjà fait.

Avant de commencer

La mutations doit être activée en définissant spec.policyController.mutation.enabled sur true dans la ressource config-management:

apiVersion: configmanagement.gke.io/v1
kind: ConfigManagement
metadata:
  name: config-management
spec:
  policyController:
    enabled: true
    mutation:
      enabled: true

Si vous utilisez la commande d'outil gcloud, vous devez utiliser la version alpha pour activer la mutation, comme indiqué dans l'exemple suivant :

  # apply-spec.yaml

  applySpecVersion: 1
  spec:
    policyController:
      enabled: true
      mutationEnabled: true

Après avoir créé le fichier apply-spec.yaml, exécutez la commande suivante pour appliquer la configuration :

  gcloud alpha container hub config-management apply \
      --membership=MEMBERSHIP_NAME \
      --config=CONFIG_YAML_PATH \
      --project=PROJECT_ID

Remplacez les éléments suivants :

  • MEMBERSHIP_NAME : nom de l'appartenance du cluster enregistré contenant les paramètres Policy Controller que vous souhaitez utiliser.
  • CONFIG_YAML_PATH : chemin d'accès au fichier apply-spec.yaml.
  • PROJECT_ID : ID de votre projet.

Définitions

  • mutateur : ressource Kubernetes qui permet de configurer le comportement de mutation de Policy Controller.
  • système : disposition de plusieurs mutateurs

Exemple

Avant d'examiner les détails, voici un exemple de mutateur qui définit imagePullPolicy sur Always pour tous les conteneurs de tous les 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"

Répartir

Il existe les champs de métadonnées Kubernetes standards (apiVersion, kind, metadata.name), mais spec est l'endroit où le comportement du mutateur est configuré.

spec.applyTo lie le mutateur aux ressources spécifiées. Étant donné que nous modifions des champs spécifiques d'un objet, nous définissons implicitement le schéma de cet objet. Par exemple, le mutateur actuel n'aurait aucun sens s'il était appliqué à une ressource Namespace. Par conséquent, ce champ est obligatoire pour que Policy Controller sache pour quels schémas ce mutateur est pertinent. Les éléments GroupVersionKinds manquants dans cette liste ne font pas l'objet d'une mutation.

spec.location nous indique le champ à modifier. Dans ce cas, nous utilisons un glob (*) pour indiquer que nous souhaitons modifier toutes les entrées de la liste des conteneurs. Notez que les seuls types de listes pouvant être parcourus par le champ location sont des listes de type carte, et le champ de clé du mappage doit être spécifié. Les listes de types de carte sont une construction Kubernetes. Pour en savoir plus sur ces types de liste, consultez la documentation de Kubernetes.

spec.parameters.assign.value est la valeur à attribuer à location. Le type de ce champ n'est pas spécifié et peut utiliser n'importe quelle valeur. Notez toutefois que Kubernetes valide toujours la requête post-mutation. Par conséquent, si vous insérez des valeurs avec un schéma incorrect pour l'objet en cours de modification, la requête est rejetée.

Flux d'exécution

Le concept de règle de rappel est peut-être le plus important à comprendre concernant les webhooks de mutation Kubernetes, car le résultat d'un mutateur peut modifier le comportement d'un autre mutateur. Par exemple, si vous ajoutez un nouveau conteneur side-car, un mutateur qui définit la stratégie d'extraction d'images pour tous les conteneurs possède maintenant un nouveau conteneur pour lequel effectuer la mutation.

En pratique, cela signifie que pour une requête donnée, le webhook de mutation de Policy Controller peut être appelé plusieurs fois.

Pour réduire la latence, Policy Controller s'appelle de nouveau pour éviter d'autres requêtes HTTP. Cela signifie que l'injection side-car et la stratégie d'extraction d'image ont le résultat attendu.

La routine de mutation de Policy Controller continuera à s'appeler jusqu'à ce que la ressource "converge", ce qui signifie que des itérations supplémentaires n'ont aucun effet supplémentaire.

Syntaxe des emplacements

La syntaxe de l'emplacement utilise le Accesseur de points (.) pour parcourir les champs. Dans le cas de listes à clés, les utilisateurs peuvent référencer des objets individuels dans une liste à l'aide de la syntaxe [<key>: <value>] ou tous les objets de la liste à l'aide de [<key>: *].

Les valeurs et les champs peuvent être placés entre guillemets simples (') ou doubles ("). Cela est nécessaire lorsqu'ils contiennent des caractères spéciaux, tels que des points ou des espaces.

Dans les valeurs entre guillemets, les caractères spéciaux peuvent être échappés en ajoutant le préfixe \. "Use \" to escape and \\\" to escape" devient Use " to escape and \" to escape.

Voici quelques exemples pour la ressource v1/Pod:

  • spec.priority fait référence à spec.priority
  • spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly fait référence au champ readOnly de l'installation /my/mount du conteneur foo.
  • spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly fait référence au champ readOnly de l'installation /my/mount de tous les conteneurs.

Si vous référencez un emplacement qui n'existe actuellement pas sur une ressource, cet emplacement est créé par défaut. Ce comportement peut être configuré via des tests de chemin d'accès.

Test de chemin

Comment définir des valeurs par défaut tout en évitant de modifier une valeur qui existe déjà ? Nous souhaitons peut-être définir /secure-mount en lecture seule pour tous les conteneurs, mais nous ne souhaitons pas créer un /secure-mount s'il n'en existe pas déjà un. Nous pouvons effectuer l'une de ces opérations via des tests de chemin d'accès.

Voici un exemple qui évite de muter imagePullPolicy s'il est déjà défini:

# 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"

Voici un autre exemple qui évite de créer un conteneur sidecar vide s'il n'existe pas déjà:

# 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 nécessaire, plusieurs tests de chemin d'accès peuvent être spécifiés.

subPath doit être un préfixe (ou égal à) location.

Les seules valeurs valides pour condition sont MustExist et MustNotExist.

Test de valeur

Si vous souhaitez uniquement effectuer une mutation des valeurs qui répondent à certaines conditions, vous pouvez le faire à l'aide du champ spec.parameters.assignIf.

Par exemple, si vous souhaitez effectuer uniquement la mutation d'un contexte de sécurité d'un pod s'il configure explicitement ce pod pour qu'il s'exécute en tant qu'utilisateur racine, procédez comme suit:

# set-non-rootyaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.securityContext.runAsUser"
  parameters:
    assign:
      value: 1111
    assignIf:
      in: [0]

assignIf comporte deux types de tests in et notIn, qui acceptent tous deux une liste de valeurs non typées. Si la valeur d'origine se trouve dans la liste de valeurs (ou non dans notIn), elle est mutée.

correspondance

Les mutateurs permettent également la mise en correspondance, en utilisant les mêmes critères que les contraintes.

Mutateurs

Il existe actuellement deux types de mutateurs : Assign et AssignMetadata.

Attribuer

Assign peut modifier n'importe quelle valeur en dehors du champ metadata d'une ressource. Étant donné que tous les GroupVersionKinds ont un schéma unique, ils doivent être liés à un ensemble de GroupVersionKinds spécifiques.

Il présente le schéma suivant:

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
    assignIf:
      in: ["mutate-if-in-this-list"]
      notIn: ["don't-mutate-if-in-this-list"]

AssignMetadata

AssignMetadata peut ajouter de nouveaux libellés de métadonnées. Il ne peut pas modifier la valeur des libellés de métadonnées existants. Sinon, il serait possible d'écrire un système de mutateurs qui se produirait indéfiniment, ce qui entraînerait l'expiration des requêtes.

Étant donné que toutes les ressources partagent le même schéma metadata, il n'est pas nécessaire de spécifier la ressource à laquelle AssignMetadata s'applique.

De plus, comme AssignMetadata n'est pas autorisé à effectuer autant de tâches, son schéma est un peu plus simple.

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

Bonnes pratiques

Mises en garde concernant Kubernetes

La documentation Kubernetes répertorie certaines considérations importantes concernant l'utilisation des webhooks de mutation. Étant donné que Policy Controller fonctionne comme un webhook d'admission Kubernetes, ce conseil s'applique ici.

La syntaxe de mutation de Policy Controller est conçue pour faciliter la conformité aux problèmes opérationnels liés aux webhooks de mutation, y compris l'idempotence.

Écrire des mutateurs

Atomicité

Il est recommandé de faire en sorte que chaque mutateur soit aussi autosuffisant que possible. Étant donné que Kubernetes est cohérent à terme, un mutateur ne doit pas compter sur une deuxième mutateur pour avoir été reconnu et pour fonctionnement correctement. Par exemple, lors de l'ajout d'un side-car, ajoutez l'intégralité du side-car, et ne le créez pas fragmenté via plusieurs mutateurs.

Validation

Si vous souhaitez appliquer une condition, il est judicieux que le mutateur dispose d'une contrainte correspondante. Cela permet de s'assurer que les requêtes non conformes sont refusées et que les violations préexistantes sont détectées lors de l'audit.

Récupération d'urgence

La mutation est mise en œuvre en tant que webhook de mutation Kubernetes. Il peut être arrêté de la même manière que le webhook de validation, mais la ressource concernée est une ressource MutatingWebhookConfiguration appelée gatekeeper-mutating-webhook-configuration.

Autres ressources

La documentation Open Source pour la mutation Gatekeeper contient d'autres explications et exemples.