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);
}
}
これの何がすごいか:
- 自動リトライ - エラーが起きても勝手に再試行
- 並列処理 - 100件の注文も同時に処理
- 疎結合 - 各機能が独立してて、変更が楽
- スケーラブル - 注文が増えても勝手に対応
深夜に「決済システムがダウンした!」って連絡が来ても、「大丈夫、キューに溜まってるから、復旧したら勝手に処理されるよ」って言えるようになりました。
イベントグリッド:「何か起きたら教えて」システム
さらに便利なのが 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 のおかげで、デバッグが劇的に楽になりました。
実際に助かった事例:
ある日の午後、サポートから連絡が。 「お客様から『注文できない』って問い合わせが...」
以前なら:
- サーバーにログイン
- ログファイルを grep
- 「あれ?ローテートされてる...」
- バックアップから復元して...
今なら:
- Application Insights を開く
- 「失敗した要求」をクリック
- 該当時刻の詳細を見る
- 「あ、在庫 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 でポチポチ):
- Key Vault を作成
- シークレットを追加
- Functions にアクセス権限を付与
- 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千円
- ストレス: 激減
一番嬉しかったこと:
妻に言われた一言。 「最近、夜中に起きなくなったね。表情も明るくなった」
そうなんです。サーバーレスは技術だけじゃなく、生活の質も変えてくれました。
これから始める人へ:
「難しそう...」って思うかもしれません。私もそうでした。 でも、やってみたら意外と簡単。
- まずは Hello World から
- 小さな API を作ってみる
- 徐々に機能を増やす
- 気づいたら本格的なシステムに
サーバーのことは Azure に任せて、あなたは素晴らしいアプリを作ることに集中しましょう。
きっと1年後、「もっと早く始めれば良かった」って思うはずです。
エンハンスド株式会社では、サーバーレスへの移行を検討している企業様に、実体験に基づいたアドバイスと技術支援を提供しています。「うちでもできるかな?」という疑問から、お気軽にご相談ください。一緒に、サーバー管理から解放される喜びを味わいましょう!
参考リンク
執筆者: エンハンスド株式会社 元サーバー管理者チーム
公開日: 2025年5月18日
カテゴリ: Azure, サーバーレス
タグ: #AzureFunctions #サーバーレス #クラウドネイティブ #脱サーバー管理 #ワークライフバランス