摘要:先簡單介紹下,通常情況下我們將其稱之為鏡像,鏡像是由多個層組成的文件,這些層用于在容器內執行代碼命令等。而的時候則會將此信息再次進行編碼。
使用 Docker 時,最常用的命令無非是 docker container 和 docker image 相關的子命令,當然最初沒有管理類命令(或者說分組)的時候,最常使用的命令也無非是 docker run docker commit docker build 和 docker images 這些。
今天來聊一下和 Docker 中核心概念 image 相關的重要命令, docker build 或者說 docker image build 為了簡便起見,下文的命令全部使用 docker build 。
Docker Image先簡單介紹下 Docker Image, 通常情況下我們將其稱之為鏡像,鏡像是由多個層組成的文件,這些層用于在容器內執行代碼(命令)等。每個鏡像基本上都是根據應用程序完整的可執行版本進行構建的,并且需要注意的是,它會依賴于主機的系統內核。當用戶在運行鏡像時,這將會創建一個或者多個容器實例。
DockerdDockerd 是 Docker 的服務端,默認情況下提供 Unix Domain Socket 連接,當然也可以監聽某個端口,用于對外提供服務。 所以有時候,我們也可以使用服務器上的 Docker daemon 來提供服務,以加快構建速度及解決一些網絡問題之類的。
好的,基礎概念了解了, 那我們開始進入正題。
使用 Dockerfile我們知道構建鏡像的方法有多種,本文中我們只介紹使用 Dockerfile 通過 docker build 的方式構建鏡像。
為了簡便,我們以一個簡單的 Dockerfile 開始。構建一個容器內使用的 kubectl 工具 (當然選擇它的原因在于 kubectl 足夠大,并不考慮可用性,這個稍后解釋)
FROM scratch LABEL maintainer="Jintao Zhang" ADD kubectl /kubectl ENTRYPOINT [ "/kubectl" ]
Dockerfile 足夠簡單,只是將 kubectl 的二進制文件拷貝進去,并將 Entrypoint 設置為 kubectl 。
Dockerd in Docker我個人一般為了避免環境的污染,大多數的事情都在容器內完成。包括 dockerd 我也啟在容器內。其中的原理不再介紹,可以參考我之前的文章或分享。使用起來很簡單:
docker run --privileged -d -P docker:stable-dind
注意這里使用了 -P 所以本地會隨機映射一個端口,當然你也可以直接指定映射到容器內的 2375 端口。
(Tao) ? build git:(master) docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b56f6483614d docker:stable-dind "dockerd-entrypoint.…" 9 hours ago Up 9 hours 0.0.0.0:32769->2375/tcp trusting_babbage構建
我們直接使用啟動在容器內的 dockerd 進行構建,通過上面的 docker ps 命令可以看到是映射到了本地的 32769 端口。所以我們使用以下命令進行構建:
(Tao) ? kubectl git:(master) docker -H 0.0.0.0:32769 images REPOSITORY TAG IMAGE ID CREATED SIZE (Tao) ? kubectl git:(master) docker -H 0.0.0.0:32769 build -t local/kubectl . Sending build context to Docker daemon 55.09MB Step 1/4 : FROM scratch ---> Step 2/4 : LABEL maintainer="Jintao Zhang" ---> Running in ebcf44071bf0 Removing intermediate container ebcf44071bf0 ---> eb4ea1725ff2 Step 3/4 : ADD kubectl /kubectl ---> 1aad06c4dbb4 Step 4/4 : ENTRYPOINT [ "/kubectl" ] ---> Running in 2fc78fe974e3 Removing intermediate container 2fc78fe974e3 ---> 457802d4bf3e Successfully built 457802d4bf3e Successfully tagged local/kubectl:latest (Tao) ? kubectl git:(master) docker -H 0.0.0.0:32769 images REPOSITORY TAG IMAGE ID CREATED SIZE local/kubectl latest 457802d4bf3e 3 seconds ago 55.1MB
看日志及結果,可以看到我們已經成功的構建了我們所需的鏡像。說了這么多,其實我們今天的內容才剛剛開始。
深入原理 Dockerd 服務在本文一開始,我已經提過 Dockerd 是 Docker 的后端服務,通過上面的
docker -H 0.0.0.0:32769 images
這條命令可以看到我們通過 -H 指定了本地 32679 端口的 dockerd 服務,這其實是個 HTTP 服務,我們來驗證下。
(Tao) ? kubectl git:(master) curl -i 0.0.0.0:32769/_ping HTTP/1.1 200 OK Api-Version: 1.37 Docker-Experimental: false Ostype: linux Server: Docker/18.03.1-ce (linux) Date: Tue, 04 Sep 2018 17:20:51 GMT Content-Length: 2 Content-Type: text/plain; charset=utf-8 OK%
可以看到幾條關鍵的信息 Api-Version: 1.37 這個表明了當前使用的 API 版本,本文的內容也是以 1.37 為例進行介紹,這是當前的穩定版本。我們也可以通過 docker version 進行查看。
(Tao) ? kubectl git:(master) docker -H 0.0.0.0:32769 version Client: Version: 18.06.0-ce API version: 1.37 (downgraded from 1.38) Go version: go1.10.3 Git commit: 0ffa825 Built: Wed Jul 18 19:11:45 2018 OS/Arch: linux/amd64 Experimental: false Server: Engine: Version: 18.03.1-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:23:03 2018 OS/Arch: linux/amd64 Experimental: false
可以看到我本地在用的 docker cli 版本較高,當連接到低版本的 dockerd 時,API 版本降級至與 dockerd 版本保持一致。
當然,你可能會問,如果是 dockerd 版本高會如何呢?其實我日常中的開發環境就是這樣,大多數 API 都沒什么影響, 不過這并不是今天的重點。
root@bdcdac73ee20:/# docker version Client: Version: 17.06.0-ce API version: 1.30 Go version: go1.8.3 Git commit: 02c1d87 Built: Fri Jun 23 21:15:15 2017 OS/Arch: linux/amd64 Server: Version: dev API version: 1.39 (minimum version 1.12) Go version: go1.10.3 Git commit: e8cc5a0b3 Built: Tue Sep 4 10:00:36 2018 OS/Arch: linux/amd64 Experimental: falsebuild context
回到我們上面的構建過程中。我們可以看到日志內容的第一行:
... Sending build context to Docker daemon 55.09MB
從這條日志,我們可以得到兩個信息:
構建的過程是將 build context 發送給 dockerd , 實際的構建壓力在 dockerd 上
發送了 55.09 MB
第一條結論,我們在上一小節已經討論過了,我們來重點看下第二條結論。
(Tao) ? kubectl git:(master) ls -al 總用量 53808 drwxrwxr-x. 2 tao tao 4096 9月 5 01:00 . drwxrwxr-x. 3 tao tao 4096 9月 5 00:57 .. -rw-rw-r--. 1 tao tao 109 9月 5 01:00 Dockerfile -rwxrwxr-x. 1 tao tao 55084063 9月 5 00:53 kubectl (Tao) ? kubectl git:(master) du -sh . 53M . (Tao) ? kubectl git:(master) du -sh kubectl Dockerfile 53M kubectl 4.0K Dockerfile
按照我們 Dockerfile 的內容,我們需要將 kubectl 的二進制包放入鏡像內,所以 build context 雖然比二進制文件多出來 2M 左右的大小你也不會很意外。
但我這里做了另一個例子,不多贅述,代碼可以在我的 [GitHub]() 中找到。這里貼出來結果:
(Tao) ? text git:(master) ls -al 總用量 16 drwxrwxr-x. 2 tao tao 4096 9月 5 01:45 . drwxrwxr-x. 4 tao tao 4096 9月 5 01:44 .. -rw-rw-r--. 1 tao tao 77 9月 5 01:45 Dockerfile -rw-rw-r--. 1 tao tao 61 9月 5 01:45 file (Tao) ? text git:(master) du -b Dockerfile file 77 Dockerfile 61 file (Tao) ? text git:(master) docker -H 0.0.0.0:32769 build --no-cache=true -t local/file . Sending build context to Docker daemon 3.072kB ...
相信你看到這個結果已經明白我想表達的意思,我們繼續探索下這個過程。
/build 請求前面我們已經說過,這就是個普通的 HTTP 請求,所以我們當然可以直接抓包來看看到底發生了什么?
很簡單,通過 dockerd 的地址,使用 POST 方法,訪問 /build 接口, 當然實際情況是會增加前綴,即我在上面提到的版本號,在目前的環境中使用的是 /v1.37/build 這個接口。
而這個請求攜帶了一些很有用的參數,和頭信息。這里我來簡單說下:
Headerbuild 請求的頭部,主要有以下兩個
Content-Type 默認值為 application/x-tar,表明自己是一個歸檔。
X-Registry-Config 這個頭部信息中包含著 registry 的地址及認證信息,并且以 base64 進行編碼。對 docker 熟悉的朋友或者看過我之前文章的朋友應該知道, Docker cli 在 login 成功后,會將認證信息保存至本地,密碼做 base64 保存。而 build 的時候則會將此信息再次 base64 進行編碼。通過這里也可以看出來,在使用遠端 Dockerd 的時候, 應該盡量配置 TLS 以防止中間人攻擊,造成密碼泄漏等情況。
Parameters請求參數中,列幾個比較有意義的:
t 這其實就是我們 docker build -t 時候指定的參數,并且,我們可以同時指定多個 -t 同時構建多個不同名稱的鏡像。
memory cpusetcpus 這些主要用于資源限制
buildargs 如果想要了解這個參數,可以回憶下 Dockerfile 中的 ARG 指令的用法
當然,我們想要探索的過程其實重點就在于請求頭部了, 整個請求的輸入流,必須是一個 tar 壓縮包,并且支持 identity (不壓縮), gzip, bzip2, xz 等壓縮算法。
實現我們來看下基本的實現:
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { query, err := cli.imageBuildOptionsToQuery(options) if err != nil { return types.ImageBuildResponse{}, err } headers := http.Header(make(map[string][]string)) buf, err := json.Marshal(options.AuthConfigs) if err != nil { return types.ImageBuildResponse{}, err } headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) headers.Set("Content-Type", "application/x-tar") serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) if err != nil { return types.ImageBuildResponse{}, err } osType := getDockerOS(serverResp.header.Get("Server")) return types.ImageBuildResponse{ Body: serverResp.body, OSType: osType, }, nil }總結
這篇主要內容集中在 docker build 的過程及其原理上,為什么首先要寫這篇,主要是因為鏡像和我們息息相關, 并且也是我們使用的第一步。而很多情況下,推進業務容器化也都要面臨著性能優化及其他規范之類的。
其實關于 build 的細節還有很多,如果有空,我就再更新下一篇。
可以通過下面二維碼訂閱我的文章公眾號【MoeLove】
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/27433.html
摘要:先簡單介紹下,通常情況下我們將其稱之為鏡像,鏡像是由多個層組成的文件,這些層用于在容器內執行代碼命令等。而的時候則會將此信息再次進行編碼。 使用 Docker 時,最常用的命令無非是 docker container 和 docker image 相關的子命令,當然最初沒有管理類命令(或者說分組)的時候,最常使用的命令也無非是 docker run docker commit dock...
摘要:是一個構建工具,基于的平臺運行,使用的是的模塊化語法。我們使用需要用到的包一個任務,對應一個包,對應一個處理邏輯對應的是同步任務,從左到右,依次執行任務。時間長對應的是異步任務,效率高,時間短。 gulp 是一個構建工具,基于Node.js的平臺運行,使用的是commonJs的模塊化語法。 我們使用gulp需要用到的包 一個TASK任務,對應一個包,對應一個處理邏輯、 gulp.s...
摘要:是一個構建工具,基于的平臺運行,使用的是的模塊化語法。我們使用需要用到的包一個任務,對應一個包,對應一個處理邏輯對應的是同步任務,從左到右,依次執行任務。時間長對應的是異步任務,效率高,時間短。 gulp 是一個構建工具,基于Node.js的平臺運行,使用的是commonJs的模塊化語法。 我們使用gulp需要用到的包 一個TASK任務,對應一個包,對應一個處理邏輯、 gulp.s...
摘要:布局直接閱讀大神文章阮一峰寫的布局。有幾個注意的點,我在剛剛開始中總結的容器屬性,,布局方式主軸對齊方式交叉軸對齊方式這里需要特別注意的就是主軸和交叉軸。特別注意的作用對象是主軸在中設置是水平方向上占滿整個容器。 FlexBox布局 直接閱讀大神文章:阮一峰寫的FlexBox布局。在react-native中原理是一樣的,只不過可能有寫屬性在react-native中簡化了。有幾個注意...
閱讀 3421·2021-10-20 13:49
閱讀 2793·2021-09-29 09:34
閱讀 3691·2021-09-01 11:29
閱讀 3081·2019-08-30 11:01
閱讀 838·2019-08-29 17:10
閱讀 866·2019-08-29 12:48
閱讀 2776·2019-08-29 12:40
閱讀 1347·2019-08-29 12:30