2023年10月30日 星期一

Dockerfile編寫準則及優化

基礎準則

  1. 儘量挑選官方鏡像
  2. 選擇合適的基礎鏡像
    • 能用小的就不要用大的
    • 能用輕量級的就不要用重量級的
    • 儘量選擇用runtime的
  3. 利用多階段構建
  4. 優化指令順序: 變動越小的放在越前面

選擇合適的基礎映像

選擇正確的基礎映像是優化的第一步。對於ASP.NET Core應用,推薦使用官方的ASP.NET Core運行時映像。這些映像已經為性能和安全性進行了優化,且體積相對較小。

下面以asp.net core 6.0的Web專案建立Dockerfile並分析其內容:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

多階段構建

多階段構建可以幫助您在一個Dockerfile中分別進行應用的構建和運行,這樣可以大大減少最終映像的大小。

# 構建階段 
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 
WORKDIR /src 
COPY ["MyApp/MyApp.csproj", "MyApp/"] 
RUN dotnet restore "MyApp/MyApp.csproj"
COPY . . 
WORKDIR "/src/MyApp" 
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
# 發布階段
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish
# 運行階段
FROM base AS final
WORKDIR /app 
COPY --from=publish /app/publish . 
ENTRYPOINT ["dotnet", "MyApp.dll"]

構建Image過程分析

使用Dockerfile建立Image過程中,每個可執行的指令基本上都會產生一個新的層(快照)。在上面的Dockerfile 中,指令及其對應的層如下:

在 Docker 中,每個可執行的指令基本上都會產生一個新的層(快照)。在您問的 Dockerfile 範例中,指令及其對應的層如下:

  1. FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base - 第一個基礎映像層
  2. WORKDIR /app - 變更工作目錄到/app
  3. EXPOSE 80 - 暴露Port
  4. EXPOSE 443 - 暴露Port
  5. FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build - 第二個基礎映像層(用於建置)
  6. WORKDIR /src - 變更工作目錄
  7. COPY ["MyApp/MyApp.csproj", "MyApp/"] - 複製專案檔案到image的/src的MyApp目錄下(不存在則建立)
  8. RUN dotnet restore "MyApp/MyApp.csproj" - Restore專案及相關套件
  9. COPY . . - 複製本地目錄所有檔案到到image的/src中
  10. WORKDIR "/src/MyApp" - 再次變更工作目錄
  11. RUN dotnet build "MyApp.csproj" -c Release -o /app/build - 建置應用程序
  12. FROM build AS publish - 從建置階段開始新的階段
  13. RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish - 發布應用程序
  14. FROM base AS final - 從基礎映像開始最終階段
  15. WORKDIR /app - 變更工作目錄到/app(可能不會產生新層,因為這個工作目錄已經在基礎映像階段被設定)
  16. COPY --from=publish /app/publish . - 從發布階段複製檔案到/app
  17. ENTRYPOINT ["dotnet", "MyApp.dll"] - 設置容器入口點

案例-變更Promgram.cs重新建𦋠Image的緩存使用

  1. FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base - 使用快取,因為基礎映像沒有改變。
  2. WORKDIR /app - 使用快取,這一步和 Program.cs 的改變無關。
  3. EXPOSE 80 - 使用快取,無相關改變。
  4. EXPOSE 443 - 使用快取,無相關改變。
  5. FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build - 使用快取,同樣因為基礎映像沒有改變。
  6. WORKDIR /src - 使用快取,這一步和 Program.cs 的改變無關。
  7. COPY ["MyApp/MyApp.csproj", "MyApp/"] - 使用快取,假設 .csproj 檔案沒有改變。
  8. RUN dotnet restore "MyApp/YourApp.csproj" - 使用快取,因為沒有 .csproj 檔案的改變。
  9. COPY . . - 重新建置新層,因為您修改了 Program.cs,而這一步涉及複製整個專案目錄。
  10. WORKDIR "/src/MyApp" - 重新建置新層,因為它跟在一個已經改變的層後面。
  11. RUN dotnet build "MyApp.csproj" -c Release -o /app/build - 重新建置新層,需要重新編譯因為源代碼已經改變。
  12. FROM build AS publish - 重新建置新層,這是一個新階段,而前面的步驟已經更改。
  13. RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish - 重新建置新層,必須發布修改後的代碼。
  14. FROM base AS final - 使用快取,回到了最初的基礎映像,且此時階段與 Program.cs 的修改無關。
  15. WORKDIR /app - 使用快取,如果這個指令沒有在前面的基礎映像層中被改變。
  16. COPY --from=publish /app/publish . - 重新建置新層,需要從已更新的發布階段複製文件。
  17. ENTRYPOINT ["dotnet", "MyApp.dll"] - 重新建置新層,雖然這個指令本身未改變,但它跟隨在已更改的層後面。

總之,合理規劃 Dockerfile 中命令的順序對於優化建置過程、減少建置時間、利用 Docker 快取、降低映像大小,以及保持容易維護性都非常重要。

沒有留言:

張貼留言