Docker鏡像優化

前言

       上篇博文說到使用Visual Studio Tools for Docker幫助咱們生成Dockerfile,如今咱們討論下生成的Dockerfile的優劣。html


 

1、以往Dockerfile構建模式

       (1)發佈API項目docker

       新建Web API項目,項目名稱爲APIshell

       在項目所在目錄輸入指令:dotnet publish --runtime ubuntu.16.04-x64ubuntu

       (2)建立鏡像api

       在發佈目錄publish文件下新建Dockerfile文件,黏貼如下代碼app

# 聲明使用的基礎鏡像

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  裏面有各個指令的詳細介紹。


 

2、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鏡像同樣大,這下知足了,畢竟咱們沒寫什麼代碼到項目中。


 

3、優化Docker鏡像的方向

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

       2.選用合適的基礎鏡像。

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

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

       5.使用多階段構建鏡像。

相關文章
相關標籤/搜索