📝

サービスプリンシパルを利用しないGitlab→Azure App Serviceへの継続的デプロイ

に公開

はじめに

Azure App ServiceへDockerコンテナを継続デプロイする場合、構成管理やCI/CD環境毎にベストプラクティスが変わってきます。
↓は私が思う、リポジトリサービス毎のベストプラクティスです。

  1. (Github) 専用のGithub Actions(webapps-deploy)でデプロイ
  2. (Azure Repos) Azure Pipelinesを利用してデプロイ
  3. (その他) サービスプリンシパルを発行、パイプライン中でazコマンドを利用してデプロイ

自社の環境ではイントラのGitlabでコードを管理していますが、諸事情でサービスプリンシパルの発行ができず3.の方法が取れませんでした。

一応、Azure App ServiceにはAzure Container Registryのイメージが更新された際、App Serviceで使用するイメージも更新する機能があるものの、致命的な欠陥※1があり正直使えるレベルではありません。
(詳しくは末尾の参考文献を参照してください。)

上記の背景があり、サービスプリンシパルを利用せず社内イントラ->Azure App Serviceへの継続的デプロイのための環境を構築できたので情報を共有したいと思います。

※1: Web App for Containers のデプロイでつまずいた話 p20~を参照。

前提

だいぶニッチですが、以下の条件下でCI/CD環境を構築します。

  • App ServiceはLinux環境、コンテナ方式のデプロイを想定
  • Dockerイメージはコンテナレジストリ(e.g. Azure Container Registry)で管理
  • サービスプリンシパルは未利用
  • 構成管理はイントラのGitlab、CI/CDはGitlab CIの利用を想定
  • 立ち上げたいコンテナは一種(複数種のコンテナを同時に起動しない)

要点と結論

Kudu REST API & 発行プロファイルによる認証でApp Serviceで利用するイメージを更新できることがわかりました。

発行プロファイルの認証は一般的なベーシック認証で、サービスプリンシパルが不要です。
以下に相当するコマンドをGitlab CIに組み込めば、サービスプリンシパルを利用しないApp Serviceの継続的デプロイが実現できます。

$ curl -X POST "${SCM_URL}/api/app/update" \
    -u "${PUBLISH_PROFILE_USER}:${PUBLISH_PROFILE_PASSWORD}" \
    -H "Content-Type: application/json" \
    -H "LinuxFxVersion:DOCKER|${ImageName}:${TagName}"
  • SCM_URL: App ServiceのSCMのURL、Azure PortalでApp Serviceのページに飛び、開発ツール -> 高度なツールからアクセス可能なページのURL
  • PUBLISH_PROFILE_USER: 発行プロファイルのユーザー名、Azure Portalのデプロイ -> デプロイ センター -> 発行プロファイルの管理からダウンロード
  • PUBLISH_PROFILE_PASSWORD: 発行プロファイルのパスワード
  • ImageName: デプロイしたいイメージ
  • TagName: デプロイしたいイメージのタグ

前提条件と制約

前述したコマンドを使うためには、事前にしておくべき設定や、一部設定値に制約があります。

  • Kuduを利用するため、Azure PortalのApp Serviceのページで設定->構成からSCM 基本認証の発行資格情報の有効化が必要
  • コンテナのデプロイのため、デプロイ->デプロイセンターで以下の設定が必要
    • ソースContainer Registryとして設定済み
    • コンテナーの種類単一コンテナーとして設定済み
    • レジストリ ソースは適当なものが選択され、必須の設定項目が設定済み
      • (Kudu REST APIではDockerhub→Azure Container Registryのようなコンテナレジストリの変更は不可)

調査

以降は前述した対策にたどり着くまでの取り組みを順を追って説明します。
とりあえず、めぼしい情報がないかMicrosoftの用意している公式ドキュメントをまず参照しました。

残念ながら、Gitlab(Azure Reposは除外)とApp Serviceの継続的デプロイに関係するドキュメントは見つからなかったので、次に先駆者がいないかググりました。
こちら、有益な情報が得られなかったので、一旦最初に見つけていたGithub Actions webapps-deployのコードを覗いてみることにしました。

以下はwebapps-deployリポジトリのREADME.mdに記載されているGithub Actionsの設定ファイルの抜粋です。

on: [push]

name: Linux_Container_Node_Workflow

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    # checkout the repo
    - name: 'Checkout Github Action'
      uses: actions/checkout@master

    - uses: azure/docker-login@v1
      with:
        login-server: contoso.azurecr.io
        username: ${{ secrets.REGISTRY_USERNAME }}
        password: ${{ secrets.REGISTRY_PASSWORD }}

    - run: |
        docker build . -t contoso.azurecr.io/nodejssampleapp:${{ github.sha }}
        docker push contoso.azurecr.io/nodejssampleapp:${{ github.sha }} 

    - uses: azure/webapps-deploy@v2
      with:
        app-name: 'node-rnc'
        publish-profile: ${{ secrets.azureWebAppPublishProfile }}
        images: 'contoso.azurecr.io/nodejssampleapp:${{ github.sha }}'

このファイルでは以下の流れで進むように処理が記述されています。

  1. Azure Container Registry(ACR)へログイン
  2. ACRへイメージをプッシュ
  3. プッシュしたイメージのタグを指定する形でApp Serviceへデプロイ

App Serviceへのデプロイにあたり、azure/loginaz loginを事前に実行していないため、サービスプリンシパルやEntra IDの資格情報は使用していません。
代わりに、発行プロファイル(publish-profile)と呼ばれる資格情報をSecretで渡して、ACRのDockerイメージのデプロイをしています。
発行プロファイルはデプロイ操作のための資格情報、およびそれを格納したxmlファイルを指します。

ファイルにはベーシック認証のためのuserName(ユーザー名)とuserPWD(パスワード)が記載されています。

<publishData>
    <publishProfile profileName="XXXXXXX - Web Deploy" publishMethod="MSDeploy" publishUrl="XXXXXXX.scm.azurewebsites.net:443" msdeploySite="XXXXXXX" userName="$XXXXXXX" userPWD="hogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge" destinationAppUrl="http://XXXXXXX.azurewebsites.net" SQLServerDBConnectionString="" mySQLDBConnectionString="" hostingProviderForumLink="" controlPanelLink="http://windows.azure.com" webSystem="WebSites">
        <databases />
    </publishProfile>
    <publishProfile profileName="XXXXXXX - FTP" publishMethod="FTP" publishUrl="ftp://waws-prod-ty1-051.ftp.azurewebsites.windows.net/site/wwwroot" ftpPassiveMode="True" userName="XXXXXXX\$XXXXXXX" userPWD="hogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge" destinationAppUrl="http://iwasa20211103aspcore.azurewebsites.net" SQLServerDBConnectionString="" mySQLDBConnectionString="" hostingProviderForumLink="" controlPanelLink="http://windows.azure.com" webSystem="WebSites">
        <databases />
    </publishProfile>
    <publishProfile profileName="XXXXXXX - Zip Deploy" publishMethod="ZipDeploy" publishUrl="XXXXXXX.scm.azurewebsites.net:443" userName="$XXXXXXX" userPWD="hogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge" destinationAppUrl="http://XXXXXXX.azurewebsites.net" SQLServerDBConnectionString="" mySQLDBConnectionString="" hostingProviderForumLink="" controlPanelLink="http://windows.azure.com" webSystem="WebSites">
        <databases />
    </publishProfile>
</publishData>

Azure App Serviceではデプロイを管理するためのKuduと呼ばれる管理用のアプリケーションがWebアプリとは別で起動しており、発行プロファイルはKubu側の操作に必要になる資格情報です。
KuduのREST APIは一応Githubで公開されていますが、Dockerイメージの更新に使えそうなAPIがわからなかったので、ChatGPTに調べてもらいました。

ChatGPTにwebapps-deployのソースコードを渡して調べてもらった結果、使用しているAPIはPOST /api/app/updateであることがわかりました。

このAPIは公式のリファレンスには記載されておらず、Githubで検索可能なコードの中で使っているのはAzure/webapps-deployとAzure/fuction-actionの2つのリポジトリだけでした。
Microsoft側はこのAPIを公開していない意図は不明ですが、一旦は使えているのでよしとしています。

REST APIはヘッダーとしてDockerイメージ、およびタグを受け取れるようになっています。

$ curl -X POST "${SCM_URL}/api/app/update" \
    -u "${PUBLISH_PROFILE_USER}:${PUBLISH_PROFILE_PASSWORD}" \
    -H "Content-Type: application/json" \
    -H "LinuxFxVersion:DOCKER|${ImageName}:${TagName}"

Gitlab CIに発行プロファイルをシークレットとして登録しておけば、イメージのビルド、レジストリへのプッシュに加え、App Serviceへのイメージの適応までCI/CDで実行できるので、継続的デプロイが実現できます。

GitHubで編集を提案

Discussion