Restart Policy, Inspect และ Resource Limit
วัดบน Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64), 2026-09-30
Restart policy
หัวข้อที่มีชื่อว่า “Restart policy”โดยค่าเริ่มต้น container ที่หยุดหรือ crash จะหยุดค้างอยู่ restart policy บอก Docker ว่าเมื่อใดควร restart อัตโนมัติ แหล่งอ้างอิง: Start containers automatically และการวัดด้านล่าง
| Policy | พฤติกรรม |
|---|---|
no | ไม่ restart เลย (ค่าเริ่มต้น) |
always | restart ทุกครั้งที่ exit ถ้าคุณหยุดเองด้วยมือ มันจะหยุดค้างจนกว่า Docker daemon จะ restart (หรือคุณ start เอง) เริ่มตอน daemon boot |
unless-stopped | เหมือน always แต่ container ที่คุณหยุดเองจะหยุดค้างแม้ daemon restart แล้ว |
on-failure[:N] | restart เฉพาะเมื่อ exit code ไม่ใช่ศูนย์ ระบุจำนวนครั้งสูงสุดได้ ไม่ restart เมื่อ daemon restart |
# Recommended for long-running servicesdocker run -d --restart unless-stopped nginx:1.31-alpine
# Restart up to 5 times on failuredocker run -d --restart on-failure:5 myworker:latestทั้งสี่แบบ ที่สร้างขึ้นจริง
หัวข้อที่มีชื่อว่า “ทั้งสี่แบบ ที่สร้างขึ้นจริง”container ห้าตัว แต่ละตัวรันคำสั่งที่ exit ทันที ตรวจหลังผ่านไป 8 วินาทีด้วย docker inspect -f 'policy={{.HostConfig.RestartPolicy.Name}}:{{.HostConfig.RestartPolicy.MaximumRetryCount}} status={{.State.Status}} exit={{.State.ExitCode}} restarts={{.RestartCount}}':
sh -c 'exit 1' --restart no policy=no:0 status=exited exit=1 restarts=0sh -c 'exit 1' --restart on-failure:3 policy=on-failure:3 status=exited exit=1 restarts=3sh -c 'exit 1' --restart always policy=always:0 status=restarting exit=1 restarts=7sh -c 'exit 1' --restart unless-stopped policy=unless-stopped:0 status=restarting exit=1 restarts=7sh -c 'exit 0' --restart on-failure:3 policy=on-failure:3 status=exited exit=0 restarts=0noรันครั้งเดียวแล้วค้างที่exitedon-failure:3restart 3 ครั้งพอดี (RestartCount=3) แล้วยอมแพ้:exitedด้วย exit 1 เมื่อ exit code เป็น 0 มันไม่ restart เลยalwaysและunless-stoppedไม่มีวันยอมแพ้: หลัง 8 วินาทีอยู่ที่การ restart ครั้งที่ 7 statusrestartingช่วงห่างยาวขึ้นเรื่อย ๆ — Docker รอ 100 ms ก่อน restart ครั้งแรกและคูณสองทุกครั้ง สูงสุดหนึ่งนาที และรีเซ็ตเมื่อ container รันครบอย่างน้อย 10 วินาที (moby restartmanager) crash loop จึงถูกแต่มองไม่เห็น;docker psแสดงRestarting (1)
docker stop ทำอะไรกับ policy
หัวข้อที่มีชื่อว่า “docker stop ทำอะไรกับ policy”docker run -d --restart always alpine:3.24 sleep 300 ; docker stop -t 1 ... -> status=exited exit=137 (still exited 3 s later)docker run -d --restart unless-stopped alpine:3.24 sleep 300 ; docker stop -t 1 ... -> status=exited exit=137 (still exited 3 s later)ทั้งสองแบบไม่ restart container ที่คุณหยุดด้วยมือขณะที่ daemon ยังรันอยู่ ความต่างระหว่างสองแบบปรากฏเฉพาะเมื่อ daemon restart: always นำ container ที่หยุดด้วยมือกลับมา ส่วน unless-stopped ไม่ (ตามตารางในเอกสารข้างบน; การ restart daemon จะรบกวนทุก container บน host ที่ใช้ร่วมกันนี้ จึงไม่ได้ทำซ้ำ) ข้อความเก่าที่บอกว่า always restart “แม้หลัง docker stop ด้วยมือ” ผิดสำหรับ daemon ที่ยังรันอยู่
เปลี่ยน policy ของ container ที่มีอยู่
หัวข้อที่มีชื่อว่า “เปลี่ยน policy ของ container ที่มีอยู่”docker update --restart unless-stopped webdocker update --restart no webdocker update --restart no กับ container always ที่วน crash ทำให้ loop จบ: status=exited restarts=7 และตัวนับหยุดเพิ่ม
ตรวจ container
หัวข้อที่มีชื่อว่า “ตรวจ container”docker inspect คืน configuration JSON เต็มของ container — network settings, mounts, environment variable, restart policy และอื่น ๆ:
docker inspect webserverดึงฟิลด์เดียวด้วย --format ที่ใช้ไวยากรณ์ Go template:
# Get the container's IP address on each network it is attached todocker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webserver
# Get the restart policydocker inspect --format '{{.HostConfig.RestartPolicy.Name}}' webserver
# The image reference, status and commanddocker inspect --format '{{.Config.Image}} | {{.State.Status}} | {{json .Config.Cmd}}' webserveroutput จริงของคำสั่งเหล่านั้นกับ container ที่รัน sleep 60: 172.17.0.3, no และ alpine:3.24 | running | ["sleep","60"] {{json .X}} พิมพ์ค่าเป็น JSON (health check ที่ไม่มีพิมพ์ null)
--format เป็น Go template ดังนั้น path ที่ผิดคือ error ไม่ใช่สตริงว่าง Engine 29 ลบ field เครือข่ายระดับบนสุดแบบเก่าออกแล้ว:
$ docker inspect --format '{{.NetworkSettings.IPAddress}}' webtemplate parsing error: template: :1:18: executing "" at <.NetworkSettings.IPAddress>: map has no entry for key "IPAddress"บทเรียนเก่า ๆ ใช้ .NetworkSettings.IPAddress (และ .Gateway) ให้ใช้รูปแบบต่อ network ({{range .NetworkSettings.Networks}}…{{end}} หรือ {{.NetworkSettings.Networks.bridge.IPAddress}}) IP เป็นของ Desktop VM; จาก Mac ไม่สามารถ route ไปถึง
ดูทรัพยากรแบบสดด้วย docker stats
หัวข้อที่มีชื่อว่า “ดูทรัพยากรแบบสดด้วย docker stats”docker stats แสดงตารางที่อัปเดตสดของ CPU, memory, network I/O และ disk I/O สำหรับ container ที่รันอยู่:
# All running containersdocker stats
# Specific container, one snapshot (no live update)docker stats --no-stream webserveroutput จริงกับ container สองตัวที่รัน busy loop ตัวหนึ่งจำกัดครึ่ง CPU:
NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDSdocker-b1-cpu 51.91% 560KiB / 64MiB 0.85% 0B / 0B 0B / 0B 1docker-b1-cpu2 102.24% 560KiB / 64MiB 0.85% 0B / 0B 0B / 0B 1CPU % สัมพันธ์กับ หนึ่ง core (100% = หนึ่ง core; container ใช้เกิน 100% ได้เมื่อใช้หลาย core) container ที่ --cpus 0.5 อยู่ราว 50%; loop เธรดเดียวที่ไม่จำกัดอยู่ที่ 100% MEM % คือการใช้ต่อ limit, PIDS คือจำนวน process ใน cgroup ของ container NET I/O และ BLOCK I/O เป็นตัวนับสะสม ไม่ใช่อัตรา
Resource limit
หัวข้อที่มีชื่อว่า “Resource limit”container ใช้หน่วยความจำและ CPU ทั้งหมดที่มีได้ถ้าไม่จำกัด ตั้ง limit ตอนรัน:
docker run -d \ --name limited \ --memory 256m \ --cpus 0.5 \ nginx:1.31-alpine--memory 256m— เพดานหน่วยความจำแบบเด็ดขาด (container ถูก OOM-kill ถ้าเกิน)--cpus 0.5— ให้ container ใช้ CPU ได้มากที่สุดครึ่ง core
ตรวจว่า limit มีผล:
docker inspect --format 'Memory={{.HostConfig.Memory}} NanoCpus={{.HostConfig.NanoCpus}}' limited# Memory=268435456 NanoCpus=500000000 (256 * 1024 * 1024 bytes; 0.5 CPU = 500000000 units of 1e-9 CPU)--cpus 0.5 ถูกเก็บเป็น NanoCpus=500000000 และปรากฏใน cgroup เป็น cpu.max = 50000 100000 (CPU 50 ms ต่อคาบ 100 ms ดู containers-vs-vms)
การถูกฆ่าเพราะหน่วยความจำหมด (OOM)
หัวข้อที่มีชื่อว่า “การถูกฆ่าเพราะหน่วยความจำหมด (OOM)”process ที่เกิน --memory ถูกฆ่าโดย kernel ไม่ใช่ Docker เมื่อใช้ --memory 16m --memory-swap 16m กับ process Node ที่ allocate 100 MiB:
$ docker run --name oom --memory 16m --memory-swap 16m node:24-alpine node -e "Buffer.alloc(100*1024*1024,1)"$ echo $?137$ docker inspect -f 'OOMKilled={{.State.OOMKilled}} exit={{.State.ExitCode}} mem={{.HostConfig.Memory}}' oomOOMKilled=true exit=137 mem=16777216137 = 128 + 9 (SIGKILL) และ OOMKilled=true บอกว่า kernel เป็นผู้ทำ หากไม่ตั้ง --memory-swap เท่ากับ --memory container อาจใช้ swap ขนาดเท่ากันก่อนตายด้วย เมื่อจับคู่ --memory กับ restart policy คุณจะได้ crash loop ที่มีเหตุผลเป็น OOM ซึ่งเห็นได้จาก docker inspect เท่านั้น
# 1. Run nginx with a restart policy
docker run -d --name webserver --restart unless-stopped nginx:1.31-alpine
# 2. Inspect the full config (JSON)
docker inspect webserver | head -60
# 3. Extract just the restart policy
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' webserver
# 4. Live stats (one snapshot)
docker stats --no-stream webserver
# 5. Run a memory-limited container
docker run -d --name limited --memory 128m --cpus 0.25 nginx:1.31-alpine
# 6. Confirm the memory limit
docker inspect --format '{{.HostConfig.Memory}}' limited
# 7. Clean up
docker stop webserver limited && docker rm webserver limitedReading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”สคริปต์ monitoring ที่ AI เขียนเพื่อพิมพ์ว่าแต่ละ container อยู่ที่ไหน:
🐛 หา bug ในโค้ดนี้
#!/bin/sh# AI: "print the IP of every running container"for c in $(docker ps -q); doname=$(docker inspect --format '{{.Name}}' "$c")ip=$(docker inspect --format '{{.NetworkSettings.IPAddress}}' "$c")echo "$name $ip"doneเฉลย
.NetworkSettings.IPAddress ถูกลบออกจากระดับบนสุดของ docker inspect ใน Engine 29 สคริปต์จึงตายด้วย template error ที่ container แรก (ข้อความจริงข้างบน: map has no entry for key "IPAddress") บทเรียนและข้อมูลฝึกสอน AI รุ่นเก่ายังใช้มันอยู่ ให้ range ผ่าน network ซึ่งรองรับ container ที่อยู่หลาย network หรือไม่มีเลยด้วย:
#!/bin/shset -eufor c in $(docker ps -q); do docker inspect --format '{{.Name}} {{range $net, $cfg := .NetworkSettings.Networks}}{{$net}}={{$cfg.IPAddress}} {{end}}' "$c"doneรูปแบบ output: /web bridge=172.17.0.3 container ที่ start ด้วย --network host พิมพ์ host=invalid IP (ไม่มี address ของตัวเอง — แชร์ stack ของ engine host) และอย่าลืมว่า address เหล่านี้อยู่ภายใน Docker Desktop VM
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”restart policy คือ supervisor ตัวเล็กใน daemon: มันเฝ้าดู การ exit ของ PID 1 และถ้า policy สั่งและคุณไม่ได้หยุด container เอง ก็ start ใหม่หลังจากหน่วงเวลาที่เพิ่มขึ้นเรื่อย ๆ limit คือไฟล์ cgroup ที่ kernel บังคับใช้: เกิน memory หมายถึง SIGKILL (137, OOMKilled=true) เกิน CPU แค่ถูกทำให้ช้าลง docker inspect คือความจริง (HostConfig = สิ่งที่คุณขอ, State = สิ่งที่เกิดขึ้น); docker stats คือตัวนับสด
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ตัวเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
--restart always | รักษา container ที่ crash เองโดยไม่ต้องมีคนเฝ้า กลับมาหลัง daemon restart แม้คุณหยุดเอง | ซ่อน crash loop — container ที่ตายซ้ำ ๆ ดูเหมือน “ปกติ” เพราะ Docker restart ให้ตลอด (7 ครั้งใน 8 วินาทีที่นี่) |
--restart unless-stopped | รอดจาก reboot และ crash เคารพการหยุดที่ตั้งใจ | มองไม่เห็น crash loop เหมือนกัน |
--restart on-failure:N | จำกัดจำนวนครั้ง (3 ครั้งพอดีที่นี่) เลี่ยง loop ไม่รู้จบ เห็นปัญหาเร็วเพราะสุดท้ายมันหยุด | ถ้า N ต่ำเกินไป container อาจหยุดถาวรก่อนที่ปัญหาจริง (เช่น dependency ยังไม่พร้อม) จะหายเอง |
--memory | memory leak ไม่ทำให้ host ล่ม | แอปถูกฆ่ากะทันหัน (137) เมื่อถึง limit |
--cpus | แบ่งปันอย่างเป็นธรรมระหว่าง container | latency ถูกหน่วงเมื่อโหลดสูง |
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- ตั้ง
--restart alwaysแล้วไม่เคยดูdocker logsทำให้ crash-restart loop รันเงียบ ๆ อยู่เบื้องหลัง - รัน
docker restartทันทีที่ container มีปัญหา โดยไม่ดู exit code จากdocker inspect --format '{{.State.ExitCode}}'(และOOMKilled) ก่อน จนแก้ผิดจุด - ไม่ตั้ง
--memory/--cpusเลย ปล่อยให้ container เดียวใช้ทรัพยากรมากพอจะทำให้ host หรือเพื่อนบ้านล่ม - ใช้ template ของ
docker inspectที่ก๊อบจากโพสต์เก่า (.NetworkSettings.IPAddress) ซึ่งล้มบน Engine 29
โครงสร้างเดียวกันมีใน Kubernetes: restartPolicy (Always/OnFailure/Never) ตัดสินว่าจะทำอย่างไรเมื่อ container exit และ probe เพิ่มการ restart ตามสุขภาพ (Pod lifecycle) Docker ทำครึ่งที่อิง exit บนเครื่องเดียว
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
alwaysrestart container แม้หลังdocker stop” ไม่ใช่ขณะที่ daemon ยังรัน: มันค้างที่exitedหลัง 3 วินาที มันกลับมาหลัง daemon restart เท่านั้น - “
on-failure:3คือพยายามสามครั้งรวม” หมายถึง restart สามครั้ง: รันครั้งแรกบวก restart 3 ครั้งRestartCount=3 - “
--cpus 0.5ทำให้ container ใช้ 50% ของทุก core” มันคือโควต้าเวลา CPU ครึ่งหนึ่งโดยรวม (คาบ 100 ms อนุญาต 50 ms) - “OOM kill ถูกบันทึกโดยแอป” แอปไม่มีโอกาส: kernel ส่ง SIGKILL ดู
OOMKilledในdocker inspect