Exec และ Logs
วัดบน Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64) image nginx:1.31-alpine, 2026-09-30
เปิด shell ใน container ที่กำลังรัน
หัวข้อที่มีชื่อว่า “เปิด shell ใน container ที่กำลังรัน”docker exec รัน process ใหม่ ภายใน container ที่กำลังรันอยู่ การใช้งานที่พบบ่อยที่สุดคือเปิด shell แบบ interactive:
docker exec -it <container> sh-iเปิด stdin ค้างไว้ (interactive)-tจัดสรร pseudo-TTY (เพื่อให้ได้ terminal prompt ที่ถูกต้อง)shคือคำสั่งที่จะรัน — ใช้bashเฉพาะเมื่อ image มี image ที่ใช้ Alpine (nginx:1.31-alpine,node:24-alpine,alpine) มี BusyBoxshแต่ ไม่มีbash
ภายใน shell คุณตรวจไฟล์ รันคำสั่ง และ debug ได้ เหมือนอยู่บนเครื่อง host เลย
# Example sessiondocker run -d --name webserver nginx:1.31-alpinedocker exec -it webserver sh
# Inside the container:# / # cat /etc/os-release# / # ls /usr/share/nginx/html# / # exitเลือก user, ไดเรกทอรี และ environment
หัวข้อที่มีชื่อว่า “เลือก user, ไดเรกทอรี และ environment”docker exec override ได้สามอย่างสำหรับ process ใหม่เท่านั้น (environment ของ container เองไม่เปลี่ยน):
$ docker exec -u nginx -w /etc/nginx -e GREETING=hello webserver sh -c 'id -un; pwd; echo $GREETING'nginx/etc/nginxhelloหากไม่ใส่ -u process รันด้วย user ที่ container ตั้งไว้ (root สำหรับ image นี้) -w กำหนด working directory และ -e เพิ่มตัวแปรสำหรับ process นี้ตัวเดียว process ที่ exec เห็น filesystem และ network เดียวกับ PID 1; มันไม่ปรากฏใน docker ps
เมื่อ image ไม่มี shell ที่ต้องการ
หัวข้อที่มีชื่อว่า “เมื่อ image ไม่มี shell ที่ต้องการ”$ docker exec webserver bash -c 'echo hi'OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH$ echo $?127$ docker exec webserver sh -c 'echo hi'hi127 คือรหัส “ไม่พบคำสั่ง” จากบทเรียน lifecycle image ที่เล็กมาก (distroless, scratch) ไม่มี sh ด้วย ที่นั่น docker exec เปิด shell ไม่ได้เลย และคุณ debug ด้วย docker logs, docker cp หรือ debug sidecar
ดู log ของ container
หัวข้อที่มีชื่อว่า “ดู log ของ container”stdout และ stderr ของทุก container ถูกเก็บโดย logging driver ค่าเริ่มต้นคือ json-file (docker info --format '{{.LoggingDriver}}' พิมพ์ json-file):
# Print all logs so fardocker logs webserver
# Follow (stream) new log lines in real time — like tail -fdocker logs -f webserver
# Show the last 20 linesdocker logs --tail 20 webserver
# Add timestampsdocker logs -t webserver
# Only lines from the last 30 secondsdocker logs --since 30s webserveroutput จริงของ docker logs --tail 2 -t (timestamp แบบ RFC 3339, UTC):
2026-09-30T08:29:16.234130419Z 2026/09/30 08:29:16 [notice] 1#1: start worker process 392026-09-30T08:29:16.234618711Z 2026/09/30 08:29:16 [notice] 1#1: start worker process 40กับ container ที่พิมพ์ tick N วินาทีละครั้ง docker logs --since 2s คืนสาม tick ล่าสุด และ --tail 1 คืนแค่อันสุดท้าย log คือที่แรกที่ควรดูเมื่อ container ทำงานผิดปกติ ถ้า container restart วนไปเรื่อย ๆ docker logs <name> มักบอกเหตุผลได้ตรง ๆ
ไฟล์ log โตไม่จำกัดโดยค่าเริ่มต้น
หัวข้อที่มีชื่อว่า “ไฟล์ log โตไม่จำกัดโดยค่าเริ่มต้น”json-file ไม่มีขีดจำกัดขนาด หากคุณไม่ตั้ง: docker inspect -f '{{.HostConfig.LogConfig.Config}}' พิมพ์ map[] container ที่พูดมากจะทำให้ดิสก์ที่เก็บ /var/lib/docker เต็ม (อยู่ใน VM บน Docker Desktop; docker inspect -f '{{.LogPath}}' พิมพ์ path) หมุนเวียน log ด้วย --log-opt:
#!/bin/shdocker run --name noisy --log-opt max-size=1k --log-opt max-file=2 alpine:3.24 \ sh -c 'for i in $(seq 1 200); do echo "line $i"; done'docker logs noisy | wc -ldocker logs noisy | head -1docker rm noisyผลจริง: loop เดียวกันเมื่อมี rotation เก็บไว้ 18 จาก 200 บรรทัด (จาก line 183 ถึง line 200); container เดียวกันที่ไม่มี --log-opt เก็บครบ 200 config ถูกเก็บไว้บน container: json-file map[max-file:2 max-size:1k] ในทางปฏิบัติให้ใช้ค่าอย่าง max-size=10m max-file=3 หรือตั้งให้ทุก container ใน daemon.json (log-driver, log-opts) ตามเอกสาร json-file driver โปรดทราบว่าบรรทัดที่ถูกหมุนทิ้งหายไปแล้ว: limit คือการแลกประวัติกับดิสก์
ตรวจ process ด้วย docker top
หัวข้อที่มีชื่อว่า “ตรวจ process ด้วย docker top”docker top แสดง process ที่รันภายใน container โดยไม่ต้องเข้าไป:
docker top webserveroutput จริงจาก Mac เครื่องนี้:
UID PID PPID C STIME TTY TIME CMDroot 60551 60527 0 08:29 ? 00:00:00 nginx: master process nginx -g daemon off;statd 60587 60551 0 08:29 ? 00:00:00 nginx: worker processstatd 60588 60551 0 08:29 ? 00:00:00 nginx: worker process... # 11 workers in totalสามจุดที่ต่างจากตัวอย่างเรียบร้อยที่คุณอาจเห็นที่อื่น:
- PID เป็นของ host (ที่นี่คือ Desktop VM) ไม่ใช่ของ container
docker exec webserver psแสดง process เดียวกันเป็น PID1(master) และ30,31, … (worker) — นี่คือ PID namespace ที่ทำงาน - คอลัมน์ user คือ
statdไม่ใช่nginx:docker topแปลง uid ตัวเลข (101) ด้วย/etc/passwdของ host ซึ่ง uid 101 บังเอิญเป็นstatdใน container uid เดียวกันคือnginxอย่าเชื่อชื่อในdocker topสำหรับการตัดสินใจด้านความปลอดภัย ให้ดู uid - มี worker 11 ตัว หนึ่งต่อ CPU ที่ VM มี (
worker_processes auto) ไม่ใช่ตัวเดียว
ส่งตัวเลือกของ ps ได้: docker top webserver -o pid,user,args
รันคำสั่งครั้งเดียว
หัวข้อที่มีชื่อว่า “รันคำสั่งครั้งเดียว”คุณไม่จำเป็นต้องใช้ shell เต็มรูปแบบเสมอไป docker exec รันคำสั่งใดก็ได้โดยตรง:
# Check nginx configdocker exec webserver nginx -t
# View a filedocker exec webserver cat /etc/nginx/nginx.confnginx -t พิมพ์ syntax is ok / test is successful
exec เทียบกับ attach
หัวข้อที่มีชื่อว่า “exec เทียบกับ attach”docker execเริ่ม process ใหม่ ออกจากมัน (หรือ Ctrl+C ในนั้น) จบแค่ process นั้นdocker attachเชื่อม terminal ของคุณกับ stdin/stdout/stderr ของ PID 1 ที่มีอยู่ signal ไปที่ PID 1
พิสูจน์กับ container ที่ PID 1 เป็น loop while true; do echo tick; sleep 1: docker attach stream tick 3, 4, 5…; การส่ง SIGINT ไปยัง attach client (สิ่งที่ Ctrl+C ทำ) ถูก proxy ไปที่ PID 1 โดยค่าเริ่มต้น (--sig-proxy=true) และ container จบด้วย exited 130 (128 + 2, SIGINT) เอกสารจึงแนะนำให้ detach ด้วย Ctrl-p Ctrl-q หรือ attach ด้วย --sig-proxy=false หากต้องการแค่ดู (docker attach reference)
# 1. Start nginx in the background
docker run -d --name webserver nginx:1.31-alpine
# 2. Check its logs
docker logs webserver
# 3. Run a one-off command inside it
docker exec webserver nginx -t
# 4. Open an interactive shell
# (type 'exit' when done)
docker exec -it webserver sh
# 5. Watch processes
docker top webserver
# 6. Follow logs in real time (Ctrl+C to stop)
docker logs -f webserverReading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”“debug helper” สำหรับ wiki ของทีมที่ AI เขียน:
🐛 หา bug ในโค้ดนี้
#!/bin/sh# AI: "open a shell in the api container (works for any image)"docker exec -it api bashเฉลย
container คือ nginx:1.31-alpine (หรือ node:24-alpine หรือ image ใดที่ใช้ Alpine) ซึ่งไม่มี bash พิสูจน์ข้างบน: OCI runtime exec failed: ... exec: "bash": executable file not found in $PATH exit code 127 helper เดียวกันยังพังในงาน automation: เมื่อ stdin ไม่ใช่ terminal -t ถูกปฏิเสธ (echo hi | docker exec -it web sh พิมพ์ cannot attach stdin to a TTY-enabled container because stdin is not a terminal exit 1) ขณะที่ -i อย่างเดียวทำงานและพิมพ์ hi ให้เลือก shell ที่มีอยู่จริง และขอ TTY เฉพาะเมื่อมี:
#!/bin/shset -euc=${1:?usage: shell.sh <container>}# Prefer bash if present, else fall back to sh; only allocate a TTY on a terminalsh_bin=$(docker exec "$c" sh -c 'command -v bash || command -v sh')if [ -t 0 ]; then exec docker exec -it "$c" "$sh_bin"else exec docker exec -i "$c" "$sh_bin"fiหาก image ไม่มี shell เลย docker exec จะล้มเหลวแบบเดียวกัน ให้ใช้ docker logs, docker cp หรือรัน debug container ใน network/PID namespace เดียวกัน
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”container ที่รันอยู่คือ PID 1 หนึ่งตัวบวกสิ่งอื่นที่ kernel ใส่ไว้ใน namespace ของมัน logs อ่านสิ่งที่ PID 1 เขียนลง stdout/stderr (เก็บโดย logging driver นอก filesystem ของ container) exec เพิ่ม process เข้า namespace เดียวกัน top มองที่ process เดียวกันจาก ภายนอก ด้วย PID และชื่อ user ของ host attach ต่อ terminal ของคุณเข้ากับ PID 1 โดยตรง
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ทางเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
docker exec -it debug สด | เห็นสถานะจริงทันที เร็วสำหรับแก้ปัญหาเฉพาะหน้า | ไม่ถูกเก็บถาวร ไม่มี audit trail และลืมง่ายว่าแก้อะไรไป |
docker logs + centralized logging | เก็บประวัติ ค้นหาได้ แชร์กับทีมได้ | ต้องตั้ง logging driver หรือ log aggregator |
json-file พร้อม max-size/max-file | ใช้ดิสก์มีขอบเขต | บรรทัดเก่าถูกทิ้ง (เก็บ 18 จาก 200) |
json-file ไม่จำกัด (ค่าเริ่มต้น) | ไม่มีอะไรหาย | container ที่พูดมากทำดิสก์เต็มได้ |
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- ใช้
docker execแก้ไฟล์สดใน container แทนการแก้ image หรือ Dockerfile แล้ว rebuild ทำให้การแก้หายไปทันทีที่ลบ container - กระโดดไป
docker execเพื่อ debug ก่อนเช็คdocker logsทั้งที่ log มักบอกชัดเจนอยู่แล้วว่าผิดตรงไหน - พึ่ง
docker execเป็นเครื่องมือ debug หลักแทน logging/monitoring ที่เหมาะสม — ใช้ได้เฉพาะเมื่อ container ยังมีชีวิต container ที่ crash แล้ว exec เข้าไปไม่ได้ - copy-paste
docker exec -it x bashสำหรับ image ที่มีแต่sh - ปล่อย
json-fileไม่จำกัดบน container ที่อยู่นานและพูดมาก
โครงสร้างเดียวกันมีใน Kubernetes: kubectl logs อ่านสิ่งที่ container เขียน kubectl exec รัน process ใหม่ในนั้น (kubectl reference) เครื่องมือต่าง แต่โมเดลเดียวกัน
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
docker execเริ่ม container อีกตัว” มันเพิ่ม process เข้า container เดิม บรรทัดdocker psของ container ไม่เปลี่ยน - “PID ใน
docker topคือ PID ของ container” มันคือของ host ข้างใน master คือ PID 1 - “Docker เก็บเฉพาะ log ล่าสุด” ไม่ใช่โดยค่าเริ่มต้น:
json-fileเก็บทุกอย่างจนกว่า container ถูกลบ เว้นแต่ตั้งmax-size - “Ctrl+C ใน
docker attachไม่เป็นไร” มันถูก proxy ไปที่ PID 1; container จบด้วย 130