Dockerfile實例


一、先看最簡單的例子,定制nginx鏡像,打印出 <h1>Hello, Docker!</h1>

 Dockerfile文件:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

 在該Dockerfile目錄下運行

docker build -t nginx:v1 .

 則將生成一個 nginx:v1 的新鏡像,運行該鏡像

docker run -p 80:80 nginx:v1

 在瀏覽器直接訪問地址 localhost,可以看到nginx的主頁已被修改

 

 

 二、RUN 命令關鍵解析

Dockerfile 中每一個指令都會建立一層, RUN 也不例外。每一個 RUN 的行為,新建立一層,在其上執行這些命令,執行結束后, commit 這一層的修改,構成新的鏡像。

FROM debian:jessie
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install

如上dockerfile所示,創建了 7 層鏡像,這是完全沒有意義的,而且很多運行時不需要的東西,都被裝進了鏡像里,比如編譯環境、更新的軟件包等等。結果就是產生非常臃腫、非常多層的鏡像,不僅僅增加了構建部署的時間,也很容易出錯。

正確的寫法:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps

 

三、CMD 與 ENTRYPOINT對比

場景1、讓鏡像變成像命令一樣使用

假設我們需要一個得知自己當前公網 IP 的鏡像,那么可以先用 CMD 來實現:

FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://ip.cn" ]

 

使用docker build -t myip . 來構建鏡像的話,如果我們需要查詢當前公網 IP,只需要執行:

docker run myip

可以打印出當前的ip地址

這么看起來好像可以直接把鏡像當做命令使用了,不過命令總有參數,如果我們希望加參數呢?比如從上面的 CMD 中可以看到實質的命令是 curl ,那么如果我們希望顯示 HTTP頭信息,就需要加上 -i 參數。那么我們可以直接加 -i 參數給 docker run myip 么?

$ docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: con
tainer_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable
file not found in $PATH\"\n".

可以看到可執行文件找不到的報錯, executable file not found 。之前我們說過,跟在鏡像名后面的是 command ,運行時會替換 CMD 的默認值。因此這里的 -i 替換了遠了的CMD ,而不是添加在原來的 curl -s http://ip.cn 后面。而 -i 根本不是命令,所以自然找不到。
那么如果我們希望加入 -i 這參數,我們就必須重新完整的輸入這個命令:

$ docker run myip curl -s http://ip.cn -i

這顯然不是很好的解決方案,而使用 ENTRYPOINT 就可以解決這個問題。現在我們重新用ENTRYPOINT 來實現這個鏡像:

FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

這次我們再來嘗試直接使用 docker run myip -i

成功!這是因為當存在 ENTRYPOINT 后, CMD 的內容將會作為參數傳給ENTRYPOINT ,而這里 -i 就是新的 CMD ,因此會作為參數傳給 curl ,從而達到了我們預期的效果

 

 

場景2:應用運行前的准備工作

啟動容器就是啟動主進程,但有些時候,啟動主進程前,需要一些准備工作。
比如 mysql 類的數據庫,可能需要一些數據庫配置、初始化的工作,這些工作要在最終的mysql 服務器運行之前解決。
此外,可能希望避免使用 root 用戶去啟動服務,從而提高安全性,而在啟動服務前還需要以 root 身份執行一些必要的准備工作,最后切換到服務用戶身份啟動服務。或者除了服務外,其它命令依舊可以使用 root 身份執行,方便調試等。
這些准備工作是和容器 CMD 無關的,無論 CMD 為什么,都需要事先進行一個預處理的工作。這種情況下,可以寫一個腳本,然后放入 ENTRYPOINT 中去執行,而這個腳本會將接到的參數(也就是 <CMD> )作為命令,在腳本最后執行。

 

四、HEALTHCHECK 健康檢查

HEALTHCHECK 指令是告訴 Docker 應該如何進行判斷容器的狀態是否正常,這是 Docker 1.12引入的新指令

HEALTHCHECK 支持下列選項:
--interval=<時長> :兩次健康檢查的間隔,默認為 30 秒;
--timeout=<時長> :健康檢查命令運行超時時間,如果超過這個時間,本次健康檢查就被
視為失敗,默認 30 秒;
--retries=<次數> :當連續失敗指定次數后,則將容器狀態視為 unhealthy ,默認 3
次。

FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1

這里我們設置了每 5 秒檢查一次(這里為了試驗所以間隔非常短,實際應該相對較長),如果健康檢查命令超過 3 秒沒響應就視為失敗,並且使用 curl -fs http://localhost/ || exit 1 作為健康檢查命令。

構建鏡像,並且啟動容器

$ docker build -t myweb:v1 .

$ docker run -d --name web -p 80:80 myweb:v1

當運行該鏡像后,可以通過 docker ps 看到最初的狀態為 (health: starting) 

在等待幾秒鍾后,再次 docker ps ,就會看到健康狀態變化為了 (healthy) 

如果健康檢查連續失敗超過了重試次數,狀態就會變為 (unhealthy) 。
為了幫助排障,健康檢查命令的輸出(包括 stdout 以及 stderr )都會被存儲於健康狀態里,可以用 docker inspect 來查看。

$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
"FailingStreak": 0,
"Log": [
{
"End": "2016-11-25T14:35:37.940957051Z",
"ExitCode": 0,
"Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</titl
e>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-f
amily: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welc
ome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully inst
alled and\nworking. Further configuration is required.</p>\n\n<p>For online documentat
ion and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCo
mmercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n
<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"Start": "2016-11-25T14:35:37.780192565Z"
}
],
"Status": "healthy"
}

 

五、COPY與ADD區別

在 Docker 官方的最佳實踐文檔中要求,盡可能的使用 COPY ,因為 COPY 的語義很明確,就是復制文件而已,而 ADD 則包含了更復雜的功能,其行為也不一定很清晰。最適合使用ADD 的場合,就是所提及的需要自動解壓縮的場合。
另外需要注意的是, ADD 指令會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。
因此在 COPY 和 ADD 指令中選擇的時候,可以遵循這樣的原則,所有的文件復制均使用COPY 指令,僅在需要自動解壓縮的場合使用 ADD 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM