しばやん雑記

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

Azure Functions の開発環境を Visual Studio Code の Dev Container を使って構築する

Azure Functions は C# と Node.js を使う場合には開発環境をサクッと構築できるのですが、今回 Python を使って開発しようとしたときに開発環境で割とはまったので、自分が最適だと思った VS Code の Dev Container を使う方法をメモしておきます。

開発環境を丸ごと Docker Image として定義して、そのコンテナー内部で VS Code を使って開発が行えるので環境構築の手間が大幅に省けます。VS Code と Docker が使える環境があればよいので準備も簡単です。

Windows の場合は WSL 2 と Docker Desktop WSL 2 Backend を有効化すればよいです。詳細な手順は Docs に用意されているので、こっちを参照してください。

最近は Node.js などを使った開発は WSL 2 上で行うようになったので、今回のように Dev Container を使う準備はほぼ整っていました。マシンスペックが十分であれば簡単に準備できるはずです。

新しい Azure Functions プロジェクトを作成

Python を使って Azure Functions の開発を行うドキュメントは用意されているので、基本的にはこれに従って行けばよいのですが、デフォルトでは venv を使うようになっているのでその点だけは注意が必要です。

ドキュメントには Python バージョンの管理に関しては特に書かれていないので、特定のバージョンを使う場合には若干戸惑うかもしれませんが、今回は Dev Container を使うので気にする必要はありません。

VS Code で新しく Azure Functions プロジェクトを作成すると、途中で Python のバージョンを選択する必要が出てくるので、一番下にある "Skip virtual environment" を選びます。

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

venv の利用をスキップしたので、作成されるプロジェクトは以下のようにシンプルなものになります。

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

このままではシステムデフォルトの Python バージョンが使われてしまうので、ここから先は Dev Container の設定を追加して Docker Image のタグ名でバージョンを固定していきます。

Azure Functions 向けの Dev Container を作成

VS Code に Remote Containers 拡張をインストールしているとコマンドパレットから各種コマンドを実行できるようになります。インストールしていない場合は "Remote Development" 拡張パックを入れると、WSL / Container / SSH がまとめて入るので楽です。

新しくプロジェクトに Dev Container の設定を追加するためには、コマンドパレットから "Remote-Containers" の "Open Container Configuration File" を選択していきます。

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

選択すると利用可能な Dev Container のテンプレートがいくつか表示されますが、一番下にある "Show All Definitions" を選ぶと Azure Functions 向けのテンプレートが見つかるはずです。

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

今回は Python を使うので "Azure Functions & Python 3" を選んでおきます。これは公式のイメージが使われているので、Azure Functions 上と同じ環境となるので安心です。

ここで表示される Dev Container のテンプレートは以下で管理されているものになります。用意されているか事前に確認したい場合は、ここを検索すると良いです。

Dev Container の定義が作成されると VS Code の通知に Remote Container として開きなおすかを聞かれるので、素直に Reopen in Container を選んで開きなおします。

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

初回は必ず Docker Image のビルドが走るので少し時間がかかります。ログを見ながら待ちます。

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

Container の準備が完了して開きなおされると Remote Explorer に起動中の Dev Container が表示されます。

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

デフォルトでは Python 3.8 の Azure Functions 向けイメージが使われますが、その辺りは .devcontainer 以下にある Dockerfile でバージョンを指定できるようになっています。

テンプレートから作成された状態の Dev Container はそのままだと requirements.txt に書かれたパッケージがインストールされないので、devcontianer.json にある postCreateCommand を修正して pip install を実行するようにします。

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

Dev Container 定義の修正後はコンテナーの再ビルドが必要になるので、コマンドパレットから "Rebuild Container" を実行します。これで最新の定義に従って再ビルドされます。

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

この時にログを見ると pip install が実行されているのが分かります。

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

再ビルドが完了すると Azure Functions の開発環境が完成です。この状態で F5 を押してデバッグ実行を行うと、Azure Functions Host が起動されてアクセス可能になります。

デバッガーがコンテナー内のインスタンスにアタッチされて、ブレークポイントもすんなり動作します。

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

ポートもフォワーディングされるので、ブラウザからそのまま Function にアクセス可能です。

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

これで Dev Container は完成したので、リポジトリに .devcontainer をコミットしておけば他のマシン上でも同じ環境で開発が行えるようになります。Docker Image を使っているので環境の再現性が高いです。

GitHub Codespaces を使ったデバッグ実行

話題の GitHub Codespaces は Dev Container で環境が作成されるので、リポジトリにコミットさえしておけば Azure Functions の開発環境をそのまま Codespaces 上にも構築できます。

コミット済みのリポジトリで "New codespace" を選ぶと数分で環境が作成されます。

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

初回は Dev Container の時と同様にビルドが走るので少し時間がかかります。ログを見ながら待ちましょう。

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

Codespaces は VS Code と設定の同期をしておくとテーマなどが自動で反映されるので楽です。

GitHub Codespaces 上で F5 を押してデバッグ実行を行うと、ローカルと同様にポートフォワーディングが行われて Function にアクセスが可能になるのと同時に、ブレークポイントなどの機能もそのまま使えます。

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

ブラウザから GitHub にアクセス出来ればよいので、マシンスペックやネットワークに不安のある環境の場合は Codespaces を使った方が捗りそうな気配でした。

今回初めて GitHub Codespaces を試しましたがかなり快適だったので、課金体系が少し不安ですが選択肢としては十分ありかなと思っています。

Custom Container を使った Azure Functions のデプロイ

最後に Azure Functions に Custom Container としてデプロイするための方法を簡単に書いておきます。間違っても Dev Container の Dockerfile を使ってデプロイをしてはいけません。

必要な Dockerfile は以下のように func コマンドを使うと簡単に生成できます。

func init --worker-runtime python --docker-only

生成された Dockerfile は以下のようになります。最低限の pip install が書かれているぐらいのシンプルな定義ですが、必要に応じてパッケージのインストールなど追加できます。

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

Docker Image としてビルドして Azure Functions にデプロイするには ACR Build や GitHub Actions を使う方法がありますが、今回の本題とズレるので省略します。

ビルドが必要なパッケージを使う場合には Custom Container としてデプロイするのが安全ですね。