Search code examples
node.jsrubyaws-lambdaserverless-frameworkserverless-webpack-plugin

How to setup serverless.yml and webpack.config for a multiple-runtime AWS Lambda service


I want to deploy AWS Lambda functions with Node8.10 and Ruby2.5 runtimes from one serverless.yml file.

I set up the following folder structure, with /node and /ruby holding my respective handlers.

 -/nodeRubyLambdas
  -/node
    -handler.js
    -package.json, package-lock.json, /node_modules
  -/ruby
    -rubyRijndaelEncryption.rb
    -Gemfile, Gemfile.lock, /vendor
  -serverless.yml
  -webpack.config.js
  -package.json for serverless-webpack

Here is my serverless.yml

service: nodeRubyLambdas

plugins:
  - serverless-webpack
  - serverless-offline

custom:
  webpack:
    webpackConfig: ./webpack.config.js
    includeModules: true

provider:
  name: aws
  stage: dev
  region: us-west-2
  iamRoleStatements:
    - Effect: Allow
      Action:
        - lambda:InvokeFunction
      Resource: "*"

  package:
    individually: true

functions:
  nodeMain:
    handler: node/handler.main
    runtime: nodejs8.10
    events:
      - http:
          path: main
          method: get
    package:
      individually: true
  rubyEncryption:
    handler: ruby/rubyRijndaelEncryption.lambda_handler
    runtime: ruby2.5
    environment:
      RIJNDAEL_PASSWORD: 'a string'
    package:
      individually: true

My webpack configuration: (This is the base example, I just added the bit to ignore ruby files when I got my first error.)

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

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  // Generate sourcemaps for proper error messages
  devtool: 'source-map',
  // Since 'aws-sdk' is not compatible with webpack,
  // we exclude all node dependencies
  externals: [nodeExternals()],
  mode: slsw.lib.webpack.isLocal ? "development" : "production",
  optimization: {
   // We do not want to minimize our code.
   minimize: false
  },
  performance: {
    // Turn off size warnings for entry points
    hints: false
  },
  // Run babel on all .js files and skip those in node_modules
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: __dirname,
        exclude: [/node_modules/, /\.rb$/]
      }
    ]
  }
};

Fail #0: [Webpack Compilation error] Module parse failed

Fail #1: Basically, webpack assumes all functions are .js and tries to package them as such. Based off this suggestion, I forced my entry point in webpack config to be my handler.js

 module.exports = {
  entry: "./node/handler.js",
  target: "node",
  ...

This packages ONLY the Node Lambda. An empty placeholder for the Ruby Lambda is created on AWS.

Fail #2: I commented out webpack from serverless.yml and added include and exclude statements in the functions package options.

functions:
  nodeMain:

    package:
      individually: true
      include:
        - node/**
        - handler.js
      exclude:
        - ruby/**
        - rubyLambda/**
  rubyEncryption:

    package:
      individually: true
      include:
        - vendor/**
        - rubyRijndaelEncryption.rb
      exclude:
        - Gemfile
        - Gemfile.lock
        - node/**

This gets an [ENOENT: no such file or directory] for node/node_modules/@babel/core/node_modules/.bin/parser. This file is not there but I don't understand why it is looking for it, since webpack is not being called.

Sort of success?: I was able to get the Lambdas to deploy if I commented out webpack and used

    serverless deploy function -f <function name here>

to deploy the Ruby Lambda and then uncommented webpack and used the same thing to deploy the Node Lambda.

I'm convinced that there's a better way to get them to deploy; Have I missed something in my setup? Is there another option I haven't tried?

P.S. I did see this pull request https://github.com/serverless-heaven/serverless-webpack/pull/256, but it seems to be abandoned since 2017.


Solution

  • serverless-webpack is not designed for non-JS runtimes. It hijacks serverless packaging and deploys ONLY the webpack output.

    Here are your options:

    1. Don't use serverless-webpack and simply use serverless' built-in packaging.
    2. You can use webpack directly (not serverless-webpack), and change your build process to compile using webpack first and then let serverless deploy the output folder.

    P.S. The package.individually property is a root-level property in your serverless.yml. It shouldn't be in provider or in your function definitions.