[英]How can you override "cmd" of docker for aws lambda in CDK?
[英]AWS CDK: How to create a Docker Lambda function with Typescript?
我正在嘗試使用 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"]
,
添加一個 ENTRYPOINT 到["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
非常感謝你的幫助: :)
如果您想為 Lambda 函數使用 Docker 映像,則需要使用DockerImageFunction 。 NodejsFunction
不支持。
然后在你的Dockerfile
中,正確的CMD
行應該是(假設index.js
導出了handler()
函數):
CMD ["index.handler"]
該文檔有更多示例,例如:
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" ]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.