container แรกของคุณ
วัดบน Docker Desktop 4.81.0 / Engine 29.6.1, macOS บน Apple Silicon (linux/arm64), 2026-09-30
container แบบ interactive: เปิด shell ภายใน Ubuntu
หัวข้อที่มีชื่อว่า “container แบบ interactive: เปิด shell ภายใน Ubuntu”การรวม flag -it คือวิธีที่คุณเปิด session terminal แบบ interactive ภายใน container รันคำสั่ง:
docker run -it ubuntu:24.04 bashเมื่อ Docker ดึง image และเริ่มต้น container แล้ว prompt ของคุณจะเปลี่ยนเป็นลักษณะนี้:
root@8de6c159ca01:/#ตอนนี้คุณอยู่ภายใน container แล้ว hostname ที่แสดง (8de6c159ca01) คือ container ID คุณมี userland ของ Ubuntu ที่แยกออกมาอย่างสมบูรณ์ — มี filesystem และ process tree ของตัวเอง — รันบน kernel ของ engine สิ่งที่เห็นข้างใน (จับโดย pipe คำสั่งเข้า docker run -i จึงไม่มี TTY):
$ printf 'hostname\ncat /etc/os-release | head -2\nuname -m\nexit\n' | docker run -i ubuntu:24.04 bash8de6c159ca01PRETTY_NAME="Ubuntu 24.04.5 LTS"NAME="Ubuntu"aarch64สังเกต aarch64: บน Mac Apple Silicon Docker ดึง variant linux/arm64 ของ image ubuntu แบบ multi-platform image ที่มีแต่ amd64 จะรันผ่าน emulation — ตัวอย่างเช่น docker run --platform linux/amd64 alpine:3.24 uname -m พิมพ์ x86_64 ที่นี่ ช้ากว่า และจะแทนที่ alpine:3.24 ในเครื่องด้วย variant amd64 จนกว่าคุณจะ pull arm64 กลับมา (docker pull --platform linux/arm64 alpine:3.24)
เมื่อเสร็จแล้ว พิมพ์ exit เพื่อกลับไปยัง shell ของ host container จะหยุดทันทีที่ process bash สิ้นสุด
ความหมายของ flag ต่าง ๆ:
-i— เปิด stdin ค้างไว้เพื่อให้คุณพิมพ์คำสั่งเข้าไปใน container ได้-t— จัดสรร pseudo-TTY เพื่อให้ shell prompt แสดงผลได้ถูกต้องและรองรับ terminal control sequences- เมื่อใช้ร่วมกัน
-itจะให้ session shell แบบ interactive ภายใน container
บทเรียนนี้ pin ubuntu:24.04 แทน ubuntu เฉย ๆ เพื่อให้ output ตรงกับที่คุณจะได้ ubuntu เฉย ๆ หมายถึง ubuntu:latest ซึ่งเปลี่ยนไปเรื่อย ๆ
Web server แบบ detached: รัน Nginx ในพื้นหลัง
หัวข้อที่มีชื่อว่า “Web server แบบ detached: รัน Nginx ในพื้นหลัง”ไม่ใช่ทุก container ที่จำเป็นต้องใช้ session แบบ interactive บริการที่ทำงานต่อเนื่อง เช่น web server และฐานข้อมูล จะถูกเริ่มต้นใน detached mode ด้วย -d เพื่อให้ทำงานในพื้นหลังโดยไม่ครอบครอง terminal ของคุณ:
docker run -d --name web -p 8080:80 nginx:1.31-alpineคำอธิบาย flag:
-d— detached mode: container ทำงานในพื้นหลังและ Docker พิมพ์เฉพาะ container ID--name web— ชื่อที่คุณเลือกเอง แทนชื่อสุ่มอย่างelegant_morse-p 8080:80— publish port: map host port 8080 ไปยัง container port 80 traffic ที่มาถึงlocalhost:8080บนเครื่องของคุณจะถูกส่งต่อไปยัง port 80 ภายใน container ที่ Nginx ฟังอยู่
หลังรันคำสั่งนี้ เปิด http://localhost:8080 ในเบราว์เซอร์ แล้วคุณจะเห็นหน้าต้อนรับของ Nginx จาก terminal curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080 พิมพ์ 200 ที่นี่
แสดงรายการ container ที่กำลังรันด้วย docker ps
หัวข้อที่มีชื่อว่า “แสดงรายการ container ที่กำลังรันด้วย docker ps”ดูว่าตอนนี้อะไรกำลังรันอยู่:
docker psoutput จริง (ตอนทดสอบ container ถูก publish ที่ 127.0.0.1:18001 ส่วน host จึงต่างจาก -p 8080:80):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESc0d6336221c2 nginx:1.31-alpine "/docker-entrypoint.…" 1 second ago Up Less than a second 127.0.0.1:18001->80/tcp docker-b1-webคอลัมน์ต่าง ๆ บอกทุกอย่างในพริบตา: container ID, image ที่ใช้เริ่ม, คำสั่งที่รันอยู่ภายใน, อยู่มานานเท่าไร, สถานะ, port mapping และชื่อ
เมื่อใช้ -p 8080:80 แบบธรรมดา (ไม่ระบุ address) Engine 29 publish บน ทั้ง IPv4 และ IPv6 และ docker ps กับ docker port แสดงสองรายการ:
$ docker port web80/tcp -> 0.0.0.0:808080/tcp -> [::]:8080$ docker ps --format 'table {{.Names}}\t{{.Ports}}\t{{.Status}}'NAMES PORTS STATUSweb 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp Up Less than a secondบทเรียนเก่า ๆ แสดงเฉพาะบรรทัด 0.0.0.0
หยุด container
หัวข้อที่มีชื่อว่า “หยุด container”ส่ง container ID (หรือชื่อ) ให้ docker stop:
docker stop webDocker ส่ง SIGTERM ไปยัง process หลักภายใน container (PID 1) และให้เวลาสูงสุด 10 วินาทีเพื่อปิดตัวอย่างนุ่มนวล หาก process ยังไม่จบภายในเวลานั้น Docker จะส่ง SIGKILL เพื่อบังคับหยุด exit code บอกว่าเกิดอะไรขึ้น: 128 + หมายเลข signal ดังนั้น 143 คือ SIGTERM (15) และ 137 คือ SIGKILL (9)
จะใช้เวลานานแค่ไหนขึ้นกับ PID 1 ล้วน ๆ การหยุดจริงสามแบบ จับเวลาด้วย time docker stop:
nginx:1.31-alpine stop -> 0.31 s exit code 0 # nginx handles SIGTERM and exits cleanlyalpine sleep 600 stop -> 10.16 s exit code 137 # SIGTERM ignored, killed after the timeoutalpine sleep 600 --init stop -> 0.14 s exit code 143 # docker-init forwards SIGTERM, sleep dies from itทำไม? PID 1 ใน container พิเศษ: kernel ไม่มี default action ให้กับ signal ที่ยังไม่ได้ติดตั้ง handler sleep ไม่ได้ติดตั้ง handler เมื่อเป็น PID 1 มันจึงเมิน SIGTERM และ Docker รอเต็ม 10 วินาทีก่อนส่ง SIGKILL nginx ติดตั้ง handler จึงออกทันที เมื่อใช้ --init Docker รัน docker-init เป็น PID 1 ซึ่งส่งต่อ signal ไปยัง process ของคุณ (sleep ไม่ใช่ PID 1 แล้ว default action จึงทำงาน) โมดูล Dockerfile อธิบายว่ารูปแบบของ CMD ส่งผลอย่างไร ปรับระยะเวลารอด้วย docker stop -t <seconds> ได้
แสดงรายการ container ทั้งหมด (รวมที่หยุดแล้ว)
หัวข้อที่มีชื่อว่า “แสดงรายการ container ทั้งหมด (รวมที่หยุดแล้ว)”เมื่อหยุดแล้ว container จะไม่ปรากฏใน docker ps เพิ่ม flag -a เพื่อดูทุกอย่าง รวมถึง container ที่ exit แล้ว:
docker ps -aoutput จริงหลังรัน true และ exit 3:
NAMES STATUSdocker-b1-x1 Exited (3) Less than a second agodocker-b1-x0 Exited (0) Less than a second agoตัวเลขคือ exit code ของ process หลัก มีประโยชน์สำหรับตรวจ exit code, กู้ log (docker logs) หรือล้าง container เก่าด้วย docker rm container ที่หยุดแล้ว ยังคงมีอยู่และยังครองชื่อของมัน ซึ่งเป็นที่มาของ bug ด้านล่าง
docker run -d --name web -p 8080:80 nginx:1.31-alpine
docker ps
docker stop web
docker ps -a
docker rm webReading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”helper “start dev server” ที่ AI เขียน ซึ่งนักพัฒนารันวันละหลายครั้ง:
🐛 หา bug ในโค้ดนี้
#!/bin/sh# AI: "start (or restart) the local web server"docker run -d --name web -p 8080:80 nginx:1.31-alpineเฉลย
รันครั้งแรกใช้ได้ ทุกครั้งหลังจากนั้นล้มเหลว ไม่ว่า container เดิมจะยังรันอยู่หรือไม่ เพราะชื่อ web ยังเป็นของ container เก่า (container ที่หยุดแล้วไม่ถูกลบ) พิสูจน์บน Mac เครื่องนี้ (ชื่อจริงของ container ยาวกว่านี้ ตัดเหลือ web ที่นี่):
$ docker run -d --name web -p 8080:80 nginx:1.31-alpine # first time: ok$ docker stop web$ docker run -d --name web -p 8080:80 nginx:1.31-alpinedocker: Error response from daemon: Conflict. The container name "/web" is already in use by container "365d43a0…". You have to remove (or rename) that container to be able to reuse that name.$ echo $?125exit code 125 หมายถึง Docker daemon (ไม่ใช่ container) ล้มเหลว docker ps -a แสดง Exited (0) — ตัวที่หลงเหลือ ทำให้สคริปต์ idempotent โดยลบ container เดิมก่อน (-f หยุดตัวที่กำลังรันให้ด้วย):
#!/bin/shset -eudocker rm -f web >/dev/null 2>&1 || truedocker run -d --name web -p 127.0.0.1:8080:80 nginx:1.31-alpineการรันสคริปต์ที่แก้แล้วสองครั้งเหลือ container สถานะ Up เพียงตัวเดียว หากคุณไม่ต้องการ container ที่หยุดแล้ว docker run --rm จะลบให้เองเมื่อ exit (พิสูจน์แล้ว: docker stop แล้ว docker ps -a -q ไม่พิมพ์อะไร) แต่ --rm อย่างเดียวไม่แก้กรณีรันซ้ำขณะตัวแรกยังรันอยู่: ชื่อยังถูกใช้อยู่
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”docker run = สร้าง container จาก image + start process หลักของมัน docker stop = ส่ง SIGTERM ให้ process นั้น รอ แล้วส่ง SIGKILL ชีวิตของ container คือชีวิตของ PID 1: เมื่อมันออก container ก็หยุด และ container ที่หยุดแล้ว (พร้อมชื่อ, log และ exit code) ยังอยู่บนดิสก์จนกว่าคุณจะ docker rm เรื่องหยุดเร็วหรือช้าทั้งหมดขึ้นกับว่า PID 1 จัดการ signal อย่างไร
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ตัวเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
-it (interactive) | เห็น output แบบ real time debug ง่าย | ต้องเปิด terminal ค้างไว้ container หยุดถ้า session ปิด |
-d (detached) | ทำงานเป็น background service ต่อเนื่อง ไม่ครอบครอง terminal | ต้องใช้ docker ps / docker logs เพื่อดู — ไม่มี output ทันที |
--rm | ไม่มี container หรือชื่อที่หยุดแล้วค้างอยู่ | exit code และ log หายไปพร้อม container ทำให้ debug crash ยากขึ้น |
--init | ส่งต่อ signal, เก็บกวาด zombie process, docker stop เร็ว | มี process เพิ่มอีกตัว; โปรแกรมที่จัดการ SIGTERM เองอยู่แล้วไม่จำเป็นต้องใช้ |
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- ลืม
-dแล้ว container ยึด terminal ไว้เพราะรันแบบ foreground โดยไม่ตั้งใจ - รัน
docker runซ้ำโดยไม่เช็คdocker psก่อน จบด้วยหลาย container ที่รัน service เดียวกัน — หรือเมื่อใช้--nameก็เจอ name conflict ในการรันครั้งที่สอง - ลืมว่า container หยุดทันทีที่ process หลักของมันจบ (เช่นพิมพ์
exitใน session-it) แล้วสงสัยว่าทำไมมันหายจากdocker ps(มันอยู่ในdocker ps -a) - มองว่า
docker stopที่ใช้ 10 วินาทีเป็นเรื่องปกติ ทั้งที่เป็นสัญญาณว่า PID 1 เมิน SIGTERM
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
docker stopลบ container” มันแค่หยุด container ยังอยู่ในdocker ps -aและชื่อยังถูกจองไว้จนกว่าจะdocker rm - “
docker stopรอ 10 วินาทีเสมอ” มันรอ สูงสุด 10 วินาที nginx หยุดใน 0.31 วินาที มีแต่ PID 1 ที่เมิน SIGTERM ที่ใช้เต็ม timeout - “exit code 137 แปลว่าแอป crash” 137 คือ 128 + 9: มีบางอย่าง (ที่นี่คือ Docker หลัง timeout ที่อื่นอาจเป็น OOM killer) ส่ง SIGKILL
- “Ubuntu ใน container คือเครื่องอีกเครื่อง” มันคือ userland ของ Ubuntu บน kernel ของ engine;
uname -mยังบอกaarch64บน Mac Apple Silicon