发布于 2026-01-06 0 阅读
0

使用 AWS CodePipeline 在 Amazon EC2 上部署 NodeJS 应用程序总结 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

使用 AWS CodePipeline 在 Amazon EC2 上部署 NodeJS 应用程序

结论

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

尽管大多数开发者都在转向无服务器和容器化架构来构建应用程序,但 EC2 实例仍然是最流行、使用最广泛的 AWS 服务之一。在本博客中,我将引导您完成使用 AWS CodePipeline 在 Amazon EC2 上部署可扩展 NodeJS 应用程序所需的步骤,并提及您在设置此解决方案时可能遇到的一些挑战。乍一看似乎很简单,但相信我,它需要的精力比您预想的要多,而这正是我今天撰写这篇博客的主要原因。

好了,不多说了,现在让我们摇滚起来吧! 🎸

本博客涵盖的服务包括:

我假设您已经使用您首选的方法(手动、CDK、CloudFormation、Terraform 等)成功设置了底层基础设施。

您已配置好 EC2 实例、CodeDeploy Agent 和自动扩展组,并在 EC2 实例上安装了最新版本的 Nginx、NodeJS 和 PM2,现在准备通过 AWS CodePipeline 部署您的 NodeJS 应用程序。首先,您需要创建一个新的 Pipeline 项目,连接到您的源代码提供商(例如 GitHub),然后使用 CodeBuild 编译源代码并运行一些单元测试。最后,选择 AWS CodeDeploy 通过部署组将最新版本部署到 Amazon EC2 实例上。难点在于 buildspec.yml 和 appspec.yml 文件,您可以在其中设置用于构建和部署代码的一系列命令。首先想到的是创建以下 buildspec 和 appspec 文件。

buildspec.yml 文件

version: 0.2
phases:
  install:
    runtime-versions:
      nodejs: 10
    commands:
      - echo Installing
  pre_build:
    commands:
      - echo Installing source NPM dependencies.
      - npm install
  build:
    commands:
      - echo Build started on `date`
      - echo Compiling the Node.js code
      - npm run build
  post_build:
    commands:
      - echo Build completed on `date`
artifacts:
  files:
    - '**/*'
Enter fullscreen mode Exit fullscreen mode

appspec.yml 文件

version: 0.0
os: linux
files:
  - source: /
    destination: /usr/share/nginx/html
Enter fullscreen mode Exit fullscreen mode

你将代码推送到版本控制系统(在本例中是 GitHub),并触发了你的第一个 CodePipeline 流水线,猜猜会发生什么?流水线在这个阶段成功完成。现在,我们正兴奋地使用“npm start”运行我们的 Node 脚本,却突然遇到了以下错误:

Error: Cannot find module '../package.json'

但是该如何解决呢?我们非常确定 package.json 文件位于根目录下,而 libraries 位于 node_modules 文件夹中。说实话,解决这个问题的唯一办法就是运行npm rebuild或删除 node_modules 文件夹,然后npm install在 EC2 实例上重新运行。完成这些操作后,您就可以启动 Node 脚本了。这固然不错,但并不符合我们的需求。我们想要的是一个完全自动化的部署,无需任何人工干预。幸运的是, Code Deploy 的 appspec.yml 文件中的生命周期事件钩子部分可以帮我们解决这个问题,它创建了几个 bash 脚本来替换 Code Build 执行的“npm install 和 build”步骤,从而将 AWS Code Build 仅用于测试用例阶段。以下是我们现在的两个文件的样子:

buildspec.yml 文件

version: 0.2
phases:
  pre_build:
    commands:
      - echo Installing source NPM dependencies...
      - npm install
  build:
    commands:
      - echo Build started on `date`
      - echo Compiling the Node.js code
      - echo Running unit tests
      - npm test
  post_build:
    commands:
      - echo Build completed on `date`
artifacts:
  files:
    - '**/*'
Enter fullscreen mode Exit fullscreen mode

appspec.yml 文件

version: 0.0
os: linux
files:
  - source: /
    destination: /usr/share/nginx/html
hooks:
  BeforeInstall:
    - location: scripts/BeforeInstallHook.sh
      timeout: 300
  AfterInstall:
    - location: scripts/AfterInstallHook.sh
      timeout: 300
Enter fullscreen mode Exit fullscreen mode
  1. BeforeInstall:用于在创建替换任务集之前运行任务。一个目标组与原始任务集关联。如果指定了可选的测试监听器,则该监听器与原始任务集关联。此时无法回滚。
#!/bin/bash
set -e
yum update -y
pm2 update
Enter fullscreen mode Exit fullscreen mode
  1. AfterInstall:用于在创建替换任务集并将其与某个目标组关联后运行任务。如果指定了可选的测试监听器,则该监听器与原始任务集关联。此生命周期事件中钩子函数的结果可以触发回滚。
#!/bin/bash
set -e
cd /usr/share/nginx/html
npm install
npm run build
Enter fullscreen mode Exit fullscreen mode

注意:我们设置了 -e 标志,以便在发生错误时停止脚本的执行。

即使更新了 appspec 和 buildspec 文件,您可能还会遇到以下问题:The deployment failed because a specified file already exists at this location: /usr/share/nginx/html/.cache/plugins/somefile.js

在本例中,我们将通过简单地要求 CodeDeploy 使用该选项替换已存在的文件来解决这个问题overwrite:true

最终的 appspec.yml 文件

version: 0.0
os: linux
files:
  - source: /
    destination: /usr/share/nginx/html
    overwrite: true
hooks:
  BeforeInstall:
    - location: scripts/BeforeInstallHook.sh
      timeout: 300
  AfterInstall:
    - location: scripts/AfterInstallHook.sh
      timeout: 300
Enter fullscreen mode Exit fullscreen mode

完美!AWS CodePipeline 成功部署后,我们现在可以顺利启动 npm 脚本,不会遇到任何问题。接下来,我们需要使用 PM2(一款负责运行和管理 Node.js 应用的进程管理工具)在每次部署后自动重启应用。

只需sudo npm install pm2@latest -g在您的 EC2 实例上运行此命令,然后生成 pm2 ecosystem.config.js 文件,以声明您希望将代码部署到的应用程序/服务pm2 ecosystem。PM2 将为您生成一个示例文件,请确保其与您的应用程序结构相匹配。

ecosystem.config.js 文件

module.exports = {
  apps : [{
    name: "npm",
    cwd: '/usr/share/nginx/html',
    script: "npm",
    args: 'start',
    env: {
      NODE_ENV: "production",
      HOST: '0.0.0.0',
      PORT: '3000',
    },
  }]
}
Enter fullscreen mode Exit fullscreen mode

现阶段,您只需运行pm2 start ecosystem.config.jsPM2 即可启动您的应用程序。但这并非 PM2 的全部功能。只需在 ecosystem.config.js 文件中添加 watch 参数,该模块即可在每次新版本发布时自动重启您的应用程序。

最终的 ecosystem.config.js 文件

module.exports = {
  apps : [{
    name: "npm",
    cwd: '/usr/share/nginx/html',
    script: "npm",
    args: 'start',
    watch: true,
    env: {
      NODE_ENV: "production",
      HOST: '0.0.0.0',
      PORT: '3000',
    },
  }]
}
Enter fullscreen mode Exit fullscreen mode

太棒了!我们已经建立了一个全自动的部署管道,可以运行单元测试,在 Amazon EC2 实例上安装、构建和部署节点模块,然后 PM2 会负责为我们重启应用程序。

如果服务器因为某种原因重启了怎么办?我们希望应用程序能够自动启动,这也可以通过pm2 startup在应用程序启动后执行的参数来实现。

我们之前是不是漏掉了什么?哦,对了!自动扩缩容。
我们希望确保生产环境具有足够的扩展性,能够应对应用程序的巨大负载。

这可以通过 AWS CodeDeploy 轻松实现,只需将部署组环境配置从 Amazon EC2 实例的“标记策略”更新为 Amazon EC2 自动扩展组即可。AWS CodeDeploy 的这项强大功能可以自动将最新版本部署到新实例,并在整个部署过程中保持所需数量的实例处于健康状态。然而,我们在这里会遇到另一个挑战。PM2 启动机制确保应用程序在任何实例重启后都能启动,但遗憾的是,当自动扩展组启动新实例时,它无法正常工作,因此在水平扩展的情况下,应用程序不会自动运行。不过别担心,我会帮你解决这个问题!

要解决此问题,请转到启动配置设置,并在“userdata”部分中添加以下 bash 脚本。

#!/bin/bash -ex
# restart pm2 and thus node app on reboot
crontab -l | { cat; echo "@reboot sudo pm2 start /usr/share/nginx/html/ecosystem.config.js -i 0 --name \"node-app\""; } | crontab -
# start the server
pm2 start /usr/share/nginx/html/ecosystem.config.js -i 0 --name "node-app"
Enter fullscreen mode Exit fullscreen mode

好了!现在您拥有了一个高度可扩展的 NodeJS 应用程序,它使用 AWS CodePipeline 实现了完全自动化。

结论

希望这篇博客对大家有所帮助。我尽量让这篇博客读起来像个故事,因为写它的主要目的是向大家展示DevOps工程师和开发人员在搭建这套解决方案时面临的诸多挑战,以及他们使用的各种解决方案。我会持续更新这个项目,并确保它有改进计划,因为我知道它还可以做得更好!

参考:

文章来源:https://dev.to/bassel_alannan/deploying-nodejs-application-on-amazon-ec2-using-aws-codepipeline-17hg