You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 07:05:20