Github Actionsにて自動的にデプロイする環境作成(Webサイト編)

## 概要

[Github Actions](https://github.com/features/actions)が使用できるようになったのでGithub上でのCI/CDの検証を行います。
開発において毎回デプロイ環境を構築したり、デプロイ手順書の作成、把握、手動での実施、といった一連のフローを行っていくのは非常に手間がかかります。
今回Github ActionsのCI/CDの検証を行い、定常的な方法を確立し、他の開発プロジェクトにも適用できるようなテンプレートを作成しました。
そのような環境の構築にあたっての備忘録としてここに記述しておきます。
今回、作成したテンプレートのプロジェクトは以下になります。
[github-actions-examples](https://github.com/TakuKobayashi/github-actions-examples)

今回はWebサイトの環境における自動デプロイの手法になります。
この他の環境については

 * [Github Actionsにて自動的にビルドする環境作成(Android編)](https://qiita.com/taptappun/items/92ec63b7e89e53b68efa)
 * [Github Actionsにて自動的にビルドする環境作成(Unity編)](https://qiita.com/taptappun/items/7c309b67e000a19c4666)

を参照してください。

また、現在
 * iOS

での自動ビルド&デプロイの環境構築を進めており、こちらは確立され次第、後日紹介します。

## 使用構成

運用費用が月額0円になるようなサービスを中心に利用していきます。
使用する言語やツール、フレームワークについては以下を使用します。

 * Frontend(フレームワーク/ツール/言語)
   * React
   * Typescript
   * Node.js
 * Backend(フレームワーク/ツール/言語)
   * [Serverless Framework](https://serverless.com/)
   * Typescript
   * Node.js

今回紹介するサーバー環境

 * Frontend
   * [Github Pages](https://pages.github.com/)
   * [Firebase Hosting](https://firebase.google.com/docs/hosting)
 * Backend
   * AWS([Lambda](https://aws.amazon.com/jp/lambda/) + [API Gateway](https://aws.amazon.com/jp/api-gateway/))
   * [Firebase Cloud Functions](https://firebase.google.com/docs/functions/)

## やりたいこと

 * [React Web](https://ja.reactjs.org/): ReactでWeb frontendの開発を行い、masterにpushされたら自動的にbuildする。Buildが完了したら [Github Pages](https://pages.github.com/) または [Firebase Hosting](https://firebase.google.com/docs/hosting) にDeployして内容が反映されること
 * [Serverless Framework](https://serverless.com/), [Firebase Cloud Functions](https://firebase.google.com/docs/functions/): [Serverless Framework](https://serverless.com/), [Firebase Cloud Functions](https://firebase.google.com/docs/functions/) にてBackendの開発を行い、Web APIを作成。masterにpushされたら自動的にdeployコマンドを実行し、それぞれ[Lambda](https://aws.amazon.com/jp/lambda/) + [API Gateway](https://aws.amazon.com/jp/api-gateway/), [Firebase Cloud Functions](https://firebase.google.com/docs/functions/) の環境にdeployして反映されること

## Github Actionsについて

### Github Actionsとは

通称 [CI(継続的インテグレーション: continuous integration)](https://ja.wikipedia.org/wiki/%E7%B6%99%E7%B6%9A%E7%9A%84%E3%82%A4%E3%83%B3%E3%83%86%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3) / [CD(継続的デリバリー: Continuous Delivery)](https://cloudbees.techmatrix.jp/devops/cd/) が実行できるGithubの機能です。
詳しくは[こちら](https://help.github.com/ja/articles/about-github-actions)を参照してください。

CI/CD実行環境: つまり開発していく中で繰り返し行われる作業を自動的に実行してくれる環境になります。基本的にCLIで実行可能な状態にすることでCI/CDに組み込むことができます。

### 競合サービス

 * [Gitlab CI](https://docs.gitlab.com/ee/ci/)
 * [Circle CI](https://circleci.com/ja/)

他

また、この他、ツールとして

 * [Jenkins](https://ja.wikipedia.org/wiki/Jenkins)

などがあります

### Github Actionsを使うメリット

 * Githubの中だけで完結できる
 * 設定が簡単(ymlファイルに記述していくだけ)
 * MacOS/WindowsOSが利用できる(Gitlab CIではLocalへのインストールが必要)
 * 上記OSの利用も含めて無料(Publicリポジトリは完全無料、privateリポジトリは2000時間/月まで利用無料 → 詳しくは[こちら](https://github.com/features/actions))
  * ※ 以下に記載しますがpublicリポジトリであっても、鍵の情報などは公開されることはありません
 * 有志が作成したGithub Actions用のライブラリ(Plugin)があり、それを組み込みことができる


### Github Actionsのはじめ方

GithubにてGithub Actionsを有効にして、 `.github/workflows/フォーマットしたymlファイル` を設置する事でGithub Actionsが実行可能になります。(現在まだオープンβ盤なの利用するには申請が必要になります。[こちら](https://github.com/features/actions)から申し込みを行ってください。)

<img width="1440" alt="GithubActions.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/bcc507cb-60d8-c7c7-c075-a27852cf5048.png">

なお、今回作成したymlファイルの設定内容の詳細についてはそれぞれ[こちら](https://github.com/TakuKobayashi/github-actions-examples/tree/master/.github/workflows)を参照してください。

### ymlファイルの書き方

構文の内容が多いのでまずは `Github Actions` を有効にした時に `Template` の使用を勧められるのでそちらを選択してください。また以下に詳細が載っていますのでこちらを参照してください。

[GitHub Actionsのワークフロー構文](https://help.github.com/ja/articles/workflow-syntax-for-github-actions)

簡単に設定例と解説として、AWS LambdaへのServerless Deployの設定ファイルについて以下に示します。(詳しくは[こちら](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/serverless-deploy.yml))

```yml
on:
  push:
    branches:
    - serverless-deploy
name: Serverless Deploy
jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: setup ubuntu
        run: sudo apt-get --quiet update --yes
      - name: yarn install |
        working-directory: ./serverless
        run: yarn install
      - name: serverless package and deploy
        working-directory: ./serverless
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run:
          yarn add serverless
          ./node_modules/serverless/bin/serverless package --package ./build
          ./node_modules/serverless/bin/serverless config credentials --provider aws --key ${AWS_ACCESS_KEY_ID} --secret ${AWS_SECRET_ACCESS_KEY}
          ./node_modules/serverless/bin/serverless deploy --package ./build
      - uses: actions/upload-artifact@master
        with:
          name: build
          path: serverless/build
```

ここでは、`serverless-deploy` というブランチにpushされたら、 `ubuntu-latest` のdockerイメージをもちいて、 `steps:` 以降の処理を順次実行していってます。
`steps:` の中の `uses:` で使用するライブラリを指定、`run:` で実行するコマンドを記述していきます。また、連続してコマンドを実行したい場合は `run: |` で複数行にまたがって記述することで連続してコマンドが実行できます。
`working-directory:` では`run:` コマンドを実行するときの ルートディレクトリを指定することができます。

## その他、雑多なこと

### Workflow

以下のワークフローを元にデプロイしていきます。

* Reactをデプロイするまでのワークフローは以下の通り

![React Web Workflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/da9111ad-e535-8e6b-8f41-c21db19c9c98.png)

```plantuml
@startuml
title React Web Workflow

(*) --> [yarn install] "インストール"
"インストール" --> [yarn run build] "Build"
"Build" --> "Deploy(HTML)"
"Deploy(HTML)" --> (*)

@enduml
```

* ServerlessをDeployするまでのワークフローは以下の通り

![Serverless Workflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/d1475662-9bb0-d2f8-a2cb-374757767d4c.png)

```plantuml
@startuml
title Serverless Workflow

(*) --> [serverless config credentials] "設定"
"設定" --> [yarn install] "インストール"
"インストール" --> [serverless package] "Build"
"Build" -->[serverless deploy] "Deploy"
"Deploy" --> (*)

@enduml
```

上記Workflowの内容をそれぞれ、順次実行していくため、Github Actionsのymlファイルに適用していきます。

### Github Actionsにて非公開にしたい変数を登録して使用する

API Keyや環境変数などオープンにしたくない情報は `Secrets` に変数を登録することで、公開されることなく利用することができます。
以下のようにリポジトリから `Settings` → `Secrets` と選択し、`Add a new Secrets` と選択することで設定することができます。

<img width="1440" alt="GithubSettingsSecrets.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/e77b4aff-8a9c-915a-9a31-9c1b992e5173.png">

<font color="#FF0000">※ 一度設定したSecretsは内容を確認する事はできません。またGithub Actionsの中で出力しようとしても値は表示されませんので、設定には気をつけてください。</font>

設定した, `Secrets` は以下のように `env:` の項目に設定した `Key名` を代入する事でコマンドの中などで使用されます。

```yml
- name: serverless package and deploy
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  run: |
    ./node_modules/serverless/bin/serverless config credentials --provider aws --key ${AWS_ACCESS_KEY_ID} --secret ${AWS_SECRET_ACCESS_KEY}
```

上記の内容はServerless FrameworkにてAWS環境にDeployするためにAWSへのAPIアクセスキーを設定している内容になります。
具体的な実装例は
[こちら](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/serverless-deploy.yml) になります。


### Github PagesへのDeploy方法

 * `Settings` よりGithub Pagesを有効にする

<img width="1440" alt="GithubSettings.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/9844d288-ea5f-e46b-db05-a1f7cf1e46b4.png">

<img width="1440" alt="GithubPagesSettings.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/8bf93f0b-415e-5e6b-eb66-398c3824c2b4.png">

 * 公開鍵と秘密鍵のペアを生成する(以下のコマンドを実行する)

```
ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""
```

* 作成した公開鍵(gh-pages.pub)の中身を `Deploy Keys` に登録する

<img width="1440" alt="GithubSettingsDeployKey.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/78b10a10-9ff9-9ef0-35fd-f78861e1b484.png">

* 登録する時、以下のように「Allow write access」にチェックを入れる

<img width="1219" alt="GithubActionsSetDeployKey.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/7ce694b9-593e-0a32-7811-35b44c4798e4.png">


* 作成した秘密鍵(gh-pages)の中身を `Secrets` に登録する

<img width="1440" alt="GithubSettingsSecrets.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/e77b4aff-8a9c-915a-9a31-9c1b992e5173.png">

* `Workflowのyml`の中の`steps`の項目に以下の内容を適用する

```yml
    - name: deploy
      uses: peaceiris/actions-gh-pages@v2.5.0
      env:
        ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
        PUBLISH_BRANCH: gh-pages
        PUBLISH_DIR: frontend/build
```

これで設定完了なので、特定のブランチにpushしたら、自動的に Github Pagesにデプロイしてくれるようになります。

#### 設定したymlについての解説

詳細については[こちら](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/react-web-ci-cd.yml)を参照してください。

 * Github Pagesへのデプロイは[actions-gh-pages](https://github.com/peaceiris/actions-gh-pages)というライブラリを使用しています。このライブラリを使用しています。ライブラリを使用する場合は `step` の項目のところで、`uses:` の項目を指定すると使用できます。この場合、`run:` 項目や `working-directory:`項目を使用することができません。
 * `secrets.ACTIONS_DEPLOY_KEY`は上記で登録した`Secret` の変数名の値が適用されます。Github Actionsの中で使用したい場合は `env:` 項目内で変数に指定することで使用することができます。(ライブラリの場合は指定があります)
 * `PUBLISH_BRANCH`の項目で`Github Pages`にデプロイしたいブランチを指定することができます。このとき `gh-pages` を指定していますが存在しなければ、Workflowを実行している中で勝手に作成してくれます。しかし、最初に作成する時はうまくdeployできないこともあるので、あらかじめ作っておくといいかもしれません。また `gh-pages` ブランチは Github Pagesの公開用のブランチとして指定することもできます。(うまくいかないかもしれないので、指定しておいたほうがいいと思います)

### serverless framework

serverless frameworkのコマンドについて詳細は[こちら](https://serverless.com/framework/docs/)を参照してください。
詳細は[こちら](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/serverless-deploy.yml)
serverlessコマンドを使用するためにはserverlessをインストールしてserverlessコマンドを実行できるようにする必要があります。そのため、ワークフローの途中にて`yarn install serverless`を実行してインストールしています。環境変数の設定の関係により `./node_modules/serverless/bin/serverless` でpathを直接指定しています。

### artifactの設定 ~ビルドした成果物をダウンロードできるようにする~

 Buildした成果物をダウンロードできるようにするためには [upload-artifact](https://github.com/actions/upload-artifact) というライブラリを使用して成果物をアップロードしています。
以下のように設定することで成果物をアップロードすることができます。

```yml
  - uses: actions/upload-artifact@master
    with:
      name: build
      path: serverless/build
```

このとき、`name:` でダウンロードするファイル名を指定し、`path:` で指定したパス以下のディレクトリをアップロードします。
アップロードしたArtifactは以下のようにActionsの中の `Artifacts` からダウンロードすることができます。

<img width="1680" alt="GithubActionsArtifactDownload.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/2783c7bf-c0e2-469c-3730-bafb128c48df.png">

### 参考
 * [actions-gh-pages](https://github.com/peaceiris/actions-gh-pages)
 * [GitHub Actions による GitHub Pages への自動デプロイ](https://qiita.com/peaceiris/items/d401f2e5724fdcb0759d)
 * [GitHub Actionsの仮想環境](https://help.github.com/ja/articles/virtual-environments-for-github-actions)