记一个 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
相关文章
相关标签/搜索