1. 程式人生 > >九個編寫Dockerfiles的常見錯誤

九個編寫Dockerfiles的常見錯誤

【編者的話】我們每天基於Dockerfiles工作;所有執行的程式碼都來自一系列的Dockerfiles。這篇文章將會討論編寫Dockerfile時人們經常犯的錯誤以及如何改進。對於Docker專家說,這篇文章裡的許多技巧可能會非常明顯進而會得到很多的認同。但是對於初級到中級開發者,該文章將會是一份很有用的指南,它有助於理清以及加速你們的工作流程。

1. 執行 apt-get

執行apt-get install是每一個Dockerfile都有的東西之一。你需要安裝一些外部的包來執行程式碼。但使用apt-get相應地會帶來一些問題。 一個是執行apt-get upgrade 會更新所有包到最新版本 —— 不能這樣做的理由是它會妨礙Dockerfile構建的持久與一致性。 另一個是在不同的行之間執行apt-get updateapt-get install命令。不能這樣做的原因是,只有apt-get update的程式碼會在構建過程中被快取,而且你需要執行apt-get install命令的時候不會每次都被執行。因此,你需要將apt-get update
跟所要安裝的包都在同一行執行,來確保它們正確的更新。 在以下 Golang Dockerfileapt-install命令就是一個不錯的例子: RUN apt-get update && \ apt-get install -y --no-install-recommends \ g++ \ gcc \ libc6-dev \ make \ && rm -rf /var/lib/apt/lists/*

2. 使用ADD而非COPY

ADDCOPY是完全不同的命令。COPY是這兩個中最簡單的,它只是從主機複製一份檔案或者目錄到映象裡。ADD同樣可以這麼做,但是它還有更神奇的功能,像解壓TAR檔案或從遠端URLs獲取檔案。為了降低Dockerfile的複雜度以及防止意外的操作,最好用COPY
來複制檔案。 FROM busybox:1.24 ADD example.tar.gz /add #解壓縮檔案到add目錄 COPY example.tar.gz /copy #直接複製檔案

3. 在一行內新增整個應用目錄

明確程式碼的哪些部分以及什麼時候應該放在構建映象內或許是最重要的事了,它可以顯著加快構建速度。 Dockerfile裡經常會看到如下這些內容: # !!! ANTIPATTERN !!! COPY ./my-app/ /home/app/ RUN npm install # or RUN pip install or RUN bundle install # !!! ANTIPATTERN !!! 這就意味著每次修改檔案之後都需要重新構建那行以下的所有東西。多數情況下(包括上面的例子),它意味著重新安裝應用依賴。為了儘可能地使用Docker的快取,首先複製所有安裝依賴所需要的檔案,然後執行命令安裝這些依賴。在複製剩餘檔案(這一步儘可能放到最後一行)之前先做這兩個步驟,會使程式碼的變更被快速的重建。 COPY ./my-app/package.json /home/app/package.json # Node/npm packages WORKDIR /home/app/ RUN npm install

或許還要安裝python依賴?

COPY ./my-app/requirements.txt /home/app/requirements.txt RUN pip install -r requirements.txt COPY ./my-app/ /home/app/ 這樣做會確保構建儘可能快的執行。

4. 使用:latest標籤

許多Dockerfiles在開頭都使用FROM node:latest模板,用來從Docker registry拉取最新的映象。簡單地說,使用latest標籤的映象意味著如果這個映象得到更新,那麼Dockerfile的構建可能會突然中斷。弄清這件事可能會非常難,因為Dockerfile的維護者實際上並沒做任何修改。為了防止這種情況,只需要確保映象使用特定的標籤(例如:node:6.2.1)。這樣就可以確保Dockerfile的一致性。

5. 構建映象時使用外部服務

很多人會忽視構建Docker映象與執行一個Docker容器的區別。在構建映象時,Docker讀取Dockerfile裡的命令並建立映象。在依賴或程式碼修改之前,映象是保持不變以及可重複使用的。這個過程完全獨立於其它容器。需要與其它容器或服務(如資料庫)進行互動則會在容器執行的時候發生。 舉一個例子,執行資料庫遷移。很多人試圖在構建映象時執行此操作。這樣做會導致許多問題。首先,在構建時資料庫可能不可用,因為它可能沒建在它將要執行的伺服器上。其次,你可能想使用同一個映象來連線不同的資料庫(在開發或生產環境中),在這種情況下,如果它在構建過程中,遷移是不能進行的。 # !!! ANTIPATTERN !!! COPY /YOUR-PROJECT /YOUR-PROJECT RUN python manage.py migrate # 嘗試遷移資料,但是並不能 CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] # !!! ANTIPATTERN !!!

6. 在Dockerfile開始部分加入EXPOSE和ENV

EXPOSE和ENV是廉價的執行命令。如果你破壞它們的快取,幾乎瞬時就可以重建。所以,最好儘可能晚地宣告這些命令。在構建過程中應該直到需要的時候才宣告ENV。如果在構建的時候不需要他們,那麼應該在Dockerfile的末尾附加EXPOSE。 再次檢視Golang的Dockerfile,你會看到,所有ENVS都是在使用前宣告的,並且在最後宣告其餘的: ENV GOLANG_VERSION 1.7beta1 ENV GOLANG_DOWNLOAD_SHA256 a55e718935e2be1d5b920ed262fd06885d2d7fc4eab7722aa02c205d80532e3b RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \ && echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \ && tar -C /usr/local -xzf golang.tar.gz \ && rm golang.tar.gz ENV GOPATH /go ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH 如需修改ENV GOPATHENV PATH,映象幾乎會馬上重建成功。

7. 多個FROM宣告

嘗試使用多個FROM宣告來將不同的映象組合到一起,這樣不會起任何作用。Docker僅使用最後一個FROM並且忽略前面所有的。 所以如果你有這樣的Dockerfile: # !!! ANTIPATTERN !!! FROM node:6.2.1 FROM python:3.5 CMD ["sleep", "infinity"] # !!! ANTIPATTERN !!! 那麼docker exec進入執行的容器中,會得到下面的結果: $ docker exec -it d86fcf0775d3 bash [email protected]:/# which python /usr/local/bin/python [email protected]:/# which node [email protected]:/# 這其實是GitHub上的一個問題:合併不同的映象,但它看起來不會很快就增加的功能。

8. 多個服務執行在同一個容器內

這可能是瞭解Docker的開發者遇到的最大問題。而公認的最佳實踐是:每個不同的服務,包括應用,應該在它自己的容器中執行。在一個Docker映象裡面加入多個服務非常容易,但是有一定的負面影響。 首先,橫向擴充套件應用會變得很困難。其次,額外的依賴和層次會使映象構建變慢。最終,增大了Dockerfile的編寫、維護以及除錯難度。 當然,像所有的技術建議一樣,你需要用你的最佳判斷。如果想快速安裝一個Django+Nginx的應用的開發環境,那麼讓它們執行在同一個容器裡面,同時生產環境中有一個不同的Dockerfile,讓他們分開執行,是合理可行的。

9. 在構建過程中使用VOLUME

Volume是在執行容器時候加入的,而不是構建的時候。與第五個誤區類似,在構建過程中不應該與你宣告的volume有互動。相反地,你只是在執行容器的時候使用它。例如,如果在以下構建過程中建立檔案並且在執行那個映象時候使用它,一切正常: FROM busybox:1.24 RUN echo "hello-world!!!!" > /myfile.txt CMD ["cat", "/myfile.txt"] ... $ docker run volume-in-build hello-world!!!! 但是,如果我對一個儲存在volume上的檔案做同樣的事,就不會起作用。 FROM busybox:1.24 VOLUME /data RUN echo "hello-world!!!!" > /data/myfile.txt CMD ["cat", "/data/myfile.txt"] ... $ docker run volume-in-build cat: can't open '/data/myfile.txt': No such file or directory 一個有趣的問題是:如果你前面的任何一個層次聲明瞭一個VOLUME(也可能是幾個FROMS)依然會遇到同樣的問題。因此,最好留意一下父類映象都聲明瞭什麼volume。如果遇到問題,請使用docker inspect檢查。

結論

理解怎樣寫好一個Dockerfile將會是一個漫長的路程,它會帶你理解Docker是如何工作的,同時也幫助你建立你的基礎架構。理解Docker快取會為你節省好多等待構建完成的時間!

相關推薦

編寫Dockerfiles常見錯誤

【編者的話】我們每天基於Dockerfiles工作;所有執行的程式碼都來自一系列的Dockerfiles。這篇文章將會討論編寫Dockerfile時人們經常犯的錯誤以及如何改進。對於Docker專家說,這篇文章裡的許多技巧可能會非常明顯進而會得到很多的認同。但是對於初級到中級開發者,該文章將會是一份很有用的

MySQL十常見錯誤

問題一:Too many connections(連線數過多,導致連線不上資料庫,業務無法正常進行) 問題還原 mysql> show variables like '%max_connection%'; | Variable_name | Value | max_conn

三年的python開發經驗,總結出這【30常見錯誤】,避免重蹈覆轍!!!

導讀:在這篇文章中,我將總結新老Python程式設計師常犯的一些錯誤,以幫助你們在自己的工作避免犯同樣或類似錯誤。 在這篇文章中,我將總結新老Python程式設計師常犯的一些錯誤,以幫助你們在自己的工作避免犯同樣或類似錯誤。 首先我要說明一下的是,這些都是來源於第一手的經驗。我以講授Python的知識為生

新入職專案經理務必避免的七常見錯誤

摘自公號:慧翔天地PMP,可向小助手領取專案管理全部書籍和文件模板。   一、不明確自身的職責 職責不明確,會造成問題處理不及時甚至沒人處理,影響工作效率。剛進入公司要很清楚的明白自己的工作職責,是一名專案經理,主要是做管理,協調,溝通等等。。。 二、分工不明確

轉:Spring Boot 錯誤總結(累計30常見錯誤

版權宣告:本文為博主原創文章,未經博主允許不得轉載。    https://blog.csdn.net/qq_32447301/article/details/77161272 1.新建Spring boot,出現src的包上出現錯誤的叉號:   

Python程式設計師的30常見錯誤

在這篇文章中,我將總結新老Python程式設計師常犯的一些錯誤,以幫助你們在自己的工作避免犯同樣或類似錯誤。 首先我要說明一下的是,這些都是來源於第一手的經驗。我以講授Python的知識為生。在過去的7年裡,我已經給上千名學生講授上百堂Python的課程,同時看著這些學生們犯同樣的錯。也就是

5Excel最常見的「錯誤值」,這些含義你都知道嗎?

在職場辦公中,很多朋友都會遇到這樣的情況,明明自己輸入的是正確的公式,但顯示出來的數值卻 是錯誤值,那你知道他們都是什麼含義嗎?不知道的趕緊來看。 一、【#DIV/0】 錯誤原因:公式中有除數為0或者除數為空的單元格。 解決方法:更正除數為0或者除數為空的單元格

程式設計師程式設計面試中的十常見錯誤,茶話匯教你如何化解!

身為程式設計師,你肯定知道和其他技術工作面試比起來,程式設計工作的面試流程略有不同。 本文就程式設計師在程式設計面試中應當避免的10個問題進行說明,同時也給出瞭如何解決這些問題的建議。 1.從未在紙上或白板上寫過程式碼 這是求職者最容易犯的大錯之一。絕大多數程式設計面試都會安排在紙上或白板上。而與電

關於兩數字順序顛倒函式的幾種常見錯誤

在我沒學指標之前,編寫兩個數字顛倒的這個函式時,我感覺沒有太大的問題,但是老師給我們講了指標的相關的知識後,這個函式的問題就出現了以下問題: (前提是不允許在函式中使用列印函式)第一個函式一直到第三個函式的錯誤都是沒有把數字換過來,例如在下面這個主函式中,輸入a=10,b=

1000Mysql常見錯誤程式碼

1004 => ‘無法建立檔案’, 1005 => ‘無法建立表’, 1006 => ‘無法建立資料庫’, 1007 => ‘無法建立資料庫,資料庫已存在。’, 1008 => ‘無法撤銷資料庫,資料庫不存在。’, 1009 => ‘撤銷資料庫時出錯’

C++語言99常見程式設計錯誤 常見錯誤89:持有class物件的陣列

常見錯誤89:持有class物件的陣列  當心持有class物件的陣列,尤其是持有基類物件的陣列。  陣列的索引操作只是指標算術的縮寫,因此array[i]等價於*(array+i)。不幸的是,編譯器會基於“array指標指向有B物件的陣列”的前提做指標的加法運算。如果派生物

Spring Boot 錯誤總結(累計30常見錯誤

1.新建Spring boot,出現src的包上出現錯誤的叉號:    分析原因: 你要更新一下選擇專案-----Maven----Updata project,或者刪除jar包---Libraries---Maven Dependencies,然後重新關閉eclipse,

前端八常見錯誤

第一,  檔案無法上傳。 下面上傳檔案的程式碼經常寫過,但是點選提交之後,卻發現沒有上傳檔案:          <formaction="xx.php" method="post">                    <inputtype="file

Python二十常見的腳本匯總!

ima 斐波那契數 是我 取消 process fda cdb 排序 fff 1、冒泡排序2、計算x的n次方的方法3、計算aa + bb + c*c + ……4、計算階乘 n!5、列出當前目錄下的所有文件和目錄名6、把一個list中所有的字符串變成小寫:7、輸出某個路徑下的

令人興奮的新功能將與Java 9 展示兩點

java googl pre api ogl body 特性 gen 大神 HTTP/2 Java 9 中有新的方式來處理 HTTP 調用。這個遲到的特性用於代替老舊的 `HttpURLConnection` API,並提供對 WebSocket 和 HTTP/2 的支持。

nginx常見錯誤

不能 被拒絕 sed 請求 delet 輸出 cep 個人 available 404 bad request 一般原因:請求的Header過大 解決方法:配置nginx.conf相關設置 client_header_buffer_size 16k; large_c

Ubuntu 搭建svn服務器 ,以及常見錯誤解決方案

mkdir creat mod 服務 csdn 解決 details 守護 center 一、安裝命令: 1)以root身份登錄。執行:sudo su -命令 2)執行安裝命令:apt-get install subversion 二、創建項目目錄 1)mkdir /

JDK源碼調試常見錯誤

jdk 技術 ges 選擇 第一條 找不到 需要 進行 根據 1、刪除不需要的代碼,即swing相關的代碼 2、執行命令時要將前提環境進入文件夾如下: 起初沒有完全執行第一條,因為網上說可以根據需要選擇相關的代碼,於是就沒有刪除,以後第一次模仿網上的例子的時候要按照原

github常見錯誤整理!

cti could tail 我們 fatal detail 提示 文件 fig文件 1.fatal: remote origin already exists.錯誤 如果輸入$ Git remote add origin [email protected]/*

php上傳文件常見錯誤

php style input error 今天 ont 大小 配置文件 inpu 今天在文件上傳過程中遇到的文件上傳不過去,和網頁報錯,最後經查看總結有以下幾個方面 上傳文件錯誤碼 error=0 正常上傳 error=1 上傳的大小超過了input[type=file]