Insiden produksi terjadi pada layanan parser kompilator berbasis binari native FreeOberon yang dijalankan di cluster Kubernetes. Pod mengalami siklus CrashLoopBackOff seketika setelah release container image baru. Analisis runtime menunjukkan pod gagal start dengan kode keluar kontainer yang mengindikasikan masalah dynamic linking tingkat rendah.

1. Gejala Kegagalan: Exit Code 127 dan 139

Identifikasi awal dilakukan melalui kubectl describe pod dan inspeksi status container. Ditemukan dua gejala primer pada deployment:

  • Exit Code 127: Pesan log kontainer menampilkan exec /usr/local/bin/oberon-runner: no such file or directory. Path file binari valid di dalam image, namun kernel gagal memuat interpreter ELF dynamic loader (/lib64/ld-linux-x86-64.so.2) yang tidak tersedia di filesystem target.
  • Exit Code 139: Terjadi pada varian image glibc-compat ketika dynamic linker memaksakan resolusi symbol ke library runtime yang memiliki struct padding atau calling convention berbeda, memicu Segmentation Fault (SIGSEGV).

Observabilitas: Probe, Metrics, dan Dump Collector

Deteksi otomatis mengandalkan metrik ekspor kube-state-metrics dan probe liveness TCP/exec. Jika binari mati sebelum bind network socket, liveness probe memicu restart cepat.

# Query Prometheus untuk restart loop dalam 5 menit
rate(kube_pod_container_status_restarts_total{container="oberon-runner"}[5m]) > 0

Pengumpulan stderr dan core dump dikonfigurasi melalui shared volume dan pengaturan kernel core_pattern pada host level untuk membaca crash state:

apiVersion: v1
kind: Pod
metadata:
  name: oberon-debug
spec:
  containers:
  - name: oberon-runner
    image: registry.internal/oberon-runner:v1.4.2
    command: ["/bin/sh", "-c"]
    args:
      - ulimit -c unlimited; /usr/local/bin/oberon-runner 2>> /var/log/oberon/stderr.log
    volumeMounts:
    - name: crash-dumps
      mountPath: /var/log/oberon
  volumes:
  - name: crash-dumps
    emptyDir: {}

2. Automated Canary Rollback via Argo Rollouts

Deployment service menggunakan CRD Rollout. Evaluasi metrics dilakukan saat fasa canary. Analisis kegagalan pod langsung membatalkan deployment otomatis sebelum trafik dialihkan ke versi baru.

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: oberon-runner
spec:
  replicas: 10
  strategy:
    canary:
      analysis:
        templates:
        - templateName: pod-restart-analysis
        args:
        - name: service-name
          value: oberon-runner
      steps:
      - setWeight: 10
      - pause: {duration: 60s}
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: pod-restart-analysis
spec:
  metrics:
  - name: restart-rate
    interval: 15s
    failureLimit: 1
    provider:
      prometheus:
        address: http://prometheus-k8s.monitoring:9090
        query: |
          sum(rate(kube_pod_container_status_restarts_total{container="oberon-runner"}[1m])) > 0

Argo Rollouts menghentikan step canary, menetapkan status Degraded, dan mengembalikan routing trafik sepenuhnya ke revision Stable (Rollback).

3. Root Cause Analysis (RCA)

Penyebab utama insiden adalah ABI Mismatch antara build runner dan container runtime:

  • Kompiler FreeOberon dikompilasi pada host Linux (Ubuntu CI runner) menggunakan gcc yang di-link secara dinamis terhadap glibc (GNU C Library).
  • Container image target berbasis alpine:3.19 yang menggunakan musl libc sebagai implementasi library sistem default.
  • Binary format ELF 64-bit menyertakan header PT_INTERP yang menunjuk ke dynamic linker glibc. Kernel Linux pada kontainer Alpine mencari path tersebut, gagal menemukannya, dan menghasilkan error misleading no such file or directory (Exit Code 127).

4. Solusi dan Pencegahan

A. Multi-stage Dockerfile untuk Fully Static Compilation

Gunakan compiler toolchain yang mendukung flag -static untuk mengeliminasi ketergantungan runtime shared object.

# Stage 1: Build binary statically
FROM alpine:3.19 AS builder
RUN apk add --no-cache build-base git
WORKDIR /src
COPY . .
# Kompilasi C source dari output transpile FreeOberon secara statis
RUN gcc -static -O2 -o oberon-runner main.c

# Stage 2: Minimal runtime image
FROM scratch
WORKDIR /app
COPY --from=builder /src/oberon-runner /app/oberon-runner
USER 65534:65534
ENTRYPOINT ["/app/oberon-runner"]

B. Verifikasi Binary via CI Pipeline

Tambahkan langkah inspeksi dynamic linking pada pipeline integrasi (GitHub Actions atau GitLab CI) sebelum container image di-push ke registry:

# Verifikasi binary statis di pipeline
file ./bin/oberon-runner | grep "statically linked" || {
  echo "Error: Binary bukan static binary!";
  exit 1;
}

# Pastikan ldd memberikan return non-zero atau not a dynamic executable
if ldd ./bin/oberon-runner 2>&1 | grep -E "not a dynamic executable|statically linked"; then
  echo "OK: Verifikasi dynamic linking lolos.";
else
  echo "Error: Terdeteksi dynamic link dependency:";
  ldd ./bin/oberon-runner;
  exit 1;
fi

C. Pre-deployment Smoke Test

Eksekusi binary di lingkungan runtime identik dalam CI job sebelum trigger CD rollout:

docker run --rm -i registry.internal/oberon-runner:local-test --version || exit 1

Uji eksekusi langsung mendeteksi ketiadaan loader dynamic library sebelum manifest Kubernetes diterapkan pada cluster.