mof-brown

ずっとモフモフしてたい

AWS SAM を使って API Gateway + Lambda + コンテナ構成を試してみる

2021-01-05

昨年 Lambda のコンテナサポートが発表されました。

https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-lambda-now-supports-container-images-as-a-packaging-format/

ついに Cloud Run が来たか!?と興奮しつつ、ちょうど AWS SAM を使った Lambda の開発を試したかったので、合わせて検証してみました。結論を先に書くと、Cloud Run とは開発体験が異なるものでした。残念。

基本的に以下の記事に沿って進めていきました。

https://aws.amazon.com/jp/blogs/compute/using-container-image-support-for-aws-lambda-with-aws-sam/

実行環境の準備

ホストマシンに aws cli をセットアップするのがあまり好みではないので、generic/centos8 をベースにした Vagrant で実行環境を構築します。

まずは git と pip をインストールします。

$ yum install -y git
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ python3 get-pip.py

aws cli をインストールします。

$ pip install awscli

aws sam cli をインストールします。

$ pip install aws-sam-cli

公式サイトを参考に docker をインストールします。

$ yum install -y yum-utils
$ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ yum install -y docker-ce docker-ce-cli containerd.io
$ service docker start

SAM アプリケーションを作成する

sam init コマンドでアプリケーションの雛形を作成します。

今回はコンテナイメージを利用したいので、パッケージタイプに 2 - Image を選択します。

$ sam init
Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location
Choice: 1
What package type would you like to use?
    1 - Zip (artifact is a zip uploaded to S3)
    2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 2

Which base image would you like to use?
    1 - amazon/nodejs12.x-base
    2 - amazon/nodejs10.x-base
    3 - amazon/python3.8-base
    4 - amazon/python3.7-base
    5 - amazon/python3.6-base
    6 - amazon/python2.7-base
    7 - amazon/ruby2.7-base
    8 - amazon/ruby2.5-base
    9 - amazon/go1.x-base
    10 - amazon/java11-base
    11 - amazon/java8.al2-base
    12 - amazon/java8-base
    13 - amazon/dotnetcore3.1-base
    14 - amazon/dotnetcore2.1-base
Base image: 9

Project name [sam-app]: sam-sample-app

Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates

    -----------------------
    Generating application:
    -----------------------
    Name: sam-sample-app
    Base Image: amazon/go1.x-base
    Dependency Manager: mod
    Output Directory: .

    Next steps can be found in the README file at ./sam-sample-app/README.md

無事成功すると、以下の構成のディレクトリが作成されます。

sam-sample-app/
├── hello-world
│   ├── Dockerfile
│   ├── go.mod
│   ├── main.go
│   └── main_test.go
├── README.md
└── template.yaml

Lambda をローカルで実行してみる

次に sam build を実行します。

このコマンドによってコンテナがビルドされたり、CloudFormation のファイルが作成されたりと、この後に続く操作の準備を色々と行ってくれます。

$ cd sam-sample-app/
$ sam build
Building codeuri: . runtime: None metadata: {'DockerTag': 'go1.x-v1', 'DockerContext': './hello-world', 'Dockerfile': 'Dockerfile'} functions: ['HelloWorldFunction']
Building image for HelloWorldFunction function
Setting DockerBuildArgs: {} for HelloWorldFunction function
Step 1/7 : FROM golang:1.14 as build-image
 ---> ae11962d94b7
Step 2/7 : WORKDIR /go/src
 ---> Using cache
 ---> d52382eade7e
Step 3/7 : COPY go.mod main.go ./
 ---> bbfed89458c1
Step 4/7 : RUN go build -o ../bin
 ---> Running in d2c7b7a2c797
go: downloading github.com/aws/aws-lambda-go v1.13.3
 ---> 1f0312801cea
Step 5/7 : FROM public.ecr.aws/lambda/go:1
 ---> a526d7ecd02d
Step 6/7 : COPY --from=build-image /go/bin/ /var/task/
 ---> Using cache
 ---> ddb553eec20d
Step 7/7 : CMD ["hello-world"]
 ---> Using cache
 ---> 9b27735619ae
Successfully built 9b27735619ae
Successfully tagged helloworldfunction:go1.x-v1

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

ビルドが成功すると、sam local invoke を叩いて Lambda が実行できるようになります。

$ sam local invoke
Invoking Container created from helloworldfunction:go1.x-v1
Image was not found.
Building image.................
Skip pulling image and use local one: helloworldfunction:rapid-1.15.0.

START RequestId: xxxxxx-xxxx-xxxx-xxxx-xxxxxx Version: $LATEST
END RequestId: xxxxxx-xxxx-xxxx-xxxx-xxxxxx
REPORT RequestId: xxxxxx-xxxx-xxxx-xxxx-xxxxxx  Init Duration: 0.18 ms  Duration: 765.21 ms Billed Duration: 800 ms Memory Size: 128 MB Max Memory Used: 128 MB
{"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"Hello, xxx.xxx.xxx.xxx\n"}

API Gateway 相当を起動することも可能です。

$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-01-03 13:44:45  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

コンテナイメージ用の ECR リポジトリを作成する

次に Lambda で利用するコンテナイメージの登録先となる ECR リポジトリを作成します。

まずは aws configure でアクセストークン等を設定します。ざっくりですが、この時必要な権限は大体この辺になります。

  • IAMFullAccess
  • AmazonS3FullAccess
  • AmazonAPIGatewayAdministrator
  • AWSCloudFormationFullAccess
  • AWSLambda_FullAccess
  • ECR 関連の権限

ECR 関連の権限については規定のポリシーが無いため、手動で登録する必要がありました。具体的には以下の権限を含めたポリシーを作成してアタッチしました。

[
  "ecr:GetAuthorizationToken",
  "ecr:GetDownloadUrlForLayer",
  "ecr:CreateRepository",
  "ecr:GetRepositoryPolicy",
  "ecr:SetRepositoryPolicy",
  "ecr:InitiateLayerUpload",
  "ecr:UploadLayerPart",
  "ecr:CompleteLayerUpload",
  "ecr:PutImage",
  "ecr:BatchGetImage",
  "ecr:BatchCheckLayerAvailability"
]

aws configure を行ったら、以下のコマンドで ECR リポジトリを作成します。登録後、リポジトリの URL をコピーしておきます。

$ aws ecr create-repository --repository-name sam-sample-app-hello-world --image-tag-mutability IMMUTABLE --image-scanning-configuration scanOnPush=true

デプロイしてみる

sam deploy コマンドで実際にデプロイしてみます。初回は -g オプションを付与して対話的にデプロイ操作を行います。

$ sam deploy -g

Configuring SAM deploy
======================

    Looking for config file [samconfig.toml] :  Not found

    Setting default arguments for 'sam deploy'
    =========================================
    Stack Name [sam-app]: sam-sample-app
    AWS Region [us-east-1]: ap-northeast-1
    Image Repository for HelloWorldFunction: xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/sam-sample-app-hello-world
      helloworldfunction:go1.x-v1 to be pushed to xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/sam-sample-app-hello-world:helloworldfunction-xxxxxxxx-go1.x-v1

    #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
    Confirm changes before deploy [y/N]: y
    #SAM needs permission to be able to create roles to connect to the resources in your template
    Allow SAM CLI IAM role creation [Y/n]: Y
    HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
    Save arguments to configuration file [Y/n]: Y
    SAM configuration file [samconfig.toml]:
    SAM configuration environment [default]:

    Looking for resources needed for deployment: Not found.
    Creating the required resources...
    Successfully created!

成功すると samconfig.toml 似上記設定内容が記録されるため、次回からは指定が不要となります。

出力結果に出てきた URL をベースに /hello にアクセスして、以下のような結果が出力されれば成功です。

Hello, xxx.xxx.xxx.xxx

API Gateway のステージについて

SAM で API Gateway を作成すると Prod/Stage というステージが勝手に作られてしまうようです。 軽く調べてみたところ不具合のようで、下記記事を参考に対処しました。

https://medium.com/veltra-engineering/avoid-aws-sam-stage-stage-45f7331b7b5d

おわりに

以上で API Gateway + Lambda + コンテナでの REST API 構成を作ることができました。

この構成を元に簡単なマイクロサービスを作る予定なので、モノができたら会社のテックブログでまたご紹介する予定です。

@macococo

株式会社 hacomono 取締役 CTO | フィットネスクラブ・ヨガスタジオ向け会員管理・予約・決済システム hacomono を開発しています。 | ネコチャン | ゲーム | ポケカ初心者勢