Pre release 版 Azure Functions Runtime を動かす

以前から Pre release 版の Azure Functions Runtime を試そうとしては失敗してたが、ここにヒント(というかそのものずばり)があったので、その構築手順をまとめる。

Pre release 版?

まだ Azure Functions 上にリリースされていないランタイム*1
なので、ご利用は自己責任で。

構築手順

といっても、zip をダウンロードして展開するだけ。

まじめに書くと、

  1. Pre release 版の zip ファイルをダウンロードする
  2. ダウンロードしてきた zip ファイルをを好きなところに展開する
  3. func コマンドにパスを通す
  4. (Linux/Mac のみ) func コマンドに実行パーミッションを付与する

とこれだけ。

func コマンドを切り替える

上の手順で Pre release 版の func コマンドと Azure Functions Runtime が使えるようになるが、「展開してパスを通す」を素直にやってしまうと、以降も Pre release 版を使うことになるので、いざ正式リリース版を使う時にまたパスから直さないといけなくなる。

それはツライなので、Azure Functions Core Tools のバージョン マネージャー funcvm で Pre release 版も管理してみることにする。

github.com

funcvm 自体は npm i @anthonychu/funcvm -g でインストールする。

インストールされた funcvm は、 ~/.funcvm/download の下で Azure Functions Core Tools (つまり func コマンド) の各バージョンを管理している。
なので、この ~/.funcvm/download に適当な名前でディレクトリーを作って、その下に Pre release 版のパッケージを展開したらいいだけ。

~/.funcvm/download/
├── 3.0.4585
├── 4.0.4544
├── 4.x.xxxx-prerelease
└── funcvm-core-tools-version.txt

みたいな。

また funcvm では、

いつもはこのバージョンの func だけど、このディレクトリ配下ではこのバージョンの func を使う

みたいなことが、対象のディレクトリに .func-version ファイルを作ってその中にバージョン番号を書くことでできる。

この機能を使って、Pre release 版を試したいプロジェクトにだけ .func-version ファイルを作っておき、その中に上で作ったディレクトリ名 ( 上の例なら 4.x.xxxx-prerelease ) を書いておくと、そのディレクトリで実行した func コマンドは ~/.funcvm/download/4.x.xxxx-prerelease の下にある func を使ってくれる。
便利。

最後に funcvm で管理するためのバージョン番号に関する注意点。

まずディレクトリ名が正規表現 /^\d+\.\d+\.\d+/ にマッチしない場合 funcvm list に出てこない。
正直 ls ~/.funcvm/downloads とやれば出てくるディレクトリ一覧と同じなので、funcvm list で出せないから困るもの、って程でもないが、どうしても funcvm list で出力させたい場合は 4.9.9999 とかにしておけばいい。

また正規表現にマッチしていたとしても、そのバージョン番号が Azure Functions Core Tools の GitHub Releases に載っていない場合、funcvm use での .func-version の作成に失敗する。
これも、バージョン番号が書かれているだけのファイルなので、echo 4.9.9999 > .func-version で作ったとしてもその後の挙動に特に違いはない。

*1:Azure Functions 上のランタイムは、2022年6月3日現在、v4 系で v4.3.2、v3 系で 3.7.1

Deploy to Azure ボタンで Bicep ファイルからデプロイする (っぽく見せる powered by GitHub Actions )。

Deploy to Azure ボタン

GitHub リポジトリーの README.md などで見かけるこのボタン。

https://aka.ms/deploytoazurebutton

これ自体は単なる画像でクリックしても何も起きないが、Markdown 中に以下の書式を書いておくと、クリック一つで Azure ポータルでデプロイ画面が開くボタンになる。

[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/<URLEncoded ARM Template URL>)

肝は <URLEncoded ARM template URL> で、Azure プラットフォームがこの <URLEncoded ARM template URL> で指定された URL エンコード済み URL から ARM テンプレート (カスタム デプロイ テンプレート) を取ってきて、それを基にデプロイ画面を作るらしい。

docs.microsoft.com

Deploy to Azure ボタンの弱点

弱点はこの ARM テンプレートが JSON 形式のみ対応なこと。Bicep 形式の ARM テンプレートは、別途 JSON 形式にビルドしておいて、その URL を使う必要がある。
ドキュメントにもハッキリと。

多分、Azure プラットフォーム側に Bicep をビルドする仕組みがないのでは*1

なので、このボタンがあるリポジトリーには、Bicep ファイルがあったとしても、大抵 JSON もある。
編集は Bicep でして、ローカルでこのデプロイ ボタンのために JSON にビルドして、Bicep と JSON をセットでリポジトリーにあげているんだろう。

同じ内容なのに形式が違うものを一緒に管理する、というのは個人的に気持ち悪いものを感じる。

GitHub Actions で克服してみる

この気持ち悪さを解消する。

といっても、Bicep のビルド自体は GitHub Actions の中で bicep build (az bicep build) してしまえばいいので難しくない。

悩み

ただ問題は出来上がった JSON ファイルをどこに置くか。

GitHub Actions から Artifacts として保存してしまうと、zip ファイル形式なるし、ワークフローごとに Artifacts の URL も変わってしまう。
特に URL が変わってしまうと Deploy to Azure ボタンに指定する URL も変えないといけないので、README.md を都度編集するのか、となる。
そんなことしたくない。有効期限もある。

なので、

  1. JSON 形式のまま保存できる場所
  2. 繰り返し実行しても URL が変わらないプロセス (上書きしてくれる)

を満足する必要がある。

解決

ヒントは GitHub Pages の仕組みに。

GitHub Pages は開発用ブランチにある Markdown 等のコンテンツを、 HTML 等の Web ブラウザーで表示可能な形式に変換して別ブランチに配置し、それに *.github.io な URL を割り当てている。
コンテンツを更新しても URL は基本変わらない。

この仕組みをマルっとパクって、開発用ブランチでの管理は Bicep で行い、GitHub Actions で JSON にビルドした後、その JSON を別ブランチに置く。
Markdown 内の Deploy to Azure ボタンからは、別ブランチ上の JSON ファイルの URL を参照する。

おまけに、保存先のブランチ名と保存対象のディレクトリを指定するだけで、ブランチに保存してくれる便利な GitHub Pages 用の GitHub Actions がある。
先人に感謝。

github.com

ワークフロー完成

ということで、出来上がったワークフローがこちら。

on:
  push:
    branches:
      - main  # Set a branch name to trigger deployment
    paths:
      - '**.bicep'
  workflow_dispatch:

env:
  out_dir: public
  main_bicep_file: bicep/main_parent.bicep
  publish_branch: json_template

jobs:
  deploy:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - name: Build bicep
        run: |
          mkdir ./${{ env.out_dir }}
          az bicep build --file ${{ env.main_bicep_file }} --outdir ${{ env.out_dir }}
      - name: Publish to other branch
        uses: peaceiris/actions-gh-pages@v3
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          personal_token: ${{ secrets.PERSONAL_TOKEN }}
          publish_dir: ./${{ env.out_dir }}
          publish_branch: ${{ env.publish_branch }}

注意点として peaceiris/actions-gh-pages アクションで別ブランチにアクセスするには、 public_repo のスコープを持った Personal Access Token が必要。理由はわかってない。

あとは Markdown 内の Deploy to Azure ボタンに渡す URL を、JSON を保存したブランチ名とパスに合わせて指定するだけ。

https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2F<org>%2F<repo>%2F<branch>%2Fpath%2Fto%2Ftemplate.json

これでリポジトリーで管理する ARM テンプレートは、一見 Bicep ファイルだけになって、Bicep からデプロイしているように見える、かもしれない。

めでたしめでたし。

*1:あくまでも想像。ホントのところは知りません。

Web App に Node.js アプリをデプロイする GitHub Actions を速くする

Web App の [デプロイ センター] ブレードで、ソースコード等が置いてある GitHub レポジトリを選択すると、その Web App にデプロイするための GitHub Actions ワークフローを作成してくれる。

Node.js アプリの場合、既定では下記の yaml ファイルが GitHub レポジトリの .github/workflows に作成される。

# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - some-web-app-name

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Set up Node.js version
        uses: actions/setup-node@v1
        with:
          node-version: '16.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: .

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: 'production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v2
        with:
          name: node-app

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'some-web-app-name'
          slot-name: 'production'
          publish-profile: ${{ secrets.AzureAppService_PublishProfile_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }}
          package: .

この既定のワークフローのままだと、ところどころ遅いところがあるので速くしてみる。

1. devDependencies なモジュールはデプロイしない

build ジョブの 3 つ目の step npm install, build, and test の中で npm run build でアプリをビルドしている。
その為に npm installpackage.jsondevDependencies で指定しているモジュールも全てインストールしている。

これ。

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present

このインストールはビルドに必要だからしょうがないが、以降 node_modules に触れず、次の step, job まで進んでデプロイしているので、devDependencies なモジュールも含めて Web App にデプロイしていることになる。
当然、これらは運用環境に不要なモジュールなので、本当はビルドが終わったら削除するのが適切。

なので、

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present
          npm prune --production # `devDependencies` なモジュールを削除

と最後に npm prune --production を追加する。
これで dependencies なモジュールは残しつつ devDependencies なモジュールを削除できる。

2. artifact を zip 化する

既定では、ビルドした後のプロジェクトを、特にまとめることもなく、そのまま artifact として保存している。

ここ。

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: .

上の 1. で npm prune --production を実行してモジュールを減らしても、大抵node_modules 配下には大量のファイルがある。
なので、この artifact のアップロードをする actions/upload-artifact アクションと deploy ジョブ内のダウンロードをする actions/download-artifact アクションに時間がかかる。

こういう警告も出る。

そこで、build ジョブ内の actions/upload-artifact アクションの前に一度 zip ファイル化しておき、それを artifact としてアップロードする。
すると、アップロードするファイルが 1 つになるので速くなるし、release ジョブの actions/download-artifact アクションでも 1 つの zip ファイルのダウンロードになり、こっちも速くなる。 さらに azure/webapps-deploy アクションで Web App にデプロイする時も ダウンロードしてきた zip ファイルをそのまま使えば、デプロイするファイルが 1 つになるので速くなる。

build ジョブ

      - name: Zip artifact for deployment
        run: zip release.zip ./* -qr # 除外したいディレクトリがあるなら -x オプションで指定

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: release.zip

release ジョブ

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'some-web-app-name'
          slot-name: 'production'
          publish-profile: ${{ secrets.AzureAppService_PublishProfile_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }}
          package: release.zip

完成形

以上、2 点を反映させた完成形がこちら。

# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - some-web-app-name

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Set up Node.js version
        uses: actions/setup-node@v1
        with:
          node-version: '16.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present
          npm prune --production # `devDependencies` なモジュールを削除

      - name: Zip artifact for deployment
        run: zip release.zip ./* -qr # 除外したいディレクトリがあるなら -x オプションで指定する

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: release.zip

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: 'production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v2
        with:
          name: node-app

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'some-web-app-name'
          slot-name: 'production'
          publish-profile: ${{ secrets.AzureAppService_PublishProfile_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }}
          package: release.zip

Azure Functions in Javascript (Node.js) で SQL binding を試した

C# だけに来てた Azure Functions の SQL binding が、Javascript/Typescript (Node.js) でも動くようになったらしい*1ので試してみた。


https://github.com/Azure/azure-functions-sql-extension/releases/tag/v.0.1.304-preview

何ができるのか

Input/Output binding ができる。トリガーはできない。でもこれは C# も一緒。
トリガーはできなくても、これまでは tedious*2*3 とかを使って自前で接続を書いていたはずなので、それよりずっと楽になる。

が、肝心の JS での設定例が main ブランチに見当たらないので発掘してみると、開発用ブランチで発見。

https://github.com/Azure/azure-functions-sql-extension/tree/maddy/jsSamples/samples/samples-js

これを元に*4サンプルを作ってみた。

github.com


環境構築 (2022/05/04 追記)

まずは SQL binding 用の拡張機能をインストール。

func extensions install --package Microsoft.Azure.WebJobs.Extensions.Sql --version 0.1.304-preview

binding を使うために拡張機能が必要なので、これはわかる。

次がわからない。かつ必須。
上のコマンドでできたであろう ./bin/extensions.deps.json を同じディレクトリーの function.deps.json としてコピーする。

cp ./bin/extensions.deps.json ./bin/function.deps.json

もしかしたら cp ではなく mv でもいいのかもしれないが、念のため元のファイルも残している。

今まで拡張機能のインストールでこの手順を踏んだことがないが、この ./bin/function.deps.json がないと動かないので、今は必要なんだろう。
どこからどういう経緯で参照されているのか、 extensions.deps.json のままじゃダメなのか、など諸々気になるが、まだプレビューということで優しく見守る。

作ったサンプルでは、vscodetasks.json でこの手の処理を定義しておいたので、F5 デバッグで自動的にやってくれるはず。

(追記ここまで)


Binding 設定

サンプル/リファレンスから読み解いた設定方法は以下の通り。

Input binding

    {
      "type": "sql",
      "direction": "in",
      "name": "employees",
      "CommandType": "Text",
      "CommandText": "select * from [dbo].[Employees]",
      "ConnectionStringSetting": "SqlConnectionString"
    },

ConnectionStringSetting に指定している SqlConnectionStringSQL サーバーの接続文字列を収めたアプリケーション設定名*5
CommandText は Binding でデータを取る時に使われる SQL 文。

https://github.com/Azure/azure-functions-sql-extension#input-binding

Output binding

    {
      "type": "sql",
      "direction": "out",
      "name": "employees",
      "CommandText": "dbo.Employees",
      "ConnectionStringSetting": "SqlConnectionString"
    },

ConnectionStringSetting は Input Binding と同じ。
CommandText は Output binding でデータを突っ込むテーブル名。

https://github.com/Azure/azure-functions-sql-extension#output-binding

Output Binding でハマったポイント

注意: 0.1.311-preview で解消済み

最初、Output binding で例外*6が出ていたので issue を立てたら、「カラム名を全部小文字にしてみて」と。
回避策がサクッと出てきたので、問題は把握していていたんだと思う。なので、そう時間はかからずに直るだろう。

OK:

全部小文字だと成功する。

    context.bindings.employees = JSON.stringify([{
      "employeeid": 1,
      "firstname": "Hello",
      "lastname": "World",
      "company": "Microsoft",
      "team": "Functions"
    }])

NG:

DB のテーブルに合わせる感じで大文字を使っちゃうとダメ。

    context.bindings.employees = JSON.stringify([{
      "EmployeeId": 1,
      "FirstName": "Hello",
      "LastName": "World",
      "Company": "Microsoft",
      "Team": "Functions"
    }])

※ テーブル設計

まだプレビューなので、早期の GA を期待しつつ、温かく見守ろう。

*1:と言っても、C# でもプレビュー。

*2:https://www.npmjs.com/package/tedious

*3:なぜこの名前なのか。意味を知ってびっくりした。

*4:ほぼコピー。

*5:ローカル開発なら local.settings.json に書く。

*6:曰く「Primary Key が null だぞ」という内容。

PHP8/Laravel8 アプリを App Service on Linux で動かす方法 (2022/04 暫定版)

どうやら App Service on Linux で PHP8 / Laravel8 のアプリを動かす時に、いろいろ気を付けることがあるようなので、回避策含め備忘録としてまとめておく。

主な注意点

大きく分けて二つ。

  1. PHP を動かす Web サーバーの設定
  2. ビルド方法

1. Web サーバー設定

App Service on LinuxPHP のコンテナー イメージは、PHP7 までは apache だったのが nginx に変更になった。
それに伴って、PHP を動かす設定も変えないといけないらしい。
# ちなみに、OS は Debian 10 (buster)

具体的には、Startup Command として、nginx へのリクエストを PHP アプリへルーティングする?ための設定を変更する処理を指定する。

まずは nginx の設定変更。
App Service on Linux 内の nginx は /etc/nginx/sites-available/default を見ているので、これをコピーして PHP8/Laravel8 の構成に合わせて変更する。

これをアプリケーション プロジェクトの scripts/default として保存しておく。

scripts/default

server {
    #proxy_cache cache;
        #proxy_cache_valid 200 1s;
    listen 8080;
    listen [::]:8080;

    # 変更 1) index.php があるディレクトリを指定。これは Laravel8 の場合。
    root /home/site/wwwroot/public;  
    index  index.php index.html index.htm;
    server_name  example.com www.example.com; 

    location / {            
        index  index.php index.html index.htm hostingstart.html;
        # 変更 2) 詳細は https://docs.microsoft.com/en-us/answers/questions/542749/deploying-an-app-service-with-laravel-8-and-php-8.html
        try_files $uri $uri/ /index.php?$args;
    }

    # 以下デフォルトのまま
}

次に scripts/default/etc/nginx/sites-available/default に戻して、nginx を再起動し、変更した設定を反映するスクリプトを書く。
同じく scripts/startup.sh としておく。

scripts/startup.sh

#!/bin/bash

cp /home/site/wwwroot/scripts/default /etc/nginx/sites-available/default
service nginx reload

最後に App Service 側の Startup Command を使って、起動時に scripts/starup.sh を実行するよう設定する。

これで nginx と PHP との間の設定は解決。

2. ビルド設定

2022 年 4 月現在、PHP8 のアプリは既定の設定のままだと App Service のビルド エンジンである Oryx でのビルド (compser install コマンドの実行) ができない。

下記 Oryx の GitHub リポジトリの issue にある通り、Kudu コンテナー*1上でのビルド時に必要なファイル libonig.so.4 が見つからずエラーになってしまう。

github.com

追えるとこまで頑張ってみた感じ、これは、

  1. Kudu コンテナー上での composer install 実行に使用される PHP バイナリーが libong.so.4 とリンクしている
  2. リンクされている libonig.so.4 は、Debian 9 (コードネーム stretch) 向けのライブラリーである
    https://packages.debian.org/search?keywords=libonig
  3. でも Kudu コンテナーは Debian 10 (コードネーム buster) ベースのコンテナーである

    だから libonig.so.4 は入っていないし入れられない。

なので、この PHP バイナリーは今の Kudu コンテナーでは実行できない、という話。
たぶん PHP バイナリーをビルドしている環境が stretch だったのではないか、という疑惑。

一応、修正済みの Oryx が GitHub 上にはリリースされているので、Azure 上への次のリリース*2を待てばビルドできるようになるはず。

github.com


(以下 4/30 追記)

上記修正が適用されているかどうかは、Kudu コンソール上で oryx --version を実行してみて、ReleaseTagName20220427.1 かそれ以降になっているかを見たらいいっぽい。

(追記ここまで)


それが待てない人は以下の回避策をお試しあれ。

回避策 1. Oryx でビルドしない

最初はシンプルな策、Oryx でのビルドをやめる。
Oryx でビルドできないんだから、ローカルや CI 環境でビルドして、ビルド済みのものを App Service にデプロイしてしまえばいい。

App Service のアプリケーション設定で SCM_DO_BUILD_DURING_DEPLOYMENTfalse を指定すると、デプロイ時のビルドは無効になる。

回避策 2. Kudu を stretch ベースにする

issue で Corp. のエンジニアから出ている方法で、アプリケーション設定で SCM_DISABLE_BUSTER_KUDUtrue を指定する。
すると、Kudu のコンテナーが stretch ベースのものになるらしい。

試してみる。

既定の状態は buster なのは上のスクショの通り。

次に SCM_DISABLE_BUSTER_KUDUtrue を指定した状態。

おー確かに stretch になった。マジかよ知らなかった*3

これで libonig.so.4 が使えるので、composer install はできるはず。

ただし、ビルドは stretch ベースのコンテナー上で行われる一方で、実際のアプリケーションは buster ベースのランタイム コンテナー上で動くことになるので、ライブラリーによってはこの違いで動かなくなる(かもしれない)。

回避策 3. PHP バイナリー自体を置き換える

Kudu コンテナー内での composer install に使われている PHP バイナリーは、上記の通り stretch 上でビルドされたものと推察されるが、これは Oryx を起動した時にここからダウンロードして Kudu コンテナー内に展開したもの。
これを buster 上でビルドされた PHP バイナリーとに置き換えてしまえばいい。

そうすると

  • buster ベースの KutuLite コンテナー
  • buster 向けの PHP バイナリーを使って composer install を実行してビルド
  • ランタイムも buster

という感じで全て buster で統一できてハッピー。

buster 向け PHP バイナリーは自前でビルドしてデプロイ パッケージに入れることもできるし、実は stretch 向けの PHP バイナリーと同じところにあるのでそっちも使える。

「置き換え」処理そのものは、Oryx が持っている「ビルド前後に任意のスクリプトを実行する」仕組みを使う。

github.com

PHP バイナリーの置き換えはビルドの前にやらないといけないので、 PRE_BUILD_SCRIPT_PATHPRE_BUILD_COMMAND を使う。
単発コマンドでいい場合、またはワンライナーで書ける場合は PRE_BUILD_COMMAND がいいが、スクリプト ファイルを書く場合は PRE_BUILD_SCRIPT_PATH でファイルを指定する。

例えば、下記のようなスクリプトscripts/prebuild.sh として配置するなら、PRE_BUILD_SCRIPT_PATHscripts/prebuild.sh と指定する。

scripts/prebuild.sh

#!/bin/bash

PHP_VERSION=8.0.17

cd /tmp
mkdir -p /tmp/oryx/platforms/php/${PHP_VERSION}
curl -O https://oryx-cdn.microsoft.io/php/php-buster-${PHP_VERSION}.tar.gz
tar -xzf /tmp/php-buster-${PHP_VERSION}.tar.gz -C /tmp/oryx/platforms/php/${PHP_VERSION}

これで、composer install の直前に buster 向けの PHP バイナリーに差し替えができる。

懸念点は Corp. のエンジニアから「そっちのバイナリーは、依存モジュールが変わっているから、何が起こるか。。。」(超意訳)と言われたこと*4だけど、試した限り composer install くらいなら動く。
もちろん自前でビルドして持ち込む場合は自己責任で。

startup.sh など scripts 配下もまとめて GitHub に公開してみた。

github.com

一応、修正リリースと同じアプローチのはず。

ビルド エラー回避策のまとめ

ここまで書いたビルド設定に関する 4 種類の回避策のまとめ。

# 回避策 設定方法 メリット デメリット
1 Oryx でのビルドをやめる SCM_DO_BUILD_DURING_DEPLOYMENT = false ビルド環境の自由度が高い CI 等のビルド環境が別途必要
2 stretch ベースの Kudu コンテナー SCM_DISABLE_BUSTER_KUDU = true 設定簡単 ビルド/ランタイム環境でベース OS が異なり、アプリ実行時に不整合が起こるかも。
3 ビルド用 PHP バイナリーの変更 PRE_BUILD_SCRIPT_PATH = <script_path> ビルド/ランタイム環境の統一感 動くことは動くがエンジニアが前向きじゃない。自己責任。
4 次リリースを待つ - 公式方法。 「いつから使える」ってはっきりしたことは言えない

あなたはどれを選びます?

*1:KuduLite と言われるコンテナー イメージ。1. の nginx が動くアプリ コンテナーとは別コンテナー

*2:2022 年 5 月か 6 月かな?

*3:App Service にはこういう知らない設定がいっぱいある

*4:なんで公開しているんですかねぇ。。。

Teams 会議で AfterShokz のマイクをミュートすると、ビープ音が鳴りませんか?

はじめに

今まで何ともなかったのに、ホントについ最近、AfterShokz OpenComm を使って参加した Teams 会議中にミュートにすると、ビープ音が鳴るようになってしまった。

よくよく思いかえせば、1 年位前に同僚から似たような症状を聞いていたけど、自環境では発生してなかったのですっかり他人事で忘れてた。

なぜ突然この症状が出たのかはわからないが、AfterShokz がマイク ミュートの制御を PC から受け取るとビープ音が鳴るのは、どうやら元々そういう仕様らしい。
で、何の拍子かで(Teams のアップデート?)、自分の Teams がミュートの指示を出すようになったんだろう。

AfterShokz はファームウェア アップデートの仕組みは持ってないだろうから、本来はこれが正しい状態なのかもしれない。

そうはいってもビープ音はうるさい。他の人の発言を遮らないためにマイク ミュートしているのに、ビープ音が邪魔で集中できない。

解決策

VB-Audio Virtual Cable という仮想オーディオデバイスを使おう。

こんな感じに、Teams のスピーカーは、AfterShokz を直接指定し、マイクは仮想オーディオ出力に指定する。AfterShokz のマイクは仮想オーディオ入力につなぐ。

こうすると、Teams からのマイク ミュートの制御が(なぜか)AfterShokz まで伝搬しないので、AfterShokz はミュートに気づかず、結果ビープ音もならない。

手順

ググってみるものの、「仮想オーディオ デバイスを使えばいいよ!」という情報は見つかるが、どう使うのかいまいち書いていない。
あっているかわからないが、とりあえず動いている設定を書いておく。

1. インストール

まずインストーラをダウンロードする。

Mac は持っていないので、Windowsインストーラが入った zip ファイルをダウンロードする。
2015 年のものらしいが、Windows 11 でも問題なし。互換性バンザイ。

vb-audio.com

zip ファイルを展開すると、その中のインストーラを実行する。
今どきは 64 bit OS なので、VBCABLE_Setup_x64.exe で。

インストールが完了したら、指示に従って再起動。

2. デバイス設定

設定 アプリを開いて システム > サウンド と辿り、その一番下 サウンドの詳細設定 を開く。

サウンド のプロパティ シートが開くので、AfterShokz のデバイスを選択して、その中の 聴く タブの このデバイスを聴く にチェックを入れ、このデバイスを使用して再生するCABLE Input を選択する。

3. Teams 設定

上で書いた通り、Teams では、スピーカーはヘッドセットのものを使って、マイクは CABLE Output を選択する。

これで Teams でミュートしてもビープ音に悩まされずに済む。

おわりに

もしかしたら 仮想オーディオ デバイスを挟むことで、AfterShokz ならではの機能がつぶれてるかもしれないけど、今のところ困ってないので、そんな機能があっとしても使ってなかったんだろう。

Bicep メモ

f:id:horihiro:20220402143937p:plain

都度検索するのが大変なので、メモとして。

1. 子リソースの書き方

例えば Microsoft.Web/sites/sourcecontrols

1-1. 入れ子にする。

親リソースの中に resource で子リソースを宣言する。

resource appService 'Microsoft.Web/sites@2021-02-01' = {
  name: webSiteName
  location: location
  kind: 'app'
  properties: {
    :
  }
  resource web 'sourcecontrols@2021-02-01' = {  // ここから子リソース
    name: 'web'
    properties: {
      :
    }
  }
}

1-2. 子から親を参照する。

子リソースは親リソースと同じレベルで宣言する。
加えて parent で親リソースを参照する。

resource appService 'Microsoft.Web/sites@2021-02-01' = {
  name: webSiteName
  location: location
  kind: 'app'
  properties: {
    :
  }
}

resource web 'Microsoft.web/sites/sourcecontrols@2021-02-01' = {
  parent: appService // ここで親リソースを参照する
  name: 'web'
  properties: {
    :
  }
}

2. ロール割り当て

デプロイ時にマネージド ID を有効にして、同時にそれを他のリソースのロールをhttps://www.google.com/url?sa=i&url=https%3A%2F%2Fdevblogs.microsoft.com%2Fdevops%2Fazurefunbytes-intro-to-bicep%2F&psig=AOvVaw0zWLc6KaUdbfEbb_8IGnfO&ust=1648963806189000&source=images&cd=vfe&ved=0CAgQjRxqFwoTCLjLpcrU9PYCFQAAAAAdAAAAABAD割り当てる。

Web App とストレージ アカウントのデプロイタイミングで、Web App のマネージド ID に ストレージ アカウント共同作成者 を割り当てる例。

param roleNameGuid string = newGuid()

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

resource appService 'Microsoft.Web/sites@2021-02-01' = {
  name: webSiteName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  kind: 'app'
  properties: {
    :
  }
}

// https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/scenarios-rbac#role-assignments
resource assignRole 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
  name: roleNameGuid
  scope: storageAccount
  properties: {
    principalId: appService.identity.principalId
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') // "ストレージ アカウント共同作成者" のロール定義 ID
    // https://docs.microsoft.com/ja-jp/azure/role-based-access-control/role-assignments-template#new-service-principal
    principalType: 'ServicePrincipal'
  }
}