これまでは、GitHub は個人で利用し、業務では Azure DevOps を使う機会が多く、結果として CI/CD パイプラインは Azure DevOps が提供する Azure Pipelines を使うことがほとんどでした。GitHub が Microsoft に Join し、GitHub / GitHub Enterprise を業務で利用できるようになったことで、私自身、GitHub Actions を使う機会が徐々に増えてきました。
今回は、アプリケーション開発で頻繁に利用する Azure Functions (Function App) に対して、GitHub Actions を使ったデプロイ自動化の手順を整理します。エンタープライズ向けの開発では、Function App を Private Endpoint (インバウンド側の組み込み) を利用して、VNet に組み込む方法が一般的になってきましたが、その場合、インバウンドはプライベート通信しかサポートされない為、標準の GitHub-hosted Runner (ビルド&デプロイ サーバー) を使ったパブリック IP 通信でのデプロイができず、その VNet に配置した VM 上に Self-hosted Runner をインストールして利用する必要があります。
#1. 環境
本稿では、以下の環境を利用しています。尚、GitHub リポジトリや GitHub Desktop の詳細な使い方は割愛します。
#2. Azure Functions (Function App) の作成
Azure Portal から以下のリソースを作成します。VNet に組み込んだ環境も作る為、Function App 以外のリソースも作成します。
- Resource Group : TEST_GitHub
- VNet : VNET_JE_GitHub
- Linux VM : seijim-linux-vm-github
- Function App : seijim-func-github
###(2) VNet の作成
サブネットは以下を作成します。
- Private Endpoint 配置用サブネット : func-inbound-pe
- Function App VNet 統合用サブネット : func-outbound-integrated
- VM 配置用サブネット : vm
###(3) Linux VM の作成
イメージは、Data Science Virtual Machine - Ubuntu (ソフトウェアの追加インストールが不要) を指定し、ネットワークは、VNET_JE_GitHub の vm サブネットを指定します。
###(4) Function App (C#) の作成
Function App は、Private Endpoint による接続をサポートしている Functions Premium Plan を選択します。Private Endpoint は、App Service Plan の PremiumV2, PremiumV3 でもサポートされています。閉域網でのデプロイが不要な場合は、実行プランの制約はありません。この段階では、VNet への組み込みはせずに、後のステップで実施します。
#3. Function App のコード作成
###(1) GitHub リポジトリの作成
GitHub Desktop を使って、Function App のコードを作成する前に、新しいリポジトリを作成しておきます。
###(2) Visual Studio で Function App のコード作成
Visual Studio を起動して、「新しいプロジェクトの作成」をクリックします。
テンプレートの検索で、「function」と入力して、「Azure Functions」を選択します。
プロジェクト名を入力して、ローカル Git リポジトリのパスを指定します。
関数名を変更したい場合は、FunctionName 属性の「Function1」を変更してください。ここではそのままにしておきます。
デバッグ実行「▷」ボタンをクリックして、ローカル環境で動作確認を行います。
ブラウザを起動し、アドレス バーに、コマンド ウィンドウに表示されている URI をコピーして、クエリ パラメーター「name」に適当な文字列を設定して実行します。以下のような画面が表示されれば成功です。
###(3) GitHub に Function App ソリューション/プロジェクトをプッシュ
Function App のコードをコミットして、GitHub リポジトリのプッシュします。
#4. GitHub Actions の構成
手順については、GitHub リポジトリ Azure/functions-action の Readme の「Using Publish Profile as Deployment Credential (recommended)」セクションに記載されています。以下の手順となりますので、このステップを1つずつ確認して行きます。
- Azure Portal で当該 Function App に行く
- 「発行プロファイルの取得」をクリックして、.PublishSettings ファイルをダウンロードする
- .PublishSettings ファイルを開き、中身をコピーしておく
- GitHub に行き、Repository > Settings > Secrets で、コピーしておいた XML を貼り付けて新しいシークレットを作成する
- Azure/functions-action に用意されたテンプレートを利用して、リポジトリに .github/workflows/.yml を作成する
- 「env:」セクションにある変数を当該 Function App に合わせて変更する
- プロジェクト (コード) を GitHub リポジトリにコミット&プッシュすると、新しい GitHub ワークフローが Actions タブで開始される
###(1) Azure Portal で Function App 発行プロファイルを取得
Azure Portal で対象の Function App に行き、概要ページの上部にある「発行プロファイルの取得」をクリックし、.PublishSettings ファイルをダウンロードします。
###(2) .PublishSettings ファイルの中身をコピー
ダウンロードした .PublishSettings ファイルを開き、中身をコピーします。
###(3) GitHub で新しいシークレットの作成
自分の GitHub リポジトリに行き、「Settings」タブの「Secrets」を選択して、「New repository secret」ボタンをクリックします。
「Name」に AZURE_FUNCTIONAPP_PUBLISH_PROFILE、「Value」には直前の手順でコピーしておいた XML を貼り付け、「Add secret」ボタンをクリックして、新しいシークレットを作成します。
###(4) GitHub ワークフロー (GitHub Actions) の作成
GitHub Azure/functions-action に行き、Readme の以下セクションにあるテンプレートの表から DotNet 行 / Windows 列にある「windows-dotnet-functionapp-on-azure.yml」のリンクをクリックします。
このテンプレート ファイル「windows-dotnet-functionapp-on-azure.yml」の中身をコピーしておきます。
自分の GitHub リポジトリの「Actions」タブで、「set up a workflow yourself」リンクをクリックします。
生成された「main.yml」ファイルの中身を事前にコピーしておいたテンプレートの中身で置き換えます。
###(5) GitHub ワークフロー (GitHub Actions) の修正
「main.yml」にコピペしたテンプレート (Function App のビルド&デプロイ) を自分の Function App の環境に合わせる為、「env:」セクションにある以下の変数を修正します。尚、DOTNET_VERSION については、こちらのサイトを確認して、3.x の最新バージョンを選択しています。
- AZURE_FUNCTIONAPP_NAME: seijim-func-github # Function App name
- AZURE_FUNCTIONAPP_PACKAGE_PATH: './FuncGitHub/FuncGitHub/' # VS Project (csproj) file directory
- DOTNET_VERSION: '3.1.413' # Latest .NET SDK 3.x version
変数を修正したら、「Start commit」をクリックします。
###(6) GitHub ワークフロー (GitHub Actions) の実行結果を確認
「Actions」タブを開くと、ワークフロー (この場合、main.yml) の実行結果を確認することができます。今回は、main.yml のコミットでワークフローが実行されましたが、ソースコードの変更がコミットされると、同様にワークフローが実行されます。
「Start commit」を実施した際のコミット名称をクリックすると、ジョブ毎の実行結果が表示されます。
ジョブ名をクリックすると、各ステップ毎の実行結果が表示されます。
###(7) Function App のデプロイ後のテスト
Azure Portal で対象の Function App に行き、「関数」ページから対象の関数 (この場合、Function1) をクリックし、「コードとテスト」ページ上部の「関数の URL の取得」をクリックします。
表示された URL をコピーし、ブラウザを起動して、アドレスバーに貼り付け、クエリパラメーター「name=Azure!!」を追加して実行してみます。ローカルで実行した時と同じように、以下のような表示になれば、成功です。
#5. Function App を VNet に組み込む (閉域化)
2章の手順 4 でも記載しましたが、Function App が Functions Premium Plan (もしくは、App Service Plan - PremiumV2 or PremiumV3) になっていることを確認してください。
###(1) インバウンド通信の閉域化
Function App では、インバウンド通信とアウトバウンド通信それぞれで VNet への組み込み方が異なります。ここでは、インバウンド通信を Private Endpoint/Link 機能を使って組み込みます。
Azure Portal で対象の Function App に行き、「ネットワーク」ページで、「受信トラフィック」ボックスの「プライベート エンドポイント」をクリックします。
「プライベート エンドポイント接続」ページで、「+ 追加」をクリックします。
Private Endpoint の名称の他、2章で作成した VNet および サブネットを指定し、「OK」をクリックします。
「接続状態」が「Approved」になっていれば、Private Endpoint は有効です。
###(2) アウトバウンド通信の閉域化
アウトバウンド通信は、VNet 統合機能を使って組み込みます。「ネットワーク」ページの「送信トラフィック」ボックスの「VNET 統合」に該当しますが、今回の検証において、アウトバウンド通信 (DB などへのアクセス) は発生しない為、ここでは 割愛します。
###(3) GitHub ワークフローで再度ビルド&デプロイを実行
GitHub リポジトリの「Code」タブで、Function1.cs ファイルを編集モードで開き、ログ出力部分のコードを修正し、コミットします。
修正前
log.LogInformation("C# HTTP trigger function processed a request.");
修正後
log.LogInformation("C# HTTP trigger function processed a request!!!!!");
###(4) GitHub ワークフローの実行結果
Function App の閉域化によって、デプロイ先にアクセスが出来ず、ワークフローが失敗することが分かります。
#6. Self-hosted Runner による閉域環境への対応
5章で、標準の GitHub-hosted Runner (ビルド&デプロイ サーバー) では、パブリック IP 通信でのデプロイができないことが分かりました。これに対処する為、Function App を組み込んだ VNet に Self-hosted Runner を立てる必要があります。ここでは、その手順を記載します。
###(1) GitHub での操作
GitHub リポジトリで、「Settings」タブの「Actions」>「Runners」を開き、「New self-hosted runner」ボタンをクリックします。
2章で作成した Linux VM を使う為、「Runner Image」は「Linux」を選択します。ここに Self-hosted Runner のダウンロード、セットアップの手順が示されています。
###(2) SSH クライアントでの操作
2章で作成した Linux VM に SSH 接続を行い、前の手順 (1) で表示しているページに記載された Self-hosted Runner のダウンロード、セットアップ手順通りに実行します。以下は実行例ですので、必ず自分の GitHub 上の手順に従ってください。
# Create a folder
mkdir actions-runner && cd actions-runner
# Download the latest runner package
curl -o actions-runner-linux-x64-2.283.1.tar.gz -L https://github.com/actions/runner/releases/download/v2.283.1/actions-runner-linux-x64-2.283.1.tar.gz
# Optional: Validate the hash
echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx actions-runner-linux-x64-2.283.1.tar.gz" | shasum -a 256 -c
# Extract the installer
tar xzf ./actions-runner-linux-x64-2.283.1.tar.gz
# Create the runner and start the configuration experience
./config.sh --url https://github.com/seijim/seijim-func-github --token XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
./config.sh のステップを実行すると、GitHub に接続した後、いくつか入力を促されますが、基本的には Enter で大丈夫です。ラベル付けについては、既定のラベル ('self-hosted', 'Linux', 'X64') 以外が必要な場合に入力しておきます。ここでは、「runner1」と入力しました。
最後に ./run.sh で Self-hosted Runner を起動します。
# Last step, run it!
./run.sh
以下のメッセージが表示されれば、Self-hosted Runner が正常に起動し、ジョブを待機している状態を示しています。
###(3) GitHub での状況確認
GitHub リポジトリの「Settings」タブで、「Actions」>「Runners」を開きます。作成した Self-hosted Runner が表示され、「Status」が「Idle」となっていれば、正常です。
###(4) GitHub ワークフローの修正
GitHub リポジトリの「Code」タブで、./github/workflow/main.yml を開き、「runs-on」セクションで「windows-latest」から Self-hosted Runner のラベル ('self-hosted', 'Linux', 'X64', 'runner1') を設定することで、ワークフローを Self-hosted Runner で実行することができます。
jobs:
build-and-deploy:
runs-on: [self-hosted, linux, x64, runner1]
###(5) GitHub ワークフロー実行結果の確認
GitHub リポジトリの「Actions」タブで、ワークフローの実行結果を確認します。Function App の閉域環境においても、デプロイが成功することが分かります。
###(6) Self-hosted Runner のサービス化
サービス化については、こちらのページで、手順をご確認ください。
#まとめ
いかがだったでしょうか。比較的つまずくポイントは少ないのではないかと思います。
また、Windows OS を選択した Azure Functions へのビルド&デプロイに Linux VM を使えるというのは、面白かったのではないでしょうか。
.NET (C#) が完全なマルチプラットフォーム OSS 言語ランタイムであることが分かるケースとなります。
#参照