本記事は「AWS LambdaとServerless Advent Calendar 2020」の23日目の記事です。
LambdaとServerlessのadvent calendarなのでLambdaの事を書きます。
コンテナをそのままLambdaのランタイムに使えるようになってたりとかそのへんが気になってますが、個人的に好きな組み合わせを紹介いたします。
これやります
SAM(Serverless Application Model)にて管理するLambdaをCodeBuildを使ってdeployをする話を書きます。
githubにpushすると、CodeBuildが実行されてLambdaがdeployされる仕組みです。
ついでにcustom runtimeを使って実行するJuliaのLayerもdeployします。
何がいいのか
SAMやChalise、Serverless FrameworkなどのツールはCLIからtestからdeployまで出来て便利です。S3やらSQSやらIAMロールやらいろんなリソースも同時に作る事もできます。
が、その反面、IAMユーザー(or ロール)の権限がめっちゃ強くないといけない。個人でやる分にはいいけど、会社でやるとなるとそんな強いユーザーを扱えない人もちらほらと。
でもgithubにpushなら開発者なら誰でもできる。なんでdeployだけは個人のIAMとは切り離して行いたいわけです。
目次
- SAMのインストールとHello World
- custom runtimeのLayerをsam build
- Lambdaをsam build
- Code Buildの設定
環境
- Amazon Linux 2 (on Cloud9)
- t3.micro
- docker 19.03.13-ce
SAMのインストールとHello World
まずはインストール周りを軽く。
ここ見ればできるんですけどね。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
Linux環境でやってます。
AWS アカウント作成方法
IAM アクセス許可の設定
Docker をインストール
すでにできてたので割愛
インストール Homebrew.
macで使うパッケージマネージャかと思ってたんだけど、Linuxで使ってもいいんですね。
ちょっと意外でした。
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
$ test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv)
$ test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
$ test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile
$ echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile
$ brew --version
$ Homebrew 2.6.2
Homebrew/linuxbrew-core (git revision ee302cd; last commit 2020-12-19)
1. AWS SAM CLI のインストール
そしてhomebrewにてインストールします。
$ brew tap aws/tap
$ brew install aws-sam-cli
$ sam --version
SAM CLI, version 1.13.2
version 1.13.2 が入りました。
ついでにHelloWorldだけ。
Hello World
チュートリアル: Hello Worldアプリケーションの導入
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html
$ sam init
SAM CLI now collects telemetry to better understand customer needs.
You can OPT OUT and disable telemetry collection by setting the
environment variable SAM_CLI_TELEMETRY=0 in your shell.
Thanks for your help!
Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html
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: 1
Which runtime would you like to use?
1 - nodejs12.x
2 - python3.8
3 - ruby2.7
4 - go1.x
5 - java11
6 - dotnetcore3.1
7 - nodejs10.x
8 - python3.7
9 - python3.6
10 - python2.7
11 - ruby2.5
12 - java8.al2
13 - java8
14 - dotnetcore2.1
Runtime: 2
Project name [sam-app]:
Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates
AWS quick start application templates:
1 - Hello World Example
2 - EventBridge Hello World
3 - EventBridge App from scratch (100+ Event Schemas)
4 - Step Functions Sample App (Stock Trader)
5 - Elastic File System Sample App
Template selection: 1
-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: python3.8
Dependency Manager: pip
Application Template: hello-world
Output Directory: .
Next steps can be found in the README file at ./sam-app/README.md
お!なんか早速、2 - Image (artifact is an image uploaded to an ECR image
ってのが増えてる。ECRからLambda作れるようになりましたよね。
ちょっと試してみたいけど遠回りする余裕がないのでなくなくスルー。あとでやる。
$ cd sam-app
$ sam build
Building codeuri: hello_world/ runtime: python3.8 metadata: {} functions: ['HelloWorldFunction']
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource
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
これであっさりとLambdaは完成。sam deploy
ってやればオンラインで動いてしまう。
でもここはローカルで試すだけにしておきます。
$ sam local invoke
Invoking app.lambda_handler (python3.8)
Image was not found.
(略)
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"
OK。動きました。
sam local invoke
はaws cli lambda invoke
をdeploy前のローカルのLambdaに実行する感じのコマンドですね。
2. custom runtimeのLayerをsam build
さて、お次です。
なんの脈略もないのですが、Juliaという言語を扱うためのcustom runtimeをbuildしてみます。
Juliaは使ったことないのですが、試しにという事で。もちろん、Lambdaがデフォルトで扱える言語には含まれていません。
カスタム ランタイムの作成
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/building-custom-runtimes.html
こちらを参考にさせていただきます。
https://github.com/FugroRoames/aws-lambda-julia-runtime
ちょっとversionが古いみたいですけどね。今回はまあいいです。
$ git clone https://github.com/FugroRoames/aws-lambda-julia-runtime
$ cd aws-lambda-julia-runtime
$ make build-runtime
ってやるとzipファイルが生成されます。これをupできれば良さそう。
途中でコンテナも作られてるし、Type:Image
にした方が綺麗そうではあるけども。
ちょっと今回は手抜きで、このフォルダ(aws-lambda-julia-runtime)内で作業をしていきます。
なお、今回ですが、LayerとFunctionを別々にdeployしていきます。
Layerは本来共通化して使うのがいいと思いますし。
Layerのdeploy
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: A starter AWS Lambda function.
Resources:
JuliaLayer:
Type: AWS::Serverless::LayerVersion
Properties:
Description: Layer description
ContentUri: '.'
CompatibleRuntimes:
- provided
Metadata:
BuildMethod: makefile
Makefileですがsamのルールにしたがって、buildターゲット名を変更しています。
上の方の Transform
はcloudformationで使う時に役に立つみたい。今回はこれが必要になります。
それと、もともとはzipファイルを生成して完了なbuildなのですが、ローカルに一度展開するという無駄な作業も。ちょっと強引ですけどね。
unzipの展開先をホームディレクトリ直下の~/aws-lambda-julia-runtime
にしてしまったので、同じことやるかたは自分の環境に合わせてください。
( Type:Image
にしてDockerイメージのままupしたいところだけど、そこまでやる余裕がなかった。。)
21c21
< build-JuliaLayer:
---
> build-runtime:
36d35
< @unzip $(CURDIR)/packaging/$(BUNDLE) -d ~/aws-lambda-julia-runtime
それともう一点。local moduleをincludeしないといけなかったのと、samとaws cliとで展開先が異なるみたいで、packaging/bootstrap
も少しいじりました。
$ diff packaging/bootstrap{,.orig}
10d9
< CMD='using AWSLambdaJuliaRuntime; include("'$CLIENT_MODULE'.jl"); using '$CLIENT_MODULE'; AWSLambdaJuliaRuntime.main('$CLIENT_MODULE');'
12,18c11
< #/opt/bin/julia -e 'using AWSLambdaJuliaRuntime; include("hello.jl"); using hello; AWSLambdaJuliaRuntime.main(hello);'
<
< if [ -f "/opt/bin/julia" ]; then
< /opt/bin/julia -e "$CMD"
< else
< ${LAMBDA_TASK_ROOT}/bin/julia -e $"CMD"
< fi
---
> ${LAMBDA_TASK_ROOT}/bin/julia -e "using AWSLambdaJuliaRuntime; using ${CLIENT_MODULE}; AWSLambdaJuliaRuntime.main(${CLIENT_MODULE});"
まずはbuildです。作成したテンプレートを指定しつつおこないます。
$ sam build -t template-layer.yml
Building layer 'JuliaLayer'
Running CustomMakeBuilder:CopySource
Running CustomMakeBuilder:MakeBuild
Current Artifacts Directory : /home/user/aws-lambda-julia-runtime/.aws-sam/build/JuliaLayer
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
次はdeployします。ガイドを用いつつもおおむねはdefaultで。
無事完了するとAWSコンソール上でできてるのが確認できます。
$ sam deploy -t template-layer.yml --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: julia-layer
AWS Region [us-east-1]: ap-northeast-1
#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]:
Save arguments to configuration file [Y/n]:
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
....
Layerはできました!
3. Lambdaをsam build
お次はFunctionです。
これについては別でDirectory作ります。
あとaws-lambda-julia-runtime
にあるexamplesをコピっておきます。
$ mkdir -p advntLambda1223/lambda_functions/julia-lambda-function
$ cd advntLambda1223/lambda_functions/julia-lambda-function
$ cp -a (任意)/aws-lambda-julia-runtime/examples ./src
ここにtemplate.ymlを作ります。
Layersにarnが記載されてるのがポイントですね。AWSコンソールから確認して貼り付けると良いかと思います。
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Resources:
HelloJuliaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: word_count.handler
Runtime: provided
CodeUri: ./src
Description: ''
MemorySize: 512
Timeout: 30
Layers:
- 'arn:aws:lambda:ap-northeast-1:11111111:layer:JuliaLayer:2'
Events:
Hello:
Type: Api
Properties:
Path: /hello
Method: get
あと、samの場合でRuntime: provided
の場合、Makefileがないとエラーになっちゃったのでほとんど空ですが一応作っておきます。
build-HelloJuliaFunction:
@echo '$(GREEN)Building runtime...$(RESET)'
これで準備は完了なのでbuildしつつ、試しにinvokeもしておきます。
$ sam build
Building codeuri: ./src runtime: provided metadata: {} functions: ['HelloJuliaFunction']
Running CustomMakeBuilder:CopySource
Running CustomMakeBuilder:MakeBuild
Current Artifacts Directory : /home/user/advntLambda1223/lambda_functions/julia-lambda-function/.aws-sam/build/HelloJuliaFunction
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
Invoking word_count.handler (provided)
Downloading arn:aws:lambda:ap-northeast-1:11111111:layer:JuliaLayer [####################################] 73945677/73945677
Image was not found.
Building image.......................
Skip pulling image and use local one: samcli/lambda:provided-aba26257d2bcf3655ab04c8a2.
Mounting /home/user/advntLambda1223/lambda_functions/julia-lambda-function/.aws-sam/build/HelloJuliaFunction as /var/task:ro,delegated inside runtime container
START RequestId: f7fab733-e3da-4d31-93b1-fbcbf04e15b7 Version: $LATEST
Hello World : {}
END RequestId: f7fab733-e3da-4d31-93b1-fbcbf04e15b7
REPORT RequestId: f7fab733-e3da-4d31-93b1-fbcbf04e15b7 Init Duration: 0.06 ms Duration: 3597.43 ms Billed Duration: 3600 ms Memory Size: 512 MB Max Memory Used: 512 MB
{"msg": "Me Rockz!"}
ちゃんと動きましたね!
内容はちょっとシンプルすぎてつまらないですが。。
じゃあこのまま一旦deployまでやっておきます。
$ sam build
Building codeuri: ./examples runtime: provided metadata: {} functions: ['HelloJuliaFunction']
Running CustomMakeBuilder:CopySource
Running CustomMakeBuilder:MakeBuild
Current Artifacts Directory : /home/user/advntLambda1223/aws-lambda-julia-runtime/.aws-sam/build/HelloJuliaFunction
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
ec2-user:~/aws-lambda-julia-runtime (master) $ sam deploy --guided -t template-2.yml
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Found
Reading default arguments : Success
Setting default arguments for 'sam deploy'
=========================================
Stack Name [julia-layer]: julia-function
AWS Region [ap-northeast-1]:
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [Y/n]:
#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]:
Save arguments to configuration file [Y/n]:
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
Looking for resources needed for deployment: Found!
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-xxxxxxxx
A different default S3 bucket can be set in samconfig.toml
Saved arguments to config file
Running 'sam deploy' for future deployments will use the parameters saved above.
The above parameters can be changed by modifying samconfig.toml
.....
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
2020-12-20 05:51:20 - Waiting for stack create/update to complete
......
Successfully created/updated stack - julia-function in ap-northeast-1
できました!
いったんまとめ
ここまでで、
- samを使ってJuliaを実行するcustom runtime Layerをdeploy
- 上記のcustom runtimeを使うfunctionをdeploy
ができました。
4. Code Buildの設定
次はCode Build上でdeployをする仕組みを構築していきます。
何をやるのかというと、
- Gitにup
- CodeBuildにてBuild&deploy
というシンプルな流れを作っていきます。
samで使ったtemplateを使いますが、ちょっとだけいじります。
Api Gatewayも使ってみる。スクリプトは単純なままですが。
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Resources:
HelloJuliaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: hello.handler
Runtime: provided
CodeUri: ./src
Description: ''
MemorySize: 512
Timeout: 30
#Role: arn:aws:iam::1111111:role/LambdaExecRole
Layers:
- 'arn:aws:lambda:ap-northeast-1:11111111:layer:JuliaLayer:1'
Events:
Hello:
Type: Api
Properties:
Path: /
Method: get
ディレクトリ構造
$ tree -la
.
└── lambda_functions
├── .build
│ ├── build.sh #CodeBuildが実行するshell
│ └── buildspec.yml #Build設定
└── julia-lambda-function
├── build.sh
├── .gitignore
├── src
│ ├── Makefile
│ ├── REQUIRE
│ └── hello.jl
└── template-export.yml #template.ymlにrenameします
gitにup
こちらのリポジトリを作りました。
https://github.com/ikegam1/advendLambda1223
各ファイルの中身については割愛しますが、リポジトリを直接見ていただければと思っています。
CodeBuild設定
CodeBuildではリポジトリにtagを切ってpushされた動作をトリガーにbuildを実行していきます。
なお、複数のfunctionに対応したいため、タグ名にfunction名を含んでもらうようにしています。
タグ名のルールはこんな感じです。
[function-name]-[stage].[version]
例えば、julia-lambda-function
の develop
で version: 1
であれば、
julia-lambda-function.develop.1
という感じになります。
Aws Console
ではコンソールでサクサクやっていきます。
まずはCodeBuildの画面を開きまして、ビルドプロジェクトを作成します。
プロジェクト名は任意。今回はadvendLambda1223
にしておきます。
ソースのところではGitHubを選択し、ソースのあるリポジトリを連携します。
WebHookの設定も必要で、タグが切られた時に走るようになります。
環境は以下。正直、適当です。
なお、buildspec.ymlのある場所はちゃんと指定しないといけないです。
iam Role設定
こちらで用いるiamロールですが、cloudformationとかバリバリ使うので強力な権限が必要です。samで使ってるiamと同じ程度の強さのやつ。
CloudFormationのFullとS3のFull、apiのAdmin・・とIam RoleのCreate権限があれば今回のFunctionは大丈夫かなと思います。
が、Iam Roleはtemplate.ymlに既存のやつを書いておいた方がいいですね。たぶん、samに自動で生成させてそれを使うと早いと思います。
Resource>HelloJuliaFunction>Properties>Role
です。
ほんとはもっと絞った方がいいと思うのですが、お試しということで試した後にすぐ消す前提なら大丈夫かなって思います。
deploy
試しにREADMEでも作ってpushしてみますね。
$ echo '# READ ME!' > README.md
$ git add README.md
$ git commit -m 'deploy test'
$ git push
$ git tag -a julia-lambda-function.develop.1 -m 'deploy start'
$ git push -u origin julia-lambda-function.develop.1
Buildが走りました!
そして成功!
実行もOKでした!
URLの末尾に?msg=hogehoge
みたいにするとechoしてくれます。
lambda_functions/julia-lambda-function/build.sh
ちょっと工夫してるのはこのシェルです。
ローカルではsam
コマンドでLambda Functionをdeployしていますが、CodeBuildではこれを使っていません。
serverless frameworkとかもあるけど、samの好きなところはここですね。CodeBuild上でいちいちCLIをインストールする必要がない。
#!/bin/bash
export BUCKET="sam-packages"
pwd
aws cloudformation package --force-upload --template-file template.yml --s3-bucket $BUCKET --output-template template-export.yml
aws cloudformation deploy --force-upload --template-file template-export.yml --s3-bucket $BUCKET \
--s3-prefix $FUNCTION --stack-name "${TAG}-${STAGE}" --capabilities "CAPABILITY_IAM" --region ap-northeast-1
aws cloudformation describe-stacks --stack-name "${TAG}-${STAGE}" --query 'Stacks[]'
exit 0
※だいぶ簡素にはしています。実際の運用ではこのshellからslack通知したりとかはしてます。
代わりにつかっているのはaws cloudformation package
とaws cloudformation deploy
。
samってcloudformationの拡張みたいなやつでして、cloudformationでも同じテンプレートが使えるのですよね。
ここではpackageでS3にuploadして、それをdeployでdeployしてる感じです。
最後に
advent-calendar、けっこうギリギリになってしまって余裕がなかったです。。
ECR周りも少し試してみた所便利そうでした。ただ、Layerとの絡みはsamの対応がまだ追いついてなさそう。すぐ追いつくとは思うけど。
あとは、Juliaが気になってたので試しに触れてみた。pythonに近い感じかと思ってたけど、コードはそんなに似てないかもしれない。