Recursos "mutate"

Esta página mostra como modificar recursos usando o Policy Controller. Isso é útil para definir valores padrão. Por exemplo, convém injetar um rótulo para todos os recursos em um namespace específico ou definir o padrão de imagePullPolicy de um pod como Always se ele ainda não estiver definido.

Ativar mutação

Console

Para ativar a mutação, siga estas etapas:

  1. No console do Google Cloud, acesse a página Política do GKE Enterprise na seção Gerenciamento de postura.

    Acessar a política

  2. Na guia Configurações, na tabela de clusters, selecione Editar na coluna Editar configuração.
  3. Expanda o menu Editar configuração do Policy Controller.
  4. Marque a caixa de seleção Ativar webhook de mutação.
  5. Selecione Salvar alterações.

Policy Controller da gcloud

Para ativar a mutação, execute o seguinte comando:

gcloud container fleet policycontroller update \
    --memberships=MEMBERSHIP_NAME \
    --mutation

Substitua MEMBERSHIP_NAME pelo nome de assinatura do cluster registrado para ativar a mutação. Você pode especificar várias associações separadas por uma vírgula.

gcloud ConfigManagement

A mutação precisa estar ativada definindo spec.policyController.mutation.enabled como true no recurso config-management:

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

Se você estiver usando o comando da CLI gcloud, use a versão Alfa para ativar a mutação, conforme mostrado no exemplo a seguir:

  # apply-spec.yaml

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

Depois de criar o arquivo apply-spec.yaml, execute o seguinte comando para aplicar a configuração:

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

Substitua:

  • MEMBERSHIP_NAME: o nome da assinatura do cluster registrado que contém as configurações do Policy Controller que você quer usar
  • CONFIG_YAML_PATH: o caminho para o arquivo apply-spec.yaml
  • PROJECT_ID: ID do projeto

Definições

  • mutador: um recurso do Kubernetes que ajuda a configurar o comportamento de mutação do Policy Controller.
  • sistema: uma organização de vários mutadores

Exemplo de mutação

O exemplo a seguir mostra um mutador que define imagePullPolicy para todos os contêineres em todos os pods como Always:

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

Neste exemplo, há os campos de metadados padrão do Kubernetes (apiVersion, kind, metadata.name), mas spec é onde o comportamento do mutador está configurado.

spec.applyTo vincula o mutador aos recursos especificados. Como estamos alterando campos específicos em um objeto, estamos definindo implicitamente o esquema desse objeto. Por exemplo, o mutador atual não faria sentido se fosse aplicado a um recurso Namespace. Por isso, esse campo é obrigatório para que o Policy Controller saiba para quais esquemas esse mutador é relevante. Os GroupVersionKinds ausentes da lista não são alterados.

spec.location informa qual campo modificar. Nesse caso, estamos usando um glob (*) para indicar que queremos modificar todas as entradas na lista de contêineres. Os únicos tipos de lista que podem ser percorridos pelo campo location são as listas de tipo de mapa, e o campo de chave do mapa precisa ser especificado. Listas do tipo mapa são uma construção do Kubernetes. Veja mais detalhes na documentação do Kubernetes (em inglês).

spec.parameters.assign.value é o valor a ser atribuído a location. Esse campo não tem tipo e pode receber qualquer valor. No entanto, o Kubernetes ainda valida a solicitação após a mutação. Portanto, inserir valores com um esquema incorreto para o objeto que está sendo modificado resulta na rejeição da solicitação.

Usar mutadores para jobs

Se estiver configurando um job ou um CronJob, especifique a versão e o grupo separadamente, como mostrado no exemplo a seguir:

applyTo:
- groups: ["batch"]
  kind: ["Job"]
  versions: ["v1"]

Quando você usa um mutador, os jobs atuais não vão ser alterados, a menos que sejam modificados. Modificar um job aciona um pedido para o webhook de mutação e faz com que ele seja transformado.

Fluxo de execução

Talvez o conceito mais importante para entender os webhooks de mutação do Kubernetes seja a política de reinvocação porque a saída de um mutador pode alterar o comportamento de outro mutador. Por exemplo, se você adicionar um novo contêiner de arquivo secundário, um mutador que define a política de extração de imagens para todos os contêineres agora terá um novo contêiner.

Na prática, o que isso significa é que, para qualquer solicitação, o webhook de mutação do controlador de políticas pode ser chamado mais de uma vez.

Para reduzir a latência, o Policy Controller é invocado novamente para evitar solicitações HTTP adicionais. Isso significa que a injeção de arquivo secundário e a política de extração de imagem têm o resultado esperado.

A rotina de mutação do Policy Controller continuará a ser invocada novamente até que o recurso convirja, e isso significa que outras iterações não terão mais efeito.

Sintaxe do local

A sintaxe do local usa o acessador de ponto (.) para percorrer campos. No caso de listas com chave, os usuários podem referenciar objetos individuais em uma lista usando a sintaxe [<key>: <value>] ou todos os objetos na lista usando [<key>: *].

Valores e campos podem ser colocados entre aspas simples (') ou duplas ("). Isso é necessário quando eles têm caracteres especiais, como pontos ou espaços.

Nos valores entre aspas, faça o escape de caracteres especiais usando \ como prefixo. "Use \" to escape and \\\" to escape" se transforma em Use " to escape and \" to escape.

Alguns exemplos do recurso v1/Pod:

  • spec.priority referencia spec.priority
  • spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly faz referência ao campo readOnly da ativação /my/mount do contêiner foo.
  • spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly faz referência ao campo readOnly da montagem /my/mount de todos os contêineres.

Se você se referir a um local que não existe atualmente em um recurso, esse local será criado por padrão. Esse comportamento pode ser configurado por meio de teste de caminho.

Teste de caminho

Como executar a padronização, em que evitamos modificar um valor que já existe? Podemos querer definir /secure-mount como somente leitura para todos os contêineres, mas não criar um /secure-mount se ainda não houver um. Podemos fazer isso por meio de teste de caminho.

Veja um exemplo que evita a mutação de imagePullPolicy se ele já estiver 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"

Veja a seguir outro exemplo que evita a criação de um contêiner sidecar vazio se ele ainda não existir:

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

Vários testes de caminho podem ser especificados, se necessário.

subPath precisa ser um prefixo (ou igual a) location.

Os únicos valores válidos para condition são MustExist e MustNotExist.

Correspondência

Os mutadores também permitem a correspondência, usando os mesmos critérios de restrições.

Mutadores

Atualmente, há dois tipos de mutadores: Assign e AssignMetadata.

Atribuir

Assign pode alterar qualquer valor fora do campo metadata de um recurso. Como todos os GroupVersionKinds têm um esquema exclusivo, ele precisa estar vinculado a um conjunto de GroupVersionKinds específico.

Ele tem o seguinte 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 pode adicionar novos rótulos de metadados. Não é possível alterar o valor dos rótulos de metadados atuais. Caso contrário, seria possível escrever um sistema de mutadores que se repetiriam indefinidamente, fazendo com que as solicitações expirassem.

Como todos os recursos compartilham o mesmo esquema metadata, não é necessário especificar a que recurso um AssignMetadata se aplica.

Além disso, como AssignMetadata não tem permissão para fazer tanto, o esquema é um pouco mais simples.

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áticas recomendadas

Advertências do Kubernetes

A documentação do Kubernetes lista algumas considerações importantes sobre como usar webhooks de mutação. Como o Policy Controller funciona como um webhook de admissão do Kubernetes, essa recomendação se aplica aqui.

A sintaxe de mutação do Policy Controller foi projetada para facilitar a conformidade com as preocupações operacionais relacionadas a webhooks de mutação, incluindo a idempotência.

Gravar mutadores

Atomicidade

A prática recomendada é tornar cada mutador o mais autossuficiente possível. Como o Kubernetes tem consistência posterior, um mutador não pode confiar em um segundo mutador para ser reconhecido para fazer o job corretamente. Por exemplo, ao adicionar um arquivo secundário, adicione-o por completo, não o crie de forma fragmentada usando vários mutadores.

Validação

Se você quiser aplicar uma condição, é recomendável que o mutador tenha uma restrição correspondente. Isso ajuda a garantir que solicitações de violação sejam rejeitadas e que violações preexistentes sejam detectadas na auditoria.

Recuperação de emergência

A mutação é implementada como um webhook de mutação do Kubernetes. Ele pode ser interrompido da mesma forma que o webhook de validação, mas o recurso relevante é um MutatingWebhookConfiguration chamado gatekeeper-mutating-webhook-configuration.

A seguir