GNU/Linux >> Linux の 問題 >  >> Panels >> Docker

ASP.NET Core Docker イメージ サイズの最適化

ASP.NET Docker イメージ サイズの最適化に関する 2016 年の Steve Laster による素晴らしい投稿があります。それ以来、Docker はマルチステージ ビルド ファイルを追加して、1 つの Dockerfile でより多くのことを実行できるようにしました。コンテナーは、簡単で信頼性の高い展開が重要であり、密度も重要です。メモリの使用をできるだけ少なくしたいのは確かですが、メモリをネットワーク上で移動するのに時間を費やさないように、メモリをできるだけ小さくするのも良いことです。イメージ ファイルのサイズも、コンテナーの起動時間に影響を与える可能性があります。さらに、きちんと整頓されています。

私は今週、あなたと同じように私の机の上に小さな 6 ノードの Raspberry Pi (ARM) Kubenetes Cluster を構築してきましたが、イメージのサイズが思ったよりも少し大きいことに気付きました。比較的低電力のシステムであるため、これはより大きな問題ですが、繰り返しになりますが、必要がないのに、不要な x メガバイトを持ち歩く必要はありません。

Alex Ellis は、Raspberry Pi 用の .NET Core アプリの構築に関するすばらしいブログと、YouTube ビデオを公開しています。彼のビデオとブログで、彼は "Console.WriteLine()" コンソール アプリを作成しています。これは OpenFaas (オープン ソース サーバーレス プラットフォーム) に最適ですが、Raspberry Pi k8s クラスターに ASP.NET Core アプリも入れたいと考えていました。彼はこれを彼のブログに「挑戦」として含めたので、挑戦は受け入れられました!助けてくれてありがとう、アレックス!

Docker 上の ASP.NET Core (ARM 上)

まず、基本的な ASP.NET Core アプリを作成します。 Web API を行うこともできますが、今回は Razor Pages を使用して MVC を行います。明確にするために、それらは出発点が異なるだけで同じものです。後でいつでもページを追加したり、JSON を追加したりできます。

「dotnet new mvc」(または dotnet new razor など) から始めます。 Kuberenetes が管理する Docker でこれを実行しますが、いつでも Program.cs の WebHost を変更して、Kestrel Web サーバーの起動方法を次のように変更できます。

WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:5000;http://localhost:5001;https://hostname:5002")

Docker のユース ケースでは、環境変数を使用してリッスン URL を変更する方が簡単です。確かに 80 かもしれませんが、私は 5000 が好きです。Dockerfile を作成するときに、ASPNETCORE_URLS 環境変数を http://+:5000 に設定します。

ASP.NET 用に最適化された MultiStage Dockerfile

これを行うための「正しい」方法はたくさんあるので、シナリオについて考えてみてください。以下では、私が ARM を使用していることがわかります (Raspberry Pi のため)。 エラーが表示された場合 「qemu:Unsupported syscall:345」のようにコンテナーを実行している場合、x86/x64 で ARM イメージを実行しようとしています。 Windows から ARM コンテナーをビルドしようとしていますが、ここでは実行できません。それをコンテナー レジストリにプッシュしてから、Raspberry Pi クラスターにそれをプルダウンするように指示する必要があります。そうすれば、そこで実行されます。

これが私がこれまでに持っているものです。コメントアウトされているものもありますので注意してください。これは私にとって学習課題です/でした。何が起きているのかわからない場合は、コピー/貼り付けしないでください。間違いがあれば、私の Dockerfile の GitHub Gist を参照してください。変更および改善してください。

.NET Core には、ビルド ツール、開発キット、コンパイラなどを備えた SDK があり、さらにランタイムがあることを理解することが重要です。ランタイムには「アプリを作成する」機能はなく、「アプリを実行する」機能しかありません。現在、ARM 用の SDK はありません。そのため、マルチステージ ビルド ファイルを使用して (やや洗練された方法で) 作業を進めているのはこの制限です。ただし、ARM 用の SDK があったとしても、このような Dockerfile を使用したいと考えています。これは、スペースを効率的に使用し、イメージを小さくするためです。

これを分解しましょう。 2 つの段階があります。最初の FROM は、コードをビルドする SDK イメージです。私たちは Docker 内でビルドを行っています - これは素晴らしく信頼性の高いビルド方法です。

<ブロック引用>

プロからのヒント: Docker は中間イメージの作成と最小限の作業に優れています 、しかし、私たち (作者) がそれを助けるために正しいことをするなら、それは役に立ちます.

たとえば、.csproj をコピーしてから "dotnet restore" を実行する場所を参照してください。多くの場合、人々が「COPY . .」を行うのを目にします。そして復元を行います。これにより、Docker は何が変更されたかを検出できず、ビルドごとに復元の料金を支払うことになります。

この 2 つのステップ (プロジェクトのコピー、復元、コードのコピー) を行うことで、「dotnet 復元」の中間ステップが Docker によってキャッシュされ、作業が大幅に高速化されます。

ビルドしたら、公開します。私のように宛先がわかっている場合 (linux-arm)、-r linux-arm (または debian など) を使用して自己完結型の RID (ランタイム ID) パブリッシュを行うことができ、完全な自己を得ることができます。

それ以外の場合は、アプリのコードを公開し、.NET Core ランタイム イメージを使用してそれを実行できます。このイメージには完全な自己完結型のビルドを使用しているため、.NET ランタイムも含めるのはやり過ぎです。 Microsoft/dotnet の Docker ハブを見ると、「依存関係」の「deps」と呼ばれるイメージが表示されます。これらは、.NET が実行する必要があるものを含む debian の上にあるイメージですが、.NET 自体は含まれません。

画像のスタックは一般的に次のようになります (例)

  • debian:stretch から
  • microsoft/dotnet:2.0-runtime-deps から
  • microsoft/dotnet:2.0-runtime から

これで、ベース イメージ、依存関係、および .NET ランタイムが完成しました。 SDK イメージには、コードをビルドする必要があるため、さらに多くのものが含まれます。繰り返しますが、これが "as builder" イメージに使用し、コンパイルの結果をコピーする理由です。 それらを別のランタイム イメージに配置します。すべての世界で最高のものを手に入れることができます。

FROM microsoft/dotnet:2.0-sdk as builder  

RUN mkdir -p /root/src/app/aspnetcoreapp
WORKDIR /root/src/app/aspnetcoreapp

#copy just the project file over
# this prevents additional extraneous restores
# and allows us to re-use the intermediate layer
# This only happens again if we change the csproj.
# This means WAY faster builds!
COPY aspnetcoreapp.csproj .
#Because we have a custom nuget.config, copy it in
COPY nuget.config .
RUN dotnet restore ./aspnetcoreapp.csproj

COPY . .
RUN dotnet publish -c release -o published -r linux-arm

#Smaller - Best for apps with self-contained .NETs, as it doesn't include the runtime
# It has the *dependencies* to run .NET Apps. The .NET runtime image sits on this
FROM microsoft/dotnet:2.0.0-runtime-deps-stretch-arm32v7

#Bigger - Best for apps .NETs that aren't self-contained.
#FROM microsoft/dotnet:2.0.0-runtime-stretch-arm32v7

# These are the non-ARM images.
#FROM microsoft/dotnet:2.0.0-runtime-deps
#FROM microsoft/dotnet:2.0.0-runtime

WORKDIR /root/
COPY --from=builder /root/src/app/aspnetcoreapp/published .
ENV ASPNETCORE_URLS=http://+:5000
EXPOSE 5000/tcp
# This runs your app with the dotnet exe included with the runtime or SDK
#CMD ["dotnet", "./aspnetcoreapp.dll"]
# This runs your self-contained .NET Core app. You built with -r to get this
CMD ["./aspnetcoreapp"]

また、カスタムの nuget.config があることにも注意してください。その場合は、ビルド時に dotnet restore がすべてのパッケージを取得できるようにする必要があります。

第 2 段階の FROM の束をコメントアウトして含めました。私は ARM のみを使用していますが、他のものも見てもらいたいと思います。

ビルドしたコードをランタイム イメージにコピーしたら、環境変数を設定して、すべてのポートが内部でポート 5000 をリッスンするようにします (上記のことを覚えていますか?)。次に、アプリを実行します。ランタイムがある場合は "dotnet foo.dll" で実行できますが、私のように自己完結型のビルドを使用している場合は、"foo" を実行するだけです。

要約すると:

  • FROM microsoft/dotnet:2.0-sdk をビルダーとしてビルド
  • 結果をランタイムにコピー
  • 適切なランタイム FROM を使用する
    • 適切な CPU アーキテクチャ?
    • .NET ランタイムを使用する (標準) または自己完結型ビルドを使用する (それほどでもない)
  • 適切なポートでリッスンしていますか (ウェブ アプリの場合)?
  • アプリを正常に正しく実行していますか?
  • .dockerignore はありますか? /obj、/bin などをコピーしたくないが、/published が必要なため、.NET ビルドにとって非常に重要です。
    obj/
    bin/
    !published /

もう少し最適化

アプリを調べて、呼び出していないコードとバイナリを削除できるプレリリースの "ツリー トリミング" ツールがいくつかあります。 Microsoft.Packaging.Tools.Trimming も含めて試してみて、プロジェクトにパッケージを追加するだけで、最終的なイメージから未使用のコードをさらに取得しました。

Step 8/14 : RUN dotnet publish -c release -o published -r linux-arm /p:LinkDuringPublish=true
---> Running in 39404479945f
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

Trimmed 152 out of 347 files for a savings of 20.54 MB
Final app size is 33.56 MB
aspnetcoreapp -> /root/src/app/aspnetcoreapp/bin/release/netcoreapp2.0/linux-arm/aspnetcoreapp.dll
Trimmed 152 out of 347 files for a savings of 20.54 MB
Final app size is 33.56 MB

最終的なイメージで docker history を実行すると、サイズがどこから来ているかを正確に確認できます。 Microsoft が Debian ベース イメージから Alpine イメージに切り替えた場合、これはさらに小さくなるはずです。

C:\Users\scott\Desktop\k8s for pi\aspnetcoreapp>docker history c60
IMAGE CREATED CREATED BY SIZE COMMENT
c6094ca46c3b 3 minutes ago /bin/sh -c #(nop) CMD ["dotnet" "./aspnet... 0B
b7dfcf137587 3 minutes ago /bin/sh -c #(nop) EXPOSE 5000/tcp 0B
a5ba51b91d9d 3 minutes ago /bin/sh -c #(nop) ENV ASPNETCORE_URLS=htt... 0B
8742269735bc 3 minutes ago /bin/sh -c #(nop) COPY dir:cc64bd3b9bacaeb... 56.5MB
28c008e38973 3 minutes ago /bin/sh -c #(nop) WORKDIR /root/ 0B
4bafd6e2811a 4 hours ago /bin/sh -c apt-get update && apt-get i... 45.4MB
<missing> 3 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:8b7cf813a113aa2... 85.7MB

変更を加えたときの Dockerfile の進化を次に示します。最終結果はどんどん小さくなっていきます。少し手を加えて 45 メガバイト、または約 20% 縮小したように見えます。

C:\Users\scott\Desktop\k8s for pi\aspnetcoreapp>docker images | find /i "aspnetcoreapp"
shanselman/aspnetcoreapp 0.5 c6094ca46c3b About a minute ago 188MB
shanselman/aspnetcoreapp 0.4 083bfbdc4e01 12 minutes ago 196MB
shanselman/aspnetcoreapp 0.3 fa053b4ee2b4 About an hour ago 199MB
shanselman/aspnetcoreapp 0.2 ba73f14e29aa 4 hours ago 207MB
shanselman/aspnetcoreapp 0.1 cac2f0e3826c 3 hours ago 233MB

後で、この YAML の説明を使用してこの標準の ASP.NET Core Web アプリを Kubernetes に配置し、Raspberry Pi でスケールアウトするブログ投稿を行う予定です。私はたくさんのことを学んでいます!時間を割いてくれた Alex Ellis、Glenn Condron、Jessie Frazelle に感謝します!

スポンサー: DocuVieware HTML5 Viewer と Document Management Kit を使用して、ドキュメントのライフ サイクルの各ステップを管理する強力な Web アプリケーションを作成します。デモをチェックして、100 以上のフォーマットを取得、スキャン、編集、注釈付けし、UI をカスタマイズしてください!


Docker
  1. Dockerイメージからファイルを抽出しますか?

  2. Dockerイメージを変更する方法

  3. Dockerイメージに変更をコミットする方法

  1. Dockerfileの簡単な紹介

  2. Dockerfileを使用してDockerイメージを構築する方法

  3. 自己完結型の ASP.NET Core アプリケーションを Ubuntu で実行する

  1. Dockerで.NETアプリを実行する方法

  2. Dockerイメージマニフェストとは何ですか?

  3. DockerHubでのDockerイメージの共有