🫐

Azure FunctionsをGithub Actions経由でデプロイする

2023/02/27に公開

株式会社IVRyのエンジニア、町田です。

IVRyはMicrosoft Startupsに採択されており、R&D等でAzureを活用しています。
https://ivry.jp/column/microsoft-for-startups-press-release/
様々な用途でAzure Functionsを活用することがありますが、本番運用を考えるとCI/CDの利用は外せません。先日、Azure FunctionsをGithub Actions経由でデプロイしようとしたのですが、結構躓いてしまいました。中々情報が見つけられなかったこともあり、一つの参考として記事にいたします。

Azure Functionsの3種類のデプロイフロー

Azureの公式ドキュメントを見ると、Functionsのデプロイには3種類の方法が設定されています。

  • VSCode, Visual Studio等のツールベース
  • AzureのDeployment Centerを利用したマネージドなデプロイ
  • Github Actions等の外部パイプライン

最も簡単な手法はVSCodeを利用したものかと思います。
流石にMicrosoft製ですからアドオン経由でシームレスにAzureのデプロイが可能です。

しかし、チームで開発をする場合や、本番運用を考えるとVSCode経由で誰かのローカルからデプロイするのはあまり望ましくありません。CI/CDの観点からは自動化したいところです。

自動化に当たり、Deployment Centerの利用かGithub Actionsの利用が考えられますが、今回はGithub Actionsでのデプロイをしてみたいと思います。

https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-deployment-technologies

公式のやり方

早速ですが公式ドキュメントにGithub Actions経由のデプロイ方法が載っています。

ワークフローは以下の3段階です

  • 認証
  • ビルド
  • デプロイ

※以降、デプロイに必要な関数アプリはすでに構築済みであることを仮定しています。

認証

デプロイ資格情報を利用する方法

デプロイ資格情報経由で認証を行うことができます。

これはドキュメント通りなので割愛しますが、Functionページから「デプロイ資格情報」をダウンロードして、ActionsのSecretにAZURE_FUNCTIONAPP_PUBLISH_PROFILEとして登録すればよいです。

そうすると、次のような形でworkflow.ymlのdeploy stepがかけるはずです。
(env.AZURE_FUNCTIONAPP_PACKAGE_PATHは後で説明します。)

name: 'Run Azure Functions action'
uses: Azure/functions-action@v1
with:
    app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
    package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
   publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}

実際、これでうまくいくと思いますが、実は別のやり方もあります。

Open ID Connectを利用する方法

GitHub Actions for deploying to Azureにある通り、Github Actions中でAzureにLoginすることでも認証が可能です。OpenID Connectに対応したサービスプリンシパルを作成することで認証を行っています。
公式ドキュメントにもちゃんと書かれてるのですが、Azure Functionのドキュメントにはリンクが貼られておらず、この手法の存在に気づきませんでした。
デプロイ資格情報はAzure Functionsへのアクセス権限があるユーザーであれば誰でもアクセスできるようです。一方、サービスプリンシパルを利用する方法は、設定は多少面倒ですが権限管理の面ではよりセキュアな手法になりそうです。

設定方法はドキュメントどおりですが、

  1. サービスプリンシパルを作成する
  2. フェデレーション資格情報の追加
  3. Secretの登録
    となります。

躓いたところとして、Azure Portal経由だとフェデレーション資格情報が分かりづらいです。

Active Directory > アプリの登録 > (アプリケーション名)からフェデレーション資格情報の設定にいけるのですが、これが分かりづらいです。キャプチャを貼りますが、そこがタブになっていると気づけませんでした。

ビルド

次にビルド部分の設定です。
ドキュメントには以下のように書かれています。

name: 'Resolve Project Dependencies Using Pip'
shell: bash
run: |
    pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
    python -m pip install --upgrade pip
    pip install -r requirements.txt --target=".python_packages/lib/site-packages"
    popd

ここで注意が必要なのは、Azure Functionを開発しているディレクトリの構造です。
自分はこういうディレクトリ構造で開発していたのでハマりました。

root
├── azure_function
│   ├── function
│   │    ├──__init__.py
│   │    └──function.json
│   ├── host.json
│   ├── proxies.json
│   └── requirements.txt
└── README.md

この構造の場合、AZURE_FUNCTIONAPP_PACKAGE_PATH./azure_functionとする必要があります。
ここを.としたり./azure_function/functionと設定すると正しくデプロイできません。
ハマったのですが「正しくデプロイできない」だけで
Github Actionsは正常に終了することがあります。
もし正常に終了しているのに動かない!という状況が発生したら、ディレクトリ構造が怪しいかもしれません。

デプロイ

ここまで正しく設定できていればデプロイ自体で詰まることは無いと思います。

name: 'Run Azure Functions action'
uses: Azure/functions-action@v1
id: fa
with:
    app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
    package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}

workflow.yml全体

name: Deploy function app

on:
  workflow_dispatch:

env:
  AZURE_FUNCTIONAPP_NAME: 'YOUR FUNCTION APP NAME'
  AZURE_FUNCTIONAPP_PACKAGE_PATH: 'path/to/your/package/root'
  PYTHON_VERSION: '3.9'

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - name: 'Checkout GitHub action'
      uses: actions/checkout@v2

    - name: Azure Login
      uses: azure/login@v1
      with:
        client-id: ${{ secrets.ARM_CLIENT_ID }}
        tenant-id: ${{ secrets.ARM_TENANT_ID }}
        subscription-id: ${{ secrets.ARM_SUBSCRIPTION_ID }}

    - name: Setup Python ${{ env.PYTHON_VERSION }} Environment
      uses: actions/setup-python@v1
      with:
        python-version: ${{ env.PYTHON_VERSION }}

    - name: 'Resolve Project Dependencies Using Pip'
      shell: bash
      run: |
        pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
        python -m pip install --upgrade pip
        pip install -r requirements.txt --target=.python_packages/lib/site-packages
        popd

    - name: 'Run Azure Functions action'
      uses: Azure/functions-action@v1
      id: fa
      with:
        app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
        package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}

注意として、公式ドキュメントPYTHON_VERSION3.7になっていますが3.9が推奨されているようでした。(2023/2/25現在)

まとめ

途中のディレクトリ構造で大分ハマってしまったので、相当時間がかかってしまったのですが、無事にGithub Actions経由でデプロイが出来ました。手間はかかったものの、結果的には開発速度の向上やデプロイ時の負担軽減の効果は大きいです。

実はAzure Functions自体もTerraformで作成してみたりしているのですが、ここでもかなり詰まりました。また後日その記事も書きたいと思います。

最後に、IVRyではインフラエンジニア・SREエンジニアを始め、エンジニアの絶賛採用中です。
電話という領域に最新の技術で挑む、やりがいのある仕事です。一緒に働いてくれる仲間を募集しています!

https://ivry-jp.notion.site/IVRy-e1d47e4a79ba4f9d8a891fc938e02271
https://www.wantedly.com/projects/1241848

IVRyテックブログ

Discussion