ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

Home จำกัดขนาด Log ของ Docker Container

จำกัดขนาด Log ของ Docker Container

ทุกข้อความที่โปรแกรมใน Container พิมพ์ออกทาง stdout และ stderr จะถูก Docker เก็บไว้ ค่าเริ่มต้นของ Docker Engine บน Linux คือ Log Driver แบบ json-file ซึ่ง ไม่จำกัดขนาด ไฟล์จึงโตขึ้นเรื่อย ๆ Container ที่พิมพ์ Log เยอะ เช่น Web Server ที่มีผู้เข้าชมมาก หรือแอปที่เปิดโหมด Debug ทิ้งไว้ สร้างไฟล์ Log หลายสิบ GB ได้ภายในไม่กี่สัปดาห์ จนดิสก์เต็ม

บทความนี้แสดงวิธีหาว่า Container ใดมี Log ใหญ่ ล้างไฟล์ที่ใหญ่อยู่แล้วอย่างปลอดภัย และตั้งการหมุนเวียน Log (Log Rotation) ทั้งแบบค่าเริ่มต้นของทั้งเครื่องและแบบรายโปรเจกต์ใน Docker Compose

สิ่งที่ต้องเตรียม

  • Linux Server ที่ใช้ Docker Engine (ตัวอย่างนี้ทดสอบแนวทางกับ Ubuntu 22.04/24.04 และ AlmaLinux/Rocky Linux 8-9)
  • สิทธิ์ sudo
  • ช่วงเวลาที่รีสตาร์ต Docker และสร้าง Container ใหม่ได้ เพราะการเปลี่ยนค่า Log มีผลกับ Container ที่สร้างใหม่เท่านั้น

ขั้นตอนที่ 1: หา Container ที่มีไฟล์ Log ใหญ่

ไฟล์ Log ของ Driver json-file อยู่ที่ /var/lib/docker/containers/<ID>/<ID>-json.log ดูขนาดทั้งหมดเรียงจากเล็กไปใหญ่

sudo sh -c 'du -h /var/lib/docker/containers/*/*-json.log' | sort -h | tail
120M    /var/lib/docker/containers/3f2a.../3f2a...-json.log
8.4G    /var/lib/docker/containers/9c1d.../9c1d...-json.log

ชื่อโฟลเดอร์เป็น ID ของ Container ดูว่าเป็นตัวไหนด้วยคำสั่งนี้ ซึ่งแสดงชื่อ Container คู่กับ path ของไฟล์ Log

docker ps -aq | xargs docker inspect --format '{{.Name}} {{.LogPath}}'

ตรวจว่า Container แต่ละตัวใช้ Log Driver และตัวเลือกใดอยู่

docker inspect --format '{{.HostConfig.LogConfig}}' web

ถ้าได้ {json-file map[]} แปลว่าไม่มีการจำกัดขนาด

ขั้นตอนที่ 2: ล้างไฟล์ Log ที่ใหญ่อยู่แล้ว

ถ้าดิสก์ใกล้เต็ม ให้ล้างไฟล์ก่อนเพื่อคืนพื้นที่ทันที ใช้ truncate เพื่อทำให้ไฟล์ว่างเปล่าโดยไม่ลบไฟล์ทิ้ง

sudo truncate -s 0 "$(docker inspect --format '{{.LogPath}}' web)"

หมายเหตุ: Log ที่ถูกล้างจะหายถาวร ถ้าต้องใช้เป็นหลักฐานหรือหาสาเหตุปัญหา ให้คัดลอกเก็บก่อน เช่น docker logs --since 24h web > web-last24h.log 2>&1 และห้ามใช้ rm ลบไฟล์ -json.log ขณะ Container ทำงาน เพราะ Docker ยังเปิดไฟล์ค้างไว้ พื้นที่จะไม่ถูกคืนจนกว่าจะรีสตาร์ต และคำสั่ง docker logs อาจใช้งานไม่ได้

การล้างเป็นแค่การแก้เฉพาะหน้า ไฟล์จะโตกลับมาอีก ขั้นตอนถัดไปคือการแก้ที่ต้นเหตุ

ขั้นตอนที่ 3: ตั้งค่าเริ่มต้นให้ทั้งเครื่องใน daemon.json

แก้หรือสร้างไฟล์ /etc/docker/daemon.json (ถ้ามีไฟล์อยู่แล้ว ให้เพิ่มคีย์เข้าไปใน Object เดิม อย่าเขียนทับค่าอื่นที่ตั้งไว้)

sudo nano /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
  • max-size ขนาดสูงสุดของไฟล์ Log แต่ละไฟล์ ก่อนเริ่มไฟล์ใหม่ (หน่วย k, m, g)
  • max-file จำนวนไฟล์สูงสุดที่เก็บไว้ ไฟล์เก่าที่สุดจะถูกลบเมื่อครบ ตัวอย่างนี้จึงใช้พื้นที่ไม่เกินประมาณ 30 MB ต่อ Container
  • ค่าใน log-opts ต้องเป็นข้อความในเครื่องหมายคำพูด ทั้งหมด แม้จะเป็นตัวเลข เช่น "3" ถ้าเขียน 3 เฉย ๆ Docker จะเริ่มทำงานไม่ได้

ทางเลือก: Log Driver แบบ local

Driver local หมุนเวียนไฟล์และบีบอัดให้อัตโนมัติ โดยค่าเริ่มต้นเก็บไม่เกิน 5 ไฟล์ ไฟล์ละ 20 MB ใช้พื้นที่น้อยกว่า json-file แต่ไฟล์เป็นรูปแบบภายในของ Docker อ่านผ่าน docker logs ได้ตามปกติ แต่เครื่องมือเก็บ Log ภายนอกที่อ่านไฟล์ JSON โดยตรงจะอ่านไม่ได้

{
  "log-driver": "local"
}
Driverจำกัดขนาดเองบีบอัดเหมาะกับ
json-file (ค่าเริ่มต้น)ไม่ ต้องตั้ง max-sizeได้ ถ้าตั้ง "compress": "true"ต้องการให้เครื่องมืออื่นอ่านไฟล์ JSON ได้
localใช่ (5 x 20 MB)ใช่เซิร์ฟเวอร์ทั่วไปที่ดู Log ด้วย docker logs
journaldตามการตั้งค่าของ journaldใช่ต้องการรวม Log กับ journalctl

ถ้าเลือก journald ขนาดรวมถูกควบคุมโดยค่า SystemMaxUse ของ journald ดูรายละเอียดที่ อ่าน Log ระบบบน Linux ด้วย journalctl

ขั้นตอนที่ 4: ตรวจไฟล์และรีสตาร์ต Docker

ตรวจความถูกต้องของไฟล์ก่อนรีสตาร์ต (Docker Engine 23.0 ขึ้นไป)

sudo dockerd --validate --config-file=/etc/docker/daemon.json

ผลที่ถูกต้องคือ configuration OK จากนั้นรีสตาร์ตบริการ

sudo systemctl restart docker

หมายเหตุ: การรีสตาร์ต Docker ทำให้ Container ทั้งหมดบนเครื่องหยุดและเริ่มใหม่ (ยกเว้นเปิด live-restore ไว้) เว็บไซต์จะหยุดให้บริการชั่วครู่ ควรทำในช่วงที่มีผู้ใช้น้อย ถ้า Docker เริ่มไม่ขึ้นหลังรีสตาร์ต ให้ดูสาเหตุด้วย sudo journalctl -u docker -n 50 ซึ่งส่วนใหญ่เป็น JSON ผิดรูปแบบ

ขั้นตอนที่ 5: สร้าง Container ใหม่ให้ใช้ค่าใหม่

ค่าใน daemon.json ใช้กับ Container ที่ สร้างใหม่ เท่านั้น Container เดิมยังใช้ค่าตอนที่ถูกสร้าง แม้จะรีสตาร์ตแล้วก็ตาม ต้องสร้างใหม่

สำหรับโปรเจกต์ Docker Compose

cd /opt/myapp
sudo docker compose up -d --force-recreate

สำหรับ Container ที่สร้างด้วย docker run ต้องสั่ง docker rm -f แล้วรันคำสั่งเดิมอีกครั้ง ข้อมูลใน Volume ยังอยู่ แต่ไฟล์ที่เขียนไว้ใน Container นอก Volume จะหาย ตรวจด้วย docker inspect ก่อนว่าข้อมูลสำคัญอยู่ใน Volume แล้ว

ทางเลือก: กำหนดรายโปรเจกต์ใน compose.yaml

ถ้าไม่ต้องการเปลี่ยนค่าทั้งเครื่อง หรือต้องการให้บาง Service เก็บ Log มากกว่าตัวอื่น กำหนดใน Compose ได้ ค่าที่นี่จะแทนค่าจาก daemon.json

x-logging: &default-logging
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

services:
  web:
    image: nginx:1.27
    logging: *default-logging

  app:
    image: myapp:1.0
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"

ส่วน x-logging กับ &default-logging เป็น YAML Anchor ช่วยให้ไม่ต้องพิมพ์ค่าซ้ำทุก Service จากนั้นสั่ง sudo docker compose up -d Compose จะสร้าง Container ที่ค่าเปลี่ยนใหม่ให้เอง สำหรับ docker run ใช้ตัวเลือก --log-opt max-size=10m --log-opt max-file=3

ตรวจสอบผลลัพธ์

docker inspect --format '{{.HostConfig.LogConfig}}' web

ต้องได้ {json-file map[max-file:3 max-size:10m]} (หรือ {local map[]} ถ้าใช้ Driver local) หลังจากใช้งานไปสักพัก ตรวจว่ามีไฟล์หมุนเวียนเกิดขึ้น

sudo sh -c 'ls -lh /var/lib/docker/containers/*/*-json.log*'

จะเห็นไฟล์ -json.log, -json.log.1 และ -json.log.2 ขนาดไม่เกิน 10 MB ต่อไฟล์

ปัญหาที่พบบ่อย

Docker ไม่ยอมเริ่มหลังแก้ daemon.json

JSON ผิดรูปแบบ เช่น ลืมเครื่องหมายจุลภาค มีจุลภาคเกินท้ายรายการ หรือเขียนตัวเลขไม่มีเครื่องหมายคำพูด ใช้ dockerd --validate หรือ python3 -m json.tool /etc/docker/daemon.json หาบรรทัดที่ผิด

ตั้งค่าแล้วแต่ไฟล์ Log ยังโตเกินกำหนด

Container ยังไม่ได้ถูกสร้างใหม่ ตรวจด้วย docker inspect ตามหัวข้อตรวจสอบผลลัพธ์ แล้วสร้างใหม่ตามขั้นตอนที่ 5

docker logs แสดง Log ย้อนหลังได้ไม่มาก

เป็นผลปกติของการจำกัดขนาด ถ้าต้องการเก็บ Log นานขึ้นเพื่อการตรวจสอบ ให้เพิ่ม max-file หรือส่ง Log ไปยังระบบรวม Log ภายนอก แทนการปล่อยไฟล์โตไม่จำกัด

Log ของแอปที่เขียนลงไฟล์ภายใน Container ยังโต

การตั้งค่านี้ควบคุมเฉพาะ stdout และ stderr ถ้าแอปเขียน Log ลงไฟล์ใน Volume หรือ Bind Mount เอง ต้องใช้ logrotate บนเครื่อง ดู ตั้งค่า logrotate ไม่ให้ไฟล์ Log เต็มดิสก์

หากดิสก์ยังเต็มจากสาเหตุอื่น ดูต่อที่ คืนพื้นที่ดิสก์ที่ Docker ใช้ด้วย docker system prune หรือติดต่อทีมงาน THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security