目錄
Docker 容器生命週期:建立、啟動、停止與刪除
容器從建立到刪除會經過幾種狀態。理解這些狀態,就能知道為什麼有時候容器「跑一下就停了」、為什麼停止的容器還佔著名稱,以及為什麼 docker stop 有時候要等 10 秒。
容器的狀態
| 狀態 | 意思 | docker ps -a 的 STATUS |
|---|---|---|
created | 已建立但從未啟動 | Created |
running | 主程序正在執行 | Up 3 minutes |
paused | 所有程序被凍結 | Up 3 minutes (Paused) |
exited | 主程序已結束,容器還在 | Exited (0) 10 seconds ago |
restarting | 正依照重啟策略重新啟動 | Restarting (1) 2 seconds ago |
Exited 後面括號中的數字是主程序的結束代碼,0 代表正常結束,下面會詳細說明。
容器什麼時候會停止
容器的生命 = 主程序(PID 1)的生命。 主程序結束,容器就停止。
這解釋了一個很常見的困惑:
$ docker run ubuntu:26.04
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66e6f85c66e0 ubuntu:26.04 "/bin/bash" Less than a second ago Exited (0) Less than a second ago dreamy_solomon
docker run ubuntu:26.04 沒有任何輸出就結束了,docker ps 也看不到它。用 docker ps -a 才看得到:它的主程序是 /bin/bash,但沒有加 -it,bash 沒有可以讀取的輸入,啟動後立刻結束,容器也就停了(Exited (0))。
加上 -it 給它一個互動終端機,bash 才會持續等你輸入:
docker run -it ubuntu:26.04
nginx、PostgreSQL 這類服務的映像檔,主程序會一直在前景執行,所以容器會持續運作。
建立與啟動
docker run 其實是 docker create(建立)加上 docker start(啟動)。也可以分開執行:
$ docker create --name web nginx:1.30
ad99713ea5acddf70d854365e882fd0a3b6d4508d50da25993b94c9878f1c3bc
$ docker ps -a --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" Less than a second ago Created web
$ docker start web
web
$ docker ps --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" Less than a second ago Up Less than a second 80/tcp web
建立後狀態是 Created,啟動後變成 Up。--filter name=web 只列出名稱包含 web 的容器。
名稱不能重複
docker run 每次都會建立新的容器。容器名稱已經被用掉時,即使那個容器已經停止,也會出錯:
$ docker run -d --name web nginx:1.30
docker: Error response from daemon: Conflict. The container name "/web" is already in use by container "ad99713ea5acddf70d854365e882fd0a3b6d4508d50da25993b94c9878f1c3bc". You have to remove (or rename) that container to be able to reuse that name.
Run 'docker run --help' for more information
要再次啟動之前的容器,用 docker start web;要用新的設定重建,先 docker rm -f web 再 docker run。
停止:stop 與 kill 的差別
| 指令 | 行為 |
|---|---|
docker stop | 先送出 SIGTERM,讓程式自己收尾;等 10 秒還沒結束,再送 SIGKILL 強制結束 |
docker kill | 直接送出 SIGKILL,立刻結束 |
stop 讓程式有機會「好好收尾」,例如資料庫把資料寫回硬碟、網頁伺服器處理完進行中的請求。平常一律用 stop,只有程式卡住時才用 kill。
$ docker start web
web
$ docker kill web
web
$ docker ps -a --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" 14 seconds ago Exited (137) Less than a second ago web
被 kill 的容器結束代碼是 137(128 + 9,9 是 SIGKILL 的編號)。
為什麼 docker stop 有時要等 10 秒?
下面實際比較三個容器停止所花的時間:
$ time docker stop web
web
real 0m 0.10s
user 0m 0.01s
sys 0m 0.00s
$ docker run -d --name slow alpine:3.23 sh -c "sleep 300"
Unable to find image 'alpine:3.23' locally
3.23: Pulling from library/alpine
d0c1d894c237: Pull complete
Digest: sha256:85fe1e81d6758c208f3e1eed4338a1997e19d4be002d4dd32d3100c9a8c010a0
Status: Downloaded newer image for alpine:3.23
170d08a64bb8ec1519d183f8c20508d669358eea4d5a978fa4f4675ffcec41ab
$ time docker stop slow
slow
real 0m 10.17s
user 0m 0.00s
sys 0m 0.01s
$ docker run -d --name slow2 --init alpine:3.23 sh -c "sleep 300"
d1854be4790b5fc9375004e05708d27a27d56c2c540a8159424351985db20ef9
$ time docker stop slow2
slow2
real 0m 0.13s
user 0m 0.00s
sys 0m 0.01s
| 容器 | 停止時間 | 原因 |
|---|---|---|
| nginx | 0.10 秒 | nginx 收到 SIGTERM 會立刻正常關閉 |
sh -c "sleep 300" | 10.17 秒 | 主程序是 sh,以 PID 1 執行時會忽略 SIGTERM,Docker 等滿 10 秒才用 SIGKILL 強制結束 |
同上,加上 --init | 0.13 秒 | --init 用一個小型 init 程序當 PID 1,它會把訊號轉給子程序 |
Linux 對 PID 1 有一條特別的規則:PID 1 沒有自己處理 SIGTERM 時,這個訊號會被忽略。所以自己寫的映像檔如果每次 docker stop 都要等 10 秒,通常是主程序以 PID 1 執行、程式裡又沒有處理 SIGTERM。例如直接以 node server.js 當主程序的 Node.js 程式,實測同樣要等 10 秒,見 Dockerfile 指令大全 中的實際比較。解決方法是在程式中處理 SIGTERM,或加上 --init。
結束代碼
$ docker run --name exit3 alpine:3.23 sh -c "exit 3"
$ docker ps -a --filter name=exit3
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84996cca5ec5 alpine:3.23 "sh -c 'exit 3'" 1 second ago Exited (3) Less than a second ago exit3
$ docker inspect --format '{{.State.ExitCode}}' exit3
3
主程序以什麼代碼結束,Exited (...) 就顯示什麼。常見的代碼:
| 代碼 | 常見原因 |
|---|---|
0 | 正常結束 |
1 等其他數字 | 程式本身回報的錯誤,看 docker logs |
137 | 被 SIGKILL 結束:docker kill、docker stop 等太久,或記憶體不足被系統終止(OOM) |
143 | 收到 SIGTERM 後正常結束(128 + 15) |
暫停與恢復
$ docker start web
web
$ docker pause web
web
$ docker ps --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" 15 seconds ago Up Less than a second (Paused) 80/tcp web
$ docker unpause web
web
pause 會凍結容器內所有程序(不會結束它們),狀態顯示 (Paused)。很少用到,適合暫時釋放 CPU 但保留記憶體中的狀態。
刪除容器
$ docker rm web
Error response from daemon: cannot remove container "web": container is running: stop the container before removing or force remove
$ docker rm -f web
web
執行中的容器不能直接刪除,要先 stop,或用 -f 強制刪除(等於 kill 再 rm)。
一次清除所有已停止的容器
練習一陣子後,docker ps -a 會累積很多已停止的容器:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
75ee00dc4081 nginx:1.30 "/docker-entrypoint.…" 1 second ago Up Less than a second 80/tcp web
5dfb843c0b40 alpine:3.23 "sh -c 'echo startin…" 26 seconds ago Exited (1) 24 seconds ago flaky
84996cca5ec5 alpine:3.23 "sh -c 'exit 3'" 27 seconds ago Exited (3) 26 seconds ago exit3
d1854be4790b alpine:3.23 "sh -c 'sleep 300'" 29 seconds ago Exited (143) 28 seconds ago slow2
170d08a64bb8 alpine:3.23 "sh -c 'sleep 300'" 39 seconds ago Exited (137) 29 seconds ago slow
66e6f85c66e0 ubuntu:26.04 "/bin/bash" 43 seconds ago Exited (0) 42 seconds ago dreamy_solomon
$ docker container prune -f
Deleted Containers:
5dfb843c0b400146827fe138890ee4d62a24104cad5531aff4100fc471b0727f
84996cca5ec5bfe7ca51c6365b60ef89e49055cf06a3f0dd3a49f9e50e08d6f4
d1854be4790b5fc9375004e05708d27a27d56c2c540a8159424351985db20ef9
170d08a64bb8ec1519d183f8c20508d669358eea4d5a978fa4f4675ffcec41ab
66e6f85c66e0f2b586c5919a900a0e16d6f0fc49535d35fa7d30f77747f1ce0e
Total reclaimed space: 24.58kB
container prune 只刪除已停止的容器,執行中的 web 不受影響。也注意對照上面的結束代碼:slow 是被強制結束的 137,加了 --init 的 slow2 是正常收到 SIGTERM 的 143。
想讓一次性的容器結束後自動刪除,執行時加 --rm:
docker run --rm alpine:3.23 echo "用完就丟"
重啟策略:--restart
伺服器重開機或程式崩潰時,要不要自動重新啟動容器?用 --restart 設定:
| 值 | 行為 |
|---|---|
no | 預設值,不自動重啟 |
on-failure[:次數] | 只有結束代碼不是 0 時才重啟,可限制次數 |
always | 總是重啟;手動 stop 後,Docker 本身重新啟動時仍會再啟動它 |
unless-stopped | 類似 always,但手動 stop 過的就不會再自動啟動 |
實際看看 on-failure
下面這個容器每次啟動都會印出 starting 然後以代碼 1 結束,模擬一個不斷崩潰的程式:
$ docker run -d --name flaky --restart on-failure:3 alpine:3.23 sh -c "echo starting; exit 1"
5dfb843c0b400146827fe138890ee4d62a24104cad5531aff4100fc471b0727f
$ docker ps -a --filter name=flaky
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5dfb843c0b40 alpine:3.23 "sh -c 'echo startin…" 25 seconds ago Exited (1) 23 seconds ago flaky
$ docker inspect --format '{{.RestartCount}} {{.State.Status}} {{.State.ExitCode}}' flaky
3 exited 1
$ docker logs flaky
starting
starting
starting
starting
日誌中有 4 次 starting:第一次啟動,加上重啟 3 次(RestartCount 是 3),達到上限後就停在 Exited (1)。每次重啟之間 Docker 會逐漸拉長等待時間,避免瘋狂重啟拖垮主機。
修改已存在容器的重啟策略
一般的長期服務建議用 unless-stopped。已經在執行的容器也可以修改:
$ docker run -d --name web --restart unless-stopped nginx:1.30
75ee00dc4081f4c2849562fbd89edfdd9386839a161ea9d66956eaa41257d4cf
$ docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
unless-stopped
$ docker update --restart always web
web
$ docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
always
在 Linux 上要讓容器隨開機啟動,Docker 本身也必須開機自動啟動:
sudo systemctl enable docker。Docker Desktop 則要在設定中開啟「Start Docker Desktop when you sign in」。
指令整理
| 指令 | 作用 |
|---|---|
docker run | 建立並啟動新容器 |
docker create | 只建立 |
docker start / stop / restart | 啟動 / 停止 / 重啟已存在的容器 |
docker kill | 強制結束(SIGKILL) |
docker pause / unpause | 暫停 / 恢復 |
docker rm [-f] | 刪除容器 |
docker container prune | 刪除所有已停止的容器 |
docker ps [-a] | 列出容器(-a 含已停止) |
docker update --restart | 修改重啟策略 |
下一節完整整理 docker run 的常用參數。
重點回顧
- 容器的狀態:created、running、paused、exited、restarting。
- 容器在主程序結束時停止;
docker stop先送 SIGTERM,10 秒後才送 SIGKILL。 - 主程序是 PID 1 且沒有處理 SIGTERM 時,stop 會等滿 10 秒,可用
--init解決。 - 長期執行的服務加上
--restart unless-stopped。
動手練習
練習 1:用 docker run -d --name t1 alpine:3.23 sh -c "exit 3" 啟動容器,它的狀態和結束代碼是什麼?
答案
docker ps -a 顯示 Exited (3)。也可以用 docker inspect -f "{{.State.ExitCode}}" t1 取得結束代碼 3。
練習 2:啟動 docker run -d --name t2 alpine:3.23 sleep 300,分別用 time docker stop t2(記得先重新建立)和加上 --init 的版本比較停止所需時間。
提示
不加 --init 時,sleep 是 PID 1,忽略 SIGTERM,約 10 秒後才被強制結束;加上 --init 後,init 程序把訊號轉給 sleep,幾乎立刻停止。
常見問題
Exited (137) 是什麼意思?
137 = 128 + 9,表示容器被 SIGKILL 強制結束,可能是 docker kill、docker stop 等太久,或記憶體不足被系統終止。
伺服器重開機後,容器會自動啟動嗎?
要設定重啟策略(例如 --restart unless-stopped),而且 Docker 本身也要開機自動啟動(Linux 執行 sudo systemctl enable docker)。
暫停(pause)和停止(stop)有什麼不同?
pause 凍結容器內的所有程序,記憶體內容保留,恢復後從原來的狀態繼續;stop 讓程序結束,記憶體中的狀態就沒了。
更新於 2026年10月14日