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

container แรกของคุณ

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

การรวม flag -it คือวิธีที่คุณเปิด session terminal แบบ interactive ภายใน container รันคำสั่ง:

Terminal window
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 bash
8de6c159ca01
PRETTY_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 ซึ่งเปลี่ยนไปเรื่อย ๆ

ไม่ใช่ทุก container ที่จำเป็นต้องใช้ session แบบ interactive บริการที่ทำงานต่อเนื่อง เช่น web server และฐานข้อมูล จะถูกเริ่มต้นใน detached mode ด้วย -d เพื่อให้ทำงานในพื้นหลังโดยไม่ครอบครอง terminal ของคุณ:

Terminal window
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 ที่นี่

ดูว่าตอนนี้อะไรกำลังรันอยู่:

Terminal window
docker ps

output จริง (ตอนทดสอบ container ถูก publish ที่ 127.0.0.1:18001 ส่วน host จึงต่างจาก -p 8080:80):

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c0d6336221c2 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 web
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
$ docker ps --format 'table {{.Names}}\t{{.Ports}}\t{{.Status}}'
NAMES PORTS STATUS
web 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp Up Less than a second

บทเรียนเก่า ๆ แสดงเฉพาะบรรทัด 0.0.0.0

ส่ง container ID (หรือชื่อ) ให้ docker stop:

Terminal window
docker stop web

Docker ส่ง 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 cleanly
alpine sleep 600 stop -> 10.16 s exit code 137 # SIGTERM ignored, killed after the timeout
alpine 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 จะไม่ปรากฏใน docker ps เพิ่ม flag -a เพื่อดูทุกอย่าง รวมถึง container ที่ exit แล้ว:

Terminal window
docker ps -a

output จริงหลังรัน true และ exit 3:

NAMES STATUS
docker-b1-x1 Exited (3) Less than a second ago
docker-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 web

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-alpine
docker: 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 $?
125

exit code 125 หมายถึง Docker daemon (ไม่ใช่ container) ล้มเหลว docker ps -a แสดง Exited (0) — ตัวที่หลงเหลือ ทำให้สคริปต์ idempotent โดยลบ container เดิมก่อน (-f หยุดตัวที่กำลังรันให้ด้วย):

scripts/start-web.sh
#!/bin/sh
set -eu
docker rm -f web >/dev/null 2>&1 || true
docker 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 อย่างเดียวไม่แก้กรณีรันซ้ำขณะตัวแรกยังรันอยู่: ชื่อยังถูกใช้อยู่

docker run = สร้าง container จาก image + start process หลักของมัน docker stop = ส่ง SIGTERM ให้ process นั้น รอ แล้วส่ง SIGKILL ชีวิตของ container คือชีวิตของ PID 1: เมื่อมันออก container ก็หยุด และ container ที่หยุดแล้ว (พร้อมชื่อ, log และ exit code) ยังอยู่บนดิสก์จนกว่าคุณจะ docker rm เรื่องหยุดเร็วหรือช้าทั้งหมดขึ้นกับว่า PID 1 จัดการ signal อย่างไร

ตัวเลือกข้อดีข้อเสีย
-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 เองอยู่แล้วไม่จำเป็นต้องใช้
  • ลืม -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
  • “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
flag -d ใน docker run -d ทำอะไร?
-p 8080:80 ใน docker run -d -p 8080:80 nginx หมายความว่าอะไร?
คำสั่งใดแสดงเฉพาะ container ที่กำลังรันอยู่?
docker stop ส่ง signal ใดไปให้ process ใน container ก่อน?
docker stop ของ container ที่รัน sleep 600 เป็น PID 1 ใช้เวลาราว 10 วินาทีและ exit code เป็น 137 เกิดอะไรขึ้น?
สคริปต์รัน docker run -d --name web nginx:1.31-alpine ทุกวัน แล้วล้มเหลวในวันที่สองด้วย Conflict. The container name "/web" is already in use เพราะอะไร?