背景
Docker 容器啟動(dòng)失敗是日常運(yùn)維中最常遇到的問題之一。和物理機(jī)/虛擬機(jī)不同,容器啟動(dòng)失敗的原因更加多樣化——可能是鏡像問題、配置錯(cuò)誤、資源不足、網(wǎng)絡(luò)不通、權(quán)限限制、健康檢查失敗、依賴服務(wù)未就緒等多種原因。容器退出后docker ps看不到(除非加-a參數(shù)),日志也可能不夠詳細(xì),這讓排查變得有一定門檻。
本文系統(tǒng)梳理 Docker 容器啟動(dòng)失敗的常見原因、排查思路和具體命令,形成一套可直接照著操作的排查流程。適合初中級(jí)運(yùn)維工程師在實(shí)際工作中對(duì)照使用。
排查總流程
當(dāng)一個(gè)容器啟動(dòng)失敗時(shí),按以下順序排查:
第一步:確認(rèn)容器狀態(tài) └─ docker ps -a | grep第二步:查看容器退出信息 └─ docker logs └─ docker inspect 第三步:定位根因 ├─ 鏡像問題(鏡像不存在、損壞、tag 錯(cuò)誤) ├─ 配置問題(端口占用、環(huán)境變量缺失、命令錯(cuò)誤) ├─ 資源問題(內(nèi)存不足、磁盤空間不足) ├─ 權(quán)限問題(SELinux/AppArmor、用戶權(quán)限、掛載權(quán)限) ├─ 網(wǎng)絡(luò)問題(DNS、端口映射、overlay 網(wǎng)絡(luò)) ├─ 依賴問題(依賴的服務(wù)未啟動(dòng)、數(shù)據(jù)卷未就緒) └─ 健康檢查(健康檢查失敗導(dǎo)致重啟循環(huán)) 第四步:修復(fù)并驗(yàn)證 └─ docker run / docker start
第一步:確認(rèn)容器狀態(tài)
基本狀態(tài)查看
# 查看所有容器(包括已退出的) docker ps -a # 查看特定容器的簡(jiǎn)要狀態(tài) docker ps -a | grep# 格式化輸出(更清晰) docker ps -a --format"table {{.ID}} {{.Names}} {{.Status}} {{.Image}}"
容器的 Status 字段會(huì)顯示類似這樣的信息:
Exited (1) 5 minutes ago:以退出碼 1 退出了
Created:創(chuàng)建了但還沒啟動(dòng)
Up 2 hours:正常運(yùn)行中
Restarting (1) (starting):正在重啟(通常意味著不斷崩潰重啟)
快速判斷退出原因
# 一行命令看容器狀態(tài)、退出碼、啟動(dòng)命令 docker inspect--format='{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.Config.Cmd}}' # 查看完整狀態(tài)信息 docker inspect | jq'.State'
第二步:查看容器日志
docker logs
# 查看容器日志(stdout + stderr) docker logs# 實(shí)時(shí)跟蹤日志 docker logs -f # 查看最近 100 行 docker logs --tail 100 # 顯示時(shí)間戳 docker logs --timestamps # 查看特定時(shí)間的日志 docker logs --since"2024-01-15T1000" docker logs --since 30m # 如果日志量很大,查看 ERROR 級(jí)別日志 docker logs 2>&1 | grep -i error
為什么 docker logs 可能看不到日志
這是很多人遇到的問題:docker logs什么都看不到。原因是:
容器啟動(dòng)命令沒有產(chǎn)生 stdout/stderr:如果CMD或ENTRYPOINT把輸出重定向到文件(如CMD ["python", "app.py", "> /var/log/app.log"]),日志就不會(huì)到 docker log driver
日志驅(qū)動(dòng)不對(duì):有些容器使用json-file日志驅(qū)動(dòng),有些用syslog、fluentd、awslogs等
日志被輪轉(zhuǎn)清理了
# 檢查容器的日志驅(qū)動(dòng) docker inspect--format='{{.HostConfig.LogConfig.Type}}' # 如果是 json-file,可以直接查看容器內(nèi)的日志文件 cat /var/lib/docker/containers/ /*-json.log | tail -100
第三步:查看容器詳細(xì)信息
docker inspect
docker inspect是排查容器問題的終極工具,能看到容器從創(chuàng)建到運(yùn)行的所有配置細(xì)節(jié)。
# 查看完整 inspect 輸出(內(nèi)容很多) docker inspect# 用 jq 提取特定字段 docker inspect --format='{{json .State}}'| jq . docker inspect --format='{{json .Config}}'| jq . docker inspect --format='{{json .HostConfig}}'| jq . # 常見關(guān)鍵字段提取 docker inspect --format=' State: {{.State.Status}} ExitCode: {{.State.ExitCode}} OOMKilled: {{.State.OOMKilled}} Error: {{.State.Error}} StartedAt: {{.State.StartedAt}} FinishedAt: {{.State.FinishedAt}} Path: {{.Path}} Args: {{.Args}} WorkingDir: {{.Config.WorkingDir}} Cmd: {{.Config.Cmd}} Entrypoint: {{.Config.Entrypoint}} Env: {{range .Config.Env}}{{.}} {{end}} '
常見 State 狀態(tài)及其含義
{
"Status":"exited",
"Running":false,
"ExitCode":127, // 退出碼:127=命令找不到, 1=一般錯(cuò)誤, 137=SIGKILL (OOM), 139=SIGSEGV
"OOMKilled":false,
"Dead":false,
"Error":"", // 如果有錯(cuò)誤,這里會(huì)顯示
"StartedAt":"...",
"FinishedAt":"..."
}
退出碼含義:
| 退出碼 | 含義 | 常見原因 |
|---|---|---|
| 0 | 正常退出 | 進(jìn)程執(zhí)行完成 |
| 1 | 一般錯(cuò)誤 | 應(yīng)用自己的錯(cuò)誤(配置錯(cuò)誤、參數(shù)錯(cuò)誤) |
| 125 | Docker daemon 錯(cuò)誤 | 如--memory超限導(dǎo)致被 daemon 殺掉 |
| 126 | 命令無法執(zhí)行 | CMD/ENTRYPOINT 權(quán)限問題或路徑錯(cuò)誤 |
| 127 | 命令找不到 | PATH 問題或二進(jìn)制文件不存在 |
| 137 | SIGKILL (128+9) | 被 SIGKILL 殺掉,通常是 OOMKill |
| 139 | SIGSEGV (128+11) | 段錯(cuò)誤,內(nèi)存越界訪問 |
| 143 | SIGTERM (128+15) | 優(yōu)雅退出(docker stop 觸發(fā)) |
第四步:分類排查
4.1 鏡像問題
現(xiàn)象
Error: image nginx:1.24 not found Layer already exists docker: Error response from daemon: manifestforxxx not found
排查命令
# 查看本地鏡像 docker images # 查看鏡像詳細(xì)信息 docker inspect nginx:1.24 # 拉取鏡像 docker pull nginx:1.24 # 檢查鏡像 tag 是否存在 docker manifest inspect nginx:1.24 # 清理懸空鏡像 docker image prune -f
常見場(chǎng)景
tag 寫成 latest 但鏡像沒更新:構(gòu)建了新鏡像但沒 push 或沒打 tag
registry 地址錯(cuò)誤:私有倉(cāng)庫地址寫錯(cuò)了
鏡像被刪除:其他機(jī)器 push 覆蓋了 tag,但本地還在用舊的 digest
跨架構(gòu)問題:在 ARM 機(jī)器上拉取 x86 鏡像(manifest 不兼容)
# 檢查鏡像架構(gòu) docker inspect| grep Architecture # 查看鏡像的完整 digest docker images --digests
修復(fù)方案
# 重新拉取最新鏡像 docker pull: # 如果是私有倉(cāng)庫 docker login registry.example.com docker pull registry.example.com/my-app:v1.2.3 # 如果鏡像 digest 變了導(dǎo)致啟動(dòng)失敗,回滾到舊 digest docker images --digests | grep docker run --rm @sha256:xxxxdigestxxxx
4.2 配置問題
4.2.1 端口占用
現(xiàn)象
docker: Error response from daemon: Ports are not available: bindaddress port alreadyinuse.
排查命令
# 檢查哪個(gè)進(jìn)程占用了端口 # Linux ss -tlnp | grep :80 netstat -tlnp | grep :80 # macOS(沒有 ss/netstat) lsof -i :80 # 查看 dockerd 當(dāng)前監(jiān)聽的端口 ps aux | grep dockerd
修復(fù)方案
# 方案一:換一個(gè)宿主機(jī)端口
docker run -p 8080:80 nginx
# 方案二:停止占用端口的服務(wù)
systemctl stop nginx
kill$(lsof -t i:80)
# 方案三:檢查是否有其他容器占用了同一個(gè)端口
docker ps --format"{{.Names}} {{.Ports}}"
4.2.2 環(huán)境變量缺失或錯(cuò)誤
現(xiàn)象
# 應(yīng)用日志 FATAL: Required environment variable DATABASE_URL is notset # 或者應(yīng)用直接退出,docker logs 看不到輸出 # 容器退出碼可能是 0(因?yàn)槭?shell 腳本檢測(cè)到缺失后 exit)
排查命令
# 查看容器的環(huán)境變量配置 docker inspect--format='{{range .Config.Env}}{{.}} {{end}}' # 對(duì)比預(yù)期和實(shí)際 docker inspect | jq'.Config.Env' # 檢查容器是否使用了 .env 文件 docker inspect | grep -i env # 如果容器還沒啟動(dòng),用 --env-file 檢查 docker run --rm --env-file .env my-app:latest env | grep DATABASE
修復(fù)方案
# 運(yùn)行命令傳入環(huán)境變量 docker run -e"DATABASE_URL=postgres://user:pass@host:5432/db"my-app # docker-compose 方式 docker-compose run -e"DATABASE_URL=..."app # 檢查 .env 文件是否存在、格式是否正確 cat .env
4.2.3 啟動(dòng)命令錯(cuò)誤
現(xiàn)象
# 退出碼 127 或 126 docker: Error response from daemon: OCI runtime create failed: container_linux.go # 或應(yīng)用啟動(dòng)后立即退出,日志顯示 "command not found"
排查命令
# 查看容器配置的 CMD docker inspect--format='{{.Config.Cmd}}' # 查看容器配置的 ENTRYPOINT docker inspect --format='{{.Config.Entrypoint}}' # 手動(dòng)測(cè)試命令是否正確 docker run --rm # 進(jìn)入鏡像內(nèi)測(cè)試 docker run --rm -it sh # 然后手動(dòng)執(zhí)行 CMD 中的命令,看是否報(bào)錯(cuò)
常見錯(cuò)誤
# 錯(cuò)誤:ENTRYPOINT 和 CMD 順序反了 # 正確寫法: ENTRYPOINT ["python","app.py"] CMD ["--help"] # 不要寫 shell 形式(信號(hào)處理問題) # CMD python app.py # 不好 # 路徑錯(cuò)誤 # CMD ["app.py"] # 如果 WORKDIR 不是 /app 會(huì)找不到 CMD ["/app/app.py"]
4.3 資源問題
4.3.1 內(nèi)存不足(OOM)
現(xiàn)象
# docker logs 可能看不到任何輸出 # docker inspect 顯示 OOMKilled: true # 退出碼 137 或 143
排查命令
# 檢查容器是否被 OOMKilled docker inspect| grep OOMKilled # 檢查宿主機(jī)內(nèi)存 free -h # 查看 cgroup 內(nèi)存限制 cat /sys/fs/cgroup/memory/docker/ /memory.limit_in_bytes cat /sys/fs/cgroup/memory/docker/ /memory.usage_in_bytes # 查看 dmesg 中的 OOM 記錄 dmesg | grep -i oom | tail -20 journalctl | grep -i oom | tail -20
修復(fù)方案
# 增加內(nèi)存限制 docker run --memory=1g my-app # 或者去掉限制(不推薦生產(chǎn)環(huán)境) docker run --memory=""my-app # 檢查應(yīng)用本身的內(nèi)存問題(內(nèi)存泄漏) docker stats --no-stream
4.3.2 磁盤空間不足
現(xiàn)象
no space left on device Error: disk quota exceeded docker: writing tmp: no space left on device
排查命令
# 檢查磁盤使用 df -h # 檢查 Docker 數(shù)據(jù)目錄所在分區(qū) df -h /var/lib/docker # 查看 Docker 資源占用 docker system df # 查看具體容器占用 docker ps -s | sort -k3 -rh | head # 清理 Docker 資源 docker system prune -af docker builder prune -af
修復(fù)方案
# 清理未使用的資源 docker system prune -a --volumes # 清理特定容器日志 > /var/lib/docker/containers//*-json.log # 配置日志輪轉(zhuǎn)(/etc/docker/daemon.json) { "log-driver":"json-file", "log-opts": { "max-size":"100m", "max-file":"3" } }
4.4 權(quán)限問題
4.4.1 SELinux / AppArmor 阻止操作
現(xiàn)象
permission denied: /var/www/html docker: Error response from daemon: OCI runtime create failed: ... operation not permitted
排查命令
# 檢查 SELinux 狀態(tài)(CentOS/RHEL) getenforce # Enforcing 表示 SELinux 啟用 # 查看 SELinux 拒絕日志 ausearch -m AVC -ts recent # 檢查 AppArmor 狀態(tài)(Ubuntu) aa-status apparmor_parser -r /etc/apparmor.d/* # 查看容器是否以特權(quán)模式運(yùn)行 docker inspect| grep Privileged
修復(fù)方案
# 方案一:臨時(shí)關(guān)閉 SELinux(不推薦生產(chǎn)環(huán)境) setenforce 0 # 方案二:使用 --privileged(授予所有能力,不推薦) docker run --privileged my-app # 方案三:正確配置 SELinux 標(biāo)簽(推薦) docker run -v /data:/data:Z my-app # :Z 標(biāo)簽會(huì)為容器自動(dòng)設(shè)置正確的 SELinux 上下文 # 方案四:禁用 AppArmor 某個(gè)配置文件 # 臨時(shí)測(cè)試用,不推薦生產(chǎn)環(huán)境 docker run --security-opt apparmor=unconfined my-app
4.4.2 文件系統(tǒng)權(quán)限
現(xiàn)象
cannot create directory'/var/log/xxx': Permission denied read-only file system
排查命令
# 檢查容器的只讀文件系統(tǒng)設(shè)置 docker inspect| grep ReadonlyRootfs # 檢查掛載卷的權(quán)限 ls -la /var/lib/docker/volumes/ /_data # 檢查容器運(yùn)行用戶 docker inspect | grep -E"User|WorkingDir"
修復(fù)方案
# 在 Dockerfile 里設(shè)置正確的工作目錄和用戶 WORKDIR /app RUN chown -R appuser:appuser /app # 運(yùn)行容器時(shí)指定用戶 docker run -u appuser my-app # 如果需要以 root 運(yùn)行但訪問掛載目錄 docker run -u root --privileged my-app
4.5 網(wǎng)絡(luò)問題
4.5.1 依賴服務(wù)未就緒(啟動(dòng)順序)
現(xiàn)象
# 容器啟動(dòng)了,但連接數(shù)據(jù)庫失敗 # 應(yīng)用日志:dial tcp 192.168.1.21 connection refused # 容器可能處于 Restarting 狀態(tài) # 這是最常見的"容器啟動(dòng)失敗但 logs 看起來正常"的場(chǎng)景
排查命令
# 檢查依賴服務(wù)的端口是否可達(dá) dockerexecnc -zv db-host 5432 dockerexec curl -v http://api-host:8080/health # 檢查 DNS 解析 dockerexec nslookup db-host dockerexec cat /etc/resolv.conf # 檢查容器網(wǎng)絡(luò) docker inspect | grep -A 10"Networks"
修復(fù)方案
方案一:使用 depends_on(僅解決啟動(dòng)順序,不解決等待)
# docker-compose.yml services: app: image:my-app depends_on: -db -redis db: image:postgres:15 redis: image:redis:alpine
方案二:使用 healthcheck + depends_on condition
services: db: image:postgres:15 healthcheck: test:["CMD-SHELL","pg_isready -U postgres"] interval:5s timeout:3s retries:5 app: image:my-app depends_on: db: condition:service_healthy
方案三:應(yīng)用層實(shí)現(xiàn)重試機(jī)制
# Python 示例:帶重試的數(shù)據(jù)庫連接
importtime
importpsycopg2
defconnect_with_retry(max_retries=10, delay=5):
foriinrange(max_retries):
try:
conn = psycopg2.connect(os.environ['DATABASE_URL'])
returnconn
exceptpsycopg2.OperationalErrorase:
print(f"Attempt{i+1}failed:{e}")
time.sleep(delay)
raiseException("Could not connect to database after retries")
4.5.2 健康檢查失敗導(dǎo)致重啟循環(huán)
現(xiàn)象
# docker ps 顯示容器不斷重啟 # docker inspect 顯示 "Healthcheck" 狀態(tài)非 healthy # 應(yīng)用日志正常,但健康檢查一直失敗
排查命令
# 查看容器的健康檢查配置 docker inspect| grep -A 10"Health" # 查看健康檢查日志 docker inspect --format='{{json .State.Health}}' | jq . # 查看健康檢查的日志輸出 docker inspect | grep -A 5"Log"
常見原因和修復(fù)
# docker-compose.yml 中的健康檢查配置 services: app: image:my-app healthcheck: test:["CMD","curl","-f","http://localhost:8080/health"] interval:30s timeout:10s retries:3 start_period:40s # 啟動(dòng)初期的寬限期
常見問題:
curl不在容器內(nèi),需要安裝或改用wget
健康檢查路徑返回的是非 200 狀態(tài)碼(如 401、403),需要調(diào)整判斷條件
start_period太短,應(yīng)用啟動(dòng)慢,還沒 Ready 就開始健康檢查了
# 測(cè)試健康檢查命令 dockerexeccurl -f http://localhost:8080/health ||exit1
4.6 數(shù)據(jù)卷問題
4.6.1 掛載的宿主機(jī)目錄不存在
現(xiàn)象
docker: Error response from daemon: invalid mount configfortype"bind": bindsourcepath does not exist: /data/logs.
排查命令
# 檢查宿主機(jī)目錄是否存在 ls -la /data/logs # 檢查宿主機(jī)目錄權(quán)限 ls -la /data # 檢查 Docker 數(shù)據(jù)卷 docker volume ls docker volume inspect
修復(fù)方案
# 創(chuàng)建目錄 mkdir -p /data/logs chmod 755 /data/logs # 如果用 docker-compose,用 volumes_from 或者預(yù)先創(chuàng)建 named volume # named volume 會(huì)在不存在時(shí)自動(dòng)創(chuàng)建 docker volume create my-data
4.6.2 數(shù)據(jù)卷權(quán)限覆蓋問題
現(xiàn)象
# 容器啟動(dòng)后,目錄是空的(宿主機(jī)目錄內(nèi)容被覆蓋) # 或者相反:宿主機(jī)目錄內(nèi)容沒有反映到容器內(nèi)
原理
bind mount 會(huì)完全覆蓋容器內(nèi)的目標(biāo)目錄。如果宿主機(jī)/data有內(nèi)容,容器內(nèi)/data原來的內(nèi)容就不可見了。
修復(fù)方案
# 不要 bind mount 到已有重要數(shù)據(jù)的目錄 # 用空目錄或者專門的目錄 # 如果需要同時(shí)看到宿主機(jī)和容器內(nèi)的內(nèi)容,用 volumes(named volume) # docker-compose 示例 services: app: volumes: - app_data:/var/lib/app volumes: app_data: driver:local
第五步:完整復(fù)現(xiàn)場(chǎng)景練習(xí)
場(chǎng)景一:MySQL 容器啟動(dòng)失敗
# 現(xiàn)象:MySQL 容器啟動(dòng)后立即退出,docker logs 顯示密碼錯(cuò)誤 # 排查步驟 docker ps -a | grep mysql docker logs# 常見原因 # 1. 密碼包含特殊字符,環(huán)境變量解析問題 # 2. 初始化腳本失敗 # 3. 數(shù)據(jù)目錄權(quán)限問題 # 修復(fù) docker run -e MYSQL_ROOT_PASSWORD='MyPass@123!'mysql:8.0 # 驗(yàn)證 dockerexec-it mysql -uroot -p'MyPass@123!'-e"SELECT VERSION();"
場(chǎng)景二:Redis 容器無法綁定端口
# 現(xiàn)象:docker logs 顯示 "Creating Server TCP listening socket * bind: Cannot assign requested address" # 原因:宿主機(jī)沒有 6379 端口,或者端口被占用了 # 排查 ss -tlnp | grep 6379 # 修復(fù) docker run -p 6379:6379 redis:alpine
場(chǎng)景三:Nginx 容器無法訪問后端
# 現(xiàn)象:Nginx 啟動(dòng)成功,但 502 Bad Gateway # 排查 dockerexeccat /etc/nginx/conf.d/default.conf dockerexec ping backend-app dockerexec nc -zv backend-app 8080 # 檢查網(wǎng)絡(luò) docker network ls docker network inspect bridge docker network inspect # 修復(fù):讓 nginx 和 backend 在同一個(gè) network docker network create my-net docker run --network my-net --name backend my-app docker run --network my-net --name nginx -p 80:80 nginx
場(chǎng)景四:Java 應(yīng)用容器無法啟動(dòng)
# 現(xiàn)象:java.lang.NoClassDefFoundError,容器退出碼 1 # 排查 docker logsdocker run --rm -it java -version # 原因 # 1. Java 鏡像沒有正確安裝 JRE/JDK # 2. 類路徑配置錯(cuò)誤 # 3. 依賴的 JAR 包沒有打包進(jìn)去 # 檢查鏡像 docker run --rm -it ls -la /app docker run --rm -it env | grep JAVA # 修復(fù) Dockerfile FROM eclipse-temurin:17-jre-alpine COPY app.jar /app/app.jar WORKDIR /app CMD ["java","-jar","app.jar"]
常用排查命令速查表
# 容器狀態(tài) docker ps -a docker ps -a | grep# 容器日志 docker logs docker logs -f docker logs --tail 100 docker logs --timestamps # 容器詳情 docker inspect docker inspect --format='{{.State.Status}}' docker inspect | jq'.State' # 鏡像 docker images docker pull : docker rmi # 網(wǎng)絡(luò) docker network ls docker network inspect dockerexec cat /etc/resolv.conf # 資源占用 docker stats --no-stream docker system df # 清理 docker system prune -af docker image prune -af docker builder prune -af docker container prune
總結(jié)
容器啟動(dòng)失敗的排查思路總結(jié):
| 階段 | 關(guān)鍵命令 | 重點(diǎn)關(guān)注 |
|---|---|---|
| 確認(rèn)狀態(tài) | docker ps -a +docker logs | 退出碼、OOMKilled 標(biāo)記 |
| 查看詳情 | docker inspect | State、Config.Env、HostConfig |
| 鏡像問題 | docker pull +docker images | tag、digest、registry |
| 配置問題 | docker inspect Env | 環(huán)境變量、端口、CMD |
| 資源問題 | docker stats +df -h | 內(nèi)存、磁盤、CPU |
| 權(quán)限問題 | docker exec 測(cè)試命令 | SELinux、文件權(quán)限、用戶 |
| 網(wǎng)絡(luò)問題 | docker exec nslookup/curl/nc | DNS、端口連通性 |
| 數(shù)據(jù)卷問題 | docker volume inspect | 掛載路徑、權(quán)限 |
| 依賴問題 | docker exec nc -zv | 依賴服務(wù)端口、healthcheck |
最后一條最重要的經(jīng)驗(yàn):永遠(yuǎn)先看 docker logs 和 docker inspect 的輸出。這兩個(gè)命令能解決 80% 的容器啟動(dòng)問題。如果日志里看不出問題,再用docker run --rm -it
-
容器
+關(guān)注
關(guān)注
0文章
540瀏覽量
23044 -
命令
+關(guān)注
關(guān)注
5文章
763瀏覽量
24060 -
Docker
+關(guān)注
關(guān)注
0文章
539瀏覽量
14447
原文標(biāo)題:Docker 容器啟動(dòng)失???常見原因和排查命令總結(jié)
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Docker容器啟動(dòng)失敗的常見原因和排查思路
評(píng)論