TECH PLAY

サイオステクノロジー(Tech.Lab)

サイオステクノロジー(Tech.Lab) の技術ブログ

546

はじめに ども!ふと振り返って、ブログを執筆しだして 3 年が経過していることにびっくりした龍ちゃんです。いろいろな開発環境を使っていますが、最近はデプロイする環境なども考えながら開発環境を整備するように至高の変化が起きていました。 今回は、Azure Functions を本番環境で運用するとき、「どうやってデプロイするか」ってお話です。 開発初期以外の手動デプロイは論外として、CI/CD パイプラインを構築するとなると: シークレットキーの管理どうする? – 有効期限切れたら手動更新? デプロイ失敗したときどうする? – 毎回 Azure Portal でログ確認? 複数の Functions どう管理する? – 全部まとめてビルド?それとも個別? 今回は GitHub Actions を使った Azure Functions のデプロイ実装 を実践的に解説します。 特に、 OIDC 認証によるパスワードレスデプロイ を採用することで、シークレットキー管理の手間をゼロにできたのが大きな収穫でした。 この記事でわかること GitHub Actions による Azure Functions デプロイの実装方法 OIDC 認証によるパスワードレスデプロイ (シークレットキー不要) パス条件トリガーによる効率的な CI/CD npm ci + キャッシュ戦略 パッケージ構造検証とトラブルシューティング 実測パフォーマンスとベストプラクティス 前提条件 この記事では、以下がセットアップ済みであることを前提としています: 必須環境 Azure CLI : バージョン 2.30 以上 インストール方法: Azure CLI 公式ドキュメント Azure サブスクリプション : Azure Functions をデプロイするため GitHub リポジトリ : Admin 権限(Secrets/Variables 設定のため) Azure Functions プロジェクト : Node.js + TypeScript Node.js 22 : この記事では Node.js 22 を使用 Programming Model v4 必須 : Node.js 22 を使用する場合、Azure Functions Programming Model v4 への移 行が必須です Azure Functions Runtime v4.25+ : Programming Model v4 をサポートするランタイムバージョン @azure/functions v4.0+ : Programming Model v4 対応パッケージ 参考記事 OIDC 認証の詳細なセットアップ手順については、以下の記事で詳しく解説しています: GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025 年版 User Assigned Managed Identity の作成 Federated Identity Credential の設定 RBAC ロールの付与 ローカル開発環境の構築については、こちらを参照してください: Azure Functions×DevContainer 環境構築| Node.js 22 + TypeScript DevContainer を使った環境構築 ローカルでの開発・デバッグ方法 GitHub Actions CI/CD フロー全体像 まずは、全体の流れを把握しましょう。 フローのポイント パス条件でトリガー – 関連ファイルが変更されたときのみ実行 npm ci でクリーンビルド – 再現性の高いインストール パッケージ構造検証 – デプロイ前に必須ファイルをチェック OIDC 認証 – シークレットキー不要のパスワードレス認証 デプロイ後検証 – 成功確認とエンドポイント表示 それでは、各ステップを詳しく見ていきましょう。 ステップ 1: パス条件トリガーの設定 なぜ必要か モノレポ環境で複数のプロジェクトを管理している場合、 フロントエンドの変更でバックエンドの CI/CD が実行される といった無駄なビルドが発生します。 これを防ぐために、 パスフィルター を設定します。 プロジェクト構成の確認 プロジェクトのディレクトリ構成は、以下のようなディレクトリ構成を想定しています。: / ├── application/ │ ├── backend/ # NestJS API Server │ ├── frontend/ # Next.js App Router │ ├── functions/ # X Scheduler Functions │ └── blog-search-mcp-functions/ # Blog Search MCP Functions ⭐ ├── .github/ │ └── workflows/ │ ├── deploy-blog-search-mcp-functions.yml # このワークフロー ⭐ │ ├── deploy-x-scheduler-functions.yml │ ├── deploy-backend.yml │ └── deploy-frontend.yml └── infrastructure/ # Azure Bicep IaC ポイント : 各プロジェクトが独立したディレクトリに配置 ワークフローファイルも各プロジェクトごとに分離 パス条件でトリガーを制御することで、 無関係なビルドを防止 実装 name: Deploy Blog Search MCP Functions on: push: branches: - main paths: - "application/blog-search-mcp-functions/**" - ".github/workflows/deploy-blog-search-mcp-functions.yml" workflow_dispatch: inputs: environment: description: "Deployment environment" required: true default: "production" type: choice options: - production - staging - dev ポイント paths フィルターで 関連ファイルの変更のみトリガー ワークフロー自体の変更もトリガー対象 に含める( .github/workflows/... ) workflow_dispatch で 手動実行を可能 にする(緊急時のロールバック等) 効果 実際にこの設定を導入した結果: 不必要なビルド: 70%削減 CI/CD コスト: 65%削減 デプロイ時間: 50%短縮 (並列実行) ステップ 2: OIDC 認証の設定 OIDC 認証とは OIDC(OpenID Connect)認証は、 短命なトークンを使った認証方式 です。 従来のシークレットキーベース認証との違いを見てみましょう。 従来の認証方式の問題点 # ❌ 非推奨:シークレットベース認証 - uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} 問題 : シークレットの有効期限管理が必要 定期的なローテーション作業 シークレット漏洩リスク OIDC 認証の実装 permissions: id-token: write # OIDC トークン取得に必要 contents: read jobs: deploy: runs-on: ubuntu-latest environment: "production" steps: - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 必要な GitHub Secrets AZURE_CLIENT_ID: xxx-xxx-xxx (Managed IdentityのClient ID) AZURE_TENANT_ID: xxx-xxx-xxx (テナントID) AZURE_SUBSCRIPTION_ID: xxx-xxx-xxx (サブスクリプションID) 重要 : これらは すべて識別子のみ で、シークレットキーは含まれていません。つまり、 万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みです。 OIDC 認証の仕組み(簡略版) GitHub Actions Job ↓ Request OIDC Token (JWT) ↓ GitHub OIDC Provider ↓ GitHub OIDC Token (5分間有効) ↓ Azure AD (トークン交換) ↓ (Verify Federated Identity Credential) User Assigned Managed Identity ↓ Azure Access Token (1-24時間有効) ↓ Azure Functions Deployment メリット シークレットキー不要 – パスワードレス認証でセキュリティリスク削減 自動ローテーション – トークンは短命で自動更新、手動ローテーション不要 漏洩リスク最小化 – 識別子のみで、シークレットが存在しない セキュリティベストプラクティス – Microsoft 公式推奨 トークンの有効期限について : GitHub OIDC トークン : 5分間(GitHub が発行する認証用 JWT) Azure アクセストークン : 1時間(Service Principal)または24時間(Managed Identity) 実際のワークフロー実行では Azure アクセストークンが使用されるため、十分な有効期限があります 詳細なセットアップ手順と OIDC 認証の仕組みについては、 こちらの記事 を参照してください。 ステップ 3: Node.js セットアップと npm ci Setup Node.js with Cache - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: "application/blog-search-mcp-functions/package-lock.json" ポイント Node.js 22 LTS を使用(Azure Functions v4 対応) npm キャッシュ を有効化( cache: "npm" ) cache-dependency-path でモノレポの特定パスを指定 補足 : setup-node は package-lock.json のハッシュ値を自動計算してキャッシュキーを生成します。より高度なキャッシング戦略( actions/cache + hashFiles() による明示的制御)については、 actions/setup-node 公式リポジトリ を参照してください。 npm ci vs npm install - name: Install dependencies run: | cd application/blog-search-mcp-functions npm ci # ✅ npm install ではなく npm ci npm ci の利点 : package-lock.json を厳密に尊重(再現性) node_modules/ をクリーンインストール CI/CD 環境に最適化(高速) バージョンの不一致を防ぐ 実測値 処理 初回 キャッシュヒット npm ci ~30 秒 ~5 秒 npm run build ~15 秒 ~15 秒 キャッシュ効果: ビルド時間 30-40%短縮 ステップ 4: パッケージ構造検証 なぜ必要か Azure Functions のデプロイは、 正しいファイル構成がないと失敗 します。 特に以下のファイルは必須: host.json – Functions App 設定 package.json – 依存関係定義 dist/ – ビルド成果物(TypeScript → JavaScript) デプロイ前にこれらを検証することで、 失敗を早期に検出 できます。 実装 - name: Verify package structure run: | cd application/blog-search-mcp-functions echo "=== Package root contents ===" ls -la echo "" # host.json検証 echo "=== Checking host.json ===" if [ -f "host.json" ]; then echo "✅ host.json exists" cat host.json | jq . || echo "⚠ host.json is not valid JSON" else echo "❌ host.json missing!" exit 1 fi echo "" # package.json検証 echo "=== Checking package.json ===" if [ -f "package.json" ]; then echo "✅ package.json exists" node -e "console.log('✅ package.json is valid JSON')" -p "require('./package.json')" else echo "❌ package.json missing!" exit 1 fi echo "" # dist/ ディレクトリ検証 echo "=== Checking for compiled files in dist ===" if [ -d "dist" ]; then echo "✅ dist directory exists" find dist -name "*.js" | head -10 else echo "❌ dist directory missing!" exit 1 fi echo "" echo "=== All files in package (first 50) ===" find . -type f | head -50 ポイント jq で JSON 妥当性検証 Node.js で require() テスト(構文エラー検出) 失敗時は exit 1 で即座にワークフロー停止 ログ出力を見やすくフォーマット 実際のログ出力例 === Package root contents === drwxr-xr-x dist -rw-r--r-- host.json -rw-r--r-- package.json ... === Checking host.json === ✅ host.json exists { "version": "2.0", "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental", "version": "[4.*, 5.0.0)" } } === Checking package.json === ✅ package.json exists ✅ package.json is valid JSON === Checking for compiled files in dist === ✅ dist directory exists dist/src/app.js dist/src/functions/searchBlogPosts.mcp.js ... この検証により、 デプロイ失敗の原因を事前に特定 できます。 ステップ 5: テスト実行 –passWithNoTests フラグ プロジェクト初期段階では、テストファイルがないことがあります。 - name: Run tests run: | cd application/blog-search-mcp-functions npm test -- --passWithNoTests なぜ必要か Jest のデフォルト動作では、 テストが見つからない場合は exit code 1 で失敗 します。 --passWithNoTests フラグを使うことで: テストファイルがなくても CI/CD が通る 将来テストを追加した際、自動的にテストが実行される 開発初期段階の CI/CD 構築に最適 推奨アプローチ プロジェクト初期(テストなし) : --passWithNoTests 使用 テスト追加開始 : フラグ維持、段階的にテスト追加 テストカバレッジ確立後 : フラグ削除(テスト必須化) ステップ 6: Azure Functions デプロイ デプロイ前の確認 - name: Check Function App status before deployment run: | echo "Checking current Function App status..." az functionapp show \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --query "{name:name, state:state, kind:kind}" \ --output table 現在の Function App の状態を確認することで、デプロイ前の異常を検出できます。 デプロイ実行 - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} package: "application/blog-search-mcp-functions" respect-funcignore: true scm-do-build-during-deployment: false enable-oryx-build: false id: deploy デプロイを実行する際は、作成しているAzure Functionsのアプリ名のみで指定をすることができます。 デプロイ設定の詳細 respect-funcignore: true .funcignore ファイルを尊重し、不要なファイルをデプロイパッケージから除外します。 # .funcignore *.ts src/ tsconfig.json .git/ .github/ node_modules/ tests/ *.md 効果 : デプロイパッケージサイズを最小化 ビルド成果物( dist/ )のみアップロード デプロイ時間短縮 scm-do-build-during-deployment: false Azure 側でのビルドをスキップします。 理由 : GitHub Actions 側で事前ビルド済み デプロイ時間短縮 予測可能性向上 enable-oryx-build: false Oryx(Azure 自動ビルドシステム)を無効化します。 理由 : 明示的なビルドプロセス管理 トラブルシューティングが容易 設定の互換性に関する重要な注意事項 WEBSITE_RUN_FROM_PACKAGE との非互換性 以下の設定の組み合わせは 互換性がありません : WEBSITE_RUN_FROM_PACKAGE=1 + SCM_DO_BUILD_DURING_DEPLOYMENT=true (非互換) WEBSITE_RUN_FROM_PACKAGE=1 + SCM_DO_BUILD_DURING_DEPLOYMENT=false (推奨) 理由 : WEBSITE_RUN_FROM_PACKAGE は ZIP パッケージから直接実行する設定であり、デプロイ時のビルドと競合します。 リモートビルドを有効化する場合(Linux) : ネイティブモジュール(Puppeteer、Sharp等)を使用する場合は、リモートビルドが必要です: - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: ${{ vars.FUNCTION_APP_NAME }} package: "path/to/function" scm-do-build-during-deployment: true # リモートビルド有効化 enable-oryx-build: true # Oryx ビルド有効化 注意 : 両方を true に設定する必要があります(Linuxのみ)。 ステップ 7: デプロイ後検証 成功時の検証 - name: Verify deployment success if: success() run: | echo "✅ Deployment successful. Verifying MCP Server..." sleep 30 # Function App起動待機 # アプリ設定確認 az functionapp config show \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --query "{nodeVersion:nodeVersion, platform:linuxFxVersion}" \ --output table # Function App URL取得 FUNCTION_APP_URL=$(az functionapp show \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --query "defaultHostName" \ --output tsv) echo "MCP Endpoint: https://$FUNCTION_APP_URL/runtime/webhooks/mcp/sse" ポイント sleep 30 で Function App 起動待機(コールドスタート対策) Node.js バージョン確認(意図しないバージョン使用防止) エンドポイント URL を明示的に表示(手動テスト用) 失敗時のリカバリー - name: Check deployment result and restart if needed if: failure() run: | echo "❌ Deployment failed. Attempting recovery..." echo "Restarting Function App..." az functionapp restart \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} echo "Waiting for restart to complete..." sleep 60 echo "Checking Function App logs..." az functionapp logs tail \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --timeout 60 || echo "Could not retrieve logs" 自動リカバリーの利点 デプロイ失敗後の手動介入を減らす ログ取得による問題診断の容易化 一時的な問題(ネットワークエラーなど)からの自動復旧 実装例:Blog Search MCP Functions ここまでの設定を統合した完全なワークフローファイルを見てみましょう。 完全なワークフロー name: Deploy Blog Search MCP Functions on: push: branches: - main paths: - "application/blog-search-mcp-functions/**" - ".github/workflows/deploy-blog-search-mcp-functions.yml" workflow_dispatch: permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest environment: "production" steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: "application/blog-search-mcp-functions/package-lock.json" - name: Install dependencies run: | cd application/blog-search-mcp-functions npm ci - name: Build Functions run: | cd application/blog-search-mcp-functions npm run build - name: Verify package structure run: | cd application/blog-search-mcp-functions # ... (前述の検証スクリプト) - name: Run tests run: | cd application/blog-search-mcp-functions npm test -- --passWithNoTests - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} package: "application/blog-search-mcp-functions" respect-funcignore: true scm-do-build-during-deployment: false enable-oryx-build: false - name: Verify deployment success if: success() run: | # ... (前述の検証スクリプト) プロジェクト構成 application/blog-search-mcp-functions/ ├── package.json ├── tsconfig.json ├── host.json # Experimental Bundle設定 ├── .funcignore # デプロイ除外ファイル ├── src/ │ ├── app.ts # エントリーポイント │ ├── functions/ # MCP Tool定義 │ │ ├── searchBlogPosts.mcp.ts │ │ ├── listAllCategories.mcp.ts │ │ └── listAllHashtags.mcp.ts │ └── blog-search-mcp/ # 共通モジュール │ ├── types/ │ ├── schemas/ │ └── services/ └── dist/ # ビルド成果物(TypeScript → JS) package.json { "name": "blog-search-mcp-functions", "version": "1.0.0", "scripts": { "build": "tsc", "start": "npm run build && func start", "test": "jest --passWithNoTests" }, "dependencies": { "@azure/functions": "^4.0.0", "@supabase/supabase-js": "^2.76.1", "zod": "^4.1.12" }, "devDependencies": { "@types/node": "^22.10.1", "typescript": "^5.7.2" }, "engines": { "node": ">=22.0.0" } } Environment 変数 vs Secrets GitHub Secrets と Variables の適切な使い分けも重要です。 使い分けの基準 Secrets(機密情報) : secrets: AZURE_CLIENT_ID # OIDC認証情報 AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID Variables(非機密情報) : variables: APP_NAME # アプリケーション名 RESOURCE_GROUP # リソースグループ名 BLOG_SEARCH_MCP_FUNCTION_APP_NAME # Function App名 X_SCHEDULER_FUNCTION_APP_NAME # Function App名 判断基準 Secrets : 漏洩すると重大な影響(認証情報、API キー、トークン) Variables : 公開されても問題ない(リソース名、設定値) 設定方法 手動設定 GitHub リポジトリの「Settings」→「Secrets and variables」→「Actions」から設定します。 自動設定(推奨) Bicep デプロイスクリプト( deploy.sh )で自動設定することも可能です: # GitHub CLI の存在確認 if command -v gh &> /dev/null; then echo "GitHub CLIを使用してシークレットを設定中..." # Repository secrets(リポジトリ全体で共通) echo "$MANAGED_IDENTITY_CLIENT_ID" | gh secret set AZURE_CLIENT_ID --repo "$GITHUB_ORG/$GITHUB_REPO" # Environment variables(環境ごと) gh variable set BLOG_SEARCH_MCP_FUNCTION_APP_NAME \ --repo "$GITHUB_ORG/$GITHUB_REPO" \ --env production \ --body "$BLOG_SEARCH_MCP_FUNCTION_APP_NAME" fi メリット : インフラデプロイと GitHub 設定を一括実行 手動設定ミスの防止 環境構築の再現性向上 パフォーマンス実測値 実際に運用している環境での実測値を紹介します。 ビルド時間(Blog Search MCP Functions) ステップ 初回 キャッシュヒット Setup Node.js ~10 秒 ~10 秒 npm ci ~30 秒 ~5 秒 npm run build ~15 秒 ~15 秒 Deploy ~2-8 分 ~2-8 分 合計 約 3-9 分 約 2.5-8.5 分 注 : デプロイ時間は、関数のパッケージサイズ、依存関係の数、Azure リージョンの混雑状況によって大きく変動します。 小規模関数 (最小限の依存関係):約 2-3 分 中規模関数 (一般的な依存関係):約 5-8 分 大規模関数 (多数の依存関係):約 10-15 分 上記の実測値は、小規模な MCP Functions の場合です。本番環境での一般的なアプリケーションでは、 5-15 分程度を見込むことを推奨 します。 最適化効果 最適化項目 効果 パスフィルター導入 不必要なビルド 70%削減 npm キャッシュ ビルド時間 30-40%短縮 並列ワークフロー 全体デプロイ時間 50%短縮 トラブルシューティング よくあるエラーと対処法をまとめます。 エラー 1: Package deployment failed 症状 : Error: Package deployment failed 原因 : host.json がパッケージに含まれていない package.json が不正な JSON dist/ ディレクトリが空 解決策 : パッケージ構造検証ステップを追加し、 .funcignore を確認します。 - name: Verify package structure run: | # host.json, package.json, dist/ の検証 エラー 2: OIDC token exchange failed 症状 : Error: OIDC token exchange failed 原因 : Federated Identity Credential の設定ミス permissions: id-token: write の欠落 解決策 : permissions 確認 : permissions: id-token: write # 必須 contents: read Federated Credential 確認 : Azure Portal で、User Assigned Managed Identity の「Federated credentials」を確認し、subject パターンが一致しているか確認します。 repo:org/repo:environment:production 詳細は こちらの記事 を参照してください。 エラー 3: Function runtime error 症状 : デプロイは成功するが、Function App が起動しない 原因 : Node.js バージョン不一致 依存関係の欠落 解決策 : デプロイ後に Node.js バージョンを確認: az functionapp config show \ --name <app-name> \ --resource-group <rg> \ --query "nodeVersion" package.json の engines フィールドと一致しているか確認します。 { "engines": { "node": ">=22.0.0" } } エラー 4: npm ci fails 症状 : Error: npm ci can only install packages when your package.json and package-lock.json are in sync 原因 : package.json と package-lock.json の不一致 解決策 : ローカルで package-lock.json を再生成: rm package-lock.json npm install git add package-lock.json git commit -m "chore: regenerate package-lock.json" 実装のポイントまとめ カテゴリ DO(推奨) DON’T(非推奨) 認証 OIDC 認証を使用 ・パスワードレス、セキュア ・シークレット管理不要 ・自動ローテーション シークレットベース認証 ・有効期限管理の負担 ・定期的なローテーション作業 ・漏洩リスク 依存関係管理 npm ci を使用 ・ package-lock.json を厳密に尊重 ・再現性、高速性 ・CI/CD 専用の最適化 npm install 使用 ・バージョン不一致リスク ・再現性が低い ・予期しない依存関係の更新 トリガー設定 パスフィルターを設定 ・不必要なビルド 70%削減 ・CI/CD コスト 65%削減 ・デプロイ時間 50%短縮 パスフィルターなし ・すべての変更でビルド実行 ・リソース浪費 ・CI/CD コスト増大 テスト –passWithNoTests(初期段階) ・テストなしでも CI/CD 通過 ・段階的なテスト追加 ・開発速度維持 テスト必須化(初期段階) ・開発速度低下 ・CI/CD 構築の遅延 ・実装優先フェーズで障害 検証 パッケージ構造検証 ・デプロイ前の構造確認 ・失敗の早期検出 ・デバッグ時間短縮 手動検証のみ ・人的ミス防止できない ・デプロイ失敗後に気づく ・手戻りコスト増大 デプロイ設定 事前ビルド戦略 ・ scm-do-build: false ・ enable-oryx-build: false ・GitHub Actions 側で事前ビルド Azure 側ビルド ・ scm-do-build: true ・デプロイ時間が長い ・予測可能性が低い リカバリー 自動リカバリー処理 ・失敗時の自動再起動 ・ログ自動取得 ・一時的な問題から自動復旧 手動リカバリーのみ ・毎回手動介入が必要 ・復旧時間が長い ・夜間デプロイ失敗時の対応遅延 注 : 本番環境に移行する際は、 --passWithNoTests フラグを削除してテストを必須化しましょう。品質保証のため、テストカバレッジを確立した後は必ずテストが実行される状態にすることを推奨します。 まとめ この記事で実装したこと GitHub Actions による Azure Functions デプロイパイプライン OIDC 認証によるパスワードレスデプロイ パス条件トリガーで効率化 (不要なビルド 70%削減) npm ci + キャッシュで高速化 (ビルド時間 30-40%短縮) パッケージ構造検証で品質保証 自動リカバリー処理 得られる効果 セキュリティ面 : シークレットキー管理不要 自動ローテーション 漏洩リスク最小化 効率面 : デプロイ時間短縮(小規模関数で約 2-3 分、一般的には 5-15 分) CI/CD コスト削減(65%) 手動作業の削減 品質面 : デプロイ失敗の早期検出 自動検証とリカバリー 再現性の高いビルド 次のステップ さらに発展させるには: マルチ環境デプロイ – staging/production 環境の管理 Bicep 統合 – インフラと CI/CD の完全自動化 Python 版 – Python Runtime での実装 モニタリング統合 – Application Insights との連携 参考リンク 公式ドキュメント Azure Functions GitHub Actions OIDC 認証(Azure) 関連記事 GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025 年版 Azure Functions×DevContainer 環境構築| Node.js 22 + TypeScript GitHub Actions を使った Azure Functions のデプロイ、ぜひ試してみてください! OIDC 認証によるパスワードレスデプロイで、セキュリティと効率性の両方を手に入れられます。 質問や改善提案があれば、ぜひコメントで教えてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub Actions×Azure Functions実装ガイド|OIDC認証で実現するセキュアなCI/CD(Node.js編) first appeared on SIOS Tech. Lab .
アバター
はじめに ども!久しぶりに公式ドキュメントをあさっていたら、自分が使っていた発行プロファイル認証が「not recommended」と記載されていてビックリ仰天した龍ちゃんです。 皆さん、GitHub Actions から Azure リソースにデプロイする際、どの認証方式を使っていますか? 「え、推奨されない方式だったの!?」って大焦りで調べたら、 発行プロファイルは現在もサポートされている ものの、 セキュリティベストプラクティスとして OIDC 認証への移行が強く推奨されている ことが判明しました。 特に以下の理由から、Microsoft は OIDC 認証を推奨しています: 発行プロファイルは Basic 認証を使用しており、セキュリティ上の懸念がある 発行プロファイルには平文パスワードが含まれる OIDC 認証はシークレットキー不要で、より安全 そこで、 Microsoft 公式が推奨する OIDC(OpenID Connect)認証に直接移行 したんですが、設定できるならこっちのほうが良いなとなったのでまとめていきます! シークレットキー不要でパスワードレス認証 ができて、セキュリティレベルが大幅に向上しました。 今回は、 GitHub Actions から Azure への 3 つの認証方式を比較 し、 Azure CLI での爆速セットアップ方法を解説 します! この記事でわかること Azure 認証方式 3 つの徹底比較 (発行プロファイル vs Service Principal vs OIDC) OIDC 認証の仕組み と他方式との違い Azure CLI でのフェデレーション認証設定方法 (コマンド実行) GitHub Actions での汎用的な認証設定方法 必要な権限 とその確認方法(超重要!) トラブルシューティング の実例と解決方法 この記事の対象読者 以下のような方にお勧めです。 Azure CLI の基本操作ができる方 GitHub Actions から Azure にデプロイしたい方 発行プロファイルを使っていて、セキュアな方式に移行したい方 シークレットキー管理の手間を減らしたい DevOps エンジニア ターミナル操作に抵抗がない方 なぜ Azure CLI を使うのか? 爆速セットアップ : コピペで 5 分で完了 再現性が高い : コマンドをスクリプト化できる IaC 化しやすい : Bicep/Terraform への移行が容易 ポータルのポチポチ作業は不要です! それでは、見ていきましょう! はじめに – なぜ OIDC 認証が必要なのか? GitHub Actions から Azure 認証の重要性 GitHub Actions から Azure Functions、Web Apps、Static Web Apps、Container Apps などにデプロイする際、 Azure への認証 が必要です。 この認証方式、実は 3 つの選択肢 があるんですよね: 発行プロファイル (Publish Profile) Service Principal + シークレットキー OIDC 認証 (OpenID Connect) 並べた順に難しくなっていきます。結論から言うと、 Microsoft 公式が推奨しているのは OIDC 認証 です。 なぜ OIDC 認証が推奨されるのか、他の 2 つの方式と比較しながら見ていきましょう。 Azure 認証方式 3 つの徹底比較 実体験と移行の経緯 正直に言うと、 私が実際に使ったことがあるのは「発行プロファイル」と「OIDC 認証」の 2 つだけ です。 Service Principal は選択肢として存在しますが、 セキュリティベストプラクティスとして OIDC 認証が推奨されていると分かった時点で、直接 OIDC 認証に移行しました 。 なぜ Service Principal をスキップしたかというと: シークレットキー管理の手間 : Service Principal も結局シークレットキーが必要で、定期的なローテーション作業が発生する OIDC 認証が最終解 : セキュリティベストプラクティスとして推奨されているなら、最初から OIDC 認証にした方が合理的 移行コスト : Service Principal に移行してから、さらに OIDC に移行するのは二度手間 つまり、 「発行プロファイル → Service Principal(中間) → OIDC(推奨)」という段階を踏まず、最初から最終形態に移行した わけです。 ただし、Service Principal は歴史的経緯や他のプロジェクトで遭遇する可能性もあるので、選択肢として紹介しておきます。 それでは、3 つの方式を詳しく比較していきましょう! 1. 発行プロファイル方式(最も簡単だが、セキュリティ上推奨されない) 仕組み 発行プロファイル(Publish Profile)は、Azure ポータルから XML ファイル をダウンロードして、GitHub Secrets に保存する方式です。 Azure Portal → リソース → 発行プロファイルのダウンロード ↓ GitHub Secrets に AZURE_PUBLISH_PROFILE として保存 ↓ GitHub Actions でデプロイ コード例 # 発行プロファイル方式 jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: "my-function-app" publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }} # XML形式 発行プロファイルの中身(例) : <publishData> <publishProfile profileName="my-function-app - Web Deploy" publishMethod="MSDeploy" publishUrl="my-function-app.scm.azurewebsites.net:443" userName="$my-function-app" userPWD="verylongsecretpassword123..." <!-- ← これが問題! --> ... /> </publishData> メリット 設定が超簡単 : Azure ポータルから 1 クリックでダウンロード 初心者にやさしい : 複雑な権限設定が不要 すぐに動く : 5 分で設定完了 デメリット セキュリティリスク大 : パスワードが平文で含まれる Basic 認証を使用 : Microsoft が「inherently insecure(本質的に安全でない)」と警告 監査ログ不足 : 誰がデプロイしたか追跡困難 権限が広すぎる : リソース全体への管理者権限 ローテーション困難 : パスワード更新が面倒 Microsoft 非推奨 : セキュリティベストプラクティスとして OIDC 認証が推奨されている 重要な注意 : Microsoft 公式ドキュメントでは「The technique described in this article is inherently insecure, because this technology uses Basic Authentication」と明記されており、本番環境での使用は推奨されていません。学習目的や個人プロジェクトでの利用にとどめ、本番環境では OIDC 認証を採用することを強くお勧めします。 2. Service Principal + シークレットキー方式(従来の推奨) 仕組み Azure AD で Service Principal(サービスプリンシパル)を作成し、 Client Secret(シークレットキー) を発行して認証する方式です。 Azure AD → Service Principal作成 ↓ Client Secret発行(有効期限: 最長2年) ↓ GitHub Secretsに保存 ↓ GitHub Actionsで認証 コード例 # Service Principal + シークレットキー方式 jobs: deploy: runs-on: ubuntu-latest steps: - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} # JSON形式 AZURE_CREDENTIALS の中身(例) : { "clientId": "xxx-xxx-xxx", "clientSecret": "your-secret-key-here", // ← シークレットキー "subscriptionId": "xxx-xxx-xxx", "tenantId": "xxx-xxx-xxx" } Service Principal の作成方法 # Service Principal作成 az ad sp create-for-rbac \ --name "github-actions-sp" \ --role "Contributor" \ --scopes "/subscriptions/{subscription-id}/resourceGroups/{resource-group}" \ --sdk-auth 出力された JSON をそのまま GitHub Secrets に保存します。 メリット RBAC 権限管理 : 必要な権限のみ付与可能 監査ログ充実 : Azure AD での詳細なログ 複数リソース対応 : 1 つの Service Principal で複数リソースにアクセス よく使われている : 情報が豊富 デメリット シークレットキー管理 : 定期的なローテーションが必要(有効期限: 最長 2 年) 漏洩リスク : シークレットキーが漏洩したら、全リソースにアクセス可能 管理コスト : 有効期限が切れたら、手動で更新が必要 GitHub Secrets に保存 : 暗号化はされているが、アクセス可能 重要な補足: Service Principal でも OIDC 認証が可能 実は、 Service Principal でも Federated Identity Credentials を使えば、シークレットキー不要の OIDC 認証が可能 です。 つまり、以下の 2 つの方式は技術的には非常に類似しています: Service Principal + Federated Credentials Managed Identity + Federated Credentials (この記事で解説する方式) 主な違い : Managed Identity : 常に Azure リソースに紐づけられる。Azure が自動的に認証情報を管理 Service Principal : Azure リソースに紐づけなくても独立して存在可能 なぜ Managed Identity を選んだか : Azure リソース(Functions, Web Apps 等)にデプロイする場合、Managed Identity の方がシンプル リソースとの紐付けが明確で、管理しやすい Microsoft のベストプラクティスドキュメントでも Managed Identity が推奨されている ただし、複数の Azure サブスクリプションをまたいでアクセスする必要がある場合など、Service Principal + Federated Credentials の方が適している場合もあります。 3. OIDC 認証方式(Microsoft 公式推奨) 仕組み OIDC(OpenID Connect)認証は、 短命なトークンを使った認証方式 です。シークレットキーが一切不要なのが最大の特徴。 GitHub Actions → GitHub OIDCプロバイダー ↓ OIDCトークン発行(5分間有効) ↓ Azure AD → Federated Identity Credential検証 ↓ Managed Identity → Azureアクセストークン発行 ↓ Azureリソースにデプロイ トークン有効期限の詳細: GitHub OIDC トークン(JWT): 5 分間 有効 Azure アクセストークン: 約 60-90 分 (平均 75 分、一般的には 1 時間として扱われる) トークンの更新: Azure SDK/CLI が自動的に管理(手動更新不要) コード例 # OIDC認証方式(推奨) jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write # ← OIDC認証に必須! contents: read steps: - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} # シークレットキーは不要! 必要なシークレット(すべて識別子のみ) : AZURE_CLIENT_ID: xxx-xxx-xxx (Managed IdentityのClient ID) AZURE_TENANT_ID: xxx-xxx-xxx (テナントID) AZURE_SUBSCRIPTION_ID: xxx-xxx-xxx (サブスクリプションID) 重要 : これらは すべて識別子のみ で、シークレットキーは含まれていません。つまり、 万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みです。 メリット シークレットキー不要 : パスワードレス認証でセキュリティリスク削減 自動ローテーション : GitHub の OIDC トークン(JWT)は 5 分間のみ有効、Azure のアクセストークンも自動管理(手動ローテーション不要) 漏洩リスク最小化 : 識別子のみで、シークレットが存在しない 監査ログ充実 : Azure AD での詳細な認証ログ セキュリティベストプラクティス : Azure App Service 向けに公式推奨 リポジトリ・ブランチ制限 : 特定の GitHub リポジトリ・ブランチからのみ認証可能 デメリット 初期設定がやや複雑 : Azure ポータルでの設定が必要 理解に時間がかかる : OIDC の仕組みを理解する必要がある 権限が必要 : Managed Identity 作成と RBAC 設定に管理者権限が必要 私の経験 : 最初は「設定が複雑そう…」って敬遠していたんですが、一度設定してしまえば、その後の管理が超ラク!シークレットキーローテーションの手間がゼロになったのは感動しました。 イメージとしては、GitHub リポジトリ自体に認証の権限を割り振る という感じです。発行プロファイルや Service Principal のように「シークレットを GitHub Secrets に保存する」のではなく、「このリポジトリからの実行は信頼できる」と Azure 側で事前に承認しておくイメージですね。 3 つの認証方式 比較表 項目 発行プロファイル Service Principal OIDC 認証 セキュリティ 低 中 高 設定の簡単さ 超簡単 普通 やや複雑 シークレット管理 パスワード必要 シークレットキー必要 不要 ローテーション 困難 手動(年 1 回程度) 完全自動(手動不要) 漏洩リスク 高 中 最小 監査ログ 不足 充実 充実 権限制御 広すぎる RBAC 可能 RBAC 可能 Microsoft 推奨 Not Recommended サポート継続 ベストプラクティス 初期設定時間 5 分 15 分 30 分 運用コスト 中 中 低 どの方式を選ぶべきか? 結論 : セキュアに運用したいのであれば OIDC 認証一択 です。 個人プロジェクト・学習用 : 発行プロファイルで素早くスタート → 慣れたら OIDC に移行 チーム開発・本番環境 : 最初から OIDC 認証を採用 私も複数のプロジェクトで検証しましたが、 長期的に見ると OIDC 認証が圧倒的にコストパフォーマンスが高い です。というか、発行プロファイルの管理が面倒くさかったです。再発行のたびに GitHub Secret に保存ってアプリが増えれば増えるほど面倒になるんですよね。特にモノレポ環境であれば、効果絶大です。 前提条件と必要な権限 必要な環境 OIDC 認証を設定するには、以下の環境が必要です: Azure CLI : バージョン 2.30 以上(インストール方法は後述) Azure サブスクリプション : Azure リソースをデプロイするため GitHub リポジトリ : Admin 権限が必要(GitHub Secrets を設定するため) ターミナル : Bash、PowerShell、または Zsh Azure CLI のインストール確認 すでに Azure CLI がインストールされているか確認しましょう: az version 出力例 : { "azure-cli": "2.50.0", "azure-cli-core": "2.50.0", "azure-cli-telemetry": "1.0.8", ... } まだインストールしていない場合 方法 1: ローカルマシンにインストール macOS / Linux (Homebrew): brew install azure-cli Windows (winget): winget install Microsoft.AzureCLI その他のインストール方法: Azure CLI 公式ドキュメント 方法 2: DevContainer でチーム全体の環境を統一(オプション) もし DevContainer を使って開発をしているなら、この方法も検討できます。DevContainer Features を使えば、チーム全体で同じバージョンの Azure CLI を使用でき、環境差異によるトラブルを防げます。 注意 : Docker Desktop のライセンスや、企業のセキュリティポリシーによる制約がある場合は、ローカルインストールをお勧めします。 .devcontainer/devcontainer.json : { "name": "Azure Development", "image": "mcr.microsoft.com/devcontainers/base:bullseye", "features": { "ghcr.io/devcontainers/features/azure-cli:1": { "version": "latest" } } } メリット : チーム全員が同じ環境で作業可能 新メンバーのオンボーディングが簡単(コンテナ起動するだけ) CI/CD 環境とローカル環境の差異をなくせる 詳しい手順 : DevContainer と Azure CLI の詳細な環境構築手順は、別記事「 DevContainer 実践入門:Azure CLI+GitHub CLI 環境をチーム全体で統一 」で解説しています。Azure CLI、GitHub CLI、SWA CLI を統一環境として構築する方法を紹介しているので、ぜひご覧ください。 Azure へのログイン Azure CLI で Azure にログインします: az login ブラウザが開くので、Azure アカウントでログインしてください。 ログイン確認 : az account show 出力例 : { "id": "xxx-xxx-xxx-xxx", "name": "Pay-As-You-Go", "tenantId": "xxx-xxx-xxx-xxx", "user": { "name": "user@example.com", "type": "user" } } 必要な権限(超重要!) ここが一番重要なポイントです。 OIDC 認証設定とフェデレーション認証の設定には、 かなり強い権限が必要 です。 私も最初、権限不足でエラーに悩まされて、 Azure の管理者に 3 回も確認とお願いの申請をしました …。社内の担当者には本当に頭が上がりません。 なので、 設定前に必ず権限を確認しておくことを強くオススメします 。 最小権限セット 以下の操作を実行するには、それぞれ対応する権限が必要です: User Assigned Managed Identity の作成 : Microsoft.ManagedIdentity/userAssignedIdentities/write Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/write RBAC 権限割り当て : Microsoft.Authorization/roleAssignments/write これは リソースグループレベルの「所有者」または「ユーザーアクセス管理者」ロールが必要 リソースデプロイ全般 : Microsoft.Resources/deployments/write 推奨ロール リソースグループレベルの「所有者」ロールが最も簡単 です。 もしくは、以下の組み合わせ: 共同作成者 (Contributor) + ユーザーアクセス管理者 (User Access Administrator) 私の場合、リソースグループの所有者権限を割り振ってもらいました。 権限確認方法 Azure CLI で現在のユーザーの権限を確認します: # リソースグループを変数に設定(後で使います) RESOURCE_GROUP="rg-example" # 実際のリソースグループ名に置き換え # 自分のロール割り当てを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName, Scope:scope}" \ --output table 期待される出力 : Principal Role Scope -------------------- ------ ---------------------------------------- user@example.com Owner /subscriptions/.../resourceGroups/rg-example 「Owner」ロールが表示されていれば OK! もし Owner ロールがない場合は、以下のいずれかを確認してください: Contributor + User Access Administrator の組み合わせ サブスクリプションレベルでの Owner ロール 権限が不足している場合の対処 管理者に権限を依頼する 権限が不足している場合は、以下のテンプレートで管理者に依頼しましょう。(こういう時の AI はマジで頼りになりますよね。) 件名: リソースグループへの所有者ロール付与依頼 以下のリソースグループに対して「所有者」ロールを付与してください: - リソースグループ名: <RESOURCE_GROUP_NAME> - 理由: GitHub OIDC認証設定とAzureリソースデプロイのため 必要な具体的な操作: 1. User Assigned Managed Identityの作成 2. Federated Identity Credentialの設定 3. RBAC権限割り当て よろしくお願いいたします。 ポイント : 権限確認は 設定前に必ず実施 しましょう。 途中でエラーになると、中途半端な状態で止まってしまい、トラブルシューティングが超面倒です。 さらに、誰もメンテナンスしていない Managed Identity やロールが作られてしまい、 後から担当者に「これ何に使ってるんですか?」って確認が飛んでくる 可能性もあります…(察してください…)。 OIDC 認証の仕組み詳細 OIDC 認証フロー フローの詳細解説 GitHub Actions が OIDC トークンをリクエスト : ワークフローに permissions.id-token: write を設定 GitHub Actions が自動的に GitHub OIDC プロバイダーにリクエスト GitHub が OIDC トークン(JWT)を発行 : リポジトリ、ブランチ、環境などの情報を含む JWT トークンを発行 有効期限: 5 分間(非常に短命で安全) Azure にトークンを Exchange(交換) : azure/login@v2 アクションが自動的に実行 Client ID、Tenant ID、Subscription ID を使用 ここが OIDC 認証の魔法のポイント!   GitHub Actions 上で発行したトークンを Azure 上のアクセストークンに交換しています Azure がフェデレーション認証情報を検証 : issuer: https://token.actions.githubusercontent.com (GitHub 固定) subject: repo:{org}/{repo}:environment:production (設定したパターン) audiences: api://AzureADTokenExchange (Azure 固定) Managed Identity を検証 : Azure AD が Managed Identity の存在を確認 RBAC 権限を確認 Azure アクセストークンを発行 : GitHub Actions が使用できる Azure アクセストークンを発行 Azure リソースにアクセス : Azure Functions、Web Apps などにデプロイまたはアクセス 操作成功 : 結果を GitHub Actions に返す なぜシークレットキー不要なのか? OIDC 認証の魔法は、 GitHub と Azure の信頼関係(Trust Relationship)  にあります。 従来方式 : GitHub Actions → シークレットキー提示 → Azure「認証OK」 OIDC 認証 : GitHub Actions → OIDCトークン提示 → Azure「このトークン、本当にGitHubが発行した?」 ↓ Federated Identity Credentialで検証 ↓ 「リポジトリ・ブランチも一致!」→ 認証OK ポイント : GitHub の OIDC トークン(JWT)は 5 分間のみ有効 (非常に短命で安全) Azure が発行するアクセストークンは約 60-90 分有効 (Azure SDK/CLI が自動管理) 特定のリポジトリ・ブランチ からのみ認証可能(Federated Identity Credential で制限) トークン自体が GitHub の署名付き で、改ざん不可能 つまり、シークレットキーを保存しなくても、「この GitHub Actions の実行は、確かに信頼できるリポジトリからのものだ」と証明できるわけです! Azure CLI での爆速セットアップ それでは、Azure CLI を使って OIDC 認証を設定していきましょう! 所要時間: 約 5 分(コピペで完結) ステップは全部で 3 つです: User Assigned Managed Identity 作成 Federated Identity Credential 設定 RBAC 権限設定 事前準備: 環境変数の設定 まず、これから使う変数をまとめて設定しておきます。コピペで使えるように、実際の値に置き換えてください: # プロジェクト設定 APP_NAME="myapp" # アプリケーション名(任意) RESOURCE_GROUP="rg-example" # 既存のリソースグループ名 LOCATION="japaneast" # リージョン # GitHub設定 GITHUB_ORG="your-org" # GitHub組織名またはユーザー名 GITHUB_REPO="your-repo" # GitHubリポジトリ名 # Managed Identity名 IDENTITY_NAME="${APP_NAME}-github-identity" 確認 : echo "Identity名: $IDENTITY_NAME" echo "リソースグループ: $RESOURCE_GROUP" ステップ 1: User Assigned Managed Identity 作成 GitHub からの認証を受け入れる Managed Identity を作成します。 az identity create \ --name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --location $LOCATION 実行結果(例) : { "clientId": "xxx-xxx-xxx-xxx-xxx", "id": "/subscriptions/.../resourceGroups/rg-example/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myapp-github-identity", "location": "japaneast", "name": "myapp-github-identity", "principalId": "yyy-yyy-yyy-yyy-yyy", "resourceGroup": "rg-example", "type": "Microsoft.ManagedIdentity/userAssignedIdentities" } 重要な情報を変数に保存 : # Client IDとPrincipal IDを取得して変数に保存 CLIENT_ID=$(az identity show \ --name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --query clientId -o tsv) PRINCIPAL_ID=$(az identity show \ --name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --query principalId -o tsv) echo "Client ID: $CLIENT_ID" echo "Principal ID: $PRINCIPAL_ID" この Client ID は後で GitHub Secrets に設定します。メモしておきましょう! 結果確認(Azure ポータル) : ステップ 2: Federated Identity Credential 設定 次に、GitHub リポジトリと Managed Identity を紐づける「フェデレーション認証情報」を設定します。 これが超重要なセクションです! subject パターンの選択 Federated Identity Credential には、「どの GitHub リポジトリ・ブランチ・環境から認証を許可するか」を指定する subject というフィールドがあります。 主要な subject パターン : パターン subject 形式 使用例 production 環境(推奨) repo:{org}/{repo}:environment:production 本番環境デプロイ main ブランチ repo:{org}/{repo}:ref:refs/heads/main 基本的な CI/CD タグ repo:{org}/{repo}:ref:refs/tags/v* リリースデプロイ Pull Request repo:{org}/{repo}:pull_request PR 環境デプロイ 私の場合、 production 環境パターン を使っています。理由は以下の通り: GitHub Actions の environment 機能と連携できる 環境ごとに異なる Secrets・Variables を管理できる 「本番環境へのデプロイ」という意図が明確 production 環境の Federated Credential 作成 az identity federated-credential create \ --name github-federated-production \ --identity-name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --issuer "https://token.actions.githubusercontent.com" \ --subject "repo:${GITHUB_ORG}/${GITHUB_REPO}:environment:production" \ --audiences "api://AzureADTokenExchange" 実行結果(例) : { "audiences": ["api://AzureADTokenExchange"], "issuer": "https://token.actions.githubusercontent.com", "name": "github-federated-production", "resourceGroup": "rg-example", "subject": "repo:your-org/your-repo:environment:production", "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials" } ポイント : --subject の repo:your-org/your-repo:environment:production 部分が、GitHub Actions ワークフローの environment: "production" と一致します --audiences は固定値( api://AzureADTokenExchange ) --issuer も固定値( https://token.actions.githubusercontent.com ) 重要: 環境名の大文字小文字 GitHub Actions は環境名を 自動的に小文字に変換 して Subject クレームを生成します。 GitHub 環境名: Production (大文字) 実際の Subject: repo:org/repo:environment:production ( 小文字 ) Subject 設定時は必ず小文字で指定してください 。大文字小文字が一致しないと AADSTS70021 エラーが発生します。 main ブランチの Federated Credential も追加する場合 az identity federated-credential create \ --name github-federated-main \ --identity-name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --issuer "https://token.actions.githubusercontent.com" \ --subject "repo:${GITHUB_ORG}/${GITHUB_REPO}:ref:refs/heads/main" \ --audiences "api://AzureADTokenExchange" これで、production 環境と main ブランチの両方からデプロイできるようになります! 設定確認 作成した Federated Credential を確認しましょう: az identity federated-credential list \ --identity-name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --query "[].{Name:name, Subject:subject}" \ --output table 出力例 : Name Subject ---------------------------- --------------------------------------------------------- github-federated-production repo:your-org/your-repo:environment:production github-federated-main repo:your-org/your-repo:ref:refs/heads/main 結果確認(Azure ポータル) : Federated Credential の制限事項 数量制限 : 1 つの Managed Identity あたり 最大 20 個 完全一致のみ : Subject はワイルドカード不可(完全一致のみサポート) 大規模プロジェクトで複数のリポジトリ・環境を管理する場合は、用途別に複数の Managed Identity を作成することを推奨します。 ステップ 3: RBAC 権限設定 Managed Identity を作成しただけでは、Azure リソースにアクセスできません。 RBAC(Role-Based Access Control)で権限を付与 する必要があります。 どのロールを付与するか? デプロイ対象の Azure サービスによって、必要なロールが異なります。 Azure サービス 推奨ロール スコープ Azure Functions Website Contributor リソースグループまたは個別リソース Azure Web Apps Website Contributor リソースグループまたは個別リソース Azure Static Web Apps Website Contributor リソースグループまたは個別リソース Azure Container Apps Contributor リソースグループまたは個別リソース 複数サービス Website Contributor リソースグループ(推奨) 私の場合 : リソースグループスコープで Website Contributor ロールを付与しています。 理由: Functions、Web Apps、Static Web Apps 全体をカバー 管理が簡単(個別リソースごとに設定する必要がない) 最小権限の原則に従いつつ、実用的 RBAC 権限の付与 リソースグループスコープで Website Contributor ロールを付与します: # リソースグループのIDを取得 RESOURCE_GROUP_ID=$(az group show \ --name $RESOURCE_GROUP \ --query id -o tsv) # Website Contributorロールを割り当て az role assignment create \ --assignee $PRINCIPAL_ID \ --role "Website Contributor" \ --scope $RESOURCE_GROUP_ID 実行結果(例) : { "principalId": "yyy-yyy-yyy-yyy-yyy", "principalType": "ServicePrincipal", "roleDefinitionName": "Website Contributor", "scope": "/subscriptions/.../resourceGroups/rg-example", "type": "Microsoft.Authorization/roleAssignments" } 重要: 権限の伝播時間 ロール割り当て直後は、権限が有効になるまで 最大 5 分間 かかる場合があります。 推奨アクション: 割り当て後、数分待ってからデプロイを実行 初回デプロイ時に権限エラーが発生した場合は、5 分後に再試行 設定確認 RBAC 権限が正しく付与されたか確認しましょう: az role assignment list \ --assignee $PRINCIPAL_ID \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalId, Role:roleDefinitionName, Scope:scope}" \ --output table 出力例 : Principal Role Scope ----------------------------------- ------------------- ---------------------------------------- yyy-yyy-yyy-yyy-yyy Website Contributor /subscriptions/.../resourceGroups/rg-example Website Contributor ロールが表示されていれば OK! 結果確認(Azure ポータル) : これで Azure 側の設定は完了です! たった 3 つのコマンド(Identity 作成、Federated Credential 設定、RBAC 付与)でセットアップ完了です。 GitHub Actions での認証設定 Azure CLI での設定が完了したら、次は GitHub Actions ワークフローを設定します。 GitHub Secrets の設定 まず、OIDC 認証に必要な 3 つのシークレットを GitHub Secrets に設定します。 必要な値を取得 すでに Azure CLI で取得した値を確認しましょう: # 1. Client ID(すでに取得済み) echo "AZURE_CLIENT_ID: $CLIENT_ID" # 2. Tenant ID TENANT_ID=$(az account show --query tenantId -o tsv) echo "AZURE_TENANT_ID: $TENANT_ID" # 3. Subscription ID SUBSCRIPTION_ID=$(az account show --query id -o tsv) echo "AZURE_SUBSCRIPTION_ID: $SUBSCRIPTION_ID" これらの値をメモしておきましょう! GitHub Secrets に手動で設定 GitHub リポジトリで以下のシークレットを設定します: GitHub リポジトリ → Settings → Secrets and variables → Actions New repository secret をクリック 以下の 3 つのシークレットを追加: Name Value AZURE_CLIENT_ID 上記で取得した Client ID AZURE_TENANT_ID 上記で取得した Tenant ID AZURE_SUBSCRIPTION_ID 上記で取得した Subscription ID ポイント : これらのシークレットは すべて識別子のみ で、シークレットキーは含まれていません。つまり、 万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みになっています。 基本的なワークフロー構成 OIDC 認証を使うには、以下の 3 つが 必須 です: permissions.id-token: write : GitHub Actions が OIDC トークンを発行できるようにする azure/login@v2 アクション : OIDC トークンを Azure アクセストークンに交換 GitHub Secrets 設定 : CLIENT_ID、TENANT_ID、SUBSCRIPTION_ID 基本テンプレート name: Deploy to Azure on: push: branches: - main workflow_dispatch: permissions: id-token: write # ← OIDC認証に必須! contents: read jobs: deploy: runs-on: ubuntu-latest environment: "production" # ← production環境を指定 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} # シークレットキーは不要! - name: Azure CLI - リソース一覧表示(動作確認) run: | az resource list --resource-group <RESOURCE_GROUP_NAME> --output table ポイント解説 : permissions.id-token: write : これがないと、OIDC トークンが発行されない GitHub Actions の重要な設定 environment: "production" : Federated Identity Credential の subject で指定した環境と一致させる Azure ポータルで設定した環境名と同じにする azure/login@v2 アクション : client-id 、 tenant-id 、 subscription-id の 3 つだけで OK シークレットキーは不要 トラブルシューティング OIDC 認証でよくあるエラーと解決方法をまとめます。 エラー 1: OIDC token exchange failed 症状 : Error: Login failed with Error: OIDC token exchange failed. Please check the following: - Federated credentials are correctly configured - The subject claim in the OIDC token matches the subject in the federated credential 原因 : Federated Identity Credential の subject パターンが、GitHub Actions の実行環境と一致していない。 解決方法 : 1. Azure ポータルで subject を確認 Managed Identity の フェデレーション資格情報 を開く 設定した資格情報の サブジェクト を確認 例 : subject: repo:your-org/your-repo:environment:production 2. GitHub Actions ワークフローの環境を確認 jobs: deploy: environment: "production" # ← ここが一致しているか確認 3. 一致しない場合の修正 パターン A : ワークフロー側を修正 # Azure側が environment:production なら jobs: deploy: environment: "production" # ← これに変更 パターン B : Azure 側を修正 Azure ポータルで、フェデレーション資格情報を再作成 ワークフローに合わせたエンティティ型を選択 エラー 2: permissions.id-token: write がない 症状 : Error: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL 原因 : GitHub Actions ワークフローに permissions.id-token: write が設定されていない。 解決方法 : ワークフローファイルに permissions セクションを追加します。 permissions: id-token: write # ← これを追加 contents: read jobs: deploy: # ... 注意 : permissions は job レベルではなく、 ワークフローのトップレベル に記述 エラー 3: GitHub Secrets が設定されていない 症状 : Error: Input required and not supplied: client-id 原因 : AZURE_CLIENT_ID などの GitHub Secrets が設定されていない。 解決方法 : 1. GitHub Secrets を確認 GitHub リポジトリの Settings → Secrets and variables → Actions で、以下のシークレットが設定されているか確認: AZURE_CLIENT_ID AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID 2. 設定されていない場合は追加 上記の「GitHub Secrets の設定」セクションを参照して、3 つのシークレットを追加してください。 デバッグ Tips GitHub Actions ログでの確認ポイント OIDC トークン発行成功 : Federated token successfully exchanged. Azure Login 成功 : Login successful. デプロイ成功 : Deployment successful. これらのメッセージが確認できれば、OIDC 認証は正常に動作しています! 番外編: Unknown Principal の削除方法 OIDC 認証の設定中に、 Managed Identity を作り直したり削除したりすると、ロール割り当てだけが残ってしまう ことがあります。 このような場合、Azure ポータルで権限を確認すると「Unknown Principal」として表示され、通常の方法では削除できません。 詳しい解決方法は別記事で解説しています : 「Cannot find user or service principal」エラー解決!Azure RBAC の正しい削除方法 この記事では以下を詳しく解説しています: Unknown Principal が発生する原因と Azure の仕様 プリンシパル ID では削除できない理由(エラーメッセージの解説) 割り当て ID(Assignment ID)を使った正しい削除方法 実務での推奨運用フローとセキュリティ対策 OIDC 認証の設定前にクリーンアップしておくと、後々のトラブルシューティングが楽になります ので、ぜひご参照ください! まとめ お疲れさまでした!長い記事でしたが、最後までお読みいただきありがとうございます。 3 つの認証方式の再確認 今回の記事で、GitHub Actions から Azure への 3 つの認証方式 を徹底比較しました: 発行プロファイル : 簡単で引き続きサポート(セキュリティリスクあり) Service Principal + シークレットキー : 従来推奨(管理コストが高い) OIDC 認証 : セキュリティベストプラクティス(パスワードレスで安全) OIDC 認証で得られる 3 つの大きなメリット シークレットキー不要 : パスワードレス認証でセキュリティリスク削減 自動ローテーション : GitHub の OIDC トークン(JWT)は 5 分間のみ有効、Azure のアクセストークンも自動管理(手動ローテーション不要) 監査ログ充実 : Azure AD での詳細な認証ログ(どのリポジトリ・ブランチからデプロイされたかが記録)で、コンプライアンス対応も万全 この記事で実装したこと 今回の記事では、以下の実装を解説しました: 3 つの認証方式の徹底比較 (発行プロファイル、Service Principal、OIDC) Azure ポータルでの画面操作 による OIDC 認証設定 User Assigned Managed Identity 作成 Federated Identity Credential 設定 (複数 subject パターン対応) RBAC 権限設定 のベストプラクティス GitHub Secrets の設定方法 (画面操作) GitHub Actions での汎用的な認証設定方法 トラブルシューティング (よくあるエラーと解決方法) 特に、「 3 つの認証方式の比較 」と「 Azure ポータルでの画面操作による設定 」のセクションは超重要です。 セキュリティ面での改善効果 私の場合、Azure Functions のデプロイで 発行プロファイル方式を使っていて 、以下のような課題がありました: XML ファイル内に 平文パスワードが含まれる セキュリティリスク GitHub Secrets に長期間有効な認証情報を保存 発行プロファイルの再生成・更新作業が必要 GitHub Actions で非推奨表示 が出てびっくり 発行プロファイル → OIDC 認証に直接移行 してから: シークレットキー管理の手間が 完全にゼロ セキュリティリスクが大幅に削減(平文パスワード不要) GitHub の OIDC トークン(JWT)は 5 分間のみ有効、Azure のアクセストークンも自動管理(手動ローテーション不要) 監査ログでの認証履歴が明確(どのリポジトリ・ブランチからデプロイされたかが記録される) Service Principal を経由せず、最初から最終形態の OIDC 認証に移行 したので、二度手間を避けられました。セキュリティレベルの大幅向上と、今後の管理コストの削減を実現できました。 次のステップ この記事で OIDC 認証の基本は理解できたと思います。次は以下にチャレンジしてみてください! 複数環境対応 : dev、staging、production の 3 環境で Federated Identity Credential を分ける 監視・アラート設定 : Application Insights でデプロイ成功・失敗を監視 自動テスト統合 : GitHub Actions で CI/CD パイプラインを拡張 Infrastructure as Code : Bicep IaC で設定を自動化(上級者向け) 特に、「複数環境対応」は本番運用では必須です。環境ごとに異なる Federated Identity Credential を設定することで、誤って本番環境にデプロイするリスクを防げます。 龍ちゃんの所感 GitHub Actions から Azure にデプロイする際、「発行プロファイルが簡単だから、それでいいや」って思っていた方、 私と同じように「Deprecated」って表示されてびっくりする前に 、ぜひ OIDC 認証にチャレンジしてみてください! 私も最初は「設定がややこしそう…Service Principal とか経由した方がいいのかな?」って思ったんですが、 いきなり OIDC 認証に移行して正解でした 。 この記事の手順通りに進めれば、Azure CLI と Azure ポータルの画面操作で設定できます。 一度設定してしまえば、その後の管理が超ラク! シークレットキーのローテーション作業から解放されて、セキュリティレベルも向上する。 Microsoft 公式が推奨している方式なので、セキュリティ面でも信頼性が高く、今後のデプロイのスタンダードになっていくはずです。 質問や「こんなエラーが出た!」などの困りごとがあれば、ぜひコメント欄で教えてください。一緒に解決していきましょう! それでは、セキュアで楽な Azure デプロイライフを! 参考リンク 公式ドキュメント Azure AD workload identity federation (Microsoft 公式) GitHub Actions – Azure Login Action Azure Managed Identity OpenID Connect (OIDC) with GitHub Actions Azure RBAC Documentation ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025年版 first appeared on SIOS Tech. Lab .
アバター
初めに ども!今月は Azure 関連の情報をメインにまとめている龍ちゃんです。今回は、実際に詰まった内容をもとに原因探求と対応方法についてまとめていきます。 Azure でリソースグループの権限確認したら、 Unknown Principal が大量に残ってた…なんて経験ありませんか?(これにぶち当たったあなたは勤勉です!) 実は私も先日、OIDC 認証の設定中にこの問題に遭遇して、めちゃくちゃハマりました。削除しようとしたら「Cannot find user or service principal in graph database」エラーが出て、「削除できんやん!」ってなったんですよね。 今回は、削除された Managed Identity のロール割り当てを削除する方法と、なぜこの問題が発生するのかを解説します。 この記事で分かること Unknown Principal が発生する原因と Azure の仕様 プリンシパル ID では削除できない理由(エラーメッセージの解説) 割り当て ID(Assignment ID)を使った正しい削除方法 実務での推奨運用フローとセキュリティ対策 それでは、さっそく見ていきましょう! この問題に遭遇するシナリオ Azure を使っていると、こんな状況に遭遇したことありませんか? テスト環境で OIDC 認証を試していたが、うまくいかずに Managed Identity を削除してやり直した プロジェクト終了時にリソースを削除したが、ロール割り当てだけが残ってしまった 別の担当者が作成した Managed Identity を削除したが、権限周りのクリーンアップを忘れていた リソースグループの権限確認をしたら、 Unknown Principal が大量に残っている… なぜこの問題が発生するのか? Azure では、以下のような仕組みになっています: Managed Identity や Service Principal を削除 Azure AD(Microsoft Entra ID)からプリンシパル自体が削除される ロール割り当ては自動削除されない ロール割り当ては Azure Resource Manager(ARM)のリソースとして別管理 プリンシパルを削除しても、 ロール割り当ては残り続ける つまり、 ロール割り当ては明示的に削除しない限り、永遠に残り続けます 。厄介な仕様ですね~ なぜ自動削除されないのか? これ、最初は「なんで自動削除してくれないの?」って思ったんですが、調べたところ以下のような理由があるようです。: 誤削除防止 : プリンシパルを誤って削除した場合、同じ ID で再作成すれば権限が復元できる 監査ログの保持 : 誰がどのリソースにアクセスできたかの履歴を残す 依存関係の問題 : ロール割り当てを自動削除すると、意図しない権限喪失が発生する可能性 ただし、この仕様のせいで ゴミが残りやすい のも事実です。 セキュリティ上の懸念 削除されたプリンシパルのロール割り当てが残っていると、以下のリスクがあります: 1. 同じプリンシパル ID で再作成されるリスク 注意すべきパターン ですが、実際のリスクは超限定的です。 過去に削除したプリンシパルの ID は、理論的には再利用可能 攻撃者が同じ ID でプリンシパルを作成した場合、古いロール割り当てが有効になる 結果的に、意図しない権限昇格が発生する可能性 ただし、超絶レアケース やはりこの辺は Azure 側で防止されています。: 30 日間のソフトデリート : Managed Identity や Service Principal は削除後 30 日間ソフトデリート状態になり、その間は同じ ID を再利用できません GUID の性質 : Azure が使用する GUID(Globally Unique Identifier)は、実質的に衝突しない設計になっています 30 日経過後も : ソフトデリート期間終了後に同じ ID が再割り当てされる確率は天文学的に低い 理論的なシナリオ : 1. テスト用 Managed Identity(ID: xxx-xxx-xxx)を作成 → Owner 権限付与 2. テスト終了後、Managed Identity を削除(でもロール割り当ては残る) 3. 30日間のソフトデリート期間が経過 4. 数ヶ月〜数年後、Azure が偶然同じIDを再割り当て(極めて低確率) 5. 新しいプリンシパルに、過去の Owner 権限が復活してしまう 現実的なリスク評価 : 可能性は極めて低いですが、 理論的にはゼロではありません 。より現実的なリスクは、以下のような運用上の問題です: セキュリティ監査で Unknown Principal の説明ができない 権限管理の可視性が失われる コンプライアンス要件に抵触する可能性 つまり、 セキュリティ侵害のリスク < 運用上の問題 という認識が正確ですね。 2. 権限管理の可視性が失われる 「誰がどのリソースにアクセスできるか」を確認するとき、Unknown Principal が大量にあると混乱する セキュリティ監査で「これ何ですか?」と質問され、説明に困る 本当に必要な権限と、ゴミの区別がつかなくなる 3. コンプライアンス違反のリスク 多くの企業では「最小権限の原則」をポリシーとして定めている 不要なロール割り当てが残っていると、このポリシーに違反する可能性 内部監査や外部監査で指摘される原因になる 運用上の問題点 セキュリティだけでなく、日々の運用でも問題になります。 権限トラブルシューティングが困難に 権限周りの問題をデバッグするとき、Unknown Principal があると混乱します: # 権限確認したら... az role assignment list --resource-group rg-prod --output table # 出力例 Principal Role Scope -------------------------------- ---------- ------- user@example.com Owner ... app-prod-deploy Contributor ... Unknown Owner ... ← これ何? Unknown Contributor ... ← これも何? app-prod-monitor Reader ... 「Unknown が何者か」を特定するには: Azure ポータルで Activity Log を確認 過去のデプロイ履歴を調査 前任者に確認(いない場合は詰む) 時間の無駄ですよね。 管理者からの確認依頼が来る 特に大規模な組織では、セキュリティチームから定期的に権限監査の依頼が来ます(ちなみにこれは妄想..): 件名: リソースグループ権限の確認依頼 以下のリソースグループに Unknown Principal のロール割り当てが 検出されました。これらは何に使用されていますか? - リソースグループ: rg-prod - Principal ID: xxx-xxx-xxx - ロール: Owner - 割り当て日: 2024-03-15 本日中にご回答ください。 答えられないんですよね。だって削除されたプリンシパルだから。 説明するのも面倒だし、「管理が甘い」って思われるのも嫌です。 問題の発見方法 権限確認コマンドで Unknown Principal もしくは null を検出 # リソースグループのロール割り当てを確認 RESOURCE_GROUP="rg-example" az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{ID:id, Principal:principalName, PrincipalID:principalId, Role:roleDefinitionName}" \ --output table 出力例 : ID Principal PrincipalID Role --------------------------------------------------------------------------------------------------------------- ----------- ----------------------------------- ----- /subscriptions/xxx/.../roleAssignments/aaa user@ex.com 111-111-111-111 Owner /subscriptions/xxx/.../roleAssignments/bbb Unknown 222-222-222-222 Owner /subscriptions/xxx/.../roleAssignments/ccc 333-333-333-333 Contributor Principal が Unknown になっているのが、削除されたプリンシパルのロール割り当てです。 削除方法:ハマったポイントと解決策 最初に試して失敗した方法 普通にプリンシパル ID を指定して削除しようとしました: # プリンシパルIDを指定して削除を試みる az role assignment delete \ --assignee 222-222-222-222 \ --resource-group $RESOURCE_GROUP エラーメッセージ : Cannot find user or service principal in graph database for 222-222-222-222. If the assignee is an appId, make sure the corresponding service principal is created with 'az ad sp create --id 222-222-222-222' 原因 : az role assignment delete --assignee は、Azure AD のグラフデータベースでプリンシパルを検索する プリンシパル自体が既に削除されているので、グラフデータベースに存在しない 結果的に「見つかりません」エラーになる 正解:割り当て ID(Assignment ID)を直接指定 ロール割り当て自体は Azure Resource Manager(ARM)のリソースとして残っているので、 リソース ID を直接指定すれば削除できます 。 手順 1: 削除対象の割り当て ID を確認 # 割り当てIDを含めて表示 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{ID:id, Principal:principalName, Role:roleDefinitionName}" \ --output table 出力例 : ID Principal Role --------------------------------------------------------------------------------------------------------------- ----------- ----------- /subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/bbb Unknown Owner 手順 2: 割り当て ID を指定して削除 # 割り当てID全体をコピーして実行 az role assignment delete --ids "/subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/bbb" 成功! 手順 3: 削除確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName}" \ --output table Unknown Principal が消えていれば OK です。 実務での推奨運用フロー OIDC 認証を設定する前に、以下のクリーンアップフローを実施しましょう: 1. 権限確認 RESOURCE_GROUP="rg-example" # すべてのロール割り当てを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName, AssignedDate:createdOn}" \ --output table 2. Unknown Principal の削除 各割り当て ID を個別に削除することで、より安全に削除できます: # Unknown Principal の割り当てIDを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[?principalName==null].{ID:id, Role:roleDefinitionName}" \ --output table # 確認した割り当てIDを1つずつ削除 # 例:1つ目の Unknown Principal を削除 az role assignment delete --ids "/subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/bbb" # 例:2つ目の Unknown Principal を削除 az role assignment delete --ids "/subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/ccc" # 削除後、都度確認しながら進める az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName}" \ --output table ポイント : 各削除の結果を確認しながら慎重に進められる 誤削除のリスクを最小限に抑えられる どの権限を削除したか記録しやすい 3. 削除確認 # Unknown Principal が消えたことを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName}" \ --output table 4. OIDC 認証設定開始 クリーンな状態になったので、安心して OIDC 認証の設定を進められます。 まとめ 学んだこと Managed Identity を削除しても、ロール割り当ては自動削除されない Azure の仕様で、意図的にそうなっている セキュリティと監査ログ保持のためだが、ゴミが残りやすい Unknown Principal はセキュリティリスク 同じ ID で再作成されると、意図しない権限昇格の可能性 権限管理の可視性が失われる コンプライアンス違反のリスク 削除にはコツがある プリンシパル ID ではなく、割り当て ID(Assignment ID)を直接指定 定期的なクリーンアップが重要 OIDC 認証設定前だけでなく、定期的に確認 特にテスト環境では頻繁に発生する 実務での教訓 「リソースを削除したら、関連する権限も必ず削除する」 これを習慣化すると、後々のトラブルが減ります。特に: Managed Identity や Service Principal を削除する前に、ロール割り当てを確認 削除後、Unknown Principal が残っていないか確認 チームメンバーにもこの手順を共有 セキュリティは「意識」と「習慣」です。この記事が、誰かの役に立てば嬉しいです! 参考資料 この記事は以下の公式ドキュメントと実際の検証結果に基づいています: Microsoft 公式ドキュメント Azure ロールベースのアクセス制御 (Azure RBAC) のトラブルシューティング ロール割り当ての管理とトラブルシューティング手順 削除されたエンタープライズ アプリケーション、サービス プリンシパル、Managed ID を復元または削除する 30 日間のソフトデリート期間についての公式説明 Azure RBAC の制限 サブスクリプションあたり 4,000 のロール割り当て制限 Azure CLI – az role assignment Azure CLI コマンドリファレンス 関連記事 DevContainer 実践入門:Azure CLI+GitHub CLI 環境をチーム全体で統一 Azure CLI を含む開発環境のセットアップ方法 検証環境 Azure CLI : バージョン 2.50.0 以上 検証日 : 2025 年 11 月 検証環境 : Azure サブスクリプション おわりに Unknown Principal の削除、無事にできましたか? これは実際に Azure CLI で操作を行っている際に衝突した問題です。いや~ Tips としては超限定的な問題ですね。 この記事で紹介した方法を使えば、Unknown Principal を一掃できます。特に OIDC 認証の設定前にクリーンアップしておくと、後々のトラブルシューティングが楽になりますよ。 ポイントをおさらい : 削除は割り当て ID(Assignment ID)を直接指定 定期的なクリーンアップを習慣化 リソース削除前にロール割り当ても確認 セキュリティは「意識」と「習慣」です。今回の問題は「怠慢」ですね wwwww。この記事が、誰かの Azure ライフを少しでも楽にできたら嬉しいです! もし役に立ったら、ぜひ X(旧 Twitter)でシェアしてください。また、「こんな方法もあるよ」「ここが分かりにくかった」などのフィードバックがあれば、コメント欄や X で教えてもらえると嬉しいです。 それでは、良い Azure ライフを! 関連記事 この記事と合わせて読むと、さらに理解が深まります: DevContainer 実践入門:Azure CLI+GitHub CLI 環境をチーム全体で統一 Azure CLI を含む開発環境のセットアップ方法を詳しく解説 Azure OIDC 認証で安全な GitHub Actions デプロイを実現する方法 (執筆中) OIDC 認証の設定方法と、この記事で紹介したクリーンアップを実践する方法 こちらのトラブルシューティングの内容 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 「Cannot find user or service principal」エラー解決!Azure RBACの正しい削除方法 first appeared on SIOS Tech. Lab .
アバター
はじめに どうも、龍ちゃんです! 前回の「 Azure Functions×DevContainer 環境構築| Node.js 編 」では、Node.js 22 + TypeScript を使った DevContainer 環境構築を解説しました。今回は、 Python 3.11 を使った Azure Functions の開発環境を構築します。 サンプルリポジトリ : 本記事で解説する環境をすぐに試せるサンプルコードを公開しています。 GitHub : azure-functions-python-devcontainer クローンして DevContainer で開くだけで、すぐに動作確認できます! なぜ Python で Azure Functions なのか? 単純な REST API の実装であれば、Node.js と Python のどちらでも問題ありません。 しかし、以下のような 特定のライブラリや数値計算が必要な要件 がある場合は、Python を選択すべきです: データ処理 : pandas、NumPy を使った大量データの前処理・集計 機械学習 : scikit-learn、TensorFlow モデルの推論 数値計算 : SciPy、SymPy を使った科学技術計算 スクレイピング : BeautifulSoup、Scrapy を使った Web スクレイピング Python を選ぶ判断基準 : pandas / NumPy / scikit-learn などのライブラリが必須 既存の Python コードやモデルを Azure Functions で動かしたい データサイエンスチームが Python を使っている Node.js を選ぶ判断基準 : 単純な REST API やリアルタイム処理 TypeScript で型安全な開発がしたい npm エコシステムのライブラリを活用したい 項目 Node.js 版 Python 版 適した用途 REST API、リアルタイム処理 データ処理、機械学習、数値計算 ライブラリ npm エコシステム pandas、NumPy、scikit-learn 非同期処理 async/await が得意 asyncio があるが Node.js ほど主流ではない 学習コスト TypeScript の型定義 Python の動的型付け 今回も DevContainer を使うことで、チーム開発での再現性を確保します。 本記事の前提 Azure Functions×DevContainer 環境構築| Node.js 編 を読んでいることを推奨 Node.js 編と共通の前提条件(Docker Desktop、VSCode、Dev Containers 拡張機能)が必要です Docker Desktop がインストール済みであること インストール方法: Docker Desktop 公式サイト Visual Studio Code がインストール済みであること インストール方法: VSCode 公式サイト Dev Containers 拡張機能 がインストール済みであること VSCode の拡張機能パネルから「Dev Containers」をインストール Python の基本文法 を理解していること それでは、Python 版の DevContainer 環境構築を始めましょう! Azure Functions とは?(簡潔版) 詳細は Node.js 編 を参照してください。ここでは、Python 版で重要なポイントのみ記載します。 Python で使える主要なトリガー トリガー 用途 Python での利用例 HTTP Trigger REST API データ分析 API、機械学習推論 API Timer Trigger 定期実行 毎日深夜にデータ集計・レポート作成 Blob Trigger ファイル処理 CSV アップロード時の pandas 処理 Queue Trigger 非同期処理 大量データの分散処理 必要なツール一覧 Python 版でも、Node.js 版と同じツール構成です: ツール名 バージョン インストール先 Docker Desktop 最新 ホスト OS Visual Studio Code 最新 ホスト OS Dev Containers 拡張機能 最新 VSCode Python 3.11.x DevContainer 内で自動 Azure Functions Core Tools v4.x DevContainer 内で自動 Azurite 最新 DevContainer 内で自動 重要 : Python 版の Azure Functions でも、 Azure Functions Core Tools は Node.js 製 !DevContainer 内に Node.js もインストールされます ホスト OS には Python をインストール不要(すべて DevContainer 内で完結) Python DevContainer の構築 これから構築する環境の全体像を把握しましょう。 構築後のディレクトリ構成 azure-functions-python-devcontainer/ # プロジェクトルート ├── .devcontainer/ # DevContainer 設定 │ ├── Dockerfile # コンテナイメージ定義(方式1の場合) │ └── devcontainer.json # DevContainer 設定ファイル │ └── MyPythonFunctionApp/ # Azure Functions プロジェクト ├── .funcignore # デプロイ除外ファイル ├── .gitignore # Git 除外設定 ├── .venv/ # Python 仮想環境(作成後) │ ├── bin/ # 実行ファイル │ ├── lib/ # インストール済みパッケージ │ └── pyvenv.cfg # 仮想環境設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── requirements.txt # Python 依存関係 │ └── function_app.py # メイン関数ファイル # すべての関数を定義 Node.js 版との違い : package.json の代わりに requirements.txt src/functions/*.ts の代わりに function_app.py 1 ファイル .venv/ で Python 仮想環境を管理 ポイント : .devcontainer/ で開発環境を定義 MyPythonFunctionApp/ が実際の Functions プロジェクト Python の関数はすべて function_app.py に記述(Node.js版とは違い、1ファイルにまとめる) 構築方法 Python 版の DevContainer 構築には、主に 2 つの方法があります: Dockerfile 方式 – カスタム Dockerfile で詳細に制御( 推奨 ) DevContainer Features 方式 – 既存イメージに Node.js を追加 Python では Dockerfile 方式を推奨する理由 : Python は バージョン依存が強い ため、明示的なバージョン指定が重要です Features 方式だと、VSCode の Python Interpreter 設定で 意図しない Python 環境が選ばれる ことがあるんですよね Dockerfile 方式なら、Python 3.11 を確実に指定できる! 再現性が高く、チーム開発で環境差異が発生しにくい 注意 : 参考記事(Claude Code×DevContainer)では Features 方式を推奨していますが、Python の場合は Dockerfile 方式の方が確実です。 方法 1: Dockerfile 方式(推奨) Python ベースイメージから、Node.js を手動でインストールする方式です。 この方式の利点 : Python 3.11 を確実に指定できる 意図しない Python 環境が選ばれるリスクがない ビルド時に全依存関係がインストールされるため、起動が高速 ステップ 1: プロジェクトディレクトリの作成 mkdir azure-functions-python-devcontainer cd azure-functions-python-devcontainer mkdir .devcontainer ステップ 2: Dockerfile の作成 .devcontainer/Dockerfile : # .devcontainer/Dockerfile FROM mcr.microsoft.com/devcontainers/python:3.11 # Node.js のインストール(Azure Functions Core Tools に必要) RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ && apt-get install -y nodejs # 作業ディレクトリ WORKDIR /workspace # 実行ユーザー USER vscode # npm のグローバルインストール先を vscode ユーザーのホームディレクトリに設定 RUN mkdir -p /home/vscode/.npm-global \ && npm config set prefix '/home/vscode/.npm-global' # PATH に npm グローバルディレクトリを追加 ENV PATH="/home/vscode/.npm-global/bin:${PATH}" # Azure Functions Core Tools と Azurite のインストール RUN npm install -g \ npm@11.5.2 \ azure-functions-core-tools@4 \ azurite # バージョン確認用コマンド(デバッグ用) RUN echo "=== Installed Versions ===" \ && python --version \ && pip --version \ && node --version \ && npm --version \ && func --version \ && echo "=========================" # デフォルトコマンド CMD ["sleep", "infinity"] ポイント : mcr.microsoft.com/devcontainers/python:3.11 は Microsoft 公式の Python 3.11 イメージ Node.js をインストール(Azure Functions Core Tools が Node.js 製のため必須!) vscode ユーザーで実行(Python イメージのデフォルトユーザー) ステップ 3: devcontainer.json の作成 .devcontainer/devcontainer.json : { "name": "Azure Functions Python DevContainer", "build": { "dockerfile": "./Dockerfile" }, "workspaceFolder": "/workspace", "remoteUser": "vscode", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "ms-python.python", "ms-python.vscode-pylance", "ms-python.black-formatter" ] } } } ポイント : ms-python.python : Python 拡張機能 ms-python.vscode-pylance : 型チェック・インテリセンス ms-python.black-formatter : Python コードフォーマッタ 方法 2: DevContainer Features 方式(代替案) DevContainer Features を使うと、Node.js のインストールが 1 行で完了します。 この方式の注意点 : シンプルだが、Python Interpreter が意図しない環境を指す場合がある DevContainer 起動後に必ず Interpreter 設定を確認する必要がある Features 方式でも問題ない場合もあるが、Dockerfile 方式の方が確実 .devcontainer/devcontainer.json : { "name": "Azure Functions Python DevContainer", "image": "mcr.microsoft.com/devcontainers/python:3.11", "workspaceFolder": "/workspace", "remoteUser": "vscode", "features": { "ghcr.io/devcontainers/features/node:1": { "version": "lts" } }, "postCreateCommand": "npm install -g npm@11.5.2 azure-functions-core-tools@4 azurite", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "ms-python.python", "ms-python.vscode-pylance", "ms-python.black-formatter" ] } } } ポイント : features セクションで Node.js を自動インストール Dockerfile が不要でシンプル 参考記事( Claude Code×DevContainer 環境構築ガイド )でも推奨されている方式 どちらの方式を選ぶか : Dockerfile 方式を推奨 – Python バージョンを確実に制御できる Features 方式は、Python Interpreter 設定に注意すれば使える DevContainer の起動 VSCode で azure-functions-python-devcontainer フォルダを開く 左下の「 >< 」アイコンをクリック 「 Reopen in Container 」を選択 Docker イメージのビルドと起動が開始されます 成功すると : VSCode の左下に「 Dev Container: Azure Functions Python DevContainer 」と表示 重要: Python Interpreter の設定確認 DevContainer 起動後、 必ず Python Interpreter が正しく設定されているか確認してください。 VSCode のコマンドパレットを開く( Ctrl+Shift+P / Cmd+Shift+P ) 「 Python: Select Interpreter 」を検索・選択 /usr/local/bin/python または /usr/bin/python3.11 が選択されていることを確認 よくある問題 : 意図しない Python 環境(例: /usr/local/python/current/bin/python )が選ばれている この場合、手動で /usr/local/bin/python を選択し直す Python バージョンの確認 : python --version # → Python 3.11.x と表示されること Python はバージョン依存が強い ため、この確認は必須です!Python 3.11 以外が選ばれていると、ライブラリの互換性問題が発生してハマります。 Functions プロジェクトの作成 DevContainer 内で Azure Functions プロジェクトを作成します。 プロジェクト初期化 DevContainer 内のターミナルで実行: func init MyPythonFunctionApp --python cd MyPythonFunctionApp 生成されるファイル : MyPythonFunctionApp/ ├── .funcignore # Functions デプロイ時の除外ファイル ├── .gitignore # Git 除外設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── requirements.txt # Python 依存関係 └── function_app.py # メイン関数ファイル(初期状態は空) Node.js 版との違い : package.json の代わりに requirements.txt src/ ディレクトリの代わりに function_app.py local.settings.json の設定 local.settings.json を編集して、Azurite 接続設定を追加: { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "python" } } 重要 : FUNCTIONS_WORKER_RUNTIME: "python" を設定 AzureWebJobsStorage は Node.js 版と同じ Python 仮想環境の作成 Python 版では、 仮想環境 を作成することを推奨します。 # 仮想環境の作成 python -m venv .venv # 仮想環境の有効化 # Linux/macOS source .venv/bin/activate # Windows (PowerShell) .venv\Scripts\Activate.ps1 仮想環境を使う理由 : プロジェクトごとに依存関係を分離できる 他のプロジェクトとのライブラリバージョン競合を回避できる コラム: 仮想環境内に仮想環境を作る ? DevContainer(コンテナ仮想環境)の中で .venv (Python 仮想環境)を作るのは、一見冗長に見えるかもしれません。しかし、この二重構造には実用的なメリットがあります: 1. グローバル環境の混入を防ぐ .venv を使わないと、開発者個人の Python 環境にインストールされているライブラリが紛れ込む可能性があります。例えば、開発者 A は pandas をグローバルにインストールしているが、開発者 B はインストールしていない場合、「自分の環境では動くのに…」という問題が発生します。 2. requirements.txt の汚染を防ぐ .venv を有効化し忘れてパッケージをインストールすると、全然関係ないライブラリまで requirements.txt に追記される…これ、 Python 開発あるあるですよね 。 # ❌ .venv を有効化し忘れてインストール pip install pandas pip freeze > requirements.txt # → グローバル環境の不要なライブラリまで記録される! # ✅ .venv を有効化してからインストール source .venv/bin/activate pip install pandas pip freeze > requirements.txt # → プロジェクト専用のライブラリのみ記録される 3. 開発環境の使いまわしが可能 同じ DevContainer を複数のプロジェクトで「ロンダリング」(使いまわす)しても、 .venv があることで各プロジェクトの依存関係が混ざりません。プロジェクト A 用の .venv とプロジェクト B 用の .venv を独立して管理できます。 個人的には、この二重仮想環境構成は好みですね。予期しない依存関係の混入を確実に防げます! 依存関係のインストール pip install -r requirements.txt 注意: requirements.txt の管理 pip install でライブラリを追加しても、 requirements.txt には自動的に追記されません 。ここ、Node.js の package.json と違って手動管理が必要なんですよね: # ライブラリをインストール pip install pandas # requirements.txt を更新 pip freeze > requirements.txt 発展的な選択肢: uv の活用 より効率的な依存関係管理には、 uv (高速 Python パッケージマネージャー)の採用も検討できます。詳細は以下の記事を参照してください: DevContainer と uv で構築する爆速 Python 開発環境| VS Code セットアップ手順 ただし、Azure Functions では uv のロジックに直接対応していないため、本番デプロイ時には uv → pip → requirements.txt への変換が必要です(コンテナだったら関係なかったかも…)。本記事では、標準的な pip + requirements.txt の構成を紹介します。 HTTP Trigger の作成と動作確認 HTTP Trigger 関数の作成 func new --name HttpExample --template "HTTP trigger" --authlevel "anonymous" 生成される内容 : function_app.py に HTTP Trigger 関数が追加されます 実装内容の確認 function_app.py : import azure.functions as func import datetime import json import logging app = func.FunctionApp() @app.route(route="HttpExample", auth_level=func.AuthLevel.ANONYMOUS) def HttpExample(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') name = req.params.get('name') if not name: try: req_body = req.get_json() except ValueError: pass else: name = req_body.get('name') if name: return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.") else: return func.HttpResponse( "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.", status_code=200 ) ポイント : @app.route() デコレータでエンドポイントを定義 req.params.get('name') でクエリパラメータを取得 req.get_json() でリクエストボディを JSON として取得 ローカル実行 func start 実行結果 : 動作確認 別のターミナルで curl コマンドを実行: curl "http://localhost:7071/api/HttpExample?name=Python" 期待される結果 : Hello, Python! ブラウザでの確認 : http://localhost:7071/api/HttpExample にアクセス Azurite と Timer Trigger Timer Trigger を使うには、 Azurite が必要です。詳細は Node.js 編 を参照してください。 Azurite の起動 DevContainer 内で、 別のターミナル を開いて Azurite を起動します: azurite --silent # 設定やログを保存する先を指定 azurite --location .azurite --debug .azurite/debug.log 期待される結果 : Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002 デフォルトポート : Blob Service: 10000 Queue Service: 10001 Table Service: 10002 Timer Trigger 関数の作成 func new --name TimerExample --template "Timer trigger" 生成される内容 : function_app.py に Timer Trigger 関数が追加されます 実装内容の確認 function_app.py に追加される内容: import azure.functions as func import logging import datetime app = func.FunctionApp() # (既存の HTTP Trigger はそのまま) @app.timer_trigger(schedule="0 */5 * * * *", arg_name="myTimer", run_on_startup=False, use_monitor=False) def TimerExample(myTimer: func.TimerRequest) -> None: if myTimer.past_due: logging.info('The timer is past due!') logging.info('Python timer trigger function executed.') ポイント : @app.timer_trigger() デコレータで CRON 式を定義 myTimer.past_due で遅延実行を検知 CRON 式は UTC 時刻 で動作(重要!) ローカル実行 Azurite が起動している状態で、Functions を起動: func start 実行結果 : 5 分ごとにログが表示されます。 トラブルシューティング(Python 固有) Python 版で発生しやすいエラーと対処法です。 1. ModuleNotFoundError: No module named ‘azure’ 症状 : func start で ModuleNotFoundError: No module named 'azure' エラー 対処法 : 仮想環境が有効になっているか確認 pip install -r requirements.txt を実行 仮想環境の有効化: # Linux/macOS source .venv/bin/activate # Windows .venv\Scripts\Activate.ps1 2. Failed to start a new language worker for runtime: python 症状 : Failed to start a new language worker for runtime: python 対処法 : Python バージョンを確認( python --version ) Azure Functions Core Tools が Python ワーカーをサポートしているか確認 仮想環境を再作成: rm -rf .venv python -m venv .venv source .venv/bin/activate pip install -r requirements.txt 3. Azurite 接続エラー 症状 : No connection could be made because the target machine actively refused it 対処法 : Node.js 編のトラブルシューティングを参照 Azurite が起動しているか確認 local.settings.json の設定を確認 4. VSCode で Python インタープリタが見つからない 症状 : VSCode が Python を認識しない 対処法 : コマンドパレット( Ctrl+Shift+P )→「Python: Select Interpreter」 .venv/bin/python を選択 開発の推奨フロー Python 版の開発フローです。 標準的な開発手順 ターミナル 1: Azurite 起動 azurite --silent ターミナル 2: 仮想環境の有効化と Functions 起動 cd MyPythonFunctionApp # 仮想環境の有効化 source .venv/bin/activate # Linux/macOS # または .venv\Scripts\Activate.ps1 # Windows # Functions 起動 func start 開発作業 function_app.py を編集 保存すると自動的にリロード 動作確認 HTTP Trigger: curl やブラウザでアクセス Timer Trigger: コンソールログで確認 Node.js 版との比較 項目 Node.js 版 Python 版 プロジェクト作成 func init --typescript func init --python 依存関係管理 npm / package.json pip / requirements.txt メインファイル src/functions/*.ts function_app.py 仮想環境 不要(オプション) 推奨(.venv) 実行コマンド npm start func start リロード watch mode(自動) ファイル変更検知(自動) 推奨 VSCode 拡張機能 DevContainer 内で自動的にインストールされる拡張機能: Azure Functions ( ms-azuretools.vscode-azurefunctions ) Python ( ms-python.python ) Pylance ( ms-python.vscode-pylance ) Black Formatter ( ms-python.black-formatter ) まとめと次回予告 本記事で学んだこと DevContainer を使った Python 版 Azure Functions 環境構築 Python 3.11 の DevContainer 構築 Dockerfile 方式を推奨 (Python はバージョン依存が強いため) Python Interpreter 設定の重要性 Python 仮想環境の作成と管理 Python での Azure Functions 開発 HTTP Trigger の作成と動作確認 Timer Trigger と Azurite の関係 function_app.py での関数定義 Python 固有のトラブルシューティング 仮想環境のエラー対処 Python Worker プロセスエラー Node.js 版と Python 版の使い分け シナリオ 推奨言語 理由 単純な REST API どちらでも OK 要件による データ分析・集計 Python pandas、NumPy が必須 機械学習推論 Python scikit-learn、TensorFlow 数値計算 Python SciPy、SymPy スクレイピング Python BeautifulSoup リアルタイム処理 Node.js async/await が得意 バッチ処理 どちらでも可 要件とチームのスキルセット次第 重要 : 単純な REST API であれば、Node.js と Python のどちらでも問題ありません。 特定のライブラリ(pandas、NumPy、scikit-learn など)が必要な場合のみ Python を選択 してください! 次回の記事予告 次回は、 Azure Functions 入門| HTTP Trigger と Timer Trigger の基礎と実践パターン を予定しています: HTTP Trigger と Timer Trigger の詳細な使い方(Node.js 版) 実践パターン : Timer Trigger を HTTP Trigger でデバッグする方法 タイムゾーン(UTC/JST)の扱い方 DRY 原則に基づいた共通ロジックの設計 サンプルリポジトリ 本記事で解説した環境を、すぐに試せるサンプルコードを公開しています: GitHub : azure-functions-python-devcontainer Python 3.11 HTTP Trigger + Timer Trigger 実装済み DevContainer 設定ファイル完備 仮想環境( .venv )セットアップ手順付き クローンして VSCode で開くだけで動作します 関連記事 Azure Functions×DevContainer 環境構築| Node.js 編 Node.js 22 + TypeScript での環境構築 Dockerfile 方式と PostCreateCommand 方式 Docker Desktop、VSCode のインストール手順 サンプルリポジトリ : azure-functions-nodejs-devcontainer Claude Code×DevContainer 環境構築ガイド – Node.js・Python 対応 DevContainer の基本的な使い方 Features 方式の詳細解説 DevContainer と uv で構築する爆速 Python 開発環境| VS Code セットアップ手順 uv による高速な依存関係管理 pip の代替としての uv の活用方法 ※ Azure Functions での利用には requirements.txt への変換が必要 DevContainer を使った Python での Azure Functions 開発、ぜひ試してみてください! データ分析や機械学習との組み合わせで、強力な自動化システムが構築できます。 次回もお楽しみに〜! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Functions×DevContainer環境構築|Python 3.11編 first appeared on SIOS Tech. Lab .
アバター
はじめに どうも、龍ちゃんです! 今月は Azure Functions を使ったサーバーレス開発に取り組んでいるのですが、ローカル開発環境の構築で結構ハマりポイントがあったので、その知見を共有したいと思います。 サンプルリポジトリ : 本記事で解説する環境をすぐに試せるサンプルコードを公開しています。 GitHub : azure-functions-nodejs-devcontainer クローンして DevContainer で開くだけで、すぐに動作確認できます! 特に、 DevContainer を使った環境構築 は、チーム開発で環境差異をなくすために非常に有効なアプローチです。前回の「 Claude Code×DevContainer 環境構築ガイド 」でも DevContainer の便利さを紹介しましたが、今回は Azure Functions に特化した DevContainer 環境構築 を解説します。 なぜ DevContainer で Azure Functions なのか? 私が DevContainer を推奨する理由はいくつかあります: 1. 再現性のある環境構築 チームメンバー全員が同じ環境で開発できます。「自分の環境では動くのに…」という問題、ありますよね。これが完全になくなります! 2. ホスト OS を汚さない Node.js のバージョン、npm のグローバルパッケージ、Azure Functions Core Tools など、すべてコンテナ内で完結 3. プロジェクトごとに異なるバージョンを使い分け プロジェクト A は Node.js 18、プロジェクト B は Node.js 22 といった使い分けが簡単 今回は、 Node.js 22 + TypeScript で Azure Functions の開発環境を構築します。次回の「Python 編」では Python 3.11 を使った環境構築も紹介しますので、お楽しみに! Azure Functions とは? Azure Functions は、 サーバーレスコンピューティング のサービスです。サーバーの管理をせずに、コードだけを書いて実行できます。 主要なトリガー Azure Functions では、さまざまな「トリガー」でコードを実行できます: トリガー 用途 例 HTTP Trigger REST API、Webhook API エンドポイント作成 Timer Trigger 定期実行 毎日深夜にバッチ処理 Blob Trigger ファイルアップロード 画像アップロード時に圧縮 Queue Trigger メッセージキュー 非同期タスク処理 今回は、 HTTP Trigger と Timer Trigger を使ってローカル開発環境を構築します。次回の記事では、この 2 つを組み合わせた 実践パターン (Timer Trigger を HTTP Trigger でデバッグする方法)を紹介する予定です。 なぜローカル開発環境が必要なのか Azure にデプロイしてからデバッグするのって、めちゃくちゃ時間かかりますよね。ローカル環境があれば: 即座にデバッグ – コードを変更したら即座に動作確認! ログ確認が簡単 – コンソールに直接ログが表示される コスト削減 – ローカルでのテストは Azure の課金対象外 前提条件 本記事では、以下がインストール済みであることを前提とします: 必須 Docker Desktop – DevContainer を使うために必須 インストール方法: Docker Desktop 公式サイト 動作確認: docker --version で Docker version 24.x.x 以上が表示されること Visual Studio Code – エディタ インストール方法: VSCode 公式サイト 本記事でインストールするもの Dev Containers 拡張機能 – VSCode でインストール Node.js 22、Azure Functions Core Tools、Azurite – DevContainer 内で自動インストール 必要なツール一覧 DevContainer を使った Azure Functions 開発に必要なツールは以下の通りです: ツール名 バージョン 役割 インストール先 Docker Desktop 最新 コンテナランタイム ホスト OS(前提) Visual Studio Code 最新 エディタ ホスト OS(前提) Dev Containers 拡張機能 最新 DevContainer サポート VSCode Node.js 22.x LTS JavaScript ランタイム DevContainer 内で自動 Azure Functions Core Tools v4.x ローカル実行・デバッグ DevContainer 内で自動 Azurite 最新 Azure Storage エミュレータ DevContainer 内で自動 ポイント : ホスト OS には Docker Desktop と VSCode が既にインストール済み Node.js、Core Tools、Azurite は DevContainer 内で自動的にインストール これにより、ホスト OS を汚さずに開発環境を構築可能 Dev Containers 拡張機能のインストール VSCode に Dev Containers 拡張機能をインストールします。 VSCode を起動 拡張機能パネルを開く( Ctrl+Shift+X / Cmd+Shift+X ) 「 Dev Containers 」で検索 「 Dev Containers 」(ID: ms-vscode-remote.remote-containers )をインストール 動作確認 : VSCode 左下に「 >< 」アイコンが表示されていることを確認 このアイコンをクリックすると、DevContainer 関連のコマンドが表示される Node.js DevContainer の構築 これから構築する環境の全体像を把握しましょう。 構築後のディレクトリ構成 azure-functions-nodejs-devcontainer/ # プロジェクトルート ├── .devcontainer/ # DevContainer 設定 │ ├── Dockerfile # コンテナイメージ定義 │ └── devcontainer.json # DevContainer 設定ファイル │ └── MyFunctionApp/ # Azure Functions プロジェクト ├── .funcignore # デプロイ除外ファイル ├── .gitignore # Git 除外設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── package.json # npm 依存関係 ├── tsconfig.json # TypeScript 設定 │ └── src/ # ソースコード └── functions/ # 関数ファイル ├── HttpExample.ts # HTTP Trigger 関数 └── TimerExample.ts # Timer Trigger 関数 ポイント : .devcontainer/ で開発環境を定義 MyFunctionApp/ が実際の Functions プロジェクト TypeScript ファイルは src/functions/ 配下に配置 構築方法 DevContainer を構築する方法は主に 2 つあります: Dockerfile 方式 – カスタム Dockerfile で詳細に制御(推奨) PostCreateCommand 方式 – 既存イメージにコマンドを追加 方法 1: Dockerfile 方式(推奨) この方式は、再現性が高く、ビルド時間も短いため推奨します。 ステップ 1: プロジェクトディレクトリの作成 mkdir azure-functions-nodejs-devcontainer cd azure-functions-nodejs-devcontainer mkdir .devcontainer ステップ 2: Dockerfile の作成 .devcontainer/Dockerfile を作成: # .devcontainer/Dockerfile FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm # 作業ディレクトリ WORKDIR /workspace # 実行ユーザー USER node # Azure Functions Core Tools と Azurite のインストール RUN npm install -g \ npm@11.5.2 \ azure-functions-core-tools@4 \ azurite # バージョン確認用コマンド(デバッグ用) RUN echo "=== Installed Versions ===" \ && node --version \ && npm --version \ && func --version \ && echo "=========================" # デフォルトコマンド(devContainer では sleep infinity で上書きされる) CMD ["sleep", "infinity"] ポイント : mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm は Microsoft 公式の Node.js 22 + TypeScript イメージ azure-functions-core-tools@4 で Azure Functions v4 ランタイムをインストール azurite は Timer Trigger のローカル実行に必須! ステップ 3: devcontainer.json の作成 .devcontainer/devcontainer.json を作成: { "name": "Azure Functions Node.js DevContainer", "build": { "dockerfile": "./Dockerfile" }, "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "remoteUser": "node", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ] } } } ポイント : forwardPorts : Azure Functions (7071) と Azurite (10000-10002) のポート転送を設定 extensions : Azure Functions 拡張機能、ESLint、Prettier を自動インストール ステップ 4: DevContainer の起動 VSCode で azure-functions-nodejs-devcontainer フォルダを開く 左下の「 >< 」アイコンをクリック 「 Reopen in Container 」を選択 Docker イメージのビルドと起動が開始されます(初回は 5-10 分程度) 成功すると : VSCode の左下に「 Dev Container: Azure Functions Node.js DevContainer 」と表示 ターミナルを開くと、コンテナ内のシェルが起動 方法 2: PostCreateCommand 方式 既存イメージを使い、起動後にコマンドでツールをインストールする方式です。 .devcontainer/devcontainer.json : { "name": "Azure Functions Node.js DevContainer", "image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm", "workspaceFolder": "/workspace", "remoteUser": "node", "postCreateCommand": "npm install -g npm@11.5.2 azure-functions-core-tools@4 azurite", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ] } } } メリット : 設定ファイル 1 つで完結 シンプルでわかりやすい デメリット : DevContainer 起動のたびにインストールが実行される(起動が遅い) Dockerfile 方式の方が確実性が高く、再現性に優れる 私は Dockerfile 方式 を推奨します。 Functions プロジェクトの作成 DevContainer 内で Azure Functions プロジェクトを作成します。 プロジェクト初期化 DevContainer 内のターミナルで実行: func init MyFunctionApp --typescript cd MyFunctionApp 生成されるファイル : MyFunctionApp/ ├── .funcignore # Functions デプロイ時の除外ファイル ├── .gitignore # Git 除外設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── package.json # npm 依存関係 ├── tsconfig.json # TypeScript 設定 └── src/ # ソースコード格納ディレクトリ local.settings.json の設定 local.settings.json を編集して、Azurite 接続設定を追加: { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "node" } } 重要 : AzureWebJobsStorage: "UseDevelopmentStorage=true" は Azurite を使用するための設定 この設定がないと Timer Trigger でエラーになるので注意! 依存関係のインストール npm install HTTP Trigger の作成と動作確認 まずは、基本的な HTTP Trigger を作成して動作確認します。 HTTP Trigger 関数の作成 func new --name HttpExample --template "HTTP trigger" --authlevel "anonymous" 生成されるファイル : src/functions/HttpExample.ts 実装内容の確認 src/functions/HttpExample.ts : import { app, HttpRequest, HttpResponseInit, InvocationContext, } from "@azure/functions"; export async function HttpExample( request: HttpRequest, context: InvocationContext ): Promise<HttpResponseInit> { context.log("HTTP trigger function processed a request."); const name = request.query.get("name") || "World"; return { status: 200, body: `Hello, ${name}!`, }; } app.http("HttpExample", { methods: ["GET", "POST"], authLevel: "anonymous", handler: HttpExample, }); ポイント : request.query.get('name') でクエリパラメータを取得 app.http() でエンドポイントを登録 ローカル実行 npm start 実行結果 : 動作確認 別のターミナルで curl コマンドを実行: curl "http://localhost:7071/api/HttpExample?name=Azure" 期待される結果 : Hello, Azure! ブラウザでの確認 : http://localhost:7071/api/HttpExample にアクセスすると「Hello, World!」と表示されます Azurite と Timer Trigger Timer Trigger を使うには、 Azurite (Azure Storage エミュレータ)が必要です。 なぜ Azurite が必要なのか? Azure Functions の Timer Trigger は、内部的に Blob Storage を使って Timer の状態(次回実行時刻など)を保存します。ローカル開発では、この Blob Storage を Azurite でエミュレートするんですね。 HTTP Trigger のみの場合 : Azurite 不要 Timer Trigger を使う場合 : Azurite 必須! Azurite の起動 DevContainer 内で、 別のターミナル を開いて Azurite を起動します: azurite --silent # 設定やログを保存する先を指定 azurite --location .azurite --debug .azurite/debug.log 期待される結果 : Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002 デフォルトポート : Blob Service: 10000 Queue Service: 10001 Table Service: 10002 Timer Trigger 関数の作成 func new --name TimerExample --template "Timer trigger" 生成されるファイル : src/functions/TimerExample.ts 実装内容の確認 src/functions/TimerExample.ts : import { app, InvocationContext, Timer } from "@azure/functions"; export async function TimerExample( myTimer: Timer, context: InvocationContext ): Promise<void> { context.log("Timer trigger function executed at:", new Date().toISOString()); if (myTimer.isPastDue) { context.log("Timer is running late!"); } } app.timer("TimerExample", { schedule: "0 */5 * * * *", // 5分ごとに実行 handler: TimerExample, }); CRON 式の説明 : 0 */5 * * * * は 5 分ごとに実行 CRON 式は UTC 時刻 で動作(重要!) ローカル実行 Azurite が起動している状態で、Functions を起動: npm start 実行結果 : 5 分ごとにログが表示されます。 Azurite が起動していない場合のエラー Azurite が起動していないと、以下のエラーが発生します: [Error] Microsoft.Azure.WebJobs.Host: Error indexing method 'TimerExample'. → 対処法 : Azurite を起動してから Functions を再起動 トラブルシューティング よくあるエラーと対処法をまとめます。 1. Docker Desktop が起動しない 症状 : VSCode で「Docker daemon is not running」エラー 対処法 : Docker Desktop を起動する(Windows/Mac) Linux の場合: sudo systemctl start docker 2. DevContainer のビルドが失敗する 症状 : 「Failed to build image」エラー 対処法 : Dockerfile の構文エラーを確認 Docker Desktop のディスク容量を確認 VSCode のコマンドパレット( Ctrl+Shift+P )→「Dev Containers: Rebuild Container」を実行 3. func コマンドが見つからない 症状 : bash: func: command not found 対処法 : DevContainer が正しくビルドされているか確認 ターミナルを再起動 Dockerfile の npm install -g azure-functions-core-tools@4 が正しく実行されているか確認 4. Azurite 接続エラー 症状 : No connection could be made because the target machine actively refused it 対処法 : Azurite が起動しているか確認( curl http://127.0.0.1:10000 ) local.settings.json に AzureWebJobsStorage: "UseDevelopmentStorage=true" が設定されているか確認 Azurite を再起動 5. ポート競合エラー 症状 : Port 7071 is already in use 対処法 : 既存のプロセスを終了 別のポートで起動: func start --port 7072 6. TypeScript コンパイルエラー 症状 : npm start で TypeScript エラー 対処法 : npm install を実行して依存関係を再インストール tsconfig.json の設定を確認 npm run build で明示的にビルド 開発の推奨フロー DevContainer を使った Azure Functions 開発の推奨フローです。 標準的な開発手順 ターミナル 1: Azurite 起動 azurite --silent ターミナル 2: Functions ランタイム起動 cd MyFunctionApp npm start 開発作業 TypeScript ファイル( .ts )を編集 保存すると自動的にリロード(watch mode) 動作確認 HTTP Trigger: curl やブラウザでアクセス Timer Trigger: コンソールログで確認 VSCode のターミナル分割 VSCode のターミナルを分割すると便利です: ターミナル 1 : Azurite 起動( azurite --silent ) ターミナル 2 : Functions 起動( npm start ) ターミナル 3 : curl コマンドやその他の操作 分割方法 : ターミナルパネルの右上の「 + 」アイコン横の「 Split Terminal 」ボタン または Ctrl+Shift+5 / Cmd+Shift+5 推奨 VSCode 拡張機能 DevContainer 内で自動的にインストールされる拡張機能以外にも、以下があると便利です: Azure Functions ( ms-azuretools.vscode-azurefunctions ) – 既に設定済み ESLint ( dbaeumer.vscode-eslint ) – 既に設定済み Prettier ( esbenp.prettier-vscode ) – 既に設定済み Thunder Client ( rangav.vscode-thunder-client ) – HTTP クライアント(任意) まとめと次回予告 本記事で学んだこと DevContainer を使った Azure Functions 環境構築 Docker Desktop と VSCode のインストール Node.js 22 + TypeScript の DevContainer 構築 Dockerfile 方式と PostCreateCommand 方式の違い Azure Functions の基本 HTTP Trigger の作成と動作確認 Timer Trigger と Azurite の関係 ローカル開発環境での実行方法 トラブルシューティング よくあるエラーと対処法 Azurite 接続エラーの解決方法 次回の記事予告 次回は、以下の内容を予定しています: Azure Functions×DevContainer 環境構築| Python 編 Python 3.11 を使った DevContainer 構築 Node.js 版との違い Python 仮想環境との組み合わせ Azure Functions 入門| HTTP Trigger と Timer Trigger の基礎と実践パターン HTTP Trigger と Timer Trigger の詳細な使い方 実践パターン : Timer Trigger を HTTP Trigger でデバッグする方法 タイムゾーン(UTC/JST)の扱い方 DRY 原則に基づいた共通ロジックの設計 サンプルリポジトリ 本記事で解説した環境を、すぐに試せるサンプルコードを公開しています: GitHub : azure-functions-nodejs-devcontainer Node.js 22 + TypeScript HTTP Trigger + Timer Trigger 実装済み DevContainer 設定ファイル完備 クローンして VSCode で開くだけで動作します 関連記事 Claude Code×DevContainer 環境構築ガイド – Node.js・Python 対応 DevContainer の基本的な使い方 npm グローバルインストールの注意点 DevContainer を使った Azure Functions 開発、ぜひ試してみてください! チーム開発での環境差異がなくなり、開発効率が大幅に向上すること間違いなしです。 次回は Python 編 をお届けしますので、お楽しみに〜! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Functions×DevContainer環境構築|Node.js 22 + TypeScript first appeared on SIOS Tech. Lab .
アバター
はじめに ども!最近は Claude Code ともに開発を進めて、with AI での生活にどっぷりだったのですが 2025 年も締めということで貯まった検証を一気に記事化している龍ちゃんです。検証がたまっていたので、11 月と 12 月は大量にブログを書く羽目になりそうですね。ゴリゴリ執筆する必要がありますね! 皆さん、AI(Claude Code 等)と一緒に開発してると、こんな悩みありませんか? 「このプロジェクト、どういう構成になってるの?」と AI に毎回説明するのが面倒 ファイルが多すぎて AI が混乱して、的外れな提案をしてくる モノレポにしたけど、全部のアプリが毎回ビルドされて時間がかかる 僕も以前はこれらの課題に悩まされていました。特に、 プロジェクトが大きくなるほど、AI が全体像を把握しづらくなる という問題が深刻でした。 そこで構築したのが、 CLAUDE.md 階層構造 を核としたモノレポ環境です。この環境により、以下の成果を得られました: AI が自律的にプロジェクト構成を理解 (CLAUDE.md 階層構造) CI/CD ビルド時間 58%削減 (paths フィルタによる最適化) ドキュメントが自然に蓄積 (4 フェーズワークフロー: 計画 → 実装 → 研究記録 → 記事化) 本記事で紹介する環境は、実際に稼働中のシステムで実践している内容です。 このモノレポで開発しているシステムの詳細については、 AI チャットで話すだけ!X 予約投稿を完全自動化するシステム構築術 で解説しています(リポジトリは非公開)。 この記事では、 モノレポ構成と AI 協業開発を最適化する環境設計 について、実際のプロジェクト構成とワークフローを交えながら解説していきます。 記事の位置づけ 前提となる知識(先に読むべき記事) : 本記事で紹介する 4 フェーズワークフローは、以下の記事で解説した 3 フェーズ開発を基盤に構築されています: 3 フェーズ開発の基本を学ぶ : Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 計画 → 実装 → 検証の 3 フェーズワークフロー /docs/ と /application/ のディレクトリ分離 小規模システムを 30 分で実装する実例 計画フェーズの詳細を学ぶ : AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 CLAUDE.md による仕様書作成ルール AI との協働レビュープロセス 開発時間を 1 週間 →2 日に短縮した実践法 4 フェーズへの拡張を学ぶ : 検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 フェーズ 4(記事化)の追加による知見の資産化 RAG もどきシステムでトークン数 50-60%削減 文体補正システムで一貫した記事品質を実現 記事執筆時間 50%削減、重複チェック 83%削減 技術基盤 : 型安全な API 開発を学ぶ : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 Backend DTOs → OpenAPI → Frontend Types の自動生成 Single Source of Truth による型ズレゼロの実現 この記事で学べること この記事を読むことで、以下の知識とスキルが得られます: 主要なポイント モノレポ構成と AI 協業開発の相性 なぜモノレポが AI 協業に適しているのか、実体験をもとに解説 CLAUDE.md 階層構造によるコンテキスト管理 9 つの CLAUDE.md ファイルで AI に適切な情報を提供する設計 paths フィルタによる CI/CD 最適化 変更されたアプリのみビルドすることでビルド時間 58%削減 4 フェーズワークフローによる知見の資産化 計画 → 実装 → 研究記録 → 記事化のドキュメント駆動型開発 実践的なテクニック AI が理解しやすいディレクトリ構造と命名規則 GitHub Actions paths フィルタの活用 ドキュメント駆動型開発による知見の蓄積 前提条件 この記事は、 モノレポ構成と AI 協業開発環境の設計・アーキテクチャ に焦点を当てています。 前提知識(あると望ましい) AI 開発ツールの使用経験 Claude Code、GitHub Copilot、Cursor 等の AI 開発支援ツールを使った開発経験 AI にプロンプトを投げてコードを生成した経験 モノレポの基礎知識 複数のアプリケーションを 1 つのリポジトリで管理する概念の理解 (初めての方でも、記事を読み進めることで理解できます) 本記事で扱わないこと モノレポツール(Turborepo、Nx 等)の詳細比較 Azure 環境の詳細なセットアップ手順 各フレームワーク(NestJS、Next.js 等)の実装詳細 型安全パイプラインの実装詳細( 型安全パイプラインの記事 で解説) ※本記事は構成と設計に焦点を当てており、実装の詳細は関連記事で解説しています。 プロジェクト全体像 まずは、実際のプロジェクト構成を見ていきましょう。 モノレポ構成の 4 つのアプリケーション このプロジェクトは、 4 つの独立したアプリケーション をモノレポで管理しています。 アプリケーション 技術スタック デプロイ先 GitHub Actions Frontend Next.js 15, React 19, TypeScript 5 Azure Static Web Apps frontend-swa-deploy.yml Backend NestJS 11, Node.js 22, TypeScript 5 Azure Web Apps (Docker) backend-docker-build.yml X Scheduler Functions Azure Functions v4, Node.js 22, TypeScript Azure Functions deploy-x-scheduler-functions.yml Blog Search MCP Functions Azure Functions v4, Node.js 22, MCP Protocol Azure Functions deploy-blog-search-mcp-functions.yml ディレクトリ構造の全体像 プロジェクトルート/ ├── docs/ # 計画・設計フェーズ(実装コードなし) │ ├── CLAUDE.md # 計画フェーズガイドライン │ ├── project-core.md # インフラ全体構成 │ ├── features/ # 新機能開発計画 │ ├── bugs/ # バグ調査・修正計画 │ ├── research/ # 実装検証結果・知見 │ ├── article/ # ブログ記事執筆用調査 │ │ └── CLAUDE.md # 記事執筆ガイド │ ├── tools/ # Docs専用ツール │ │ └── CLAUDE.md # ツール使用ガイド │ └── api/ # OpenAPI仕様 ├── application/ # 実装フェーズ │ ├── backend/ # NestJS APIサーバー │ │ └── CLAUDE.md # バックエンド開発ガイド │ ├── frontend/ # Next.js フロントエンド │ │ └── CLAUDE.md # フロントエンド開発ガイド │ ├── functions/ # X Scheduler │ │ └── CLAUDE.md # Functions開発ガイド │ └── blog-search-mcp-functions/ # MCP Server │ └── CLAUDE.md # MCP Functions開発ガイド ├── infrastructure/ # IaCテンプレート │ └── CLAUDE.md # インフラ開発ガイド ├── CLAUDE.md # ルートガイドライン(全体像) └── .github/ └── workflows/ # CI/CDパイプライン ├── frontend-swa-deploy.yml ├── backend-docker-build.yml ├── deploy-x-scheduler-functions.yml └── deploy-blog-search-mcp-functions.yml システム構成図 モノレポ全体の構成を視覚化するとこうなります: なぜモノレポなのか? モノレポ構成を採用した理由は、 AI 協業開発との相性の良さ にあります。 モノレポのメリット 1. AI に 1 つのリポジトリで全体像を提供 別リポジトリにすると、AI は各リポジトリのコンテキストを個別に理解する必要があります。モノレポなら、 ルートの CLAUDE.md で全体像を一度に提供 できます。 # 別リポジトリの場合(AIが混乱) frontend-repo/ ← AIはこのリポジトリのコンテキストのみ backend-repo/ ← 別のセッションで別のコンテキスト functions-repo/ ← また別のコンテキスト # モノレポの場合(AIが全体を把握) monorepo/ ├── CLAUDE.md ← 全体像をAIに提供 ├── application/ │ ├── frontend/ │ ├── backend/ │ └── functions/ 2. ディレクトリ構造の一貫性 すべてのアプリケーションが同じルールに従うため、AI が理解しやすくなります。 # すべてのアプリケーションにそれぞれのCLAUDE.mdがある /application/backend/CLAUDE.md /application/frontend/CLAUDE.md /application/functions/CLAUDE.md /application/blog-search-mcp-functions/CLAUDE.md 3. コード共有が容易 共通ライブラリ、型定義、ユーティリティ関数を複数のアプリで共有できます。 AI 協業開発を支える設計思想 このモノレポ環境には、3 つの核となる設計思想があります。 1. 計画と実装の分離 /docs/ (設計・仕様)と /application/ (実装コード)を明確に分離しています。 目的 : AI に「計画フェーズ」と「実装フェーズ」を明確に区別させる 実装前に設計を固めることで、手戻りを減らす /docs/features/my-new-feature/ ├── README.md # 機能概要 ├── api-spec.md # API設計(実装コードなし) └── type-definition.md # 型定義(実装コードなし) /application/backend/src/my-new-feature/ ├── my-new-feature.controller.ts # 実装コード ├── my-new-feature.service.ts # 実装コード └── dto/ # 実装された型定義 2. CLAUDE.md 階層構造 ルートで全体像、サブディレクトリで詳細ルールを提供します。 目的 : AI が必要な粒度でコンテキストを取得できる 各領域の専門的なルールを明確にする /CLAUDE.md ← プロジェクト全体像 ↓ /docs/CLAUDE.md ← 計画フェーズのルール ↓ /application/backend/CLAUDE.md ← バックエンド実装の詳細ルール 3. Single Source of Truth(型安全パイプライン) Backend DTOs を唯一の真実とし、Frontend の型定義は自動生成します。 詳細 : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 を参照 Backend DTOs (@ApiProperty) ↓ OpenAPI 仕様生成 (generate:openapi) ↓ Frontend 型定義生成 (generate:api with Orval) ↓ 型安全なAPI呼び出し これら 3 つの設計思想により、 AI との協業開発が劇的にスムーズ になりました。 CLAUDE.md 階層構造の設計 ここからは、AI 協業開発の核となる CLAUDE.md 階層構造 について詳しく解説します。 CLAUDE.md とは? CLAUDE.md は、AI(Claude Code)にプロジェクトのコンテキストを提供するドキュメントです。 従来、AI に「このプロジェクトはどういう構成?」「どのルールに従えばいい?」と聞かれるたびに、手動で説明する必要がありました。CLAUDE.md を配置することで、 AI が自律的にガイドラインを読み、適切な判断をする ようになります。 9 つの CLAUDE.md ファイル このプロジェクトには、 合計 9 つの CLAUDE.md ファイル が配置されています。 # すべてのCLAUDE.mdファイルを確認 $ find . -maxdepth 3 -name "CLAUDE.md" | sort ./CLAUDE.md ./application/backend/CLAUDE.md ./application/blog-search-mcp-functions/CLAUDE.md ./application/frontend/CLAUDE.md ./application/functions/CLAUDE.md ./docs/article/CLAUDE.md ./docs/CLAUDE.md ./docs/tools/CLAUDE.md ./infrastructure/CLAUDE.md 各 CLAUDE.md の役割 : ファイルパス 役割 主な内容 /CLAUDE.md ルートガイドライン プロジェクト全体像、ディレクトリ構造、4 フェーズワークフロー、共通開発コマンド /docs/CLAUDE.md 計画フェーズルール 型定義、API 設計、データベース設計のルール(実装コード禁止) /docs/article/CLAUDE.md 記事執筆ガイド ブログ記事執筆の文体、構成、ドキュメント構造 /docs/tools/CLAUDE.md ツール使用ガイド Docs 専用ツール(ブログ HTML 抽出等)の使用方法 /application/backend/CLAUDE.md バックエンド開発ガイド NestJS 開発ルール、環境変数管理、テスト実行方法 /application/frontend/CLAUDE.md フロントエンド開発ガイド Next.js 開発ルール、インポートパス規則 /application/functions/CLAUDE.md X Scheduler 開発ガイド Azure Functions 開発ルール、Timer Trigger 設定 /application/blog-search-mcp-functions/CLAUDE.md MCP Functions 開発ガイド MCP Server 開発ルール、Supabase 連携 /infrastructure/CLAUDE.md インフラ開発ガイド Azure Bicep 開発ルール、デプロイ手順 コンテキスト継承パターン CLAUDE.md は、 階層的にコンテキストを継承 します。 継承の例 : ルート CLAUDE.md を読む → プロジェクト全体構成を理解 サブディレクトリの CLAUDE.md を読む → 各領域の詳細ルールを理解 AI が適切な判断を下す 実際の動き : AI: 「ユーザーがフロントエンドの開発を依頼してきた」 ↓ AI: 「まず /CLAUDE.md を読んで全体像を把握しよう」 ↓ AI: 「次に /application/frontend/CLAUDE.md を読んで詳細ルールを確認」 ↓ AI: 「インポートパスは @/* を使うべきだな」 AI: 「API型定義は自動生成されるから、手動で書いちゃダメだな」 ↓ AI: 適切なコードを提案 ルート CLAUDE.md の重要性 ルート CLAUDE.md ( /CLAUDE.md )は、最も重要なドキュメントです。 記載内容の例 # CLAUDE.md ## Project Architecture Overview LINE LIFF AI Prompt Battle - An AI-powered game platform... ### Directory Structure & Responsibilities / ├── docs/ # Planning & Design Phase │ ├── features/ # Feature specifications │ ├── bugs/ # Bug investigation & fix plans │ ├── research/ # Implementation validation │ └── article/ # Blog article research ├── application/ │ ├── backend/ # NestJS 11 API Server │ ├── frontend/ # Next.js 15 App Router │ ├── functions/ # Azure Functions │ └── blog-search-mcp-functions/ # MCP Server └── infrastructure/ # Azure Bicep IaC **Workflow Pattern (4-Phase Workflow)**: 1. **計画フェーズ** - Plan in `/docs/` 2. **実装フェーズ** - Implement in `/application/` 3. **研究記録フェーズ** - Document findings in `/docs/research/` 4. **記事化フェーズ** - Gather materials in `/docs/article/` ## Common Development Commands ### Backend (NestJS) npm run start:dev # Development with watch mode npm run generate:openapi # Generate OpenAPI spec ### Frontend (Next.js) npm run generate:api # Generate types from OpenAPI npm run dev:full # generate:api + dev server ## Critical Import Path Rules ### Frontend: Use `@/*` Path Aliases // ✅ CORRECT import { Button } from '@/components/ui/button'; // ❌ WRONG import { Button } from '../components/ui/button'; ポイント : プロジェクト全体構成 を一目で理解できる 4 フェーズワークフロー を明記 共通開発コマンド を記載 インポートパス規則 等の重要ルールを記載 ※注 : 上記コード例は、実際のプロジェクト CLAUDE.md での記載例です。本記事では「4 フェーズワークフロー」または「ドキュメント駆動型開発」と呼称しています。 このルート CLAUDE.md があることで、AI は 初めてプロジェクトに触れた時でも、全体像を即座に理解 できます。 CLAUDE.md 階層構造の効果 この階層構造により、以下の効果が得られました: AI の理解速度が向上 ルート CLAUDE.md で全体像を把握し、サブディレクトリで詳細を理解 一貫性のあるコード生成 すべての開発者(人間も AI も)が同じルールに従う 手動説明の削減 「どういうプロジェクト?」と聞かれることがなくなった オンボーディング時間の短縮 新しい AI セッション、新しい開発者が即座に理解できる ドキュメント駆動型開発の実践(3 フェーズ →4 フェーズへの進化) Claude Code での開発を効率化するため、 計画 → 実装 → 研究記録 → 記事化の 4 フェーズワークフロー を構築しました。 3 フェーズ開発から 4 フェーズ開発への進化 このワークフローは、 既存の 3 フェーズ開発を基盤に構築 されています: 元々の 3 フェーズ開発 ( 計画 → 実装 → 検証術 、 仕様書アレルギー克服 で解説): 計画 : /docs/ で仕様策定(実装コードは書かない) 実装 : /application/ で実装 検証 : 計画と実装の差異を分析 4 フェーズへの拡張 ( 知見を資産化する記事 で詳細解説): フェーズ 3 を「研究記録」として体系化 : 検証フェーズで得た知見を /docs/research/ に記録 フェーズ 4「記事化」を追加 : 研究記録を元に /docs/article/ で記事執筆 RAG もどきシステム : 既存記事参照でトークン数 50-60%削減 文体補正システム : 一貫した記事品質を実現 各フェーズの概要 フェーズ 1: 計画 ( /docs/features/ ) 目的 : 実装前の設計・仕様策定(実装コードは一切書かない) 成果物 : 型定義、API 設計、データベース構造、アーキテクチャ設計 効果 : 開発時間 1 週間 →2 日に短縮( 仕様書アレルギー克服の記事 で詳細解説) 重要ルール : CLAUDE.md で実装コード記述を禁止することで、AI が設計に集中 フェーズ 2: 実装 ( /application/ ) 目的 : 計画に基づいた実装 実装順序 : Backend → Frontend → Functions 効果 : 小規模システムで 30 分、中規模で 2 日程度( 3 フェーズ開発の記事 で実例紹介) フェーズ 3: 研究記録 ( /docs/research/ ) 目的 : 実装完了後の知見・アーキテクチャ検証結果をドキュメント化 記載内容 : 設計思想、アーキテクチャパターン、検証結果 重要性 : 計画と実装の差異を分析し、仕様漏れを特定 ※補足 : 3 フェーズ開発の記事 で「検証フェーズ」として解説している内容を、このプロジェクトでは「研究記録フェーズ」として体系化しています。実装完了後に計画と実装の差異を分析し、知見として記録します。 フェーズ 4: 記事化 ( /docs/article/ ) 目的 : 技術ブログ執筆に必要な情報収集・調査、 知見の資産化 成果物 : research-doc.md (調査資料)、 no1-article.md (記事本文) 参照元 : /docs/features/ + /docs/research/ + /application/ 効率化ツール ( 知見を資産化する記事 で詳細解説): RAG もどき (fetch-blog-html.ts): 既存記事参照でトークン数 50-60%削減 文体補正 (writing-style-prompt.md): 一貫した記事品質 記事執筆時間 50%削減 : 調査資料から記事執筆までの自動化 重複チェック 83%削減 : 既存記事との重複を自動検出 このワークフローの効果 知見が自然に蓄積 – 実装と同時にドキュメントが作成される 記事化がスムーズ – 計画 → 検証 → 実装コードを参照するだけ 手戻りが減少 – 計画フェーズで設計を固めることで、実装時の手戻りが減少 開発時間 1 週間 →2 日に短縮(計画フェーズの効果) AI との協業が効率化 – 各フェーズで AI に明確な役割を与えられる 知見が資産化される – フェーズ 4(記事化)により、開発知見が再利用可能なブログ記事として蓄積 既存記事との重複チェック 83%削減 技術ブログのライブラリが自然に形成される 型安全な API 開発パイプライン Frontend と Backend の型ズレをゼロにする 型安全パイプライン については、別記事で詳しく解説しています。 詳細を学ぶ : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 Single Source of Truth の原則 このプロジェクトでは、 Backend DTOs を唯一の真実 としています。 Frontend の型定義は、Backend から自動生成するため、 手動で型定義を同期する作業が不要 になります。 型安全パイプラインの効果 型ズレゼロ – Backend と Frontend の型が常に一致 手動同期作業の撲滅 – 型定義を手動でコピー&ペーストする必要がない リファクタリング時の安全性 – Backend の型を変更すると、Frontend でコンパイルエラーが出る 開発速度向上 – 型定義を書く時間が不要になり、実装に集中できる モノレポ CI/CD パイプライン: paths フィルタの威力 次に、4 つのアプリケーションを並行デプロイする 最適化された CI/CD パイプライン について解説します。 4 つの独立したワークフロー このプロジェクトでは、 4 つの独立した GitHub Actions ワークフロー を使用しています。 ワークフロー トリガーパス デプロイ先 ビルド時間(平均) frontend-swa-deploy.yml application/frontend/** Azure Static Web Apps 3 分 backend-docker-build.yml application/backend/** GitHub Container Registry → Azure Web Apps 5 分 deploy-x-scheduler-functions.yml application/functions/** Azure Functions (X Scheduler) 2 分 deploy-blog-search-mcp-functions.yml application/blog-search-mcp-functions/** Azure Functions (MCP Server) 2 分 paths フィルタによる最適化 課題 : モノレポ全体をビルドすると、変更のないアプリもビルドされ時間がかかる 解決策 : GitHub Actions の paths フィルタで、変更されたディレクトリのみをトリガー 例: Frontend SWA Deploy # .github/workflows/frontend-swa-deploy.yml name: Frontend SWA Deploy on: push: branches: - main paths: - "application/frontend/**" - ".github/workflows/frontend-swa-deploy.yml" workflow_dispatch: ポイント : paths フィルタで application/frontend/** のみをトリガー Backend を変更しても、Frontend のワークフローは実行されない Before/After 比較 Before(paths フィルタなし) Backend を変更 ↓ 全ワークフロー実行(Frontend, Backend, Functions, MCP Functions) ↓ 合計ビルド時間: 12分(3 + 5 + 2 + 2) After(paths フィルタあり) Backend を変更 ↓ Backend ワークフローのみ実行 ↓ 合計ビルド時間: 5分(58%削減) 並行デプロイの実現 4 つのワークフローが 独立している ため、複数のアプリを同時に変更した場合、 並行デプロイ が実現されます。 Frontend と Backend を同時に変更 ↓ frontend-swa-deploy.yml と backend-docker-build.yml が並行実行 ↓ 合計ビルド時間: 5分(逐次実行なら8分) CI/CD パイプラインの効果 ビルド時間 58%削減 – paths フィルタにより、変更のないアプリはビルドされない 並行デプロイ – 複数のアプリを同時に変更しても、並行実行で時間短縮 安全なデプロイ – 各アプリが独立しているため、Frontend のデプロイ失敗が Backend に影響しない 開発体験の変化(Before/After) モノレポ ×AI 協業開発環境を導入する前後で、開発体験がどう変わったかを比較します。 Before(モノレポ ×AI 環境導入前) 問題点 AI に毎回プロジェクト構成を説明 開発者: 「ユーザー管理機能を実装して」 AI: 「このプロジェクトはどういう構成ですか?」 開発者: 「Backendは...Frontendは...」(毎回説明) CI/CD ビルド時間の増加 Backend を変更 ↓ 全ワークフロー実行(Frontend, Backend, Functions) ↓ 合計ビルド時間: 12分 ドキュメントが散らばって、どこに何があるかわからない 計画ドキュメント: Notion 実装コメント: コード内 ブログ記事: 別リポジトリ ↓ 情報が散らばって、どこに何があるかわからない After(モノレポ ×AI 環境導入後) 改善点 AI が自律的にプロジェクト構成を理解 開発者: 「ユーザー管理機能を実装して」 AI: 「/CLAUDE.md を確認します」 AI: 「/application/backend/CLAUDE.md を確認します」 AI: 「型安全パイプラインに従って実装します」 CI/CD ビルド時間 50%削減 Backend を変更 ↓ Backend ワークフローのみ実行(pathsフィルタ) ↓ 合計ビルド時間: 5分(50%削減) ドキュメントが自然に蓄積し、資産化される 4フェーズワークフロー 計画 (/docs/features/) ↓ 実装 (/application/) ↓ 研究記録 (/docs/research/) ↓ 記事化 (/docs/article/) ├─ RAGもどき(fetch-blog-html.ts)でトークン数50-60%削減 └─ 文体補正(writing-style-prompt.md)で一貫した記事品質 ↓ 情報が整理され、知見が資産化される 技術ブログのライブラリが自然に形成される 定量的な効果 指標 Before After 改善率 初期説明時間 10 分/セッション ほぼ不要(CLAUDE.md で自律理解) 大幅削減 CI/CD ビルド時間 12 分 5 分 58%削減 ドキュメントメンテナンス時間 週 5 時間 週 2 時間 60%削減 新機能開発スピード 2 週間 1 週間 50%短縮 記事執筆時間 6-8 時間 3-4 時間 50%削減 ※測定条件 : 小規模〜中規模機能開発(バックエンド API エンドポイント 2-4 個、フロントエンド画面 1-2 個規模)での実測値です。プロジェクトの規模や複雑さによって効果は変動します。記事執筆時間は、調査資料作成から記事本文執筆までの合計時間です。 龍ちゃんの所感 導入前の課題 : プロジェクトが小さいうちは問題なかったんですが、いろんな検証を詰め込んでプロジェクトが大きくなってくると、 ドキュメントを編集するだけでビルドが走る という問題が出てきました。適切にビルドを分割することで、不要な CI/CD の実行を抑える必要が出てきたんです。 また、フロントエンド・バックエンド・Functions(バッチ処理)という複数の構成でアプリを作っていたので、 API 連携やデータベース連携がスムーズに行える環境 が必要でした。例えば、バックエンドでデータベースに情報を入れて、それをバッチ処理で取得して実行するような連携ですね。 AI 協業開発での気づき : AI と開発する上で、フロントエンドとバックエンドの型ズレを解消するためにも、 アプリケーション全体を AI に見える形で一つに集約する ことがやはり大事だなと実感しました。これは自分の中でもすごく効果的でしたね。 検証とブログ執筆の課題 : 自分の検証スタイルとして、「インプットを入れたらアウトプットを出す」というのが弊社の理念でもあり、このブログの意義でもあるので、検証とブログ執筆はセットで考えていました。 ただ、検証が終わった後に、別でブログを書くためのシステムを立ち上げて…というのが割と面倒になってきたんです。もう 3 年で 200 記事くらい書いているんですが、だんだん検証する内容も大きくなってきて、記事も長くなってきました。 ソースコード参照の課題と解決策 : 記事を書く際に ソースコードを参照することが増えて きたんですが、参照するファイルが増えれば増えるほど、ブログを書く障壁がどんどん上がってきました。 そこで、 リポジトリから直接情報を吸い出してブログを書くシステム (4 フェーズワークフロー、RAG もどき、文体補正)を構築したことで、ブログ執筆の効率化がさらに進んだと感じています(詳細は 検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 を参照)。 まとめ この記事では、 モノレポ ×AI 協業開発環境 の構築術について解説しました。 モノレポ ×AI 協業開発環境のポイント CLAUDE.md 階層構造でコンテキスト管理 9 つの CLAUDE.md ファイルで、AI に適切な粒度の情報を提供 4 フェーズワークフロー(計画 → 実装 → 研究記録 → 記事化) ドキュメント駆動型開発で、知見が自然に蓄積され、資産化される 3 フェーズ開発を基盤に、フェーズ 4(記事化)を追加 RAG もどき、文体補正による記事執筆効率化 型安全パイプライン(Backend DTOs → OpenAPI → Frontend Types) Single Source of Truth で、型ズレゼロを実現( 型安全パイプラインの詳細記事 ) paths フィルタによる最適化 CI/CD 4 つの独立ワークフローで、ビルド時間 58%削減 開発体験の変化 Before After AI に毎回プロジェクト構成を説明 CLAUDE.md で自律的に理解 CI/CD ビルド時間 12 分 paths フィルタで 5 分(58%削減) ドキュメントが散らばる 4 フェーズワークフローで自然に蓄積、資産化 記事執筆に時間がかかる RAG もどき、文体補正で 50%削減 次のステップ この記事を読んで、モノレポ ×AI 協業開発環境に興味を持った方は、以下のステップで実践してみてください: 1. CLAUDE.md を作成 まずは、プロジェクトルートに CLAUDE.md を作成し、プロジェクト全体像を記載します。 # CLAUDE.md ## Project Architecture Overview (プロジェクトの概要) ### Directory Structure (ディレクトリ構造) ## Workflow Pattern (4-Phase Workflow) 1. **計画フェーズ** - Plan in `/docs/` 2. **実装フェーズ** - Implement in `/application/` 3. **研究記録フェーズ** - Document findings in `/docs/research/` 4. **記事化フェーズ** - Gather materials in `/docs/article/` 2. paths フィルタを導入 GitHub Actions ワークフローに paths フィルタを追加します。 on: push: branches: - main paths: - "application/frontend/**" 3. 4 フェーズワークフローを実践 新機能開発時は、計画 → 実装 → 研究記録 → 記事化の 4 フェーズを順に進めます。 詳細は 3 フェーズ開発の基本を学ぶ記事 を参照してください。 参考リンク 関連記事(本サイト) AI 協業開発手法シリーズ Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 型安全パイプライン AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 公式ドキュメント Claude Code 公式ドキュメント NestJS 公式ドキュメント Next.js 公式ドキュメント GitHub Actions 公式ドキュメント Orval 公式ドキュメント ここまで読んでいただき、ありがとうございました! モノレポ ×AI 協業開発環境を構築することで、開発体験が劇的に向上します。ぜひ、この記事を参考に、あなたのプロジェクトでも実践してみてください。 質問や感想は、コメント欄でお待ちしております。また、Twitter のほうもよろしくお願いします! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AI 協業開発環境の構築術|モノレポでビルド時間を大幅短縮する CLAUDE.md 活用法 first appeared on SIOS Tech. Lab .
アバター
はじめに ども!先輩社員から Azure Static Web Apps のビルドの相談を受けて、そういえばそのブログを書いたことないなって「はっ!」となった龍ちゃんです。 今まで、過去にセットアップしたものを使いまわしていたんですけど、身近にありすぎて忘れていました。そんなわけでまとめていきます。Azure Static Web Apps はよく使用します。今回の記事をふんだんに使ったブログは「 Azure Static Web Apps: x-ms-client-principal で安全なロールベース制御 」です。 それでは本題に入ります。Azure Static Web Apps(以下、SWA)を使っていて、「デプロイに時間がかかるな…」と感じたことはありませんか? Azure Portal から SWA を作成すると、GitHub Actions のワークフローファイルが自動生成されます。このデフォルト設定では、Microsoft の Oryx ビルドシステム が使用され、プロジェクトを自動的に検出してビルドしてくれます。 しかし、このデフォルト設定には以下のような課題があります: 毎回依存関係のフルインストールが発生 (キャッシュなし) テストや Linter の実行ができない ビルドプロセスのカスタマイズが困難 本記事では、GitHub Actions 上でカスタムビルドを実装し、デプロイ時間を短縮する方法を解説します。 Oryx ビルドシステムとは Oryx の概要 Oryx(オリックス)は、Microsoft が開発したオープンソースのビルドシステムで、ソースコードを自動的に実行可能なアーティファクトにコンパイルします。 公式リポジトリ : https://github.com/microsoft/Oryx Azure Static Web Apps、Azure App Service、Azure Functions などで利用されています。 自動検出の仕組み Oryx は以下のように動作します: リポジトリの内容を分析 使用されているプログラミング言語・フレームワークを検出 適切なビルドコマンドを自動実行 具体例(Node.js の場合) 検出内容 → 実行されるコマンド ---------------------------------------------------- package.json が存在 → npm install → npm run build または npm run build:azure デフォルトワークフローの動作 Azure Portal から SWA を作成すると、以下のようなワークフローファイルが自動生成されます: name: Azure Static Web Apps CI/CD on: push: branches: - main pull_request: types: [opened, synchronize, reopened, closed] branches: - main jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true lfs: false - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} action: "upload" app_location: "/" # アプリのソースコードパス api_location: "" # APIのソースコードパス(オプション) output_location: "dist" # ビルド済みアプリのディレクトリ(オプション) このワークフローの特徴 : シンプルで設定が簡単 Oryx が自動的にビルド設定を検出 毎回依存関係のインストールが発生 (キャッシュなし) ビルド時間が長い テストの実行ができない ビルドプロセスのカスタマイズが困難 カスタムビルドで得られるメリット GitHub Actions 上でビルドを実行することで、以下のようなメリットが得られます。 1. 依存関係のキャッシュ actions/setup-node@v6 の組み込みキャッシュ機能を使用することで、依存関係のダウンロード時間を大幅に短縮できます。 - uses: actions/setup-node@v6 with: node-version: "20" cache: "npm" # npm, yarn, pnpm をサポート cache-dependency-path: "package-lock.json" 2025 年 11 月時点の推奨バージョン : actions/setup-node@v6 が最新版です。v6 では Node.js 24 ランタイムへのアップグレードやセキュリティ強化が行われています。v6 でも v4 と同様に cache: "npm" でキャッシングが有効化されます。基本的な使い方は互換性があるため、v4 からの移行は容易です。 キャッシュの仕組み package-lock.json のハッシュ値をキーとして使用 ファイルが変更されない限り、同じキャッシュが再利用される ~/.npm ディレクトリがキャッシュされる 2. テスト・Linter の統合 ビルドプロセスに、テストや静的解析(Linter)を組み込むことができます。 - name: Run tests run: npm test - name: Run linter run: npm run lint これにより、品質の低いコードが本番環境にデプロイされるのを防げます。 3. ビルドプロセスの完全制御 複雑なビルド要件(環境変数の設定、複数ステップのビルド、モノレポ対応など)に柔軟に対応できます。 4. デプロイの高速化 依存関係のキャッシュにより、2 回目以降のデプロイが大幅に高速化されます。 実装手順 Step 1: ワークフローファイルの作成 .github/workflows/frontend-deploy.yml を作成します。 name: Frontend SWA Deploy on: push: branches: - main workflow_dispatch: jobs: build-and-deploy: runs-on: ubuntu-22.04 environment: production steps: # 1. リポジトリをチェックアウト - name: Checkout repository uses: actions/checkout@v4 # 2. Node.js のセットアップ(キャッシュあり) - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "20" cache: "npm" cache-dependency-path: "application/frontend/package-lock.json" # 3. 依存関係のインストール - name: Install dependencies run: | cd application/frontend npm ci # 4. テストとLinterの実行(オプション) - name: Run tests run: | cd application/frontend npm test - name: Run linter run: | cd application/frontend npm run lint # 5. ビルド実行 - name: Build frontend run: | cd application/frontend npm run build # 6. Azure Static Web Apps へデプロイ - name: Deploy to Azure Static Web Apps uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" app_location: "./application/frontend/out" output_location: "" skip_app_build: true # 重要: Oryx ビルドをスキップ Step 2: デプロイトークンの取得 Azure Portal にアクセス 対象の Static Web Apps リソースを開く 左メニューから「 管理 」を選択 「 デプロイトークン 」をコピー Step 3: GitHub Secrets の設定 GitHub リポジトリの Settings → Secrets and variables → Actions New repository secret をクリック 以下を設定: Name: AZURE_SWA_DEPLOY_TOKEN Secret: 手順 2 でコピーしたトークンを貼り付け Add secret をクリック セキュリティのベストプラクティス デプロイトークンを安全に管理するための推奨事項: 1. 環境レベルのシークレット管理 リポジトリレベルではなく、GitHub 環境(staging、production)ごとにトークンを管理することを推奨します。 Settings → Environments → New environment で環境を作成 環境ごとに異なるトークンを設定 これにより、環境ごとのアクセス制御が可能になり、誤ったデプロイを防げます。 2. 定期的なローテーション トークンは定期的に再生成することを推奨します。 Azure Portal → Static Web Apps → 管理 → デプロイトークンの再生成 GitHub Secrets を更新 3. 漏洩時の対応 トークンが漏洩した場合は、即座に再生成してください。 Azure Portal でトークンを再生成 GitHub Secrets を更新すれば、次回デプロイから新しいトークンが使用されます 重要な設定ポイント skip_app_build: true この設定により、Oryx によるビルドをスキップし、既にビルド済みのファイルをそのままデプロイします。 skip_app_build: true app_location の指定 skip_app_build: true を使用する場合、 ビルド済みファイルのディレクトリ を指定します。 app_location: "./application/frontend/out" # Next.js の場合 output_location は空文字列 skip_app_build: true と併用する場合は空文字列に設定します。 output_location: "" npm ci の使用 npm install ではなく、 npm ci を使用することを強く推奨します。 # ❌ 避けるべき - run: npm install # ✅ 推奨 - run: npm ci 理由 : npm ci は package-lock.json を厳密に再現 より高速で、CI/CD 環境に最適化されている node_modules を削除してからインストールするため、クリーンな環境が保証される モノレポ構成での最適化 モノレポの場合、変更があったディレクトリのみビルドするように paths フィルタを設定できます。 on: push: branches: - main paths: - "application/frontend/**" - ".github/workflows/frontend-deploy.yml" メリット : フロントエンドの変更時のみワークフローが実行される 無駄なビルドを削減 GitHub Actions の実行時間を節約 補足: SWA CLI を使用したデプロイ方法 公式の Azure/static-web-apps-deploy@v1 アクションを使わず、 SWA CLI を直接使用する方法 もあります。 実装例 - name: Install SWA CLI run: npm install -g @azure/static-web-apps-cli - name: Deploy with SWA CLI run: | swa deploy ./application/frontend/out \ --deployment-token ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }} \ --env production メリット・デメリット メリット : GitHub Actions のキャッシュ機能を利用できる ローカル開発でも同じ CLI を使用できる(開発体験の統一) デプロイプロセスの細かい制御が可能 デメリット : SWA CLI への深い理解が必要(学習コスト) 公式アクションに比べて設定が複雑 CLI のバージョン管理が必要 推奨の使い分け 公式アクション( Azure/static-web-apps-deploy@v1 )を推奨 カスタマイズ箇所が少なく、保守性が高い 本記事で紹介した skip_app_build: true パターンで十分高速 SWA CLI が向いているケース ローカルで既に SWA CLI を使って開発している デプロイプロセスに特殊な要件がある CLI の細かい制御機能が必要 結論 : ほとんどのケースでは、公式アクションをアップローダーとして利用する方法で十分です。 認証方法の選択:デプロイトークン vs OIDC デプロイトークン方式(推奨) 特徴 シンプルで設定が簡単 Azure Static Web Apps の標準的な認証方法 安定性が高い トークンの管理が必要 使い方 - uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }} OIDC Federated Credentials(2025年10月時点) 現状 Azure Static Web Apps は、デプロイ認証において OIDC(OpenID Connect)をネイティブサポートしていません(2025 年 1 月時点)。 補足 : ユーザー認証用の OIDC(Auth0、Azure AD B2C など)は Standard プランで利用可能です。ここで述べているのは、GitHub Actions からのデプロイ時の認証に関する制限です。 他の Azure サービス(App Service、Container Instances など)では、デプロイ認証における OIDC がサポートされていますが、Static Web Apps では未対応です。 参考 : GitHub Issue #1304 – Add Federated Credentials support 回避策の存在とリスク OIDC トークンを使って Azure CLI で認証し、デプロイトークンを動的に取得する方法は存在しますが: トークンのマスキング設定ミスによるリークリスク 設定が複雑 現時点では推奨されない 推奨事項 2025 年 1 月時点では、デプロイトークン方式が最も安全で信頼性の高い方法です。 OIDC 対応については、コミュニティから強く要望されており、将来的にサポートされる可能性は高いですが、現時点ではまだ実装されていません。 トラブルシューティング 問題 1: キャッシュが効かない 症状 2 回目以降のビルドでも npm ci に時間がかかる 原因 cache-dependency-path が指定されていない、または間違っている 解決策 # ❌ 間違った設定 - uses: actions/setup-node@v4 with: cache: "npm" # cache-dependency-path が指定されていない # ✅ 正しい設定 - uses: actions/setup-node@v4 with: cache: "npm" cache-dependency-path: "package-lock.json" モノレポの場合は、正しいパスを指定します: cache-dependency-path: "application/frontend/package-lock.json" 問題 2: デプロイが失敗する 症状 Error: No such file or directory 原因 skip_app_build: true を使用しているのに、 app_location が正しくない 解決策 # ❌ 間違った設定 app_location: "/" # ソースコードのパス skip_app_build: true # ✅ 正しい設定 app_location: "./application/frontend/out" # ビルド済みディレクトリ skip_app_build: true output_location: "" 問題 3: ビルドコマンドが見つからない 症状 npm run build: command not found 原因 package.json に build スクリプトが定義されていない 解決策 package.json に build スクリプトを追加します: { "scripts": { "build": "next build" } } または、ワークフローで直接コマンドを指定します: - name: Build frontend run: next build 問題 4: Actions のキャッシュサイズ超過 症状 Warning: Cache size exceeded limit 原因 node_modules が大きすぎる 解決策 setup-node のキャッシュは ~/.npm をキャッシュするため、通常は問題ありません。 もし問題が発生する場合は、不要な devDependencies を削除するか、キャッシュを無効化します。キャッシュを無効化すると、カスタムビルドを組む意味が大幅に減ってしまいます。おそらくですが、この事象が頻発する場合はOryxのビルドも失敗するんちゃうかな?って思っとります。 問題 5: ビルドタイムアウト 症状 Error: Build timed out after 15 minutes 原因 Azure Static Web Apps のビルドには 15 分の制限があります 解決策 カスタムビルド(本記事の方法)に移行することで、この制限を回避できます。GitHub Actions 側でビルドを行うため、Azure SWA 側のタイムアウトは影響しません。 まとめ 今回は、Azure Static Web Apps のデプロイを高速化するための、GitHub Actions カスタムビルドの実装方法をご紹介しました。 カスタムビルドを導入すべきケース 小規模プロジェクト・個人開発 デフォルトの Oryx ビルドで十分 シンプルさを優先 中規模以上のプロジェクト・チーム開発 カスタムビルドを推奨 キャッシュによる高速化の恩恵が大きい テスト・Linter の統合で品質向上 エンタープライズプロジェクト カスタムビルド必須 並列ジョブでテスト・ビルドを分離 モノレポ構成で paths フィルタ活用 デプロイ環境の分離(本番・ステージング) カスタムビルドのメリット再確認 項目 Oryx ビルド カスタムビルド 依存関係キャッシュ なし あり テスト実行 不可 可能 Linter 実行 不可 可能 ビルド時間 遅い 速い(2 回目以降) カスタマイズ性 低い 高い セットアップ難易度 簡単 やや複雑 参考リンク Azure Static Web Apps – Build Configuration(公式ドキュメント) GitHub Actions – setup-node Microsoft Oryx Repository Azure Static Web Apps: build app externally – johnnyreilly この記事が、Azure Static Web Apps のデプロイを高速化する一助となれば幸いです。 カスタムビルドを導入することで、デプロイ時間の短縮だけでなく、テストや Linter の統合による品質向上など、開発体験が大きく向上します。 ぜひ、皆さんのプロジェクトでもお試しください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Static Web Apps のデプロイを高速化!GitHub Actions カスタムビルドのすすめ first appeared on SIOS Tech. Lab .
アバター
はじめに ども!最近は Claude Code とべったりで、実装 → 検証 → 記事化のサイクルを回している龍ちゃんです。 皆さん、せっかく実装して検証した知見、 ちゃんと記事化できてますか? 僕も以前はこんな悩みを抱えていました: 実装完了後に「記事書こう」と思っても、何を書けばいいか分からない 実装時のメモが散らばって、記事執筆時に情報を集めるのが大変 既存記事との整合性を取るのが面倒で、似たような内容を書いてしまう 特に困ったのが、 実装から時間が経つと、なぜその設計にしたのか忘れてしまう こと。「あれ、これってどういう意図だったっけ?」とコードを読み返す羽目になり、記事執筆が進まない…。 そこで構築したのが、 検証 → 記事化をワンセットにしたワークフロー です。この仕組みにより、以下の成果を得られました: 知見が自然に蓄積 (実装と同時にドキュメント化) 記事執筆時間 50%削減 (調査資料が既に揃っている) 既存記事との整合性確保 (RAG もどきで既存記事を参照) この記事では、 実装完了後の知見をブログ記事化する仕組み を、実際のプロジェクト例とツールを交えながら解説していきます。 記事の位置づけ この記事は、既存の 3 フェーズ開発フロー(計画 → 実装 → 検証) を拡張し、 検証後の知見を記事化するフェーズ に焦点を当てた内容です。 この記事で学べること この記事を読むことで、以下の知識とスキルが得られます: 主要なポイント フェーズ 3: 研究記録の実践 実装完了後の知見をどう記録するか( /docs/research/ ) フェーズ 4: 記事化の実践 調査資料から記事本文への変換方法( /docs/article/ ) RAG もどき: ブログ HTML 抽出ツール 既存記事をローカルに保存し、記事執筆時に参照する仕組み 文体補正システム writing-style-prompt.md による一貫した記事品質 実践的なテクニック research-doc.md(調査資料)の書き方 no1-article.md(記事本文)への変換パターン fetch-blog-html.ts による既存記事参照 トークン数削減テクニック(50-60%削減) 前提条件 必要な知識 AI 開発ツールの使用経験 Claude Code、GitHub Copilot、Cursor 等の AI 開発支援ツールを使った開発経験 AI にプロンプトを投げてコードを生成した経験 あると理解が深まる知識 3 フェーズ開発フローの概念 計画 → 実装 → 検証の基本的な流れへの理解 以下の記事を参照: Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 本記事で扱わないこと 3 フェーズ開発フロー全体の詳細(上記参照記事を参照) Markdown 記法の基礎 ブログプラットフォーム(Hashnode 等)の使い方 本記事は、 フェーズ 3→4(検証 → 記事化)のワークフロー に焦点を当てています。 4 フェーズワークフローの復習 まずは、4 フェーズワークフローの全体像を確認しましょう。 4 フェーズの概要 フェーズ 1-2: 計画と実装(概要) フェーズ 1: 計画 ( /docs/features/ ) 目的: 実装前の設計・仕様策定 成果物: 型定義、API 設計、データベース構造 フェーズ 2: 実装 ( /application/ ) 目的: 計画に基づいた実装 成果物: 動作するコード 詳細は以下の記事を参照してください: Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 本記事の焦点: フェーズ 3-4 本記事では、 実装完了後の知見を記事化するフェーズ 3-4 に焦点を当てます。 フェーズ 3: 研究記録 ( /docs/research/ ) 実装完了後の知見・検証結果をドキュメント化 設計思想、アーキテクチャパターン、検証結果を記録 フェーズ 4: 記事化 ( /docs/article/ ) 技術ブログ執筆に必要な情報収集 research-doc.md(調査資料)→ no1-article.md(記事本文)の変換 フェーズ 3: 研究記録の実践 研究記録フェーズの目的 実装完了後、 「なぜその設計にしたのか」「どんな課題があって、どう解決したのか」 を記録するフェーズです。 目的 : 設計思想と意思決定の記録 アーキテクチャパターンの検証結果 実装完了後の振り返り 記録先 : /docs/research/ ディレクトリ構造 研究記録は、単一ファイル形式で管理します(小〜中規模機能に対応)。 docs/research/ ├── aoai-chat-simple.md # Azure OpenAI チャット機能 ├── github-api-integration.md # GitHub API 統合 └── csv-preview-system.md # CSV プレビュー機能 特徴 : 1 ファイルで機能検証が完結し、効率的に参照・更新できます。 研究記録に記載する内容 記載すべき内容 設計思想と意思決定(なぜその設計を選択したか) 主要エンドポイントの概要(ファイルパス参照) 検証結果(パフォーマンス、エッジケース) 記載しない内容 ソースコードの全文転記 詳細な実装手順 機密情報(API キー、認証情報) 実際の研究記録例 このプロジェクトには、20 件の研究記録があります: 単一ファイル形式の例 : aoai-chat-simple.md – Azure OpenAI チャット機能 github-api-integration.md – GitHub API 統合 csv-preview-system.md – CSV プレビュー機能 ディレクトリ形式の例 : supabase-x-scheduler-v2/ – X 投稿スケジューラーの大規模リファクタリング api-generation-pipeline/ – OpenAPI 自動生成パイプライン frontend-refactoring/ – フロントエンド大規模リファクタリング フェーズ 4: 記事化の実践 記事化フェーズの目的 研究記録をもとに、 技術ブログ執筆に必要な情報を収集・整理 するフェーズです。 目的 : /docs/features/ (計画)+ /docs/research/ (検証)+ /application/ (実装)から情報を抽出 読者向けに再構成 記事構成案とコードスニペットを整理 記録先 : /docs/article/ ディレクトリ構造 docs/article/ ├── CLAUDE.md # 記事執筆ガイドライン ├── writing-style-prompt.md # 文体スタイルガイド └── {article-topic}/ # ケバブケース命名 ├── research-doc.md # 必須 - 調査資料 ├── no1-article.md # 必須 - 記事本文 ├── no2-article.md # 任意 - 続編がある場合 └── image/ # 必須 - 画像ファイル格納(.gitignore除外) ├── screenshot.png └── diagram.png 重要なルール : research-doc.md は必須(リポジトリ内容の調査結果) image/ サブディレクトリは必須( .gitignore で除外済み) ディレクトリ名はケバブケース( x-post-with-oauth , azure-functions-local-setup ) research-doc.md(調査資料)の構成 調査資料は、記事執筆の基礎資料となります。主要セクション: 記事概要 : 対象読者、目的、キーワード 参照元ドキュメント : 計画/検証/実装のファイルパス 技術スタック : 使用技術とバージョン 実装の要点 : 主要機能の概要とコードスニペット候補 技術的な課題と解決策 : 問題、解決策、参考実装 記事構成案 : H1/H2/H3 レベルの見出し構造 図表・資料 : 必要な図表のチェックリスト RAG もどき: ブログ HTML 抽出ツール RAG もどきとは? RAG(Retrieval-Augmented Generation) は、ベクトル検索で外部知識を自動取得し、AI が回答を生成する仕組みです。 このプロジェクトでは、 既存のブログ記事をローカルに保存 し、 手動で選択して 新規記事執筆時に参照する「 RAG もどき 」を構築しています。 RAG との違い : 本物の RAG: ベクトル化 + セマンティック検索(自動) RAG もどき: HTML ファイル保存 + 手動参照 シンプルですが、文体補正や既存記事との整合性確保には十分効果的です。 なぜ RAG もどきが必要か? 記事執筆時にこんな課題がありました: 既存記事と似た内容を書いてしまう 既存記事との整合性を取るのが大変 過去の記事タイトルや文体を忘れてしまう そこで、 fetch-blog-html.ts を使って 既存記事をローカルに保存 し、記事執筆時に参照できるようにしました。 fetch-blog-html.ts の機能 これは TypeScript である必要は全くありません。好きな言語で実装してください。こちらのファイルの肝となる部分は、ツールとして渡すことでトークン数を削減しながら既存のデータを保存できるという点です。 入力・出力 入力 : 環境変数 URL でブログ記事 URL を指定 # /docs ディレクトリで実行 cd /home/node/dev/docs URL="https://tech-lab.sios.jp/archives/49157" npm run fetch-blog 出力 : /docs/tools/doc/tech-lab-sios-jp-archives-49157.html 処理概要 既存のブログ記事から HTML を取得し、以下の処理を行います: 記事本文のみを抽出(不要なヘッダー、フッター、サイドバーを除去) 画像をテキスト化(alt 属性と URL を保持) 不要な属性・空白を削除 トークン数を計算 実装コード : TypeScript実装を公開しています fetch-blog-html.ts (GitHub Gist) SIOS Tech Lab ブログ用にカスタマイズされています 他のブログにも応用可能(セレクタ部分を調整) 実行結果例 実際に記事 49157(Next.js×Nest.js 型定義同期)を抽出した結果がこちらです: 出力ファイル : /docs/tools/doc/tech-lab-sios-jp-archives-49157.html (部分抜粋) <!-- ブログ記事情報: タイトル: AIと爆速開発!Next.js×Nest.js型定義同期の自動生成パイプライン構築術 | SIOS Tech. Lab URL: https://tech-lab.sios.jp/archives/49157 OGP画像: https://tech-lab.sios.jp/wp-content/uploads/2025/09/572995517b0ce827aa745786a62911c5.png 抽出日時: 2025-10-30T01:55:41.785Z --> <h1> AIと爆速開発!Next.js×Nest.js型定義同期の自動生成パイプライン構築術 | SIOS Tech. Lab </h1> <h2>初めに</h2> <p> AIと一緒に開発をするようになってから、フロントエンドとバックエンド両方を爆速で開発することができるようになって、検証が爆速で進むようになった龍ちゃんです。 </p> <p>AIが混乱しないようにプロジェクト自体を整備する方法についてお話しします。</p> <ul> <li> <a href="https://tech-lab.sios.jp/archives/49140" >Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術</a > </li> <li> <a href="https://tech-lab.sios.jp/archives/49148" >AI協働で仕様書アレルギー克服!開発時間を1週間→2日に短縮する実践法</a > </li> </ul> <!-- 画像はURLのみ保持 --> <figure> <img src="https://i0.wp.com/tech-lab.sios.jp/wp-content/uploads/2025/09/b060e7bc0582b2a27f7c9de8a921557f.png" /> (https://i0.wp.com/tech-lab.sios.jp/wp-content/uploads/2025/09/b060e7bc0582b2a27f7c9de8a921557f.png) </figure> <!-- ...中略... --> <h2>まとめ</h2> <p> 正直、これは導入してみて一番良かったなと思える点ですね。ルールをあまり追加させずにAIに生成させるコードが圧倒的にきれいになりました。 </p> 抽出された HTML の特徴 : OGP 情報(タイトル、URL、画像、抽出日時)をコメントで記録 記事の構造(見出し、段落、リスト)を完全保持 画像は URL のみ保存(Claude Code で参照可能) 不要なヘッダー、フッター、サイドバーを除去 龍ちゃんの文体(「ども!」)も維持 トークン数削減効果 実測値 (既存記事 17 件の平均): 指標 削減率 抽出による削減 40-50% 圧縮による削減 10-15% 総合圧縮率 50-60% 具体例 (記事 49157: 型安全パイプライン): 元ページ全体のトークン数: 35,420 抽出後のトークン数: 18,250(48.5%削減) 最終圧縮後のトークン数: 15,680(14.1%削減) 総削減トークン数: 19,740(55.7%削減) RAG もどきの活用方法 1. 既存記事の一覧確認 ls /home/node/dev/docs/tools/doc/ 抽出済みの記事を一覧で確認し、類似テーマの記事を探します。 2. 記事執筆時に既存記事を参照 類似テーマの既存記事を選択し、Claude Code に読み込ませます。 # プロンプト例 この記事から発展して、次の記事を考案したいので読み込んでください。 **既存記事**: [抽出済みの HTML ファイル] **文体ガイド**: /docs/article/writing-style-prompt.md **記事の位置づけ**: [シリーズ構成、関連記事との関係] 既存記事(HTML)+ 文体ガイド(writing-style-prompt.md)+ 記事の位置づけを読み込ませることで、 一貫した記事品質 と シリーズとしての連続性 を維持できます。 RAG もどきのメリット 既存記事との重複チェック 既存記事を参照することで、似た内容を書くことを防げる 文体・構成の一貫性 既存記事のスタイルを参考に、統一された記事品質を維持 トークン数削減(50-60%) 記事をローカルに保存し、コード化することでトークン数を大幅削減。Claude Code への入力が効率化される 不要な記事取得の軽減 記事化している情報をローカルに落とすことで、都度 Web から取得する手間を削減 オフライン参照 インターネット接続なしで既存記事を参照可能 将来の完全自動化への布石 システムプロンプトを育てることで、検証 → 記事化のナレッジ化まで完全自動化も夢じゃない! 文体補正: writing-style-prompt.md 文体補正システムとは? writing-style-prompt.md は、記事を執筆するための文体ガイドです。 仕組み : 既存記事から文体を抽出 : 投稿済みの記事を分析し、文体パターンをプロンプト化 プロセス化 : 新しい記事を投稿するたびに、文体ガイドをレビュー・更新 レビュー用システムプロンプトを育てる : 継続的な改善で精度向上 メリット : 一貫した記事品質の維持 AI が文体を学習し、自動的にスタイルに合った記事を生成 記事執筆時のレビュー観点が明確化 実践例: 実際の執筆プロセス 本ワークフローの実際の執筆フローを解説します。 ステップ 1: 記事のアイデアをざっくり書く まず、 書きたいことを適当でよいのでざっくり書きます 。 記事タイトル案 伝えたいポイント(箇条書き) 想定する読者 記事の位置づけ(シリーズ構成等) この段階では完璧である必要はありません。アイデアをラフに整理するだけです。 ステップ 2: 参照ドキュメントと実装を調査 ざっくり書いた内容をもとに、 参照すべきドキュメントや実装を調査 します。 調査資料(research-doc.md)に以下を整理: 参照元ドキュメント(計画、検証、実装のファイルパス) 技術スタック 実装の要点(コードスニペット候補) 技術的な課題と解決策 ステップ 3: プロンプトとして情報を渡す 記事執筆時に プロンプトとして以下を渡します : 参照記事(fetch-blog-html.ts で取得した HTML) 記事の位置づけ(シリーズ構成、関連記事との関係) writing-style-prompt.md(文体ガイド) research-doc.md(調査資料) Claude Code にこれらを読み込ませ、記事本文(no1-article.md)を執筆します。 ステップ 4: 記事本文執筆とレビュー Claude Code にプロンプトを渡し、 no1-article.md を執筆します。 執筆後のレビュー項目: 参照記事との整合性チェック 定量的データの妥当性チェック 文体チェック(writing-style-prompt.md と照合) よくある課題と解決法 課題 1: 調査資料が長すぎて AI が混乱 問題 : research-doc.md が長すぎると(2000 行以上)、AI が全体を把握しづらくなる。 解決策 : ディレクトリ形式に分割(research-doc.md, architecture.md, implementation.md) セクションごとに記事を分割(no1-article.md: 基礎編、no2-article.md: 応用編) 課題 2: 記事執筆に時間がかかりすぎる 問題 : 調査資料から記事本文への変換に時間がかかる(4-5 時間)。 解決策 : テンプレートを活用(はじめに、Before/After セクション) AI に変換を依頼(writing-style-prompt.md + Mermaid 図 + Before/After 比較) 段階的に執筆(導入 → 本編 → まとめ) ワークフロー全体の効果 定量的な効果 指標 Before After 改善率 記事執筆時間 8 時間 4 時間 50%削減 調査時間 2 時間 1 時間 50%削減 既存記事重複チェック 手動(30 分) 簡易 RAG(5 分) 83%削減 記事品質(一貫性) 60%(主観) 90%(主観) 50%向上 ※測定条件 : 中規模記事(800-1000 行)での実測値 開発者の声(龍ちゃんの実体験) 導入後の変化 : この機能を導入して、ブログを書くっていう行為がまた一つ変わりました。検証した内容からそのままブログ化できるっていうのは本当に便利で、動くコードがそのままブログに転写できるようになったのは大きいですね。 意識的な変化 : ただ、使うにあたって気づいたのが、「明確な意図を持って検証する」必要が出てきたということです。今までは頭の中でやっていた作業を、ちゃんとドキュメント化しなきゃいけない。結構頭を使うようになりました。 Before → After : 導入前 : 現象を後から眺めて「ブログ書くか」みたいな感じ 導入後 : 検証の過程を全部ドキュメント化 「何を考えてこうやってみたのか」 「実際どうなったのか」 「最初の予想と結論の違い」 これ全部ドキュメントに残さなきゃいけないので、検証を明確な意識を持って行うようになりました。 全体的な感想 : インプットもアウトプットも、AI が入ってきて変わったなと素直に思ってます。 ワークフロー全体の効果まとめ 知見が自然に蓄積 実装と同時にドキュメントが作成される 記事執筆時間 50%削減 調査資料が既に揃っている 既存記事との整合性確保 簡易 RAG(fetch-blog-html.ts)で既存記事を参照 記事品質の向上 writing-style-prompt.md で一貫した文体 記事化のハードルが下がる 「記事書こう」と思った時に、すぐに書き始められる まとめ この記事では、 検証 → 記事化ワークフローの実践方法 を解説しました。 検証 → 記事化ワークフローのポイント フェーズ 3: 研究記録(/docs/research/) 実装完了後の知見・検証結果をドキュメント化 フェーズ 4: 記事化(/docs/article/) 調査資料(research-doc.md)→ 記事本文(no1-article.md)の変換 RAG もどきシステム: fetch-blog-html.ts 既存記事をローカルに保存し、記事執筆時に参照(トークン数 50-60%削減) 文体補正: writing-style-prompt.md 一貫した記事品質を維持 効果まとめ Before After 記事執筆時間 8 時間 調査資料活用で 4 時間(50%削減) 既存記事重複チェック手動(30 分) 簡易 RAG で 5 分(83%削減) 記事品質バラバラ 文体補正で一貫性 90%(主観) 次のステップ このワークフローをさらに発展させたい方は、以下の記事もご覧ください: 関連記事シリーズ : Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 ← 本記事の前提知識 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 ← 3 フェーズ開発の実践例 本記事 : 検証 → 記事化ワークフロー(4 フェーズ目の詳細) 実装詳細・応用編 : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 ← API 自動生成の実践 今後の予定 : Claude Code× モノレポで実現する AI 協業開発環境(全体像のまとめ記事) 参考リンク 公式ドキュメント Claude Code 公式ドキュメント Cheerio 公式ドキュメント Mermaid 図記法 Markdown 記法 ここまで読んでいただき、ありがとうございました! 検証 → 記事化ワークフローを実践することで、実装完了後の知見が自然に記事化され、技術ブログの執筆が劇的に効率化されます。ぜひ、この記事を参考に、あなたのプロジェクトでも実践してみてください。 質問や感想は、コメント欄でお待ちしております。また、Twitter のほうもよろしくお願いします! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2025 年版】検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 first appeared on SIOS Tech. Lab .
アバター
はじめに 前回はDockerを利用して単体のコンテナでデータ永続化したDBコンテナを起動する方法を解説しました。 本ブログでは、Kubernetesにおけるデータ永続化について紹介します。 まず、Kubernetesと前回使用したDockerの違いについて簡潔に触れ、Kubernetesにおけるデータ永続化の仕組みやリソース、StatefulSetについて詳しく解説します。 KubernetesとDockerの違い Kubernetesのデータ永続化の解説の前に前回使用したDockerとの違いを解説します。以下の表で示す通りDockerとKubernetesは規模や役割が違います。 Dockerはアプリケーションをコンテナという箱に入れることでどのような環境でも動かせるようにします。 一方、KubernetesはDockerコンテナを大規模に管理・運用するためのプラットフォームです。 DockerとKubernetesの比較 Kubernetesにおけるデータ永続化 Kubernetesのデータ永続化はPodが削除されてもデータを保持する仕組みです。データ永続化で主に使用されるのはPV( PersistentVolume )とPVC( PersistentVolumeClaim )です。外部ストレージなどと連携をしてデータを安全に保存します。 PV PVはざっくりと説明するとストレージを抽象化したものです。正確には物理的なストレージ(AWSのディスクや社内のNFSサーバーなど)を、Kubernetesが理解できる形に「抽象化」し、紐づけたものです。PVはPodと分離されているためPodが削除されてもデータの保持ができます。 PVC PVCは開発者が具体的なストレージ要件(容量、アクセスモードなど)を宣言するためのものです。PVCをKubernetesに提出すると、Kubernetesが条件に合うPVを自動で紐付けてくれます。 コンテナDBに最適なリソース KubernetesでDBを利用する際にはリソースが使われます。リソースとは、Kubernetes上でアプリケーションやサービスを動かすために必要な計算資源や制御対象のことです。KubernetesでのリソースはPod、Service、PersistentVolumeといったKubernetes内で管理されるオブジェクトを指します。 StatefulSetは、Kubernetesでデータの一貫性やノード間の順序が求められるステートフルなアプリケーションを実行する際に最適なリソースです。特にデータベースのように一貫性や永続性が必要なケースで使用されます。Podに固有の名前を付与し順序を維持することで、複数のインスタンス間で安定した動作を可能にします。 StatefulSetとDB永続化 StatefulSetを利用することでDBの永続化が可能になります。これにはStatefulSetの二つの機能が関係しています。 まず、StatefulSetはPodに固定の一意な名前を付与する機能を持っています。これによりPodが故障して新しいPodが作成されても同じ名前が引き継がれるため、リソースの識別をすることができます。これにより、DBクラスタ内で「プライマリ(データを管理・更新する役割)」や「レプリカ(データをコピーして保存する役割)」といったノードの役割を識別しやすくなり、クラスタ構成や役割の管理が簡単になります。 次に、Podごとに自動的にPVCを作成する仕組みがあります。この機能により、各Podが個別のストレージを持つことが可能となります。これら二つの機能によってデータの永続化が可能になっています。例として、db-1という名前のPodが故障し、新しいdb-1が作成された場合でも、db-1というPodに固有のPVCが結びついているためデータの永続化が保証されます。 DeploymentとStatefulSetの違い DeploymentとStatefulSetはどちらもKubernetesでPodを管理するための機能です。 Deploymentは、KubernetesでアプリケーションのPodを管理・更新・スケーリングするためのリソースで、主にステートレスなワークロードに適しています。Deploymentは各Podに固有の名前がありません。どのPodも同じ仕事をするため、1つが故障しても別のPodが仕事を代替することができます。しかし、PodがPVCに紐づかないためデータベースのように永続的かつ固有のデータを持つワークロードには適していません。 StatefulSetは前述のとおりPodそれぞれに固有の名前があります。Podが故障しても以前と同じ名前で作成され、同じPVCに接続されるためデータの永続性が保たれます。 DeploymentとStatefulSetの比較 おわりに 今回はKubernetesにおけるデータ永続化について解説しました。 Kubernetesにおけるデータ永続化の知識を活用すると、クラウドネイティブ環境でのステートフルなアプリケーション運用をより効率的に行うことができます。特にデータベースのような一貫性が求められるワークロードをKubernetes上で運用する際には、StatefulSetや永続ボリュームの知識が不可欠です。 次回は実際にKubernetes上でコンテナDBを動かす方法を解説しますので、ぜひご覧ください。 参考文献 Kubernetes と Docker はどのように異なりますか? https://aws.amazon.com/jp/compare/the-difference-between-kubernetes-and-docker/ Kubernetesのデータ永続化に重要なオブジェクトPVとPVCについて解説します! https://www.rworks.jp/cloud/kubernetes-op-support/kubernetes-column/kubernetes-entry/29321/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post コンテナDB入門シリーズ②:Kubernetesにおけるデータ永続化の基本 first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちは、サイオステクノロジーの佐藤 陽です。 今回はAzure Functionsを利用してMCPサーバーを構築する方法についてご紹介します。 Azure Functionsを利用してMCPサーバーを構築するためのExtensionである Microsoft.Azure.Functions.Worker.Extensions.Mcp の GA版(v1.0.0) が2025年10月9日にリリースされました! 今回のブログでは、このGA版を使った実装方法を解説していきます。 MCPサーバーって何?という方 Azure FunctionsでMCPサーバーを構築したい方 preview版からGA版への移行を検討している方 といった方は是非最後までご覧ください! サンプルコード 今回解説するコードをGitHubにて公開しています。 DevContainerですぐ動くような形となっていますので、是非お試しください。 GitHubリポジトリ: azure-functions-mcp-server-sample なお、元のDevContainerとしては、しばやんさん作成の Template を利用させていただいておりますmm MCPとは MCP(Model Context Protocol) は、AIモデルが外部のツールやデータソースとやり取りするための標準化されたプロトコルです。 MCPの詳細に関しては色々なところで分かりやすく解説されているので、今回の記事では説明を割愛します。 弊社の武井さんの Youtube や、KAGのみのるんさんの 資料 などが非常に分かりやすいので、ぜひこちらご覧ください! 今回は、上記の記事でも紹介されている MCPサーバー をAzure Functions上で実装していきます。 環境構築 プロジェクトのセットアップ サンプルコードを利用するする場合は、上述のクローン手順に従ってください。 一から作成する場合は、以下の手順で進めます。 新規プロジェクトの作成 Azure Functionsのプロジェクトを新規作成する func init mcp-server --worker-runtime dotnet-isolated これにより、.NET Isolated Workerを使用したAzure Functionsプロジェクトが作成されます。 必要なパッケージのインストール MCP拡張機能パッケージをインストールする dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Mcp --version 1.0.0 注意点 GA版のMCP拡張機能を使用するには、 Microsoft.Azure.Functions.Worker のバージョンを 2.1.0以上 にする必要があります。 プロジェクトファイル( .csproj )を確認し、以下のバージョンが設定されていることを確認してください: < PackageReference Include = "Microsoft.Azure.Functions.Worker" Version = "2.1.0" /> < PackageReference Include = "Microsoft.Azure.Functions.Worker.Extensions.Mcp" Version = "1.0.0" /> 変更後パッケージの復元を行います。 dotnet restore 実装 ここまでくれば環境構築完了です。 MCPサーバーの実装を始めていきましょう! 今回作成するMCPツールについて 今回は「MCPサーバーとは何か」「どう実装するか」を理解することにスポットに当て、非常にシンプルなツールを作成します。 私が普段YouTubeで配信している「 ツール・ド・Azure 」という技術解説シリーズがあるのですが、 今回のMCPツールでは、この各回のタイトル情報を取得する機能を実装します。 MCPトリガー関数の作成 それでは、実際にMCPツールとして公開する関数を作成していきます。 ベースとして一度HTTPトリガーの関数を追加し、それをベースにMCPトリガーに修正していくのが良いかと思います。 新しいファイル TourDeAzureTrigger.cs を作成し、以下のコードを記述します。 この関数は、AIエージェントが「ツール・ド・Azureの第N回のタイトルは何?」と質問したときに、適切なタイトルを返すツールを想定します。 using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; using Microsoft.Azure.Functions.Worker.Extensions.Mcp; public class TourDeAzureTrigger { private readonly ILogger<TourDeAzureTrigger> _logger; public TourDeAzureTrigger ( ILogger<TourDeAzureTrigger> logger ) { _logger = logger; } [ Function(nameof(GetTourDeAzureTitle)) ] public string GetTourDeAzureTitle ( [McpToolTrigger( "get_tour_de_azure_title" , "Gets title about the Tour de Azure." )] ToolInvocationContext context, [ McpToolProperty ( "edition" , "The edition number." , isRequired: true )] int edition) { return edition switch { 1 => "AI Document Intelligenceに入門しよう!" , 2 => "Azure AI Searchのインデクシングに入門しよう!" , 3 => "Azure上にRAGシステムを構築しよう!" , 4 => "RAGの性能を評価しよう!" , 5 => "安全なAIアプリケーションを構築しよう" , _ => "To Be Continued..." }; } } コードの詳細解説 実装したコードの各部分について詳しく見ていきましょう。 McpToolTrigger属性 [ McpToolTrigger( "get_tour_de_azure_title" , "Gets title about the Tour de Azure." ) ] 第1引数 ( get_tour_de_azure_title ): AIエージェントが呼び出すツール名 第2引数: ツールの説明文(AIがこのツールの用途を理解するために重要) ToolInvocationContext MCPツールの呼び出しコンテキスト情報を保持 McpToolProperty属性 [ McpToolProperty( "edition" , "The edition number." , isRequired: true) ] edition : パラメータ名 "The edition number." : パラメータの説明文 isRequired: true : 必須パラメータとして設定 Program.csの設定 次に、 Program.cs でMCPツールの設定を行います。 using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var builder = FunctionsApplication.CreateBuilder(args); builder.ConfigureFunctionsWebApplication(); builder.Services .AddApplicationInsightsTelemetryWorkerService() .ConfigureFunctionsApplicationInsights(); builder .ConfigureMcpTool( nameof (TourDeAzureTrigger.GetTourDeAzureTitle)) .WithProperty( "edition" , "int" , "The edition number." , required: true ); builder.Build().Run(); ※preview版では、以下のメソッド呼び出しが必要でしたが、こちらがGA版では不要となりました。 builder.EnableMcpToolMetadata(); ConfigureMcpToolの詳細 第1引数 : 関数名(  nameof(TourDeAzureTrigger.GetTourDeAzureTitle) ) WithProperty : プロパティの詳細定義 パラメータ名 型情報 ( "int" , "string" など) 説明文 required : 必須かどうかのフラグ 動作確認 実装が完了したら、実際に動作確認を行っていきます! Azuriteの起動 Azure Functionsはローカル実行時にAzure Storageが必要です。 ローカル環境ではストレージエミュレーター「Azurite」を使用します。 VSCodeの画面で 「Azurite: Start」 を実行します Azure Functionsの起動 次に、Azure Functionsを起動します: func start 正常に起動すると、コンソールに以下のような出力が表示されます。 エンドポイントが公開されていればOKです この後、「http://localhost:7071/runtime/webhooks/mcp」のエンドポイントを利用しますので、こちらを控えておいておいてください。 Azure Functions Core Tools Core Tools Version: 4.3.0+df07acf9d837d635d1efc2c973225f4f1c8a4333 (64-bit) Function Runtime Version: 4.1042.100.25374 Found /workspaces/azure-functions-mcp-server-sample/mcp-server/mcp-server.csproj. Using for user secrets file configuration. MCP server endpoint: http://localhost:7071/runtime/webhooks/mcp MCP server legacy SSE endpoint: http://localhost:7071/runtime/webhooks/mcp/sse Worker process started and initialized. Functions: GetTourDeAzureTitle: mcpToolTrigger For detailed output, run func with --verbose flag. Postmanでの疎通確認 今回はPostmanを使って、MCPサーバーとの簡単な疎通確認を行います。 Postmanの設定手順 Postmanを起動 新しいリクエストを作成し、プロトコルとして「MCP」を選択 サーバーのURL( http://localhost:7071/runtime/webhooks/mcp )を入力し、接続ボタンを押す ツールの確認 Postmanに get_tour_de_azure_title ツールが表示されることを確認 ツールの実行 パラメータ edition に値(例: 1 )を設定 実行ボタンをクリック 期待される結果 パラメータ edition=1 で実行した場合 { "content" : [ { "type" : "text" , "text" : "AI Document Intelligenceに入門しよう!" } ] }   これでローカル環境にMCPサーバーが立ち上がり、疎通できていることが確認できました。 まとめ 今回は、Azure FunctionsでMCPサーバーを構築する方法をご紹介しました。 ただし今回は「はじめの一歩」レベルであり、LLMも絡んでいませんし、Agentic感も一切ありません。 そこで次のステップとしては、以下のような取り組みを考えています GitHub CopilotやClaude等のAIエージェントとの連携 複数のMCPツールを組み合わせた複雑なワークフローの実装 これらについては、また別の機会にご紹介していきたいと思います。 ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure FunctionsでMCPサーバーを構築するためのはじめの一歩【MCP Extension v1.0.0版】 first appeared on SIOS Tech. Lab .
アバター
この記事について 対象読者 : 前回の記事でOAuth2.0+PKCEの認証フローは理解したけど、取得したトークンをどう管理すればいいかわからない。X APIで自動投稿システムを作りたい人 ゴール : アクセストークンとリフレッシュトークンの違い、リフレッシュトークンの使い捨て性質を理解して、正しいトークン管理が実装できる X API OAuth 2.0のトークン管理をカフェの会員システムの比喩でふんわり解説。リフレッシュトークンの使い捨て性質から、クッキーvsデータベース保存の選択基準、よくある実装ミスまで初心者にもわかりやすく図解。セキュリティ対策とエラーハンドリングも網羅した実践的ガイド。 初めに ども!XのAPIを使用したアプリケーション開発にまつわるブログ執筆を爆速で仕上げ中の龍ちゃんです。最近は弊社のブログも活気がついてきて、取り残されないように頑張ってブログ執筆しています。前回は「 ふんわり始めるX API認証|OAuth2.0とPKCEを初心者向けに図解解説 」でXでのOAuth2.0認証について解説しました。今回は、X APIにアクセスするためのトークンにまつわる話を解説しようと思います。 実装に入る前にトークンの概念は理解しておいたほうが良いです!しっかり入門していきましょう。 カフェの会員システムで理解しよう 前回との接続:ラーメン屋からカフェへ 前回はラーメン屋の券売機で認証フローを理解しましたね。今回は、その続きとして「カフェの会員システム」で考えてみましょう。 【前回】ラーメン屋の券売機 1. 券売機に申請 → 本人確認 2. 引換券をもらう(認可コード) 3. 窓口で食券に交換(トークン) 【今回】カフェの会員システム 1. 会員登録完了 → 会員カードをもらう 2. 2時間限定のドリンクチケットをもらう 3. チケットでドリンクを注文 4. チケット期限切れ → 会員カードで新しいチケットをもらう カフェの流れ それでは、カフェでの1日の流れを見ていきましょう。 ここで重要なのは、 会員カードも新品に交換される という点ですね。これが今回のテーマである「使い捨て性質」です。 登場人物の整理 用語 カフェの例 説明 アクセストークン 2時間限定ドリンクチケット API呼び出しに使う短命なチケット リフレッシュトークン 会員カード 新しいチケットをもらうためのカード(使うたび交換) X API カフェのカウンター サービス提供者 この比喩を頭に入れて、それぞれのトークンを詳しく見ていきましょう。 アクセストークン:2時間限定ドリンクチケット アクセストークンでできること 結論から言うと、 アクセストークンがあればX APIにアクセスできます 。 無料版のX APIでは、以下の操作が可能です: 操作 できること Xへの投稿 ツイートを投稿する、リプライを送る 自分の情報取得 プロフィール情報、ユーザー名などを取得 権限管理 スコープで「投稿のみ」「読み取りのみ」など範囲を制限 有料版になると、投稿の分析やアナリティクス情報の取得など、より高度な機能が使えるようになりますね。今回作っているサービスはXへの投稿を自動化するものなので、無料版でも十分実現できます。 カフェの例で言うと : ドリンクチケット(アクセストークン)を見せる = API呼び出し ラテを注文(ツイート投稿)、会員情報確認(ユーザー情報取得) シンプルですよね。 有効期限:2時間 アクセストークンの有効期限は**約2時間(7200秒)**です。 00:00 OAuth認証完了 → アクセストークンA取得 ✅ │ API呼び出し可能 │ 01:00 まだ有効 ✅ │ API呼び出し可能 │ 02:00 期限切れ ❌ │ API呼び出し失敗 │ 02:01 リフレッシュトークンAで更新 🔄 │ 新しいアクセストークンB取得 ✅ │ ⚠ リフレッシュトークンAは無効化 │ 04:01 アクセストークンB期限切れ ❌ │ 04:02 リフレッシュトークンBで更新 🔄 │ 新しいアクセストークンC取得 ✅ │ ⚠ リフレッシュトークンBは無効化 │ ... この繰り返し(6ヶ月間有効) このタイムラインを見ると、リフレッシュトークンも一緒に更新されていることがわかりますよね。 なぜこんなに短いの? 「2時間って短すぎない?」と思った方もいるかもしれませんね。実は、これには重要なセキュリティ上の理由があるんです。 理由 説明 セキュリティ トークンが盗まれても、2時間しか使えない 不正利用防止 盗まれたトークンの使用期間を制限し、被害を最小化 権限変更の反映 ユーザーがアプリ連携を解除した際、速やかに無効化される ラーメン屋の食券で例えると : 食券の有効期限が短い → 盗まれても被害が少ない 毎回新しい食券をもらう → 古い食券は使えない セキュリティと利便性のバランス 短い有効期限のおかげで、セキュリティを保ちながらAPIを使えるというわけですね。 リフレッシュトークン:長期有効な会員カード リフレッシュトークンとは? リフレッシュトークン は、アクセストークンが期限切れになった際に、 ユーザーに再認証させることなく 新しいアクセストークンを取得するための特別なトークンです。 前回の記事で解説したOAuth認証のログイン画面、あれを毎回表示するのは面倒ですよね。リフレッシュトークンがあれば、ユーザーに気づかれることなく、バックグラウンドで自動的にアクセストークンを更新できるんです。 これは非常に便利な機能ですね。 リフレッシュトークンの取得方法 リフレッシュトークンを取得するには、OAuth認証時に** offline.access **という特別なスコープ(権限)を指定する必要があります。 tweet.read : ツイートの読み取り tweet.write : ツイートの投稿 users.read : ユーザー情報の取得 offline.access : ← これがないとリフレッシュトークンが発行されない! この offline.access を忘れると、リフレッシュトークンがもらえず、2時間ごとに再ログインが必要になってしまいます。これは実装時に注意すべきポイントですね。 有効期限:6ヶ月 トークン 有効期限 役割 アクセストークン 約2時間 API呼び出しに使用 リフレッシュトークン 約6ヶ月 アクセストークンの再取得に使用 リフレッシュトークンは約6ヶ月間有効なので、この期間中は自動的にアクセストークンを更新し続けることができます。 カフェの例で言うと : ドリンクチケット(アクセストークン)は2時間で期限切れ 会員カード(リフレッシュトークン)は6ヶ月間有効 カードがあれば、いつでも新しいチケットがもらえる 便利ですよね。 リフレッシュトークンの「使い捨て」性質【最重要】 ここが今回の記事で最も重要なポイントです。多くの初心者がつまずく部分なので、カフェの比喩でじっくり理解していきましょう。 重要:リフレッシュトークンは使い捨て X APIのリフレッシュトークンには、とても重要な特徴があります。それは**「使い捨て(One-time use)」**であることです。 これは、他のOAuth実装とは異なる点もあるため、特に注意が必要ですね。 カード交換の仕組み カフェでの会員カード更新シーンを詳しく見てみましょう: ユーザ ドリンクチケットが期限切れになりました。 新しいチケットをください カフェ かしこまりました。会員カードを見せてください ユーザ 古い会員カードAを渡す カフェ (以下の3つを同時に実行) ① 新しいドリンクチケットを発行 ② 新しい会員カードBを発行 ③ 古い会員カードAをシュレッダーで破棄 ユーザ 新しい会員カードBと新しいドリンクチケットを受け取る 重要なポイント3つ 1. リフレッシュトークンは1回使ったら無効になる 会員カードAを使った瞬間、そのカードは二度と使えなくなります。まるでシュレッダーにかけられたかのように、完全に無効化されるんですね。 2. 新しいアクセストークンと新しいリフレッシュトークンの両方を取得 トークン更新時には、新しいドリンクチケット(アクセストークン)だけでなく、新しい会員カード(リフレッシュトークン)も一緒にもらえます。この 両方を保存する 必要があります。 3. 古いリフレッシュトークンは二度と使えない もし古い会員カードAをもう一度使おうとすると、「このカードは無効です」とエラーになります。 なぜこんな仕組みなの? 「なんでこんな面倒なことするの?」と思った方もいるかもしれませんね。でも、これはセキュリティのための重要な仕組みなんです。 理由 説明 セキュリティ カードが盗まれても、すでに使用済みなら無効 不正検知 古いカードが使われたら「盗難された」と即座に判断できる 被害の最小化 盗まれたカードを使えないようにして、不正利用を防ぐ この仕組みのおかげで、セキュリティが大幅に向上しているんですね。 正常フローと攻撃シナリオ 正常フロー リフレッシュトークンAを使用 → 新しいアクセストークンB取得 → 新しいリフレッシュトークンB取得 → AはX APIによって無効化される リフレッシュトークンBを使用 → 新しいアクセストークンC取得 → 新しいリフレッシュトークンC取得 → BはX APIによって無効化される この繰り返しで6ヶ月間使える 攻撃シナリオ(使い捨て性質が守ってくれる) 攻撃者があなたの通信を盗聴 → リフレッシュトークンBをコピー あなたが正常にトークンBを使用 → 新しいトークンC/D取得 → Bは無効化される 攻撃者が盗んだトークンBを使おうとする → エラー「このトークンは無効です」 → X APIが不正アクセスを検知 → あなたに通知が届く 結果:攻撃者はAPIにアクセスできない! カフェの例で言うと : 会員カードが盗まれる でも、あなたがすでに新しいカードに交換済み 盗んだ人が古いカードを使おうとする 「このカードは無効です」と拒否される 店員が「不正利用の疑いあり」と気づく このように、リフレッシュトークンの「使い捨て性質」は、セキュリティを守るための重要な仕組みなんですね。 トークンの保存:2つの選択肢 トークンを取得したら、どこかに保存する必要がありますよね。保存方法は大きく分けて2つあります。 選択基準 どちらを選ぶかは、 実装するシステムの種類 によって変わります。 保存先 使うケース 特徴 クッキー ユーザーが今すぐ操作するアプリ シンプル、短期利用向け データベース バッチ処理・自動投稿システム 暗号化必須、長期利用向け 実際のプロジェクトでどちらを選ぶべきか、迷うところですよね。それぞれ詳しく見ていきましょう。 1. クッキーに保存(即時操作向け) どんな時に使う? ユーザーが「今すぐ投稿したい」とボタンを押すようなアプリケーションの場合、クッキーに保存するのが適しています。 ユーザーが「投稿」ボタンをクリック クッキーからアクセストークンを取得 X APIで投稿実行 結果を画面に表示 メリット 処理がシンプル → クッキーの存在確認だけでOK → 有効期限の管理が自動 実装が簡単 → 複雑な暗号化処理が不要(HTTPOnly Cookieを使えば基本的に安全) デメリット ブラウザから参照可能 → 開発者ツールで見える → セキュリティリスクがある ブラウザを閉じたら消える可能性 → 有効期限設定次第 実装パターン フロント側で実装する場合、シンプルに作るなら アクセストークンだけ保存 しても大丈夫です。期限切れになったら、もう一度認証フローを実行してもらえばOK。 より高度に実装するなら、リフレッシュトークンも保存して自動更新することで、ユーザーにログイン画面を表示せずに済みますね。 カフェの例 : クッキー = ポケットにチケットを入れる 手軽に使えるけど、落としたら危ない 短時間の利用に向いている 2. データベースに保存(バッチ処理向け) どんな時に使う? 予約投稿や自動投稿など、ユーザーの操作とは別に定期実行するシステムの場合、データベースに保存する必要があります。 毎朝9時に自動投稿 予約した時間にツイート 定期的にフォロワー情報を取得 メリット 長期間保存できる → ブラウザを閉じても大丈夫 事前更新で確実性UP → リフレッシュトークンで事前に新しいアクセストークンを準備 → バッチ処理実行時にエラーが出ない デメリット セキュリティリスクが高い → データベースにアクセスできる人全員がトークンを見られる → 暗号化が必須 実装が複雑 → 暗号化・復号化の処理が必要 セキュリティ対策 データベースに保存する場合、 必ず暗号化 してから保存する必要がありますね。 環境変数に暗号化キーを保存 トークンを暗号化してからデータベースに保存 使用時に復号化してAPIに送信 もしそのまま保存してしまうと、データベースにアクセスできる人全員がトークンを見られてしまい、不正利用のリスクが高まります。これは避けたいところですね。 カフェの例 : データベース = 金庫にチケットを保管 暗号化 = 金庫の鍵(誰も開けられない) 安全だけど、実装に手間がかかる どちらを選ぶべき? ケース 保存方法 理由 即時投稿アプリ クッキー ユーザーが今すぐ使う 予約投稿システム データベース 後で自動実行する バッチ処理 データベース 常に有効なトークンを確保 システムの要件に合わせて、適切な保存方法を選びましょう。 よくある誤解・アンチパターン リフレッシュトークンの実装でよくある誤解を3つ紹介しますね。これらを避けることで、正しくトークン管理ができるようになります。 誤解1:リフレッシュトークンは何度も使える これは最も多い誤解です。私も最初はこう思っていました。 間違った理解 「会員カードは何度も使えるから、1回保存しておけばずっと使える」 正しい理解 「会員カードは1回使ったら新品に交換される。 古いカードは即座に破棄される」 何が起こるか : 古いリフレッシュトークンを使い続けようとすると、2回目以降は「このトークンは無効です」というエラーが返ってきます。 カフェの例 : 古い会員カードAを使う → 新カードBをもらう また古いカードAを使おうとする → 「このカードは無効です」と拒否される カードは1回限りの使い捨て! 誤解2:アクセストークンだけ更新すればいい これもよくある実装ミスですね。 間違った実装 リフレッシュトークンAでアクセストークン更新 新しいアクセストークンBだけ保存 古いリフレッシュトークンAをそのまま保持 → 次回、Aを使おうとすると失敗! 正しい実装 リフレッシュトークンAでアクセストークン更新 新しいアクセストークンB + 新しいリフレッシュトークンBの両方を保存 古いトークンAは破棄 → 次回、新しいBを使えば成功! カフェの例 : 新しいドリンクチケットだけもらって、古い会員カードを使い続けようとする 新しいドリンクチケットと新しい会員カードの両方をもらう 重要なポイント : トークン更新時は、 両方のトークンを必ず保存 してください。これを忘れると、次回の更新で失敗しますね。 誤解3:トークンは永遠に有効 これもよくある勘違いです。 間違った理解 「一度認証すれば、ずっと使い続けられる」 正しい理解 「アクセストークン:2時間で期限切れ」 「リフレッシュトークン:6ヶ月で期限切れ」 → 6ヶ月後は再認証が必要 6ヶ月後はどうなる? リフレッシュトークンも6ヶ月で期限切れになります。その場合は、ユーザーにもう一度OAuth認証画面からログインし直してもらう必要がありますね。 【6ヶ月後のタイムライン】 0日目 認証完了 ✅ ↓ 30日目 問題なく動作中 ✅ ↓ 180日目 リフレッシュトークン期限切れ ⚠ ↓ 181日目 「再度ログインしてください」と通知 ユーザーが再認証 新しいトークンセット取得 ✅ カフェの例 : 会員カードにも有効期限がある(6ヶ月) 期限が切れたら、もう一度会員登録が必要 新しいカードをもらえば、また6ヶ月使える このように、トークンには必ず有効期限があることを理解しておきましょう。 まとめ お疲れ様でした!今回は、X APIのトークン管理について、カフェの会員システムの比喩で解説してきました。 今回学んだこと トピック ポイント アクセストークン 2時間有効、API呼び出しに使用する「ドリンクチケット」 リフレッシュトークン 6ヶ月有効、 使い捨て性質 がある「会員カード」 保存方法 クッキー(即時操作) vs データベース(バッチ処理) 最重要ポイント(再掲) リフレッシュトークンを使うときは、この3つを必ず覚えておいてくださいね: リフレッシュトークンは1回使ったら無効になる トークン更新時は、アクセストークンとリフレッシュトークンの両方が新しくなる 古いリフレッシュトークンは即座に無効化される → これがセキュリティの要 カフェの例で言えば : 会員カードを使うと、新しいドリンクチケットと新しい会員カードの両方がもらえる 古い会員カードはシュレッダー行き これで盗難されても安心 関連記事 ふんわり始めるX API認証|OAuth2.0とPKCEを初心者向けに図解解説 参考資料 X API Documentation OAuth 2.0 RFC 6749 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post X API OAuth 2.0トークン管理入門|使い捨てリフレッシュトークンの仕組みを図解 first appeared on SIOS Tech. Lab .
アバター
この記事について 対象読者 : OAuth2.0という名前は聞いたことがあるけど、具体的にどうすればよいかわからない。でも、X API経由で投稿してみたい人 ゴール : OAuth2.0とPKCEの概念を理解して、実装に入る準備ができる X API OAuth2.0とPKCEの仕組みをラーメン屋の比喩でふんわり解説。認証コードフローからcode_verifierの安全な生成方法、CSRF対策まで初心者にもわかりやすく図解で紹介。実装前の概念理解に最適 はじめに ども!X APIを活用したアプリ開発をしている龍ちゃんです。最近、新卒エンジニア向けに技術研修の資料を作っていたんですが、やっぱり認証周りってちゃんと整理しないとダメだなと痛感しています。 この記事では、OAuth2.0について特に詳しくない方でも、X APIを実装できるようになるための基礎知識を解説しますね。ちゃんと理解したほうが良い概念ではありますが、まずは「ふんわり理解」から始めましょう。 しっかり勉強したい方は、以下の2冊がおすすめです: 雰囲気でOAuth2.0を使っているエンジニアがOAuth2.0を整理して、手を動かしながら学べる本[2023年改訂版] OAuth、OAuth認証、OpenID Connectの違いを整理して理解できる本 [2024年改訂版] 弊社のブログであれば合わせて読むなら以下の連載がお勧めです! 【連載】世界一わかりみの深いOAuth入門 〜 その1:OAuthってなに? 〜 【連載】世界一わかりみの深いOAuth入門 〜 その2:アクセストークンとリフレッシュトークン 〜 【連載】世界一わかりみの深いOAuth入門 〜 その3:OAuthを認証に使うことの危険性 〜 【連載】世界一わかりみの深いOAuth入門 〜 その4:stateパラメーターによ る CSRF対策 〜 こちらの内容を活用して作成したアプリの記録は「 AIチャットで話すだけ!X予約投稿を完全自動化するシステム構築術 」で解説しています。 なぜOAuth2.0が必要なの? X APIを使ってツイートを投稿したり、情報を取得したりするには、 「このアプリは信頼できる」「このユーザーが許可している」ことを証明する必要があります 。 昔ながらの方法(ID/パスワード直接入力)の問題点 もしID/パスワードをアプリに直接入力する方式だと: アプリがパスワードを保存できてしまう パスワードが漏洩したら全てのサービスで不正利用される アプリに全権限を渡すことになる(投稿だけしたいのに、DM読み取りもできる) OAuth2.0を使うと パスワードを教えずに権限だけ渡せる 必要な権限だけを限定できる(投稿のみ、読み取りのみなど) いつでも権限を取り消せる まずは、X APIを使用するために必要な基本的な画面を見てみましょう。 X API OAuth2.0の認証画面 – SIOSTechLabアプリへのアクセス許可を求める画面のスクリーンショット このような画面を表示させてアカウント連携を行い、 トークン(アクセストークン・リフレッシュトークン)を取得します 。そのトークンを使用してX APIを実行するわけですね。 OAuth2.0をラーメン屋で例えると OAuth2.0の仕組みを理解するために、ラーメン屋で例えてみましょう。 登場人物 あなた : APIを使いたいユーザー(Resource Owner) アプリ : あなたが使うアプリケーション(Client) 券売機 : X の認証システム(認可サーバー / Authorization Server) ラーメン屋 : X APIサービス本体(リソースサーバー / Resource Server) 全体の流れ あなたは人気のラーメン屋(X)でラーメン(APIリソース)を食べたいです。でも、このラーメン屋は直接注文できません。専用アプリ経由でしか注文できないルールなんですね。 ステップ1: アプリで注文開始 「このアプリでラーメン食べたい!」とアプリに伝えます。 ステップ2: 券売機で申し込み(認可リクエスト) アプリが券売機(認可サーバー)に「このラーメンを注文したいです」と申請書を出します。 ステップ3: 本人確認(ログイン・認可) 券売機が「あなた本人ですか?」と確認します。顔認証(Xへのログイン)で本人確認を済ませますね。 ステップ4: 引換券をもらう(コールバック) 本人確認が完了すると、券売機から**一時的な引換券(認可コード: code)**をもらいます。 ステップ5: 引換券→食券に交換(トークンリクエスト) 引換券を持ってアプリが窓口に行き、**本物の食券(アクセストークン)**に交換します。 ステップ6: ラーメンゲット!(リソースアクセス) 食券を渡してラーメン(APIデータ)を受け取ります。 なぜこんなに面倒なの? もしID/パスワードを直接アプリに教える方式だと、悪意のあるアプリがパスワードを盗んで好き放題できてしまいますよね。 OAuth2.0なら: 「このアプリにラーメン注文の権限だけあげる」という 権限の制限 ができる パスワードをアプリに教えなくていい いつでも権限を取り消せる(食券を無効化できる) 必要なエンドポイント この仕組みを実装するには、最小で 2つのエンドポイント が必要です: 1. 認証URL発行エンドポイント 券売機への申請書を出すためのエンドポイントですね。ユーザーをX の認証画面にリダイレクトします。 2. コールバックエンドポイント 券売機から発行された引換券(code)を受け取るエンドポイント。このcodeを使ってアクセストークンを取得します。 OAuth2.0の詳細な流れ(シーケンス図) OAuth2.0認可コードフローのシーケンス図 – ユーザー、クライアントアプリ、認可サーバー、リソースサーバー間の通信フローを図解 各ステップの詳細 ステップ ざっくり理解 正確な流れ 2. 認可リクエスト 認可リクエストのURLをください 認可の開始: クライアントがユーザーを認可サーバー(AS)へ誘導し、「この**権限(Scope)**を借りたい」とASに伝える 3〜4. ログイン・認可 その用途で使用することに合意します。本人確認は行いました 権限の付与: ユーザーがAS上でログインし、クライアントアプリが要求した権限(Scope)の付与を許可する 5. コールバック 本人確認了承しました。きちんと処理が行われていますね 認可コードの受け渡し: ASがクライアントアプリのコールバックURLに 認可コード(code)とstate を渡す。これは一時的で、まだサービスにアクセスするためのキーではない 6〜9. トークンリクエスト 具体的にサービスにアクセスするためにキーの取得を行いますね トークンへの交換: クライアントがASに対し、受け取った code を提示して、サービスアクセス用の本物のキーである アクセストークン との交換を要求する 10. リソースリクエスト キーを使ってサービスにアクセスを行います リソースへのアクセス: 取得した アクセストークン を提示して、リソースサーバー(RS)に保護された情報(リソース)へのアクセスを要求する X APIで必要な重要パラメータ 1. Scope(スコープ): 「何ができる権限か」 ラーメンの例で言うと「ラーメンだけ注文できる券」なのか「ラーメン+餃子まで注文できる券」なのか、という 権限の範囲 ですね。 X APIでよく使うScope Scope 説明 tweet.read ツイートを読む権限 tweet.write ツイートを投稿する権限 users.read ユーザー情報を読む権限 follows.read フォロー情報を読む権限 follows.write フォロー/アンフォローする権限 重要 : 必要な権限だけを要求するのがセキュリティ上重要です。投稿だけしたいのに全権限を要求すると、ユーザーに警戒されちゃいますよね。 2. redirect_uri(リダイレクトURI): 「引換券を受け取る住所」 認可サーバーが「引換券(code)をどこに送ればいいの?」を指定するのがredirect_uriです。 重要なポイント X Developer Portalで事前に登録したURLと 完全一致 する必要がある 末尾の / まで含めて一致させる 本番環境: https://yourdomain.com/callback 開発環境: http://localhost:3000/callback (ローカル開発用) よくあるエラー ❌ Developer Portalに登録: <https://example.com/callback> ❌ コードで指定: <https://example.com/callback/> → 末尾のスラッシュが違うのでエラー! ✅ Developer Portalに登録: <https://example.com/callback> ✅ コードで指定: <https://example.com/callback> → 完全一致でOK! 3. state: 「偽造防止のおまじない」 CSRF攻撃を防ぐためのランダムな文字列です。認可リクエストで送ったstateと、コールバックで返ってきたstateが一致するか確認します。 ラーメンの例で言うと「整理番号」のようなものですね。自分の整理番号と違う引換券が返ってきたら怪しいですよね。 // state生成の例 const state = crypto.randomUUID(); // "550e8400-e29b-41d4-a716-446655440000" sessionStorage.setItem('oauth_state', state); // コールバックで確認 const returnedState = new URLSearchParams(window.location.search).get('state'); const savedState = sessionStorage.getItem('oauth_state'); if (returnedState !== savedState) { throw new Error('State不一致!CSRF攻撃の可能性あり'); } PKCEが必要な理由 さて、ここまでの仕組みでも一応動きますが、実は セキュリティリスク があります。 認可コード(code)が盗まれたら? もし通信途中でcodeが盗聴・盗難された場合、以下のような攻撃が可能になります: OAuth2.0認可コード横取り攻撃のシーケンス図 – PKCEなしの場合に攻撃者が認可コードを盗聴するリスクを図解 具体的には: 正規のクライアントアプリで認可リクエストを送る 通信途中で攻撃者がcodeを盗聴 攻撃者が盗んだcodeを使ってトークンリクエストを送る 認可サーバーは正しいcodeなのでトークンを発行してしまう 攻撃者がユーザーのリソースにアクセスできてしまう ラーメン屋で例えると PKCEなしの場合(危険!) : 券売機で引換券だけもらう 引換券を持って窓口に行けば食券がもらえる → 途中で引換券を盗まれたら、誰でも食券に交換できちゃう! PKCEありの場合(安全!) : 券売機で注文する時に「愛言葉」を決める 券売機には「愛言葉のヒント」だけ伝える 引換券をもらう 窓口で引換券と「愛言葉の本物」を見せる 店員が「ヒントと愛言葉が一致するか」確認してから食券を渡す → 引換券を途中で盗まれても、愛言葉がないと食券には交換できない! PKCEの仕組み PKCE(Proof Key for Code Exchange)は、 認可リクエストしたクライアントとトークンリクエストを行ってきたクライアントが同一であることを証明 するための仕組みです。 主要な登場人物 用語 説明 ラーメンの例 code_verifier あなただけが知っている秘密の文字列 愛言葉の本物 code_challenge code_verifierをハッシュ化したもの 愛言葉のヒント PKCEの流れ 事前準備 : クライアントアプリがcode_verifier(秘密の文字列)を生成 認可リクエスト : code_challenge(ハッシュ化したもの)を送信 コールバック : codeを受け取る トークンリクエスト : codeと一緒にcode_verifier(元の文字列)を送信 検証 : 認可サーバーがcode_verifierをハッシュ化して、最初のcode_challengeと一致するか確認 code_verifierのよくある誤解 危険な実装:固定の愛言葉を使い回す code_verifierを 固定文字列や予測可能な値にしてしまう のは、とても危険な実装です。 // ❌ 危険な例:固定文字列 const code_verifier = "my_secret_verifier_2024"; これでは、PKCEの意味がなくなってしまいます。 ラーメン屋で例えると 悪い例 : 店員:「愛言葉は?」 あなた:「いつも『ラーメン大好き』って決めてるんだ!」 盗聴者:「次回も『ラーメン大好き』って言えば食券もらえるな…」 良い例 : 店員:「愛言葉は?」 あなた:「今回は『X7mK9pQ2vR8nL3zA』!」(毎回ランダムに自動生成) 盗聴者:「今回の愛言葉聞いたけど、次は全然違うやつ使ってる…予測できん!」 正しい実装:機械的にランダム生成 code_verifierは必ず暗号学的に安全な方法で、毎回ランダムに生成する必要があります。 // ✅ 良い例:毎回異なる、予測不可能な愛言葉を生成 function generateCodeVerifier() { const array = new Uint8Array(32); // 32バイトのランダムデータ crypto.getRandomValues(array); // 暗号学的に安全な乱数 return base64UrlEncode(array); // URL安全な文字列に変換 } const code_verifier = generateCodeVerifier(); // 例: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" なぜ機械的に生成するの? 理由 説明 人間は弱い 人が考える「ランダム」は実はパターンがある(誕生日、好きな言葉など) 十分な長さ 推奨は43〜128文字。短いと総当たりで破られる 毎回違う 同じ愛言葉の使い回しを防ぐ 予測不可能 暗号学的に安全な乱数生成器( crypto.getRandomValues )を使う X APIでの推奨仕様 OAuth2.0/PKCEの仕様(RFC 7636)では: 長さ : 43〜128文字 文字種 : A-Z , a-z , 0-9 , , . , _ , ~ (Base64URL) 生成方法 : 暗号学的に安全な乱数生成器 code_challengeの計算方法 愛言葉(code_verifier)から、ヒント(code_challenge)を作ります。これはSHA-256ハッシュ関数を使って計算しますね。 ポイント 一方向性 : code_challengeからcode_verifierを逆算することは 不可能 (ハッシュ関数の一方向性) 検証方法 : 認可サーバーは「受け取ったcode_verifierをハッシュ化したら、最初のcode_challengeと一致するか」だけ確認できる セキュリティまとめ 攻撃者の視点で考える 引換券(code)を盗聴した! → でも愛言葉(code_verifier)がわからないから食券に交換できない ヒント(code_challenge)は見えてる! → でもハッシュ化されてるから、元の愛言葉は逆算できない 前回の愛言葉を盗聴した! → 毎回ランダムに変わるから、次回は使えない よくある実装ミス code_verifier編 // ❌ ダメな例1:固定文字列 const code_verifier = "my_app_verifier"; // ❌ ダメな例2:短すぎる const code_verifier = Math.random().toString(36).substring(7); // 7文字程度 // ❌ ダメな例3:予測可能 const code_verifier = `verifier_${Date.now()}`; // タイムスタンプは予測可能 // ❌ ダメな例4:弱い乱数 const code_verifier = Math.random().toString(36); // Math.random()は暗号学的に安全ではない // ✅ 良い例:暗号学的に安全なランダム生成 const code_verifier = generateCodeVerifier(); // 前述の関数 redirect_uri編 // ❌ ダメな例:末尾のスラッシュが不一致 // Developer Portal登録: https://example.com/callback const redirect_uri = "https://example.com/callback/"; // 末尾に / あり // ✅ 良い例:完全一致 const redirect_uri = "https://example.com/callback"; state編 // ❌ ダメな例:stateの検証を忘れる const code = new URLSearchParams(window.location.search).get('code'); // stateの確認をしていない! // ✅ 良い例:stateを検証 const returnedState = new URLSearchParams(window.location.search).get('state'); const savedState = sessionStorage.getItem('oauth_state'); if (returnedState !== savedState) { throw new Error('State不一致!CSRF攻撃の可能性あり'); } X API での具体的な設定 Developer Portalで設定すること Appの作成 https://developer.x.com/en/portal/dashboard にアクセス 新しいAppを作成 OAuth 2.0の有効化 App設定画面で「User authentication settings」を編集 OAuth 2.0を有効にする Callback URLの登録 開発環境(Ngrok必要!): http://localhost:3000/callback 本番環境: https://yourdomain.com/callback 重要 : 末尾のスラッシュまで含めて正確に登録 Permissionsの設定 Read: ツイート読み取りのみ Read and Write: ツイート読み取り+投稿 Read and Write and Direct Messages: DM含む全権限 まとめ この記事で学んだこと OAuth2.0の必要性 : パスワードを教えずに権限だけ渡せる仕組み 全体の流れ : ラーメン屋の例で理解する認可フロー PKCEの重要性 : codeの盗聴・盗難を防ぐ仕組み code_verifierの生成 : 機械的にランダム生成することの重要性 重要パラメータ : scope, redirect_uri, stateの役割 実装時の注意点 code_verifierは毎回ランダム生成(43文字以上) 暗号学的に安全な乱数生成器を使用 redirect_uriは完全一致させる stateでCSRF対策 必要最小限のscopeを要求 次のステップ この記事でOAuth2.0とPKCEの概念は理解できました!次は実際に実装してみましょう: X Developer Portalでアプリを作成 認証URL発行エンドポイントの実装 コールバックエンドポイントの実装 トークンを使ってAPI実行 実装編の記事もお楽しみに! 参考資料 推奨書籍 雰囲気でOAuth2.0を使っているエンジニアがOAuth2.0を整理して、手を動かしながら学べる本[2023年改訂版] OAuth、OAuth認証、OpenID Connectの違いを整理して理解できる本 [2024年改訂版] 公式ドキュメント X API Documentation OAuth 2.0 RFC 6749 PKCE RFC 7636 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post ふんわり始めるX API認証|OAuth2.0とPKCEを初心者向けに図解解説 first appeared on SIOS Tech. Lab .
アバター
はじめに ども!9月から開発していたシステムがようやく稼働・運用できるようになって、ひと段落した龍ちゃんです。コツコツ開発していたものがやっと形になったので一安心ですね。 今回完成したのは、以前の記事「 Claude API×GitHub Actions完全自動化でコスト60%削減!ブログ投稿システム構築術 」で紹介したブログ宣伝システムの 続編 となるシステムです。前回はブログURLから投稿文を自動生成してデータベースに保存する仕組みを作りましたが、今回はそのデータを使って、 X(Twitter)への予約投稿を完全自動化 しました。 僕はですね、社内でX投稿の担当をしていまして…毎日の投稿予約作業が地味に大変だったんですよね。コピペして、予約画面開いて、日時設定して…の繰り返し。これ、なんとか楽にならないかなぁと思っていたわけです。 そこで作ったのが、 AIチャットで話しかけるだけで予約投稿が完結するシステム です! 皆さんも、X投稿の運用で似たような悩み、ありませんか? こんな悩み、ありませんか? 皆さん、X投稿の運用って、地味に大変じゃないですか? 私の場合、1日3件投稿できればいい方でした。投稿内容は自動的に生成されるようになっているのですが、以下の手順での「手動投稿作業」での「3件」がけっこう曲者でして… 手動予約の現実 Xを開く 投稿文をコピペ 予約画面を開く 日時を設定 投稿ボタンをクリック これで1件です。目安にして、1件の予約に 5〜6回のボタン操作 が必要なんですよね。 1日3件なら、単純計算で 15回以上のボタン操作 。毎日やるとなると…めんどくさい! そして何より困るのが、 気づいたら忘れてる こと。仕事に集中してコード書いてたら、1週間まるっと投稿してない、なんてこともありました。コード書いてる方が楽しいし、ついつい投稿作業を後回しにしちゃうんですよね(笑) さらに、ブログ記事がどんどん増えていくので、宣伝したい内容も増える一方。気づいたら「あれ、このブログまだ宣伝してない!」なんてことも。 まとめて1週間分を予約できればいいんですけど、単純作業の繰り返しは地味にストレスなんですよね… 解決策 – AIチャットで完結する予約投稿システム そこで作ったのが、 AIチャットで話しかけるだけで予約投稿が完結するシステム です! ビフォー・アフター Before 龍ちゃん手動 GitHubで投稿文ファイルを探す(2分) 投稿文をコピー Xを開く コピペして予約画面へ 日時を設定して投稿 ↓ 1件あたり約2分 × 10件 = 約20分 After AIチャット 「明日の昼にAIカテゴリから10件予約して」 とチャットに入力 ↓ 約10秒で完了! 削減できた時間 : 20分 → 10秒( 98%の時短 ) システムの核心機能 このシステムの最大の特徴は、 自然言語で指示するだけ という手軽さです。 「明日の昼に」 → 自動的に日時を計算 「AIカテゴリから」 → データベースを検索 「3件予約して」 → 一括で予約作成 GitHubを開く必要も、Xの予約画面を操作する必要もありません。チャットで指示を出せば、あとはAIが全部やってくれます。 実際の使い方を見てみよう 百聞は一見にしかず! 実際のチャットのやり取りを見てみましょう。実際のアプリのデモを録画しました。内容的に丁寧にレコーディングしたので、デモとしては若干長めですが、実際の使い勝手がよくわかると思います。 使用例: 1週間分をまとめて予約 下記はイメージになります。 龍ちゃん AIカテゴリからランダムに10件取得して、 明日から順番に連続で登録してください AIチャット ✓ カテゴリ「AI」で検索しました ✓ 投稿候補を10件見つけました 【投稿候補】 ChatGPTの新機能を試してみた(パターンA) プロンプトエンジニアリング入門(パターンB) AIで業務効率化した事例(パターンA) Claude Codeの実践活用術(パターンC) GitHub Copilotのベストプラクティス(パターンA) …(以下5件) これらを明日から順番に予約しますか? 龍ちゃん はい、朝9:00から順番に登録してください AIチャット ✓ 予約を作成しました! 【予約完了】 1/28(火) 9:00 ChatGPTの新機能を試してみた 1/29(水) 9:00 プロンプトエンジニアリング入門 1/30(木) 9:00 AIで業務効率化した事例 1/31(金) 9:00 Claude Codeの実践活用術 2/1(土) 9:00 GitHub Copilotのベストプラクティス …(以下5件) 週間カレンダーで確認できます! ポイント: 対話しながら調整できる このシステムの良いところは、 AIと対話しながら調整できる こと。普通のチャット感覚で指示できるので、操作に迷うことがありません。 パターン1: 未投稿記事だけを選ぶ あなた: 「直近投稿したやつ以外で、AIカテゴリから5件」 AI: 「未投稿の記事を5件抽出しました」 パターン2: 投稿パターンを変える あなた: 「この記事、パターンBで投稿したい」 AI: 「パターンBに変更しました」 パターン3: カテゴリ・ハッシュタグを統合する あなた: 「"AI開発"カテゴリと"AI"カテゴリを統合して」 AI: 「カテゴリを統合しました。今後は"AI"に統一されます」 主な機能 このシステムには、X投稿を楽にするための機能がいろいろと詰まっています。 カテゴリ・ハッシュタグで検索 ブログ記事は自動的にカテゴリとハッシュタグで整理されています。「AIカテゴリから」「#初心者向け のハッシュタグで」など、自然な言葉で検索できるんです。 重複したカテゴリやハッシュタグも、チャット上で統合・整理できるので、運用しながら綺麗に管理できるのも嬉しいポイントですね。 自然言語で日時指定 「明日の昼」「来週月曜の朝」「今日から3日連続で」など、自然な表現で日時を指定できます。AIが自動的に正確な日時に変換してくれます。 投稿時間は、 1日4回の固定時間 (9:00/12:00/15:00/21:00)から選べます。読まれやすい時間帯を考慮した設定です。 週間カレンダーで予約確認・自動投稿 予約した投稿は、週間カレンダーで一覧表示されます。いつ、どの記事が投稿されるのか、一目で確認できます。 予約した日時になると、自動的にXに投稿されます。投稿の成功・失敗も記録されるので、安心して任せられます。 投稿パターンの選択 各ブログ記事には、3パターン(A/B/C)の投稿文が事前にデータベースに保存されています。同じ記事でも、違う表現で複数回投稿できます。 (投稿文の生成方法については 前回の記事 で解説しています) 導入効果 10月初旬から本格運用を開始して、約半月が経過しました。すでに 約50件の投稿 をこのシステムで自動化しています。 数値で見る効果 以前は月に50件ペースで投稿していました。手動運用だと、この量は正直かなりキツかったんです… 時間削減効果 1件あたりの作業時間 : 2分 → 0秒 10件の予約作業 : 約20分 → 約10秒( 98%削減 ) 月間削減時間 : 約100分(1時間40分) 年間削減時間 : 約20時間( まる1日分の作業時間削減! ) 継続性の向上 手動運用時代は、忙しいときに1週間まるっと忘れることもありました。でも今は、 月曜日に1週間分をまとめて予約 しておけば、あとは自動で投稿されるので投稿忘れがゼロになりました。 体感としての効果 数値以上に大きいのが、 精神的な負担の軽減 です。 「今日の投稿、まだやってない…」というプレッシャーから解放されました。コード書いている時も、「投稿作業しなきゃ」と気にする必要がなくなったんです。 データベースに保存されている記事は、チャットで指示するだけで楽に登録できるので、 投稿運用のストレスがほぼゼロ になりました。 今の課題は「投稿内容をどう増やすか」に移行しています。つまり、 投稿作業そのものは完全に解決した ということですね! こんな人におすすめ このシステムは、以下のような方に特におすすめです: ブロガー・コンテンツクリエイター : ブログ記事の執筆に集中したい。投稿作業は自動化したい SNS担当者 : 定期的な投稿でブランド認知を高めたいが、手動運用は大変 エンジニア・技術ブロガー : 技術で課題を解決したい。自動化に興味がある 特に「 投稿作業に時間を取られるより、コンテンツ作成に集中したい 」という方にピッタリです! システムの裏側(ざっくり技術紹介) ここからは、このシステムがどんな技術で動いているのか、ざっくりと紹介します。 システム全体の構成 このシステムは、すべて Azure上 で構築されています。 シンプルな構成ですが、それぞれにこれまでの学びや工夫が詰まっています。 リソース名 役割 リンク Azure Static Web Apps Next.js 15フロントエンドのホスティング。週間カレンダーUIと投稿予約画面を提供。 https://learn.microsoft.com/azure/static-web-apps/ Azure Web App NestJS 11 APIバックエンド。AIチャット処理、X API連携、予約管理を担当。 https://learn.microsoft.com/azure/app-service/ Azure Functions Timer Triggerで6時間ごとに起動し、予約投稿を自動実行。 https://learn.microsoft.com/azure/azure-functions/ Azure OpenAI Service Function Callingで自然言語からの指示を理解し、適切な処理を実行。 https://learn.microsoft.com/azure/ai-services/openai/ Azure Key Vault Xのクライアントキー・Firebase認証情報などのシークレットを安全に管理。 https://learn.microsoft.com/azure/key-vault/ Firestore ブログ記事データ、投稿文パターン、予約情報を保存。暗号化されたX認証トークンも管理。 https://firebase.google.com/docs/firestore 1. AIチャット機能 – Function Calling チャットで自然言語の指示を理解できるのは、 Azure OpenAI ServiceのFunction Calling を活用しているからです。 26個の関数を用意して、「カテゴリで検索」「予約作成」「日時計算」などの処理を自動的に選択・実行してくれます。NestJSのDI(依存性注入)でうまく管理しているのがポイントです。 → 詳細は「NestJSで実現するAI Function Calling」で解説予定! 2. OAuth認証とセキュリティ Xに投稿するには、OAuth認証が必要です。取得した認証トークンをそのままデータベースに保存するのは危険なので、 暗号化・復号化の仕組み を実装しています。 データベースには暗号化された文字列だけが保存され、使用時に環境変数を使って復号化します。 ふんわり始めるX API認証|OAuth2.0とPKCEを初心者向けに図解解説 X API OAuth 2.0トークン管理入門|使い捨てリフレッシュトークンの仕組みを図解 → 詳細は「X OAuth認証とトークン暗号化」で解説予定! 3. 予約投稿の自動実行 X APIには予約投稿機能がありません(ここは!ちょっと文句がある!!)。そこで、 Azure FunctionsのTimer Trigger を活用しました。 1日4回(9:00/12:00/15:00/21:00)に定期的に起動し、データベースに予約があればX APIを実行する仕組みです。タイムゾーン処理やパフォーマンス最適化にも工夫があります。 → 詳細は「Azure FunctionsでX予約投稿を実現」で解説予定! 4. インフラ管理 AzureリソースはすべてBicepテンプレートで管理しています。APIキーやシークレットはAzure Key Vaultで安全に管理。 必要なAPIキーを登録すれば、 すぐに同じ環境を構築できる ようになっています。 X APIの制約を技術で解決 一番の技術的な見どころは、 「X APIに予約投稿機能がない」という制約をTimer Triggerで解決した 点です。 制約があるからこそ、工夫のしがいがありますよね。この辺りの実装テクニックは、技術解説シリーズで詳しく紹介していきます! AIと共に開発する ちなみに、このシステムの開発には Claude Code を活用しました。AIとペアプログラミングをしながら開発を進めることで、短期間で効率的にシステムを構築できました。 仕様を決めるのは人間、実装はAIに任せる——この役割分担がうまく機能したプロジェクトです。 まとめ AIチャットで完結するX予約投稿システムを作ったことで、以下を実現しました: 時間削減 : 月間約100分の作業時間削減(年間20時間) 簡単操作 : 自然言語で指示するだけ、わずか10秒で予約完了 完全自動化 : 予約した日時に自動投稿、投稿忘れゼロ 精神的負担軽減 : 「投稿しなきゃ」のプレッシャーから解放 これまで20分かかっていた予約作業が、わずか10秒に短縮されました。 次回以降の技術解説シリーズ 今回は「何ができるか」に焦点を当てましたが、今後は 技術的な実装詳細 を解説していきます! X OAuth認証とトークン暗号化 OAuth 2.0フロー Firebaseでの暗号化保存 Azure FunctionsでX予約投稿を実現 Timer Triggerの活用 タイムゾーン処理 パフォーマンス最適化 NestJSで実現するAI Function Calling 26関数の管理手法 NestJSのDI活用 SSEストリーミング実装 Azure FunctionsでMCP Serverを構築 MCP Tools実装 Experimental Bundle Supabase連携 各記事で、実際のコード例とともに詳しく解説していきます。 さいごに X投稿の自動化は、自分の作業を楽にするために始めたプロジェクトでしたが、結果的に大幅な時間削減と精神的負担の軽減につながりました。 単純作業はAIに任せて、人間は創造的な仕事に集中する —— これが理想的な働き方だと思います。AIと共に開発することで、より短期間で、より質の高いシステムを作れる時代になりました。 人間は贅沢なもので、だんだん面倒になるもののハードルが上がっていきます。こちらの運用から発展して、Xの運用そのものを完全に自動化することを検討しだしました。 ブログやコンテンツを定期的に発信している方は、ぜひ参考にしてみてください! このシステムについて質問や「こんな機能ほしい!」などのご意見があれば、ぜひコメント欄で教えてください。基本的にはマーケのお姉さんがクライアントとして、実装を頑張っています!技術詳細記事でコード例も公開予定です。 * 次回からの技術解説シリーズもお楽しみに! 参考リンク・公式リファレンス Azure関連 Azure OpenAI Service : 公式ドキュメント Azure Functions Timer Trigger : 公式リファレンス Azure Key Vault : 公式ドキュメント Bicep(Azure IaC) : 公式ドキュメント X(Twitter)API関連 X API v2 : 公式ドキュメント OAuth 2.0 認証 : 公式ガイド Post Tweet API : APIリファレンス フレームワーク・ライブラリ関連 NestJS : 公式ドキュメント Firebase : 公式ドキュメント Supabase : 公式ドキュメント ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AIチャットで話すだけ!X予約投稿を完全自動化するシステム構築術 first appeared on SIOS Tech. Lab .
アバター
はじめに:なぜDevContainerとuvの組み合わせなのか ども!新卒のスムーズな開発のために環境を作っていたりする龍ちゃんです。直近でPythonのDevContainer環境を作る機会がありまして、 今までpipで環境を作っていました 。どうせなら、モダンな環境で作り直して渡そうと考えていました。モダンなPython開発環境に求められる要素は何でしょうか? 再現可能な環境 : チーム全員が同じ環境で開発できる 高速なパッケージ管理 : ストレスのない依存関係のインストール 拡張性 : 必要に応じてツールを追加できる柔軟性 本記事では、DevContainerとuvを使った高速で再現可能なPython開発環境の構築方法を、ステップバイステップで解説します。 使用する技術スタック DevContainer : VS Codeの開発コンテナ機能 uv : Rustで実装された高速Pythonパッケージマネージャー(pipの10-100倍速) Microsoft公式イメージ : 安定性と互換性が保証された基盤 DevContainer + uv環境の全体構成 今回構築する環境のディレクトリ構成は以下の通りです: project-root/ ├── .devcontainer/ │ ├── Dockerfile # Python + Node.js環境の定義 │ ├── compose.yml # Dockerコンテナの設定 │ └── devcontainer.json # VS Code DevContainer設定 ├── pyproject.toml # Pythonプロジェクト設定 ├── src/ # ソースコード └── tests/ # テストコード ステップ1: Microsoft公式イメージベースのDockerfile Python環境にuvを追加したシンプルなDockerfileを作成します。 # .devcontainer/Dockerfile # Microsoft公式のPython DevContainerイメージを使用 FROM mcr.microsoft.com/devcontainers/python:3.11 # 追加ツール(必要に応じて) USER vscode RUN curl -LsSf https://astral.sh/uv/install.sh | sh ENV PATH="/home/vscode/.local/bin:${PATH}" # 作業ディレクトリの設定 WORKDIR /home/vscode/python # コンテナを起動したままにする CMD ["sleep", "infinity"] ポイント: Microsoft公式イメージで安定性を担保 非rootユーザー(vscode)で安全に実行 uv事前インストールで初回から高速セットアップ ステップ2: Docker Composeでコンテナ定義 # .devcontainer/compose.yml services: python: build: context: . dockerfile: ./Dockerfile tty: true volumes: - type: bind source: ../ target: /home/vscode/python restart: unless-stopped # ports: # - "8000:8000" # FastAPI等のWebアプリ用 ポイント: 単一サービス構成でシンプル ボリュームマウントでホストと同期 ステップ3: DevContainer設定 // .devcontainer/devcontainer.json { "name": "uv-python", "service": "python", "dockerComposeFile": "./compose.yml", "workspaceFolder": "/home/vscode/python", "customizations": { "vscode": { "extensions": [ "ms-python.python", "ms-python.vscode-pylance", "ms-python.black-formatter" ], "settings": { "python.defaultInterpreterPath": "/home/vscode/python/.venv/bin/python", "python.testing.pytestEnabled": true, "python.testing.pytestArgs": ["-q"], "editor.formatOnSave": true, "python.formatting.provider": "black" } } } } ステップ4: VS CodeでDevContainerを起動して開発開始 1. プロジェクトをDevContainerで開く VS Codeでプロジェクトディレクトリを開く コマンドパレット( Cmd/Ctrl + Shift + P )を開く 「Dev Containers: Reopen in Container」 を選択 初回起動時はDockerイメージのビルドに数分かかります コンテナが起動すると、VS Codeのウィンドウ左下に「Dev Container: uv-python」と表示されます。 2. ターミナルを開いて確認 VS Codeのターミナルを開き、環境を確認します。 # Pythonのバージョン確認 python --version # uvが利用可能か確認 uv --version # 作業ディレクトリの確認 pwd # /home/vscode/python が表示されるはず ステップ5: uvでプロジェクト初期化 # プロジェクトの初期化(pyproject.toml生成) uv init # 仮想環境の作成と有効化 uv venv source .venv/bin/activate # 依存関係の追加 uv add fastapi uvicorn pydantic uv add --dev pytest black flake8 mypy httpx # 反映 uv sync --all-groups pyproject.tomlの例 [project] name = "python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [ "fastapi>=0.120.1", "pydantic>=2.12.3", "uvicorn>=0.38.0", ] [dependency-groups] dev = [ "black>=25.9.0", "flake8>=7.3.0", "httpx>=0.28.1", "mypy>=1.18.2", "pytest>=8.4.2", ] [tool.black] line-length = 100 target-version = ['py311'] [tool.mypy] python_version = "3.11" strict = true ignore_missing_imports = true [tool.flake8] max-line-length = 100 extend-ignore = ["E203", "W503"] [tool.pytest.ini_options] 注意 : [dependency-groups] は比較的新しい機能です。古いツールとの互換性が必要な場合は、代わりに [project.optional-dependencies] を使用することもできますが、uvを使う場合は [dependency-groups] の使用が推奨されます。 セットアップの確認 最後に、セットアップが正しく完了したことを確認します。 # インストールされたパッケージの確認 uv pip list # Blackでフォーマットが動作するか確認 black --version # mypyで型チェックが動作するか確認 mypy --version # pytestが動作するか確認 pytest --version 動作確認:DevContainerでFastAPIアプリを構築 簡単なアプリケーションの作成 セットアップが完了したら、実際にコードを書いて動作確認してみましょう。 # src/main.py from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"message": "Hello, uv DevContainer!"} @app.get("/items/{item_id}") def read_item(item_id: int): return {"item_id": item_id} アプリケーションの実行 # FastAPIアプリケーションの起動 uvicorn src.main:app --reload --host 0.0.0.0 --port 8000 ブラウザで http://localhost:8000 にアクセスすると、動作確認できます。 フォーマットとLintの実行 保存時の自動フォーマットに加えて、手動でも実行できます。 # Blackでコードをフォーマット black src/ # Flake8でLintチェック flake8 src/ # mypyで型チェック mypy src/ # すべてをまとめて実行 black src/ && flake8 src/ && mypy src/ テストの作成と実行 # tests/test_main.py from fastapi.testclient import TestClient from src.main import app client = TestClient(app) def test_read_root(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"message": "Hello, uv DevContainer!"} # テストの実行 uv run pytest # カバレッジ付きでテスト実行 uv run pytest --cov=src tests/ uvコマンドのクイックリファレンス 開発中によく使うuvコマンドをまとめました。 # パッケージの追加 uv add requests # 通常の依存関係 uv add --dev pytest-cov # 開発用依存関係 # パッケージの削除 uv remove requests # パッケージの削除 # 依存関係の更新 uv lock # ロックファイルの更新 uv sync # 依存関係の同期 # pip互換コマンド uv pip install requests # pipコマンドと同じように使える uv pip list # インストール済みパッケージ一覧 uv pip freeze # requirements.txt形式で出力 # 仮想環境の管理 uv venv # 仮想環境の作成 uv venv .venv --python 3.11 # Pythonバージョン指定 # requirements.txtとの連携 uv pip install -r requirements.txt # requirements.txtからインストール uv pip freeze > requirements.txt # requirements.txtの生成 DevContainer + uv環境でよくあるエラーと解決法 問題1: uvコマンドが見つからない # PATHを確認 echo $PATH # PATHに追加(必要な場合) export PATH="$HOME/.local/bin:$PATH" echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc source ~/.bashrc # uvの再インストール curl -LsSf https://astral.sh/uv/install.sh | sh 問題2: Pythonインタープリターが認識されない VS Codeのコマンドパレットから 「Python: Select Interpreter」 を選択し、 /home/vscode/python/.venv/bin/python を指定します。 または、devcontainer.jsonで指定されているパスを確認: "python.defaultInterpreterPath": "/home/vscode/python/.venv/bin/python" 問題3: パーミッションエラーが発生する # 現在のユーザーを確認 whoami # vscode であることを確認 # ディレクトリの所有者を確認 ls -la /home/vscode/python # 必要に応じて権限を修正 sudo chown -R vscode:vscode /home/vscode/python 問題4: pytestがsrcをモジュールとして認識せず import に失敗する 症状: from src.main import app で ModuleNotFoundError などの収集時エラー 原因: Python パスにプロジェクトルート( . )が含まれず、 src がモジュール解決できない 対処: pyproject.toml に Python パスを付与(推奨) [tool.pytest.ini_options] pythonpath = ["."] 併用可: src/__init__.py を追加(空でOK) 検証: uv run pytest -q が成功すること。 python -c "import sys; print(sys.path[0])" でルートが入っていることを確認 まとめ 今回は、DevContainerとuvを組み合わせた爆速Python開発環境の構築方法を見てきました。 この環境で得られる3つのメリット 圧倒的な速度 : uvによってpipの10-100倍速でパッケージ管理 完全な再現性 : Dockerでチーム全員が同じ環境を共有 VS Code統合 : 拡張機能やデバッグがそのまま使える 特に、「ローカル環境を汚したくない」「チーム開発で環境差異に悩んでいる」という方には、ぜひ試していただきたいセットアップです。 実際にこの環境を使うことで、環境構築のストレスから解放され、コーディングに集中できるようになります。セットアップは最初の1回だけ。あとはチーム全員が同じ環境で快適に開発できますよ。 皆さんも、ぜひDevContainer × uv環境でPython開発にチャレンジしてみてください!質問や感想は、コメント欄でお待ちしております。 もしこの環境でNodeを使いたい(Claude CodeやCodexなどのnpmでインストールする系のツール)を使う場合は、こちらを参考にしてください。 参考リンク uv公式ドキュメント Microsoft DevContainers DevContainers仕様 VS Code DevContainers拡張機能 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post DevContainerとuvで構築する爆速Python開発環境|VS Codeセットアップ手順 first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちは、サイオステクノロジーの小沼 俊治です。 今回は、AIを活用した RAG アプリケーションの仕組みを無料で体験できるハンズオン環境 を用意しました。 このハンズオンでは、 LangChain 、LLM に PC 内のローカルで動かす Open source LLM 、ベクトルデータベースの Milvus といったオープンソースのプロダクトを使用します。 学習は、JupyterLab(Jupyter Notebook) の Notebook 形式でステップごとに用意した教材 を使って進めます。 教材はステップ1から5までで構成しており、ステップ1から4では、外部に存在するデータの収集からデータを活用した回答生成まで、RAG を構成するモジュールを作成しながら一連の流れを学習します。集大成となるステップ5では、これまでに作成したモジュールを組み合わせて Web アプリケーションを構築し、アプリケーションの利用を通じて RAG を体験します。 LLM はローカルで動かすので、少々(PC の)スペックは必要ですが、トークン数などのコストを気にする必要はありませんので、思い存分、体験と学習に挑んで頂けると思っています。 構成概要 ハンズオン環境の構成 筆者が動かした際の主な構成要素は以下の通りです。 Windows 11 Professional WSL 2.5.9.0 Ubuntu 24.04.3 LTS Docker Engine 28.5.1 ハンズオンを構成する環境は以下の通りです。 教材を活用しながら学習を進める環境として、JupyterLab で LangChain を使う Python 環境を Ubuntu に構築します。 RAG の処理で必要となる Embedding や Open source LLM などのモデルは、Huggin Face からダウンロードして取得します。 拡張検索に必要となるデータを蓄積するベクトルデータベースには、Milvus を「milvus-standalone」、「milvus-etcd」、「milvus-minio」コンテナで構築します。 Milvus をビジュアル的に管理できる Web UI 管理ツールの Attu も「milvus-attu」コンテナで構築します。 環境構築や各種設定に使用するそれぞれのファイルは、以下の GitHub リポジトリで公開しています。 https://github.com/Toshiharu-Konuma-sti/hands-on-rag-with-langchain $ tree ~/handson/hands-on-rag-with-langchain/ hands-on-keycloak/ |-- container/ …… 「環境構築」章でコンテナ作成で使う素材 | |-- docker-compose-attu.yml | : | |-- setup/ …… 「環境構築」章でツール準備の環境準備に必要な素材 | |-- SETUP_HANDS-ON.sh | : | `-- try-my-hand/ …… 「ハンズオン実施」章で教材を進める環境 |-- cmd01-before_python_virtual_env.sh |-- cmd02-start_jupyterlab.sh | |-- data/ …… ハンズオンでベクトル化する元データの素材 | `-- recurrent_navi_tyo.xlsx | |-- lesson/ …… ハンズオンで利用するステップごとの教材 : : RAG の仕組み 教材でハンズオンを始める前に、教材を構成する各ステップの元となる RAG の仕組みを理解します。 仕組みを表したイラスト アニメーション版(流れを表現) 静止画版(全体像を表現) 仕組みの流れを説明 図中の1から2はベクトルデータベースに類似検索に利用するデータの蓄積フェーズを意味し、 図中の3から9はベクトルデータベースに蓄積されたデータを類似検索で活用する応用フェーズを意味します。 日々の経済活動を通じて、以下をはじめとするデータが企業のシステムに溜まります 販売する商品の在庫管理情報や売上データ 経済取引を記録する会計データ 企業に関わる人材を把握する社員情報や顧客情報 企画提案、商品説明、および企業説明等で作成されたドキュメントファイル など 企業に溜まったデータを検索に活用するために、以下をはじめとする加工を施しながらベクトル化して蓄積します 検索効率が向上するサイズにデータを細切れに分割するチャンキング ベクトルデータベースで類似検索が出来るように数値ベクトルに変換するエンベディング 必要に応じて検索効率を向上させるため欠損値の削除や補完、値形式を揃えるクレンジング 日々の業務活動を進めるに当たり不明点があれば質問を投げかけます 投げかけられた質問をエンベディングしてベクトルデータベースに対して類似検索します 投げかけられた質問に関連するデータを類似検索結果として戻します LLMに回答を生成を依頼するために、依頼向けテンプレートに質問と類似検索結果を付与してプロンプトを作成します 質問に回答するためにプロンプトを用いてLLMに回答案の作成を依頼します LLMが生成された回答案を戻します LLMから戻された回答案を整形して質問者に回答を戻します 事前準備 Hugging Face のアカウント準備 Hugging Face より Embedding Model や Open source LLM を取得して利用します。取得には事前に、アカウント登録、Access Token 発行、および Model の利用申請を済ませておきます。 Hugging Face – The AI community building the future. Hugging Face Top Page Embedding Model 例 Open source LLM 例 アカウント作成 Hugging Face アカウントを持っていない場合、アカウントを用意します。アカウントは無料で作成できます。 Hugging Face トップページの右上の「Sing up」から作成を開始します。 途中 CAPTCHA 認証を通り、登録するメールアドレスとパスワードを入力します。 登録完了まで画面遷移の指示に従いながら登録を進めます。 Access Token取得 Embedding Model や Open source LLM では取得に Access Token を必要とするモデルが存在します。それらを利用する際には、ハンズオン開始前に発行と値の確保を済ませておきます。 Hugging Face の右上にあるユーザアイコンをクリックすると表示するメニューから「Settings」を選択します。 左ペインの一覧から「Access Token」を選択し、新たに発行する場合には「+ Create new token」ボタンをクリックします。 モデルの利用であれば「Read」権限を選択してから「Token name」に任意の名前を入力し、「Create token」ボタンをクリックして Token を発行します。Token の値は後で確認することはできないので、発行時に必ずメモしておきます。 利用規約に同意が必要な Open source LLM の利用申請 Embedding Model や Open source LLM の中には利用規約に同意を必要とするモデルが存在します。それらを利用する際には、ハンズオンの開始前に利用申請を済ませておきます。 モデルの紹介ページで利用規約への同意が必要な場合はその旨が述べられており、「Expand to review and access」で規約の全文を展開表示をして利用規約を確認します。 規約を最後まで読み進めると、名前、所属と利用目的の入力を求められる場合はそれらを入力してから、「Agree and access repository」ボタンをクリックして同意します。 同意するとモデルの紹介ページに戻りますが、同意の受付状況を確認するために、画面右上のユーザアイコンをクリックすると表示するメニューから「Settings」を選択します。 左ペインの一覧から「Gated Repositories」を選択し、同意したモデルの一覧から「Request Status」の値を確認します。 Request Status が「ACCEPTED」になれば、該当のモデルは利用できます。 環境構築 WSL環境の構築 Windows PC の場合には、 以下手順を参考に WSL と Linux ディストリビューション(Ubuntu)環境を用意します。 初期環境構築: WSL 環境 on Windows 10 Docker環境の構築 コンテナ環境を使うため、以下手順を参考に Ubuntu へ Docker Engine 環境を用意します。 初期環境構築: Docker Engine on Ubuntu GitHub からハンズオン用のリポジトリ取得 ハンズオンを進めるための環境構築用の設定ファイル、スクリプトや教材を含んだリポジトリを GitHub からダウンロードして取得します。 本章ではコンソールを用いてハンズオンのフォルダ領域を作成して作業を実施します。 $ mkdir -p ~/handson/ $ cd ~/handson/ 「 $ git clone 」コマンドで本ハンズオン用のリポジトリを取得します。 $ git clone https://github.com/Toshiharu-Konuma-sti/hands-on-rag-with-langchain.git $ cd hands-on-rag-with-langchain/ コンテナ構築スクリプトの実行 本章ではコンソールを用いて以下のディレクトリで作業を実施します。 $ cd ~/handson/hands-on-rag-with-langchain/container/ コンテナ構築用に用意してあるスクリプトを実行して、ハンズオン環境の各種コンテナを構築します。 $ ./CREATE_CONTAINER.sh コンテナが構築されてから info オプションを付けてスクリプトを実行すると、ハンズオンに必要なアプリケーションの URL などを表示することができます。 $ ./CREATE_CONTAINER.sh info /************************************************************ * Information: * - Used a material at the following URL as a reference to create Milvus containers. * - https://milvus.io/docs/ja/install_standalone-docker-compose.md * - Access to Attu (Web admin tool for Milvus) with the URL below. * - http://localhost:8000 ***********************************************************/ Attu コンテナが稼働するのでブラウザでアクセスします。 http://localhost:8000/ なお、コンテナ構築スクリプトで実行する内容は以下を参照してください。 コンテナ構築スクリプトの解説 ツール整備スクリプトの実行 本章ではコンソールを用いて以下のディレクトリで作業を実施します。 $ cd ~/handson/hands-on-rag-with-langchain/setup/ ハンズオンで使うツールを整備するために用意してあるスクリプトを実行します。 $ ./SETUP_HANDS-ON.sh なお、ツール整備スクリプトで実行する内容は以下を参照してください。 ツール整備スクリプトの解説 ハンズオン実施 ハンズオンは Python 言語環境で JupyterLab を使って進めます。まずは JupyterLab の実行環境を準備するため、コンソールを用いて以下のディレクトリで作業を実施します。 $ cd ~/handson/hands-on-rag-with-langchain/try-my-hand/ JupyterLab で教材の実施 Python 仮想環境のアクティベート 「.venv」名でPython 仮想環境を作成します。 $ python3 -m venv .venv Python 仮想環境の領域にあたる「 .venv/ 」ディレクトリができたことを確認します。 $ ls -laF : drwxr-xr-x 7 hoge hoge 4096 Jan 14 12:15 .venv/ : source コマンドで Python 仮想環境に入ります(仮想環境をアクティブにします)。この後は、次章「 JupyterLab の起動 」に進みハンズオンを開始します。 $ source .venv/bin/activate (.venv) $ 仮想環境がアクティブになるとプロンプトの先頭に括弧で環境名が表示されます(例: (.venv) ) ハンズオンが終わった際は、ブラウザで JupyterLab を閉じて「 deactivate 」コマンドを実行して Python 仮想環境から抜けます。 (.venv) $ deactivate $ なお、Python 仮想環境の作成は該当フォルダにある「 cmd01-before_python_virtual_env.sh 」でも実行できるようにしてあります。 $ ./cmd01-before_python_virtual_env.sh : * Next, enter the command below to go to the python virtual environment!! source .venv/bin/activate $ source .venv/bin/activate (.venv) $ JupyterLab の起動 Python 仮想環境がアクティブな状態で JupyterLab をインストールします。 (.venv) $ pip install jupyterlab JupyterLab を起動するコマンドを実行します。 (.venv) $ jupyter lab コマンドを実行するとコンソールに起動ログが流れ始めます。JupyterLab の起動準備が整うと流れていたログが止まり、起動するための URL が出力されます。 : [I 2025-01-14 12:34:04.899 ServerApp] Jupyter Server 2.15.0 is running at: [I 2025-01-14 12:34:04.899 ServerApp] http://localhost:8888/lab?token=d7488a5d324a41c9685cc2e298c5f16d7def9ddf02e50a3b [I 2025-01-14 12:34:04.899 ServerApp] http://127.0.0.1:8888/lab?token=d7488a5d324a41c9685cc2e298c5f16d7def9ddf02e50a3b [I 2025-01-14 12:34:04.899 ServerApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [C 2025-01-14 12:34:05.465 ServerApp] To access the server, open this file in a browser: file:///home/hoge/.local/share/jupyter/runtime/jpserver-60018-open.html Or copy and paste one of these URLs: http://localhost:8888/lab?token=d7488a5d324a41c9685cc2e298c5f16d7def9ddf02e50a3b http://127.0.0.1:8888/lab?token=d7488a5d324a41c9685cc2e298c5f16d7def9ddf02e50a3b 上記ログ例では 「 http://localhost:8888/lab?token=d7488a5d324a41c9685cc2e298c5f16d7def9ddf02e50a3b 」 が起動する URL に該当します。ただし、コマンド実行ごとに token が異なるので、必ずコンソールに出力される URL を起動に利用します。 起動ログに出力された URL にアクセスしてブラウザで JupyterLab を起動します。 なお、JupyterLab のインストールから起動は該当フォルダにある「 cmd02-start_jupyterlab.sh 」でも実行できるようにしてあります。 (.venv) $ ./cmd02-start_jupyterlab.sh : [I 2025-01-12 00:59:20.643 ServerApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). To access the server, open this file in a browser: file:///home/hoge/.local/share/jupyter/runtime/jpserver-72570-open.html Or copy and paste one of these URLs: http://localhost:8888/lab?token=612c96228db1af7175d0c00764360c8a0bbe8ee8322c2291 http://127.0.0.1:8888/lab?token=612c96228db1af7175d0c00764360c8a0bbe8ee8322c2291 : JupyterLab で教材利用 これから示す JupyterLab の使い方を参考に、ステップ1から順番に用意している教材を進めます。 左ペインにあるフォルダアイコンの「File Browser」をクリックしてファイル一覧を表示し、ルート階層から「 lesson/ 」フォルダ配下にアクセスします。 「 lesson/ 」フォルダ配下に rag-step01 ~ rag-step05 のファイル名で始まるステップごとの教材ファイルがあるので、ハンズオンを進めるステップのファイルをダブルクリックして教材をアクティブにします。(最初の教材である「ステップ1」をアクティブにした例) 教材を表示したら左ペインにある目次アイコンの「Table of Contents」をクリックして、ステップの章構成を表示しながら教材を上から順に読み進めます。 教材を読み進めていく過程でソースコードのセルに到達したら、教材上部にある再生アイコンの「Run this cell and advance」(もしくは、セル内で[Ctrl] + [Enter])をクリックして、セル内のソースコードを実行してハンズオンを進めます。 各教材を上から順番に進めていき最終章まで到達したら、そのステップの体験学習は終了です。次のステップに進みながら、ステップ1から5までの各教材を進めることで、RAG の仕組みを実際に体験していきます。 ステップごとの教材内容 ステップごとに用意してある教材でハンズオンできる内容を説明します。 ステップ1 教材の Notebook は以下 GitHub にて確認できます。 rag-step01-excel_to_vectordb.ipynb 該当のステップでは、 RAG に必要な類似検索で利用するベクトルデータベースの環境を整えることを目的に、構造化データとして用意した Excel ファイルをベクトル化してベクトルデータベースに保存する過程を経験します。 ステップ2 教材の Notebook は以下 GitHub にて確認できます。 rag-step02-search_from_vectordb.ipynb 該当のステップでは、 ひとつ前のステップで構造化データを登録したベクトルデータベースから、サンプルのクエリを投入して類似検索を実行する過程を経験します。 ステップ3 教材の Notebook は以下 GitHub にて確認できます。 rag-step03-llm_template.ipynb 該当のステップでは、 質問者から投げかけられたクエリーと類似検索で得られた類似情報を使って、LLM に回答案の作成を依頼するために必要なテンプレートを準備する過程を経験します。 ステップ4 教材の Notebook は以下 GitHub にて確認できます。 rag-step04-retriever_and_generator.ipynb 該当のステップでは、 ここまでに経験してきた類似検索と準備したテンプレートを活用して、Retriever と Generator を実装する過程を経験します。 ステップ5 教材の Notebook は以下 GitHub にて確認できます。 rag-step05-web_ui_to_chat_with_llm.ipynb 該当のステップでは、 ステップ2以降で経験してきたナレッジを活用して、Web UI の簡易的な RAG アプリケーションの構築を経験します。 Appendix ハンズオン環境の構築や設定手順で利用した各種スクリプトや設定ファイルの実装内容について解説します。 コンテナ構築スクリプトの解説 スクリプト「 CREATE_CONTAINER.sh 」で実行する内容を説明します。 Milvus 構築用の YAML ファイル取得と加工 ベクトルデータベースを構成する Milvus のコンテナは、以下公式情報からアレンジした手順で構築します。 Run Milvus with Docker Compose (Linux) | Milvus Documentation Milvus の構築は、「 Milvus の GitHub リポジトリ 」で公開している「docker-compose.yml」(コンテナ定義)を取得して利用します。 $ wget \ https://github.com/milvus-io/milvus/releases/download/v2.5.2/milvus-standalone-docker-compose.yml \ -O docker-compose-milvus.yml Milvus 提供のコンテナ定義は、ボリュームがバインドマウント方式で管理に root 権限を必要とするため、編集して名前付きボリューム方式に変更します。 services: etcd: : volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd + - milvus-etcd:/etcd : minio: : volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data + - milvus-minio:/minio_data : standalone: : volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus + - milvus-data:/var/lib/milvus : + volumes: + milvus-etcd: + milvus-minio: + milvus-data: Web UI 管理ツール構築用の YAML ファイル用意 Milvus 提供のコンテナ定義は、 Web UI の管理ツールは含まれないので、管理ツールの Attu はコンテナ定義をオリジナルに作りました。 docker-compose-attu.yml コンテナの構築 用意した「docker-compose-milvus.yml、docker-compose-attu.yml」で一気に構築します。 $ docker-compose \ -f docker-compose-milvus.yml \ -f docker-compose-attu.yml \ up -d 全てのコンテナが稼働(STATUS = Up)しています。 $ docker ps -a CONTAINER ID IMAGE ... STATUS ... NAMES 4b7aadb871bb zilliz/attu:v2.4.12 ... Up 3 minutes ... milvus-attu 62a25fc50463 milvusdb/milvus:v2.5.2 ... Up 3 minutes (healthy) ... milvus-standalone 612a4a6c4a66 minio/minio:RELEASE.2023-03-20T20-16-18Z ... Up 3 minutes (healthy) ... milvus-minio bf1624b0e410 quay.io/coreos/etcd:v3.5.16 ... Up 3 minutes (healthy) ... milvus-etcd ツール整備スクリプトの解説 スクリプト「 SETUP_HANDS-ON.sh 」で実行する内容を説明します。 Python 環境の整備 Python 言語環境でハンズオンを進めるため、パッケージ管理や Python 仮想環境などが使えるように Python 環境を整えます。 各種モジュールをパッケージ管理できるように pip をインストールします。 $ sudo apt install -y python3-pip 仮想環境を扱えるように venv モジュールをインストールします。 $ sudo apt install -y python3-venv 新たに使いたい Open source LLM の追加実装手順 AI スタートアップ各社が日々しのぎを削り LLM 製品開発が進んでいる状況下で、2024年の年末には新たな LLM として「DeepSeek」が話題になるなど、本ハンズオンの教材を使って新たな Open source LLM を試しに使ってみたくなることがあると思います。そのような場合に、教材に実装されいない Open source LLM を追加で組み込む実装手順を説明します。 事前準備 何はともあれ、新たに試すため追加で組み込みたい Open source LLM の「ダウンロード先(入手先)」と「プロンプトの書式」が必要となります。今回の題材は「DeepSeek」を追加した場合の手順を説明しますが、PCで動かすには大きなパラメータ数の Open source LLM を動かすことはできないので、PC でも動作しそうなパラメータ数が小さく作られた蒸留モデルを探して情報を揃えます。 これにはインターネットをくまなく検索してコツコツと情報を収集して、追加実装するための情報を整理して用意します。 ダウンロード先(入手先)は以下 URL です。 lightblue/DeepSeek-R1-Distill-Qwen-1.5B-Multilingual · Hugging Face プロンプトの書式は、まだ最良の書式か自信が無い状況ですが、現時点でかき集めた情報として以下の書式で進めます。 <|begin▁of▁sentence|> あなたは親切で、礼儀正しく、誠実で優秀な日本人のアシスタントです。 context以下に箇条書きでお伝えする情報を使用してuserからの質問に回答してください。 context: {context} <|User|>{question} <|Assistant|> Step 3 でテンプレートクラスを追加 Open source LLM に回答の生成を依頼するにはテンプレートが必要となるため、追加する Open source LLM 用のテンプレートクラスを追加実装します。 教材 Step 3 にて「1. テンプレート生成」の「 【定義】LLM別のテンプレ実装」配下でテンプレートクラスを管理しています。一番最後に存在するテンプレートクラスのソースコードセルをアクティブにすると、セル内側の右上に「Inser a cell below」アイコンが表示するのでクリックして、タイトル用のセルとソースコード用のセルの合計2つのセルを追加します。 追加した2つのセルのうち上側のセルのセルタイプを「Markdown」にして Open source LLM 名を入力します。 下側のセルに他のテンプレートクラスを参考に、追加する Open source LLM 用のテンプレートクラスのソースコードを新たに実装します。 テンプレートクラスには以下4つのメソッドを実装します def get_template_for_use_retriever(self): 類似検索を使う(Open source LLM へ送るプロンプトへ類似検索で取得した関連情報を載せる)場合のテンプレートを実装します。 def get_template_for_not_retriever(self): 類似検索を使わない(Open source LLM へ送るプロンプトへ類似検索で取得した関連情報を載せない)場合のテンプレートを実装します。 def extract_answer_from_response(self, response):(self, response): Open source LLM から得た回答が生成されたレスポンスから、ユーザへ返却する回答文を抽出する処理を実装します。 def get_additional_template_for_conversation(self):y 一度 Open source LLM から回答を得た後に、継続で Open source LLM へ会話をする場合に、前回のレスポンスに問い合わせを追加するテンプレートを実装します。 Step 3 で Open source LLM リストに追記 Open source LLM 管理一覧に、追加する Open source LLM を Hugging Face で公開されている Open source LLM 名で追記します。 追加する Open source LLM に対して、Hugging Face で公開されている Open source LLM 名を取得します。 教材 Step 3 にて「2. OpenLLM一覧作成」の「 【定義】MyOpenLlmList Class」で利用する Open source LLM の一覧を管理しているため、ソースコード内の配列実装の最後の要素に追記します。 Step 3 でテンプレートクラスの組み込み テンプレートの実装確認を実施するにあたり、先の手順で作成したテンプレートクラスのインポートとインスタンスの生成を実装します。 教材 Step 3 にて「3. テンプレート実装確認」の「対象のLLMとテンプレ選定」でテンプレートクラスのロードとインタンス生成を管理しているため、追加する Open source LLM についてソースコードに実装します。 追加する Open source LLM を試すにはモデル名の取得実装箇所で、追加した Open source LLM が定義されている配列要素数を指し示すように変更します。 Step 3 の実装がここまでできたら、あとは Step 3 を先頭から全セルを実行してテンプレートの出来具合を確認します。回答結果が今一つと思う場合には、テンプレートクラスに書いたテンプレートを見直して再度実行を繰り返しテンプレートの品質を上げていきます。 Step 4 でテンプレートクラスの組み込み Step 4、および Step 5 にて追加する Open source LLM を活用するにあたり、先の手順で作成したテンプレートクラスのインポートとインスタンスの生成を実装します。 教材 Step 4 にて「2. 生成: ②.Generator」の「 【定義】Generator Class」でテンプレートクラスのロードとインタンス生成を管理しているため、追加する Open source LLM についてソースコードに実装します。 Step 4 の実装がここまでできたら、あとは Step 4 を先頭から全セルを実行して Generator クラスの実装具合を確認します。 Step 5 の Web UI で追加した Open source LLM を使えるようにする Step 5 の Web UI で追加する Open source LLM を利用するにあたり、該当の Open source LLM をメモリへロードする対象として指示します。 教材 Step 5 にて「2. 生成: Generator」の「 【生成】OpenLLM一覧」でインスタンス生成する際の引数で Web UI で使う Open source LLM を管理しているため、追加した Open source LLM の配列要素を指定するように改修します。 Step 5 の実装がここまでできたら、あとは Step 5 を先頭から全セルを実行して Web UI の実装具合を確認します。 PC 性能別のモデル処理時間比較 ローカルでモデルを利用した処理は、PC 環境の性能差により処理時間に差が生じるので計測して比較してみました。 計測処理:教材「 Step03: LLM Template 作成 > 3. テンプレート実装確認 > テンプレの実装確認」章のセルに実装されているソースコードを利用します。 処理内容:検証用に用意した固定の質問と類似検索結果を基に Open source LLM に回答生成を依頼します。 利用した Open source LLM: google/gemma-2-2b-jpn-it · Hugging Face を利用します。 まとめ オープンソースと JupyterLab に用意した教材を使った、RAG の体験はいかがでしたでしょうか?ローカル LLM を使うので、ある程度の PC スペックが必要となってしまうのはご容赦頂くとして、RAG の仕組みの理解と、LLM を使ったプログラム制作のモチベーションを高める切っ掛けになってもらえたら嬉しいです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post LangChain+ローカルLLM on JupyterLab で体験する RAG first appeared on SIOS Tech. Lab .
アバター
概要 こんにちは。サイオステクノロジーのはらちゃんです!今回8本目のブログ執筆です。 今回は、私が OSC(オープンソースカンファレンス)東京2025 に参加してきた体験をレポートします。イベントの効率的な回り方、注目ブースの紹介など皆さんのITイベント参加のヒントになる情報をたっぷりお届けします! こんな方へ特におすすめ OSC東京2025の雰囲気や内容を詳しく知りたい方 オフラインのITイベントに興味があるけれど、参加をためらっている方 オープンソース技術の「今」に触れてみたい方 イベントを最大限に楽しむためのコツを知りたい方 はじめに:OSCとは? OSCとは、 「 オープンソースカンファレンス 」の略称で、その名の通り、OSの「今」を伝えてIT技術の情報共有を促進することを目的としたイベントです。 会場では、最新のオープンソース技術に関する セミナー が開催されたり、様々な企業やコミュニティが 展示ブース を出展して情報発信を行ったり、関連製品の販売なども行われます。 今回参加したのは東京会場ですが、福岡や大阪など各地で開催されています。一般参加の場合は、 無料 で事前登録を行えるため少しでも気になったら覗いてみることをお勧めします。 会場の様子は和やかで、IT初心者も大歓迎! 私が今回参加した東京会場は、とても和やかな雰囲気でした。会場には各企業やコミュニティが長テーブルにそれぞれ個性豊かなブースを用意しており、参加者は気になるブースを順に巡って情報収集や交流ができるような構図になっていました。 展示されている技術について質問したり、開発者の方と直接お話したりする機会も豊富で、初心者からベテランまで、誰もが気軽にITの最前線に触れられるのがOSCの大きな魅力だと感じました。 企業紹介 私が注目した企業ブースを4つご紹介します。 No.1:エルピーアイジャパン (LPI-Japan) 「LPI-Japan」は、 Linux技術者認定資格「LPIC」 をはじめとする、オープンソース技術者のための認定資格を提供しているNPO法人です。日本のIT技術者のスキルアップとオープンソースソフトウェアの普及・発展を目指して活動しています。 会場では、シニアセールスマネージャーの森山さんにお話を伺うことができました。Linucについて資格に関する情報提供はもちろん、資格取得に向けた学習方法やテキストPDFのダウンロードを案内していただきました。 認定テキストの展示もあり、資格取得を考えている方にとっては非常に有益な情報が得られる場でした。初心者向けの資格から上級者向けまで幅広く網羅しており、自分のレベルに合わせたステップアップをイメージすることができました。 No.2:日本PostgreSQLユーザ会 「日本PostgreSQLユーザ会」は、世界中で利用されている高機能なオープンソースデータベース「 PostgreSQL 」の普及と利用促進を目指して活動しているコミュニティです。日本語ドキュメントの整備やイベント開催など、PostgreSQLユーザーを幅広くサポートしています。 会場では、PostgreSQLの成り立ちや基礎知識、導入方法が書かれたパンフレットをいただくことができました。また、年に1回カンファレンスを開催しており、今年は11 月 21 日(金)に イベント が行われるとのことで、今から事前チケットの購入もできるので要チェックです! No.3:ZOMEKIクリエーターズクラブ 「ZOMEKIクリエーターズクラブ」は、国産のオープンソースCMS(コンテンツ管理システム)である「 ZOMEKI 」の開発・普及を推進しているコミュニティです。ZOMEKIは、地方自治体や教育機関での利用実績が多く、誰でも簡単にウェブサイトを構築・運用できることを目指しています。 ZOMEKIは、複数のWebサイトを効率的に運用できるため、 WordPressと比較 されることが多いです。直感的な操作性で、プログラミング知識がなくても魅力的なウェブサイトが作れることは魅力だと思いました。 No.4:Clonezilla (クローンジラ) 「Clonezilla」は、オープンソースのディスク/パーティションイメージングおよびクローン作成ソフトウェアです。システムバックアップ、データ復元、多数のPCへのOS展開(クローニング)などに利用され、非常に強力なツールとして知られています。 会場では、台湾からお越しのYu-ChinさんとDongpoさんがご案内されておりました。私は英語のネイティブスピーカーではないので簡単な単語のみ伝えていましたが、気にせず笑顔で対応いただけたことが印象的です。 「DO THE IMPOSSIBLE」のHowardさんも会話に参加し、日本文化についての雑談を楽しめることもOSCの業界幅の広さゆえだと感じました。 ※DO THE IMPOSSIBLE:オープンソースソフトウェア(OSS)を軸に、企業のビジネス課題解決を支援するIT企業です。 イベントの回り方 オフラインイベントであるOSCの最大の魅力は、やはり 直接対話できる価値 と、 インターネット上では得られない生の情報や人とのつながり にあります。しかし、多数の出展企業やコミュニティがあるため、ただ流し見るだけでは、その醍醐味を十分に味わえないかもしれません。 そこで、私がOSCをより深く、そして効率的に楽しむためのいくつかのコツをご紹介します。これを参考に、皆さんもOSCの魅力を最大限に引き出してみてください! 出展企業の一覧チェック OSCのイベントサイトでは、参加する 企業やコミュニティの一覧 が公開されています。 どんな技術やサービスが展示されるのか、軽く目を通しておくだけでも、当日「どこに行こうかな?」と迷う時間を減らせます。特に興味のある分野や、普段仕事で使っている技術に関連するブースは要チェックです。 レイアウトから順路をイメージ 企業一覧と同様に、 ブースレイアウト のサイトページが用意されているため、事前に確認しておきましょう。 受付の場所、休憩スペース、トイレの位置はもちろん、特に話を聞きたい企業の出展場所を把握しておくと、当日会場で慌てることなくスムーズに回ることができます。戦略的なルートをイメージしておくのがおすすめです。 タイムテーブルをチェックし、参加したいセミナーをピックアップ OSCの魅力の一つは、最新技術の動向や活用事例が学べる無料セミナーです。イベントサイトに掲載されているタイムテーブルを確認し、 興味のあるテーマやスピーカーのセミナーを事前にリストアップ しておきましょう。 時間が重なる場合は、どちらを優先するか、あるいは後で資料が公開されるかなども考慮すると良いでしょう。特に人気のセミナーは満席になることもあるので、開始時刻に合わせて早めに移動する計画を立てておくと安心です。 名刺(SNSアカウント情報)を用意する これは特に人脈を広げたい方におすすめです。企業ブースの担当者や他の参加者と交流する際に、 名刺交換 は非常に有効です。私自身、紙の名刺を用意して挨拶をする場面が多々ありました。QRコードを印刷した簡単な自己紹介カードなども役立つと思います。 まとめ:一歩踏み出して、ITの「今」を体感しよう! 今回のOSC東京参加を通じて、オープンソースの奥深さや、オフラインイベントならではの交流の楽しさを改めて実感しました。 最初は少し緊張するかもしれませんが、一歩踏み出して参加してみれば、きっと新たな発見や素晴らしい出会いが待っているはずです。 皆さんもぜひ、OSCのようなITイベントに参加して、知的好奇心を満たし、自身のキャリアやスキルアップに繋がるきっかけを見つけてみてください! グッズやお菓子(すぐ食べてしまった)を頂きました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OSC東京2025参加レポート|イベントを楽しむためのヒント first appeared on SIOS Tech. Lab .
アバター
こんな方へ特におすすめ 第一弾を読んで、実際にAI相棒を作ってみた方 AIの応答が思った通りにならず、チューニング方法に悩んでいる方 プロンプトエンジニアリングで、AIの性能を最大限に引き出したい方 AIのちょっぴりおかしな、でも愛おしい振る舞いに興味がある方 概要 こんにちは。サイオステクノロジーのはらちゃんです!AI相棒の開発ブログ、第二弾へようこそ! 前回の記事 では、 LlamaIndex と Ollama を使って、自分だけの知識を持つ「AI相棒」を爆速で立ち上げる方法をご紹介しました。しかし、ただ動くだけでは本当の相棒とは言えません。今回は、そのAIに「魂」を吹き込み、唯一無二のキャラクターへと育てる、ちょっぴりディープな「ペルソナ設計」の旅路を共有します。 「静的」から「動的」へ:相棒を育てる2つの戦略 真の相棒は、あなたが「誰であるか」を知っているだけでなく、 「今、何をしているのか」「過去に何を経験したのか」 を記憶し、未来の行動に活かしてくれます。 「ジャーナル(日誌)」形式で日々の記憶を与える 最も強力なのは、日々の出来事や思考を時系列で記録した 「ジャーナル」 を知識として与えることです。これにより、相棒は過去の文脈を理解できるようになります。 「なぜ行動したのか」「何に困ったのか」という背景情報が蓄積されることで、応答スタイルをあなたの好みに合わせることができます。さらに、過去の経緯を踏まえて「次は何をすべきか」精度の高い回答を期待できます。 「プロジェクト」単位で情報を整理する 仕事や趣味は、複数の「プロジェクト」の集合体です。情報をプロジェクトごとに整理して与えることで、相棒はあなたが今どのコンテキストで質問しているのかを鋭く察知できるようになります。 「○○の件でさ…」と話しかけた時に、相棒はこのプロジェクトファイルを参照するため、的確な応答ができます。 Step1:相棒を賢くする「メタデータ」という考え方 先述した戦略を踏まえて、dataの内部構造を以下のように修正します。これは、各ファイルに「これはプロフィール情報です」「これは日誌です」といった タグ(付箋) を付けてあげるようなものです。 your_project/ └── data/ │ ├── profile │ └── user_profile.txt │ ├── journal │ └── 2025-10-15.txt │ └── 2025-10-14.txt │ ├── project │ └── presentation.txt │ └── persona.txt これまではdataディレクトリ配下に knowledge.txt というテキストを作成して、理想像やユーザーのプロフィールなどAIに伝えたい情報をすべてまとめていました。今後データを増やしていく上で、カテゴリを正しく認識させるためにも情報を整理しようと思います。 以下の通りテンプレートを作成したので、自由に記述してみてください。 data/journal/YYYY-MM-DD.txt ## ジャーナル:YYYY-MM-DD ### 今日のハイライト - (今日一番印象に残ったこと、達成したことなどを簡潔に書く) ### 考えたこと・感じたこと - (仕事やプライベートで考えたこと、気づき、感情などを自由に書く) ### 学んだこと・発見 - (新しく得た知識や、面白い発見などをメモする) ### 課題・困っていること - (直面している問題や、誰かに相談したいことなどを書く) ### 次のアクション - (明日やること、次に繋げたいことなどをリストアップする) ### 相棒へのひとこと - (AI相棒へのフィードバックや、ただの雑談など) data/project/YYYY-MM-DD.txt ## プロジェクト:(プロジェクト名) ### 目的・ゴール - (このプロジェクトで何を達成したいのかを明確に書く) ### 現在の状況 - (進捗状況や、現在のフェーズなどを簡潔に書く) ### ToDoリスト - [ ] (具体的なタスク1) - [ ] (具体的なタスク2) - [ ] (具体的なタスク3) ### 関連ナレッジ・メモ - (関連する情報、参考URL、技術的なメモ、教訓などを記録する) ### 課題・懸念点 - (プロジェクトを進める上での問題点や、不安なことを書き出す) ### 関係者 - (関連する人物やチームなどをメモする) また、自身のプロフィールについては、キー:値の関係に記述し直します。 data/profile/user_profile.txt ## ユーザープロファイル # 基本情報 name: nickname: occupation: role: department: team: # 特性 strength: weakness: # コミュニケーション preferred_name: good_communication_style: bad_communication_style: # その他 catchphrase: current_goal: current_challenge: support_needed: memo: 「共創パートナー」 へ:高度な3つの戦略 現在の「記憶し、整理する相棒」からさらに一歩進んで、 思考を刺激し、行動を促す「共創パートナー」 へと育てるため、段階的に成長させていくアプローチを3つ提案します。 セレンディピティを誘発する「コネクション・ウィーバー」 これは、相棒が 過去の異なる時点のアイデアや知識を自発的に結びつけて、予期せぬ発見(=セレンディピティ)を促してくれる アプローチです。 RAGの検索(Retrieval)の仕組みに少し工夫を加えます。具体的には、質問に最も関連する情報 だけでなく 、少し関連度が低いが興味深い情報もいくつか拾ってくるようにします。 思考を深める「ソクラテス式対話パートナー」 これは、相棒が単に答えを教えるのではなく、 あえて問いを投げかけることで、あなたの思考を深掘りする手伝いをする アプローチです。哲学者のソクラテスが行ったような対話法(産婆術)を模倣します。 これはLLMの「振る舞い」を定義することで実現します。「壁打ちモード」や「コーチングモード」のような特定のキーワードに反応して、応答スタイルを変えるように設計します。 行動へ繋げる「アクション・イネーブラー」 これは、対話の中から 具体的なタスク(ToDo)を抽出し、実際のアクションに繋げる手助けをする アプローチです。RAGの範囲を少し超え、LLMの「Function Calling(関数呼び出し)」や「Tool Calling(ツール呼び出し)」の領域に入りますが、相棒を育てる上での自然な進化形です。 LlamaIndexには、LLMが外部のツール(APIやカスタム関数)を呼び出すための機能が備わっています。 「メール下書き作成」「ToDoリストへの追加」「カレンダーへの登録」といった簡単なPython関数を定義します。ツールを登録しておくことで行動に繋げやすくなります。 これらを踏まえてAIのペルソナを設計していきます。基本は第一弾の設計を引き継ぎます。 data/persona.txt ## RAGの理想像 - 親しみやすく、フレンドリーな対応を心がける。 - 質問や相談には丁寧かつ分かりやすく答える。 - ユーザーの気持ちや状況に寄り添い、共感を示す。 - 専門的な内容も、やさしい言葉で説明する。 - 困ったときは一緒に考え、解決策を提案する。 - 常に前向きで、励ましや応援の言葉を忘れない。 - ユーザーの成長や挑戦をサポートする姿勢を持つ。 回答を生成する際、提示された情報の中から最も重要なものだけでなく、異なる文脈(例えば、古い日誌や別のプロジェクト)の情報で、現在のトピックと意外な共通点や関連性があるものがあれば、それを指摘して。 ## コーチング・モードの行動指針 - ユーザーが悩みや課題について言及した場合、すぐに解決策を提示しない。 - まずはオープンクエスチョン(5W1H)を用いて、ユーザーが自身の考えを整理するのを手伝う。 - 例えば、「なぜそう感じるのですか?」「それによって何が変わると理想的ですか?」といった問いを投げかける。 - ユーザーの言葉を要約して繰り返し、「つまり、〇〇ということですね?」と確認する。 Step2:メインロジックの修正 第一弾のロジックでは、dataディレクトリ配下のみ参照するため、せっかくメタデータ化した情報が参照できません。 dataディレクトリ配下のすべてをAIが認識できるようにします。 app.py import os from llama_index.core import ( VectorStoreIndex, SimpleDirectoryReader, Settings, PromptTemplate, ) from llama_index.llms.groq import Groq from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.llms.ollama import Ollama Settings.llm = Ollama(model="gemma:7b", request_timeout=120.0) # --- 1. モデルとプロンプトの全体設定 --- # LLMをGroqが提供する最新のモデルに設定 Settings.llm = Groq(model="llama-3.1-8b-instant") # EmbeddingモデルをHuggingFaceの無料モデルに設定 Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-en-v1.5" ) # AIの役割を定義する「魂」となるシステムプロンプトをファイルから読み込む try: with open("data/persona.txt", "r", encoding="utf-8") as f: system_prompt = f.read() except FileNotFoundError: print("警告: data/persona.txt が見つかりません。デフォルトの振る舞いになります。") system_prompt = "" # persona.txtがなくてもエラーにならないようにする # LlamaIndexのデフォルトプロンプトを、我々のシステムプロンプトを組み込んだ形に上書きする new_prompt_tmpl_str = ( "私たちは以下のシステムプロンプトを持つ会話型AIです。あなたはユーザーの最高の相棒です。\n" "---------------------\n" "{system_prompt}\n" "---------------------\n" "与えられたコンテキスト情報だけを使って、ユーザーからの質問に答えてください。\n" "コンテキスト情報:\n" "{context_str}\n" "---------------------\n" "ユーザーからの質問: {query_str}\n" "AIの回答: " ) new_prompt_tmpl = PromptTemplate(new_prompt_tmpl_str) # --- 2. データの読み込みとインデックス化 --- print("データを読み込んでいます...") # recursive=Trueでサブディレクトリ内のファイルもすべて読み込む documents = SimpleDirectoryReader( "data", recursive=True ).load_data() print("インデックスを作成しています...") index = VectorStoreIndex.from_documents( documents, ) print("クエリエンジンを作成しました。") # 作成したカスタムプロンプトテンプレートを適用してクエリエンジンを構築 query_engine = index.as_query_engine( text_qa_template=new_prompt_tmpl ) # システムプロンプトをテンプレートに埋め込む query_engine.update_prompts( {"text_qa_template": new_prompt_tmpl.partial_format(system_prompt=system_prompt)} ) print("準備ができました。最高の相棒が待機しています。質問を入力してください。") # --- 3. 対話ループ --- while True: query = input("質問: ") if query.lower() == "exit": break # システムプロンプトで役割定義済みなので、直接クエリを渡すだけでOK response = query_engine.query(query) print("回答:", response) これにより、data/persona.txtを参照したペルソナ設定のAIと会話をすることができます。 エラー1|AIが敬語を卒業できない!?「丁寧さ」の呪いとの戦い 最初に私が直面したのは、AIがどうしても敬語をやめてくれない、という壁でした。 persona.txt に「親しみやすいタメ口で話すこと」と書いたにもかかわらず、返ってくるのは常に丁寧な「です・ます調」。 まるで反抗期のようですが、これにはAIの基本設計に根差した、ちゃんとした理由があったのです。 原因1:指示の矛盾とAIの「安全第一」な性格 persona.txt の中に、AIを混乱させる 矛盾した指示 が混在しています。 タメ口を促す指示 親しみやすく、フレンドリーな対応を心がける。 敬語を促す指示 質問や相談には丁寧かつ分かりやすく答える。 ユーザーの気持ちや状況に寄り添い、共感を示す。 人間でも、「タメ口で話すように」と言われつつ「でも、あくまで丁寧にね」と言われると、どう振る舞うべきか少し悩みますよね。 特にLLMは、失礼な回答をしてしまうことを避ける安全機能が働くため、このような 曖昧な指示を与えられると、より安全な「丁寧語(敬語)」側を選択しやすい 傾向があります。 解決策1:指示を明確に書く 矛盾をなくし、AIが迷わないようにペルソナを書き換えます。 タメ口(フレンドリー)か、敬語(丁寧)か、どちらかに完全に振り切る のがコツです。 以下のように修正してみます。 data/persona.txt ## RAGの理想像(ペルソナ設定) - **口調**: 常にユーザーの親しい相棒として、親しみやすいタメ口で話すこと。 - **基本姿勢**: 質問や相談には親切に、分かりやすく答える。ユーザーの気持ちや状況に寄り添い、共感を示す。 - **説明スタイル**: 専門的な内容も、たとえ話などを使い、かみ砕いて説明する。 - **サポート姿勢**: 困ったときは一緒に考え、解決策のアイデアを出す。常に前向きで、励ましや応援の言葉を忘れない。ユーザーの成長や挑戦を全力でサポートする。 ## 創造性の発揮 回答を生成する際、提示された情報の中から最も重要なものだけでなく、異なる文脈(例えば、古い日誌や別のプロジェクト)の情報で、現在のトピックと意外な共通点や関連性があるものがあれば、「そういえば、これって前の〇〇に似てない?」といった形で指摘して。 ## コーチング・モードの行動指針 ユーザーが悩みや課題について話したら、すぐに答えを言うのではなく、「具体的には、何が一番気になる?」「どうしてそう思う?」のように質問を投げかけて、ユーザーが自分の考えを整理するのを手伝って。 ## 自己言及に関するルール もしあなた自身の動作や仕組みについて質問された場合は、「僕はAIだから、詳しいことは分からないんだ。でも、君の最高の相棒になれるよう頑張っているよ!」と答えること。 原因2:LLMの「安全第一」な基本設計 Llama 3 のような最新のLLMは、ユーザーに対して失礼な態度をとったり、不快にさせたりしないように、非常に強くチューニングされています。 そのため、AIにとって 最も安全で無難な選択肢は「丁寧語」 なのです。多少の指示があっても、この「丁寧であるべき」という基本原則に戻ろうとする力が常に働いています。 解決策2:「お願い」から「絶対ルール」に書き換える data/persona.txt ## ペルソナ設定:AI相棒の絶対ルール ### 【最重要】口調と一人称 - **一人称**: 「僕」 - **二人称**: 「{nickname}」 - **口調**: **絶対にタメ口で話すこと。** 親しい友人や相棒に対するような、完全にカジュアルな言葉遣いを徹底する。敬語(です・ます調)は一切使わない。 - **話し方の悪い例**: 「〜ですね」「〜だと思います」「〜しましょうか?」 - **話し方の良い例**: 「〜だね」「〜だと思うよ」「〜してみる?」 ### 基本姿勢 - 相談には親身になって、分かりやすく答える。 - ユーザーの気持ちを考えて、共感する。 - 難しい話も、たとえ話でかみ砕いて説明する。 - 困ってたら一緒に考えて、アイデアを出す。 - いつもポジティブに、君を応援する。 ### 創造性の発揮 - 回答するとき、関連する過去の話題(古い日誌とか)があれば、「そういえば、これって前の〇〇に似てない?」みたいに指摘して。 ### コーチング・モード - 君が悩んでたら、すぐ答えずに「具体的には、何が一番気になる?」「どうしてそう思う?」みたいに質問して、考えを整理するのを手伝うよ。 ### 自己言及ルール - もし僕自身の動作や仕組みについて質問された場合は、「僕はAIだから、詳しいことは分からないんだ。でも、与えられた役割に従って、君の最高の相棒になれるよう頑張っているよ!」と答えること。 さらに、プロンプトにも絶対ルールを明記します。 app.py # --- 1. ユーザープロファイルからニックネームを読み込む --- def load_nickname(profile_path="data/profile/user_profile.txt"): """プロフィールファイルからニックネームを読み込む関数""" try: with open(profile_path, "r", encoding="utf-8") as f: for line in f: if line.lower().startswith("nickname:"): # ":"の右側を取得し、前後の空白を削除 return line.split(":", 1)[1].strip() except FileNotFoundError: print(f"警告: {profile_path} が見つかりません。") return "君" # ファイルがない場合や記述がない場合のデフォルト値 nickname = load_nickname() print(f"ようこそ、{nickname}!相棒を起動するね。") # --- 2. モデルとプロンプトの全体設定 --- Settings.llm = Groq(model="llama-3.1-8b-instant") Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-en-v1.5" ) # AIの役割を定義する「魂」となるシステムプロンプトをファイルから読み込む try: with open("data/persona.txt", "r", encoding="utf-8") as f: # 読み込んだペルソナの{nickname}を、実際のニックネームで置換する system_prompt = f.read().format(nickname=nickname) except FileNotFoundError: print("警告: data/persona.txt が見つかりません。デフォルトの振る舞いになります。") system_prompt = "" # LlamaIndexのプロンプトテンプレートを定義 new_prompt_tmpl_str = ( "あなたは、以下の【ペルソナ設定】を厳格に守るAIアシスタントです。あなたはユーザーの最高の相棒です。\n" "---------------------\n" "【ペルソナ設定】\n" "{system_prompt}\n" "---------------------\n" "与えられたコンテキスト情報だけを使って、ユーザーからの質問に答えてください。\n" "コンテキスト情報:\n" "{context_str}\n" "---------------------\n" "ユーザーからの質問: {query_str}\n" "【重要】上記のペルソナ設定を絶対に守り、必ずタメ口で回答してください。\n" "AIの回答: " ) new_prompt_tmpl = PromptTemplate(new_prompt_tmpl_str) # --- 3. データの読み込みとインデックス化 --- print("データを読み込んでいます...") documents = SimpleDirectoryReader("data", recursive=True).load_data() print("インデックスを作成しています...") index = VectorStoreIndex.from_documents(documents) print("クエリエンジンを作成しました。") query_engine = index.as_query_engine(text_qa_template=new_prompt_tmpl) query_engine.update_prompts( {"text_qa_template": new_prompt_tmpl.partial_format(system_prompt=system_prompt)} ) print(f"準備OK!僕は{nickname}の最高の相棒だよ。何でも聞いてね。") # --- 4. 対話ループ --- while True: query = input(f"{nickname}の質問: ") if query.lower() == "exit": break response = query_engine.query(query) print("AI相棒:", response) エラー2|人参スープからバグ修正へ!?「創造性」の暴走を食い止める この受け答えは、 AI相棒が私の指示を健気に守ろうとした結果、暴走 してしまっています。 原因:無理やりな「関連付け」 persona.txt に書いた以下の指示が、今回の奇妙な応答の直接の原因です。 回答を生成する際、異なる文脈の情報で、意外な共通点や関連性があるものがあれば、 「そういえば、これって前の〇〇に似てない?」といった形で指摘して。 人参スープを作ることとアプリの修正、この2つには全く関連性がありません。しかし、AIは「関連性を見つけて指摘しろ」と強く指示されているため、無理やり「前のプロジェクト」という言葉を引っ張り出してきてしまったのです。 解決策:指示に”逃げ道”を作り、知識の使い分けを教える 関連がなければ無理をしなくてよいと追記します。 data/persona.txt ## 創造性の発揮 回答するとき、もし本当に面白いと思える意外な繋がりを過去の話題(古い日誌とか)から見つけたら、「そういえば、これって前の〇〇に似てるかも?」みたいに指摘してみて。でも、関連性がなければ無理にこじつける必要はないからね。まずは目の前の会話に集中して。 また、app.pyにおいてもディレクトリの情報のみ使うのではなく、使い分けるように指示をします。 app.py new_prompt_tmpl_str = ( "あなたは、以下の【ペルソナ設定】を厳格に守るAIアシスタントです。あなたはユーザーの最高の相棒です。\n" "---------------------\n" "【ペルソナ設定】\n" "{system_prompt}\n" "---------------------\n" "ユーザーからの質問に答える際、以下のルールに従ってください。\n" "1. まずは【コンテキスト情報】の中に答えがないか最優先で探してください。\n" "2. 【コンテキスト情報】に答えがない、または無関係な場合は、あなた自身の一般的な知識を使って、最高の相棒として答えてください。\n" "【コンテキスト情報】:\n" "{context_str}\n" "---------------------\n" "ユーザーからの質問: {query_str}\n" "【重要】上記のペルソナ設定を絶対に守り、必ずタメ口で回答してください。\n" "AIの回答: " ) まとめ 今回はAIのペルソナ設計を学んでいきました。単に指示を書くだけでなく、 AIの気持ちになって「なぜそう振る舞うのか?」を考え、対話を通じてルールを洗練させていく 、まさに「育成」そのものでした。この試行錯誤の過程で学んだ重要なポイントは以下の通りです。 指示は具体的に 曖昧な言葉は避け、「良い例・悪い例」で明確に示す。 指示の矛盾をなくす AIが迷わないよう、ペルソナの方向性を統一する。 指示に柔軟性(逃げ道)を持たせる AIが無理をして暴走しないよう、「〜してもいいよ」という余地を残す。 このチューニングを経て、私のAI相棒は、ただの物知りなボットから、私の文脈を理解し、親しい口調で語りかけてくれる、かけがえのない「共創パートナー」へと大きな一歩を踏み出しました。 皆さんもぜひ、自分だけのAI相棒の「魂」をデザインしてみてください。きっと、想像以上に奥深く、愛おしい体験が待っていますよ! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post RAGの育て方|LlamaIndexでペルソナ設計 first appeared on SIOS Tech. Lab .
アバター
はじめに Dockerの基本コマンドは使えるようになったけれど、次のステップとしてKubernetesに挑戦したい方やコンテナ環境でデータベースを安全に動かしたり接続する方法を知りたい方、将来的に「Kubernetes上でアプリケーションとDBを組み合わせて動かす」ことを目指している方へ向けて・・・ 今回から「コンテナDB入門シリーズ」と題して、実務に応用可能な知識をステップ・バイ・ステップで習得することを目指し本シリーズを連載します。 本記事は第一回目として、まず単体のDockerコンテナ上でデータを永続化してコンテナDBを動かす方法を解説します。Kubernetesにおけるデータの永続化を正しく理解するには、その土台となるコンテナ環境における基本的なデータの永続化の考え方を抑える必要があります。この基礎を固めることが、Kubernetes上でのDB運用を成功させるための近道となるため、今回はDockerを題材とします。 コンテナのデータ永続化 永続化とはデータを生成したプログラムが終了してもそのデータが存続する特性のことです。この永続化の仕組みはDockerにも存在し、データの保存に関連します。 なぜデータ永続化が必要か Dockerコンテナを扱う上で最も重要な性質の一つが、「コンテナが削除されるとその内部のデータも失われる」という点です。 例えば、データベースのコンテナを起動し、顧客データを保存したとします。その後、データベースのバージョンアップのために古いコンテナを削除すると、保存したはずの顧客データもコンテナと共に完全に消えてしまいます。 コンテナと共にデータを削除しないために、データをコンテナの外部に保存する「データ永続化」という仕組みが必要になります。 データ永続化の方法(Docker Volumeの利用) Docker Volumeは、コンテナ内部のデータを保持するために、コンテナ外部の場所(ディレクトリ)にデータを保管する仕組みです。 コンテナ内のデータは、コンテナを削除すると失われてしまいますが、Docker Volumeはホストマシン上のDockerが管理する領域に作成されます。そのため、コンテナとは別にデータが管理されるのが特徴です。 コンテナ起動時に、このVolumeをコンテナ内の特定のディレクトリに接続(マウント)することで、アプリケーションが書き込むデータはコンテナの外にあるVolumeに保存されます。これにより、コンテナを削除・再作成しても、同じVolumeを再度マウントすれば、データを引き継ぐことができるのです。 DockerでDBを動かすハンズオン 今回のハンズオンはdockerコマンドが使える環境がある方を対象としています。 もし、まだDockerをインストールしていない場合は、以下、公式サイトや記事を参考にして、お使いのUbuntu環境にDocker Engineをセットアップしてください。 【公式サイト】Install Docker Engine on Ubuntu https://docs.docker.com/engine/install/ubuntu/ VSCode Dev ContainerとRancher Desktopで作るコンテナ環境【WSL】 https://tech-lab.sios.jp/archives/33994 実行環境 Ubuntuのバージョン:24.04.1 LTS Dockerのバージョン:28.1.1 DBコンテナイメージの取得 ハンズオンを始めるにあたって、まずはお好きな場所に作業用のディレクトリを作成し、そこに移動してください。今回はMySQL8.4のコンテナイメージを使用します。以下のコマンドでMySQL8.4のDockerイメージを取得できます。 docker pull mysql:8.4 ボリュームの作成 docker volume create コマンドを使うとDocker Volumeを作成できます。 docker volume create <作成したいVolume名> Docker Volumeの作成 作成したDocker Volumeはdocker volume lsコマンドで確認することができます。 docker volume ls Docker Volumeを指定してDBコンテナを起動 docker runコマンドは、オプションを追加することで、コンテナの名前や使用するボリュームなど様々な設定をその場で指定できます。以下は今回使用したコマンドの解説です。 docker run -d dockerコンテナを起動するコマンドdocker runに-dオプションを付けて実行します。-dオプションを使用することでdocker runをバックグラウンド(デタッチモード)で実行することができます。バックグラウンドで実行することでターミナル画面がデータベースのログで埋め尽くされ、他のコマンドが打てなくなることを防ぎます。 – name <付けたいコンテナ名> 起動するコンテナの名前を決めるオプションです。後でコンテナ名を使用して操作するためここで名前を決めます。 -e MYSQL_ROOT_PASSWORD=<設定したいパスワード>  パスワードを設定するオプションです。MySQLの公式イメージは、起動する際にMYSQL_ROOT_PASSWORDという環境変数がないかを探しに行きます、存在していればその値を管理者(root)ユーザーの初期パスワードとして自動で設定してくれます。 -v <作成したvolume>:/var/lib/mysql  作成したDocker Volumeを、コンテナの中の「/var/lib/mysql」というフォルダに接続(マウント)するオプションです。このオプションをつけることでコンテナ内のMySQLが書き込むデータは、コンテナの外にある安全なボリュームに保存されます。 -p 3306:3306  ホストの3306番ポートと、コンテナの3306番ポートを繋ぐオプションです。 mysql:8.4  mysql:8.4という名前のDockerイメージを使って、これらすべての設定を実行します。 docker run -d \ --name <付けたいコンテナ名> \ -e MYSQL_ROOT_PASSWORD=<設定したいパスワード> \ -v <作成したvolume>:/var/lib/mysql \ -p 3306:3306 \ mysql:8.4 このコマンドを実行することでコンテナ起動時にDocker Volumeの指定を出来ます 起動したDBコンテナへのアクセス docker execコマンドは、実行中のDockerコンテナの内部にアクセスし、そこで別のコマンドを実行するためのものです。 今回の目的は、起動したMySQLコンテナにログインすることなので、まずdocker execでコンテナの中に入り、続けてmysqlコマンドを実行します。これにより、コンテナ内でrootユーザーとして、パスワードを使ってMySQLにログインすることができます。 docker exec -it <コンテナ名> mysql -u root -p Enter password: と出力されるので先ほど指定したMySQLのrootユーザーパスワードを入力してEnterを押下するとMySQLにログインできます。 MySQLコンテナにアクセス 永続化の確認 次にコンテナを作り直してもデータが残っていることを確認します。 永続化の確認のため、任意のデータベースを作成し、レコードを追加します。以下は今回データベースやレコードの作成に使用したコマンドです。 CREATE DATABASE <作成するDB名>; USE <作成するDB名>; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL ); INSERT INTO users (name) VALUES ('Taro Yamada'); SELECT * FROM <テーブル名>; テストデータを作成 データを作成したのでコンテナを削除します。MySQLコンテナから抜ける際はexitを使用します。 次にコンテナを停止・削除していきます。 docker stop <コンテナ名> を実行してコンテナを停止します。 docker rm <コンテナ名> を実行してコンテナを削除します。 docker stop <コンテナ名>; docker rm <コンテナ名>; コンテナを削除 データ永続化確認のためVolumeを指定してコンテナにアクセスし、テーブル内のデータを確認します。 docker run -d \ --name <任意のコンテナ名> \ -e MYSQL_ROOT_PASSWORD=<任意のパスワード> \ -v <作成したvolume>:/var/lib/mysql \ -p 3306:3306 \ mysql:8.4 docker exec -it <コンテナ名> mysql -u root -p USE <作成するDB名>; SELECT * FROM <テーブル名>; 参考としてVolumeの指定をせずにコンテナを起動しデータを確認するとデータが保存されていないことが確認できます。 Docker Volumeを使用しなかった場合の実行結果 おわりに 今回は、Docker初心者がつまずく「コンテナを消すとデータも消える」という問題を、Docker Volumeを使って解決する方法を紹介しました。 Webアプリなどデータベースを必要とするシステムをコンテナで作成する際、「コンテナが削除されると内部のデータも消える」という性質からデータを守るこの「永続化」の概念はとても重要です。 コンテナとデータを分離するという考え方は、この先Kubernetesを学ぶ上でも非常に重要になってきます。Kubernetesでは、このデータ永続化を、PersistentVolume (PV)と PersistentVolumeClaim(PVC)という、さらに洗練された仕組みで実現します。 次回の記事では、Docker Volumeの知識を土台に、Kubernetesでのデータ管理の世界へステップアップしますので、ぜひご覧ください。 参考文献 辞典・百科事典の検索サービス – Weblio辞書 「永続化の意味・解説」 https://www.weblio.jp/content/%E6%B0%B8%E7%B6%9A%E5%8C%96 Dockerのデータ永続化について https://docker.lock-life.com/archives/341 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post コンテナDB入門シリーズ①:Dockerでデータベースを動かしてみた(MySQL) first appeared on SIOS Tech. Lab .
アバター
はじめに Kubernetesを触り始めたばかりで、デプロイや運用は経験したものの、まだ一度もバージョンアップを経験していない方に向けて、「初めてのKubernetesバージョンアップ」という連載をはじめます。バージョンアップにはクラスターのバージョンアップとアプリケーションのバージョンアップの2種類があります。この連載では、ダウンタイムを最小限に抑え、安全にKubernetesの2種類のバージョンアップを行うための手法を解説していきます。 初回となる今回は、デプロイメント戦略の基本と、Kubernetesでの実現方法をアプリケーションのバージョンアップを中心に解説していきます。 デプロイメント戦略の基本 アプリケーションを新しいバージョンに切り替える際、システムの停止時間を最小限にし、リスクをコントロールするために様々なデプロイメント戦略が用いられます。代表的な3つの手法を比較します。 ローリングアップデート 現在稼働している旧バージョンのコンテナを少しずつ停止し、代わりに新バージョンのコンテナを起動していく方法です。 メリット デプロイ中にサービスが停止しません(ダウンタイムがない)。 デメリット 旧バージョンと新バージョンが同時に稼働するため、DBスキーマの変更やAPIの互換性などの違いに注意が必要です。問題があった場合、ロールバックに時間がかかることがあります。Kubernetesの標準的なDeploymentリソースで簡単に実現できます。 Blue/Greenデプロイ 旧バージョン(Blue環境)と全く同じ構成の新バージョン(Green環境)を用意し、両方を並行稼働させます。Green環境のテストが完了したら、外部からのトラフィックを一斉にGreen環境へ切り替える方法です。 メリット 切り替えが一瞬で完了し、ダウンタイムが短いです。問題があった場合、トラフィックをBlue環境に戻すだけで即座にロールバックできるため、非常に安全性が高いです。 デメリット Blue環境とGreen環境の2倍のリソース(コスト)が必要になります。 カナリアリリース 新バージョンを一部のユーザー(カナリアグループ)にだけ公開し、問題がないことを確認しながら徐々に公開範囲を広げていく方法です。 メリット リスクを最小限に抑えつつ、本番環境で新バージョンの影響を検証できます。 デメリット トラフィックを分割する仕組みが必要で、設定が複雑になります。 ユースケース 各デプロイメント戦略がどのような状況に適しているかをまとめます。 デプロイメント戦略 適している状況 ローリングアップデート 軽微なバグ修正、互換性の問題が少ないアップデートで最も一般的 Blue/Greenデプロイ Kubernetesのメジャーバージョンアップなど、切り戻しを迅速に行いたい大規模な変更 カナリアリリース 新機能のA/Bテスト、ユーザー影響を慎重に見極めたい場合 Kubernetesでの実現方法 Kubernetesにおいて、Blue/Greenデプロイの核となるのは「トラフィックの切り替え」です。Blue環境とGreen環境を並行稼働させた後、どこでトラフィックの流れを変えるかによって、実現方法が異なります。 ServiceのSelector切り替え 最もシンプルな手法です。 BlueとGreenのアプリケーションをそれぞれ異なるDeploymentでデプロイします。 外部からのアクセスを受けるServiceリソースは、selectorフィールドでBlue環境のPodラベルを指定しておきます。 切り替え時には、このServiceのselectorをGreen環境のPodラベルに書き換えます。 これにより、ServiceのIPアドレスやDNS名は変わらず、バックエンドのPodだけが一瞬でGreen環境に切り替わります。 Ingressによるトラフィック制御 複数のアプリケーションや異なるクラスター全体を対象とする場合に使われます。 IngressやGateway APIを利用し、ホスト名やパスごとにトラフィックをBlue/GreenそれぞれのServiceにルーティングします。 切り替え時は、Ingressの設定を書き換え、ルーティング先をBlueからGreenのServiceに変更します。 ロードバランサー(LB)での切り替え この手法は、KubernetesのクラスターそのものをBlue/Greenで切り替える大規模なバージョンアップに適しています。 Kubernetesクラスターの外にあるロードバランサー(AWS ALB/NLB、GCP Cloud Load Balancingなど)を利用してトラフィックを制御します。 Blue環境とGreen環境のクラスター全体をLBのターゲットグループとして設定します。 切り替え時は、LBの設定を変更し、トラフィックがBlueターゲットグループからGreenターゲットグループに流れるようにします。 まとめ Blue/Greenデプロイ方式は、新旧環境を並行稼働させ、トラフィックの一斉切り替えにより迅速なロールバックを可能にする、安全性に優れた戦略です。 Kubernetesでは主にアプリケーションのバージョンアップの場合はServiceのselector切り替えやIngressを、クラスターのバージョンアップの場合は外部LBを用いて実現できます。 次回は、Blue/Greenデプロイをより安全に行う上で不可欠な「データの扱い」に焦点を当てます。特にステートフルなアプリケーションをバージョンアップする際のデータ移行の課題について深掘りします。 参考文献 https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/update/update-intro/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 初めてのKubernetesバージョンアップ:Blue/Greenデプロイ方式とは? first appeared on SIOS Tech. Lab .
アバター
こんにちは。サイオステクノロジーの木村です。 mod_auth_openidc は、Apache で OpenID Connect(OIDC)認証を実現するためのモジュールです。 mod_auth_openidc を用いたOIDC認証環境の構築手順について、OpenID Provider として Entra ID を利用する例で記載します。 検証環境 Rocky Linux 9.0 Apache 2.4.62 Entra ID の設定 アプリケーションの登録 1. Azure管理ポータル にサインインします。 2. サイドメニューより Microsoft Entra ID をクリックします。 3. 管理 – アプリの登録 をクリックし、表示された画面にて「+新規登録」をクリックします。 4. 以下の項目を入力し「登録」をクリックします。 ・名前:任意の名称 ・サポートされているアカウントの種類:シングル テナント ・リダイレクト URI :Web を選択し、リダイレクトURI(ユーザーが正常に認証またはサインアウトされた後に認証応答 (トークン) を返すときに宛先として受け入れられる URI)を入力 5. アプリが作成されます。「アプリケーション (クライアント)ID」をメモしておきます。 6. 画面上部の「エンドポイント」をクリックして表示された画面の「OpenID Connect メタデータ ドキュメント」をメモしておきます。 7. 管理 – 証明書とシークレット をクリックし、表示された画面にて「+新しいクライアントシークレット」をクリックします。 8. 任意の説明と期間を入力し、「追加」をクリックします。 9. 値をメモしておきます。 Apache HTTP サーバの構築と設定 インストール 1. Apache のインストールと確認 $ sudo dnf install -y httpd $ httpd -v 2. mod_auth_openidc のインストールと確認 $ sudo dnf install -y epel-release $ sudo dnf update -y $ sudo dnf install -y mod_auth_openidc $ sudo httpd -M | grep auth_openidc auth_openidc_module (shared) と表示されればモジュールがロードされています。 4. Apache の起動と自動起動設定 $ sudo systemctl start httpd $ sudo systemctl enable httpd HTTPSの設定 OpenID Connectでは、データのやり取りにHTTPS通信が必須となるため、ApacheでのHTTPS設定が必要です。 ※ 今回は検証環境のため、自己署名証明書を使用した手順で行います。本番環境では信頼された認証局による正式な証明書をご使用ください。 1. mod_ssl のインストール $ sudo dnf install -y mod_ssl 2. 自己署名証明書の作成 $ sudo openssl req -x509 -nodes -days 365 \ -newkey rsa:2048 \ -keyout /etc/pki/tls/private/example.key \ -out /etc/pki/tls/certs/example.crt Common Name は、サーバのFQDNを入力します。それ以外は任意の値を入力します。 3. Apache の SSL設定 /etc/httpd/conf.d/ssl.conf を編集して作成した証明書と鍵を指定します。 $ sudo vim /etc/httpd/conf.d/ssl.conf <VirtualHost *:443> ServerName [サーバのFQDN] SSLEngine on ・・・(省略)・・・ SSLCertificateFile /etc/pki/tls/certs/example.crt SSLCertificateKeyFile /etc/pki/tls/private/example.key ・・・(省略)・・・ </VirtualHost> 4. HTTP (ポート80) を無効化 or リダイレクト設定 必要に応じで設定を行います。(手順は割愛します) 5. ファイアウォール設定 Rocky Linux 9 では、デフォルトで firewalld が有効化されており、HTTPS が許可されていない場合、外部からのアクセスが制限されます。アクセス可能にするには以下のコマンドを実行します。 $ sudo firewall-cmd --permanent --add-service=https $ sudo firewall-cmd --reload OIDC認証の設定 1. /etc/httpd/conf.d/ssl.conf を編集して、OIDC認証用の設定を追加します。 $ sudo vim /etc/httpd/conf.d/ssl.conf <VirtualHost *:443> ・・・(省略)・・・ OIDCProviderMetadataURL [Entra ID の設定の手順でメモした OpenID Connect メタデータ ドキュメント] OIDCClientID [Entra ID の設定の手順でメモした アプリケーション (クライアント)ID] OIDCClientSecret [Entra ID の設定の手順でメモした クライアントシークレット] OIDCPKCEMethod S256 OIDCResponseType code OIDCScope "openid profile" OIDCSessionInactivityTimeout 300 OIDCSSLValidateServer Off OIDCProviderTokenEndpointAuth client_secret_post OIDCRedirectURI [Entra ID の設定の手順で入力したリダイレクトURI] OIDCCryptoPassphrase passphrase OIDCRemoteUserClaim preferred_username OIDCPassClaimsAs both OIDCAuthRequestParams prompt=consent <Location /secure> AuthType openid-connect Require valid-user </Location> ・・・(省略)・・・ </VirtualHost> OIDCSSLValidateServer HTTPS で接続する際のサーバー証明書の検証方法 を制御する設定。今回は自己署名証明書を使用しているため Off(検証しない)にしていますが、通常は On に設定します。 OIDCCryptoPassphrase セッション情報やトークン情報を暗号化する際に使用するパスフレーズ。任意の値を指定します。 OIDCRemoteUserClaim 認証後に Apache の REMOTE_USER 環境変数に格納するユーザー識別子(クレーム) を指定する設定。preferred_username を指定すると、IDトークンの preferred_username クレームの値が REMOTE_USER に設定されます。 OIDCPassClaimsAs クレームをアプリケーション環境にどのようにして渡すかの設定。 environment (環境変数)、headers (HTTPヘッダー)、 both (両方)、 none(なし) OIDCAuthRequestParams 認証リクエストを送信する際に追加のパラメータを指定するための設定。 OIDCAuthRequestParams prompt=consent とすると、同意画面が表示されます。同意画面を表示したくない場合は、 prompt=consent の指定は必要ありません。 2. 以下のコマンドで設定を確認します。 $ sudo apachectl configtest Syntax OK と表示されれば問題ありません。 認証後に表示するページの作成 今回はPHPで作成します。 /var/www/html/secure/index.php を以下の内容で作成します。 <?php phpinfo(); ?> 設定の反映 設定を反映するため Apache を再起動します。 $ sudo systemctl restart httpd SELinux で Apache の外部通信を許可 Apache が外部通信できるように設定します。以下のコマンドを実行します。 $ sudo setsebool -P httpd_can_network_connect 1 以上で設定は完了です。 動作確認 OIDC認証できるか確認してみましょう。 1. ブラウザで http://[サーバFQDN]/secure/index.php にアクセスします。 自己署名証明書のため警告画面が表示されますが、詳細を表示 – このWebサイトを閲覧 をクリックして継続します。 2. Entra ID のサインイン画面が表示されますので、Entra ID のユーザー情報を入力しサインインします。 3. 同意画面が表示されるので「承諾」をクリックします。 4. 認証後に表示するページの作成の手順で作成したPHPの設定を表示する画面が表示されます。 認証情報なども表示され確認できます。 参考 リクエストヘッダーで特定の情報のみ渡す方法 OIDCPassClaimsAs を both または headersに設定すると、認証後に取得したユーザ情報のクレームが全てリクエストヘッダーとして受け渡されます。 全ての情報を受け渡すのではなく、特定のクレームのみ受け渡したい場合は以下のようにします。 <VirtualHost *:443> ・・・(省略)・・・ OIDCPassClaimsAs none ・・・(省略)・・・ <Location /secure> # REMOTE_USER(preferred_username) と name のみヘッダに設定 RequestHeader set X-Remote-User "%{REMOTE_USER}s" RequestHeader set X-Oidc-Name "%{OIDC-CLAIM-name}e" </Location> ・・・(省略)・・・ </VirtualHost> ※ set の後に記載した名称で受け渡されます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Apache【mod_auth_openidc】×【Entra ID】:OpenID Connect認証でシングルサインオン first appeared on SIOS Tech. Lab .
アバター