1. 程式人生 > >Docker映象優化

Docker映象優化

前言

上篇博文說到使用Visual Studio Tools for Docker幫助我們生成Dockerfile,現在我們討論下生成的Dockerfile的優劣。


 

一、以往Dockerfile構建模式

(1)釋出API專案

新建Web API專案,專案名稱為API

在專案所在目錄輸入指令:dotnet publish --runtime ubuntu.16.04-x64

(2)建立映象

在釋出目錄新建Dockerfile檔案,黏貼以下程式碼

# 宣告使用的基礎映象

FROM microsoft/dotnet:2.1-sdk

# 設定工作目錄

WORKDIR
/app # 將本地應用拷貝到 容器/app 目錄下 COPY ./ ./ # 設定匯出埠 EXPOSE 80 # 指定應用入口點 API.dll代表的是主程式檔案 ENTRYPOINT ["dotnet", "API.dll"]

 在Dockerfile所在的目錄下輸入指令生成映象:docker build -t api .

不要忘記後面有一個點 .

生成api:latest映象成功

參考部落格操作步驟:https://www.cnblogs.com/bluesummer/p/8087326.html

(3)分析映象

我們來檢視映象資訊,輸入:docker images

發現api:latest映象的大小為1.83GB,大得有點誇張。

現在我們來分析這個Dockerfile:

FROM microsoft/dotnet:2.1-sdk

FROM:指定所建立的基礎映象,如果本地不存在,則預設去Docker Hub下載指定映象。任何Dockerfile中的第一條指令必須為FROM指令。並且,如果在同一個Dockerfile中建立多個映象,可以使用多個FROM指令。

文中Dockerfile基於microsoft/dotnet:2.1-sdk映象,而圖中可看到,microsoft/dotnet:2.1-sdk映象大小已經達到1.73GB了,所以最後生成的api:latest映象大小為1.83GB也不足為怪。

 

那問題來了,我們應該採用哪個映象作為基礎映象,來建立我們自己的映象呢?

查閱了微軟官網文件說明:

microsoft/dotnet:<version>-sdk包含帶有.NET Core 和命令列工具 (CLI) 的.NET Core SDK。此映象將對映到開發方案,可使用此映象進行本地開發、除錯和單元測試。

microsoft/dotnet:<version>-runtime包含.NET Core(runtime和庫),並且針對在生產環境中執行.NET Core 應用進行了優化。

 

我們修改Dockerfile的FROM指令為

FROM microsoft/dotnet:2.1-aspnetcore-runtime

其他指令保持不變,新建一個api:1.0.0的映象。

可以看到,映象大小瞬間小了很多。但是我們還不滿足,因為原本microsoft/dotnet:2.1-aspnetcore-runtime映象才253MB,而我們的映象是353MB。

 

WORKDIR /app

WORKDIR:為後續RUN、CMD和ENTRYPOINT指令設定工作目錄。可以使用多個WORKDIR,後續命令如果引數是相對路徑,則會基於之前命令指定路徑。例如:

WORKDIR /app

WORKDIR publish

WORKDIR api

最終路徑為:/app/publish/api

這個指令在這裡看上去應該優化不了。

 

COPY ./ ./

COPY:格式為COPY <src> <dest>。複製本地主機的<src>(為Dockerfile所在的目錄的相對路徑)下的內容到容器中的<dest>下。目標路徑不存在,則自動建立。

./ ./ 就是將本地Dockerfile所在的目錄的檔案和資料夾都複製到映象中的/app目錄下。

注意區分以下兩條指令:

COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/

COPY test /absoluteDir/  # adds "test" to /absoluteDir/

想了下,這裡應該是不合理的,把全部檔案都複製過去,這肯定會造成映象變大。

又想了下,這個已經是我們釋出過的檔案,那還能怎麼辦。問題先放這裡,等會再解決。

 

EXPOSE 80

EXPOSE:宣告映象內服務所監聽的埠。

 

ENTRYPOINT ["dotnet", "API.dll"]

ENTRYPOINT:指定映象的預設入口命令,該入口命令會在啟動容器是作為跟命令執行,所有傳入值作為該命令的引數。支援兩種格式:

ENTRYPOINT [“executable”,”param1”,”param2”] (exec呼叫執行)

ENTRYPOINT command param1 param2 (shell中執行)

每個Dockerfile裡若出現多個ENTRYPOINT,只有放後面的那個ENTRYPOINT有效。

Dockerfile參考:https://docs.docker.com/engine/reference/builder/#usage  裡面有各個指令的詳細介紹。


 

二、multi-stage builds(多階段構建)

在多階段構建的過程中,我們在Dockerfile使用多個FROM指令,每個FROM指令使用不同的基礎映象構成了不同階段。你可以選擇從上一個階段的產物(artifacts)複製到下一個階段,從而確保不會把不需要的東西帶到下一階段。這種方法可以有效減小Docker映象的大小。

預設情況下,這些階段沒有被命名,可以通過它們的整數引用它們,第一個FROM指令從0開始。然而,我們也可以以as <NAME>的方式命名每個階段。

參考官網:https://docs.docker.com/develop/develop-images/multistage-build/

以下我們用Visual Studio Tools for Docker生成的Dockerfile進行介紹。

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base

WORKDIR /app

EXPOSE 80

 

FROM microsoft/dotnet:2.1-sdk AS build

WORKDIR /src

COPY ["API/API.csproj", "API/"]

RUN dotnet restore "API/API.csproj"

COPY . .

WORKDIR "/src/API"

RUN dotnet build "API.csproj" -c Release -o /app

 

FROM build AS publish

RUN dotnet publish "API.csproj" -c Release -o /app

 

FROM base AS final

WORKDIR /app

COPY --from=publish /app .

ENTRYPOINT ["dotnet", "API.dll"]

它分為四個階段,分別是basebuildpublishfinal

base階段:上面已經分析了這裡不再詳述。

build階段:

FROM microsoft/dotnet:2.1-sdk AS build以microsoft/dotnet:2.1-sdk為基礎映象

WORKDIR /src工作目錄為/src

COPY ["API/API.csproj", "API/"]把Dockerfile所在目錄的API/API.csproj檔案複製到容器的/src/API/中

RUN dotnet restore "API/API.csproj"在當前映象的基礎上執行dotnet restore "API/API.csproj",把API專案的依賴項和工具還原,並輸出結果。

dotnet restore這條命令是使用 NuGet 還原依賴項以及在 project 檔案中指定專案特殊的工具執行對依賴項和工具的還原。

COPY . .

WORKDIR "/src/API"切換工作目錄到/src/API,可以用WORKDIR API替代,但明顯第一種方法更直觀。

RUN dotnet build "API.csproj" -c Release -o /app執行dotnet build "API.csproj" -c Release -o /app,以Release模式生成API專案及其所有依賴項並把生成的二進位制檔案輸出到/app目錄。

 

publish階段:

FROM build AS publish以上一階段build為基礎映象

RUN dotnet publish "API.csproj" -c Release -o /app執行dotnet publish "API.csproj" -c Release -o /app,以Release模式把API應用程式及其依賴項打包到/app目錄以部署到託管系統。

 

final階段:

FROM base AS final以上階段base為基礎映象

WORKDIR /app以/app為工作目錄

COPY --from=publish /app .把publish階段生成的/app目錄下的檔案和資料夾複製到/app目錄。

這樣做的原因是,上階段的產物是不會帶到下一階段。

 

現在可以解釋為什麼使用Visual Studio Tools for Docker不用釋出也能生成可執行的映象了,它實時 (JIT) 編譯,提高啟動效能。而且它只獲取了程式執行所需要的檔案放到映象中。

我們生成一個最新的映象

發現它和microsoft/dotnet:2.1-aspnetcore-runtime映象一樣大,這下滿足了,畢竟我們沒寫什麼程式碼到專案中。


 

三、優化Docker映象的方向

1.精簡映象用途,儘量讓每個映象的用途都比較集中、單一,避免構造大而複雜,功能多的映象。

2.選用合適的基礎映象。

3.在Dockerfile中寫上註釋,方便維護和他人使用。

4.正確使用版本號,如1.0.1。

5.使用多階段構建映象。