天天看點

如何縮減docker鏡像體積大小

docker 如何消減鏡像體積?

docker 體積越小,那麼傳輸和部署就越友善。

實際開發過程中,大部分人都是從dockerhub中拉取現有鏡像,然後基于此建構自己的鏡像,實際上,這樣的操作雖然友善,但是帶來的問題也很大。

就是,打包後的 image 體積很大。

如何解決這個問題?推薦三個方法

利用多階段建構方法

建立檔案夾 nodefs

在檔案夾下建立test.js

// test.js
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(3000, () => {
  console.log(`Example app listening on port 3000!`)
})
           

執行

npm init

,來建立生成 package.json

編輯 package.json,添加如下代碼:

{
  "name": "hello-world",
  "version": "1.0.0",
  "main": "test.js",
  "dependencies": {
    "express": "^4.16.2"
  },
  "scripts": {
    "start": "node test.js"
  }
}
           

建立 Dockerfile 檔案并編輯:

FROM node:8
EXPOSE 3000
WORKDIR /app
COPY package.json test.js ./
RUN npm install
CMD ["npm", "start"]
           

然後利用 docker 指令

docker build -t node-vanilla .

建構一個鏡像,建構完成後

再次編輯 Dockerfile :

FROM node:8 as build
WORKDIR /app
COPY package.json test.js ./
RUN npm install
FROM node:8
COPY --from=build /app /
EXPOSE 3000
CMD ["test.js"]
           

然後建構一個新鏡像

docker build -t node-vanillas-m .

,完成後

利用指令

docker images |grep node-

檢視鏡像,得到結果如下

node-vanilla-m      latest    331b81a245b1    2seconds ago    903MB
node-vanilla        latest    8ec8897e1157    12seconds ago    904MB
           

可以看到,分層建構後的鏡像體積稍微減小了一點,但是與正體體積相比幾乎可以忽略,這是因為我們的代碼非常簡單,Dockerfile 分層也比較少

利用distroless

如何縮減docker鏡像體積大小

換句話說,我們的容器在運作時僅僅依賴 node ,但是我們建構的鏡像不僅僅包括 node,還有許許多多的配置檔案,二進制檔案,以及一些實用程式等,是以我們可以删除不必須的内容,來縮小鏡像體積。

Google 在 github 上建立了 distroless 項目來提供隻包含運作應用程式的最新容器鏡像

重新編輯 Dockerfile 檔案:

FROM node:8 as build
WORKDIR /app
COPY package.json test.js ./
RUN npm install
FROM gcr.io/distroless/nodejs
COPY --from=build /app /
EXPOSE 3000
CMD ["test.js"]
           

然後建構一個新的鏡像

docker build -t node-distroless .

運作這個鏡像驗證一下建構是否可用

docker run -p 3000:3000 -ti --rm --init node-distroless
           

檢視這個鏡像

docker images | grep node-distroless
node-distroless   latest  7b4db3b7f1e5    2seconds ago   76.7MB
           

這個鏡像的大小簡直令人驚訝!

使用 distroless 的時候有一些注意事項,比方,想檢查某個正在運作的容器,

dc exec containername bash
           

這就像是建立了一個SSH會話一樣,但是我們的鏡像裡沒有其他的二進制檔案,也沒有shell,唯一存在的二進制檔案就是 node.js,這是個壞消息也是個好消息

壞消息是因為在容器内你隻能執行二進制

dc exec containername node
           

好消息則是因為即便攻擊者利用你的應用程式獲得對容器的通路權限,也無法像通路shell那樣造成太多破壞。

即更少的二進制檔案意味着更小的體積和更高的安全性。

distroless 的使用基于你不在生産環境進行調試,而是充分利用日志和監控

但是,加入真的需要在生産環境進行調試工作呢?distroless 就會變得異常痛苦。

小體積的Alpine基礎鏡像

Alpine Linux 是基于 musl libc 和 busybox ,以安全性為目标的輕量級 Linux 發行版。意思是:size 更小,安全性更高!

是以,我們可以把 Alpine 作為父鏡像,來減少鏡像體積。

但需要注意的是Alpine Linux 中的 C 庫不是我們常用的 glibc,而是 muslc。

glibc更常見,速度也更快;

muslc使用較少的空間,并側重于安全性。

也就是說,使用基于 Alpine 的鏡像有可能産生 C 庫不同導緻的問題。

編輯Dockerfile

FROM node:8 as build
WORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8-alpine
COPY --from=build /app /
EXPOSE 3000
CMD ["npm", "start"]
           

建構鏡像

docker build -t node-alpine .

檢視一下建構後的鏡像

docker images | grep node-alpine
node-alpine   latest   aa1f85f8e724  2seconds ago   69.7MB
           

運作鏡像驗證是否可用

docker run -p 3000:3000 -ti --rm --init node-alpine
           

進入容器

docker exec -ti aa1f85f8e724 sh
           

雖然不支援 bash,但是 sh 是完全可以的

總結

如果在生産環境中運作容器,并且更注重安全性,那麼可能distroless鏡像更合适

如果更注重更小的鏡像體積,那麼可以考慮基于Alpine的鏡像

原始基礎鏡像則非常适合用于測試和開發