让Sequelize.js库在Amazon Lambda上运行

13

我正在尝试在亚马逊上运行一个Lambda函数,最终通过在亚马逊的测试控制台中测试Lambda函数缩小了错误范围。

我得到的错误是:

{
  "errorMessage": "Please install mysql2 package manually",
  "errorType": "Error",
  "stackTrace": [
    "new MysqlDialect (/var/task/node_modules/sequelize/lib/dialects/mysql/index.js:14:30)",
    "new Sequelize (/var/task/node_modules/sequelize/lib/sequelize.js:234:20)",
    "Object.exports.getSequelizeConnection (/var/task/src/twilio/twilio.js:858:20)",
    "Object.<anonymous> (/var/task/src/twilio/twilio.js:679:25)",
    "__webpack_require__ (/var/task/src/twilio/twilio.js:20:30)",
    "/var/task/src/twilio/twilio.js:63:18",
    "Object.<anonymous> (/var/task/src/twilio/twilio.js:66:10)",
    "Module._compile (module.js:570:32)",
    "Object.Module._extensions..js (module.js:579:10)",
    "Module.load (module.js:487:32)",
    "tryModuleLoad (module.js:446:12)",
    "Function.Module._load (module.js:438:3)",
    "Module.require (module.js:497:17)",
    "require (internal/module.js:20:19)"
  ]
}

很简单,我需要安装mysql2。所以我把它添加到了我的package.json文件中。

{
  "name": "test-api",
  "version": "1.0.0",
  "description": "",
  "main": "handler.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 0"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "aws-sdk": "^2.153.0",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-3": "^6.24.1",
    "serverless-domain-manager": "^1.1.20",
    "serverless-dynamodb-autoscaling": "^0.6.2",
    "serverless-webpack": "^4.0.0",
    "webpack": "^3.8.1",
    "webpack-node-externals": "^1.6.0"
  },
  "dependencies": {
    "babel-runtime": "^6.26.0",
    "mailgun-js": "^0.13.1",
    "minimist": "^1.2.0",
    "mysql": "^2.15.0",
    "mysql2": "^1.5.1",
    "qs": "^6.5.1",
    "sequelize": "^4.31.2",
    "serverless": "^1.26.0",
    "serverless-plugin-scripts": "^1.0.2",
    "twilio": "^3.10.0",
    "uuid": "^3.1.0"
  }
}

我注意到当我执行 sls deploy 命令时,似乎只有部分模块被打包了?

Serverless: Package lock found - Using locked versions
Serverless: Packing external modules: babel-runtime@^6.26.0, twilio@^3.10.0, qs@^6.5.1, mailgun-js@^0.13.1, sequelize@^4.31.2, minimi
st@^1.2.0, uuid@^3.1.0
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Validating template...
Serverless: Updating Stack...

Serverless: Checking Stack update progress...
................................
Serverless: Stack update finished...

我认为这就是它不起作用的原因。简而言之,我该如何使mysql2库与serverless正确打包,以便我的lambda函数可以与sequelize库一起使用?

请注意,当我在本地测试时,我的代码运行良好。

下面是我的serverless文件:

service: testapi

# Use serverless-webpack plugin to transpile ES6/ES7
plugins:
  - serverless-webpack
  - serverless-plugin-scripts
  # - serverless-domain-manager

custom:
  #Define the Stage or default to Staging.
  stage: ${opt:stage, self:provider.stage}
  webpackIncludeModules: true
  #Define Databases Here
  databaseName: "${self:service}-${self:custom.stage}"
  #Define Bucket Names Here
  uploadBucket: "${self:service}-uploads-${self:custom.stage}"
  #Custom Script setup
  scripts:
    hooks:
      #Script below will run schema changes to the database as neccesary and update according to stage.
      'deploy:finalize':  node database-schema-update.js --stage ${self:custom.stage}
  #Domain Setup
  # customDomain:
  #    basePath: "/"
  #    domainName: "api-${self:custom.stage}.test.com"
  #    stage: "${self:custom.stage}"
  #    certificateName: "*.test.com"
  #    createRoute53Record: true

provider:
  name: aws
  runtime: nodejs6.10
  stage: staging
  region: us-east-1
  environment:
    DOMAIN_NAME: "api-${self:custom.stage}.test.com"
    DATABASE_NAME: ${self:custom.databaseName}
    DATABASE_USERNAME: ${env:RDS_USERNAME}
    DATABASE_PASSWORD: ${env:RDS_PASSWORD}
    UPLOAD_BUCKET: ${self:custom.uploadBucket}
    TWILIO_ACCOUNT_SID: ""
    TWILIO_AUTH_TOKEN: ""
    USER_POOL_ID: ""
    APP_CLIENT_ID: ""
    REGION: "us-east-1"
    IDENTITY_POOL_ID: ""
    RACKSPACE_API_KEY: ""
  #Below controls permissions for lambda functions.
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:UpdateTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:us-east-1:*:*"

functions:
  create_visit:
    handler: src/visits/create.main
    events:
      - http:
          path: visits
          method: post
          cors: true
          authorizer: aws_iam
  get_visit:
    handler: src/visits/get.main
    events:
      - http:
          path: visits/{id}
          method: get
          cors: true
          authorizer: aws_iam
  list_visit:
    handler: src/visits/list.main
    events:
      - http:
          path: visits
          method: get
          cors: true
          authorizer: aws_iam
  update_visit:
    handler: src/visits/update.main
    events:
      - http:
          path: visits/{id}
          method: put
          cors: true
          authorizer: aws_iam
  delete_visit:
    handler: src/visits/delete.main
    events:
      - http:
          path: visits/{id}
          method: delete
          cors: true
          authorizer: aws_iam
  twilio_send_text_message:
    handler: src/twilio/twilio.send_text_message
    events:
      - http:
          path: twilio/sendtextmessage
          method: post
          cors: true
          authorizer: aws_iam
  #This function handles incoming calls and where to route it to.
  twilio_incoming_call:
    handler: src/twilio/twilio.incoming_calls
    events:
      - http:
          path: twilio/calls
          method: post
  twilio_failure:
    handler: src/twilio/twilio.twilio_failure
    events:
      - http:
          path: twilio/failure
          method: post
  twilio_statuschange:
    handler: src/twilio/twilio.statuschange
    events:
      - http:
          path: twilio/statuschange
          method: post
  twilio_incoming_message:
    handler: src/twilio/twilio.incoming_message
    events:
      - http:
          path: twilio/messages
          method: post
  twilio_whisper:
    handler: src/twilio/twilio.whisper
    events:
      - http:
          path: twilio/whisper
          method: post
      - http:
          path: twilio/whisper
          method: get
  twilio_start_call:
    handler: src/twilio/twilio.start_call
    events:
      - http:
          path: twilio/startcall
          method: post
      - http:
          path: twilio/startcall
          method: get

resources:
  Resources:
    uploadBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: ${self:custom.uploadBucket}
    RDSDatabase:
      Type: AWS::RDS::DBInstance
      Properties:
        Engine : mysql
        MasterUsername: ${env:RDS_USERNAME}
        MasterUserPassword: ${env:RDS_PASSWORD}
        DBInstanceClass : db.t2.micro
        AllocatedStorage: '5'
        PubliclyAccessible: true
        #TODO: The Value of Stage is also available as a TAG automatically which I may use to replace this manually being put here..
        Tags:
          -
            Key: "Name"
            Value: ${self:custom.databaseName}
      DeletionPolicy: Snapshot
    DNSRecordSet:
      Type: AWS::Route53::RecordSet
      Properties:
        HostedZoneName: test.com.
        Name: database-${self:custom.stage}.test.com
        Type: CNAME
        TTL: '300'
        ResourceRecords:
        - {"Fn::GetAtt": ["RDSDatabase","Endpoint.Address"]}
      DependsOn: RDSDatabase

更新:所以我确认运行sls package --stage dev似乎会在最终上传到AWS的zip文件夹中创建这个问题。 这证实了无服务器未能正确创建具有mysql2引用的软件包,出现了某些问题? 为什么会这样呢?

输入图像描述

如请求的webpack配置文件

const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  // Since 'aws-sdk' is not compatible with webpack,
  // we exclude all node dependencies
  externals: [nodeExternals()],
  // Run babel on all .js files and skip those in node_modules
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: __dirname,
        exclude: /node_modules/
      }
    ]
  }
};

1
您可能需要将mysql2作为外部模块添加。您能展示一下您的webpack.config.js文件吗? - Noel Llevares
好的,稍等一下,我正在使用无服务器技术,所以得看看它们把那个藏在哪里。 - Joseph Astrahan
好的,我已经将它添加到我的问题底部。 - Joseph Astrahan
2个回答

12

感谢dashmugs在这个页面(https://github.com/serverless-heaven/serverless-webpack)的评论后进行了一些调查,其中有一个关于强制包含的部分。 我会在这里进行解释。

强制包含 有时您可能会在代码中使用动态 require,即需要在运行时才知道的模块。Webpack 无法检测此类外部引用,编译后的包将缺少所需的依赖项。 在这种情况下,您可以通过设置forceInclude数组属性来强制插件包含某些模块。 但是,该模块必须出现在package.json的生产依赖项中。

# serverless.yml
custom:
  webpackIncludeModules:
    forceInclude:
      - module1
      - module2

所以我就这样做了...

webpackIncludeModules:
    forceInclude:
      - mysql
      - mysql2

现在它可以工作了!希望这能帮助其他遇到同样问题的人。


2
最初的回答没有帮助我,我使用了这个解决方案:https://github.com/sequelize/sequelize/issues/9489#issuecomment-493304014 关键是使用 dialectModule 属性并覆盖 sequelize。
import Sequelize from 'sequelize';
import mysql2 from 'mysql2'; // Needed to fix sequelize issues with WebPack

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    dialect: 'mysql',
    dialectModule: mysql2, // Needed to fix sequelize issues with WebPack
    host: process.env.DB_HOST,
    port: process.env.DB_PORT
  }
)

export async function connectToDatabase() {
  console.log('Trying to connect via sequelize')
  await sequelize.sync()
  await sequelize.authenticate()
  console.log('=> Created a new connection.')

  // Do something 
}

之前的代码可以在 MySql 上运行,但在 Postgres 上无法运行。

最初的回答:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接