EKS向けAWS CDK:現実世界のマルチアカウントKubernetesデプロイメントにおける不備
AWS Cloud Development Kit (CDK) は、使い慣れたプログラミング言語を使用してクラウドインフラストラクチャのプロビジョニングを簡素化することを目的としています。そのEKSモジュールはKubernetesクラスターの作成と管理を合理化することを約束していますが、詳しく見ると、特に実用的なマルチアカウントEKSデプロイメントを考慮する場合、重大な欠点が明らかになります。この記事では、これらの制限について詳しく説明し、AWS CDKの現在のEKS実装、特に Cluster.addManifest 関数が、共有マルチアカウントEKS戦略を採用する組織にとって真に役立たないと主張します。
単純さの幻想:Cluster.addManifestとそのアカウント境界
CDKの Cluster.addManifest(id: string, ...manifest: Record<string, any>[]): KubernetesManifest 関数は、KubernetesマニフェストをEKSクラスターにデプロイするための簡単な方法を提供しているように見えます。しかし、EKSクラスターが複数のAWSアカウント間で共有されるように設計されている現実世界のシナリオを考慮すると、この単純さは欺瞞的です。
実際には、中央のEKSクラスターは、別々のAWSアカウントに存在するさまざまなチームやアプリケーションによって共有されることがよくあります。このマルチアカウントアプローチは、セキュリティ、分離、およびコスト管理にとって不可欠です。しかし、Cluster.addManifest は、単一のアカウントとリージョンでのデプロイメントという暗黙の仮定の下で動作します。
この制限の証拠:
- CDK設計における暗黙の同一アカウントの仮定: AWS CDKのコアコンストラクトとIAMロール管理は、本質的に単一のAWSアカウント内でのデプロイメント用に設計されています。KubernetesManifest のCDKドキュメントではクロスアカウントデプロイメントを明示的に禁止していませんが、その例と基礎となるメカニズムは単一アカウントのユースケース向けです。
- クロスアカウントIAMの複雑さ: 異なるアカウントのEKSクラスターにマニフェストをデプロイするには、複雑なクロスアカウントIAMロール構成が必要です。CDKでのクロスアカウントリソースアクセスに関するStack Overflowの議論で強調されているように、CloudFormationに依存するCDKは、アカウント間でリソースを管理する際に固有の課題に直面します。Cluster.addManifest は、必要なクロスアカウントIAMロールの引き受けを自動的に処理しないため、共有EKS環境での使用が面倒になります。
- AWSのベストプラクティスはマルチアカウントEKSを推奨: AWS自体が、Amazon EKSのマルチアカウント戦略に関する公式ドキュメントで概説されているように、EKSのマルチアカウント戦略を推奨しています。このドキュメントでは、VPCサブネットを共有し、IAM Roles for Service Accounts (IRSA) を活用して安全なクロスアカウントアクセスを実現する方法について詳しく説明しています。これらのベストプラクティスとCluster.addManifestの制限との間の著しい対照は、現実世界のEKSデプロイメントに対するツールの不備を浮き彫りにしています。
ネットワーク基盤の無視:適切な配管のない家
真に実用的なEKSソリューション、特にマルチアカウント設定では、堅牢なネットワーク基盤が不可欠です。これには通常、以下が含まれます。
- トランジットゲートウェイ: 異なるアカウント間でVPC間の安全でスケーラブルな接続を確立するため。
- VPC共有: 複数アカウントが中央のVPCとそのサブネット(多くの場合EKSクラスターをホストする)を共有できるようにするため。
- プライベートサブネット: セキュリティを強化し、マニフェストのデプロイメントとアプリケーションのワークロードがプライベートネットワークセグメント内で動作するようにするため。
しかし、aws-quickstart/cdk-eks-blueprintsのようなブループリントを含むAWS CDKのEKS実装は、この重要なネットワークレイヤーを見落としたり単純化したりすることがよくあります。これらのツールはEKSクラスターの作成やVPCプロビジョニングを自動化するかもしれませんが、EKSデプロイメントプロセスの不可欠な部分としてトランジットゲートウェイやVPC共有を設定するための包括的で自動化されたソリューションを提供するという点では、しばしば不十分です。
現実世界のEKSアーキテクチャでは、ネットワークレイヤーは後付けではありません。それは、安全でスケーラブルなマルチアカウントKubernetes環境を構築するための基盤です。CDKがネットワークの複雑さを抽象化しながらクラスター作成の簡素化に重点を置いているため、本番環境グレードの共有EKSデプロイメントには不向きなソリューションが生まれます。
トークン解決の失敗:損なわれたCDKの約束
CDKの強みは、トークンの使用にあります。トークンはデプロイメント中に解決されるプレースホルダーであり、動的な構成とリソース参照を可能にします。しかし、Cluster.addManifest はこれらのトークンを適切に解決できず、その実用性をさらに妨げています。
CDKトークンは、単一のCDKアプリケーションとCloudFormationスタックのスコープ内で解決されるように設計されています。Cluster.addManifest を使用してクラスターにデプロイされたマニフェスト内のリソースからトークンを使用しようとすると、トークン解決がしばしば失敗します。CDKのデフォルトのトークン解決メカニズムは、アカウント境界を越えるようには設計されていません。
この制限により、ユーザーはCDKのエレガントなトークンベースのアプローチを放棄し、VPC ID、サブネットID、セキュリティグループIDなどの具体的な値をコンテキストパラメータまたは環境変数としてCDKアプリケーションに手動で渡すという手段に頼らざるを得なくなります。この手動の値の受け渡しは、エレガントでないだけでなく、エラーの機会を増やし、そもそもCDKを使用する全体的な利点を減少させます。
解決策はネットワーキングから始まる:
前提条件:クラウドにおけるアプリケーション中心のインフラストラクチャの採用 1
- 環境: 物理的な場所や種類に関係なく、ビジネス機能を提供するために必要なすべてのリソースを持つ、境界付けられたコンテキストとしてのすべての密結合された論理リソース—自己完結型の垂直スライス。
- Enver: 環境バージョン。異なるenverは、異なる構成値を持つ論理的/機能的に一貫しています。enverはユニットとしてデプロイおよびロールバックされます。
- サービスとしてのネットワーキング: ネットワークチームは、コードとライブラリを介して管理することにより、すべてのVPC関連リソース(元のテキスト/図では赤でマークされています)を所有します。ネットワーキングアカウントは複数のネットワーキングenverを実行し、各ネットワーキングenverはIPAM、トランジットゲートウェイとNATを備えたVPCをワークスペースアカウントと共有し、共有IPAMからCIDRの範囲を取得し、ワークロードenverにVPCをデプロイする際にNATと内部ネーミングシステムを共有します。各VPCは1つのトランジットゲートウェイにのみ接続できるため、VPCとその内部のリソースはTGWを介して接続されますが、異なるTGWに接続されたVPCは物理的に切断されます。
- サービスとしてのRDS: DBチームは、DB/スキーマ/ロール/ユーザーを定義/所有/制御する他のenverのDBをホストするDBクラスターを所有します。
- サービスとしてのEKS: k8sチームは、k8sマニフェストとサービスアカウントからIAMロールへのマッピングを定義/所有/制御する他のenverのコンテナオーケストレーションをホストするEksクラスターを所有します。
- EC2、MSK、Opensearch、ECS、ElasticCache、Redshift、プライベートリンクなども同様です...
この図では:
(元のテキストはここにはない図を参照しています)
AWSアカウントネットワーキングは、NT Enver LEとNT Enver Prodという2つの分離されたenverを実行しています。NT Enver Prodを例にとると:
- 複数のアカウント(同じリージョン)にまたがる複数のVPCを接続する1つのトランジットゲートウェイ。
- 接続されているすべてのVPCとインターネットを共有するための1つのNAT。
- IPの競合を避けるために、接続されているすべてのVPCのサブネット用の1つのIPAMとCIDRプール。
- 表示されていないもの:サブネット、ルーティング、SG、DNS、ホストゾーン、組織、管理者委任、証明書など...
VPCをまたいでリソースをデプロイするためのプロキシとしての中央VPC:
- 異なるenverから異なるEKSクラスターにk8sマニフェストをデプロイするためのLambdaを実行します。
- 異なるenverから異なるRDSクラスターにDB/スキーマ/ロール/ユーザーをデプロイするためのLambdaを実行します。
VPCをまたいで接続するためのプロキシ/ハブとしての中央VPC:EKS内のポッド、ECS内のタスクが異なるenverから異なるRDSクラスター内の異なるデータベースに接続します:
Enver 1:
k8sマニフェストやデータベース関連リソース(DB、スキーマ、ロールなど)を含むすべてのリソース(緑色でマーク)を論理的に内部で宣言/制御し、トランザクションでデプロイまたはロールバックします。
- マニフェストはEKS Enver1のクラスターにデプロイされ、ポッドはIamロール1(SA/oidc経由)を引き受けてDynamoDBにアクセスします。
- データベース、スキーマ、ロール/ユーザーはRDS Enver Prodにデプロイされ、内部のECSタスクはiamロール2を引き受けてTGW経由でホストされているDBにアクセスします。
Enver 2:
すべてのリソース(紫色でマーク)も論理的に内部で宣言/制御し、マニフェストがEKS Enver1のクラスターにデプロイされた後、ポッドは次のようになります。
- IAMロールAを引き受けてトランジットゲートウェイ経由でDBにアクセスします(Enver 2ではVPCは不要です!)。
- IAMロールBを引き受けてファイル用のS3バケットにアクセスします。
プラットフォームがデプロイメントを処理するため、アプリとサービスはビジネスロジック/機能に集中できます:
- Enver 1とEnver 2で宣言されたk8sマニフェストは、中央アカウントのVPC-Prod内のLambda関数を介してEKSクラスターに送信されます。
- Enver 1とEnver 2で宣言されたDBスキーマ/ロール/ユーザーは、中央アカウントのVPC-Prod内のLambda関数を介してRDSクラスターに送信されます。
Enver中心設計の実用例
https://github.com/ondemandenv/spring-boot-swagger-3-example
このプロジェクトは、クラウドネイティブ開発に対するアプリケーション中心のアプローチを例示しており、すべてのリソース(アプリケーションコード、インフラストラクチャ、依存関係)が単一の垂直な「enver」として定義されています。これは、ユニットとしてデプロイ/ロールバックされる自己完結型の境界付けられたコンテキストです。
1. 垂直リソース所有権
このチュートリアルAPI機能を提供するために必要なすべてのリソースは同じ場所に配置されています。
- アプリケーションコード(src/)
- コンテナ定義(Dockerfile)
- Infrastructure-as-Code(CDK、cdk/内)
- IAMロールとポリシー
- データベーススキーマ定義
- サービスネットワーキング要件
2. バージョン管理されたユニットとしての環境
各「enver」には以下が含まれます。
- 論理リソース(S3バケット、IAMロール)
- 物理リソース参照(VPC ID、EKSクラスター名)
- 構成値(S3バケット名、OIDCプロバイダーARN)
- サービス間依存関係(DBスキーマ、メッセージキュー)
3. プラットフォームサービス抽象化
(元のテキストにはここにさらに多くのコンテンツが含まれている可能性があります)
重要な統合ポイント
1. IAMロールバインディング(CDKスタック)
// cdk/lib/cdk-stack.ts
const podSaRole = new Role(this, 'podSaRole', {
assumedBy: new FederatedPrincipal(
myEnver.oidcProvider.getSharedValue(this), // From platform enver
{
StringEquals: {
[`${oidcProvider}:aud`]: 'sts.amazonaws.com',
[`${oidcProvider}:sub`]: `system:serviceaccount:${namespace}:${serviceAccountName}`
}
},
'sts:AssumeRoleWithWebIdentity'
)
});
2. 環境対応構成
// src/main/java/com/bezkoder/spring/swagger/config/OpenAPIConfig.java
@Value("${aws.s3.bucket-name}")
private String bucketName; // Injected from enver-specific config
@Bean
public S3Client s3Client() {
return S3Client.builder()
.credentialsProvider(WebIdentityTokenFileCredentialsProvider.create())
.build(); // Auto-utilizes IRSA credentials
}
3. インフラストラクチャの一貫性
// cdk/lib/cdk-stack.ts
new cdk8splus.Deployment(chart, 'to-eks', {
containers: [{
image: ContainerImage.fromEcrRepository(
Repository.fromRepositoryName(this, 'repo',
myEnver.appImgRepoRef.getSharedValue(this)), // Shared ECR
Fn.select(0, Fn.split(',', // Git SHA-based tagging
myEnver.appImgLatestRef.getSharedValue(this)))
),
envVariables: {
bucket_arn: {value: bucket.bucketArn}, // Enver-owned bucket
region: {value: this.region} // Inherited from platform
}
}]
});
主な利点
- アトミックなデプロイメント: すべてのリソース(アプリ+インフラ)はCloudFormationスタックの更新を使用して一緒にデプロイ/ロールバックされます。
- アカウントに依存しない: enverのCDKコードは、サンドボックス/本番アカウントにデプロイする場合でも変更されません。物理的なアカウントの詳細はenverコンテキストを通じて解決されます。
- デフォルトで最小権限によるセキュリティ。
明らかに、設計と実装全体がプラットフォームに依存します。