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

執行流程:

  1. Docker daemon 透過 runtime 啟動容器,建立 namespace
  2. 容器主行程是使用者指定的指令(例如 bash),它會成為 PID Namespace 中的 PID 1
  3. host 上的 docker CLI 透過 shim 與容器的 stdin/stdout/stderr 串接
  4. 終端關閉或主行程結束時,容器即停止

容器內的 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 -ddocker run -it 進入容器後看到的行程列表完全相同。

延伸閱讀#