容器化技術 ch05 容器生命週期:為什麼它一啟動就結束了?
下一章→
CH 05 日常必備

容器生命週期:為什麼它一啟動就結束了?

狀態機與指令對照容器的命 = PID 1 的命exec form vs shell form優雅關機與 SIGTERM常用指令的正確心智模型

新手最常見的兩個崩潰時刻:

  1. docker run ubuntu —— 容器啟動後立刻就結束了,什麼都沒發生。
  2. 部署新版本時 docker stop,結果卡了整整 10 秒才停,而且正在處理的請求全部斷線。

這兩件事看起來毫無關聯,其實是同一個原理的兩面:容器的生命,完全等於它裡面 PID 1 那個 process 的生命。

這一章先把生命週期的狀態機講清楚,然後深入 PID 1 這個「容器裡的一號人物」—— 它有什麼特殊責任、為什麼你的 Spring Boot 收不到關機訊號、以及一個只差幾個字元卻決定成敗的寫法差異(CMD java -jar app.jar vs CMD ["java", "-jar", "app.jar"])。

容器不是「開著」或「關著」兩種狀態而已,它有一個完整的生命週期。先建立這張地圖,之後每個指令的作用就有地方掛。

        docker create                docker start
(映像) ──────────→ [Created] ──────────────→ [Running]
                                                 │  ↑
                              docker pause  ─────┤  │ docker unpause
                                                 ↓  │
                                             [Paused]
                                                 │
                          docker stop / 主程式自己結束
                                                 ↓
                                            [Exited]
                                                 │ docker rm
                                                 ↓
                                            (消失,可寫層一併刪除)

五個狀態的意義

  • Created(已建立):容器的檔案系統、網路設定都準備好了,但裡面的程式還沒開始跑。
  • Running(執行中):PID 1 的程式正在執行。
  • Paused(暫停):process 被凍結(用 cgroup 的 freezer),記憶體內容保留著,可以解凍繼續。很少用。
  • Exited(已結束):裡面的程式已經結束(正常或異常)。注意:容器本身還在,可寫層還在,log 也還在——只是沒在跑。這就是 docker ps -a 看得到、docker ps 看不到的那些。
  • Removed(已刪除):docker rm 之後才真正消失,可寫層一併刪掉(ch03 講的資料就是在這一刻沒的)。

那 docker run 是什麼?

docker run = docker create + docker start,是一個方便的組合技。
所以「run 一個容器」實際上是「用映像造一個新容器,然後啟動它」——每次 run 都是一個全新的容器,不是把舊的叫醒。這是新手最常搞混的地方:反覆 docker run 會累積出一堆 Exited 的殭屍容器(用 docker ps -a 就看得到),而不是重複使用同一個。
⭐ 要「重新啟動同一個容器」用 docker start <名字>,不是 docker run。養成加 --name 的習慣(docker run --name myapp ...),之後所有指令都能用這個名字操作,不用複製那串亂碼 ID。臨時測試則加 --rm,結束後自動刪除,不留垃圾。

來看這個讓無數新手困惑的現象:

$ docker run ubuntu
$ docker ps
CONTAINER ID   IMAGE   STATUS
(空的,什麼都沒有)

$ docker ps -a
CONTAINER ID   IMAGE    STATUS
7f3a9c1b2e4d   ubuntu   Exited (0) 2 seconds ago    ← 已經結束了

「我明明啟動了一個 Ubuntu,怎麼馬上就死了?是不是壞了?」

核心規則:容器的生命 = 它裡面 PID 1 那個 process 的生命。PID 1 一結束,容器立刻結束。

推理過程

  • ubuntu 映像的預設指令是 bash。
  • 你沒有給它終端機(沒加 -it),bash 一啟動發現沒有輸入可讀,立刻正常結束。
  • PID 1 結束 → 容器結束。Exit code 0 代表「正常結束」,不是錯誤。

所以這不是壞掉,是它正確地做完了你叫它做的事——執行 bash,然後 bash 結束了。

正確的做法

# 想要進去互動:給它終端機
docker run -it ubuntu bash

# 想要它一直跑:讓 PID 1 是一個不會結束的程式
docker run -d nginx        # nginx 本來就會常駐
  • -i(interactive):保持標準輸入開著。
  • -t(tty):分配一個虛擬終端機。兩個要一起用才有互動 shell 的效果。
  • -d(detach):背景執行,指令立刻返回,不佔住你的終端機。

由此推導出一條 Dockerfile 鐵律

🚨 容器裡的主程式必須在「前景」執行,絕對不能 daemon 化。
傳統伺服器上你習慣 systemctl start nginx 讓服務跑到背景——在容器裡這樣做會直接自殺:啟動指令把服務丟到背景後自己結束了,PID 1 一死,容器就跟著死,那個背景服務也一起被清掉。

這就是為什麼官方 nginx 映像的啟動指令是 nginx -g 'daemon off;'——明確叫 nginx 不要進背景。同理,Java 應用直接 java -jar app.jar(本來就是前景),千萬不要寫成 nohup java -jar app.jar &。
💡 你也會看到有人用 tail -f /dev/null 或 sleep infinity 當作容器的主指令,目的就是「弄一個永遠不會結束的 PID 1,讓容器活著」,方便進去除錯。這是個 hack,理解原理後就知道它為什麼有效。

PID 1 在 Linux 裡不是普通的編號,它有兩個特殊責任。搞不清楚這個,你的服務每次部署都會硬生生斷線。

特殊責任一:預設不理會訊號

  • 一般 process 收到 SIGTERM(禮貌的結束請求)時,就算程式沒寫處理邏輯,核心也會用預設行為把它終止。
  • 但 PID 1 例外:核心對 PID 1 不套用預設訊號行為(因為在真實系統上 PID 1 是 init,隨便被殺掉整台機器就掛了)。所以 PID 1 如果自己沒有註冊訊號處理器,SIGTERM 就等於被完全忽略。

這造成 docker stop 卡 10 秒的現象

docker stop myapp
  ① Docker 送 SIGTERM 給容器的 PID 1 →「請你收工」
  ② 等待寬限期(預設 10 秒)
  ③ 時間到還沒死 → 送 SIGKILL(強制砍,無法被忽略)

如果你的程式收不到 SIGTERM,就會走完整整 10 秒然後被硬殺——正在處理的 HTTP 請求直接斷線、資料庫交易沒有 rollback、連線池沒有正常釋放。

真正的兇手:shell form vs exec form(差幾個字元,天差地遠)

Dockerfile 的 CMD 和 ENTRYPOINT 有兩種寫法:

# ❌ shell form(字串)
CMD java -jar /app/app.jar

# ✅ exec form(JSON 陣列)
CMD ["java", "-jar", "/app/app.jar"]

shell form 會被 Docker 自動改寫成 /bin/sh -c "java -jar /app/app.jar",於是容器裡變成:

PID 1: /bin/sh -c java -jar /app/app.jar   ← 收到 SIGTERM 的是它
  └─ PID 7: java -jar /app/app.jar          ← 你的應用在這裡
災難鏈:SIGTERM 送給 PID 1(那個 sh)→ sh 是 PID 1 且沒有訊號處理器 → 忽略 → 而且 sh 也不會把訊號轉發給子行程 → 你的 Java 完全不知道有人要它收工 → 10 秒後整組被 SIGKILL 強制砍掉。

你的 Spring Boot 明明實作了 graceful shutdown,卻永遠不會被觸發。

改用 exec form,Java 就直接是 PID 1,SIGTERM 直達 JVM,Spring Boot 的關機流程正常運作:

PID 1: java -jar /app/app.jar   ← 訊號直達,優雅關機生效

Spring Boot 的完整優雅關機設定

# application.yml
server:
  shutdown: graceful          # 停止收新請求,等現有請求做完
spring:
  lifecycle:
    timeout-per-shutdown-phase: 20s

搭配 docker stop --time=30(把寬限期拉長到 30 秒),確保應用有足夠時間收尾。兩邊的時間要對得上:Docker 的寬限期必須大於應用的關機逾時,否則還是會被硬殺。

特殊責任二:收養孤兒行程

  • 真實系統上,父行程死掉後留下的孤兒會被 PID 1(init)收養,並在它結束時負責回收,避免變成殭屍行程(zombie)。
  • 但你的 Java 應用當 PID 1 時,它不會做這件事——它不是 init。如果你的程式會 fork 出子行程(例如呼叫外部指令),久了就會累積殭屍行程佔用 PID 表。
  • 解法:docker run --init,Docker 會塞一個極小的 init 程式(tini)當 PID 1,由它負責轉發訊號與回收殭屍,你的程式當 PID 2。兩個問題一次解決。

學過前四章之後,這些參數不再是要背的咒語,而是有原理可推的開關。

docker run -d --name myapp \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=dev \
  -v app-data:/data \
  -m 512m --cpus 1.5 \
  --restart unless-stopped \
  myapp:1.0
參數作用對應原理(哪一章)
-d背景執行本章:不佔住終端機
-it互動式終端機本章:讓 shell 有輸入可讀而不結束
--name取名字本章+ch07:名字也是網路上的 DNS 名稱
--rm結束後自動刪除容器ch03:可寫層一併清掉,不留垃圾
-p 8080:8080連接埠映射ch02 network namespace/ch07 詳解
-e KEY=VAL設定環境變數ch12:設定與秘密注入的主要管道
-v 名稱:/路徑掛載 volumech03 可寫層會消失/ch06 詳解
-m / --cpus資源上限ch02:cgroup
--restart重啟策略本章下一段
--init塞一個 init 當 PID 1本章:訊號轉發+回收殭屍
-u 1000:1000以指定使用者執行ch02 安全/ch10 詳解

重啟策略(--restart):容器界的自動復活

  • no(預設):死了就死了。
  • on-failure[:次數]:只有非零結束碼才重啟,可限制次數。適合批次任務。
  • unless-stopped:常駐服務的最佳選擇——會一直重啟,但如果是你主動 docker stop 的,就尊重你的決定不再自動拉起(重開機後也不會擅自啟動)。
  • always:無論如何都重啟,連你手動停掉後、Docker 服務重啟時也會把它拉回來。
🚨 重啟策略不是萬靈丹:如果應用因為設定錯誤而啟動即崩潰,它會陷入無窮重啟迴圈(Docker 有指數退避,K8s 裡叫 CrashLoopBackOff)。看到容器不斷重啟,第一件事是 docker logs 查真正的死因,而不是加大重試次數。

docker logs —— 它其實只是在讀一個檔案

docker logs -f --tail 100 myapp      # 跟隨最後 100 行
docker logs --since 10m myapp        # 只看最近 10 分鐘
關鍵理解:docker logs 顯示的是容器 PID 1 的「標準輸出(stdout)與標準錯誤(stderr)」,Docker 把它們寫到宿主機上一個 JSON 檔案裡。

這個心智模型直接推導出兩個重要結論:

  • 如果你的應用把日誌寫進容器裡的檔案(例如 /var/log/app.log),docker logs 什麼都看不到,而且那個檔案會隨容器一起消失。容器化應用的日誌應該直接印到 stdout,讓外部的日誌系統去收集。(Spring Boot 預設就是印到 console,不要畫蛇添足去設定 file appender。)
  • 日誌檔會無限長大吃光磁碟——這是真實會發生的事故。生產環境務必設定輪替:
    docker run --log-opt max-size=10m --log-opt max-file=3 ...

docker exec —— 進去看看(但不要改東西)

docker exec -it myapp bash          # 開一個 shell 進去
docker exec myapp ls /app           # 只執行一個指令就出來
docker exec -u root -it myapp bash  # 以 root 身分進去(除錯用)
  • 它是在既有容器裡「多開」一個 process,跟 PID 1 並存。所以 exit 離開時只是結束那個 shell,容器不會停。
  • 對比 docker attach:attach 是「接上 PID 1 的終端機」,你在裡面按 Ctrl+C 會直接殺掉主程式、容器結束。幾乎所有情況都該用 exec 而不是 attach。
  • 如果映像很精簡(distroless、scratch),裡面連 bash 都沒有,exec 會失敗。這是刻意的取捨(ch10),除錯要改用 docker debug 或臨時掛一個含工具的容器進同一個 namespace。
  • 再提醒一次(ch01 的心態):exec 進去是為了觀察,不是為了修改。你在裡面裝的東西重建就消失。

其他日常指令

  • docker ps -a:列出所有容器(含已結束的)。容器「消失了」時的第一個排查點。
  • docker inspect myapp:傾印該容器的完整設定 JSON——IP、掛載、環境變數、OOMKilled 與 ExitCode(ch02 的 137 就在這裡確認)。
  • docker stats:即時看各容器的 CPU/記憶體用量,記憶體那欄會顯示「已用 / cgroup 上限」。
  • docker cp myapp:/app/heapdump.hprof ./:把容器裡的檔案複製出來(撈 heap dump、log 很好用)。
  • docker top myapp:看容器裡有哪些 process,可以驗證你的程式到底是不是 PID 1。
⭐ 停止相關指令的差別:docker stop(送 SIGTERM,等寬限期,再 SIGKILL)= 有禮貌;docker kill(直接 SIGKILL)= 沒禮貌,只在卡死時用;docker rm(刪掉已停止的容器,可寫層一併消失);docker rm -f(kill + rm 一次做完)。

Healthcheck:讓 Docker 知道「活著」不等於「能服務」

容器在 Running 狀態,只代表 PID 1 還活著。但你的 Spring Boot 可能正卡在啟動階段、或資料庫連線池已經爆掉——process 活著,服務其實已經廢了。

# Dockerfile 裡宣告
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1
  • --start-period:啟動寬限期,對 Java 特別重要——Spring Boot 冷啟動動輒 30~60 秒,這段期間檢查失敗不算數,否則容器會在啟動途中被判死刑。
  • 狀態會顯示在 docker ps 的 STATUS 欄:(healthy) / (unhealthy)。
  • 在 Compose 裡它更關鍵:depends_on 搭配 condition: service_healthy,才能真正做到「等資料庫準備好再啟動應用」(ch11 會詳細拆解這個大坑)。

Docker 會偷偷吃掉你的磁碟

用久了突然發現 C 槽/根目錄滿了,元兇通常是這四類:

  • 停止但沒刪除的容器(可寫層還在)——docker ps -a 一看嚇一跳。
  • 懸空映像(dangling images):重新 build 後被取代、標籤變成 <none> 的舊映像層。
  • 沒人用的 volume:容器刪了但 volume 留著(這是刻意的保護,避免誤刪資料)。
  • 建置快取:BuildKit 的快取可以長到數十 GB。
docker system df           # 先看誰在吃空間(強烈建議先跑這個)
docker system prune        # 清停止的容器、無用網路、懸空映像
docker system prune -a     # 連「沒有容器在用的映像」都清掉
docker builder prune       # 清建置快取
🚨 docker system prune --volumes 會刪掉沒被使用的 volume——你的資料庫資料很可能就在裡面。手滑一次就是整組開發資料歸零。養成習慣:先 docker system df 看清楚,再決定清什麼;--volumes 這個旗標請當作危險品對待。
生命週期指令對照表
指令做什麼狀態變化注意事項
docker runcreate + start(造新容器並啟動)映像 → Running每次都是全新容器,不是叫醒舊的
docker start啟動既有的容器Exited/Created → Running要重啟同一個容器用這個,不是 run
docker stop送 SIGTERM,等寬限期後 SIGKILLRunning → Exited預設等 10 秒;--time 可調
docker kill直接送 SIGKILLRunning → Exited沒有收尾機會,只在卡死時用
docker restartstop + startRunning → Running可寫層保留,資料還在
docker rm刪除容器Exited → 消失可寫層一併刪除,沒 volume 的資料就沒了
docker exec在既有容器裡多開一個 process不變exit 只結束那個 shell,容器不會停
docker attach接上 PID 1 的終端機可能變 ExitedCtrl+C 會殺掉主程式!幾乎都該改用 exec
exec form vs shell form:差幾個字元,決定服務會不會斷線
面向shell form(字串)exec form(JSON 陣列)
寫法CMD java -jar app.jarCMD ["java", "-jar", "app.jar"]
實際執行/bin/sh -c "java -jar app.jar"直接 exec java
誰是 PID 1/bin/sh(你的程式是 PID 7)你的程式本身
收得到 SIGTERM 嗎收不到——sh 忽略且不轉發收得到,直達應用
docker stop 的結果卡 10 秒後被 SIGKILL 硬殺,請求斷線觸發優雅關機,請求做完才退出
能不能用 $變數、管線可以(有 shell 幫忙展開)不行(除非自己寫 sh -c)
建議避免用在主程式預設就用這個
重啟策略(--restart)選哪個
策略行為適用場景
no(預設)結束就結束,不重啟一次性任務、本機隨手測試
on-failure[:n]只有非零結束碼才重啟,可限次數批次工作、允許失敗重試的任務
unless-stopped一直重啟,但尊重你手動停止的決定常駐服務的最佳預設
always無論如何都拉起來,連手動停掉的也會在 Docker 重啟時復活絕對不能中斷的基礎服務

練習題 點選選項查看解析

0 / 9
01 / 9
docker run ubuntu 之後容器立刻變成 Exited (0)。原因是?
A 映像損壞
B 容器的生命等於 PID 1 的生命。ubuntu 的預設指令是 bash,沒有 -it 就沒有輸入可讀,bash 立刻正常結束,容器隨之結束
C 記憶體不足被 OOM Killer 殺掉
D 缺少網路設定
解析
Exit code 0 代表「正常結束」不是錯誤——它正確地執行了 bash,而 bash 做完就退出了。要互動請加 -it(-i 保持 stdin 開著、-t 分配虛擬終端機);要它常駐就讓 PID 1 是個不會結束的程式,例如 nginx。
02 / 9
為什麼容器裡的服務絕對不能 daemon 化(跑到背景)?
A 背景執行會消耗更多記憶體
B 啟動指令把服務丟到背景後自己就結束了,PID 1 一死容器立刻結束,那個背景服務也會被一起清掉
C Docker 不支援背景程序
D 會導致日誌無法寫入
解析
這是傳統伺服器習慣帶進容器的頭號錯誤。官方 nginx 映像用 nginx -g 'daemon off;' 就是明確要求它留在前景。Java 應用直接 java -jar app.jar(本來就前景),千萬別寫成 nohup ... &。
03 / 9
你的 Dockerfile 寫 CMD java -jar app.jar(shell form),docker stop 時卻總是卡 10 秒才停。為什麼?
A JVM 關閉本來就需要 10 秒
B shell form 會被改寫成 /bin/sh -c "...",PID 1 變成 sh;PID 1 沒有訊號處理器就忽略 SIGTERM,而且 sh 不會轉發給子行程,於是 Java 完全不知道要收工,10 秒後整組被 SIGKILL
C 網路連線需要等待逾時
D Docker 版本的 bug
解析
這是 Java 開發者最常踩、也最難自己發現的坑:你的 Spring Boot 明明實作了 graceful shutdown 卻永遠不會觸發。改成 exec form(JSON 陣列)CMD ["java", "-jar", "app.jar"],Java 就直接是 PID 1,SIGTERM 直達 JVM。用 docker top 可以驗證你的程式到底是不是 PID 1。
04 / 9
關於 PID 1 在 Linux 中的特殊性,正確的是?
A PID 1 執行速度比較快
B 核心不對 PID 1 套用預設訊號行為——它若沒註冊訊號處理器,SIGTERM 就等於被完全忽略;此外它還負責收養孤兒行程並回收殭屍
C PID 1 擁有 root 權限
D PID 1 不能被停止
解析
這個保護機制的原意是避免真實系統的 init 被隨便殺死導致整台機器掛掉,但搬到容器裡就變成陷阱。順帶一提第二個責任(回收殭屍)你的 Java 應用也不會做,若程式會 fork 子行程就會累積殭屍。兩個問題都可用 docker run --init 解決(塞一個 tini 當 PID 1)。
05 / 9
docker exec 和 docker attach 的關鍵差別是?
A 沒有差別,只是別名
B exec 是在容器裡「多開」一個 process(離開不影響容器);attach 是接上 PID 1 的終端機,在裡面按 Ctrl+C 會直接殺掉主程式導致容器結束
C exec 只能執行單一指令
D attach 速度比較快
解析
幾乎所有情況都該用 exec。exec 開的 shell 與 PID 1 並存,exit 只結束那個 shell。另外提醒:exec 進去是為了觀察不是修改——你在裡面裝的東西重建就消失(ch01 的不可變思維)。精簡映像(distroless)連 bash 都沒有,exec 會直接失敗,那是刻意的取捨。
06 / 9
你的應用把日誌寫到容器內的 /var/log/app.log,結果 docker logs 什麼都看不到。正確作法是?
A 改用 docker exec 進去 tail 那個檔案就好
B 容器化應用應該把日誌直接印到 stdout/stderr,讓 Docker 與外部日誌系統收集——寫進容器內檔案的日誌不但看不到,還會隨容器消失
C 把 log 檔設為唯讀
D 增加日誌等級
解析
docker logs 顯示的就是 PID 1 的 stdout/stderr(Docker 寫到宿主機上的 JSON 檔)。Spring Boot 預設印到 console,不要畫蛇添足去設 file appender。另外記得設定輪替(--log-opt max-size=10m --log-opt max-file=3),否則日誌檔會無限長大吃光磁碟。
07 / 9
常駐服務的重啟策略,最推薦哪一個?
A no
B unless-stopped——會自動重啟,但你主動 docker stop 之後就尊重你的決定不再擅自拉起
C always,因為最保險
D on-failure:3
解析
always 的問題是連你手動停掉的容器都會在 Docker 服務重啟時復活,維護時很困擾。另外要注意:重啟策略不是萬靈丹——設定錯誤導致啟動即崩潰時會陷入無窮重啟(K8s 裡的 CrashLoopBackOff)。看到不斷重啟,第一件事是 docker logs 查真正死因。
08 / 9
為 Spring Boot 設定 HEALTHCHECK 時,--start-period 這個參數為什麼特別重要?
A 它決定健康檢查的逾時時間
B 它是啟動寬限期:Spring Boot 冷啟動動輒 30~60 秒,這段期間的檢查失敗不計入失敗次數,否則容器會在還沒啟動完成時就被判定 unhealthy
C 它設定檢查的間隔頻率
D 它指定要檢查哪個 port
解析
沒設 start-period 的 Java 容器常常在啟動途中就被判死刑。健康檢查在 Compose 裡更關鍵:depends_on 搭配 condition: service_healthy 才能真正做到「等資料庫準備好再啟動應用」——ch11 會拆解這個大坑。
09 / 9
磁碟被 Docker 吃光了,下列哪個操作風險最高?
A docker system df
B docker system prune --volumes——會刪掉沒被使用的 volume,你的資料庫開發資料很可能就在裡面
C docker builder prune
D docker ps -a
解析
volume 在容器刪除後被保留是「刻意的保護」,避免誤刪資料;--volumes 這個旗標正好把保護拆掉。安全流程:先 docker system df 看清楚誰在吃空間,再決定清什麼。一般清理用 docker system prune(停止的容器、無用網路、懸空映像)通常就夠了。

點擊卡片翻面查看答案,共 11 張。

QUESTION
容器生命週期的五個狀態?
點擊翻面
ANSWER
Created → Running →(Paused)→ Exited → Removed
Exited 時容器還在(可寫層、log 都在),只是沒跑
docker rm 之後才真的消失
點擊翻回
QUESTION
docker run 等於什麼?
點擊翻面
ANSWER
docker create + docker start
每次 run 都是造一個全新容器,不是叫醒舊的
要重啟同一個容器請用 docker start <名字>
點擊翻回
QUESTION
容器生命的鐵律?
點擊翻面
ANSWER
容器的生命 = 它裡面 PID 1 那個 process 的生命
PID 1 結束 → 容器立刻結束
(所以主程式必須前景執行,絕不能 daemon 化)
點擊翻回
QUESTION
docker run ubuntu 為何立刻結束?
點擊翻面
ANSWER
預設指令是 bash,沒 -it 就沒有輸入可讀 → 立刻正常結束(exit 0)
-i 保持 stdin、-t 分配虛擬終端機,要一起用
點擊翻回
QUESTION
PID 1 的兩個特殊責任?
點擊翻面
ANSWER
① 核心不套用預設訊號行為 → 沒註冊處理器就忽略 SIGTERM
② 負責收養孤兒、回收殭屍行程(但你的應用不會做)
兩者都可用 docker run --init(tini)解決
點擊翻回
QUESTION
shell form vs exec form 的差別?
點擊翻面
ANSWER
shell form:CMD java -jar app.jar → 被包成 /bin/sh -c,sh 變 PID 1,SIGTERM 收不到也不轉發
exec form:CMD ["java","-jar","app.jar"] → 應用直接是 PID 1,訊號直達
→ 主程式一律用 exec form
點擊翻回
QUESTION
docker stop 的三個步驟?
點擊翻面
ANSWER
① 送 SIGTERM(請你收工)
② 等寬限期(預設 10 秒,--time 可調)
③ 還沒死就送 SIGKILL(無法被忽略)
Docker 寬限期要大於應用的關機逾時
點擊翻回
QUESTION
exec vs attach?
點擊翻面
ANSWER
exec:在容器裡多開一個 process,exit 不影響容器 ✅
attach:接上 PID 1 的終端機,Ctrl+C 會殺掉主程式 ⚠️
幾乎所有情況都用 exec
點擊翻回
QUESTION
docker logs 顯示的是什麼?
點擊翻面
ANSWER
PID 1 的 stdout / stderr(Docker 寫到宿主機的 JSON 檔)
→ 日誌寫進容器內檔案就看不到、還會隨容器消失
→ 容器化應用一律印到 stdout,並設定輪替避免撐爆磁碟
點擊翻回
QUESTION
HEALTHCHECK 的 --start-period 為何重要?
點擊翻面
ANSWER
啟動寬限期:Java 冷啟動 30~60 秒,這期間失敗不算數
沒設會導致容器啟動途中就被判 unhealthy
搭配 compose 的 condition: service_healthy 才能真的等 DB 就緒
點擊翻回
QUESTION
清理 Docker 磁碟的安全流程?
點擊翻面
ANSWER
先 docker system df 看誰在吃空間
docker system prune(容器/網路/懸空映像)
docker builder prune(建置快取)
⚠️ --volumes 會刪掉資料,當危險品對待
點擊翻回