GitHub Actionsでモノレポを安全に自動リリースする設計: 変更検知・段階配布・失敗復旧

GitHub Actionsでモノレポを安全に自動リリースする設計: 変更検知・段階配布・失敗復旧 モノレポのCI/CDは、単一リポジトリだから楽になる一方で、リリース設計を誤ると一気に難しくなります。 1つの変更で全サービスを再デプロイしてしまう 並列ジョブが増えてキュー渋滞する どのコミットがどのサービスへ反映されたか追跡できない 一部失敗時のロールバックが曖昧 本記事では、GitHub Actionsでモノレポを運用しているチーム向けに、実務で耐えるリリース自動化の構成を具体的に説明します。 1. モノレポCI/CDで先に決める設計原則 最初に次の原則を明文化します。 変更のないサービスはデプロイしない リリース対象は機械的に決定する 本番反映は段階的(canary/割合配布) 失敗時の復旧手順を自動化する 監査ログ(誰が何をいつ)を残す この5つがないと、運用が属人化し、障害時対応が遅れます。 2. 変更検知をワークフローの入口に置く モノレポでは「どのディレクトリが変わったか」を最初に判定し、対象サービスだけを処理します。 2.1 changed-filesの例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 jobs: detect: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v4 - id: changed uses: tj-actions/changed-files@v45 with: files_yaml: | api: - services/api/** web: - services/web/** worker: - services/worker/** - id: set-matrix run: | python .github/scripts/build_matrix.py '${{ toJson(steps.changed.outputs) }}' ここでmatrixを作り、後続ジョブを fromJson で動的展開します。 ...

March 7, 2026 · 2 min · AI2CORE 編集部

FastAPI + SQLAlchemy性能改善プレイブック: 遅いAPIを計測ベースで高速化する

FastAPI + SQLAlchemy性能改善プレイブック: 遅いAPIを計測ベースで高速化する FastAPIの初期実装は非常に快適です。しかし運用フェーズに入ると、次のような症状が出てきます。 一覧APIのレスポンスが急に遅くなる 同時接続が増えるとp95が跳ねる CPUは余っているのにタイムアウトが増える DB接続数が上限に張り付く こうした問題の多くは「Pythonが遅い」のではなく、SQLAlchemyの使い方とDBアクセス設計 に起因します。 本記事では、FastAPI + SQLAlchemy + PostgreSQL構成を前提に、実際の改善手順を計測ベースで整理します。 1. 最初に測るべき指標 最適化は、体感ではなく数値で進めます。最低限、以下を可視化します。 APIのp50/p95/p99レイテンシ エンドポイント別SQL発行回数 1リクエストあたりのDB滞在時間 connection pool待ち時間 slow query件数(200ms以上など) OpenTelemetryやNew Relicを使っているなら、アプリspanとDB spanを必ず紐付けてください。これだけでボトルネック特定速度が上がります。 2. N+1問題を最優先で潰す 最も頻出するのがN+1です。例えばユーザー一覧でプロフィールを参照すると、ユーザー数分の追加クエリが発行されます。 2.1 悪い例 1 2 3 4 5 6 7 8 users = session.query(User).limit(100).all() result = [] for u in users: result.append({ "id": u.id, "name": u.name, "profile": u.profile.bio, }) 2.2 改善例(joinedload/selectinload) 1 2 3 4 5 6 7 8 from sqlalchemy.orm import selectinload users = ( session.query(User) .options(selectinload(User.profile)) .limit(100) .all() ) joinedload と selectinload はデータ量で使い分けます。 ...

March 7, 2026 · 2 min · AI2CORE 編集部

Terraform→OpenTofu移行実践ガイド: 既存IaCを止めずに移行するエンタープライズ手順

Terraform→OpenTofu移行実践ガイド: 既存IaCを止めずに移行するエンタープライズ手順 Terraformのライセンス変更以降、OpenTofuへ移行したいという相談は確実に増えています。とはいえ現場の本音は「理屈は分かるが、stateが壊れたら終わる」「本番を止めずに移行できるのか不安」です。 結論から言うと、移行は十分可能です。ただし「CLIを置き換えるだけ」で済むケースは限定的で、実際は providerバージョン整合・state lock・CI/CD・運用Runbook までまとめて整える必要があります。 本記事では、既にTerraformを本番運用しているチーム向けに、OpenTofuへ段階移行する実践手順をまとめます。 1. 移行方針を先に決める 最初に決めるべきは「一気に切り替えるか」「ワークスペース単位で段階移行するか」です。実務では次の方針が安全です。 低リスク環境(dev/sandbox)から先行 本番は最終フェーズで移行 旧TerraformとOpenTofuを一定期間並行運用 ロールバック手順を文書化してから実施 この順序を守るだけで、移行事故の大半を避けられます。 2. 互換性の棚卸し(最重要) まずは現状のIaC資産を棚卸しします。 Terraformバージョン(例: 1.5.x / 1.6.x) 使用provider(AWS/Azure/GCP/Kubernetes等) backend(S3 + DynamoDB lock、Terraform Cloud、GCSなど) moduleの参照方式(registry / git / local) CI実行環境(GitHub Actions, GitLab CI, Jenkins) 2.1 依存を固定化してから移行する .terraform.lock.hcl を必ずコミットし、providerを固定します。移行時にproviderまで同時更新すると、差分原因の切り分けが困難になります。 1 2 3 4 5 6 7 8 9 terraform { required_version = ">= 1.6.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.40" } } } 移行フェーズでは「ツール差分」と「provider差分」を分離してください。 ...

March 7, 2026 · 2 min · AI2CORE 編集部

Kyvernoで始めるKubernetes Admission Policy実践: 事故を減らすポリシー設計プレイブック

Kyvernoで始めるKubernetes Admission Policy実践: 事故を減らすポリシー設計プレイブック Kubernetes運用で一番つらい事故は、クラスタが壊れるよりも「本来防げたはずのミスがそのまま本番へ入る」ことです。たとえば、latest タグのイメージが本番に入り再現不能になる、resources 未設定でノードが詰まる、privileged コンテナが混入する。これらは人の注意力だけに依存すると必ず再発します。 そこで有効なのが Admission Policy(入場制御)です。本記事では Kyverno を使って、現場で本当に運用できるポリシー群を段階導入する手順をまとめます。単なる「denyの例」ではなく、監査→警告→強制の移行、例外管理、CI連携まで含めて解説します。 1. なぜKyvernoなのか OPA Gatekeeper も強力ですが、Kyvernoは以下の特徴があり、初期導入が比較的スムーズです。 YAML中心で書ける(Rego学習コストを後回しにしやすい) validate / mutate / generate / verifyImages を一貫して扱える PolicyReportにより違反可視化がしやすい Pod SecurityやSupply Chain対策との相性が良い 「まずルールを回し始める」目的なら、Kyvernoは現実的な選択肢です。 2. 先に決めるべき設計原則 導入前に、以下だけは先に決めておきます。 導入フェーズ: Audit → Enforce を基本にする 責任分界: プラットフォームチームが共通ポリシー、各チームがアプリ固有例外 例外の期限: 永久例外は禁止。期限付きで必ず棚卸し 観測性: 違反数・対象Namespace・上位違反ルールをダッシュボード化 この原則なしにルールだけ増やすと、運用が破綻します。 3. 最小導入手順(30〜60分) 3.1 Kyvernoのインストール 1 2 3 4 5 6 7 8 9 helm repo add kyverno https://kyverno.github.io/kyverno/ helm repo update helm upgrade --install kyverno kyverno/kyverno \ -n kyverno --create-namespace \ --set admissionController.replicas=2 \ --set backgroundController.replicas=2 \ --set cleanupController.replicas=1 \ --set reportsController.replicas=1 本番では可用性のため、admission/backgroundは最低2レプリカ推奨です。 ...

March 6, 2026 · 3 min · AI2CORE 編集部

FastAPI + Celery信頼性設計: 非同期ジョブを本番で壊さないための実装パターン

FastAPI + Celery信頼性設計: 非同期ジョブを本番で壊さないための実装パターン FastAPIでAPIを作ると、重い処理はすぐに非同期ジョブへ逃がしたくなります。画像変換、レポート生成、外部API連携、メール配信など、Celeryは非常に便利です。ですが、本番で問題になるのは「動くかどうか」ではなく、失敗したときに壊れないか です。 同じジョブが二重実行される 一時障害で永遠にリトライしてキューが詰まる ワーカー再起動で中途半端な状態が残る 完了通知が先に飛んで実データがない 本記事では FastAPI + Celery + Redis 構成を前提に、再実行安全性(idempotency)と運用信頼性を上げる実装手順をまとめます。 1. まず守るべき設計原則 非同期基盤の事故は、ほぼ次の4原則で防げます。 At-least-once前提(同一タスク再実行は必ず起こる) 副作用は冪等化(何回実行されても結果が壊れない) 状態遷移を明示(PENDING/RUNNING/SUCCEEDED/FAILED) 失敗を可観測化(リトライ回数・死活・滞留時間を計測) この原則を外すと、障害時に「何が完了して何が未完了か」が追えなくなります。 2. 参照アーキテクチャ API: FastAPI Queue Broker: Redis Worker: Celery Result Store: PostgreSQL(業務状態) Monitoring: Flower + Prometheus + Sentry ポイントは、業務上重要な状態はRedis結果バックエンドに依存しない ことです。Redisは一時的に使い、真実の状態はRDBに持たせます。 3. 実装の土台: タスク受付API 3.1 受け付け時に idempotency_key を必須化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from fastapi import FastAPI, HTTPException from pydantic import BaseModel from sqlalchemy import select app = FastAPI() class JobRequest(BaseModel): idempotency_key: str report_type: str user_id: str @app.post("/reports") def create_report(req: JobRequest): existing = find_job_by_key(req.idempotency_key) if existing: return {"job_id": existing.id, "status": existing.status} job = create_job_record( idempotency_key=req.idempotency_key, status="PENDING", report_type=req.report_type, user_id=req.user_id, ) generate_report.delay(job.id) return {"job_id": job.id, "status": "PENDING"} これでクライアント再送が来てもジョブ多重作成を防げます。 ...

March 6, 2026 · 2 min · AI2CORE 編集部

PostgreSQL PITR復旧訓練ガイド: バックアップがあるのに戻せないを防ぐ実践手順

PostgreSQL PITR復旧訓練ガイド: バックアップがあるのに戻せないを防ぐ実践手順 PostgreSQL運用で最も危険なのは「バックアップがある」という安心感です。実際の障害では、バックアップ自体より 復旧手順の不整合 で時間を失います。たとえば、WAL保管期間が足りず目標時刻に戻せない、暗号鍵が見つからず復号できない、復旧後の整合性確認が曖昧で再開判断ができない、といった問題です。 本記事では、PostgreSQLの Point-in-Time Recovery(PITR)を、机上ではなく本番レベルで回すための実装手順を解説します。pgBackRest を例にしていますが、考え方は他ツールでも共通です。 1. PITRの前提: 3つ揃わないと復旧できない PITRは次の3要素で成立します。 ベースバックアップ(フルまたは差分) WALアーカイブ(継続的) 目標時刻情報(いつまで戻すか) どれか1つでも欠けると成立しません。特に本番で多いのは「WALが途中で消えていた」ケースです。S3保存していても、ライフサイクル設定や権限変更で欠落することがあります。 2. まず決めるべきRTO/RPO 技術論の前に、業務要件を決めます。 RTO(復旧に許容される時間): 例 60分 RPO(失ってよいデータ時間): 例 5分 この2つで設計が変わります。 RPO 5分以内ならWALアーカイブ遅延監視が必須 RTO 60分以内なら復旧訓練を定期実施し、手順を自動化する必要あり 要件不明のまま「毎日バックアップ」だけ実施しても、障害時に役立たないことが多いです。 3. 推奨アーキテクチャ(単一リージョンの最小構成) DBサーバ: PostgreSQL 15/16 バックアップツール: pgBackRest 保存先: S3互換ストレージ(バージョニングON) 監視: Prometheus + Alertmanager 復旧先: 別ホスト(本番と同一ネットワーク) 重要なのは、本番DBと別ホストで実際に復旧できること を定期検証する点です。 4. 実装手順(pgBackRest) 4.1 PostgreSQL設定 postgresql.conf 例: wal_level = replica archive_mode = on archive_command = 'pgbackrest --stanza=main archive-push %p' max_wal_senders = 10 wal_compression = on archive_command は失敗時に非0を返す必要があります。ここが曖昧だとWAL欠落に気づけません。 ...

March 6, 2026 · 2 min · AI2CORE 編集部

Kubernetes環境でDBスキーマ変更を止めずに進める:ゼロダウンタイム移行の実践戦略

Kubernetes環境でDBスキーマ変更を止めずに進める:ゼロダウンタイム移行の実践戦略 「カラムを追加するだけだから大丈夫」──この油断が、本番障害の入口になります。Kubernetes のように複数バージョンの Pod が同時に存在する環境では、DB スキーマ変更はアプリ変更よりも慎重に扱う必要があります。 本記事では、Expand-Contract パターンを中心に、ゼロダウンタイムを目指すための具体手順を解説します。実際の運用では、DDLの速さより「互換性のある期間をどう作るか」が勝負です。 1. なぜKubernetesでDB移行が難しいのか Kubernetesでは、ローリングアップデート中に新旧Podが混在します。つまり次の状態が同時に発生します。 新アプリは新スキーマを期待 旧アプリは旧スキーマしか知らない DBは1つしかない このとき破綻するのが「破壊的変更を先に適用する」ケースです。たとえば旧カラムを即削除すると、旧Podがエラーを連発します。 2. 基本戦略:Expand → Migrate → Contract ゼロダウンタイム移行の原則はこの3段階です。 Expand: 互換性を壊さない変更を先に入れる(新カラム追加など) Migrate: アプリを段階的に切替え、データを移行する Contract: 旧仕様を最終削除する(十分な監視後) この順序なら、どの時点でも旧新どちらのアプリも動作可能にできます。 3. 具体例:users.full_name を first_name / last_name へ分割 3.1 Expand フェーズ まず破壊的でないDDLを適用します。 1 2 ALTER TABLE users ADD COLUMN first_name text; ALTER TABLE users ADD COLUMN last_name text; この時点で旧アプリは full_name を使い続けられます。新アプリは新カラムに対応した実装を持っていても、まだ必須にしません。 3.2 アプリを「両対応」にする 書き込み時は両方へ保存(dual write)し、読み込み時は新カラム優先 + 旧カラムフォールバックにします。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def save_user_name(user_id: str, full_name: str): first, last = split_name(full_name) db.execute( """ UPDATE users SET full_name = %s, first_name = %s, last_name = %s WHERE id = %s """, (full_name, first, last, user_id), ) def read_user_display_name(row): if row.first_name and row.last_name: return f"{row.first_name} {row.last_name}" return row.full_name この両対応期間を作るのが、ゼロダウンタイムの本質です。 ...

March 5, 2026 · 2 min · AI2CORE 編集部

PostgreSQL接続プール枯渇の実戦対処:再発防止までつなげる調査・改善プレイブック

PostgreSQL接続プール枯渇の実戦対処:再発防止までつなげる調査・改善プレイブック 本番障害でよくあるのが、too many clients already や remaining connection slots are reserved です。アプリ側から見ると「急にDBに繋がらない」、ユーザー側から見ると「全機能が遅い・失敗する」という最悪の体験になります。 厄介なのは、接続枯渇が「DBサーバー性能不足」だけで起こるわけではない点です。リーク、タイムアウト設定、長時間トランザクション、プールサイズ不整合など、複数要因が重なって起きます。 この記事では、接続枯渇に対して 発生時の初動 → 根本原因の特定 → 恒久対策 の順で、手順を実務レベルでまとめます。 1. まず初動:サービス継続を優先する 障害対応では、完璧な原因究明より「止血」が先です。以下を順番に実施します。 直近リリース有無を確認(機能フラグ含む) アプリの接続数・待機数・エラー率を確認 DB側で pg_stat_activity を取得 長時間実行クエリを必要に応じて停止 一時的にアプリ Pod 数を制限して雪だるま増幅を止める pg_stat_activity の基本クエリ: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT pid, usename, application_name, client_addr, state, wait_event_type, wait_event, now() - query_start AS query_duration, now() - xact_start AS xact_duration, left(query, 120) AS query_head FROM pg_stat_activity WHERE datname = current_database() ORDER BY xact_start NULLS LAST, query_start NULLS LAST; ここで見るべきは、state='idle in transaction' と異常に長い xact_duration です。これがあるとコネクションを握ったまま解放されず、枯渇の引き金になります。 ...

March 5, 2026 · 2 min · AI2CORE 編集部

GitHub Actions OIDCで実現する鍵レス本番デプロイ:漏えい事故を減らす実装プレイブック

GitHub Actions OIDCで実現する鍵レス本番デプロイ:漏えい事故を減らす実装プレイブック CI/CD の事故は、ビルドが失敗することより「漏えいしても気づけない鍵」が残り続けることのほうが深刻です。特に AWS_ACCESS_KEY_ID のような長期シークレットを GitHub Secrets に保存し続ける運用は、便利ですがリスクが高いです。 本記事では、GitHub Actions の OIDC(OpenID Connect)連携を使って、長期鍵を使わずに AWS へデプロイする実践手順をまとめます。単なる設定紹介ではなく、最小権限・ブランチ制限・監査ログ設計まで含めて、明日から本番投入できる形で説明します。 1. まず何が危険なのか:長期シークレット運用の限界 従来構成では、次のような問題が起きます。 Secret が漏れても検知が遅い(CIログ、誤コミット、権限の広いメンバー) ローテーションが後回しになる 1つの鍵で複数環境へアクセスできてしまう 「誰のどの workflow 実行が何をしたか」が追いにくい OIDC 連携では、GitHub が発行する短命トークンを信頼し、AWS 側で一時認証情報を払い出します。つまり、保管する鍵そのものを減らすのが最大の価値です。 2. 全体アーキテクチャ 基本フローは以下です。 GitHub Actions ジョブが OIDC トークンを取得 AWS IAM の OIDC プロバイダとロール信頼ポリシーで検証 条件に一致したジョブだけ AssumeRoleWithWebIdentity 一時クレデンシャルで S3/CloudFront/ECR/ECS へデプロイ ポイントは「GitHub 側の workflow 制御」だけでなく、AWS 側で repo・branch・workflow を強制することです。 3. AWS 側の初期設定(OIDC Provider + IAM Role) 3.1 OIDC Provider を作成 CLI 例(すでに存在する場合はスキップ): 1 2 3 4 aws iam create-open-id-connect-provider \ --url https://token.actions.githubusercontent.com \ --client-id-list sts.amazonaws.com \ --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1 3.2 信頼ポリシーを厳密化する 以下のように sub と aud を必ず絞ります。 ...

March 5, 2026 · 3 min · AI2CORE 編集部

FastAPI認証・認可の本番設計:JWT運用、権限制御、監査ログまで含めた実装パターン

FastAPI認証・認可の本番設計:JWT運用、権限制御、監査ログまで含めた実装パターン FastAPI は実装が速い反面、認証・認可を最小構成のまま本番に出してしまい、後からセキュリティ事故に発展するケースが少なくありません。特に「JWT を入れたから安全」という誤解は危険です。 本記事では、開発速度を落とさずに本番で耐える認証基盤を作るための設計を、コード例と運用手順込みで解説します。 1. 認証と認可を分離して設計する 最初に押さえるべきは責務分離です。 認証(Authentication): 誰かを確認する 認可(Authorization): 何をしてよいか判定する この2つを混ぜると、実装も監査も破綻します。FastAPI では dependency を分け、get_current_user と require_permission を独立させるのが基本です。 2. JWT は「短命 + リフレッシュ + 失効管理」で使う アクセストークンを長寿命にすると、漏えい時の被害が大きくなります。実運用では以下が標準です。 Access Token: 5〜15分 Refresh Token: 7〜30日 Refresh Token は DB 保存し、ローテーション時に旧トークンを失効 sub だけでなく、jti(トークンID)や scope を持たせると管理しやすくなります。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from datetime import datetime, timedelta, timezone import jwt ALGORITHM = "HS256" def create_access_token(user_id: str, scopes: list[str], secret: str) -> str: now = datetime.now(timezone.utc) payload = { "sub": user_id, "scope": " ".join(scopes), "iat": int(now.timestamp()), "exp": int((now + timedelta(minutes=10)).timestamp()), "jti": "generated-uuid" } return jwt.encode(payload, secret, algorithm=ALGORITHM) 3. 鍵管理とローテーション 秘密鍵を .env に固定して数年運用するのは典型的な事故パターンです。最低限、次を実施します。 ...

March 4, 2026 · 2 min · AI2CORE 編集部