しばやん雑記

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

Azure Pipelines から Run From Package としてデプロイする

そろそろ Azure Functions だけじゃなくて Web Apps でも Run From Package を使ったデプロイを行っていきたいので、ひとまず標準で Run From Package でのデプロイに対応している Azure Pipelines を使ってパイプラインを組んでみました。

Run From Package 自体は wwwroot 以下への書き込みが無ければ、問題なく Web Apps で使えます。該当の Issue はちょいちょい更新されてるので確認しておきます。

zip に Unicode のファイル名が含まれていると、マウント後にファイルがおかしくなることがあるようです。日本語を使っている場合は気を付けておいた方が良いでしょう。

アプリケーションパッケージをビルド

Run From Package は wwwroot に指定した zip を直接マウントする方式なので、zip のディレクトリ構造は wwwroot 以下に配置する時と同じ構造にしないとちゃんと動きません。

例えば ASP.NET や ASP.NET Core では zip の直下に bin や Web.config が必要だったりします。

ASP.NET Core や Azure Functions v2 の場合は Web Deploy を最初から使わない前提でタスクが作られていますが、ASP.NET の場合はそうはいかないので少し工夫が必要になります。もう少しそれぞれのビルドについて説明していくことにします。

ASP.NET の場合

これまでも ASP.NET アプリケーションを単一の zip にパッケージングすることは出来ましたが、Web Deploy 向けのフォーマットになっているので、今回の Run From Package では使えません。

なのでファイルシステムに対して書き出してから、zip にする処理を挟み込んで実現します。

ファイルシステムへの書き出しは pubxml を使うと簡単に設定できますが、MSBuild のオプションだけで済ませたかったので以下のようなオプションを用意して ASP.NET アプリケーションのビルドを行います。

/p:DeployOnBuild=true /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:publishUrl="$(Agent.TempDirectory)\publish\\" /p:PrecompileBeforePublish=true /p:EnableUpdateable=false

zip にすると読み込みのみになるので、更新不可能な形でビューのプリコンパイルを行っておきます。

基本的には Azure Pipelines にある ASP.NET 向けテンプレートを使えば良いですが、zip にする処理を挟む必要があるので、とりあえず参考までに YAML の定義を載せておきます。

queue:
  name: Hosted VS2017
  demands: 
  - msbuild
  - visualstudio
  - vstest

steps:
- task: NuGetToolInstaller@0
  displayName: 'Use NuGet 4.4.1'
  inputs:
    versionSpec: 4.4.1

- task: NuGetCommand@2
  displayName: 'NuGet restore'
  inputs:
    restoreSolution: '$(Parameters.solution)'

- task: VSBuild@1
  displayName: 'Build solution'
  inputs:
    solution: '$(Parameters.solution)'
    msbuildArgs: '/p:DeployOnBuild=true /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:publishUrl="$(Agent.TempDirectory)\publish\\" /p:PrecompileBeforePublish=true /p:EnableUpdateable=false'
    platform: '$(BuildPlatform)'
    configuration: '$(BuildConfiguration)'

- task: ArchiveFiles@2
  displayName: 'Archive Artifact'
  inputs:
    rootFolderOrFile: '$(Agent.TempDirectory)/publish'
    includeRootFolder: false

- task: VSTest@2
  displayName: 'Test Assemblies'
  inputs:
    testAssemblyVer2: |
     **\$(BuildConfiguration)\*test*.dll
     !**\obj\**
    platform: '$(BuildPlatform)'
    configuration: '$(BuildConfiguration)'

- task: PublishSymbols@2
  displayName: 'Publish symbols path'
  inputs:
    SearchPattern: '**\bin\**\*.pdb'
    PublishSymbols: false
  continueOnError: true

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'
    ArtifactName: '$(Parameters.ArtifactName)'

このパイプラインを実行すると Web Deploy 形式ではない、シンプルな zip が生成されます。

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

ここで生成した zip はこの後に作成する Release パイプラインから利用します。牛尾さんから Build と Release はちゃんと分けてパイプラインを作った方が良いと聞いていたので、ちゃんと分離しておきます。*1

ASP.NET Core の場合

先ほど少し書いたように、ASP.NET Core は Web Deploy に依存しない形でタスクが用意されているので、Azure Pipelines から簡単に必要なパッケージを作成することが出来ます。

基本的には ASP.NET Core 向けのテンプレートを利用するだけで良いです。

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

.NET Framework で ASP.NET Core を書いてるケースは非常に少ないと思いますが、似たようなテンプレート名になっているので間違えないようにします。

作成されたパイプラインを見ると、publish で zip にするチェックが入っているはずなので確認します。

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

このパイプラインを実行すると、同様にシンプルな zip が生成されるのでこれで終わりです。

Azure Functions v2 の場合

Azure Functions v2 は .NET Core を使って作られているので、ビルド自体は ASP.NET Core のテンプレートで作られるパイプラインとほぼ同じですが、Web プロジェクトではないので、設定を変更しておきます。

デフォルトでは Publish Web Projects にチェックが入っているので、それを外しておきます。

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

これで ASP.NET Core の時と同様に zip が生成されますが、テンプレートではビルドするプロジェクトの指定が **/*.csproj になっているので、ビルドするリポジトリに複数のプロジェクトを含んでいる場合は、予めプロジェクト名を明示的に指定しておきます。

Run From Package としてデプロイ

ここまででビルドと zip の生成まで完了しているはずなので、後は Release 用のパイプラインを作成すれば終わりです。単純に App Service にデプロイする場合は 1 タスクだけで済むので非常に簡単です。

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

Run From Package に対応しているのは 4.* のプレビュー版になるので、サクッと変更しておきます。

下の方にある Additional Deployment Options を開くと、デプロイ方法を指定出来るようになっているので、ドロップダウンから Run From Package を選べば完了です。

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

ついでに Continuous deployment trigger の設定をしておくと、指定したブランチがビルドされた後に自動的に Release 用のパイプラインを実行出来ます。

他にも Pre-deployment conditions からスケジュールやユーザーの承認が必要などといった設定も出来ますが、今回の話とは関係ないので省略します。Release はいろいろと面白い使い方が出来そうです。*2

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

実際にこれまで Kudu でビルドさせていたアプリを Azure Pipelines + Run From Package でのデプロイに変更してみました。ASP.NET Core なのでビルドパイプラインはデフォルトのままです。

https://appservice.info/

Azure Functions を使っている場合は Run From Package は必須で、それ以外の ASP.NET / ASP.NET Core でもデプロイでの一貫性を担保するためにも利用を検討した方が幸せになれると思います。

ロールバックもマウントしている zip を差し替えるだけなので Web Deploy よりも高速です。

*1:一部のテンプレートはデプロイまで行ってしまうものがある

*2:複数の App Service へのデプロイとか、事前にステージングにデプロイして承認後に本番デプロイとか