ブログ記事

Edge Functionマスターガイド2025 - エッジコンピューティングの完全攻略

Cloudflare Workers、Vercel Edge Functions、Deno Deployの最新機能を徹底比較。パフォーマンス最適化、コスト削減、実装パターン、本番運用のベストプラクティスまで、エッジコンピューティングを極める完全ガイドです。

Web開発
Edge Functions Cloudflare Workers Vercel Deno Deploy エッジコンピューティング
Edge Functionマスターガイド2025 - エッジコンピューティングの完全攻略のヒーロー画像

2025 年、エッジコンピューティングは新たな転換点を迎えています。Cloudflare Workers が 6 月にコンテナサポートを追加、Vercel が 300 秒の実行時間制限を導入、Deno Deploy は 35 リージョンから 6 リージョンへ戦略的撤退。この激動の中で、どのプラットフォームを選び、どう活用すべきか?

本記事では、400msのコールドスタートから始まる実装の詳細から、月額5ドルで1000万リクエストを処理する最適化テクニックまで、エッジコンピューティングの全てを解説します。

この記事で学べること

  • 2025 年最新の Edge Functions 機能比較と選定基準
  • プラットフォーム別の実装パターンとベストプラクティス
  • パフォーマンス最適化とコスト削減の具体的手法
  • 実際のユースケースと本番運用のノウハウ
  • 各プラットフォームの料金体系と隠れたコスト
  • マイグレーション戦略とハイブリッド構成

目次

  1. エッジコンピューティングの現在地
  2. プラットフォーム徹底比較 2025
  3. 実装パターンとベストプラクティス
  4. パフォーマンス最適化テクニック
  5. コスト最適化戦略
  6. 実践的なユースケース
  7. 本番運用とモニタリング
  8. 未来への展望

エッジコンピューティングの現在地

なぜ今エッジなのか

従来のサーバーレス(コールドスタート) 20 %
Edge Functions(グローバル配信) 90 %
最適化されたEdge Functions 95 %

エッジコンピューティングが解決する 3 つの課題:

  1. レイテンシー: ユーザーに最も近い場所でコードを実行
  2. スケーラビリティ: 自動的にグローバルに分散
  3. コスト効率: 使った分だけの課金モデル

2025年の技術トレンド

Edge Functions主流化

主要プラットフォームが本格採用

AIワークロード対応

エッジでのAI推論が可能に

コンテナサポート

Cloudflare Workersがコンテナ対応

統合プラットフォーム化

CDN、エッジ、DBの垂直統合

次世代アーキテクチャ

Durable Objects型の状態管理が標準化

プラットフォーム徹底比較2025

機能比較マトリックス

2025年Edge Functionsプラットフォーム機能比較
機能 Cloudflare Workers Vercel Edge Deno Deploy
基盤技術 V8 Isolates Cloudflare Workers V8 Isolates
グローバル展開 280+ 都市 119 PoPs 6 リージョン
コールドスタート 0ms(常時warm) 9x高速化 400ms中央値
実行時間制限 30秒(有料:30分) 300秒 60秒
メモリ制限 128MB 128MB 512MB
料金(無料枠) 10万req/日 100万req/月 制限付き無料
追加料金 $5/1000万req $2/100万req $2/100万req
コンテナ対応 2025年6月予定 非対応 非対応
状態管理 Durable Objects Edge Config KV Store
データベース統合 D1, Hyperdrive Vercel Postgres Deno KV

選定フローチャート

Edge Functionsプラットフォーム選定フロー

チャートを読み込み中...

プラットフォーム別の強み

プラットフォーム選択のポイント

Cloudflare Workersを選ぶべき場合:

  • 最高のコストパフォーマンスが必要
  • グローバルな低レイテンシーが必須
  • 高度な機能(Durable Objects、D1)を使いたい
  • 2025 年 6 月以降:コンテナワークロードも統合したい

Vercel Edge Functionsを選ぶべき場合:

  • Next.js アプリケーションを開発中
  • 開発体験を最優先したい
  • Vercel のエコシステムを活用したい

Deno Deployを選ぶべき場合:

  • Deno 環境で開発したい
  • 特定リージョンでの高パフォーマンスが必要
  • Typescript ファーストの開発を重視

実装パターンとベストプラクティス

基本的な実装

// Cloudflare Workers
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    // リクエストの解析
    const url = new URL(request.url);
    
    // キャッシュの確認
    const cacheKey = new Request(url.toString(), request);
    const cache = caches.default;
    let response = await cache.match(cacheKey);
    
    if (!response) {
      // ビジネスロジックの実行
      response = await handleRequest(request, env);
      
      // レスポンスをキャッシュ
      ctx.waitUntil(cache.put(cacheKey, response.clone()));
    }
    
    return response;
  }
};

async function handleRequest(request: Request, env: Env) {
  // KVストアからデータ取得
  const data = await env.MY_KV.get('key', 'json');
  
  // D1データベースクエリ
  const result = await env.DB.prepare(
    'SELECT * FROM users WHERE id = ?'
  ).bind(1).first();
  
  return Response.json({
    data,
    user: result,
    timestamp: new Date().toISOString()
  });
}
// Vercel Edge Functions
import { NextRequest, NextResponse } from 'next/server';

export const config = {
  runtime: 'edge',
};

export async function GET(request: NextRequest) {
  // Edge Configからフィーチャーフラグ取得
  const featureFlags = await getEdgeConfig();
  
  // 地理的情報の取得
  const geo = request.geo;
  const country = geo?.country || 'unknown';
  
  // パーソナライズされたレスポンス
  const content = await personalizeContent(country);
  
  // キャッシュヘッダーの設定
  return NextResponse.json(
    { 
      content,
      features: featureFlags,
      location: country 
    },
    {
      headers: {
        'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
      },
    }
  );
}

async function personalizeContent(country: string) {
  // 国別のコンテンツを返す
  const contentMap = {
    'JP': '日本向けコンテンツ',
    'US': 'Content for US',
    'default': 'Default content'
  };
  
  return contentMap[country] || contentMap.default;
}
// Deno Deploy
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

serve(async (req: Request) => {
  const url = new URL(req.url);
  
  // Deno KVの使用
  const kv = await Deno.openKv();
  
  // ルーティング
  if (url.pathname === "/api/users") {
    return handleUsers(kv);
  } else if (url.pathname === "/api/cache") {
    return handleCache(req, kv);
  }
  
  return new Response("Not Found", { status: 404 });
});

async function handleUsers(kv: Deno.Kv) {
  // KVからユーザーリスト取得
  const users = [];
  const entries = kv.list({ prefix: ["users"] });
  
  for await (const entry of entries) {
    users.push(entry.value);
  }
  
  return Response.json({ users });
}

async function handleCache(req: Request, kv: Deno.Kv) {
  const cacheKey = ["cache", req.url];
  
  // キャッシュの確認
  const cached = await kv.get(cacheKey);
  if (cached.value) {
    return Response.json(cached.value);
  }
  
  // データの生成
  const data = { 
    timestamp: Date.now(),
    random: Math.random()
  };
  
  // 60秒間キャッシュ
  await kv.set(cacheKey, data, { 
    expireIn: 60 * 1000 
  });
  
  return Response.json(data);
}

高度な実装パターン

1. リクエストの地理的ルーティング

// 地理情報に基づくルーティング
export async function geoRoute(request: Request) {
  const cf = (request as any).cf;
  
  // 地理情報の取得
  const country = cf?.country || 'unknown';
  const continent = cf?.continent || 'unknown';
  
  // リージョン別のバックエンドURL
  const backends = {
    'AS': 'https://api-asia.example.com',
    'EU': 'https://api-eu.example.com',
    'NA': 'https://api-na.example.com',
    'default': 'https://api.example.com'
  };
  
  const backendUrl = backends[continent] || backends.default;
  
  // プロキシリクエスト
  const backendRequest = new Request(backendUrl + new URL(request.url).pathname, {
    method: request.method,
    headers: request.headers,
    body: request.body
  });
  
  return fetch(backendRequest);
}

2. エッジでのA/Bテスト

// A/Bテストの実装
export async function abTest(request: Request) {
  const url = new URL(request.url);
  
  // ユーザー識別子の取得または生成
  const userId = getCookie(request, 'user_id') || crypto.randomUUID();
  
  // バリアントの決定(永続的)
  const variant = hashToVariant(userId);
  
  // バリアント別の処理
  const handlers = {
    'A': () => handleVariantA(request),
    'B': () => handleVariantB(request),
    'control': () => handleControl(request)
  };
  
  const response = await handlers[variant]();
  
  // クッキーにユーザーIDを保存
  response.headers.append('Set-Cookie', `user_id=${userId}; Path=/; Max-Age=31536000`);
  
  // アナリティクスへの送信
  trackEvent({
    event: 'page_view',
    variant,
    userId,
    timestamp: Date.now()
  });
  
  return response;
}

function hashToVariant(userId: string): string {
  const hash = Array.from(userId).reduce((acc, char) => 
    acc + char.charCodeAt(0), 0
  );
  
  const percentage = hash % 100;
  if (percentage < 33) return 'A';
  if (percentage < 66) return 'B';
  return 'control';
}

3. エッジでの画像最適化

// クライアントサイドで画像をリクエスト
async function loadImage(url: string) {
  const img = new Image();
  img.src = url;
  
  // 大きな画像がそのままロード
  await img.decode();
  document.body.appendChild(img);
}
// エッジで画像を最適化
export async function optimizeImage(request: Request) {
  const url = new URL(request.url);
  const imageUrl = url.searchParams.get('url');
  const width = parseInt(url.searchParams.get('w') || '800');
  const quality = parseInt(url.searchParams.get('q') || '85');
  
  // Cloudflare Image Resizing
  const cf = {
    image: {
      width,
      quality,
      format: 'auto', // WebP/AVIF自動選択
      fit: 'scale-down'
    }
  };
  
  const imageRequest = new Request(imageUrl, {
    headers: request.headers,
    cf
  });
  
  const response = await fetch(imageRequest);
  
  // キャッシュヘッダーの設定
  const newResponse = new Response(response.body, response);
  newResponse.headers.set('Cache-Control', 'public, max-age=31536000');
  
  return newResponse;
}
従来の実装
// クライアントサイドで画像をリクエスト
async function loadImage(url: string) {
  const img = new Image();
  img.src = url;
  
  // 大きな画像がそのままロード
  await img.decode();
  document.body.appendChild(img);
}
エッジ最適化版
// エッジで画像を最適化
export async function optimizeImage(request: Request) {
  const url = new URL(request.url);
  const imageUrl = url.searchParams.get('url');
  const width = parseInt(url.searchParams.get('w') || '800');
  const quality = parseInt(url.searchParams.get('q') || '85');
  
  // Cloudflare Image Resizing
  const cf = {
    image: {
      width,
      quality,
      format: 'auto', // WebP/AVIF自動選択
      fit: 'scale-down'
    }
  };
  
  const imageRequest = new Request(imageUrl, {
    headers: request.headers,
    cf
  });
  
  const response = await fetch(imageRequest);
  
  // キャッシュヘッダーの設定
  const newResponse = new Response(response.body, response);
  newResponse.headers.set('Cache-Control', 'public, max-age=31536000');
  
  return newResponse;
}

パフォーマンス最適化テクニック

コールドスタート最適化

Cloudflare(常時warm) 100 %
完了
Vercel(9x高速化) 85 %
Deno Deploy(400ms) 60 %

最適化戦略

// 1. グローバル変数でのコネクション再利用
let dbConnection: Database | null = null;

async function getDB(): Promise<Database> {
  if (!dbConnection) {
    dbConnection = await createConnection();
  }
  return dbConnection;
}

// 2. レイジーローディング
const heavyModule = {
  _instance: null as HeavyModule | null,
  
  async get(): Promise<HeavyModule> {
    if (!this._instance) {
      const { HeavyModule } = await import('./heavy-module');
      this._instance = new HeavyModule();
    }
    return this._instance;
  }
};

// 3. プリフェッチとウォーミング
export async function scheduled(event: ScheduledEvent) {
  // 定期的にエンドポイントをウォーム
  const endpoints = ['/api/hot-path-1', '/api/hot-path-2'];
  
  await Promise.all(
    endpoints.map(endpoint => 
      fetch(`https://example.com${endpoint}`, {
        method: 'HEAD'
      })
    )
  );
}

メモリ使用量の最適化

// ストリーミングレスポンスで大きなデータを処理
export async function streamLargeData(request: Request) {
  const { readable, writable } = new TransformStream();
  const writer = writable.getWriter();
  
  // 非同期でデータをストリーミング
  (async () => {
    try {
      await writer.write('[');
      
      for (let i = 0; i < 1000000; i++) {
        const chunk = JSON.stringify({ id: i, data: 'item' });
        await writer.write(i > 0 ? ',' + chunk : chunk);
        
        // メモリ圧迫を避けるため定期的にyield
        if (i % 1000 === 0) {
          await new Promise(resolve => setTimeout(resolve, 0));
        }
      }
      
      await writer.write(']');
    } finally {
      await writer.close();
    }
  })();
  
  return new Response(readable, {
    headers: {
      'Content-Type': 'application/json',
      'Transfer-Encoding': 'chunked'
    }
  });
}

エッジキャッシュ戦略

エッジキャッシュ戦略と適用場面
キャッシュ戦略 用途 実装例
Cache API 動的コンテンツ caches.default.put()
KV Store セッション/設定 env.KV.put()
CDNキャッシュ 静的アセット Cache-Control: max-age=31536000
ブラウザキャッシュ パーソナライズ Cache-Control: private
Stale-While-Revalidate 準リアルタイム stale-while-revalidate=300

コスト最適化戦略

料金シミュレーター

月間1億リクエストの場合のコスト比較

  • Cloudflare Workers: $45(無料枠含む)
  • Vercel Edge: $199(Pro plan + 追加リクエスト)
  • Deno Deploy: $398(Builder plan + 追加リクエスト)

※ 帯域幅やその他のサービス利用料は含まず

コスト削減テクニック

// 1. 効率的なキャッシング
export async function costOptimizedHandler(request: Request) {
  const cache = caches.default;
  const cacheKey = new Request(request.url, {
    method: 'GET',
    headers: { 'Cache-Version': 'v1' }
  });
  
  // キャッシュヒット率を最大化
  let response = await cache.match(cacheKey);
  if (response) {
    return response;
  }
  
  // 2. バッチ処理でAPI呼び出しを削減
  const batchKey = `batch:${Math.floor(Date.now() / 60000)}`;
  const batchData = await env.KV.get(batchKey, 'json');
  
  if (batchData) {
    response = new Response(JSON.stringify(batchData));
  } else {
    // 高コストな処理
    const data = await expensiveOperation();
    
    // バッチキャッシュに保存
    await env.KV.put(batchKey, JSON.stringify(data), {
      expirationTtl: 60
    });
    
    response = new Response(JSON.stringify(data));
  }
  
  // 3. 適切なキャッシュヘッダー
  response.headers.set('Cache-Control', 'public, max-age=300, s-maxage=3600');
  
  // エッジキャッシュに保存
  await cache.put(cacheKey, response.clone());
  
  return response;
}

実践的なユースケース

1. リアルタイムAPIゲートウェイ

// 認証、レート制限、ルーティングを統合
export async function apiGateway(request: Request) {
  // 認証チェック
  const authResult = await authenticate(request);
  if (!authResult.valid) {
    return new Response('Unauthorized', { status: 401 });
  }
  
  // レート制限
  const rateLimitKey = `rate:${authResult.userId}`;
  const requests = await incrementAndCheck(rateLimitKey);
  
  if (requests > 1000) {
    return new Response('Rate limit exceeded', { 
      status: 429,
      headers: {
        'Retry-After': '3600'
      }
    });
  }
  
  // 動的ルーティング
  const route = matchRoute(request.url);
  const backend = selectBackend(route, authResult.tier);
  
  // リクエストの転送
  return proxyRequest(request, backend, {
    headers: {
      'X-User-ID': authResult.userId,
      'X-User-Tier': authResult.tier
    }
  });
}

2. エッジでのWebSocketプロキシ

// WebSocket接続のプロキシとスケーリング
export async function websocketProxy(request: Request) {
  const upgradeHeader = request.headers.get('Upgrade');
  
  if (upgradeHeader !== 'websocket') {
    return new Response('Expected WebSocket', { status: 426 });
  }
  
  // 接続先の選択(負荷分散)
  const backend = await selectWebSocketBackend();
  
  // WebSocketペアの作成
  const { 0: client, 1: server } = new WebSocketPair();
  
  // バックエンドへの接続
  const backendWS = new WebSocket(backend);
  
  // メッセージの中継
  server.accept();
  server.addEventListener('message', event => {
    backendWS.send(event.data);
  });
  
  backendWS.addEventListener('message', event => {
    server.send(event.data);
  });
  
  // エラーハンドリング
  const closeHandler = () => {
    server.close();
    backendWS.close();
  };
  
  server.addEventListener('close', closeHandler);
  backendWS.addEventListener('close', closeHandler);
  
  return new Response(null, {
    status: 101,
    webSocket: client
  });
}

3. エッジでのML推論

// Cloudflare Workers AIを使用した推論
export async function mlInference(request: Request) {
  const formData = await request.formData();
  const image = formData.get('image') as File;
  
  // 画像分類
  const imageArray = new Uint8Array(await image.arrayBuffer());
  const classification = await env.AI.run(
    '@cf/microsoft/resnet-50',
    { image: Array.from(imageArray) }
  );
  
  // テキスト分析
  const text = formData.get('text') as string;
  const sentiment = await env.AI.run(
    '@cf/huggingface/distilbert-sst-2-int8',
    { text }
  );
  
  // 結果の組み合わせ
  return Response.json({
    image: classification,
    sentiment: sentiment,
    timestamp: new Date().toISOString()
  });
}

本番運用とモニタリング

可観測性の実装

// 包括的なロギングとメトリクス
export async function observableHandler(request: Request) {
  const startTime = Date.now();
  const requestId = crypto.randomUUID();
  
  try {
    // リクエストログ
    await logEvent({
      type: 'request',
      requestId,
      url: request.url,
      method: request.method,
      headers: Object.fromEntries(request.headers),
      geo: (request as any).cf
    });
    
    // ビジネスロジック
    const response = await processRequest(request);
    
    // レスポンスログ
    await logEvent({
      type: 'response',
      requestId,
      status: response.status,
      duration: Date.now() - startTime
    });
    
    // メトリクスの送信
    await sendMetrics({
      endpoint: new URL(request.url).pathname,
      duration: Date.now() - startTime,
      status: response.status
    });
    
    return response;
    
  } catch (error) {
    // エラーログ
    await logEvent({
      type: 'error',
      requestId,
      error: error.message,
      stack: error.stack,
      duration: Date.now() - startTime
    });
    
    return new Response('Internal Server Error', { status: 500 });
  }
}

デプロイメント戦略

ローカル開発

Miniflare/Wrangler Dev

プレビュー環境

Branch Deployments

段階的ロールアウト

5% → 25% → 50% → 100%

Blue-Green Deploy

即座にロールバック可能

継続的モニタリング

アラート設定とダッシュボード

セキュリティベストプラクティス

セキュリティチェックリスト

✅ 環境変数でのシークレット管理 ✅ CORS ヘッダーの適切な設定 ✅ 入力値の検証とサニタイゼーション ✅ レート制限の実装 ✅ CSRF トークンの検証 ✅ セキュリティヘッダーの設定 ✅ 定期的な依存関係の更新

エッジコンピューティングの限界と対策

Edge Functionsの制限事項と対策
制限事項 影響 対策
CPU時間制限 重い処理が不可 バックグラウンドジョブと分離
メモリ制限 大規模データ処理が困難 ストリーミング処理を活用
実行時間制限 長時間処理が不可 非同期処理とキューイング
ファイルシステムなし 一時ファイル作成不可 オブジェクトストレージ活用
ネイティブモジュール非対応 一部ライブラリ使用不可 WebAssembly版を使用

未来への展望

2025年後半の注目トレンド

コンテナのサポートにより、エッジコンピューティングは 単なる軽量な処理から、本格的なアプリケーション実行環境へと進化します。 2025 年後半には、エッジネイティブなアーキテクチャが主流になるでしょう。

エッジコンピューティング専門家 Cloudflare

次世代エッジアーキテクチャ

2025年のエッジコンピューティングエコシステム

チャートを読み込み中...

まとめ:エッジファーストな設計へ

エッジコンピューティングは、2025 年において単なるパフォーマンス最適化の手段から、アプリケーションアーキテクチャの中核へと進化しました。

実装チェックリスト

Edge Functions導入前の確認事項

✅ ユースケースがエッジに適しているか評価 ✅ プラットフォームの選定(コスト vs 機能) ✅ 既存システムとの統合方法を設計 ✅ モニタリングとロギングの準備 ✅ 段階的移行計画の策定 ✅ チーム全体でのナレッジ共有

エッジコンピューティングは、適切に活用すればパフォーマンス向上コスト削減を同時に実現できる強力な技術です。本記事で紹介したベストプラクティスを参考に、あなたのプロジェクトでもエッジファーストな設計を検討してみてください。

この記事は役に立ちましたか?

Daily Hackでは、開発者の皆様に役立つ情報を毎日発信しています。