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

วงจรชีวิตของ Container

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

docker run คือคำสั่งที่คุณจะใช้บ่อยที่สุด มัน pull image หากจำเป็น สร้าง container และ start มัน

Terminal window
docker run nginx:1.31-alpine

คำสั่งนี้รัน nginx แบบ foreground — terminal ของคุณเกาะอยู่กับ output ของมัน กด Ctrl+C เพื่อหยุด

เพิ่ม -d เพื่อรัน container ในพื้นหลัง:

Terminal window
docker run -d nginx:1.31-alpine

Docker พิมพ์ container ID เต็ม 64 ตัวอักษรและคืน prompt ทันที docker ps แสดง 12 ตัวแรก (docker ps --no-trunc แสดงครบ 64)

โดยค่าเริ่มต้น Docker ตั้งชื่อแบบสุ่ม ใช้ --name เพื่อตั้งชื่อที่คาดเดาได้:

Terminal window
docker run -d --name webserver nginx:1.31-alpine

ชื่อไม่ซ้ำกันภายใน engine เดียว ซึ่งสำคัญภายหลัง: container ที่หยุดแล้วยังเก็บชื่อของมันไว้

Terminal window
# Only running containers
docker ps
# All containers — running AND stopped
docker ps -a
CONTAINER ID IMAGE STATUS NAMES
c0d6336221c2 nginx:1.31-alpine Up 2 minutes webserver
b7c8d9e0f1a2 alpine:3.24 Exited (0) 5 minutes ago old-task

คอลัมน์ STATUS บอกว่า container กำลังรัน (Up) หรือหยุดแล้ว (Exited พร้อม exit code ของ process หลักในวงเล็บ)

Terminal window
# Graceful stop — sends SIGTERM, waits 10 s, then SIGKILL
docker stop webserver
# Start a stopped container
docker start webserver
# Restart (stop then start)
docker restart webserver

docker 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 t
run at 1790756754

การเปลี่ยนแปลงของ 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 w
A /big.bin
A /f.txt
$ docker ps -as --filter name=w --format 'table {{.Names}}\t{{.Status}}\t{{.Size}}'
NAMES STATUS SIZE
w 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 SIZE
w Exited (137) Less than a second ago 52.4MB (virtual 61.1MB)
$ docker start w && docker exec w cat /f.txt
hi
$ docker exec w ls -l /big.bin | awk '{print $5}'
52428800

docker 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)

ตัวเลขใน Exited (N) คือ exit code ของ process หลัก และเป็นเครื่องมือวินิจฉัยที่มีประโยชน์ ทุกค่าด้านล่างสร้างขึ้นจริงที่นี่:

Codeเกิดขึ้นอย่างไรความหมาย
0docker run alpine:3.24 trueสำเร็จ
1–125sh -c 'exit 42' พิมพ์ 42; exit 3 พิมพ์ 3exit status ของแอปเอง
125docker run --name web … เมื่อชื่อถูกใช้แล้วDocker daemon เองรัน container ไม่สำเร็จ
126docker run alpine:3.24 /etc → exec: "/etc": is a directory: permission deniedพบคำสั่งแต่รันไม่ได้
127docker run alpine:3.24 nosuchcmd → exec: "nosuchcmd": executable file not found in $PATHไม่พบคำสั่ง
137docker stop กับ sleep PID 1 หลัง timeout 10 วินาที; docker rm -f128 + 9: SIGKILL (การถูกฆ่าเพราะ out-of-memory ก็ออกมาแบบนี้)
143docker stop กับ sleep ที่รันด้วย --init128 + 15: process ตายจาก SIGTERM
Terminal window
# Remove a stopped container
docker rm webserver
# Force-remove a running container (sends SIGKILL)
docker rm -f webserver

หากไม่มี -f การลบ container ที่กำลังรันจะถูกปฏิเสธ:

$ docker rm web
Error response from daemon: cannot remove container "web": container is running: stop the container before removing or force remove

docker rm -f ฆ่าด้วย SIGKILL ทันที (ไม่มี SIGTERM ไม่มีช่วงผ่อนผัน): time docker rm -f กับ container sleep ใช้ 0.16 วินาที ขณะที่ docker stop กับ container เดียวกันใช้เต็ม 10 วินาที ใช้ stop เมื่อ process ต้อง flush ข้อมูล ใช้ rm -f เมื่อไม่ต้อง

สำหรับงานครั้งเดียวที่คุณไม่อยากจัดการเอง ให้เพิ่ม --rm container จะถูกลบอัตโนมัติทันทีที่ process ของมันจบ

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

ขั้นตอน “เคลียร์พื้นที่ดิสก์บน 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 reclaimed
Removed: (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 แทนการเลือก “ทุกอย่าง”:

scripts/free-space.sh
#!/bin/sh
set -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) หรือลบตามชื่อ

container มี วงจรชีวิตของ process (created → running → exited) และ วงจรชีวิตของการมีอยู่ แยกกัน (มีอยู่จนกว่า docker rm) stop, start, restart, pause ขยับ process; มีแต่ rm ที่จบการมีอยู่ ข้อมูลใน writable layer ตามการมีอยู่ ไม่ตาม process นั่นคือเหตุผลที่ container ที่หยุดแล้วยังเสียดิสก์และยังครองชื่อ และเป็นเหตุผลที่การลบเป็นจังหวะที่ไฟล์ของมันหายไปจริง

ทางเลือกข้อดีข้อเสีย
รัน foreground (ไม่มี -d)เห็น log แบบ real time ทันที หยุดง่ายด้วย Ctrl+Cบล็อก terminal — ใช้ทำอย่างอื่นไม่ได้จนกว่าจะหยุด
รัน detached (-d)terminal ว่าง รันหลาย container พร้อมกันได้ง่ายต้องใช้ docker logs เพื่อดู output และลืมง่ายว่ามี container รันอยู่เบื้องหลัง
docker stopprocess flush และออกอย่างสะอาดได้ (exit 0)รอสูงสุด 10 วินาทีถ้า PID 1 เมิน SIGTERM
docker rm -fทันที (0.16 วินาทีที่นี่)SIGKILL: ไม่มีการปิดอย่างนุ่มนวล exit 137
  • รัน 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

  • “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 ไม่พบ
'docker stop' ส่งอะไรไปให้ process หลักของ container ก่อน?
คำสั่งใดแสดง container ทั้งหมด รวมที่หยุดแล้ว?
flag --rm ทำอะไร?
ลบ container ที่กำลังรันโดยไม่ใช้ flag -f ได้ไหม?
container เขียนข้อมูล 50 MB ลง filesystem แล้วคุณรัน docker stop docker ps -as แสดงอะไร?
docker run alpine nosuchcmd exit ด้วย code อะไร และเพราะอะไร?