Buzeli
buzeliSoluções Digitais
Kubernetes

Fluentd DaemonSet no OKE ARM64: 8 erros em sequência até os logs chegarem no OCI Logging

Publicado em 17 de abril de 2026

Fluentd DaemonSet no OKE ARM64: 8 erros em sequência resolvidos para enviar logs ao OCI Logging

O objetivo: pods Next.js → OCI Logging Service

Um cluster OKE (Oracle Kubernetes Engine) com nodes ARM64 (A1.Flex Ampere) precisava enviar os logs de erro dos pods de aplicação para o OCI Logging Service. A aplicação já emitia logs estruturados via stdout/stderr — o trabalho era capturar esses logs com Fluentd, filtrar pelos pods corretos e ingerir no OCI Logging via HTTPS autenticado por instance principal.

A arquitetura planejada era direta:

Copiar
Pod (console.error → stdout/stderr)
  → CRI-O (grava em /var/log/containers/*.log)
    → Fluentd DaemonSet (tail + filter + match)
      → OCI Logging Service (via HTTPS, auth: instance principal)

O que não estava no plano eram os 8 erros entre o kubectl apply e o primeiro log confirmado no OCI Logging.

Erro 1: imagem do OCI Container Registry exige autenticação

A documentação oficial do OCI Logging sugeria usar a imagem do próprio Oracle Container Registry:

Copiar
container-registry.oracle.com/oci_logging/fluentbit

Ao tentar fazer pull desta imagem nos nodes OKE, o kubelet retornava erro de autenticação. O Oracle Container Registry requer login mesmo para imagens públicas — e configurar ImagePullSecrets para um registry externo em todos os nodes do cluster não era o caminho.

Fix: usar a imagem oficial do Fluent Bit no Docker Hub público, que não requer autenticação:

Copiar
docker.io/fluent/fluent-bit:3.2

Erro 2: CRI-O rejeita nomes de imagem curtos

Mesmo com a imagem trocada para fluent/fluent-bit:3.2, o kubelet rejeitava com:

Copiar
short name mode is enforcing, but image name "fluent/fluent-bit:3.2"
returns ambiguous list of candidates

O Oracle Linux 8.10 que roda nos nodes OKE tem o CRI-O configurado em modo short name enforcing — todos os nomes de imagem precisam ser fully qualified com o registry explícito.

Fix: prefixar com docker.io/ em todos os nomes de imagem do manifest:

Copiar
image: docker.io/fluent/fluent-bit:3.2

Erro 3: Fluent Bit 3.2 não tem plugin oci_logging

Com o pod subindo, os logs mostravam que o Fluent Bit não conseguia carregar o output plugin necessário para enviar ao OCI Logging Service. O plugin oci_logging não existe no Fluent Bit — ele existe apenas na gem Ruby do Fluentd.

Fix: migrar de Fluent Bit para Fluentd com a gem fluent-plugin-oci-logging. Isso exigiu construir uma imagem customizada baseada no Fluentd oficial:

Copiar
FROM docker.io/fluent/fluentd:v1.17-debian-1
# gems instaladas nas próximas etapas...

Erro 4: build falhou — sem toolchain na imagem base

A imagem base do Fluentd (debian) não inclui compilador C. A instalação da gem fluent-plugin-oci-logging falhou na etapa de compilação de extensões nativas:

Copiar
make: g++: No such file or directory
make[1]: *** [Makefile:234: <target>.o] Error 127

Fix: adicionar build-essential no Dockerfile antes da instalação das gems:

Copiar
FROM docker.io/fluent/fluentd:v1.17-debian-1

USER root
RUN apt-get update && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/*
RUN gem install fluent-plugin-oci-logging                 fluent-plugin-kubernetes_metadata_filter                 fluent-plugin-parser-cri

Erro 5: File.exists? removido no Ruby 3.2

Com as gems instaladas, o pod iniciava mas quebrava imediatamente com:

Copiar
NoMethodError: undefined method 'exists?' for File:Class
/usr/local/bundle/gems/fluent-plugin-oci-logging-1.0.12/lib/fluent/plugin/os.rb

O método File.exists? foi removido no Ruby 3.2 (deprecated desde Ruby 2.1). A gem fluent-plugin-oci-logging versão 1.0.12 ainda usava o método antigo e não havia sido atualizada.

Fix: patch inline no Dockerfile via sed:

Copiar
RUN sed -i 's/File\.exists?/File.exist?/g'     /usr/local/bundle/gems/fluent-plugin-oci-logging-*/lib/fluent/plugin/os.rb
Verificar em versões futuras da gem se File.exists? já foi corrigido upstream. O patch é necessário enquanto a gem não for atualizada.

Erro 6: Permission denied ao criar diretório de buffer

Pod subindo, gems funcionando — mas o Fluentd travava na inicialização com:

Copiar
Permission denied @ dir_s_mkdir - /var/log/fluentd-buffers

O DaemonSet estava configurado para rodar com o usuário padrão do Fluentd (não-root). O diretório /var/log no host pertence ao root e o usuário fluent não tinha permissão de escrita.

Fix: adicionar securityContext: runAsUser: 0 no DaemonSet e mover o buffer para /tmp/fluentd-buffers/:

Copiar
spec:
  template:
    spec:
      securityContext:
        runAsUser: 0
      containers:
        - name: fluentd
          # ...
          # no fluent.conf:
          # <buffer>
          #   path /tmp/fluentd-buffers/
          # </buffer>

Nota: DaemonSets de coleta de logs que precisam ler /var/log/containers/ no host tipicamente precisam rodar como root. Isso é esperado e documentado para coletores de log como Fluentd e Fluentbit.

Erro 7: 404 na ingestão — Dynamic Group com OCID errado

Com o pod rodando e sem erros de permissão, as tentativas de ingestão no OCI Logging falhavam com:

Copiar
NOT_AUTHORIZED_OR_NOT_FOUND (404) - Authorization failed or resource not found

O Fluentd usava autenticação por instance principal — os nodes OKE precisam estar em um Dynamic Group com política de acesso ao OCI Logging. O Dynamic Group havia sido criado com a seguinte regra:

Copiar
# Rule incorreta — usa tag que não existe nos nodes OKE
All {tag.Oracle-Tags.CreatedBy.value = '<ocid-do-cluster>'}

O problema: a tag Oracle-Tags.CreatedBy nos nodes OKE contém o OCID do nodepool, não do cluster. Nodes de clusters diferentes no mesmo compartment compartilham o mesmo OCID de cluster — mas os OCIDs de nodepool são únicos. Usar o OCID errado fazia o Dynamic Group não reconhecer os nodes.

Fix — opção mais simples e robusta: usar instance.compartment.id em vez de tags:

Copiar
# Rule correta — todos os nodes do compartment
All {instance.compartment.id = '<ocid-do-compartment>'}
Usar instance.compartment.id é mais simples e mais robusto que depender de tags de nodepool. Se o cluster for recriado ou o nodepool substituído, a regra continua válida. A desvantagem é a granularidade: todos os nodes do compartment entram no grupo — avaliar se isso é aceitável no ambiente.

Erro 8: bloco <auth> no config quebra a autenticação

Após corrigir o Dynamic Group, os logs de ingestão mostravam warnings sobre o bloco de autenticação no fluent.conf:

Copiar
[warn]: #0 unknown parameter 'auth' in <match>

O plugin fluent-plugin-oci-logging usa auto-detection do tipo de autenticação — ele detecta automaticamente se está rodando em uma instância OCI e usa instance principal. Adicionar um bloco <auth> explícito não só é desnecessário como interfere na autenticação automática.

Fix: remover completamente o bloco <auth> do fluent.conf. O plugin cuida da autenticação automaticamente via instance principal.

Copiar
<match kubernetes.**>
  @type oci_logging
  log_object_id <ocid-do-log>
  # <auth> — NÃO adicionar. O plugin usa auto-detection.
</match>

Resultado: DaemonSet Running, primeiro log em 11 segundos

Após resolver os 8 erros em sequência, o DaemonSet subiu nos 2 nodes ARM64 do cluster sem restart:

Copiar
kubectl get pods -n logging
# fluentd-a1b2c   1/1   Running   0   5m
# fluentd-d3e4f   1/1   Running   0   5m

O primeiro log confirmado no OCI Logging chegou 11 segundos após o pod ficar Running — exatamente o flush_interval de 10 segundos configurado no fluent.conf mais a latência de rede.

Copiar
# Primeiro log ingerido:
{
  "timestamp": "2026-04-16T18:21:23Z",
  "message": "Error: Loading chunk failed.",
  "stream": "stderr"
}
# Ingerido em: 2026-04-16T18:21:34Z  (+11s)

Resumo dos 8 erros e fixes

1. Registry requer auth → usar docker.io/fluent/fluentd:v1.17-debian-1 (Docker Hub público)

2. CRI-O rejeita short names → prefixar todos os nomes de imagem com docker.io/

3. Fluent Bit não tem oci_logging → migrar para Fluentd + gem fluent-plugin-oci-logging

4. Build sem toolchain → adicionar build-essential no Dockerfile

5. File.exists? removido no Ruby 3.2 → patch sed no Dockerfile

6. Permission denied no buffer → runAsUser: 0 + buffer em /tmp/fluentd-buffers/

7. Dynamic Group com OCID errado → usar instance.compartment.id em vez de tag de cluster/nodepool

8. Bloco <auth> interfere na autenticação → remover — plugin usa auto-detection de instance principal