Serverless内で使っている環境変数を外部と共有する方法を紹介します。Serverlessでは設定ファイル内でCloudFormationを書くことができ、そこで作成したS3などのResourceをServerlessプロジェクトから使うことができます。この機能はとても便利なのですが、作りたいアプリケーションが、常にServerlessだけで完結できるとは限らず、ときにはServerlessプロジェクト外のスクリプトから、Resourceの値を参照したい場合があります。
例えば、以下の記事ではSageMakerの学習にあたってServerlessプロジェクト外の環境からSageMakerのAPIを呼び出しています。具体的には、「ローカルスクリプトで学習」「Serveless(Lambda)から予測」という感じです。
こんなときに、どうやってServerlessプロジェクト外のスクリプトからServerless内のResourceを参照すれば良いか、について書きたいと思います。
問題設定
まず、「解決したいのがどんな問題なのか」を明確にしたいと思います。 想定するのは、下記のようにServerlessを使って作成したS3 Bucketに、ローカルのPythonスクリプトでファイルをアップロードするような処理です。
このとき、ローカルのPythonスクリプトはS3 Bucketの名前を知らないと、当然ですがファイルをアップロードすることが出来ません。 このS3 Bucketの名前のようなServerlessで作成したResource情報にローカルのPythonスクリプトからアクセスしたいと思います。
サンプルとして下記のserverless.ymlを作成してデプロイしてみます。
serverless.yml
service: sharing-env custom: default_stage: dev stage: ${opt:stage, self:custom.default_stage} s3_bucket: ${self:service}-${self:custom.stage} provider: name: aws runtime: python3.7 stage: ${self:custom.stage} region: ${self:custom.region} functions: hello: handler: handler.hello resources: Resources: S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:custom.s3_bucket} AccessControl: Private
sls deploy
デプロイが成功するとS3 Bucketが作成されます。
このS3 Bucketの名前(sharing-dev)をローカルのPythonスクリプトから取得するのがゴールです。
CloudFormationにアクセスする
このS3 Bucketの名前を取得するために、boto3を使ってCloudFormationのStackにアクセスします。Serverlessは「 service
に設定した名前+stage」 を名前としてStackを作成するので、そのStackの名前を指定し、ローカルのPythonからアクセスします。アクセスにあたってCloudFormationのOutputsを使うのが良いでしょう。
それでは先程作成したサンプルを使って実証してみます。まず、先ほどのserverless.ymlにOutputsを追加します。
serverless.yml
resources: Resources: S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:custom.s3_bucket} AccessControl: Private Outputs: # この行以降を追加 S3Bucket: # <= この名前がKeyになる Value: ${self:custom.s3_bucket}
これでデプロイするとCloudFormationのOutputsにS3 Bucketの名前が出力されます。
このOutputsにアクセスします。boto3を使って下記のように取得することができます。
upload.py
import os import boto3 # CloudFormationから環境変数を読み出し ## CFのStack設定 SERVICE_NAME = "sharing-env" ENV = os.environ.get("ENV", "dev") STACK_NAME = f"{SERVICE_NAME}-{ENV}" ## Outputsを{Key: Valueの形で読み出し} stack = boto3.resource('cloudformation').Stack(STACK_NAME) outputs = {o["OutputKey"]: o["OutputValue"] for o in stack.outputs} S3_BUCKET = outputs["S3Bucket"] print(S3_BUCKET) # => sharing-env-dev (Bucket名) # ...アップロード処理(略)
Serverlessで作成したResourceにアクセスすることができました。
今回は単純にS3 Bucketの名前を共有しましたが、 GetAtt
や Ref
などのCloudFormationの関数を使ってARNなどの値を共有することも可能です。
さいごに
Serverlessプロジェクト内の情報を外部と共有する方法を紹介しました。 出来てしまうと簡単なことなのですが、意外と同じ内容を記載している記事が見当たらなかったかつ、よくありそうなユースケースなのでまとめました。 使ったコードは↓のリポジトリに置いてあります。