ข้ามไปยังเนื้อหา

Restart Policy, Inspect และ Resource Limit

วัดบน Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64), 2026-09-30

โดยค่าเริ่มต้น container ที่หยุดหรือ crash จะหยุดค้างอยู่ restart policy บอก Docker ว่าเมื่อใดควร restart อัตโนมัติ แหล่งอ้างอิง: Start containers automatically และการวัดด้านล่าง

Policyพฤติกรรม
noไม่ restart เลย (ค่าเริ่มต้น)
alwaysrestart ทุกครั้งที่ exit ถ้าคุณหยุดเองด้วยมือ มันจะหยุดค้างจนกว่า Docker daemon จะ restart (หรือคุณ start เอง) เริ่มตอน daemon boot
unless-stoppedเหมือน always แต่ container ที่คุณหยุดเองจะหยุดค้างแม้ daemon restart แล้ว
on-failure[:N]restart เฉพาะเมื่อ exit code ไม่ใช่ศูนย์ ระบุจำนวนครั้งสูงสุดได้ ไม่ restart เมื่อ daemon restart
Terminal window
# Recommended for long-running services
docker run -d --restart unless-stopped nginx:1.31-alpine
# Restart up to 5 times on failure
docker 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=0
sh -c 'exit 1' --restart on-failure:3 policy=on-failure:3 status=exited exit=1 restarts=3
sh -c 'exit 1' --restart always policy=always:0 status=restarting exit=1 restarts=7
sh -c 'exit 1' --restart unless-stopped policy=unless-stopped:0 status=restarting exit=1 restarts=7
sh -c 'exit 0' --restart on-failure:3 policy=on-failure:3 status=exited exit=0 restarts=0
  • no รันครั้งเดียวแล้วค้างที่ exited
  • on-failure:3 restart 3 ครั้งพอดี (RestartCount=3) แล้วยอมแพ้: exited ด้วย exit 1 เมื่อ exit code เป็น 0 มันไม่ restart เลย
  • always และ unless-stopped ไม่มีวันยอมแพ้: หลัง 8 วินาทีอยู่ที่การ restart ครั้งที่ 7 status restarting ช่วงห่างยาวขึ้นเรื่อย ๆ — Docker รอ 100 ms ก่อน restart ครั้งแรกและคูณสองทุกครั้ง สูงสุดหนึ่งนาที และรีเซ็ตเมื่อ container รันครบอย่างน้อย 10 วินาที (moby restartmanager) crash loop จึงถูกแต่มองไม่เห็น; docker ps แสดง Restarting (1)
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 ที่ยังรันอยู่

Terminal window
docker update --restart unless-stopped web
docker update --restart no web

docker update --restart no กับ container always ที่วน crash ทำให้ loop จบ: status=exited restarts=7 และตัวนับหยุดเพิ่ม

docker inspect คืน configuration JSON เต็มของ container — network settings, mounts, environment variable, restart policy และอื่น ๆ:

Terminal window
docker inspect webserver

ดึงฟิลด์เดียวด้วย --format ที่ใช้ไวยากรณ์ Go template:

Terminal window
# Get the container's IP address on each network it is attached to
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webserver
# Get the restart policy
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' webserver
# The image reference, status and command
docker inspect --format '{{.Config.Image}} | {{.State.Status}} | {{json .Config.Cmd}}' webserver

output จริงของคำสั่งเหล่านั้นกับ 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}}' web
template 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 แสดงตารางที่อัปเดตสดของ CPU, memory, network I/O และ disk I/O สำหรับ container ที่รันอยู่:

Terminal window
# All running containers
docker stats
# Specific container, one snapshot (no live update)
docker stats --no-stream webserver

output จริงกับ container สองตัวที่รัน busy loop ตัวหนึ่งจำกัดครึ่ง CPU:

NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
docker-b1-cpu 51.91% 560KiB / 64MiB 0.85% 0B / 0B 0B / 0B 1
docker-b1-cpu2 102.24% 560KiB / 64MiB 0.85% 0B / 0B 0B / 0B 1

CPU % สัมพันธ์กับ หนึ่ง 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 เป็นตัวนับสะสม ไม่ใช่อัตรา

container ใช้หน่วยความจำและ CPU ทั้งหมดที่มีได้ถ้าไม่จำกัด ตั้ง limit ตอนรัน:

Terminal window
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 มีผล:

Terminal window
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)

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}}' oom
OOMKilled=true exit=137 mem=16777216

137 = 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 limited

สคริปต์ monitoring ที่ AI เขียนเพื่อพิมพ์ว่าแต่ละ container อยู่ที่ไหน:

🐛 หา bug ในโค้ดนี้

#!/bin/sh
# AI: "print the IP of every running container"
for c in $(docker ps -q); do
name=$(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 หรือไม่มีเลยด้วย:

scripts/container-ips.sh
#!/bin/sh
set -eu
for 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

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 คือตัวนับสด

ตัวเลือกข้อดีข้อเสีย
--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 ยังไม่พร้อม) จะหายเอง
--memorymemory leak ไม่ทำให้ host ล่มแอปถูกฆ่ากะทันหัน (137) เมื่อถึง limit
--cpusแบ่งปันอย่างเป็นธรรมระหว่าง containerlatency ถูกหน่วงเมื่อโหลดสูง
  • ตั้ง --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 บนเครื่องเดียว

  • “always restart 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
restart policy ใด restart container เฉพาะเมื่อ exit ด้วย code ที่ไม่ใช่ศูนย์?
flag ใดตั้ง limit หน่วยความจำแบบเด็ดขาด 256 MB?
docker stats --no-stream ทำอะไร?
container ที่ใช้ --restart always ถูกหยุดด้วย docker stop และ daemon ยังรันอยู่ อีกไม่กี่วินาทีต่อมาอยู่ใน state ใด?
container exit ด้วย code 137 และ docker inspect แสดง OOMKilled=true เกิดอะไรขึ้น?
docker inspect --format '{{.NetworkSettings.IPAddress}}' web ล้มบน Engine 29 อะไรใช้ได้?