【Azure Pipelines】WebAppsへDeployするためのyamlファイルを徹底解説【初心者向け】

こんにちは、サイオステクノロジーの佐藤 陽です。

今回は AzureDevOpsのサービスであるAzurePipelinesに関する記事です。

ASP.NET CoreのアプリケーションをAzureのWebAppへデプロイするCI/CDの流れを徹底解説したいと思います。
  • CI/CD始めてみたいけど、よくわからない…
  • yamlファイル、何書かれているか全然分からない…
という方は是非最後までご覧ください!
Azure Functions編も書きました!

【Azure Pipelines】Azure FunctionsへDeployするためのyamlファイルを徹底解説【初心者向け】

はじめに

今回はAzurePipelinesを利用し、ASP.NET CoreのアプリケーションをAzureのWebAppへDeployしていきます。

Pipelines上にテンプレート等も用意されており、一見親切な作りにはなっているのですが
yamlファイルを見たときに「?」となり、何をどうすればいいのかわからない人もいるかと思います。

実際自分がそうでした。

そういった人に向けて、基本のyamlファイルの内容を丁寧に解説していきたいと思います。

事前準備

今回の構成としては以下の内容です。
  • .NET 6
  • ASP.NET Core6
  • Azure Repos
  • Azure Pipelines
  • Azure WebApp
今回、デプロイするASP.NET Coreのアプリケーションは以下のコマンドで作成しておきました。
dotnet new mvc -n siostech

作成したプロジェクトをReposのリポジトリにpushしておきましょう。

また、デプロイ先のWebAppも作成しておきます。
Freeプランで問題ないので、環境を.NET 6として作成しましょう。

Pipeline構成

早速Pipelineを作成していきます。
まずは、ソースコードの置き場所を選択します。

次に対象のリポジトリを選択していきます。

次にテンプレートを選択します。

  • ASP.NET
  • ASP.NET Core(.NET Framework)
といったものが用意されていますが
今回は.NETを対象とするASP.NET Coreのアプリケーションをデプロイするので対象外です。

今回はStarter pipelineを選択し、自力で書いていきます。

 

といっても、ゼロベースで書いていくのはつらいので、今回はサンプルコードを以下に置いておきます。
今からこの内容を徹底敵に解説していきたいと思います。
trigger:
  - master

pool:
  vmImage: 'windows-2022'

variables:
  buildConfiguration: 'Release'
  framework: 'net6.0'
  targetRuntime: 'win-x86'
  selfContained: 'false'

steps:
- task: UseDotNet@2
  displayName: 'Install .NET SDK'
  inputs:
    version: 6.0.x

- task: DotNetCoreCLI@2
  displayName: 'Publish'
  inputs:
    command: publish
    modifyOutputPath: false
    arguments: '-c $(buildConfiguration) -f $(framework) -r $(targetRuntime) --self-contained $(selfContained)'
    zipAfterPublish: true

- task: AzureRmWebAppDeployment@4
  inputs:
    ConnectionType: 'AzureRM'
    azureSubscription:  '{Service Connection Name}'
    appType: 'webApp'
    WebAppName: 'app-sios-techblog'
    package: '$(Pipeline.Workspace)/**/*.zip'

yaml解説

まずそもそもなんですが、yamlファイルで書かれていることは決して特別な内容ではありません。

サーバー上に実行環境を用意し、そのうえでdotnetコマンド等を順々に実行しているだけです。

そのため基本的には、手元(ローカル)の環境で実行しているコマンドを、yamlファイルに書き起こしていくだけです。
個人的には、この事に気づくことで一気にyamlが読みやすくなった気がします。

それでは順を追って見ていきましょう。

 

trigger

trigger:
  - master
ここはPipelineのトリガー部分です。
triggerのパラメータに指定したブランチにpushがあった場合にPipelineが実行されます。
今回であれば、masterにpushがあった場合にPipelineが実行されるよう設定します。

 

pool

pool:
  vmImage: 'windows-2022'
ここはランタイム環境を指定する部分です。

自分でランタイム環境を用意することもできますが、今回はMicrosoftが提供している環境を利用します。

Windowsや、Linux, MacOSなど様々な環境が用意されています。

 

variables

variables:
  buildConfiguration: 'Release'
  framework: 'net6.0'
  targetRuntime: 'win-x86'
  selfContained: 'false'

この後のstepで使う変数を定義しています。

steps

実際にコマンドを実行していく部分になります。
上記で指定したpool上で、指定したコマンドが順々に実行されていきます。

 

UseDotNet@2

steps:
- task: UseDotNet@2
  displayName: 'Install .NET SDK'
  inputs:
    version: 6.0.x

task:のパラメータに書かれているUseDotNet@2は、Pipelinesが提供するコマンドです。
ググると、どんな処理を行うかや、コマンドの引数などがドキュメントとして書かれています。

今回のUseDotNet@2に関してもドキュメントに記載がありました。

指定したバージョンの.NET SDKをインストールしてくれるようで
コマンドに対する引数はinputs:のパラメータに与えていきます。

ASP.NET Coreのアプリケーションをビルドするには.NET SDKが必要です。
そして、poolを指定するだけでは空っぽのサーバーが用意されるだけであるため、そこに実行環境を構築していく必要があります。

今回は.NET 6を想定しているため、6.0.xを指定しました。
xを使う事で指定したメジャーバージョン・マイナーバージョンにおける最新のSDKをインストールしてくれます。

他にもパラメータは色々とあるので、自分に必要なものを選択していきましょう。

DotNetCoreCLI@2

- task: DotNetCoreCLI@2
  displayName: 'Publish'
  inputs:
    command: publish
    modifyOutputPath: false
    arguments: '-c $(buildConfiguration) -f $(framework) -r $(targetRuntime) --self-contained $(selfContained)'
    zipAfterPublish: true
次はDotNetCoreCLI@2です。

このコマンドは特定の処理を行うという訳では無く、dotnetコマンド全般の実行に対応しています。

今回は発行を行うのでcommand:にpublishを指定します。

modifyOutputPath:のパラメータに関しては、複数プロジェクトを同一フォルダにoutputしたい場合に設定します。
今回はそのようなケースを想定していないので、falseとしておきます。

arguments:にビルド構成、フレームワーク、ターゲットランタイムなどを指定していきます。
手元の環境でビルドしていたものに併せて指定していきましょう。
こういった細かいパラメータはvariablesのパラメータに定義しておくと、後々変更が楽になります。

またzipAfterPublish: trueを設定して、publishが終わった後にzip化しておくのもポイントです。
App Serviceのドキュメントにもありますが、デプロイにおいてはZIPファイルである必要があります。
(他の方法でもデプロイは可能ですが今回は省略します)

dotnetコマンドとは?

少し話が逸れますが

普段開発する時からdotnetコマンドを叩いてビルド等している方は馴染みがあると思うのですが
VisualStudioを利用し、GUI操作からビルドしている方にはあまり馴染みが無いかもしれません。

VisualStudioでビルドのボタンを押した際には、実は内部的にはこういったdotnetコマンドが実行されています。
自分も最初ここの部分が慣れずに戸惑いましたが、しっかり理解しておきましょう。

逆にdotnetコマンドをしっかり意識できれば、それをyamlファイルに書き起こすだけです。

AzureRmWebAppDeployment@4

- task: AzureRmWebAppDeployment@4
  inputs:
    ConnectionType: 'AzureRM'
    azureSubscription: '{Service Connection Name}'
    appType: 'webApp'
    WebAppName: 'app-sios-techblog'
    package: '$(Pipeline.Workspace)/**/*.zip'

最後に、AzureRmWebAppDeployment@4です。
こちらは実際にWebAppへデプロイするタスクになります。

ConnectionType:に関しては基本的にAzureRMで問題ないかと思います。
PublishProfileというパラメータも設定できますが、これはVisualStudioで発行プロファイルを作成した場合に利用するものであり、今回は利用しません。

azureSubscription:に関しては、ServiceConnection名を記載します。
このServiceConnectionというのが少し厄介なのですが、簡単に言うとAzureDevOpsとAzureテナントを結び付ける接続みたいなものです。
この後少しだけ解説します。

appType:に関しては、AppServiceの種類を指定します。

webAppName:に関しては、デプロイ先のWebAppのリソース名です。

package:に関しては、前taskでPublishした成果物を指定します。

ドキュメントにもあるようにPublishしたものは、既定では $(Pipeline.Workspace) にダウンロードされます。
なおPipeline.Workspaceは事前定義済み変数とよばれているものであり、pool上のd:\a\1のパスを指しています。

正しくpackageのパスを指定するためにも、この定義済み変数がどのパスを指すのかを理解しておくことが大事です。
ただ、異なる定義済み変数でも同じパスを指定していたり、変数名が直感的でなかったりと混乱しがちです。

このあたり、しばやんさんのブログで分かりやすく解説してあるので参考にしてみてください。

もしどうしても分からなくなった場合は、lsコマンドなどを実行してパスを確認してみると良いかと思います。

例えば、今回の(Pipeline.Workspace)の配下がどういう構成になっているかを確認したい場合は、

- script: |
    ls -R $(Pipeline.Workspace)

など実行すればディレクトリ構成が確認できます。
地味な作業ですが、理解も深まるかと思うので試してみてください。

Service Connections

ではAzureRmWebAppDeploymentのタスクで出てきたService Connectionsに関して少しだけ説明したいと思います。

詳細に説明すると初心者向けではなくなってしまうと思うので、概要だけ…。
詳細はまた別記事にでもしたいと思います。

ServiceConnectionとは何なのかというと、
AzureDevOpsからAzureテナントへリソースをデプロイするためのコネクションです。
(正確には「Azureテナントへ」には限らず、様々なサービスへの接続が可能です。)

VisualStudioなどからWebAppsへデプロイする時にも、誰でもデプロイができるわけではありません。
Azureに対する資格情報をVisualStudioに与えてあげ、その情報に基づきデプロイが行われます。

AzureDevOpsからデプロイする場合も同様です。
同じAzureの名前を冠しているからといって何でもかんでもデプロイできるわけではありません。

今から実行するPipelineが、対象のAzureリソースに対してデプロイ権限を持っていることを示さなくてはいけません。
それがService Connectionsです。

作成方法について解説します。
PipelineのTask作成する中で自動で生成される場合もあるのですが、今回は手動で作っていきます。

ServiceConnection作成

AzureDevOpsの画面左下のProject Settingsを開き、Service Connectionsを選択します。
すると既に生成されているServiceConnection一覧が存在します。

既に使いまわせるものがある場合は、今回作成は不要です。
ServiceConnection名をコピーして、AzureRmWebAppDeploymentタスクのazureSubscriptionのパラメータに与えてください。

使っていいか分からない場合はServiceConnectionのところにCreatorが書いてあるかと思うので、その人に確認しましょう。

新規に作成する場合はnew service conenctionを選択します。

右側に新規ウィンドウが出てくるかと思います。
今回はAzureのリソースに対する接続を作成するため、Azure Resource Managerを選択します。

次にアプリに対する認証方法を設定します。
Service Principal(automatic)を選択します。

このServicePrincipalに関しては、武井さんが非常に分かりやすい記事書いてくれているので是非ご一読を!
これを読むとどうやってService Principalで認証を行っているか分かるかと思います。

もう多分怖くないサービスプリンシパル

次にAzureのリソース情報を入力します。

今回はScopeはできるだけ狭めて、リソースグループ単位でのservice connectionとしました。
こうすることで、対象のリソースグループへのデプロイしか行えず、事故防止になります。

また、このServiceConnectionを作成する時には、自身がそのリソースグループへのデプロイ権限を持っている必要があります。
自分がデプロイ権限を持っていないのに、勝手にデプロイ権限を割り当てることはできないですもんね。

もし自身が持っていない場合はAzureのテナントを管理している人にお願いしてみてください。

また、今回は簡易的に「Grant access permission to all pipelines」をenableにしています。
もちろん個別に設定することも可能なので、実際の運用では適切なスコープに絞り込みましょう。

作成が完了したら、service connection nameの値をAzureRmWebAppDeploymentタスクのazureSubscriptionのパラメータに与えてください。

Run Pipeline

では作成したPipelineを実行してみましょう。
yamlファイルをmasterにpushすれば、自動的にPipelineが実行されます。

特に問題なければ全てのタスクが完了しているかと思います。

デプロイ先のWebAppも見てみましょう。

WebAppを開き、デプロイセンター→ログを開きます。

するとデプロイされた履歴が確認でき、関連するPipelineへのリンクも貼られています。
リンクを参照することで、どういう状態のソースコードで、どういったビルド環境でデプロイされたか分かるので非常に助かります。

アプリにアクセスするとデプロイされていることも確認できました。

まとめ

非常にシンプルな形でWebAppへのデプロイを構成し、yamlファイルの内容を解説してきました。
これが一番ベーシックな内容になるかと思います。

実際に運用する際は、これに加えてテストの追加や、staging環境へのデプロイ、デプロイ前に承認フェーズなどの追加も必要になって来るかと思います。
ただ、今回のyamlファイルの中身がしっかりできていれば応用していくことは難しくないと思います。

個人的に大事だと思うポイントとしては、以下3つだと思います。

  • yamlはローカルで実行していることを書きだしていくだけ
  • Pipelineが提供しているTaskのドキュメントを読み込む
  • 定義済み変数のパスを理解する

発展編に関しては、機会があればまたブログに書いていきたいと思います。

ではまた!

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です