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 この両対応期間を作るのが、ゼロダウンタイムの本質です。 ...