記一次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有怎樣的具體影響?待來日機緣成熟再一探究竟。

相關文章
相關標籤/搜索