記一個 Docker 鏡像沒法運行的坑

最近在工做中遇到了一個 Docker 鏡像沒法運行的問題,過後總結時發現其中有幾個點挺有意思,值得記錄下來以備後用,也能夠避免其餘人踩坑。css

1、運行鏡像報錯

個人開發環境是 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

2、檢查鏡像

那麼這個鏡像是怎麼回事?看報錯信息,應該是運行鏡像時,找不到某個文件或目錄,可是個人這個鏡像一共就兩個文件,一個 entrypoint.sh 一個 war 包,難道這兩個文件沒有 ADD 到鏡像裏?github

因而試着運行命令 docker run --rm -it manager sh,想着進 shell 查看下文件,發現依然報錯,難道鏡像裏沒有 sh 腳本?試着連換了幾個命令:docker run --rm -it manager /bin/bashdocker 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,這讓我一度懷疑人生,遇到靈異事件了。網絡

3、換行惹的禍

不得已只能繼續 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,此次終於運行成功了。

4、最坑的 Git 配置

到這裏原本已經結束了,不事後來又發生了一件小事,讓我又發現另外一個坑,才找到了這個問題最根本的緣由。當我解決了這個問題以後,就去給同事分享,但是聽了個人分享以後,同事卻一臉懵逼的表示本身歷來都沒遇到過這個問題,而且在他電腦上給我演示了一遍 docker builddocker 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
複製代碼

坑之總結

  • 坑一:鏡像若是配置了 entrypointdocker run 的時候應該加上 --entrypoint 參數來檢查鏡像
  • 坑二:腳本運行時報 not found,最可能的緣由是 Sha-Bang 寫的有問題,檢查腳本文件中是否存在隱藏字符,譬如 Windows 的換行符,這裏不得不吐槽下,這個 not found 提示真的讓人迷惑,提示裏能不能把隱藏字符也帶上?
  • 坑三:建議關閉 Git 的 autocrlf 配置

參考

  1. How to see docker image contents
  2. Standard_init_linux.go:175 exec user process caused no such file
  3. GitHub 第一坑:換行符自動轉換
  4. 自定義 Git - 配置 Git
相關文章
相關標籤/搜索