如何修改CDK流水线以缓存Lambda Docker镜像?
问题
假设我们有一个包含单个基于Docker镜像创建的Lambda函数的CDK栈,代码如下:
import { Stack, StackProps, Duration } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as lambda from 'aws-cdk-lib/aws-lambda'; export class FunStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const exampleFun = new lambda.DockerImageFunction(this, "ExampleFun", { code: lambda.DockerImageCode.fromImageAsset("lambda/example_fun"), timeout: Duration.seconds(10) }); } }
lambda/example_fun目录包含一个简单的.py处理程序文件和Dockerfile,以public.ecr.aws/lambda/python:3.9为基础镜像。
当存在大量或体积较大的此类Lambda时,AWS CDK流水线不会对这些镜像进行缓存。现有流水线代码如下:
import * as cdk from 'aws-cdk-lib'; import * as codecommit from 'aws-cdk-lib/aws-codecommit'; import { Construct } from 'constructs'; import {CodeBuildStep, CodePipeline, CodePipelineSource} from "aws-cdk-lib/pipelines"; import { FunStack } from "./fun-stack"; import { Stage, StageProps } from "aws-cdk-lib"; export class FunPipelineStage extends Stage { constructor(scope: Construct, id: string, props?: StageProps) { super(scope, id, props); new FunStack(this, 'Fun'); } } export class FunPipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const repo = new codecommit.Repository(this, 'FunRepo', { repositoryName: "FunRepo" }); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'FunLambdaPipeline', synth: new CodeBuildStep('SynthStep', { input: CodePipelineSource.codeCommit(repo, 'master'), installCommands: [ 'npm install -g aws-cdk' ], commands: [ 'npm ci', 'npm run build', 'npx cdk synth' ] }) }); const deploy = new FunPipelineStage(this, 'Deploy'); const deployStage = pipeline.addStage(deploy); } }
如何修改该流水线,以缓存部署过程中生成的DockerImageFunction镜像?期望实现:当Docker镜像未变更时,从缓存读取以避免重复构建。
解决方案
1. 配置CodeBuild的Docker层缓存
修改流水线的SynthStep,启用CodeBuild的Docker缓存功能,让构建过程复用Docker镜像层:
首先导入所需模块:
import * as codebuild from 'aws-cdk-lib/aws-codebuild'; import * as s3 from 'aws-cdk-lib/aws-s3';
然后更新SynthStep配置:
const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'FunLambdaPipeline', synth: new CodeBuildStep('SynthStep', { input: CodePipelineSource.codeCommit(repo, 'master'), installCommands: [ 'npm install -g aws-cdk' ], commands: [ 'npm ci', 'npm run build', 'npx cdk synth' ], // 启用基于S3的构建缓存 cache: codebuild.Cache.bucket(new s3.Bucket(this, 'BuildCacheBucket', { encryption: s3.BucketEncryption.S3_MANAGED, autoDeleteObjects: true, removalPolicy: cdk.RemovalPolicy.DESTROY }), { prefix: 'docker-cache' }), // 自定义BuildSpec,指定Docker缓存路径 buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', phases: { install: { commands: ['npm install -g aws-cdk'] }, build: { commands: ['npm ci', 'npm run build', 'npx cdk synth'] } }, cache: { paths: [ '/root/.docker/**/*', '/root/.cache/**/*' ] } }) }) });
2. 为Docker镜像资产配置ECR缓存仓库
修改FunStack中的DockerImageFunction,指定从ECR仓库缓存镜像,只有当镜像内容变更时才重新构建:
首先导入ECR模块:
import * as ecr from 'aws-cdk-lib/aws-ecr';
然后更新FunStack代码:
export class FunStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // 创建ECR缓存仓库(已有仓库可直接引用) const cacheRepo = new ecr.Repository(this, 'LambdaImageCacheRepo', { repositoryName: 'lambda-image-cache', imageScanOnPush: false, removalPolicy: cdk.RemovalPolicy.DESTROY }); const exampleFun = new lambda.DockerImageFunction(this, "ExampleFun", { code: lambda.DockerImageCode.fromImageAsset("lambda/example_fun", { // 从ECR仓库拉取已有镜像层作为缓存 cacheFrom: [lambda.DockerCache.fromEcr(cacheRepo)], // 将新构建的镜像层推送到ECR仓库作为缓存 cacheTo: lambda.DockerCache.toEcr(cacheRepo, { mode: lambda.DockerCacheMode.LAYER }) }), timeout: Duration.seconds(10) }); // 授予构建进程访问ECR缓存仓库的权限 cacheRepo.grantPullPush(exampleFun.grantPrincipal); } }
3. 拆分流水线阶段(可选优化)
如果Lambda镜像数量较多,建议将镜像构建与栈部署拆分为独立步骤,避免在Synth阶段处理大量镜像构建:
// 创建镜像构建步骤 const buildImagesStep = new CodeBuildStep('BuildImagesStep', { input: CodePipelineSource.codeCommit(repo, 'master'), commands: [ 'npm ci', 'npm run build', // 执行CDK打包,仅构建镜像资产不部署 'npx cdk deploy --app "npx ts-node fun-stack.ts" --no-execute' ], cache: codebuild.Cache.bucket(new s3.Bucket(this, 'ImageBuildCacheBucket'), { prefix: 'lambda-image-build' }), buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', phases: { build: { commands: [ 'npm ci', 'npm run build', 'npx cdk deploy --app "npx ts-node fun-stack.ts" --no-execute' ] } }, cache: { paths: [ '/root/.docker/**/*', '/root/.cache/**/*', 'node_modules/**/*' ] } }) }); // 将镜像构建步骤添加到流水线独立阶段 pipeline.addWave('BuildImages', { post: [buildImagesStep] });
内容的提问来源于stack exchange,提问作者Juho
相关产品推荐
相关产品推荐

