Cotas de Recursos
Quando vários usuários ou equipes compartilham um cluster com um número fixo de nós, há uma preocupação de que uma equipe possa usar mais do que é justo durante o compartilhamento de recursos.
As cotas de recursos são uma ferramenta para os administradores resolverem essa preocupação.
Uma cota de recurso, definida por um objeto ResourceQuota
, fornece restrições que limitam
consumo de recursos agregados por namespace. Pode limitar a quantidade de objetos que podem
ser criado em um namespace por tipo, bem como a quantidade total de recursos computacionais que podem
ser consumidos por recursos nesse namespace.
As cotas de recursos funcionam assim:
Diferentes equipes trabalham em diferentes namespaces. Atualmente, isso é voluntário, mas o suporte para tornar isso obrigatório por meio de ACLs está planejado.
O administrador cria uma
ResourceQuota
para cada namespace.Os usuários criam recursos (pods, serviços, etc.) no namespace e o sistema de cotas rastreia o uso para garantir que ele não exceda os limites de recursos definidos em um
ResourceQuota
.Se a criação ou atualização de um recurso violar uma restrição de cota, a solicitação falhará com código de status HTTP
403 FORBIDDEN
acompanhado de uma mensagem explicando a restrição que foi violada.Se a cota estiver habilitada em um namespace para recursos computacionais como
cpu
ememória
, os usuários devem especificar solicitações ou limites para esses valores; caso contrário, o sistema de cotas poderá rejeitar a criação de pods. Dica: use o controlador de admissãoLimitRanger
para forçar padrões para pods que não exigem recursos computacionais.Veja o passo a passo para um exemplo de como evitar este problema.
O nome de um objeto ResourceQuota
deve ser um nome do subdomínio DNS válido.
Exemplos de políticas que podem ser criadas usando namespaces e cotas são:
- Em um cluster com capacidade de 32 GiB de RAM e 16 núcleos, deixe a equipe A usar 20 GiB e 10 núcleos, deixe B usar 10GiB e 4 núcleos e mantenha 2GiB e 2 núcleos em reserva para alocação futura.
- Limite o namespace "testing" para usar 1 núcleo e 1GiB de RAM. Deixe o namespace "produção" usar qualquer quantia.
Caso a capacidade total do cluster seja menor que a soma das cotas dos namespaces, pode haver contenção de recursos. Isso é tratado por ordem de chegada.
Nem a contenção nem as alterações na cota afetarão os recursos já criados.
Ativando a cota de recursos
O suporte à cota de recursos é ativado por padrão para muitas distribuições do Kubernetes. Isto é
ativado quando a flag API server --enable-admission-plugins=
tem ResourceQuota
como
um de seus argumentos.
Uma cota de recurso é aplicada em um namespace específico quando há um ResourceQuota
nesse namespace.
Cota de recursos computacionais
Você pode limitar a soma total de recursos computacionais que pode ser solicitado em um determinado namespace.
Os seguintes tipos de recursos são suportados:
Nome do Recurso | Descrição |
---|---|
limits.cpu | Em todos os pods em um estado não terminal, a soma dos limites de CPU não pode exceder esse valor. |
limits.memory | Em todos os pods em um estado não terminal, a soma dos limites de memória não pode exceder esse valor. |
requests.cpu | Em todos os pods em um estado não terminal, a soma das solicitações da CPU não pode exceder esse valor. |
requests.memory | Em todos os pods em um estado não terminal, a soma das solicitações de memória não pode exceder esse valor. |
hugepages-<size> | Em todos os pods em um estado não terminal, o número de solicitações de grandes páginas do tamanho especificado não pode exceder esse valor. |
cpu | O mesmo que requests.cpu |
memory | O mesmo que requests.memory |
Cota de recursos para recursos estendidos
Além dos recursos mencionados acima, na versão 1.10, suporte a cotas para recursos estendidos foi adicionado.
Como o overcommit
não é permitido para recursos estendidos, não faz sentido especificar tanto requests
e limits
para o mesmo recurso estendido em uma cota. Portanto, para recursos estendidos, apenas itens de cota com prefixo requests.
é permitido por enquanto.
Tome o recurso GPU como exemplo, se o nome do recurso for nvidia.com/gpu
e você quiser limitar o número total de GPUs solicitadas em um namespace para 4, você pode definir uma cota da seguinte maneira:
requests.nvidia.com/gpu: 4
Veja como visualizar e definir cotas para mais informações.
Cota de recursos de armazenamento
Você pode limitar a soma total de recursos de armazenamento que podem ser solicitados em um determinado namespace.
Além disso, você pode limitar o consumo de recursos de armazenamento com base na classe de armazenamento associada.
Nome do recurso | Descrição |
---|---|
requests.storage | Em todas as solicitações de volume persistentes, a soma das solicitações de armazenamento não pode exceder esse valor. |
persistentvolumeclaims | O número total de PersistentVolumeClaims que podem existir no namespace. |
<storage-class-name>.storageclass.storage.k8s.io/requests.storage | Em todas as solicitações de volume persistentes associadas ao <storage-class-name> , a soma das solicitações de armazenamento não pode exceder esse valor. |
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims | Em todas as declarações de volume persistentes associadas ao storage-class-name, o número total de declarações de volume persistente que podem existir no namespace. |
Por exemplo, se um operador deseja cotar armazenamento com classe de armazenamento gold
separada da classe de armazenamento bronze
, o operador pode definir uma cota da seguinte forma:
gold.storageclass.storage.k8s.io/requests.storage: 500Gi
bronze.storageclass.storage.k8s.io/requests.storage: 100Gi
Na versão 1.8, o suporte de cota para armazenamento temporário local foi adicionado como um recurso alfa:
Nome do Recurso | Descrição |
---|---|
requests.ephemeral-storage | Em todos os pods no namespace, a soma das solicitações de armazenamento local efêmero não pode exceder esse valor. |
limits.ephemeral-storage | Em todos os pods no namespace, a soma dos limites de armazenamento temporário local não pode exceder esse valor. |
ephemeral-storage | O mesmo que requests.ephemeral-storage . |
Nota:
Ao usar um tempo de execução do contêiner CRI, os logs do contêiner serão contabilizados na cota de armazenamento efêmero. Isso pode resultar no despejo inesperado de pods que esgotaram suas cotas de armazenamento. Consulte Arquitetura de registro para mais detalhes.Cota de contagem de objetos
Você pode definir cotas para o número total de determinados recursos de todos os padrões, tipos de recursos com namespace usando a seguinte sintaxe:
count/<resource>.<group>
para recursos de grupos não principaiscount/<resource>
para recursos do grupo principal
Exemplo de conjunto de recursos que os usuários podem querer colocar na cota de contagem de objetos:
count/persistentvolumeclaims
count/services
count/secrets
count/configmaps
count/replicationcontrollers
count/deployments.apps
count/replicasets.apps
count/statefulsets.apps
count/jobs.batch
count/cronjobs.batch
A mesma sintaxe pode ser usada para recursos personalizados. Por exemplo, para criar uma cota em um recurso personalizado widgets
no grupo de API example.com
, use count/widgets.example.com
.
Ao usar a cota de recurso count/*
, um objeto é cobrado na cota se existir no armazenamento do servidor. Esses tipos de cotas são úteis para proteger contra o esgotamento dos recursos de armazenamento. Por exemplo, você pode desejar limitar o número de segredos em um servidor devido ao seu grande tamanho. Muitos segredos em um cluster podem
na verdade, impedir que servidores e controladores sejam iniciados. Você pode definir uma cota para projetos para proteger contra um CronJob
mal configurado. CronJobs
que criam muitos Jobs
em um namespace podem levar a uma negação de serviço.
Também é possível fazer uma cota de contagem de objetos genéricos em um conjunto limitado de recursos. Os seguintes tipos são suportados:
Nome do Recurso | Descrição |
---|---|
configmaps | O número total de ConfigMaps que podem existir no namespace. |
persistentvolumeclaims | O número total de PersistentVolumeClaims que podem existir no namespace. |
pods | O número total de pods em um estado não terminal que pode existir no namespace. Um pod está em um estado terminal se .status.phase in (Failed, Succeeded) for verdadeiro. |
replicationcontrollers | O número total de ReplicationControllers que podem existir no namespace. |
resourcequotas | O número total de ResourceQuotas que podem existir no namespace. |
services | O número total de Serviços que podem existir no namespace. |
services.loadbalancers | O número total de serviços do tipo LoadBalancer que podem existir no namespace. |
services.nodeports | O número total de serviços do tipo NodePort que podem existir no namespace. |
secrets | O número total de segredos que podem existir no namespace. |
Por exemplo, a cota de pods
conta e impõe um número máximo de pods
criados em um único namespace que não é terminal. Você pode querer definir uma cota pods
em um namespace para evitar o caso em que um usuário cria muitos pods
pequenos e esgota o fornecimento de IPs de pod do cluster.
Escopos de cota
Cada cota pode ter um conjunto associado de scopes
. Uma cota só medirá o uso de um recurso se corresponder
a interseção de escopos enumerados.
Quando um escopo é adicionado à cota, ele limita o número de recursos aos quais ele dá suporte a aqueles que pertencem ao escopo. Os recursos especificados na cota fora do conjunto permitido resultam em um erro de validação.
Escopo | Descrição |
---|---|
Terminating | Pods correspondentes onde .spec.activeDeadlineSeconds >= 0 |
NotTerminating | Pods correspondentes onde .spec.activeDeadlineSeconds is nil |
BestEffort | Pods correspondentes que tenham a qualidade de serviço de melhor esforço. |
NotBestEffort | Pods correspondentes que não têm qualidade de serviço de melhor esforço. |
PriorityClass | Corresponde aos pods que fazem referência à classe de prioridade especificada. |
CrossNamespacePodAffinity | Corresponde a pods que tenham termos de (anti)afinidade de namespace cruzado. |
O escopo BestEffort
restringe uma cota ao rastreamento do seguinte recurso:
pods
Os escopos Termination
, NotTerminate
, NotBestEffort
e PriorityClass
restringem uma cota para rastrear os seguintes recursos:
pods
cpu
memory
requests.cpu
requests.memory
limits.cpu
limits.memory
Observe que você não pode especificar os escopos Terminate
e o NotTerminate
na mesma cota, e você também não pode especificar o BestEffort
eNotBestEffort
na mesma cota.
O scopeSelector
suporta os seguintes valores no campo operator
:
In
NotIn
Exists
DoesNotExist
Ao usar um dos seguintes valores como o scopeName
ao definir oscopeSelector
, o operator
deve ser Exists
.
Terminating
NotTerminating
BestEffort
NotBestEffort
Se o operator
for In
ou NotIn
, o campo values
deve ter pelo menos um valor. Por exemplo:
scopeSelector:
matchExpressions:
- scopeName: PriorityClass
operator: In
values:
- middle
Se o operator
for Exists
ou DoesNotExist
, o campo values
NÃO deve ser especificado.
Cota de recursos por classe de prioridade
Kubernetes v1.17 [stable]
Os pods podem ser criados em uma prioridade específica. Você pode controlar o consumo de recursos do sistema de um pod com base na prioridade de um pod, usando o scopeSelector
campo na especificação de cota.
Uma cota é correspondida e consumida apenas se scopeSelector
na especificação de cota selecionar o pod.
Quando a cota está no escopo da classe de prioridade usando o campo scopeSelector
, objeto de cota
está restrito a rastrear apenas os seguintes recursos:
pods
cpu
memory
ephemeral-storage
limits.cpu
limits.memory
limits.ephemeral-storage
requests.cpu
requests.memory
requests.ephemeral-storage
Este exemplo cria um objeto de cota e o corresponde a pods em prioridades específicas. O exemplo funciona da seguinte forma:
- Os pods no cluster têm uma das três classes de prioridade, "baixa", "média", "alta".
- Um objeto de cota é criado para cada prioridade.
Salve o seguinte YAML em um arquivo quota.yml
.
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-high
spec:
hard:
cpu: "1000"
memory: 200Gi
pods: "10"
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["high"]
- apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-medium
spec:
hard:
cpu: "10"
memory: 20Gi
pods: "10"
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["medium"]
- apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-low
spec:
hard:
cpu: "5"
memory: 10Gi
pods: "10"
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["low"]
Aplique o YAML usando kubectl create
.
kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created
Verifique se a cota Used
é 0
usando kubectl describe quota
.
kubectl describe quota
Name: pods-high
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 1k
memory 0 200Gi
pods 0 10
Name: pods-low
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 5
memory 0 10Gi
pods 0 10
Name: pods-medium
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 10
memory 0 20Gi
pods 0 10
Crie um pod com prioridade "high". Salve o seguinte YAML em um arquivo high-priority-pod.yml
.
apiVersion: v1
kind: Pod
metadata:
name: high-priority
spec:
containers:
- name: high-priority
image: ubuntu
command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]
resources:
requests:
memory: "10Gi"
cpu: "500m"
limits:
memory: "10Gi"
cpu: "500m"
priorityClassName: high
Applique com kubectl create
.
kubectl create -f ./high-priority-pod.yml
Verifique se as estatísticas "Used" para a cota de prioridade "high", pods-high
foram alteradas e se
as outras duas cotas permanecem inalteradas.
kubectl describe quota
Name: pods-high
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 500m 1k
memory 10Gi 200Gi
pods 1 10
Name: pods-low
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 5
memory 0 10Gi
pods 0 10
Name: pods-medium
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 10
memory 0 20Gi
pods 0 10
Cota de afinidade de pod entre namespaces
Kubernetes v1.22 [beta]
Os operadores podem usar o escopo de cota CrossNamespacePodAffinity
para limitar quais namespaces têm permissão para ter pods com termos de afinidade que cruzam namespaces. Especificamente, ele controla quais pods são permitidos para definir os campos namespaces
ou namespaceSelector
em termos de afinidade de pod.
Impedir que os usuários usem termos de afinidade entre namespaces pode ser desejável, pois um pod com restrições antiafinidade pode bloquear pods de todos os outros namespaces de ser agendado em um domínio de falha.
O uso desses operadores de escopo pode impedir certos namespaces (foo-ns
no exemplo abaixo) de ter pods que usam afinidade de pod entre namespaces criando um objeto de cota de recurso nesse namespace com escopo CrossNamespaceAffinity
e limite rígido de 0:
apiVersion: v1
kind: ResourceQuota
metadata:
name: disable-cross-namespace-affinity
namespace: foo-ns
spec:
hard:
pods: "0"
scopeSelector:
matchExpressions:
- scopeName: CrossNamespaceAffinity
Se os operadores quiserem proibir o uso de namespaces
e namespaceSelector
por padrão, e
permitir apenas para namespaces específicos, eles podem configurar CrossNamespaceAffinity
como um recurso limitado definindo o sinalizador kube-apiserver --admission-control-config-file
para o caminho do seguinte arquivo de configuração:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ResourceQuotaConfiguration
limitedResources:
- resource: pods
matchScopes:
- scopeName: CrossNamespaceAffinity
Com a configuração acima, os pods podem usar namespaces
e namespaceSelector
apenas na afinidade do pod se o namespace em que foram criados tiver um objeto de cota de recurso com escopo CrossNamespaceAffinity
e um limite rígido maior ou igual ao número de pods usando esses campos.
Esse recurso é beta e ativado por padrão. Você pode desativá-lo usando o feature gate PodAffinityNamespaceSelector
no kube-apiserver e no kube-scheduler.
Solicitações comparadas aos limites
Ao alocar recursos computacionais, cada contêiner pode especificar uma solicitação e um valor limite para CPU ou memória. A cota pode ser configurada para cotar qualquer valor.
Se a cota tiver um valor especificado para requests.cpu
ou requests.memory
, ela exigirá que cada container faça uma solicitação explícita para esses recursos. Se a cota tiver um valor especificado para limits.cpu
ou limits.memory
, em seguida exige que cada contêiner de entrada especifique um limite explícito para esses recursos.
Como visualizar e definir cotas
O Kubectl é compatível com a criação, atualização e visualização de cotas:
kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
configmaps: "10"
persistentvolumeclaims: "4"
pods: "4"
replicationcontrollers: "20"
secrets: "10"
services: "10"
services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME AGE
compute-resources 30s
object-counts 32s
kubectl describe quota compute-resources --namespace=myspace
Name: compute-resources
Namespace: myspace
Resource Used Hard
-------- ---- ----
limits.cpu 0 2
limits.memory 0 2Gi
requests.cpu 0 1
requests.memory 0 1Gi
requests.nvidia.com/gpu 0 4
kubectl describe quota object-counts --namespace=myspace
Name: object-counts
Namespace: myspace
Resource Used Hard
-------- ---- ----
configmaps 0 10
persistentvolumeclaims 0 4
pods 0 4
replicationcontrollers 0 20
secrets 1 10
services 0 10
services.loadbalancers 0 2
Kubectl also supports object count quota for all standard namespaced resources
using the syntax count/<resource>.<group>
:
kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name: test
Namespace: myspace
Resource Used Hard
-------- ---- ----
count/deployments.apps 1 2
count/pods 2 3
count/replicasets.apps 1 4
count/secrets 1 4
Capacidade e cota de Cluster
ResourceQuotas
são independentes da capacidade do cluster. Eles estão expresso em unidades absolutas. Portanto, se você adicionar nós ao cluster, isso não
dá automaticamente a cada namespace a capacidade de consumir mais recursos.
Às vezes, políticas mais complexas podem ser necessárias, como:
- Divida proporcionalmente os recursos totais do cluster entre várias equipes.
- Permita que cada locatário aumente o uso de recursos conforme necessário, mas tenha um generoso limite para evitar o esgotamento acidental de recursos.
- Detecte a demanda de um namespace, adicione nós e aumente a cota.
Tais políticas podem ser implementadas usando ResourceQuotas
como blocos de construção, por
escrevendo um "controlador" que observa o uso da cota e ajusta os limites rígidos da cota de cada namespace de acordo com outros sinais.
Observe que a cota de recursos divide os recursos agregados do cluster, mas não cria restrições em torno dos nós: pods de vários namespaces podem ser executados no mesmo nó.
Limite de consumo de classe de prioridade por padrão
Pode ser desejado que os pods com uma prioridade particular, por exemplo. "cluster-services", deve ser permitido em um namespace, se, e somente se, existir um objeto de cota correspondente.
Com este mecanismo, os operadores podem restringir o uso de certas classes de prioridade para um número limitado de namespaces , e nem todos poderão consumir essas classes de prioridade por padrão.
Para impor isso, a flag kube-apiserver
--admission-control-config-file
deve ser
usada para passar o caminho para o seguinte arquivo de configuração:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ResourceQuotaConfiguration
limitedResources:
- resource: pods
matchScopes:
- scopeName: PriorityClass
operator: In
values: ["cluster-services"]
Em seguida, crie um objeto de cota de recurso no namespace kube-system
:
apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-cluster-services
spec:
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created
Nesse caso, a criação de um pod será permitida se:
- O
priorityClassName
do pod não foi especificado. - O
priorityClassName
do pod é especificado com um valor diferente decluster-services
. - O
priorityClassName
do pod está definido comocluster-services
, ele deve ser criado no namespacekube-system
e passou na verificação de cota de recursos.
Uma solicitação de criação de pod é rejeitada caso seu priorityClassName
estiver definido como cluster-services
e deve ser criado em um namespace diferente de kube-system
.
Próximos passos
- Veja o documento de design de cota de recursos para mais informações.
- Veja um exemplo detalhado de como usar a cota de recursos.
- Leia o documento de design de suporte de cota para prioridade de classe.
- Veja recursos limitados