Microsoft Azure Tech Advent Calendar 2019 の21日目です。
Intro
パイプラインを実現する様々な GitHub Actions の開発がオープンソースで進んでいますが、利用するだけでなく独自の Actions を作成することができます。
この記事では、まずは Azure Functions を GitHub Actions でデプロイしたうえで、パイプラインの動きをログで確認しながら、Actions のソースコードを見ていきたいと思います。
Azure Functions を GitHub Actions でデプロイする
正式な手順やドキュメントは以下に情報があります。
大枠の流れは以下のとおりです。
- デプロイ先の Azure Functions を作成する
- その Functions に対する権限を持ったサービスプリンシパルを作成する
- GitHub Actions が Functions に対して操作できるよう、Settings > Secrets に登録する
- .github/workflows/***.yml を用意して、パイプライン処理を実行する
今回、なるべくデプロイまでは簡単に済むようにサンプルコードを用意したので、以下からの手順を参考ください。
手順
GitHub リポジトリ
このリポジトリをご自身のレポジトリへフォークします。
https://github.com/hoisjp/hello-ghactions-azfunc-nodejs.git
git clone します。YOUR_ACCOUNT
をご自身のアカウントに置き換えてください。
git clone https://github.com/YOUR_ACCOUNT/hello-ghactions-azfunc-nodejs.git
次に、Azure 側の準備をします。用意するのは2つ、(1) Azure Functions 、(2) サービスプリンシパルの作成です。
Azure Functions
デプロイ先の Azure Functions を作成します。Azure ポータルから作成しましょう。今回は以下の構成で作成します。
- 従量課金プラン
- ランタイム: Node.js
サービスプリンシパル
サービスプリンシパルを作成します。Azure Cloud Shell にログインして、以下のコマンドを実行します。上の手順で作成した Functions の <SUBSCRIPTION_ID>
、<RESOURCE_GROUP>
、<APP_NAME>
を置き換えてください。
az ad sp create-for-rbac \
--name "myApp" \
--role contributor \
--scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.Web/sites/<APP_NAME> \
--sdk-auth
実行すると、以下のようにサービスプリンシパルのJSONが表示されます。これを後ほど設定に利用するため、控えておきます。
{
"clientId": "{UUID_MASKED}",
"clientSecret": "{UUID_MASKED}",
"subscriptionId": "{UUID_MASKED}",
"tenantId": "{UUID_MASKED}",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
GitHub Secrets 設定
次に GitHub 側に Secrets を登録していきます。
AZURE_CREDENTIALS
AZURE_CREDENTIALS
に先ほど作成したサービスプリンシパルを設定します。
AZURE_FUNCTIONAPPNAME
続いて、AZURE_FUNCTIONAPPNAME
に Functions の名前を設定します。
※補足:この手順は、本来のドキュメントでは直接 YAML ファイルを書き換えるようになっています。
サンプルコードを公開するうえで、デプロイ先の名前がリポジトリ上に記載されるのが気持ち悪いと思いますので、以下のように secrets.AZURE_FUNCTIONAPPNAME
から名前を取得するようにしました。
.github/workflows/windows-nodejs-functionapp-on-azure.yml
- name: 'Run Azure Functions Action'
uses: Azure/functions-action@v1
id: fa
with:
app-name: ${{ secrets.AZURE_FUNCTIONAPPNAME }}
さて、設定は以上です。
GitHub Actions をトリガー
あとは git に変更があるたびに、GitHub Actions が実行されます。適当に Hello *** などの部分を変更してみます。
/HttpTrigger1/index.js
body: "Hello GitHub Actions " + (req.query.name || req.body.name)
push 直後から、GitHub レポジトリの、Actions
というタブから実行されたことを確認できます。
GitHub Actions 本体の仕組みを調べていく
さてここからは GitHub Actions のソースコードを覗いていきたいと思います。
ソースコードを探索
GitHub Actions 実行ログ
先ほど実行された Actions の Run Azure Functions Action
を展開すると実行ログの詳細を確認することができます。
以下のような実行ログを確認することができます。
Run Azure Functions Action1m 1s
[GET]***.scm.azurewebsites.net/api/settings
Run Azure/functions-action@v1
##[Initialize]
##[ValidateParameter]
Finding files matching input: .
##[ValidateAzureResource]
Using RBAC for authentication, GitHub Action will perform resource validation.
[GET]https://management.azure.com/subscriptions/***/resources?$filter=resourceType EQ 'Microsoft.Web%2FSites' AND name EQ '***'&api-version=2016-07-01
[POST]https://management.azure.com/subscriptions/***/resourceGroups/***-rg/providers/Microsoft.Web/sites/***/config/publishingcredentials/list?api-version=2016-08-01
[GET]https://management.azure.com/subscriptions/***/resourceGroups/***-rg/providers/Microsoft.Web/sites/***/?api-version=2016-08-01
Sucessfully acquired site configs from function app!
Detected function app sku: Consumption
[POST]https://management.azure.com/subscriptions/***/resourceGroups/***-rg/providers/Microsoft.Web/sites/***/config/appsettings/list?api-version=2016-08-01
Sucessfully acquired app settings from function app (RBAC)!
Detected function app language: Node
[POST]https://management.azure.com/subscriptions/***/resourceGroups/***-rg/providers/Microsoft.Web/sites/***/publishxml?api-version=2016-08-01
Could not parse response: ***
Response: undefined
##[PreparePublishContent]
This is folder package
Will archive . into D:\a\_temp\temp_web_package_***.zip as function app content
Archiving . to D:\a\_temp\temp_web_package_***.zip
Successfully created archive D:\a\_temp\temp_web_package_***.zip
Will use api/zipdeploy to deploy (rbac authentication)
[GET]***.scm.azurewebsites.net/api/settings
ここで出力されている Initialize
や Sucessfully acquired site configs from function app!
のようなログから、ソースコードを探索してみます。
Functions の GitHub Actions のソースコードはこちらです。
https://github.com/Azure/functions-action
src/main.ts
エントリポイントとなる箇所は、以下の src/main.ts
です。
https://github.com/Azure/functions-action/blob/master/src/main.ts
actionManager
に対して、Initializer
や ParameterValidator
といった Handler を登録して各処理を順番に実行していることがわかります。名前からわかりやすいのは、ParameterValidator
でパラメータの検証、ContentPublisher
で実際のデプロイ、を行うものですね。
....
async function main(): Promise<void> {
const actionManager = new Orchestrator();
actionManager.register(StateConstant.Initialize, new Initializer());
actionManager.register(StateConstant.ValidateParameter, new ParameterValidator());
actionManager.register(StateConstant.ValidateAzureResource, new ResourceValidator());
actionManager.register(StateConstant.PreparePublishContent, new ContentPreparer());
actionManager.register(StateConstant.PublishContent, new ContentPublisher());
actionManager.register(StateConstant.ValidatePublishedContent, new PublishValidator());
....
src/handlers/***
では次に、各種 Handler を見ていきましょう。src/handlers/***
の中にあります。
https://github.com/Azure/functions-action/tree/master/src/handlers
一番メインとなりそうな ContentPublisher.ts
を見ていきます。
https://github.com/Azure/functions-action/blob/master/src/handlers/contentPublisher.ts
export class ContentPublisher implements IOrchestratable {
public async invoke(state: StateConstant, _1: IActionParameters, context: IActionContext): Promise<StateConstant> {
switch (context.publishMethod) {
case PublishMethodConstant.ZipDeploy:
await ZipDeploy.execute(state, context);
break;
case PublishMethodConstant.WebsiteRunFromPackageDeploy:
await WebsiteRunFromPackageDeploy.execute(state, context);
break;
default:
throw new ValidationError(state, "publisher", "can only performs ZipDeploy and WebsiteRunFromPackageDeploy");
}
return StateConstant.ValidatePublishedContent;
}
}
context.publishMethod
を見て、デプロイ方法を ZipDeploy
か WebsiteRunFromPackageDeploy
かを操作しています。
この値は事前に src/handlers/contentPreparer.ts
で設定されています。
https://github.com/Azure/functions-action/blob/master/src/handlers/contentPreparer.ts
private derivePublishMethod(
....
// Uses api/zipdeploy endpoint if scm credential is provided
if (authenticationType == AuthenticationType.Scm) {
Logger.Log('Will use api/zipdeploy to deploy (scm credential)');
return PublishMethodConstant.ZipDeploy;
}
// Linux Consumption sets WEBSITE_RUN_FROM_PACKAGE app settings when scm credential is not available
if (osType === RuntimeStackConstant.Linux && sku === FunctionSkuConstant.Consumption) {
Logger.Log('Will use WEBSITE_RUN_FROM_PACKAGE to deploy');
return PublishMethodConstant.WebsiteRunFromPackageDeploy;
}
....
src/publishers/***
では最後に、実際のデプロイ部分である、publisher の部分を見ていきます。GitHub Actions の実行ログから、以下のように ZipDeploy がされていることがわかります。
Package deployment using ZIP Deploy initiated.
それでは、以下 publishers ディレクトリの中から、
https://github.com/Azure/functions-action/tree/master/src/publishers
zipDeploy を見ていきます。メインの箇所は以下のあたりです。
https://github.com/Azure/functions-action/blob/master/src/publishers/zipDeploy.ts
public static async execute(state: StateConstant, context: IActionContext): Promise<string> {
...
try {
await this.patchApplicationSettings(context);
await this.waitForSpinUp(state, context.appUrl);
deploymentId = await context.kuduServiceUtil.deployUsingZipDeploy(filePath);
isDeploymentSucceeded = true;
}
...
スピンアップを待ったり、context.kuduServiceUtil.deployUsingZipDeploy(filePath)
のように、デプロイは Kudu の機能を使っていることが確認できました。
まとめ
さて GitHub Actions の仕組みをソースコードから追いかけてみました。
大きなフレームワークなどと違って、あくまでパイプライン上の末端部分の仕組みなので、何が行われているか中身を見れば大体把握できる感覚をつかんでいただけたら嬉しいです。
中身が把握できていることで、パイプラインがどうしても期待通りに動かない時などに、実行ログから実際に何が問題なのか切り分けするための大きな手助けになります。
そして、どの GitHub Actions も構造は大きく変わりません。ご自身が必要とするパイプラインを自作してしまうことも可能です。 このあたりは私もぜひ挑戦してみたいと思います。
参考リンク
記事中のリンクをまとめて再掲します。
GitHub Actions
Azure Functions を GitHub Actions でデプロイする
正式な手順やドキュメントは以下に情報があります。