如何寫一個最優的Dockerfile

[TOC]

1. 爲什麼要優化Dockerfile

我們如果使用Dockerfile來構建Docker鏡像,如果一不小心就會導致鏡像大小超過1G,這是非常恐怖的。一般也都是好幾百兆。較大的鏡像往往會導致移植,遷移緩慢,部署上線也就慢。
Dockerfile就像代碼一樣需要持續去進行優化。使用下面的幾個優化方案,可以大幅度的減小鏡像的大小。

2. 優化方案

2.1 減少鏡像層數

最重要的因素是減少鏡像的層數,這樣能大大減小鏡像的大小;
當然在減少層數和增加層數但能減少編譯時長上,可以適當衡量。

如何寫一個最優的Dockerfile

說明:

  1. docker鏡像可以看出是分層的,分層方向與Dockerfile相反,自下而上;
  2. docker鏡像每一層是共享的,即同一臺機器中,如果Dockerfile編譯的時候,前面的內容相同,則相應的層是引用相同的,當然內容自Dockerfile中要自上而下相同,有出現不一樣的層數的時候,後面的層內容都會不同,基於這個原理,在沒有任何修改的情況下,後面的編譯都是使用之前的鏡像緩存;
  3. 一層新的命令形成新的一層,如果涉及磁盤更新,而且沒有在同一層刪除,無論文件是否最後刪除,都會帶到下一層。

基於上面的說明,層數越小,每一層大小越小,鏡像總體就越小。

以下爲一個示例,其優化原理是相鄰的命令間用&&讓其只形成一層。

# 基礎鏡像
FROM node:10.16-alpine as builder

# 拷貝靜態資源文件
COPY . /app/

# 工作目錄
WORKDIR /app

RUN yarn config set registry https://registry.npm.taobao.org \
  && yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass \
  && yarn global add [email protected] \
  && yarn install \
  && yarn build

# 暴露端口
EXPOSE 80

# 啓動參數
CMD [ "http-server", "build", "-p", "80" ]

但是,有種情況是分層更優的,共目的是爲了減少docker編譯時間,比如:

FROM alpine:latest

# command1耗時久,且比較穩定
RUN command1
# command2涉及更新內容頻繁
RUN command2

因爲command1耗時久,比如安裝依賴包,而command2更新頻繁,比如代碼修改。這種場景下,如果每次編譯都需要安裝很久的依賴包,這樣體驗非常差,因爲安裝依賴包這部分很少有變化,所以如果分開2層,前面安裝依賴包就會使用緩存,這樣編譯就非常快了。

2.2 基於更小的鏡像

在保證功能前提下,儘量使用更小的鏡像。比如使用基於alpine製作的鏡像,或者帶alpine tag的鏡像。
還有使用谷歌Distroless

Alpine Linux 是:一個基於 musl libc 和 busybox 的面向安全的輕量級 Linux 發行版。
換句話說,它是一個體積更小也更安全的 Linux 發行版。

比如以下示例,選擇帶alpine

FROM node:lts-alpine

RUN apk --no-cache add ca-certificates curl git \
  && rm -rf /var/cache/apk/* \
  && update-ca-certificates

2.3 在每一層清理產生的垃圾文件或臨時文件

以下整理了常用的基礎鏡像的清理命令:

基礎鏡像 清理命令
alpine rm -rf /var/cache/apk/*
centos/oraclelinux rm -rf /var/cache/yum/*
ubuntu/debian apt autoclean -y && apt autoremove -y && rm -rf /var/lib/apt/*

仍然是這個示例,以下含有刪除緩存命令rm -rf /var/cache/apk/*

FROM node:lts-alpine

RUN apk --no-cache add ca-certificates curl git \
  && rm -rf /var/cache/apk/* \
  && update-ca-certificates

2.4 使用.dockerignore

.dockerignore 文件的作用類似於 git 工程中的 .gitignore 。不同的是 .dockerignore 應用於 docker 鏡像的構建,它存在於 docker 構建上下文的根目錄,用來排除不需要上傳到 docker 服務端的文件或目錄。

docker 在構建鏡像時首先從構建上下文找有沒有 .dockerignore 文件,如果有的話則在上傳上下文到 docker 服務端時忽略掉 .dockerignore 裏面的文件列表。這麼做顯然帶來的好處是:

  • 構建鏡像時能避免不需要的大文件上傳到服務端,從而拖慢構建的速度、網絡帶寬的消耗,減少鏡像體積;
  • 可以避免構建鏡像時將一些敏感文件及其他不需要的文件打包到鏡像中,從而提高鏡像的安全性;

.dockerignore 示例:

.codeclimate
.gitlab-ci.yml
Dockerfile
.git
.gitignore
ci

使用方法具體參考:
https://docs.docker.com/engine/reference/builder/#dockerignore-file

2.5 使用multi-stage功能

前提:docker版本17.05或更高

示例Dockerfile

# 基礎鏡像
FROM node:10.16-alpine as builder

# 拷貝靜態資源文件
COPY . /app/

# 工作目錄
WORKDIR /app

################# 舊版本使用http-server插件 #####################
# 舊運行命令
#RUN yarn config set registry https://registry.npm.taobao.org \
#  && yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass \
#  && yarn global add [email protected] \
#  && yarn install \
#  && yarn build

## 暴露端口
#EXPOSE 80
#
## 啓動參數
#CMD [ "http-server", "build", "-p", "80" ]
################# 舊版本使用http-server插件 #####################

# 新運行命令
RUN yarn config set registry https://registry.npm.taobao.org \
  && yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass \
  && yarn install \
  && yarn build

FROM nginx:1.17.5-alpine

# 維護人
LABEL maintainer="ygqygq2"

# 工作目錄
WORKDIR /usr/share/nginx/html

copy --from=builder /app/build .

######################## 使用默認,可不必添加 #######################
# 暴露端口
#EXPOSE 80

# 啓動命令及參數
#ENTRYPOINT ["nginx", "-g", "daemon off;"]
######################## 使用默認,可不必添加 #######################

關鍵的地方是
FROM image:tag AS name
copy --from name /path/ /path/

參考資料:
[1] https://docs.docker.com/engine/reference/builder/#dockerignore-file
[2] https://docs.docker.com/develop/develop-images/multistage-build/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章