はじめに
皆さん、こんにちは。
私は業務でデータ利活用基盤を取り扱っているため、dbtやIceberg、そしてAWS GenUに取り組む必要があると考えています。特に AWS Japan Top Engineer として、GenUを扱い、その活用を広めることが責務だと感じています。
しかし、私はこれまで CloudFormation を好んで使っており、(逆張り思考も重なって)Cfn テンプレートをシンプルかつ汎用性・拡張性の高い形で作ることに注力してきました。そのため、改めてGenU の CDK コードを読もうとしても、なかなか理解が進みませんでした。
そこで、CDK を学びながら、その過程を記事としてまとめることにしました。
前回までのおさらい
前回までで、以下が完了しました。
今回は GenU 内の CDK スタックを確認していきたいと思います。
GenU アーキテクチャについて
2025/3/16 現在、クイックスタートに記載されているアーキテクチャは以下の通りです。
このアーキテクチャを CDK のソースコードと照らし合わせながら見ていきます。
デプロイコマンドの確認
まずはデプロイコマンドです。
最初にnpm ci
コマンドを実行します。
npm ci
コマンドについて、npm Docs の説明文を訳すと以下の通りです。
このコマンドは npm install と似ているが、テストプラットフォーム、継続的インテグレーション、デプロイなどの自動化された環境で使うことを意図している。
npm install と npm ci の主な違いは以下の通り:
- プロジェクトに既存の package-lock.json か npm-shrinkwrap.json があること。
- package-lock.json 内の依存関係が package.json 内の依存関係と一致しない場合、 npm ci はパッケージロックを更新する代わりにエラーで終了します。
- npm ci は、一度にプロジェクト全体をインストールすることしかできません: 個々の依存関係をこのコマンドで追加することはできません。
- 既に node_modules が存在する場合は、npm ci がインストールを始める前に自動的に削除されます。
- package.json や package-lock に書き込むことはありません: インストールは基本的に凍結されます。
つまり、package-lock.json のみを利用してクリーンインストールするコマンドになります。
こちらは大人しく実行しておきましょう。
npm ci
次に、以下のデプロイコマンドを実行せよと記載されています。
# 通常デプロイ
npm run cdk:deploy
# 高速デプロイ (作成されるリソースを事前確認せずに素早くデプロイ)
npm run cdk:deploy:quick
この実行コマンドは前回確認した通り、以下の通りです。
{
"name": "generative-ai-use-cases-jp",
"private": true,
"version": "3.0.0",
"scripts": {
"lint": "run-s root:lint web:lint cdk:lint",
"test": "run-s web:test",
"root:lint": "npx prettier --write .",
"web:devw": "source ./setup-env.sh ${npm_config_env} && VITE_APP_VERSION=${npm_package_version} npm -w packages/web run dev",
"web:devww": "powershell ./web_devw_win.ps1",
"web:dev": "VITE_APP_VERSION=${npm_package_version} npm -w packages/web run dev",
"web:build": "VITE_APP_VERSION=${npm_package_version} npm -w packages/web run build --",
"web:build:analyze": "VITE_APP_VERSION=${npm_package_version} npm -w packages/web run build -- --mode analyze",
"web:lint": "npm -w packages/web run lint",
"web:test": "npm -w packages/web run test",
+ "cdk:deploy": "npm -w packages/cdk run cdk deploy -- --all",
+ "cdk:deploy:quick": "npm -w packages/cdk run cdk deploy -- --all --asset-parallelism --asset-prebuild=false --concurrency 3 --method=direct --require-approval never --force",
"cdk:deploy:quick:hotswap": "npm -w packages/cdk run cdk deploy -- --all --asset-parallelism --asset-prebuild=false --concurrency 3 --method=direct --require-approval never --force --hotswap",
"cdk:destroy": "npm -w packages/cdk run cdk destroy --",
"cdk:lint": "npm -w packages/cdk run lint",
"cdk:test": "npm -w packages/cdk run test",
"cdk:test:update-snapshot": "npm -w packages/cdk run test -- --update-snapshot",
"extension:ci": "cd browser-extension && npm ci",
"extension:dev": "cd browser-extension && npm run dev",
"extension:devw": "source ./setup-env.sh && cd browser-extension && npm run dev",
"extension:build": "cd browser-extension && npm run build",
"extension:buildw": "source ./setup-env.sh && cd browser-extension && npm run build",
"extension:lint": "npx prettier --write browser-extension/. && cd browser-extension && npm run lint",
"docs:dev": "mkdocs serve",
"docs:build": "mkdocs build",
"docs:gh-deploy": "mkdocs gh-deploy --"
},
"devDependencies": {
"npm-run-all": "^4.1.5",
"prettier": "^3.2.5"
},
"workspaces": [
"packages/*"
]
}
つまり、以下のコマンドを実行しています。
# npm run cdk:deploy
npm -w packages/cdk run cdk deploy -- --all
# npm run cdk:deploy:quick
npm -w packages/cdk run cdk deploy -- --all --asset-parallelism --asset-prebuild=false --concurrency 3 --method=direct --require-approval never --force
デプロイコマンド(2 層目)の確認
上記のデプロイコマンドは packages/cdk を Workspace としてrun cdk deploy --all # +オプション
を実行しています。
ここで--
は最上階の npm コマンドではなく、run cdk deploy
に--all
オプションをつけるという記法です。
packages/cdk/package.json の npm-scripts を確認します。
{
"name": "cdk",
"private": true,
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"test": "jest",
+ "cdk": "cdk",
"lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0",
"postinstall": "npm ci --prefix custom-resources"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.137",
"@types/jest": "^29.5.12",
"@types/node": "^20.12.5",
"@typescript-eslint/eslint-plugin": "^7.6.0",
"@typescript-eslint/parser": "^7.6.0",
"aws-cdk": "^2.154.1",
"eslint": "^8.56.0",
"jest": "^29.7.0",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "~5.4.5"
},
"dependencies": {
"@aws-cdk/aws-cognito-identitypool-alpha": "^2.154.1-alpha.0",
"@aws-cdk/aws-lambda-python-alpha": "^2.154.1-alpha.0",
"@aws-sdk/client-bedrock-agent": "^3.755.0",
"@aws-sdk/client-bedrock-agent-runtime": "^3.755.0",
"@aws-sdk/client-bedrock-runtime": "^3.755.0",
"@aws-sdk/client-dynamodb": "^3.755.0",
"@aws-sdk/client-kendra": "^3.755.0",
"@aws-sdk/client-s3": "^3.755.0",
"@aws-sdk/client-sagemaker-runtime": "^3.755.0",
"@aws-sdk/client-transcribe": "^3.755.0",
"@aws-sdk/client-transcribe-streaming": "^3.755.0",
"@aws-sdk/lib-dynamodb": "^3.755.0",
"@aws-sdk/s3-request-presigner": "^3.755.0",
"@aws-solutions-constructs/aws-cloudfront-s3": "^2.68.0",
"aws-cdk-lib": "^2.154.1",
"aws-jwt-verify": "^4.0.0",
"constructs": "^10.3.0",
"deploy-time-build": "^0.3.17",
"node-html-parser": "^6.1.13",
"sanitize-html": "^2.13.0",
"source-map-support": "^0.5.21",
"upsert-slr": "^1.0.4",
"zod": "^3.24.1"
}
}
Workspace の package/cdk でcdk deploy --all
を実行しています。
cdk deploy
コマンドに以下のオプションをつけて実行しています。
-
--all
BOOLEAN: CDK アプリにすべてのスタックをデプロイします。
ここまでで、クイックスタートのコマンドは、すべての CDK スタックをデプロイするコマンドであることが確認できました。
GenU に含まれている CDK スタックの確認
次に GenU に含まれている CDK スタックを確認します。
CDK のcdk list
コマンドを使います。
--show-dependencies
オプションをつけて依存関係情報も確認します。
cd packages/cdk
cdk list --show-dependencies
スタックはGenerativeAiUseCasesStack
の 1 つのみで、依存関係はないようです。
GenU の CDK スタックの確認
それでは、CDK ソースコードを読んでいきます。
なお、この記事に記載のコードは、あくまでもこの記事の執筆時点(2025/3/17)のもの であることにご留意ください。
まずは packages/cdk/cdk.json
を確認します。
{
"app": "npx ts-node --prefer-ts-exts bin/generative-ai-use-cases.ts",
~~ 省略 ~~
}
bin/generative-ai-use-cases.ts
を実行しています。
次に、 packages/cdk/bin/generative-ai-use-cases.ts
を確認します。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { getParams } from '../parameter';
import { createStacks } from '../lib/create-stacks';
const app = new cdk.App();
const params = getParams(app);
createStacks(app, params);
createStacks 関数でスタックを作成しているため、packages/cdk/lib/create-stacks.ts
を確認します。
import * as cdk from 'aws-cdk-lib';
import { IConstruct } from 'constructs';
import { GenerativeAiUseCasesStack } from './generative-ai-use-cases-stack';
import { CloudFrontWafStack } from './cloud-front-waf-stack';
import { DashboardStack } from './dashboard-stack';
import { AgentStack } from './agent-stack';
import { RagKnowledgeBaseStack } from './rag-knowledge-base-stack';
import { GuardrailStack } from './guardrail-stack';
import { StackInput } from './stack-input';
class DeletionPolicySetter implements cdk.IAspect {
constructor(private readonly policy: cdk.RemovalPolicy) {}
visit(node: IConstruct): void {
if (node instanceof cdk.CfnResource) {
node.applyRemovalPolicy(this.policy);
}
}
}
export const createStacks = (app: cdk.App, params: StackInput) => {
// CloudFront WAF
// IP アドレス範囲(v4もしくはv6のいずれか)か地理的制限が定義されている場合のみ、CloudFrontWafStack をデプロイする
// WAF v2 は us-east-1 でのみデプロイ可能なため、Stack を分けている
const cloudFrontWafStack =
params.allowedIpV4AddressRanges ||
params.allowedIpV6AddressRanges ||
params.allowedCountryCodes ||
params.hostName
? new CloudFrontWafStack(app, `CloudFrontWafStack${params.env}`, {
env: {
account: params.account,
region: 'us-east-1',
},
params: params,
crossRegionReferences: true,
})
: null;
// RAG Knowledge Base
const ragKnowledgeBaseStack =
params.ragKnowledgeBaseEnabled && !params.ragKnowledgeBaseId
? new RagKnowledgeBaseStack(app, `RagKnowledgeBaseStack${params.env}`, {
env: {
account: params.account,
region: params.modelRegion,
},
params: params,
crossRegionReferences: true,
})
: null;
// Agent
const agentStack = params.agentEnabled
? new AgentStack(app, `WebSearchAgentStack${params.env}`, {
env: {
account: params.account,
region: params.modelRegion,
},
params: params,
crossRegionReferences: true,
})
: null;
// Guardrail
const guardrail = params.guardrailEnabled
? new GuardrailStack(app, `GuardrailStack${params.env}`, {
env: {
account: params.account,
region: params.modelRegion,
},
crossRegionReferences: true,
})
: null;
// GenU Stack
const generativeAiUseCasesStack = new GenerativeAiUseCasesStack(
app,
`GenerativeAiUseCasesStack${params.env}`,
{
env: {
account: params.account,
region: params.region,
},
description: params.anonymousUsageTracking
? 'Generative AI Use Cases JP (uksb-1tupboc48)'
: undefined,
params: params,
crossRegionReferences: true,
// RAG Knowledge Base
knowledgeBaseId: ragKnowledgeBaseStack?.knowledgeBaseId,
knowledgeBaseDataSourceBucketName:
ragKnowledgeBaseStack?.dataSourceBucketName,
// Agent
agents: agentStack?.agents,
// Guardrail
guardrailIdentifier: guardrail?.guardrailIdentifier,
guardrailVersion: 'DRAFT',
// WAF
webAclId: cloudFrontWafStack?.webAclArn,
// Custom Domain
cert: cloudFrontWafStack?.cert,
}
);
cdk.Aspects.of(generativeAiUseCasesStack).add(
new DeletionPolicySetter(cdk.RemovalPolicy.DESTROY)
);
const dashboardStack = params.dashboard
? new DashboardStack(
app,
`GenerativeAiUseCasesDashboardStack${params.env}`,
{
env: {
account: params.account,
region: params.modelRegion,
},
params: params,
userPool: generativeAiUseCasesStack.userPool,
userPoolClient: generativeAiUseCasesStack.userPoolClient,
appRegion: params.region,
crossRegionReferences: true,
}
)
: null;
return {
cloudFrontWafStack,
ragKnowledgeBaseStack,
agentStack,
guardrail,
generativeAiUseCasesStack,
dashboardStack,
};
};
GenU の CDK は最大で以下の 6 つの子スタックを作成します。
CloudFrontWafStack
RagKnowledgeBaseStack
AgentStack
GuardrailStack
GenerativeAiUseCasesStack
DashboardStack
デプロイオプションを設定しない場合、デフォルトでは GenerativeAiUseCasesStack
スタックのみ作成する作りになっています。
そのため、cdk list
コマンドでは GenerativeAiUseCasesStack
スタックしか出力されませんでした。
他の5つのスタックについては、前回の記事で少し触れたデプロイオプションを指定することで作成されます。
それぞれのスタックの作成条件について見ていきましょう。
CloudFrontWafStack の作成条件
CloudFrontWafStack は、AWS WAF による制限を有効化するか、カスタムドメインの使用 を行うと作成されます。
パラメータ例として、packages/cdk/parameter.ts
に以下を設定します。
dev: {
allowedIpV4AddressRanges: ["192.168.0.0/24"],
allowedIpV6AddressRanges: ["2001:0db8::/32"],
allowedCountryCodes: ["JP"]
}
dev: {
hostName: 'genai',
domainName: 'example.com',
hostedZoneId: 'Z0123456789ABCDEFGHIJ',
}
RagKnowledgeBaseStack の作成条件
RagKnowledgeBaseStack は、RAG チャット (Knowledge Base) ユースケースの有効化 を行うと作成されます。
パラメータ例として、packages/cdk/parameter.ts
に以下を設定します。
dev: {
ragKnowledgeBaseEnabled: true,
ragKnowledgeBaseId: 'XXXXXXXXXX',
ragKnowledgeBaseStandbyReplicas: false,
ragKnowledgeBaseAdvancedParsing: false,
ragKnowledgeBaseAdvancedParsingModelId: 'anthropic.claude-3-sonnet-20240229-v1:0',
embeddingModelId: 'amazon.titan-embed-text-v2:0',
}
AgentStack の作成条件
AgentStack は、Agent チャットユースケースの有効化 を行うと作成されます。
パラメータ例として、packages/cdk/parameter.ts
に以下を設定します。
dev: {
agentEnabled: true,
}
GuardrailStack の作成条件
GuardrailStack は、ガードレール を適用すると作成されます。
パラメータ例として、packages/cdk/parameter.ts
に以下を設定します。
dev: {
guardrailEnabled: true,
}
GenerativeAiUseCasesStack の作成条件
GenerativeAiUseCasesStack は唯一、無条件で作成されるスタックです。
DashboardStack の作成条件
DashboardStack は、モニタリング用のダッシュボードの有効化 を行うと作成されます。
パラメータ例として、packages/cdk/parameter.ts
に以下を設定します。
dev: {
dashboard: true,
}
次回はこの 6 つのスタックをもう少し深堀りしていきます。