.NET アプリケーションをコンテナー化するための 9 つのヒント

過去 5 年間、 .NET はプロの開発者の間でトップ フレームワークとしての地位を維持してきました。 Stack Overflow の 2022 年開発者調査 では 、.NET は "その他のフレームワークとライブラリ" カテゴリで 1 位にランクされました 。Stack Overflo w は、昨年、 主要なテクノロジーを使用し て 広範な開発作業を行 い 、来年もそれらを引き続き使用したい 開発者のためにこれを予約しています

 

画像1 1
スタックオーバーフローのデータ提供。

 

60,000 を超える開発者と 3,700 社を超える企業が .NET プラットフォームに貢献しています。 2002 年のデビュー以来、.NET は複数の 言語 (C#、F#、Visual Basic)、プラットフォーム (.NET Core、.NET framework、Mono)、エディター、およびさまざまなアプリケーション向けにビルドするための ライブラリ をサポートしてきました。 .NET には、すべての .NET アプリケーションに共通の基本クラス ライブラリと API の標準セットが用意されています。

NETアプリケーションのコンテナ化が重要なのはなぜですか?

.NET はもともと Windows 用に設計されました。 一方、私たちはもともとLinuxを中心にDockerをベースにしていました。NETには、アプリケーション仮想マシン(共通 言語ランタイムと呼ばれる)と、 10〜20年前の大規模なエンタープライズアプリケーションに共通するビルドの問題を解決することを目的としたその他のコンポーネントがあります。この2つは、初日から本質的に互換性がありませんでした。

その後、どちらもクロスプラットフォームのオープンソース開発者プラットフォームに進化しました。 内部で実行される単一のプロセスで小さなコンテナを構築する場合、通常、直接コンパイルされた言語を使用する方が高速です。 とは言うものの、.NET は長い道のりを歩んできましたが、現在はコンテナーに対応しています。 マイクロソフトは、Windows Server 2016 SP2 以降、コンテナー システムを有効にするために協力して取り組んできました。 その目標は、この成長するコンテナエコシステムに追いつくことです。 現在、Linux カーネルだけでなく、Windows カーネルにも基づいている Windows ホストでコンテナーを実行できます。

あなたの実行 。網 Dockerコンテナ内のアプリケーションには多くの利点があります。 まず、Docker コンテナーは分離されたテスト環境として機能できます。 .NET 開発者は、開発と運用の間の一貫性を確保しながら、ローカルでコーディングとテストを行うことができます。 次に、運用環境への移行中に依存関係が見つからないことによって引き起こされる展開の問題を排除します。 第 3 に、コンテナーを使用すると、あらゆるスキル レベルの開発者がコンテナー化された .NET アプリケーションを構築、共有、および実行できます。 Cオンテイナー は不変のインフラストラクチャであり、移植性を提供し、スケーラビリティの向上に役立ちます。同様に、.NET 6のモジュール性と軽量性により、 コンテナに最適です 

.NET アプリケーションのコンテナー化 簡単です。 これを行うには、ソース コード ファイルをコピーして Docker イメージをビルドします。 また、イメージの肥大化、イメージ タグの欠落、ビルド パフォーマンスの低下などの一般的な懸念事項についても、.NET アプリケーション コードをコンテナー化するための 9 つのヒントについて説明します。

学生レコード管理アプリケーションのコンテナ化

これらの懸念をよりよく理解するために、簡単な学生記録管理アプリケーションを見てみましょう。 前回の ブログ投稿では、 学生データベースアプリケーションの構築と展開がいかに簡単であるか Dockerfile を見てきました。

アプリケーションの実行は簡単です。 GitHub プロジェクト リポジトリを複製し、Docker Compose CLI を使用して、次のコマンドで完全なアプリケーションを起動します。

git clone https://github.com/dockersamples/student-record-management

 

ディレクトリを次のように student-record-management変更して 、次の Docker 作成ファイルを表示します。

services:
db:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: example
volumes:
- postgres-data:/var/lib/postgresql/data
adminer:
image: adminer
restart: always
ports:
- 8080:8080
app:
build:
context: .
dockerfile: ./Dockerfile
ports:
- 5000:80
depends_on:
- db
volumes:
postgres-data:

 

この Compose ファイルでは、名前 db と app 属性によって 2 つのサービスを定義しました。 (以前の phpMinAdmin  )Dockerイメージ は Adminer 、PHPで書かれたフル機能のデータベース管理ツールです。属性を使用して ports ポート転送を設定しました。 この属性を使用すると depends_on 、サービス間の依存関係を表現できます。 この場合、コア アプリケーションの前に開始 Postgres します。 

次のコマンドを実行して、学生記録管理アプリケーションを起動します。

docker-compose up -d

 

起動して実行したら、Dockerダッシュボードを表示し、「矢印」キー(app-1に表示)をクリックして、アプリケーションにすばやくアクセスできます

 

画像4 1

 

通常、開発者は次のものを使用します Dockerfile テンプレートを使用して Docker イメージをビルドします。 ある Dockerfile のリストです シーケンシャル命令 コンテナー イメージをビルドする. この画像はレイヤーのスタックで構成されており、それぞれが ドッカーファイル.各レイヤーには、その下にあるレイヤーへの変更が含まれています。

FROM mcr.microsoft.com/dotnet/sdk:6.0

WORKDIR /src
COPY . ./src

RUN dotnet build -o /app
RUN dotnet publish -o /publish

WORKDIR /publish
ENV ASPNETCORE_URLS=http://+:80/
EXPOSE 80
CMD ["./myWebApp"]

 

最初の行は、 ベースイメージ、 サイズは約754MBです (または、Nano Server の場合は 994 MB、Windows Server の場合は 6.34GB).  COPY 、必要なプロジェクト ファイルを ホスト システムから  Docker イメージのルートにコピーします。この命令は EXPOSE 、コンテナーが実行時にネットワーク ポート 80 で特にリッスンすることを Docker に通知します。最後に、 CMD 実行可能ファイルとして実行されるコンテナーを構成できます。

Docker イメージをビルドするには、 docker build 命令:

docker build -t student-app .

 

新しい Docker イメージのサイズを確認しましょう。

docker images
REPOSITORY                             TAG       IMAGE ID       CREATED         SIZE
student-app                            latest    d3caa8643c2c   4 minutes ago   827MB

 

この例の主な欠点の 1 つは、Docker イメージが最適化されていないことです。 重要なのは、最適化により、チームはより小さな画像を共有し、パフォーマンスを向上させ、デバッグを容易にすることができるということです。 これは、本番環境を含むすべてのCI / CD段階で不可欠です。 Windows 基本イメージを使用している場合は、Linux の基本イメージよりもイメージがはるかに大きくなることが予想されます。 コンパイル後に不要なファイルは最終的なイメージでは必要ないため、破棄できるより良いビルドアプローチが必要です。

1)適切な.NET Dockerイメージの選択

公式の .NET Docker イメージは、 Docker Hub の Microsoft リポジトリで公開されています。 アプリケーションの構築中に適切なコンテナー ベース イメージを特定して選択するプロセスは、混乱を招く可能性があります。 選択プロセスを簡素化するために、ほとんどのイメージ リポジトリには、特定のフレームワーク バージョンの両方を選択するのに役立つ拡張機能のタグ付けが用意されています。 また、特定のLinuxディストリビューションやWindowsバージョンなど、適切なオペレーティングシステムを選択することもできます。

マイクロソフトでは、2 つのカテゴリのイメージを提供しています。 1 つ目は .NET アプリ の開発 と ビルド に使用されるイメージを含み、2 つ目は .NET アプリ の実行に使用されるイメージを格納します。 たとえば、 mcr.microsoft.com/dotnet/sdk:6.0 は開発およびビルド プロセス中に使用されます。このイメージには、コンパイラとその他の .NET 依存関係が含まれています。一方、 mcr.microsoft.com/dotnet/aspnet:6.0 は本番環境に最適です。このイメージには、Linux および Windows (マルチアーキテクチャ) での ASP.NET コアの最適化と並行してランタイムのみを含む ASP.NET コアが含まれています。

GitHub にアクセスして 、利用可能な Docker イメージを参照できます。

2)ドットネット復元用にドッカーファイルを最適化する

Docker を使用して .NET Core アプリを構築する場合は、 アプリの構築中に Docker がレイヤーをキャッシュする方法を考慮することが重要です。

ビルドキャッシュを利用する一般的な方法は、 .csproj.slnおよび nuget.config 完全なソース コードをコピーする代わりに、dotnet 復元を実行する前にアプリのファイル。 NuGet パッケージの復元は、ビルドの最も遅い部分の 1 つになる可能性があり、これらのファイルにのみ依存します。 最初にそれらをコピーすることで、Docker は復元結果をキャッシュできます。 たとえば、ファイルを変更するだけの場合は、 .cs再度実行する必要はありません 。

FROM mcr.microsoft.com/dotnet/sdk:6.0
WORKDIR /src

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet build -o /app
RUN dotnet publish -o /publish
WORKDIR /publish
ENV ASPNETCORE_URLS=http://+:80/
EXPOSE 80
CMD ["./myWebApp"]

 

💁  このコマンドは dotnet restore 、NuGet を使用して、プロジェクト ファイルで指定されている依存関係とプロジェクト固有のツールを復元します。

3) マルチステージビルドを使用する

マルチステージ ビルドでは、Docker はコンパイル、パッケージ化、単体テストに 1 つの基本イメージを使用できます。 その後、別のイメージがアプリケーション ランタイムを保持します。 これにより、最終的なイメージがより安全でサイズが小さくなります(開発ツールやデバッグツールが含まれていないため)。 多段 Dockerビルドは、ビルドが100%再現可能で、可能な限り無駄がないことを保証するための優れた方法です。 内に Dockerfile 複数のステージを作成し 、そのイメージの構築方法を制御できます。

.NET SDK には、.NET アプリケーションを開発、ビルド、およびパッケージ化するための .NET ランタイムとツールが含まれています。 Docker イメージを作成する際のベスト プラクティスの 1 つは、イメージをコンパクトに保つことです。 .NET アプリケーションは、多層アプローチを使用してコンテナー化できます。 各レイヤーには、依存関係、ソース コード、リソース、さらにはスナップショットの依存関係など、アプリケーションのさまざまな部分が含まれる場合があります。 または、実行可能なアプリケーションを含む最終イメージとは別のイメージとしてアプリケーションをビルドすることもできます。 これをよりよく理解するために、以下 Dockerfileを分析しましょう。

ビルド・ステージでは、SDK イメージを使用してアプリケーションをビルドし、 publish フォルダ。 T最終段階では、アーティファクトをビルドステージからappフォルダーにコピーします。 博覧 会着信要求にing port 80 し、アプリケーションを実行するコマンドを指定します。 WebApp. 最初の段階では、依存関係を抽出します。 第 2 段階では、抽出された依存関係を最終的なイメージにコピーします。  学生データベースの例のサンプルm アルティステージDockerfile は次のとおりです 。

FROM mcr.microsoft.com/dotnet/sdk:6.0 as build

WORKDIR /src
COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet build -o /app
RUN dotnet publish -o /publish

FROM mcr.microsoft.com/dotnet/aspnet:6.0 as base
COPY --from=build  /publish /app
WORKDIR /app
EXPOSE 80
CMD ["./myWebApp"]


最初のステージにはラベルが付けられています build
どこ mcr.microsoft.com/dotnet/sdk は基本イメージです

docker images
REPOSITORY                                  TAG              IMAGE ID       CREATED         SIZE
mywebapp_app                                latest           1d4d9778ce14   3 hours ago     229MB

 

最終的な画像サイズは、シングルステージ Dockerfile サイズの827MBと比較して、 229MBに劇的に縮小します 。

4)「最新」ではなく、特定の基本画像タグを使用する

Docker イメージを構築する際には、バージョン情報、目的の宛先 (本番環境やテスト環境など)、安定性、またはさまざまな環境にアプリケーションをデプロイするためのその他の有用な情報を体系化した便利なタグでタグ付けすることを常にお勧めしました。 逆に、 :latest タグ。 これ :latest タグは頻繁に更新され、新しいバージョンでは 破壊的変更。 破壊的変更から身を守りたい場合は、特定のバージョンにピン留めし、準備ができたら新しいバージョンに更新することをお勧めします。

たとえば、 mcr.microsoft.com/dotnet/sdk:latest 基本イメージとして。 代わりに、次のような mcr.microsoft.com/dotnet/sdk:6.0特定のタグを使用する必要があります。 mcr.microsoft.com/dotnet/sdk:6.0-windowsservercore-ltsc2019またはその他.

5)セキュリティ上の目的で非rootユーザーとして実行する

Docker コンテナー内でアプリケーションを実行している間、Linux の場合はルートへの既定のアクセス権、Windows の場合は管理者権限があります。 これにより、アプリケーションのセキュリティが損なわれる可能性があります。 この問題は、内に Dockerfile 指示を追加すること USER で解決できます。この命令は USER 、イメージの実行中に優先ユーザー名 (または UID) とオプションでユーザーグループ (または GID) を設定し、後続の RUN, CMDまたは ENTRYPOINT命令に対して設定します 。

Windows ネットワークでは、通常、Active Directory (AD) を使用して、ユーザー、コンピューター、およびその他のネットワーク リソース間の認証と承認を有効にします。 Windows アプリケーション開発者は、多くの場合、統合 Windows 認証を使用します。 これにより、ユーザーや他のサービスは、資格情報を使用してアプリケーションに自動的に透過的にサインインすることが容易になります。 Windows コンテナーをドメインに参加させることはできませんが、Active Directory ドメイン ID を使用してさまざまな認証シナリオをサポートすることはできます。

これを実現するには、Windows Server 2012 で導入された特殊な種類のサービス アカウントである グループの管理されたサービス アカウント (gMSA) で実行するように Windows コンテナーを構成できます。 これは、パスワードを必要とせずに複数のコンピューターが ID を共有できるように設計されています

6) .ドッカー無視を使用する

ビルドのパフォーマンスを向上させるには(そして一般的なベストプラクティスとして)、 .ドッカー無視 ファイルと同じディレクトリに Dockerfile.このチュートリアルでは、 .dockerignore ファイルには次の行が含まれている必要があります。

Dockerfile*
**/[b|B]in/
**/[O|o]bj/

 

これらの行は、  bin そして obj ファイルを Docker ビルド コンテキストから取得します。 慎重に構成する多くの正当な理由があります .dockerignore ファイルですが、この単純なバージョンは今のところ機能します。 また、コマンドの動作 docker build とビルド コンテキストの意味を理解することも役立ちます。

ビルドコンテキスト は、開発者が作業する場所またはスペースです。これは、Windows のフォルダでも、Linux のディレクトリでもかまいません。このディレクトリには、ソースコード、構成ファイル、ライブラリ、プラグインなど、必要なすべてのアプリコンポーネントがあります。新しいイメージを構築するときに、これらのコンポーネントのどれを含めるかを決定します。

.dockerignore ファイルを使用すると、どのコンポーネントが重要であるかを判断できます。それらは最終的に私たちが構築している新しいイメージに属します。

たとえば、イメージビルドにand conf ディレクトリを含めた bin くない場合は、ファイル内で .dockerignore それを示す必要があります 。

7)コンテナにヘルスチェックを追加する

この命令は HEALTHCHECK 、コンテナーをテストし、それがまだ機能していることを確認する方法を Docker に指示します。これにより、たとえば、サーバープロセスがまだ実行されているにもかかわらず、Webサーバーが無限ループに陥り、新しい接続を処理できない場合を検出できます。

アプリケーションが運用環境にデプロイされると、ほとんどの場合、Kubernetes やサービス ファブリックなどのオーケストレーターがアプリケーションを管理します。 正常性チェックを提供することで、コンテナーの状態をオーケストレーターと共有し、構成に基づいて管理タスクを許可します。 次の例を見てみましょう。

FROM mcr.microsoft.com/dotnet/sdk:6.0 as build

WORKDIR /src
COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet build -o /app
RUN dotnet publish -o /publish

FROM mcr.microsoft.com/dotnet/aspnet:6.0 as base
COPY --from=build  /publish /app
WORKDIR /app
EXPOSE 80
#If you’re using the Linux Container
HEALTHCHECK CMD curl --fail http://localhost || exit 1
#If you’re using Windows Container with Powershell
#HEALTHCHECK CMD powershell -command `
#    try { `
#     $response = iwr http://localhost; `
#     if ($response.StatusCode -eq 200) { return 0} `
#     else {return 1}; `
#    } catch { return 1 }

CMD ["./myWebApp"]

 

いつ HEALTHCHECK は ドッカーファイルをクリックすると、コンテナーの正常性が STATUS 実行中の列 docker ps.このチェックに合格したコンテナは次のように表示されます。 healthy.異常なコンテナは次のように表示されます。 unhealthy.

docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                           PORTS                  NAMES
7bee4d6a652a   student-app    "./myWebApp"             2 seconds ago   Up 1 second (health: starting)   0.0.0.0:5000-80/tcp   modest_murdock

 

8) 起動パフォーマンスを最適化する

.NET アプリの起動時間を短縮し、実行 準備完了 (R2R) コンパイルを使用してアセンブリをコンパイルすることで、待機時間を短縮できます。ただし、これにより、妥協点としてビルド時間が長くなります。 これを行うには 、アプリケーションを発行するときに有効になるプロパティ を設定します PublishReadyToRun

プロパティは PublishReadyToRun 、次の 2 つの方法で追加できます。

1)プロジェクトファイル内で設定します。

<PropertyGroup>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>

 

2)コマンドラインを使用して設定します。

/p:PublishReadyToRun=true

 

サンプルに付属する既定値 Dockerfile では、アプリケーションが小さすぎて保証できないため、コンパイルは使用 R2R されません。 このサンプル アプリケーションで実行されるコードの大部分は IL 、.NET のライブラリは既に R2R でコンパイルされています。 R2R この例では、 で Dockerfile と dotnet publish コマンドに渡します dotnet build /p:PublishReadyToRun=true 。

FROM mcr.microsoft.com/dotnet/sdk:6.0 as build

WORKDIR /src
COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet build -o /app -r linux-x64 /p:PublishReadyToRun=true
RUN dotnet publish -o /publish -r linux-x64 --self-contained true --no-restore /p:PublishTrimmed=true /p:PublishReadyToRun=true /p:PublishSingleFile=true

FROM mcr.microsoft.com/dotnet/aspnet:6.0 as base
COPY --from=build  /publish /app
WORKDIR /app
EXPOSE 80
HEALTHCHECK CMD curl --fail http://localhost || exit 1

CMD ["./myWebApp"]

9) Windowsコンテナに適した分離モードを選択します

Windows コンテナーのランタイム分離には、次の 2 つの異なるモードがあります。  

  • プロセスの分離  このモードでは、ファイルシステム、レジストリ、ネットワークポート、プロセス、スレッド ID 空間、およびオブジェクトマネージャ名前空間で分離された複数のコンテナインスタンスを同じホストで同時に実行できます。これは、Linuxコンテナの実行方法とほぼ同じです。
  • Hyper-V 分離 – このモードでは、コンテナーは高度に最適化された仮想マシン内で実行され、コンテナーとホストの間にハードウェア レベルの分離が提供されます。

最も 開発者は、ローカルで開発するときにプロセスの分離を好みます。 通常、消費するハードウェアリソースは ハイパーV 分離。そのため、開発者は、Hyper-V モードでコンテナーを実行するときに必要な追加のハードウェアを考慮する必要があります。 ただし、Hyper-V 分離を選択する際の主な考慮事項は、ハードウェア レベルの分離が追加されるため、セキュリティです。 Windows Server は両方のオプション (既定値: プロセス分離) をサポートしていますが、 Windows 10+では Hyper-V 分離のみがサポートされています。

独立性レベルを指定するには、フラグを指定する必要があります --isolation 。 

docker run -it --isolation=process mcr.microsoft.com/windows/servercore:ltsc2019 cmd

結論

これで、Docker イメージを最適化するための多くの方法のいくつかを見てきました。 いずれにせよ、慎重にあなた Dockerfileを作る ことが不可欠です。さらに詳しく知りたい場合は、安全な運用グレードの Docker イメージを構築するための推奨事項とベスト プラクティスをカバーする次のボーナス リソースを確認してください。

Dockerでは、活気に満ちた多様で創造的なコミュニティを非常に誇りに思っています。 時々、私たちはブログでコミュニティからのクールな貢献を特集し、私たちのコミュニティが行っている素晴らしい仕事のいくつかを強調しています。 Dockerで何か素晴らしいことに取り組んでいますか? 私たちのア ジートシンライナ(@ajeetraina) にあなたの貢献を送ってください Docker Community Slack チャンネル、そして私たちはあなたの作品を特集するかもしれません!

 

画像2 1