【編者的話】本文介紹了微容器的概念和好處,並用一些例子介紹了如何構建微鏡像,從scratch到Alpine Linux,並推薦了一些已有的基礎微鏡像,方便為幾乎所有主流語言的應用構建微鏡像。本文也指出了構建微鏡像的基本原理:將構建時依賴和運行時依賴分開,構建時所用的鏡像包含所有構建所用的工具,它可以比較大,但運行時的基礎鏡像應該僅包含運行時依賴。使用微容器,no going
back!
Docker使你能把你的應用和應用的依賴打包到一個良好的自包含鏡像中。然後你可以用該那個鏡像來在容器中運行你的應用。問題是,你通常也打包了一些你可能不需要比你需要的更多的東西,最終座椅最重你得到了一個巨大的鏡像和巨大的容器。大多數開始使用Docker的人會使用Docker的
官方倉庫作為他們的語言的選擇,但是不幸的是,如果你使用官方鏡像,你會得到一個巨大的鏡像,而本來你可以得到一個小鏡像的。你並不需要和這些官方鏡像中一起的許多複雜的東西。例如,如果你使用
官方的Node鏡像構建一個你的應用的Node的鏡像,它至少有643MB大,因為這是
官方Node鏡像的大小。
我構建了一個簡單的
Hello World Node 應用,在官方Node鏡像上構建,它的大小是644MB。
這太大了!我的應用加上依賴也不超過1MB,Node.js的運行時大概20MB,那是什麼占據了剩下的620MB?我們應該能做得更好。
什麼是微容器?一個微容器僅包含OS庫和運行應用所需要的語言依賴以及應用本身。其他都不需要。
與其包含所有,不如僅包含最基本的,在需要的時候添加依賴。
以上面的Node應用為例,使用一個小的基礎鏡像,然後安裝核心的部分,即Node.js和它的依賴,它只有29MB,小了22倍(見下圖)!
如果你想,現在試試運行這兩個鏡像,
docker run –rm -p 8080:8080 treeder/tiny-node:fat然後
docker run –rm -p 8080:8080 treeder/tiny-node:latest。完全一樣的應用,而大小大不相同。
為什麼微容器很棒?有許多使用微容器的好處:
- 大小 —— 微容器很小。像上面展示的,沒有改變任何代碼,鏡像大小減少了22倍。
- 快速/簡單的發布 —— 因為鏡像非常小,可以更快地從 Docker registry(例如Docker Hub)下載鏡像,因而可以更快地發布到不同的機器。
- 提高安全性 —— 更少的代碼和程序在容器中意味著更小的攻擊面。基礎OS就更安全(詳見下文)。
這些好處和
Unikernels的好處類似,沒有任何缺點。
如何構建微容器所有Docker鏡像的基礎鏡像是
scratch鏡像。它本質上什麼都沒有。聽起來它好像沒什麼無用,但是你可以用它
創建你的應用的最小可能鏡像,如果你把你的應用編譯為一個沒有依賴的靜態二進位文件,像Go或C那樣。例如,我的
treeder/static-go
鏡像包含了一個Go的Web應用,則整個鏡像(包括我的應用)是5MB。
這是關於如何得到儘可能小的鏡像:scratch鏡像+你的應用的二進位文件。
然而並不是所有人都是用Go,所以你可能有更多的依賴,並且你需要比scratch鏡像要多一些的東西。關於
Alpine Linux,我不會枯燥地告訴你關於它的細節,但是他們的廣告語說出了全部:「Alpine Linux是一個面向安全的,輕量的Linux發行版,基於musl libc和busybox。」
你可以在
這裡得到詳細說明,但是我們這篇文章最關心的是「輕量」的部分。基礎的Alpine鏡像只有5MB。
現在我們有一個非常好的OS作為一個基礎鏡像,它有一個
很好的包管理系統來添加我們的依賴。對於我們的簡單的Node應用而言,我們只需要Node自己,所以我們可以只添加Node包。我們的Dockerfile看起來像這樣:
FROM alpine
RUN apk update && apk upgrade
RUN apk add nodejs
簡單而整潔。現在我們只在鏡像中有Node和Node的依賴。
現在為了添加我們的代碼到鏡像中,只需要在我們的Dockerfile中添加幾行:
FROM alpine
RUN apk update && apk upgrade
RUN apk add nodejs
WORKDIR /app
ADD . /app
ENTRYPOINT [ "node", "server.js" ]
你可以獲得示例代碼並且在
這裡查看完整的構建指導。
同樣的規則適用於所有其他的語言。
適用於所有語言的基礎鏡像幸運的是,我們已經構建了一個適用所有主要語言的基礎鏡像,你可以在這裡找到它們:。
它們有一些優化來使它們儘可能的小,並且我們會定期更新,使用基礎鏡像使得它們比起你自己來更好。通過使用Iron.io的基礎鏡像,上面Node應用的Dockerfile變成這樣:
FROM iron/node
WORKDIR /app
ADD . /app
ENTRYPOINT [ "node", "server.js" ]
另外,對於每一種其他語言,我們構建了兩個版本的這種鏡像,一個用於構建,另一個用於運行。用於構建的鏡像有所有的構建工具在其中,所以可能比運行時要更大。
例如,為了構建Node的依賴,你要像這樣使用
iron/node:dev:
docker run --rm -v "$PWD":/app -w /app iron/node:dev npm install
然後在你的Dockerfile中使用
iron/node然後運行它:
docker run --rm -it -p 8080:8080 -v "$PWD":/app -w /app iron/node node server.js
上述方法同樣適用於其他語言,但是你需要使用它們自己的
build/vendor/run的指令。
如果你想要一個語言的不同版本,你可以改變tag,例如你可以使用iron/node:4.1或者iron/node:0.12。你可以在Docker Hub上找到每個語言的所有的版本tag。例如Node的tag在這裡:。你可以在
iron-io/dockers的項目里找到所有其他Docker Hub tags的連結。
如何為所有的語言構建和打包這可能沒那麼幸運了,但是我們有一些例子可以讓大多數主流語言使用上面的基礎鏡像:。
你可以看看那個項目中的每個語言的README,它會指引你如何構建你的依賴,測試你的代碼,構建一個小的Docker鏡像和測試鏡像。
沒有回頭讀完本文之後,你應該可以為你的應用創建只包含運行應用所需要的東西的Docker鏡像。容器本質是一個鏡像的實例,所以一旦你開始用你的鏡像來運行容器,你就進入了微容器的世界。沒有(理由)回頭。
剛使用了你的「小鏡像」技術在我的一個golang項目上,很棒!感謝這篇偉大的文章。竟然令人震驚地減小到5mb。 —— Harlow Ward @ Clearbit
原文連結:
Microcontainers – Tiny, Portable Docker Containers(翻譯:陳光 審校:高婧雅)