记一次Docker构建失败

见字如晤。node

前段时间,Node.js 官方发布了Node 8.9.3 LTS版本,而且官网首页提示新版本有重要安全更新,“Important security releases, please update now!” ,而后我当即着手公司产品各个模块的Node版本升级。git

发布基础镜像

咱们全部项目均使用Node.js实现,并采用Docker容器交付和部署,因此要升级全部产品的线上Node.js版本,只须要更新一下Docker镜像打包时的配置文件Dockerfile便可。下方就是一个典型项目的Dockerfile配置文件。github

FROM maichong/node:8.9.1

MAINTAINER Maichong Cloud <support@maichong.io>

RUN apt-get update \
    && apt-get install -y --no-install-recommends openssh-client \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

COPY package.json /app/

WORKDIR /app

RUN npm install --production \
 && rm -R ~/.npm*

COPY . /app

CMD node index.js

脚本大意:docker

  1. 基于 maichong/node:8.9.1 基础镜像构建
  2. 记录做者信息
  3. 运行 apt ,为镜像安装 openssh-client 软件
  4. 拷贝代码 package.js 到目标镜像中
  5. 设置镜像容器工做目录为 /app
  6. 在镜像中运行 npm 安装依赖
  7. 拷贝代码到目标镜像的 /app 目录中
  8. 设置镜像的启动命令

技巧: 这里先拷贝 package.js 再安装npm依赖,最后拷贝代码,这样的好处是:项目代码常常变更,而项目的npm依赖通常不会变化,这样的顺序安排能够有效利用docker build缓存,在package.js没有变化的状况下跳过漫长的npm依赖安装时间,大大提升打包速度。npm

从上边的Dockerfile配置文件中看到,咱们的项目均依赖于基础镜像 maichong/node,这是咱们本身发布的Node.js镜像,和Node.js官方镜像的主要区别是:咱们的镜像使用了阿里云的 debian apt 源镜像,并采用了 registry.npm.taobao.org 作npm加速镜像。json

因此,首先要作的是发布一个新版本的Node.js镜像:maichong/node:8.9.3。缓存

发布过程很简单,编写基础镜像的Dockerfile,在本地构建镜像,最后将镜像推送到Docker官方的仓库,Done!安全

基础镜像的Dockerfile能够在这里找到 https://github.com/liangxingc...

之因此在本地构建,而没有使用Docker仓库的 automated build,是由于,咱们的镜像采用了国内阿里云的 Debian apt 源,再加上某些很奇妙的网络因素,在Docker Hub中自动构建时,apt升级总会失败。服务器

升级项目

maichong/node:8.9.3 基础镜像已经准备就绪,接下来开始升级公司的各个项目。网络

咱们的全部项目都基于脉冲云 maichong.io 进行管理,脉冲云包含了从代码仓库,到自动化构建,再到自动化部署等持续集成流程。

修改项目的Dockerfile,将基础镜像变动为 maichong/node:8.9.3,而后将代码提交到代码仓库,而后起身泡了一杯咖啡,悠然地等待脉冲云自动地在线编译打包Docker镜像,并自动将最新的镜像部署到服务器集群上,完成升级工做。

意外!

当我端者刚刚泡好的咖啡回到工位,意外发现,脉冲云在线编译构建失败!内心一凉,我*,何方BUG在此做祟?!赶快在线查看构建日志,致使失败的关键部分日志以下:

2017-12-12 10:04:05 Step 5/12 : RUN apt-get update && apt-get install -y --no-install-recommends openssh-client && apt-get clean && rm -rf /var/lib/apt/lists/*
2017-12-12 10:04:05 ---> Running in c3fb701ef925
2017-12-12 10:04:06 Ign http://mirrors.aliyun.com jessie InRelease
...
2017-12-12 10:04:07 Fetched 11.1 MB in 1s (6028 kB/s)
2017-12-12 10:04:09 Reading package lists...
2017-12-12 10:04:10 Building dependency tree...
2017-12-12 10:04:10 Reading state information...
2017-12-12 10:04:10 The following extra packages will be installed:
2017-12-12 10:04:10 adduser debconf debianutils dpkg ... (一共40个软件包)
2017-12-12 10:04:10 Suggested packages: ...
2017-12-12 10:04:10 Recommended packages: ...
2017-12-12 10:04:12 0 upgraded, 40 newly installed, 0 to remove and 0 not upgraded.
2017-12-12 10:04:12 Need to get 16.7 MB of archives.
2017-12-12 10:04:12 After this operation, 44.7 MB of additional disk space will be used.
2017-12-12 10:04:12 Get:1 http://mirrors.aliyun.com/debian/ jessie/main sensible-utils all 0.0.9 [11.3 kB]
...
2017-12-12 10:04:22 dpkg: error processing archive /var/cache/apt/archives/libgcc1_1%3a4.9.2-10_amd64.deb (--unpack):
2017-12-12 10:04:22 pre-dependency problem - not installing libgcc1:amd64
为了限制篇幅,上边贴出的构建日志进行了精简,大量的 apt 网络请求和解压缩包日志使用 … 代替,同时apt显示了大量的必须或推荐安装的软件包也以 … 省略。

从日志中获得错误信息是,在docker build打包过程当中,运行apt安装openssh-client失败,最直接的错误是由于openssh-client依赖的libgcc1包安装失败。

直接反应是,莫非又是apt软件仓库依赖的问题?!咦,我为何要说又呢?

apt 是Debian和Ubuntu系统使用的包管理器,相似Node.js世界的npm的做用,用来管理、安装Linux系统中的各类软件包,各类软件包又有不一样版本并互相依赖。

apt安装软件时,会从网络服务器上获取所需软件包和相关依赖包,这里的“网络服务器”被称为“源”,因此上文中说到的 maichong/node 镜像用到了国内阿里云的“源”,是为了加快apt安装软件时的网络速度,阿里云的“源”服务器是对Debian官方源的加速镜像。

因为apt所管理的软件包数量众多、版本众多、互相依赖、互相依赖指定版本,因此容易形成依赖问题,好比 A依赖于B的1.0版本,C依赖于B的2.0版本,若是安装了A,安装C时就会出错,由于系统中没法共存B的1.0和2.0两个版本。

题外话,npm采用多层node_modules目录嵌套解决了一个包不一样版本共存的问题。

仔细观察安装openssh-client时所安装的40个依赖包,发现居然有dpkg这样很是基础的包,怎么可能?!dpkg是Debian系统最基础的包管理器,apt都是依赖于dpkg,全部能跑起来的Debian系统必定是存在dpkg包的,难道是...

基础镜像是“坏”的?

Docker的镜像一旦构建完成,就必定可以正确执行,我我的从未见过某个镜像自身是“坏”的,可能此次要开眼!

这里之因此说“坏”,是由于镜像中整个apt管理依赖都错乱了,多是某些缘由形成了镜像的“损坏”!

在我本地执行构建命令:

docker build -t test ./

而后居然构建成功!说明基础镜像没问题,莫非又是环境差别问题?!本地构建日志以下:

Step 5/12 : RUN apt-get update     && apt-get install -y --no-install-recommends openssh-client       && apt-get clean       && rm -rf /var/lib/apt/lists/*
 ---> Running in 237bec98f4e6
Ign http://mirrors.aliyun.com jessie InRelease
...
Fetched 11.1 MB in 11s (972 kB/s)
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  libbsd0 libedit2
Suggested packages:
  ssh-askpass libpam-ssh keychain monkeysphere
Recommended packages:
  xauth
The following NEW packages will be installed:
  libbsd0 libedit2 openssh-client
0 upgraded, 3 newly installed, 0 to remove and 8 not upgraded.

从日志中发现,在我本地构建时,apt安装openssh-client只须要安装3个软件包。这怎么可能?!一样的基础镜像,一样都使用阿里云的源,难道是...

阿里云源镜像数据问题?

阿里云的源镜像服务是搭建在CDN上的,CDN的目的是为了让用户就近访问不一样地理位置的服务器,以达到不一样地区的用户访问速度都能很快,因此虽然使用了一样的源镜像地址,可是在不一样位置更新apt时,所请求的阿里云服务器是不同的。那么就有多是阿里CDN数据不一样或是从国外Debian官方服务器数据同步未完成致使的问题。

登陆到远程脉冲云Builder Runner,这是专门用来执行用户自动化集成的服务器,一开始咱们的在线构建就是发生在这里的。在Runner上 ping mirrors.aliyun.com 获得的源镜像IP地址和我本地的果真不同。

而后我将在Runner上获得的IP地址,加到了我本地DNS解析中,这样,我本地再运行apt时,访问的服务器就和在Runner上是同样的了。

然而,在本地再次构建成功。说明并不是是阿里云源镜像数据问题。一样的镜像,一样的源服务器,不一样的apt运行结果,难道是...

远程主机上的镜像损坏?

会不会是我本地拉到的镜像是好的,可是远程Runner上拉到的镜像倒是坏的?

这是不可能的,由于Docker在拉取镜像后,会对镜像进行验证,因此同一个镜像版本,多人拉取完成后能够保证每一个人所得是如出一辙的。不会存在某人拉取到一个损坏的镜像的状况。

彷佛全部可能性都被排除了,问题仍然得不到解决,真是莫名其妙,手上的线索已经所有断掉,案件侦破进入了僵局。

我向团队说明了我遇到的状况,邻桌Mr.Li断言道:“必定是环境差别致使的BUG!”

是呀,必定是环境差别问题,可是全部环境差别都排除过了,一样的网络环境,一样的构建配置,一样的镜像...

咱们知道Docker的优点就在于将软件和运行环境打包成一个镜像,一个镜像在不一样外部环境下执行,可以保证镜像内的程序所在的镜像内部环境如出一辙,由于Docker运行容器时,会将环境彻底隔离。不对,并无“彻底”隔离,难道是...

宿主机内核差别问题?

在本地和在Ranner上分别执行uname -a,果真获得的内核版本是不同的。

本地:

Linux local 4.4.0-103-generic #126-Ubuntu SMP Mon Dec 4 16:23:28 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

Runner:

Linux runner02 4.4.0-98-generic #121-Ubuntu SMP Tue Oct 10 14:24:03 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

本地的Linux内核版本是 4.4.0-103-generic ,远程Runner宿主机内核版本是 4.4.0-98-generic 。尝试升级远程Runner宿主机内核:

apt-get update
apt-get dist-upgrade

将宿主机内核升级到了最新版4.4.0-103-generic,重启Runner后,在线Docker打包成功!

总结

其实Docker运行容器时,并不是将全部环境“彻底隔离”,好比宿主机内核就没有隔离。Docker并不像VMware那样,在启动虚拟机时,彻底虚拟一个硬件环境,而后从头加载虚拟机操做系统的内核。Docker容器运行时,仍然依赖于宿主机内存里正在运行的内核,虽然不一样容器使用不一样镜像,但镜像的本质是文件系统的压缩包,是让你的容器运行时有一个本身定义的文件系统和软件群,而执行容器程序时,并无从头为你启动一个系统内核。因此咱们称Docker为轻量级虚拟化技术。

本文所遇到的问题的缘由应该是,在构建 maichong/node:8.9.3 镜像时,是在 4.4.0-103-generic 版本内核环境下执行的,apt 安装的一系列软件是适用于 4.4.0-103-generic 版本的,而在Runner上执行构建时,内核又变为了 4.4.0-98-generic可能 apt 将以前基础镜像中的一些软件标识为无效,又要进行从新安装。

这种状况我以前也从未见过,因此撰文记录,能够判定,Docker Hub大部分镜像编译时的内核环境和咱们本地的内核是不同的,怎么单单此次apt会出错?apt在管理软件时,系统内核对apt有怎样的具体影响?待来日机缘成熟再一探究竟。

相关文章
相关标签/搜索