こんにちは、Syunです。
GitHub ActionsによるAWS Lambdaへの自動デプロイ方法をまとめます。
AWSマネージメントコンソールにログインします。
AWS IAMでユーザー、ポリシー、ロールの三つを登録します。
適宜IAMユーザーを作成します。詳しくは省略します。
IAM → ユーザーでユーザーを選択し、認証情報タブ → アクセスキーの作成をクリックします。
ここでアクセスキーを作成します。
作成したアクセスキーはCSVファイルでダウンロードできるので、これを保存しておきます。
このCSVファイルにはアクセスキーIDとシークレットアクセスキーが書かれており、これを後ほど使います。
シークレットアクセスキーは再確認できないので、ここで忘れずに保存する必要があります。
ポリシーを作成します。
IAM → ポリシー → ポリシーの作成で、以下のJSONを貼り付けます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:*",
"iam:*",
"cloudformation:*",
"apigateway:*",
"logs:*",
"lambda:*"
],
"Resource": "*"
}
]
}
名前を設定し、新しいポリシーとして登録します。
※注意!
Create SLR With Star In Action And Resource:
ワイルドカード (*) をアクションおよびリソースで使用すると、
すべてのリソースで iam:CreateServiceLinkedRole アクセスが許可されるので、
意図しないサービスにリンクされたロールが作成されることがあります。
これを避けるために、代わりにリソース ARN を指定することをお勧めします。
と警告が表示されます。
ワイルドカード(*)は本来使うべきではありません。
ロールを作成します。
IAM → ロール → ロールの作成で、「信頼されたエンティティを選択」の画面が出るので、
エンティティタイプをウェブアイデンティティに、
プロバイダーをtoken.actions.githubusercontent.comに、
Audienceをsts.amazonaws.comに、
それぞれ選択して、次に進みます。
「許可を追加」の画面で、先ほど作成したポリシーを選択します。
選択後、「次へ」で確認画面に進みます。
確認画面ではロール名を設定し、信頼されたエンティティの設定を変更します。
信頼されたエンティティは、以下のようにします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"
},
"Action": [
"sts:AssumeRoleWithWebIdentity",
"sts:TagSession"
],
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:aud": "repo:{GITHUB_ORG_NAME}/{GITHUB_REPO_NAME}:*"
}
}
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::{ACCOUNT_ID}:user/{USER_NAME}"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::{ACCOUNT_ID}:user/{USER_NAME}"
},
"Action": "sts:TagSession"
}
]
}
{GITHUB_ORG_NAME}
はGitHubの組織名、
{GITHUB_REPO_NAME}
はGitHubの配下のレポジトリ名、
{ACCOUNT_ID}
はAWSのアカウントID(4桁-4桁-4桁の数字)、
{USER_NAME}
はAWSのユーザー名です。
適宜書き換えてください。
GitHub上でシークレットを登録します。
GitHubのレポジトリに入り、「上のメニューバーからSettings → 左のメニューからSecrets and variables → Actions」と進むと「Actions secrets and variables」という画面が出ます。
下のような画面です。
上の画面で、「New repository secret」を押し、シークレットを登録します。
ここではAWS_ACCESS_KEY_ID
、AWS_REGION
、AWS_ROLE_ARN
、AWS_SECRET_ACCESS_KEY
の四つのシークレットを登録します。
AWS_ACCESS_KEY_ID
は上で作成したIAMユーザーのアクセスキーIDです。
AWS_SECRET_ACCESS_KEY
は上で作成したIAMユーザーのシークレットアクセスキーです。
なお、アクセスキーIDとシークレットアクセスキーは同じCSVファイルに書かれています。
保存しておいたものを登録します。
AWS_REGION
はAWSの地域です。
東京ならば「ap-northeast-1」です。
これを登録します。
AWS_ROLE_ARN
はロールのARN(アマゾンリソースネーム)です。
これはIAMのロールのページで確認できます。
先ほど作成したロールのARNを登録します。
これにより、GitHubとAWSの接続ができるようになりました。
GitHubからAWSにデプロイするためのワークフローを記述します。
まずはaws-lambda-testというディレクトリを作ります。
ワークフローファイルは、aws-lambda-test/.github/workflows/の配下に以下のようなyamlファイルとして記述します。
aws-lambda-test/.github/workflows/deploy.yml
name: Deploy to AWS Lambda
on:
push:
branches:
- main
workflow_dispatch:
env:
NODE_VERSION: '14.x'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: npm install, build, and test
run: |
npm install
npm run build --if-present
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v3
with:
name: node-app
path: .
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
role-session-name: GitHubActions
- name: lambda update
run: |
cd lambda && npx serverless deploy
このデプロイファイルは「mainブランチにプッシュされた際に自動でワークフローが実行される」ということです。
それはこの部分で指定しています。
on:
push:
branches:
- main
なお、${{ secrets.AWS_ACCESS_KEY_ID }}
などの記述が出てきますが、ここで先ほどGitHub上に登録した各シークレットが使われます。
AWS LambdaにデプロイするLambda関数とAPIを、ローカル上で記述します。
aws-lambda-test/lambda/app.js
const express = require('express');
const app = express();
const router = require('./router');
app.use('/', router);
module.exports = app;
aws-lambda-test/lambda/lambda.js
const serverlessExpress = require('@vendia/serverless-express')
const app = require('./app')
const server = serverlessExpress.createServer(app)
exports.handler = (event, context) =>
serverlessExpress.proxy(server, event, context)
aws-lambda-test/lambda/router.js
const express = require('express');
const router = express.Router();
const usersList = ["Alice", "Bob", "Carol", "Dave", "Emily"];
const petsList = ["Cat", "Dog", "Rabbit", "Parakeet", "Turtle"];
router.get("/", (_, res) => {
res.status(200).send({
message: "Hello from Lambda!!!"
})
})
router.get("/users", (_, res) => {
res.status(200).send({
message: String(usersList)
})
})
router.get("/users/:id", (req, res) => {
const user_id = req.params.id
const user_name = String(usersList[Number(user_id) - 1])
res.status(200).send({
id: user_id,
name: user_name
})
})
router.get("/pets", (_, res) => {
res.status(200).send({
message: String(petsList)
})
})
router.get("/pets/:id", (req, res) => {
const pet_id = req.params.id
const pet_name = String(petsList[Number(pet_id) - 1])
res.status(200).send({
id: pet_id,
name: pet_name
})
})
module.exports = router;
上のaws-lambda-test
レポジトリでは、lambdaディレクトリを作って、その中に記述しています。
なお、ここではnodejsのExpressというフレームワークを使用するために、Serverlessというフレームワークを使います。
aws-lambda-test/lambda/serverless.yml
service: lambda
provider:
name: aws
runtime: nodejs18.x
region: ap-northeast-1
package:
include:
- serverless.yml
- app.js
- lambda.js
- local-app.js
- router.js
exclude:
- '**'
functions:
func:
handler: lambda.handler
events:
- http:
path: /
method: get
- http:
path: /users
method: get
- http:
path: /users/{id}
method: get
- http:
path: /pets
method: get
- http:
path: /pets/{id}
method: get
serverless.yml
ファイルを記述し、これをGitHub Actionsからnpx serverless deploy
コマンドでデプロイするという流れです。
GitHub ActionsからAWS Lambdaへのデプロイを実行します。
プルリクエストを作ってmainブランチにマージするなどして、mainブランチにpushが入ると、デプロイが自動で実行されます。
※なお、ここでは15分~20分ほど時間がかかりました。
デプロイが成功すると、GitHub Actions上で緑色のチェックマークが表示され、失敗すると赤色のバツマークが表示されます。
デプロイ完了後は、AWS Lambda上にLambda関数が表示されます。
すでにあるLambda関数を更新して再デプロイした場合は、Lambda関数の新しいバージョンが生成されます。
Lambda関数の名前ですが、serverless.ymlファイルの
サービス名(service: lambda
よりlambda)、
デプロイされたステージ名(dev)、
関数名(functions: func:
よりfunc)、
の三つが連結された名前(lambda-dev-func)になるようです。
また、同時にLambda関数のトリガーとなるAPIも生成され、そのAPI名は、
デプロイされたステージ名(dev)、
サービス名(service: lambda
よりlambda)、
の二つが連結された名前(dev-lambda)になるようです。
このままだとAPIを実行してもサーバーエラーになります。
Lambda関数内で用いる各種パッケージ(express
や@vendia/serverless-express
など)が使えないためです。
これらを使うためには、レイヤーというものを登録する必要があります。
aws-lambda-test/lambda/nodejs
という名のディレクトリを作成し(nodejsという名前は強制)、その中で
npm init
npm install express
npm install @vendia/serverless-express
を実行します。
これでnodejs配下にpackage.json
とpackage-lock.json
とnode_modules
ディレクトリが作成されます。
nodejsディレクトリをzip化し、AWS Lambdaの「レイヤーの作成」でこのzipファイルをアップロードします。
ここで作成したレイヤーを「レイヤーの追加」でLambda関数に追加すると、APIが実行できるようになります。
npx serverless deploy
コマンドをローカル上で実行すれば、ローカルからAWS Lambdaへデプロイもできます。
ただし、その際はローカル上に、GitHubに登録したようなAWS_ACCESS_KEY_ID
、AWS_REGION
、AWS_ROLE_ARN
、AWS_SECRET_ACCESS_KEY
などを登録する必要があります。
また、その際はIAMユーザーの許可ポリシーに、
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:*",
"iam:*",
"cloudformation:*",
"apigateway:*",
"logs:*",
"lambda:*"
],
"Resource": "*"
}
]
}
このJSONを登録する必要があります。
aws-lambda-test/lambda
ディレクトリに、以下のファイルを追加します。
aws-lambda-test/lambda/local-app.js
const app = require('./app')
app.listen(3000, () => {
console.log('listening');
})
ローカル上でサーバーを起動させ、localhost:3000
をたたいてAPIが正しく動いているか確認してください。
AWS Lambda 関数
AWS Lambda コード