2018 年 11 月

Volume 33 Number 11

.NET Core - .NET Core による公開オプション

執筆者: Jamie Phillips | 2018 年 11 月

.NET Core の登場により、アプリケーションを公開するための新しいパラダイムが導入され、フレームワーク依存の配置 (FDD) と自己完結型の配置 (SCD) という 2 つの新しい技法を利用できるようになりました。それぞれのオプションには長所と短所があり、公開する前に検討することが必要です。この記事では、2 つのアプローチを検証するため、サンプル アプリケーションをビルドしてそれぞれの長所と短所を説明します。その後、開発の初期段階にある別の配置オプション (CoreRT) を簡単に紹介します。

.NET Core による公開では、多くの情報を検討する必要があります。これまで、.NET Framework のデスクトップ アプリケーションまたはコンソール アプリケーションの一般的な配置オプションは、実行可能ファイルでした。ASP.NET アプリケーションでは、DLL です。利用可能なオプションはその 2 つだけで、どちらを使用するかはビルドするアプリケーションの種類に依存していました。新しいパラダイムでは、.NET Core がインストールされているかどうかが重要なポイントです。FDD 技法では .NET Core をインストールする必要がありますが、SCD 技法では .NET Core ランタイムが同梱されているため、.NET Core がインストールされていないマシンでも実行できます。ASP.NET アプリケーションは、独自の Web サーバー (Kestrel) を実行可能ファイルとして提供するため、スタンドアロンのコンソール アプリケーションとして追加でパッケージ化できるようになりました。

2 つのアプローチを検証するため、ここでは .NET Core SDK 2.1 をコマンド ライン インターフェイス (CLI) で使用します。.NET Core 2.1 のインストール手順については、microsoft.com/net/download を参照してください。この記事では、すべてのコマンドの実行にほぼ PowerShell のみを使用していますが、コマンド プロンプト (cmd.exe) でも正常に動作します。実際、それらのコマンドは macOS や Linux など、他の OS のシェルでも実行できます。また、SCD を試す場合、Linux 用に作成された実行ファイルを直接テストするために Windows Subsystem for Linux (WSL) は非常に役に立ちます。WSL の詳細については、bit.ly/2Nj7FuQ を参照してください。

サンプル アプリケーション

.NET Core 2.1 を使用する 2 つの公開方法をテストするため、きわめて基本的なサンプル アプリケーションを作成します。それは、入力された名前とともに「hello」を表示するコンソール アプリケーションです。cmd.exe または PowerShell のいずれかを使用して、次のコードを入力します。

> dotnet new console -o Publishing

新しいコンソール アプリケーションが作成されたら、Program.cs を開いて、次のコードを入力します。

using System;
namespace Publishing
{
  class Program
  {
    static void Main(string[] args) => Console.WriteLine($"Hello {args[0]}!");
  }
}

アプリケーションを実行すると、次のテキストが表示されます。

> dotnet run -- Jamie

Hello Jamie!

ご覧のように、期待どおりに動作します。ちなみに、2 つのハイフンから成る構文 (--) により、"dotnet run" コマンドを使用する際にパラメーターをアプリケーションに渡します。コンソール アプリケーションが完成したので、次は公開方法を検討します。

publish コマンド

まずは、このコマンドで使用できるオプションを確認するため、publish コマンドに "-h" (ヘルプ) オプションを付けて実行します。

dotnet publish [<PROJECT>] [-c|--configuration] [-f|--framework] [--force]
                           [--manifest]
                           [--no-build] [--no-dependencies] [--no-restore]
                           [-o|--output]
                           [-r|--runtime] [--self-contained]
                           [-v|--verbosity] [--version-suffix]

これらのオプションのうちの 2 つ、--runtime と --self-contained は、.NET Core アプリケーションを公開する場合に特に役立ちます。この 2 つのオプションは一緒に使用します。runtime オプションでは、SCD の作成時にターゲットにする 1 つまたは複数のランタイムを指定します。一方、self-contained オプションは、ランタイムを含む自己完結型パッケージを作成する必要があることを示します。runtime オプションを渡すと、self-contained オプションも自動的に適用されます。

その他の注目すべきオプションは、no-restore オプションと no-build オプションです。no-restore オプションを指定すると、publish コマンドを実行する際に暗黙のリストアが実行されません。no-build オプションを指定すると、プロジェクトはビルドされず、restore コマンドも実行されません。したがって、publish コマンドは既存のビルドに対して実行されます。このオプションを使用すると、ビルドとリストアが何度もトリガーされることがないため、継続的インテグレーションや継続的配置を行う場合に役立ちます。最後に、framework オプションでは、フレームワークのバージョンを指定できます。公式ドキュメントについては、bit.ly/2pizcOL を参照してください。

フレームワーク依存の配置

.NET Core CLI で publish コマンドを実行する場合、FDD が既定値になります。publish コマンドは、アプリケーションのビルドに使用されたマイナー バージョンと同じかそれより新しいバージョンの .NET Core ランタイムで実行可能な、プラットフォームに依存しないアプリケーションを作成します (バージョン 2.0 では 1.x をターゲットとするアプリケーションを実行できず、3.0 では 2.x をターゲットとするアプリケーションを実行できません)。アプリケーションにはターゲット ランタイムがパッケージされないため、このオプションを使用すると小規模のパッケージが生成されます。複数のアプリケーションをシステムに配置する場合、アプリケーション間でランタイムを共有できるようにすることで、システム全体のメモリとディスクの使用量を削減できます。共有ランタイムのもう 1 つの利点は、将来のランタイム更新がすべてのアプリケーションに適用されることです。

コマンド ラインで次のコマンドを入力して、サンプル アプリケーションを FDD として公開してみます。

> dotnet publish

これにより、publishing.dll ファイルを含む出力が bin\Debug\netcoreapp2.0 フォルダーに作成されます。この 5 KB の DLL がアプリケーションです。その後、次のように dotnet CLI を使用してアプリケーションを実行します。

> dotnet Publishing.dll Jamie

Hello Jamie!

これで完了です。

FDD の長所には、配置パッケージが小さいこと、システムによってランタイムが管理されること、複数のアプリケーション間でランタイムを共有してディスクとメモリの使用量を削減できることなどがあります。また、互換性のあるランタイムがインストールされているすべてのプラットフォームでのアプリの実行が保証されます。

FDD の短所は、システム全体にわたるランタイムの更新によって、互換性がなくなり、アプリケーションをコンパイルしたフレームワークのバージョン (またはそれ以降のバージョン) でしかアプリケーションを実行できないなどの問題が発生する可能性があります。また、ユーザーはアプリケーションを実行する前に、対応するバージョンの .NET Core ランタイムをマシンにインストールしておく必要があります。

自己完結型の配置

続いては、.NET Core CLI を使用してアプリケーションを公開するための 2 つ目のアプローチである SCD を見ていきましょう。これまでと同じ公開コマンドを使用しますが、今回は publish コマンドのオプションとしてランタイム識別子 (RID) を渡します。RID は、アプリケーションの作成時にターゲットとする 1 つまたは複数のプラットフォームを .NET Core CLI に指示します。以下の例では、アプリケーションの Windows 10 x64 ビルドを作成します。任意の OS から任意のプラットフォームをターゲットに設定できるため、Windows から他の OS の実行可能ファイルを作成できます。このサンプルで使用するコマンドは次のとおりです。

> dotnet publish -r win10-x64

netcoreapp2.0 フォルダーの下にサブフォルダーが作成され、渡されたターゲット ランタイムにちなんだ名前が付けられます。今回、win10-x64 フォルダーにあるのは、DLL ではなく実行可能ファイルです。実行可能ファイルのサイズは 77KB で、前のアプリケーションより 72KB 大きくなっています。この実行可能ファイルを実行して、正常に動作することを確認します。

> .\Publishing.exe Jamie

Hello Jamie!

これで Windows 10 x64 用の SCD が用意できたので、Ubuntu x64 をターゲットとするアプリケーションを次のようにビルドします。

> dotnet publish -r ubuntu-x64

その後、WSL を使用して、Ubuntu バージョンが期待どおりに実行されるかどうかをテストできます。Windows から WSL を開き、新しく作成したアプリケーションを実行可能にします。これで、アプリケーションを実行して、それが動作することを確認できます。入力と結果の出力は次のとおりです。

> chmod +x Publishing
> ./Publishing Jamie

Hello Jamie!

SCD の利点は、.NET Core ランタイムが制御されてアプリケーションに同梱されることです。これはユーザーにとって便利です。ランタイムのインストールが不要で、システムのランタイムを変更してもアプリケーションに影響しないからです。しかし、SCD ではランタイムがアプリケーションに同梱されるため、作成される配置パッケージのサイズが大きくなります。また、ターゲットごとにパッケージを生成するためには、ターゲット プラットフォームをあらかじめ知っておく必要があります。さらに、セキュリティ修正やバグ修正などのランタイム更新があるたびに、マシン全体のランタイム更新を 1 回インストールするのではなく、マシン上のすべてのアプリケーションを再公開して修正を反映する必要があります。

以前は、.NET アプリケーションを配布する場合、.NET Framework をインストールするか、インストーラーでパッケージ化する必要がありました。.NET Core と SCD を使用すると、特別なインストーラーを作成しなくても基本的なアプリケーションを配布できます。

SCD は便利ですが、アプリケーションを Linux システムにデプロイする場合は、解決すべき問題が 1 つあります。SCD には .NET Core のランタイム依存関係が含まれていません。つまり、ユーザーがランタイムをインストールする必要はありません。しかし、Linux の配布では、パッケージ マネージャーから 6 つの依存関係をインストールする必要があります。たとえば、Ubuntu では、次の依存関係のインストールが必要です。

liblttng-ust0
libcurl3
libssl1.0.0
libkrb5-3
zlib1g
libicu52 (for 14.x)
libicu55 (for 16.x)
libicu57 (for 17.x)
libicu60 (for 18.x)

この問題を解決するには、配布用のネイティブなパッケージ形式で SCD アプリケーションをパッケージ化して、追加の依存関係を定義できるようにする必要があります。

これは SCD の短所ですが、長所は、.NET Core ランタイムをインストールしなければならない場合、必要な追加のパッケージ リポジトリをユーザーが追加しなくてもよいことです。

SCD は魅力的なので、さらに紹介を続けます。SCD が機能するための要素はいくつかありますが、.NET CLI のインストールにおいて重要な役割を果たすコンポーネントは 2 つあります。1 つ目のコンポーネントは共有ランタイムです。これは .NET Core ランタイムの再配布可能バージョンであり、CLI とエンド ユーザーによって使用されます。2 つ目のコンポーネントは共有ホストです。こちらは、公開プロセスの一環として生成される DLL を使用します。共有ホストは、.NET Core ライブラリ (DLL) をアプリケーションとして実行することを可能にする汎用の apphost です。"dotnet run my.dll" を実行すると、my.dll はこの共有ホストの内部でホストされます。SCD アプリケーションをパッケージ化すると、共有ランタイム、共有ホスト、アプリケーション DLL が、1 つの実行可能パッケージ (Windows では .exe、Linux と macOS では該当の実行可能ファイル) にまとめて組み込まれます。この設計に関する実際のドキュメントは、bit.ly/2QCgZIp の .NET CLI リポジトリにあります。

ランタイム識別子

RID は、Windows 以外のプラットフォーム用の自己完結型の配置を作成する際にサポートされているプラットフォームを示します。たとえば、Windows 10 は x86、x64、ARM、ARM64 をサポートします。ARM のサポートは、Windows 10 の IoT (モノのインターネット) デバイスに対応しています。Linux では、次の配布で x64 のみがサポートされています。Ubuntu、RedHat、CentOS、Fedora、Debian、Gentoo、OpenSuse、Oracle、Tizen、LinuxMint。macOS では、10.10 から 10.13 までのバージョンがサポートされています。最後に、Android は .NET Core 2.0 以降でサポートされており、特定のターゲット グループのすべてのオプションをビルドする移植可能な RID も導入されています。詳細については、bit.ly/2PKXRXi にある RID カタログを参照してください。

CoreRT 配置

Microsoft では、FDD と SCD に加え、3 つ目のオプションとして CoreRT を開発中です。このオプションは、.NET Core ベースのコードからネイティブ バイナリを生成する機能を提供します。CoreRT は、CoreCLR Just-in-Time (JIT) コンパイラを使用して、Ahead-of-Time (AoT) コンパイルを実行します。これにより、.NET Core コードは、C ++ などの他の言語でも使用できる単一の実行可能ファイルとライブラリの両方を生成できます。CoreRT を使用すると、.NET 開発者はターゲット プラットフォームに対してネイティブなライブラリと実行可能ファイルを作成できるため、利用できる .NET プラットフォームの範囲を広げることができます。

CoreRT は、プロジェクトに NuGet パッケージを追加するだけで使用できます。私が作業しているサンプル プロジェクトでは、次のコマンドを実行するだけです。

> dotnet new nuget

nuget.config ファイルを追加して、次の行で .NET MyGet フィードを呼び出します。

<add key="dotnet-core"
  value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json"/>
<add key="nuget.org"
  value="https://api.nuget.org/v3/index.json" protocolVersion="3"/>

その後、次のように CoreRT NuGet パッケージを追加します。

> dotnet add package Microsoft.DotNet.ILCompiler -v 1.0.0-alpha-*

続いて、次に示すように、publish コマンドを実行して、プラットフォームの RID を渡します。

> dotnet publish -r win-x64

最後に、ネイティブにコンパイルされたアプリケーションとしてアプリケーションをテストできます。アプリケーションは native という名前のフォルダーに作成され、サイズは約 6KB です。これは FDD アプリケーションのサイズとほぼ同じです。また、SCD を使用する場合のように、ランタイムを同梱する必要はありません。

CoreRT の長所は、各ターゲットに対して、依存関係を含む単一のネイティブ バイナリへのネイティブ コンパイルが可能な点です。JIT コンパイルが不要なため実行速度が向上します。各ターゲットのコンパイラの最適化により、アプリケーションのスループットも向上します。

CoreRT の短所は、アプリケーションをターゲット プラットフォームごとにコンパイルする必要があり、プラットフォームを事前に選択する必要がある点です。現時点で、プレリリース段階の CoreRT には小さな問題があります。

CoreRT アプリケーションの動作は、SCD アプリケーションの動作と非常によく似ています。小規模なネイティブ実行エンジンである CoreRT ネイティブ ランタイムは、アプリケーションとともにコンパイルされたガベージ コレクションなどのランタイム サービスを、単一のネイティブ パッケージに組み込んで提供します。また、CoreRT には C# で記述された管理対象の要素があります。.NET Core アプリケーションは最初に Roslyn コンパイラでコンパイルされ、次に CoreRT および CoreFX と共に中間言語コンパイラに渡されます。ここで依存関係が分析されて処理が行われ、必要最低限のライブラリのみが LLVM に基づくコンパイラでネイティブ コードにコンパイルされます。最後に、リンカーによって CoreRT ネイティブ ランタイムがアプリケーションのコンパイル済みネイティブ出力にリンクされ、最終的なネイティブ実行可能ファイルが生成されます。bit.ly/2NRS5pj に、このテーマを扱った Matt Warren の有用なブログ記事があります。github.com/dotnet/corert にある CoreRT の GitHub リポジトリには、設計の各部分へのリンクが記載されています。

まとめ

SCD には FDD をしのぐ大きな利点があります。それは、インストールしたアプリケーションをサポートする追加のソフトウェアをユーザーがインストールする必要がないことです。私は、通常は最初から .NET Framework が利用可能な Windows でアプリケーションをビルドすることに慣れています。しかし、.NET Framework がインストールされていなかったり、間違ったバージョンがインストールされていたりすると、ユーザーは対応に戸惑う可能性があります。

.NET Core は、ユーザーがランタイムだけでなく、アプリケーションをサポートする特定のバージョンのランタイムをインストールすることが必要な今の状況を変えることを約束します。SCD ではランタイムがアプリケーションと一緒にパッケージ化されるため、より大きなアプリケーション (したがって、より大きなダウンロード ファイル) が生成されますが、ユーザーは追加の要件を気にせずにアプリケーションをインストールできます。Windows 以外の macOS や Linux などのユーザーにとっては、SCD はユーザーが期待する一般的な操作であるため、容易に採用できます。開発者や組織によって管理されている環境では、上記の点があまり問題にならないので、FDD のほうが有利である場合があります。

ネイティブにコンパイルする CoreRT 配置は、まだ開発の初期段階にあります。この配置には、FDD と SCD の両方の多くの利点があり、フレームワークのインストールやアプリケーション ファイルの圧縮は必要ありません。しかし、このアプローチが実用化されるのはまだ先のことです。


Jamie Phillips  は、イーストテネシーにある SentryOne のシニア ソフトウェア開発エンジニアです。2007 年から .NET に携わっており、DevOps とクラウドに大きな関心を抱いています。Twitter: @phillipsj73。ブログ: phillipsj.net。GitHub: phillipsj

この記事のレビューに協力してくれた次の技術スタッフに心より感謝いたします。Andrew Hall (Microsoft)、Daniel Oliver、Cameron Presley (SentryOne)
Andrew は Visual Studio の .NET、Web、および Azure App Service ツールのプログラム マネージャー リードです。大学卒業後、コンピューター サイエンスの修士号を取得するため学校に戻る前は、基幹業務アプリケーションを作成していました。その後、Visual Studio の診断チームに参加し、デバッグ、プロファイル、コード分析ツールを担当しました。現在は .NET および Web ツール チームで働いており、プロジェクト、IntelliSense、生産性、Azure ツールのサポートを含む、.NET のコア開発体験を担当するプログラム マネージャーのチームを率いています。

Cameron Presley は、開発者の日々の向上を支援する Microsoft MVP です。

Daniel Oliver は、テネシー州在住のソフトウェア開発者で、.NET と Azure を使って新しいことを学び、新しいものを構築しています



この記事について MSDN マガジン フォーラムで議論する