Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
[AWS CDK] API Gatewayの自動デプロイができない! 解消したらCDKコントリ...
Search
酢ろう
November 04, 2024
Technology
1.1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
[AWS CDK] API Gatewayの自動デプロイができない! 解消したらCDKコントリビュートできた話
#cm_cdk_special
酢ろう
November 04, 2024
More Decks by 酢ろう
See All by 酢ろう
WingLangでインフラとアプリを同時にコード化!
engineer12taro
0
750
Other Decks in Technology
See All in Technology
Frontier Teams — Amazon は AI で「開発のやり方」をどう変えたのか
kmotohas
0
130
Kiro Crew 入門 (手を離しても進む仕事) | 目指せ Kiro マスター 11
inariku
PRO
4
680
長い仕事を任せる (Workflows、クラウドセッション、Kiro Web) | 目指せ Kiro マスター 10
inariku
PRO
3
170
[dbts2026]数十PBの超巨大データ×AI活用を支えるSnowflakeの実力〜大規模環境におけるアーキテクチャと運用のリアル〜
y_matsubara
0
130
SREのためのFinOps入門
muziyoshiz
2
1.3k
Claude Codeで作ったアプリを安全に公開する/Built with Claude Japan 2026
yoshidashingo
0
230
GCASアップデート(202607-202610)
techniczna
1
250
AIエージェントの監視、思考を見るか行動を見るか / Monitoring AI Agents: Thoughts or Actions
yusukeshimizu
2
180
Microsoft Entra Kerberos で実現するクラウド デバイス管理
tamaiyutaro
2
660
顧客合意を取りながら進めるAWSコスト削減の実践
optim
0
160
内製アプリとClaudeスキルのセキュリティチェック、 Backlogの課題で回しています。
ayaka_egaaa
0
170
Building Audio Plugin Ecosystem for Android in 2026
atsushieno
2
160
Featured
See All Featured
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.9k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
1
300
Optimizing for Happiness
mojombo
378
71k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
Thoughts on Productivity
jonyablonski
76
5.5k
How STYLIGHT went responsive
nonsquared
100
6.4k
Git: the NoSQL Database
bkeepers
PRO
433
67k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.4k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
2
520
It's Worth the Effort
3n
188
29k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
690
Transcript
API Gatewayの自動デプロイができない! → 解消したらCDKコントリビュートできた話 クラスメソッドのCDK事情大公開スペシャル#1 塚本太朗 1
自己紹介 塚本太朗 リテールアプリ共創部 エンハンスチーム 2023年5月入社(1年半!) 最近ハマっていること HUNTER×HUNTERをちゃんと読む 継承戦編が難しすぎる X: @9Lgo1
2
💬 今日話すこと3行 チームで困っていた謎の挙動を解決したら… → CDK調査の勘所が身につき… → CDKコントリビュートもできた! 3
📇 目次 どんな問題があったか? 問題の原因を紹介 解消方法 CDKの挙動調査Tips CDKコントリビュートを紹介 まとめ 4
cdk deployしても、API Gatewayが更新されな い! 😞 5
😞 どんな問題があったか? CDKでデプロイ (GitHub Actions) API Gatewayが自動デプロイされないので、手動でデプロイを行う CDKでデプロイ 〜 手動デプロイの間に瞬断が発生する
定期的に 🌙深夜リリースが必要だった 6
😀 改善した結果 深夜リリースの対応が不要に! メンバーのリソースを削減 深夜リリースによる身体的ダメージを抑制 7
原因はスタック分割! ✂️ 8
🔍 原因 API GatewayのリソースとLambdaのリソースが別スタックで定義さ れていた 9
🔍 原因: 実装ソース(一部抜粋) /* ApiGatewayのStack */ export class ApiStack extends cdk.Stack
{ constructor(scope: Construct, id: string, props?: cdk.StackProps) { const api = new RestApi(this, "base-api", {}); } } /* LambdaのStack */ export class LambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props: LambdaStackProps) { // ⭐ API GatewayをfromXXXXAttributeで取得する const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // API GatewayとLambdaの紐付けを行う apiGateway.root.addResource("hoge").addMethod("GET", hogeIntegration); } } 10
🔍 原因: 実装ソース(一部抜粋) const api = new RestApi(this, "base-api", {}); /*
ApiGatewayのStack */ export class ApiStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { } } /* LambdaのStack */ export class LambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props: LambdaStackProps) { // ⭐ API GatewayをfromXXXXAttributeで取得する const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // API GatewayとLambdaの紐付けを行う apiGateway.root.addResource("hoge").addMethod("GET", hogeIntegration); } } 10
🔍 原因: 実装ソース(一部抜粋) // ⭐ API GatewayをfromXXXXAttributeで取得する const apiGateway = RestApi.fromRestApiAttributes(this,
"api", { restApiId: props.apiId, rootResourceId: props.rootId, }); /* ApiGatewayのStack */ export class ApiStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { const api = new RestApi(this, "base-api", {}); } } /* LambdaのStack */ export class LambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props: LambdaStackProps) { // API GatewayとLambdaの紐付けを行う apiGateway.root.addResource("hoge").addMethod("GET", hogeIntegration); } } 10
🔍 原因: 実装ソース(一部抜粋) // API GatewayとLambdaの紐付けを行う apiGateway.root.addResource("hoge").addMethod("GET", hogeIntegration); /* ApiGatewayのStack */
export class ApiStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { const api = new RestApi(this, "base-api", {}); } } /* LambdaのStack */ export class LambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props: LambdaStackProps) { // ⭐ API GatewayをfromXXXXAttributeで取得する const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); } } 10
🔍 原因: 実装ソース(一部抜粋) /* ApiGatewayのStack */ export class ApiStack extends cdk.Stack
{ constructor(scope: Construct, id: string, props?: cdk.StackProps) { const api = new RestApi(this, "base-api", {}); } } /* LambdaのStack */ export class LambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props: LambdaStackProps) { // ⭐ API GatewayをfromXXXXAttributeで取得する const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // API GatewayとLambdaの紐付けを行う apiGateway.root.addResource("hoge").addMethod("GET", hogeIntegration); } } 10
⛏️さらに深掘り: CDKのソースを追ってみる Method, Resourceの作成時、デプロイメントのIDを更新するように なっている。 aws-cdk-lib/aws-apigateway/lib/method.tsの抜粋 export class Method extends
Resource { constructor(scope: Construct, id: string, props: MethodProps) { // RestApiのlatestDeploymentがある時... const deployment = props.resource.api.latestDeployment; if (deployment) { deployment.node.addDependency(resource); // ロジカルIDを変更する deployment.addToLogicalId({ method: { ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); } 11
⛏️さらに深掘り: CDKのソースを追ってみる Method, Resourceの作成時、デプロイメントのIDを更新するように なっている。 aws-cdk-lib/aws-apigateway/lib/method.tsの抜粋 // RestApiのlatestDeploymentがある時... const deployment
= props.resource.api.latestDeployment; if (deployment) { export class Method extends Resource { constructor(scope: Construct, id: string, props: MethodProps) { deployment.node.addDependency(resource); // ロジカルIDを変更する deployment.addToLogicalId({ method: { ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); } 11
⛏️さらに深掘り: CDKのソースを追ってみる Method, Resourceの作成時、デプロイメントのIDを更新するように なっている。 aws-cdk-lib/aws-apigateway/lib/method.tsの抜粋 // ロジカルIDを変更する deployment.addToLogicalId({ method:
{ ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); export class Method extends Resource { constructor(scope: Construct, id: string, props: MethodProps) { // RestApiのlatestDeploymentがある時... const deployment = props.resource.api.latestDeployment; if (deployment) { deployment.node.addDependency(resource); } 11
⛏️さらに深掘り: CDKのソースを追ってみる Method, Resourceの作成時、デプロイメントのIDを更新するように なっている。 aws-cdk-lib/aws-apigateway/lib/method.tsの抜粋 export class Method extends
Resource { constructor(scope: Construct, id: string, props: MethodProps) { // RestApiのlatestDeploymentがある時... const deployment = props.resource.api.latestDeployment; if (deployment) { deployment.node.addDependency(resource); // ロジカルIDを変更する deployment.addToLogicalId({ method: { ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); } 11
⛏️さらに深掘り: CDKのソースを追ってみる fromRestApiAttributeで取得した IRestApi には latestDeployment が設定されていない aws-cdk-lib/aws-apigateway/lib/restapi.tsの抜粋 export class
RestApi extends RestApiBase { public static fromRestApiAttributes(scope: Construct, id: string, attrs: RestApiAttributes): IRestApi { class Import extends RestApiBase { public readonly restApiId = attrs.restApiId; public readonly restApiName = attrs.restApiName ?? id; public readonly restApiRootResourceId = attrs.rootResourceId; public readonly root: IResource = new RootResource(this, {}, this.restApiRootResourceId); } return new Import(scope, id); } } 12
⛏️さらに深掘り: CDKのソースを追ってみる 再び Methodクラスの実装 export class Method extends Resource {
constructor(scope: Construct, id: string, props: MethodProps) { // RestApiのlatestDeploymentがある時... const deployment = props.resource.api.latestDeployment; if (deployment) { // ⭐️ここに入ってこない! deployment.node.addDependency(resource); // ロジカルIDを変更する deployment.addToLogicalId({ method: { ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); } } } 13
⛏️さらに深掘り: CDKのソースを追ってみる 再び Methodクラスの実装 // RestApiのlatestDeploymentがある時... const deployment = props.resource.api.latestDeployment;
if (deployment) { // ⭐️ここに入ってこない! export class Method extends Resource { constructor(scope: Construct, id: string, props: MethodProps) { deployment.node.addDependency(resource); // ロジカルIDを変更する deployment.addToLogicalId({ method: { ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); } } } 13
⛏️さらに深掘り: CDKのソースを追ってみる 再び Methodクラスの実装 export class Method extends Resource {
constructor(scope: Construct, id: string, props: MethodProps) { // RestApiのlatestDeploymentがある時... const deployment = props.resource.api.latestDeployment; if (deployment) { // ⭐️ここに入ってこない! deployment.node.addDependency(resource); // ロジカルIDを変更する deployment.addToLogicalId({ method: { ...methodProps, integrationToken: bindResult?.deploymentToken, }, }); } } } 13
💡 解決方法 毎回新しいDeploymentを作成するため、専用のスタックを用意した ※ 0から作るなら、この実装はあまりオススメできません 14
💡 解決方法: コード紹介 // ApiDeploymentStack export class ApiDeploymentStack extends cdk.Stack
{ constructor(scope: Construct, id: string, props: ApiDeploymentStackProps) { // api-stackで定義したAPI Gateway const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // Deploymentを定義 const deployment = new Deployment(this, "base-api-deployment", { api: apiGateway, retainDeployments: true, }); deployment.addToLogicalId(new Date().toISOString()); // ⭐️ Deploymentを更新 // Stageを定義し、deploymentを紐づける const stage = new Stage(this, "base-api-stage", { deployment, stageName: "prod", }); } } 15
💡 解決方法: コード紹介 // api-stackで定義したAPI Gateway const apiGateway = RestApi.fromRestApiAttributes(this,
"api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // ApiDeploymentStack export class ApiDeploymentStack extends cdk.Stack { constructor(scope: Construct, id: string, props: ApiDeploymentStackProps) { // Deploymentを定義 const deployment = new Deployment(this, "base-api-deployment", { api: apiGateway, retainDeployments: true, }); deployment.addToLogicalId(new Date().toISOString()); // ⭐️ Deploymentを更新 // Stageを定義し、deploymentを紐づける const stage = new Stage(this, "base-api-stage", { deployment, stageName: "prod", }); } } 15
💡 解決方法: コード紹介 // Deploymentを定義 const deployment = new Deployment(this,
"base-api-deployment", { api: apiGateway, retainDeployments: true, }); deployment.addToLogicalId(new Date().toISOString()); // ⭐️ Deploymentを更新 // ApiDeploymentStack export class ApiDeploymentStack extends cdk.Stack { constructor(scope: Construct, id: string, props: ApiDeploymentStackProps) { // api-stackで定義したAPI Gateway const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // Stageを定義し、deploymentを紐づける const stage = new Stage(this, "base-api-stage", { deployment, stageName: "prod", }); } } 15
💡 解決方法: コード紹介 // ApiDeploymentStack export class ApiDeploymentStack extends cdk.Stack
{ constructor(scope: Construct, id: string, props: ApiDeploymentStackProps) { // api-stackで定義したAPI Gateway const apiGateway = RestApi.fromRestApiAttributes(this, "api", { restApiId: props.apiId, rootResourceId: props.rootId, }); // Deploymentを定義 const deployment = new Deployment(this, "base-api-deployment", { api: apiGateway, retainDeployments: true, }); deployment.addToLogicalId(new Date().toISOString()); // ⭐️ Deploymentを更新 // Stageを定義し、deploymentを紐づける const stage = new Stage(this, "base-api-stage", { deployment, stageName: "prod", }); } } 15
🕵️♀️ Tips: 役に立った調査方法 CDKのソースコードを解析する(今までの流れ) CloudTrailでデプロイ時の流れを確認する 16
🕵️♀️ CloudTrailでデプロイ時の流れを確認する デプロイ時のリソース作成・更新などを時系列で確認 正常動作と異常動作の差を確認できる 17
🌱 0から作れるなら? RestApiを作成したスタックで、LambdaのIDを参照する 参考: AWS CDKでAPI Gateway+Lambdaを作成する際のベストな スタック構成について Lambdalithの構成を検討する 参考: LambdalithとSingle
purpose Lambdaは1つのAPI Gateway で共存できる 18
🎉 CDKコントリビュートもできました! https://github.com/aws/aws-cdk/pull/29691 19
🎉CDKコントリビュートもできました!: 対応内容 今回紹介した問題が起こるサンプルコードがCDKの公式ドキュメン トで紹介されていた そのサンプルコードの下に注意文言を記載するPRを作成 対応内容はシンプルですが、レビュアーの方とのやり取りで色々勉 強になりました!! 20
得られた知見 他スタックから fromXXX などでインポートしたリソースが、予想外 の挙動を引き起こすかもしれない。 CloudTrailを活用して、CDKデプロイ時の挙動を分析できる。 CDKで謎の挙動をする時はソースを見てみる。読みやすいので、意 外と早く原因が特定できる。 21
まとめ 慎重にスタック分割をしよう!メリット・デメリットを把握した上 で慎重に行う。 CDKの構成があまり良くない時は、OSSコントリビュートのチャン ス転がってくるかも! とはいえ…サーバーレス構成をCDKで実装する時は、さまざまな制 限に注意して構成を考えよう。 22
ご清聴ありがとうございました!質問あればお願 いします! 23
参考 aws-cdk AWS CDKでAPI Gateway+Lambdaを作成する際のベストなスタック 構成について LambdalithとSingle purpose Lambdaは1つのAPI Gatewayで共存で
きる 今回対応したPR 登壇の元ブログ 24
この登壇の元ブログ 25
26