Skip to content

feat(monitoring): 学生ハッカソン向け監視基盤実装#127

Open
Inlet-back wants to merge 3 commits intodevelopfrom
feature/monitoring-student-budget
Open

feat(monitoring): 学生ハッカソン向け監視基盤実装#127
Inlet-back wants to merge 3 commits intodevelopfrom
feature/monitoring-student-budget

Conversation

@Inlet-back
Copy link
Contributor

📋 概要

学生ハッカソンという予算制約の厳しい環境において、月$0-10で運用可能な監視基盤を構築しました。

🎯 目的

課題

  • OpenAI APIの使用量が無制限で、コスト爆発のリスク
  • サービスのダウンタイムを検知する仕組みがない
  • エラー発生時の追跡・通知が不在
  • 学生予算では月$100の監視コストは払えない

解決策

既存の無料SaaSツールを組み合わせ、最小限のコード変更で監視基盤を実現:

  • OpenAI Dashboard: コスト上限設定(Hard $10, Soft $5/月)
  • UptimeRobot: 死活監視(5分間隔、無料)
  • Sentry: エラー追跡(5,000イベント/月無料)← オプション
  • Render Alert: リソース監視(無料)

📝 変更内容

1. ヘルスチェックエンドポイント実装

ファイル: backend/app/api/api.py

@api_router.get("/health")
async def health_check(db: Session = Depends(get_db)) -> dict:
    """UptimeRobot監視用のヘルスチェックエンドポイント"""
    checks = {
        "database": "healthy",
        "openai": "healthy",
        "cors": "healthy"
    }
    # DB接続、OpenAI設定、CORS設定を確認
    # 問題があれば "degraded" or "unhealthy" を返す

効果:

  • UptimeRobotが5分ごとにこのエンドポイントを確認
  • ダウン時は即座にメール通知

2. Sentry統合準備

ファイル: backend/app/main.py, backend/app/core/config.py

# main.py
if settings.SENTRY_DSN:
    sentry_sdk.init(
        dsn=settings.SENTRY_DSN,
        traces_sample_rate=0.1,  # 10%のトランザクションのみ追跡
        environment="production" if "vercel.app" in settings.FRONTEND_URL else "development"
    )

効果:

  • Python例外の自動追跡
  • エラー発生時のスタックトレース・コンテキスト保存
  • 無料枠(5,000イベント/月)内で十分運用可能

3. 学生向けコスト設定ガイド

ファイル: DEPLOYMENT.md

Before(企業向け想定)

  • OpenAI上限: $100/月
  • 監視目標: $50/月以下

After(学生ハッカソン向け)

  • OpenAI上限: $10/月(Hard Limit)
  • 警告レベル: $5/月(Soft Limit)
  • 監視目標: $3/月以下
  • 初期3ヶ月: $0-10(完全無料運用可能)

完全無料運用ガイド追加

  • OpenAI: 初回$5クレジット付与
  • PostgreSQL: 90日間無料 → Supabase無料枠(500MB)へ移行手順
  • Render: 750時間/月無料
  • 監視ツール: 全て無料枠

🔍 設計判断(ADR参照)

Sentry選定理由の詳細は docs/adr/0001-sentry-for-error-tracking.md を参照

選定基準:

  1. 無料枠の充実度: 5,000イベント/月(学生には十分)
  2. 学習コスト: FastAPI公式ドキュメントに記載
  3. コード変更の少なさ: 10行の初期化コードのみ
  4. 将来性: 有料化しても Developer Plan $26/月(許容範囲)

🧪 テスト

ヘルスチェックエンドポイント

# ローカルテスト
curl http://localhost:8000/api/v1/health

# 期待されるレスポンス
{
  "status": "healthy",
  "timestamp": "2026-02-22T12:00:00Z",
  "version": "1.0.0",
  "checks": {
    "database": "healthy",
    "openai": "healthy",
    "cors": "healthy"
  }
}

Sentry統合テスト

# backend/app/main.pyで手動テスト
@app.get("/sentry-debug")
async def trigger_error():
    division_by_zero = 1 / 0  # Sentryにキャプチャされる

📊 コスト試算

デモ発表まで(1-2週間)

  • 開発テスト: $1-2
  • 本番環境: $0(初回$5クレジット)
  • 合計: $1-2

ハッカソン期間中(3ヶ月)

  • OpenAI API: $3-10(月$1-3 × 3ヶ月)
  • Render: $0(無料枠)
  • PostgreSQL: $0(90日無料 → Supabase移行)
  • Vercel: $0(完全無料)
  • 監視ツール: $0(全て無料枠)
  • 合計: $3-10

3ヶ月後(継続運用する場合)

  • OpenAI API: $3-10/月
  • PostgreSQL: $0(Supabase無料枠)または $7/月(Render)
  • 合計: $3-17/月

🚀 デプロイ手順

1. 即座に実行(5分×3タスク)

OpenAI使用量上限設定(最優先)

1. https://platform.openai.com/account/limits にアクセス
2. Hard Limit: $10/月
3. Soft Limit: $5/月
4. Email通知設定
5. Save

UptimeRobot設定

1. https://uptimerobot.com/ サインアップ
2. モニター追加:
   - Backend: https://team29-backend.onrender.com/api/v1/health
   - Frontend: https://your-app.vercel.app/
   - Interval: 5分
3. Email通知設定

Render Alert設定

1. Dashboard → Settings → Notifications
2. 有効化:
   - Deploy Failure
   - Service Down
   - High CPU (> 80%)
   - High Memory (> 90%)
3. Email追加

2. オプション(Sentry統合)

# 1. https://sentry.io/ でプロジェクト作成
# 2. DSNコピー
# 3. Renderの環境変数に追加
SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx

# 4. sentry-sdkインストール
cd backend
poetry add sentry-sdk

# 5. 再デプロイ(自動)
git push origin main

✅ チェックリスト

セキュリティ

  • JWT_SECRET_KEYは十分にランダムか
  • ADMIN_API_KEYは推測不可能か
  • .envファイルが.gitignoreに含まれているか

監視・アラート設定

  • OpenAI使用量上限を設定したか(Hard: $10, Soft: $5)← 最優先
  • UptimeRobotでBackend/Frontend監視を設定したか
  • Renderでアラート通知を有効化したか
  • 週次監視の担当者・曜日を決めたか
  • (オプション)Sentryを統合したか

週次監視(毎週月曜日5分)

  • OpenAI使用量確認(目標: $3/月以下)
  • UptimeRobotでダウンタイム確認
  • Renderでリソース使用率確認

🔄 今後の改善案

すぐできる(優先度: 高)

  • レート制限強化
    • スキルツリー生成: 1ユーザー1カテゴリ1回まで
    • 問題生成: 1日5回まで
  • OpenAI APIキャッシュ戦略の見直し
    • SKILL_TREE_CACHE_MINUTES: 10分 → 60分に延長検討

中期的に検討(優先度: 中)

  • CloudWatch Logs的な集約ログ基盤
    • まずはRender標準ログで十分
    • 必要になったらBetterStack(無料枠あり)検討
  • アラート通知をSlack/Discord統合
    • UptimeRobot Webhookで実現可能

長期的に検討(優先度: 低)

  • Prometheusメトリクス収集
    • 学生ハッカソンでは過剰スペック
    • 企業向けプロダクトになったら検討

📖 参考資料

👥 レビューポイント

重点確認事項

  1. コスト設定の妥当性: 学生予算で $5-10/月は許容範囲か?
  2. 監視頻度: UptimeRobot 5分間隔で十分か?(無料枠の制約)
  3. Sentry統合: 本番で本当に有効化すべきか?(オプション扱いでOK?)
  4. 週次監視: 担当者と曜日を誰が決めるか?

細かい確認

  • ヘルスチェックエンドポイントのレスポンス形式は適切か?
  • Sentry初期化のエラーハンドリングは十分か?
  • DEPLOYMENT.mdの説明は初心者にも分かりやすいか?

🎓 学び・気づき

良かった点

  • 自前実装ではなく既存ツール活用で工数削減
  • OpenAI公式のUsage Dashboard機能を発見
  • 学生予算に合わせた現実的な設定

改善が必要な点

  • レート制限がまだ未実装(次のPRで対応予定)
  • キャッシュ戦略の最適化余地あり

学んだこと

  • UptimeRobotの無料枠(50モニター)は学生に最適
  • Sentryの無料枠(5,000イベント/月)は意外と使える
  • PostgreSQLはSupabase移行で長期的にも無料運用可能

- /api/v1/health エンドポイント追加(UptimeRobot監視用)
  - DB接続、OpenAI設定、CORS確認
- Sentry統合準備(SENTRY_DSN環境変数)
- OpenAI コスト上限を学生向けに設定($5-10/月)
- 完全無料運用ガイド追加(3ヶ月$0-10)
- Supabase移行手順追加(90日後の選択肢)

監視ツール:
- OpenAI Dashboard: Hard $10, Soft $5(学生向け)
- UptimeRobot: 死活監視(5分間隔)
- Sentry: エラー追跡(5,000イベント/月無料)
- Render Alert: リソース監視
@vercel
Copy link

vercel bot commented Feb 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
2026-team29 Ready Ready Preview, Comment Feb 22, 2026 1:11pm

問題:
- config.pyにSKILL_TREE_CACHE_MINUTESを定義したが、
  skill_tree_service.pyではハードコード値(CACHE_VALID_MINUTES=10)を使用
- テストも固定値を前提にしており、環境変数変更が反映されない

修正:
- skill_tree_service.py:
  - ハードコード定数CACHE_VALID_MINUTESを削除
  - settings.SKILL_TREE_CACHE_MINUTESを参照するよう変更
  - docstringも更新
- test_skill_tree_service.py:
  - 全キャッシュ関連テストでsettingsをモック
  - mock_settings.SKILL_TREE_CACHE_MINUTES = 10 で制御
  - 環境変数変更に対応した堅牢なテストに修正

影響:
- .envでSKILL_TREE_CACHE_MINUTES=60と設定すれば60分キャッシュが有効になる
- 本番環境で柔軟にキャッシュ時間を調整可能

テスト: 12 passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

このPRは、学生ハッカソン向けに月$0-10で運用可能な監視基盤を構築するものです。既存の無料SaaSツール(UptimeRobot、Sentry、Renderアラート)を活用し、OpenAI APIのコスト管理を重視した設計となっています。特にOpenAI使用量上限設定(Hard $10/月、Soft $5/月)とヘルスチェックエンドポイントの実装により、予算制約の厳しい学生チームでも安心して運用できる仕組みを提供しています。

Changes:

  • ヘルスチェックエンドポイント(/api/v1/health)の実装:DB接続、OpenAI設定、CORS設定を監視
  • Sentry統合準備(オプション機能):エラー追跡とスタックトレースの自動収集
  • スキルツリーキャッシュ設定の環境変数化:SKILL_TREE_CACHE_MINUTESで調整可能に
  • 学生ハッカソン向けデプロイメントガイドの拡充:完全無料運用の手順と監視チェックリスト
  • Sentry選定理由の詳細なADRドキュメント作成

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
backend/app/api/api.py ヘルスチェックエンドポイント(/api/v1/health)の実装 - DB、OpenAI、CORS設定の監視
backend/app/main.py Sentry統合コードの追加(SENTRY_DSN設定時のみ有効化)
backend/app/core/config.py SKILL_TREE_CACHE_MINUTESとSENTRY_DSN設定の追加
backend/app/services/skill_tree_service.py ハードコードされたCACHE_VALID_MINUTESをsettings.SKILL_TREE_CACHE_MINUTESに変更
backend/tests/test_services/test_skill_tree_service.py キャッシュ有効期間のテストでsettingsをモック化して環境依存を排除
DEPLOYMENT.md 学生ハッカソン向け監視ツール設定ガイドとコスト試算の追加
.github/decisions/019-sentry-for-error-tracking.md Sentry選定理由とトレードオフを記載した詳細なADRドキュメント

Comment on lines +140 to +144
), patch(
"app.services.skill_tree_service.settings"
) as mock_settings:
mock_settings.SKILL_TREE_CACHE_MINUTES = 10 # 15分前のキャッシュは無効
mock_settings.SKIP_LLM_FOR_SKILL_TREE = False # LLMを使用
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settings オブジェクト全体をモックしていますが、SKILL_TREE_CACHE_MINUTES と SKIP_LLM_FOR_SKILL_TREE 以外の属性へのアクセスがあると AttributeError が発生する可能性があります。より安全な方法として、patch.object を使用して特定の属性のみをモックするか、MagicMock に spec 属性を設定することを推奨します。例:複数の patch.object を使用するか、mock_settings.configure_mock でその他の必要な属性も設定してください。

Copilot uses AI. Check for mistakes.
Comment on lines +376 to +377
with patch("app.services.skill_tree_service.settings") as mock_settings:
mock_settings.SKILL_TREE_CACHE_MINUTES = 10
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settings オブジェクト全体をモックしていますが、SKILL_TREE_CACHE_MINUTES 以外の属性へのアクセスがあると AttributeError が発生する可能性があります。より安全な方法として、patch.object を使用して特定の属性のみをモックすることを推奨します。例:patch.object(settings, "SKILL_TREE_CACHE_MINUTES", 10)

Copilot uses AI. Check for mistakes.
"""
health = {
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

datetime.utcnow()は非推奨です。コードベースの他の部分では一貫してdatetime.now(UTC)が使用されています(例:backend/app/services/skill_tree_service.py:221)。datetime.now(UTC)に変更する場合は、インポート文も「from datetime import datetime, UTC」に更新する必要があります。これにより、タイムゾーン対応が明示的になり、コードベース全体で一貫性が保たれます。

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +24
environment=(
"production"
if settings.FRONTEND_URL.startswith("https://")
else "development"
),
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

環境判定ロジックに問題があります。settings.FRONTEND_URLが"https://"で始まるかどうかで環境を判定していますが、本番環境が必ずhttpsで始まるとは限りません(例:開発環境でhttps付きのプレビューURLを使用する場合)。より確実な方法として、専用の環境変数(例:ENVIRONMENT="production")を導入するか、特定のドメイン("vercel.app"など)を含むかで判定することをお勧めします。

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +69
@api_router.get("/health", tags=["monitoring"])
def health_check(db: Session = Depends(get_db)) -> dict:
"""ヘルスチェックエンドポイント(UptimeRobot等の監視用)

- データベース接続確認
- 基本設定確認
- タイムスタンプ返却

Returns:
status: healthy/degraded/unhealthy
timestamp: 現在時刻(UTC)
checks: 各コンポーネントの状態
"""
health = {
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
"version": "1.0.0",
"checks": {},
}

# データベース接続チェック
try:
db.execute(text("SELECT 1"))
health["checks"]["database"] = "ok"
except Exception as e:
health["checks"]["database"] = f"error: {str(e)}"
health["status"] = "unhealthy"

# OpenAI API Key設定確認(キー自体は返さない)
try:
if settings.OPENAI_API_KEY and len(settings.OPENAI_API_KEY) > 10:
health["checks"]["openai_configured"] = "ok"
else:
health["checks"]["openai_configured"] = "not_configured"
health["status"] = "degraded"
except Exception:
health["checks"]["openai_configured"] = "error"
health["status"] = "degraded"

# CORS設定確認
try:
if settings.BACKEND_CORS_ORIGINS:
health["checks"]["cors_configured"] = "ok"
else:
health["checks"]["cors_configured"] = "not_configured"
except Exception:
health["checks"]["cors_configured"] = "error"

return health
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ヘルスチェックエンドポイントのテストが不足しています。このプロジェクトでは他のAPIエンドポイントに対して包括的なテストが存在します(例:backend/tests/test_api/test_analyze.py)。監視用のヘルスチェックエンドポイントは重要な機能なので、以下のテストケースを追加することを推奨します:

  1. 正常時のレスポンスチェック(status="healthy")
  2. データベース接続失敗時の動作(status="unhealthy")
  3. OpenAI API Key未設定時の動作(status="degraded")
  4. レスポンス形式の検証(timestamp、version、checksフィールド)

Copilot uses AI. Check for mistakes.
Comment on lines 213 to +225

→ OPENAI_API_KEYが正しく設定されているか、クレジットが残っているか確認

## 📊 モニタリング
## 📊 モニタリング・監視基盤

### 標準ログ確認

- **Render Logs**: Dashboard → Logs
- **Vercel Logs**: Dashboard → Deployments → Logs
- **エラートラッキング**: RenderのMetricsタブで確認
- **Render Metrics**: Dashboard → Metrics(CPU/メモリ/リクエスト数)

### 推奨監視ツール(全て無料)

Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEPLOYMENT.mdのサンプルレスポンスに一貫性の問題があります。213-225行目のヘルスチェックエンドポイントの例では、checksの値が「"healthy"」という文字列になっていますが、実際の実装(backend/app/api/api.py:44,52)では「"ok"」、「"not_configured"」、「"error"」といった異なる値が使用されています。ドキュメントを実装と一致させる必要があります。

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +47
except Exception as e:
health["checks"]["database"] = f"error: {str(e)}"
health["status"] = "unhealthy"
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

データベースエラーの詳細がヘルスチェックエンドポイントのレスポンスに含まれており、潜在的なセキュリティリスクがあります。str(e)により、データベース接続情報やスタックトレースの一部が外部に漏洩する可能性があります。本番環境では、エラーの詳細をログに記録しつつ、クライアントには汎用的なエラーメッセージ(例:「"error"」のみ)を返すことを推奨します。

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,281 @@
# ADR-0001: Sentryをエラー追跡ツールとして選定
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR説明とADRファイルパスに不一致があります。PR説明では「docs/adr/0001-sentry-for-error-tracking.md」と記載されていますが、実際のファイルは「.github/decisions/019-sentry-for-error-tracking.md」に作成されています。PR説明を実際のファイルパスに合わせて修正する必要があります。

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +47
health = {
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
"version": "1.0.0",
"checks": {},
}

# データベース接続チェック
try:
db.execute(text("SELECT 1"))
health["checks"]["database"] = "ok"
except Exception as e:
health["checks"]["database"] = f"error: {str(e)}"
health["status"] = "unhealthy"
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ヘルスチェックエンドポイントがstatus="unhealthy"の場合でもHTTP 200を返します。一般的な監視ツール(UptimeRobotを含む)は、サービスに問題がある場合にHTTP 5xxステータスコード(例:503 Service Unavailable)を返すことを期待します。データベース接続に失敗した場合など、status="unhealthy"の場合は適切なHTTPステータスコード(503など)を返すことで、監視ツールが即座に問題を検知できるようになります。

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +68
with patch("app.services.skill_tree_service.settings") as mock_settings:
mock_settings.SKILL_TREE_CACHE_MINUTES = 10
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settings オブジェクト全体をモックしていますが、SKILL_TREE_CACHE_MINUTES 以外の属性へのアクセスがあると AttributeError が発生する可能性があります。より安全な方法として、patch.object を使用して特定の属性のみをモックするか、MagicMock に spec 属性を設定することを推奨します。例:patch.object(settings, "SKILL_TREE_CACHE_MINUTES", 10) または mock_settings.configure_mock(**{"SKILL_TREE_CACHE_MINUTES": 10, ...その他の必要な属性...})

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: セキュリティ強化(管理API監査ログ + レートリミット)

2 participants