Log Rotation และ Log Retention: จัดการ Disk Space สำหรับ Logs

Log files เป็นข้อมูลสำคัญที่ช่วยให้ทีม ops และ dev ติดตามปัญหา, debug, และเข้าใจพฤติกรรมของระบบ แต่ในขณะเดียวกันถ้าปล่อยให้ log โตขึ้นเรื่อย ๆ โดยไม่จัดการ อาจทำให้ disk เต็ม, service ล่ม, หรือ query ช้าจนใช้งานไม่ได้ การวาง policy เรื่อง log rotation และ retention จึงเป็นพื้นฐานของระบบ production ที่มั่นคง

บทความนี้จะอธิบายหลักการของ log rotation, การใช้งาน logrotate บน Linux, การกำหนด retention policy ที่เหมาะสม, และแนวทางการจัดการ log บนระบบ cloud และ containerized environment เพื่อให้ทีมสามารถควบคุม disk usage ได้อย่างมีประสิทธิภาพ

ทำไม Log Rotation ถึงสำคัญ

บริการอย่าง Nginx, MySQL, application logs สามารถสร้าง log ได้ปริมาณมากต่อวัน โดยเฉพาะเมื่อมี traffic สูงหรือ debug mode เปิดอยู่ ถ้าเขียนลงไฟล์เดียวต่อเนื่อง ผลที่ตามมาคือไฟล์ใหญ่เกิน GB ภายในไม่กี่วัน — กระทบทั้ง disk space, I/O performance, และความยากในการค้นหาข้อมูล

การ rotate คือการตัดไฟล์ log ปัจจุบันออกเป็น archive แล้วสร้างไฟล์ใหม่ให้ service เขียนต่อ วิธีนี้ช่วยให้แต่ละไฟล์มีขนาดจำกัด, สามารถ compress ไฟล์เก่าเพื่อประหยัด disk, และตั้งอายุการเก็บได้ชัดเจน เมื่อครบกำหนดก็ลบอัตโนมัติ — ทำให้ disk usage คงที่แม้บริการรันต่อเนื่องเป็นปี

logrotate — เครื่องมือมาตรฐานบน Linux

logrotate เป็น utility ที่ติดมากับ Linux distro เกือบทุกตัว ทำงานร่วมกับ cron/systemd timer เพื่อตรวจและ rotate log ตาม config ที่กำหนด ไฟล์ config หลักอยู่ที่ /etc/logrotate.conf และ config ย่อยของแต่ละบริการอยู่ใน /etc/logrotate.d/

ตัวอย่าง config สำหรับ rotate Nginx access log:

/var/log/nginx/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    create 0640 nginx adm
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}

ความหมายแต่ละ directive: daily rotate วันละครั้ง, rotate 14 เก็บย้อนหลัง 14 ไฟล์, compress บีบอัดไฟล์เก่าด้วย gzip, delaycompress เลื่อนการ compress ไปอีกรอบ (เผื่อ service ยังเขียนลงไฟล์เดิม), postrotate สั่งให้ Nginx reopen log file หลังจาก rotate เพื่อไม่ให้ process ค้างที่ file descriptor เก่า

Directive ที่ใช้บ่อย

  • hourly / daily / weekly / monthly — กำหนดความถี่ในการ rotate
  • size 100M — rotate เมื่อไฟล์ถึงขนาดที่กำหนด ไม่สนใจเวลา
  • maxsize 500M — rotate ทันทีเมื่อเกิน แม้ยังไม่ถึงกำหนดเวลา
  • rotate N — จำนวนไฟล์ที่จะเก็บย้อนหลัง ไฟล์เกินจะถูกลบ
  • maxage N — ลบไฟล์ที่เก่ากว่า N วัน แม้จำนวนยังไม่ถึง rotate limit
  • compress / compresscmd — บีบอัดไฟล์เก่า (default gzip, เปลี่ยนเป็น xz/zstd ได้)
  • copytruncate — copy ไฟล์แล้วทำให้ไฟล์เดิมว่าง เหมาะกับ service ที่ไม่รองรับ SIGHUP
  • sharedscripts — รัน postrotate เพียงครั้งเดียวแม้ match หลายไฟล์

copytruncate vs create

โหมด create (default) จะย้ายไฟล์เดิมไปเป็น archive แล้วสร้างไฟล์ใหม่ จากนั้นส่งสัญญาณให้ service เปิด file descriptor ใหม่ — วิธีนี้ปลอดภัย ไม่สูญเสีย log แต่ต้องให้ service รองรับ (เช่น SIGHUP หรือ reopen command)

ส่วน copytruncate เหมาะกับ application ที่ไม่สามารถสั่งให้ reopen log ได้ โดย logrotate จะ copy ไฟล์เดิมไปเป็น archive แล้ว truncate ไฟล์ต้นฉบับให้ว่าง ข้อเสียคืออาจมี log หายช่วงระหว่าง copy-to-truncate สั้น ๆ หากมีการเขียน log เข้มข้น จึงควรใช้เฉพาะเมื่อไม่มีทางเลือกอื่น

Log Retention Policy — ตั้งนานแค่ไหนดี

การกำหนด retention ควรพิจารณาจาก 3 ปัจจัย: requirement ทางกฎหมาย, operational need, และ storage cost ตัวอย่าง policy ที่ใช้กันทั่วไป:

  • Hot logs (7-14 วัน) — เก็บใน local disk หรือ ES cluster สำหรับ query แบบ real-time
  • Warm logs (30-90 วัน) — ย้ายไป storage ราคาถูก เช่น S3 Glacier, archive ES node
  • Cold/Audit logs (1-7 ปี) — compress หนักและเก็บใน object storage เฉพาะกรณี compliance

สำหรับธุรกิจที่อยู่ภายใต้ PDPA, GDPR, หรือ PCI-DSS ต้องเช็คเอกสารกำกับว่าต้องเก็บ audit log อย่างน้อยกี่ปี (เช่น PCI-DSS ต้องการอย่างน้อย 1 ปี โดย 3 เดือนล่าสุดต้องพร้อม query) — ออกแบบ retention ให้สอดคล้องกับข้อกำหนดเหล่านี้ก่อนเรื่องอื่น

Log ใน Container และ Kubernetes

ใน container environment การ rotate แบบ traditional ใช้ไม่ได้ตรง ๆ เพราะ container เขียน log ผ่าน stdout/stderr ไปยัง container runtime (Docker, containerd) ที่จัดการไฟล์เอง — ต้องตั้งค่าที่ระดับ runtime หรือ orchestrator แทน

สำหรับ Docker ตั้งค่าใน /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "5",
    "compress": "true"
  }
}

บน Kubernetes สามารถตั้ง kubelet flag --container-log-max-size และ --container-log-max-files เพื่อจำกัดขนาด log ต่อ container สำหรับ production ระดับใหญ่ ควร ship log ไป centralized system เช่น Loki หรือ Elasticsearch แล้วลบออกจาก node อย่างรวดเร็ว เพื่อไม่ให้ kubelet disk เต็ม

Monitoring Log Growth

แม้จะตั้ง rotation แล้ว ควร monitor disk usage ของ directory log อยู่เสมอ เพราะอาจมีกรณี debug mode ถูกเปิดโดยไม่ได้ตั้งใจ, traffic spike เกินคาด, หรือบาง service ไม่ respect postrotate signal ทำให้ไฟล์ไม่ถูกปิด

ใช้ Prometheus node_exporter ร่วมกับ alert rule เช่น node_filesystem_avail_bytes{mountpoint="/var/log"} / node_filesystem_size_bytes < 0.2 เพื่อเตือนเมื่อ partition log ใกล้เต็ม ก่อนจะกลายเป็น incident จริง

Best Practices

  • แยก partition /var/log ออกจาก root เพื่อไม่ให้ log เต็มทำระบบล่ม
  • ใช้ delaycompress เพื่อหลีกเลี่ยงปัญหา service เขียนลงไฟล์ที่กำลัง compress
  • ทดสอบ config ด้วย logrotate -d /etc/logrotate.d/nginx (dry run) ก่อนใช้จริง
  • Ship log สำคัญไปปลายทางส่วนกลาง (Loki/ES) ก่อน rotate ออก — ไม่พึ่ง local เป็น source of truth
  • กำหนด immutable audit log แยกจาก application log ที่ rotate ได้อิสระ
  • Monitor metric จำนวนไฟล์และขนาด directory log ผ่าน Prometheus หรือ Zabbix
  • ตรวจ postrotate script ว่าสำเร็จจริง — บาง service ต้องการ full restart ไม่ใช่แค่ SIGHUP

สรุป

Log rotation และ retention policy เป็นเรื่องพื้นฐานแต่สำคัญมากสำหรับการดูแลระบบ production — การปล่อยให้ไฟล์ log โตไม่จำกัดไม่ใช่แค่เปลืองพื้นที่ แต่ยังอาจทำให้ service ล่มและ query ช้าลงเมื่อต้องหาข้อมูลย้อนหลัง

logrotate เป็นเครื่องมือที่ลงตัวสำหรับ traditional server ส่วน container environment ต้องพึ่ง runtime-level config และ centralized logging การออกแบบ retention ให้ตรงกับความต้องการทางกฎหมายและ operational จะช่วยให้ทีมมีข้อมูลพอใช้โดยไม่สิ้นเปลือง resource