最近在工做中遇到了一个 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
配置