基礎準則
- 儘量挑選官方鏡像
- 選擇合適的基礎鏡像
- 能用小的就不要用大的
- 能用輕量級的就不要用重量級的
- 儘量選擇用runtime的
- 利用多階段構建
- 優化指令順序: 變動越小的放在越前面
選擇合適的基礎映像
選擇正確的基礎映像是優化的第一步。對於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 範例中,指令及其對應的層如下:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base- 第一個基礎映像層WORKDIR /app- 變更工作目錄到/appEXPOSE 80- 暴露PortEXPOSE 443- 暴露PortFROM mcr.microsoft.com/dotnet/sdk:6.0 AS build- 第二個基礎映像層(用於建置)WORKDIR /src- 變更工作目錄COPY ["MyApp/MyApp.csproj", "MyApp/"]- 複製專案檔案到image的/src的MyApp目錄下(不存在則建立)RUN dotnet restore "MyApp/MyApp.csproj"- Restore專案及相關套件COPY . .- 複製本地目錄所有檔案到到image的/src中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- 變更工作目錄到/app(可能不會產生新層,因為這個工作目錄已經在基礎映像階段被設定)COPY --from=publish /app/publish .- 從發布階段複製檔案到/appENTRYPOINT ["dotnet", "MyApp.dll"]- 設置容器入口點
案例-變更Promgram.cs重新建𦋠Image的緩存使用
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base- 使用快取,因為基礎映像沒有改變。WORKDIR /app- 使用快取,這一步和Program.cs的改變無關。EXPOSE 80- 使用快取,無相關改變。EXPOSE 443- 使用快取,無相關改變。FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build- 使用快取,同樣因為基礎映像沒有改變。WORKDIR /src- 使用快取,這一步和Program.cs的改變無關。COPY ["MyApp/MyApp.csproj", "MyApp/"]- 使用快取,假設.csproj檔案沒有改變。RUN dotnet restore "MyApp/YourApp.csproj"- 使用快取,因為沒有.csproj檔案的改變。COPY . .- 重新建置新層,因為您修改了Program.cs,而這一步涉及複製整個專案目錄。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- 使用快取,回到了最初的基礎映像,且此時階段與Program.cs的修改無關。WORKDIR /app- 使用快取,如果這個指令沒有在前面的基礎映像層中被改變。COPY --from=publish /app/publish .- 重新建置新層,需要從已更新的發布階段複製文件。ENTRYPOINT ["dotnet", "MyApp.dll"]- 重新建置新層,雖然這個指令本身未改變,但它跟隨在已更改的層後面。
總之,合理規劃 Dockerfile 中命令的順序對於優化建置過程、減少建置時間、利用 Docker 快取、降低映像大小,以及保持容易維護性都非常重要。