最近在工做中遇到了一個 Docker 鏡像沒法運行的問題,過後總結時發現其中有幾個點挺有意思,值得記錄下來以備後用,也能夠避免其餘人踩坑。css
個人開發環境是 Windows,使用 Docker Desktop 做爲本地 Docker 環境。最近在一個項目中須要把 war 包打成 Docker 鏡像並推送到倉庫裏去,卻發現打好的這個鏡像運行不起來,運行時報下面這樣的錯:java
# docker run --rm -it manager
standard_init_linux.go:207: exec user process caused "no such file or directory"
複製代碼
檢查 Dockerfile 文件,是很是簡單的:linux
FROM openjdk:8-jre-alpine
ADD entrypoint.sh entrypoint.sh
ADD *.war app.war
EXPOSE 8088
RUN chmod 755 entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]
複製代碼
鏡像是基於 openjdk 的基礎鏡像,將 entrypoint.sh
和 *.war
拷貝到鏡像中,並將 entrypoint.sh
做爲默認的啓動腳本文件。初看應該是沒問題的,war 包在本地使用 java -jar
也能夠跑起來。git
那麼這個鏡像是怎麼回事?看報錯信息,應該是運行鏡像時,找不到某個文件或目錄,可是個人這個鏡像一共就兩個文件,一個 entrypoint.sh
一個 war 包,難道這兩個文件沒有 ADD 到鏡像裏?github
因而試着運行命令 docker run --rm -it manager sh
,想着進 shell 查看下文件,發現依然報錯,難道鏡像裏沒有 sh 腳本?試着連換了幾個命令:docker run --rm -it manager /bin/bash
和 docker run --rm -it manager ls
發現都是這個錯,這怎麼可能呢,連 ls 都沒有?docker
無奈 Google 之,才發現原來若是鏡像配置了 entrypoint
,要使用下面這樣的方式來檢查鏡像:shell
# docker run --rm -it --entrypoint sh manager
複製代碼
進到容器裏來了以後,使用 ls 能夠看到文件都在,沒毛病:bash
/ # ls
app.war entrypoint.sh lib opt run sys var
bin etc media proc sbin tmp
dev home mnt root srv usr
複製代碼
使用 java -jar app.war
能夠正常運行,可是奇怪的是,./entrypoint.sh
腳本卻運行不了:服務器
/ # ./entrypoint.sh
sh: ./entrypoint.sh: not found
複製代碼
這個腳本文件明明就在這,卻報錯 not found,這讓我一度懷疑人生,遇到靈異事件了。網絡
不得已只能繼續 Google 之,發現網絡上跟我同樣的人還有不少,腳本沒法運行最可能的緣由是 Sha-Bang
寫的有問題,所謂 Sha-Bang 就是 #!
,一般會寫在 shell 文件第一行,用於指定命令行解釋器。相似於下面這樣:
/ # cat entrypoint.sh
#!/bin/sh
blabla
複製代碼
使用 ls /bin/sh
發現 /bin/sh
文件也在,應該沒問題啊。苦思冥想之際,忽然腦海中閃過一個想法,難道這裏有隱藏字符?通常遇到這種靈異事件的時候,都極可能和隱藏字符有關。使用 cat -v
查看文件,果真發現這裏的 /bin/sh
後面多了個 ^M
:
/ # cat -v entrypoint.sh
#!/bin/sh^M
blabla
複製代碼
頓時豁然開朗,這不就是 Windows 下的換行符嗎?一切都是換行符惹的禍。
使用 dos2unix
將 entrypoint.sh 文件中的 Windows 格式的換行符轉爲 UNIX 格式,再一次使用 docker build
,此次終於運行成功了。
到這裏原本已經結束了,不事後來又發生了一件小事,讓我又發現另外一個坑,才找到了這個問題最根本的緣由。當我解決了這個問題以後,就去給同事分享,但是聽了個人分享以後,同事卻一臉懵逼的表示本身歷來都沒遇到過這個問題,而且在他電腦上給我演示了一遍 docker build
和 docker run
,一切正常,並無報錯,一樣的一份代碼,爲何在個人電腦上 build 就有問題?看着同事對我露出的迷之微笑,我又一次陷入了困惑。
我讓他把 entrypoint.sh
發給我,看了下,他的換行符格式居然是 UNIX 的,但是我本地代碼換行符明明是 Windows 的,使用 git status
也能夠看到 nothing to commit, working tree clean
,代表本地代碼和倉庫代碼是同樣的,爲何換行符卻不同呢?
查看 Git 的配置文件,和他的對比了一下,發現一個極可能的疑點:
[core]
autocrlf = true
複製代碼
查詢官網文檔,終於找到了緣由,原來 Git 在 pull 代碼的時候可能會偷偷的對你的代碼作手腳。若是你配置了 autocrlf = true
,那麼當你簽出代碼時,Git 會自動的把 LF 轉換成 CRLF,而後當你 push 的時候,又自動的將 CRLF 轉換成 LF。這看上去很貼心的功能,實際上卻有着很大的漏洞,譬如像我這樣,直接在本地打鏡像,或者須要直接將 shell 文件上傳到 Linux 服務器上運行的,均可能會出問題。關鍵這個配置在 Windows 下默認是打開的,因此建議把這個配置關掉:
$ git config --global core.autocrlf false
複製代碼
entrypoint
,docker run
的時候應該加上 --entrypoint
參數來檢查鏡像Sha-Bang
寫的有問題,檢查腳本文件中是否存在隱藏字符,譬如 Windows 的換行符,這裏不得不吐槽下,這個 not found 提示真的讓人迷惑,提示裏能不能把隱藏字符也帶上?autocrlf
配置