Java や Python の実行環境で Serverless Framework を利用するときにハマったこと

March 27, 2021

Serverless Framework でにデプロイするとき、実行環境の言語(Java、Python)ごとにいくつかハマりポイントがあったので、メモしておきます。

環境

  • Node.js v12.20.1
  • Serverless Framework v2.30.2

Java の場合

基本:デプロイするにはパッケージングが必要

実行環境が Node.js の場合は、 serverless deploy するだけで、関連モジュールを含めてデプロイされます。 TypeScript の場合も、トランスパイルを含めて行ってくれます。

Java の場合、deplopy する前にローカルで jar を作ってデプロイする必要があります。 Maven の場合は以下でパッケージングします。

$ mvn install
$ mvn package

外部ライブラリを含めて fat jar にしたい

先に説明したとおり、Java をデプロイするときにはパッケージングします。 外部ライブラリを利用するときは fat jar を作る必要があります。

Maven の場合は、 maven-shade-plugin を使って fat jar を作ります。

  <dependencies>
    <dependency>
      <groupId>com.example.chick-p</groupId>
      <artifactId>chick-p-lib</artifactId>
      <version>0.1</version>
      <scope>system</scope>
      <systemPath>${basedir}/lib/chick-p-lib-0.1.jar</systemPath>
    </dependency>
  </dependencies>

ローカルにある jar を fat jar に含めたい

先の設定だけでは、ローカルにある jar が fat jar に含まれまないため、次の設定をします。 なお、外部ライブラリは lib 以下に置くとします。

  <build>
    <resources>
      <!--ここから -->
      <resource>
        <directory>${project.basedir}</directory>
        <includes>
          <include>lib/*.jar</include>
        </includes>
      </resource>
      <!--ここまで -->
    </resources>
    <plugins>

これでローカルにある jar を含めた fat jar を作成できます。

serverless invoke local すると途中で止まるのを解決したい

問題

Serverless Framework では、Lambda へデプロイする前に次のコマンドでローカル実行できます。

$ serverless invoke local --function FUNCTION_NAME

ただ、Java の場合、次のメッセージが表示されたまま、ローカル実行できませんでした。

$ mvn package
$ serverless invoke local --function hello
Serverless: Building Java bridge, first invocation might take a bit longer.

解決方法

これについては Issue があがってました。 それよると、初回時に次のコマンドを実行する必要があります。

${NPM_DIR} は Serverless Framework がインストールされているディレクトリのひとつ上のディレクトリです。グローバルインストールしている場合は、npm root -g で確認したディレクトリのひとつ上のディレクトリを指定します。

$ cd ${NPM_DIR}/node_modules/serverless/lib/plugins/aws/invokeLocal/runtimeWrappers/java
$ mvn package

この設定をしたあと serverless invoke local すると、無事にローカルで実行できます。

$ mvn package
$ serverless invoke local --function hello
Serverless: In order to get human-readable output, please implement "toString()" method of your "ApiGatewayResponse" object.
2020-03-28 10:05:00  INFO  Handler:18 - received: {}

com.serverless.ApiGatewayResponse@71e9ddb4

ただし、ローカルにある jar を含めた fat jar でローカル実行できませんでした。ローカル jar に含まれるクラスに対し、ClassNotFoundException という例外が出力されました。この解決方法はわかっていません。

Python の場合

外部モジュールを使いたい

実行環境が Python で外部モジュールを利用する場合、いくつかの設定が必要です。

まず、npm で Serverless Python Requirements をインストールします。

$ npm init
$ npm install -D serverless-python-requirements

動作を確認したバージョンは Serverless Python Requirements v5.1.0 です。

serverless.yml にプラグインの設定をします。

plugins:
  - serverless-python-requirements

requirements.txt に利用するモジュール名を記載します。この例では requests を利用することとします。

requests

この設定をしたあと、serverless deploy することで、Python でも外部モジュールを利用した Lambda 関数をデプロイできます。

なお、モジュールを利用する Python ファイルに import requirements が必要という記事をみましたが、現在は不要のようです。

Python のバージョンを指定したい

Serverless Framework v2.30.2 で aws-python3 テンプレートを利用すると、Python 3.8 が必要でした。

Python のバージョンを指定して Serverless Framework を利用するには、公式ドキュメントに案内されているように、Python のランタイムのパスを指定します。

custom:
  pythonRequirements:
    pythonBin: /opt/python3.8/bin/python

全体

実行環境によりませんが、次のハマりごともありました。

AWS のプロファイルの設定を読み取ってくれない

問題

AWS のプロファイル設定を --aws-profile オプションで渡したとき、認証できないというエラーが出ることがありました。

$ serverless delopy --aws-profile PROFILE_NAME
User: arn:aws:iam::111111111111::assumed-role/role-name is not authorized to perform: cloudformation:DescribeStacks on resource: arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/sample ....

このエラーメッセージで検索すると、IAM ロールへアタッチしたポリシーの権限が足りないのでは、というアドバイスが見受けられました。 そこでポリシーを確認してみましたが、十分な権限を渡していました。

  • AWSLambdaFullAccess
  • IAMFullAccess
  • AmazonAPIGatewayAdministrator
  • AWSCloudFormationFullAccess

AWS-CLI で cloudformation のスタック一覧を取得してみても、問題なさそうでした(スタックがないので空が返ってきました)。

$ aws cloudformation describe-stacks --profile PROFILE_NAME
[]

解決方法

AWS_SDK_LOAD_CONFIGtrue に設定します。

$ export AWS_SDK_LOAD_CONFIG=1

AWS SDK for JavaScript のドキュメントによるとこの環境変数を設定することで、プロファイルの内容を読み取ってくれるようです。

https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/setting-region.html#setting-region-config-file AWS_SDK_LOAD_CONFIG 環境変数が真の値に設定されている場合、SDK for JavaScript はロード時に config ファイルを自動的に検索します。

エラーが出た AWS アカウントは SSO 管理されたアカウントで、通常の AWS アカウントのときに比べて、プロファイルの設定項目が増えていました。 そのため、この環境変数を設定しないと読み取ってくれない項目があったようです。