はじめに
Javascript初心者がAzureFunctionsの開発を行った際に嵌ったポイントをお伝えしたいと思います。
前提
実際の案件概要は以下のような感じです
- 概要:検証用のPoCアプリの開発
- 構成:SPA(Vue.js) + AzureFunctions + CosmosDBでのサーバレスアプリケーション(基本PaaSで)
- ローカル環境:Windows + VisualStudioCode
- 言語:Javascript(フロントエンドとバックエンドで統一したかった)
- 開発時期:2019年8月~2019年11月
※本記事で書いている内容は現在時点でも修正されているかもしれませんし、今後も修正される予定のものも多いので、あくまで参考程度に見てもらえると助かります
本記事で触れるテーマ
- ベースOSの選択
- ローカル環境構築について
- Proxyを突破する技術
- Lintの設定
- Bindingsの落とし穴
ベースOSの選択
Functionsのリソースを作成する際、事情がない限りはWindowsを指定する形で良いと思います。
Azureの開発速度は凄まじいですが、優先順位はとしてはどうしてもWindows > Linux、 C# > その他の言語になると思います。
(ドキュメント量や対応済・未対応のIssueの数から鑑みて)
嵌ったポイント①:LinuxOSを選択
個人的にはLinuxOSの方が慣れていたので、今回の開発でもLinuxを使おうと思い、インフラ担当の方にLinuxでリソースを作製していただきました。
WebApps
AzureWebAppsでフロントエンドを作成した際、ベースOSとしてlinuxを選択してはまりました。
【事象】アプリをリリースしても資産が置き換わらない
【対策】資産の中に入ってコマンド打って修正する
【参考URL】https://stackoverflow.com/questions/54236862/cannot-get-index-html-azure-linux-web-app
上記のstackoverflowを見てヒイヒイ言いながらexpressの設定をしたおかげで、なんとか対応すること出来ました
Functions
今回CosmosDBを利用する必要があったため、Functionsを利用する際に
ExtensionBandlesをインストールする必要がありました。
【参考URL】https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-register
LinuxベースだとHTTPTriggerとTimerTriggerの選択肢しか表示されないため、手動インストールができません(Windowsだと出てきます)
色々試行錯誤しましたが解決策がわからなかったため、結局リソースをWindowsベースで作り直してもらうことで対応しました。
将来的には改善されるかもしれませんが、特別な理由がない限り現時点でのベースOSはWindowsで良いと思います(そもそもOSを意識していては、折角のサーバレスが台なしな感じもしますが・・・)
ローカル環境構築について
次に嵌まったポイントとしてはローカル開発環境の構築です。
基本的にIDEとしてはVisualStudioCode(以下vscode)を使います。
素直にMicrosoft製品で固めます。
基本的にはazure-functions-core-toolsをnpm install
すればローカルのデバッグはできるのですが、
やはりというか、認証Proxyで引っかかりました。
認証Proxyの仕様については各社によって差異があるので、以下のやり方で突破可能か不明ですが、ある程度参考にしてもらえたらと思います。
認証Proxyのどこで引っかかるのか
AzureFunctionsを起動する際、ローカルでもクラウドでもExtension.Bandle(C#で書かれたライブラリ)が必要になります。
AzureFunctions自体は様々な言語で開発することができますが、基本的にはExtension.Bandleのライブラリを呼び出して様々な処理を実行させています。
-
func start
コマンドでFunctionsを起動する
- ローカルでExtension.Bandleを探す
- ない場合、ダウンロードしに行く
- Extension.Bandleのダウンロードが完了次第、各Functionが起動する
認証Proxy化で引っかかるのは3の部分です。
Extension.Bandleをダウンロードする処理はazure-functions-core-tools
の機能の中で行われるのですが、これがおそらくC#で書かれている処理のため、ローカルで環境変数にProxyを設定しても処理が途中で止まり、タイムアウトとなってしまいました。
ExtensionBundlesの入手方法
ではどうしたか、というとAzure Functionsの実行に必要なExtensionBundlesをネット経由で入手する方法が分かりました。
以下のGitHubのページからリリース版のzipファイルをローカルにダウンロードし、ローカルのC:\Users\[ユーザ名]\AppData\Local\Temp\Functions\ExtensionBundles
の配下(パスについてはユーザごとに変わる可能性がありますので、VSCODEの画面からご確認ください)に解凍後のフォルダを置くことで、ExtensionBundleを用いたFunctionsの実行が可能となります
これでなんとかローカルでデバッグができる状況まで漕ぎつけました。
Lintについて
ESLintのデフォルトのルールを採用すると、Azure Functionsの性質上、必ず通せないエラーが発生します。
https://github.com/eslint/eslint/issues/11723
Functionsを記述する場合、引数として、context
を指定します。これはお約束
module.exports = async function(context, req) {
そしてDBやBLOB、Queueに結果を出力しようとすると以下のような記述になります。
context.bindings.XXXX = AAAAA
このような記述をするとLintのルールの再代入にあたるため、エラーとなってしまいます
今回私はルールの方を無効化する対応をとってしまいましたが、
functionsをreturn文で終わらせる書き方をすると、ここのLintのルールを守りつつコードを書くことが可能になると思います。
【参考URL】https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-node
Bindingsの落とし穴
AzureFunctionsを使う上ではfunction.jsonに記述するBindings指定が本当に便利です。
わざわざDBコネクションを確立したり、オブジェクトストレージのパスを気にしたりみたなところに気を遣わずにプログラムを作成することができます。
※Bindingsを使わない場合、各関数にライブラリをimportしないといけなくなるため、処理を軽量化することが難しくなります
【参考URL】https://github.com/MicrosoftDocs/azure-docs.ja-jp/blob/master/articles/azure-functions/functions-bindings-cosmosdb.md#tab/javascript
function.jsonでCosmosDBにつなぐ際、SQL文を書かずともある程度データを選択して抽出することができます。
書き方は以下のようになります。
{
"type": "cosmosDB",
"name": "index.jsの中で使う変数名",
"databaseName": "DB名",
"collectionName": "コレクション名",
"connectionStringSetting": "DB接続文字列",
"direction": "in",
"id": "{hugahuga}",
"partitionKey": "{hogehoge}"
},
この書き方をすると、Pertition=hogehoge
内のid=hugahuga
のデータをオブジェクトとして抽出してくれるので非常に便利です。
が、実際に動かすうえでは以下のような制約がありました
- id指定とPertition指定は同時にやらないといけない
- id指定のみだと実行時にエラーとなる
- Pertition指定のみだとエラーにならないが、実際はPertition指定が効いてないため、全件検索と同じになってしまう
対象データをidで一意に特定する場合は、idとpertitionKeyの併用は良いかもしれませんが、素直にSqlQueryパラメーターを使った方が良いかもしれません
最後に
つらつらとめんどくさかったことばかり書いていますが、
サーバレスによるアプリ開発は開発が不慣れな人でもある程度の速度を持って開発できるし、インフラやミドルレイヤを気にしなくてよいなど良いこともたくさんあります!
Javascriptは個人的にはとっつきやすい言語(フロントが絡むとまた別かもしれませんが)でした。
ぜひとも、Javascript + AzureFunctionsによるサーバレスデビューを!