GitLab Runnerで静的ウェブページをS3に自動デプロイ

GitLabには専用のCI/CD機能があります。
今回はそれを用いてGitLabで管理しているReactのbuild、deployを自動化しています。

# GitLab CI/CDについて
実際には別途のGitLab Runnerというアプリケーションを通して行われます。
デフォルトではGitLabがもつサーバーを利用することができますが、[Freeプランではグループあたり1ヶ月で2000分の使用時間制限](https://about.gitlab.com/pricing/#gitlab-com)があります。
自前のサーバーにGitLab Runnerをインストールして使うこともできます。

# サーバーの環境構築
*GitLabが提供する環境を用いる方は飛ばしてください。*

ここではAWSのEC2のlinux環境にGitLab Runnerをインストールします。
環境によりコマンドの違いは適宜読み替えてください。

## GitLab Runnerのインストール
公式ドキュメントは[こちら](https://docs.gitlab.com/runner/install/)です。

- 使用するlinuxに合わせて以下のコマンドでバイナリファイルをダウンロードします。

```
# Linux x86-64
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

# Linux x86
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386

# Linux arm
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm

# Linux arm64
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64
```

- 実行権限を付与します。

```
sudo chmod +x /usr/local/bin/gitlab-runner
```

- GitLab Runner用のユーザーを追加します。

```
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
```

- インストールを行います。

```
sudo /usr/local/bin/gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
```

- 自動起動の設定を行います。

```
sudo systemctl enable gitlab-runner
```

- 起動します。

```
sudo systemctl start gitlab-runner
```

## GitLab Runnerのセットアップ
公式ドキュメントは[こちら](https://docs.gitlab.com/runner/register/)です。

- 以下のコマンドを実行します。

```
sudo /usr/local/bin/gitlab-runner register
```

- URLには`https://gitlab.com`と入力します。
 
```
> Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com
```

- GitLab UIを開き、リポジトリの左サイドバーの歯車アイコンからCI/CDを選択し、Runnerを展開します。"Set up a specific Runner manually"枠内にあるトークンを以下の項目に入力します。

```
> Please enter the gitlab-ci token for this runner
your_token
```

- 登録するrunnerの名前を入力します。

```
> Please enter the gitlab-ci description for this runner
[hostname] for_something
```

- タグを入力します。なくても構いません。

```
> Please enter the gitlab-ci tags for this runner (comma separated):
my-tag,another-tag
```

- 実行環境を選択します。ローカル環境が汚れないdockerを推奨します。後述する設定ファイルもdockerを前提としたものとなっています。

```
> Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
docker
```

- デフォルトのdockerのイメージを選択します。後述の設定ファイルで個別に選択することも可能です。

```
> Please enter the Docker image (eg. ruby:2.1):
node:alpine
```

## Dockerのインストール
以下のコマンドを実行します。

```
sudo yum install -y docker
sudo systemctl enable docker
sudo systemctl start docker
```

# AWSのIAMユーザーの作成
AWS CLIを用いてS3へのアップロードを行います。`AmazonS3FullAccess`権限を持つIAMユーザーを作成し、`Access key ID`と`Secret access key`をメモしておきましょう。

# S3バケットの作成
ウェブページを公開するためのS3バケットを用意します。詳しいやり方については[こちら](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/HostingWebsiteOnS3Setup.html)などを参考にしてください。

# GitLab UIで環境変数の設定
GitLab UIを開き、リポジトリの左サイドバーの歯車アイコンからCI/CDを選択し、Variablesを展開します。

- ここに登録された値はCI/CD実行時に`$key_name`で取得することができます。
- 今回は先程の`Access key ID`と`Secret access key`を`AWS_ACCESS_KEY_ID`と`AWS_SECRET_ACCESS_KEY`というキーで登録しておきます。

# CI/CDパイプラインの定義
リポジトリのルートディレクトリに`.gitlab-ci.yml`を作成し、この中にパイプラインを定義していきます。
公式ドキュメントは[こちら](https://docs.gitlab.com/ce/ci/yaml/README.html)です。

今回作成したものは以下のものとなります。
buildとdeployのみを行っています。必要に応じてtestなどは追加してください。

```yml:.gitlab-ci.yml
stages:
  - build
  - deploy

build:
  image: node:alpine
  stage: build
  cache:
    paths:
      - build/
  script:
    - npm install
    - npm run build --prod

deploy:
  image: python:alpine
  stage: deploy
  cache:
    paths:
      - build/
    policy: pull
  script:
    - apk add python3-dev
    - pip3 install awscli
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws s3 sync build/ s3://your_backet_name # 使用するbacket_nameに変更
```

## 注意点
- Lintのエラーを無視してbuildを行いたい方は、buildコマンドを`CI=false npm run build --prod`に変更してください。
- 使用するdockerイメージを、buildとdeployで分けています。buildではnpmを使いたいためnode、deployではawscliを使いやすいpythonの環境としています。
- コンテナはパイプラインごとに作成されます。buildされたファイルはルートディレクトリディレクトリ以下にbuildという名前で生成されますが、buildのパイプラインが終わると消滅し、そのままではdeployに渡すことはできません。このような場合、cacheに指定することでパイプライン間のファイルの共有を行うことができます。

# 関連記事
- [GitLab CI/CDパイプライン設定リファレンス(日本語訳:GitLab CI/CD Pipeline Configuration Reference)](https://qiita.com/ynott/items/1ff698868ef85e50f5a1)