しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

PDFium を Windows on ARM (ARM64) 向けにビルドする

作っているアプリケーションに ARM64 対応を将来的に入れるにあたって、依存しているライブラリで PDFium だけが x86 / x64 だけの対応だったので、ARM64 向けビルドを試しておきました。

既に Chromium は Microsoft からのコントリビューションが行われているので、ARM64 向けビルドは多少はまりましたが比較的すんなりと行えました。

手順をメモしておかないと絶対に忘れるので残しておきます。

事前準備

ビルドに必要なものは Chromium と共通なので、以下のドキュメントを読みつつ準備します。

手順に従い ARM64 向けのツールをインストールする必要がありますが、実際のコンパイルには Clang が使われているようだったので、ここでインストールしたコンパイラは使われていない気がします。

中でも "Debugging Tools For Windows" は Visual Studio から Windows SDK をインストールしただけでは入らないので、アプリケーションの変更からコンポーネントを選択する必要があります。

depot_tools を展開しつつパスを通して、gclient で PDFium のソースと依存関係をダウンロードすれば大体完了です。set DEPOT_TOOLS_WIN_TOOLCHAIN=0 は忘れがちなので注意です。

PDFium をビルドする

GN と Ninja を使ってビルドしていきますが、必要な args.gn は以下のように用意しました。target_cpu = arm64 以外は x86 / x64 と共通です。

pdf_is_standalone = true
pdf_enable_v8 = false
pdf_enable_xfa = false
pdf_use_win32_gdi = false

is_component_build = false
is_debug = false

target_cpu = "arm64"

今回は必要なかったので v8 と XFA はオフにしてビルドします。GDI も使わないのでオフにしました。

普通にこのままビルドすると DLL が生成されないので、多少パッチを当てて DLL を生成しつつ呼び出し規約を一応 stdcall にしておきました。*1

diff --git a/BUILD.gn b/BUILD.gn
index 8bfe0ca55..1abb71741 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -42,6 +42,7 @@ config("pdfium_common_config") {
   if (is_win) {
     # Assume UTF-8 by default to avoid code page dependencies.
     cflags += [ "/utf-8" ]
+    defines += [ "FPDFSDK_EXPORTS" ]
   }
 }
 
@@ -139,7 +140,7 @@ group("pdfium_public_headers") {
   ]
 }
 
-component("pdfium") {
+shared_library("pdfium") {
   libs = []
   configs += [ ":pdfium_core_config" ]
   public_configs = [ ":pdfium_public_config" ]
diff --git a/public/fpdfview.h b/public/fpdfview.h
index debe083be..a228cdc50 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -175,7 +175,7 @@ typedef int FPDF_ANNOT_APPEARANCEMODE;
 // Dictionary value types.
 typedef int FPDF_OBJECT_TYPE;
 
-#if defined(COMPONENT_BUILD)
+#if defined(COMPONENT_BUILD) || defined(FPDFSDK_EXPORTS)
 // FPDF_EXPORT should be consistent with |export| in the pdfium_fuzzer
 // template in testing/fuzzers/BUILD.gn.
 #if defined(WIN32)
@@ -193,7 +193,7 @@ typedef int FPDF_OBJECT_TYPE;
 #endif  // defined(WIN32)
 #else
 #define FPDF_EXPORT
-#endif  // defined(COMPONENT_BUILD)
+#endif  // defined(COMPONENT_BUILD) || defined(FPDFSDK_EXPORTS)
 
 #if defined(WIN32) && defined(FPDFSDK_EXPORTS)
 #define FPDF_CALLCONV __stdcall

これで DLL が生成されるようになります。ビルド自体は gn gen を実行して Ninja の定義を作成した後に ninja -C directory pdfium を実行すると行われます。

今回は out\arm64 以下に args.gn を置いてあるので、以下のようなコマンドを実行します。

gn gen out\arm64
ninja -C out\arm64 pdfium

これで x86 と x64 の場合は問題なく DLL が生成されますが、ARM64 の場合は以下のようなエラーが出るケースがあるようです。CRT のディレクトリ構成が微妙に異なっているのが原因のようです。

f:id:shiba-yan:20200630181202p:plain

ソート関数が int と str で比較できないのが原因なので、今回は適当に数値に変換できない場合は 0 を返して選ばれないようにしました。

f:id:shiba-yan:20200630181210p:plain

修正後は ARM64 向けでも問題なく Ninja でビルドが行えるようになりました。

f:id:shiba-yan:20200630181651p:plain

ビルド後の DLL のヘッダーを調べると AA64 になっているので、ARM64 向けの DLL であることが分かります。エクスポート関数も一応調べましたが、問題なく定義されていました。

f:id:shiba-yan:20200630181705p:plain

後は実際に ARM64 マシン上で動作するか確認するだけです。もちろん Surface Pro X を使います。

Surface Pro X で実際に試す

PDFium ではテストコードもビルド出来るのでそれを使っても良いのですが、それだとつまらないので .NET 5 Preview 6 で追加された Win Forms の ARM64 版で試しました。

サンプル自体は以下のようなコードを書きました。GDI サポートを使えば HDC に対して直接レンダリング出来るようですが、GDI も今更感あるので Bitmap に対してレンダリングします。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        var dialog = new OpenFileDialog();

        if (dialog.ShowDialog() != DialogResult.OK)
        {
            return;
        }

        var filePath = dialog.FileName;

        NativeMethods.FPDF_InitLibrary();

        var document = NativeMethods.FPDF_LoadDocument(filePath, null);
        var page = NativeMethods.FPDF_LoadPage(document, 0);

        var width = (int)NativeMethods.FPDF_GetPageWidth(page);
        var height = (int)NativeMethods.FPDF_GetPageHeight(page);

        var bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        var bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

        var pdfBitmap = NativeMethods.FPDFBitmap_CreateEx(width, height, 2, bitmapData.Scan0, bitmapData.Stride);

        NativeMethods.FPDFBitmap_FillRect(pdfBitmap, 0, 0, width, height, 0xffffffff);
        NativeMethods.FPDF_RenderPageBitmap(pdfBitmap, page, 0, 0, width, height, 0, 0);

        NativeMethods.FPDFBitmap_Destroy(pdfBitmap);

        bitmap.UnlockBits(bitmapData);

        NativeMethods.FPDF_ClosePage(page);
        NativeMethods.FPDF_CloseDocument(document);

        NativeMethods.FPDF_DestroyLibrary();

        pictureBox1.Image = bitmap;
    }
}

ARM64 向けビルドは以下のコマンドを使って Self-contained 形式でビルドしました。

これまでもコンソールアプリの場合は RID に win-arm64 を指定できましたが、Preview 6 では Win Forms に対応したのでビルドが通ります。

dotnet publish -c Release -o ./publish -r win-arm64

WPF の ARM64 サポートもひっそり入っているのを期待しましたが、ビルドエラーになりました。

ビルドしたファイルを Surface Pro X にコピーして実行すると、ちゃんと 64bit で動作しているのが確認できます。PDF 自体もちゃんとレンダリングされて表示できています。

f:id:shiba-yan:20200630190243p:plain

既に Chromium Edge が ARM64 に対応しているからか、ビルドから実行まで問題なく行えました。

割と簡単に PDF のレンダリングまで行えましたが、これを実際に PDF Viewer まで仕上げるのはちょっと面倒な感じです。時間を見つけつつ WPF で書いていこうかなという気持ちです。

*1:x64 / ARM64 だと特に意味はないと分かってはいる

Electron で Surface Pro X にネイティブ対応したアプリを作る

Surface Pro X というか ARM64 にネイティブ対応したアプリケーションが中々増えないですが、.NET Framework / .NET Core 以外ならいい感じに ARM64 対応が進んでいます。

特に Electron 7 から ARM64 対応したのは結構インパクトが大きいと思っています。

Electron の公式ドキュメントに Windows on ARM 向けのチュートリアルが用意されていますが、微妙に古い情報だったので最新の Electron 8 と Visual Studio 2019 で試してみました。

と言っても自分は Win32 とか Windows on ARM に関する知識はあっても Electron 周りの知識はほぼないので、サンプルプロジェクトを全編通して利用しています。

ARM64 ネイティブで動くと、常駐するタイプのメッセージングアプリで有利になってくるはずなので、是非とも Slack や Teams には対応してほしいところです。てか Teams は出すべき。

とりあえず Electron サンプルを動かす

前述したように Electron の経験がゼロなので、まずは普通の Windows 10 上で Electron のサンプルをビルドして動かしてみました。今回は公式の Quick Start を利用しました。

Node.js がインストールされていれば、以下のコマンドを叩くだけでアプリケーションが起動します。

git clone https://github.com/electron/electron-quick-start
cd electron-quick-start
npm install
npm start

実際に起動した例は以下になります。見慣れたインターフェースを持つアプリケーションが起動しました。

f:id:shiba-yan:20200416015437p:plain

Node.js の Web アプリケーションを動かすのとほとんど変わらない手順で Electron のアプリケーションが起動できました。このアプリケーションを Surface Pro X 上でネイティブ動作するように持っていきます。

実行可能ファイルとしてビルド

Electron を使ったアプリケーションを実行可能ファイルにするには electron-packager というツールが公式に提供されているようですが、最低限の機能しか持っていないようなので、インストーラ付きでビルドできる electron-builder を使った方が便利なようです。

使い方も簡単で、公式ドキュメントの通りに npm を使って electron-builder をインストールします。

npm install -D electron-builder

インストール後に packages.jsonscripts にコマンドを追加しつつ、同時に build セクションを追加しておきます。スクリプトベースでも定義できるようですが、簡単な方を選びました。

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "pack": "electron-builder --dir",
    "dist": "electron-builder"
  },
  "build": {
    "appId": "SampleApp"
  },
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^8.2.4",
    "electron-builder": "^22.6.0"
  }
}

appId は無くてもビルドは通りますが、ドキュメントには明示的に指定するように強く書かれていたので設定しています。本来ならユニークな値を設定する必要があります。

設定完了後に npm run dist を実行すると実行ファイルが生成されます。

f:id:shiba-yan:20200429193134p:plain

アイコンなどのリソースは決められたパスにファイルを置いておけば自動的に使われます。指定しない場合は Electron のデフォルトアイコンのままになるので、実際のアプリ開発時には用意しておきましょう。

複数アーキテクチャ対応のインストーラーを作成

デフォルトの設定のままでは x64 向けの実行可能ファイルが生成されましたが、アプリケーションの配布時には複数アーキテクチャに対応したインストーラーが必要になってきます。

Electron では Windows 環境において ia32 (x86) / x64 / arm64 がサポートされています。

electron-builder を使って複数アーキテクチャに対応させるには win セクションを追加して、その中でビルドするアーキテクチャを指定します。

以下のように書くと Windows で利用可能な全てのアーキテクチャ向けにビルドします。

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "pack": "electron-builder --dir",
    "dist": "electron-builder"
  },
  "build": {
    "appId": "SampleApp",
    "win": {
      "target": {
      	"target": "nsis",
      	"arch": ["ia32", "x64", "arm64"]
      }
    }
  },
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^8.2.4",
    "electron-builder": "^22.6.0"
  }
}

x86 ではなく ia32、amd64 ではなく x64 と書く必要があるのが少し罠っぽいです。若干統一感に欠ける印象がありますが、間違った場合にはビルドエラーになるのですぐに気が付くはずです。

これも先ほどと同様に npm run dist を実行するとインストーラー付きで作成されます。

f:id:shiba-yan:20200429204541p:plain

このインストーラーを Surface Pro X にコピーして実行してみると、問題なく arm64 版がインストールされてアプリケーションが動作していることが確認できます。

Surface Pro X 上では 64bit プロセスは ARM64 で動作している場合だけなので、タスクマネージャーから簡単に確認することが出来ます。

f:id:shiba-yan:20200429203052p:plain

Quick Start は特にプラットフォームに大きく依存するものを使っておらず、外部のライブラリも参照していないので簡単に ARM64 向けビルドが行えました。

しかし普通は何かしらライブラリを使うはずなのでその辺りも検証していきます。

Native Module をビルドして利用

Node.js のモジュールにはネイティブコードを使うものがあり、通常なら node-gyp でいい感じにビルドするか、node-pre-gyp でビルド済みバイナリをダウンロードするのであまり気にする必要がないです。

現状 Windows 10 の ARM64 向けバイナリはほぼ提供されていないので、自前でビルドする必要があります。Electron 向けにビルドするのはドキュメントがありますが割とめんどくさい感じです。

しかし electron-builder は自動的に上のドキュメントにある処理を行ってくれるので、何も考えなくてもコマンドを叩くだけで ARM64 向けビルドが行えます。

実際にビルドが必要になるライブラリで試してみます。今回は bcrypt を使ってみました。

npm install bcrypt

ARM64 向けのビルドを行うためにはコンパイラーやライブラリをインストールする必要があるので、予め Visual Studio Installer からインストールしておきます。

f:id:shiba-yan:20200430165434p:plain

これまでのように npm run dist を実行するだけで、bcrypt のビルドが実行されていることがログから確認できます。ARM64 の場合は node-gyp によってビルドが行われています。

f:id:shiba-yan:20200430163456p:plain

Native Module を使っていても、electron-builder を使っていれば自動でビルドが走るので便利です。ただしビルド対象のライブラリが ARM64 向けにコンパイルできないコードの場合はエラーになります。

アプリケーションを AppX としてビルド

インストーラーとしてこれまでビルドしてきましたが、Windows Store での配布を行う場合には AppX や MSIX としてビルドする必要があります。electron-builder は AppX としてのビルドに対応しているので、多少の設定変更でビルド出来ます。

設定は以下のように変更しています。変更点としては targetappx に変更し、appx セクションを追加しているぐらいです。AppX の場合は identityName の設定は必須のようです。

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "pack": "electron-builder --dir",
    "dist": "electron-builder"
  },
  "build": {
    "appId": "SampleApp",
    "win": {
      "target": {
        "target": "appx",
        "arch": ["ia32", "x64", "arm64"]
      }
    },
    "appx": {
      "identityName": "ElectronQuickStart"
    }
  },
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^8.2.4",
    "electron-builder": "^22.6.0"
  },
  "dependencies": {
    "bcrypt": "^4.0.1"
  }
}

electron-builder のバージョンは 22.6.0 以上を使わないと、ARM64 向けビルドの時にエラーになります。

これまでのようにビルドを行うと、各アーキテクチャ向けに AppX が生成されます。

f:id:shiba-yan:20200430170351p:plain

appxbundle は生成されないですが、Windows Store へのアップロード時には appx で問題ないです。

appx を開いてみるとインストーラーが立ち上がります。署名がないので今はインストールエラーになりますが、Windows Store へのアップロード目的の場合は署名無しで問題ありません。

f:id:shiba-yan:20200430225908p:plain

サイドローディングの場合は署名を行う必要がありますが、これもまた electron-builder が対応しています。

アプリケーションに署名を行う

electron-builder を使うと PFX とパスワードだけ用意しておけば簡単に署名が行えます。証明書はとりあえず自己署名証明書を作成して試すので、PowerShell を使って適当に PFX を作成しておきました。

署名の設定は環境変数を使って行うので、CI でも簡単に実行可能でしょう。以下のような環境変数を設定しておけば、自動的に署名が実行されます。

set CSC_LINK=certificate.pfx
set CSC_KEY_PASSWORD=P@ssw0rd

ビルド後に署名されているか確認するには、AppX のファイルプロパティを開くだけです。

f:id:shiba-yan:20200430231237p:plain

Windows Store 以外からインストールするためには信頼済みの署名が必要なので、認証局から発行されたコードサイニング証明書を使うか、自己署名証明書を「信頼されたルート証明機関」に入れる必要があります。

証明書をインストールすれば、appx を開いたときに警告が表示されなくなります。

f:id:shiba-yan:20200430232435p:plain:w550

これでインストールが完了したので、後はスタートメニューからアプリケーションを選べば起動出来ます。

f:id:shiba-yan:20200430232445p:plain

思ったより Electron のビルド周りのインフラが洗練されているという印象を持ちました。

外部のライブラリ次第ではありますが ARM64 への対応も比較的簡単になっているので、対応アプリが増えていくことを期待しています。まずは Teams は何とかしてほしい。

Vcpkg と CMake を使って Windows 10 ARM64 向けクロスコンパイルを行う

Windows 10 の ARM64 向けビルドは実質 Surface Pro X 向けですが、Surface Pro X でも x86 エミュレーションのオーバーヘッドを調べると無視できるレベルではなかったので、やっぱりアプリケーションは ARM64 ネイティブで動かしたい気持ちが高まってます。

ちなみに Geekbench 4 で x86 と ARM64 を比較した結果は以下のようになります。Surface Go よりは速いのですが、ARM64 ネイティブと比べると差がかなり大きいです。

C# での ARM64 ネイティブ実行は .NET Core 3.0 のコンソールアプリケーション以外不可能なので、現時点で ARM64 ネイティブのデスクトップ向けアプリを作るためには Win32 か UWP になります。*1

ARM64 ネイティブのアプリが増えるにはライブラリがビルド出来る必要があるので、Vcpkg と CMake の 2 つでクロスコンパイルの方法を試しました。昔に比べるとビルドが簡単になってて驚いてます。

ARM64 ビルドツールをインストール

まずは Visual Studio Installer を使って ARM64 ビルドツールをインストールします。ARM64 で検索するとたくさん出てきますが、バージョンの新しいやつを入れておけば良いです。

f:id:shiba-yan:20191201181745p:plain

Win32 向けと UWP 向けの 2 つが出てくるので、両方インストールしておけばよいです。

Vcpkg を使ってビルド

C++ 向けのパッケージマネージャは C# と同じように、NuGet を使ってプリコンパイル済みのバイナリを拾ってくるのかと思ってましたが、最近はソースからビルドする方が流行っているようです。

マシンパワーが格段に上がった昨今では、環境依存を少なく出来るので確かに便利です。

Vcpkg の使い方は既にいろんな人が書いているのと、README に書いてあるコマンドをいくつか叩くだけでセットアップできるので丸っと省略します。

最初から ARM64 Windows に対応した Triplets が用意されているので、以下のようにインストール時に arm64-windows を指定するだけでビルドが行われます。

vcpkg install zlib:arm64-windows

vcpkg install boost-regex:arm64-windows

たまに先に x86-windows 向けのパッケージをインストールしてから再実行しろというエラーメッセージが出ますが、メッセージに従えば大体は問題なくビルドに成功します。

実際に zlib と boost-regex を ARM64 向けにビルドしてみましたが、問題なく完了しました。

f:id:shiba-yan:20191202020646p:plain

パッケージマネージャらしく vcpkg list を叩くと、ビルド済みのパッケージの管理が出来ます。

f:id:shiba-yan:20191201182431p:plain

Vcpkg でビルドしたバイナリをそのまま、いい感じに C# のプロジェクトから参照するような MSBuild Task を用意したい気持ちでいっぱいです。

P/Invoke で使いたいシチュエーションも結構あると思うので、もうちょっと調べてみるつもりです。*2

CMake を直接使ってビルド

Vcpkg のベースになっている CMake も最初から ARM64 向けクロスコンパイルに対応しているので、こっちの方法も試してみました。CMake に関してはドキュメントを読むのが特に重要な感じでした。

全てのパッケージが Vcpkg でビルド出来れば楽ですが、そういう都合のいいことは無いので CMake 力を多少なりとも上げておきます。非対応のパッケージは当然ながらあります。

今回は Vcpkg で対応していたのですが、ARM64 ビルドは出来なかった OpenCV で試しました。というか ARM64 に対応させるために CMake の定義にパッチを当てました。

ARM64 向けツールセットは -A ARM64 を指定すると自動的に選択されましたが、CMAKE_SYSTEM_PROCESSOR は自動設定されないようだったので ARM64 を明示的に追加して対応しました。

今回 ARM64 のクロスコンパイルに成功した CMake のパラメータは以下のような感じでした。

cmake -G "Visual Studio 16 2019" -A ARM64 -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION=10.0 \
           -DCMAKE_SYSTEM_PROCESSOR=ARM64 -DWITH_OPENCL=OFF -DWITH_FFMPEG=OFF -DWITH_CUDA=OFF \
           -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON C:\opencv4

内部で参照されている CMAKE_CROSSCOMPILINGCMAKE_SYSTEM_NAMECMAKE_SYSTEM_VERSION を明示的に指定しないと設定されないらしく、結構な時間を溶かしてしまいました。

CMake でのクロスコンパイルで重要なのは CMAKE_SYSTEM_NAME / CMAKE_SYSTEM_VERSION / CMAKE_SYSTEM_PROCESSOR のようなので、忘れないようにしっかり残しておきます。Vcpkg は微妙にこの辺りの設定をミスっている気配ありますが…。

作業中にはまったポイントなど

C/C++ ランタイムのインストール

Vcpkg や CMake のデフォルトでは C/C++ ランタイムは動的リンクになるので、ビルドしたバイナリを直接 Surface Pro X にコピーしても動きません。忘れないように ARM64 向けのランタイムを入れておきます。

ひっそりと Visual Studio のページからダウンロード可能になっています。

f:id:shiba-yan:20191201222205p:plain

Visual Studio のインストールディレクトリの中にも C/C++ ランタイムのインストーラがあります。

f:id:shiba-yan:20191120215340p:plain

デバッグ用の C/C++ ランタイムは Visual Studio からリモートデバッグする時に、自動的にインストールがされるようなオプションがありましたが試せていません。

OpenGL ライブラリが存在しない

ARM64 Windows 10 の制約として書いてあるように OpenGL は基本的に使えないと考えてよいです。

使えないだけならまだ良いのですが、ARM64 向け SDK には opengl32.lib すら存在しないので、依存しているライブラリは軒並みビルド出来なくなっています。

代わりに ANGLE を使えと回答が付いてますが、本当に ANGLE が動くのかも少し謎です。

Vcpkg に opengl が存在しているので、依存するライブラリは opengl (!arm64) のように CONTROL ファイルで定義しないとビルド出来ないのがかなり辛いです。

デフォルトで追加されるライブラリが少ない?

これはかなり謎な挙動なのですが、OpenCV の highgui をビルド中にリンクエラーが出たので調べたら、x86 / x64 の場合より ARM64 はデフォルトライブラリが少ないような挙動でした。

user32.libkernel32.lib はありますが、comdlg32.libadvapi32.lib が無かったのでリンクエラーになっていました。どうしようもないので CMake 側で対応をしました。

アセンブラを使っていると辛い

当然ながらコンパイルには ARM64 向けのアセンブラが必要ですが、地味に VC++ の ARM アセンブラにリグレッションが多かったので困りました。

OpenCV の場合は SIMD の利用にコンパイラ側の機能を使っていたので、今度はコンパイラ依存の問題もあったりでひとまず棚上げにしたりも。リベンジはしたいです。

*1:Electron という選択肢もあるが試していない

*2:無ければ多分書く

Surface Pro X (ARM64) に対応したアプリケーションを作る

10 インチの Surface Go に ARM の CPU が載ることを期待していましたが、Surface Pro X に載ってしまったので検証用に買うべきか悩んでいます。本体だけなら $999 から買えるので比較的安いです。

例によって日本での発売は未定ですが、フラグシップとなる Surface Pro X に ARM が載ったことで、それなりの台数が出そうなのでアプリ側の対応をしておくと幸せになれる予感がするので調べました。

先に ARM64 で動作するアプリを作る方法を 32 行でまとめておきました。Windows 向けにアプリを作る一般的な方法を選んでいますが、別の UI Framework を使えば実現出来るかも知れません。

  • .NET Framework を使って Any CPU 向けにビルド
  • UWP を使って ARM64 向けにビルド
  • C/C++ (Win32) を使って ARM64 向けにビルド

もうちょっと具体的に書いてみると、以下のようになると思います。実機がないので細かい部分までは追えていないですが、ビルド通ってしまえば勝ちという感じがあります。

  • .NET Framework (Any CPU) はそのまま ARM64 x86 で動く
    • NuGet などで Native DLL を参照している場合は実行時エラーになる
  • .NET Core は Windows 10 ARM64 を未サポート
    • .NET Core 3.0 でもダメ
    • ARM32 向けにビルドすれば動く (WPF / Win Forms 以外)
    • WPF / Win Forms は x86 向けにビルドするとバイナリトランスレーションで動く
  • UWP (.NET Native) は 6.2.7 から ARM64 をサポート
    • 新しくプロジェクトを作ると ARM64 ターゲット付きになっている
  • C/C++ (Win32) は ARM64 向けにビルドすれば動く
    • ARM64 向け開発ツール (コンパイラ / ライブラリ) のインストールが必要

Any CPU な .NET Framework 向けアプリならすんなり動きますが、ネイティブコードが含まれているライブラリを使っているケースは厄介です。最近は NuGet からビルド済みアセンブリをインストール出来ますが、殆どは ARM64 向けのバイナリを持っていないので詰みます。

意外に思われるかも知れないですが、.NET Core は Windows の ARM64 には公式対応していないです。RID に win10-arm64 を指定するとビルドが通ることもありますが、不具合を抱えている可能性があります。

公式ドキュメントなど

非常に ARM64 に関するドキュメントは少ないです。実機を買って触ってみるのが一番早そうです。

基本的には Visual Studio 2019 と ARM64 の開発ツールを入れてビルドを頑張る、という流れです。

あと、つもりんが Windows on ARM64 の実機を持っているので、ちょいちょいブログに書いてくれてます。

.NET Framework

Windows 10 の ARM64 版には .NET Framework の ARM64 版がインストールされているので、Any CPU でビルドしているアプリケーションは問題なく動作するはずです。

パッと見た感じでは ARM64 に限定したビルドは無理のようでした。普通は Any CPU で作ると思うので問題ないですが、ネイティブ DLL を読み込む場合は ARM64 向けのビルドを用意しておかないと死にます。*1

外部ライブラリを使う場合はマネージドコード実装のものを選んでおくと安心です。

追記

Surface Pro X で確認したところ、.NET Framework でビルドしたアプリケーションは強制的に x86 として実行されました。ARM32 / ARM64 向けに JIT Compile されないようなので、全体的にエミュレーションでの動作になるようでした。

つまり .NET Framework のままでは ARM64 への対応は絶対に無理ということです。

.NET Core

.NET Core は ARM64 に対応していないので、Surface Pro X に持って行っても動きません。ただし ARM32 や x86 としてビルドすると動作するので、とりあえずは妥協するしかなさそうです。

つもりんに協力してもらって ARM64 の Windows 上でいろいろと確認してもらいました。何故か対応してないはずの ARM64 向けにビルドが出来たので、それも確認してもらいました。

コンソールアプリケーションは問題ないですが、WPF / Win Forms に関してはランタイムが x86 / x64 向けにしか公開されていないので、ARM としてビルドは絶対にできないようになっています。

なので x86 としてビルドして、バイナリトランスレーションに頼る形になります。

x86 から ARM への変換が行われて、問題なく WPF のアプリケーションが動きました。

WPF (.NET Core) の ARM64 対応は今のところ 5.0 で予定されているみたいですが、先なので妥協します。

Surface Pro X がめちゃくちゃ売れたら優先順位が上がるかも知れませんが、望みは薄いでしょう。

UWP (.NET Native)

UWP に関しては元々 Windows 10 Mobile で ARM 対応が行われていたのもあり、ARM64 の対応は比較的スムーズに行えそうな感じです。とはいえ外部ライブラリの問題は残って来ます。

新しくプロジェクトを作れば、最初から ARM64 のプラットフォーム設定が追加されています。

f:id:shiba-yan:20191003205511p:plain

ARM のターゲットは Windows 10 Mobile 以外では必要ないので、いっそ削除しても良い気がします。

アプリケーションのパッケージ作成時に ARM64 にチェックを入れれば、対応したパッケージが作られます。依存関係が少なければ非常に簡単なので、まずは UWP から対応するようにしています。

f:id:shiba-yan:20191003205407p:plain

ビルドが成功しても認定ツールを走らせたり、ストアへの申請時のチェック時にエラーとなる場合があります。原因は大体ライブラリが ARM64 に対応していないことなので、ライブラリを特定して対応をお願いしたり、コントリビュートが必要になるでしょう。

C/C++ (Win32)

Win32 API を使って書いている場合も、基本は ARM64 向けのプラットフォームを追加してビルドすれば良いです。依存するライブラリが ARM64 に対応している必要がありますが、自分でビルドすることも可能です。

プラットフォームの追加ですが、最初から ARM64 が用意されているので簡単に行えます。

f:id:shiba-yan:20191003210637p:plain

適当にビルドしてみましたが、ちゃんと ARM64 向けのコードが生成されていることが確認できます。

f:id:shiba-yan:20191003194053p:plain

ビルドしたアプリケーションを開発用のマシンから ARM64 へ展開する場合は、Remote Tools for Visual Studio 2019 の ARM64 版をインストールすれば良いです。開発者モードをオンにしても出来る気がします。

ARM64 への対応は簡単な場合はやばいぐらいすぐに終わりますが、極端に難易度が高くなるケースもあるので行うべきかの判断が難しいです。

Surface Pro X がバカ売れしたら対応せざるを得ないはずなので、もう少し様子を見ても良いとは思います。

*1:Win32 API は ARM64 なので問題なし

Windows Server 2019 時代の Windows Containers を使ったアプリケーション移行

Kubernetes が 1.14 から Windows Server 2019 をサポートするようになり、周辺ツールやサービスも整ってきた現状、今年こそ Windows Containers がぼちぼち使われるようになるのではないかと思っています。と言っても新規開発は ASP.NET Core を推奨しているので、基本は既存のアプリケーションの移行目的です。

まだ AKS で Windows Node を作ることは出来ないですが、例によって AKS-Engine を使うと作れるようです。最近は k8s への興味が薄れつつあるので AKS が対応するまでは試さないです。

今回 k8s でサポートされたのは Windows Server 2019 だけっぽいので、2016 は捨てていく方向で良いと思います。2016 では Docker Image サイズ削減の恩恵も受けられないですし、リビジョンが違うとサポートを受けられないのは致命傷です。

毎年言ってる気がしますが、今度こそ一通り必要な環境やサービスが整ったと思うのでまとめます。

Docker Image

去年から徐々に MCR に移行が進んできましたが、今月になって .NET Framework / .NET Core の Docker Image が MCR に移行されたので、これで移行はほぼ完了となります。

Docker Hub を指している方は今後更新されなくなるはずなので、早めに差し替えておきましょう。移行先の情報は Docker Hub か以下の記事を確認してください。

具体的な例を挙げると .NET Framework を使っている場合は、以下の 2 つだけ覚えておけば良いです。

  • mcr.microsoft.com/dotnet/framework/sdk:4.7.2-windowsservercore-ltsc2019
  • mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-ltsc2019

Windows Containers を使う場合には、今後は ltsc2019 を指定しておけば良いです。.NET Core の場合も同じような命名なので覚えやすいはずです。

開発環境

Visual Studio 2017 では ASP.NET アプリケーションを Docker 化する場合には、何故か Docker Compose が必須になっていました。専用のプロジェクトも追加されて使い勝手が悪かったです。

f:id:shiba-yan:20190328163652p:plain

来月リリースされる Visual Studio 2019 では、ASP.NET Core と同じように Dockerfile だけが追加されるシンプルな形式に変わります。

f:id:shiba-yan:20190328163721p:plain

Visual Studio を使ったデバッグはこれまで通り行えるので、別に Docker になったからといって使い勝手が変わることは特にありません。しいて言えば初回の Docker Image の pull に時間がかかるぐらいです。

現実問題として Windows Containers を使いたいケースは実行環境のカスタマイズ系だと思うので、ベースとなるイメージを CI などで作っておくことで、ある程度のタイミングは制御可能でしょう。

CI / CD

必ず必要になってくるのが CI / CD です。単純に Dockerfile から Image を作って ACR にプッシュするだけなら ACR Tasks を使って簡単に用意できます。

ACR Tasks は既に Windows Server 2019 にアップデートされているので安心です。

もう少し複雑なリリースを行いたい場合は Azure Pipelines を使います。こっちも少し前に Windows Server 2019 と Visual Studio 2019 に対応した Hosted Agent が追加されています。

f:id:shiba-yan:20190328165433p:plain

残念なことに Windows Server 2019 の Docker Image がキャッシュされていないのでビルド毎に無駄な時間が発生しますが、今後の Agent アップデートで直ると思います。

地味に嫌な問題としては公開されている .NET Framework SDK の Docker Image をそのまま使うと、ASP.NET アプリケーションのプリコンパイルに失敗するというのがありましたが、回避策があるのと Visual Studio 2019 の SDK によって解決しそうです。

Visual Studio 2017 の MSBuild が .NET 4.6.2 SDK をデフォルトで見るようになっているので、4.7.2 SDK しか入っていない Docker Image で失敗するという話でした。

実行環境

先述したように Kubernetes が Windows Server 2019 をサポートしたことで、近いうちには AKS でも簡単に Windows Server 2019 の Node が作成可能になるのではと思っています。*1

Web App for Containers (Windows) も順調にアップデートを重ね、まあまあ使えるようになってきた印象です。少し前に Windows Server 2019 へのアップデートも行われています。

GitHub ではちょっとマニアックなサンプル Dockerfile も公開されています。

この辺りの作業は VM 向けの Image を作るのとあまり変わらないので、Dockerfile を書いて環境をカスタマイズしていく形です。Server Core なので少し注意が必要ですが。

まとめ

実際のところ 98% は通常の App Service に移行が行えると思っているので、ソフトウェアのインストールが必要だったり、サンドボックスで制限されている機能を使っている 2% を VM や k8s を使って動かすのではなく、マネージド環境で動かしたいときに使うものだという認識です。

Docker Image が大きくて扱いにくいという問題は抱えつつも、環境自体は Linux と同じような構成を取れるようになったので、主にエンプラ向けで活躍できるのではないかと思います。

*1:ARM Template を使えばこれまでもデプロイは出来ていたけど

Windows 10 で Process Isolation を使う時の注意点など

Docker Engine 18.09.1 と Windows 10 Version 1809 の組み合わせ時に Process Isolation *1 が使えるようになっています。Windows Server Containers は Hyper-V Containers より軽量なので助かります。

早速弄っていましたが、クライアント OS で使う場合にありがちな問題に当たったので、調べてメモとして残します。Windows Server で使う場合には問題になりにくいです。

Process と Hyper-V で ACL が異なる

例えば Nano Server を Process Isolation かつ Volume をマウントして起動してみます。

docker run --rm --isolation=process -v "C:\Users\shibayan\source\repos\AspNetCore:C:\AspNetCore" \
           -it mcr.microsoft.com/windows/nanoserver:1809 cmd

この時にマウントしたディレクトリを確認しようとすると、アクセスが拒否されます。

f:id:shiba-yan:20190208172133p:plain

Hyper-V を使った場合は問題なくアクセス出来るのに、Process の場合は拒否されるので割と悩んでいましたが、以下のドキュメントに答えが載っていました。

Hyper-V の場合は単純に RO / RW という制御しかされないみたいですが、Process の場合は NTFS の ACL が使われるので、コンテナ内の実行ユーザー次第でアクセスが出来ないという話でした。

特に Nano Server は 1709 から実行ユーザーが ContainerAdministrator から ContainerUser に変更されているため、マウントするファイルに権限を付けるか、実行ユーザーを変更する必要があります。*2

ドキュメントには Authenticated Users が載っていたので、とりあえず付けて試します。

f:id:shiba-yan:20190208173436p:plain

これでコンテナから再度アクセスしてみると、問題なくディレクトリを確認出来ました。

f:id:shiba-yan:20190208171523p:plain

通常はサーバー OS でこういった使い方はしないので、Windows 10 で使えるようになると似たような問題が他にも出てきそうな気がします。

ちなみに Server Core の場合は ContainerAdministrator で動いているのと、ACL に大体は Administrators が入っているので問題ないみたいです。

Visual Studio からは一部利用不可

Process Isolation で Nano Server を使った場合に ACL 周りで問題が発生するため、Visual Studio のコンテナサポートを使った ASP.NET Core アプリケーションの開発は行えなくなります。

イメージのビルドまでは問題なく行えますが、デバッグ実行では Visual Studio が User Profile 以下にあるファイルをマウントしようとするため、リモートデバッガのプロセスを起動できずに失敗します。

f:id:shiba-yan:20190208174937p:plain

ASP.NET アプリケーションの場合は Server Core を使うので、問題なく Process Isolation でもデバッグ実行が行えます。Docker Compose を使おうとしたり、ベースイメージが古かったりと罠が多いですが、Hyper-V よりは素早く起動してくれます。

Default Isolation を Process に変更する

今のところ Visual Studio には Isolation を設定する機能などはないので、Process Isolation を使う場合には Default 自体を変更しておく必要があります。

Docker Deamon の設定に exec-opts を追加するだけなので簡単です。

{
  "exec-opts": [
    "isolation=process"
  ]
}

Docker の設定から Advanced を選べば JSON を書けるようになっているので、以下のように追記します。

f:id:shiba-yan:20190121021001p:plain

これで設定を保存すると Default Isolation が hyperv から process に変わります。

f:id:shiba-yan:20190121020911p:plain

Default Isolation を Process にしていても LCOW は問題なく Hyper-V で動くので、Volume 周りの影響を受けない場合は Process を使うと素早くコンテナを実行出来るので便利です。

*1:Windows Server Containers と呼ばれているやつ

*2:Nano Server の ContainerAdministrator は将来的に削除される予定なので注意

Windows Server 2019 の Docker Image と MCR への移行

停止されていた Windows Server 2019 の公開がようやく再開されました。同時に Windows Server 2019 ベースの Server Core / Nano Server イメージも公開が再開されています。

これでようやく LTSC なバージョン上でサイズが縮小された Docker Image を使う準備が出来ました。

既にこれまで Windows Server 2016 を使っていたイメージに 2019 版が追加され始めています。主に .NET Framework 周りは対応が早く、既に ltsc2019 で各種イメージが選べます。

サイズが ltsc2016 と比べて 1/3 近くになっていることも確認できます。やっと実用が可能なサイズに。

f:id:shiba-yan:20181116182816p:plain

ASP.NET や ASP.NET Core (Nano Server) や IIS 向けには 2019 はまだ提供されていないですが、既に PR は作成されているので、近日中にリリースされると思われます。

それはそうと今回の 2019 リリースに合わせて、これまで Docker Hub で公開されていたイメージが Microsoft Container Registry からの公開に切り替わっています。

2019 年から latest タグを使わないようにしていくみたいなので、既に latest を使っている場合には注意。

Docker Hub で新しいイメージ名が確認出来るので迷わないと思いますが、MCR は公開されているイメージやタグをブラウズする機能がないので、正直使いにくいなと思います。

一応タグ名は同じでイメージ名だけが変わっているみたいなので、単純な置き換えで済みます。

# これまで
docker pull microsoft/windowsservercore:ltsc2016

# これから
docker pull mcr.microsoft.com/windows/servercore:ltsc2016

命名のルールは MCR の方が分かりやすいです。現実的にはタグは ltsc2016 か ltsc2019 しか使わないと思うので、この二つだけ覚えておけばよいと思います。

今のところは Server Core / Nano Server などの OS ベースとなるイメージや、PowerShell Core / Azure Functions Runtime / SQL Server 2019 Preview などが MCR に移行済みのようです。

https://hub.docker.com/u/microsoft/

.NET Framework や .NET Core 周りのイメージも順次 MCR への移行を進めていく予定のようです。GitHub の Issue にイメージ名のプロポーザルが載っています。

まだリリースされていないし、確定でもないようですが Server Core のように階層化された形で管理されるようです。既に .NET Core のイメージは結構使われているので、バッサリと移行は行わないみたいです。

MCR は当然ながら Azure 上に構築されていて、さらに Traffic Manager を使って各地に分散されているので、Azure Pipelines や Web App for Containers などで使うとネットワーク的に有利な予感です。

Multi-arch 時代の Windows Containers 関連イメージの扱い

Windows Containers を使っていて非常に悩ましいのが OS バージョンですが、最近は Multi-arch が使われているのでホスト OS のバージョンから適切なものを自動で利用してくれます。

Multi-arch の詳細は Docker 公式ブログを読めば大体理解できるはずです。

最近はちょいちょい Server Core や Nano Server に対応したイメージを見かけますね。

イメージに含まれているプラットフォームを確認する方法も用意されているので、例えば .NET Framework 4.7.2 の SDK イメージの情報を確認したい場合には以下のコマンドを使います。

docker run --rm mplatform/mquery microsoft/dotnet-framework

実行すると、含まれているプラットフォームの一覧が出力されます。

f:id:shiba-yan:20180810133852p:plain

Windows Server の LTSC 2016 / Version 1709 / Version 1803 それぞれに対応していることがすぐにわかります。1709 からはリビジョンの制約がなくなっているので、気にせず使えるのはメリットです。

ビルド済みのイメージに対して docker history を実行すると、ベース OS のバージョンが確認出来ます。

f:id:shiba-yan:20180810141512p:plain

バージョンを気にすることなくイメージを取得して実行できるのは便利なのですが、同じイメージ名なのに落ちてくるものは環境依存となるので、開発やビルド、実行時の OS が異なると簡単に事故ります。

特に実際にイメージを作成する CI 部分と実行環境のバージョン違いは致命的なので、よく使われそうなサービスに関して情報を軽くまとめておきます。

CI SaaS

サービスの大半は LTSC 2016 が使われているので、開発環境が Windows 10 の場合はビルド番号が大きく異なっているはずです。

  • ACR Build
    • Version 1803 (Hyper-V)
  • AppVeyor
    • LTSC 2016
  • AWS CodeBuild
    • LTSC 2016
  • Visual Studio Team Services
    • LTSC 2016

ACR Build に関してはホスト OS が 1803 ですが、Hyper-V Containers が使われているので LTSC 2016 と Version 1709 のイメージも問題なく扱えるようになっています。

実行環境

数少ない実行環境ですが、こっちも大半が LTSC 2016 が使われています。2019 リリース待ちですね。

  • Web App for Containers (Windows)
    • LTSC 2016
  • Azure Container Instances
    • LTSC 2016
  • Azure Kubernetes Service
    • Version 1709 or 1803

AKS に関しては前に試した時は 1709 が使われていましたが、最近の ACS Engine を見ると 1803 にも対応しているようだったので、両方とも記載しています。Hyper-V Containers ではないので非常に厄介です。

(現時点での)最適なイメージ

現時点での結論としては、使用するイメージは全て LTSC 2016 に統一し、Dockerfile でも明示的に OS バージョンを指定することをお勧めします。

指定していない場合はホスト OS からイメージのバージョンが決定されるので、例えば ACR Build を使うと自動的に 1803 が使われることになります。

# .NET Framework の場合
FROM microsoft/dotnet-framework:4.7.2-sdk-windowsservercore-ltsc2016

# ASP.NET の場合
FROM microsoft/aspnet:4.7.2-windowsservercore-ltsc2016

例外として AKS で Windows Containers を使う場合には、既存の CI SaaS の大半が未対応となってしまうので、ACR Build しか組み合わせる選択肢が存在しないことになります。

2019 リリース後には LTSC 2016 からの移行が発生するであろうこともお忘れなく。

Windows Server Version 1709 から Windows Server Containers のバージョン要件が変更されていた

これまで何回か Windows Containers を触ってきましたが、大体は Windows Server Containers の場合はバージョンを合わせるのが面倒なので、Hyper-V Containers を使おうという方向に落ち着いてました。

ビルド番号が異なる場合には起動がブロックされますが、リビジョン違いは起動はするものの本番環境でサポートは行わないという鬼のような要件です。

なので、使うには Hyper-V Containers を使って完全に分離してあげないと、と思ってました。

最近、何となくドキュメントを眺めていたら Windows Server Version 1709 からはバージョン周りの要件が変更されていて、リビジョンは一致していなくても本番環境でサポートされるようになったみたいです。

正直なところカーネルを共有してるので、この制限は撤廃されないだろうなと考えてました。

ドキュメントによると 1709 からはコンテナホストとリビジョンまで合わせる必要はないが、最新のアップデートを適用しておくのをお勧めしますということのようです。

For Windows Server 2016 based hosts/images – the container image’s revision must match the host to be in a supported configuration. Starting with Windows Server version 1709, this is no longer applicable, and the host and container image need not have matching revisions. It is as always recommended to keep your systems up-to-date with the latest patches and updates.

Windows Container Version Compatibility | Microsoft Docs

LTSC での運用はほぼ不可能じゃないかと思ってましたが、アップデートを重ねることで何だかんだで実用的になってきているなと感じました。

運用ではコンテナホストのアップデートを考える必要がありますが、上のドキュメントには Kubernetes の nodeSelector を使って一致するバージョン上で Pod を実行させるための方法も載っています。しかし、肝心のノードを追加する方法が今の AKS には用意されていないので、単純にクラスタを作ると詰みます。

一応は対応予定ではあるらしいので、追加できるようになると新しい Windows Server のビルドをノードとして追加して、nodeSelector を使って切り替えるだけでアップデートが完了するわけです。

CI 周りも VSTS / AppVeyor / CodeBuild 以外にも、Codefresh という Kubernetes 向けサービスが Windows Containers にベータですが対応を始めているので、去年から割と整ってきた感じがあります。

Codefresh は結構面白そうなので、Windows Containers のサポートを試して見ようかと思います。

AWS CodeBuild が Windows に対応したらしいので試した

Twitter を眺めていたら AWS CodeBuild が一部のリージョンで Windows に対応したとあったので、早速オレゴンで試して見ることにしました。

Windows が使えるビルドサービスは VSTS か AppVeyor ぐらいしか選択肢がなかったので貴重です。

AWS CodeBuild Adds Support for Windows Builds

Microsoft Windows Samples for AWS CodeBuild - AWS CodeBuild

buildspec.yml は特に Windows だから変わっている部分はないみたいです。新しいビルドプロジェクトを作成する時に OS や利用するランタイムを選べるようになっています。

AWS 管理のイメージも用意されていますが、Docker Hub で公開されている任意のイメージも利用できるようになっているみたいです。これは地味に良さそう。

f:id:shiba-yan:20180527152305p:plain

ビルドした結果は S3 に保存できるので、結構扱いやすいです。実際に ASP.NET アプリケーションをビルドして、WebDeploy パッケージを作成してみましたが、すんなりと S3 に保存されてました。

f:id:shiba-yan:20180527204658p:plain

試した感じでは、まだコンテナを利用した Windows アプリのビルド環境が未成熟という印象を持ちましたが、AppVeyor の地位危うしという感じがします。

やはり毎回 init で必要なパッケージをインストールしたりするのは時間の無駄ですからね。

f:id:shiba-yan:20180527210045p:plain

CodeBuild の場合は利用するコンピューティングタイプを選択できるのも良いです。Windows Containers 自体が重いのもありますが、選択肢があるというのは重要です。

CodeBuild 提供の Windows イメージ

CodeBuild で提供されているイメージは msbuild へのパスが通っていないので、そのままだとビルドが通らないので地味に厄介です。フルパスで msbuild を指定しないとエラーになります。

f:id:shiba-yan:20180527152338p:plain

ライセンス情報を見る感じでは .NET 4.6.2 までしか Targeting Pack が入っていないみたいなので、4.7 以降をターゲットにしたアプリケーションはビルド出来ないかも知れません。

AWS CodeBuild for Windows—Third Party Notices - AWS CodeBuild

.NET Core についても、パッと見た感じでは 2.0 しか SDK が入っていないように見えます。

それ以外のバージョンをビルドする場合には専用の Docker Image を用意するか、Microsoft から公式リリースされているものを使った方が良さそうです。

.NET Command Line Tools (2.1.103)

Product Information:
 Version:            2.1.103
 Commit SHA-1 hash:  60218cecb5

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.1.103\

Microsoft .NET Core Shared Framework Host
  Version  : 2.0.6
  Build    : 74b1c703813c8910df5b96f304b0f2b78cdf194d

.NET Core の場合は Nano Server ベースのイメージを使えるのでプロビジョニングの短縮が図れます。

カスタムイメージを作成する

Microsoft から提供されているイメージは何故か WebBuildTools がインストールされていないので、ASP.NET アプリケーションのビルドには微妙に問題が出ます。

予めパッケージをインストールしておきたいという要望もあるはずなので、AWS Blog に書いてあるようにカスタムなイメージを作成して対応しましょう。

Extending AWS CodeBuild with Custom Build Environments for the .NET Framework | AWS DevOps Blog

こっちはちゃんと WebBuildTools をインストールするコマンドになっているので、ASP.NET アプリケーションのビルドも問題ありません。

pull されたイメージはキャッシュされる

条件はよくわかっていませんが、同じイメージを使っている場合にはキャッシュが効いて素早くビルドまで実行できるようになっていました。

単純に同じホストに割り当てられた場合のみなんでしょうが、Server Core のイメージは重いので重要です。

f:id:shiba-yan:20180527160521p:plain

欲を言えば、予めよく使われそうなイメージは pull 済みになっておいて欲しいですね。

LTSC 2016 のみ実行可能

.NET Framework SDK の Docker Image は LTSC 2016 以外に 1709 / 1803 が提供されているので、そのイメージを利用してビルドを実行してみましたが、コンテナが立ち上がることなく失敗しました。

f:id:shiba-yan:20180527155527p:plain

なので Hyper-V Containers ではなくて Windows Server Containers を利用していることが分かります。

軽量化された Docker Image は CodeBuild では暫く使えそうにないですね。残念ながら Windows Server 2019 のリリースを待つしかなさそうです。