ストーリー
なぜ分散設定管理が必要か
問題: 各サービスが個別に設定を持つ場合
Service A (.env) Service B (.env)
DB_HOST=db-prod.com DB_HOST=db-prod.com
REDIS_HOST=redis.com REDIS_HOST=redis.com
LOG_LEVEL=info LOG_LEVEL=info
→ DB_HOSTが変わると、全サービスの.envを変更 + 再デプロイ
→ 設定の不整合リスク(サービスCだけ古い値)
The Twelve-Factor App: 設定の外部化
// BAD: コードに設定を埋め込む
const dbHost = "db-prod.internal.com";
// BAD: 環境固有の設定ファイル
// config/production.json を各サービスにバンドル
// GOOD: 環境変数から取得
const dbHost = process.env.DB_HOST;
// BETTER: 設定サーバーから動的に取得
const config = await configServer.get("order-service", "production");
設定管理のパターン
1. 環境変数 + シークレット管理
// 基本的な設定管理
interface AppConfig {
// 環境変数から取得(起動時に注入)
port: number; // PORT=3000
logLevel: string; // LOG_LEVEL=info
dbHost: string; // DB_HOST=...
// シークレット管理サービスから取得
dbPassword: string; // AWS Secrets Manager / HashiCorp Vault
apiKeys: string[]; // 暗号化されたストアから取得
}
// AWS Secrets Managerからシークレットを取得
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
async function getSecret(secretName: string): Promise<string> {
const client = new SecretsManagerClient({ region: "ap-northeast-1" });
const command = new GetSecretValueCommand({ SecretId: secretName });
const response = await client.send(command);
return response.SecretString!;
}
2. 集中設定サーバー
// 集中設定サーバーの概念
interface ConfigServer {
// サービス名 + 環境名で設定を取得
get(serviceName: string, env: string): Promise<ServiceConfig>;
// 設定変更を監視(リアルタイム更新)
watch(serviceName: string, callback: (config: ServiceConfig) => void): void;
// 設定を更新
update(serviceName: string, env: string, config: Partial<ServiceConfig>): Promise<void>;
}
// 使用例
const configClient = new ConfigClient("http://config-server:8888");
// 起動時に設定を取得
const config = await configClient.get("order-service", "production");
// 設定変更をリアルタイムで監視
configClient.watch("order-service", (newConfig) => {
console.log("設定が更新されました:", newConfig);
// ホットリロード: 再起動なしに設定を反映
updateLogLevel(newConfig.logLevel);
refreshConnectionPool(newConfig.dbConfig);
});
3. Feature Flags
// Feature Flagsによる動的な機能切り替え
interface FeatureFlagService {
isEnabled(flagName: string, context?: UserContext): Promise<boolean>;
}
// 使用例
async function processPayment(order: Order): Promise<PaymentResult> {
const useNewGateway = await featureFlags.isEnabled("new-payment-gateway", {
userId: order.userId,
region: order.region,
});
if (useNewGateway) {
// 新しい決済ゲートウェイ(段階的にロールアウト)
return await newPaymentGateway.charge(order);
} else {
// 既存の決済ゲートウェイ
return await legacyPaymentGateway.charge(order);
}
}
設定の階層構造
優先度(高い方が勝つ):
1. 環境変数(デプロイ時に上書き)
2. サービス固有設定(order-service/production)
3. 環境共通設定(shared/production)
4. デフォルト設定(shared/default)
例:
shared/default:
log_level: info
timeout: 5000
shared/production:
log_level: warn
order-service/production:
timeout: 10000 ← 注文サービスだけタイムアウト長め
結果(order-service in production):
log_level: warn ← shared/productionから
timeout: 10000 ← order-service/productionから
主要なツール
| ツール | タイプ | 特徴 |
|---|---|---|
| AWS Systems Manager Parameter Store | パラメータストア | AWS統合、無料枠あり |
| AWS Secrets Manager | シークレット管理 | 自動ローテーション |
| HashiCorp Vault | シークレット管理 | 動的シークレット |
| HashiCorp Consul | KV Store + ディスカバリ | 設定 + ディスカバリ統合 |
| AWS AppConfig | 設定管理 | Feature Flags統合 |
| LaunchDarkly | Feature Flags | SaaS、高機能 |
ベストプラクティス
// 設定管理のベストプラクティスまとめ
const bestPractices = {
// 1. 設定とコードを分離
separation: "設定はコードに含めず外部化する",
// 2. シークレットは暗号化
encryption: "DB パスワード等は専用のシークレット管理サービスを使用",
// 3. 環境ごとの設定分離
envSeparation: "dev / staging / production で設定を分離",
// 4. 設定変更の監査ログ
auditLog: "誰が、いつ、何を変更したかを記録",
// 5. 設定のバリデーション
validation: "不正な設定値を起動時に検出",
// 6. ホットリロード対応
hotReload: "再起動なしに設定変更を反映(可能な設定のみ)",
};
まとめ
| ポイント | 内容 |
|---|---|
| 外部化の原則 | Twelve-Factor App: 設定をコードから分離 |
| 3つのパターン | 環境変数、集中設定サーバー、Feature Flags |
| 階層構造 | デフォルト → 環境共通 → サービス固有 → 環境変数 |
| シークレット管理 | 専用サービス(Vault, Secrets Manager)を使用 |
チェックリスト
- 設定を外部化する理由を説明できる
- 集中設定サーバーの仕組みを理解した
- Feature Flagsの使い方を理解した
- シークレット管理のベストプラクティスを説明できる
次のステップへ
Step 2の知識を統合する演習に進みます。実際にマイクロサービスアーキテクチャを設計してみましょう。
推定読了時間: 30分