Pular para o conteúdo

Otimizar Kubernetes em VPS Linux: recursos e custos eficientes

Por Equipe Técnica AviraHost · 14 min de leitura · Atualizado em · AviraHost, kubernetes, requests-limits, k3s, otimizacao-de-custos, resourcequota, hpa · 0

Otimizar Kubernetes em VPS Linux é ajustar o consumo de CPU e memória do cluster para pagar por menos recursos sem perder estabilidade. Na prática, isso significa usar uma distribuição leve como K3s, medir o uso real dos pods e definir requests e limits compatíveis com essa medição. Para reduzir custos, siga estes passos:

  1. Escolha uma distribuição leve (K3s) e desative os componentes que você não usa.
  2. Instale o metrics-server e meça o consumo real com kubectl top.
  3. Defina requests e limits próximos do uso observado em cada Deployment.
  4. Aplique LimitRange e ResourceQuota por namespace para impedir desperdício.
  5. Use HPA onde a carga varia e remova workloads ociosos.

Pré-requisitos

  • Um VPS com Ubuntu 24.04 LTS (os comandos valem para outras distros com poucas mudanças) e acesso root ou sudo.
  • Um cluster Kubernetes já em funcionamento: K3s de nó único ou de poucos nós.
  • kubectl configurado e apontando para o cluster.
  • Pelo menos um Deployment em execução para servir de exemplo.
  • Noções básicas de YAML e de namespaces.

Se você ainda está decidindo entre rodar containers puros ou orquestrados, veja antes Otimizar Kubernetes vs Docker no Rocky 10: checklist prático.

Escolher uma distribuição leve para o control plane

O primeiro custo fixo de um cluster em VPS é o próprio control plane. Em um VPS pequeno, uma distribuição enxuta como o K3s empacota os componentes em um único binário e reduz a memória reservada antes mesmo de você subir a primeira aplicação.

O K3s vem com Traefik (ingress) e ServiceLB habilitados por padrão. Se você já usa outro ingress ou não precisa deles, desative na instalação para economizar memória:

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable traefik --disable servicelb" sh -

Depois confira se o nó está pronto:

sudo k3s kubectl get nodes
NAME      STATUS   ROLES                  AGE   VERSION
vps-01    Ready    control-plane,master   1m    v1.34.1+k3s1

A versão exibida é ilustrativa; a sua dependerá do release do K3s instalado.

Atenção: desativar o Traefik e o ServiceLB significa que você precisa fornecer outro ingress e outra forma de expor serviços. Faça isso só se já tiver essa parte resolvida.

Alternativas leves incluem o MicroK8s. Em qualquer opção, evite instalar add-ons que você não vai usar: cada componente extra consome memória do VPS. Para ajustes no sistema operacional que complementam este passo, consulte Otimizar a Performance de VPS Linux e Servidor Dedicado com Configuração Avançada do Kernel.

Medir o consumo real com metrics-server

Sem medição, requests e limits viram palpite. O metrics-server coleta uso de CPU e memória dos nós e dos pods e alimenta tanto o kubectl top quanto o HPA. O K3s costuma incluir o metrics-server por padrão; confirme antes de instalar outro:

kubectl get deployment metrics-server -n kube-system

Se ele não existir no seu cluster, instale pelo manifesto oficial do projeto:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Com ele funcionando, veja o consumo por nó e por pod:

kubectl top nodes
kubectl top pods -A --sort-by=memory
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
vps-01   180m         9%     1450Mi          36%

Os valores variam com a sua carga. O formato das colunas é o que importa: CPU em millicores (m) e memória em Mi. Observe os pods por um período representativo, incluindo horário de pico, e não apenas um instante. Anote o uso médio e o máximo de cada aplicação: eles serão a base para os requests e limits.

Definir requests e limits dos pods

Requests e limits controlam como o scheduler distribui pods e quanto cada container pode consumir. Requests altos demais fazem o nó parecer cheio mesmo com uso baixo, e você acaba pagando por mais VPS. Requests baixos demais causam disputa por recursos e instabilidade.

Um exemplo de Deployment com valores definidos a partir de medição (ajuste os números ao que você observou):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-web
  namespace: producao
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app-web
  template:
    metadata:
      labels:
        app: app-web
    spec:
      containers:
      - name: app
        image: nginx:stable
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            memory: "256Mi"

Note que o exemplo define limite de memória, mas não de CPU. É uma escolha comum: o limite de CPU causa throttling mesmo quando o nó tem CPU livre, enquanto o limite de memória protege o nó contra um pod que consome RAM sem controle. Decida por workload.

Como regra de partida, o request fica próximo do uso médio observado e o limite de memória com folga acima do pico. Crie o namespace e aplique:

kubectl create namespace producao
kubectl apply -f app-web.yaml
kubectl get pods -n producao
kubectl top pods -n producao

Atenção: se um container ultrapassar o limite de memória, ele é encerrado por OOM kill e reiniciado. Se você vir reinícios, aumente o limite com base na medição em vez de remover a proteção.

Aplicar LimitRange e ResourceQuota por namespace

O LimitRange define valores padrão e limites mínimos e máximos por container; o ResourceQuota limita o total que um namespace pode reservar. Juntos, controlam as declarações de requests, limits e contagens de objetos admitidos por namespace, impedindo que um deploy esquecido monopolize as cotas definidas. Para impor também um teto de uso de CPU em tempo de execução, adicione os campos default e max para CPU no LimitRange e limits.cpu no ResourceQuota, tendo em mente o risco de throttling.

apiVersion: v1
kind: LimitRange
metadata:
  name: padrao-recursos
  namespace: producao
spec:
  limits:
  - type: Container
    defaultRequest:
      cpu: "50m"
      memory: "64Mi"
    default:
      memory: "256Mi"
    max:
      memory: "1Gi"
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota-producao
  namespace: producao
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.memory: 2Gi
    pods: "20"

Aplique os dois e confira o estado:

kubectl apply -f limitrange.yaml -f quota.yaml
kubectl describe resourcequota quota-producao -n producao

A saída lista cada recurso com as colunas Used e Hard. Quando um namespace tem ResourceQuota para CPU ou memória, todo pod novo precisa declarar requests e limits correspondentes; o LimitRange com valores padrão resolve isso sem editar cada manifesto.

Dimensione a quota de acordo com a capacidade do nó, deixando margem para o sistema e para o control plane. Para planejar a capacidade do VPS em si, veja VPS para Docker: como dimensionar memória, CPU e disco.

Escalar réplicas com o HPA

O Horizontal Pod Autoscaler (HPA) ajusta o número de réplicas conforme a utilização. Ele calcula a utilização como porcentagem do request, por isso só funciona com requests definidos e realistas. Crie um HPA para o Deployment de exemplo:

kubectl autoscale deployment app-web -n producao --cpu-percent=70 --min=1 --max=3

Atenção: ao criar o HPA, ele passa a gerenciar o número de réplicas do Deployment. Em re-aplicações do manifesto, omita o campo spec.replicas para evitar conflito entre o valor fixado no YAML e o valor controlado pelo HPA.

Acompanhe o comportamento:

kubectl get hpa -n producao
NAME      REFERENCE            TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
app-web   Deployment/app-web   cpu: 12%/70%  1         3         1          2m

Se a coluna TARGETS mostrar <unknown>, o metrics-server não está respondendo ou o pod não tem request de CPU. Em um VPS de nó único, o HPA não cria capacidade nova: ele redistribui o que o nó já tem. Por isso, mantenha o --max compatível com a folga real do servidor, senão as réplicas novas ficam em estado Pending.

O HPA reduz o consumo fora do pico. Em um VPS fixo, isso só se converte em economia se permitir reduzir posteriormente o plano ou a quantidade de nós. Em cargas estáveis e pequenas, um número fixo de réplicas com requests bem calibrados costuma ser mais simples e igualmente econômico.

Eliminar desperdício e reduzir o custo do cluster

Depois de calibrar os pods, procure o que não deveria estar rodando. Liste tudo que consome recursos e compare com o que você realmente usa:

kubectl get pods -A
kubectl top pods -A --sort-by=cpu
kubectl get deployments -A

Ações que reduzem o consumo sem trocar de plano de VPS:

  • Remova Deployments, Jobs e namespaces de teste esquecidos.
  • Reduza réplicas de ambientes de homologação para 1.
  • Evite agentes de monitoramento pesados; um stack completo de observabilidade pode consumir mais que a aplicação. Prefira coleta enxuta.
  • Configure rotação e limite de logs dos containers para não encher o disco.
  • Use imagens menores (variantes slim ou alpine, quando compatíveis) para reduzir disco e tempo de pull.

Para escalar a zero um workload que não precisa ficar ativo:

kubectl create namespace homologacao   # se o namespace ainda não existir
kubectl scale deployment app-teste -n homologacao --replicas=0

Esse comando não apaga o Deployment: ele apenas encerra os pods, e você pode voltar com --replicas=1. Por fim, revise o dimensionamento do VPS. Se, após os ajustes, o uso do nó ficar consistentemente baixo, um plano menor pode bastar. Outros ajustes gerais de sistema estão em Checklist para Otimização de VPS Linux: 15 Ajustes Essenciais.

Problemas comuns e como resolver

Sintoma: pods ficam em Pending com "Insufficient memory" ou "Insufficient cpu"

Causa: a soma dos requests excede a capacidade alocável do nó, mesmo que o uso real seja baixo.
Solução: rode kubectl describe pod NOME -n NAMESPACE e leia a seção Events. Compare requests com o uso medido via kubectl top e reduza os que estiverem superdimensionados. Remova workloads ociosos antes de aumentar o VPS.

Sintoma: container reinicia com status OOMKilled

Causa possível: estouro do limite de memória do container ou pressão global de memória no nó. Um container sem limite definido também pode ser encerrado pelo OOM killer do kernel quando a memória do nó está escassa.
Solução: confirme com kubectl describe pod (campo Last State: Terminated, Reason: OOMKilled) e verifique métricas, eventos e logs do kernel/kubelet com journalctl -k e journalctl -u k3s antes de aumentar o limite. Se o problema for limite do container, aumente-o com base no pico medido; se for pressão no nó, revise o total de limits definidos no namespace ou corrija vazamento de memória na aplicação.

Sintoma: kubectl top retorna "Metrics API not available"

Causa: o metrics-server não está instalado, não está pronto ou não consegue acessar os kubelets.
Solução: verifique o pod com kubectl get pods -n kube-system e leia os logs com kubectl logs -n kube-system deploy/metrics-server. Aguarde alguns instantes após a instalação, pois a primeira coleta não é imediata.

Sintoma: HPA mostra TARGETS como <unknown>

Causa: o Deployment não define request de CPU ou o metrics-server não está entregando métricas.
Solução: adicione resources.requests.cpu ao manifesto e confirme que kubectl top pods funciona.

Sintoma: aplicação lenta mesmo com CPU sobrando no nó

Causa: limite de CPU baixo causando throttling do container.
Solução: aumente ou remova o limite de CPU e mantenha apenas o request, monitorando o efeito sobre os demais pods.

Perguntas frequentes

Como reduzir custos de Kubernetes em VPS Linux?

Comece medindo o consumo real dos pods com kubectl top e ajuste requests e limits para valores próximos do uso observado. Depois use LimitRange e ResourceQuota por namespace, remova workloads ociosos e escolha uma distribuição leve, como K3s, para reduzir o consumo do próprio control plane.

Qual a diferença entre requests e limits no Kubernetes?

Requests é a quantidade de CPU e memória que o scheduler reserva para o pod ao decidir em qual nó ele roda. Limits é o teto de uso: ao ultrapassar o limite de CPU o container sofre throttling, e ao ultrapassar o de memória ele pode ser encerrado por OOM kill.

Kubernetes roda bem em um VPS pequeno?

Roda, desde que você use uma distribuição leve como K3s ou MicroK8s e limite o número de componentes extras. O control plane tem um consumo basal de memória e seu uso cresce conforme o tamanho e a atividade do cluster, então em VPS de poucos GB de RAM é essencial manter poucos serviços auxiliares e dimensionar requests com cuidado.

Para que serve o HPA e ele ajuda a economizar?

O Horizontal Pod Autoscaler ajusta o número de réplicas de um Deployment conforme a utilização de CPU ou memória, usando dados do metrics-server. Ele evita manter réplicas sobrando fora do horário de pico, mas só funciona bem se os requests refletirem o consumo real do pod.

Como ver quanto de CPU e memória cada pod consome?

Instale o metrics-server no cluster e execute kubectl top pods -A para ver o consumo por pod, e kubectl top nodes para ver os nós. Compare esses valores com os requests definidos nos manifestos para encontrar pods superdimensionados.

Conclusão

Reduzir o custo de um cluster em VPS depende de medir primeiro e ajustar depois: a distribuição leve diminui o piso de consumo, e requests e limits calibrados evitam pagar por capacidade reservada e não usada.

  • Instale o metrics-server e registre o uso médio e o pico de cada pod com kubectl top.
  • Ajuste requests e limits, e proteja cada namespace com LimitRange e ResourceQuota.
  • Remova workloads ociosos e reavalie o plano do VPS depois de uma semana de uso medido.

Leia também

Precisa de ajuda com Kubernetes em VPS?

Um VPS com recursos bem dimensionados é a base para um cluster enxuto e estável. Escolha um plano que caiba na sua carga real e ajuste conforme a medição.

Conhecer os planos de VPS


Esta resposta foi útil?