← กลับ
KubernetesLoggingDebuggingDevOps

วิธีอ่าน Log ใน Kubernetes ให้เจอ Root Cause เร็วขึ้น

log เยอะจน scroll ไม่ทัน หลายไฟล์หลาย pod — เทคนิคการ filter, follow, structured logging เพื่อหาบรรทัดที่บอก root cause ภายใน 1 นาที

2026-05-03อ่าน 6 นาทีใหม่

ปัญหาที่ทุกคนเจอ

Pod พัง — kubectl logs ออก 50,000 บรรทัด ใช้เวลา 30 นาที scroll หา error

ปัญหา 90% ของการ debug ช้า:

  • ใช้ kubectl logs แบบไม่มี filter
  • อ่าน log ของ pod ที่ผิดเครื่อง
  • log ไม่มีรูปแบบ — grep ไม่เจอ
  • ไม่รู้ว่าควรดู --previous หรือไม่

คำสั่งพื้นฐานที่ใช้ให้คล่อง

Log ปัจจุบัน

kubectl logs <pod>

Container ที่ crash ไปแล้ว

kubectl logs <pod> --previous

--previous (alias -p) = log ของ container instance ก่อนหน้า — สำคัญมากตอน CrashLoopBackOff

Follow real-time

kubectl logs -f <pod>

Tail หลังสุด N บรรทัด

kubectl logs --tail=200 <pod>

Since เวลาที่กำหนด

# ตั้งแต่ 5 นาทีที่แล้ว
kubectl logs --since=5m <pod>

# ตั้งแต่ timestamp
kubectl logs --since-time=2026-05-03T10:00:00Z <pod>

Multi-container pod

kubectl logs <pod> -c <container-name>

# log ทุก container
kubectl logs <pod> --all-containers=true

Pod หลายตัวที่ label เดียว

kubectl logs -l app=myapp --tail=50

Timestamp prefix

kubectl logs --timestamps <pod>

ผลลัพธ์:

2026-05-03T10:23:45.123Z INFO Server started
2026-05-03T10:23:46.231Z ERROR DB connection failed

เทคนิคหา Root Cause เร็วๆ

1. ลด noise ด้วย grep

# เฉพาะ ERROR
kubectl logs <pod> | grep -i error

# ERROR + 5 บรรทัดถัดไป (context)
kubectl logs <pod> | grep -A 5 -i error

# ERROR + 5 ก่อน + 5 หลัง
kubectl logs <pod> | grep -B 5 -A 5 -i error

# multiple pattern
kubectl logs <pod> | grep -iE "error|fail|exception|panic"

# exclude noise
kubectl logs <pod> | grep -v "health check"

2. หา error ตัวแรก

kubectl logs <pod> | grep -i error | head -1

มักเป็น root cause — error ตัวที่ 2-N เป็น cascade

3. ดูตอนรอบนี้ + รอบเก่า

# combine ดูทั้ง 2 รอบ
( kubectl logs <pod> --previous; kubectl logs <pod> ) \
  | grep -i error

4. Stream multi-pod พร้อมกัน

stern (install: brew install stern):

# log ของ pod ที่มี label app=myapp
stern -l app=myapp

# เฉพาะ container ชื่อ app
stern -l app=myapp -c app

# filter ด้วย regex
stern -l app=myapp --include "ERROR|WARN"

# exclude
stern -l app=myapp --exclude "health"

# ตั้งแต่ 5 นาที
stern -l app=myapp --since 5m

stern color-code log ของแต่ละ pod คนละสี — debug หลาย pod พร้อมกันสะดวกกว่า kubectl

5. ดูเหตุการณ์ก่อน pod crash

# รวม events + log
kubectl describe pod <pod> | grep -A 10 Events
kubectl logs <pod> --previous --tail=50

Structured Logging — เปลี่ยน game

ถ้า log เป็น JSON — query ด้วย jq ได้:

// app log JSON
import pino from 'pino'
const logger = pino()

logger.info({ user_id: 123, action: 'login' }, 'user logged in')
logger.error({ err, query: 'SELECT...' }, 'DB query failed')
# เฉพาะ level error
kubectl logs <pod> | jq 'select(.level == "error")'

# error ของ user เฉพาะ
kubectl logs <pod> | jq 'select(.user_id == 123 and .level == "error")'

# count error per route
kubectl logs <pod> --tail=10000 \
  | jq -r 'select(.level == "error") | .route' \
  | sort | uniq -c | sort -rn

หา request ID ตามไปทุก service

ใส่ correlation ID ใน log:

// middleware
import { v4 as uuidv4 } from 'uuid'
import { AsyncLocalStorage } from 'async_hooks'

const asyncStorage = new AsyncLocalStorage<{ requestId: string }>()

app.use((req, res, next) => {
  const requestId = req.headers['x-request-id'] ?? uuidv4()
  asyncStorage.run({ requestId }, () => next())
})

// logger middleware
const logger = pino({
  mixin() {
    const store = asyncStorage.getStore()
    return store ? { request_id: store.requestId } : {}
  },
})

ใน upstream API — forward header:

fetch('https://api.com/...', {
  headers: { 'x-request-id': asyncStorage.getStore()?.requestId },
})

หา trail ของ request เดียวกันข้าม service:

# ใน service A
stern -l app=service-a | grep "request_id\":\"abc-123"

# ใน service B
stern -l app=service-b | grep "request_id\":\"abc-123"

Log Levels — ใช้ให้ถูก

| Level | ใช้กับ | |---|---| | error | เหตุที่ต้องสนใจทันที (alert) | | warn | ผิดปกติแต่ไม่ critical (deprecated, retry) | | info | event สำคัญ (login, payment) | | debug | รายละเอียด — production ปิด | | trace | ทุกอย่าง — local เท่านั้น |

production แนะนำ info หรือ warn — ไม่ใช่ debug

ตั้งผ่าน env:

env:
  - name: LOG_LEVEL
    value: "info"

อย่า log อะไรบ้าง

⚠️ ห้าม:

  • Password
  • Credit card / CVV
  • API key / token
  • ข้อมูล PII ที่ PDPA / GDPR ห้าม

ใช้ redact:

const logger = pino({
  redact: {
    paths: ['password', 'token', 'authorization', 'creditCard.cvv', '*.password'],
    censor: '[REDACTED]',
  },
})

Centralized Logging — ทางออกถาวร

kubectl logs ใช้ debug realtime — แต่ log หายเมื่อ pod ลบ

ส่งไปเก็บ central:

  • Loki + Grafana — light, ฟรี, query ด้วย LogQL
  • ELK (Elasticsearch + Kibana) — full search
  • Datadog / Splunk — paid SaaS

ดู Centralized Logging ด้วย Loki

ใน Grafana ค้นหา:

{namespace="production",app="myapp"} |= "error"

หาได้ทุก pod ทุกชั่วโมงที่ผ่านมา — แม้ pod ลบไปแล้ว

Tools ที่ทุกคนควรลง

# stern — multi-pod log
brew install stern

# k9s — TUI ดู resource + log
brew install k9s

# kubectl-tail — alternative ของ stern
kubectl krew install tail

# jq — JSON query
brew install jq

# yq — YAML query
brew install yq

# Lnav — interactive log viewer
brew install lnav

Cheat Sheet เวลา debug ด่วน

"Application down"

# 1. ดู pod ที่ผิดปกติ
kubectl get pods -A | grep -v Running | grep -v Completed

# 2. log ของ pod ที่ crash (--previous)
kubectl logs <bad-pod> --previous --tail=50

# 3. หา error
kubectl logs <bad-pod> --previous | grep -iE "error|fail|panic" | head -5

# 4. event ก่อน crash
kubectl describe pod <bad-pod> | grep -A 10 Events

"Slow response"

# 1. log ของ app
stern -l app=myapp --since 10m | grep -iE "slow|timeout"

# 2. ดู resource
kubectl top pods -A --sort-by=memory

# 3. ดู readiness probe fail
kubectl describe pod -l app=myapp | grep -A 5 readiness

"5xx spike"

# log nginx ingress
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx \
  --tail=500 | awk '$9 ~ /^5/'

# หรือใน Loki
{namespace="ingress-nginx"} | json | status =~ "5.."

"DB query slow"

# Postgres log
kubectl logs <postgres-pod> --tail=200 | grep "duration:"

# กรองเฉพาะ > 1000ms
kubectl logs <postgres-pod> --tail=10000 \
  | grep -oP 'duration: \d+\.\d+' \
  | awk '$2 > 1000'

Lessons Learned

  1. อ่าน error แรกก่อน — error ตัวถัดมามักเป็น cascade
  2. --previous คือเพื่อนที่ดี — ตอน CrashLoopBackOff
  3. JSON log + jq = debug เร็วกว่า text log 5 เท่า
  4. stern > kubectl logs สำหรับ multi-pod
  5. Correlation ID ทุก request — debug cross-service ได้ใน 1 minutes
  6. ส่ง log ไป Loki — ไม่หาย ไม่ขึ้นกับ pod lifecycle

ก่อน publish app ใหม่ ตั้งให้ครบ

  • [ ] Structured log (JSON)
  • [ ] Log level config ผ่าน env
  • [ ] Redact sensitive data
  • [ ] Correlation/request ID
  • [ ] Health endpoint logged
  • [ ] Centralized log shipping (Promtail/Vector)
  • [ ] Alert rule บน error rate

สรุป

debug log ที่ดี = ฝีมือ + tool

ใช้:

  • kubectl logs --previous ตอน crash
  • grep -A 5 error หา context
  • stern หลาย pod
  • jq สำหรับ structured log
  • centralized (Loki) สำหรับ history

structured logging + correlation ID ใส่ตั้งแต่ project เริ่ม — debug 6 เดือนข้างหน้าขอบคุณตัวเอง

อ่านต่อ:

← ดูบทความอื่น