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

Blog

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アカウントのときに比べて、プロファイルの設定項目が増えていました。 そのため、この環境変数を設定しないと読み取ってくれない項目があったようです。