Vamos começar analisando um cluster Kubernetes de três nós. Utilizando o resourece limits e requests. Cada nó tem um conjunto de recursos de CPU e memória disponíveis. Agora, cada pod requer um conjunto de recursos para ser executado. Agora, sempre que um pod é colocado em um nó, ele consome os recursos disponíveis nesse nó. Como discutimos anteriormente, é o schedule do Kubernetes que decide para qual nó um pod vai.
Aprenda a instalar um cluster kubernetes em Kubeadm Multi-Master
O schedule leva em consideração a quantidade de recursos exigidos por um pod e os disponíveis nos nós e identifica o melhor nó para colocar um pod.
Se os nós não tiverem recursos suficientes disponíveis, o schedule evitará colocar o pod nesses nós e, em vez disso, colocará o pod em um nó em que haja recursos suficientes disponíveis.
E, se não houver recursos suficientes disponíveis em nenhum dos nós, o schedule retém o agendamento do pod. E você verá o pod em um estado pendente. E se você observar os eventos usando o comando kubectl describe pod
, verá que há uma mensagem de CPU insuficiente.
Resource Requests
Agora vamos nos concentrar nos requisitos de recursos para cada pod. Então, o que são esses blocos e quais são seus valores?
Agora você pode especificar a quantidade de CPU e memória necessária para um pod ao criá-lo. Por exemplo, poderia ser 1 CPU e 1 gibibyte de memória. Isso é conhecido como resource requests para um contêiner.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
function: font-end
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
resources:
requests:
memory: "1Gi"
cpu: 1
Isso indica, a quantidade mínima de CPU e memória solicitada pelo contêiner. Portanto, quando o schedule tenta colocar o pod em um nó, ele usa esses números para identificar um nó que tenha uma quantidade suficiente de recursos disponíveis.
Unidades de recurso são as mesmas para solicitações de recurso do pod e limites de recurso. Por exemplo: Gi significa GiB e m significa millicores. Um millicore é o equivalente a 1/1000 de um único núcleo de CPU.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
function: font-end
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
resources:
requests:
memory: "256Mi"
cpu: 100m
O G é gigabyte e se refere a 1.000 megabytes, enquanto Gi se refere a gibibyte e seria igual a 1.024 mebibytes. Portanto, o mesmo se aplica ao megabyte e ao mebibyte, e ao kilobyte e ao kibibyte.
Limits
E, por padrão, um contêiner não tem limite para os recursos que pode consumir em um nó. Portanto, digamos que um contêiner que faz parte de um pod comece com uma CPU em um nó, ele pode aumentar e consumir o máximo de recursos que precisar, sufocando os processos nativos no nó ou outros contêineres de recursos. No entanto, você pode definir um limite para o uso de recursos nesses pods. Por exemplo, se você definir um limite de uma vCPU para os contêineres, um contêiner será limitado a consumir apenas uma vCPU desse nó.
O mesmo acontece com a memória. Por exemplo, você pode definir um limite de 512 mebibyte. Agora você pode especificar os limites na seção de limites, na seção de recursos do seu arquivo de definição de pod. Portanto, aqui, especifique os novos limites de memória e CPU da seguinte forma. Agora, quando o pod é criado, o Kubernetes define novos limites para o contêiner.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
function: font-end
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
resources:
requests:
memory: "256Mi"
cpu: 100m
limits:
memory: "512Mi"
cpu: 1
E lembre-se de que as limits e as requests são definidos para cada contêiner em um pod. Portanto, se houver vários contêineres, cada um deles poderá ter uma solicitação ou um limite definido para si próprio.
Então, o que acontece quando um pod tenta exceder os recursos além do limite especificado?
Um contêiner não pode usar mais recursos de CPU do que o seu limite. Entretanto, esse não é o caso da memória. Um contêiner pode usar mais recursos de memória do que o seu limite. Portanto, se um pod tentar consumir mais memória do que seu limite constantemente, o pod será encerrado e você verá que o pod foi encerrado com um erro OOM nos logs ou na saída de eventos. Portanto, o termo OOM refere-se à Out of Memory.
Então, agora que aprendemos o que são resources resquets e o que são limits, como eles funcionam e o que acontece quando um determinado contêiner ou pod atinge os limites definidos, vamos ver qual é a configuração padrão, certo? Portanto, por padrão, o Kubernetes não tem uma solicitação ou limite de CPU ou memória definido.
Isso significa que qualquer pod pode consumir a quantidade de recursos necessária em qualquer nó e sufocar os recursos de outros pods ou processos que estejam em execução no nó.
LimitRange
O Kubernetes não tem resource requests ou limits configurados para pods. Mas então como podemos garantir que cada pod criado tenha um conjunto padrão?
Agora isso é possível com limitRange. Os limitRange podem ajudá-lo a definir valores padrão a serem definidos para contêineres em pods que são criados sem uma solicitação ou limite especificado nos arquivos de definição de pod.
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-resource-constraint
spec:
limits:
- default:
cpu: 500m
defaultRequest:
cpu: 500m
max:
cpu: "1"
memory: "1Gi"
min:
cpu: 100m
memory: "25Mi"
type: Container
Isso é aplicável no nível do namespace. Crie um arquivo de definição para o LimitRange, Observe que esses limites são impostos quando um pod é criado. Portanto, se você criar ou alterar um limitRange, isso não afetará os pods existentes. Isso afetará apenas os pods mais novos que forem criados depois que o limitRange for criado ou atualizado.
Quota
Há alguma maneira de restringir a quantidade total de recursos que podem ser consumidos pelos aplicativos implantados em um cluster do Kubernetes?
Por exemplo, se tivéssemos que dizer que todos os pods juntos não deveriam consumir mais do que essa quantidade de CPU ou memória, poderíamos criar cotas em um nível de namespace. Portanto, uma resource quota é um objeto de nível de namespace que pode ser criado para definir limites rígidos (hard) para solicitações e limites.
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-resource-quota
namespace: dev
spec:
hard:
requests.cpu: 4
requests.memory: 4Gi
limits.cpu: 10
limits.memory: 10Gi
Neste exemplo, essa resource quota limita a CPU total solicitada no namespace atual a 4 e a memória a 4Gi. E ele define um limite máximo de CPU consumida por todos os pods juntos como sendo 10 e a memória como sendo 10 gibibytes.
Portanto, essa é outra opção que pode ser explorada.
Prática Resource Requests e Limits
Definindo resource requests e limits em um Pod
Objetivo: Definir resource requests e limits para um Pod.
Passo: 1.1. Crie um arquivo YAML chamado pod.yaml
e adicione o seguinte conteúdo:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "200m"
memory: "512Mi"
Passo: 1.2. Aplique o arquivo YAML para criar o Pod:
kubectl apply -f pod.yaml
Passo: 1.3. Verifique se o Pod foi criado corretamente:
kubectl get pods
Você deve ver o Pod my-pod
na lista, indicando que foi criado com sucesso.
Configurando Quota
Objetivo: Criar uma quota para limitar os recursos consumidos por um namespace.
Passo: 2.1. Crie um arquivo YAML chamado quota.yaml
e adicione o seguinte conteúdo:
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-quota
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
Passo: 2.2. Aplique o arquivo YAML para criar a quota:
kubectl apply -f quota.yaml
Passo: 2.3. Verifique se a quota foi criada corretamente:
kubectl get resourcequota
Você deve ver a quota my-quota
na lista, indicando que foi criada com sucesso.
Definindo LimitRange
Objetivo: Configurar um LimitRange para definir limites de recursos padrão para os Pods em um namespace.
Passo: 3.1. Crie um arquivo YAML chamado limitrange.yaml
e adicione o seguinte conteúdo:
apiVersion: v1
kind: LimitRange
metadata:
name: my-limitrange
spec:
limits:
- default:
cpu: "200m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "256Mi"
type: Container
Passo: 3.2. Aplique o arquivo YAML para criar o LimitRange:
kubectl apply -f limitrange.yaml
Passo: 3.3. Verifique se o LimitRange foi criado corretamente:
kubectl get limitrange
kubectl describe limitrange my-limit
Você deve ver o LimitRange my-limitrange
na lista, indicando que foi criado com sucesso.
Crie um namespace selfservice-quotas.
Passo 4.1 Use o comando kubectl create para criar o namespace.
kubectl create namespace selfservice-quotas
Atividade 5. Crie um deployment com um contêiner que solicite uma CPU.
Passo 5.1. Use o comando kubectl create para criar a implantação.
kubectl create deployment test \
--image nginx \
-n selfservice-quotas
Passo 5.2. Use o comando kubectl set resources para solicitar uma CPU na especificação do contêiner.
kubectl set resources deployment test --requests=cpu=1 -n selfservice-quotas
Passo 5.3. Use o comando kubectl get para garantir que a implantação inicie um pod corretamente.
kubectl get pod,deployment -n selfservice-quotas
Execute o comando até que a implantação e o pod estejam prontos.
Tente dimensionar a implantação para oito réplicas.
Passo 6.1. Use o comando kubectl scale para dimensionar a implantação.
kubectl scale deployment test --replicas=8 -n selfservice-quotas
kubectl get pod,deployment
Dos oito pods que a implantação cria, apenas alguns deles mudam para o status Running. Os outros pods permanecem no status Pending. Nem todas as réplicas da implantação estão prontas e disponíveis.
Passo 6.2. Use o comando kubectl get para listar eventos. Classifique os eventos por carimbo de data/hora de criação.
kubectl get event --sort-by .metadata.creationTimestamp -n selfservice-quotas
As réplicas falham ao agendar porque o cluster tem CPU insuficiente.
Examine o cluster como um administrador.
Passo 7.1. Use o comando kubectl top para exibir o uso de recursos dos nós.
kubectl top node
O cluster não mostra o alto uso da CPU.
Passo 7.2. Use o comando kubectl describe para ver detalhes do nó.
kubectl describe node/master01
O nó tem uma capacidade para quatro CPUs e tem mais de quatro CPUs alocáveis. No entanto, como mais de quatro CPUs são solicitadas, menos de uma CPU está disponível para novas cargas de trabalho.
Atividade 8. Crie um namespace test como um administrador e verifique se não é possível criar novas cargas de trabalho que solicitem uma CPU.
Passo 8.1. Use o comando kubectl create namespace para criar o namespace.
kubectl create namespace test
Passo 8.2. Use o comando kubectl create para criar a implantação.
kubectl create deployment test --image nginx -n test
Passo 8.3. Use o comando kubectl set resources para solicitar uma CPU na especificação do contêiner.
kubectl set resources deployment test --requests=cpu=1 -n test
Passo 8.4. Use o comando kubectl get para revisar pods e implantações no namespace test.
kubectl get pod,deployment -n test
A implantação criou um pod antes de adicionar a solicitação de CPU. Quando você atualizou a implantação para solicitar uma CPU, a implantação tentou substituir o pod para adicionar a solicitação de CPU. O novo pod está no estado Pending porque o cluster tem menos de uma CPU disponível para solicitação.
A carga de trabalho no namespace selfservice-quotas impede a criação de cargas de trabalho em outros namespaces.
Passo 8.5. Use o comando kubectl delete para excluir o namespace test.
kubectl delete namespace test
Atividade 9. Como administrador, dimensione a implantação para uma réplica no namespace selfservice-quotas.
Passo 9.1. Use o comando kubectl scale para dimensionar a implantação test para uma réplica.
kubectl scale deployment test --replicas=1 -n selfservice-quotas
Atividade 10. Crie uma cota para evitar que as cargas de trabalho no namespace selfservice-quotas solicitem mais de duas CPUs.
Passo 10.1. Use o comando kubectl create para criar a cota.
kubectl create quota duas-cpus --hard=requests.cpu=2 -n selfservice-quotas
Passo 10.2. Use o comando kubectl get para verificar a cota.
kubectl get quota duas-cpus -o yaml -n selfservice-quotas
Atividade 11. Tente dimensionar a implantação para oito réplicas e criar uma segunda implantação.
Passo 11.1. Use o comando kubectl scale para dimensionar a implantação.
kubectl scale deployment test --replicas=8 -n selfservice-quotas
Passo 11.2. Use o comando kubectl create para criar uma segunda implantação.
kubectl create deployment test2 --image nginx -n selfservice-quotas
Passo 11.3. Use o comando kubectl get para revisar pods e implantações.
kubectl get pod,deployment -n selfservice-quotas
Passo 11.4. Use o comando kubectl get para examinar o status da cota.
kubectl get quota duas-cpus -o yaml -n selfservice-quotas
Os dois pods da implantação test solicitam uma CPU cada e usam todos os recursos na cota.
Passo 11.5. Use o comando kubectl get para listar eventos. Classifique os eventos por carimbo de data/hora de criação.
kubectl get event --sort-by .metadata.creationTimestamp -n selfservice-quotas
A implantação test não pode criar mais pods porque os novos pods excederiam a cota. A implantação test2 não pode criar pods porque a implantação não define uma solicitação de CPU.
Atividade 12. Crie um namespace test para verificar se você pode criar novas cargas de trabalho em outros namespaces que solicitam recursos de CPU.
Passo 12.1. Use o comando kubectl create namespace para criar o namespace.
kubectl create namespace test
Passo 12.2. Use o comando kubectl create para criar a implantação.
kubectl create deployment test --image nginx -n test
m” Passo 12.3. Use o comando kubectl set resources para solicitar uma CPU na especificação do contêiner.
kubectl set resources deployment test --requests=cpu=1 -n test
Passo 12.4. Use o comando kubectl get para revisar pods e implantações no namespace test.
kubectl get pod,deployment -n test
Embora não seja possível criar outras cargas de trabalho no namespace selfservice-quotas, você pode criar cargas de trabalho que solicitem CPUs em outros namespaces.