< Todos os tópicos

FinOps 101 no RTM OpenShift Autopilot: Comece a controlar custos de containers

Introdução

Utilizar serviços de cloud sempre traz desafios, especialmente quando o modelo de cobrança é baseado em uso. Quanto maior a flexibilidade, maior a necessidade de controle e quando falamos de containers, essa volatilidade aumenta ainda mais. Pensando nisso, escrevi este artigo para apresentar as principais boas práticas de FinOps aplicadas ao nosso serviço de containers, o RTM OpenShift Autopilot, ajudando você a operar com mais eficiência, previsibilidade e consciência de custos.

Modelo de cobrança

Quando falamos de FinOps em containers, tudo começa com entender como o custo é formado. No RTM OpenShift Autopilot, o modelo de cobrança é direto: você paga pelos limits configurados nos seus deployments, StatefulSets e ou pods. Parece simples, mas é aqui que muita gente se perde — porque escolher limits aleatórios ou superdimensionados é o atalho mais rápido para estourar o orçamento.

O ponto é que, diferentemente de máquinas virtuais tradicionais, containers são extremamente dinâmicos. Eles escalam, reduzem, reiniciam, são criados e destruídos o tempo todo. Por isso, o dimensionamento correto vira parte crítica da disciplina de FinOps. E quando você domina isso, a conta começa a trabalhar a seu favor.

No Autopilot, práticas como ajustar limits, remover workloads ociosos, usar escalonamento automático e contratar reservas de recursos formam um conjunto poderoso para ter previsibilidade e eficiência ao mesmo tempo. É o equilíbrio entre performance, custo e responsabilidade — exatamente o que o FinOps prega.

Mas afinal, o que é “Request”?

O Request é o mínimo de CPU e memória que o pod precisa para ser agendado no cluster.
Ele diz ao scheduler: “reserve pelo menos isso para mim”.

Diferente do Limit, o Request não controla o uso real — o pod pode consumir mais do que o Request enquanto estiver abaixo do Limit.

No modelo do RTM Openshift Autopilot, quem define o custo é o Limit, não o Request.
Ou seja:

  • O Request NÃO influencia preço.
  • Não tem o mesmo peso financeiro que em outros serviços baseados em custo por worker-node.

Mas afinal, o que é “Limit”?

No mundo de containers e Openshift/Kubernetes, o Limit é simplesmente o teto máximo de recurso que um pod pode usar. É como definir até onde ele pode ir.

Pense assim: o Limit é o limite superior de CPU e memória que o container está autorizado a consumir.

Se ele tentar ultrapassar isso, o RTM Openshift Autopilot entra em ação:

  • Para CPU: o consumo é “estrangulado” (throttling). Ele até tenta usar mais, mas o cluster não deixa.
  • Para Memória: se ultrapassar o limite, o container pode ser encerrado (OOMKilled).

Exemplo de configuração de limit

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  labels:
    app: httpd
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: registry.access.redhat.com/ubi8/httpd-24:latest
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: "250m"
            memory: "512Mi"

Método de acesso

O RTM OpenShift Autopilot oferece dois caminhos de acesso, cada um pensado para perfis diferentes de usuários e para tipos distintos de operações. O primeiro é a interface gráfica (UI), ideal para quem prefere visualizar recursos, navegar entre workloads e realizar ações de forma rápida e intuitiva. Já o segundo é o acesso via API utilizando o oc CLI, voltado para quem busca agilidade, precisão e automação — geralmente usuários mais avançados ou acostumados com ambientes Kubernetes e pipelines.

É importante destacar que algumas operações podem ser mais simples, mais completas ou até mesmo exclusivas de um dos métodos. A UI facilita muito o dia a dia operacional e o entendimento visual do cluster, enquanto o oc CLI permite comandos diretos, scripts, integrações e workflows automatizados. No fim das contas, a escolha do método depende não só da preferência pessoal, mas também do nível de maturidade técnica e da natureza da tarefa.

Acesso pela interface gráfica

  1. Acesse a URL do RTM OpenShift Autopilot fornecida pela RTM.
  2. Em Log in with, selecione a opção Clientes.
  3. Insira seu usuário e senha.
  4. Pronto — você estará conectado na interface gráfica do Autopilot.

Acesso pela API (oc CLI)

  1. No canto superior direito da UI, clique no seu usuário.
  2. Selecione Copy login command.
  3. Clique em Display token para visualizar o token de acesso.
  4. Copie o comando exibido e cole no seu terminal — com o oc CLI já instalado.

Se você ainda não tem o oc CLI instalado, você pode baixar no link da Red Hat:
https://docs.redhat.com/en/documentation/openshift_container_platform/4.8/html/cli_tools/openshift-cli-oc

Visualizando seus limits

Entender onde estão configurados os limits — e principalmente acompanhar seu uso real — é uma etapa fundamental no processo de FinOps dentro do RTM OpenShift Autopilot. Você pode visualizar essas informações tanto pela interface gráfica quanto via oc CLI. Cada método fornece um nível diferente de profundidade e detalhe.

Versão gráfica

ara uma visualização rápida e intuitiva dos limits configurados e do consumo atual, a interface gráfica é o caminho mais simples.

  1. No menu lateral, selecione Developer.
  2. Em seguida, clique em Observe.
  3. Dentro de Dashboard, você encontrará painéis como:
    • CPU Quota
    • Mem Quota

Esses painéis mostram não apenas os valores dos limites alocados, mas também a porcentagem de consumo — algo extremamente útil dentro de uma estratégia de FinOps. Esse contraste entre limit configurado e uso real ajuda a identificar onde há superdimensionamento, desperdício ou oportunidade de otimização.

Versão linha de comando

Os comandos a seguir ajudam a visualizar de forma global e rapida os limits de todos os deployments, statefulsets e ou Pods do namespace.

Visualizando limits por Pod

oc get pod -o custom-columns=POD:.metadata.name,CONTAINER:.spec.containers[*].name,STATUS:.status.phase,CPU-LIMITS:.spec.containers[*].resources.limits.cpu,MEMORY-LIMITS:.spec.containers[*].resources.limits.memory | column -t
Visualizando o uso por Pod
oc adm top pod

Visualizando limits por Deployment

oc get deploy -o custom-columns=DEPLOYMENT:.metadata.name,REPLICAS:.spec.replicas,CPU-LIMITS-PER-POD:.spec.template.spec.containers[*].resources.limits.cpu,MEMORY-LIMITS-PER-POD:.spec.template.spec.containers[*].resources.limits.memory | column -t

Visualizando limits por StatefulSets

oc get statefulset -o custom-columns=STATEFULSET:.metadata.name,REPLICAS:.spec.replicas,CPU-LIMIT-PER-POD:.spec.template.spec.containers[].resources.limits.cpu,MEMORY-LIMIT-PER-POD:.spec.template.spec.containers[].resources.limits.memory | column -t

Importante: os limits exibidos tanto no comando de Pods quanto no comando de Deployments representam o limit configurado para cada Pod individualmente. O comando de Deployment e Statefulset não calcula o total do workload. Portanto, para estimar o consumo total do Deployment e Statefulset, é necessário multiplicar o limit de cada Pod pelo número de réplicas configuradas.

Soft e Hard quotas

Para gerenciar Limits de forma eficiente — e manter o uso de recursos sob controle — é importante definir algumas regras e travas dentro do seu projeto. No OpenShift, isso acontece através de duas categorias principais: Soft Quotas e Hard Quotas.

Cada uma cumpre um papel diferente na estratégia de FinOps. Enquanto as Soft Quotas funcionam como alertas para avisar que você está se aproximando do limite, as Hard Quotas atuam como bloqueios reais, impedindo a criação de novos recursos quando o teto é atingido.

Hard mode: Controlando o consumo com ResourceQuota

Se você quer colocar o cluster para trabalhar a seu favor no controle de custos, o ResourceQuota é o modo Hard da história. Ele permite definir um limite máximo de recursos que o seu projeto pode consumir — como CPU, memória ou número de pods. Quando esse limite é atingido, o OpenShift simplesmente bloqueia novas alocações. Nada de criar novos pods, nada de consumir mais do que foi combinado.

Na prática, isso ajuda a manter um teto financeiro e evita surpresas desagradáveis no final do mês. Você define o máximo, e o cluster garante que ninguém passe dele.


Se você usar ResourceQuota de forma muito rígida, pode acabar bloqueando comportamentos dinâmicos, como o Horizontal Pod Autoscaler (HPA). Como o HPA cria novas réplicas automaticamente, ele pode bater no limite antes de escalar — ou simplesmente não escalar.

Para a criação de um ResourceQuota é necessario abrir um chamado através do portal de atendimento da RTM descrevendo os parametros de CPU e Memoria desejavel e o nome do Project / Namespace.

Soft mode: Monitorando o consumo com Alerts

No Soft Mode, você não impede o cluster de consumir mais recursos — mas cria uma camada de alertas inteligentes para saber quando está chegando perto do limite planejado.

Com o OpenShift Monitoring, é possível configurar notificações quando um determinado valor de uso é alcançado ou ultrapassado. Ele não trava o provisionamento, não bloqueia Pods e não interfere no autoscaling. O papel dele aqui é visibilidade: te avisar antes que o consumo saia do controle.

É uma abordagem ideal para times que querem manter liberdade operacional, mas sem abrir mão de governança e boa disciplina FinOps. Afinal, saber com antecedência que você está prestes a estourar um limite permite ajustar rapidamente os limits, revisar réplicas ou até repensar o dimensionamento dos seus workloads.

Monitorar a superutilização

Quando um alerta de superutilização dispara, os cenários mais prováveis são:

  • Demanda real aumentou, exigindo revisão de limites ou estudo de reservas;
  • Bug ou comportamento inesperado elevando o uso sem necessidade;
  • Ambiente mudou, e o alerta precisa ser ajustado para a nova realidade.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: softquota-high-usage-alerts
  namespace: <namespace>
spec:
  groups:
  - name: soft-quota-thresholds
    rules:
    - alert: SoftQuotaCPULimitReached
      expr: |
        sum by (namespace) (
          kube_pod_container_resource_limits{resource="cpu", unit="core"}
        ) >= <total CPU>
      for: 7d
      labels:
        severity: info
        category: finops
        resource: cpu
      annotations:
        summary: "The total CPU limits of the project have reached or exceeded the configured threshold."
        description: "The desired CPU allocation for this project has reached or exceeded the recommended threshold. This indicates that the configured resource limits may no longer be optimal for the current workload profile. It is recommended to review the CPU limits defined in Deployments, StatefulSets, and Pods. You may also use the provided dashboard for detailed analysis. Customers with Advanced or Premium support plans may open a support ticket to request an environment assessment and recommendations for resource adjustments."
    - alert: SoftQuotaMemoryLimitReached
      expr: |
        sum by (namespace) (
          kube_pod_container_resource_limits{resource="memory", unit="byte"}
        ) >= <total memory (GiB)> * 1024 * 1024 * 1024
      for: 7d
      labels:
        severity: info
        category: finops
        resource: memory
      annotations:
        summary: "The total Memory limits of the project have reached or exceeded the configured threshold"
        description: "The desired Memory allocation for this project has reached or exceeded the recommended threshold. This indicates that the configured resource limits may no longer be optimal for the current workload profile. It is recommended to review the Memory limits defined in Deployments, StatefulSets, and Pods. You may also use the provided dashboard for detailed analysis. Customers with Advanced or Premium support plans may open a support ticket to request an environment assessment and recommendations for resource adjustments."

Monitorar a subutilização

Quando um alerta de subutilização dispara, os cenários mais prováveis são:

  • O limite está maior do que o necessário;
  • O ambiente mudou e os valores não foram atualizados;
  • O workload está ocioso ou com carga reduzida;
  • Há uma oportunidade real de downgrade de limits e economia financeira.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: softquota-low-usage-alerts
  namespace: <namespace>
spec:
  groups:
    - name: finops-soft-low-usage-namespace
      rules:
        - alert: soft-low-usage-alert-cpu-namespace
          expr: |
            (
              avg_over_time(
                (
                  (
                    sum by (namespace) (
                      rate(container_cpu_usage_seconds_total{namespace="<namespace>, container!=""}[5m])
                    )
                  )
                  /
                  (
                    sum by (namespace) (
                      kube_pod_container_resource_limits{namespace="<namespace>", resource="cpu", unit="core"}
                    )
                  )
                )[30d:]
              ) < 0.30
            )
            and
            (
              quantile_over_time(
                0.95,
                (
                  (
                    sum by (namespace) (
                      rate(container_cpu_usage_seconds_total{namespace="<namespace>", container!=""}[5m])
                    )
                  )
                  /
                  (
                    sum by (namespace) (
                      kube_pod_container_resource_limits{namespace="<namespace>", resource="cpu", unit="core"}
                    )
                  )
                )[30d:]
              ) < 0.50
            )
          for: 1h
          labels:
            severity: info
            category: finops
            resource: cpu
          annotations:
            summary: "CPU underutilization in namespace over 30 days"
            description: "30-day average usage/limit < 30% and 30-day p95 < 50%. Indicates overprovisioning and a clear opportunity to reduce limits. Review Deployments/StatefulSets and consider limits downgrade to optimize costs."

        - alert: soft-low-usage-alert-memory-namespace
          expr: |
            (
              avg_over_time(
                (
                  (
                    sum by (namespace) (
                      container_memory_working_set_bytes{namespace="<namespace>", container!=""}
                    )
                  )
                  /
                  (
                    sum by (namespace) (
                      kube_pod_container_resource_limits{namespace="<namespace>", resource="memory", unit="byte"}
                    )
                  )
                )[30d:]
              ) < 0.35
            )
            and
            (
              quantile_over_time(
                0.95,
                (
                  (
                    sum by (namespace) (
                      container_memory_working_set_bytes{namespace="<namespace>", container!=""}
                    )
                  )
                  /
                  (
                    sum by (namespace) (
                      kube_pod_container_resource_limits{namespace="<namespace>", resource="memory", unit="byte"}
                    )
                  )
                )[30d:]
              ) < 0.55
            )
          for: 1h
          labels:
            severity: info
            category: finops
            resource: memory
          annotations:
            summary: "Memory underutilization in namespace over 30 days"
            description: "30-day average usage/limit < 30% and 30-day p95 < 50%. Indicates overprovisioning and a clear opportunity to reduce limits. Review Deployments/StatefulSets and consider limits downgrade to optimize costs."

Visualização de alertas

Escalonamento programado: Quando o HPA não é suficiente

Ambientes “não-produtivos” (ex: homologação, staging, UAT, desenvolvimento) geralmente não precisam ficar ligados o dia inteiro, mas acabam consumindo recursos como se fossem produção — e aí o custo sobe sem necessidade. Como o HPA não escala workloads para zero e reage apenas baseado no uso de recurso, ele não resolve esse tipo de cenário. É aqui que entra o escalonamento programado: um CronJob simples que “liga e desliga” seus Deployments na hora certa, evitando que workloads ociosos rodem 24×7 e garantindo economia real no RTM OpenShift Autopilot.

Criando um ServiceAccount

Para executar workloads de automação, o ideal é usar um ServiceAccount. Rodar esse tipo de tarefa com usuários comuns não só é arriscado, como também dificulta o controle de permissões e auditoria. Com um ServiceAccount, você garante que o job tenha apenas os acessos necessários e mantém o ambiente mais seguro e previsível.

oc create serviceaccount sa-scheduler-scaler -n <namespace>

Definindo permissões (Role & RoleBinding)

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: scheduler-scaler-role
  namespace: <namespace>
rules:
- apiGroups: ["apps"]
  resources: ["deployments","deployments/scale"]
  verbs: ["get", "update", "patch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: scheduler-scaler-rolebinding
  namespace: <namespace>
subjects:
- kind: ServiceAccount
  name: sa-scheduler-scaler
  namespace: <namespace>
roleRef:
  kind: Role
  name: scheduler-scaler-role
  apiGroup: rbac.authorization.k8s.io

Agendando o escalonamento

apiVersion: batch/v1
kind: CronJob
metadata:
  name: scale-up
  namespace: <namespace>
spec:
  schedule: "0 8 * * *" # todos os dias às 08:00 da manhã.
  timeZone: "America/Sao_Paulo"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 0
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: sa-scheduler-scaler
          containers:
          - name: scaler
            image: registry.redhat.io/openshift4/ose-cli
            command:
            - /bin/sh
            - -c
            - oc scale deployment httpd-deployment --replicas=<qtd.replicas> -n <namespace>
          restartPolicy: Never
apiVersion: batch/v1
kind: CronJob
metadata:
  name: scale-down
  namespace: <namespace>
spec:
  schedule: "0 20 * * *" # todos os dias às 20:00 da noite.
  timeZone: "America/Sao_Paulo"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 0
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: sa-scheduler-scaler
          containers:
          - name: scaler
            image: registry.redhat.io/openshift4/ose-cli
            command:
            - /bin/sh
            - -c
            - oc scale deployment httpd-deployment --replicas=0 -n <namespace>
          restartPolicy: Never

Bonus Track: Hora de falar de Storage

Por mais que CPU e memória sejam os recursos que mais preocupam no RTM OpenShift Autopilot — e, na verdade, em qualquer ambiente de containers — o armazenamento também merece bastante atenção. No Autopilot, os PV/PVC são cobrados pelo tamanho provisionado do volume, que funciona como o “limit” do storage: é a capacidade declarada que aquele volume pode suportar.

Quando falamos de FinOps voltado para storage, existem dois pontos essenciais de monitorar para manter o custo sob controle. O primeiro é a existência de volumes sem vínculo, que são PVs que já não estão mais associados a nenhum Pod, Deployment ou StatefulSet, mas continuam provisionados e gerando cobrança. O segundo ponto é o superdimensionamento: volumes criados com muito mais capacidade do que o necessário — algo comum em projetos que ainda estão amadurecendo ou quando o planejamento inicial não reflete a realidade atual do uso.

Monitorando volumes sem vínculo

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: unlinked-pvc-alerts
  namespace: <namespace>
spec:
  groups:
  - name: storage.finops.pvc
    rules:
    - alert: UnlinkedPersistentVolumeClaim
      expr: |
        (
          kube_persistentvolumeclaim_status_phase{namespace="<namespace>", phase="Bound"} == 1
        )
        unless on(namespace, persistentvolumeclaim)
        (
          kube_pod_spec_volumes_persistentvolumeclaims_info{namespace="<namespace>"}
          and on(namespace, pod)
          kube_pod_status_phase{namespace="<namespace>", phase=~"Pending|Running|Unknown"} == 1
        )
      for: 7d
      labels:
        severity: info
        category: finops
        resource: storage
      annotations:
        summary: "Unlinked PVC detected (Used by: none)"
        description: "The PersistentVolumeClaim '{{ $labels.persistentvolumeclaim }}' in namespace is in the Bound phase but is not linked to any active Pod (Used by: none). This indicates potential unused storage cost."

Monitorando volumes subutilizados

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: low-usage-alert-pvc
  namespace: <namespace>
spec:
  groups:
  - name: storage.finops.pvc
    rules:
    - alert: LowUsagePersistentVolumeClaim
      expr: |
        (
          (
            kubelet_volume_stats_used_bytes{namespace="<namespace>"}
            /
            kubelet_volume_stats_capacity_bytes{namespace="<namespace>"}
          ) < 0.5
        )
        and on (namespace, persistentvolumeclaim)
        (
          kube_pod_spec_volumes_persistentvolumeclaims_info{namespace="<namespace>"}
          * on (namespace, pod) group_left
          max by (namespace, pod) (
            kube_pod_status_phase{namespace="<namespace>", phase=~"Running|Pending"} == 1
          )
        )
      for: 30d
      labels:
        severity: info
        category: finops
        resource: storage
      annotations:
        summary: "Low-usage PVC detected (<50%)"
        description: >-
          The PersistentVolumeClaim '{{ $labels.persistentvolumeclaim }}' in namespace '{{ $labels.namespace }}'
          has sustained utilization below 50% while being linked to active Pod(s).

Sumário