日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

编写Dockerfiles的最佳做法

發布時間:2024/4/14 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编写Dockerfiles的最佳做法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

編寫Dockerfiles的最佳做法


Docker可以通過從Dockerfile包含所有命令的文本文件中讀取?指令,自動構建圖像,以便構建給定圖像所需的順序。Dockerfile堅持一個具體的格式,并使用一組具體的說明。您可以在Dockerfile參考頁面上了解基礎知識?。如果你是新來Dockerfile的,你應該從那里開始。

本文檔介紹了Docker,Inc.和Docker社區推薦的最佳做法和方法,以創建易于使用,有效?Dockerfile的。我們強烈建議您遵循這些建議(實際上,如果您正在創建官方圖片,則必須遵守這些做法)。

您可以在buildpack-depsDockerfile中看到許多這些做法和建議。

注意:有關這里提到的任何Dockerfile命令的更詳細的解釋,請訪問Dockerfile參考頁面。

一般準則和建議

容器應該是短暫的

由您Dockerfile定義的圖像生成的容器應盡可能短暫。通過“短暫的”,我們意味著它可以被停止和破壞,一個新的建立和建立,絕對最小的設置和配置。您可能需要查看“12因子”應用程序方法的“?過程”部分,了解以無國籍方式運行容器的動機。

使用.dockerignore文件

在大多數情況下,最好將每個Dockerfile放在一個空目錄中。然后,僅添加構建Dockerfile所需的文件。為了增加構建的性能,您可以通過向該.dockerignore目錄添加文件來排除文件和目錄。此文件支持類似于.gitignore文件的排除模式。有關創建的信息,請參閱.dockerignore文件。

避免安裝不必要的包

為了減少復雜性,依賴關系,文件大小和構建時間,您應該避免安裝額外的或不必要的軟件包,因為它們可能“很好”。例如,您不需要在數據庫中包含文本編輯器圖片。

每個容器應該只有一個問題

將應用程序解耦到多個容器中可以更輕松地水平擴展和重新使用容器。例如,Web應用程序堆??赡苡扇齻€獨立的容器組成,每個容器具有自己獨特的映像,以解耦的方式管理Web應用程序,數據庫和內存中緩存。

你可能聽說應該有“每個集裝箱一個進程”。雖然這個口頭禪意圖很好,但并不一定每個容器只能有一個操作系統進程。除了現在可以使用init進程產生容器的事實之外,一些程序可能會自行產生其他進程。例如,芹菜可以產生多個工作進程,或阿帕奇可以創建每個請求的過程。雖然“每個集裝箱的一個過程”通常是一個很好的經驗法則,但這不是一個艱難和快速的規則。盡可能地判斷容器是否干凈,模塊化。

如果容器依賴于彼此,則可以使用Docker容器網絡?來確保這些容器可以通信。

最小化層數

您需要找到可用性(從而長期可維護性)之間的平衡,Dockerfile并最大限度地減少其使用的層數。對您使用的層數進行戰略和謹慎。

排序多行參數

只要有可能,通過以字母數字排序多行參數來緩解以后的變化。這將幫助您避免重復的包,并使列表更容易更新。這也使得PR更容易閱讀和審查。在反斜杠(\)之前添加一個空格也有幫助。

這是buildpack-deps圖像的一個例子:

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

構建緩存

在構建映像的過程中,Docker將按照指定Dockerfile的順序執行每個指令。隨著每條指令的檢查,Docker將在其緩存中查找可重用的現有映像,而不是創建一個新的(重復)映像。如果您不想使用緩存,您可以使用--no-cache=true?該docker build命令的選項。

但是,如果您確實讓Docker使用其緩存,那么了解何時會找到匹配的映像是非常重要的。Docker將遵循的基本規則如下:

  • 從已經在緩存中的基本圖像開始,將下一條指令與從該基本圖像導出的所有子圖像進行比較,以查看其中一條是否使用完全相同的指令構建。如果沒有,則緩存無效。

  • 在大多數情況下,簡單地比較Dockerfile與其中一個子圖像的指令是足夠的。但是,某些說明需要更多的檢查和解釋。

  • 對于ADD和COPY指令,檢查圖像中文件的內容,并為每個文件計算校驗和。在這些校驗和中不考慮文件的最后修改和最后訪問的時間。在緩存查找期間,將校驗和與現有映像中的校驗和進行比較。如果文件(如內容和元數據)中有任何變化,則緩存無效。

  • 除了ADD和COPY命令之外,緩存檢查不會查看容器中的文件來確定緩存匹配。例如,當處理RUN apt-get -y update命令時,將不會檢查在容器中更新的文件以確定是否存在高速緩存命中。在這種情況下,只需使用命令字符串本身來查找匹配。

一旦緩存無效,所有后續Dockerfile命令將生成新的映像,并且高速緩存將不被使用。

Dockerfile指令

您可以在下面找到建議,以便最好地編寫可用于其中的各種說明Dockerfile。

FROM

用于FROM指令的Docker文件引用

只要有可能,使用當前的官方存儲庫作為您的圖像的基礎。我們建議使用Debian鏡像,?因為它是非常嚴格的控制,并保持最小(目前在150 mb),而仍然是一個完整的分布。

標簽

了解對象標簽

您可以為圖像添加標簽,以幫助按項目組織圖像,記錄許可信息,幫助自動化或其他原因。對于每個標簽,添加LABEL以一個或多個鍵值對開頭的行。以下示例顯示不同的可接受格式。解釋性意見包括在內。

注意:如果您的字符串包含空格,則必須使用引號空格必須轉義。如果您的字符串包含內部引號字符("),也可以轉義它們。

#?Set?one?or?more?individual?labelsLABEL?com.example.version="0.0.1-beta"LABEL?vendor="ACME?Incorporated"LABEL?com.example.release-date="2015-02-12"LABEL?com.example.version.is-production=""#?Set?multiple?labels?on?one?lineLABEL?com.example.version="0.0.1-beta"?com.example.release-date="2015-02-12"#?Set?multiple?labels?at?once,?using?line-continuation?characters?to?break?long?linesLABEL?vendor=ACME\?Incorporated?\??????com.example.is-beta=?\??????com.example.is-production=""?\??????com.example.version="0.0.1-beta"?\??????com.example.release-date="2015-02-12"

有關可接受的標簽鍵和值的指導,請參閱了解對象標簽。有關查詢標簽的信息,請參閱管理對象標簽中過濾相關的項目?。

Dock指令的Docker文件引用

和往常一樣,為了使您Dockerfile更易于閱讀,易于理解和可維護,RUN可以用多個行分隔長條或復合語句,并以反斜杠分隔。

APT-GET的

可能最常用的用例RUN是應用程序apt-get。該?RUN apt-get命令,因為它安裝軟件包,有幾個需要注意的問題。

您應該避免RUN apt-get upgrade或者dist-upgrade,基本圖像中的許多“基本”軟件包將不會在非特權容器內升級。如果基本圖像中包含的包裝已過期,則應聯系其維護者。如果您知道有特定的軟件包,foo需要更新,請使用?apt-get install -y foo自動更新。

總是在同一個?語句中結合?RUN apt-get update使用,例如:apt-get installRUN

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

apt-get update在RUN語句中單獨使用會導致緩存問題和后續apt-get install指令失敗。例如,說你有一個Docker文件:

????FROM?ubuntu:14.04RUN?apt-get?updateRUN?apt-get?install?-y?curl

構建圖像后,所有圖層都在Docker緩存中。假設你以后apt-get install通過添加額外的包來修改:

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

Docker將初始和修改的指令看作是相同的,并重新使用先前步驟的緩存。結果apt-get update是不執行,因為構建使用緩存的版本。因為apt-get update沒有運行,你的構建可能會有一個過時的版本curl和nginx包。

使用?RUN apt-get update && apt-get install -y確保您的Dockerfile安裝最新的軟件包版本,無需進一步的編碼或手動干預。這種技術被稱為“緩存破解”。您還可以通過指定包版本來實現緩存清除。這被稱為版本固定,例如:

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

版本固定強制構建以檢索特定版本,而不管緩存中有什么。這種技術還可以減少由于所需軟件包中意外的更改導致的故障。

以下是一個RUN格式正確的指導,顯示所有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指令指定的版本1.1.*。如果圖像以前使用過舊版本,則指定新版本會導致緩存apt-get update破壞,并確保新版本的安裝。在每行上列出包也可以防止包重復中的錯誤。

另外,當您通過刪除/var/lib/apt/lists?減少映像大小來清理apt緩存時,由于apt緩存未存儲在圖層中。由于?RUN語句開始apt-get update,包緩存將始終在之前刷新apt-get install。

注意:Debian和Ubuntu的官方圖像自動運行apt-get clean,因此不需要顯式調用。

使用管道

一些RUN命令取決于使用管道字符(|)將一個命令的輸出管道傳輸到另一個命令的能力,如以下示例所示:

RUN?wget?-O?-?https://some.site?|?wc?-l?>?/number

Docker使用/bin/sh -c解釋器執行這些命令,該解釋器僅評估管道中最后一個操作的退出代碼以確定成功。在上述示例中,只要wc -l命令成功,即使wget命令失敗,此構建步驟也可以成功并生成新映像。

如果您希望命令由于管道中任何階段的錯誤而失敗set -o pipefail &&,請先確認出現意外的錯誤會阻止構建過程無意中成功。例如:

RUN?set?-o?pipefail?&&?wget?-O?-?https://some.site?|?wc?-l?>?/number

注意:并非所有的shell都支持該-o pipefail選項。在這種情況下(例如dashshell,它是基于Debian的映像的默認shell),請考慮使用exec表單RUN?來明確選擇一個支持該pipefail選項的shell?。例如:

RUN?["/bin/bash",?"-c",?"set?-o?pipefail?&&?wget?-O?-?https://some.site?|?wc?-l?>?/number"]

CMD

用于CMD指令的Docker文件引用

該CMD指令應用于運行圖像包含的軟件以及任何參數。CMD應該幾乎總是以形式使用CMD [“executable”, “param1”, “param2”…]。因此,如果圖像用于服務,例如Apache和Rails,則可以運行類似的操作?CMD ["apache2","-DFOREGROUND"]。實際上,這種形式的指令是推薦用于任何基于服務的圖像。

在其他大多數情況下,CMD應該給出一個交互式的shell,比如bash,python和perl。例如,CMD ["perl", "-de0"],CMD ["python"],或?CMD [“php”, “-a”]。使用此表單意味著當您執行類似的操作?docker run -it python時,您將被放入可用的shell中,隨時可以使用。?CMD應該很少的方式使用CMD [“param”, “param”]會同ENTRYPOINT,除非你和你預期的用戶已經非常熟悉如何ENTRYPOINT?工作的。

EXPOSE

用于EXPOSE指令的Docker文件引用

該EXPOSE指令指示容器將偵聽連接的端口。因此,您應該為應用程序使用通用的傳統端口。例如,包含Apache Web服務器EXPOSE 80的映像將使用,而包含MongoDB的映像將使用EXPOSE 27017等等。

對于外部訪問,您的用戶可以執行docker run一個標志,指示如何將指定的端口映射到他們選擇的端口。對于容器鏈接,Docker為從容器容器返回源(即MYSQL_PORT_3306_TCP)的路徑提供環境變量。

ENV

用于ENV指令的Docker文件引用

為了使新軟件更容易運行,您可以使用它ENV來更新PATH容器安裝的軟件的?環境變量。例如,ENV PATH /usr/local/nginx/bin:$PATH將確保CMD [“nginx”]?只是工作。

該ENV指令對于提供特定于要集中化的服務所需的環境變量(如Postgres's)也很有用?PGDATA。

最后,ENV也可以用于設置常用的版本號,以便版本顛覆更容易維護,如以下示例所示:

ENV?PG_MAJOR?9.3ENV?PG_VERSION?9.3.4RUN?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指令以自動神奇地碰撞容器中的軟件版本。

添加或復制

用于ADD指令的
Dockerfile引用用于COPY指令的Dockerfile引用

雖然ADD并且COPY在功能上類似,但一般來說COPY?是優選的。這是因為它比透明度更高ADD。COPY只支持將本地文件基本復制到容器中,同時ADD具有一些功能(如本地僅提取和遠程URL支持),這些功能并不明顯。因此,最好的用途ADD是本地tar文件自動提取到圖像中,如圖所示ADD rootfs.tar.xz /。

如果您有多個Dockerfile步驟可以從上下文中使用不同的文件,那么COPY它們可以單獨使用,而不是一次使用。如果特定需要的文件更改,這將確保每個步驟的構建緩存僅被無效(強制重新運行該步驟)。

例如:

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

導致RUN步驟的緩存無效的數量減少,而不是放在?COPY . /tmp/前面。

由于圖像尺寸很重要,ADD因此強烈不鼓勵使用遠程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/thingsRUN?make?-C?/usr/src/things?all

而是做一些像:

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

對于不需要ADDtar自動提取功能的其他項目(文件,目錄),您應該始終使用COPY。

ENTRYPOINT

用于ENTRYPOINT指令的Dockerfile引用

最好的用途ENTRYPOINT是設置圖像的主要命令,允許該圖像像該命令一樣運行(然后CMD用作默認標志)。

我們從一個命令行工具圖像的例子開始s3cmd:

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

現在可以像這樣運行映像來顯示命令的幫助:

$?docker?run?s3cmd

或使用正確的參數執行命令:

$?docker?run?s3cmd?ls?s3://mybucket

這是有用的,因為圖像名稱可以加倍作為二進制文件的參考,如上面的命令所示。

該ENTRYPOINT指令也可以與輔助腳本組合使用,允許其以類似于上述命令的方式運行,即使啟動該工具可能需要多于一個步驟。

例如,Postgres Official Image?使用以下腳本作為其ENTRYPOINT:

#!/bin/bashset?-eif?[?"$1"?=?'postgres'?];?then????chown?-R?postgres?"$PGDATA"if?[?-z?"$(ls?-A?"$PGDATA")"?];?then????????gosu?postgres?initdb????fi????exec?gosu?postgres?"$@"fiexec?"$@"

:此腳本使用的execbash命令?,使最終運行的應用程序成為容器的PID 1.這允許應用程序接收發送到容器任何Unix信號。有關ENTRYPOINT?詳細信息,請參閱幫助。

幫助腳本被復制到容器中并通過ENTRYPOINT容器開始運行:

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

此腳本允許用戶以多種方式與Postgres進行交互。

它可以簡單地啟動Postgres:

$?docker?run?postgres

或者,它可以用于運行Postgres并將參數傳遞給服務器:

$?docker?run?postgres?postgres?--help

最后,它也可以用來啟動一個完全不同的工具,比如Bash:

$?docker?run?--rm?-it?postgres?bash

VOLUME

用于VOLUME指令的Docker文件引用

該VOLUME指令應用于公開您的docker容器創建的任何數據庫存儲區域,配置存儲或文件/文件夾。強烈建議您使用圖像VOLUME的任何可變和/或用戶可維修的部分。

用戶

Docker文件引用USER指令

如果一個服務可以沒有權限運行,可以使用USER來更改非root用戶。通過創建在用戶和組開始Dockerfile喜歡的東西RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres。

注意:圖像中的用戶和組獲得非確定性的UID / GID,因為“下一個”UID / GID被分配,而不管圖像重建。所以,如果是至關重要的,你應該分配一個顯式的UID / GID。

注意:由于?Go存檔/ tar包處理稀疏文件中的一個未解決的錯誤,嘗試在Docker容器中創建一個足夠大的UID的用戶可能會導致磁盤耗盡,因為/var/log/faillog容器層中填充有NUL(\ 0 )字符?將--no-log-init標志傳遞給useradd可以解決這個問題。Debian / Ubuntu?adduser包裝器不支持該--no-log-init標志,應該避免。

您應避免安裝或使用,sudo因為它具有不可預測的TTY和信號轉發行為,可能會導致比解決問題更多的問題。如果您絕對需要類似的功能sudo(例如,以root用戶身份初始化守護程序,但以非root身份運行),則可以使用?“gosu”。

最后,為了減少層次和復雜性,請避免頻繁地進行USER切換。

WORKDIR

Docker文件參考WORKDIR指令

為了清楚和可靠,您應該始終為您的絕對路徑?WORKDIR。此外,您應該使用,WORKDIR而不是增加的指令,如RUN cd … && do-something難以閱讀,排除故障和維護。

ONBUILD

用于ONBUILD指令的Dockerfile引用

一個ONBUILD命令在當前Dockerfile構建完成后執行。?ONBUILD在導出FROM當前圖像的任何子圖像中執行。將該ONBUILD命令視為父母Dockerfile給予孩子的指示Dockerfile。

Docker構建ONBUILD在子節點的任何命令之前執行命令?Dockerfile。

ONBUILD對于將要構建FROM給定圖像的圖像很有用。例如,您將使用ONBUILD一個語言堆棧映像構建在該語言中編寫的任意用戶軟件?Dockerfile,您可以在Ruby的ONBUILD變體中看到。

建立的圖像ONBUILD應該有一個單獨的標簽,例如:?ruby:1.9-onbuild或ruby:2.0-onbuild。

把時要小心,ADD或COPY在ONBUILD。如果新版本的上下文缺少添加的資源,“onbuild”映像將會嚴重失敗。如上所述,添加單獨的標簽將有助于通過允許Dockerfile作者做出選擇來緩解這一點。

我們的公共號

轉載于:https://blog.51cto.com/wuguiyunwei/1934599

總結

以上是生活随笔為你收集整理的编写Dockerfiles的最佳做法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。