AWS CDK:如何使用Typescript创建Docker Lambda函数?

rfbsl7qr  于 2022-12-14  发布在  TypeScript
关注(0)|答案(1)|浏览(136)

我试图使用AWS CDK库在Docker容器中创建一个typescript lambda函数。但是我找不到一种方法使它工作。我使用NodejsFunction构造。
首先,我使用aws-cdk-lib创建我的AWS基础设施。我有一个ContentService NestedStack,它将处理所有文件的上传和下载。为了下载和上传文件,我需要uplink-nodejs模块。
这个模块需要一些工具(make,gcc,g++,golang,...)才能正常工作。为了安装所有这些,唯一的解决方案是使用带有dockerfile的bundling
我的问题出在Dockerfile的CMD命令中。当我运行cdk diff命令时,我得到一个错误:entrypoint requires the handler name to be the first argument
我试过很多方法:

  • 我已经将CMD命令设置为["index"]
  • 将CMD命令设置为["node", "index.handler"]
  • ["node"]添加入口点
  • 在我的Stack中,将NodejsFunctionProps设置为:
{
    handler: "handler",
    functionName: "handler",
    entry: path.join(__dirname, "path_to_typescript_file")
}

我也尝试过创建一个javascript函数,但是切换到.js而不是.ts并没有解决这个问题。
下面是ContentService堆栈代码:

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as s3 from 'aws-cdk-lib/aws-s3';

import * as path from 'path';

import { Construct } from 'constructs';
import { SourceMapMode, NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { DockerImage } from 'aws-cdk-lib';
import { LambdaDestination } from 'aws-cdk-lib/aws-s3-notifications';

interface ContentServiceProps extends cdk.NestedStackProps {
    main_table: dynamodb.Table;
    user_content_bucket: s3.Bucket;
};

export class ContentService extends cdk.NestedStack {

    public readonly content_service_handler: NodejsFunction;
    public readonly storj_image_lambda: NodejsFunction;

    constructor(scope: Construct, id: string, props: ContentServiceProps) {
        super(scope, id, props);

        this.storj_image_lambda = new NodejsFunction(this, "storj_image_lambda",{
            entry: path.join(__dirname, '../../lambda-fns/src/functions/storj_upload/index.ts'),
            handler: 'handler',
            functionName: "handler",
            runtime: lambda.Runtime.NODEJS_16_X,
            bundling: {
                nodeModules: ["uplink-nodejs", "node-gyp", "esbuild"],
                forceDockerBundling: true,
                dockerImage: DockerImage.fromBuild(path.join(__dirname, '../../lambda-fns/src/functions/storj_upload')),
            }
        });

        this.content_service_handler = new NodejsFunction(this, "content_service_handler", {
            runtime: lambda.Runtime.NODEJS_16_X,
            handler: 'handler',
            entry: path.join(__dirname, "../../lambda-fns/src/functions/content/index.ts"),
            environment: {
                TABLE_NAME: props.main_table.tableName,
                USER_CONTENT: props.user_content_bucket.bucketName,
                STORJ_LAMBDA: this.storj_image_lambda.functionName
            }
        });


        props.main_table.grantFullAccess(this.content_service_handler);
        props.user_content_bucket.grantRead(this.content_service_handler);
        props.user_content_bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new LambdaDestination(this.storj_image_lambda));
    };
};

以下是Dockerfile:

FROM public.ecr.aws/lambda/nodejs:16 as builder
WORKDIR /usr/app
COPY package.json index.ts  ./
RUN yum groupinstall -y 'Development Tools'
RUN yum update -y
RUN yum install -y make gcc g++ wget
RUN yum install -y golang
RUN npm i -g node-gyp
RUN npm i -g npm 
RUN npm install uplink-nodejs
RUN npm install
RUN npm run build
    

FROM public.ecr.aws/lambda/nodejs:16
WORKDIR ${LAMBDA_TASK_ROOT}
COPY --from=builder /usr/app/dist/* ./
CMD ["index.handler"]

下面是我得到的错误:

+] Building 0.1s (19/19) FINISHED                                                                                                                              
 => [internal] load build definition from Dockerfile                                                                                                       0.0s
 => => transferring dockerfile: 37B                                                                                                                        0.0s
 => [internal] load .dockerignore                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                            0.0s
 => [internal] load metadata for public.ecr.aws/lambda/nodejs:16                                                                                           0.0s
 => [internal] load build context                                                                                                                          0.0s
 => => transferring context: 62B                                                                                                                           0.0s
 => [builder  1/12] FROM public.ecr.aws/lambda/nodejs:16                                                                                                   0.0s
 => CACHED [stage-1 2/3] WORKDIR /var/task                                                                                                                 0.0s
 => CACHED [builder  2/12] WORKDIR /usr/app                                                                                                                0.0s
 => CACHED [builder  3/12] COPY package.json index.ts  ./                                                                                                  0.0s
 => CACHED [builder  4/12] RUN yum groupinstall -y 'Development Tools'                                                                                     0.0s
 => CACHED [builder  5/12] RUN yum update -y                                                                                                               0.0s
 => CACHED [builder  6/12] RUN yum install -y make gcc g++ wget                                                                                            0.0s
 => CACHED [builder  7/12] RUN yum install -y golang                                                                                                       0.0s
 => CACHED [builder  8/12] RUN npm i -g node-gyp                                                                                                           0.0s
 => CACHED [builder  9/12] RUN npm i -g npm                                                                                                                0.0s
 => CACHED [builder 10/12] RUN npm install uplink-nodejs                                                                                                   0.0s
 => CACHED [builder 11/12] RUN npm install                                                                                                                 0.0s
 => CACHED [builder 12/12] RUN npm run build                                                                                                               0.0s
 => CACHED [stage-1 3/3] COPY --from=builder /usr/app/dist/* ./                                                                                            0.0s
 => exporting to image                                                                                                                                     0.0s
 => => exporting layers                                                                                                                                    0.0s
 => => writing image sha256:c8938e06ceb29f90e5785d75693f2aff6b190ac93898e8aa9787c2577bf81d2b                                                               0.0s
 => => naming to docker.io/library/cdk-5ab770d057c8e0a5868c2d6533bbd4fa566c53f83da671632d81307494ef0e1a                                                    0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Bundling asset MainStack/content_service/storj_image_lambda/Code/Stage...
entrypoint requires the handler name to be the first argument
/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/core/lib/asset-staging.js:2
`),localBundling=options.local?.tryBundle(bundleDir,options),!localBundling){let user;if(options.user)user=options.user;else{const userInfo=os.userInfo();user=userInfo.uid!==-1?`${userInfo.uid}:${userInfo.gid}`:"1000:1000"}options.image.run({command:options.command,user,volumes,environment:options.environment,entrypoint:options.entrypoint,workingDirectory:options.workingDirectory??AssetStaging.BUNDLING_INPUT_DIR,securityOpt:options.securityOpt??""})}}catch(err){const bundleErrorDir=bundleDir+"-error";throw fs.existsSync(bundleErrorDir)&&fs.removeSync(bundleErrorDir),fs.renameSync(bundleDir,bundleErrorDir),new Error(`Failed to bundle asset ${this.node.path}, bundle output is located at ${bundleErrorDir}: ${err}`)}if(fs_1.FileSystem.isEmpty(bundleDir)){const outputDir=localBundling?bundleDir:AssetStaging.BUNDLING_OUTPUT_DIR;throw new Error(`Bundling did not produce any output. Check that content is written to ${outputDir}.`)}}calculateHash(hashType,bundling,outputDir){if(hashType==assets_1.AssetHashType.CUSTOM||hashType==assets_1.AssetHashType.SOURCE&&bundling){const hash=crypto.createHash("sha256");return hash.update(this.customSourceFingerprint??fs_1.FileSystem.fingerprint(this.sourcePath,this.fingerprintOptions)),bundling&&hash.update(JSON.stringify(bundling)),hash.digest("hex")}switch(hashType){case assets_1.AssetHashType.SOURCE:return fs_1.FileSystem.fingerprint(this.sourcePath,this.fingerprintOptions);case assets_1.AssetHashType.BUNDLE:case assets_1.AssetHashType.OUTPUT:if(!outputDir)throw new Error(`Cannot use \`${hashType}\` hash type when \`bundling\` is not specified.`);return fs_1.FileSystem.fingerprint(outputDir,this.fingerprintOptions);default:throw new Error("Unknown asset hash type.")}}}exports.AssetStaging=AssetStaging,_a=JSII_RTTI_SYMBOL_1,AssetStaging[_a]={fqn:"aws-cdk-lib.AssetStaging",version:"2.54.0"},AssetStaging.BUNDLING_INPUT_DIR="/asset-input",AssetStaging.BUNDLING_OUTPUT_DIR="/asset-output",AssetStaging.assetCache=new cache_1.Cache;function renderAssetFilename(assetHash,extension=""){return`asset.${assetHash}${extension}`}function determineHashType(assetHashType,customSourceFingerprint){const hashType=customSourceFingerprint?assetHashType??assets_1.AssetHashType.CUSTOM:assetHashType??assets_1.AssetHashType.SOURCE;if(customSourceFingerprint&&hashType!==assets_1.AssetHashType.CUSTOM)throw new Error(`Cannot specify \`${assetHashType}\` for \`assetHashType\` when \`assetHash\` is specified. Use \`CUSTOM\` or leave \`undefined\`.`);if(hashType===assets_1.AssetHashType.CUSTOM&&!customSourceFingerprint)throw new Error("`assetHash` must be specified when `assetHashType` is set to `AssetHashType.CUSTOM`.");return hashType}function calculateCacheKey(props){return crypto.createHash("sha256").update(JSON.stringify(sortObject(props))).digest("hex")}function sortObject(object){if(typeof object!="object"||object instanceof Array)return object;const ret={};for(const key of Object.keys(object).sort())ret[key]=sortObject(object[key]);return ret}function singleArchiveFile(directory){if(!fs.existsSync(directory))throw new Error(`Directory ${directory} does not exist.`);if(!fs.statSync(directory).isDirectory())throw new Error(`${directory} is not a directory.`);const content=fs.readdirSync(directory);if(content.length===1){const file=path.join(directory,content[0]),extension=getExtension(content[0]).toLowerCase();if(fs.statSync(file).isFile()&&ARCHIVE_EXTENSIONS.includes(extension))return file}}function determineBundledAsset(bundleDir,outputType){const archiveFile=singleArchiveFile(bundleDir);switch(outputType===bundling_1.BundlingOutput.AUTO_DISCOVER&&(outputType=archiveFile?bundling_1.BundlingOutput.ARCHIVED:bundling_1.BundlingOutput.NOT_ARCHIVED),outputType){case bundling_1.BundlingOutput.NOT_ARCHIVED:return{path:bundleDir,packaging:assets_1.FileAssetPackaging.ZIP_DIRECTORY};case bundling_1.BundlingOutput.ARCHIVED:if(!archiveFile)throw new Error("Bundling output directory is expected to include only a single archive file when `output` is set to `ARCHIVED`");return{path:archiveFile,packaging:assets_1.FileAssetPackaging.FILE,extension:getExtension(archiveFile)}}}function getExtension(source){for(const ext of ARCHIVE_EXTENSIONS)if(source.toLowerCase().endsWith(ext))return ext;return path.extname(source)}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ^
Error: Failed to bundle asset MainStack/content_service/storj_image_lambda/Code/Stage, bundle output is located at /Users/thomgeenen/Git/nude_safer_cdk/cdk.out/bundling-temp-a1589b169e0084a2bad98572c39a23d5ba529eefbdce1761960c10e768a31036-error: Error: docker exited with status 142
    at AssetStaging.bundle (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/core/lib/asset-staging.js:2:614)
    at AssetStaging.stageByBundling (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/core/lib/asset-staging.js:1:4506)
    at stageThisAsset (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/core/lib/asset-staging.js:1:1867)
    at Cache.obtain (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/core/lib/private/cache.js:1:242)
    at new AssetStaging (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/core/lib/asset-staging.js:1:2262)
    at new Asset (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/aws-s3-assets/lib/asset.js:1:736)
    at AssetCode.bind (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/aws-lambda/lib/code.js:1:4628)
    at new Function (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/aws-lambda/lib/function.js:1:2803)
    at new NodejsFunction (/Users/thomgeenen/Git/nude_safer_cdk/node_modules/aws-cdk-lib/aws-lambda-nodejs/lib/function.js:1:1171)
    at new ContentService (/Users/thomgeenen/Git/nude_safer_cdk/lib/services/content/index.ts:26:35)

Subprocess exited with error 1

非常感谢你的帮助!:)

wgx48brx

wgx48brx1#

如果你想为Lambda函数使用Docker映像,你需要使用DockerImageFunctionNodejsFunction不支持。
那么在Dockerfile中,右边的CMD行应该是(假设导出了handler()函数的index.js):

CMD ["index.handler"]

documentation还有其他一些示例,如:

FROM public.ecr.aws/lambda/nodejs:18

# Assumes your function is named "app.js", and there is a package.json file in the app directory 
COPY app.js package.json  ${LAMBDA_TASK_ROOT}/

# Install NPM dependencies for function
RUN npm install

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "app.handler" ]

相关问题