しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Pipelines を使った Azure Web Apps / Azure Functions (Linux) へのデプロイを試した

Run From Package に関するドキュメントを眺めていたら、Linux の Premium Plan に対して Run From Package が使えそうな記述があったので試しました。

暫く Linux の App Service を触っていなかったので、知識のアップデートを兼ねてます。

まずは Linux の App Service について簡単にまとめました。ちょっと前に Consumption Plan が GA したり、Premium Plan が Preview で使えるようになったりと、インフラ側は Windows とほぼ同じになっています。

特に Premium V2 が値下げされたので、Windows の Premium V2 よりかなり安く使えます。*1

  • Web Apps (Linux)
    • App Service Plan (F1 / B1-B3 / S1-S3 / P1v2-P3v2)
  • Azure Functions (Linux)
    • Consumtpion Plan
    • App Service Plan (F1 / B1-B3 / S1-S3 / P1v2-P3v2 / EP1-EP3)

この中で EP1-EP3 が Premium Plan を表しています。少し混乱しそうですが、Premium Plan は App Service Plan のインスタンスサイズの一つです。

作成した Web Apps / Azure Functions へのデプロイ方法は以下の 3 種類が選べます。

  • Code
    • git push でビルド & デプロイ
    • Zip を作成してデプロイ
  • Docker
    • Docker Image を用意してデプロイ

今回は Docker を使わない方法を扱うので、Zip を使ったデプロイを行います。ちなみに Consumption と Premium Plan*2 では Docker Image を使った Azure Function のデプロイはサポートされていません。

Azure Pipelines には Web Apps / Azure Functions のデプロイタスクが用意されているので簡単です。

Web App の場合

Linux Web App へのデプロイは Windows Web App とほぼ同じなので難しくないです。デプロイタスクで appTypewebAppLinux に設定すれば、よい感じに ZipDeploy を実行してくれます。

trigger:
- master

variables:
  BuildConfiguration: 'Release'

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'publish'
    publishWebProjects: false
    projects: '**/*.csproj'
    arguments: '-c $(BuildConfiguration) -o $(Build.SourcesDirectory)/publish'
  displayName: 'Build Application'

- task: AzureWebApp@1
  inputs:
    azureSubscription: 'Azure Sponsorship'
    appType: 'webAppLinux'
    appName: 'linuxwebapp-1'
    package: '$(Build.SourcesDirectory)/publish/**/WebApplication1.zip'
  displayName: 'Deploy to Web App (Linux)'

こんな感じで YAML を書けば終わりなのですが、Premium Plan で Run From Package が使えると書いてあったので、Web Apps でも使える気がしたので設定を追加して試しました。

Configuration からサクッと WEBSITE_RUN_FROM_PACKAGE = 1 を追加します。

f:id:shiba-yan:20190901152221p:plain

設定後に Azure Pipelines から Zip Deploy を実行すると、パッと見は問題なく Run From Package として動作していました。

SSH でコンテナにアクセスして、wwwroot 以下にファイルを作ろうとするとエラーになります。

f:id:shiba-yan:20190901152232p:plain

ちゃんと Zip が Read-only としてマウントされていて動作はしましたが、Web Apps に関しては正式にサポートされている方法ではないので、何かしら問題はありそうです。

Azure Functions (Consumption Plan / App Service Plan) の場合

Azure Functions でも Web Apps と同様にデプロイタスクを使えば、自動的に Zip Deploy が実行されるので簡単なのですが、Consumption Plan のみ Run From Package としてデプロイされるようになっています。

古い情報ですが、Linux の Consumption Plan では Run From Package のみ使えると書いてあります。ただし URL を指定して、外部からパッケージを取得する形の Run From Package です。

ローカルの Zip を使う Run From Package を試しては見ましたが、ランタイムが起動しなくなったので GA した現在も非対応のようです。大人しくデプロイタスクに従いましょう。

App Service Plan の場合はこれまで通り wwwroot 以下に Zip が展開されます。

trigger:
- master

variables:
  BuildConfiguration: 'Release'

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'publish'
    publishWebProjects: false
    projects: '**/*.csproj'
    arguments: '-c $(BuildConfiguration) -o $(Build.SourcesDirectory)/publish'
  displayName: 'Build Application'

- task: AzureFunctionApp@1
  inputs:
    azureSubscription: 'Azure Sponsorship'
    appType: 'functionAppLinux'
    appName: 'linuxconsumption-1'
    package: '$(Build.SourcesDirectory)/publish/**/FunctionApp1.zip'
  displayName: 'Deploy to Consumption Plan (Linux)'

Consumption Plan の場合はデプロイタスクを使うと、URL を使った Run From Package としてデプロイされていることが確認できます。

SAS を使っていますが、有効期限は 2029 年とかでしたので 10 年間は有効です。

f:id:shiba-yan:20190901153217p:plain

10 年間もほったらかしにすることはないと思いますが、間違って Blob を消さないよう気を付けましょう。

デプロイが頻繁に行われる場合はあっという間にファイルが増えるので、Lifecycle Management を使って古い Blob を消す処理などが必要になりそうです。

Azure Functions (Premium Plan) の場合

Premium Plan では作成時に Run From Package を使ったデプロイが必要だと警告が出ます。Premium Plan は App Service Plan と Consumption Plan を足したような動きをするので理解できます。

f:id:shiba-yan:20190901012321p:plain

Consumption Plan と同じようにデプロイタスクを使えば、勝手に Run From Package としてデプロイしてくれるかと思っていましたが、タスクが Premium Plan に対応していませんでした。

デプロイ方法を変更できないため、仕方なく Azure Functions Core Tools を使ってデプロイします。

Azure Pipelines の Hosted Agent には Azure Functions Core Tools がインストールされていないので、ドキュメントにあるコマンドの通りにインストールしておきます。

デプロイには認証済みの Azure CLI が必要なので、実行は AzureCLI タスクで行います。

trigger:
- master

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: CmdLine@2
  inputs:
    script: |
      wget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
      sudo dpkg -i packages-microsoft-prod.deb
      
      sudo apt-get update
      sudo apt-get install azure-functions-core-tools
  displayName: 'Install Azure Functions Core Tools'

- task: AzureCLI@1
  inputs:
    azureSubscription: 'Azure Sponsorship'
    scriptLocation: 'inlineScript'
    inlineScript: |
      cd FunctionApp1/FunctionApp1
      func azure functionapp publish linuxpremiumapp2 --csharp
  displayName: 'Deploy to Premium Plan (Linux)'

地味にはまった点として func azure functionapp publish を実行した時に、実行に使うランタイムを判別できないと言われてエラーになったことです。

ローカルだと local.settings.json にランタイム名が含まれているので問題ないですが、自動生成された .gitignore で除外されているのでエラーとなってしまいました。流石にこのファイルを含めたくないので、コマンド実行時に --csharp オプションを付けて対応しました。

これで Premium Plan でも Run From Package を使ったデプロイが行えるようになりました。GA までにはデプロイタスクでも対応されるとは思いますが、それまでは準備が面倒ですが Core Tools を使ってデプロイする必要があります。

*1:元々 S1 ではメモリ少なすぎて辛かったので、その辺りを考慮した可能性あり

*2:Create Azure Functions on Linux using a custom image | Microsoft Docs