Serverless Frameworkで運用しているAWSアプリケーションのデプロイをGithub Actionで管理する方法

## 概要


Serverless Frameworkを使用する時、ローカルからデプロイするだけで特にCIを回していなかったのでGithub Actionで開発フローを組もうと思い調べてみたので設定手順をまとめておきたいと思います。

この記事でセットアップする開発フローは、Github Pushトリガーによって、Serverless Serviceをデプロイするアクションを起動する非常にシンプルなものです。

## 目次

- 構成
- 運用フロー
- 準備
- 設定
  - Serverless Framework
  - Github Action
- 動作確認
- 参考URL

## 構成

![sls-aws-blog](//images.ctfassets.net/bphjrdksp04w/1jOSyeoRKBAj1UXw6I8hdd/cf0db8f8fc24ea245cc2b0555de90363/sls-aws-blog.png)
Lambdaのトリガーとして設定するAPI Gatewayには、開発用（dev）・本番用（prod）の2つのステージを用意し、Functionを起動した際はそれぞれのステージのLambdaが実行されるようにします。

__開発用Lambda（dev）__
Latestバージョンを常に参照します。LatestバージョンはAWSによって、デプロイ毎にデフォルトで割り当てられる最新のバージョンの事です。

__本番用Lambda（prod）__
指定したバージョンを切り替える事で、本番用にバージョニング設定する事が出来ます。

## 運用フロー

GithubのマスターブランチへのPushをトリガーに、Serverless Serviceをデプロイするアクションが実行されるようにします。

__トリガー__

__1.開発環境（開発用Lambda）へのデプロイ__
GithubのマスターブランチへのPushをトリガーとします。

__2.本番環境（本番用Lambda）へのデプロイ__
Githubのマスターブランチへタグ付きのPushをトリガーとします。

## 準備

- __Serverless Frameworkのインストール__

```
npm install -g serverless
```
- __プロバイダーセットアップ__

今回はAWSアプリケーションを作成するので、Serverless FrameworkのプロバイダーとしてAWSを設定します。Serverless Frameworkに、AWSの機能権限を与えたIAMユーザーを割り当てるために下記を実行する必要があります。

__1.AWSでServerless用のIAMユーザを発行。__

__2.IAMユーザにAdministratorAccessの管理ポリシーを与える。__

## 設定

## *__Serverless Framwork__*

__1.サービス（Serverless Frameworkにおける実行環境の単位）の作成__

    serverless create --template aws-nodejs --name greeting-service --path greeting-service

__2.環境変数の設定（env.yml）__

```yml
prod:
  appName: "Serverless App"
default:
  appName: "DEV Serverless APP"
```

__3.サービス全体の設定（serviceless.yml）__

今回はLambdaとAPI Gatewayで構成するアプリケーションです。serverless.ymlに諸々のAWSに関する設定を記述していきます。

- __サービス内のLambdaファンクション群__

Getリクエストで、挨拶を返すHiファンクションの定義。

- __Lambdaファンクションごとのトリガーとなるイベントの定義__

Functionのトリガーとして、Getリクエストを定義。

- __環境変数の定義__

serverless.yml内で使用する変数を定義。
Function内で使用する変数を定義。

- __providerの設定__

AWSの地域設定。

変数の中身は、GithubのPushトリガーで設定出来るように定義します。この変数がServerlessのデプロイ先をルーティングする役割を担当します。

```yml
service: greeting-service
frameworkVersion: '2'

//自身で定義した変数 => serviceless.ymlで参照
custom:
  defaultStage: dev
  // 開発環境へのデプロイ　→　prod.appName変数が参照される。
  // 本番環境へのデプロイ　→　default.appName変数が参照される。
  environment: ${file(env.yml):${self:provider.stage}, file(env.yml):default}

provider:
  name: aws
  runtime: nodejs12.x
  lambdaHashingVersion: 20201221
  stage: ${opt:stage, self:custom.defaultStage}
  // lambdaで使用する変数
  environment:
    appName: ${self:custom.environment.appName}
  // 地域の指定
  region: ap-northeast-1

functions:
  greeting:
    handler: handler.greeting
    events:
      - http:
          path: /
          method: get
 ```

__4.Lambda functionの設定__

```js
'use strict';

module.exports.greeting = async (event) => {
  console.log(process.env.appName);
  console.log(event.requestContext.stage);

  let html = `
    <p>Hi!</p>
  `;

  return {
    statusCode: 200,
    headers: {
      'Content-Type':'text/html'
    },
    body: html
  };
};
```

## *Github Action*

__1.AWSのシークレットキーと環境変数をGithubで設定する。__
![スクリーンショット 2021-09-05 19.59.01](//images.ctfassets.net/bphjrdksp04w/3YLZ5pcSgTEVZDkvYsg5P/b37661c674510c5a79efc67e9d70b39d/____________________________2021-09-05_19.59.01.jpg)

__2.workflowの設定__

- __dev workflow__

マスターPushをトリガーにしたワークフローを記述します。このワークフローが走ることで、1.開発環境（開発用Lambda）へのデプロイが実行されます。

```yml
name: Deploy Dev Lambda Function by Serverless Framework
on:
  push:
    branches:
      - master
jobs:
  deploy-dev:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - name: Install Serverless Framework
      run: npm install -g serverless
    - name: Serverless AWS authentication
      run: sls config credentials --provider aws --key ${{ secrets.AWS_KEY }} --secret ${{ secrets.AWS_SECRET }}
    - name: Create env file
      run: |
        cat > env.yml << EOF
        ${{ secrets.ENV }}
        EOF
    - name: Install NPM dependencies
      run: npm install
    - name: Deploy Lambda functions
      // ステージを開発環境に指定し、デプロイ
      run: sls deploy -s dev
```

- __prod workflow__

タグ付きのマスターPushをトリガーにしたワークフローを記述。このワークフローが走ることで、2.本番環境（本番用Lambda）へのデプロイが実行されます。

```yml
name: Deploy Production Lambda Function by Serverless Framework
on:
  push:
    tags: # Deploy tag (e.g. v1.0) to production
      - 'v**'
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - name: Install Serverless Framework
      run: npm install -g serverless
    - name: Serverless AWS authentication
      run: sls config credentials --provider aws --key ${{ secrets.AWS_KEY }} --secret ${{ secrets.AWS_SECRET }}
    - name: Create env file
      run: |
        cat > env.yml << EOF
        ${{ secrets.ENV }}
        EOF
    - name: Install NPM dependencies
      run: npm install
     // ステージを本番環境に指定し、デプロイ
    - name: Deploy Lambda functions
      run: sls deploy -s prod
```

## 動作確認

設定したGithub Actionで、Serverless Applicationのデプロイが実行されるか確認したいと思います。まずは開発環境にデプロイします。

    git push origin master

Actions画面で確認してみると、開発ステージにデプロイされた事が確認出来ます。

![スクリーンショット 2021-09-05 20.52.09](//images.ctfassets.net/bphjrdksp04w/1eg0cwu9hFOyQJ8lTyJXKV/06708a3b93aa81e7f71c0ea7ef5a1f00/____________________________2021-09-05_20.52.09.jpg)

![スクリーンショット 2021-09-05 20.54.48](//images.ctfassets.net/bphjrdksp04w/22zJYJGqcY8l7Kc1PPQOCv/7fbf831b0f2ea2cb72f75104a9669ef1/____________________________2021-09-05_20.54.48.jpg)

curlコマンドでも確認してみます。
開発環境（https://＊＊＊.execute-api.＊＊＊.amazonaws.com/dev）

    curl https://＊＊＊.execute-api.＊＊＊.amazonaws.com/dev/
    <p>Hi!</p>

開発環境にアプリケーションがデプロイされている事が確認出来ました。

続いて、タグを切って本番環境にデプロイしてみます。差分を確認するために、Function関数を変更します。

```js
'use strict';
module.exports.greeting = async (event) => {
  console.log(process.env.appName);
  console.log(event.requestContext.stage);

  let html = `
    <p>Chao!</p>
  `;

  return {
    statusCode: 200,
    headers: {
      'Content-Type':'text/html'
    },
    body: html
  };
};
```

    git push origin　Tag<version>

![スクリーンショット 2021-09-05 21.17.16](//images.ctfassets.net/bphjrdksp04w/1AGNxZmvKKyrL2SH838NDY/4783fd82e2c5bcb76d45b8237b99b141/____________________________2021-09-05_21.17.16.jpg)

![スクリーンショット 2021-09-05 21.17.39](//images.ctfassets.net/bphjrdksp04w/1PHDItp7ONtZfNr2IZh5vn/358ffc4d746b7f985bcd30b1e6bd82ed/____________________________2021-09-05_21.17.39.jpg)

こちらもcurlコマンドでも確認してみます。
本番環境（https://＊＊＊.execute-api.＊＊＊.amazonaws.com/prod）

    curl https://＊＊＊.execute-api.＊＊＊.amazonaws.com/prod/
    <p>Chao!</p>

本番環境にアプリケーションがデプロイされている事が確認出来ました。

## まとめ

Serverless Frameworkを使えば、AWSアプリケーションの構築管理が便利に出来ますが環境の切り替えをActionに任せる事でよりサービスの運用が楽になる事が分かりました。

今回の運用フローでは、AWS Lambda関数を、開発用と本番用の2つに分けていますが、エイリアスでバージョニングを行えば1つの関数で環境をスイッチング出来るみたいなのでそちらの方法も試してみたいと思います。

## 参考URL

[Serverless Frameworkの使い方まとめ](https://serverless.co.jp/blog/25/ "Serverless Frameworkの使い方まとめ")
[How to deploy Serverless applications using Github Actions](https://dev.to/maxkostinevich/how-to-deploy-serverless-applications-using-github-actions-34a "How to deploy Serverless applications using Github Actions")
[APIGateway+Lambdaによるアプリサーバをバージョン管理する](https://tm-progapp.hatenablog.com/entry/2021/01/19/202623 "APIGateway+Lambdaによるアプリサーバをバージョン管理する")