LESSON 30分

ストーリー

あなた
DB接続文字列を変更したいんですが、20サービス全部にデプロイし直しですか?
高橋アーキテクト
設定をコードに埋め込んではいけない。分散環境では、設定を外部化して一元管理する仕組みが必要だ

なぜ分散設定管理が必要か

問題: 各サービスが個別に設定を持つ場合

  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 ConsulKV Store + ディスカバリ設定 + ディスカバリ統合
AWS AppConfig設定管理Feature Flags統合
LaunchDarklyFeature FlagsSaaS、高機能

ベストプラクティス

// 設定管理のベストプラクティスまとめ
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分