簡體   English   中英

如何使用 lerna 將 monorepo 代碼部署到 AWS Lambda?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM