วงจรชีวิตของ Container
วัดบน Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64), 2026-09-30
การรัน container
หัวข้อที่มีชื่อว่า “การรัน container”docker run คือคำสั่งที่คุณจะใช้บ่อยที่สุด มัน pull image หากจำเป็น สร้าง container และ start มัน
docker run nginx:1.31-alpineคำสั่งนี้รัน nginx แบบ foreground — terminal ของคุณเกาะอยู่กับ output ของมัน กด Ctrl+C เพื่อหยุด
Detached mode ด้วย -d
หัวข้อที่มีชื่อว่า “Detached mode ด้วย -d”เพิ่ม -d เพื่อรัน container ในพื้นหลัง:
docker run -d nginx:1.31-alpineDocker พิมพ์ container ID เต็ม 64 ตัวอักษรและคืน prompt ทันที docker ps แสดง 12 ตัวแรก (docker ps --no-trunc แสดงครบ 64)
ตั้งชื่อ container ด้วย --name
หัวข้อที่มีชื่อว่า “ตั้งชื่อ container ด้วย --name”โดยค่าเริ่มต้น Docker ตั้งชื่อแบบสุ่ม ใช้ --name เพื่อตั้งชื่อที่คาดเดาได้:
docker run -d --name webserver nginx:1.31-alpineชื่อไม่ซ้ำกันภายใน engine เดียว ซึ่งสำคัญภายหลัง: container ที่หยุดแล้วยังเก็บชื่อของมันไว้
แสดงรายการ container
หัวข้อที่มีชื่อว่า “แสดงรายการ container”# Only running containersdocker ps
# All containers — running AND stoppeddocker ps -aCONTAINER ID IMAGE STATUS NAMESc0d6336221c2 nginx:1.31-alpine Up 2 minutes webserverb7c8d9e0f1a2 alpine:3.24 Exited (0) 5 minutes ago old-taskคอลัมน์ STATUS บอกว่า container กำลังรัน (Up) หรือหยุดแล้ว (Exited พร้อม exit code ของ process หลักในวงเล็บ)
หยุด เริ่ม และรีสตาร์ต
หัวข้อที่มีชื่อว่า “หยุด เริ่ม และรีสตาร์ต”# Graceful stop — sends SIGTERM, waits 10 s, then SIGKILLdocker stop webserver
# Start a stopped containerdocker start webserver
# Restart (stop then start)docker restart webserverdocker stop ไม่ได้ ลบ container มันยังอยู่บนดิสก์ในสถานะหยุด docker start รัน คำสั่งเดิมอีกครั้ง ใน container เดิม พิสูจน์ด้วยคำสั่งที่พิมพ์ timestamp (docker start -a เกาะ output):
$ docker run --name t alpine:3.24 sh -c 'echo run at $(date +%s)'run at 1790756753$ docker start -a trun at 1790756754writable layer รอดจาก stop และ start
หัวข้อที่มีชื่อว่า “writable layer รอดจาก stop และ start”การเปลี่ยนแปลงของ container อยู่ใน writable layer ที่ซ้อนบน image docker stop จบ process แต่ไม่แตะ layer นั้น พิสูจน์: container เขียนไฟล์เล็กและไฟล์ 50 MB ถูกหยุด แล้ว start ใหม่ ทั้งสองไฟล์ยังอยู่:
$ docker run -d --name w alpine:3.24 sh -c 'echo hi > /f.txt; dd if=/dev/zero of=/big.bin bs=1M count=50; sleep 300'$ docker diff wA /big.binA /f.txt$ docker ps -as --filter name=w --format 'table {{.Names}}\t{{.Status}}\t{{.Size}}'NAMES STATUS SIZEw Up 1 second 52.4MB (virtual 61.1MB)$ docker stop -t 1 w$ docker ps -as --filter name=w --format 'table {{.Names}}\t{{.Status}}\t{{.Size}}'NAMES STATUS SIZEw Exited (137) Less than a second ago 52.4MB (virtual 61.1MB)$ docker start w && docker exec w cat /f.txthi$ docker exec w ls -l /big.bin | awk '{print $5}'52428800docker diff แสดงการเปลี่ยนแปลงเทียบกับ image (A เพิ่ม, C แก้, D ลบ) SIZE คือ writable layer (52.4MB) ส่วน virtual รวม read-only image ที่แชร์ (61.1MB) หลัง docker stop ขนาดยังอยู่ layer หายไปพร้อม docker rm (docker ps -as --filter name=w -q | wc -l พิมพ์ 0)
Exit code
หัวข้อที่มีชื่อว่า “Exit code”ตัวเลขใน Exited (N) คือ exit code ของ process หลัก และเป็นเครื่องมือวินิจฉัยที่มีประโยชน์ ทุกค่าด้านล่างสร้างขึ้นจริงที่นี่:
| Code | เกิดขึ้นอย่างไร | ความหมาย |
|---|---|---|
0 | docker run alpine:3.24 true | สำเร็จ |
1–125 | sh -c 'exit 42' พิมพ์ 42; exit 3 พิมพ์ 3 | exit status ของแอปเอง |
125 | docker run --name web … เมื่อชื่อถูกใช้แล้ว | Docker daemon เองรัน container ไม่สำเร็จ |
126 | docker run alpine:3.24 /etc → exec: "/etc": is a directory: permission denied | พบคำสั่งแต่รันไม่ได้ |
127 | docker run alpine:3.24 nosuchcmd → exec: "nosuchcmd": executable file not found in $PATH | ไม่พบคำสั่ง |
137 | docker stop กับ sleep PID 1 หลัง timeout 10 วินาที; docker rm -f | 128 + 9: SIGKILL (การถูกฆ่าเพราะ out-of-memory ก็ออกมาแบบนี้) |
143 | docker stop กับ sleep ที่รันด้วย --init | 128 + 15: process ตายจาก SIGTERM |
ลบ container
หัวข้อที่มีชื่อว่า “ลบ container”# Remove a stopped containerdocker rm webserver
# Force-remove a running container (sends SIGKILL)docker rm -f webserverหากไม่มี -f การลบ container ที่กำลังรันจะถูกปฏิเสธ:
$ docker rm webError response from daemon: cannot remove container "web": container is running: stop the container before removing or force removedocker rm -f ฆ่าด้วย SIGKILL ทันที (ไม่มี SIGTERM ไม่มีช่วงผ่อนผัน): time docker rm -f กับ container sleep ใช้ 0.16 วินาที ขณะที่ docker stop กับ container เดียวกันใช้เต็ม 10 วินาที ใช้ stop เมื่อ process ต้อง flush ข้อมูล ใช้ rm -f เมื่อไม่ต้อง
ล้างอัตโนมัติด้วย --rm
หัวข้อที่มีชื่อว่า “ล้างอัตโนมัติด้วย --rm”สำหรับงานครั้งเดียวที่คุณไม่อยากจัดการเอง ให้เพิ่ม --rm container จะถูกลบอัตโนมัติทันทีที่ process ของมันจบ
docker run --rm alpine:3.24 echo "Hello, Docker!"Hello, Docker!container หายไปทันทีที่ echo จบ — ไม่ต้อง docker rm พิสูจน์แล้ว: docker stop กับ container ที่ใช้ --rm แล้ว docker ps -a -q --filter name=… ไม่พิมพ์อะไร
# 1. Run nginx detached with a name
docker run -d --name webserver nginx:1.31-alpine
# 2. List running containers
docker ps
# 3. Stop it
docker stop webserver
# 4. Show all containers — notice STATUS is Exited
docker ps -a
# 5. Start it again
docker start webserver
# 6. Remove it (stop first)
docker stop webserver && docker rm webserver
# 7. One-shot: auto-cleanup with --rm
docker run --rm alpine:3.24 echo "done — already gone"Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”ขั้นตอน “เคลียร์พื้นที่ดิสก์บน build agent” ที่ AI เขียน:
🐛 หา bug ในโค้ดนี้
#!/bin/sh# AI: "stop all containers to free disk space before the next job"docker stop $(docker ps -q)df -h /var/lib/dockerเฉลย
docker stop จบแค่ process writable layer ยังอยู่ ดังนั้นการใช้ดิสก์ไม่ลดลง จากหลักฐานข้างบน (container หนึ่งตัวที่เขียน 50 MB):
Running: Up 1 second 52.4MB (virtual 61.1MB)Stopped: Exited (137) Less than a second ago 52.4MB (virtual 61.1MB) # nothing reclaimedRemoved: (docker ps -as --filter name=w -q | wc -l -> 0) # goneสคริปต์ยังมี bug ที่สอง: docker ps -q แสดงเฉพาะ container ที่กำลังรัน จึงไม่เคยมองตัวที่ exited แล้วซึ่งถือพื้นที่ส่วนใหญ่ไว้ และเมื่อไม่มีอะไรรันอยู่ $(docker ps -q) ว่างเปล่า docker stop จึงล้มด้วย "docker stop" requires at least 1 argument (exit 1) ให้ลบ container ที่คุณเป็นเจ้าของโดยเลือกด้วย label แทนการเลือก “ทุกอย่าง”:
#!/bin/shset -eu# Remove only containers this job created (labelled at run time with --label job=ci)ids=$(docker ps -aq --filter label=job=ci)[ -z "$ids" ] || docker rm -f $idsคำสั่งล้างแบบ global เช่น docker container prune มีอยู่ แต่บน engine ที่ใช้ร่วมกันมันลบ container ที่หยุดแล้วของคนอื่นด้วย ให้กรอง (--filter label=…, --filter until=24h) หรือลบตามชื่อ
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”container มี วงจรชีวิตของ process (created → running → exited) และ วงจรชีวิตของการมีอยู่ แยกกัน (มีอยู่จนกว่า docker rm) stop, start, restart, pause ขยับ process; มีแต่ rm ที่จบการมีอยู่ ข้อมูลใน writable layer ตามการมีอยู่ ไม่ตาม process นั่นคือเหตุผลที่ container ที่หยุดแล้วยังเสียดิสก์และยังครองชื่อ และเป็นเหตุผลที่การลบเป็นจังหวะที่ไฟล์ของมันหายไปจริง
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ทางเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
รัน foreground (ไม่มี -d) | เห็น log แบบ real time ทันที หยุดง่ายด้วย Ctrl+C | บล็อก terminal — ใช้ทำอย่างอื่นไม่ได้จนกว่าจะหยุด |
รัน detached (-d) | terminal ว่าง รันหลาย container พร้อมกันได้ง่าย | ต้องใช้ docker logs เพื่อดู output และลืมง่ายว่ามี container รันอยู่เบื้องหลัง |
docker stop | process flush และออกอย่างสะอาดได้ (exit 0) | รอสูงสุด 10 วินาทีถ้า PID 1 เมิน SIGTERM |
docker rm -f | ทันที (0.16 วินาทีที่นี่) | SIGKILL: ไม่มีการปิดอย่างนุ่มนวล exit 137 |
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- รัน container โดยไม่มี
-dแล้วมันบล็อก terminal โดยไม่ตั้งใจ ต้องเปิดอันใหม่เพื่อทำงานต่อ - รัน
docker stopแล้วคิดว่า container หายไปแล้ว ทั้งที่ยังอยู่บนดิสก์ในสถานะหยุด — ลืมdocker rmจนสะสมเต็ม - รีบใช้
docker restartทันทีที่ดูผิดปกติ โดยไม่เช็คdocker logsก่อนว่าทำไมมัน crash แล้วจบด้วยการแก้ปัญหาเดิมซ้ำแล้วซ้ำอีก - อ่าน exit code 137 ว่า “แอป crash” แทนที่จะเป็น “มีบางอย่างส่ง SIGKILL” (timeout ของ stop,
rm -fหรือ out-of-memory killer)
แนวคิดเดียวกันมีอยู่ในระดับที่สูงขึ้น: Kubernetes ตัดสินต่อ Pod ว่าจะทำอย่างไรเมื่อ container exit ด้วย restartPolicy (Always, OnFailure, Never) ตามที่อธิบายในเอกสาร Pod lifecycle; kubelet เป็นผู้ restart container ไม่ใช่คนพิมพ์ docker restart
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
docker stopคืนพื้นที่ดิสก์” มันคืน CPU และหน่วยความจำ writable layer (52.4MB ในหลักฐาน) ถูกปล่อยด้วยdocker rmเท่านั้น - “
docker startสร้าง container ใหม่” มันรัน container เดิมอีกครั้ง: ID เดิม writable layer เดิม คำสั่งเดิม (run at …753แล้ว…754) - “
--rmลบ image” มันลบเฉพาะ container - “Exit code 125/126/127 มาจากแอปของฉัน” มันมาจาก Docker หรือ shell: 125 daemon error, 126 รันไม่ได้, 127 ไม่พบ