一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

云服務器|WEB服務器|FTP服務器|郵件服務器|虛擬主機|服務器安全|DNS服務器|服務器知識|Nginx|IIS|Tomcat|

服務器之家 - 服務器技術 - 服務器知識 - 編寫最佳的Dockerfile的方法

編寫最佳的Dockerfile的方法

2021-01-25 19:47fundebug 服務器知識

本文給大家分享的是如何編寫最佳的dockerfile的方法,通過具體實例幫助大家快速掌握編寫Dockerfile的技巧

Dockerfile的語法非常簡單,然而如何加快鏡像構建速度,如何減少Docker鏡像的大小卻不是那么直觀,需要積累實踐經驗。這篇博客可以幫助你快速掌握編寫Dockerfile的技巧。

為了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原作者所有,翻譯僅用于學習。

我已經使用Docker有一段時間了,其中編寫Dockerfile是非常重要的一部分工作。在這篇博客中,我打算分享一些建議,幫助大家編寫更好的Dockerfile。

目標:

  1. 更快的構建速度
  2. 更小的Docker鏡像大小
  3. 更少的Docker鏡像層
  4. 充分利用鏡像緩存
  5. 增加Dockerfile可讀性
  6. 讓Docker容器使用起來更簡單

總結

  1. 編寫.dockerignore文件
  2. 容器只運行單個應用
  3. 將多個RUN指令合并為一個
  4. 基礎鏡像的標簽不要用latest
  5. 每個RUN指令后刪除多余文件
  6. 選擇合適的基礎鏡像(alpine版本最好)
  7. 設置WORKDIR和CMD
  8. 使用ENTRYPOINT (可選)
  9. 在entrypoint腳本中使用exec
  10. COPY與ADD優先使用前者
  11. 合理調整COPY與RUN的順序
  12. 設置默認的環境變量,映射端口和數據卷
  13. 使用LABEL設置鏡像元數據
  14. 添加HEALTHCHECK

示例

示例Dockerfile犯了幾乎所有的錯(當然我是故意的)。接下來,我會一步步優化它。假設我們需要使用Docker運行一個Node.js應用,下面就是它的Dockerfile(CMD指令太復雜了,所以我簡化了,它是錯誤的,僅供參考)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
FROM ubuntu
 
ADD . /app
 
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y nodejs ssh mysql
RUN cd /app && npm install
 
# this should start three processes, mysql and ssh
# in the background and node app in foreground
# isn't it beautifully terrible? <3
CMD mysql & sshd & npm start

構建鏡像:

docker build -t wtf .

1. 編寫.dockerignore文件

構建鏡像時,Docker需要先準備context ,將所有需要的文件收集到進程中。默認的context包含Dockerfile目錄中的所有文件,但是實際上,我們并不需要.git目錄,node_modules目錄等內容。 .dockerignore 的作用和語法類似于 .gitignore,可以忽略一些不需要的文件,這樣可以有效加快鏡像構建時間,同時減少Docker鏡像的大小。示例如下:

?
1
2
.git/
node_modules/

2. 容器只運行單個應用

從技術角度講,你可以在Docker容器中運行多個進程。你可以將數據庫,前端,后端,ssh,supervisor都運行在同一個Docker容器中。但是,這會讓你非常痛苦:

非常長的構建時間(修改前端之后,整個后端也需要重新構建)
非常大的鏡像大小
多個應用的日志難以處理(不能直接使用stdout,否則多個應用的日志會混合到一起)
橫向擴展時非常浪費資源(不同的應用需要運行的容器數并不相同)
僵尸進程問題 - 你需要選擇合適的init進程
因此,我建議大家為每個應用構建單獨的Docker鏡像,然后使用 Docker Compose 運行多個Docker容器。

現在,我從Dockerfile中刪除一些不需要的安裝包,另外,SSH可以用docker exec替代。示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
FROM ubuntu
 
ADD . /app
 
RUN apt-get update
RUN apt-get upgrade -y
 
# we should remove ssh and mysql, and use
# separate container for database
RUN apt-get install -y nodejs # ssh mysql
RUN cd /app && npm install
 
CMD npm start

3. 將多個RUN指令合并為一個

Docker鏡像是分層的,下面這些知識點非常重要:

Dockerfile中的每個指令都會創建一個新的鏡像層。
鏡像層將被緩存和復用
當Dockerfile的指令修改了,復制的文件變化了,或者構建鏡像時指定的變量不同了,對應的鏡像層緩存就會失效
某一層的鏡像緩存失效之后,它之后的鏡像層緩存都會失效
鏡像層是不可變的,如果我們再某一層中添加一個文件,然后在下一層中刪除它,則鏡像中依然會包含該文件(只是這個文件在Docker容器中不可見了)。
Docker鏡像類似于洋蔥。它們都有很多層。為了修改內層,則需要將外面的層都刪掉。記住這一點的話,其他內容就很好理解了。

現在,我們將所有的RUN指令合并為一個。同時把apt-get upgrade刪除,因為它會使得鏡像構建非常不確定(我們只需要依賴基礎鏡像的更新就好了)

?
1
2
3
4
5
6
7
8
9
10
FROM ubuntu
 
ADD . /app
 
RUN apt-get update \
  && apt-get install -y nodejs \
  && cd /app \
  && npm install
 
CMD npm start

記住一點,我們只能將變化頻率一樣的指令合并在一起。將node.js安裝與npm模塊安裝放在一起的話,則每次修改源代碼,都需要重新安裝node.js,這顯然不合適。因此,正確的寫法是這樣的:

?
1
2
3
4
5
6
7
FROM ubuntu
 
RUN apt-get update && apt-get install -y nodejs
ADD . /app
RUN cd /app && npm install
 
CMD npm start

4. 基礎鏡像的標簽不要用latest
當鏡像沒有指定標簽時,將默認使用latest 標簽。因此, FROM ubuntu 指令等同于FROM ubuntu:latest。當時,當鏡像更新時,latest標簽會指向不同的鏡像,這時構建鏡像有可能失敗。如果你的確需要使用最新版的基礎鏡像,可以使用latest標簽,否則的話,最好指定確定的鏡像標簽。

示例Dockerfile應該使用16.04作為標簽。

?
1
2
3
4
5
6
7
FROM ubuntu:16.04 # it's that easy!
 
RUN apt-get update && apt-get install -y nodejs
ADD . /app
RUN cd /app && npm install
 
CMD npm start

5. 每個RUN指令后刪除多余文件
假設我們更新了apt-get源,下載,解壓并安裝了一些軟件包,它們都保存在/var/lib/apt/lists/目錄中。但是,運行應用時Docker鏡像中并不需要這些文件。我們最好將它們刪除,因為它會使Docker鏡像變大。

示例Dockerfile中,我們可以刪除/var/lib/apt/lists/目錄中的文件(它們是由apt-get update生成的)。

?
1
2
3
4
5
6
7
8
9
10
11
FROM ubuntu:16.04
 
RUN apt-get update \
  && apt-get install -y nodejs \
  # added lines
  && rm -rf /var/lib/apt/lists/*
 
ADD . /app
RUN cd /app && npm install
 
CMD npm start

6. 選擇合適的基礎鏡像(alpine版本最好)

在示例中,我們選擇了ubuntu作為基礎鏡像。但是我們只需要運行node程序,有必要使用一個通用的基礎鏡像嗎?node鏡像應該是更好的選擇。

?
1
2
3
4
5
6
7
8
FROM node
 
ADD . /app
# we don't need to install node
# anymore and use apt-get
RUN cd /app && npm install
 
CMD npm start

更好的選擇是alpine版本的node鏡像。alpine是一個極小化的Linux發行版,只有4MB,這讓它非常適合作為基礎鏡像。

?
1
2
3
4
5
6
FROM node:7-alpine
 
ADD . /app
RUN cd /app && npm install
 
CMD npm start

apk是Alpine的包管理工具。它與apt-get有些不同,但是非常容易上手。另外,它還有一些非常有用的特性,比如no-cache和 --virtual選項,它們都可以幫助我們減少鏡像的大小。

7. 設置WORKDIR和 CMD

WORKDIR指令可以設置默認目錄,也就是運行RUN / CMD / ENTRYPOINT指令的地方。

CMD指令可以設置容器創建是執行的默認命令。另外,你應該講命令寫在一個數組中,數組中每個元素為命令的每個單詞(參考官方文檔)。

?
1
2
3
4
5
6
7
FROM node:7-alpine
 
WORKDIR /app
ADD . /app
RUN npm install
 
CMD ["npm", "start"]

8. 使用ENTRYPOINT (可選)

ENTRYPOINT指令并不是必須的,因為它會增加復雜度。ENTRYPOINT是一個腳本,它會默認執行,并且將指定的命令錯誤其參數。它通常用于構建可執行的Docker鏡像。entrypoint.sh如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env sh
# $0 is a script name,
# $1, $2, $3 etc are passed arguments
# $1 is our command
CMD=$1
 
case "$CMD" in
 "dev" )
  npm install
  export NODE_ENV=development
  exec npm run dev
  ;;
 
 "start" )
  # we can modify files here, using ENV variables passed in
  # "docker create" command. It can't be done during build process.
  echo "db: $DATABASE_ADDRESS" >> /app/config.yml
  export NODE_ENV=production
  exec npm start
  ;;
 
  * )
  # Run custom command. Thanks to this line we can still use
  # "docker run our_image /bin/bash" and it will work
  exec $CMD ${@:2}
  ;;
esac

示例Dockerfile:

?
1
2
3
4
5
6
7
8
FROM node:7-alpine
 
WORKDIR /app
ADD . /app
RUN npm install
 
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]

可以使用如下命令運行該鏡像:

?
1
2
3
4
5
6
7
8
# 運行開發版本
docker run our-app dev
 
# 運行生產版本
docker run our-app start
 
# 運行bash
docker run -it our-app /bin/bash

9. 在entrypoint腳本中使用exec
在前文的entrypoint腳本中,我使用了exec命令運行node應用。不使用exec的話,我們則不能順利地關閉容器,因為SIGTERM信號會被bash腳本進程吞沒。exec命令啟動的進程可以取代腳本進程,因此所有的信號都會正常工作。

10. COPY與ADD優先使用前者
COPY指令非常簡單,僅用于將文件拷貝到鏡像中。ADD相對來講復雜一些,可以用于下載遠程文件以及解壓壓縮包(參考官方文檔)。

?
1
2
3
4
5
6
7
8
9
FROM node:7-alpine
 
WORKDIR /app
 
COPY . /app
RUN npm install
 
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]

11. 合理調整COPY與RUN的順序
我們應該把變化最少的部分放在Dockerfile的前面,這樣可以充分利用鏡像緩存。

示例中,源代碼會經常變化,則每次構建鏡像時都需要重新安裝NPM模塊,這顯然不是我們希望看到的。因此我們可以先拷貝package.json,然后安裝NPM模塊,最后才拷貝其余的源代碼。這樣的話,即使源代碼變化,也不需要重新安裝NPM模塊。

?
1
2
3
4
5
6
7
8
9
10
FROM node:7-alpine
 
WORKDIR /app
 
COPY package.json /app
RUN npm install
COPY . /app
 
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]

12. 設置默認的環境變量,映射端口和數據卷

運行Docker容器時很可能需要一些環境變量。在Dockerfile設置默認的環境變量是一種很好的方式。另外,我們應該在Dockerfile中設置映射端口和數據卷。示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM node:7-alpine
 
ENV PROJECT_DIR=/app
 
WORKDIR $PROJECT_DIR
 
COPY package.json $PROJECT_DIR
RUN npm install
COPY . $PROJECT_DIR
 
ENV MEDIA_DIR=/media \
  NODE_ENV=production \
  APP_PORT=3000
 
VOLUME $MEDIA_DIR
EXPOSE $APP_PORT
 
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]

ENV指令指定的環境變量在容器中可以使用。如果你只是需要指定構建鏡像時的變量,你可以使用ARG指令。

13. 使用LABEL設置鏡像元數據
使用LABEL指令,可以為鏡像設置元數據,例如鏡像創建者或者鏡像說明。舊版的Dockerfile語法使用MAINTAINER指令指定鏡像創建者,但是它已經被棄用了。有時,一些外部程序需要用到鏡像的元數據,例如nvidia-docker需要用到com.nvidia.volumes.needed。示例如下:

?
1
2
3
FROM node:7-alpine
LABEL maintainer "[email protected]"
...

14. 添加HEALTHCHECK
運行容器時,可以指定--restart always選項。這樣的話,容器崩潰時,Docker守護進程(docker daemon)會重啟容器。對于需要長時間運行的容器,這個選項非常有用。但是,如果容器的確在運行,但是不可(陷入死循環,配置錯誤)用怎么辦?使用HEALTHCHECK指令可以讓Docker周期性的檢查容器的健康狀況。我們只需要指定一個命令,如果一切正常的話返回0,否則返回1。對HEALTHCHECK感興趣的話,可以參考這篇博客。示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FROM node:7-alpine
LABEL maintainer "[email protected]"
 
ENV PROJECT_DIR=/app
WORKDIR $PROJECT_DIR
 
COPY package.json $PROJECT_DIR
RUN npm install
COPY . $PROJECT_DIR
 
ENV MEDIA_DIR=/media \
  NODE_ENV=production \
  APP_PORT=3000
 
VOLUME $MEDIA_DIR
EXPOSE $APP_PORT
HEALTHCHECK CMD curl --fail http://localhost:$APP_PORT || exit 1
 
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]

當請求失敗時,curl --fail 命令返回非0狀態。

原文: How to write excellent Dockerfiles

譯者: Fundebug

原文鏈接:https://blog.fundebug.com/2017/05/15/write-excellent-dockerfile/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美成人精品福利网站 | 四虎国产精品免费久久麻豆 | 国产成人夜色91 | 亚洲高清成人 | 黑人巨摘花第一次出血 | 久久精品国产在热亚洲 | 狠狠色综合久久婷婷 | 国产精品亚洲专区一区 | 国产成人影院一区二区 | 公妇乱淫 | 四虎国产精品视频免费看 | 久久免费观看视频 | 亚洲欧美日韩在线观看看另类 | lilisha李丽莎喷水大胆在线 | 96日本xxxxxxxxx70 95在线观看精品视频 | 欧美午夜视频一区二区 | xxx黑人又大粗又长 xxxx性欧美极品另类 | 果冻传媒林予曦图片 | 亚洲欧美天堂综合久久 | 亚洲欧美日韩在线观看看另类 | 精品国产成人a区在线观看 精品国产91久久久久久久 | 四虎在线播放 | 欧美调教打屁股spank视频 | 色播影音先锋 | 1377大但人文艺术包子铺 | 亚洲国产成人久久综合一区 | bt天堂午夜国产精品 | 四虎最新永久在线精品免费 | 国产一区二区三区四区波多野结衣 | www.av色| 99精品网 | 色花堂中文字幕98堂网址 | 欧美人与物videos另类3d | 大桥未久一区二区 | 亚洲国产三级在线观看 | 国产成人精品午夜免费 | 狠狠色综合久久久久尤物 | 国产一级毛片国语版 | 国产香蕉一区二区在线网站 | 久久久久嫩草影院精品 | 免费又爽又黄禁片视频在线播放 |