[英]How do I deploy monorepo code to AWS Lambda using lerna?
我正在嘗試制作兩個 AWS Lambda 函數(用打字稿編寫)。 這兩個函數共享相同的代碼來與 API 交互。 為了不必將相同的代碼復制到兩個不同的 Lambda,我想將我的共享代碼移動到本地模塊,並讓我的兩個 Lambda 都依賴於所述模塊。
我最初嘗試在兩個 lambda 之間查看代碼是使用 monorepo 和 lerna。 我當前的項目結構如下所示:
- lerna.json
- package.json
- packages
- api
- package.json
- lambdas
- funcA
- package.json
- func B
- package.json
lerna.json:
{
"packages": [
"packages/api",
"packages/lambdas/*"
],
"version": "1.0.0"
}
在我的 Lambda 函數的每個 package.json 中,我可以這樣包含我的本地 api 模塊:
"dependencies": {
"@local/api": "*"
}
有了這個,我已經能夠將公共代碼移動到它自己的模塊中。 但是,我現在不確定如何捆綁我的函數以部署到 AWS Lambda。 有沒有辦法讓 lerna 能夠創建一個可以部署的包?
由於cp -rL
在 mac 上不起作用,我不得不想出類似的東西。
如果您的所有包都屬於一個范圍 (@org),這是一個有效的工作流程:
在你的 lerna 倉庫的 package.json 中:
"scripts": {
"deploy": "lerna exec \"rm -rf node_modules\" && lerna bootstrap -- --production && lerna run deploy && lerna bootstrap"
}
在包含您的 lambda 函數的包中:
"scripts":{
"deploy": "npm pack && tar zxvf packagename-version.tgz && rm -rf node_modules/@org && cp -r node_modules/* package/node_modules && cd package && npm dedupe"
}
現在用項目的相應值替換“packagename-version”和“@org”。 還將所有依賴包添加到“bundledDependencies”。
在 lerna mono 存儲庫的根目錄中運行npm run deploy
之后,您最終會在包含 lambda 函數的包中找到一個文件夾“package”。 它具有運行您的函數所需的所有依賴項。 從那里拿走。
我曾希望使用npm pack
可以讓我利用 .npmignore 文件,但似乎那行不通。 如果有人知道如何讓它工作,請告訴我。
我已經為同樣的問題苦苦掙扎了一段時間,最后我被迫為此做點什么。
我正在使用一個名為slice-node-modules的小包,正如在這個類似問題中找到的那樣,這對我的目的來說已經足夠好了一段時間了。 當我將更多的項目合並到 monorepos 中並開始使用作為兄弟而不是外部發布的共享依賴項時,我遇到了這種方法的缺點。
我創建了一個名為lerna-to-lambda的新工具,它是專門為我的用例量身定制的。 我用最少的文檔公開發布了它,希望足以幫助處於類似情況的其他人。 它的要點是你在你的捆綁步驟中運行l2l
,在你安裝了所有的依賴項之后,它將需要的東西復制到一個輸出目錄中,然后准備使用 SAM 或其他任何東西部署到 Lambda。
例如,在 README 中,您的 Lambda 函數的package.json
中可能有這樣的內容:
"scripts": {
...
"clean": "rimraf build lambda",
"compile": "tsc -p tsconfig.build.json",
"package": "l2l -i build -o lambda",
"build": "yarn run clean && yarn run compile && yarn run package"
},
在這種情況下, compile
步驟是將源目錄中的 TypeScript 文件編譯成build
目錄中的 JavaScript 文件。 然后, package
步驟將build
中的所有代碼以及 Lambda 的所有依賴項( aws-sdk
除外)捆綁到目錄lambda
中,這是您要部署到 AWS 的目錄。 如果有人使用純 JavaScript 而不是 TypeScript,他們可以在打包之前將必要的 .js 文件復制到build
目錄中。
我意識到這個問題已有 2 年多的歷史了,從那時起您可能已經找到了自己的解決方案和/或解決方法。 但由於它仍然與我相關,我認為它仍然與外面的人相關,所以我正在分享。
運行lerna bootstrap
將在每個“包”中創建一個 node_modules 文件夾。 這將包括所有 lerna 管理的依賴項以及該特定包的外部依賴項。
從那時起,您對每個 lambda 的部署將與您正在使用 lerna 的事實無關。 部署包將需要包含該特定 lambda 的代碼和該 lambda 的 node_modules 文件夾——您可以將它們壓縮並手動上傳,或者使用 SAM 或 CloudFormation 之類的東西。
編輯:正如您正確指出的那樣,您最終會在 node_modules 文件夾中找到符號鏈接,這使得打包起來很尷尬。 為了解決這個問題,您可以在打包部署之前運行類似這樣的東西:
cp -rL lambdas/funcA/node_modules lambdas/funcA/packaged/node_modules
-L
將強制將符號鏈接目錄復制到文件夾中,然后您可以對其進行壓縮。
我在安裝過程中使用了自定義腳本來復制依賴項。這將允許我使用相同的代碼開發和部署應用程序。
項目結構在 lambda_a 的 package.json 文件中,我有以下行:
"scripts": {
"install": "node ./install_libs.js @libs/library_a"
},
@libs/library_a 可以使用以下語句由 lambda 代碼使用:
const library_a = require('@libs/library_a')
對於 SAM 構建,我使用 lmbdas frolder 中的以下命令:
export SAM_BUILD=true && sam build
安裝庫.js
console.log("Starting module installation")
var fs = require('fs');
var path = require('path');
var {exec} = require("child_process");
if (!fs.existsSync("node_modules")) {
fs.mkdirSync("node_modules");
}
if (!fs.existsSync("node_modules/@libs")) {
fs.mkdirSync("node_modules/@libs");
}
const sam_build = process.env.SAM_BUILD || false
libs_path = "../../"
if (sam_build) {
libs_path = "../../" + libs_path
}
process.argv.forEach(async function (val, index, array) {
if (index > 1) {
var currentLib = libs_path + val
console.log(`Building lib ${currentLib}`)
await exec(`cd ${currentLib} && npm install` , function (error, stdout, stderr){
if (error) {
console.log(`error: ${error.message}`);
return;
}
console.log(`stdout: ${stdout}`);
console.log('Importing module : ' + currentLib);
copyFolderRecursiveSync(currentLib, "node_modules/@libs")
});
}
});
function copyFolderRecursiveSync(source, target) {
var files = [];
// Check if folder needs to be created or integrated
var targetFolder = path.join(target, path.basename(source));
if (!fs.existsSync(targetFolder)) {
fs.mkdirSync(targetFolder);
}
// Copy
if (fs.lstatSync(source).isDirectory()) {
files = fs.readdirSync(source);
files.forEach(function (file) {
var curSource = path.join(source, file);
if (fs.lstatSync(curSource).isDirectory()) {
copyFolderRecursiveSync(curSource, targetFolder);
} else {
copyFileSync(curSource, targetFolder);
}
});
}
}
function copyFileSync(source, target) {
var targetFile = target;
// If target is a directory, a new file with the same name will be created
if (fs.existsSync(target)) {
if (fs.lstatSync(target).isDirectory()) {
targetFile = path.join(target, path.basename(source));
}
}
fs.writeFileSync(targetFile, fs.readFileSync(source));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.