GitHub ActionsでHugoのブログをVPSにデプロイする

この記事は [NewsPicks Advent Calendar 2019](https://qiita.com/advent-calendar/2019/newspicks) の3日目の記事です。

## はじめに

こんにちは、NewsPicksで iOS開発や、サーバーサイドの開発をしている[@kz_morita](https://twitter.com/kz_morita) です。

私はプライベートでここ１年くらい[個人ブログ](https://blog.foresta.me)を（奇跡的に）毎週継続して書き続けています。ブログ自体は、はてなブログのような一般的なサービスを使うのではなく [Hugo](https://gohugo.io/)で生成して、VPSにホスティングするような運用をしています。具体的にいうと、VPS 上に Jenkins が動いており、GitHub の push を hook してデプロイを行うといった感じです。

今回は上記の運用から、Github Actions を用いてブログのデプロイするように移行してみたら、とてもさくっとできて非常に良かったので、実際に設定した内容をご紹介します。

## GitHub Actions とは

GitHub Actions は GitHub が提供する CI / CD 環境です。
https://help.github.com/ja/actions/automating-your-workflow-with-github-actions/about-github-actions

料金としては、Public リポジトリであれば無料。
プライベートリポジトリであれば、Github の登録プランとデフォルトの使用制限を超えると超過分が請求されるといった形です。

使用制限は下記の表の通りです。
(引用元: https://help.github.com/ja/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions)

| 製品                    | ストレージ | Minutes (per month) |
| ----------------------- | ---------- | ------------------- |
| GitHub Free             | 500 MB     | 2,000               |
| GitHub Pro              | 1 GB       | 3,000               |
| GitHub Team             | 2 GB       | 10,000              |
| GitHub Enterprize Cloud | 50 GB      | 50,000              |

詳しくは以下の公式サイトから確認してみてください。

https://help.github.com/ja/actions

## Action を作成する

それでは早速 Actions を作成していきます。

Action は GitHub の該当の Repository の Actions メニューから追加します。

![github_actions_menu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26081/23cd1c9c-c7fd-e5be-49de-d6ea581b523b.png)

New workflow を押すと実際に作成することができます。


Workflowを作成しようとすると、テンプレートのようなものがいくつか表示され選ぶことができるようです。今回は `Set up a workflow yourself` を押して一からつくってみます。

![github_actions_setup_workflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26081/7d1dcf17-6c1e-6c6c-94fe-8a28873f71a3.png)

Workflowを作成すると、以下のような編集画面が表示されます。右側の Marketplace では、公開されている Actions を使用することもできます。

![github_actions_workflow_editor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26081/181b7d85-b770-0a56-4d3e-4f588b07a085.png)


実際の yaml の内容は後述しますが、ここで実際に Actions を作成して、 `Start commit` を押してコミットをするとリポジトリの下の、`.github/workflows/{name}.yml` というファイルがコミットされます。

## Workflow の実行ログ

Action が作成され実行されると、Actions タブから実行のログを確認することができます。

![github_actions_workflow_log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26081/ccd20484-d743-2ce1-6a6e-80ebc9c9999d.png)

## VPS にデプロイする Actions をかいてみる

それでは実際に、ブログを VPS にデプロイする Actions をかいてみます。
最初に完成品である自分の設定を載せます。

```yml
name: deploy to server

on:
  push:
    branches:
      - master

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Setup hugo
      run: |
          wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}-64bit.deb
          sudo dpkg -i hugo_${HUGO_VERSION}-64bit.deb
          hugo version
      env:
          HUGO_VERSION: '0.18.1'
    - name: Setup node
      uses: actions/setup-node@v1
      with:
        node-version: '10.x'
    - name: Start build
      run: echo Build started.
    - name: Npm install
      run: npm install
    - name: Webpack
      run: npm run webpack
    - name: Build hugo
      run: hugo
    - name: Generate ssh key
      run: echo "$SSH_PRIVATE_KEY" > key && chmod 600 key
      env:
        SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
    - name: Deploy
      run: rsync -rlptgoD --delete --exclude ".git/" -e "ssh -i ./key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p ${SSH_PORT}" public/ $SSH_USER@$SSH_HOST:$DEPLOY_PATH
      env:
        SSH_USER: ${{ secrets.SSH_USER }}
        SSH_PORT: ${{ secrets.SSH_PORT }}
        DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
        SSH_HOST: ${{ secrets.SSH_HOST }}
```

上記の設定ファイルについて１つずつ説明していきます。

#### Action Name

```yml

name: deploy to server
```

こちらは作成する Action の名前になります。上記の Action の実行結果のところなどで表示されるためわかりやすい名前をつけておくと良いでしょう。

#### トリガー

```yml

on:
  push:
    branches:
      - master
```

こちらでは、Action が実行されるためのトリガーの設定が行えます。上記の設定では、master ブランチが push された時に実行されるようにしています。

#### Jobs

```yml

jobs:
  build:
```

Action で実行される実際の動作は、`jobs:` の下にかいていきます。
上記の `build:` は識別名なので自分のわかりやすい名前ならなんでも大丈夫です。

#### Runs on

```yml

    runs-on: ubuntu-latest
```

上記は、ubuntu 上で CI を行うということを指定しています。他にも windows や mac も使えるので、必要に応じて設定しましょう。

#### Steps

```yml

    steps:
```

steps:の下に実際のビルドの flow をかいていきます。

今回の Actions では以下の 9 つの step を定義しました。順を追って説明していきます。

- Checkout
- Setup hugo
- Setup node
- Start build
- Npm install
- Webpack
- Build hugo
- Generate ssh key
- Deploy

#### Checkout

```yml

    - name: Checkout
      uses: actions/checkout@v1
```

まず最初に行われているのが上記です。こちらは 本リポジトリをチェックアウトしてくるためのものになります。
`name:` ではこのステップの名前をしていします。こちらの名前は実際に実行されている Action のステップ名として以下のように表示されます。

![github_actions_workflow_steps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26081/2048f8f2-3700-9378-a217-6693cad8f210.png)

`uses:` は他の人が作成した Actions を実行するためのものです。リポジトリをチェックアウトしてくるという共通の処理は公式で Actions が用意されていてこちらを利用することができます。

#### Hugo

```yml
    - name: Setup hugo
      run: |
          wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.deb
          sudo dpkg -i hugo_${HUGO_VERSION}_Linux-64bit.deb
          hugo version
      env:
          HUGO_VERSION: '0.59.1'
```

ここでは、Hugo のセットアップをしています。今回 CI を動かす OS は ubuntu なので、`.deb` ファイルを wget で取得してきてインストールをします。

#### Node

```yml
    - name: Setup node
      uses: actions/setup-node@v1
      with:
        node-version: '10.x'
```

ここでは、Node の環境構築をしています。公式で Node のセットアップ用の Actions が公開されていたのでそちらを利用しました。

#### Build

```yml

    - name: Start build
      run: echo Build started.
```

ここまででビルド環境の構築が完了しましたので、ここからは実際にページのビルドを行っていきます。\
ここでは、ビルド開始をログとして出力しています。

#### npm install

```yml

    - name: Npm install
      run: npm install
```

まずは、npm install を行います。

#### webpack

```yml

    - name: Webpack
      run: npm run webpack
```

そして、webpack で asset をバンドルします。

`npm run webpack` で production 用にバンドルするように `package.json` に npm script を記載してあります。

#### Build hugo

```yml

    - name: Build hugo
      run: hugo
```

先ほどインストールした Hugo でマークダウンから静的サイト（html）を生成します。生成された静的ファイルは `public/` ディレクトリ以下に配置されます。

#### Generate ssh key

```yml
    - name: Generate ssh key
      run: echo "$SSH_PRIVATE_KEY" > key && chmod 600 key
      env:
        SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
```

ここでは、VPS にデプロイするための鍵の準備をしています。秘密鍵の内容はリポジトリの環境変数として設定しています。
環境変数は Settings > Secrets から設定することができます。

今回は以下のような情報を環境変数として設定しました。こちらに設定されたデータはリポジトリが Fork されてもコピーされないため安全です。

![github_actions_secrets_environment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26081/7c13fd9c-2ba0-a236-bddd-11e8ddbe7964.png)


#### Rsync deploy

```yml

    - name: Deploy
      run: rsync -rlptgoD --delete --exclude ".git/" -e "ssh -i ./key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p ${SSH_PORT}" public/ $SSH_USER@$SSH_HOST:$DEPLOY_PATH
      env:
        SSH_USER: ${{ secrets.SSH_USER }}
        SSH_PORT: ${{ secrets.SSH_PORT }}
        DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
        SSH_HOST: ${{ secrets.SSH_HOST }}

```

最後に、rsync を用いてデプロイします。
SSH の接続情報や、Deploy 先のディレクトリパスなどは Github の環境変数に登録しています。
rsync を用いて さくら VPS にデプロイするのですが、ssh 接続がひつようなため、ひとつ前の手順で作成した秘密鍵 (key) を用います。

```bash
ssh -i ./key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p ${SSH_PORT}
```

`-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null` を追加して fingerprint の確認を行わないようにしないと接続ができないため注意が必要です。

参考: [cron やプログラムで ssh 越し rsync を利用するときにカナリ便利なオプション](https://www.l2tp.org/archives/2513)

rsync の文法については、[man page](https://linux.die.net/man/1/rsync) や、[こちら](https://qiita.com/bezeklik/items/22e791df7187958d76c1) などを参考にしてみてください。

## まとめ

今回は VPS へのデプロイを Jenkins から、Github Actions へ変更してみました。基本的に、yaml を編集していくだけで非常に直感的に定義することができるのでとてもわかりやすかったです。
実際 ssh の鍵周りなどで若干戸惑ったものの、キャッチアップから含めて 2 時間くらいでさくっと移行できたのは非常に良かったです。

また、他の人が実装した Actions も再利用可能なので、たくさんのActionsが開発されることによりどんどんエコシステムが発達していけばさらに便利になっていくのだろうなと感じました。
今回のように静的サイトをデプロイするだけのような用途であれば、非常にさくっと導入できるのでもしご興味がある方は試していただけると良いと思います。


4日目は、@junichiro さんで [Redshift の運用 Tips について](https://qiita.com/junichiro/items/6490b3c42311adfc9593) です。