Boas Práticas de Namespaces em Kubernetes: Isolamento, ResourceQuotas e LimitRanges para Times Ágeis Já leu

Namespaces em Kubernetes: Isolamento, ResourceQuotas e LimitRanges Namespaces são um dos conceitos fundamentais do Kubernetes, funcionando como partições lógicas dentro de um cluster. Eles permitem que múltiplos projetos, times ou ambientes (desenvolvimento, staging, produção) coexistam no mesmo cluster físico sem conflitos. Cada namespace possui seu próprio escopo de nomes para recursos como Pods, Services e Deployments, e pode ter políticas de acesso, quotas de recursos e restrições independentes. O grande diferencial dos namespaces é que eles não são apenas uma questão de organização visual — eles implementam um verdadeiro isolamento lógico. Um desenvolvedor trabalhando no namespace não consegue acessar nem ver recursos do namespace sem permissões explícitas. Isso é essencial em ambientes corporativos onde segurança e separação de responsabilidades são críticas. Por que usar Namespaces? Quando você trabalha com Kubernetes em produção, é raro ter apenas um projeto ou um único time. A maioria das organizações enfrenta o desafio de múltiplas aplicações compartilhando a mesma infraestrutura. Sem namespaces, teríamos conflitos

Namespaces em Kubernetes: Isolamento, ResourceQuotas e LimitRanges

Namespaces são um dos conceitos fundamentais do Kubernetes, funcionando como partições lógicas dentro de um cluster. Eles permitem que múltiplos projetos, times ou ambientes (desenvolvimento, staging, produção) coexistam no mesmo cluster físico sem conflitos. Cada namespace possui seu próprio escopo de nomes para recursos como Pods, Services e Deployments, e pode ter políticas de acesso, quotas de recursos e restrições independentes.

O grande diferencial dos namespaces é que eles não são apenas uma questão de organização visual — eles implementam um verdadeiro isolamento lógico. Um desenvolvedor trabalhando no namespace dev não consegue acessar nem ver recursos do namespace prod sem permissões explícitas. Isso é essencial em ambientes corporativos onde segurança e separação de responsabilidades são críticas.

Por que usar Namespaces?

Quando você trabalha com Kubernetes em produção, é raro ter apenas um projeto ou um único time. A maioria das organizações enfrenta o desafio de múltiplas aplicações compartilhando a mesma infraestrutura. Sem namespaces, teríamos conflitos de nomenclatura (dois serviços com o mesmo nome), gerenciamento caótico de permissões, e seria impossível aplicar políticas diferenciadas por projeto.

Namespaces resolvem esse problema oferecendo isolamento de nomes, separação de recursos, controle de acesso granular (via RBAC) e a possibilidade de aplicar quotas e limites específicos por projeto. Além disso, facilitam a limpeza — deletar um namespace remove automaticamente todos os seus recursos.

Isolamento Através de Namespaces

Criação e Gerenciamento Básico

Criar um namespace em Kubernetes é direto. Você pode fazer isso via YAML ou comando kubectl:

kubectl create namespace producao
kubectl create namespace desenvolvimento
kubectl create namespace staging

O equivalente em YAML (mais recomendado para ambientes de produção) seria:

apiVersion: v1
kind: Namespace
metadata:
  name: producao
  labels:
    ambiente: produção
    criticidade: alta
---
apiVersion: v1
kind: Namespace
metadata:
  name: desenvolvimento
  labels:
    ambiente: dev
    criticidade: baixa

Você pode listar, descrever e deletar namespaces normalmente:

kubectl get namespaces
kubectl describe namespace producao
kubectl delete namespace desenvolvimento

Isolamento de Nomes e Acesso

O isolamento começa com nomes: um Pod chamado api-server no namespace producao é completamente diferente de um Pod api-server no namespace desenvolvimento. Internamente, o Kubernetes qualifica nomes como api-server.producao.svc.cluster.local, garantindo unicidade.

Para acessar um serviço em outro namespace, você usa:

# Dentro do mesmo namespace
curl http://api-server:8080

# De outro namespace
curl http://api-server.producao.svc.cluster.local:8080

Esse isolamento de nomes é apenas o primeiro nível. O acesso real é controlado por RBAC (Role-Based Access Control). Por exemplo, um usuário pode ter permissão para listar Pods apenas no namespace desenvolvimento, mas não em producao. Aqui está um exemplo prático:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: dev-reader
  namespace: desenvolvimento
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-reader-binding
  namespace: desenvolvimento
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: dev-reader
subjects:
- kind: User
  name: desenvolvedor@empresa.com
  apiGroup: rbac.authorization.k8s.io

Com essa configuração, desenvolvedor@empresa.com pode fazer kubectl get pods -n desenvolvimento, mas kubectl get pods -n producao resultará em "forbidden".

ResourceQuotas: Controlando Consumo de Recursos

O Problema da Falta de Limites

Sem controle, um desenvolvedor desatento pode deployar uma aplicação com bug que consome toda a memória do cluster, prejudicando outros projetos. ResourceQuotas são o mecanismo que previne isso, estabelecendo limites de consumo total por namespace.

Uma ResourceQuota define quantos recursos (CPU, memória, número de Pods, etc.) um namespace inteiro pode usar. Diferentemente de um LimitRange (que veremos depois), a quota é um total para todo o namespace. Se você definir uma quota de 10 Gi de memória, qualquer tentativa de deployar Pods que ultrapassem esse total será rejeitada.

Criando e Aplicando Quotas

Aqui está um exemplo prático de ResourceQuota para um namespace de desenvolvimento:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: desenvolvimento
spec:
  hard:
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"
    pods: "100"
    services: "10"
    persistentvolumeclaims: "5"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["default"]

Essa quota significa:
- O namespace pode ter no máximo 100 Pods
- A soma de todos os requests.cpu dos Pods não pode exceder 10 unidades
- A soma de todos os requests.memory dos Pods não pode exceder 20 Gi
- A soma de todos os limits.cpu não pode exceder 20 unidades
- A soma de todos os limits.memory não pode exceder 40 Gi
- No máximo 10 Services e 5 PersistentVolumeClaims

Vamos criar um Deployment que respeita essa quota:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-app
  namespace: desenvolvimento
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-app
  template:
    metadata:
      labels:
        app: api-app
    spec:
      containers:
      - name: api
        image: nginx:latest
        resources:
          requests:
            cpu: "500m"      # 0.5 unidades de CPU
            memory: "256Mi"  # 256 MiB de memória
          limits:
            cpu: "1000m"     # Máximo 1 unidade de CPU
            memory: "512Mi"  # Máximo 512 MiB

Com 3 replicas, esse Deployment consumirá:
- Requests: 1.5 CPUs e 768 Mi de memória
- Limits: 3 CPUs e 1.5 Gi de memória

Ainda dentro da quota. Mas se tentarmos escalar para 15 replicas, o scheduler rejeitará os Pods por exceder a quota.

Verificando Quota

kubectl describe resourcequota dev-quota -n desenvolvimento

Você verá algo como:

Used                 Hard
----                 ----
limits.cpu           3                    20
limits.memory        1500Mi               40Gi
pods                 3                    100
requests.cpu         1500m                10
requests.memory      768Mi                20Gi
services             1                    10

LimitRanges: Impondo Restrições Individuais

Quando ResourceQuota Não é Suficiente

ResourceQuota controla o total consumido por um namespace, mas não impede que um único Pod use recursos excessivos. Imagine um namespace com quota de 20 Gi de memória: um único container poderia consumir 19 Gi, deixando 1 Gi para todos os outros Pods. LimitRange resolve isso definindo limites e requests padrão, mínimos e máximos por Pod ou container.

LimitRange é uma política de sanidade. Ela garante que nenhum container seja extremamente ganancioso, e que desenvolvedores que se esqueçam de especificar recursos recebam defaults sensatos. É uma camada adicional de proteção que funciona em conjunto com ResourceQuota.

Implementando LimitRanges

Aqui está um LimitRange bem estruturado para um namespace de desenvolvimento:

apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: desenvolvimento
spec:
  limits:
  # Limites para containers
  - type: Container
    max:
      cpu: "2000m"
      memory: "2Gi"
    min:
      cpu: "100m"
      memory: "128Mi"
    default:
      cpu: "500m"
      memory: "512Mi"
    defaultRequest:
      cpu: "250m"
      memory: "256Mi"
    maxLimitRequestRatio:
      cpu: "2"
      memory: "2"
  # Limites para Pods (soma de todos os containers)
  - type: Pod
    max:
      cpu: "4000m"
      memory: "4Gi"
    min:
      cpu: "200m"
      memory: "256Mi"
  # Limites para PersistentVolumeClaims
  - type: PersistentVolumeClaim
    max:
      storage: "10Gi"
    min:
      storage: "1Gi"

Isso significa:
- Cada container pode ter no máximo 2 CPUs e 2 Gi de memória, mínimo 100m de CPU e 128 Mi de memória
- Se um container não especificar recursos, recebe defaults: 500m CPU e 512 Mi memória
- A razão entre limit e request não pode exceder 2 (ex: se request é 500m, limit máximo é 1000m)
- Cada Pod (soma de todos os containers) pode ter no máximo 4 CPUs e 4 Gi, mínimo 200m e 256 Mi
- Cada PersistentVolumeClaim deve estar entre 1 Gi e 10 Gi

Comportamento com LimitRange

Quando você tenta criar um Pod sem especificar recursos:

apiVersion: v1
kind: Pod
metadata:
  name: app-sem-recursos
  namespace: desenvolvimento
spec:
  containers:
  - name: app
    image: nginx:latest
    # Sem spec de resources

O LimitRange automaticamente injeta os defaults:

resources:
  requests:
    cpu: "250m"
    memory: "256Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

Se você tentar criar um Pod que viola o LimitRange:

apiVersion: v1
kind: Pod
metadata:
  name: app-excessivo
  namespace: desenvolvimento
spec:
  containers:
  - name: app
    image: nginx:latest
    resources:
      requests:
        cpu: "100m"
        memory: "128Mi"
      limits:
        cpu: "5000m"      # Viola o máximo de 2000m
        memory: "2Gi"

Você receberá um erro: Pod violates LimitRange: limits.cpu: Invalid value: "5000m": must be less than or equal to 2000m

Exemplo Completo: Configurando um Ambiente Multi-tenant

Vamos combinar tudo em um cenário real: uma empresa com três ambientes (desenvolvimento, staging, produção) e políticas diferenciadas para cada um.

---
# NAMESPACE DESENVOLVIMENTO
apiVersion: v1
kind: Namespace
metadata:
  name: desenvolvimento
  labels:
    ambiente: dev
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: desenvolvimento
spec:
  hard:
    requests.cpu: "5"
    requests.memory: "10Gi"
    limits.cpu: "10"
    limits.memory: "20Gi"
    pods: "50"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: desenvolvimento
spec:
  limits:
  - type: Container
    max:
      cpu: "1000m"
      memory: "1Gi"
    min:
      cpu: "100m"
      memory: "128Mi"
    defaultRequest:
      cpu: "250m"
      memory: "256Mi"
---
# NAMESPACE STAGING
apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    ambiente: staging
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: staging-quota
  namespace: staging
spec:
  hard:
    requests.cpu: "20"
    requests.memory: "50Gi"
    limits.cpu: "40"
    limits.memory: "100Gi"
    pods: "100"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: staging-limits
  namespace: staging
spec:
  limits:
  - type: Container
    max:
      cpu: "4000m"
      memory: "4Gi"
    min:
      cpu: "100m"
      memory: "128Mi"
    defaultRequest:
      cpu: "500m"
      memory: "512Mi"
---
# NAMESPACE PRODUÇÃO
apiVersion: v1
kind: Namespace
metadata:
  name: producao
  labels:
    ambiente: prod
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: prod-quota
  namespace: producao
spec:
  hard:
    requests.cpu: "100"
    requests.memory: "200Gi"
    limits.cpu: "200"
    limits.memory: "400Gi"
    pods: "500"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: prod-limits
  namespace: producao
spec:
  limits:
  - type: Container
    max:
      cpu: "8000m"
      memory: "8Gi"
    min:
      cpu: "100m"
      memory: "128Mi"
    defaultRequest:
      cpu: "1000m"
      memory: "1Gi"

Agora você pode deployar aplicações em cada namespace com confiança:

kubectl apply -f namespaces.yaml
kubectl get resourcequotas -n desenvolvimento
kubectl describe limitrange dev-limits -n desenvolvimento

Conclusão

Os três conceitos apresentados trabalham em camadas complementares: Namespaces fornecem isolamento lógico e segurança no nível de controle de acesso, ResourceQuotas garantem que um namespace não consuma mais que sua parcela justa de recursos do cluster, e LimitRanges protegem contra containers individuais problemáticos e estabelecem defaults sensatos. Juntos, eles são a base de uma estratégia sólida de multi-tenancy em Kubernetes, permitindo que múltiplos projetos coexistam pacificamente em um cluster compartilhado sem risco de um prejudicar o outro.

Referências


Artigos relacionados