昨年 Lambda のコンテナサポートが発表されました。
ついに 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 構成を作ることができました。
この構成を元に簡単なマイクロサービスを作る予定なので、モノができたら会社のテックブログでまたご紹介する予定です。