docker run 的兩種主要型態#
docker run 在使用者體感上有兩種常見模式:
- 前景互動模式:
docker run -it <image> - 後景守護模式:
docker run -d <image>
兩者的差異不只是「有沒有看到輸出」,而是底層 stdin/stdout 與 TTY 的處理方式不同。
前景模式:-i 與 -t#
-i(interactive):保持 stdin 開啟,host 終端的輸入會被導向容器主行程的 stdin-t(tty):分配一個偽終端(pseudo-TTY),讓容器內的 shell 可以正確顯示提示字元、處理控制字元(Ctrl-C 等)-it通常一起使用,常見於docker run -it ubuntu bash
執行流程:
- Docker daemon 透過 runtime 啟動容器,建立 namespace
- 容器主行程是使用者指定的指令(例如
bash),它會成為 PID Namespace 中的 PID 1 - host 上的 docker CLI 透過 shim 與容器的 stdin/stdout/stderr 串接
- 終端關閉或主行程結束時,容器即停止
容器內的 PID 1 行為與 host 不同:它需要處理孤兒行程回收與訊號預設行為。如果只跑一般應用程式(例如
node app.js)作為 PID 1,可能無法正確處理SIGTERM,這是tini等 init 工具存在的原因(docker run --init即由 docker 自動注入)。
後景模式:-d#
docker run -d 啟動容器後,docker CLI 立即返回,容器在背景執行:
- stdin 沒有與終端連接
- stdout/stderr 由 docker daemon(或 containerd)收集,寫入 log driver(預設 json-file)
- 行程實際上由 shim 持有,daemon 重啟也不會殺掉容器
查看 log 與狀態:
docker logs -f <container>
docker ps後景模式適合長期跑的服務(web server、worker),通常搭配 --restart 與 healthcheck。
shim 的角色#
容器啟動後並不是「daemon 直接 fork 容器」,而是透過一層 shim:
- daemon → containerd → containerd-shim → runc → 容器主行程
- shim 是常駐行程,持有容器的 stdio 與 exit code
- 即使 daemon 重啟,shim 仍持續執行,容器不受影響
這也是為什麼即便重啟 docker daemon,跑在背景的容器多半不會中斷。
兩種模式的選擇#
| 情境 | 建議 |
|---|---|
| 互動除錯、一次性指令 | -it |
| 長期服務 | -d,搭配 healthcheck |
| CI 中執行測試 | 不加 -d,等待結束取得 exit code |
| 需要 TTY 行為(color、line editing) | 加 -t |
在 CI 中常見錯誤是加上
-t但沒有真正的 TTY,導致 stdout 被當成 TTY 而出現奇怪的控制字元。CI 中通常只用-i或不加。
與 namespace 的關聯#
無論前景或後景,namespace 的建立過程是一樣的,差別只在 stdio 的綁定與 CLI 是否阻塞。理解這點,就能解釋為什麼 docker run -d 與 docker run -it 進入容器後看到的行程列表完全相同。