kubectl execをAPIゲートウェイ代わりに使う — エンタープライズKubernetesでの実践パターン

kubectl execをAPIゲートウェイ代わりに使う — エンタープライズKubernetesでの実践パターン

エンタープライズKubernetesでIngress/LBがない環境で、kubectl execをAPIゲートウェイ代わりに使い、Kafka・Elasticsearch・MySQLなどクラスタ内サービスにアクセスするパターンをTypeScriptのBuilderクラスで実装した方法を紹介します。
2026.07.26

はじめに

エンタープライズ環境のKubernetesクラスタでは、外部からのAPI公開が制限されていることがあります。Kafka、Elasticsearch、内部REST APIなどに外部からアクセスできない場合、kubectl execでPod内に入ってcurlやkafka-console-producerを実行するというパターンが有効です。

「ハック的」に聞こえるかもしれませんが、実際にはセキュリティ上の利点もあります。本記事では、このパターンをTypeScriptのBuilderクラスで保守しやすくした方法を紹介します。

前提・環境

  • Kubernetes(kubectl execが使える環境)
  • Node.js + TypeScript
  • SSH経由でリモートサーバーに接続し、そこからkubectl execを実行

なぜkubectl execを使うのか

エンタープライズKubernetesでは以下のような制約があります。

  • 内部APIにIngress/LoadBalancerが設定されていない
  • Kafkaブローカーがクラスタ内ネットワークのみでアクセス可能
  • Elasticsearchが外部公開されていない
  • VPNを使ってもService IPに直接到達できない

この場合、クラスタ内のPodからcurlkafka-console-producerを実行するのが最も確実なアクセス方法です。

kubectl-exec-as-api-gateway-pattern-access-flow

Kafkaへのイベント送信

Kafkaブローカーはクラスタ内部からのみアクセス可能です。Pod内でkafka-console-producerを実行してイベントを送信します。

Kafka.ts
class Kafka {
  private podName?: string;

  public async runEvent(config: EventConfig): Promise<void> {
    await this.checkPodname();

    const events = this.getEvents(config);

    for (const event of events) {
      const eventString = JSON.stringify(event).replace(/"/g, '\\"');

      const cmd = [
        `kubectl exec`,
        `${this.podName}`,
        `-- sh -c "unset KAFKA_JMX_OPTS`,  // JMX競合を回避
        `&&`,
        `echo '${eventString}'`,
        `|`,
        `kafka-console-producer.sh`,
        `--topic YOUR_TOPIC_NAME`,
        `--broker-list kafka-broker-0:9092"`,
      ].join(" ");

      await Remote.execRemote(cmd);
    }
  }

unset KAFKA_JMX_OPTSはPod内でJMXポートの競合を避けるための回避策です。Pod内にすでにKafkaブローカーが動いている場合、同じJMX設定でプロデューサーを起動しようとすると競合します。

MySQLへのクエリ

内部データベースへのクエリもkubectl exec経由です。

MySQL.ts
class MySQL {
  public async checkUser(userName: string): Promise<void> {
    const podName = (await Pod.getPodMySQL())[0] ?? "";
    const query = `USE mydb; SELECT id FROM records WHERE userName='${userName}';`;

    const cmd = [
      `kubectl exec -i`,  // -itではなく-i(非対話モード)
      `${podName}`,
      `-n ${this.namespace()}`,
      `-- mysqlsh`,
      `--user='${credentials.MYSQL_USERNAME}'`,
      `--password='${credentials.MYSQL_PASSWORD}'`,
      `-h ${podName}`,
      `-P ${ports.MYSQL_PORT}`,
      `--sql -e "${query}"`,
    ].join(" ");

    const result = await Remote.execRemote(cmd);
    // テキスト出力をパースしてテーブル表示
    const resultArray = result.split("\n").filter(Boolean);
    const key = resultArray[0] || "id";
    const values = resultArray.slice(1);
    console.table(values.map((value) => ({ [key]: value })));
  }
}

注意点:

  • kubectl exec -it(対話モード)ではなく-iを使用。自動化ではtty割り当て不要で、-itだとエラーになる場合がある
  • mysqlshのテキスト出力をパースしているが、--result-format=jsonを使う方が堅牢

このパターンのトレードオフ

利点:

  • 外部公開なしでクラスタ内のあらゆるサービスにアクセスできる
  • 追加のIngressやポートフォワーディング設定が不要
  • RBAC(kubectl exec権限)で制御できる

欠点:

  • コマンド結果のパースが脆い(テキスト出力に依存)
  • シェルエスケープが複雑でバグの温床になりやすい
  • Pod名が動的に変わるため、事前にPod名の取得が必要

まとめ

kubectl execをAPIゲートウェイ代わりに使うパターンを紹介しました。

  • CommandBuilderでコマンド組み立てとエスケープを一元管理
  • Kafka、MySQLなどクラスタ内サービスへのアクセスに活用
  • 入力のサニタイズとコマンドインジェクション対策が必須

「正式な方法」ではないかもしれませんが、エンタープライズKubernetesの現実的な制約の中では有効なアプローチです。

この記事をシェアする

関連記事