しばやん雑記

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

Kubernetes で .NET アプリケーションな Job を動かしてみる

Kubernetes には Job という機能があることを知ったので、Windows Server Containers でも試してみました。使いどころはわかってないですが、Windows でも動くことを確認したかったのです。

動き続けるタイプの Job を作りたい場合には、これまで通り Deployment と Replica Set を使った方が良さそうです。Job のイメージ定義は immutable なので後から変更できません。

とりあえず適当にコンソールアプリケーションを作成します。標準出力にメッセージを書くだけのアプリケーションをサクッと用意してビルドします。

using System;
using System.Threading;

namespace JobApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello Job : " + DateTime.Now);
        }
    }
}

このアプリケーションをイメージ化するために用意した Dockerfile は以下の通りです。ENTRYPOINT としてビルドしたアプリケーションを指定して、実行が終わったらコンテナも終わるようにします。

FROM では .NET Framework 4.6.2 のイメージを明示的に指定しておきます。ちなみに microsoft/dotnet は .NET Core を使うイメージなので注意。

FROM microsoft/dotnet-framework:4.6.2

COPY app /app

WORKDIR /app

ENTRYPOINT ["JobApplication.exe"]

作成したイメージを Docker Hub に push し、Kubernetes にデプロイするための YAML を用意します。ダッシュボードから作れないので YAML を用意する必要がありました。

最重要ポイントとして nodeSelector で os を windows として明示的に指定しないと、Linux で動作しているマスターにデプロイされて詰みます。

apiVersion: batch/v1
kind: Job
metadata:
  name: dotnet-job
spec:
  template:
    metadata:
      name: dotnet-job
    spec:
      containers:
      - name: dotnet-job
        image: shibayan/dotnet-job
      restartPolicy: OnFailure
      nodeSelector:
        beta.kubernetes.io/os: windows

これに気が付かずに何回も pull 時にエラーを出してしまって悩みました。デプロイメントを作成した時に上手くいった理由はよくわからないですが、Windows Server Containers を使うので指定は必要だと思います。

YAML を作成したら Kubernetes のダッシュボードからアップロードするだけです。

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

デプロイが完了すると Job が実行されて、ログが見れるようになります。

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

とりあえず Windows Server Containers でも動作はしましたが、再実行するには Job の再デプロイが必要なので、いまいち使いどころがやっぱりわかりませんでした。

ちょっと前に追加された Cron Jobs の方が使いどころが多いと思いました。

しかし、現在の Azure Container Service で作られた Kubernetes では有効化されていないので、残念ながらデプロイのタイミングで検証エラーになってしまいます。

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

acs-engine を使えばカスタマイズ出来そうですが、それはまた今度試してみることにします。

nodeSelector について学べたので、今回はそれで良しとしておきます。

AppVeyor から Azure Container Service 上の Kubernetes へ自動でデプロイを行う

これまで AppVeyor と Kubernetes でやってきたことの集大成として、AppVeyor から Azure Container Service 上の Kubernetes への自動デプロイを試してみます。

やってきたことは以下のエントリを参照してください。Kubernetes はかなり良いですね。

AppVeyor から Kubernetes にデプロイするためには、認証情報が必要なので先に作成しておきます。

Service Account Token を取得

トークンを作成するためには kubectl を使って Service Account をまず作成する必要があります。

kubectl create serviceaccount appveyor

コマンドで Service Account を作成したら、ダッシュボードでトークンを確認できるので簡単です。

コンフィグの中にあるシークレットから、Base64 デコード済みのトークンをコピーできます。

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

作成はコマンドで作りますが、削除と編集はダッシュボードから簡単に行えるのがアンバランスでした。

appveyor.yml に処理を追加

現在の Kubernetes ダッシュボードは以下のようになっています。基本的にアップデートを行うだけなので、予めアプリケーションはデプロイしてあります。

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

appveyor.yml には kubectl のダウンロードと、イメージの更新コマンドを追加します。

version: '{build}'

image: Visual Studio 2017

environment:
  DOCKER_USER: XXXXX
  DOCKER_PASS:
    secure: XXXXX
  DOCKER_IMAGE_NAME: shibayan/appveyor-aspnet-docker:$(APPVEYOR_REPO_BRANCH)-$(APPVEYOR_BUILD_NUMBER)
  KUBE_MASTER: https://XXXXX
  KUBE_TOKEN:
    secure: XXXXX

install:
  - curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.6.2/bin/windows/amd64/kubectl.exe

before_build:
  - nuget restore

build_script:
  - msbuild .\AspNetApplication\AspNetApplication.csproj /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="..\publish";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release
  - docker build -t %DOCKER_IMAGE_NAME% .

test_script:
  - ps: |
      $cid = docker run -d $env:DOCKER_IMAGE_NAME
      $hostip = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" $cid
      curl "http://${hostip}" -UseBasicParsing

deploy_script:
  - docker login -u %DOCKER_USER% -p %DOCKER_PASS%
  - docker push %DOCKER_IMAGE_NAME%
  - kubectl set image deployment/aspnet aspnet=%DOCKER_IMAGE_NAME% --server=%KUBE_MASTER% --token=%KUBE_TOKEN% --insecure-skip-tls-verify=true

kubectl は curl でダウンロードするだけなので簡単です。現在のデプロイメントに対してイメージを更新するコマンドは kubectl set image になります。

オプションとして --server と --token を追加して、Kubernetes のマスターの URL とさっき作成したトークンを渡します。証明書の検証が問題になるので --insecure-skip-tls-verify=true を指定してスキップします。

AppVeyor で実際にビルド

appveyor.yml の修正を GitHub にコミットして、実際に AppVeyor でビルドを行いました。kubectl の実行結果から成功したことが分かります。たったこれだけでアップデートが行われます。

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

アップデート中にダッシュボードを確認すると、新しいレプリカセットとポッドが作成されています。

既にベースイメージは pull されているので、コンテナの起動は非常に高速です。

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

アップデートが完了したら、ブラウザで外部エンドポイントにアクセスしてみます。アプリケーションに違いがないと、本当にアップデートが行われたのか確認しにくいので、テキストを少しだけ変えておきました。

例によって Razor のプリコンパイルを行っていないので、起動に少し時間がかかりますが表示されます。拍子抜けするぐらいあっさりと AppVeyor からのデプロイが行えました。

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

ちなみに GitHub に push してから AppVeyor でのビルドが完了するまで 2 分ほどです。Docker Hub よりネットワーク的に近い Azure Container Registry を使うと pull は早くなりそうです。

今回は 1 エージェントかつ 1 レプリカで試しましたが、Kubernetes はまだ VMSS に対応していないみたいなので、エージェントを増やす手間がかかります。レプリカに関しては別の機会にまとめます。

Azure Container Service を使って Kubernetes と Windows Server Containers を試してみる

気が付いていなかったのですが、Azure Container Service のオーケストレータとして Kubernetes を選ぶと、Windows が選択できるようになっていたらしいです。もう 2 か月ぐらい前というのが驚きです。

まだ Public Preview になってないだろと思っていたら、ちゃんとドキュメントもありました。確か Swarm が Private Preview だったはずなんですが、いったいどこに行ってしまったのやら。

Windows Server Containers が使えるコンテナサービスを待望している人間としては、これは試しておかないといけないと思ったので、実際にクラスタを作成して試します。

Kubernetes クラスタを作成

Azure Portal から ACS を作成する際に Kubernetes を選ぶのは必須です。

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

例によって Japan East は VM が高いので、少し安い Japan West を選んでおきました。

次に Kubernetes のマスター向け設定を行います。マスターは Linux で動作するので SSH の公開鍵が必要です。そして Kubernetes が Azure のリソースを操作するために必要なサービスプリンシパルも設定します。

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

サービスプリンシパルは Azure CLI 2.0 を使うと簡単に作れます。*1

最後はエージェントの設定なので、ここで Windows を選びます。Windows を選ぶと RDP で接続するための認証情報を入力する必要があるので、適当に埋めておきます。

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

VM のサイズは中途半端にけちると苦労するので、デフォルトのままにしておきました。これでデプロイすると、Windows Server Containers 対応の Kubernetes が上がってきます。

Kubernetes への接続

Kubernetes への接続を行うためには kubectl と認証情報のダウンロードが必要らしいです。幸い Azure CLI 2.0 を使うと簡単にダウンロード出来るので、ドキュメントを紹介しておきます。

手元の PC には Azure CLI 2.0 を入れていなかったので、手動で kubectl をダウンロードして認証情報も SCP で拾ってきました。kubectl は単一の実行ファイルなので楽ですね。

kubectl と認証情報の設定が終わったので、接続テストとしてノードの情報を確認しておきました。

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

マスターとエージェントの情報が返ってきたので、認証情報に問題がないことが確認できました。

最初からコマンドで全て操作するのはしんどいので、Kubernetes のダッシュボードを使います。kubectl の proxy コマンドを使うとダッシュボードへの接続が行えます。

kubectl proxy

表示されたアドレスにブラウザでアクセスすると、Kubernetes のダッシュボードが表示されます。

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

Azure Container Service のドキュメントでは YAML ベースでデプロイを行っていましたが、折角なのでダッシュボードを使って試します。

アプリケーションをデプロイ

Kubernetes の用語については説明を省きますが、基本的に画面の指示通りに進めていけば問題ないです。

ナビゲーションからノードを選択すると稼働中のインスタンスとメトリックを確認できます。ECS はメトリックの表示に対応していなかったですが、Kubernetes はちゃんと確認することが出来て良いですね。

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

それはともかく、まずはアプリケーションをデプロイしてみます。デプロイするのは前回 AppVeyor で作成した ASP.NET MVC アプリケーションのイメージです。

JSON / YAML のアップロードも出来ますが、UI でポチポチ設定してみます。

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

重要な設定としてはサービスを外部にして、ターゲットポートを 80 に設定することです。

外部にしないと外からアクセス出来なくなりますし、作成したイメージは 80 でリクエストを待機しているので、ターゲットポートを間違えると繋がりません。

実際に配備を行うと、最初は例によってイメージの pull で時間がかかりました。これは ACS で使われている Windows Server 2016 のイメージが古いのが最大の原因だと思われます。

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

使われている Windows Server 2016 と Docker のバージョンは AppVeyor と比べて古いものだったので、公式らしく最新に追従する姿勢を見せてほしいところです。

ワークロードを選択すると、一度にステータスの確認が出来るので便利です。サービスを含め全てが緑色になればデプロイ完了なので、外部エンドポイントが表示されるようになります。

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

この外部エンドポイントは Azure Load Balancer を指しています。Load Balancer と Public IP Address は Kubernetes によって自動的に作成されていました。

外部エンドポイントにアクセスすると、前回作成した ASP.NET MVC アプリケーションが表示されました。

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

前に試した ECS よりも Kubernetes の方が Windows Server Containers への対応が進んでいる印象を受けました。まだ対応自体はプレビューですが、ダッシュボードから操作が行えるのは好印象です。

そろそろ Azure Container Service も Managed Disk を使って、いい感じにリソースを整理して欲しい気持ちでいっぱいです。あとベースイメージのバージョンアップは必須だと思います。

AppVeyor を使って ASP.NET MVC アプリケーションの Docker Image を自動で作成してみる

前に AppVeyor の Windows Server 2016 と Docker 対応について調べたので、今回は実際に ASP.NET MVC アプリケーションをビルドして、Docker Image の作成までを行ってみます。

やることは CircleCI と Docker を使って App Service on Linux への CI/CD を試した時とほぼ変わりません。処理をそのまま AppVeyor と ASP.NET 向けに修正した形です。

Windows Server Core のイメージが 10GB ぐらいあるので、ビルドにも時間がかかって使い物にならないかもと思いましたが、今回の場合 2 分以下で終わりました。実用的な気がしてきます。

最初に ASP.NET MVC アプリケーションを作成してから、appveyor.yml を書いていきます。

appveyor.yml を用意する

とりあえず作成した appveyor.yml を全て載せておきます。build / test / deploy のそれぞれでカスタムなスクリプトを書いていますが、やっていること自体は大したことはありません。

テストでは作成したイメージを起動して、ちゃんとレスポンスを返すか確認しています。*1

version: '{build}'

image: Visual Studio 2017

environment:
  DOCKER_USER: XXXXXXX
  DOCKER_PASS:
    secure: XXXXXXX

before_build:
  - nuget restore

build_script:
  - msbuild .\AspNetApplication\AspNetApplication.csproj /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="..\publish";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release
  - docker build -t shibayan/appveyor-aspnet-docker .

test_script:
  - ps: |
      $cid = docker run -d shibayan/appveyor-aspnet-docker
      $hostip = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" $cid
      curl "http://${hostip}" -UseBasicParsing

deploy_script:
  - docker login -u %DOCKER_USER% -p %DOCKER_PASS%
  - docker push shibayan/appveyor-aspnet-docker

面倒なのが IP を inspect で取得する部分です。これはしばらく改善しなさそうなので諦め気味です。

用意した Dockerfile は以前に試したものとほぼ変わらず、wwwroot 以下にビルドした成果物をコピーするだけの簡単なものです。ベースイメージは 4.6.2 とタグを固定しました。

FROM microsoft/aspnet:4.6.2

COPY ./publish/ /inetpub/wwwroot

MSBuild によってビルドされたファイルは publish というディレクトリに全て保存されるので、その中身をそっくり wwwroot にコピーしています。

イメージのテストが通れば、後は Docker Hub に push するだけという簡単な処理です。

AppVeyor で実際にビルド

GitHub にリポジトリを作成して、AppVeyor で実際にビルドしてみました。事前に調べていた甲斐もあって、特に失敗することなく Docker Hub への push まで上手くいきました。

MSBuild 周りが一番複雑な部分ですが、App Service で良く使っていたコマンドなので問題なかったです。

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

MSBuild が終わると Docker Image を作成します。microsoft/aspnet の pull に 1 分ぐらいかかるので、予め pull しておきたい気持ちでいっぱいです。

Docker Image の作成が終わると、実際に起動してテストを行っています。

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

今回のコマンドでは Razor 周りのプリコンパイルまでは行っていないので、起動するまでに少し時間がかかりますが、それでも数秒で成功します。

最後に Docker Hub に push して完了ですが、例によって Azure Container Registry などに push する形にしても良いかと思います。特に ACS を使う場合には便利だと思います。

作成したイメージの動作確認

AppVeyor でのビルドが成功すると Docker Hub に作成されたイメージが追加されているので、検証用の Windows Server 2016 が入ったサーバーを使って動作確認をしておきます。

テストで利用しているコマンドとほぼ同じものを実行すると、Docker Hub から pull してくれます。

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

一応 PowerShell 上でも同じように URL を叩いてレスポンスが返ってくるのを確認しました。

コンテナホストからは確認できたので、外部の PC からブラウザを使って確認してみました。外部からのアクセス時には WinNAT によってポート変換がされるので 8080 が必要です。

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

すんなりと ASP.NET MVC アプリケーションが動作しました。コンテナとホストでビルド番号を合わせる必要があるという手間はかかってきますが、手軽にアプリケーションを実行できるのは魅力です。

Azure Container Service や EC2 Container Service などの Windows Server Containers に対応した環境にそのまま持っていけるはずですが、まだ正式リリースされていないのが課題という感じです。

*1:個人的にはちゃんと動かないイメージを生焼けと呼んでます。どうでもいい話だった。

AppVeyor が Windows Server 2016 に対応したので Docker を使うための下調べをする

2 か月ほど前になりますが、AppVeyor が Visual Studio 2017 と Windows Server 2016 が使えるワーカーイメージの提供を開始しました。

AppVeyor に限らず Windows Server 2016 対応はどのサービスも遅い気がしますね。

既に試している人もいますが、Docker 周りの状況も当時と変わってきているので改めて調べます。

最終的な目標は AppVeyor 上で ASP.NET アプリケーションの Docker Image をビルドして、Docker Hub や Azure Container Registry にプッシュして、ECS や ACS で使うことです。

Windows Server Containers の場合はベースイメージが大きいので、実用的かどうか含め調べます。

インストールされている Docker

まずは AppVeyor VM 上の Docker を確認するために docker info を実行してみました。ちなみに有料プランを契約しているので、ちょっと良い VM が用意されている状態です。

Windows Server 2016 の最新バージョンが使われていて、Docker のバージョンも 17.03.1-ee と新しいものがインストールされています。

> docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 2
Server Version: 17.03.1-ee-3
Storage Driver: windowsfilter
 Windows: 
Logging Driver: json-file
Plugins: 
 Volume: local
 Network: l2bridge l2tunnel nat null overlay transparent
Swarm: inactive
Default Isolation: process
Kernel Version: 10.0 14393 (14393.1066.amd64fre.rs1_release_sec.170327-1835)
Operating System: Windows Server 2016 Datacenter
OSType: windows
Architecture: x86_64
CPUs: 2
Total Memory: 1.464 GiB
Name: APPVYR-WIN
ID: MNR6:QBA5:QSF7:TNY2:G63V:WXA6:4TXI:CP5T:5B3B:TFBF:JLUV:LQVB
Docker Root Dir: C:\ProgramData\docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false

Windows Server 2016 の 4 月アップデートが適用されているので、Docker Swarm のサポートが改善していたりします。詳細は KB4015217 やブログに載ってました。

Improved the Host Network Service (HNS) to support an overlay network driver for use on Windows Server 2016 to connect containers across hosts using Docker Engine in Swarm Mode.

https://support.microsoft.com/en-us/help/4015217/windows-10-update-kb4015217

Docker Swarm を CI で使うかと言われると使わないですが、最新に追従する姿勢は好感が持てます。

pull 済みのイメージ

docker images を実行して、既に pull されているイメージがあるのか確認してみました。Azure と同様に windowsservercore と nanoserver の最新バージョンが、予め pull されていました。

> docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
microsoft/windowsservercore   latest              590c0c2590e4        2 weeks ago         10.1 GB
microsoft/nanoserver          latest              a943c29f0046        2 weeks ago         1.01 GB

AppVeyor の Platform Update を見ると、ワーカー VM の更新と同タイミングでイメージも更新してくれているようです。これはかなり安心して使うことが出来そうです。

tag は特定バージョンを指定せずに latest で問題なさそうです。これは良さそう。

別のイメージを pull する

pull されていない aspnet などのイメージを使う場合にどのくらい時間がかかるのかと、一度 pull したイメージはキャッシュされて次からすぐに使えるのかを確認してみます。

実際に docker pull microsoft/aspnet を実行したところ、1 分ほどで完了しました。

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

自宅の物理マシンで実行した場合は 30 秒ほどなので、そこまで極端に遅くはないという印象です。

同じ処理を 5 回繰り返しましたが、毎回 pull が実行されていたのでキャッシュに関してはあまり期待できない気がしました。運よく pull 済みのマシンに当たれば、素早く処理できるかも知れませんが未確認です。

Dockerfile からビルドする

適当に FROM だけ書いた Dockerfile を用意して docker build を実行してみました。

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

当然ながらすんなりと動作して、イメージが作られました。ここまで確認すれば、後は Container Registry に push するぐらいなので遊べるようになりそうです。

コンテナをデプロイして実行する

最後にド定番の .NET Core のサンプルアプリをデプロイして、サクッと実行してみました。

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

Nano Server ベースなので pull から実行まで 20 秒かかりませんでした。AppVeyor で楽しい Windows Server Containers 生活が送れそうな気が少ししてきました。

Surface Pro 4 に Windows 10 Creators Update をクリーンインストールしてみた

MacBook Pro の Boot Camp 環境だと Creators Update へのクリーンインストールが問題になるらしいので、仕方なくサブ環境の Surface Pro 4 を先に Creators Update に更新することにしました。

価格改定で安くなった時に買った Surface Pro 4 が活躍です。

Visual Studio 2017 のインストールとユーザープロファイルを作り直したかったので、クリーンインストールにしています。何となく USB ドライブ作ってますが、最近だと要らないのかも。

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

MSDN から ISO を落としておきましたが、同じものを Windows 10 のダウンロードページから落とせる気がします。ISO をマウントしてコピーするだけなので簡単です。

Surface Pro 4 のクリーンインストールする前に、必要なドライバーをダウンロードしておきます。検索したら、丁度 Creators Update と同じタイミングで新バージョンがリリースされていました。

Creators Update 向けにドライバも色々とアップデートされたみたいです。

割と未だにブルースクリーンが頻発するので、そろそろ改善して欲しい気がします。

Surface Pro 4

  • Surface System Aggregator Firmware (v103.1610.256.0) resolves screen brightness issue when devices come out of sleep.
  • Intel(R) Precise Touch Device (v1.2.0.83) improves system stability and performance.
  • Surface Integration (v1.1.121.0) adjusts system hibernation defaults.

ダウンロードセンターから Pro 4 の場合は SurfacePro4_Win10_15063_1701601_0.msi をダウンロードすれば良いみたいです。基本的に zip をダウンロードするより msi の方が簡単です。

Download Surface Pro 4 Drivers and Firmware from Official Microsoft Download Center

インストールを始めると、噂通り Cortana が喋ってきて少し煩かったので、途中からミュートにして進めました、セットアップ時に Wi-Fi に接続してアップデート確認とか昔はなかった気がしました。

大体 15 分ぐらいでインストールは終わった気がします。

f:id:shiba-yan:20170408195140p:plain:w700

ダウンロードしておいた Surface Pro 4 のドライバーをインストールすると、同時に UEFI もアップデートされました。なので再起動必須で少し時間がかかりました。

ドライバーインストール後 Wi-Fi が繋がると、更に Windows Update でドライバーがアップデートされたっぽいです。Windows Update の履歴が分類別になってわかりやすくなりました。

f:id:shiba-yan:20170408194941p:plain:w600

当然ながら Surface Pen / Type Cover も問題なく使えるようになってます。そろそろ Surface Dial 対応のアップデートとか来て欲しいですが、もう来ない気もしてきました。

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

まだ Visual Studio 2017 を入れたぐらいですが、トラックパッドのジェスチャーが多くなった気がします。

他にも High DPI 周りが良くなったらしいので、Surface Pro 4 を使って体験したいです。

Windows Server Containers で利用する Docker Image のバージョン管理が難しい

最近はまた Windows Server Containers の実運用に関して、いろいろと考えたり調べたりしているのですが、Windows では絶対に避けられないものとして Windows Update があります。

そして現時点での Windows Server Containers には以下のような要件があります。

Windows Server コンテナーは、ビルド番号が異なると起動をブロックされます (例: 10.0.14300.1030 (Technical Preview 5) と 10.0.14393 (Windows Server 2016 RTM))。 ビルド番号が一致し、リビジョン番号が異なる場合は、起動をブロックされません (例: 10.0.14393 (Windows Server 2016 RTM) と 10.0.14393.206 (Windows Server 2016 GA))。 ただし、技術的にはブロックされませんが、これは状況によっては正しく機能しない可能性がある構成であるため、運用環境ではサポートできません。

Windows コンテナーの要件 | Microsoft Docs

ドキュメントの内容を解釈すると、Windows Server Containers ではリビジョンまでホストとコンテナのバージョンを一致させないと、正しく動かない可能性があるし運用環境では実質使えないということでしょう。

ここで Docker の罠が出てくるわけです。普通に microsoft/windowsservercore と指定すると latest タグのイメージが落ちてくるので、リビジョンはその時の最新になり固定できません。現時点では面倒でもタグまでちゃんと指定するべきということなのでしょう。

# Windows Server Core のイメージを利用する
FROM microsoft/windowsservercore:10.0.14393.953

# IIS までインストールされたイメージを利用する
FROM microsoft/iis:windowsservercore-10.0.14393.953

# ASP.NET 4.6.2 までインストールされたイメージを利用する
FROM microsoft/aspnet:4.6.2-windowsservercore-10.0.14393.953

当然ながら Windows Update でリビジョンが変わった場合には、同様に変更する必要があります。

オンプレの仮想環境であれば、自分でバージョンやアップデートのタイミングをある程度決められるので latest でも良いかもしれないですが、Azure や AWS の場合は少し注意が必要そうです。

Azure の場合

Azure にある Windows Server 2016 with Containers を利用すると、予め Windows Server Core と Nano Server が pull された状態で起動することが出来ますが、タグが latest になっているので扱いにくいです。

最近の Windows Server Core だと多少は差分が利用されるみたいなので、5GB 落とし直しにはならないみたいですが時間はかかります。

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

Dockerfile で latest を指定したイメージを実行しようとすると、イメージを作成した時点での latest になるので奇跡的にバージョンが一致していないと pull し直しになってしまいそうです。

ある意味公式である Azure VM のイメージがこれだと、正直なところ不安になってきます。

AWS の場合

EC2 の Windows Server 2016 Base with Containers ではイメージが pull されていないので、そのまま ECS のベースイメージとして使うと全てのインスタンスで pull が走ってしまって時間がかかります。

予め pull 済みの AMI を作っておいた方が良さそうです。User Data で pull と sysprep までやらせます。

function Get-OSVersion() {
    $buildLabEx = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\').BuildLabEx
    $osBuild = $buildLabEx.Substring(0, $buildLabEx.IndexOf(".", 6))

    return "10.0." + $osBuild
}

docker pull microsoft/windowsservercore:$(Get-OSVersion)
docker pull microsoft/iis:windowsservercore-$(Get-OSVersion)
docker pull microsoft/aspnet:4.6.2-windowsservercore-$(Get-OSVersion)

docker pull microsoft/nanoserver:$(Get-OSVersion)
docker pull microsoft/iis:nanoserver-$(Get-OSVersion)

cd "$env:ProgramData\Amazon\EC2-Windows\Launch\Scripts"
.\InitializeInstance.ps1 -Schedule
.\SysprepInstance.ps1

ホストとコンテナのバージョンは合わせる必要があるので、頑張ってレジストリから取得するようにしています。ECS クラスターを作成するのは面倒だったので、普通に EC2 のインスタンスを立ち上げました。

docker images を実行すると、各ベースとなるイメージが pull 済みになっているのが確認できます。

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

AMI で用意されている Windows Server 2016 のバージョンと同じになっていることも確認できました。

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

課題としては Windows Update のタイミングでリビジョンが上がるという点ですが、こればっかりは Windows Update の時に AMI を作り直して入れ替えるぐらいしかない気がしました。

リビジョンをまで指定しないといけないのは非常に扱いにくいですが、カーネルを共有している以上仕方ない部分だと思いました。docker pull が賢くなればましになるかもしれないですが、今はこれで精いっぱい。

UWP アプリケーションの収益を PayPal で受け取ってみた

最近はちょくちょく UWP アプリケーションを開発してきて、そろそろ収益の受け取りについて調べないといけないと思ったのがきっかけです。PayPal で受け取りました。

色々と人に聞いたり、ネットで調べたりしたのですが情報が出てこなかったので、体験談を書いておきます。

PayPal ビジネスアカウントを開設

まずは支払いを受け取るための PayPal アカウントを開設しました。一応個人事業主ということになっているので、ビジネスアカウントを開設しておきました。

開設自体は必要な情報を入力して、本人確認書類(免許証とか)を送信すれば、PayPal 側で審査が行われて、後ほど書留でアクティブ化用のパスワードが送られてきます。

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

地味に時間がかかってしまうので、早めに開設した方が良さそうです。私の場合は 2 週間近くかかりましたが、全ての設定を終えていつでも収益を受け取れる状態になっています。

受け取りアカウントを PayPal に

Windows Developer Center の受け取りアカウント設定から PayPal のアカウントを追加します。

メールアドレスと名前を入力するだけなので簡単ですが、PayPal 側の表記に合わせておかないと、めんどくさいことになりそうな気がしています。

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

登録が終わればアカウント設定に情報が表示されます。支払い保留の設定はオフにします。

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

これで収益がしきい値を超えている場合には、毎月 15 日に支払処理が行われます。

収益の振り込みを確認

月初めの方に入金可能な収益が確定されるようになっています。そして 15 日になると実際に PayPal にて支払いが行われました。見た感じ手数料は引かれていないようです。

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

支払いが作成されても、実際に決済が行われて資金の移動が出来るようになるまで 3-5 営業日ぐらいかかることがあるようです。今回は 2 営業日で決済が完了しました。

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

実際に決済が行われましたが、手数料がかかっていないように見えます。

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

PayPal で支払いを受け取ったことはないですが、手数料がかからないのはちょっと怪しいです。

手数料としきい値について

Windows Store に有料アプリや、広告付きのアプリを公開している場合に大体最後の最後で苦労するのが、この収益の受け取りだと思います。単純に銀行振り込みを利用すると、場合によってはとんでもない手数料がかかることがあります。

受取りアカウントの設定と税関連の書類 - UWP app developer | Microsoft Docs

PayPal を利用した支払いの受け取りに場合には 4% の手数料がかかる*1ようですが、今回は 0 円表示ですし実際に引かれずに Dev Center の表示通りの額でした。

受け取ることが出来る収益のしきい値が決まっていますが、Windows Store での売り上げの場合は $25 から、広告収入の場合は $50 から受け取れるようになっています。

支払しきい値、方法、期間 - UWP app developer | Microsoft Docs

銀行振込の場合は Windows Store の売り上げは $200 以上ないと振り込まれないので、PayPal を使った場合にはすぐに受け取れる可能性があります。

とりあえずこの売り上げで明日は寿司を食べてこようかと思います。

HoloLens を使ってゲームが出来るのか試してみた

2017 年だし、ホログラフィック(自称)と名乗ってるし、このゲームは遊んでおかないといけないと思ったので、蛎殻町に放置されていた HoloLens を整備して試しました。

UWP 版の Remote Desktop アプリケーションを使って、ゲームをインストール済みのマシンに接続しました。Clicker が見つからなかったので、全ての操作をエアタップで行ったので腕がつりそうでした。

画面サイズはドラッグで自由に大きく出来るので、下の画像は 100 インチは超えてる気がします。

ちゃんと Remote Desktop でも音が出ます。少し帯域が足りない感じがしますが、Device Portal から Live Preview などを使わなければ割と頑張れました。

右クリックはゲームによっては出来ない場合がありそうなので、少し不便です。

App-V とかでアプリの画面だけを表示できれば、結構いい感じに遊べると思いました。キャラクターの声が耳元から聞こえてくるのは、やばさがあったと思います。HoloLens ネイティブのゲームを体験したい。

蛎殻町の若者やぶちぞう RD から Xbox One のゲームストリーミングが使えるのでは、と指摘を受けたので HoloLens を借りて自宅の Xbox One に接続してみました。

f:id:shiba-yan:20170215215832j:plain

スペック不足とか言われるのかと思いましたが、すんなりと本体が見つかってストリーミング接続できました。折角なので Forza Horizon 3 をプレイしてみました。

自宅の Wi-Fi は 11ac に対応しているので、思ったより遅延なくプレイできました。

アプリケーションは UWP にしか対応していないのが、正直もったいなく感じます。Unity は使える気がしないですが、UWP を使って HoloLens 向けのアプリケーションを作ってみたい気持ちになりました。

あと、やっぱり視野角が狭すぎると思うので、次のバージョンではもっと広くなって欲しいです。

AMI 向けに Sysprep 応答ファイルを使ってタイムゾーンとシステムロケールの設定を行う

Windows Server の AMI を作成する時に必要な sysprep を実行すると、デフォルトではタイムゾーンが UTC でシステムロケールが en-US になってしまうので、sysprep の応答ファイルを用意して変更してみます。

AWS のドキュメントに sysprep と応答ファイルについて記載があります。

SystemLocale と TimeZone を変更して、sysprep を実行すれば上手くいきそうな感じです。

EC2 の Windows Server AMI を使うと、デフォルトの応答ファイルが予め用意されているので、それに対して変更を行ってから sysprep をすることにします。User Data で実行したいので PowerShell で書きます。

PowerShell で名前空間付きの XML を扱う場合の方法は、以下のページを参考にしました。

Sysprep の応答ファイルは 2012 R2 と 2016 で特に変わっていないので、関数として切り出しておきました。実際に書いたコードは以下のような感じです。

function Update-SysprepAnswerFile($answerFile)
{
    [xml] $document = Get-Content $answerFile -Encoding UTF8

    $ns = New-Object System.Xml.XmlNamespaceManager($document.NameTable)
    $ns.AddNamespace("u", $document.DocumentElement.NamespaceURI)

    $settings = $document.SelectSingleNode("//u:settings[@pass='oobeSystem']", $ns)

    $international = $settings.SelectSingleNode("u:component[@name='Microsoft-Windows-International-Core']", $ns)
    $shell = $settings.SelectSingleNode("u:component[@name='Microsoft-Windows-Shell-Setup']", $ns)

    $international.SystemLocale = "ja-JP"
    $international.UserLocale = "ja-JP"

    $shell.TimeZone = "Tokyo Standard Time"

    $document.Save($answerFile)
}

XPath で応答ファイルの要素を引っ張ってきて、要素の値を書き換えて保存するだけのコードです。なんとなく SystemLocale だけではなく UserLocale も ja-JP に変更しておきました。

TimeZone の値は tzutil で指定するものと同じなので、少し書き方に注意したいです。

Windows Server 2012 R2 までは EC2Config を使って sysprep を実行しますが、Windows Server 2016 では EC2Launch を使う形に変更されたので、それぞれの OS 向けにスクリプトを変更して記載しておきます。

Windows Server 2012 R2

EC2ConfigService の中に存在する sysprep2008.xml が応答ファイルです。sysprep 自体も ec2config.exe にパラメータを渡して実行します。

function Update-SysprepAnswerFile
{
   # 省略
}

Update-SysprepAnswerFile "$env:ProgramFiles\Amazon\Ec2ConfigService\sysprep2008.xml"

# Execute sysprep
& "$env:ProgramFiles\Amazon\Ec2ConfigService\ec2config.exe" -sysprep

割とシンプルなスクリプトになりました。このスクリプトを実行して作成した AMI を使ってインスタンスを起動すると、システムログに設定した言語やタイムゾーンの情報が表示されています。

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

ちゃんと ja-JP Tokyo Standard Time になっています。

リモートデスクトップで接続して確認しても、設定が変更されていることが確認できました。

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

日本語言語パックをインストールしてから UILanguage を設定すると日本語にも出来る気がします。

Windows Server 2016

EC2Launch では PowerShell ベースになっているので、手順が少し変わっています。公式ドキュメントに sysprep の手順が書いてあるので、まずは熟読しておきました。

応答ファイルのパスも変わっているので、少し注意したいです。特に InitializeInstance.ps1 の Schedule パラメータを忘れないようにしたいです。ダメなイメージが完成します。

function Update-SysprepAnswerFile
{
   # 省略
}

Update-SysprepAnswerFile "$env:ProgramData\Amazon\EC2-Windows\Launch\Sysprep\Unattend.xml"

# Execute sysprep
cd "$env:ProgramData\Amazon\EC2-Windows\Launch\Scripts"
.\InitializeInstance.ps1 -Schedule
.\SysprepInstance.ps1

2012 R2 の時と同じようにイメージを作成して、新しくインスタンスを作成するとシステムログに設定した言語とタイムゾーンが表示されているので、変更されてるのが分かります。

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

そしてリモートデスクトップで接続して、システムロケールが変わっていることを確認しました。

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

システムロケールの設定は再起動が必要になるので自動化がいまいちやりにくい部分でしたが、sysprep を使って AMI を作成する場合では User Data を使って自動化が行えます。

以前書いた Elastic Beanstalk 向けの Custom AMI と同じように CI で作れそうです。

特に Elastic Beanstalk はベースとなる AMI を変更できないので、User Data を使って Custom AMI を作成しないと辛い部分が出てきそうです。ebextensions は割とすぐに限界が来ました。