Alterar recursos

Esta página mostra como alterar recursos através do Policy Controller. Isto é útil para fazer coisas como definir valores predefinidos. Por exemplo, pode querer injetar uma etiqueta para todos os recursos num espaço de nomes específico ou definir o valor predefinido de imagePullPolicy de um pod como Always se ainda não estiver definido.

Ative a mutação

Consola

Para ativar a mutação, conclua os seguintes passos:

  1. Na Google Cloud consola, aceda à página Política na secção Gestão de postura.

    Aceder à política

  2. No separador Definições, na tabela de clusters, selecione Editar na coluna Editar configuração.
  3. Expanda o menu Editar configuração do Policy Controller.
  4. Selecione a caixa de verificação Ativar webhook de mutação.
  5. Selecione Guardar alterações.

gcloud

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

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

Substitua MEMBERSHIP_NAME pelo nome do membro do cluster registado no qual quer ativar a mutação. Pode especificar várias subscrições separadas por uma vírgula.

Definições

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

Exemplo de mutação

O exemplo seguinte mostra um mutador que define o imagePullPolicy para todos os contentores 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, existem os campos de metadados padrão do Kubernetes (apiVersion, kind, metadata.name), mas é em spec que o comportamento do mutator é configurado.

spec.applyTo vincula o modificador aos recursos especificados. Como estamos a alterar campos específicos num objeto, estamos a definir implicitamente o esquema desse objeto. Por exemplo, o mutador atual não faria sentido se fosse aplicado a um recurso Namespace. Por este motivo, este campo é obrigatório para que o Policy Controller saiba para que esquemas este mutador é relevante. Os GroupVersionKinds em falta nesta lista não são alterados.

spec.location indica-nos o campo a modificar. Neste caso, estamos a usar um glob (*) para indicar que queremos modificar todas as entradas na lista de contentores. Tenha em atenção que os únicos tipos de listas que podem ser percorridos pelo campo location são listas do tipo mapa, e o campo chave do mapa tem de ser especificado. As listas do tipo mapa são uma construção do Kubernetes. Pode encontrar mais detalhes sobre os mesmos na documentação do Kubernetes.

spec.parameters.assign.value é o valor a atribuir a location. Este campo não tem tipo e pode assumir qualquer valor. No entanto, tenha em atenção que o Kubernetes continua a validar o pedido após a mutação, pelo que a inserção de valores com um esquema incorreto para o objeto que está a ser modificado resulta na rejeição do pedido.

Use mutadores para empregos

Se estiver a configurar um Job ou um CronJob, tem de especificar a versão e o grupo separadamente, conforme mostrado no exemplo seguinte:

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

Quando usa um mutador para tarefas, as tarefas existentes não são alteradas, a menos que sejam modificadas. A modificação de uma tarefa aciona um pedido para o webhook de mutação e faz com que seja alterada.

Fluxo de execução

Talvez o conceito mais importante a compreender sobre os webhooks de mutação do Kubernetes seja a respetiva política de reinvocação, porque o resultado de um mutador pode alterar o comportamento de outro. Por exemplo, se adicionar um novo contentor sidecar, um mutador que defina a política de obtenção de imagens para todos os contentores tem agora um novo contentor para mutar.

Na prática, isto significa que, para qualquer pedido, o webhook de mutação do Policy Controller pode ser chamado mais do que uma vez.

Para reduzir a latência, o Policy Controller invoca-se novamente para evitar pedidos HTTP adicionais. Isto significa que a injeção de sidecar e a política de obtenção de imagens têm o resultado esperado.

A rotina de mutação do Policy Controller continua a invocar-se até que o recurso "converja", o que significa que as iterações adicionais não têm mais efeito.

Sintaxe de localização

A sintaxe de localização usa o acessor de ponto (.) para percorrer os campos. No caso das listas com chaves, os utilizadores podem fazer referência a objetos individuais numa lista através da sintaxe [<key>: <value>] ou a todos os objetos na lista através de [<key>: *].

Os valores e os campos podem ser citados com aspas simples (') ou duplas ("). Isto é necessário quando têm carateres especiais, como pontos ou espaços.

Nos valores entre aspas, os carateres especiais podem ser ignorados prefixando-os com \. "Use \" to escape and \\\" to escape" transforma-se em Use " to escape and \" to escape.

Alguns exemplos para o recurso v1/Pod:

  • spec.priority referências spec.priority
  • spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly faz referência ao campo readOnly da montagem /my/mount do contentor foo.
  • spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly faz referência ao campo readOnly da montagem /my/mount de todos os contentores.

Se fizer referência a uma localização que não existe atualmente num recurso, essa localização é criada por predefinição. Este comportamento pode ser configurado através de testes de caminho.

Testes de caminhos

Como podemos realizar a predefinição, em que evitamos modificar um valor que já existe? Talvez queiramos definir /secure-mount como só de leitura para todos os contentores, mas não queremos criar um /secure-mount se ainda não existir. Podemos fazer qualquer uma destas ações através de testes de caminhos.

Segue-se um exemplo que evita a mutação de imagePullPolicy se 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"

Segue-se outro exemplo que evita a criação de um contentor sidecar vazio se 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"

Se necessário, podem ser especificados vários testes de caminhos.

subPath tem de ser um prefixo de (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 que as restrições.

Mutators

Atualmente, existem dois tipos de modificadores: Assign e AssignMetadata.

Atribuir

Assign pode alterar qualquer valor fora do campo metadata de um recurso. Uma vez que todos os GroupVersionKinds têm um esquema exclusivo, têm de estar associados a um conjunto de GroupVersionKinds específicos.

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 novas etiquetas de metadados. Não é possível alterar o valor das etiquetas de metadados existentes. Caso contrário, seria possível escrever um sistema de modificadores que se repetiria indefinidamente, o que faria com que os pedidos atingissem o limite de tempo.

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

Além disso, uma vez que AssignMetadata não tem tantas permissões, o respetivo 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 apresenta algumas considerações importantes sobre a utilização de webhooks de mutação. Uma vez que o Policy Controller funciona como um webhook de admissão do Kubernetes, esse conselho aplica-se aqui.

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

Escrever mutadores

Atomicidade

É uma prática recomendada tornar cada mutador o mais autónomo possível. Uma vez que o Kubernetes é eventualmente consistente, um mutador não deve depender de um segundo mutador para ter sido reconhecido para fazer o seu trabalho corretamente. Por exemplo, quando adicionar um sidecar, adicione o sidecar completo. Não o construa gradualmente com vários modificadores.

Validação

Se existir uma condição que queira aplicar, é aconselhável que o mutador tenha uma restrição correspondente. Isto ajuda a garantir que as solicitações em violação são rejeitadas e que as violações preexistentes são detetadas na auditoria.

Recuperação de emergência

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

O que se segue?