<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Multi Stage |</title><link>https://kurse.richter.consulting/tags/multi-stage/</link><atom:link href="https://kurse.richter.consulting/tags/multi-stage/index.xml" rel="self" type="application/rss+xml"/><description>Multi Stage</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>de</language><lastBuildDate>Sun, 08 Dec 2024 00:00:00 +0000</lastBuildDate><image><url>https://kurse.richter.consulting/media/logo.svg</url><title>Multi Stage</title><link>https://kurse.richter.consulting/tags/multi-stage/</link></image><item><title>Multi Stage Builds</title><link>https://kurse.richter.consulting/courses/container/multi_stage/</link><pubDate>Sun, 08 Dec 2024 00:00:00 +0000</pubDate><guid>https://kurse.richter.consulting/courses/container/multi_stage/</guid><description>&lt;p&gt;Wenn wir die Größe unseres Images in Docker Desktop anschauen (oder mit dem Docker-Befehl
auf der Kommandozeile), sehen wir, dass die Größe gewaltig ist. Auf meinem Rechner ist das Image 2.6 GB groß. Warum ist es so?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker image ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IMAGE ID DISK USAGE CONTENT SIZE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blazor-movies:from-sdk e561bfaae17f 2.55GB 758MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mcr.microsoft.com/dotnet/sdk:10.0 d1823fecac36 1.29GB 318MB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="ursachen-für-große-images"&gt;Ursachen für große Images&lt;/h2&gt;
&lt;p&gt;Schauen wir uns die Ursachen für die Größe an.&lt;/p&gt;
&lt;h3 id="größe-des-basisimages"&gt;Größe des Basisimages&lt;/h3&gt;
&lt;p&gt;Einen großen Einfluss auf die endgültige Größe hat natürlich das Basisimage. Unsere App kommt ja immer &amp;ldquo;on Top&amp;rdquo; von diesem Basisimage. Aktuell nutzen wir SDK-Image als Basis, das alleine bereits 1.3 GB groß ist. Das SDK-Image bringt alles mit, was notwendig ist, um eine dotnet Applikation ausführen, aber auch bauen zu können.&lt;/p&gt;
&lt;h3 id="daten-im-image"&gt;Daten im Image&lt;/h3&gt;
&lt;p&gt;Da wir die Applikation in dem Image selbst bauen, hat dieser nicht nur die fertige App, sondern auch noch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quellcode, den wir kopiert haben, um die Applikation bauen zu können&lt;/li&gt;
&lt;li&gt;Eventuelle &amp;ldquo;Zwischen-Dateien&amp;rdquo;, die bei einem Build-Prozess entstehen, aber nicht in die finale App reinfließen (bei dotnet z.B. die &lt;code&gt;obj&lt;/code&gt; und &lt;code&gt;bin&lt;/code&gt; Ordner)&lt;/li&gt;
&lt;li&gt;Eventuelle &amp;ldquo;Kompilate&amp;rdquo;, die auf unserem PC bereits da sind, da wir die App ja bereits local gebaut uns ausgeführt haben&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ursachen-beseitigen"&gt;Ursachen beseitigen&lt;/h2&gt;
&lt;p&gt;Beide Probleme können wir mit sogenannten &amp;ldquo;multi stage builds&amp;rdquo; lösen. Dabei werden unterschiedliche Images für das Erstellen der App (Kompilieren) und für finale Image genutzt. Dabei bekommt das finale Image nur die fertig kompilierte App.&lt;/p&gt;
&lt;p&gt;Dafür müssen wir nur wenige Änderungen an unserem Bauplan durchführen.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Image für Build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- FROM mcr.microsoft.com/dotnet/sdk:10.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Verzeichnis für das Build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit &lt;code&gt;AS &amp;lt;name&amp;gt;&lt;/code&gt; vergeben wir diesen ersten Schritt (Stage) einen Namen, so dass wir später uns auf diesen beziehen können.&lt;/p&gt;
&lt;p&gt;Für den zweiten Schritt benötigen wir ein schlankeres Basis-Image. Da wir am Ende die Applikation nur ausführen möchten, ist SDK überdimensioniert. Uns reicht für unsere Zwecke die runtime (Ausführung) Version. Das Image dazu bei Microsoft heißt &lt;code&gt;mcr.microsoft.com/dotnet/aspnet:9.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Alle Schritte in der Bauanleitung (Dockerfile), die mit dem finalen Image zu tun haben, werden auf dem &amp;ldquo;runtime&amp;rdquo; Basis-Image ausgeführt.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;RUN dotnet publish BlazorWebAppMovies.csproj -c Release -o /app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ # Image für Runtime
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ FROM mcr.microsoft.com/dotnet/aspnet:10.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;# Verzeichnis für die App
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;WORKDIR /app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ # Kopieren des aus dem Build zu Runtime Image
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ COPY --from=build /app .
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;# Datenbank Connection string
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;ENV CONNECTIONSTRINGS__BLAZORWEBAPPMOVIESCONTEXT=&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;In der Zeile &lt;mark&gt;4&lt;/mark&gt; definieren wir das Image, das als Basis für unser eigenes Image dienen soll (hier &lt;code&gt;aspnet&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;In der Zeile &lt;mark&gt;8&lt;/mark&gt; kopieren wir die fertig kompilierte Applikation aus dem SDK-Schritt (´from=build´) und Ordner &lt;code&gt;/app&lt;/code&gt; in den aktuellen Arbeitsordner. Damit enthält das fertige Image weder den Quellcode, noch die &amp;ldquo;Zwischen-Dateien&amp;rdquo;, die beim Kompilieren entstehen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bauen wir nun unser Image mit der aktuellen Version von Dockerfile und vergeben die Version &lt;code&gt;from-aspnet&lt;/code&gt; an das Image.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dockerfile" data-lang="dockerfile"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Image für Build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:10.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Verzeichnis für das Build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Kopiere das Projekt in das Arbeitsverzeichnis&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; . ./&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Wiederherstellen der Abhängigkeiten&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; dotnet restore&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Veröffentlichen der App in das Verzeichnis /app&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; dotnet publish BlazorWebAppMovies.csproj -c Release -o /app&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Image für Runtime&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/aspnet:10.0&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Verzeichnis für die App&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Kopieren des aus dem Build zu Runtime Image&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; --from&lt;span class="o"&gt;=&lt;/span&gt;build /app .&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Datenbank Connection string&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ENV&lt;/span&gt; &lt;span class="nv"&gt;CONNECTIONSTRINGS__BLAZORWEBAPPMOVIESCONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Port für Webserver&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;8080&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# App in Container starten&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dotnet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;BlazorWebAppMovies.dll&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker build -t blazor-movies:from-runtime .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unser Image ist nun deutlich kleiner geworden. Auf meinem MacBook Air ist das Image nun 462 MB, also 2.0 GB kleiner als unser erster Versuch.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker image ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IMAGE ID DISK USAGE CONTENT SIZE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blazor-movies:from-runtime 42c8d3bed245 462MB 116MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blazor-movies:from-sdk e561bfaae17f 2.55GB 758MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mcr.microsoft.com/dotnet/aspnet:10.0 eaa79205c3ad 369MB 92.4MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mcr.microsoft.com/dotnet/sdk:10.0 d1823fecac36 1.29GB 318MB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ein großer Teil kommt vom kleineren Basis-Image. Diese Änderung bringt bereits über 920 MB Ersparnis (von 1.3 GB runter auf 369 MB). Der Rest kommt durch das Weglassen des Quellcodes und der temporären Dateien, die beim Kompilieren entstehen.&lt;/p&gt;
&lt;h2 id="weitere-optimierungen"&gt;Weitere Optimierungen&lt;/h2&gt;
&lt;p&gt;Wir können unser Image noch weiter Optimieren. Die Standard-Images von Microsoft basieren normalerweise auf Debian, zwar einer sehr schlanken Version davon, aber immer noch mit einen vollwertigen Linux als Basis. Wir kennen bereits ein minimalistisches Linux, das selbst nur 20MG groß ist, &lt;code&gt;alpine&lt;/code&gt; Linux.&lt;/p&gt;
&lt;p&gt;Microsoft liefert alle eigene Basis-Images auch in der Alpine-Version.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dockerfile" data-lang="dockerfile"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Image für Build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:10.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Verzeichnis für das Build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Kopiere das Projekt in das Arbeitsverzeichnis&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; . ./&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Wiederherstellen der Abhängigkeiten&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; dotnet restore&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Veröffentlichen der App in das Verzeichnis /app&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; dotnet publish BlazorWebAppMovies.csproj -c Release -o /app&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Image für Runtime&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/aspnet:10.0-alpine&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Verzeichnis für die App&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Kopieren des aus dem Build zu Runtime Image&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; --from&lt;span class="o"&gt;=&lt;/span&gt;build /app .&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Datenbank Connection string&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ENV&lt;/span&gt; &lt;span class="nv"&gt;CONNECTIONSTRINGS__BLAZORWEBAPPMOVIESCONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Port für Webserver&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;8080&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# App in Container starten&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dotnet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;BlazorWebAppMovies.dll&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; docker build -t blazor-moviews:from-alpine .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nun sieht unser Image noch kleiner aus. Das Basis-Image ist von 369 MB runter auf 183 MB. Und unser eigener Image ist nun von 462 MB runter auf 276 MB. Das ist 11% von unseren Ausgangspunkt.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker image ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IMAGE ID DISK USAGE CONTENT SIZE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blazor-movies:from-alpine bb332d7cf91c 276MB 76MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blazor-movies:from-runtime 42c8d3bed245 462MB 116MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blazor-movies:from-sdk e561bfaae17f 2.55GB 758MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mcr.microsoft.com/dotnet/aspnet:10.0 eaa79205c3ad 369MB 92.4MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mcr.microsoft.com/dotnet/aspnet:10.0-alpine 1be14b20e4ec 183MB 51.9MB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mcr.microsoft.com/dotnet/sdk:10.0 d1823fecac36 1.29GB 318MB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Abhängig von der Programmiersprache und deren Fähigkeiten, lässt sich die finale Applikation noch weiter verkleinern (und somit auch das Image). Das erfordert aber sehr oft deutlich mehr aufwand. Mit Asp.Net wäre es möglich bei einigen Applikationen AOT (Ahead of Time) Kompilierung durchzuführen, so dass gar keine Runtime mehr benötigt wird. Mit Trimming (Abschneiden der nicht genutzten Fähigkeiten) kann die kompilierte Applikation noch weiter verkleinert werden.&lt;/p&gt;
&lt;p&gt;Mit den einfachen Mitteln haben wir aber bereits ein sehr kleines Image erstellt, das für die meisten Anforderungen gut genug ist.&lt;/p&gt;
&lt;h2 id="vorteil-von-alpine-linux"&gt;Vorteil von Alpine Linux&lt;/h2&gt;
&lt;p&gt;Der größte Vorteil von Alpine Linux ist nicht die Größe, sondern die Sicherheit. Da dieses Linux fast nichts hat, kann es weniger kompromittiert werden. Das ist direkt sichtbar in Docker Desktop. Für SDK Image liegen aktuell &lt;strong&gt;14&lt;/strong&gt; bekannte Schwachstellen (7 in Debian und 7 in SDK selbst), Runtime hat immer noch &lt;strong&gt;8&lt;/strong&gt;, Alpine hat nur &lt;strong&gt;2&lt;/strong&gt; (mit niedriger Wertung).&lt;/p&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="note"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8l.8-2.685a4.5 4.5 0 0 1 1.13-1.897zm0 0L19.5 7.125"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Hinweis&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;Das Bild der Schwachstellen ist eine Momentaufnahme und wird bei Ihnen anders aussehen. Allgemeines Bild, dass Alpine-Images deutlich weniger Schwachstellen aufweisen, wird aber bleiben.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure&gt;&lt;img src="https://kurse.richter.consulting/courses/container/multi_stage/sdk_vulnerabilities.png"&gt;&lt;figcaption&gt;
&lt;h4&gt;14 Schwachstellen in SDK Image&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://kurse.richter.consulting/courses/container/multi_stage/alpine_vulnerabilities.png"&gt;&lt;figcaption&gt;
&lt;h4&gt;2 Schwachstellen in SDK Image&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;</description></item></channel></rss>