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"]
它分為四個階段,分別是base、build、publish和final。
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.使用多階段構建映象。