こんにちは、サイオステクノロジーの佐藤 陽です。
今回も AzureDevOpsのサービスであるAzurePipelinesに関する内容です。
前回はWebAppsへデプロイする内容を徹底解説しました。
今回は、双璧をなすであろうAzure Functionsへのデプロイ関しても解説していきたいと思います。
重複する部分もあるかと思いますが、また改めて(コピペして)解説していきたいと思います。
- CI/CD始めてみたいけど、よくわからない…
- yamlファイル、何書かれているか全然分からない…
という方は是非最後までご覧ください!
(前回のWebAppsの記事はコチラ↓)
はじめに
今回はAzurePipelinesを利用し、Azure FunctionsへアプリケーションをDeployしていきます。
Pipelines上にテンプレート等も用意されており、一見親切な作りにはなっているのですが
yamlファイルを見たときに「?」となり、何をどうすればいいのかわからない人もいるかと思います。
実際自分がそうでした。
ですので、そのyamlファイルの内容を丁寧に解説していきたいと思います。
事前準備
今回の構成としては以下の内容です。
- .NET 6
- Azure Repos
- Azure Pipelines
- Azure Functions
今回、デプロイするAzure Functionsのアプリケーションは以下のコマンドで作成しておきました。
func new --template "Http Trigger" --name siostech
実行時に聞かれるパラメータとして
worker runtime:dotnet
language:C#
を今回選択しました。
作成が完了したら、Reposのリポジトリにpushしておきましょう。
また、デプロイ先のAzure Functionsも作成しておきましょう。
今回は以下のような構成で作成しました。
Pipeline構成
早速Pipelineを作成していきます。
push先のAzure Repos Gitを選択し
対象のRepositoryを選択します
Configureとしては
.NET Core Function App to Windows on Azure
と言ったものが用意されていますが、これを選択して進んでいく場合は、対象のSubscriptionに対して強い権限が必要になります。
権限を持っている人はこのまま進んでしまってOKですが、本記事ではまたサンプルのyamlを提示し、それに基づいて構成していきます。
その場合は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 $(buildConfiguration)'
inputs:
command: publish
#projects: '**/*.csproj'
publishWebProjects: false
modifyOutputPath: false
arguments: '-c $(buildConfiguration) -f $(framework) -r $(targetRuntime) --self-contained $(selfContained) -o $(Build.ArtifactStagingDirectory)/siostech_$(Build.BuildNumber)'
zipAfterPublish: true
- task: AzureFunctionApp@2
inputs:
connectedServiceNameARM: '{service connection name}'
appType: 'functionApp'
appName: 'siostech'
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
publishWebProjects: false
modifyOutputPath: false
arguments: '-c $(buildConfiguration) -f $(framework) -r $(targetRuntime) --self-contained $(selfContained) -o $(Build.ArtifactStagingDirectory)/siostech_$(Build.BuildNumber)'
zipAfterPublish: true
次はDotNetCoreCLI@2です。
このコマンドは特定の処理を行うという訳では無く、dotnetコマンド全般の実行に対応しています。
今回は発行を行うのでcommand:にpublishを指定します。
publishWebProjects: 今回はWebProjectではないためfalseに設定します。
以下のパラメータはWebAppsと同様です。
modifyOutputPath:のパラメータに関しては、複数プロジェクトを同一フォルダにoutputしたい場合に設定します。
今回はそのようなケースを想定していないので、falseとしておきます。
project:は発行対象のプロジェクトのパスを指定します。
arguments:にビルド構成、フレームワーク、ターゲットランタイムなどを指定していきます。
手元の環境でビルドしていたものに併せて指定していきましょう。
こういった細かいパラメータはvariablesのパラメータに定義しておくと、後々変更が楽になります。
またzipAfterPublish: trueを設定して、publishが終わった後にzip化しておくのもポイントです。
Azure Functionsのドキュメントにもありますが、zipファイルにてデプロイします。
(他の方法でもデプロイは可能ですが今回は省略します)
dotnetコマンドとは?
少し話が逸れますが
普段開発する時からdotnetコマンドを叩いてビルド等している方は馴染みがあると思うのですが
VisualStudioを利用し、GUI操作からビルドしている方にはあまり馴染みが無いかもしれません。
VisualStudioでビルドのボタンを押した際には、実は内部的にはこういったdotnetコマンドが実行されています。
自分も最初ここの部分が慣れずに戸惑いましたが、しっかり理解しておきましょう。
逆にdotnetコマンドをしっかり意識できれば、それをyamlファイルに書き起こすだけです。
AzureFunctionApp@2
- task: AzureFunctionApp@2
inputs:
connectedServiceNameARM: '{service connection name}'
appType: 'functionApp'
appName: 'siostech'
package: '$(Pipeline.Workspace)/**/*.zip'
最後に、AzureFunctionApp@2です。
こちらはAzureFunctionsへデプロイするタスクになります。
connectedServiceNameARM:に関しては、ServiceConnection名を記載します。
このServiceConnectionというのが少し厄介なのですが、簡単に言うとAzureDevOpsとAzureテナントを結び付ける接続みたいなものです。
この後少しだけ解説します。
appType:に関しては、Azure Functionsなので”functionApp”を指定します。
appName:に関しては、デプロイ先のAzureFunctionsのリソース名です。
package:に関しては、前taskでPublishした成果物を指定します。
ドキュメントにもあるようにPublishしたものは、既定では $(Pipeline.Workspace) にダウンロードされます。
なおPipeline.Workspaceは事前定義済み変数とよばれているものであり、pool上のd:\a\1のパスを指しています。
正しくpackageのパスを指定するためにも、この定義済み変数がどのパスを指すのかを理解しておくことが大事です。
ただ、異なる定義済み変数でも同じパスを指定していたり、変数名が直感的でなかったりと混乱しがちです。
このあたり、しばやんさんのブログで分かりやすく解説してあるので参考にしてみてください。
もしどうしても分からなくなった場合は、lsコマンドなどを実行してパスを確認してみると良いかと思います。
例えば、今回の(Pipeline.Workspace)の配下がどういう構成になっているかを確認したい場合は、
- script: |
ls -R $(Pipeline.Workspace)
など実行すればディレクトリ構成が確認できます。
地味な作業ですが、理解も深まるかと思うので試してみてください。
Service Connections
AzureFunctionApp@2のタスクに登場したService Connectionに関しては
WebAppsに書いたものと全く同一なため、こちらを参照してください。
Run Pipeline
では作成したPipelineを実行してみましょう。
yamlファイルをmasterにpushすれば、自動的にPipelineが実行されます。
特に問題なければ全てのタスクが完了しているかと思います。
デプロイ先のAzure Functionsも見てみましょう。
Azure Functionsを開き、デプロイセンター→ログを開きます。
するとデプロイされた履歴が確認でき、関連するPipelineへのリンクも貼られています。
リンクを参照することで、どういう状態のソースコードで、どういったビルド環境でデプロイされたか分かるので非常に助かります。
まとめ
非常にシンプルな形でAzureFunctionsへのデプロイを構成し、yamlファイルの内容を解説してきました。
これが一番ベーシックな内容になるかと思います。
WebAppsの記事でも書きましたが
実際に運用する際は、これに加えてテストの追加や、staging環境へのデプロイ、デプロイ前に承認フェーズなどの追加も必要になって来るかと思います。
ただ、今回のyamlファイルの中身がしっかりできていれば応用していくことは難しくないと思います。
個人的に大事だと思うポイントとしては、以下3つだと思います。
- yamlはローカルで実行していることを書きだしていくだけ
- Pipelineが提供しているTaskのドキュメントを読み込む
- 定義済み変数のパスを理解する
応用編に関しては、機会があればまたブログに書いていきたいと思います。
ではまた!