はじめに
- 下記のAzureリソースは作成してあるものとします
- ContainerをデプロイできるFunctionApp
- Azure Container Registry
- Dockerイメージとしてビルドするアプリケーションおよび、Dockerfileはできているものとします
この記事の内容を執筆するにあたり、技術検証段階でAzureサポートのOさんに大きなご助力をいただきました。この場をお借りして感謝申し上げます💐
パイプラインを作成
yamlファイルを作成
AzurePipelinesで使用できるyaml
ファイルのテンプレートや、TIPSが紹介されているMicrosoft公式のリポジトリがあります。ご存じなかった方はスターしておくと良いかもしれません。
こちらで公開されてるテンプレートの中にこんなものがありました。
Build a Docker image, push it to an Azure Container Registry, and deploy it to an Azure Functions app.
まさにやりたいこととドンピシャ!
ありがたく参考にさせてもらいましょう🎉
参考にして作ったyaml
テンプレートにはAzureリソース作成も含まれてましたが、今回は作成済みの前提なので、以下のことだけに内容を変更します。
- Dockerイメージをビルド
- Azure Container Registryにイメージをpush
- FunctionAppにデプロイするイメージを設定
長いので折りたたんでます🎬
# Docker image, Azure Container Registry, and Azure Functions app
# Build a Docker image, push it to an Azure Container Registry, and deploy it to an Azure Functions app.
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
# Copy元: https://github.com/microsoft/azure-pipelines-yaml/blob/master/templates/docker-container-functionapp.yml
# A pipeline with no CI trigger
trigger: none
resources:
- repo: self
variables:
pool: 'ubuntu-latest'
Azure.ServiceConnectionId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
FunctionApp.Name: 'docker-func-sample'
ACR.Name: 'sampleimage'
ACR.ImageName: '$(ACR.Name):$(Build.BuildId)'
ACR.FullName: 'acrqiitasample.azurecr.io'
jobs:
- job: BuildAndPushImage
displayName: Build And Push an Docker Image
condition: succeeded()
pool:
vmImage: $(pool)
steps:
- task: Docker@1
displayName: 'Build an image'
inputs:
azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)'
azureContainerRegistry: '$(ACR.FullName)'
imageName: '$(ACR.ImageName)'
command: build
dockerFile: '$(Build.SourcesDirectory)/Dockerfile'
- task: Docker@1
displayName: 'Push an image'
inputs:
azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)'
azureContainerRegistry: '$(ACR.FullName)'
imageName: '$(ACR.ImageName)'
command: push
- job: UpdateAppServiceConfiguration
displayName: Update AppService Configuration to Deploy an Image
dependsOn: BuildAndPushImage
# See https://learn.microsoft.com/ja-jp/azure/devops/pipelines/process/expressions?view=azure-devops#job-to-job-dependencies-within-one-stage
condition: or(succeeded(), eq(dependencies.BuildAndPushImage.result, 'Succeeded'))
pool:
vmImage: $(pool)
steps:
- task: AzureFunctionAppContainer@1
displayName: 'Azure Function App on Container Deploy: $(FunctionApp.Name)'
inputs:
azureSubscription: '$(Azure.ServiceConnectionId)'
appName: $(FunctionApp.Name)
imageName: '$(ACR.FullName)/$(ACR.ImageName)'
パイプラインの中でやってることをざっくり説明
-
BuildAndPushImage
Job- Dockerイメージをbuildする
- Azure Container Registry にDockerイメージをpushする
-
UpdateAppServiceConfiguration
Job- FunctionAppに使用するDockerイメージを設定
Trying to update App Service Configuration settings. Data: {"appCommandLine":null,"linuxFxVersion":"DOCKER|acrqiitasample.azurecr.io/sampleimage:1293"}
- FunctionAppに使用するDockerイメージを設定
パイプラインを動かした後に、FunctionのBashから環境変数を見ると、Dockerイメージの情報が登録されています。
$ echo $LINUX_FX_VERSION
DOCKER|acrqiitasample.azurecr.io/sampleimage:1293
Azure DevOpsのPipelineを作成する
このあたりを参考に、functionapp-from-acr.yml
をPipelineとしてAzure DevOpsで実行できるようにします。たぶんGUI見ればできると思います👍🏻
最初のパイプラインの作成 - Azure Pipelines | Microsoft Learn
PipelineからAzureリソースへの接続に使用するサービスコネクション作成
下記の接続に使用されます。
- Pipeline→ACR
- Pipeline→FunctionApp
AzureDevOpsでの作業
[ProjectSettings]-[Pipelines]-[Service connections]にて、「New service connection」をクリック

様々なAzureリソースに対し操作を行うことになるので、Azure Resource Manager
を選択。

推奨設定の Service principal (automatic)
を選択

ここで認証を求めるポップアップが出るので、ブロック設定になってると先に進めないので注意🧱
ARMで操作するスコープとなるサブスクリプションやリソースグループをプルダウンで選択し、サービスコネクション名を入力したら「Save」をクリック。

サービスコネクションが作成されます。

AzureADの「アプリの登録」画面におけるアプリケーション一覧に、作成したサービスコネクションが表示されます。隠してるところだらけで分かりづらくて申し訳ない......

作成したサービスコネクションの情報を、yamlパイプラインに反映
作成したサービスコネクション名をyamlのvariables
に記載します。
variables:
pool: 'ubuntu-latest'
- Azure.ServiceConnectionId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
+ Azure.ServiceConnectionId: 'DockerFuncPocServiceConnection'
FunctionAppがAzure Container Registryに接続するための設定
Azure Container Registry で管理者ユーザを有効化する
コンテナレジストリにログインする情報として使用します。

FunctionAppのアプリケーション設定に認証情報を追加
コンテナレジストリの管理者情報を、下記のアプリケーション設定に追加する。
DOCKER_REGISTRY_SERVER_PASSWORD
-
DOCKER_REGISTRY_SERVER_URL
※https://<acrname>.azurecr.io
形式 DOCKER_REGISTRY_SERVER_USERNAME

ここまでの設定で、ACRのDockerイメージをPullしてきて、Functionsとして動かすことができるはず。
パイプラインを動かして動作確認
Azure DevOpsでRun Pipeline
Pipelineの全てのJobがSuccessしていることを確認します。

Pipeline実行時にこのようなエラーが出力された場合は、以下いずれかの原因である可能性が高いです⚡️
There was a resource authorization issue: "The pipeline is not valid. Job BuildAndPushImage: Step Docker1 input azureSubscriptionEndpoint references service connection xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx which could not be found. The service connection does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz."
- サービスコネクション名かUUIDが間違っている
- Pipelineがサービスコネクションを使用するPermissionを持っていない
原因が2だと思われる場合、こちらの手順を参考に、PipelineにPermissionを与えてください。
ACRにPushされているかチェック
ACRのリポジトリを見ると、Pipelineの中でBuildされたImageがpushされています。

AzurePortal上で関数として読み込まれているかチェック
関数アプリの「関数」ブレードより、関数としてAzure側が識別しているかをチェックします。Dockerコンテナが正常に起動できていないと、ここに何も表示されないんです😵

実際に動作確認
関数が期待する動作をしてたらOKです💪
$ curl -X GET "https://docker-func-poc.azurewebsites.net/api/HttpTriggerSample?name=YOS0602&code=ABCDEFG=="
Hello, YOS0602. This HTTP triggered function executed successfully.
関数が読み込めていない or Dockerコンテナがうまく起動していないなどの場合
どこでコケてるか次第ですが、FunctionがImageをPullできていない場合、後述しているマネージドID有効化とIAMロール付与を試してみると良いかもしれません。
なお、npmを利用したアプリケーションでDockerを使用していると、「ユーザー名前空間再割当てエラー」が発生して、Dockerコンテナが上手く起動しないことがあります。詳細および対策はこちらの記事を参考にしてみてください。
App Service における Docker User Namespace remapping issues について - Japan PaaS Support Team Blog - #NPM 利用プロジェクトにおける ユーザー名前空間再割当てエラー
FunctionAppのマネージドIDを有効化
システム割り当てマネージドIDを有効化し、「保存」します。

「はい」をクリック。

AcrPullのIAMロールを付与
コンテナレジストリのIAMより、ロールの割り当ての追加を行います。
先ほど有効化したFunctionAppのマネージドIDを選択する。
「レビューと割り当て」を行う。
FunctionAppを再起動
az cliでも、Portalからでも良いです。設定が反映されるまで数分かかることもあるので待ちましょう⏱️
参考