欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

編寫最佳的Dockerfile的方法

 更新時間:2017年06月04日 10:05:20   作者:fundebug  
本文給大家分享的是如何編寫最佳的dockerfile的方法,通過具體實例幫助大家快速掌握編寫Dockerfile的技巧

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

為了保證可讀性,本文采用意譯而非直譯。另外,本文版權(quán)歸原作者所有,翻譯僅用于學(xué)習(xí)。

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

目標(biāo):

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

總結(jié)

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

示例

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

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 

構(gòu)建鏡像:

docker build -t wtf .

1. 編寫.dockerignore文件

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

.git/
node_modules/  

2. 容器只運行單個應(yīng)用

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

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

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

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

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

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,這顯然不合適。因此,正確的寫法是這樣的:

FROM ubuntu

RUN apt-get update && apt-get install -y nodejs 
ADD . /app 
RUN cd /app && npm install

CMD npm start 

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

示例Dockerfile應(yīng)該使用16.04作為標(biāo)簽。

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指令后刪除多余文件
假設(shè)我們更新了apt-get源,下載,解壓并安裝了一些軟件包,它們都保存在/var/lib/apt/lists/目錄中。但是,運行應(yīng)用時Docker鏡像中并不需要這些文件。我們最好將它們刪除,因為它會使Docker鏡像變大。

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

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. 選擇合適的基礎(chǔ)鏡像(alpine版本最好)

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

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發(fā)行版,只有4MB,這讓它非常適合作為基礎(chǔ)鏡像。

FROM node:7-alpine

ADD . /app 
RUN cd /app && npm install

CMD npm start 

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

7. 設(shè)置WORKDIR和 CMD

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

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

FROM node:7-alpine

WORKDIR /app 
ADD . /app 
RUN npm install

CMD ["npm", "start"] 

8. 使用ENTRYPOINT (可選)

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

#!/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:

FROM node:7-alpine

WORKDIR /app 
ADD . /app 
RUN npm install

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"] 

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

# 運行開發(fā)版本
docker run our-app dev 

# 運行生產(chǎn)版本
docker run our-app start 

# 運行bash
docker run -it our-app /bin/bash

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

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

FROM node:7-alpine

WORKDIR /app

COPY . /app 
RUN npm install

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"] 

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

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

FROM node:7-alpine

WORKDIR /app

COPY package.json /app 
RUN npm install 
COPY . /app

ENTRYPOINT ["./entrypoint.sh"] 
CMD ["start"] 

12. 設(shè)置默認的環(huán)境變量,映射端口和數(shù)據(jù)卷

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

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指令指定的環(huán)境變量在容器中可以使用。如果你只是需要指定構(gòu)建鏡像時的變量,你可以使用ARG指令。

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

FROM node:7-alpine 
LABEL maintainer "jakub.skalecki@example.com" 
...

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

FROM node:7-alpine 
LABEL maintainer "jakub.skalecki@example.com"

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"] 

當(dāng)請求失敗時,curl --fail 命令返回非0狀態(tài)。

原文: How to write excellent Dockerfiles

譯者: Fundebug

相關(guān)文章

  • docker中配置hosts的方法實現(xiàn)

    docker中配置hosts的方法實現(xiàn)

    本文主要介紹了docker中配置hosts的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Docker容器操作方法詳解

    Docker容器操作方法詳解

    Docker 在隔離的容器中運行進程。之前總結(jié)了Docker鏡像的使用詳解,本篇按照之前測試openGauss容器過程中總結(jié)出來使用Docker容器的常用命令
    2022-08-08
  • docker 部署prometheus+grafana的過程詳解

    docker 部署prometheus+grafana的過程詳解

    這篇文章主要介紹了docker 部署prometheus+grafana的過程,本文通過示例圖文相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下
    2024-03-03
  • Docker私有倉庫打開2375端口的實現(xiàn)

    Docker私有倉庫打開2375端口的實現(xiàn)

    本文主要介紹了Docker私有倉庫打開2375端口的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • docker?安裝?Influxdb-relay及配置教程

    docker?安裝?Influxdb-relay及配置教程

    這篇文章主要介紹了docker?安裝?Influxdb-relay及配置教程的相關(guān)資料,需要的朋友可以參考下
    2023-08-08
  • Docker搭建Portainer可視化界面步驟詳解

    Docker搭建Portainer可視化界面步驟詳解

    在本篇文章里小編給大家分享的是一篇關(guān)于Docker搭建Portainer可視化界面的實例操作方法,需要的朋友們參考下。
    2019-10-10
  • 詳解Docker數(shù)據(jù)管理(數(shù)據(jù)卷&數(shù)據(jù)卷容器)

    詳解Docker數(shù)據(jù)管理(數(shù)據(jù)卷&數(shù)據(jù)卷容器)

    容器中管理數(shù)據(jù)主要有兩種方式,這篇文章主要介紹了詳解Docker數(shù)據(jù)管理(數(shù)據(jù)卷&數(shù)據(jù)卷容器) ,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 講解使用Docker搭建Java Web運行環(huán)境

    講解使用Docker搭建Java Web運行環(huán)境

    本篇文章詳細的介紹了使用Docker搭建Java Web運行環(huán)境,想要學(xué)習(xí)docker的同學(xué)可以了解一下。
    2016-11-11
  • 詳解掛載運行的docker容器中如何掛載文件系統(tǒng)

    詳解掛載運行的docker容器中如何掛載文件系統(tǒng)

    這篇文章主要給大家介紹了如何在掛載運行中的docker容器中掛載文件系統(tǒng),文中通過一步步的實踐過程介紹的很詳細,相信對有需要的朋友們來說具有一定的參考借鑒價值,感興趣的朋友們下面來一起看看吧。
    2016-12-12
  • Docker鏡像加速的詳細教程

    Docker鏡像加速的詳細教程

    Docker 鏡像非常輕量級,它們是基于分層的文件系統(tǒng)構(gòu)建的,因此許多 Docker 鏡像可以共享相同的基礎(chǔ)鏡像,這也使得它們在存儲和傳輸時非常高效,這篇文章主要介紹了Docker鏡像加速的詳細教程,需要的朋友可以參考下
    2024-02-02

最新評論