This is the multi-page printable view of this section. Click here to print.
Configuração
1 - Melhores Práticas de Configuração
Esse documento destaca e consolida as melhores práticas de configuração apresentadas em todo o guia de usuário, na documentação de introdução e nos exemplos.
Este é um documento vivo. Se você pensar em algo que não está nesta lista, mas pode ser útil para outras pessoas, não hesite em criar uma issue ou submeter um PR.
Dicas Gerais de Configuração
Ao definir configurações, especifique a versão mais recente estável da API.
Os arquivos de configuração devem ser armazenados em um sistema de controle antes de serem enviados ao cluster. Isso permite que você reverta rapidamente uma alteração de configuração, caso necessário. Isso também auxilia na recriação e restauração do cluster.
Escreva seus arquivos de configuração usando YAML ao invés de JSON. Embora esses formatos possam ser usados alternadamente em quase todos os cenários, YAML tende a ser mais amigável.
Agrupe objetos relacionados em um único arquivo sempre que fizer sentido. Geralmente, um arquivo é mais fácil de gerenciar do que vários. Veja o guestbook-all-in-one.yaml como exemplo dessa sintaxe.
Observe também que vários comandos
kubectl
podem ser chamados em um diretório. Por exemplo, você pode chamarkubectl apply
em um diretório de arquivos de configuração.Não especifique valores padrões desnecessariamente: configurações simples e mínimas diminuem a possibilidade de erros.
Coloque descrições de objetos nas anotações para permitir uma melhor análise.
"Naked" Pods comparados a ReplicaSets, Deployments, e Jobs
Se você puder evitar, não use "naked" Pods (ou seja, se você puder evitar, pods não vinculados a um ReplicaSet ou Deployment). Os "naked" pods não serão reconfigurados em caso de falha de um nó.
Criar um Deployment, que cria um ReplicaSet para garantir que o número desejado de Pods esteja disponível e especifica uma estratégia para substituir os Pods (como RollingUpdate), é quase sempre preferível do que criar Pods diretamente, exceto para alguns cenários explícitos de restartPolicy:Never. Um Job também pode ser apropriado.
Services
Crie o Service antes de suas cargas de trabalho de backend correspondentes (Deployments ou ReplicaSets) e antes de quaisquer cargas de trabalho que precisem acessá-lo. Quando o Kubernetes inicia um contêiner, ele fornece variáveis de ambiente apontando para todos os Services que estavam em execução quando o contêiner foi iniciado. Por exemplo, se um Service chamado
foo
existe, todos os contêineres vão receber as seguintes variáveis em seu ambiente inicial:FOO_SERVICE_HOST=<o host em que o Service está executando> FOO_SERVICE_PORT=<a porta em que o Service está executando>
Isso implica em um requisito de pedido - qualquer Service
que um Pod
quer acessar precisa ser criado antes do Pod
em si, ou então as variáveis de ambiente não serão populadas. O DNS não possui essa restrição.
Um cluster add-on opcional (embora fortemente recomendado) é um servidor DNS. O servidor DNS monitora a API do Kubernetes buscando novos
Services
e cria um conjunto de DNS para cada um. Se o DNS foi habilitado em todo o cluster, então todos osPods
devem ser capazes de fazer a resolução deServices
automaticamente.Não especifique um
hostPort
para um Pod a menos que isso seja absolutamente necessário. Quando você vincula um Pod a umhostPort
, isso limita o número de lugares em que o Pod pode ser agendado, porque cada combinação de <hostIP
,hostPort
,protocol
> deve ser única. Se você não especificar ohostIP
eprotocol
explicitamente, o Kubernetes vai usar0.0.0.0
como ohostIP
padrão eTCP
comoprotocol
padrão.Se você precisa de acesso a porta apenas para fins de depuração, pode usar o apiserver proxy ou o
kubectl port-forward
.Se você precisa expor explicitamente a porta de um Pod no nó, considere usar um Service do tipo NodePort antes de recorrer a
hostPort
.Evite usar
hostNetwork
pelos mesmos motivos dohostPort
.Use headless Services (que tem um
ClusterIP
ouNone
) para descoberta de serviço quando você não precisar de um balanceador de cargakube-proxy
.
Usando Labels
- Defina e use labels que identifiquem atributos semânticos da sua aplicação ou Deployment, como
{ app: myapp, tier: frontend, phase: test, deployment: v3 }
. Você pode usar essas labels para selecionar os Pods apropriados para outros recursos; por exemplo, um Service que seleciona todos os Podstier: frontend
, ou todos os componentes deapp: myapp
. Veja o app guestbook para exemplos dessa abordagem.
Um Service pode ser feito para abranger vários Deployments, omitindo labels específicas de lançamento de seu seletor. Quando você precisar atualizar um serviço em execução sem downtime, use um Deployment.
Um estado desejado de um objeto é descrito por um Deployment, e se as alterações nesse spec forem aplicadas o controlador do Deployment altera o estado real para o estado desejado em uma taxa controlada.
Use as labels comuns do Kubernetes para casos de uso comuns. Essas labels padronizadas enriquecem os metadados de uma forma que permite que ferramentas, incluindo
kubectl
e a dashboard, funcionem de uma forma interoperável.Você pode manipular labels para depuração. Como os controladores do Kubernetes (como ReplicaSet) e Services se relacionam com os Pods usando seletor de labels, remover as labels relevantes de um Pod impedirá que ele seja considerado por um controlador ou que seja atendido pelo tráfego de um Service. Se você remover as labels de um Pod existente, seu controlador criará um novo Pod para substituí-lo. Essa é uma maneira útil de depurar um Pod anteriormente "ativo" em um ambiente de "quarentena". Para remover ou alterar labels interativamente, use
kubectl label
.
Imagens de Contêiner
A imagePullPolicy e tag da imagem afetam quando o kubelet tenta puxar a imagem especificada.
imagePullPolicy: IfNotPresent
: a imagem é puxada apenas se ainda não estiver presente localmente.imagePullPolicy: Always
: sempre que o kubelet inicia um contêiner, ele consulta o registry da imagem do contêiner para verificar o resumo de assinatura da imagem. Se o kubelet tiver uma imagem do contêiner com o mesmo resumo de assinatura armazenado em cache localmente, o kubelet usará a imagem em cache, caso contrário, o kubelet baixa(pulls) a imagem com o resumo de assinatura resolvido, e usa essa imagem para iniciar o contêiner.imagePullPolicy
é omitido se a tag da imagem é:latest
ou seimagePullPolicy
é omitido é automaticamente definido comoAlways
. Observe que não será utilizado paraifNotPresent
se o valor da tag mudar.imagePullPolicy
é omitido se uma tag da imagem existe mas não:latest
:imagePullPolicy
é automaticamente definido comoifNotPresent
. Observe que isto não será atualizado paraAlways
se a tag for removida ou alterada para:latest
.imagePullPolicy: Never
: presume-se que a imagem exista localmente. Não é feita nenhuma tentativa de puxar a imagem.
Nota:
Para garantir que seu contêiner sempre use a mesma versão de uma imagem, você pode especificar seu resumo de assinatura; substitua<nome-da-imagem>:<tag>
por <nome-da-imagem>@<hash>
(por exemplo, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
). Esse resumo de assinatura identifica exclusivamente uma versão
específica de uma imagem, então isso nunca vai ser atualizado pelo Kubernetes a menos que você mude o valor do resumo de assinatura da imagem.Nota:
Você deve evitar o uso da tag:latest
em produção, pois é mais difícil rastrear qual versão da imagem está sendo executada e mais difícil reverter adequadamente.Nota:
A semântica de cache do provedor de imagem subjacente torna até mesmoimagePullPolicy: Always
eficiente, contanto que o registro esteja acessível de forma confiável. Com o Docker, por exemplo, se a imagem já existe, a tentativa de baixar(pull) é rápida porque todas as camadas da imagem são armazenadas em cache e nenhum download de imagem é necessário.Usando kubectl
Use
kubectl apply -f <directory>
. Isso procura por configurações do Kubernetes em todos os arquivos.yaml
,.yml
em<directory>
e passa isso paraapply
.Use labels selectors para operações
get
edelete
em vez de nomes de objetos específicos. Consulte as seções sobre label selectors e usando Labels efetivamente.Use
kubectl create deployment
ekubectl expose
para criar rapidamente Deployments e Services de um único contêiner. Consulte Use um Service para acessar uma aplicação em um cluster para obter um exemplo.
2 - ConfigMaps
Um ConfigMap é um objeto da API usado para armazenar dados não-confidenciais em pares chave-valor. Pods podem consumir ConfigMaps como variáveis de ambiente, argumentos de linha de comando ou como arquivos de configuração em um volume.
Um ConfigMap ajuda a desacoplar configurações vinculadas ao ambiente das imagens de contêiner, de modo a tornar aplicações mais facilmente portáveis.
Cuidado:
O ConfigMap não oferece confidencialidade ou encriptação. Se os dados que você deseja armazenar são confidenciais, utilize Secret ao invés de um ConfigMap, ou utilize ferramentas adicionais (de terceiros) para manter seus dados privados.Motivação
Utilize um ConfigMap para manter a configuração separada do código da aplicação.
Por exemplo, imagine que você esteja desenvolvendo uma aplicação que pode ser executada
no seu computador local (para desenvolvimento) e na nuvem (para manipular tráfego real).
Você escreve código para ler a variável de ambiente chamada DATABASE_HOST
.
No seu ambiente local, você configura essa variável com o valor localhost
. Na nuvem, você
configura essa variável para referenciar um serviço
do Kubernetes que expõe o componente do banco de dados ao seu cluster.
Isto permite que você baixe uma imagem de contêiner que roda na nuvem e depure exatamente
o mesmo código localmente se necessário.
Um ConfigMap não foi planejado para conter grandes quantidades de dados. Os dados armazenados em um ConfigMap não podem exceder 1 MiB. Se você precisa armazenar configurações que são maiores que este limite, considere montar um volume ou utilizar um serviço separado de banco de dados ou de arquivamento de dados.
Objeto ConfigMap
Um ConfigMap é um objeto
da API que permite o armazenamento de configurações para consumo por outros objetos. Diferentemente
de outros objetos do Kubernetes que contém um campo spec
, o ConfigMap contém os campos data
e
binaryData
. Estes campos aceitam pares chave-valor como valores. Ambos os campos data
e binaryData
são opcionais. O campo data
foi pensado para conter sequências de bytes UTF-8, enquanto o campo binaryData
foi planejado para conter dados binários em forma de strings codificadas em base64.
É obrigatório que o nome de um ConfigMap seja um subdomínio DNS válido.
Cada chave sob as seções data
ou binaryData
pode conter quaisquer caracteres alfanuméricos,
-
, _
e .
. As chaves armazenadas na seção data
não podem colidir com as chaves armazenadas
na seção binaryData
.
A partir da versão v1.19 do Kubernetes, é possível adicionar o campo immutable
a uma definição de ConfigMap
para criar um ConfigMap imutável.
ConfigMaps e Pods
Você pode escrever uma spec
para um Pod que se refere a um ConfigMap e configurar o(s) contêiner(es)
neste Pod baseados em dados do ConfigMap. O Pod e o ConfigMap devem estar no mesmo
namespace.
Nota:
Aspec
de um Pod estático não pode se referir a um
ConfigMap ou a quaisquer outros objetos da API.Exemplo de um ConfigMap que contém algumas chaves com valores avulsos e outras chaves com valores semelhantes a fragmentos de arquivos de configuração:
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# chaves com valores de propriedades; cada chave mapeia para um valor avulso
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# chaves semelhantes a fragmentos de arquivos
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
Existem quatro formas diferentes para consumo de um ConfigMap na configuração de um contêiner dentro de um Pod:
- Dentro de um comando de contêiner e seus argumentos.
- Variáveis de ambiente para um contêiner.
- Criando um arquivo em um volume somente leitura, para consumo pela aplicação.
- Escrevendo código para execução dentro do Pod que utilize a API do Kubernetes para ler um ConfigMap.
Os diferentes métodos de consumo oferecem diferentes formas de modelar os dados sendo consumidos. Para os três primeiros métodos, o kubelet utiliza os dados de um ConfigMap quando o(s) contêiner(es) do Pod são inicializados.
O quarto método envolve escrita de código para leitura do ConfigMap e dos seus dados. No entanto, como a API do Kubernetes está sendo utilizada diretamente, a aplicação pode solicitar atualizações sempre que o ConfigMap for alterado e reagir quando isso ocorre. Acessar a API do Kubernetes diretamente também permite ler ConfigMaps em outros namespaces.
Exemplo de um Pod que utiliza valores do ConfigMap game-demo
para configurar um Pod:
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: alpine
command: ["sleep", "3600"]
env:
# Define as variáveis de ambiente
- name: PLAYER_INITIAL_LIVES # Note que aqui a variável está definida em caixa alta,
# diferente da chave no ConfigMap.
valueFrom:
configMapKeyRef:
name: game-demo # O ConfigMap de onde esse valor vem.
key: player_initial_lives # A chave que deve ser buscada.
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# Volumes são definidos no escopo do Pod, e os pontos de montagem são definidos
# nos contêineres dentro dos pods.
- name: config
configMap:
# Informe o nome do ConfigMap que deseja montar.
name: game-demo
# Uma lista de chaves do ConfigMap para serem criadas como arquivos.
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"
ConfigMaps não diferenciam entre propriedades com valores simples ou valores complexos, que ocupam várias linhas. O importante é a forma que Pods e outros objetos consomem tais valores.
Neste exemplo, definir um volume e montar ele dentro do contêiner demo
no caminho /config
cria dois arquivos: /config/game.properties
e /config/user-interface.properties
, embora existam
quatro chaves distintas no ConfigMap. Isso se deve ao fato de que a definição do Pod contém uma lista
items
na seção volumes
.
Se a lista items
for omitida, cada chave do ConfigMap torna-se um arquivo cujo nome é a sua chave
correspondente, e quatro arquivos serão criados.
Usando ConfigMaps
ConfigMaps podem ser montados como volumes de dados. ConfigMaps também podem ser utilizados por outras partes do sistema sem serem diretamente expostos ao Pod. Por exemplo, ConfigMaps podem conter dados que outras partes do sistema devem usar para configuração.
A forma mais comum de utilização de ConfigMaps é a configuração de contêineres executando em Pods no mesmo namespace. Você também pode utilizar um ConfigMap separadamente.
Por exemplo, existem complementos ou operadores que adaptam seus comportamentos de acordo com dados de um ConfigMap.
Utilizando ConfigMaps como arquivos em um Pod
Para consumir um ConfigMap em um volume em um Pod:
- Crie um ConfigMap ou utilize um ConfigMap existente. Múltiplos Pods podem referenciar o mesmo ConfigMap.
- Modifique sua definição de Pod para adicionar um volume em
.spec.volumes[]
. Escolha um nome qualquer para o seu volume, e referencie o seu objeto ConfigMap no campo.spec.volumes[].configMap.name
. - Adicione um campo
.spec.containers[].volumeMounts[]
a cada um dos contêineres que precisam do ConfigMap. Especifique.spec.containers[].volumeMounts[].readOnly = true
e informe no campo.spec.containers[].volumeMounts[].mountPath
um caminho de um diretório não utilizado onde você deseja que este ConfigMap apareça. - Modifique sua imagem ou linha de comando de modo que o programa procure
por arquivos no diretório especificado no passo anterior. Cada chave no
campo
data
do ConfigMap será transformado em um nome de arquivo no diretório especificado pormountPath
.
Exemplo de um Pod que monta um ConfigMap em um volume:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
configMap:
name: myconfigmap
Cada ConfigMap que você deseja utilizar precisa ser referenciado em
.spec.volumes
.
Se houver múltiplos contêineres no Pod, cada contêiner deve ter seu
próprio bloco volumeMounts
, mas somente uma instância de .spec.volumes
é necessária por ConfigMap.
ConfigMaps montados são atualizados automaticamente
Quando um ConfigMap que está sendo consumido em um volume é atualizado, as chaves projetadas são
eventualmente atualizadas também. O Kubelet checa se o ConfigMap montado está atualizado em cada
sincronização periódica.
No entanto, o kubelet utiliza o cache local para buscar o valor atual do ConfigMap.
O tipo de cache é configurável utilizando o campo ConfigMapAndSecretChangeDetectionStrategy
na
configuração do Kubelet (KubeletConfiguration).
Um ConfigMap pode ter sua propagação baseada em um watch (comportamento padrão), que é o sistema
de propagação de mudanças incrementais em objetos do Kubernetes; baseado em TTL (time to live,
ou tempo de expiração); ou redirecionando todas as requisições diretamente para o servidor da API.
Como resultado, o tempo decorrido total entre o momento em que o ConfigMap foi atualizado até o momento
quando as novas chaves são projetadas nos Pods pode ser tão longo quanto o tempo de sincronização
do kubelet somado ao tempo de propagação do cache, onde o tempo de propagação do cache depende do
tipo de cache escolhido: o tempo de propagação pode ser igual ao tempo de propagação do watch,
TTL do cache, ou zero, de acordo com cada um dos tipos de cache.
ConfigMaps que são consumidos como variáveis de ambiente não atualizam automaticamente e requerem uma reinicialização do pod.
ConfigMaps imutáveis
Kubernetes v1.21 [stable]
A funcionalidade Secrets e ConfigMaps imutáveis do Kubernetes fornece uma opção para marcar Secrets e ConfigMaps individuais como imutáveis. Para clusters que utilizam ConfigMaps extensivamente (ao menos centenas de milhares de mapeamentos únicos de ConfigMaps para Pods), prevenir alterações dos seus dados traz as seguintes vantagens:
- protege de atualizações acidentais ou indesejadas que podem causar disrupção na execução de aplicações
- melhora o desempenho do cluster através do fechamento de watches de ConfigMaps marcados como imutáveis, diminuindo significativamente a carga no kube-apiserver
Essa funcionalidade é controlada pelo feature gate
ImmutableEphemeralVolumes
. É possível criar um ConfigMap imutável adicionando o campo
immutable
e marcando seu valor com true
.
Por exemplo:
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
immutable: true
Após um ConfigMap ser marcado como imutável, não é possível reverter a alteração, nem
alterar o conteúdo dos campos data
ou binaryData
. É possível apenas apagar e recriar
o ConfigMap. Como Pods existentes que consomem o ConfigMap em questão mantém um ponto de
montagem que continuará referenciando este objeto após a remoção, é recomendado recriar
estes pods.
Próximos passos
- Leia sobre Secrets (em inglês).
- Leia Configure a Pod to Use a ConfigMap (em inglês).
- Leia The Twelve-Factor App (em inglês) para entender a motivação da separação de código e configuração.
3 - Secrets
Um Secret é um objeto que contém uma pequena quantidade de informação sensível, como senhas, tokens ou chaves. Este tipo de informação poderia, em outras circunstâncias, ser colocada diretamente em uma configuração de Pod ou em uma imagem de contêiner. O uso de Secrets evita que você tenha de incluir dados confidenciais no seu código.
Secrets podem ser criados de forma independente dos Pods que os consomem. Isto reduz o risco de que o Secret e seus dados sejam expostos durante o processo de criação, visualização e edição ou atualização de Pods. O Kubernetes e as aplicações que rodam no seu cluster podem também tomar outras precauções com Secrets, como por exemplo evitar a escrita de dados confidenciais em local de armazenamento persistente (não-volátil).
Secrets são semelhantes a ConfigMaps, mas foram especificamente projetados para conter dados confidenciais.
Cuidado:
Os Secrets do Kubernetes são, por padrão, gravados não-encriptados no sistema de armazenamento de dados utilizado pelo servidor da API (etcd). Qualquer pessoa com acesso à API ou ao etcd consegue obter ou modificar um Secret. Além disso, qualquer pessoa que possui autorização para criar Pods em um namespace consegue utilizar este privilégio para ler qualquer Secret naquele namespace. Isso inclui acesso indireto, como por exemplo a permissão para criar Deployments.
Para utilizar Secrets de forma segura, siga pelo menos as instruções abaixo:
- Habilite encriptação em disco para Secrets.
- Habilite ou configure regras de RBAC que restrinjam o acesso de leitura a Secrets (incluindo acesso indireto).
- Quando apropriado, utilize mecanismos como RBAC para limitar quais perfis e usuários possuem permissão para criar novos Secrets ou substituir Secrets existentes.
Consulte Segurança da informação para Secrets para mais detalhes.
Usos para Secrets
Existem três formas principais para um Pod utilizar um Secret:
- Como arquivos em um volume montado em um ou mais de seus contêineres.
- Como uma variável de ambiente de um contêiner.
- Pelo kubelet ao baixar imagens de contêiner para o Pod.
A camada de gerenciamento do Kubernetes também utiliza Secrets. Por exemplo, os Secrets de tokens de autoinicialização são um mecanismo que auxilia a automação do registro de nós.
Alternativas a Secrets
Ao invés de utilizar um Secret para proteger dados confidenciais, você pode escolher uma maneira alternativa. Algumas das opções são:
- se o seu componente cloud native precisa autenticar-se a outra aplicação que está rodando no mesmo cluster Kubernetes, você pode utilizar uma ServiceAccount e seus tokens para identificar seu cliente.
- existem ferramentas fornecidas por terceiros que você pode rodar, no seu cluster ou externamente, que providenciam gerenciamento de Secrets. Por exemplo, um serviço que Pods accessam via HTTPS, que revelam um Secret se o cliente autenticar-se corretamente (por exemplo, utilizando um token de ServiceAccount).
- para autenticação, você pode implementar um serviço de assinatura de certificados X.509 personalizado, e utilizar CertificateSigningRequests para permitir ao serviço personalizado emitir certificados a pods que os necessitam.
- você pode utilizar um plugin de dispositivo para expor a um Pod específico um hardware de encriptação conectado a um nó. Por exemplo, você pode agendar Pods confiáveis em nós que oferecem um Trusted Platform Module, configurado em um fluxo de dados independente.
Você pode também combinar duas ou mais destas opções, incluindo a opção de utilizar objetos do tipo Secret.
Por exemplo: implemente (ou instale) um operador que solicite tokens de sessão de curta duração a um serviço externo, e crie Secrets baseado nestes tokens. Pods rodando no seu cluster podem fazer uso de tokens de sessão, e o operador garante que estes permanecem válidos. Esta separação significa que você pode rodar Pods que não precisam ter conhecimento do mecanismo exato para geração e atualização de tais tokens de sessão.
Trabalhando com Secrets
Criando um Secret
Existem diversas formas de criar um Secret:
- crie um Secret utilizando o comando
kubectl
- crie um Secret a partir de um arquivo de configuração
- crie um Secret utilizando a ferramenta kustomize
Restrições de nomes de Secret e dados
O nome de um Secret deve ser um subdomínio DNS válido.
Você pode especificar o campo data
e/ou o campo stringData
na criação de um
arquivo de configuração de um Secret. Ambos os campos data
e stringData
são
opcionais. Os valores das chaves no campo data
devem ser strings codificadas
no formato base64. Se a conversão para base64 não for desejável, você pode
optar por informar os dados no campo stringData
, que aceita strings arbitrárias
como valores.
As chaves dos campos data
e stringData
devem consistir de caracteres
alfanuméricos, -
, _
, ou .
. Todos os pares chave-valor no campo stringData
são internamente combinados com os dados do campo data
. Se uma chave aparece
em ambos os campos, o valor informado no campo stringData
tem a precedência.
Limite de tamanho
Secrets individuais são limitados a 1MiB em tamanho. Esta limitação tem por objetivo desencorajar a criação de Secrets muito grandes que possam exaurir a memória do servidor da API e do kubelet. No entanto, a criação de vários Secrets pequenos também pode exaurir a memória. Você pode utilizar uma cota de recurso a fim de limitar o número de Secrets (ou outros recursos) em um namespace.
Editando um Secret
Você pode editar um Secret existente utilizando kubectl:
kubectl edit secrets mysecret
Este comando abre o seu editor padrão configurado e permite a modificação dos
valores do Secret codificados em base64 no campo data
. Por exemplo:
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file, it will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: { ... }
creationTimestamp: 2016-01-22T18:41:56Z
name: mysecret
namespace: default
resourceVersion: "164619"
uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
Este manifesto de exemplo define um Secret com duas chaves no campo data
:
username
and password
.
Os valores são strings codificadas em formato base64. No entanto, quando um
Secret é utilizado em um Pod, o kubelet fornece os dados decodificados ao Pod
e seus contêineres.
Você pode especificar muitas chaves e valores em um Secret só, ou utilizar muitos Secrets. Escolha a opção que for mais conveniente para o caso de uso.
Utilizando Secrets
Secrets podem ser montados como volumes de dados ou expostos como variáveis de ambiente para serem utilizados num container de um Pod. Secrets também podem ser utilizados por outras partes do sistema, sem serem diretamente expostos ao Pod. Por exemplo, Secrets podem conter credenciais que outras partes do sistema devem utilizar para interagir com sistemas externos no lugar do usuário.
Secrets montados como volumes são verificados para garantir que o nome referenciado realmente é um objeto do tipo Secret. Portanto, um Secret deve ser criado antes de quaisquer Pods que dependem deste Secret.
Se um Secret não puder ser encontrado (porque não existe, ou devido a um problema de conectividade com o servidor da API) o kubelet tenta periodicamente reiniciar aquele Pod. O kubelet também relata um evento para aquele Pod, incluindo detalhes do problema ao buscar o Secret.
Secrets Opcionais
Quando você define uma variável de ambiente em um contêiner baseada em um Secret, você pode especificar que o Secret em questão será opcional. O padrão é o Secret ser requerido.
Nenhum dos contêineres de um Pod irão inicializar até que todos os Secrets requeridos estejam disponíveis.
Se um Pod referencia uma chave específica em um Secret e o Secret existe, mas não possui a chave com o nome referenciado, o Pod falha durante a inicialização.
Utilizando Secrets como arquivos em um Pod
Se você deseja acessar dados de um Secret em um Pod, uma das formas de consumir esta informação é fazer com que o Kubernetes deixe o valor daquele Secret disponível como um arquivo dentro do sistema de arquivos de um ou mais dos contêineres daquele Pod.
Para configurar isso:
- Crie um Secret ou utilize um previamente existente. Múltiplos Pods podem referenciar o mesmo secret.
- Modifique sua definição de Pod para adicionar um volume na lista
.spec.volumes[]
. Escolha um nome qualquer para o seu volume e adicione um campo.spec.volumes[].secret.secretName
com o mesmo valor do seu objeto Secret. - Adicione um ponto de montagem de volume à lista
.spec.containers[].volumeMounts[]
de cada contêiner que requer o Secret. Especifique.spec.containers[].volumeMounts[].readOnly = true
e especifique o valor do campo.spec.containers[].volumeMounts[].mountPath
com o nome de um diretório não utilizado onde você deseja que os Secrets apareçam. - Modifique sua imagem ou linha de comando de modo que o programa procure por
arquivos naquele diretório. Cada chave no campo
data
se torna um nome de arquivo no diretório especificado emmountPath
.
Este é um exemplo de Pod que monta um Secret de nome mysecret
em um volume:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret # configuração padrão; "mysecret" precisa existir
Cada Secret que você deseja utilizar deve ser referenciado na lista
.spec.volumes
.
Se existirem múltiplos contêineres em um Pod, cada um dos contêineres
necessitará seu próprio bloco volumeMounts
, mas somente um volume na lista
.spec.volumes
é necessário por Secret.
Nota:
Versões do Kubernetes anteriores a v1.22 criavam automaticamente credenciais para acesso à API do Kubernetes. Este mecanismo antigo era baseado na criação de Secrets com tokens que podiam então ser montados em Pods em execução. Em versões mais recentes, incluindo o Kubernetes v1.31, credenciais para acesso à API são obtidas diretamente através da API TokenRequest e são montadas em Pods utilizando um volume projetado. Os tokens obtidos através deste método possuem tempo de vida limitado e são automaticamente invalidados quando o Pod em que estão montados é removido.
Você ainda pode criar manualmente um Secret de token de service account se você precisa de um token que não expire, por exemplo. No entanto, o uso do subrecurso TokenRequest é recomendado para obtenção de um token para acesso à API ao invés do uso de Secrets de token de service account.
Projeção de chaves de Secrets em caminhos específicos
Você pode também controlar os caminhos dentro do volume onde as chaves do Secret
são projetadas. Você pode utilizar o campo .spec.volumes[].secret.items
para
mudar o caminho de destino de cada chave:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
Neste caso:
- O valor da chave
username
é armazenado no arquivo/etc/foo/my-group/my-username
ao invés de/etc/foo/username
. - O valor da chave
password
não é projetado no sistema de arquivos.
Se .spec.volumes[].secret.items
for utilizado, somente chaves especificadas
na lista items
são projetadas. Para consumir todas as chaves do Secret, deve
haver um item para cada chave no campo items
.
Se você listar as chaves explicitamente, então todas as chaves listadas precisam existir no Secret correspondente. Caso contrário, o volume não é criado.
Permissões de arquivos de Secret
Você pode trocar os bits de permissão POSIX de uma chave avulsa de Secret.
Se nenhuma permissão for especificada, 0644
é utilizado por padrão.
Você pode também especificar uma permissão padrão para o volume inteiro de
Secret e sobrescrever esta permissão por chave, se necessário.
Por exemplo, você pode especificar uma permissão padrão da seguinte maneira:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
defaultMode: 0400
Dessa forma, o Secret será montado em /etc/foo
e todos os arquivos criados
no volume terão a permissão 0400
.
Nota:
Se você estiver definindo um Pod ou um template de Pod utilizando JSON, observe que a especificação JSON não suporta a notação octal. Você pode utilizar o valor decimal para o campodefaultMode
(por exemplo, 0400 em base octal equivale a
256 na base decimal).Se você estiver escrevendo YAML, você pode escrever o valor para
defaultMode
em octal.Consumindo valores de Secrets em volumes
Dentro do contêiner que monta um volume de Secret, as chaves deste Secret aparecem como arquivos e os valores dos Secrets são decodificados do formato base64 e armazenados dentro destes arquivos.
Ao executar comandos dentro do contêiner do exemplo anterior, obteremos os seguintes resultados:
ls /etc/foo
O resultado é semelhante a:
username
password
cat /etc/foo/username
O resultado é semelhante a:
admin
cat /etc/foo/password
O resultado é semelhante a:
1f2d1e2e67df
A aplicação rodando dentro do contêiner é responsável pela leitura dos Secrets dentro dos arquivos.
Secrets montados são atualizados automaticamente
Quando um volume contém dados de um Secret, e o Secret referenciado é atualizado, o Kubernetes rastreia a atualização e atualiza os dados no volume, utilizando uma abordagem de consistência eventual.
Nota:
Um contêiner que utiliza Secrets através de um volume montado com a propriedadesubPath
não recebe
atualizações automatizadas para este Secret.O kubelet mantém um cache das chaves e valores atuais dos Secrets que são
utilizados em volumes de Pods daquele nó. Você pode configurar a forma que o
kubelet detecta diferenças dos valores armazenados em cache. O campo
configMapAndSecretDetectionStrategy
na
configuração do kubelet
controla qual estratégia o kubelet usa. A estratégia padrão é Watch
.
Atualizações em Secrets podem ser propagadas por um mecanismo de observação da API (estratégia padrão), baseado em cache com um tempo de expiração definido (time-to-live), ou solicitado diretamente ao servidor da API do cluster a cada iteração do ciclo de sincronização do kubelet.
Como resultado, o atraso total entre o momento em que o Secret foi atualizado até o momento em que as novas chaves são projetadas no Pod pode ser tão longo quanto a soma do tempo de sincronização do kubelet somado ao tempo de atraso de propagação do cache, onde o atraso de propagação do cache depende do tipo de cache escolhido. Seguindo a mesma ordem listada no parágrafo anterior, estes valores são: atraso de propagação via watch, tempo de expiração configurado no cache (time-to-live, ou TTL), ou zero para solicitação direta ao servidor da API.
Utilizando Secrets como variáveis de ambiente
Para utilizar um secret em uma variável de ambiente em um Pod:
- Crie um Secret ou utilize um já existente. Múltiplos Pods podem referenciar o mesmo Secret.
- Modifique a definição de cada contêiner do Pod em que desejar consumir o
Secret, adicionando uma variável de ambiente para cada uma das chaves que
deseja consumir.
A variável de ambiente que consumir o valor da chave em questão deverá
popular o nome do Secret e a sua chave correspondente no campo
env[].valueFrom.secretKeyRef
. - Modifique sua imagem de contêiner ou linha de comando de forma que o programa busque os valores nas variáveis de ambiente especificadas.
Este é um exemplo de um Pod que utiliza Secrets em variáveis de ambiente:
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
optional: false # valor padrão; "mysecret" deve existir
# e incluir uma chave com o nome "username"
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
optional: false # valor padrão; "mysecret" deve existir
# e incluir uma chave com o nome "password"
restartPolicy: Never
Variáveis de ambiente inválidas
Secrets utilizados para popular variáveis de ambiente através do campo envFrom
que possuem chaves consideradas inválidas para nomes de variáveis de ambiente
têm tais chaves ignoradas. O Pod irá iniciar normalmente.
Se você definir um Pod contendo um nome de variável de ambiente inválido, os
eventos de inicialização do Pod incluirão um evento com a razão
InvalidVariableNames
e uma mensagem que lista as chaves inválidas ignoradas.
O exemplo abaixo demonstra um Pod que referencia um Secret chamado mysecret
,
onde mysecret
contém duas chaves inválidas: 1badkey
and 2alsobad
.
kubectl get events
O resultado é semelhante a:
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON
0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentVariableNames kubelet, 127.0.0.1 Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.
Consumindo valores de Secret em variáveis de ambiente
Dentro de um contêiner que consome um Secret em variáveis de ambiente, as chaves do Secret aparecem como variáveis de ambiente comuns, contendo os dados do Secret decodificados do formato base64. Ao executar comandos no contêiner do exemplo anterior, obteremos os resultados abaixo:
echo $SECRET_USERNAME
O resultado é semelhante a:
admin
echo $SECRET_PASSWORD
O resultado é semelhante a:
1f2d1e2e67df
Nota:
Se um contêiner já consome um Secret em uma variável de ambiente, uma atualização do Secret não será detectada pelo contêiner a menos que este seja reiniciado. Há soluções de terceiros que fornecem a funcionalidade de reinicialização automática de Pods quando o valor dos Secrets mudam.Secrets para obtenção de imagens de contêiner
Se você deseja obter imagens de contêiner de um repositório privado, você
precisa fornecer ao kubelet uma maneira de se autenticar a este repositório.
Você pode configurar o campo imagePullSecrets
para esta finalidade. Estes
Secrets são configurados a nível de Pod.
O campo imagePullSecrets
de um Pod é uma lista de referências a Secrets
no mesmo namespace que o Pod.
Você pode utilizar imagePullSecrets
para enviar credenciais para acesso a um
registro de contêineres ao kubelet. O kubelet utiliza essa informação para
baixar uma imagem privada no lugar do seu Pod.
Veja o campo PodSpec
na
referência da API de Pods
para maiores detalhes sobre o campo imagePullSecrets
.
Usando imagePullSecrets
O campo imagePullSecrets
é uma lista de referências a Secrets no mesmo
namespace.
Você pode utilizar o campo imagePullSecrets
para enviar um Secret que contém
uma senha para um registro de imagens de contêiner do Docker (ou outro registro
de imagens de contêiner). O kubelet utiliza essa informação para baixar uma
imagem privada no lugar do seu Pod.
Veja a API PodSpec
para mais informações sobre o campo imagePullSecrets
.
Especificando imagePullSecrets
manualmente
Você pode ler sobre como especificar imagePullSecrets
em um Pod na
documentação de imagens de contêiner.
Configurando imagePullSecrets
para serem adicionados automaticamente
Você pode criar manualmente imagePullSecrets
e referenciá-los em uma
ServiceAccount. Quaisquer Pods criados com esta ServiceAccount, especificada
explicitamente ou por padrão, têm o campo imagePullSecrets
populado com os
mesmos valores existentes na service account.
Veja adicionando imagePullSecrets
a uma service account
para uma explicação detalhada do processo.
Utilizando Secrets com pods estáticos
Você não pode utilizar ConfigMaps ou Secrets em Pods estáticos.
Casos de uso
Caso de uso: Como variáveis de ambiente em um contêiner
Crie um manifesto de Secret
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
USER_NAME: YWRtaW4=
PASSWORD: MWYyZDFlMmU2N2Rm
Crie o Secret no seu cluster:
kubectl apply -f mysecret.yaml
Utilize envFrom
para definir todos os dados do Secret como variáveis de
ambiente do contêiner. Cada chave do Secret se torna o nome de uma variável de
ambiente no Pod.
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: registry.k8s.io/busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- secretRef:
name: mysecret
restartPolicy: Never
Caso de uso: Pod com chaves SSH
Crie um Secret contendo chaves SSH:
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
O resultado é semelhante a:
secret "ssh-key-secret" created
Você também pode criar um manifesto kustomization.yaml
com um campo
secretGenerator
contendo chaves SSH.
Cuidado:
Analise cuidadosamente antes de enviar suas próprias chaves SSH: outros usuários do cluster podem ter acesso a este Secret.
Como alternativa, você pode criar uma chave SSH privada representando a identidade de um serviço que você deseja que seja acessível a todos os usuários com os quais você compartilha o cluster do Kubernetes em questão. Desse modo, você pode revogar esta credencial em caso de comprometimento.
Agora você pode criar um Pod que referencia o Secret com a chave SSH e consome-o em um volume:
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
labels:
name: secret-test
spec:
volumes:
- name: secret-volume
secret:
secretName: ssh-key-secret
containers:
- name: ssh-test-container
image: mySshImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
Ao rodar o comando do contêiner, as partes da chave estarão disponíveis em:
/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey
O contêiner então pode utilizar os dados do secret para estabelecer uma conexão SSH.
Caso de uso: Pods com credenciais de ambientes de produção ou testes
Este exemplo ilustra um Pod que consome um Secret contendo credenciais de um ambiente de produção e outro Pod que consome um Secret contendo credenciais de um ambiente de testes.
Você pode criar um manifesto kustomization.yaml
com um secretGenerator
ou
rodar kubectl create secret
.
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
O resultado é semelhante a:
secret "prod-db-secret" created
Você pode também criar um Secret com credenciais para o ambiente de testes.
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
O resultado é semelhante a:
secret "test-db-secret" created
Nota:
Caracteres especiais como $
, \
, *
, +
e !
serão interpretados pelo seu
shell e precisam
de sequências de escape.
Na maioria dos shells, a forma mais fácil de gerar sequências de escape para
suas senhas é escrevê-las entre aspas simples ('
). Por exemplo, se a sua senha
for S!B\*d$zDsb=
, você deve executar o comando da seguinte forma:
kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
Não é necessário gerar sequências de escape para caracteres especiais em arquivos
(utilizados com a opção --from-file
).
Agora, crie os Pods:
cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
apiVersion: v1
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: prod-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
- kind: Pod
apiVersion: v1
metadata:
name: test-db-client-pod
labels:
name: test-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: test-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
EOF
Adicione os Pods a um manifesto kustomization.yaml
:
cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF
Crie todos estes objetos no servidor da API rodando o comando:
kubectl apply -k .
Ambos os contêineres terão os seguintes arquivos presentes nos seus sistemas de arquivos, com valores para cada um dos ambientes dos contêineres:
/etc/secret-volume/username
/etc/secret-volume/password
Observe como as spec
s para cada um dos Pods diverge somente em um campo. Isso
facilita a criação de Pods com capacidades diferentes a partir de um template
mais genérico.
Você pode simplificar ainda mais a definição básica do Pod através da utilização de duas service accounts diferentes:
prod-user
com o Secretprod-db-secret
test-user
com o Secrettest-db-secret
A especificação do Pod é reduzida para:
apiVersion: v1
kind: Pod
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
serviceAccount: prod-db-client
containers:
- name: db-client-container
image: myClientImage
Caso de uso: dotfiles em um volume de Secret
Você pode fazer com que seus dados fiquem "ocultos" definindo uma chave que se
inicia com um ponto (.
). Este tipo de chave representa um dotfile, ou
arquivo "oculto". Por exemplo, quando o Secret abaixo é montado em um volume,
secret-volume
:
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: registry.k8s.io/busybox
command:
- ls
- "-l"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
Este volume irá conter um único arquivo, chamado .secret-file
, e o contêiner
dotfile-test-container
terá este arquivo presente no caminho
/etc/secret-volume/.secret-file
.
Nota:
Arquivos com nomes iniciados por um caractere de ponto são ocultados do resultado do comandols -l
. Você precisa utilizar ls -la
para vê-los ao
listar o conteúdo de um diretório.Caso de uso: Secret visível somente em um dos contêineres de um pod
Suponha que um programa necessita manipular requisições HTTP, executar regras de negócio complexas e então assinar mensagens com HMAC. Devido à natureza complexa da aplicação, pode haver um exploit despercebido que lê arquivos remotos no servidor e que poderia expor a chave privada para um invasor.
Esta aplicação poderia ser dividida em dois processos, separados em dois contêineres distintos: um contêiner de front-end, que manipula as interações com o usuário e a lógica de negócio, mas não consegue ver a chave privada; e um contêiner assinador, que vê a chave privada e responde a requisições simples de assinatura do front-end (por exemplo, através de rede local).
Com essa abordagem particionada, um invasor agora precisa forçar o servidor de aplicação a rodar comandos arbitrários, o que é mais difícil de ser feito do que apenas ler um arquivo presente no disco.
Tipos de Secrets
Ao criar um Secret, você pode especificar o seu tipo utilizando o campo type
do objeto Secret, ou algumas opções de linha de comando equivalentes no comando
kubectl
, quando disponíveis. O campo type
de um Secret é utilizado para
facilitar a manipulação programática de diferentes tipos de dados confidenciais.
O Kubernetes oferece vários tipos embutidos de Secret para casos de uso comuns. Estes tipos variam em termos de validações efetuadas e limitações que o Kubernetes impõe neles.
Tipo embutido | Caso de uso |
---|---|
Opaque | dados arbitrários definidos pelo usuário |
kubernetes.io/service-account-token | token de service account (conta de serviço) |
kubernetes.io/dockercfg | arquivo ~/.dockercfg serializado |
kubernetes.io/dockerconfigjson | arquivo ~/.docker/config.json serializado |
kubernetes.io/basic-auth | credenciais para autenticação básica (basic auth) |
kubernetes.io/ssh-auth | credenciais para autenticação SSH |
kubernetes.io/tls | dados para um cliente ou servidor TLS |
bootstrap.kubernetes.io/token | dados de token de autoinicialização |
Você pode definir e utilizar seu próprio tipo de Secret definindo o valor do
campo type
como uma string não-nula em um objeto Secret (uma string em branco
é tratada como o tipo Opaque
).
O Kubernetes não restringe nomes de tipos. No entanto, quando tipos embutidos são utilizados, você precisa atender a todos os requisitos daquele tipo.
Se você estiver definindo um tipo de Secret que seja para uso público, siga a
convenção e estruture o tipo de Secret para conter o seu domínio antes do nome,
separado por uma barra (/
).
Por exemplo: cloud-hosting.example.net/cloud-api-credentials
.
Para melhor desempenho em uma requisição get
repetitiva, clientes podem criar
objetos que referenciam o Secret e então utilizar a requisição watch
neste
novo objeto, requisitando o Secret novamente quando a referência mudar.
Além disso, uma API de "observação em lotes"
para permitir a clientes observar recursos individuais também foi proposta e
provavelmente estará disponível em versões futuras do Kubernetes.
Opaque
é o tipo predefinido de Secret quando o campo type
é omitido em um
arquivo de configuração de Secret. Quando um Secret é criado usando o comando
kubectl
, você deve usar o subcomando generic
para indicar que um Secret é
do tipo Opaque
. Por exemplo, o comando a seguir cria um Secret vazio do tipo
Opaque
:
kubectl create secret generic empty-secret
kubectl get secret empty-secret
O resultado será semelhante ao abaixo:
NAME TYPE DATA AGE
empty-secret Opaque 0 2m6s
A coluna DATA
demonstra a quantidade de dados armazenados no Secret. Neste
caso, 0
significa que este objeto Secret está vazio.
Secrets de token de service account (conta de serviço)
Secrets do tipo kubernetes.io/service-account-token
são utilizados para
armazenar um token que identifica uma service account (conta de serviço). Ao
utilizar este tipo de Secret, você deve garantir que a anotação
kubernetes.io/service-account.name
contém um nome de uma service account
existente. Um controlador do Kubernetes preenche outros campos, como por exemplo
a anotação kubernetes.io/service-account.uid
e a chave token
no campo data
com o conteúdo do token.
O exemplo de configuração abaixo declara um Secret de token de service account:
apiVersion: v1
kind: Secret
metadata:
name: secret-sa-sample
annotations:
kubernetes.io/service-account-name: "sa-name"
type: kubernetes.io/service-account-token
data:
# Você pode incluir pares chave-valor adicionais, da mesma forma que faria com
# Secrets do tipo Opaque
extra: YmFyCg==
Ao criar um Pod, o Kubernetes automaticamente cria um Secret de service account e automaticamente atualiza o seu Pod para utilizar este Secret. O Secret de token de service account contém credenciais para acessar a API.
A criação automática e o uso de credenciais de API podem ser desativados ou substituídos se desejado. Porém, se tudo que você necessita é poder acessar o servidor da API de forma segura, este é o processo recomendado.
Veja a documentação de
ServiceAccount
para mais informações sobre o funcionamento de service accounts. Você pode
verificar também os campos automountServiceAccountToken
e serviceAccountName
do Pod
para mais informações sobre como referenciar service accounts em Pods.
Secrets de configuração do Docker
Você pode utilizar um dos tipos abaixo para criar um Secret que armazena credenciais para accesso a um registro de contêineres para busca de imagens:
kubernetes.io/dockercfg
kubernetes.io/dockerconfigjson
O tipo kubernetes.io/dockercfg
é reservado para armazenamento de um arquivo
~/.dockercfg
serializado. Este arquivo é o formato legado para configuração
do utilitário de linha de comando do Docker. Ao utilizar este tipo de Secret,
é preciso garantir que o campo data
contém uma chave .dockercfg
cujo valor
é o conteúdo do arquivo ~/.dockercfg
codificado no formato base64.
O tipo kubernetes.io/dockerconfigjson
foi projetado para armazenamento de um
conteúdo JSON serializado que obedece às mesmas regras de formato que o arquivo
~/.docker/config.json
. Este arquivo é um formato mais moderno para o conteúdo
do arquivo ~/.dockercfg
. Ao utilizar este tipo de Secret, o conteúdo do campo
data
deve conter uma chave .dockerconfigjson
em que o conteúdo do arquivo
~/.docker/config.json
é fornecido codificado no formato base64.
Um exemplo de um Secret do tipo kubernetes.io/dockercfg
:
apiVersion: v1
kind: Secret
metadata:
name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
.dockercfg: |
"<base64 encoded ~/.dockercfg file>"
Nota:
Se você não desejar fazer a codificação em formato base64, você pode utilizar o campostringData
como alternativa.Ao criar estes tipos de Secret utilizando um manifesto (arquivo YAML), o
servidor da API verifica se a chave esperada existe no campo data
e se o valor
fornecido pode ser interpretado como um conteúdo JSON válido. O servidor da API
não verifica se o conteúdo informado é realmente um arquivo de configuração do
Docker.
Quando você não tem um arquivo de configuração do Docker, ou quer utilizar o
comando kubectl
para criar um Secret de registro de contêineres, você pode
rodar o comando:
kubectl create secret docker-registry secret-tiger-docker \
--docker-email=tiger@acme.example \
--docker-username=tiger \
--docker-password=pass1234 \
--docker-server=my-registry.example:5000
Esse comando cria um secret do tipo kubernetes.io/dockerconfigjson
. Se você
obtiver o conteúdo do campo .data.dockerconfigjson
deste novo Secret e
decodificá-lo do formato base64:
kubectl get secret secret-tiger-docker -o jsonpath='{.data.*}' | base64 -d
o resultado será equivalente a este documento JSON (que também é um arquivo de configuração válido do Docker):
{
"auths": {
"my-registry.example:5000": {
"username": "tiger",
"password": "pass1234",
"email": "tiger@acme.example",
"auth": "dGlnZXI6cGFzczEyMzQ="
}
}
}
Nota:
O valor do campoauth
no exemplo acima é codificado em base64; ele está
ofuscado mas não criptografado. Qualquer pessoa com acesso a este Secret pode
ler o conteúdo do token bearer.Secret de autenticação básica
O tipo kubernetes.io/basic-auth
é fornecido para armazenar credenciais
necessárias para autenticação básica. Ao utilizar este tipo de Secret, o campo
data
do Secret deve conter as duas chaves abaixo:
username
: o usuário utilizado para autenticação;password
: a senha ou token para autenticação.
Ambos os valores para estas duas chaves são textos codificados em formato base64.
Você pode fornecer os valores como texto simples utilizando o campo stringData
na criação do Secret.
O arquivo YAML abaixo é um exemplo de configuração para um Secret de autenticação básica:
apiVersion: v1
kind: Secret
metadata:
name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
username: admin # required field for kubernetes.io/basic-auth
password: t0p-Secret # required field for kubernetes.io/basic-auth
O tipo de autenticação básica é fornecido unicamente por conveniência. Você pode
criar um Secret do tipo Opaque
utilizado para autenticação básica. No entanto,
utilizar o tipo embutido e público de Secret (kubernetes.io/basic-auth
)
auxilia outras pessoas a compreenderem o propósito do seu Secret, e define uma
convenção de expectativa de nomes de chaves
O tipo embutido também fornece verificação dos campos requeridos pelo servidor
da API.
Secret de autenticação SSH
O tipo embutido kubernetes.io/ssh-auth
é fornecido para armazenamento de dados
utilizados em autenticação SSH. Ao utilizar este tipo de Secret, você deve
especificar um par de chave-valor ssh-privatekey
no campo data
(ou no campo
stringData
) com a credencial SSH a ser utilizada.
O manifesto abaixo é um exemplo de configuração para um Secret de autenticação SSH com um par de chaves pública/privada:
apiVersion: v1
kind: Secret
metadata:
name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
# os dados estão abreviados neste exemplo
ssh-privatekey: |
MIIEpQIBAAKCAQEAulqb/Y ...
O Secret de autenticação SSH é fornecido apenas para a conveniência do usuário.
Você pode criar um Secret do tipo Opaque
para credentials utilizadas para
autenticação SSH. No entanto, a utilização do tipo embutido e público de Secret
(kubernetes.io/tls
) auxilia outras pessoas a compreenderem o propósito do
seu Secret, e define uma convenção de quais chaves podem ser esperadas.
O tipo embutido também fornece verificação dos campos requeridos em uma
configuração de Secret.
Cuidado:
Chaves privadas SSH não estabelecem, por si só, uma comunicação confiável entre um cliente SSH e um servidor. Uma forma secundária de estabelecer confiança é necessária para mitigar ataques man-in-the-middle (MITM), como por exemplo um arquivoknown_hosts
adicionado a um ConfigMap.Secrets TLS
O Kubernetes fornece o tipo embutido de Secret kubernetes.io/tls
para
armazenamento de um certificado e sua chave associada que são tipicamente
utilizados para TLS.
Uma utilização comum de Secrets TLS é a configuração de encriptação em trânsito para um recurso Ingress, mas este tipo de secret pode também ser utilizado com outros recursos ou diretamente por uma carga de trabalho.
Ao utilizar este tipo de Secret, as chaves tls.key
e tls.crt
devem ser
informadas no campo data
(ou stringData
) da configuração do Secret, embora o
servidor da API não valide o conteúdo de cada uma destas chaves.
O YAML a seguir tem um exemplo de configuração para um Secret TLS:
apiVersion: v1
kind: Secret
metadata:
name: secret-tls
type: kubernetes.io/tls
data:
# os dados estão abreviados neste exemplo
tls.crt: |
MIIC2DCCAcCgAwIBAgIBATANBgkqh ...
tls.key: |
MIIEpgIBAAKCAQEA7yn3bRHQ5FHMQ ...
O tipo TLS é fornecido para a conveniência do usuário. Você pode criar um
Secret do tipo Opaque
para credenciais utilizadas para o servidor e/ou
cliente TLS. No entanto, a utilização do tipo embutido auxilia a manter a
consistência dos formatos de Secret no seu projeto; o servidor da API
valida se os campos requeridos estão presentes na configuração do Secret.
Ao criar um Secret TLS utilizando a ferramenta de linha de comando kubectl
,
você pode utilizar o subcomando tls
conforme demonstrado no exemplo abaixo:
kubectl create secret tls my-tls-secret \
--cert=path/to/cert/file \
--key=path/to/key/file
O par de chaves pública/privada deve ser criado previamente. O certificado
de chave pública a ser utilizado no argumento --cert
deve ser codificado em
formato DER conforme especificado na
seção 5.1 da RFC 7468
e deve corresponder à chave privada fornecida no argumento --key
(PKCS #8 no formato DER;
seção 11 da RFC 7468).
Nota:
Um Secret kubernetes.io/tls armazena o conteúdo de chaves e certificados em formato DER codificado em base64. Se você tem familiaridade com o formato PEM para chaves privadas e certificados, o conteúdo é o mesmo do formato PEM, excluindo-se a primeira e a última linhas.
Por exemplo, para um certificado, você não inclui as linhas
--------BEGIN CERTIFICATE-----
e -------END CERTIFICATE----
.
Secret de token de autoinicialização
Um Secret de token de autoinicialização pode ser criado especificando o tipo de
um Secret explicitamente com o valor bootstrap.kubernetes.io/token
. Este tipo
de Secret é projetado para tokens utilizados durante o processo de inicialização
de nós. Este tipo de Secret armazena tokens utilizados para assinar ConfigMaps
conhecidos.
Um Secret de token de autoinicialização é normalmente criado no namespace
kube-system
e nomeado na forma bootstrap-token-<id-do-token>
, onde
<id-do-token>
é um texto com 6 caracteres contendo a identificação do token.
No formato de manifesto do Kubernetes, um Secret de token de autoinicialização se assemelha ao exemplo abaixo:
apiVersion: v1
kind: Secret
metadata:
name: bootstrap-token-5emitj
namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
token-id: NWVtaXRq
token-secret: a3E0Z2lodnN6emduMXAwcg==
usage-bootstrap-authentication: dHJ1ZQ==
usage-bootstrap-signing: dHJ1ZQ==
Um Secret do tipo token de autoinicialização possui as seguintes chaves no campo
data
:
token-id
: Uma string com 6 caracteres aleatórios como identificador do token. Requerido.token-secret
: Uma string de 16 caracteres aleatórios como o conteúdo secreto do token. Requerido.description
: Uma string contendo uma descrição do propósito para o qual este token é utilizado. Opcional.expiration
: Um horário absoluto UTC no formato RFC3339 especificando quando o token deve expirar. Opcional.usage-bootstrap-<usage>
: Um conjunto de flags booleanas indicando outros usos para este token de autoinicialização.auth-extra-groups
: Uma lista separada por vírgulas de nomes de grupos que serão autenticados adicionalmente, além do gruposystem:bootstrappers
.
O YAML acima pode parecer confuso, já que os valores estão todos codificados em formato base64. Você pode criar o mesmo Secret utilizando este YAML:
apiVersion: v1
kind: Secret
metadata:
# Observe como o Secret é nomeado
name: bootstrap-token-5emitj
# Um Secret de token de inicialização geralmente fica armazenado no namespace
# kube-system
namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
expiration: "2020-09-13T04:39:10Z"
# Esta identificação de token é utilizada no nome
token-id: "5emitj"
token-secret: "kq4gihvszzgn1p0r"
# Este token pode ser utilizado para autenticação
usage-bootstrap-authentication: "true"
# e pode ser utilizado para assinaturas
usage-bootstrap-signing: "true"
Secrets imutáveis
Kubernetes v1.21 [stable]
O Kubernetes permite que você marque Secrets (e ConfigMaps) específicos como imutáveis. Prevenir mudanças nos dados de um Secret existente tem os seguintes benefícios:
- protege você de alterações acidentais (ou indesejadas) que poderiam provocar disrupções em aplicações.
- em clusters com uso extensivo de Secrets (pelo menos dezenas de milhares de montagens únicas de Secrets a Pods), utilizar Secrets imutáveis melhora o desempenho do seu cluster através da redução significativa de carga no kube-apiserver. O kubelet não precisa manter um watch em Secrets que são marcados como imutáveis.
Marcando um Secret como imutável
Você pode criar um Secret imutável adicionando o campo immutable
com o valor
true
ao manifesto do Secret. Por exemplo:
apiVersion: v1
kind: Secret
metadata:
...
data:
...
immutable: true
Você pode também atualizar qualquer Secret mutável existente para torná-lo imutável.
Nota:
Uma vez que um Secret ou ConfigMap seja marcado como imutável, não é mais possível reverter esta mudança, nem alterar os conteúdos do campodata
. Você
pode somente apagar e recriar o Secret. Pods existentes mantém um ponto de
montagem referenciando o Secret removido - é recomendado recriar tais Pods.Informações de segurança sobre Secrets
Embora ConfigMaps e Secrets funcionem de formas similares, o Kubernetes aplica proteções extras aos objetos Secret.
Secrets frequentemente contém valores dentro de um espectro de importância, muitos dos quais podem provocar escalações de privilégios dentro do Kubernetes (por exemplo, um token de service account) e em sistemas externos. Mesmo que uma aplicação individual possa avaliar o poder dos Secrets com os quais espera interagir, outras aplicações dentro do mesmo namespace podem tornar tais suposições inválidas.
Um Secret só é enviado a um nó se um Pod naquele nó precisa do Secret em questão.
Para montar Secrets em Pods, o kubelet armazena uma cópia dos dados dentro de um
sistema de arquivos tmpfs
, de modo que os dados confidenciais não sejam
escritos em armazenamento durável. Uma vez que o Pod que dependia do Secret seja
removido, o kubelet apaga sua cópia local dos dados confidenciais do Secret.
Um Pod pode possuir vários contêineres. Por padrão, contêineres que você define têm acesso somente à ServiceAccount padrão e seu Secret relacionado. Você deve explicitamente definir variáveis de ambiente ou mapear um volume dentro de um contêiner para ter acesso a qualquer outro Secret.
Podem haver Secrets para vários Pods no mesmo nó. No entanto, somente os Secrets que um Pod requisitou estão potencialmente visíveis dentro de seus contêineres. Portanto, um Pod não tem acesso aos Secrets de outro Pod.
Aviso:
Quaisquer contêineres privilegiados em um nó são passíveis de acesso a todos os Secrets naquele nó.Recomendações de segurança para desenvolvedores
- Aplicações ainda devem proteger o valor da informação confidencial após lê-la de uma variável de ambiente ou volume. Por exemplo, sua aplicação deve evitar imprimir os dados do Secret sem encriptação ou transmitir esta informação para aplicações terceiras de confiabilidade não-estabelecida.
- Se você estiver definindo múltiplos contêineres em um Pod, e somente um destes contêineres necessita acesso a um Secret, defina o volume ou variável de ambiente de maneira que os demais contêineres não tenham acesso àquele Secret.
- Se você configurar um Secret através de um manifesto, com os dados codificados em formato base64, compartilhar este arquivo ou salvá-lo em um sistema de controle de versão de código-fonte significa que o Secret está disponível para qualquer pessoa que pode ler o manifesto. O formato base64 não é um método de encriptação e não fornece nenhuma confidencialidade adicional em comparação com texto puro.
- Ao instalar aplicações que interagem com a API de Secrets, você deve limitar o acesso utilizando políticas de autorização, como por exemplo RBAC.
- Na API do Kubernetes, requisições
watch
elist
em Secrets dentro de um namespace são extremamente poderosas. Evite fornecer este acesso quando possível, já que listar Secrets permite aos clientes inspecionar os valores de todos os Secrets naquele namespace.
Recomendações de segurança para administradores de cluster
Cuidado:
Um usuário que pode criar um Pod que utiliza um Secret pode também ver o valor daquele Secret. Mesmo que as permissões do cluster não permitam ao usuário ler o Secret diretamente, o mesmo usuário poderia ter acesso a criar um Pod que então expõe o Secret.- Restrinja a habilidade de usar as requisições
watch
elist
para listar todos os Secrets em um cluster (utilizando a API do Kubernetes) de modo que somente os componentes mais privilegiados e de nível de sistema possam realizar esta ação. - Ao instalar aplicações que interajam com a API de Secrets, você deve limitar o acesso utilizando políticas de autorização, como por exemplo RBAC.
- No servidor da API, objetos (incluindo Secrets) são persistidos no
etcd; portanto:
- somente permita a administradores do sistema o acesso ao etcd (incluindo acesso somente-leitura);
- habilite encriptação em disco para objetos Secret, de modo que os dados de tais Secrets não sejam armazenados em texto plano no etcd;
- considere a destruição do armazenamento durável previamente utilizado pelo etcd quando não estiver mais em uso;
- se houverem múltiplas instâncias do etcd em uso, garanta que o etcd esteja configurado para utilizar SSL/TLS para comunicação entre instâncias.
Próximos passos
- Aprenda a gerenciar Secrets utilizando
kubectl
- Aprenda a gerenciar Secrets utilizando arquivos de configuração
- Aprenda a gerenciar Secrets utilizando kustomize
- Leia a documentação de referência da API de Secrets
4 - Gerenciamento de recursos em Pods e contêineres
Ao criar a especificação de um Pod, você pode opcionalmente especificar quanto de cada recurso um contêiner precisa. Os recursos mais comuns a serem especificados são CPU e memória (RAM); há outros recursos que podem ser especificados.
Quando você especifica o requerimento de recursos em um Pod, o kube-scheduler utiliza esta informação para decidir a qual nó o Pod será atribuído. Quando você especifica um limite de recurso para um contêiner, o kubelet garante o cumprimento de tais limites, de modo que o contêiner em execução não consiga utilizar uma quantidade de tal recurso além do limite especificado. O kubelet também reserva pelo menos o requerimento daquele recurso de sistema especificamente para que este contêiner utilize.
Requerimentos e limites
Se o nó em que um Pod está rodando tem o suficiente de um recurso específico
disponível, é possível (e permitido) a um contêiner utilizar mais do que o seu
request
para aquele recurso especifica. No entanto, não é permitido a um
contêiner consumir mais do que o seu limit
para um recurso.
Por exemplo, se você especificar um requerimento de memory
de 256 MiB para um
contêiner, e aquele contêiner está em um Pod atribuído a um nó com 8GiB de
memória, sem outros Pods, então este contêiner pode tentar consumir mais memória
RAM.
Se você especificar um limite de memory
de 4GiB para aquele contêiner, o
kubelet (e o
agente de execução de contêiner)
vão garantir o cumprimento do limite. O agente de execução impede que o contêiner
utilize mais de um recurso do que seu limite configurado. Por exemplo, quando
um processo no contêiner tenta consumir mais que o limite permitido de memória,
o núcleo do sistema encerra o processo que tentou efetuar a alocação de memória
com um erro de memória esgotada (out of memory (OOM) error).
Limites podem ser implementados de forma reativa (o sistema intervém quando uma violação ocorre) ou por garantia (o sistema previne o contêiner de exceder o limite). Diferentes agentes de execução implementam as mesmas restrições de maneiras diferentes.
Nota:
Se um contêiner especifica seu próprio limite de memória, mas não especifica seu requerimento de memória, o Kubernetes automaticamente cria um requerimento de memória com o mesmo valor do limite. A mesma regra vale para o limite de CPU: quando não há requerimento de CPU, o Kubernetes automaticamente cria um requerimento de CPU idêntico ao limite.Tipos de recursos
CPU e memória são tipos de recursos. Um tipo de recurso possui uma unidade básica. CPU representa processamento computacional e é especificada em unidades de CPU do Kubernetes. Memória é especificada em bytes. Em cargas de trabalho Linux, você pode especificar o recurso huge pages. Huge pages são uma funcionalidade específica do Linux que permite ao núcleo do sistema operacional alocar blocos de memória muito maiores que o tamanho de página de memória padrão.
Por exemplo, em um sistema onde o tamanho da página de memória padrão é de 4 KiB,
você pode especificar um limite hugepages-2Mi: 80Mi
. Se o contêiner tentar
alocar mais de 40 huge pages de 2 MiB cada, ou um total de 80 MiB, essa
alocação irá falhar.
Nota:
Você não pode superdimensionar (ou solicitar acima do limite físico) recursos do tipohugepages-*
.
O recurso hugepages-*
difere dos recursos memory
e cpu
neste aspecto.CPU e memória são chamados coletivamente de recursos computacionais, ou apenas recursos. Recursos computacionais são quantidades mensuráveis que podem ser requisitadas, alocadas, e consumidas. Estes recursos diferem dos recursos de API. Recursos de API, como Pods e Services são objetos que podem ser lidos e modificados através do servidor da API do Kubernetes.
Requerimentos de recursos e limites de Pod e contêiner
Para cada contêiner, você pode especificar limites e requerimentos de recursos, incluindo os seguintes recursos:
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.limits.hugepages-<size>
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
spec.containers[].resources.requests.hugepages-<size>
Embora você possa especificar apenas requerimentos e limites para contêineres individuais, é útil também pensar sobre os requerimentos e limites gerais de um Pod. Para um recurso em particular, um requerimento ou limite de recurso de um Pod é a soma de todos os valores dos requerimentos ou limites de um recurso daquele tipo, especificados em cada um dos contêineres daquele Pod.
Unidades de recursos no Kubernetes
Unidades de recurso de CPU
Limites e requerimentos de recursos de CPU são mensurados em unidades de cpu. No Kubernetes, uma unidade de CPU é equivalente a um núcleo físico de CPU, ou um núcleo virtual, dependendo se o nó é uma máquina física ou uma máquina virtual rodando em uma máquina física.
Requerimentos fracionários são permitidos. Quando você define um contêiner cujo
valor do campo spec.containers[].resources.requests.cpu
é 0.5
, você está
solicitando metade da quantidade de CPU que teria sido solicitada caso o valor
fosse 1.0
.
No caso de unidades de recurso de CPU, a expressão de
quantidade 0.1
é equivalente à expressão 100m
, que pode ser lida como "cem milicpus", ou
"cem milinúcleos". "Milicpu" ou "milinúcleo" equivalem à milésima parte de um
núcleo ou CPU, de modo que "100m" equivalem a 10% do tempo computacional de um
processador.
Recursos de CPU são sempre especificados como uma quantidade absoluta de recurso,
nunca como uma quantidade relativa. Por exemplo, 500m
de CPU representam
grosseiramente a mesma quantidade de poder computacional, independentemente do
contêiner rodar em uma máquina com processador de núcleo único, de dois núcleos
ou de 48 núcleos.
Nota:
O Kubernetes não permite que você especifique recursos de CPU com uma precisão maior que1m
. Devido a isso, é útil especificar unidades de CPU menores do que
1.0
ou 1000m
utilizando a notação de milicpu. Por exemplo, 5m
ao invés de
0.005
.Unidades de recurso de memória
Limites e requerimentos de memory
são medidos em bytes. Você pode expressar
memória como um número inteiro ou como um número de ponto fixo, utilizando um
destes sufixos de
quantidade:
E, P, T, G, M, k. Você também pode utilizar os equivalentes de potência de dois:
Ei, Pi, Ti, Gi, Mi, Ki. Por exemplo, as quantidades abaixo representam, a grosso
modo, o mesmo valor:
128974848, 129e6, 129M, 128974848000m, 123Mi
Tome cuidado com os sufixos. Se você solicitar 400m
de memória, esta
quantidade estará de fato requerendo o equivalente a 0,4 byte de memória. A
intenção da pessoa que fez esta requisição provavelmente era solictar 400
mebibytes (400Mi
) ou 400 megabytes (400M
).
Exemplo de recursos de contêiner
O Pod seguinte tem dois contêineres. Ambos os contêineres têm um requerimento de 0,25 CPU e 64 MiB (ou 226 bytes) de memória. Cada contêiner tem um limite de 0,5 CPU e 128 MiB de memória. Você pode dizer que o Pod tem um requerimento de 0,5 CPU e 128 MiB de memória, e um limite de 1 CPU e 256 MiB de memória.
---
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Como Pods com requerimentos de recursos são agendados
Quando você cria um Pod, o escalonador do Kubernetes seleciona um nó para que o Pod rode. Cada nó possui uma capacidade máxima para cada um dos tipos de recurso: a quantidade de CPU e memória que o nó pode fornecer aos Pods. O escalonador garante que, para cada tipo de recurso, a soma dos requerimentos de recursos dos contêineres agendados seja menor que a capacidade do nó. Note que, embora o consumo de memória ou CPU real nos nós seja muito baixo, o escalonador ainda irá se recusar a agendar um Pod em um nó se a verificação de capacidade falhar. Isso protege contra a falta de um recurso em um nó quando o consumo de recursos aumenta com o passar do tempo, como por exemplo durante o pico diário de requisições a um serviço.
Como o Kubernetes aplica requisições e limites de recursos
Quando o kubelet inicia um contêiner como parte de um Pod, o kubelet envia as requisições e limites de memória e de CPU ao agente de execução de contêiner.
No Linux, o agente de execução de contêiner normalmente configura os cgroups que aplicam e garantem os limites que você definiu.
- O limite de CPU determina um teto de quanto tempo de CPU o contêiner pode utilizar. A cada intervalo de agendamento, o núcleo do sistema operacional do Linux verifica se este limite foi excedido; se este for o caso, o núcleo aguarda antes de permitir que aquele cgroup continue sua execução.
- O requerimento de CPU normalmente define um método de balanceamento. Se vários contêineres diferentes (cgroups) querem rodar em um sistema disputado, cargas de trabalho com requerimentos maiores de CPU têm mais tempo de CPU alocado para si do que cargas de trabalho com pequenos requerimentos.
- O requerimento de memória é usado principalmente durante o agendamento de um
Pod. Em um nó que utiliza cgroups v2, o agente de execução de contêiner pode
utilizar o requerimento de memória como uma dica para definir valores para
memory.min
ememory.low
. - O limite de memória define um limite de memória para aquele cgroup. Se o contêiner tenta alocar mais memória que aquele limite, o subsistema out-of-memory do núcleo do sistema operacional Linux é ativado e, normalmente, intervém encerrando um dos processos do contêiner que tentou alocar mais memória. Se o processo em questão for o PID 1 do contêiner, e o contêiner estiver marcado como reinicializável, então o Kubernetes irá reiniciar o contêiner.
- O limite de memória para um Pod ou contêiner é também aplicado a páginas em
volumes armazenados em memória, como um
emptyDir
. O kubelet considera sistemas de arquivostmpfs
em volumes do tipoemptyDir
como uso de memória em um contêiner, ao invés de armazenamento efêmero local.
Se um contêiner exceder seu requerimento de memória e o nó em que esse contêiner está rodando ficar com pouca memória no total, é provável que o Pod a que este contêiner pertence seja removido.
A um contêiner pode ou não ser permitido exceder seu limite de CPU por períodos de tempo estendidos. No entanto, agentes de execução de contêiner não encerram Pods por uso excessivo de CPU.
A fim de determinar se um contêiner não pode ser agendado ou está sendo encerrado devido a limites de recursos, consulte a seção de solução de problemas.
Monitorando utilização de recursos computacionais e de memória
O kubelet relata a utilização de recursos de um Pod como parte do
status
do Pod.
Se ferramentas opcionais para monitoramento de recursos estiverem disponíveis em seu cluster, a utilização de recursos de um Pod pode ser verificada diretamente através de API de métricas ou através das suas ferramentas de monitoramento
Armazenamento efêmero local
Kubernetes v1.10 [beta]
Nós possuem armazenamento efêmero local, através de dispositivos de escrita conectados localmente ou através de RAM. "Efêmero" significa que não há garantia de longo termo com relação a durabilidade.
Pods utilizam armazenamento local efêmero para dados temporários, cache e logs.
O kubelet pode fornecer armazenamento temporário a Pods que utilizam
armazenamento local efêmero para montar volumes
do tipo emptyDir
em contêineres.
O kubelet também utiliza este tipo de armazenamento para logs de contêineres a nível de nó, imagens de contêiner e camadas graváveis de contêineres em execução.
Cuidado:
Se um nó falhar, os dados em seu armazenamento efêmero podem ser perdidos. Suas aplicações não devem ter expectativas de cumprimento de SLAs de desempenho (como quantidade de operações de entrada e saída de disco por segundo (IOPS), por exemplo) pelo armazenamento local efêmero.Com esta funcionalidade em fase beta, o Kubernetes permite que você rastreie, reserve e limite quanto armazenamento local efêmero um Pod pode consumir.
Configurações para armazenamento local efêmero
O Kubernetes suporta duas formas de configuração para o armazenamento local efêmero em um nó:
Nesta configuração, você armazena todos os tipos diferentes de dados locais
efêmeros (volumes do tipo emptyDir
, camadas graváveis, imagens de contêiner,
logs) em um sistema de arquivos único. A forma mais efetiva de configurar o
kubelet é dedicar este sistema de arquivos aos dados do Kubernetes (kubelet).
O kubelet também escreve logs de contêiner a nível de nó e trata estes logs de maneira semelhante ao armazenamento efêmero local.
O kubelet escreve logs em arquivos dentro do seu diretório de log configurado
(/var/log
por padrão) e possui um diretório base para outros dados armazenados
localmente (/var/lib/kubelet
por padrão).
Normalmente, ambos os diretórios /var/lib/kubelet
e /var/log
encontram-se no
sistema de arquivos raiz, e o kubelet é projetado com este desenho em mente.
Seu nó pode ter tantos outros sistemas de arquivos não utilizados pelo Kubernetes quantos você desejar.
Você tem um sistema de arquivos no nó que você utiliza para dados efêmeros que
vêm de Pods em execução: logs e volumes do tipo emptyDir
. Você pode utilizar
este sistema de arquivos para outros dados (por exemplo, logs de sistema não
relacionados ao Kubernetes); este sistema de arquivos pode até mesmo ser o
sistema de arquivos raiz.
O kubelet também escreve logs de contêiner a nível de nó no primeiro sistema de arquivos e os trata de forma semelhante ao armazenamento local efêmero.
Você também tem um segundo sistema de arquivos, separado, conectado a um dispositivo lógico de armazenamento distinto. Nesta configuração, o diretório que você configurou o kubelet para armazenar as camadas de imagens de contêiner e as camadas graváveis de contêineres em execução estará neste segundo sistema de arquivos.
O primeiro sistema de arquivos não armazena nenhuma camada de imagens de contêiner ou camada gravável.
Seu nó pode ter tantos outros sistemas de arquivos não utilizados pelo Kubernetes quantos você desejar.
O kubelet consegue medir quanto armazenamento local está sendo utilizado. O kubelet faz isso desde que:
- o feature gate
LocalStorageCapacityIsolation
esteja habilitado (a funcionalidade está ligada por padrão), e - você tenha configurado o nó utilizando uma das configurações suportadas para o armazenamento local efêmero.
Se você tiver uma configuração diferente, o kubelet não irá aplicar limites de recursos para o armazenamento local efêmero.
Nota:
O kubelet rastreia volumesemptyDir
que utilizem o sistema de arquivos tmpfs
como uso de memória de contêiner, ao invés de consumo de armazenamento local
efêmero.Configurando requerimentos e limites para armazenamento local efêmero
Você pode especificar o recurso ephemeral-storage
para gerenciar o
armazenamento local efêmero. Cada contêiner de um Pod pode especificar um dos
valores abaixo, ou ambos:
spec.containers[].resources.limits.ephemeral-storage
spec.containers[].resources.requests.ephemeral-storage
Limites e requerimentos de ephemeral-storage
são medidos em quantidades de
bytes. Você pode expressar armazenamento como um inteiro ou como um valor de
ponto fixo utilizando um dos seguintes sufixos: E, P, T, G, M, k. Você pode
também utilizar os equivalentes de potência de dois: Ei, Pi, Ti, Gi, Mi, Ki.
Por exemplo, as quantidades abaixo representam grosseiramente o mesmo valor:
128974848
129e6
129M
123Mi
No exemplo a seguir, o Pod tem dois contêineres. Cada contêiner tem um requerimento de 2GiB de armazenamento efêmero local. Cada contêiner tem um limite de 4GiB de armazenamento efêmero local. Portanto, o Pod tem um requerimento de 4GiB e um limite de 8GiB de armazenamento efêmero local.
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
volumeMounts:
- name: ephemeral
mountPath: "/tmp"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
volumeMounts:
- name: ephemeral
mountPath: "/tmp"
volumes:
- name: ephemeral
emptyDir: {}
Como Pods com requerimentos de ephemeral-storage
são agendados
Quando você cria um Pod, o Kubernetes seleciona um nó para o Pod rodar. Cada nó tem uma quantidade máxima de armazenamento efêmero local que pode ser fornecida aos Pods. Para mais informações, consulte Node Allocatable.
O escalonador garante que a soma dos requerimentos de recursos dos contêineres agendados é menor que a capacidade do nó.
Gerenciamento do consumo do armazenamento efêmero
Se o kubelet estiver gerenciando armazenamento local efêmero como um recurso, o kubelet irá medir o consumo de armazenamento em:
- volumes
emptyDir
, com exceção dos volumes do tipotmpfs
- diretórios que armazenem logs a nível de nó
- camadas de contêiner graváveis
Se um Pod estiver utilizando mais armazenamento efêmero do que o permitido, o kubelet irá gerar um sinal de remoção para aquele Pod.
Para isolamento a nível de contêiner, se o consumo de armazenamento de um contêiner em camadas graváveis e logs exceder seu limite de armazenamento, o kubelet irá marcar o Pod para remoção.
Para isolamento a nível de Pod, o kubelet calcula um limite de armazenamento
total para um Pod somando os limites de cada contêiner naquele Pod. Neste caso,
se a soma do consumo de armazenamento efêmero local de todas os contêineres e
também dos volumes emptyDir
de um Pod exceder o limite de armazenamento total
do Pod, então o kubelet marca o Pod para remoção.
Cuidado:
Se o kubelet não estiver medindo armazenamento efêmero local, um Pod que exeder seu limite de armazenamento local não será removido por exceder os limites de recurso de armazenamento local.
No entanto, se o espaço de um sistema de arquivos para camadas de contêiner
graváveis, logs a nível de nó, ou volumes emptyDir
ficar reduzido, o nó irá
marcar a si próprio com um taint
indicando que está com armazenamento local reduzido, e esse taint dispara a
remoção de Pods que não toleram o taint em questão.
Veja as configurações suportadas para armazenamento efêmero local.
O kubelet suporta formas diferentes de medir o uso de armazenamento dos Pods:
O kubelet executa verificações agendadas, em intervalos regulares, que varrem
cada volume do tipo emptyDir
, diretório de log de contêiner, e camada gravável
de contêiner.
A varredura mede quanto espaço está sendo utilizado.
Nota:
Neste modo, o kubelet não rastreia descritores de arquivos abertos para arquivos removidos.
Se você (ou um contêiner) criar um arquivo dentro de um volume emptyDir
, um
processo ou usuário abrir tal arquivo, e você apagar o arquivo enquanto ele
ainda estiver aberto, o nó de índice para o arquivo apagado será mantido até que
o arquivo seja fechado novamente. O kubelet, no entanto, não computa este espaço
como espaço em uso.
Quotas de projeto são uma funcionalidade a nível de sistema operacional para
gerenciamento de uso do armazenamento em sistemas de arquivos. Com o Kubernetes,
você pode habilitar quotas de projeto para o monitoramento de armazenamento em
uso. Tenha certeza que o sistema de arquivos do nó que esteja sendo utilizado em
volumes do tipo emptyDir
possui suporte a quotas de projeto. Por exemplo,
os sistemas de arquivos XFS e ext4fs oferecem suporte a quotas de projeto.
Nota:
Quotas de projeto permitem o monitoramento do uso de armazenamento, mas não garantem limites.O Kubernetes utiliza IDs de projeto iniciando em 1048576
. Os IDs em uso estão
registrados nos diretórios /etc/projects
e /etc/projid
. Se os IDs de projeto
nestes intervalos forem utilizados para outros propósitos no sistema, estes IDs
de projeto deverão estar registrados nos diretórios especificados acima para que
o Kubernetes não os tente utilizar.
Quotas fornecem melhor desempenho e mais precisão do que varredura de diretórios. Quando um diretório é atribuído a um projeto, todos os arquivos criados no diretório são também criados no projeto, e o núcleo do sistema pode simplesmente manter controle de quantos blocos estão em uso por arquivos daquele projeto. Se um arquivo é criado e apagado, mas possui um descritor de arquivo aberto, ele continua a consumir espaço. O rastreio de quotas registra este espaço de forma precisa, enquanto varreduras de diretório ignoram o uso de espaço de armazenamento por arquivos apagados.
Se você deseja utilizar quotas de projeto, você deve:
Habilitar o feature gate
LocalStorageCapacityIsolationFSQuotaMonitoring=true
utilizando o campofeatureGates
na configuração do kubelet ou a opção de linha de comando--feature-gates
.Garantir que o sistema de arquivos raiz (ou o sistema de arquivos opcional de tempo de execução) tem quotas de projeto habilitadas. Todos os sistemas de arquivos XFS suportam quotas de projeto. Em sistemas de arquivos ext4, você precisa habilitar a funcionalidade de rastreio de quotas de projeto enquanto o sistema de arquivos ainda não está montado.
# Para sistema de arquivos ext4, com o volume /dev/block-device não montado sudo tune2fs -O project -Q prjquota /dev/block-device
Garanta que o sistema de arquivos raiz (ou sistema de arquivos opcional de tempo de execução) esteja montado com quotas de projeto habilitadas. Em ambos os sistemas XFS e ext4fs, a opção de montagem é chamada
prjquota
.
Recursos estendidos
Recursos estendidos são nomes de recursos absolutos fora do domínio
kubernetes.io
. Estes recursos permitem a operadores de cluster anunciar e a
usuários consumir recursos que não são embutidos pelo Kubernetes.
Dois passos são necessários para a utilização de recursos estendidos. Primeiramente, o operador do cluster deve anunciar um recurso estendido. Em segundo lugar, os usuários devem solicitar o recurso estendido em Pods.
Gerenciando recursos estendidos
Recursos estendidos a nível de nó
Recursos estendidos a nível de nó são recursos ligados ao nó.
Recursos gerenciados por dispositivos conectados
Veja Device Plugin para mais informações sobre como anunciar recursos gerenciados por dispositivos conectados em cada nó.
Outros recursos
A fim de anunciar um novo recurso estendido a nível de nó, o operador do cluster
pode enviar uma requisição HTTP com o método PATCH
para o servidor da API do
Kubernetes para especificar a quantidade disponível em um nó no cluster, através
do campo status.capacity
. Após a realização desta operação, o campo
status.capacity
do nó irá conter um novo recurso. O campo status.allocatable
é atualizado automaticamente pelo kubelet, de forma assíncrona, com o novo
recurso.
Como o escalonador utiliza o valor do campo status.allocatable
do nó ao
verificar a saúde do Pod, o escalonador somente considerará o novo valor do
campo após esta atualização assíncrona. Pode haver um pequeno atraso entre a
atualização da capacidade do nó com um novo recurso e o momento em que o
primeiro Pod que requer o recurso poderá ser agendado naquele nó.
Exemplo:
Este exemplo demonstra como utilizar a ferramenta curl
para criar uma
requisição HTTP que anuncia cinco recursos "example.com/foo" no nó k8s-node-1
,
cujo nó da camada de gerenciamento é k8s-master
.
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status
Nota:
Na requisição anterior, a notação~1
é a codificação do caractere /
no campo
path
para a operação de atualização. O valor do campo path
em JSON-Patch é
interpretado como um JSON-Pointer. Para maiores detalhes, veja
a seção 3 da IETF RFC 6901.Recursos estendidos a nível de cluster
Recursos estendidos a nível de cluster não são vinculados aos nós. Estes recursos são normalmente gerenciados por extensões do escalonador, que manipulam o consumo e as quotas de recursos.
Você pode especificar os recursos estendidos que são manipulados por extensões do escalonador nas configurações do kube-scheduler.
Exemplo:
A configuração abaixo para uma política do escalonador indica que o recurso estendido a nível de cluster "example.com/foo" é manipulado pelas extensões do escalonador.
- O escalonador envia um Pod para a extensão do escalonador somente se o Pod solicitar "example.com/foo".
- O campo
ignoredByScheduler
especifica que o escalonador não verifica o recurso "example.com/foo" em seu predicadoPodFitsResources
.
{
"kind": "Policy",
"apiVersion": "v1",
"extenders": [
{
"urlPrefix":"<extender-endpoint>",
"bindVerb": "bind",
"managedResources": [
{
"name": "example.com/foo",
"ignoredByScheduler": true
}
]
}
]
}
Consumindo recursos estendidos
Usuários podem consumir recursos estendidos em especificações de Pods como CPU e memória. O escalonador controla a contagem de recursos de modo que a quantidade alocada simultaneamente a Pods não seja maior que a quantidade disponível.
O servidor da API limita as quantidades de recursos estendidos a números inteiros.
Exemplos de quantidades válidas são 3
, 3000m
e 3Ki
. Exemplos de
quantidades inválidas são 0.5
e 1500m
.
Nota:
Recursos estendidos substituem os Recursos Inteiros Opacos. Usuários podem escolher qualquer prefixo de nome de domínio, com exceção do domíniokubernetes.io
, que é reservado.Para consumir um recurso estendido em um Pod, inclua o nome do recurso como uma
chave no mapa spec.containers[].resources.limits
na especificação do contêiner.
Nota:
Recursos estendidos não podem ser superdimensionados. Portanto,request
e
limit
devem ser iguais se ambos estiverem presentes na especificação de um
contêiner.Um Pod só é agendado se todos os seus requerimentos de recursos forem
satisfeitos, incluindo CPU, memória e quaisquer recursos estendidos. O Pod
permanece no estado PENDING
enquanto seus requerimentos de recursos não puderem
ser satisfeitos.
Exemplo:
O Pod abaixo requisita duas CPUs e um "example.com/foo" (um recurso estendido).
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: myimage
resources:
requests:
cpu: 2
example.com/foo: 1
limits:
example.com/foo: 1
Limitação de PID
Limites de ID de processo (PID) permitem à configuração de um kubelet limitar o número de PIDs que um dado Pod pode consumir. Consulte PID Limiting para mais informações.
Solução de problemas
Meus pods estão pendentes com um evento FailedScheduling
Se o escalonador não conseguir encontrar nenhum nó que atenda aos requisitos de
recursos do Pod, este Pod permanecerá não-agendado até que um local destino
possa ser encontrado. Um Evento
é produzido cada vez que o escalonador falhar em encontrar um local para agendar
o Pod. Você pode utilizar o utilitário kubectl
para ver os eventos de um Pod.
Por exemplo:
kubectl describe pod frontend | grep -A 9999999999 Events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 23s default-scheduler 0/42 nodes available: insufficient cpu
No exemplo acima, o Pod de nome "frontend" não pôde ser agendado devido à nenhum
nó possuir CPU suficiente para suprir seu requerimento de CPU. Mensagens de erro
semelhantes a essa podem sugerir falha devido a falta de memória
(PodExceedsFreeMemory
). De maneira geral, se um Pod estiver pendente com uma
mensagem deste tipo, há diversas possibilidades de solução a serem tentadas:
- Adicione mais nós ao cluster.
- Encerre Pods desnecessários para liberar espaço para Pods pendentes.
- Verifique se o Pod não é maior que todos os nós. Por exemplo, se todos os nós
têm uma capacidade de
cpu: 1
, um Pod que requisitacpu: 1.1
nunca será agendado. - Verifique se os nós não possuem taints. Se a maioria dos seus nós possuem taints, e o novo Pod não tolera tal taint, o escalonador somente considera agendar o Pod nos nós que não possuem aquele taint.
Você pode verificar capacidades de nós e quantidades alocadas com o comando
kubectl describe nodes
. Por exemplo:
kubectl describe nodes e2e-test-node-pool-4lw4
Name: e2e-test-node-pool-4lw4
[ ... linhas abreviadas para simplificação ...]
Capacity:
cpu: 2
memory: 7679792Ki
pods: 110
Allocatable:
cpu: 1800m
memory: 7474992Ki
pods: 110
[ ... linhas abreviadas para simplificação ...]
Non-terminated Pods: (5 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
kube-system fluentd-gcp-v1.38-28bv1 100m (5%) 0 (0%) 200Mi (2%) 200Mi (2%)
kube-system kube-dns-3297075139-61lj3 260m (13%) 0 (0%) 100Mi (1%) 170Mi (2%)
kube-system kube-proxy-e2e-test-... 100m (5%) 0 (0%) 0 (0%) 0 (0%)
kube-system monitoring-influxdb-grafana-v4-z1m12 200m (10%) 200m (10%) 600Mi (8%) 600Mi (8%)
kube-system node-problem-detector-v0.1-fj7m3 20m (1%) 200m (10%) 20Mi (0%) 100Mi (1%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
680m (34%) 400m (20%) 920Mi (11%) 1070Mi (13%)
No exemplo anterior, você pode verificar que se um Pod requisitar mais que 1,120 CPUs ou mais que 6,23Gi de memória, tal Pod não caberá neste nó.
Ao verificar a seção "Pods", você pode observar quais Pods estão consumindo espaço neste nó.
A quantidade de recursos disponível aos Pods é menor que a capacidade do nó, pois
daemons do sistema utilizam uma parcela dos recursos disponíveis. Dentro da API
do Kubernetes, cada nó tem um campo .status.allocatable
(consulte NodeStatus
para mais detalhes).
O campo .status.allocatable
descreve a quantidade de recursos que está
disponível a Pods naquele nó (por exemplo: 15 CPUs virtuais e 7538 MiB de
memória). Para mais informações sobre recursos alocáveis do nó no Kubernetes,
veja Reserve Compute Resources for System Daemons.
Você pode configurar quotas de recursos
para limitar a quantidade total de recursos que um namespace pode consumir.
O Kubernetes garante quotas para objetos em um namespace específico quando há
uma ResourceQuota
naquele namespace. Por exemplo, se você atribuir namespaces
específicos a times diferentes, você pode adicionar ResourceQuota
s nestes
namespaces. Criar quotas de recursos ajuda a evitar que um time utilize tanto de
um recurso que chegue a afetar outros times utilizando o mesmo cluster.
Você deve também considerar o nível de acesso fornecido aos usuários de qualquer
namespace: acesso completo para escrita permite a alguém com este acesso
remover qualquer recurso, incluindo uma configuração de ResourceQuota
.
Meu contêiner foi terminado
Seu contêiner pode ser terminado se faltar recursos para que este rode. Para
verificar se um contêiner está sendo terminado por chegar no limite de algum
recurso, utilize o comando kubectl describe pod
no Pod em questão:
kubectl describe pod simmemleak-hra99
A saída será semelhante a:
Name: simmemleak-hra99
Namespace: default
Image(s): saadali/simmemleak
Node: kubernetes-node-tf0f/10.240.216.66
Labels: name=simmemleak
Status: Running
Reason:
Message:
IP: 10.244.2.75
Containers:
simmemleak:
Image: saadali/simmemleak:latest
Limits:
cpu: 100m
memory: 50Mi
State: Running
Started: Tue, 07 Jul 2019 12:54:41 -0700
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Started: Fri, 07 Jul 2019 12:54:30 -0700
Finished: Fri, 07 Jul 2019 12:54:33 -0700
Ready: False
Restart Count: 5
Conditions:
Type Status
Ready False
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 42s default-scheduler Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
Normal Pulled 41s kubelet Container image "saadali/simmemleak:latest" already present on machine
Normal Created 41s kubelet Created container simmemleak
Normal Started 40s kubelet Started container simmemleak
Normal Killing 32s kubelet Killing container with id ead3fb35-5cf5-44ed-9ae1-488115be66c6: Need to kill Pod
No exemplo acima, o campo Restart Count: 5
indica que o contêiner simmemleak
deste Pod foi terminado e reiniciado cinco vezes até o momento. A razão
OOMKilled
demonstra que o contêiner tentou consumir mais memória do que o seu
limite.
O próximo passo neste cenário seria vasculhar e depurar o código da aplicação, procurando por vazamentos de memória. Se você determinar que a aplicação está se comportando conforme o esperado, considere aumentar o limite (e possivelmente o requerimento) de memória para aquele contêiner.
Próximos passos
- Pratique a criação de requerimentos de recursos de memória em contêineres e Pods.
- Pratique a criação de requerimentos de CPU em contêineres and Pods.
- Leia como a referência da API define um contêiner e seus requerimentos de recursos.
- Leia sobre quotas de projeto no XFS.
- Leia mais sobre a referência de configuração do kube-scheduler (v1beta3).
5 - Organizando o acesso ao cluster usando arquivos kubeconfig
Utilize arquivos kubeconfig para organizar informações sobre clusters, usuários, namespaces e mecanismos de autenticação. A ferramenta de linha de comando kubectl
faz uso dos arquivos kubeconfig para encontrar as informações necessárias para escolher e se comunicar com o serviço de API de um cluster.
Nota:
Um arquivo que é utilizado para configurar o acesso aos clusters é chamado de kubeconfig. Esta á uma forma genérica de referenciamento para um arquivo de configuração desta natureza. Isso não significa que existe um arquivo com o nomekubeconfig
.Por padrão, o kubectl
procura por um arquivo de nome config
no diretório $HOME/.kube
Você pode especificar outros arquivos kubeconfig através da variável de ambiente KUBECONFIG
ou adicionando a opção --kubeconfig
.
Para maiores detalhes na criação e especificação de um kubeconfig, veja o passo a passo em Configurar Acesso para Múltiplos Clusters.
Suportando múltiplos clusters, usuários e mecanismos de autenticação
Imagine que você possua inúmeros clusters, e seus usuários e componentes se autenticam de várias formas. Por exemplo:
- Um kubelet ativo pode se autenticar utilizando certificados
- Um usuário pode se autenticar através de tokens
- Administradores podem possuir conjuntos de certificados os quais provém acesso aos usuários de forma individual.
Através de arquivos kubeconfig, você pode organizar os seus clusters, usuários, e namespaces. Você também pode definir contextos para uma fácil troca entre clusters e namespaces.
Contexto
Um elemento de contexto em um kubeconfig é utilizado para agrupar parâmetros de acesso em um nome conveniente. Cada contexto possui três parâmetros: cluster, namespace, e usuário.
Por padrão, a ferramenta de linha de comando kubectl
utiliza os parâmetros do contexto atual para se comunicar com o cluster.
Para escolher o contexto atual:
kubectl config use-context
A variável de ambiente KUBECONFIG
A variável de ambiente KUBECONFIG
possui uma lista dos arquivos kubeconfig. Para Linux e Mac, esta lista é delimitada por vírgula. No Windows, a lista é delimitada por ponto e vírgula. A variável de ambiente KUBECONFIG
não é um requisito obrigatório - caso ela não exista o kubectl
utilizará o arquivo kubeconfig padrão localizado no caminho $HOME/.kube/config
.
Se a variável de ambiente KUBECONFIG
existir, o kubectl
utilizará uma configuração que é o resultado da combinação dos arquivos listados na variável de ambiente KUBECONFIG
.
Combinando arquivos kubeconfig
Para inspecionar a sua configuração atual, execute o seguinte comando:
kubectl config view
Como descrito anteriormente, a saída poderá ser resultado de um único arquivo kubeconfig, ou poderá ser o resultado da junção de vários arquivos kubeconfig.
Aqui estão as regras que o kubectl
utiliza quando realiza a combinação de arquivos kubeconfig:
Se o argumento
--kubeconfig
está definido, apenas o arquivo especificado será utilizado. Apenas uma instância desta flag é permitida.Caso contrário, se a variável de ambiente
KUBECONFIG
estiver definida, esta deverá ser utilizada como uma lista de arquivos a serem combinados, seguindo o fluxo a seguir:- Ignorar arquivos vazios.
- Produzir erros para aquivos cujo conteúdo não for possível desserializar.
- O primeiro arquivo que definir um valor ou mapear uma chave determinada, será o escolhido.
- Nunca modificar um valor ou mapear uma chave.
Exemplo: Preservar o contexto do primeiro arquivo que definir
current-context
. Exemplo: Se dois arquivos especificarem umred-user
, use apenas os valores do primeirored-user
. Mesmo se um segundo arquivo possuir entradas não conflitantes sobre a mesma entradared-user
, estas deverão ser descartadas.
Para um exemplo de definição da variável de ambiente
KUBECONFIG
veja Definido a variável de ambiente KUBECONFIG.Caso contrário, utilize o arquivo kubeconfig padrão encontrado no diretório
$HOME/.kube/config
, sem qualquer tipo de combinação.Determine o contexto a ser utilizado baseado no primeiro padrão encontrado, nesta ordem:
- Usar o conteúdo da flag
--context
caso ela existir. - Usar o
current-context
a partir da combinação dos arquivos kubeconfig.
Um contexto vazio é permitido neste momento.
- Usar o conteúdo da flag
Determinar o cluster e o usuário. Neste ponto, poderá ou não existir um contexto. Determinar o cluster e o usuário no primeiro padrão encontrado de acordo com a ordem à seguir. Este procedimento deverá executado duas vezes: uma para definir o usuário a outra para definir o cluster.
- Utilizar a flag caso ela existir:
--user
ou--cluster
. - Se o contexto não estiver vazio, utilizar o cluster ou usuário deste contexto.
O usuário e o cluster poderão estar vazios neste ponto.
- Utilizar a flag caso ela existir:
Determinar as informações do cluster atual a serem utilizadas. Neste ponto, poderá ou não existir informações de um cluster.
Construir cada peça de informação do cluster baseado nas opções à seguir; a primeira ocorrência encontrada será a opção vencedora:
- Usar as flags de linha de comando caso existirem:
--server
,--certificate-authority
,--insecure-skip-tls-verify
. - Se algum atributo do cluster existir a partir da combinação de kubeconfigs, estes deverão ser utilizados.
- Se não existir informação de localização do servidor falhar.
- Usar as flags de linha de comando caso existirem:
Determinar a informação atual de usuário a ser utilizada. Construir a informação de usuário utilizando as mesmas regras utilizadas para o caso de informações de cluster, exceto para a regra de técnica de autenticação que deverá ser única por usuário:
- Usar as flags, caso existirem:
--client-certificate
,--client-key
,--username
,--password
,--token
. - Usar os campos
user
resultado da combinação de arquivos kubeconfig. - Se existirem duas técnicas conflitantes, falhar.
- Usar as flags, caso existirem:
Para qualquer informação que ainda estiver ausente, utilizar os valores padrão e potencialmente solicitar informações de autenticação a partir do prompt de comando.
Referências de arquivos
Arquivos e caminhos referenciados em um arquivo kubeconfig são relativos à localização do arquivo kubeconfig.
Referências de arquivos na linha de comando são relativas ao diretório de trabalho vigente.
No arquivo $HOME/.kube/config
, caminhos relativos são armazenados de forma relativa, e caminhos absolutos são armazenados de forma absoluta.
Próximos passos
6 - Gerenciamento de recursos para nós Windows
Esta página descreve as diferenças em como os recursos são gerenciados entre o Linux e o Windows.
Em nós Linux, cgroups são usados como uma divisão para o controle de recursos em Pods. Os contêineres são criados dentro desse limite para o isolamento de rede, processo e sistema de arquivos. As APIs de cgroup do Linux podem ser usadas para coletar estatísticas de uso de CPU, E/S e memória.
Em contraste, o Windows usa um objeto de trabalho por contêiner com um filtro de namespace do sistema para conter todos os processos em um contêiner e fornecer isolamento lógico ao hospedar. (Os objetos de trabalho são um mecanismo de isolamento de processo do Windows e são diferentes dos que o Kubernetes chama de Job).
Não há como executar um contêiner do Windows sem a filtragem de namespace. Isso significa que os privilégios do sistema não podem ser assegurados no contexto do host e, portanto, os contêineres privilegiados não estão disponíveis no Windows. Os contêineres não podem assumir uma identidade do host porque o Gerente de Conta de Segurança (Security Account Manager, ou SAM) é separado.
Gerenciamento de memória
O Windows não possui um eliminador de processo por falta de memória como o Linux. O Windows sempre trata todas as alocações de memória do modo de usuário como virtuais e os arquivos de paginação são obrigatórios.
Os nós Windows não superdimensionam a memória para os processos. O efeito real é que o Windows não atingirá as condições de falta de memória da mesma forma que o Linux, e estará processando a página em disco em vez de estar sujeito ao encerramento por falta de memória (OOM). Se a memória for superprovisionada e toda a memória física estiver esgotada, a paginação poderá diminuir o desempenho.
Gerenciamento de CPU
O Windows pode limitar a quantidade de tempo de CPU alocado para diferentes processos, mas não pode garantir uma quantidade mínima de tempo de CPU.
No Windows, o kubelet oferece suporte a uma flag de linha de comando para definir a
prioridade do escalonador do processo kubelet:
--windows-priorityclass
. Essa flag permite que o processo kubelet obtenha
mais fatias de tempo de CPU quando comparado a outros processos em execução no host do Windows.
Mais informações sobre os valores permitidos e os seus significados estão disponíveis em
classes de prioridade do Windows.
Para garantir que os Pods em execução não deixem o kubelet sem ciclos de CPU, defina essa flag como ABOVE_NORMAL_PRIORITY_CLASS
ou acima.
Reserva de recursos
Para contabilizar a memória e a CPU usadas pelo sistema operacional, o agente de execução de contêiner
e os processos de host do Kubernetes, como o kubelet, você pode (e deve)
reservar recursos de memória e CPU com as flags --kube-reserved
e/ou --system-reserved
do kubelet.
No Windows, esses valores são usados apenas para calcular o recursos
alocáveis pelo nó.
Cuidado:
Conforme você implanta cargas de trabalho, defina a memória de recursos e os limites de CPU nos contêineres.
Isso também subtrai de NodeAllocatable
e ajuda o escalonador de todo o cluster a determinar quais pods colocar em quais nós.
Alocar pods sem limites pode superprovisionar os nós do Windows e, em casos extremos, fazer com que os nós não sejam íntegros.
No Windows, uma boa prática é reservar pelo menos 2GiB de memória.
Para determinar quanta CPU reservar, identifique a densidade máxima do pod para cada nó e monitore o uso da CPU dos serviços do sistema em execução, depois escolha um valor que atenda às necessidades das suas cargas de trabalho.