日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Python3 - Dockerfile 最佳实践

發(fā)布時(shí)間:2023/12/16 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python3 - Dockerfile 最佳实践 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

    • 一、一般準(zhǔn)則和建議
      • 1.1 容器應(yīng)該是短暫的
      • 1.2 建立上下文
      • 1.3 使用`.dockerignore`文件
      • 1.4 使用多階段構(gòu)建
      • 1.5 避免安裝不必要的包
      • 1.6 一個(gè)容器只專注做一件事情
      • 1.7 最小化鏡像層數(shù)
      • 1.8 對多行參數(shù)排序
      • 1.9 構(gòu)建緩存
    • 二、Dockerfile 指令
      • 2.1 FROM
      • 2.2 LABEL
      • 2.3 CMD
      • 2.4 EXPOSE
      • 2.5 ENV
      • 2.6 ADD 和 COPY
      • 2.7 ENTRYPOINT
      • 2.8 VOLUME
      • 2.9 USER
      • 2.10 WORKDIR
      • 2.11 ONBUILD
      • 2.12 官方倉庫示例

Docker官方關(guān)于Dockerfile最佳實(shí)踐原文鏈接地址:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

Docker 可以通過從 Dockerfile 包含所有命令的文本文件中讀取指令自動(dòng)構(gòu)建鏡像,以便構(gòu)建給定鏡像。

Dockerfiles 使用特定的格式并使用一組特定的指令。您可以在Dockerfile Reference頁面上了解基礎(chǔ)知識(shí) 。如果你是新手寫作Dockerfile,你應(yīng)該從那里開始。

本文檔介紹了由 Docker,Inc. 和 Docker 社區(qū)推薦的用于構(gòu)建高效鏡像的最佳實(shí)踐和方法。要查看許多實(shí)踐和建議,請查看Dockerfile for buildpack-deps。

一、一般準(zhǔn)則和建議

1.1 容器應(yīng)該是短暫的

通過 Dockerfile 構(gòu)建的鏡像所啟動(dòng)的容器應(yīng)該盡可能短暫(生命周期短)。「短暫」意味著可以停止和銷毀容器,并且創(chuàng)建一個(gè)新容器并部署好所需的設(shè)置和配置工作量應(yīng)該是極小的。我們可以查看下12 Factor(12要素)應(yīng)用程序方法的進(jìn)程部分,可以讓我們理解這種無狀態(tài)方式運(yùn)行容器的動(dòng)機(jī)。

1.2 建立上下文

當(dāng)你發(fā)出一個(gè)docker build命令時(shí),當(dāng)前的工作目錄被稱為構(gòu)建上下文。默認(rèn)情況下,Dockerfile 就位于該路徑下,當(dāng)然您也可以使用-f參數(shù)來指定不同的位置。無論 Dockerfile 在什么地方,當(dāng)前目錄中的所有文件內(nèi)容都將作為構(gòu)建上下文發(fā)送到 Docker 守護(hù)進(jìn)程中去。

下面是一個(gè)構(gòu)建上下文的示例,為構(gòu)建上下文創(chuàng)建一個(gè)目錄并 cd 放入其中。將“hello”寫入一個(gè)文本文件hello,然后并創(chuàng)建一個(gè)Dockerfile并運(yùn)行cat。從構(gòu)建上下文(.)中構(gòu)建圖像:

mkdir myproject && cd myproject echo "hello" > hello echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile docker build -t helloapp:v1 .

現(xiàn)在移動(dòng) Dockerfile 和 hello 到不同的目錄,并建立了圖像的第二個(gè)版本(不依賴于緩存中的最后一個(gè)版本)。使用-f指向 Dockerfile 并指定構(gòu)建上下文的目錄:

mkdir -p dockerfiles context mv Dockerfile dockerfiles && mv hello context docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context

在構(gòu)建的時(shí)候包含不需要的文件會(huì)導(dǎo)致更大的構(gòu)建上下文和更大的鏡像大小。這會(huì)增加構(gòu)建時(shí)間,拉取和推送鏡像的時(shí)間以及容器的運(yùn)行時(shí)間大小。要查看您的構(gòu)建環(huán)境有多大,請?jiān)跇?gòu)建您的系統(tǒng)時(shí)查找這樣的消息

Dockerfile: Sending build context to Docker daemon 4.096kB Sending build context to Docker daemon 2.607kB

1.3 使用.dockerignore文件

使用 Dockerfile 構(gòu)建鏡像時(shí)最好是將 Dockerfile 放置在一個(gè)新建的空目錄下。然后將構(gòu)建鏡像所需要的文件添加到該目錄中。為了提高構(gòu)建鏡像的效率,你可以在目錄下新建一個(gè).dockerignore文件來指定要忽略的文件和目錄。.dockerignore 文件的排除模式語法和 Git 的 .gitignore 文件相似。

1.4 使用多階段構(gòu)建

在 Docker 17.05 以上版本中,你可以使用 多階段構(gòu)建 來減少所構(gòu)建鏡像的大小。上一節(jié)課我們已經(jīng)重點(diǎn)講解過了。

1.5 避免安裝不必要的包

為了降低復(fù)雜性、減少依賴、減小文件大小和構(gòu)建時(shí)間,應(yīng)該避免安裝額外的或者不必要的軟件包。例如,不要在數(shù)據(jù)庫鏡像中包含一個(gè)文本編輯器。

1.6 一個(gè)容器只專注做一件事情

應(yīng)該保證在一個(gè)容器中只運(yùn)行一個(gè)進(jìn)程。將多個(gè)應(yīng)用解耦到不同容器中,保證了容器的橫向擴(kuò)展和復(fù)用。例如一個(gè) web 應(yīng)用程序可能包含三個(gè)獨(dú)立的容器:web應(yīng)用、數(shù)據(jù)庫、緩存,每個(gè)容器都是獨(dú)立的鏡像,分開運(yùn)行。但這并不是說一個(gè)容器就只跑一個(gè)進(jìn)程,因?yàn)橛械某绦蚩赡軙?huì)自行產(chǎn)生其他進(jìn)程,比如 Celery 就可以有很多個(gè)工作進(jìn)程。雖然“每個(gè)容器跑一個(gè)進(jìn)程”是一條很好的法則,但這并不是一條硬性的規(guī)定。我們主要是希望一個(gè)容器只關(guān)注意見事情,盡量保持干凈和模塊化。

如果容器互相依賴,你可以使用Docker 容器網(wǎng)絡(luò)來把這些容器連接起來,我們前面已經(jīng)跟大家講解過 Docker 的容器網(wǎng)絡(luò)模式了。

1.7 最小化鏡像層數(shù)

在 Docker 17.05 甚至更早 1.10之 前,盡量減少鏡像層數(shù)是非常重要的,不過現(xiàn)在的版本已經(jīng)有了一定的改善了:

  • 在 1.10 以后,只有 RUN、COPY 和 ADD 指令會(huì)創(chuàng)建層,其他指令會(huì)創(chuàng)建臨時(shí)的中間鏡像,但是不會(huì)直接增加構(gòu)建的鏡像大小了。
  • 上節(jié)課我們也講解到了 17.05 版本以后增加了多階段構(gòu)建的支持,允許我們把需要的數(shù)據(jù)直接復(fù)制到最終的鏡像中,這就允許我們在中間階段包含一些工具或者調(diào)試信息了,而且不會(huì)增加最終的鏡像大小。

當(dāng)然減少RUN、COPY、ADD的指令仍然是很有必要的,但是我們也需要在 Dockerfile 可讀性(也包括長期的可維護(hù)性)和減少層數(shù)之間做一個(gè)平衡。

1.8 對多行參數(shù)排序

只要有可能,就將多行參數(shù)按字母順序排序(比如要安裝多個(gè)包時(shí))。這可以幫助你避免重復(fù)包含同一個(gè)包,更新包列表時(shí)也更容易,也更容易閱讀和審查。建議在反斜杠符號(hào) \ 之前添加一個(gè)空格,可以增加可讀性。 下面是來自 buildpack-deps 鏡像的例子:

RUN apt-get update && apt-get install -y \bzr \cvs \git \mercurial \subversion

1.9 構(gòu)建緩存

在鏡像的構(gòu)建過程中,Docker 根據(jù) Dockerfile 指定的順序執(zhí)行每個(gè)指令。在執(zhí)行每條指令之前,Docker 都會(huì)在緩存中查找是否已經(jīng)存在可重用的鏡像,如果有就使用現(xiàn)存的鏡像,不再重復(fù)創(chuàng)建。當(dāng)然如果你不想在構(gòu)建過程中使用緩存,你可以在 docker build 命令中使用--no-cache=true選項(xiàng)。

如果你想在構(gòu)建的過程中使用了緩存,那么了解什么時(shí)候可以什么時(shí)候無法找到匹配的鏡像就很重要了,Docker中緩存遵循的基本規(guī)則如下:

  • 從一個(gè)基礎(chǔ)鏡像開始(FROM 指令指定),下一條指令將和該基礎(chǔ)鏡像的所有子鏡像進(jìn)行匹配,檢查這些子鏡像被創(chuàng)建時(shí)使用的指令是否和被檢查的指令完全一樣。如果不是,則緩存失效。
  • 在大多數(shù)情況下,只需要簡單地對比 Dockerfile 中的指令和子鏡像。然而,有些指令需要更多的檢查和解釋。
  • 對于 ADD 和 COPY 指令,鏡像中對應(yīng)文件的內(nèi)容也會(huì)被檢查,每個(gè)文件都會(huì)計(jì)算出一個(gè)校驗(yàn)值。這些文件的修改時(shí)間和最后訪問時(shí)間不會(huì)被納入校驗(yàn)的范圍。在緩存的查找過程中,會(huì)將這些校驗(yàn)和和已存在鏡像中的文件校驗(yàn)值進(jìn)行對比。如果文件有任何改變,比如內(nèi)容和元數(shù)據(jù),則緩存失效。
  • 除了 ADD 和 COPY 指令,緩存匹配過程不會(huì)查看臨時(shí)容器中的文件來決定緩存是否匹配。例如,當(dāng)執(zhí)行完 RUN apt-get -y update 指令后,容器中一些文件被更新,但 Docker 不會(huì)檢查這些文件。這種情況下,只有指令字符串本身被用來匹配緩存。
  • 一旦緩存失效,所有后續(xù)的 Dockerfile 指令都將產(chǎn)生新的鏡像,緩存不會(huì)被使用。

二、Dockerfile 指令

下面是一些常用的 Dockerfile 指令,我們也分別來總結(jié)下,根據(jù)上面的建議和下面這些指令的合理使用,可以幫助我們編寫高效且易維護(hù)的 Dockerfile 文件。

2.1 FROM

盡可能使用當(dāng)前官方倉庫作為你構(gòu)建鏡像的基礎(chǔ)。推薦使用Alpine鏡像,因?yàn)樗粐?yán)格控制并保持最小尺寸(目前小于 5 MB),但它仍然是一個(gè)完整的發(fā)行版。

2.2 LABEL

你可以給鏡像添加標(biāo)簽來幫助組織鏡像、記錄許可信息、輔助自動(dòng)化構(gòu)建等。每個(gè)標(biāo)簽一行,由 LABEL 開頭加上一個(gè)或多個(gè)標(biāo)簽對。

下面的示例展示了各種不同的可能格式。#開頭的行是注釋內(nèi)容。

注意:如果你的字符串包含空格,那么它必須被引用或者空格必須被轉(zhuǎn)義。如果您的字符串包含內(nèi)部引號(hào)字符("),則也可以將其轉(zhuǎn)義。

# Set one or more individual labels LABEL com.example.version="0.0.1-beta" LABEL vendor="ACME Incorporated" LABEL com.example.release-date="2022-04-13" LABEL com.example.version.is-production=""

一個(gè)鏡像可以包含多個(gè)標(biāo)簽,在 1.10 之前,建議將所有標(biāo)簽合并為一條LABEL指令,以防止創(chuàng)建額外的層,但是現(xiàn)在這個(gè)不再是必須的了,以上內(nèi)容也可以寫成下面這樣:

# Set multiple labels at once, using line-continuation characters to break long lines LABEL vendor=ACME\ Incorporated \com.example.is-production="" \com.example.version="0.0.1-beta" \com.example.release-date="2022-04-13"

關(guān)于標(biāo)簽可以接受的鍵值對,參考Understanding object labels。

為了保持 Dockerfile 文件的可讀性,以及可維護(hù)性,建議將長的或復(fù)雜的RUN指令用反斜杠\分割成多行。

RUN 指令最常見的用法是安裝包用的apt-get。因?yàn)镽UN apt-get指令會(huì)安裝包,所以有幾個(gè)問題需要注意。

  • 不要使用 RUN apt-get upgrade 或 dist-upgrade,如果基礎(chǔ)鏡像中的某個(gè)包過時(shí)了,你應(yīng)該聯(lián)系它的維護(hù)者。如果你確定某個(gè)特定的包,比如 foo,需要升級(jí),使用 apt-get install -y foo 就行,該指令會(huì)自動(dòng)升級(jí) foo 包。

  • 永遠(yuǎn)將 RUN apt-get update 和 apt-get install 組合成一條 RUN 聲明,例如:

    RUN apt-get update && apt-get install -y \package-bar \package-baz \package-foo

將 apt-get update 放在一條單獨(dú)的 RUN 聲明中會(huì)導(dǎo)致緩存問題以及后續(xù)的 apt-get install 失敗。比如,假設(shè)你有一個(gè) Dockerfile 文件:

FROM ubuntu:14.04 RUN apt-get update RUN apt-get install -y curl

構(gòu)建鏡像后,所有的層都在 Docker 的緩存中。假設(shè)你后來又修改了其中的 apt-get install 添加了一個(gè)包:

FROM ubuntu:14.04 RUN apt-get update RUN apt-get install -y curl nginx

Docker 發(fā)現(xiàn)修改后的 RUN apt-get update 指令和之前的完全一樣。所以,apt-get update 不會(huì)執(zhí)行,而是使用之前的緩存鏡像。因?yàn)?apt-get update 沒有運(yùn)行,后面的 apt-get install 可能安裝的是過時(shí)的 curl 和 nginx 版本。

使用RUN apt-get update && apt-get install -y可以確保你的Dockerfiles每次安裝的都是包的最新的版本,而且這個(gè)過程不需要進(jìn)一步的編碼或額外干預(yù)。這項(xiàng)技術(shù)叫作cache busting(緩存破壞)。你也可以顯示指定一個(gè)包的版本號(hào)來達(dá)到 cache-busting,這就是所謂的固定版本,例如:

RUN apt-get update && apt-get install -y \package-bar \package-baz \package-foo=1.3.*

固定版本會(huì)迫使構(gòu)建過程檢索特定的版本,而不管緩存中有什么。這項(xiàng)技術(shù)也可以減少因所需包中未預(yù)料到的變化而導(dǎo)致的失敗。

下面是一個(gè) RUN 指令的示例模板,展示了所有關(guān)于 apt-get 的建議。

RUN apt-get update && apt-get install -y \aufs-tools \automake \build-essential \curl \dpkg-sig \libcap-dev \libsqlite3-dev \mercurial \reprepro \ruby1.9.1 \ruby1.9.1-dev \s3cmd=1.1.* \&& rm -rf /var/lib/apt/lists/*

其中 s3cmd 指令指定了一個(gè)版本號(hào) 1.1.*。如果之前的鏡像使用的是更舊的版本,指定新的版本會(huì)導(dǎo)致 apt-get udpate 緩存失效并確保安裝的是新版本。 另外,清理掉 apt 緩存 var/lib/apt/lists 可以減小鏡像大小。因?yàn)?RUN 指令的開頭為 apt-get udpate,包緩存總是會(huì)在 apt-get install 之前刷新。

注意:官方的 Debian 和 Ubuntu 鏡像會(huì)自動(dòng)運(yùn)行 apt-get clean,所以不需要顯式的調(diào)用 apt-get clean。

2.3 CMD

CMD指令用于執(zhí)行目標(biāo)鏡像中包含的軟件和任何參數(shù)。CMD 幾乎都是以CMD ["executable", "param1", "param2"...]的形式使用。因此,如果創(chuàng)建鏡像的目的是為了部署某個(gè)服務(wù)(比如 Apache),你可能會(huì)執(zhí)行類似于CMD ["apache2", "-DFOREGROUND"]形式的命令。

多數(shù)情況下,CMD 都需要一個(gè)交互式的 shell (bash, Python, perl 等),例如 CMD [“perl”, “-de0”],或者 CMD [“PHP”, “-a”]。使用這種形式意味著,當(dāng)你執(zhí)行類似docker run -it python時(shí),你會(huì)進(jìn)入一個(gè)準(zhǔn)備好的 shell 中。

CMD 在極少的情況下才會(huì)以 CMD [“param”, “param”] 的形式與ENTRYPOINT協(xié)同使用,除非你和你的鏡像使用者都對 ENTRYPOINT 的工作方式十分熟悉。

2.4 EXPOSE

EXPOSE指令用于指定容器將要監(jiān)聽的端口。因此,你應(yīng)該為你的應(yīng)用程序使用常見的端口。

例如,提供 Apache web 服務(wù)的鏡像應(yīng)該使用 EXPOSE 80,而提供 MongoDB 服務(wù)的鏡像使用 EXPOSE 27017。

對于外部訪問,用戶可以在執(zhí)行 docker run 時(shí)使用一個(gè)標(biāo)志來指示如何將指定的端口映射到所選擇的端口。

2.5 ENV

為了方便新程序運(yùn)行,你可以使用ENV來為容器中安裝的程序更新 PATH 環(huán)境變量。例如使用ENV PATH /usr/local/nginx/bin:$PATH來確保CMD ["nginx"]能正確運(yùn)行。

ENV 指令也可用于為你想要容器化的服務(wù)提供必要的環(huán)境變量,比如 Postgres 需要的 PGDATA。 最后,ENV 也能用于設(shè)置常見的版本號(hào),比如下面的示例:

ENV PG_MAJOR 9.3 ENV PG_VERSION 9.3.4 RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

類似于程序中的常量,這種方法可以讓你只需改變 ENV 指令來自動(dòng)的改變?nèi)萜髦械能浖姹尽?/p>

2.6 ADD 和 COPY

雖然ADD和COPY功能類似,但一般優(yōu)先使用 COPY。因?yàn)樗?ADD 更透明。COPY 只支持簡單將本地文件拷貝到容器中,而 ADD 有一些并不明顯的功能(比如本地 tar 提取和遠(yuǎn)程 URL 支持)。因此,ADD的最佳用例是將本地 tar 文件自動(dòng)提取到鏡像中,例如ADD rootfs.tar.xz

如果你的 Dockerfile 有多個(gè)步驟需要使用上下文中不同的文件。單獨(dú) COPY 每個(gè)文件,而不是一次性的 COPY 所有文件,這將保證每個(gè)步驟的構(gòu)建緩存只在特定的文件變化時(shí)失效。例如:

COPY requirements.txt /tmp/ RUN pip install --requirement /tmp/requirements.txt COPY . /tmp/

如果將COPY . /tmp/放置在 RUN 指令之前,只要 . 目錄中任何一個(gè)文件變化,都會(huì)導(dǎo)致后續(xù)指令的緩存失效。

為了讓鏡像盡量小,最好不要使用 ADD 指令從遠(yuǎn)程 URL 獲取包,而是使用 curl 和 wget。這樣你可以在文件提取完之后刪掉不再需要的文件來避免在鏡像中額外添加一層。比如盡量避免下面的用法:

ADD http://example.com/big.tar.xz /usr/src/things/ RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things RUN make -C /usr/src/things all

而是應(yīng)該使用下面這種方法:

RUN mkdir -p /usr/src/things \&& curl -SL http://example.com/big.tar.xz \| tar -xJC /usr/src/things \&& make -C /usr/src/things all

上面使用的管道操作,所以沒有中間文件需要?jiǎng)h除。 對于其他不需要 ADD 的自動(dòng)提取功能的文件或目錄,你應(yīng)該使用 COPY。

2.7 ENTRYPOINT

ENTRYPOINT的最佳用處是設(shè)置鏡像的主命令,允許將鏡像當(dāng)成命令本身來運(yùn)行(用 CMD 提供默認(rèn)選項(xiàng))。

例如,下面的示例鏡像提供了命令行工具 s3cmd:

ENTRYPOINT ["s3cmd"] CMD ["--help"]

現(xiàn)在直接運(yùn)行該鏡像創(chuàng)建的容器會(huì)顯示命令幫助:

docker run s3cmd

或者提供正確的參數(shù)來執(zhí)行某個(gè)命令:

docker run s3cmd ls s3://mybucket

這樣鏡像名可以當(dāng)成命令行的參考。ENTRYPOINT 指令也可以結(jié)合一個(gè)輔助腳本使用,和前面命令行風(fēng)格類似,即使啟動(dòng)工具需要不止一個(gè)步驟。

例如,Postgres 官方鏡像使用下面的腳本作為 ENTRYPOINT:

#!/bin/bash set -e if [ "$1" = 'postgres' ]; thenchown -R postgres "$PGDATA"if [ -z "$(ls -A "$PGDATA")" ]; thengosu postgres initdbfiexec gosu postgres "$@"fi exec "$@"

注意:該腳本使用了 Bash 的內(nèi)置命令 exec,所以最后運(yùn)行的進(jìn)程就是容器的 PID 為 1 的進(jìn)程。這樣,進(jìn)程就可以接收到任何發(fā)送給容器的 Unix 信號(hào)了。

該輔助腳本被拷貝到容器,并在容器啟動(dòng)時(shí)通過 ENTRYPOINT 執(zhí)行:

COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"]

該腳本可以讓用戶用幾種不同的方式和 Postgres 交互。你可以很簡單地啟動(dòng) Postgres:

docker run postgres

也可以執(zhí)行 Postgres 并傳遞參數(shù):

docker run postgres postgres --help

最后,你還可以啟動(dòng)另外一個(gè)完全不同的工具,比如 Bash:

docker run --rm -it postgres bash

2.8 VOLUME

VOLUME指令用于暴露任何數(shù)據(jù)庫存儲(chǔ)文件,配置文件,或容器創(chuàng)建的文件和目錄。強(qiáng)烈建議使用 VOLUME來管理鏡像中的可變部分和用戶可以改變的部分。

2.9 USER

如果某個(gè)服務(wù)不需要特權(quán)執(zhí)行,建議使用 USER 指令切換到非 root 用戶。先在 Dockerfile 中使用類似 RUN groupadd -r postgres && useradd -r -g postgres postgres 的指令創(chuàng)建用戶和用戶組。

注意:在鏡像中,用戶和用戶組每次被分配的 UID/GID 都是不確定的,下次重新構(gòu)建鏡像時(shí)被分配到的 UID/GID 可能會(huì)不一樣。如果要依賴確定的 UID/GID,你應(yīng)該顯示的指定一個(gè) UID/GID。

你應(yīng)該避免使用 sudo,因?yàn)樗豢深A(yù)期的 TTY 和信號(hào)轉(zhuǎn)發(fā)行為可能造成的問題比它能解決的問題還多。如果你真的需要和 sudo 類似的功能(例如,以 root 權(quán)限初始化某個(gè)守護(hù)進(jìn)程,以非 root 權(quán)限執(zhí)行它),你可以使用 gosu。

最后,為了減少層數(shù)和復(fù)雜度,避免頻繁地使用 USER 來回切換用戶。

2.10 WORKDIR

為了清晰性和可靠性,你應(yīng)該總是在WORKDIR中使用絕對路徑。另外,你應(yīng)該使用 WORKDIR 來替代類似于 RUN cd … && do-something 的指令,后者難以閱讀、排錯(cuò)和維護(hù)。

2.11 ONBUILD

格式:ONBUILD <其它指令>。 ONBUILD是一個(gè)特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而這些指令,在當(dāng)前鏡像構(gòu)建時(shí)并不會(huì)被執(zhí)行。只有當(dāng)以當(dāng)前鏡像為基礎(chǔ)鏡像,去構(gòu)建下一級(jí)鏡像的時(shí)候才會(huì)被執(zhí)行。Dockerfile 中的其它指令都是為了定制當(dāng)前鏡像而準(zhǔn)備的,唯有 ONBUILD 是為了幫助別人定制自己而準(zhǔn)備的。

假設(shè)我們要制作 Node.js 所寫的應(yīng)用的鏡像。我們都知道 Node.js 使用 npm 進(jìn)行包管理,所有依賴、配置、啟動(dòng)信息等會(huì)放到 package.json 文件里。在拿到程序代碼后,需要先進(jìn)行 npm install 才可以獲得所有需要的依賴。然后就可以通過 npm start 來啟動(dòng)應(yīng)用。因此,一般來說會(huì)這樣寫 Dockerfile:

FROM node:slim RUN mkdir /app WORKDIR /app COPY ./package.json /app RUN [ "npm", "install" ] COPY . /app/ CMD [ "npm", "start" ]

把這個(gè) Dockerfile 放到 Node.js 項(xiàng)目的根目錄,構(gòu)建好鏡像后,就可以直接拿來啟動(dòng)容器運(yùn)行。但是如果我們還有第二個(gè) Node.js 項(xiàng)目也差不多呢?好吧,那就再把這個(gè) Dockerfile 復(fù)制到第二個(gè)項(xiàng)目里。那如果有第三個(gè)項(xiàng)目呢?再復(fù)制么?文件的副本越多,版本控制就越困難,讓我們繼續(xù)看這樣的場景維護(hù)的問題:

如果第一個(gè) Node.js 項(xiàng)目在開發(fā)過程中,發(fā)現(xiàn)這個(gè) Dockerfile 里存在問題,比如敲錯(cuò)字了、或者需要安裝額外的包,然后開發(fā)人員修復(fù)了這個(gè) Dockerfile,再次構(gòu)建,問題解決。第一個(gè)項(xiàng)目沒問題了,但是第二個(gè)項(xiàng)目呢?雖然最初 Dockerfile 是復(fù)制、粘貼自第一個(gè)項(xiàng)目的,但是并不會(huì)因?yàn)榈谝粋€(gè)項(xiàng)目修復(fù)了他們的 Dockerfile,而第二個(gè)項(xiàng)目的 Dockerfile 就會(huì)被自動(dòng)修復(fù)。

那么我們可不可以做一個(gè)基礎(chǔ)鏡像,然后各個(gè)項(xiàng)目使用這個(gè)基礎(chǔ)鏡像呢?這樣基礎(chǔ)鏡像更新,各個(gè)項(xiàng)目不用同步 Dockerfile 的變化,重新構(gòu)建后就繼承了基礎(chǔ)鏡像的更新?好吧,可以,讓我們看看這樣的結(jié)果。那么上面的這個(gè) Dockerfile 就會(huì)變?yōu)?#xff1a;

FROM node:slim RUN mkdir /app WORKDIR /app CMD [ "npm", "start" ]

這里我們把項(xiàng)目相關(guān)的構(gòu)建指令拿出來,放到子項(xiàng)目里去。假設(shè)這個(gè)基礎(chǔ)鏡像的名字為 my-node 的話,各個(gè)項(xiàng)目內(nèi)的自己的 Dockerfile 就變?yōu)?#xff1a;

FROM my-node COPY ./package.json /app RUN [ "npm", "install" ] COPY . /app/

基礎(chǔ)鏡像變化后,各個(gè)項(xiàng)目都用這個(gè) Dockerfile 重新構(gòu)建鏡像,會(huì)繼承基礎(chǔ)鏡像的更新。

那么,問題解決了么?沒有。準(zhǔn)確說,只解決了一半。如果這個(gè) Dockerfile 里面有些東西需要調(diào)整呢?比如 npm install 都需要加一些參數(shù),那怎么辦?這一行 RUN 是不可能放入基礎(chǔ)鏡像的,因?yàn)樯婕暗搅水?dāng)前項(xiàng)目的 ./package.json,難道又要一個(gè)個(gè)修改么?所以說,這樣制作基礎(chǔ)鏡像,只解決了原來的 Dockerfile 的前4條指令的變化問題,而后面三條指令的變化則完全沒辦法處理。

ONBUILD 可以解決這個(gè)問題。讓我們用 ONBUILD 重新寫一下基礎(chǔ)鏡像的 Dockerfile:

FROM node:slim RUN mkdir /app WORKDIR /app ONBUILD COPY ./package.json /app ONBUILD RUN [ "npm", "install" ] ONBUILD COPY . /app/ CMD [ "npm", "start" ]

這次我們回到原始的 Dockerfile,但是這次將項(xiàng)目相關(guān)的指令加上 ONBUILD,這樣在構(gòu)建基礎(chǔ)鏡像的時(shí)候,這三行并不會(huì)被執(zhí)行。然后各個(gè)項(xiàng)目的 Dockerfile 就變成了簡單地:

FROM my-node

是的,只有這么一行。當(dāng)在各個(gè)項(xiàng)目目錄中,用這個(gè)只有一行的 Dockerfile 構(gòu)建鏡像時(shí),之前基礎(chǔ)鏡像的那三行 ONBUILD 就會(huì)開始執(zhí)行,成功的將當(dāng)前項(xiàng)目的代碼復(fù)制進(jìn)鏡像、并且針對本項(xiàng)目執(zhí)行 npm install,生成應(yīng)用鏡像。

2.12 官方倉庫示例

這些官方倉庫的 Dockerfile 都是參考典范:https://github.com/docker-library/docs

好啦🌶🌶, Dockerfile的最佳使用就到這里, 喜歡就點(diǎn)個(gè)贊吧~ ???

總結(jié)

以上是生活随笔為你收集整理的Python3 - Dockerfile 最佳实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。