Azure Functionsサーバーレス開発のベストプラクティス

29分で読めます
エンハンスド技術チーム
Azure FunctionsサーバーレスベストプラクティスクラウドFaaS
Azure Functionsを使ったサーバーレス開発の実践的なベストプラクティスを紹介します。

Azure Functions:「サーバー管理から解放された」日のこと

はじめに:夜中のサーバー再起動にさよなら

「また落ちた!今度は何が原因だ?」

深夜2時、携帯が鳴る。サーバーダウンの通知。パジャマのまま PC を開いて、SSH でログイン...

こんな生活を送っていた私が、Azure Functions と出会って人生が変わりました。サーバーレスって聞いたことありますか?「サーバーがない」って最初は意味不明でしたが、使ってみたら「これ、革命じゃん!」って感動しました。

今回は、サーバー管理地獄から解放されて、本来のコーディングに集中できるようになった話をします。

最初の一歩:「えっ、コードだけ書けばいいの?」

はじめての Azure Functions 体験

正直に言います。最初は懐疑的でした。

「サーバーなしってどういうこと?」 「どこで動くの?」 「落ちたらどうするの?」

疑問だらけでしたが、先輩エンジニアが見せてくれたデモで目が覚めました。

たった10行のコードで API が完成

[FunctionName("HelloWorld")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
    ILogger log)
{
    log.LogInformation("誰かがアクセスしてきた!");
    return new OkObjectResult("こんにちは、サーバーレスの世界へようこそ!");
}

これだけ。マジでこれだけで動くんです。

デプロイボタンをポチッと押したら、もう世界中からアクセスできる API の完成。nginx の設定も、ファイアウォールの設定も、何もいらない。

「なにこれ、魔法?」って思いました。

本格的に使い始めて気づいた「すごさ」

最初の Hello World から数週間後、本格的な注文処理システムを作ることに。

「でも、データベース接続とか、外部 API の呼び出しとか、どうするの?」

心配無用でした。普通の ASP.NET Core と同じように書けるんです。

実際に作った注文処理の例(シンプル版):

[FunctionName("ProcessOrder")]
public async Task<IActionResult> ProcessOrder(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    ILogger log)
{
    // 注文データを読み取る
    var orderData = await req.GetJsonAsync<Order>();
    
    // データベースに保存(普通に Entity Framework が使える!)
    await _dbContext.Orders.AddAsync(orderData);
    await _dbContext.SaveChangesAsync();
    
    // メール送信(SendGrid とか使える)
    await _emailService.SendOrderConfirmationAsync(orderData.Email);
    
    log.LogInformation($"注文 {orderData.Id} を処理しました");
    
    return new OkObjectResult(new { success = true, orderId = orderData.Id });
}

驚いたポイント:

  • スケーリングは Azure が勝手にやってくれる
  • 1日10件でも100万件でも、コードは同じ
  • 使った分だけの課金(10件なら10円、みたいな)
  • ログも勝手に Application Insights に送られる

もう、サーバーのことは考えなくていい。ビジネスロジックに集中できる。これが本当に嬉しかった。

イベント駆動の魔法:「勝手に連携する」システム

「キューに入れたら、あとはお任せ」の快感

ある日、注文システムが複雑になってきました。

  • 注文を受ける
  • 在庫を確認する
  • 決済を処理する
  • メールを送る
  • 配送を手配する

以前なら、これ全部を1つの処理で書いて、どこかでエラーが起きたら大変なことに...

でも Azure Functions なら違います。

「注文きたよ〜」とキューに投げるだけ:

// 注文を受け付ける関数
[FunctionName("ReceiveOrder")]
public async Task<IActionResult> ReceiveOrder(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    [ServiceBus("order-queue")] IAsyncCollector<string> orderQueue)
{
    var order = await req.GetJsonAsync<Order>();
    
    // キューに投げるだけ!
    await orderQueue.AddAsync(JsonSerializer.Serialize(order));
    
    return new OkObjectResult("注文を受け付けました!処理中です...");
}

// 在庫確認する関数(勝手に起動される)
[FunctionName("CheckInventory")]
public async Task CheckInventory(
    [ServiceBusTrigger("order-queue")] string orderJson,
    [ServiceBus("payment-queue")] IAsyncCollector<string> paymentQueue,
    ILogger log)
{
    var order = JsonSerializer.Deserialize<Order>(orderJson);
    
    // 在庫チェック
    if (await _inventoryService.IsAvailable(order.ProductId))
    {
        log.LogInformation($"在庫OK!次は決済だ");
        await paymentQueue.AddAsync(orderJson); // 次のキューへ
    }
    else
    {
        log.LogWarning($"在庫切れ... {order.ProductId}");
        await _emailService.SendSorryEmail(order.Email);
    }
}

これの何がすごいか:

  1. 自動リトライ - エラーが起きても勝手に再試行
  2. 並列処理 - 100件の注文も同時に処理
  3. 疎結合 - 各機能が独立してて、変更が楽
  4. スケーラブル - 注文が増えても勝手に対応

深夜に「決済システムがダウンした!」って連絡が来ても、「大丈夫、キューに溜まってるから、復旧したら勝手に処理されるよ」って言えるようになりました。

イベントグリッド:「何か起きたら教えて」システム

さらに便利なのが Event Grid。これは本当に革命的でした。

実際にあった嬉しい出来事:

金曜日の夕方、新機能のリリース直前。 「注文が完了したら、ポイントを付与して、アンケートメールを送って、分析データも更新したい」

普通なら「えー、今から?週末かかるよ...」ってなるところが。

[FunctionName("OrderCompletedHandler")]
public async Task HandleOrderCompleted(
    [EventGridTrigger] EventGridEvent eventData,
    ILogger log)
{
    // 注文完了イベントが飛んできた!
    log.LogInformation($"おっ、注文 {eventData.Subject} が完了したみたい");
    
    // それぞれ独立して処理(エラーが起きても他に影響しない)
    var tasks = new[]
    {
        SendThankYouEmail(eventData),
        AddLoyaltyPoints(eventData),
        UpdateAnalytics(eventData)
    };
    
    await Task.WhenAll(tasks);
    
    log.LogInformation("全部終わった!週末楽しもう!");
}

30分で実装完了。しかも:

  • 既存の注文システムには一切手を加えない
  • それぞれの処理は独立(ポイント付与が失敗してもメールは送られる)
  • あとから機能追加も簡単(新しい関数を追加するだけ)

「これ、もしかして理想のアーキテクチャなんじゃ...」って思いました。

タイマー処理:「cron の設定で悩まない」幸せ

定期処理が簡単すぎて泣いた

以前の職場での悪夢:

  • crontab -e(あれ?書式なんだっけ)
  • 「なんで動かない?」(ログはどこ?)
  • 「サーバー再起動したら cron 止まってた...」

Azure Functions なら:

[FunctionName("DailyReport")]
public async Task GenerateDailyReport(
    [TimerTrigger("0 0 9 * * *")] TimerInfo timer, // 毎朝9時
    ILogger log)
{
    log.LogInformation($"おはよう!日次レポートの時間だよ");
    
    // 昨日の売上を集計
    var yesterday = DateTime.Today.AddDays(-1);
    var sales = await _db.GetSalesForDate(yesterday);
    
    // レポート作成
    var report = GenerateReport(sales);
    
    // メールで送信
    await _email.SendToManager("日次売上レポート", report);
    
    log.LogInformation($"レポート送信完了!今日もがんばろう");
}

感動ポイント:

  • Visual Studio で書いてデプロイするだけ
  • ログは Application Insights で見放題
  • 失敗したら自動でアラート
  • サーバーのことは一切考えなくていい

実際に助かった事例:

年末年始の休暇中、毎日のレポート生成を忘れてた! 普通なら「やばい、サーバーにログインして...」ってなるところが、スマホから Azure Portal 開いて、関数を有効化するだけで解決。

ビーチでカクテル飲みながら5分で対応完了。これがクラウドネイティブってやつか...と実感しました。

パフォーマンスの話:「え、勝手に速くなるの?」

最初の衝撃:コールドスタート問題

正直に言います。最初はパフォーマンスで悩みました。

「なんか最初のアクセスだけ遅いんだけど...」

これが有名な「コールドスタート」問題。でも、解決法は意外とシンプルでした。

やったこと1:Premium プランにした

月額: 2万円くらい
効果: 常に1台は起動してる状態に
結果: 初回アクセスも爆速!

やったこと2:軽量化

// ❌ 重い書き方
public class HeavyFunction 
{
    // コンストラクタで重い処理...
    public HeavyFunction() 
    {
        LoadHugeDictionary(); // 10秒かかる
    }
}

// ✅ 軽い書き方
public static class LightFunction 
{
    // static にして使い回す
    private static readonly HttpClient httpClient = new HttpClient();
    private static readonly Dictionary<string, string> cache = new();
    
    [FunctionName("FastAPI")]
    public static async Task<IActionResult> Run(...)
    {
        // サクサク動く!
    }
}

結果:

  • 初回起動: 10秒 → 0.5秒
  • 通常のレスポンス: 200ms → 50ms
  • 月間コスト: ほぼ変わらず(使用量は同じだから)

キャッシュの魔法

「同じデータを何度も DB から取るの無駄じゃない?」

そう思って Redis キャッシュを導入したら、世界が変わりました。

// 5分間キャッシュするだけで劇的改善
var cached = await _redis.GetAsync<Product>($"product:{id}");
if (cached != null) return cached; // 爆速!

var product = await _db.GetProductAsync(id);
await _redis.SetAsync($"product:{id}", product, TimeSpan.FromMinutes(5));

実測値:

  • キャッシュヒット時: 5ms(DB アクセスなし)
  • キャッシュミス時: 100ms(DB アクセスあり)
  • ヒット率: 約80%

結果、レスポンスタイムが平均70%削減!お客様から「最近サイト速くなった?」って言われた時は嬉しかったなぁ。

ウォームアップ作戦:「朝一の準備運動」

ある日の発見:「朝一番のアクセスが遅い問題」を解決する面白い方法を見つけました。

アイデア:自分で自分を起こす!

[FunctionName("MorningWakeUp")]
public static async Task WakeUp(
    [TimerTrigger("0 0 7 * * *")] TimerInfo timer, // 毎朝7時
    ILogger log)
{
    log.LogInformation("おはよう!Functions を起こすよ〜");
    
    // 主要な API を叩いて起こす
    var urls = new[] 
    {
        "https://myapi.azurewebsites.net/api/health",
        "https://myapi.azurewebsites.net/api/products/warmup",
        "https://myapi.azurewebsites.net/api/users/warmup"
    };
    
    foreach (var url in urls)
    {
        try
        {
            await httpClient.GetAsync(url);
            log.LogInformation($"起きた! {url}");
        }
        catch
        {
            log.LogWarning($"まだ寝てる... {url}");
        }
    }
    
    log.LogInformation("みんな起きた!今日も一日がんばろう!");
}

効果てきめん:

  • 朝8時の始業時には全部準備完了
  • 「朝一遅い」クレームがゼロに
  • コスト増加:月100円くらい(安い!)

こういう「ちょっとした工夫」で大きく改善できるのが、サーバーレスの面白いところです。

監視とトラブルシューティング:「見えない敵との戦い」

Application Insights で「全部見える」感動

昔の私:「なんかエラー出てるらしいけど、ログどこ?」 今の私:「あ、3分前にユーザーID 12345 でエラー出てるね。原因はこれだ」

Application Insights のおかげで、デバッグが劇的に楽になりました。

実際に助かった事例:

ある日の午後、サポートから連絡が。 「お客様から『注文できない』って問い合わせが...」

以前なら:

  1. サーバーにログイン
  2. ログファイルを grep
  3. 「あれ?ローテートされてる...」
  4. バックアップから復元して...

今なら:

  1. Application Insights を開く
  2. 「失敗した要求」をクリック
  3. 該当時刻の詳細を見る
  4. 「あ、在庫 API がタイムアウトしてる」

3分で原因特定!

// エラーが起きた時の詳細ログ
log.LogError(new 
{
    UserId = order.UserId,
    OrderId = order.Id,
    ProductId = order.ProductId,
    ErrorType = "InventoryCheckFailed",
    Timestamp = DateTime.UtcNow
}, "在庫確認でエラーが発生しました");

こう書いておくと、Application Insights で検索できて超便利。 「UserId = 12345 のエラーを全部見せて」みたいなことができます。

セキュリティ:「鍵の管理で泣かない」方法

API キーの管理で学んだ教訓

恥ずかしい話をします。

最初、API キーをコードに直書きしてました。

// ❌ 絶対ダメな例
var apiKey = "sk-1234567890abcdef"; // GitHub に上げちゃった...

GitHub のセキュリティアラートが飛んできて、冷や汗が... 慌ててキーを無効化して、正しい方法を学びました。

正しい方法:Azure Key Vault を使う

// ✅ 安全な方法
[FunctionName("SecureFunction")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
    ILogger log)
{
    // Key Vault から取得(環境変数経由)
    var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
    
    // これなら GitHub に上げても安心!
    var client = new OpenAIClient(apiKey);
    
    // 処理実行...
}

Key Vault の設定(Azure Portal でポチポチ):

  1. Key Vault を作成
  2. シークレットを追加
  3. Functions にアクセス権限を付与
  4. Functions の設定で参照を追加

「こんなに簡単に安全にできるのか!」って感動しました。

認証の実装:「誰でもアクセスできちゃう」問題

最初は AuthorizationLevel.Anonymous で全部公開してました。 「社内用だから大丈夫でしょ」って思ってたら...

ある日の事件: Application Insights を見たら、中国からのアクセスが大量に! 「えっ、なんで海外から...?」

慌てて認証を実装:

// Function レベルの認証(簡単!)
[FunctionName("ProtectedAPI")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    ILogger log)
{
    // AuthorizationLevel.Function にするだけで
    // コードキーが必要に!
}

さらに Azure AD 認証も追加して、今では鉄壁のセキュリティ。 「認証って難しそう」と思ってたけど、Azure がほとんどやってくれるので楽でした。

コスト最適化:「え、今月の請求これだけ?」

最初の請求書を見て驚いた

移行前のオンプレサーバー:

  • サーバー代: 月15万円
  • 電気代: 月3万円
  • 保守契約: 月5万円
  • 合計: 月23万円

Azure Functions に移行後の初月:

  • Functions 使用料: 3,500円
  • Storage: 500円
  • Application Insights: 1,000円
  • 合計: 5,000円

「桁が違う...本当にこれで合ってる?」 何度も確認しました。本当でした。

なぜこんなに安いのか

理由はシンプル:使った分だけ課金

我々のケース:
- 1日の実行回数: 約10,000回
- 平均実行時間: 200ms
- メモリ使用量: 256MB

計算すると...
実行時間: 10,000 × 0.2秒 = 2,000秒/日
月間: 2,000秒 × 30日 = 60,000秒 = 16.7時間

サーバーなら24時間×30日=720時間動かすところを
実際に使うのは17時間だけ!

コストを抑える工夫

1. タイムアウトを短くする

// host.json で設定
{
  "functionTimeout": "00:05:00" // 5分でタイムアウト
}

2. 不要な関数は無効化 開発中のテスト関数とか、忘れがち。Azure Portal から簡単に ON/OFF できます。

3. ログレベルの調整

// 本番環境では Information 以上のみ
"logging": {
  "logLevel": {
    "default": "Information"
  }
}

結果: 月5,000円前後で安定運用。以前の1/40以下のコストで、むしろ性能は向上。 「クラウドは高い」は昔の話だと実感しました。

まとめ:サーバーレスで人生変わった(マジで)

1年前の私に言いたい。 「サーバー管理やめて、コード書こうよ」って。

Before(サーバー管理地獄):

  • 深夜の障害対応: 月3〜4回
  • サーバーメンテナンス: 毎週末
  • スケーリング対応: 手動で大変
  • 月額コスト: 23万円
  • ストレス: MAX

After(サーバーレス天国):

  • 深夜対応: ほぼゼロ
  • メンテナンス: Azure におまかせ
  • スケーリング: 勝手にやってくれる
  • 月額コスト: 5千円
  • ストレス: 激減

一番嬉しかったこと:

妻に言われた一言。 「最近、夜中に起きなくなったね。表情も明るくなった」

そうなんです。サーバーレスは技術だけじゃなく、生活の質も変えてくれました。

これから始める人へ:

「難しそう...」って思うかもしれません。私もそうでした。 でも、やってみたら意外と簡単。

  1. まずは Hello World から
  2. 小さな API を作ってみる
  3. 徐々に機能を増やす
  4. 気づいたら本格的なシステムに

サーバーのことは Azure に任せて、あなたは素晴らしいアプリを作ることに集中しましょう。

きっと1年後、「もっと早く始めれば良かった」って思うはずです。

エンハンスド株式会社では、サーバーレスへの移行を検討している企業様に、実体験に基づいたアドバイスと技術支援を提供しています。「うちでもできるかな?」という疑問から、お気軽にご相談ください。一緒に、サーバー管理から解放される喜びを味わいましょう!

参考リンク


執筆者: エンハンスド株式会社 元サーバー管理者チーム
公開日: 2025年5月18日
カテゴリ: Azure, サーバーレス
タグ: #AzureFunctions #サーバーレス #クラウドネイティブ #脱サーバー管理 #ワークライフバランス

技術的な課題をお持ちですか?

記事でご紹介した技術や実装について、
より詳細なご相談やプロジェクトのサポートを承ります

無料技術相談を申し込む