容器化的過程當中老是免不了要構建鏡像,一個體積更小的鏡像除了可以節省機器的磁盤空間以外,還可以提高傳輸效率。這篇文章主要是想講述一下本身在優化鏡像體積時所採起的措施,固然並非全部方案都對減小鏡像體積有明顯效果,具體項目還要具體分析。這篇文章我以Rails項目的鏡像構建做爲例子。node
在優化鏡像大小以前首先要知道爲什麼咱們所構建的鏡像會這麼大?下面是我項目中用於構建鏡像的Dockerfile
文件mysql
FROM ruby:2.5.3
RUN apt-get update -y && apt-get install -y \
build-essential \
imagemagick \
default-libmysqlclient-dev
RUN apt-get install -y \
nodejs \
yarn
RUN rm -rf /var/lib/apt/lists/*
WORKDIR /beansmile-web
COPY . /beansmile-web
RUN bundle install
複製代碼
鏡像文件我定義得比較隨意,它所構建出的鏡像信息以下git
web1 latest 1a8a32d5253a 9 hours ago 1.26GB
複製代碼
構建的鏡像的過程跟日常基於一個操做系統打造供項目運行基礎環境的過程差很少。只是平常的操做系統一般都不僅一個項目在運行,所以系統裏所包含的東西是比較全面的。而鏡像只指望提供給特定的項目使用,所以所依賴的東西比較有針對性,沒必要要的東西儘可能不要加進去。github
針對上面的Dockerfile文件我以爲有如下幾個優化方向web
下面一條條來分析。sql
前面的例子最終構建出來的鏡像體積十分龐大,主要歸咎於相關的基礎鏡像自己就很大。docker
REPOSITORY TAG IMAGE ID CREATED SIZE
ruby 2.5.3 60c3a1518797 3 weeks ago 871MB
web1 latest 1a8a32d5253a 9 hours ago 1.26GB
複製代碼
可見咱們的基礎Ruby鏡像自己就800多M了,構建鏡像的過程還須要安裝依賴,致使了最終的web
鏡像體積會達到1.26G。這個體積可不利於網絡傳輸,官方所提供的Ruby基礎鏡像有許多個版本,除了Ruby自己的版本不一樣以外,還有許多基於不一樣操做系統所構建的基礎鏡像能夠選擇,而這些不一樣的操做系統所構建出來的Ruby基礎鏡像的體積相差甚大ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
ruby 2.5.3-slim-stretch 20132a4ab93d 2 weeks ago 129MB
ruby 2.5.3 60c3a1518797 3 weeks ago 871MB
ruby 2.5.3-alpine b3361f13ff1f 3 weeks ago 43.6MB
複製代碼
基於alpine
操做系統的Ruby鏡像是最迷你的,只有43.6MB。slim-stretch
也是個不錯的選擇。或許採用更輕量級的鏡像將會是一個優化的契機。緩存
經驗小貼士: 從我本身的構建經驗來看,採用slim-stretch
或許會是更加親民的選擇,它是Debian系,包管理器跟ubuntu
是同樣的都是用apt-get
,用慣ubuntu
的人確定會以爲比較親切。alpine
所用的包管理器是apk
(是否是想到安卓的安裝包?),一些經常使用包的命名有點不太同樣須要本身慢慢去解決。*ruby
不過不管用哪一種方案都避免不了時間的投入,網上也沒那麼多現成的解決方案,迷你鏡像的話你不得不本身安裝一些構建過程當中所依賴的軟件。
Docker官網對鏡像的說法是,它是由一層層的只讀層組成的,層次越少鏡像的性能表現越出衆。這也是官方建議咱們採用特定基礎鏡像去構建本身的項目鏡像,而不是基於一個赤裸裸的操做系統鏡像(如Ubuntu鏡像)的緣由。
上述的例子中咱們用了三個RUN
命令,這會無心中多構建了兩個層,其實咱們能夠把它合併成一條RUN
命令
RUN apt-get update -y && apt-get install -y \
build-essential \
imagemagick \
default-libmysqlclient-dev \
nodejs \
yarn \
&& rm -rf /var/lib/apt/lists/*
複製代碼
基於這個改動從新建立一個鏡像web2
REPOSITORY TAG IMAGE ID CREATED SIZE
web2 latest 221a316a6903 14 minutes ago 1.25GB
web1 latest 1a8a32d5253a 9 hours ago 1.26GB
複製代碼
可見這種改動對於縮減鏡像體積效果並不明顯
官方的說法是這樣的
In older versions of Docker, it was important that you minimized the number of layers in your images to ensure they were performant.
咱們能夠得出結論,或許縮減層數主要是爲了讓鏡像操做起來更高效吧,減小層數這個優化方向對於縮減鏡像體積並無多大的幫助,不過咱們這樣作仍是有好處的。
從上面的配置能夠看出,爲了方便鏡像的構建我直接把整個項目都移動到鏡像中去(COPY
命令)。然而對於構建的鏡像而言,並非全部的文件咱們都應該關心,最爲值得關心的應該只有源碼部分。因此我預想着在構建的鏡像中能夠把如下的目錄剔除掉
PS: 固然每一個人對實際項目的考量會有所不一樣,這幾個目錄只是根據我我的的項目狀況所作的決定,並不具備通用性。
要忽略這些文件,咱們採用一個名爲.dockerignore
的文件,把它放在當前的目錄下便可,它的寫法跟.gitignore
文件很類似,內容大概以下
/public/**
/tmp/**
/log/**
複製代碼
而後從新構建鏡像
web3 latest fb13cc1301b2 About a minute ago 1.2GB
web2 latest 221a316a6903 23 hours ago 1.25GB
web1 latest 1a8a32d5253a 33 hours ago 1.26GB
複製代碼
這種方式的影響也不怎麼大,這是由於目前我本地這些目錄下所包含的「垃圾」資源所佔的比重較小。
這個是官方推薦的方案,在Docker17.05以後能夠使用
In Docker 17.05 and higher, you can do multi-stage builds and only copy the artifacts you need into the final image. This allows you to include tools and debug information in your intermediate build stages without increasing the size of the final image.
好像看起來有點複雜,不過它的原理大概就是先使用一個體積較大,依賴較爲齊全的鏡像來構建所須要的資源,而後把這些資源複製到一個輕量的基礎鏡像中,並繼續咱們的鏡像構建工做,這樣就能夠把原先龐大的基礎鏡像給拋棄了。這種作法能避免咱們最終的鏡像中包含了一堆無用的依賴,在某種程度上可以減小最終鏡像的體積。
這看起來是個很不錯的策略,我也在項目中進行了嘗試。咱們決定把bundle依賴包的安裝以及,靜態文件的編譯都放到一個功能完備的基礎鏡像中去完成,而後把所須要的資源拷貝到一個輕量級的基礎鏡像中(相似alpine這種輕量級系統的相關鏡像)再繼續完成構建步驟。
不過我構建過程當中遇到以下問題
bin/rails c
須要依賴JS運行時,這不管對於開發仍是生產都是一個比較重要的操做,所以在最終鏡像中捨棄JS運行時並非個好主意。前面提到了4個優化的方向,但彷佛最終只有
對最終的鏡像體積影響較大。考慮到multi-stage
的解決方案所帶來的好處可能還不如麻煩來得多,所以最終仍是捨棄了這個方案,與其這樣繞來繞去還不如直接採用最精簡的ruby:2.5.3-alpine
做爲基礎鏡像來打造本身的項目鏡像。選擇一個精簡的操做系統最大的問題就是在構建項目鏡像過程當中的全部基礎依賴都得本身一個個去解決,要投入很多的時間和精力,如下是我通過反覆測試所獲得的Dockerfile文件(僅供參考,畢竟你的項目所依賴的東西可能有所不一樣)
FROM ruby:2.5.3-alpine
RUN apk --update --upgrade add \
# bundle 安裝相關的依賴
git \
curl \
# mysql2 依賴
mysql-dev \
# 基礎設施,好比gcc相關的東西
build-base \
# nokogiri 相關依賴
libxslt-dev \
libxml2-dev \
# 圖片處理相關依賴
imagemagick \
# tz相關,若是沒有bundle的時候會報錯
tzdata \
nodejs \
yarn \
&& rm -rf /var/cache/apk/*
WORKDIR /beansmile-web
COPY . /beansmile-web/
RUN bundle install
複製代碼
構建出來的鏡像以下
web4 latest 71b75128d0d9 14 hours ago 586MB
複製代碼
與以前的鏡像相比體積大幅度減小了。這是一個咱們能夠接受的大小了,考慮到時間成本就不進一步壓縮了。
這篇文章主要簡單總結了我的在縮減Rails項目鏡像方面的探究。爲了縮減鏡像體積提出了4個主要的優化方向,用迷你的操做系統構建鏡像的方式來減小鏡像的體積的方式十分有效。不過不一樣類型,基於不一樣語言的項目可能會有不一樣的側重點,不能一律而論,可能有的項目中multi-stage
會幫你省下更多的時間。