はじめに

Serverless Framework というサーバーレスアプリケーションの構築、デプロイ、管理ができるフレームワークがあります。有名なので利用されている方も多いと思います。

Serverless: Zero-Friction Serverless Apps On AWS Lambda & Beyond.

Serverless Framework でアプリケーションをAWSにデプロイする際、設定ファイルの serverless.yml に以下の設定があると「xxxx-custom-resource-apigw-cw-role」という名前のLambda関数が自動生成されます。

  logs:
    restApi:  # これ

API Gateway のログを CloudWatch Logs に出力させるためにこの関数が自動生成されるようです。関数のランタイムはNode.jsですが、Serverless Framework v1とv2ではバージョンが古く(v12〜16の範囲)、いずれもNode.jsとしてはすでにEOLであり、AWSでも2025年3月31日以降はこのバージョンのLambda関数は作成も更新もできなくなります。

Lambda ランタイム > 非推奨のランタイム

※なお Serverless Framework v3の最終バージョンである v3.39.0 では Node.js 18 、最新のv4では Node.js 20 になっています。

調べたところランタイム(Node.js)バージョンを上げる単純な方法はなさそうで、結果的にやや強引な手段をとることになりました。

古い Serverless Framework を使わざるをえない状況もまだあるかと思うので、ニッチな話題ではありますが共有したいと思います。

xxxx-custom-resource-apigw-cw-role の何が問題か

1. ランタイムバージョンが低い

あらためてですが、Serverless Framework v1とv2の最終バージョンで自動生成されるLambda関数「xxxx-custom-resource-apigw-cw-role」はそれぞれ以下のNode.jsランタイムを使用しています。

  • v1.83.3 … Node.js v12.x
  • v2.72.4 … Node.js v16.x

「はじめに」で書いたようにこれらはNode.jsとしてもEOL、AWSでもサポートが終了しており、v2系でも2025年3月31日以降は Serverless Framework によるデプロイができなくなる(失敗する)可能性があります。

2. Node.jsのバージョン指定がハードコーディングされている

このLambda関数のNode.jsランタイムバージョンの指定は Serverless Framework を構成するファイルの一つ lib/plugins/aws/customResources/index.js にハードコーディングされています。

例:v2.72.4の場合

  const customResourceFunction = {
    Type: 'AWS::Lambda::Function',
    Properties: {
      Code: {
        S3Bucket,
        S3Key,
      },
      FunctionName: absoluteFunctionName,
      Handler,
      MemorySize: 1024,
      Runtime: 'nodejs16.x',  // これ
      Timeout: 180,
    },
    DependsOn: [],
  };

つまり設定ファイル serverless.yml でのバージョン指定などはできず、このファイルを直接書き換えるしか方法はありません。

Node.js 18.x以上に変更する方法

というわけで lib/plugins/aws/customResources/index.js'nodejs16.x' の数字を変更すればNode.jsのバージョンを上げることができます。

  const customResourceFunction = {
    Type: 'AWS::Lambda::Function',
    Properties: {
      Code: {
        S3Bucket,
        S3Key,
      },
      FunctionName: absoluteFunctionName,
      Handler,
      MemorySize: 1024,
+     Runtime: 'nodejs18.x',  // 18.xに変更
      Timeout: 180,
    },
    DependsOn: [],
  };

基本的にはこれで今回の話は終わるのですが、
「Serverless Framework をインストール(npm install)するたびに書き換えないといけない」という問題があります。
たとえばDockerコンテナ内で作業するような場合、コンテナを起動するたびに書き換え作業も発生します。
Dockerで手作業による書き換えをせずに済ませる方法が2つあるので紹介します。

Dockerでの対応方法1:変更後のファイルをイメージに含める

Serverless Framework を始めとした諸々のnpmパッケージがインストールされた状態でDockerイメージを作成できる環境であれば、上述の変更後の index.js を含めた状態でイメージ化し、以後そのイメージからコンテナを立ち上げることで毎回の変更作業を不要にできます。

Dockerでの対応方法2:Dockerfileに書き換え処理を書く

対応方法1をとれない場合はDockerfileにバージョンの書き換え処理を入れておくことで代替します。

RUN sed -i -E "s/'nodejs.{2}\.x'/'nodejs18.x'/g" /path/to/node_modules/serverless/lib/plugins/aws/customResources/index.js

Dockerfileの npm install している行の後にこのような行を追加することで、書き換えられた状態でイメージビルドされます。

注意点として npm install の直後に追加すると、docker build 時にインストールされたファイルがディスクに書き込まれる前に sed が走ってしまい書き換えに失敗することがあります。
その場合は間に sleep を入れて少し待ち時間を作ります。

RUN npm install && sleep 2  # npm install後2秒間待機
RUN sed -i -E "s/'nodejs.{2}\.x'/'nodejs18.x'/g" /path/to/node_modules/serverless/lib/plugins/aws/customResources/index.js