午前2時14分、PagerDutyの呼び出し音
午前2時14分、スマートフォンの呼び出し音が鳴り響きます。最新のAPIデプロイメントがクラッシュループに陥り、PagerDutyのアラートが眠りを妨げました。ログを確認すると、おなじみの悪夢のようなエラーが表示されています:ERROR: column "last_login_at" of relation "users" does not exist(エラー:「users」リレーションに「last_login_at」カラムが存在しません)。
誰かがマイグレーション・スクリプトの実行を忘れたのかもしれません。あるいはもっと悪いことに、スクリプトの全10ステップのうち4番目で失敗し、本番データベースが中途半端に壊れた状態になっている可能性もあります。FlywayやLiquibaseのような従来のツールは強力ですが、命令型のモデルで動作します。これらは、バージョン管理されたスクリプト(001, 002, 003…)の厳格な順序に依存しています。もし1つのステップが失敗したり、誰かが本番環境で手動変更を加えたりすれば、パイプライン全体が停止してしまいます。
現代のKubernetes環境では、デプロイメントからネットワーク・ポリシーに至るまで、ほぼすべてを「宣言的」に管理しています。データベース・スキーマも例外であってはなりません。SchemaHeroはこのパラダイムを転換します。ALTER TABLEスクリプトを書く代わりに、YAMLファイルでテーブルの「あるべき状態」を定義します。SchemaHeroが差分の抽出と実行を処理します。このモデルに移行して以来、マイグレーション関連のデプロイ失敗を80%以上削減しつつ、GitOpsワークフローをクリーンに保っているチームをいくつも見てきました。
インストール:Operatorのデプロイ
SchemaHeroは、Kubernetes OperatorとCLIプラグインの組み合わせで動作します。Operatorはクラスター内の変更を監視し、CLIは既存のデータベースからスキーマを生成するのを手助けします。
まず、Krew経由でkubectlプラグインをインストールします。このツールは、マイグレーションの検査や、現在の環境からのYAML定義の生成に不可欠です:
kubectl krew install schemahero
次に、クラスターでOperatorを実行する必要があります。CLIから直接インストールすることもできますが、本番環境ではHelmを使用することを強くお勧めします。これにより、インストール自体がバージョン管理され、再現性が確保されます:
helm repo add schemahero https://charts.schemahero.io
helm install schemahero schemahero/schemahero \
--namespace schemahero-system \
--create-namespace
一度 schemahero-system 内のPodが正常に起動すれば、クラスターの準備は完了です。Operatorは、DatabaseとTableという2つの主要なカスタムリソース定義(CRD)を監視するようになります。
設定:SQLをコードとして扱う
SchemaHeroは「過程」ではなく「目的地」を重視します。最終的な状態を定義すれば、Operatorがそこに到達するための経路を計算します。
1. インスタンスへの接続
まず、Databaseオブジェクトを定義します。これはSchemaHeroに対し、データベースの場所と認証方法を伝えます。PostgreSQL、MySQL、CockroachDBをサポートしています。以下はPostgreSQLインスタンスの標準的な設定です:
apiVersion: databases.schemahero.io/v1alpha4
kind: Database
metadata:
name: app-db
namespace: storage
spec:
connection:
postgres:
uri:
valueFrom:
secretKeyRef:
name: db-credentials
key: uri
接続文字列は安全に管理してください。db-credentialsシークレットには、postgres://user:password@postgres-svc:5432/appdbのようなURIを含める必要があります。SchemaHeroはこの接続を使用して「ドリフト検出(drift detection)」を行い、YAMLと実際のデータベースの状態を比較します。
2. テーブル構造の定義
.sqlファイルのことは忘れてください。これからはテーブルをTableオブジェクトとして定義します。last_login_atカラムを追加する必要がある場合、ALTER文を書くのではなく、YAMLスペックにカラムを追加するだけです。
apiVersion: schemas.schemahero.io/v1alpha4
kind: Table
metadata:
name: users
namespace: storage
spec:
database: app-db
name: users
schema:
postgres:
primaryKey: [id]
columns:
- name: id
type: integer
constraints:
notNull: true
- name: username
type: varchar(255)
constraints:
notNull: true
- name: last_login_at
type: timestamp with time zone
constraints:
nullable: true
このYAMLを適用すると、SchemaHeroは単にコードを闇雲に実行するのではなく、プラン(実行計画)を生成します。カラムがすでに存在すれば何もしません。型が異なれば、変換を計画します。
安全第一:検証と承認
データベース変更の自動化は、幼児にチェーンソーを渡すような不安を感じさせるかもしれません。事故を防ぐために、SchemaHeroは変更ごとにMigrationオブジェクトを作成します。これは、生成されたSQLがデータに適用される前に内容をレビューできるステージング領域として機能します。
保留中の変更ステータスは、プラグインで確認できます:
kubectl schemahero get migrations -n storage
SchemaHeroが実行しようとしているSQLを正確に確認するには、マイグレーションの詳細を表示します:
kubectl schemahero describe migration <migration-name> -n storage
ArgoCDを使用した厳格なGitOps構成では、本番環境のマイグレーションに手動承認を必要とするようSchemaHeroを設定できます。開発環境やステージング環境では、immediateDeploy: trueに設定してパイプラインを高速に回すことができます。もしマイグレーションが失敗した場合(例えば、すでに50万行のNULLデータがあるテーブルにNOT NULL制約を追加しようとした場合など)、OperatorはMigrationオブジェクトのステータスにエラーを報告します。その際、YAMLを更新してデフォルト値を含めるように修正し、再度プッシュすればよいのです。
構成ドリフトの終焉
SchemaHeroは標準的なKubernetes의パターンに従っているため、これらの変更の監視は簡単です。OperatorのログをLokiに転送したり、失敗したMigrationオブジェクトに対してPrometheusアラートを設定したりできます。Operatorのヘルスチェックを行うには、次のコマンドを実行します:
kubectl logs -n schemahero-system -l app.kubernetes.io/name=schemahero
ここでの本当の強みは「一貫性」です。もしジュニアDBAが本番環境で手動でカラムを削除したとしても、SchemaHeroの次のリコンシリエーション・ループ(状態回復ループ)がそのドリフトを検知します。実際の状態がGitリポジトリと一致していないことを認識し、欠落したカラムを自動的に再作成します。データベースを単なるKubernetesリソースの1つとして扱うことで、ストレスの多いマイグレーションを単なる「日常的な出来事」に変えることができます。これにより、インフラの回復力が高まり、そして何より、夜ぐっすり眠れるようになるのです。

