您好,登錄后才能下訂單哦!
本篇內容介紹了“Dockerfile定制鏡像的方法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
鏡像的定制實際上就是定制每一層所添加的配置、文件。我們可以把每一層修改、安裝、構建、操作的命令都寫入一個腳本,這個腳本就是Dockerfile;
Dockerfile是一個文本文件,其內包含了一條條的指令,每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建;
FROM指定基礎鏡像
所謂定制鏡像,一定是以一個鏡像為基礎,在其上進行定制。基礎鏡像是必須指定的,而FROM就是指定基礎鏡像,因此一個Dockerfile中FROM是必備的指令,并且必須是第一條指令;
在docker hub上有非常多的高質量的官方鏡像,有可以直接拿來使用的服務類鏡像,如nginx、redis、mysql、tomcat等;可以在其中尋找一個最符合我們最終目標的鏡像為基礎鏡像進行定制;
如果沒有找到對應服務的鏡像,官方鏡像中還提供了一些更為基礎的操作系統鏡像,如ubuntu、debian、centos、alpine等,這些操作系統的軟件庫為我們提供了更廣闊的擴展空間;
除了選擇現有鏡像為基礎鏡像外,Docker還存在一個特殊的鏡像,名為scratch。這個鏡像是一個虛擬的概念,并不實際存在,它表示一個空白的鏡像;
FROM scratch
如果你以scratch為基礎鏡像的話,意味著你不以任何鏡像為基礎,接下來所寫的指令將作為鏡像的第一層開始存在。
對于Linux下靜態編譯的程序來說,并不需要有操作系統提供運行時支持,所需的一切庫都已經在可執行文件里了,因此直接FROM scratch
會讓鏡像體積更加小巧。使用Go語言開發的應用很多會使用這種方式來制作鏡像,這也是為什么有人認為Go是特別適合容器微服務架構的語言的原因之一。
RUN執行命令
RUN指令是用來執行命令行命令的。由于命令行的強大能力,RUN指令在定制鏡像時是最常用的指令之一。其格式有兩種:
// shell格式 RUN <命令>
// 示例 RUN echo '<h2>Hello, Docker!</h2>' /usr/share/nginx/html/index.html
// exec格式 RUN ["可執行文件", "參數1", "參數2"]
// 示例 RUN tar -zxf redis.tar.gz -C /usr/src/redis --strip-components=1 RUN make -C /usr/src/redis RUN make -C /usr/src/redis install
構建鏡像
上面我們利用Dockerfile定制了nginx鏡像,現在我們明白了Dockerfile的內容,接下來我們來構建這個鏡像。在Dockerfile文件所在目錄執行:
docker build -t nginx:v3 . // 注意最后有一個.表示當前目錄
從命令的輸出結果中,我們可以清晰的看到鏡像的構建過程。在step 2中,RUN指令啟動了一個容器,執行了所要求的命令,并最后將該容器進行了提交,隨后刪除了所用到的這個容器。
COPY復制文件
格式
COPY <源路徑>...<目標路徑>
COPY ["<源路徑1>",..."[目標路徑]"]
COPY指令將從構建上下文目錄中<源路徑>的文件/目錄復制到新的一層的鏡像內的<目標路徑>位置。比如:
COPY package.json /usr/src/app/
<源路徑>可以是多個,甚至可以是通配符,如:
COPY hom* /mydir/ COPY hom?.txt /mydir/
ADD更高級的復制文件
ADD指令和COPY的格式和性質基本一致。但是在COPY基礎上增加了一些功,比如<源路徑>可以是一個URL,這種情況下,Docker引擎會試圖去下載這個鏈接的文件放到<目標路徑>去。
在Docker官方的Dockerfile最佳實踐文檔中要求,盡可能的使用COPY,因為COPY的語義很明確,就是復制文件而已,而ADD則包含了更復雜的功能,其行為也不一定很清晰。最適合使用ADD的場合,就是所提及的需要自動解壓縮的場合。因此在COPY和ADD指令中選擇的時候,可以遵循這樣的原則,所有的文件復制均使用COPY指令,僅在需要自動解壓縮的場合使用ADD。
CMD容器啟動命令
shell格式:CMD <命令>
;
exec格式:CMD ["可執行文件", "參數1", "參數2",...]
;
參數列表格式:CMD ["參數1", "參數2",...]
。在指定了ENTRYPOINT
指令后,用CMD指定具體的參數;
CMD命令的格式和RUN相似,也是兩種格式:
docker不是虛擬機,容器就是進程。既然是進程,那么在啟動容器的時候,需要指定所運行的程序及參數。CMD指令就是用于指定默認的容器主進程啟動命令的。
ENTRYPOINT入口點
ENTRYPOINT的目的和CMD一樣,都是在指定容器啟動程序及參數。ENTRYPOINT在運行時也可以替代,不過比CMD要略顯繁瑣,需要通過docker run
的參數--entrypoint
來指定。當指定了ENTRYPOINT之后,CMD的含義就發生了改變,不再是直接的運行其命令,而是將CMD的內容作為參數傳給ENTRYPOINT指令,換句話說,實際執行命令時,將變為:
<ENTRYPOINT> "<CMD>"
ENV設置環境變量
ENV <key> <value>
;
ENV <key1>=<value1> <key2>=<value2>
;
格式有兩種:
這個指令很簡單,就是設置環境變量而已,無論是后面的其他指令,如RUN,還是運行時的應用,都可以直接使用這里定義的環境變量;
ENV VERSION=1.0 DEBUG=ON NAME="Happy Feet" $VERSION # 使用環境變量
下列指令可以支持環境變量展開:ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD。
ARG構建參數
ARG <參數名>[=<默認值>]
;
格式:
構建參數和ENV的效果一樣,都是設置環境變量。所不同的是,ARG所設置的構建環境變量,在將來容器運行時是不會存在這些環境變量的。但是不要因此就使用ARG保存密碼之類的信息,因為docker history還是可以看到所有值的;
Dockerfile中的ARG指令是定義參數名稱,以及定義其默認值。該默認值可以在構建命令docker build
中使用--build-arg <參數名>=<參數值>
來覆蓋。
VOLUME定義匿名卷
格式:
VOLUME ["<路徑1>", "<路徑2>",...]
VOLUME <路徑>
容器運行時應盡量保持容器存儲層不發生寫操作,對于數據庫類需要保存動態數據的應用,其數據庫文件應該保存于卷(volume)中,為了防止運行時用戶忘記將動態文件所保存目錄掛載為卷,在Dockerfile中,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器存儲層寫入大量數據。
VOLUMEN /data
EXPOSE聲明端口
幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;
在運行時使隨機端口映射時,也就是docker run -P
時,會自動隨機映射EXPOSE的端口。
格式為EXPOSE <端口1> [<端口2>,...]
;
EXPOSE指令是聲明運行時容器提供服務端口,這只是一個聲明,在運行時并不會因為這個聲明應用就會開啟這個端口的服務。
在Dockerfile中使用EXPOSE進行聲明有兩個好處:
WORKDIR指定工作目錄
在shell中,連續兩行是同一個進程執行環境,因此前一個命令修改的內存狀態,會直接影響后一個命令;
在Dockerfile中,這兩行RUN命令的執行環境根本不同,是兩個完全不同的容器。這就是對Dockerfile構建分層存儲的概念不了解所導致的錯誤。每一個RUN都是啟動一個容器、執行命令、然后提交存儲層文件變更。第一層RUN cd /app
的執行僅僅是當前進程的工作目錄變更,一個內存上的變化而已,其結果不會造成任何文件變更。而到第二層的時候,啟動的是一個全新的容器,跟第一層的容器更完全沒關系,自然不可能繼承前一層構建過程中的內存變化。
因此如果需要改變以后各層的工作目錄的位置,那么應該使用WORKDIR指令。
格式為WORKDIR <工作目錄路徑>
;
使用WORKDIR指令可以來指定工作目錄(或者稱為當前目錄),以后各層的當前目錄就被改為指定的目錄,如果該目錄不存在,WORKDIR會幫你建立目錄;
之前提到一些初學者常犯的錯誤是把Dockerfile等同于shell腳本來寫,這種錯誤的理解還可能會導致出現下面這樣的錯誤:
RUN cd /app RUN echo "hello" > world.txt
如果將這個Dockerfile進行構建鏡像運行后,會發現找不到/app/world.txt文件。
USER指定當前用戶
格式:USER <用戶名>
;
USER指令和WORKDIR相似,都是改變環境狀態并影響以后的層。WORKDIR是改變工作目錄,USER則是改變之后層的執行RUN,CMD以及ENTRYPOINT這類命令的身份。
當然,和WORKDIR一樣,USER只是幫助你切換到指定用戶而已,這個用戶必須是事先建立好的,否則無法切換
RUN groupadd -r redis && useradd -r -g redis redis USER redis RUN ["redis-server"]
HEALTHCHECK健康檢查
--interval=<間隔>
:兩次健康檢查的間隔,默認為30秒;
--timeout=<時長>
:健康檢查命令運行超時時間,如果超過這個事件,本次健康檢查就被視為失敗,默認為30秒;
--retries=<次數>
:當連續失敗指定次數后,則將容器狀態視為unhealthy,默認3次。
格式:
# 設置檢查容器健康狀況的命令 HEALTHCHECK [選項] CMD <命令> # 如果基礎鏡像有健康檢查指令,可以屏蔽掉其健康檢查指令 HEALTHCHECK NONE
HEALTHCHECK指令是告訴Docker應該如何進行判斷容器的狀態是否正常,這是Docker 1.12引入的新指令。通過該指令指定一行命令,用這行命令來判斷容器主進程的服務狀態是否正常,從而比較真實的反應容器實際狀態。
一個鏡像指定了HEALTHCHECK指令后,用其啟動容器,初始狀態會為starting,在執行健康檢查成功后變為healthy,如果連續一定次數失敗,則會變為unhealthy;
HEALTHCHECK支持以下選項:
為了幫助排障,健康檢查命令的輸出(包括stdout一級stderr)都會被保存于健康狀態里,可以通過docker inspect
來查看。
ONBUILD為他人做嫁衣
格式:ONBUILD <其他指令>
;
ONBUILD是一個特殊的指令,它后面跟的是其他指令,比如RUN,COPY等,而這些指令,在當前鏡像構建時并不會被執行,只有當以當前鏡像為基礎鏡像,去構建下一級鏡像的時候才會被執行;
Dockerfile中的其他指令都是為了定制當前鏡像而準備的,唯有ONBUILD是為了幫助別人定制自己而準備的。
docker save和docker load
Docker還提供了docker save
和docker load
命令,用以將鏡像保存為一個tar文件,然后傳輸到另一個位置上,再加載進來。這是沒有Docker Registry時的做法,現在已經不推薦使用,鏡像遷移應該直接使用Docker Registry,無論是直接使用Docker Hub還是使用內網私有Registry都可以。
例如:保存nginx鏡像:
docker save nginx |gzip > nginx-latest.tar.gz
然后我們將nginx-latest.tar.gz文件復制到另一個機器上,再次加載鏡像:
docker load -i nginx-latest.tar.gz
“Dockerfile定制鏡像的方法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。