From 5965a9e351f6c8b0bc2cc081a2816eef6c9ef226 Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Mon, 20 Nov 2023 22:49:20 -0500 Subject: [PATCH 1/9] Add container optimizations --- .../Backend.Api.Dockerfile | 24 +++ .../Backend.Api.Dockerfile.chiseled | 24 +++ .../Backend.Api.Dockerfile.chiseled.aot | 24 +++ docs/aca/99-optimize-containers/index.md | 158 ++++++++++++++++++ .../backend-api-chiseled-aot-image-stats.png | Bin 0 -> 25578 bytes .../backend-api-chiseled-aot.png | Bin 0 -> 4421 bytes .../backend-api-chiseled-image-stats.png | Bin 0 -> 25450 bytes .../backend-api-chiseled.png | Bin 0 -> 3725 bytes .../backend-api-status-quo-image-stats.png | Bin 0 -> 25953 bytes .../backend-api-status-quo.png | Bin 0 -> 2651 bytes mkdocs.yml | 1 + snippets/persist-state.md | 13 +- 12 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 docs/aca/99-optimize-containers/Backend.Api.Dockerfile create mode 100644 docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled create mode 100644 docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot create mode 100644 docs/aca/99-optimize-containers/index.md create mode 100644 docs/assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png create mode 100644 docs/assets/images/99-optimize-containers/backend-api-chiseled-aot.png create mode 100644 docs/assets/images/99-optimize-containers/backend-api-chiseled-image-stats.png create mode 100644 docs/assets/images/99-optimize-containers/backend-api-chiseled.png create mode 100644 docs/assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png create mode 100644 docs/assets/images/99-optimize-containers/backend-api-status-quo.png diff --git a/docs/aca/99-optimize-containers/Backend.Api.Dockerfile b/docs/aca/99-optimize-containers/Backend.Api.Dockerfile new file mode 100644 index 00000000..396a1ddd --- /dev/null +++ b/docs/aca/99-optimize-containers/Backend.Api.Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.TasksManager.Backend.Api/TasksTracker.TasksManager.Backend.Api.csproj", "TasksTracker.TasksManager.Backend.Api/"] +RUN dotnet restore "TasksTracker.TasksManager.Backend.Api/TasksTracker.TasksManager.Backend.Api.csproj" +COPY . . +WORKDIR "/src/TasksTracker.TasksManager.Backend.Api" +RUN dotnet build "TasksTracker.TasksManager.Backend.Api.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.TasksManager.Backend.Api.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.TasksManager.Backend.Api.dll"] diff --git a/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled b/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled new file mode 100644 index 00000000..2a1dfc35 --- /dev/null +++ b/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.TasksManager.Backend.Api/TasksTracker.TasksManager.Backend.Api.csproj", "TasksTracker.TasksManager.Backend.Api/"] +RUN dotnet restore "TasksTracker.TasksManager.Backend.Api/TasksTracker.TasksManager.Backend.Api.csproj" +COPY . . +WORKDIR "/src/TasksTracker.TasksManager.Backend.Api" +RUN dotnet build "TasksTracker.TasksManager.Backend.Api.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.TasksManager.Backend.Api.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.TasksManager.Backend.Api.dll"] diff --git a/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot b/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot new file mode 100644 index 00000000..862cdb9a --- /dev/null +++ b/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-jammy-chiseled-aot AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/nightly/sdk:8.0-jammy-aot AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.TasksManager.Backend.Api/TasksTracker.TasksManager.Backend.Api.csproj", "TasksTracker.TasksManager.Backend.Api/"] +RUN dotnet restore "TasksTracker.TasksManager.Backend.Api/TasksTracker.TasksManager.Backend.Api.csproj" +COPY . . +WORKDIR "/src/TasksTracker.TasksManager.Backend.Api" +RUN dotnet build "TasksTracker.TasksManager.Backend.Api.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.TasksManager.Backend.Api.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.TasksManager.Backend.Api.dll"] diff --git a/docs/aca/99-optimize-containers/index.md b/docs/aca/99-optimize-containers/index.md new file mode 100644 index 00000000..cdf4db9c --- /dev/null +++ b/docs/aca/99-optimize-containers/index.md @@ -0,0 +1,158 @@ +--- +canonical_url: 'https://azure.github.io/aca-dotnet-workshop' +--- + +# Module 99 - Container Optimization + +!!! info "Module Duration" + 45 minutes + +## Objective + +In this module, we will accomplish two objectives: + +1. Learn how to reduce container footprints. +1. Build & deploy updated, optimized images to Azure. + +## Module Sections + +--8<-- "snippets/restore-variables.md" + +### 1. Optimizing Containers + +Azure Container Apps makes it simply to quickly become effective with containers. But even a managed container platform requires hygiene and can benefit greatly from smaller containers. + +In this module, we will look into the benefits of optimized containers such as: + +- Smaller images to store and transfer to and from the container registry. +- Potentially less *Common Vulnerabilities and Exposures (CVEs)*. +- No bloat and unnecessary components such as shells, package managers, etc. + +While available prior to .NET 8, the general availability introduction of .NET 8 in November 2023 came with an expanded focus on container optimization and a move away from general-purpose containers. + +Please ensure you have the Docker daemon ready. Running *Docker Desktop* does it. + +#### 1.1 The Status Quo + +Let's focus on our first project, the Backend API. This is an ASP.NET Core application. + +Our original `Dockerfile` looks like this: + +=== "Backend.Api Dockerfile" +```Dockerfile +--8<-- "docs/aca/99-optimize-containers/Backend.Api.Dockerfile" +``` + +```shell +cd ~\TasksTracker.ContainerApps +``` + +```shell +docker build -t backend-api-status-quo -f .\TasksTracker.TasksManager.Backend.Api\Dockerfile . + +docker image list +``` + +This yields a sizable image at **222 MB**! + +![Backend API Status Quo](../../assets/images/99-optimize-containers/backend-api-status-quo.png) + +This image is comprised of two images, 452 packages, and has 19 vulnerabilities. + +![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png) + +#### 1.2. Chiseled Images + +Microsoft and Ubuntu's creator, Canonical, collaborated on the concept of a [chiseled image for .NET](https://learn.microsoft.com/en-us/dotnet/core/docker/container-images#scenario-based-images){target=_blank}. Take a general-purpose base image and start chiseling away until you are left with an image that contains nothing more than the bare necessities to run your workload. No shell, no package manager, no bloat. + +=== "Backend.Api Dockerfile.chiseled" +```Dockerfile hl_lines="1 8" +--8<-- "docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled" +``` + +Create a new file, `Dockerfile.chiseled` in the Backend Api root directory, then build the image again: + +```shell +docker build -t backend-api-chiseled -f .\TasksTracker.TasksManager.Backend.Api\Dockerfile.chiseled . + +docker image list +``` + +Our image now stands at a much smaller **115 MB** - a drop of 107 MB and a size just 51.8% of the status quo image! + +![Backend API Chiseled](../../assets/images/99-optimize-containers/backend-api-chiseled.png) + +This image is comprised of one image, 331 packages, and has five vulnerabilities. + +![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-chiseled-image-stats.png) + +#### 1.3 Ahead-of-time (AOT) Compilation + +[Ahead-of-time (AOT) compilation](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot){target_blank} was first introduced with .NET 7. AOT compiles the application to native code instead of Intermediate Language (IL). This means that we must have foresight as to what platform will be hosting the application. Our process is simplified by the fact that containers in Azure Container Apps are only Linux-hosted. By using native code, we will bypass the just-in-time (JIT) compiler when the container executes, which means we will have faster startup and a smaller memory footprint. It also means these images can run in environments where JIT compilation may not be permitted. + +=== "Backend.Api Dockerfile.chiseled.aot" +```Dockerfile hl_lines="1 8" +--8<-- "docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot" +``` + +Create a new file, `Dockerfile.chiseled.aot` in the Backend Api root directory, then build the image again: + +```shell +docker build -t backend-api-chiseled-aot -f .\TasksTracker.TasksManager.Backend.Api\Dockerfile.chiseled.aot . + +docker image list +``` + +!!! note "Nightly Images" + + As the name implies, these images are produced nightly. They are not yet images that are versioned and stable in the registry. Your mileage may vary. + +Another massive reduction takes the image down to a mere **16 MB** - a total drop of 206 MB and a size just 7.2% of the status quo image! + +![Backend API Chiseled AOT](../../assets/images/99-optimize-containers/backend-api-chiseled-aot.png) + +This image is comprised of one image, just 23 packages, and has nine vulnerabilities. +Notably, the four additional vulnerabilities are in the `openssl 3.0.2` package in this image. + +![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png) + +#### 1.4 Deploying the new Status Quo + +While the image is vastly reduced, what hasn't changed is the functionality of the API. Whether you are executing it locally or deploying to Azure, the Backend API will continue to function as it always has. However, now it has less vulnerabilities, less time to transfer from the registry, less startup time, and less of a memory footprint. Furthermore, 16 MB is the uncompressed image. With compression, we are likely to continue dropping in size. + +Let's update our existing Backend API container app with a new build and revision: + +```shell hl_lines="6" +## Build Backend Service on ACR and Push to ACR + +az acr build ` +--registry $AZURE_CONTAINER_REGISTRY_NAME ` +--image "tasksmanager/$BACKEND_API_NAME" ` +--file 'TasksTracker.TasksManager.Backend.Api/Dockerfile' . + +# Update Backend API App container app and create a new revision +az containerapp update ` +--name $BACKEND_API_NAME ` +--resource-group $RESOURCE_GROUP ` +--revision-suffix v$TODAY-6 ` +--set-env-vars "ApplicationInsights__InstrumentationKey=secretref:appinsights-key" +``` + +Verify that the application continues to work: + +```shell +$FRONTEND_UI_BASE_URL +``` + +#### 2. Wash/Rinse/Repeat for Frontend UI & Backend Service + +As all three projects use ASP.NET Core, you can follow this same exercise for the other two projects and see how much you are able to reduce! + +--8<-- "snippets/persist-state.md:module99" + +## Review + +In this module, we have accomplished two objectives: + +1. Learned how to reduce container footprints. +1. Built & deployed updated, optimized images to Azure. diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png b/docs/assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png new file mode 100644 index 0000000000000000000000000000000000000000..0c2de8da0eddb81c9c23a07d5b81036e73831346 GIT binary patch literal 25578 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 literal 0 HcmV?d00001 diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled-aot.png b/docs/assets/images/99-optimize-containers/backend-api-chiseled-aot.png new file mode 100644 index 0000000000000000000000000000000000000000..27051d426f165bf5323b273e06b38c6a6425055d GIT binary patch literal 4421 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

b$cq9)G$YXuKZsd(d~hPd!x zoRPe{OA8Ajfjwdq3lu@8Hfol4JNVvzn!YwXscHEhanPGDQ;TLZC$W3xnk;c3oo=d) zG4{Rslj--^H}ukd-g$qrZ+5nKa&mBTGCuVRt6Y6~$*p?-_k-*4+`ev#*WzM9qJ#(J zVF2gSgg&$MxOuo-RL-Em2Xy{Um8phxaWTljX60`xKL?k1+mrS}>!)rdJ|d#C!@!&5 z<0+r>gT>PA-^{41LR?9vP!A4uG5`wFsTva-TbnNeCZXz&g_>c#`I0W_Gc>u6c4)}+ z-Dl|j9e?>TqHLe&T@@d3Y#xn?^mVMP#u#c&9{9j%D577W6 zyqKDaSH3QbJIYmi5GOoZXY8;QL-PHdZ-1Gmzssalb5l|>(Kmb*R(`2V5L5ES@{SbX ztI4V#r4y4*3Q10@Qr`T zbbkYZ(kI}qOkd$O6urp$!Rpzz3-}ax{%IsQLMJo&>q)WF<_^V!W2HM`lzwFfqvXjS z1OkYG;T-)2&CXsM->Y&D3fzjmQsSYS(G>8|AZX`BmV- zU-PvG+W1NXGUM9V2!&Ixy~pq+>a`=m^of{%b?L{j>5GCc4#7GBYDylLhU39VFY&{+ zZ}~y;oHGLmMeP#FVn!+H*_7xs@g2sdtlxOT?2`L=MgiT7>hu!3K!O(qq}B7=t_E%*B-XX$byPN9rXn(QKIPIv ziVhcO?=UW_?P9gdcC39uv|o)FI5HvCYOAZ`c`(vaCnhH5=jW&MZ}e!m>nt_7Md^*$ z22Yh}Rs=DO$uKt(#rl@r57%5Yi3TwajDOynW)QL6+L`oE1-0qIF=A%}Q+Ek(Kg1@= z)RM0saLcX>P5TH7kXd1c{=6*YjdVSs?PY?fMM`84vJcMBu`&NZsd|?uz$VB#yL3PG zMw@mJEnP-?lB>x`6~FUyRQRubQw5RaiUF+~?l)zwXa)@W7Z@bQ%2d)i%?81FvkiX& zmiWj-qPhFQZr!ZOmV&kld`%&5_Z4Ud9cwcwjYQswP|C-8pG{Y+hej)Q*#qxO5Ox{C5?7Q%y%Zl z3kF791d(#|Wip7Srwmc--vaD4(bEH;j6PvW`rd*8d( zyIv>II6?bCy-S{!8|X2Wi^IeG;TTo#ruyWZtN|_D8sfL!(gs}7z0<)mY*h2u>AR*Cjl2^_u;g z3mx-bXB7DC5XD+eG&2!7@h+>{<`$Q>K2toy#nQ~?7&4PL6SJctPakAb$}hiJ98zcM zt4CR*_G*t|A-=sK*#~te^5=5H9;Noo?3wEX>C`qRt%DDc%283m_>0@5^JA$U@nIhQ zty*LaTU*UrU^LbAnP=7VABBiACwxQT8w zEbWSi`&$$-YR(_K<1&h5P*&y+aq6keB5Novgt3jc?9Yt+1p}$>z&xbbJ!9}aHrKOD z2uX}5jUQ2s%N{`!m?Y@K%go$|_S|<)VyLuZL|z+$vvVQ5FofD+SeQxX@Ji?iFcw!h zQ4bftrQnm|`0x2ypTbBA&5617{iOI7!O5Pbh|O?!koUfXADwne+RWv*H$sM)ye;W- z4dR!|zpC`<3XQ2b$xBy7Vo1BUF?}ZIQvH1v1%2?vEgslRC@;{;{mAPsFX)B%3m^|x zk<1n>VDqBU&ZED^{{(=3d}0fPwg8~MoiO^8Sv;U#+*MS9ikM!|- zM@q0YIO+97O7jxQYzE>`wB{-414~wgkyF(cJ-24muw3ooS1b7;$RtwG^iO6PR(M~m zPytSRtZNst=UC9*8d*vT>0euYq9mW>A)1UgLt?u6)4w3K>?Q?VP(v28c3_Yha-?Zz zblf`KHEI}tbmlF7dTD6X!cbyt9gF6iopv( +{*whBncMIYLeAGOx)4^|S+d)>Q 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 F{{?|F`~3g_ literal 0 HcmV?d00001 diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled.png b/docs/assets/images/99-optimize-containers/backend-api-chiseled.png new file mode 100644 index 0000000000000000000000000000000000000000..a523364fea2aa8cf2bbcbfe5ec65569e054599a7 GIT binary patch literal 3725 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 DzG^Kp literal 0 HcmV?d00001 diff --git a/docs/assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png b/docs/assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png new file mode 100644 index 0000000000000000000000000000000000000000..059223ee49368124e1da2e288e3d5f9239807923 GIT binary patch literal 25953 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 Xxl`O6!1~)i{skpDb=mSaAHMuA`lh@^ literal 0 HcmV?d00001 diff --git a/docs/assets/images/99-optimize-containers/backend-api-status-quo.png b/docs/assets/images/99-optimize-containers/backend-api-status-quo.png new file mode 100644 index 0000000000000000000000000000000000000000..f1fb748df87422ffcc06e9d706310281a7b716b8 GIT binary patch literal 2651 zcmV-h3Z(UkP)Px#1ZP1_K>z@;j|==^1poj59#BkFMGOoKl$4ar%*<3&R5Ua+ZEbC&q@;s`gSWT0 z{{R3lIF982000SaNLh0L01|Tm01|Tnj3T)Q000TbNkl?#GDXskqY2N9{w%Z_wCH zW*dr&Ycwt#*TvwA@|2e6f!z)5#{DsWcV!uCKq<=W`j?G)DWJ)+bjjsxdPUcF^S_u$ zdC(GIwXWl=%Q^*_)69&I+J_<^9^;bEGFu3_aQ*!dYK}%sxvOc+=401pPO2GWG>vt6 z>3ZyO_-S4X)}+5W?ac7&k$3ZTvECoFgeZ-sTR*4D+J}(eps}0G7D6sue=>K_NG1D2 zZ>C@MCc1Lan0;hV28zz$279yhltX&=ck_Qw|Fb=432_=v$NS|Dxtj$vviv5EU1YWq za$zR3D2+plxLzQrj@(P@v#(gwh;r07!%zO^UJ2G@@-g`^lldd>=KnhX>S$CFV70D; zPxXGe;}xaahmh}wAu8!AvyG4o7ZMwq8%%NLo<^SHII_+5Y%)8mU!@e~sPE{0={&X4 zWIc7CW;Qo#d+7RZ{*Tq%(WoTEY3wp}L%7pEg#0FrPfiB|E+_&-Y1|3lSa1l9&hC|o z(@6brJ2x)|Ym)EWnEW7(oATgjR1)Gr^)VW~4BU*iMOnmY?0*F`*3E9G z(J4;ftNB`F*qLy_geEAD&`57-3Ew!yseIU%aQB^4974qfe>w4)YN2Vw!h&)X3Ze^+ z;%Ct~=4CHA?@=1Hgy$C=gL=UsJ{-%!{5uyMgo+D}&rurJ2TdcE`D5CvI!2fOir!bk zkIydPUk(q`s3m;E@;{g5f8|4w|EIluo<_&DEV_} zq^Xy46m6w-2~*gNV;Z#tSgq?kxEZH>DDvx>>f!m#IL!s;7lGykQekKV8nHFL<=CKU z#MU_1|5-HVQv~3w*B#!TtYDej{T`PZs%ps|e~Mxz-$PUD4s@*|@cl9$wwU46cR!8Go4eL==oM=n=> zG(6EmN$*5=8lkyPaUW&KKPUg+&^WaGXYRQ2dHzFiG|~^^Y~3ZO?**TmB^vgxpH+G% z9-tALs}%Q9=p^OkC^T;Am*o*Y#Wj6%C3jjwF=nuw*fWKcdDS@A9DMRy6I*`-e0ghx z(*j%vdHPIFga?Qafzolv9lRDx~t`pFR%XU@Gyr|3X{g9bPB^eq9?OuCx=8qVb z8|0nAdN{|-%#dj0TLJ|g+Ovl~-KpF3DkpKx0iB#Lf{=XKnZFqcZji=#p-Y|` zZm}3!p>be$eS2q+VH&k$)-Y)I+M5fU;vr>_M=Ev=S8 zmWpmH0X7DnRNjC^D35r<(+EdbDej|SoD>?VW71P{Huu@`pYBr`JH@jDJOZUggxbpIWcwo

2-!#9LBjk;tfw@Sp+HWqfk&S zj}?vC8F#6qZs(mUsD-}!6s&%6|O=;z0g+^J`Ky9um>qb{@{U?s~4=sH*B8~Hu&`edWh5~^ Date: Mon, 20 Nov 2023 22:58:35 -0500 Subject: [PATCH 2/9] Fix copy/paste issue --- docs/aca/99-optimize-containers/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/aca/99-optimize-containers/index.md b/docs/aca/99-optimize-containers/index.md index cdf4db9c..381f3cd5 100644 --- a/docs/aca/99-optimize-containers/index.md +++ b/docs/aca/99-optimize-containers/index.md @@ -128,7 +128,7 @@ Let's update our existing Backend API container app with a new build and revisio az acr build ` --registry $AZURE_CONTAINER_REGISTRY_NAME ` --image "tasksmanager/$BACKEND_API_NAME" ` ---file 'TasksTracker.TasksManager.Backend.Api/Dockerfile' . +--file 'TasksTracker.TasksManager.Backend.Api/Dockerfile.chiseled.aot' . # Update Backend API App container app and create a new revision az containerapp update ` From f36af89c8b90172252ac4c7c9e35c5904efb4cb2 Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 10:34:35 -0500 Subject: [PATCH 3/9] Add frontend UI and backend svc as well --- .../Backend.Svc.Dockerfile | 24 +++ .../Backend.Svc.Dockerfile.chiseled.aot | 24 +++ .../Frontend.Ui.Dockerfile | 24 +++ .../Frontend.Ui.Dockerfile.chiseled.aot | 24 +++ docs/aca/99-optimize-containers/index.md | 158 +++++++++++++++++- 5 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 docs/aca/99-optimize-containers/Backend.Svc.Dockerfile create mode 100644 docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot create mode 100644 docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile create mode 100644 docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot diff --git a/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile b/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile new file mode 100644 index 00000000..b60a9b20 --- /dev/null +++ b/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.Processor.Backend.Svc/TasksTracker.Processor.Backend.Svc.csproj", "TasksTracker.Processor.Backend.Svc/"] +RUN dotnet restore "TasksTracker.Processor.Backend.Svc/TasksTracker.Processor.Backend.Svc.csproj" +COPY . . +WORKDIR "/src/TasksTracker.Processor.Backend.Svc" +RUN dotnet build "TasksTracker.Processor.Backend.Svc.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.Processor.Backend.Svc.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.Processor.Backend.Svc.dll"] diff --git a/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot b/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot new file mode 100644 index 00000000..8db548e0 --- /dev/null +++ b/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-jammy-chiseled-aot AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/nightly/sdk:8.0-jammy-aot AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.Processor.Backend.Svc/TasksTracker.Processor.Backend.Svc.csproj", "TasksTracker.Processor.Backend.Svc/"] +RUN dotnet restore "TasksTracker.Processor.Backend.Svc/TasksTracker.Processor.Backend.Svc.csproj" +COPY . . +WORKDIR "/src/TasksTracker.Processor.Backend.Svc" +RUN dotnet build "TasksTracker.Processor.Backend.Svc.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.Processor.Backend.Svc.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.Processor.Backend.Svc.dll"] diff --git a/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile b/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile new file mode 100644 index 00000000..d59ec115 --- /dev/null +++ b/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.WebPortal.Frontend.Ui/TasksTracker.WebPortal.Frontend.Ui.csproj", "TasksTracker.WebPortal.Frontend.Ui/"] +RUN dotnet restore "TasksTracker.WebPortal.Frontend.Ui/TasksTracker.WebPortal.Frontend.Ui.csproj" +COPY . . +WORKDIR "/src/TasksTracker.WebPortal.Frontend.Ui" +RUN dotnet build "TasksTracker.WebPortal.Frontend.Ui.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.WebPortal.Frontend.Ui.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.WebPortal.Frontend.Ui.dll"] diff --git a/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot b/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot new file mode 100644 index 00000000..f0d24b3d --- /dev/null +++ b/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-jammy-chiseled-aot AS base +WORKDIR /app +EXPOSE 5000 + +ENV ASPNETCORE_URLS=http://+:5000 + +USER app +FROM mcr.microsoft.com/dotnet/nightly/sdk:8.0-jammy-aot AS build +ARG configuration=Release +WORKDIR /src +COPY ["TasksTracker.WebPortal.Frontend.Ui/TasksTracker.WebPortal.Frontend.Ui.csproj", "TasksTracker.WebPortal.Frontend.Ui/"] +RUN dotnet restore "TasksTracker.WebPortal.Frontend.Ui/TasksTracker.WebPortal.Frontend.Ui.csproj" +COPY . . +WORKDIR "/src/TasksTracker.WebPortal.Frontend.Ui" +RUN dotnet build "TasksTracker.WebPortal.Frontend.Ui.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "TasksTracker.WebPortal.Frontend.Ui.csproj" -c $configuration -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TasksTracker.WebPortal.Frontend.Ui.dll"] diff --git a/docs/aca/99-optimize-containers/index.md b/docs/aca/99-optimize-containers/index.md index 381f3cd5..5ce3ee88 100644 --- a/docs/aca/99-optimize-containers/index.md +++ b/docs/aca/99-optimize-containers/index.md @@ -86,7 +86,7 @@ This image is comprised of one image, 331 packages, and has five vulnerabilities ![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-chiseled-image-stats.png) -#### 1.3 Ahead-of-time (AOT) Compilation +#### 1.3 Chiseled & Ahead-of-time (AOT) Compilation [Ahead-of-time (AOT) compilation](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot){target_blank} was first introduced with .NET 7. AOT compiles the application to native code instead of Intermediate Language (IL). This means that we must have foresight as to what platform will be hosting the application. Our process is simplified by the fact that containers in Azure Container Apps are only Linux-hosted. By using native code, we will bypass the just-in-time (JIT) compiler when the container executes, which means we will have faster startup and a smaller memory footprint. It also means these images can run in environments where JIT compilation may not be permitted. @@ -144,9 +144,161 @@ Verify that the application continues to work: $FRONTEND_UI_BASE_URL ``` -#### 2. Wash/Rinse/Repeat for Frontend UI & Backend Service +### 2. Optimizing Frontend UI & Backend Service Containers -As all three projects use ASP.NET Core, you can follow this same exercise for the other two projects and see how much you are able to reduce! +As all three projects use ASP.NET Core, we can follow the same approach with these two projects as well.how much you are able to reduce! + +#### 2.1 Frontend UI + +#### 2.1.1 The Status Quo + +Our original `Dockerfile` looks like this: + +=== "Frontend.Ui Dockerfile" +```Dockerfile +--8<-- "docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile" +``` + +```shell +cd ~\TasksTracker.ContainerApps +``` + +```shell +docker build -t frontend-ui-status-quo -f .\TasksTracker.WebPortal.Frontend.Ui\Dockerfile . + +docker image list +``` + +This yields an image size of **227 MB**. + +#### 2.1.2 Chiseled & Ahead-of-time (AOT) Compilation + +Skipping straight to AOT images: + +=== "Frontend.Ui Dockerfile.chiseled.aot" +```Dockerfile hl_lines="1 8" +--8<-- "docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot" +``` + +Create a new file, `Dockerfile.chiseled.aot` in the Frontend Ui root directory, then build the image again: + +```shell +docker build -t frontend-ui-chiseled-aot -f .\TasksTracker.WebPortal.Frontend.Ui\Dockerfile.chiseled.aot . + +docker image list +``` + +This much-improved image is now **20.6 MB**. + +#### 2.2 Backend Service + +#### 2.2.1 The Status Quo + +Our original `Dockerfile` looks like this: + +=== "Backend.Svc Dockerfile" +```Dockerfile +--8<-- "docs/aca/99-optimize-containers/Backend.Svc.Dockerfile" +``` + +```shell +cd ~\TasksTracker.ContainerApps +``` + +```shell +docker build -t backend-svc-status-quo -f .\TasksTracker.Processor.Backend.Svc\Dockerfile . + +docker image list +``` + +This yields an image size of **222 MB**. + +#### 2.2.2 Chiseled & Ahead-of-time (AOT) Compilation + +Skipping straight to AOT images: + +=== "Backend.Svc Dockerfile.chiseled.aot" +```Dockerfile hl_lines="1 8" +--8<-- "docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot" +``` + +Create a new file, `Dockerfile.chiseled.aot` in the Backend Svc root directory, then build the image again: + +```shell +docker build -t backend-svc-chiseled-aot -f .\TasksTracker.Processor.Backend.Svc\Dockerfile.chiseled.aot . + +docker image list +``` + +This much-improved image is now **16 MB**. + +### 3. Optimization Summary + +#### 3.1 Table of Improvements + +The Backend API and the Backend Svc projects are all but identical while the Frontend UI project is just slightly larger. All three projects were cut down to less than 10% of their original size! + +| | Image Size | Size Reduction | Size compared to Original | Packages | CVEs | +|----------------------------|-----------:|---------------:|--------------------------:|---------:|-----:| +| Backend API Original | 222 MB | | | 452 | 19 | +| Backend API Chiseled & AOT | 16 MB | 206 MB | 7.2% | 23 | 9 | +| Frontend UI Original | 226 MB | | | 447 | 19 | +| Frontend UI Chiseled & AOT | 21 MB | 205 MB | 9.3% | 18 | 9 | +| Backend Svc Original | 222 MB | | | 452 | 19 | +| Backend Svc Chiseled & AOT | 16 MB | 206 MB | 7.2% | 23 | 9 | + +#### 3.2 Build & Deploy All Services + +The last step is to build & deploy updated images. For good measure, let's do the Backend API as well even though we did it earlier already. + +```shell hl_lines="5 11 17" +# Build Backend API on ACR and Push to ACR +az acr build ` +--registry $AZURE_CONTAINER_REGISTRY_NAME ` +--image "tasksmanager/$BACKEND_API_NAME" ` +--file 'TasksTracker.TasksManager.Backend.Api/Dockerfile.chiseled.aot' . + +# Build Backend Service on ACR and Push to ACR +az acr build ` +--registry $AZURE_CONTAINER_REGISTRY_NAME ` +--image "tasksmanager/$BACKEND_SERVICE_NAME" ` +--file 'TasksTracker.Processor.Backend.Svc/Dockerfile.chiseled.aot' . + +# Build Frontend Web App on ACR and Push to ACR +az acr build ` +--registry $AZURE_CONTAINER_REGISTRY_NAME ` +--image "tasksmanager/$FRONTEND_WEBAPP_NAME" ` +--file 'TasksTracker.WebPortal.Frontend.Ui/Dockerfile.chiseled.aot' . +``` + +```shell +# Update Backend API App container app and create a new revision +az containerapp update ` +--name $BACKEND_API_NAME ` +--resource-group $RESOURCE_GROUP ` +--revision-suffix v$TODAY-7 ` +--set-env-vars "ApplicationInsights__InstrumentationKey=secretref:appinsights-key" + +# Update Frontend Web App container app and create a new revision +az containerapp update ` +--name $FRONTEND_WEBAPP_NAME ` +--resource-group $RESOURCE_GROUP ` +--revision-suffix v$TODAY-7 ` +--set-env-vars "ApplicationInsights__InstrumentationKey=secretref:appinsights-key" + +# Update Backend Background Service container app and create a new revision +az containerapp update ` +--name $BACKEND_SERVICE_NAME ` +--resource-group $RESOURCE_GROUP ` +--revision-suffix v$TODAY-7 ` +--set-env-vars "ApplicationInsights__InstrumentationKey=secretref:appinsights-key" +``` + +Verify that the application continues to work with the three much smaller containers: + +```shell +$FRONTEND_UI_BASE_URL +``` --8<-- "snippets/persist-state.md:module99" From 36e0e1a516d70000289a4b8845d48b854d5631a7 Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 10:36:44 -0500 Subject: [PATCH 4/9] Move appendix from 13 to 30 --- CONTRIBUTING.md | 2 +- docs/aca/00-workshop-intro/4-prerequisites.md | 2 +- docs/aca/03-aca-dapr-integration/index.md | 2 +- docs/aca/04-aca-dapr-stateapi/index.md | 2 +- docs/aca/05-aca-dapr-pubsubapi/index.md | 4 ++-- docs/aca/06-aca-dapr-bindingsapi/index.md | 2 +- .../01-run-debug-dapr-app-vscode.md | 0 .../02-github-local-codespaces.md | 0 docs/aca/{13-appendix => 30-appendix}/03-variables.md | 0 docs/aca/{13-appendix => 30-appendix}/Set-Variables.ps1 | 0 docs/aca/{13-appendix => 30-appendix}/launch.json | 0 docs/aca/{13-appendix => 30-appendix}/tasks.json | 0 mkdocs.yml | 6 +++--- 13 files changed, 10 insertions(+), 10 deletions(-) rename docs/aca/{13-appendix => 30-appendix}/01-run-debug-dapr-app-vscode.md (100%) rename docs/aca/{13-appendix => 30-appendix}/02-github-local-codespaces.md (100%) rename docs/aca/{13-appendix => 30-appendix}/03-variables.md (100%) rename docs/aca/{13-appendix => 30-appendix}/Set-Variables.ps1 (100%) rename docs/aca/{13-appendix => 30-appendix}/launch.json (100%) rename docs/aca/{13-appendix => 30-appendix}/tasks.json (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bdac44d9..eda79cd5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,7 +121,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/Azure/ ### Making Contributions -Please familiarize yourself with how to run this project [here](https://azure.github.io/aca-dotnet-workshop/aca/13-appendix/02-github-local-codespaces). +Please familiarize yourself with how to run this project [here](https://azure.github.io/aca-dotnet-workshop/aca/30-appendix/02-github-local-codespaces). Whenever you are submitting any changes to the guide, please follow these recommendations: diff --git a/docs/aca/00-workshop-intro/4-prerequisites.md b/docs/aca/00-workshop-intro/4-prerequisites.md index 30ecfb8f..8b28d97e 100644 --- a/docs/aca/00-workshop-intro/4-prerequisites.md +++ b/docs/aca/00-workshop-intro/4-prerequisites.md @@ -78,7 +78,7 @@ This workshop typically spans several days. As such, you may close your tools, e - In the root create a new file called `Set-Variables.ps1`. -- Copy the [Set-Variables.ps1 script](../../aca/13-appendix/03-variables.md){target=_blank} into the newly-created `Set-Variables.ps1` file and save it. +- Copy the [Set-Variables.ps1 script](../../aca/30-appendix/03-variables.md){target=_blank} into the newly-created `Set-Variables.ps1` file and save it. - Perform an initial commit of the `Set-Variables.ps1` file. diff --git a/docs/aca/03-aca-dapr-integration/index.md b/docs/aca/03-aca-dapr-integration/index.md index 4819c94e..21c855ff 100644 --- a/docs/aca/03-aca-dapr-integration/index.md +++ b/docs/aca/03-aca-dapr-integration/index.md @@ -245,7 +245,7 @@ We are ready now to verify the changes on the Frontend Web App and test locally. Notice how we assigned the Dapr App Id “tasksmanager-frontend-webapp” to the Frontend WebApp. !!! note - If you need to run both microservices together, you need to keep calling `dapr run` manually each time in the terminal. And when you have multiple microservices talking to each other you need to run at the same time to debug the solution. This can be a convoluted process. You can refer to the [debug and launch Dapr applications in VSCode](../13-appendix/01-run-debug-dapr-app-vscode.md) to see how to configure VScode for running and debugging Dapr applications. + If you need to run both microservices together, you need to keep calling `dapr run` manually each time in the terminal. And when you have multiple microservices talking to each other you need to run at the same time to debug the solution. This can be a convoluted process. You can refer to the [debug and launch Dapr applications in VSCode](../30-appendix/01-run-debug-dapr-app-vscode.md) to see how to configure VScode for running and debugging Dapr applications. #### 2.5 Test `{{ apps.frontend }}` and `{{ apps.backend }}` Locally Using Dapr diff --git a/docs/aca/04-aca-dapr-stateapi/index.md b/docs/aca/04-aca-dapr-stateapi/index.md index dd4f5f3c..112cc7b3 100644 --- a/docs/aca/04-aca-dapr-stateapi/index.md +++ b/docs/aca/04-aca-dapr-stateapi/index.md @@ -304,7 +304,7 @@ To add the component file state store, add a new folder named **components** und Now you should be ready to launch both applications and start doing CRUD operations from the Frontend Web App including querying the store. All your data will be stored in Cosmos DB Database you just provisioned. -If you have been running the different microservices using the [debug and launch Dapr applications in VSCode](../13-appendix/01-run-debug-dapr-app-vscode.md) then remember to uncomment the following line inside tasks.json file. +If you have been running the different microservices using the [debug and launch Dapr applications in VSCode](../30-appendix/01-run-debug-dapr-app-vscode.md) then remember to uncomment the following line inside tasks.json file. This will instruct dapr to load the local projects components located at **./components** instead of the global components' folder. ```json hl_lines="2" diff --git a/docs/aca/05-aca-dapr-pubsubapi/index.md b/docs/aca/05-aca-dapr-pubsubapi/index.md index 2a42dcc4..f1a13209 100644 --- a/docs/aca/05-aca-dapr-pubsubapi/index.md +++ b/docs/aca/05-aca-dapr-pubsubapi/index.md @@ -48,7 +48,7 @@ However, we want to have more control and provide our own component file, so let --8<-- "docs/aca/05-aca-dapr-pubsubapi/dapr-pubsub-redis.yaml" ``` -To try out the Pub/Sub API, run the Backend API from VS Code by running the below command or using the Run and Debug tasks we have created in the [appendix](../13-appendix/01-run-debug-dapr-app-vscode.md). +To try out the Pub/Sub API, run the Backend API from VS Code by running the below command or using the Run and Debug tasks we have created in the [appendix](../30-appendix/01-run-debug-dapr-app-vscode.md). === ".NET 6 or below" @@ -298,7 +298,7 @@ Shut down the sessions. #### 2.6 Optional: Update VS Code Tasks and Launch Configuration Files -If you have followed the steps in the [appendix](../13-appendix/01-run-debug-dapr-app-vscode.md) so far in order to be able to run the three services together (frontend, backend api, and backend processor) and debug them in VS Code, we need to update the files `tasks.json` and `launch.json` to include the new service we have added. +If you have followed the steps in the [appendix](../30-appendix/01-run-debug-dapr-app-vscode.md) so far in order to be able to run the three services together (frontend, backend api, and backend processor) and debug them in VS Code, we need to update the files `tasks.json` and `launch.json` to include the new service we have added. ??? example "Click to expand the files to update" diff --git a/docs/aca/06-aca-dapr-bindingsapi/index.md b/docs/aca/06-aca-dapr-bindingsapi/index.md index ed7f2bbf..998bd930 100644 --- a/docs/aca/06-aca-dapr-bindingsapi/index.md +++ b/docs/aca/06-aca-dapr-bindingsapi/index.md @@ -225,7 +225,7 @@ Now we are ready to give it an end-to-end test on our dev machines. To do so, ru --8<-- "snippets/dapr-run-backend-service.md:dapr-components" +in this [section](../30-appendix/01-run-debug-dapr-app-vscode.md). --> Open Azure Storage Explorer on your local machine. If you don't have it installed you can install it from [here](https://azure.microsoft.com/en-us/products/storage/storage-explorer/#overview){target=_blank}. Login to your Azure Subscription and navigate to the storage account already created, create a queue, and use the same name you already used in the Dapr Input configuration file. diff --git a/docs/aca/13-appendix/01-run-debug-dapr-app-vscode.md b/docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md similarity index 100% rename from docs/aca/13-appendix/01-run-debug-dapr-app-vscode.md rename to docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md diff --git a/docs/aca/13-appendix/02-github-local-codespaces.md b/docs/aca/30-appendix/02-github-local-codespaces.md similarity index 100% rename from docs/aca/13-appendix/02-github-local-codespaces.md rename to docs/aca/30-appendix/02-github-local-codespaces.md diff --git a/docs/aca/13-appendix/03-variables.md b/docs/aca/30-appendix/03-variables.md similarity index 100% rename from docs/aca/13-appendix/03-variables.md rename to docs/aca/30-appendix/03-variables.md diff --git a/docs/aca/13-appendix/Set-Variables.ps1 b/docs/aca/30-appendix/Set-Variables.ps1 similarity index 100% rename from docs/aca/13-appendix/Set-Variables.ps1 rename to docs/aca/30-appendix/Set-Variables.ps1 diff --git a/docs/aca/13-appendix/launch.json b/docs/aca/30-appendix/launch.json similarity index 100% rename from docs/aca/13-appendix/launch.json rename to docs/aca/30-appendix/launch.json diff --git a/docs/aca/13-appendix/tasks.json b/docs/aca/30-appendix/tasks.json similarity index 100% rename from docs/aca/13-appendix/tasks.json rename to docs/aca/30-appendix/tasks.json diff --git a/mkdocs.yml b/mkdocs.yml index 8242a45f..49c8c899 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -27,9 +27,9 @@ nav: - Module 99 - Optimize Containers: aca/99-optimize-containers/index.md - About The Authors: aca/12-about-the-authors/index.md - Appendix: - - Debug and Launch Dapr Applications in VSCode: aca/13-appendix/01-run-debug-dapr-app-vscode.md - - Testing Changes Locally or with GitHub Codespaces: aca/13-appendix/02-github-local-codespaces.md - - Variables: aca/13-appendix/03-variables.md + - Debug and Launch Dapr Applications in VSCode: aca/30-appendix/01-run-debug-dapr-app-vscode.md + - Testing Changes Locally or with GitHub Codespaces: aca/30-appendix/02-github-local-codespaces.md + - Variables: aca/30-appendix/03-variables.md theme: name: material From 7c86f68824fbcfbf3f05ab6c1974bcecdd687842 Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 10:39:52 -0500 Subject: [PATCH 5/9] Move appendix from 13 to 30 --- docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md | 4 ++-- docs/aca/30-appendix/03-variables.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md b/docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md index 690de23d..3dee98b0 100644 --- a/docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md +++ b/docs/aca/30-appendix/01-run-debug-dapr-app-vscode.md @@ -24,7 +24,7 @@ To accomplish this, open file `launch.json` and add the two configurations shown === "launch.json" ```json - --8<-- "docs/aca/13-appendix/launch.json" + --8<-- "docs/aca/30-appendix/launch.json" ``` !!! note @@ -43,7 +43,7 @@ Now we will add 4 tasks, for each application, there will be a task to support t === "tasks.json" ```json - --8<-- "docs/aca/13-appendix/tasks.json" + --8<-- "docs/aca/30-appendix/tasks.json" ``` ??? tip "Curious to learn more about the tasks.json file above?" diff --git a/docs/aca/30-appendix/03-variables.md b/docs/aca/30-appendix/03-variables.md index 8abf365e..bc0a6446 100644 --- a/docs/aca/30-appendix/03-variables.md +++ b/docs/aca/30-appendix/03-variables.md @@ -13,7 +13,7 @@ Execute this script to persist all variables in the current session at any time. === "Set-Variables.ps1" ```powershell - --8<-- "docs/aca/13-appendix/Set-Variables.ps1" + --8<-- "docs/aca/30-appendix/Set-Variables.ps1" ``` ## Restoring Variables From c0ce921b9daed63c784b2a82c5e276d96e2dea5e Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 10:40:46 -0500 Subject: [PATCH 6/9] Move authors to 29 --- README.md | 2 +- .../aca/{12-about-the-authors => 29-about-the-authors}/index.md | 0 docs/index.markdown | 2 +- mkdocs.yml | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename docs/aca/{12-about-the-authors => 29-about-the-authors}/index.md (100%) diff --git a/README.md b/README.md index f3082af8..3edb548c 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,4 @@ We are most grateful for community involvement. Please see [CONTRIBUTING.md](htt ##### Acknowledgment -The workshop's material, concepts, and code samples draw inspiration from a collection of blog articles authored by [Taiseer Joudeh](https://github.com/tjoudeh) and published on his [personal blog](https://bitoftech.net). The [workshop authors](https://azure.github.io/aca-dotnet-workshop/aca/12-about-the-authors/) have worked collaboratively to modify and augment the content, resulting in the current version of the workshop. +The workshop's material, concepts, and code samples draw inspiration from a collection of blog articles authored by [Taiseer Joudeh](https://github.com/tjoudeh) and published on his [personal blog](https://bitoftech.net). The [workshop authors](https://azure.github.io/aca-dotnet-workshop/aca/29-about-the-authors/) have worked collaboratively to modify and augment the content, resulting in the current version of the workshop. diff --git a/docs/aca/12-about-the-authors/index.md b/docs/aca/29-about-the-authors/index.md similarity index 100% rename from docs/aca/12-about-the-authors/index.md rename to docs/aca/29-about-the-authors/index.md diff --git a/docs/index.markdown b/docs/index.markdown index b49b8434..7edbb923 100644 --- a/docs/index.markdown +++ b/docs/index.markdown @@ -14,4 +14,4 @@ We are most grateful for community involvement. Please see [CONTRIBUTING.md](htt ##### Acknowledgment -The workshop's material, concepts, and code samples draw inspiration from a collection of blog articles authored by [Taiseer Joudeh](https://github.com/tjoudeh){target=_blank} and published on his [personal blog](https://bitoftech.net){target=_blank}. The [workshop authors](aca/12-about-the-authors/index.md) have worked collaboratively to modify and augment the content, resulting in the current version of the workshop. +The workshop's material, concepts, and code samples draw inspiration from a collection of blog articles authored by [Taiseer Joudeh](https://github.com/tjoudeh){target=_blank} and published on his [personal blog](https://bitoftech.net){target=_blank}. The [workshop authors](aca/29-about-the-authors/index.md) have worked collaboratively to modify and augment the content, resulting in the current version of the workshop. diff --git a/mkdocs.yml b/mkdocs.yml index 49c8c899..7f0c6933 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,7 +25,7 @@ nav: - Deploy infrastructure using Azure Devops Pipeline: aca/10-aca-iac-bicep/ci-cd-azdo.md - Module 11 - Integration with Azure Container Apps landing zone accelerator: aca/11-aca-landing-zone/index.md - Module 99 - Optimize Containers: aca/99-optimize-containers/index.md - - About The Authors: aca/12-about-the-authors/index.md + - About The Authors: aca/29-about-the-authors/index.md - Appendix: - Debug and Launch Dapr Applications in VSCode: aca/30-appendix/01-run-debug-dapr-app-vscode.md - Testing Changes Locally or with GitHub Codespaces: aca/30-appendix/02-github-local-codespaces.md From b6cbf483f889618dd554b2d5ea2491e081ade2fe Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 10:43:50 -0500 Subject: [PATCH 7/9] Change Optimize modules from 99 to 12 --- .../Backend.Api.Dockerfile | 0 .../Backend.Api.Dockerfile.chiseled | 0 .../Backend.Api.Dockerfile.chiseled.aot | 0 .../Backend.Svc.Dockerfile | 0 .../Backend.Svc.Dockerfile.chiseled.aot | 0 .../Frontend.Ui.Dockerfile | 0 .../Frontend.Ui.Dockerfile.chiseled.aot | 0 .../index.md | 6 +++--- .../backend-api-chiseled-aot-image-stats.png | Bin .../backend-api-chiseled-aot.png | Bin .../backend-api-chiseled-image-stats.png | Bin .../backend-api-chiseled.png | Bin .../backend-api-status-quo-image-stats.png | Bin .../backend-api-status-quo.png | Bin mkdocs.yml | 2 +- snippets/persist-state.md | 6 +++--- 16 files changed, 7 insertions(+), 7 deletions(-) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Backend.Api.Dockerfile (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Backend.Api.Dockerfile.chiseled (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Backend.Api.Dockerfile.chiseled.aot (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Backend.Svc.Dockerfile (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Backend.Svc.Dockerfile.chiseled.aot (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Frontend.Ui.Dockerfile (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/Frontend.Ui.Dockerfile.chiseled.aot (100%) rename docs/aca/{99-optimize-containers => 12-optimize-containers}/index.md (99%) rename docs/assets/images/{99-optimize-containers => 12-optimize-containers}/backend-api-chiseled-aot-image-stats.png (100%) rename docs/assets/images/{99-optimize-containers => 12-optimize-containers}/backend-api-chiseled-aot.png (100%) rename docs/assets/images/{99-optimize-containers => 12-optimize-containers}/backend-api-chiseled-image-stats.png (100%) rename docs/assets/images/{99-optimize-containers => 12-optimize-containers}/backend-api-chiseled.png (100%) rename docs/assets/images/{99-optimize-containers => 12-optimize-containers}/backend-api-status-quo-image-stats.png (100%) rename docs/assets/images/{99-optimize-containers => 12-optimize-containers}/backend-api-status-quo.png (100%) diff --git a/docs/aca/99-optimize-containers/Backend.Api.Dockerfile b/docs/aca/12-optimize-containers/Backend.Api.Dockerfile similarity index 100% rename from docs/aca/99-optimize-containers/Backend.Api.Dockerfile rename to docs/aca/12-optimize-containers/Backend.Api.Dockerfile diff --git a/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled b/docs/aca/12-optimize-containers/Backend.Api.Dockerfile.chiseled similarity index 100% rename from docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled rename to docs/aca/12-optimize-containers/Backend.Api.Dockerfile.chiseled diff --git a/docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot b/docs/aca/12-optimize-containers/Backend.Api.Dockerfile.chiseled.aot similarity index 100% rename from docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot rename to docs/aca/12-optimize-containers/Backend.Api.Dockerfile.chiseled.aot diff --git a/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile b/docs/aca/12-optimize-containers/Backend.Svc.Dockerfile similarity index 100% rename from docs/aca/99-optimize-containers/Backend.Svc.Dockerfile rename to docs/aca/12-optimize-containers/Backend.Svc.Dockerfile diff --git a/docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot b/docs/aca/12-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot similarity index 100% rename from docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot rename to docs/aca/12-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot diff --git a/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile b/docs/aca/12-optimize-containers/Frontend.Ui.Dockerfile similarity index 100% rename from docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile rename to docs/aca/12-optimize-containers/Frontend.Ui.Dockerfile diff --git a/docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot b/docs/aca/12-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot similarity index 100% rename from docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot rename to docs/aca/12-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot diff --git a/docs/aca/99-optimize-containers/index.md b/docs/aca/12-optimize-containers/index.md similarity index 99% rename from docs/aca/99-optimize-containers/index.md rename to docs/aca/12-optimize-containers/index.md index 5ce3ee88..6a5d224b 100644 --- a/docs/aca/99-optimize-containers/index.md +++ b/docs/aca/12-optimize-containers/index.md @@ -2,10 +2,10 @@ canonical_url: 'https://azure.github.io/aca-dotnet-workshop' --- -# Module 99 - Container Optimization +# Module 12 - Container Optimization !!! info "Module Duration" - 45 minutes + 45-60 minutes ## Objective @@ -300,7 +300,7 @@ Verify that the application continues to work with the three much smaller contai $FRONTEND_UI_BASE_URL ``` ---8<-- "snippets/persist-state.md:module99" +--8<-- "snippets/persist-state.md:module12" ## Review diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png b/docs/assets/images/12-optimize-containers/backend-api-chiseled-aot-image-stats.png similarity index 100% rename from docs/assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png rename to docs/assets/images/12-optimize-containers/backend-api-chiseled-aot-image-stats.png diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled-aot.png b/docs/assets/images/12-optimize-containers/backend-api-chiseled-aot.png similarity index 100% rename from docs/assets/images/99-optimize-containers/backend-api-chiseled-aot.png rename to docs/assets/images/12-optimize-containers/backend-api-chiseled-aot.png diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled-image-stats.png b/docs/assets/images/12-optimize-containers/backend-api-chiseled-image-stats.png similarity index 100% rename from docs/assets/images/99-optimize-containers/backend-api-chiseled-image-stats.png rename to docs/assets/images/12-optimize-containers/backend-api-chiseled-image-stats.png diff --git a/docs/assets/images/99-optimize-containers/backend-api-chiseled.png b/docs/assets/images/12-optimize-containers/backend-api-chiseled.png similarity index 100% rename from docs/assets/images/99-optimize-containers/backend-api-chiseled.png rename to docs/assets/images/12-optimize-containers/backend-api-chiseled.png diff --git a/docs/assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png b/docs/assets/images/12-optimize-containers/backend-api-status-quo-image-stats.png similarity index 100% rename from docs/assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png rename to docs/assets/images/12-optimize-containers/backend-api-status-quo-image-stats.png diff --git a/docs/assets/images/99-optimize-containers/backend-api-status-quo.png b/docs/assets/images/12-optimize-containers/backend-api-status-quo.png similarity index 100% rename from docs/assets/images/99-optimize-containers/backend-api-status-quo.png rename to docs/assets/images/12-optimize-containers/backend-api-status-quo.png diff --git a/mkdocs.yml b/mkdocs.yml index 7f0c6933..a40ac298 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,7 +24,7 @@ nav: - Deploy Infrastructure using Bicep via GitHub Actions: aca/10-aca-iac-bicep/ci-cd-git-action.md - Deploy infrastructure using Azure Devops Pipeline: aca/10-aca-iac-bicep/ci-cd-azdo.md - Module 11 - Integration with Azure Container Apps landing zone accelerator: aca/11-aca-landing-zone/index.md - - Module 99 - Optimize Containers: aca/99-optimize-containers/index.md + - Module 12 - Optimize Containers: aca/12-optimize-containers/index.md - About The Authors: aca/29-about-the-authors/index.md - Appendix: - Debug and Launch Dapr Applications in VSCode: aca/30-appendix/01-run-debug-dapr-app-vscode.md diff --git a/snippets/persist-state.md b/snippets/persist-state.md index ff48cb4a..363910db 100644 --- a/snippets/persist-state.md +++ b/snippets/persist-state.md @@ -97,13 +97,13 @@ --8<-- [end:module8] ---8<-- [start:module99] +--8<-- [start:module12] - Navigate to the root and persist the module to Git. ```shell git add . - git commit -m "Add Module 99" + git commit -m "Add Module 12" ``` ---8<-- [end:module99] +--8<-- [end:module12] From f23a0e3728a72a277bcf2f07615efe8006b9da81 Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 11:13:19 -0500 Subject: [PATCH 8/9] Update path from 99 to 12 --- docs/aca/12-optimize-containers/index.md | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/aca/12-optimize-containers/index.md b/docs/aca/12-optimize-containers/index.md index 6a5d224b..48347efa 100644 --- a/docs/aca/12-optimize-containers/index.md +++ b/docs/aca/12-optimize-containers/index.md @@ -40,7 +40,7 @@ Our original `Dockerfile` looks like this: === "Backend.Api Dockerfile" ```Dockerfile ---8<-- "docs/aca/99-optimize-containers/Backend.Api.Dockerfile" +--8<-- "docs/aca/12-optimize-containers/Backend.Api.Dockerfile" ``` ```shell @@ -55,11 +55,11 @@ docker image list This yields a sizable image at **222 MB**! -![Backend API Status Quo](../../assets/images/99-optimize-containers/backend-api-status-quo.png) +![Backend API Status Quo](../../assets/images/12-optimize-containers/backend-api-status-quo.png) This image is comprised of two images, 452 packages, and has 19 vulnerabilities. -![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-status-quo-image-stats.png) +![Backend API Status Quo Image Stats](../../assets/images/12-optimize-containers/backend-api-status-quo-image-stats.png) #### 1.2. Chiseled Images @@ -67,7 +67,7 @@ Microsoft and Ubuntu's creator, Canonical, collaborated on the concept of a [chi === "Backend.Api Dockerfile.chiseled" ```Dockerfile hl_lines="1 8" ---8<-- "docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled" +--8<-- "docs/aca/12-optimize-containers/Backend.Api.Dockerfile.chiseled" ``` Create a new file, `Dockerfile.chiseled` in the Backend Api root directory, then build the image again: @@ -80,11 +80,11 @@ docker image list Our image now stands at a much smaller **115 MB** - a drop of 107 MB and a size just 51.8% of the status quo image! -![Backend API Chiseled](../../assets/images/99-optimize-containers/backend-api-chiseled.png) +![Backend API Chiseled](../../assets/images/12-optimize-containers/backend-api-chiseled.png) This image is comprised of one image, 331 packages, and has five vulnerabilities. -![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-chiseled-image-stats.png) +![Backend API Status Quo Image Stats](../../assets/images/12-optimize-containers/backend-api-chiseled-image-stats.png) #### 1.3 Chiseled & Ahead-of-time (AOT) Compilation @@ -92,7 +92,7 @@ This image is comprised of one image, 331 packages, and has five vulnerabilities === "Backend.Api Dockerfile.chiseled.aot" ```Dockerfile hl_lines="1 8" ---8<-- "docs/aca/99-optimize-containers/Backend.Api.Dockerfile.chiseled.aot" +--8<-- "docs/aca/12-optimize-containers/Backend.Api.Dockerfile.chiseled.aot" ``` Create a new file, `Dockerfile.chiseled.aot` in the Backend Api root directory, then build the image again: @@ -109,12 +109,12 @@ docker image list Another massive reduction takes the image down to a mere **16 MB** - a total drop of 206 MB and a size just 7.2% of the status quo image! -![Backend API Chiseled AOT](../../assets/images/99-optimize-containers/backend-api-chiseled-aot.png) +![Backend API Chiseled AOT](../../assets/images/12-optimize-containers/backend-api-chiseled-aot.png) This image is comprised of one image, just 23 packages, and has nine vulnerabilities. Notably, the four additional vulnerabilities are in the `openssl 3.0.2` package in this image. -![Backend API Status Quo Image Stats](../../assets/images/99-optimize-containers/backend-api-chiseled-aot-image-stats.png) +![Backend API Status Quo Image Stats](../../assets/images/12-optimize-containers/backend-api-chiseled-aot-image-stats.png) #### 1.4 Deploying the new Status Quo @@ -156,7 +156,7 @@ Our original `Dockerfile` looks like this: === "Frontend.Ui Dockerfile" ```Dockerfile ---8<-- "docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile" +--8<-- "docs/aca/12-optimize-containers/Frontend.Ui.Dockerfile" ``` ```shell @@ -177,7 +177,7 @@ Skipping straight to AOT images: === "Frontend.Ui Dockerfile.chiseled.aot" ```Dockerfile hl_lines="1 8" ---8<-- "docs/aca/99-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot" +--8<-- "docs/aca/12-optimize-containers/Frontend.Ui.Dockerfile.chiseled.aot" ``` Create a new file, `Dockerfile.chiseled.aot` in the Frontend Ui root directory, then build the image again: @@ -198,7 +198,7 @@ Our original `Dockerfile` looks like this: === "Backend.Svc Dockerfile" ```Dockerfile ---8<-- "docs/aca/99-optimize-containers/Backend.Svc.Dockerfile" +--8<-- "docs/aca/12-optimize-containers/Backend.Svc.Dockerfile" ``` ```shell @@ -219,7 +219,7 @@ Skipping straight to AOT images: === "Backend.Svc Dockerfile.chiseled.aot" ```Dockerfile hl_lines="1 8" ---8<-- "docs/aca/99-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot" +--8<-- "docs/aca/12-optimize-containers/Backend.Svc.Dockerfile.chiseled.aot" ``` Create a new file, `Dockerfile.chiseled.aot` in the Backend Svc root directory, then build the image again: From 068d9132426aa1bb87e10db6a7036c780685ee40 Mon Sep 17 00:00:00 2001 From: Simon Kurtz Date: Tue, 21 Nov 2023 11:44:26 -0500 Subject: [PATCH 9/9] Minor copy change --- docs/aca/12-optimize-containers/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/aca/12-optimize-containers/index.md b/docs/aca/12-optimize-containers/index.md index 48347efa..82d76bd7 100644 --- a/docs/aca/12-optimize-containers/index.md +++ b/docs/aca/12-optimize-containers/index.md @@ -20,7 +20,7 @@ In this module, we will accomplish two objectives: ### 1. Optimizing Containers -Azure Container Apps makes it simply to quickly become effective with containers. But even a managed container platform requires hygiene and can benefit greatly from smaller containers. +Azure Container Apps makes it simple to quickly become effective with containers. But even a managed container platform requires hygiene and can benefit greatly from smaller containers. In this module, we will look into the benefits of optimized containers such as: