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

Exec และ Logs

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

docker exec รัน process ใหม่ ภายใน container ที่กำลังรันอยู่ การใช้งานที่พบบ่อยที่สุดคือเปิด shell แบบ interactive:

Terminal window
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) มี BusyBox sh แต่ ไม่มี bash

ภายใน shell คุณตรวจไฟล์ รันคำสั่ง และ debug ได้ เหมือนอยู่บนเครื่อง host เลย

Terminal window
# Example session
docker run -d --name webserver nginx:1.31-alpine
docker exec -it webserver sh
# Inside the container:
# / # cat /etc/os-release
# / # ls /usr/share/nginx/html
# / # exit

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/nginx
hello

หากไม่ใส่ -u process รันด้วย user ที่ container ตั้งไว้ (root สำหรับ image นี้) -w กำหนด working directory และ -e เพิ่มตัวแปรสำหรับ process นี้ตัวเดียว process ที่ exec เห็น filesystem และ network เดียวกับ PID 1; มันไม่ปรากฏใน docker ps

$ 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'
hi

127 คือรหัส “ไม่พบคำสั่ง” จากบทเรียน lifecycle image ที่เล็กมาก (distroless, scratch) ไม่มี sh ด้วย ที่นั่น docker exec เปิด shell ไม่ได้เลย และคุณ debug ด้วย docker logs, docker cp หรือ debug sidecar

stdout และ stderr ของทุก container ถูกเก็บโดย logging driver ค่าเริ่มต้นคือ json-file (docker info --format '{{.LoggingDriver}}' พิมพ์ json-file):

Terminal window
# Print all logs so far
docker logs webserver
# Follow (stream) new log lines in real time — like tail -f
docker logs -f webserver
# Show the last 20 lines
docker logs --tail 20 webserver
# Add timestamps
docker logs -t webserver
# Only lines from the last 30 seconds
docker logs --since 30s webserver

output จริงของ 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 39
2026-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> มักบอกเหตุผลได้ตรง ๆ

json-file ไม่มีขีดจำกัดขนาด หากคุณไม่ตั้ง: docker inspect -f '{{.HostConfig.LogConfig.Config}}' พิมพ์ map[] container ที่พูดมากจะทำให้ดิสก์ที่เก็บ /var/lib/docker เต็ม (อยู่ใน VM บน Docker Desktop; docker inspect -f '{{.LogPath}}' พิมพ์ path) หมุนเวียน log ด้วย --log-opt:

scripts/log-rotation.sh
#!/bin/sh
docker 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 -l
docker logs noisy | head -1
docker 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 คือการแลกประวัติกับดิสก์

docker top แสดง process ที่รันภายใน container โดยไม่ต้องเข้าไป:

Terminal window
docker top webserver

output จริงจาก Mac เครื่องนี้:

UID PID PPID C STIME TTY TIME CMD
root 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 process
statd 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 เดียวกันเป็น PID 1 (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 รันคำสั่งใดก็ได้โดยตรง:

Terminal window
# Check nginx config
docker exec webserver nginx -t
# View a file
docker exec webserver cat /etc/nginx/nginx.conf

nginx -t พิมพ์ syntax is ok / test is successful

  • 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 webserver

“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 เฉพาะเมื่อมี:

scripts/shell.sh
#!/bin/sh
set -eu
c=${1:?usage: shell.sh <container>}
# Prefer bash if present, else fall back to sh; only allocate a TTY on a terminal
sh_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 เดียวกัน

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 โดยตรง

ทางเลือกข้อดีข้อเสีย
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 ที่พูดมากทำดิสก์เต็มได้
  • ใช้ 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) เครื่องมือต่าง แต่โมเดลเดียวกัน

  • “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
flag -t ใน 'docker exec -it container sh' ทำอะไร?
คำสั่งใด stream บรรทัด log ใหม่จาก container แบบ real time?
เกิดอะไรขึ้นกับ container เมื่อคุณ 'exit' shell ที่เปิดด้วย docker exec?
คำสั่งใดแสดง process ที่รันภายใน container โดยไม่ต้องเปิด shell?
docker exec -it web bash ล้มเหลวบน nginx container ที่ใช้ Alpine error คืออะไรและแก้อย่างไร?
container พิมพ์ 200 บรรทัดด้วย --log-opt max-size=1k --log-opt max-file=2 docker logs คืนกี่บรรทัด เทียบกับไม่มี --log-opt?