透過AWS EventBridge排程觸發Container類型的Lambda服務
Scenario
本篇透過Lambda Docker Node.js環境
建立一個micro service
該服務啟動後立即執行js透過Line Notify API發送測試訊息
專案架構
- app(folder)
- app.js: 核心Node.js程式碼
- package.json
- yarn.lock
- .dockerignore
- docker-compose.yml
- Dockerfile
Dockerfile設定
這邊Container內使用Node.js環境
可直接參考AWS Lambda Node.js Image說明頁面的Usge分頁
# env ref: https://docs.aws.amazon.com/zh_tw/lambda/latest/dg/configuration-envvars.html
FROM public.ecr.aws/lambda/nodejs:12
ENV LAMBDA_TASK_ROOT_PATH="${LAMBDA_TASK_ROOT}/"
WORKDIR $LAMBDA_TASK_ROOT_PATH
# 將專案的code複製到lambda task root
COPY ./app ${LAMBDA_TASK_ROOT_PATH}
RUN ls \
&& npm install --global yarn \
&& yarn install
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD ["app.handler"]
app/app.js
這邊可以看到透過env環境變數設定Line Notify Token
const axios = require('axios')
const qs = require('qs')
class App {
constructor() {
this.token = process.env.LINE_NOTIFY_TOKEN // line notify token透過env帶入
}
delay(secondo = 1) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, second*1000)
})
}
async lineNotify(message) {
const result = await axios({
url: 'https://notify-api.line.me/api/notify',
method: 'post',
headers: {
Authorization: `Bearer ${this.token}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
data: qs.stringify({
message,
}, { arrayFormat: 'indices' }),
})
}
async start() {
console.warn('start app')
await this.lineNotify('測試訊息')
console.warn('finished function')
return {
status: 200,
body: 'success!'
}
}
}
exports.handler = async function(event) {
console.warn('event', event)
if(event) {
console.warn('payload', event.payload)
}
const app = new App()
return await app.start()
}
測試服務
可參考此篇文件
build image
docker build -t lambda-nodejs . --no-cache
run service
透過env帶入LINE_NOTIFY_TOKEN
並將container內部的8000 port轉至外部的9000 port
docker run -p 9000:8080 --name lambda-nodejs --env LINE_NOTIFY_TOKEN={YOUR_LINE_NOTIFY_TOKEN} lambda-nodejs
測試
curl --request POST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
若測試成功
將會在Line到測試通知
並且可在service log看到接收到payload的資訊
透過docker-compose打包、測試
docker-compose.yml
透過env_file設定將所有env都放到同一個檔案
以下範例預設env檔案路徑為/default/docker-env/file/path
若有需要覆蓋此設定
可在執行docker-compose時透過設定dockerEnv參數來覆寫
version: "3.8"
services:
aws-lambda:
container_name: aws-lambda-nodejs
build:
context: .
env_file:
- '${dockerEnv-/default/docker-env/file/path}'
restart: unless-stopped
tty: true
ports:
- "9000:8080"
working_dir: /var/task
networks:
- lambda-test
networks:
lambda-test:
driver: bridge
docker env file
LINE_NOTIFY_TOKEN=foobar
Build、Run
# build
docker-compose build
# build with no cache
docker-compose build --no-cache
# run
docker-compose up
# run with custom docker env file path
dockerEnv=/custom/docker-env-file/path docker-compose
將Image push到AWS ECR
參考文件
接著在ECR建立一個新的Registry
ECR登入驗證
登入成功後將會看到成功訊息輸出
aws ecr get-login-password --region region | docker login --username AWS --password-stdin aws_account_id.dkr.ecr.region.amazonaws.com
tag image
docker tag [docker-tag] aws_account_id.dkr.ecr.region.amazonaws.com/[registry-name]:tag
push
docker push aws_account_id.dkr.ecr.region.amazonaws.com/[registry-name]:tag
設定Lambda、測試功能
Lambda設定
建立容器類型的lambda funcction
並設定container image URI
設定環境變數
依照本篇需求
設定LINE_NOTIFY_TOKEN環境變數
測試lambda函式
測試成功將跟本機測試一樣
會收到Line Notify通知
此外可查看執行細節
這邊可以看到response
以及跟本機測試一樣的service log輸出
透過EventBridge觸發Lambda服務
在Lambda建立新的觸發條件(Trigger)
排程的Expression可參考AWS文件
可使用Cron(排程)或Rate(頻率)
此範例使用排程
要注意的是Cron設定的時間為UTC
台灣時間為+8
因此UTC時間直接-8計算即可
下圖設定為每日台灣時間22點整(14 = 22-8)
在Event帶指定的參數
EventBrige可使用JSON常數來傳遞值給Container
多樣化的觸發條件
除了此範例的排程觸發
AWS Lambda還提供各種類型的觸發方式
像是S3、SQS、SNS、API Gateway等
可依照需求自行使用各種方式來觸發
目的地
AWS Lambda可設定在函式執行完成功/失敗後
進行後續不同的動作(目的地)
像是SQS、SNS、Eventbrige、其他Lambda Function等
透過Gitlab CI自動更新Lambda
設定IAM User
設定一個可操作AWS ECR、Lambda的IAM User
並產生一組Key
設定AWS Credentials
前往Gitlab專案的CI/CD Settings > Variables
加入一個AWS_SHARED_CREDENTIALS_FILE環境變數
將內容設定上述的IAM User Key
[default]
aws_access_key_id = aws_access_key_id
aws_secret_access_key = aws_secret_access_key
此變數為AWS CLI的憑證設定環境變數(可參考此文件)
並將此變數類型設定為File Type
.gitlab-ci.yml
以此方式設定.gitlab-ci.yml
當push含有"build-image"的git tag即可自動執行build image job
當push含有"deploy-lambda"的git tag即可自動執行Lambda更新Image流程
variables:
ECR_REGISTRY_URI: {AccountId}.dkr.ecr.ap-southeast-1.amazonaws.com/{RegistryName}
TARGET_IMAGE: $ECR_REGISTRY_URI:latest
LAMBDA_FUNCTION_NAME: {LambdaFunctionName}
# AWS CLI需要使用的環境變數
AWS_DEFAULT_REGION: ap-southeast-1
# 設定docker in docker service連接的port
DOCKER_HOST: tcp://docker:2375
image:
name: amazon/aws-cli
entrypoint: [""]
build:
stage: build
services:
- docker:dind
only:
variables:
- $CI_COMMIT_MESSAGE =~ /build-image/
- $CI_COMMIT_TAG =~ /build-image/
before_script:
- amazon-linux-extras install docker
- aws --version
- docker --version
script:
# 取得臨時登入憑證
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY_URI
# build image
- docker build -t $TARGET_IMAGE .
# push image to ECR
- docker push $TARGET_IMAGE
# 將aws cli找到的untagged image存至env $IMAGES_TO_DELETE
- IMAGES_TO_DELETE=$(aws ecr list-images --repository-name $ECR_REGISTRY_NAME --filter "tagStatus=UNTAGGED" --query 'imageIds[*]' --output json)
# 移除舊的所有untagged image
- aws ecr batch-delete-image --repository-name $ECR_REGISTRY_NAME --image-ids "$IMAGES_TO_DELETE" || true
deploy:
stage: deploy
services:
- docker:dind
only:
variables:
- $CI_COMMIT_MESSAGE =~ /deploy-lambda/
- $CI_COMMIT_TAG =~ /deploy-lambda/
script:
# 更新lambda image
- aws lambda update-function-code --function-name $LAMBDA_FUNCTION_NAME --image-uri $TARGET_IMAGE
實際執行
build image
更新Lambda Function Image