我试图使用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
非常感谢你的帮助!:)
1条答案
按热度按时间wgx48brx1#
如果你想为Lambda函数使用Docker映像,你需要使用DockerImageFunction。
NodejsFunction
不支持。那么在
Dockerfile
中,右边的CMD
行应该是(假设导出了handler()
函数的index.js
):documentation还有其他一些示例,如: