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 Trigger REST API、Webhook API エンドポイント作成
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.js 22.x LTS JavaScript ランタイム DevContainer 内で自動
Azure Functions Core Tools v4.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 でエラーになるので注意!

依存関係のインストール

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 {
  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() でエンドポイントを登録

ローカル実行

実行結果:

動作確認

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

curl "http://localhost:7071/api/HttpExample?name=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 {
  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 を起動:

実行結果:

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 起動
  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 編をお届けしますので、お楽しみに〜!


元の記事を確認する

関連記事