This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Contêineres

Tecnologia para empacotar aplicações com suas dependências em tempo de execução

Cada contêiner executado é repetível; a padronização de ter dependências incluídas significa que você obtém o mesmo comportamento onde quer que você execute.

Os contêineres separam os aplicativos da infraestrutura de host subjacente. Isso torna a implantação mais fácil em diferentes ambientes de nuvem ou sistema operacional.

Imagem de contêiner

Uma imagem de contêiner é um pacote de software pronto para executar, contendo tudo que é preciso para executar uma aplicação: o código e o agente de execução necessário, aplicação, bibliotecas do sistema e valores padrões para qualquer configuração essencial.

Por design, um contêiner é imutável: você não pode mudar o código de um contêiner que já está executando. Se você tem uma aplicação conteinerizada e quer fazer mudanças, você precisa construir uma nova imagem que inclui a mudança, e recriar o contêiner para iniciar a partir da imagem atualizada.

Agente de execução de contêiner

O agente de execução (runtime) de contêiner é o software responsável por executar os contêineres.

O Kubernetes suporta diversos agentes de execução de contêineres: Docker, containerd, CRI-O, e qualquer implementação do Kubernetes CRI (Container Runtime Interface).

Próximos passos

1 - Imagens

Uma imagem de contêiner representa dados binários que encapsulam uma aplicação e todas as suas dependências de software. As imagens de contêiner são pacotes de software executáveis que podem ser executados de forma autônoma e que fazem suposições muito bem definidas sobre seu agente de execução do ambiente.

Normalmente, você cria uma imagem de contêiner da sua aplicação e a envia para um registro antes de fazer referência a ela em um Pod.

Esta página fornece um resumo sobre o conceito de imagem de contêiner.

Nomes das imagens

As imagens de contêiner geralmente recebem um nome como pause, exemplo/meuconteiner, ou kube-apiserver. As imagens também podem incluir um hostname de algum registro; por exemplo: exemplo.registro.ficticio/nomeimagem, e um possível número de porta; por exemplo: exemplo.registro.ficticio:10443/nomeimagem.

Se você não especificar um nome de host do registro, o Kubernetes assume que você está se referindo ao registro público do Docker. Você pode alterar esse comportamento definindo um registro de imagem padrão na configuração do agente de execução do contêiner.

Após a parte do nome da imagem, você pode adicionar uma tag ou digest (da mesma forma que faria ao usar comandos como docker ou podman). As tags permitem identificar diferentes versões da mesma série de imagens. Digests são identificadores únicos para uma versão específica de uma imagem. Digests são hashes do conteúdo da imagem e são imutáveis. As tags podem ser movidas para apontar para imagens diferentes, mas os digests são fixos.

Tags de imagem consistem em letras minúsculas e maiúsculas, dígitos, sublinhados (_), pontos (.) e hifens (-). Elas podem ter até 128 caracteres de comprimento e devem seguir o seguinte padrão de expressão regular: [a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}. Você pode ler mais sobre e encontrar a expressão regular de validação na Especificação de Distribuição OCI. Se você não especificar uma tag, o Kubernetes assume que você está se referindo à tag latest.

Digests de imagem consistem em um algoritmo de hash (como sha256) e um valor de hash. Por exemplo: sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07. Você pode encontrar mais informações sobre o formato de digests na Especificação de Imagem OCI.

Alguns exemplos de nomes de imagem que o Kubernetes pode usar são:

  • busybox - Nome da imagem apenas, sem tag ou digest. O Kubernetes usará o registro público do Docker e a tag latest. (Equivalente a docker.io/library/busybox:latest)
  • busybox:1.32.0 - Nome da imagem com tag. O Kubernetes usará o registro público do Docker. (Equivalente a docker.io/library/busybox:1.32.0)
  • registry.k8s.io/pause:latest - Nome da imagem com um registro personalizado e tag latest.
  • registry.k8s.io/pause:3.5 - Nome da imagem com um registro personalizado e tag diferente de latest.
  • registry.k8s.io/pause@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 - Nome da imagem com digest.
  • registry.k8s.io/pause:3.5@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 - Nome da imagem com tag e digest. Apenas o digest será usado para o download.

Atualizando imagens

Quando você cria um Deployment, StatefulSet, Pod ou outro objeto que inclua um template de Pod, por padrão a política utilizada para baixar as imagens dos contêineres nesse Pod será definida como IfNotPresent quando não especificada explicitamente. Essa política faz com que o kubelet ignore o download da imagem se ela já existir.

Política de download de imagem

A imagePullPolicy de um contêiner e a tag da imagem afetam quando o kubelet tenta puxar (download) a imagem especificada.

Aqui está uma lista dos valores que você pode definir para imagePullPolicy e os efeitos que esses valores têm:

IfNotPresent
a imagem será baixada apenas se não estiver presente localmente.
Always
toda vez que o kubelet iniciar um contêiner, ele consultará o registro de imagens de contêiner para resolver o nome para um digest. Se o kubelet tiver uma imagem de contêiner com exatamente esse digest em cache local, ele usará a imagem em cache; caso contrário, o kubelet fará o download da imagem com o digest resolvido e usará essa imagem para iniciar o contêiner.
Never
o kubelet não tenta buscar a imagem. Se a imagem já estiver presente localmente de alguma forma, o kubelet tentará iniciar o contêiner; caso contrário, a inicialização falhará. Veja imagens pré-baixadas para mais detalhes.

A semântica de cache do provedor de imagens subjacente torna mesmo imagePullPolicy: Always eficiente, desde que o registro esteja acessível de forma confiável. Seu agente de execução de contêiner pode perceber que as camadas da imagem já existem no nó, evitando que precisem ser baixadas novamente.

Para garantir que o Pod sempre use a mesma versão de uma imagem de contêiner, você pode especificar o digest da imagem; substitua <image-name>:<tag> por <image-name>@<digest> (por exemplo, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2).

Ao usar tags de imagem, se o registro de imagens alterar o código que a tag representa, você pode acabar com uma mistura de Pods executando o código antigo e o novo. Um digest de imagem identifica de forma única uma versão específica da imagem, então o Kubernetes executa o mesmo código sempre que inicia um contêiner com aquele nome de imagem e digest especificado. Especificar uma imagem por digest fixa o código que será executado, de modo que uma alteração no registro não leve a essa mistura de versões.

Existem controladores de admissão de terceiros que mutam Pods (e templates de Pods) quando eles são criados, de forma que a carga de trabalho em execução seja definida com base em um digest de imagem em vez de uma tag. Isso pode ser útil se você quiser garantir que toda sua carga de trabalho esteja executando o mesmo código, independentemente das mudanças de tags no registro.

Política padrão de download de imagem

Quando você (ou um controlador) envia um novo Pod para o servidor de API, seu cluster define o campo imagePullPolicy quando certas condições são atendidas:

  • se você omitir o campo imagePullPolicy e especificar o digest da imagem do contêiner, o imagePullPolicy será automaticamente definido como IfNotPresent;
  • se você omitir o campo imagePullPolicy e a tag da imagem do contêiner for :latest, o imagePullPolicy será automaticamente definido como Always;
  • se você omitir o campo imagePullPolicy e não especificar uma tag para a imagem do contêiner, o imagePullPolicy será automaticamente definido como Always;
  • se você omitir o campo imagePullPolicy e especificar uma tag para a imagem do contêiner que não seja :latest, o imagePullPolicy será automaticamente definido como IfNotPresent.

Download obrigatório da imagem

Se você deseja forçar sempre o download da imagem, pode fazer uma das seguintes opções:

  • Defina o imagePullPolicy do contêiner como Always.
  • Omita o imagePullPolicy e use :latest como a tag da imagem a ser usada; o Kubernetes definirá a política como Always ao enviar o Pod.
  • Omita o imagePullPolicy e a tag da imagem a ser usada; o Kubernetes definirá a política como Always ao enviar o Pod.
  • Ative o controlador de admissão AlwaysPullImages.

ImagePullBackOff

Quando o kubelet começa a criar contêineres para um Pod usando um agente de execução de contêiner, é possível que o contêiner esteja no estado Waiting devido a ImagePullBackOff.

O status ImagePullBackOff significa que um contêiner não pôde ser iniciado porque o Kubernetes não conseguiu fazer o download da imagem do contêiner (por motivos como nome de imagem inválido ou tentativa de download de um registro privado sem imagePullSecret). A parte BackOff indica que o Kubernetes continuará tentando fazer o download da imagem, com um atraso incremental entre as tentativas.

O Kubernetes aumenta o intervalo entre cada tentativa até atingir um limite definido no código, que é de 300 segundos (5 minutos).

Download de imagem por classe de agente de execução

ESTADO DA FUNCIONALIDADE: Kubernetes v1.29 [alpha] (habilitado por padrão: false)
O Kubernetes inclui suporte em estado alpha para realizar o download de imagens com base na RuntimeClass de um Pod.

Se você habilitar o feature gate RuntimeClassInImageCriApi, o kubelet passará a referenciar imagens de contêiner por uma tupla (nome da imagem, manipulador de agente de execução) em vez de apenas pelo nome da imagem ou digest. Seu agente de execução do contêiner pode adaptar seu comportamento com base no manipulador de agente de execução selecionado. Fazer download de imagens com base na classe de agente de execução será útil para contêineres baseados em máquina virtual, como contêineres do tipo Windows Hyper-V.

Downloads de imagem em série e em paralelo

Por padrão, o kubelet realiza downloads de imagens de forma sequencial. Em outras palavras, o kubelet envia apenas uma solicitação de download de imagem por vez para o serviço de imagens. Outras solicitações de download precisam aguardar até que a solicitação em andamento seja concluída.

Os Nós tomam decisões de download de imagem de forma isolada. Mesmo quando você usa downloads de imagem em série, dois Nós diferentes podem puxar a mesma imagem em paralelo.

Se você quiser habilitar downloads de imagem em paralelo, pode definir o campo serializeImagePulls como false na configuração do kubelet. Com serializeImagePulls definido como false, as solicitações de download de imagem serão enviadas imediatamente para o serviço de imagens, permitindo que várias imagens sejam puxadas ao mesmo tempo.

Ao habilitar downloads de imagem em paralelo, certifique-se de que o serviço de imagens do seu agente de execução do contêiner pode lidar com esse tipo de operação.

O kubelet nunca realiza download de múltiplas imagens em paralelo para um único Pod. Por exemplo, se você tiver um Pod com um Init Container e um contêiner de aplicação, os downloads de imagem desses dois contêineres não serão paralelizados. No entanto, se você tiver dois Pods que usam imagens diferentes, o kubelet puxará as imagens em paralelo para os dois Pods diferentes, quando o download paralelo estiver habilitado.

Máximo de downloads de imagem em paralelo

ESTADO DA FUNCIONALIDADE: Kubernetes v1.32 [beta]

Quando serializeImagePulls está definido como false, o kubelet, por padrão, não impõe limite ao número máximo de imagens sendo puxadas ao mesmo tempo. Se você quiser limitar a quantidade de downloads de imagem paralelos, pode definir o campo maxParallelImagePulls na configuração do kubelet. Com maxParallelImagePulls definido como n, apenas n imagens podem ser puxadas simultaneamente, e qualquer download de imagem além de n terá que aguardar até que pelo menos um download em andamento seja concluído.

Limitar o número de downloads de imagem paralelos ajuda a evitar que o processo de download consuma muita largura de banda de rede ou I/O de disco quando esta funcionalidade estiver habilitada.

Você pode definir maxParallelImagePulls para um número positivo maior ou igual a 1. Se você definir maxParallelImagePulls como maior ou igual a 2, também deverá definir serializeImagePulls como false. O kubelet não iniciará se as configurações de maxParallelImagePulls forem inválidas.

Multiarquitetura de imagens com índice de imagens

Além de fornecer o binário das imagens, um registro de contêiner também pode servir um índice de imagem do contêiner. Um índice de imagem pode apontar para múltiplos manifestos da imagem para versões específicas de arquitetura de um contêiner. A ideia é que você possa ter um nome para uma imagem (por exemplo: pause, exemple/meuconteiner, kube-apiserver) e permitir que diferentes sistemas busquem o binário da imagem correta para a arquitetura de máquina que estão usando.

O próprio Kubernetes normalmente nomeia as imagens de contêiner com o sufixo -$(ARCH). Para retrocompatibilidade, gere as imagens mais antigas com sufixos. A ideia é gerar a imagem pause que tem o manifesto para todas as arquiteturas e pause-amd64 que é retrocompatível com as configurações anteriores ou arquivos YAML que podem ter codificado as imagens com sufixos.

Usando um registro privado

Os registros privados podem exigir chaves para acessar as imagens deles. As credenciais podem ser fornecidas de várias maneiras:

  • Configurando nós para autenticação em um registro privado
    • todos os pods podem ler qualquer registro privado configurado
    • requer configuração de nó pelo administrador do cluster
  • Imagens pré-obtidas
    • todos os pods podem usar qualquer imagem armazenada em cache em um nó
    • requer acesso root a todos os nós para configurar
  • Especificando ImagePullSecrets em um Pod
    • apenas pods que fornecem chaves próprias podem acessar o registro privado
  • Extensões locais ou específicas do fornecedor
    • se estiver usando uma configuração de nó personalizado, você (ou seu provedor de nuvem) pode implementar seu mecanismo para autenticar o nó ao registro do contêiner.

Essas opções são explicadas com mais detalhes abaixo.

Configurando nós para autenticação em um registro privado

As instruções específicas para configurar as credenciais dependem do agente de execução de contêiner e do registro que você escolheu utilizar. Você deve consultar a documentação da sua solução para obter as informações mais precisas.

Para um exemplo de configuração de um registro de imagens de contêiner privado, veja a tarefa Realizar download de uma Imagem a partir de um Registro Privado. Esse exemplo utiliza um registro privado no Docker Hub.

Provedor de credenciais do kubelet para downloads de imagem autenticados

Você pode configurar o kubelet para invocar um binário de plugin a fim de buscar dinamicamente as credenciais de registro para uma imagem de contêiner. Essa é a maneira mais robusta e versátil de obter credenciais para registros privados, mas também exige uma configuração no nível do kubelet para ser habilitada.

Veja Configurar um provedor de credenciais de imagem no kubelet para mais detalhes.

Interpretação do config.json

A interpretação do config.json varia entre a implementação original do Docker e a interpretação feita pelo Kubernetes. No Docker, as chaves em auths podem especificar apenas URLs raiz, enquanto o Kubernetes permite URLs com glob e também caminhos com correspondência por prefixo. A única limitação é que os padrões glob (*) devem incluir o ponto (.) para cada subdomínio. A quantidade de subdomínios correspondentes deve ser igual à quantidade de padrões glob (*.), por exemplo:

  • *.kubernetes.io não corresponderá a kubernetes.io, mas corresponderá a abc.kubernetes.io
  • *.*.kubernetes.io não corresponderá a abc.kubernetes.io, mas corresponderá a abc.def.kubernetes.io
  • prefix.*.io corresponderá a prefix.kubernetes.io
  • *-good.kubernetes.io corresponderá a prefix-good.kubernetes.io

Isso significa que um config.json como este é válido:

{
    "auths": {
        "my-registry.io/images": { "auth": "…" },
        "*.my-registry.io/images": { "auth": "…" }
    }
}

As operações de pull de imagem agora passarão as credenciais para o agente de execução de contêiner via CRI para cada padrão válido. Por exemplo, os seguintes nomes de imagem de contêiner corresponderiam com sucesso:

  • my-registry.io/images
  • my-registry.io/images/my-image
  • my-registry.io/images/another-image
  • sub.my-registry.io/images/my-image

Mas não:

  • a.sub.my-registry.io/images/my-image
  • a.b.sub.my-registry.io/images/my-image

O kubelet realiza downloads de imagem de forma sequencial para cada credencial encontrada. Isso significa que múltiplas entradas no config.json para caminhos diferentes também são possíveis:

{
    "auths": {
        "my-registry.io/images": {
            "auth": "…"
        },
        "my-registry.io/images/subpath": {
            "auth": "…"
        }
    }
}

Se agora um contêiner especificar uma imagem my-registry.io/images/subpath/my-image para ser baixada, o kubelet tentará fazer o download utilizando ambas as fontes de autenticação, caso uma delas falhe.

Imagens pré-obtidas

Por padrão, o kubelet tenta realizar um "pull" para cada imagem do registro especificado. No entanto, se a propriedade imagePullPolicy do contêiner for definida como IfNotPresent ou Never, em seguida, uma imagem local é usada (preferencial ou exclusivamente, respectivamente).

Se você quiser usar imagens pré-obtidas como um substituto para a autenticação do registro, você deve garantir que todos os nós no cluster tenham as mesmas imagens pré-obtidas.

Isso pode ser usado para pré-carregar certas imagens com o intuíto de aumentar a velocidade ou como uma alternativa para autenticação em um registro privado.

Todos os pods terão permissão de leitura a quaisquer imagens pré-obtidas.

Especificando imagePullSecrets em um pod

O Kubernetes oferece suporte à especificação de chaves de registro de imagem de contêiner em um Pod. Todos os imagePullSecrets devem estar no mesmo namespace que o Pod. Os Secrets referenciados devem ser do tipo kubernetes.io/dockercfg ou kubernetes.io/dockerconfigjson.

Criando um segredo com Docker config

Você precisa saber o nome de usuário, a senha do registro, o endereço de e-mail do cliente para autenticação no registro, além do nome do host. Execute o seguinte comando, substituindo os valores em letras maiúsculas pelos apropriados:

kubectl create secret docker-registry <name> \
  --docker-server=DOCKER_REGISTRY_SERVER \
  --docker-username=DOCKER_USER \
  --docker-password=DOCKER_PASSWORD \
  --docker-email=DOCKER_EMAIL

Se você já tem um arquivo de credenciais do Docker, em vez de usar o comando acima, você pode importar o arquivo de credenciais como um Kubernetes Secrets. Criar um segredo com base nas credenciais Docker existentes explica como configurar isso.

Isso é particularmente útil se você estiver usando vários registros privados de contêineres, como kubectl create secret docker-registry cria um Segredo que só funciona com um único registro privado.

Referenciando um imagePullSecrets em um pod

Agora, você pode criar Pods que referenciam esse Secret adicionando uma seção imagePullSecrets na definição do Pod. Cada item no array imagePullSecrets pode referenciar apenas um Secret no mesmo namespace.

Por exemplo:

cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey
EOF

cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF

Isso precisa ser feito para cada pod que está usando um registro privado.

No entanto, a configuração deste campo pode ser automatizada definindo o imagePullSecrets em um recurso de ServiceAccount.

Verifique Adicionar ImagePullSecrets a uma conta de serviço para obter instruções detalhadas.

Você pode usar isso em conjunto com um .docker / config.json por nó. As credenciais serão mescladas.

Casos de uso

Existem várias soluções para configurar registros privados. Aqui estão alguns casos de uso comuns e soluções sugeridas.

  1. Cluster executando apenas imagens não proprietárias (por exemplo, código aberto). Não há necessidade de ocultar imagens.
    • Use imagens públicas no Docker hub.
      • Nenhuma configuração necessária.
      • Alguns provedores de nuvem armazenam em cache ou espelham automaticamente imagens públicas, o que melhora a disponibilidade e reduz o tempo para extrair imagens.
  2. Cluster executando algumas imagens proprietárias que devem ser ocultadas para quem está fora da empresa, mas visível para todos os usuários do cluster.
    • Use um registro Docker privado hospedado.
      • Pode ser hospedado no Docker Hub ou em outro lugar.
      • Configure manualmente .docker/config.json em cada nó conforme descrito acima.
    • Ou execute um registro privado interno atrás de seu firewall com permissão de leitura.
      • Nenhuma configuração do Kubernetes é necessária.
    • Use um serviço de registro de imagem de contêiner que controla o acesso à imagem
      • Funcionará melhor com o escalonamento automático do cluster do que com a configuração manual de nós.
    • Ou, em um cluster onde alterar a configuração do nó é inconveniente, use imagePullSecrets.
  3. Cluster com imagens proprietárias, algumas das quais requerem controle de acesso mais rígido.
    • Certifique-se de que o controlador de admissão AlwaysPullImages está ativo. Caso contrário, todos os pods têm potencialmente acesso a todas as imagens.
    • Mova dados confidenciais para um recurso "secreto", em vez de empacotá-los em uma imagem.
  4. Um cluster multilocatário em que cada locatário precisa de seu próprio registro privado.
    • Certifique-se de que o controlador de admissão AlwaysPullImages está ativo. Caso contrário, todos os Pods de todos os locatários terão potencialmente acesso a todas as imagens.
    • Execute um registro privado com autorização necessária.
    • Gere credenciais de registro para cada locatário, coloque em segredo e preencha o segredo para cada namespace de locatário.
    • O locatário adiciona esse segredo a imagePullSecrets de cada namespace.

Se precisar de acesso a vários registros, você pode criar um segredo para cada registro.

Provedor de credenciais legado embutido no kubelet

Em versões mais antigas do Kubernetes, o kubelet tinha uma integração direta com as credenciais de provedores de nuvem. Isso permitia buscar dinamicamente as credenciais para registros de imagens.

Havia três implementações embutidas do provedor de credenciais do kubelet: ACR (Azure Container Registry), ECR (Elastic Container Registry) e GCR (Google Container Registry).

Para mais informações sobre o mecanismo legado, consulte a documentação da versão do Kubernetes que você está utilizando. As versões do Kubernetes da v1.26 até a v1.32 não incluem mais esse mecanismo legado, portanto, você precisará:

  • configurar um provedor de credenciais de imagem no kubelet em cada nó
  • ou especificar credenciais de download de imagem usando imagePullSecrets e pelo menos um Secret

Próximos passos

2 - Ambiente de Contêiner

Essa página descreve os recursos disponíveis para contêineres no ambiente de contêiner.

Ambiente de contêiner

O ambiente de contêiner do Kubernetes fornece recursos importantes para contêineres:

  • Um sistema de arquivos, que é a combinação de uma imagem e um ou mais volumes.
  • Informação sobre o contêiner propriamente.
  • Informação sobre outros objetos no cluster.

Informação de contêiner

O hostname de um contêiner é o nome do Pod em que o contêiner está executando. Isso é disponibilizado através do comando hostname ou da função gethostname chamada na libc.

O nome do Pod e o Namespace são expostos como variáveis de ambiente através de um mecanismo chamado downward API.

Variáveis de ambiente definidas pelo usuário a partir da definição do Pod também são disponíveis para o contêiner, assim como qualquer variável de ambiente especificada estáticamente na imagem Docker.

Informação do cluster

Uma lista de todos os serviços que estão executando quando um contêiner foi criado é disponibilizada para o contêiner como variáveis de ambiente. Essas variáveis de ambiente são compatíveis com a funcionalidade docker link do Docker.

Para um serviço nomeado foo que mapeia para um contêiner nomeado bar, as seguintes variáveis são definidas:

FOO_SERVICE_HOST=<o host em que o serviço está executando>
FOO_SERVICE_PORT=<a porta em que o serviço está executando>

Serviços possuem endereço IP dedicado e são disponibilizados para o contêiner via DNS, se possuírem DNS addon habilitado.

Próximos passos

3 - Classes de execução

ESTADO DA FUNCIONALIDADE: Kubernetes v1.20 [stable]

Essa página descreve o recurso RuntimeClass e a seleção do mecanismo do agente de execução.

RuntimeClass é uma funcionalidade para selecionar as configurações do agente de execução do contêiner. A configuração do agente de execução de contêineres é usada para executar os contêineres de um Pod.

Motivação

Você pode configurar um RuntimeClass diferente entre os diferentes Pods para prover um equilíbrio entre performance versus segurança. Por exemplo, se parte de sua carga de trabalho necessita de um alto nível de garantia de segurança da informação, você pode optar em executar esses Pods em um agente de execução que usa virtualização de hardware. Você então terá o benefício do isolamento extra de um agente de execução alternativo, ao custo de uma latência adicional.

Você pode ainda usar um RuntimeClass para executar diferentes Pods com o mesmo agente de execução de contêineres mas com diferentes configurações.

Configuração

  1. Configure a implementação do CRI nos nós (depende do agente de execução)
  2. Crie o recurso RuntimeClass correspondente.

1. Configure a implementação do CRI nos nós

As configurações disponíveis através do RuntimeClass sáo dependentes da implementação do Container Runtime Interface (Container runtime interface (CRI)). Veja a documentação correspondente abaixo para a sua implementação CRI para verificar como configurar.

As configurações possuem um nome handler correspondente, referenciado pelo RuntimeClass. Esse nome deve ser um valor DNS 1123 válido (letras, números e o carácter -).

2. Crie o recurso RuntimeClass correspondente

As etapas de configuração no passo 1 devem todas estar associadas a um nome para o campo handler que identifica a configuração. Para cada um, crie o objeto RuntimeClass correspondente.

O recurso RuntimeClass atualmente possui apenas 2 campos significativos: o nome do RuntimeClass (metadata.name) e o agente (handler). A definição do objeto se parece conforme a seguir:

apiVersion: node.k8s.io/v1  # RuntimeClass é definido no grupo de API node.k8s.io
kind: RuntimeClass
metadata:
  name: myclass  # O nome que o RuntimeClass será chamado como
  # RuntimeClass é um recurso global, e não possui namespace.
handler: myconfiguration  # Nome da configuração CRI correspondente

O nome de um objeto RuntimeClass deve ser um nome de subdomínio DNS válido.

Uso

Uma vez que as classes de execução estão configuradas no cluster, usar elas é relativamente simples. Especifique um runtimeClassName na especificação do Pod. Por exemplo:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

Isso irá instruir o kubelet a usar o RuntimeClass nomeado acima (myclass) para esse Pod. Se o nome do RuntimeClass não existir, ou o CRI não puder executar a solicitação, o Pod entrará na fase final Failed. Procure por um evento correspondente para uma mensagem de erro.

Se nenhum runtimeClassName for especificado, o RuntimeHandler padrão será utilizado, que é equivalente ao comportamento quando a funcionalidade de RuntimeClass está desativada.

Configuração do CRI

Para maiores detalhes de configuração dos agentes de execução CRI, veja instalação do CRI.

dockershim

O CRI dockershim embutido no Kubernetes não suporta outros agentes de execução.

containerd

Agentes de execução são configurados através da configuração do containerd em /etc/containerd/config.toml. Agentes válidos são configurados sob a seção de runtimes:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]

Veja a documentação de configuração do containerd para maiores detalhes: https://github.com/containerd/containerd/blob/main/docs/cri/config.md

CRI-O

Agentes de execução são configurados através da configuração do CRI-O em /etc/crio/crio.conf. Agentes válidos são configurados na seção crio.runtime table:

[crio.runtime.runtimes.${HANDLER_NAME}]
  runtime_path = "${PATH_TO_BINARY}"

Veja a documentação de configuração do CRI-O para maiores detalhes.

Associação

ESTADO DA FUNCIONALIDADE: Kubernetes v1.16 [beta]

Ao especificar o campo scheduling para um RuntimeClass, você pode colocar limites e garantir que os Pods executando dentro de uma RuntimeClass sejam associados a nós que suportem eles. Se o scheduling não estiver configurado, assume-se que esse RuntimeClass é suportado por todos os nós.

Para garantir que os Pods sejam executados em um nó que suporte um RuntimeClass específico, aquele conjunto de nós deve possuir uma marca/label padrão que é selecionado pelo campo runtimeclass.scheduling.nodeSelector. O nodeSelector do RuntimeClass é combinado com o nodeSelector do Pod em tempo de admissão, obtendo a intersecção do conjunto de nós selecionado por cada. Se existir um conflito, o pod será rejeitado.

Se os nós suportados possuírem marcação de restrição para prevenir outros Pods com uma classe de execução diferente de executar no nó, você pode adicionar o campo tolerations ao objeto RuntimeClass. Assim como com o nodeSelector, o tolerations é combinado com o campo tolerations do Pod em tempo de admissão, efetivamente pegando a intersecção do conjunto de nós aplicáveis para cada.

Para saber mais sobre a configuração de seleção de nós e tolerâncias, veja Associando Pods a Nós.

Sobrecarga de Pods

ESTADO DA FUNCIONALIDADE: Kubernetes v1.18 [beta]

Você pode especificar os recursos extra que estão associados à execução de um Pod. Declarar esses recursos extra permite ao cluster (incluindo o agendador/scheduler de pods) contabilizar por esses recursos quando estiver decidindo sobre Pods e recursos. Para usar a contabilização desses recursos extras, você deve estar com o feature gate PodOverhead habilitado (ele já está habilitado por padrão).

Os recursos extras utilizados são especificados no objeto RuntimeClass através do campo overhead. Ao usar esses campos, você especifica o uso extra de recursos necessários para executar Pods utilizando-se desse Runtimeclass e assim contabilizar esses recursos para o Kubernetes.

Próximos passos

4 - Hooks de Ciclo de Vida do Contêiner

Essa página descreve como os contêineres gerenciados pelo kubelet podem usar a estrutura de hook de ciclo de vida do contêiner para executar código acionado por eventos durante seu ciclo de vida de gerenciamento.

Visão Geral

Análogo a muitas estruturas de linguagem de programação que tem hooks de ciclo de vida de componentes, como angular, o Kubernetes fornece aos contêineres hooks de ciclo de vida. Os hooks permitem que os contêineres estejam cientes dos eventos em seu ciclo de vida de gerenciamento e executem código implementado em um manipulador quando o hook de ciclo de vida correspondente é executado.

Hooks do contêiner

Existem dois hooks que são expostos para os contêiners:

PostStart

Este hook é executado imediatamente após um contêiner ser criado. Entretanto, não há garantia que o hook será executado antes do ENTRYPOINT do contêiner. Nenhum parâmetro é passado para o manipulador.

PreStop

Esse hook é chamado imediatamente antes de um contêiner ser encerrado devido a uma solicitação de API ou um gerenciamento de evento como liveness/startup probe failure, preemption, resource contention e outros. Uma chamada ao hook PreStop falha se o contêiner já está em um estado finalizado ou concluído e o hook deve ser concluído antes que o sinal TERM seja enviado para parar o contêiner. A contagem regressiva do período de tolerância de término do Pod começa antes que o hook PreStop seja executado, portanto, independentemente do resultado do manipulador, o contêiner será encerrado dentro do período de tolerância de encerramento do Pod. Nenhum parâmetro é passado para o manipulador.

Uma descrição mais detalhada do comportamento de término pode ser encontrada em Término de Pods.

Implementações de manipulador de hook

Os contêineres podem acessar um hook implementando e registrando um manipulador para esse hook. Existem dois tipos de manipuladores de hooks que podem ser implementados para contêineres:

  • Exec - Executa um comando específico, como pre-stop.sh, dentro dos cgroups e Namespaces do contêiner.
  • HTTP - Executa uma requisição HTTP em um endpoint específico do contêiner.

Execução do manipulador de hook

Quando um hook de gerenciamento de ciclo de vida do contêiner é chamado, o sistema de gerenciamento do Kubernetes executa o manipulador de acordo com a ação do hook, httpGet e tcpSocket são executados pelo processo kubelet e exec é executado pelo contêiner.

As chamadas do manipulador do hook são síncronas no contexto do Pod que contém o contêiner. Isso significa que para um hook PostStart, o ENTRYPOINT do contêiner e o hook disparam de forma assíncrona. No entanto, se o hook demorar muito para ser executado ou travar, o contêiner não consegue atingir o estado running.

Os hooks PreStop não são executados de forma assíncrona a partir do sinal para parar o contêiner, o hook precisa finalizar a sua execução antes que o sinal TERM possa ser enviado. Se um hook PreStop travar durante a execução, a fase do Pod será Terminating e permanecerá até que o Pod seja morto após seu terminationGracePeriodSeconds expirar. Esse período de tolerância se aplica ao tempo total necessário para o hook PreStopexecutar e para o contêiner parar normalmente. Se por exemplo, o terminationGracePeriodSeconds é 60, e o hook leva 55 segundos para ser concluído, e o contêiner leva 10 segundos para parar normalmente após receber o sinal, então o contêiner será morto antes que possa parar normalmente, uma vez que o terminationGracePeriodSeconds é menor que o tempo total (55 + 10) que é necessário para que essas duas coisas aconteçam.

Se um hook PostStart ou PreStop falhar, ele mata o contêiner.

Os usuários devem tornar seus hooks o mais leve possíveis. Há casos, no entanto, em que comandos de longa duração fazem sentido, como ao salvar o estado antes de parar um contêiner.

Garantias de entrega de hooks

A entrega do hook é destinada a acontecer pelo menos uma vez, o que quer dizer que um hook pode ser chamado várias vezes para qualquer evento, como para PostStart ou PreStop. Depende da implementação do hook lidar com isso corretamente.

Geralmente, apenas entregas únicas são feitas. Se, por exemplo, um receptor de hook HTTP estiver inativo e não puder receber tráfego, não há tentativa de reenviar. Em alguns casos raros, no entanto, pode ocorrer uma entrega dupla. Por exemplo, se um kubelet reiniciar no meio do envio de um hook, o hook pode ser reenviado depois que o kubelet voltar a funcionar.

Depurando manipuladores de hooks

Os logs para um manipulador de hook não são expostos em eventos de Pod. Se um manipulador falhar por algum motivo, ele transmitirá um evento. Para PostStart é o evento FailedPostStartHook e para PreStop é o evento FailedPreStopHook. Você pode ver esses eventos executando kubectl describe pod <nome_do_pod>. Aqui está um exemplo de saída de eventos da execução deste comando:

Events:
  FirstSeen  LastSeen  Count  From                                                   SubObjectPath          Type      Reason               Message
  ---------  --------  -----  ----                                                   -------------          --------  ------               -------
  1m         1m        1      {default-scheduler }                                                          Normal    Scheduled            Successfully assigned test-1730497541-cq1d2 to gke-test-cluster-default-pool-a07e5d30-siqd
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Pulling              pulling image "test:1.0"
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Created              Created container with docker id 5c6a256a2567; Security:[seccomp=unconfined]
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Pulled               Successfully pulled image "test:1.0"
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Started              Started container with docker id 5c6a256a2567
  38s        38s       1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Killing              Killing container with docker id 5c6a256a2567: PostStart handler: Error executing in Docker Container: 1
  37s        37s       1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Killing              Killing container with docker id 8df9fdfd7054: PostStart handler: Error executing in Docker Container: 1
  38s        37s       2      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}                         Warning   FailedSync           Error syncing pod, skipping: failed to "StartContainer" for "main" with RunContainerError: "PostStart handler: Error executing in Docker Container: 1"
  1m         22s       2      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Warning   FailedPostStartHook

Próximos passos