Azure Functions×DevContainer環境構築|Node.js 22 + TypeScript

目次

はじめに

どうも、龍ちゃんです!

今月は Azure Functions を使ったサーバーレス開発に取り組んでいるのですが、ローカル開発環境の構築で結構ハマりポイントがあったので、その知見を共有したいと思います。

📦 サンプルリポジトリ: 本記事で解説する環境をすぐに試せるサンプルコードを公開しています。

特に、DevContainer を使った環境構築は、チーム開発で環境差異をなくすために非常に有効なアプローチです。前回の「Claude Code×DevContainer 環境構築ガイド」でも DevContainer の便利さを紹介しましたが、今回は Azure Functions に特化した DevContainer 環境構築を解説します。

なぜ DevContainer で Azure Functions なのか?

私が DevContainer を推奨する理由はいくつかあります:

1. 再現性のある環境構築
チームメンバー全員が同じ環境で開発できます。「自分の環境では動くのに…」という問題、ありますよね。これが完全になくなります!

2. ホスト OS を汚さない
Node.js のバージョン、npm のグローバルパッケージ、Azure Functions Core Tools など、すべてコンテナ内で完結

3. プロジェクトごとに異なるバージョンを使い分け
プロジェクト A は Node.js 18、プロジェクト B は Node.js 22 といった使い分けが簡単

今回は、Node.js 22 + TypeScript で Azure Functions の開発環境を構築します。次回の「Python 編」では Python 3.11 を使った環境構築も紹介しますので、お楽しみに!

Azure Functions とは?

Azure Functions は、サーバーレスコンピューティングのサービスです。サーバーの管理をせずに、コードだけを書いて実行できます。

主要なトリガー

Azure Functions では、さまざまな「トリガー」でコードを実行できます:

トリガー用途
HTTP TriggerREST API、WebhookAPI エンドポイント作成
Timer Trigger定期実行毎日深夜にバッチ処理
Blob Triggerファイルアップロード画像アップロード時に圧縮
Queue Triggerメッセージキュー非同期タスク処理

今回は、HTTP TriggerTimer Trigger を使ってローカル開発環境を構築します。次回の記事では、この 2 つを組み合わせた実践パターン(Timer Trigger を HTTP Trigger でデバッグする方法)を紹介する予定です。

なぜローカル開発環境が必要なのか

Azure にデプロイしてからデバッグするのって、めちゃくちゃ時間かかりますよね。ローカル環境があれば:

  • 即座にデバッグ – コードを変更したら即座に動作確認!
  • ログ確認が簡単 – コンソールに直接ログが表示される
  • コスト削減 – ローカルでのテストは Azure の課金対象外

前提条件

本記事では、以下がインストール済みであることを前提とします:

必須

  • Docker Desktop – DevContainer を使うために必須
  • Visual Studio Code – エディタ

本記事でインストールするもの

  • Dev Containers 拡張機能 – VSCode でインストール
  • Node.js 22、Azure Functions Core Tools、Azurite – DevContainer 内で自動インストール

必要なツール一覧

DevContainer を使った Azure Functions 開発に必要なツールは以下の通りです:

ツール名バージョン役割インストール先
Docker Desktop最新コンテナランタイムホスト OS(前提)
Visual Studio Code最新エディタホスト OS(前提)
Dev Containers 拡張機能最新DevContainer サポートVSCode
Node.js22.x LTSJavaScript ランタイムDevContainer 内で自動
Azure Functions Core Toolsv4.xローカル実行・デバッグDevContainer 内で自動
Azurite最新Azure Storage エミュレータDevContainer 内で自動

ポイント:

  • ホスト OS には Docker Desktop と VSCode が既にインストール済み
  • Node.js、Core Tools、Azurite は DevContainer 内で自動的にインストール
  • これにより、ホスト OS を汚さずに開発環境を構築可能

Dev Containers 拡張機能のインストール

VSCode に Dev Containers 拡張機能をインストールします。

  1. VSCode を起動
  2. 拡張機能パネルを開く(Ctrl+Shift+X / Cmd+Shift+X
  3. Dev Containers」で検索
  4. Dev Containers」(ID: ms-vscode-remote.remote-containers)をインストール

動作確認:

  • VSCode 左下に「><」アイコンが表示されていることを確認
  • このアイコンをクリックすると、DevContainer 関連のコマンドが表示される

Node.js DevContainer の構築

これから構築する環境の全体像を把握しましょう。

構築後のディレクトリ構成

azure-functions-nodejs-devcontainer/     # プロジェクトルート
├── .devcontainer/                       # DevContainer 設定
│   ├── Dockerfile                       # コンテナイメージ定義
│   └── devcontainer.json                # DevContainer 設定ファイル
│
└── MyFunctionApp/                       # Azure Functions プロジェクト
    ├── .funcignore                      # デプロイ除外ファイル
    ├── .gitignore                       # Git 除外設定
    ├── host.json                        # Functions ランタイム設定
    ├── local.settings.json              # ローカル環境変数
    ├── package.json                     # npm 依存関係
    ├── tsconfig.json                    # TypeScript 設定
    │
    └── src/                             # ソースコード
        └── functions/                   # 関数ファイル
            ├── HttpExample.ts           # HTTP Trigger 関数
            └── TimerExample.ts          # Timer Trigger 関数

ポイント:

  • .devcontainer/ で開発環境を定義
  • MyFunctionApp/ が実際の Functions プロジェクト
  • TypeScript ファイルは src/functions/ 配下に配置

構築方法

DevContainer を構築する方法は主に 2 つあります:

  1. Dockerfile 方式 – カスタム Dockerfile で詳細に制御(推奨)
  2. PostCreateCommand 方式 – 既存イメージにコマンドを追加

方法 1: Dockerfile 方式(推奨)

この方式は、再現性が高く、ビルド時間も短いため推奨します。

ステップ 1: プロジェクトディレクトリの作成

mkdir azure-functions-nodejs-devcontainer
cd azure-functions-nodejs-devcontainer
mkdir .devcontainer

ステップ 2: Dockerfile の作成

.devcontainer/Dockerfile を作成:

# .devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm

# 作業ディレクトリ
WORKDIR /workspace

# 実行ユーザー
USER node

# Azure Functions Core Tools と Azurite のインストール
RUN npm install -g \
    npm@11.5.2 \
    azure-functions-core-tools@4 \
    azurite

# バージョン確認用コマンド(デバッグ用)
RUN echo "=== Installed Versions ===" \
    && node --version \
    && npm --version \
    && func --version \
    && echo "========================="

# デフォルトコマンド(devContainer では sleep infinity で上書きされる)
CMD ["sleep", "infinity"]

ポイント:

  • mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm は Microsoft 公式の Node.js 22 + TypeScript イメージ
  • azure-functions-core-tools@4 で Azure Functions v4 ランタイムをインストール
  • azurite は Timer Trigger のローカル実行に必須!

ステップ 3: devcontainer.json の作成

.devcontainer/devcontainer.json を作成:

{
  "name": "Azure Functions Node.js DevContainer",
  "build": {
    "dockerfile": "./Dockerfile"
  },
  "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
  "remoteUser": "node",
  "forwardPorts": [7071, 10000, 10001, 10002],
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-azuretools.vscode-azurefunctions",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  }
}

ポイント:

  • forwardPorts: Azure Functions (7071) と Azurite (10000-10002) のポート転送を設定
  • extensions: Azure Functions 拡張機能、ESLint、Prettier を自動インストール

ステップ 4: DevContainer の起動

  1. VSCode で azure-functions-nodejs-devcontainer フォルダを開く
  2. 左下の「><」アイコンをクリック
  3. Reopen in Container」を選択
  4. Docker イメージのビルドと起動が開始されます(初回は 5-10 分程度)

成功すると:

  • VSCode の左下に「Dev Container: Azure Functions Node.js DevContainer」と表示
  • ターミナルを開くと、コンテナ内のシェルが起動

方法 2: PostCreateCommand 方式

既存イメージを使い、起動後にコマンドでツールをインストールする方式です。

.devcontainer/devcontainer.json:

{
  "name": "Azure Functions Node.js DevContainer",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
  "workspaceFolder": "/workspace",
  "remoteUser": "node",

  "postCreateCommand": "npm install -g npm@11.5.2 azure-functions-core-tools@4 azurite",

  "forwardPorts": [7071, 10000, 10001, 10002],

  "customizations": {
    "vscode": {
      "extensions": [
        "ms-azuretools.vscode-azurefunctions",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  }
}

メリット:

  • 設定ファイル 1 つで完結
  • シンプルでわかりやすい

デメリット:

  • DevContainer 起動のたびにインストールが実行される(起動が遅い)
  • Dockerfile 方式の方が確実性が高く、再現性に優れる

私は Dockerfile 方式を推奨します。

Functions プロジェクトの作成

DevContainer 内で Azure Functions プロジェクトを作成します。

プロジェクト初期化

DevContainer 内のターミナルで実行:

func init MyFunctionApp --typescript
cd MyFunctionApp

生成されるファイル:

MyFunctionApp/
├── .funcignore            # Functions デプロイ時の除外ファイル
├── .gitignore             # Git 除外設定
├── host.json              # Functions ランタイム設定
├── local.settings.json    # ローカル環境変数
├── package.json           # npm 依存関係
├── tsconfig.json          # TypeScript 設定
└── src/                   # ソースコード格納ディレクトリ

local.settings.json の設定

local.settings.json を編集して、Azurite 接続設定を追加:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "node"
  }
}

重要:

  • AzureWebJobsStorage: "UseDevelopmentStorage=true" は Azurite を使用するための設定
  • この設定がないと Timer Trigger でエラーになるので注意!

依存関係のインストール

npm install

HTTP Trigger の作成と動作確認

まずは、基本的な HTTP Trigger を作成して動作確認します。

HTTP Trigger 関数の作成

func new --name HttpExample --template "HTTP trigger" --authlevel "anonymous"

生成されるファイル:

  • src/functions/HttpExample.ts

実装内容の確認

src/functions/HttpExample.ts:

import {
  app,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
} from "@azure/functions";

export async function HttpExample(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
  context.log("HTTP trigger function processed a request.");
  const name = request.query.get("name") || "World";

  return {
    status: 200,
    body: `Hello, ${name}!`,
  };
}

app.http("HttpExample", {
  methods: ["GET", "POST"],
  authLevel: "anonymous",
  handler: HttpExample,
});

ポイント:

  • request.query.get('name') でクエリパラメータを取得
  • app.http() でエンドポイントを登録

ローカル実行

npm start

実行結果:

動作確認

別のターミナルで curl コマンドを実行:

curl "http://localhost:7071/api/HttpExample?name=Azure"

期待される結果:

Hello, Azure!

ブラウザでの確認:

  • http://localhost:7071/api/HttpExample にアクセスすると「Hello, World!」と表示されます

Azurite と Timer Trigger

Timer Trigger を使うには、Azurite(Azure Storage エミュレータ)が必要です。

なぜ Azurite が必要なのか?

Azure Functions の Timer Trigger は、内部的に Blob Storage を使って Timer の状態(次回実行時刻など)を保存します。ローカル開発では、この Blob Storage を Azurite でエミュレートするんですね。

  • HTTP Trigger のみの場合: Azurite 不要
  • Timer Trigger を使う場合: Azurite 必須!

Azurite の起動

DevContainer 内で、別のターミナルを開いて Azurite を起動します:

azurite --silent

# 設定やログを保存する先を指定
azurite --location .azurite --debug .azurite/debug.log

期待される結果:

Azurite Blob service is starting at http://127.0.0.1:10000
Azurite Blob service is successfully listening at http://127.0.0.1:10000
Azurite Queue service is starting at http://127.0.0.1:10001
Azurite Queue service is successfully listening at http://127.0.0.1:10001
Azurite Table service is starting at http://127.0.0.1:10002
Azurite Table service is successfully listening at http://127.0.0.1:10002

デフォルトポート:

  • Blob Service: 10000
  • Queue Service: 10001
  • Table Service: 10002

Timer Trigger 関数の作成

func new --name TimerExample --template "Timer trigger"

生成されるファイル:

  • src/functions/TimerExample.ts

実装内容の確認

src/functions/TimerExample.ts:

import { app, InvocationContext, Timer } from "@azure/functions";

export async function TimerExample(
  myTimer: Timer,
  context: InvocationContext
): Promise<void> {
  context.log("Timer trigger function executed at:", new Date().toISOString());

  if (myTimer.isPastDue) {
    context.log("Timer is running late!");
  }
}

app.timer("TimerExample", {
  schedule: "0 */5 * * * *", // 5分ごとに実行
  handler: TimerExample,
});

CRON 式の説明:

  • 0 */5 * * * * は 5 分ごとに実行
  • CRON 式は UTC 時刻で動作(重要!)

ローカル実行

Azurite が起動している状態で、Functions を起動:

npm start

実行結果:

5 分ごとにログが表示されます。

Azurite が起動していない場合のエラー

Azurite が起動していないと、以下のエラーが発生します:

[Error] Microsoft.Azure.WebJobs.Host: Error indexing method 'TimerExample'.

対処法: Azurite を起動してから Functions を再起動

トラブルシューティング

よくあるエラーと対処法をまとめます。

1. Docker Desktop が起動しない

症状:

  • VSCode で「Docker daemon is not running」エラー

対処法:

  • Docker Desktop を起動する(Windows/Mac)
  • Linux の場合: sudo systemctl start docker

2. DevContainer のビルドが失敗する

症状:

  • 「Failed to build image」エラー

対処法:

  • Dockerfile の構文エラーを確認
  • Docker Desktop のディスク容量を確認
  • VSCode のコマンドパレット(Ctrl+Shift+P)→「Dev Containers: Rebuild Container」を実行

3. func コマンドが見つからない

症状:

  • bash: func: command not found

対処法:

  • DevContainer が正しくビルドされているか確認
  • ターミナルを再起動
  • Dockerfile の npm install -g azure-functions-core-tools@4 が正しく実行されているか確認

4. Azurite 接続エラー

症状:

  • No connection could be made because the target machine actively refused it

対処法:

  • Azurite が起動しているか確認(curl http://127.0.0.1:10000
  • local.settings.jsonAzureWebJobsStorage: "UseDevelopmentStorage=true" が設定されているか確認
  • Azurite を再起動

5. ポート競合エラー

症状:

  • Port 7071 is already in use

対処法:

  • 既存のプロセスを終了
  • 別のポートで起動: func start --port 7072

6. TypeScript コンパイルエラー

症状:

  • npm start で TypeScript エラー

対処法:

  • npm install を実行して依存関係を再インストール
  • tsconfig.json の設定を確認
  • npm run build で明示的にビルド

開発の推奨フロー

DevContainer を使った Azure Functions 開発の推奨フローです。

標準的な開発手順

  1. ターミナル 1: Azurite 起動
   azurite --silent
  1. ターミナル 2: Functions ランタイム起動
   cd MyFunctionApp
   npm start
  1. 開発作業
  • TypeScript ファイル(.ts)を編集
  • 保存すると自動的にリロード(watch mode)
  1. 動作確認
  • HTTP Trigger: curl やブラウザでアクセス
  • Timer Trigger: コンソールログで確認

VSCode のターミナル分割

VSCode のターミナルを分割すると便利です:

  • ターミナル 1: Azurite 起動(azurite --silent
  • ターミナル 2: Functions 起動(npm start
  • ターミナル 3: curl コマンドやその他の操作

分割方法:

  • ターミナルパネルの右上の「+」アイコン横の「Split Terminal」ボタン
  • または Ctrl+Shift+5 / Cmd+Shift+5

推奨 VSCode 拡張機能

DevContainer 内で自動的にインストールされる拡張機能以外にも、以下があると便利です:

  • Azure Functions (ms-azuretools.vscode-azurefunctions) – 既に設定済み
  • ESLint (dbaeumer.vscode-eslint) – 既に設定済み
  • Prettier (esbenp.prettier-vscode) – 既に設定済み
  • Thunder Client (rangav.vscode-thunder-client) – HTTP クライアント(任意)

まとめと次回予告

本記事で学んだこと

DevContainer を使った Azure Functions 環境構築

  • Docker Desktop と VSCode のインストール
  • Node.js 22 + TypeScript の DevContainer 構築
  • Dockerfile 方式と PostCreateCommand 方式の違い

Azure Functions の基本

  • HTTP Trigger の作成と動作確認
  • Timer Trigger と Azurite の関係
  • ローカル開発環境での実行方法

トラブルシューティング

  • よくあるエラーと対処法
  • Azurite 接続エラーの解決方法

次回の記事予告

次回は、以下の内容を予定しています:

Azure Functions×DevContainer 環境構築| Python 編

  • Python 3.11 を使った DevContainer 構築
  • Node.js 版との違い
  • Python 仮想環境との組み合わせ

Azure Functions 入門| HTTP Trigger と Timer Trigger の基礎と実践パターン

  • HTTP Trigger と Timer Trigger の詳細な使い方
  • 実践パターン: Timer Trigger を HTTP Trigger でデバッグする方法
  • タイムゾーン(UTC/JST)の扱い方
  • DRY 原則に基づいた共通ロジックの設計

サンプルリポジトリ

本記事で解説した環境を、すぐに試せるサンプルコードを公開しています:

  • GitHub: azure-functions-nodejs-devcontainer
    • Node.js 22 + TypeScript
    • HTTP Trigger + Timer Trigger 実装済み
    • DevContainer 設定ファイル完備
    • クローンして VSCode で開くだけで動作します

関連記事

DevContainer を使った Azure Functions 開発、ぜひ試してみてください!

チーム開発での環境差異がなくなり、開発効率が大幅に向上すること間違いなしです。

次回は Python 編をお届けしますので、お楽しみに〜!

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です