Cloudflare Workers — Azure Functions相当のエッジ関数
Cloudflare WorkersはAzure Functionsに相当するサーバーレス関数プラットフォームですが、実行モデルが根本的に異なります。
| 観点 | Azure Functions | Cloudflare Workers |
|---|---|---|
| 実行場所 | 特定リージョン(japaneast等) | ユーザー最寄りのエッジPoP(300+拠点) |
| コールドスタート | あり(Consumption planで数百ms〜秒単位) | なし(V8 Isolateの事前起動) |
| スケールアウト | インスタンスが複製される(stateful可) | リクエストごとにIsolateが起動(stateless) |
| 最大実行時間 | 10分(Consumption)〜無制限(Premium/Dedicated) | CPU時間:無料10ms / 有料30秒 |
| ランタイム | .NET / Node.js / Python / Java 等 | JavaScript / TypeScript / Python / Rust |
| 課金 | 実行時間 × メモリ | リクエスト数(無料10万回/日) |
Cloudflare WorkersはC#/.NETをサポートしていません。 技術的にはWebAssemblyへのコンパイルで動作させる方法はありますが、.NET WASMバンドルサイズ(数十MB)がWorkersの上限(有料プランで10MB compressed)を大幅に超えるため、現実的ではありません。
Azure Functions + .NETを移行・並用する場合のアーキテクチャは後述を参照してください。
トリガーの対比
Azure Functionsのトリガーに相当するWorkersのハンドラーです。
| Azure Functions トリガー | Workers ハンドラー | 用途 |
|---|---|---|
| HTTP Trigger | fetch | HTTPリクエスト処理 |
| Timer Trigger | scheduled | Cron定期実行 |
| Queue Trigger (Service Bus) | queue | Cloudflare Queuesからの消費 |
| Blob Trigger | なし(R2イベントは別途) | — |
| Event Grid Trigger | なし | — |
| Email Trigger | email | Cloudflare Email Routing |
バインディングの対比
Azure Functionsの入出力バインディングに相当するWorkers Bindingsです。envオブジェクト経由でアクセスします。
| Azure Functions バインディング | Workers Binding | 説明 |
|---|---|---|
| Blob Storage (in/out) | R2Bucket | オブジェクトストレージ |
| Table Storage | KVNamespace | キーバリューストア |
| Service Bus | Queue | メッセージキュー |
| SQL Database | D1Database | SQLiteエッジDB |
| Cosmos DB | KVNamespace / D1Database | 用途に応じて選択 |
| SignalR | DurableObject | WebSocket・リアルタイム |
| HTTP (output) | Fetcher(Service Binding) | 別Workerの呼び出し |
Workerの基本構造
HTTPトリガー(Azure HTTP Trigger相当)
// src/index.ts
export interface Env {
// wrangler.tomlで定義したバインディングを型で宣言
MY_KV: KVNamespace;
MY_BUCKET: R2Bucket;
MY_DB: D1Database;
API_KEY: string; // wrangler secret put API_KEY で設定した値
}
export default {
// Azure Functions の Run(HttpRequest req, ...) に相当
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
// ルーティング(Azure Functions の function.json routeに相当)
if (url.pathname === '/api/hello' && request.method === 'GET') {
return Response.json({ message: 'Hello from the edge!' });
}
if (url.pathname === '/api/items' && request.method === 'POST') {
const body = await request.json<{ name: string }>();
// KVに保存(Azure Table Storage outputバインディング相当)
await env.MY_KV.put(`item:${crypto.randomUUID()}`, JSON.stringify(body));
return Response.json({ ok: true }, { status: 201 });
}
return new Response('Not Found', { status: 404 });
},
};
Timer Trigger(Cron定期実行)
Azure FunctionsのTimerTriggerに相当します。wrangler.tomlでCronスケジュールを定義します。
# wrangler.toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-11-01"
# Azure Functions の TimerTrigger schedule に相当
[[triggers]]
crons = ["0 */6 * * *"] # 6時間ごと
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return new Response('OK');
},
// Timer Trigger に相当
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
console.log(`Cron triggered: ${event.cron} at ${event.scheduledTime}`);
// D1 DBのクリーンアップ処理など
await env.MY_DB.prepare(
'DELETE FROM logs WHERE created_at < ?'
).bind(Date.now() - 7 * 24 * 60 * 60 * 1000).run();
},
};
Queue Consumer(Queue Trigger相当)
Azure FunctionsのService Bus Queue Triggerに相当します。
# wrangler.toml
[[queues.consumers]]
queue = "my-queue"
max_batch_size = 10
max_batch_timeout = 30
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return new Response('OK');
},
// Azure Functions の ServiceBusTrigger に相当
async queue(batch: MessageBatch<{ orderId: string }>, env: Env): Promise<void> {
for (const message of batch.messages) {
try {
await processOrder(message.body.orderId, env);
message.ack(); // 処理成功
} catch (err) {
message.retry(); // リトライキューに戻す
}
}
},
};
リソースバインディングの使い方
KV(キーバリューストア)
// 書き込み(TTL付き)
await env.MY_KV.put('session:abc123', JSON.stringify({ userId: 1 }), {
expirationTtl: 3600, // 1時間後に自動削除
});
// 読み取り
const raw = await env.MY_KV.get('session:abc123');
const session = raw ? JSON.parse(raw) : null;
// 削除
await env.MY_KV.delete('session:abc123');
// プレフィックスで一覧取得
const { keys } = await env.MY_KV.list({ prefix: 'session:' });
R2(オブジェクトストレージ)
// アップロード(Azure Blob Storage output bindingに相当)
await env.MY_BUCKET.put('uploads/image.png', request.body, {
httpMetadata: { contentType: 'image/png' },
customMetadata: { uploadedBy: 'user123' },
});
// ダウンロード
const object = await env.MY_BUCKET.get('uploads/image.png');
if (!object) return new Response('Not Found', { status: 404 });
return new Response(object.body, {
headers: { 'Content-Type': object.httpMetadata?.contentType ?? 'application/octet-stream' },
});
// 削除
await env.MY_BUCKET.delete('uploads/image.png');
D1(SQLiteエッジDB)
// SELECT
const result = await env.MY_DB.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(userId).first<{ id: number; name: string; email: string }>();
// INSERT
await env.MY_DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind('山田太郎', 'yamada@example.com').run();
// バッチ実行(トランザクション相当)
await env.MY_DB.batch([
env.MY_DB.prepare('UPDATE orders SET status = ? WHERE id = ?').bind('shipped', orderId),
env.MY_DB.prepare('INSERT INTO order_logs (order_id, event) VALUES (?, ?)').bind(orderId, 'shipped'),
]);
Service Binding(Worker間呼び出し)
別のWorkerをHTTPなしで内部呼び出しできます。Azure FunctionsのDurable Functions CallActivityに近いです。
# wrangler.toml
[[services]]
binding = "AUTH_WORKER"
service = "auth-worker"
// 別Workerを内部呼び出し(HTTPオーバーヘッドなし)
const authResponse = await env.AUTH_WORKER.fetch(
new Request('https://internal/verify', {
method: 'POST',
body: JSON.stringify({ token }),
})
);
const { valid, userId } = await authResponse.json<{ valid: boolean; userId: string }>();
デプロイ方法
1. Wrangler CLIで手動デプロイ
# 本番にデプロイ
wrangler deploy
# 環境指定デプロイ(wrangler.tomlの[env.staging]を使用)
wrangler deploy --env staging
2. GitHub Actionsで自動デプロイ
# .github/workflows/deploy.yml
name: Deploy to Cloudflare Workers
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env production
3. Terraformでデプロイ(Infrastructure as Code)
Azure ARM Templates / BicepのようにIaCでWorkerを管理できます。
# main.tf
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
# Workerスクリプトのデプロイ
resource "cloudflare_worker_script" "api" {
account_id = var.account_id
name = "my-api-worker"
content = file("dist/index.js")
# 環境変数(非機密)
plain_text_binding {
name = "API_BASE_URL"
text = "https://api.example.com"
}
# KVバインディング
kv_namespace_binding {
name = "MY_KV"
namespace_id = cloudflare_workers_kv_namespace.sessions.id
}
# R2バインディング
r2_bucket_binding {
name = "MY_BUCKET"
bucket_name = cloudflare_r2_bucket.uploads.name
}
}
# KV Namespace
resource "cloudflare_workers_kv_namespace" "sessions" {
account_id = var.account_id
title = "my-sessions-kv"
}
# R2 Bucket
resource "cloudflare_r2_bucket" "uploads" {
account_id = var.account_id
name = "my-uploads"
location = "APAC"
}
# HTTPルート(カスタムドメイン)
resource "cloudflare_worker_route" "api" {
zone_id = var.zone_id
pattern = "api.example.com/*"
script_name = cloudflare_worker_script.api.name
}
# Cronトリガー
resource "cloudflare_worker_cron_trigger" "cleanup" {
account_id = var.account_id
script_name = cloudflare_worker_script.api.name
schedules = ["0 */6 * * *"]
}
D1マイグレーション管理
D1はバージョン管理されたマイグレーション機構を備えています。連番のSQLファイルを順番に適用し、適用済みの状態を d1_migrations テーブルで追跡します。
EF Core MigrationsはAzure SQL Databaseの機能ではなく、Entity Framework Core(.NETのORM)が提供する機能です。SQL Server / Azure SQL / PostgreSQL / SQLiteなど複数のデータベースで動作します。「Azure SQL DatabaseのMigrations」という固有機能が存在するわけではありません。
設計思想も異なります。
- D1マイグレーション:手書きのSQLファイルを順番に適用する**SQLスクリプトベース(DBファースト)**の方式。.NET/Azureの世界では DbUp / Flyway や SQL Database Project(DACPAC / sqlpackage)に近い。
- EF Core Migrations:C#のモデルクラスの差分からマイグレーションコード(
Up/Down)を自動生成するコードファーストの方式。
「バージョン管理されたマイグレーションを順次適用する」という点は共通ですが、両者を単純に同一視するのは正確ではありません。
# マイグレーションファイルを作成
wrangler d1 migrations create my-database add_users_table
# → migrations/0001_add_users_table.sql が生成される
-- migrations/0001_add_users_table.sql
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE INDEX idx_users_email ON users(email);
# ローカルDBに適用
wrangler d1 migrations apply my-database --local
# 本番DBに適用
wrangler d1 migrations apply my-database
# 適用状況確認
wrangler d1 migrations list my-database
.NET開発者のための移行戦略
WorkersはC#をサポートしないため、.NETのワークロードを完全にWorkersへ移行することは現実的ではありません。以下のハイブリッドアーキテクチャが推奨です。
ユーザー
│
▼
[Cloudflare Workers — TypeScript]
├── 認証・セッション検証(JWT検証、KVでセッション管理)
├── レートリミット・Bot判定
├── 静的アセット配信(R2)
├── 軽量なCRUD(D1)
└── 重い処理はAzureへプロキシ
│
▼
[Azure Functions — .NET 10]
├── 複雑なビジネスロジック
├── Azure SQL / Cosmos DB 操作
├── Azure Service Bus 処理
└── 外部API統合
役割分担の目安
| 処理 | Workers (TypeScript) | Azure Functions (.NET) |
|---|---|---|
| JWT検証 | ✅ 向いている(エッジで即時検証) | — |
| 静的ファイル配信 | ✅ R2 + CDN | — |
| 軽量なKVキャッシュ | ✅ Workers KV | — |
| 簡単なCRUD (SQLite規模) | ✅ D1 | — |
| 複雑なビジネスロジック | — | ✅ 向いている |
| Azure SQL / Cosmos DB | ❌ 直接接続できない | ✅ ネイティブバインディング |
| 既存の.NETライブラリ利用 | ❌ 非対応 | ✅ |
| バッチ・長時間処理 | ❌ CPU時間上限あり | ✅ |
制限事項
| 項目 | 無料プラン | 有料プラン (Workers Paid) |
|---|---|---|
| リクエスト数 | 10万/日 | 1,000万/月(超過分 $0.30/100万) |
| CPU時間/リクエスト | 10ms | 30秒 |
| メモリ | 128MB | 128MB |
| スクリプトサイズ | 3MB(gzip後) | 10MB(gzip後) |
| KVデータサイズ(値) | 25MB | 25MB |
| R2オブジェクトサイズ | 5GB | 5GB |
| D1データベースサイズ | 500MB | 2GB |
| Workers実行数(同時) | 制限なし(Isolateによる分離) | 制限なし |
WorkersのCPU時間は「実際にCPUを使った時間」のみカウントされます。外部APIへのfetch待機中はカウントされないため、Azure Functionsの実行時間とは異なる概念です。I/Oバウンドな処理であれば30秒の制限はほとんど影響しません。