schedule2020-12-24

GitHub Actionsで静的サイトジェネレーターをサーバにデプロイする

静的サイトジェネレーター(SSG)で作ったサイトをGitHub Actionsを使ってサーバに公開するところまでの手順をまとめます。 2~3年目の自分向けに詳細に書いてます。

nuxt-hosting ▲ 構成の全体図。masterを更新したらGitHub actionsでビルドしてさくらのVPSにデプロイします。GitHub actionsからサーバへはrsyncで接続しているため、サーバにNodeがなくてもOK。サーバではNginxとLet's Encriptを利用して配信してます。

以上の構成を建てるところまで順に紹介していきます。

Nuxtjsで作りましたが、NextGatsbyなどのSSG、SPAも同様にデプロイできます。

また、サーバもさくらのVPSでなくEC2や他のサーバも大丈夫です。

初のCIなので不備があるかもしれません。ご指摘頂けるとありがたいです。

Table of Contents

デプロイするアプリの概要

今回はNuxt/contentで作ったサイトをデプロイします。

デプロイで必要な実行するコマンド。

  1. yarn install: 必要なnode_modulesを追加する。
  2. yarn lint: Lintをチェック。
  3. yarn test: Jetでユニットテスト。
  4. yarn generate: 静的サイトを生成する。

yarnを利用している。./distディレクトリに生成されるので、これをサーバにアップロードする必要がある。また、GENERATED_BYという環境変数をトップページで使います。

サイトの作り方はnuxt/contentでブログを作るで紹介してます。

rsync出来るようにサーバを設定する

GitHub actionsで生成したサイトをサーバに設置するのにrsyncを使います (GitHub actionsのワークフローでmarketplaceのssh-deployを利用しており、その中でrsyncを利用している)。そのため、外部からrsync出来るようにサーバを設定します。SSHには公開鍵認証を利用します。

今回、さくらのVPSにCentOS7を建ててホスティングします。 5つの手順でrsync出来るところまでサーバをたてます。

  1. さくらのVPSでCentOS 7を建てる
  2. rsync用のユーザを追加
  3. 公開鍵の作成と登録をする
  4. sshd_configの設定
  5. SSHのポート番号の変更

サーバにデプロイする設定までです。 別の記事でNginxとLet's Encriptの設定をします。

1. さくらのVPSでCentOS 7を建てる

さくらのVPSの概要は公式の入門講座を参照してください。 ネコでもわかる!さくらのVPS講座 ~第二回「サーバーをさわってみよう!」で契約からSSH接続まで紹介されてます。

作成したユーザでSSHのパスワード接続が出来ていていればOK。 もしくはこれからの手順でユーザが追加できる。

2. rsync用のユーザを追加

rsync用のユーザを作成する。 ユーザグループとパスワードはsudo実行のために付けたが、設定が終われば外してよさそう。

## ユーザ追加
# useradd rsync_user
## ユーザグループに追加
# gpasswd -a rsync_user wheel
## パスワード設定
# passwd $user

visudoでrsyncするときroot権限で実行できるように設定する

# visudo

rsync_user ALL=(ALL) NOPASSWD:/usr/bin/rsync

※ 公開先のディレクトリのパーミッションをちゃんと設定すれば、root権限渡さなくても良いかも。

3. 公開鍵の作成と登録をする

rsync_userの公開鍵を作成します。

$ sudo su rsync_user
$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# 次の二つが出来る。
# /home/rsync_user/.ssh/id_rsa
# /home/rsync_user/.ssh/id_rsa.pub 

ssh-deployが対応していないため、パスフレーズは設定しない。 id_rsa.pubをこのユーザの公開鍵として、id_rsaがGitHub actionsに登録する秘密鍵とします。ローカルで鍵を作った方は適宜読み替えてください。

rsync_userの公開鍵を登録します。

# authorized_keysを作成
$ cd /home/rsync_user/.ssh/
$ cp id_rsa.pub authorized_keys

# 権限を変更
$ sudo chmod 700 /home/rsync_user/.ssh
$ sudo chmod 600 /home/rsync_user/.ssh/authorized_keys

authorized_keysが既に登録済みの場合は、末尾に公開鍵を追加する。 .sshディレクトリとauthorized_keysの権限をそれぞれ700600にする。

ここまでで、ssh接続ができるようになっているはず。

# サーバの中から
$ ssh rsync_user@localhost -i /home/rsync_user/.ssh/id_rsa

# ローカルのPCから
> ssh rsync_user@{サーバのIP・ホスト名} -i path/to/id_rsa

接続出来ない場合はauthorized_keysの権限とsshdを確認する。 サーバの中からできて外部から出来ない場合は、さくらのVPSの場合パケットフィルタのポートとIPを確認する。

参考: お前らのSSH Keysの作り方は間違っている

rsync -avz rsync://rsync_user1@10.0.1.108/tmp /var/www/test

4. sshd_configを設定する

セキュリティを高めるための設定です。 rsyncに直接関係ないので後からでもよい。

まず、sshd_configをバックアップはしておく。消しちゃったら建て直せばいい。

cd /etc/ssh
sudo cp sshd_config sshd_config.org

sshd_configを編集する。

$ sudo vi sshd_config

PermitRootLogin no
PasswordAuthentication no
AuthorizedKeysFile      .ssh/authorized_key

PermitRootLogin no はrootユーザでのログインを拒絶する。 PasswordAuthentication noはパスワードでのログインを拒絶する。 どちらもセキュリティを高めるための設定です。 AuthorizedKeysFile .ssh/authorized_keyは公開鍵のパスを指定している。デフォルトで変更の必要ないはず。

sshdの設定を更新する。

$ sudo systemctl restart sshd
$ sudo systemctl status sshd
* sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-12-22 22:27:56 JST; 1 day 1h ago
     Docs: man:sshd(8)
           man:sshd_config(5)
 Main PID: 20149 (sshd)
   CGroup: /system.slice/sshd.service
           `-20149 /usr/sbin/sshd -D

参考: ネコでもわかる!さくらのVPS講座 ~第二回「サーバーをさわってみよう!」

5. SSHのポート番号の変更

SSHのポートを2022に変更してみる。

$ sudo vi sshd_config
Port 2022

sshdの設定を更新する。

sudo systemctl restart sshd

ポートの変更ではfirewalldのsshの設定も変更します。

$ cd /usr/lib/firewalld/services/
# コピーする
$ sudo cp ssh.xml ssh.xml.org

# firewallの設定を変更する
$ vi /usr/lib/firewalld/services/ssh.xml
22 -> 1022

ポートを変更したらfirewalldを再起動する。

$ sudo systemctl restart firewalld.service
success

$ sudo systemctl status firewalld.service
* firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)   Active: active (running) since Mon 2020-12-21 22:41:40 JST; 2 days ago
     Docs: man:firewalld(1)

ここまでで、22での接続が出来ず、2022での接続が確認できる。

# サーバの中から
$ ssh rsync_user@localhost -p 2022 -i /home/rsync_user/.ssh/id_rsa

# ローカルのPCから
> ssh rsync_user@{サーバのIP・ホスト名} -p 2022 -i path/to/id_rsa

参考: CentOS 7 firewalld よく使うコマンド

さくらのVPSの場合はパケットフィルタの設定も行う。

  • カスタムTCP
  • ポート: 2022
  • すべて許可する

Sakura VPS Docs パケットフィルタ

GitHub actions

GitHub actionsのワークフローの設定と環境変数を設定します。

ワークフローの設定

generateまではNuxt.jsのプロジェクトを作成したときに出来たワークフローです。 サーバへデプロイするところは、マーケットプレイスのssh-deployを利用しています。

nuxt-content-sample/.github/workflows/ci.ymlに全文があります。

理解したことをブロック単位で説明を添えてみます。

ci.yml
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master

▲ トリガーとなる動作。masterへのプッシュとプルリクエストでCIが動き出す。ブランチごとにテスト用とか分けられる。

ワークフローの確認には手動で実行できるworkflow_dispatchもあるるようです。

jobs:
  ci:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest]
        node: [14]

▲ ここからワークフローのジョブを定義する。runs-onで動作する環境を選ぶ。Ubuntuの最新版でNode v14の環境でビルドします。

steps:
      - name: Checkout 🛎
        uses: actions/checkout@master

▲ リポジトリからチェックアウト。

- name: Setup node env 🏗
        uses: actions/setup-node@v2.1.2
        with:
          node-version: ${{ matrix.node }}

▲ Nodeのバージョンを定義する。strategyでセットしたパラメータが使える。

- name: Get yarn cache directory path 🛠
        id: yarn-cache-dir-path
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - name: Cache node_modules 📦
        uses: actions/cache@v2
        id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      - name: Install dependencies 👨🏻‍💻
        run: yarn

▲ node-modulesのキャッシュを定義して、yarnでインストールする。 キャッシュがあると次回のビルドでnode-modulesのインストール時間を軽減できる。 初心者にこの設定を組むのは厳しいので、Nuxt.jsのプリセットが有って助かった!

- name: Run linter 👀
        run: yarn lint

      - name: Run tests 🧪
        run: yarn test

      - name: Run generate
        run: yarn generate

▲ lintとテストとビルドを実行している。 lintとテストでエラーが出た場合はちゃんとワークフローも止まります。 ちゃんとプッシュする前に確認する習慣がつきそう。

- name: Deploy to Server
        uses: easingthemes/ssh-deploy@v2.1.5
        env:
            SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
            ARGS: "-rltgoDzvO --delete"
            SOURCE: "dist/"
            REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
            REMOTE_USER: ${{ secrets.REMOTE_USER }}
            REMOTE_PORT: ${{ secrets.REMOTE_PORT }}
            TARGET: ${{ secrets.REMOTE_TARGET }}

▲ サーバへデプロイするssh-deployの処理です。 generateで生成した./distディレクトリ以下をsecrets.REMOTE_TARGETに置きます。 サーバのsecrets.REMOTE_TARGETのパスからNginxがホスティングしている。 rsyncのコマンドを自由度高めに設定できる。

secretsはリポジトリで設定した環境変数です。${{ secrets.REMOTE_HOST }}`のようにして使えます。

https://github.com/marketplace/actions/ssh-deploy

環境変数を設定

ワークフローの中で利用できる環境変数は、リポジトリのSetting → Secrets → New repository secretから追加できる。

secret

ワークフローでは${{ secrets.REMOTE_HOST }}`のように使える。

どうもEnvironment secretsRepository secretsがあって使い分け出来る。 Repository secretsの方が暗号化されていて個人ではリポジトリのオーナーだけ見られるようです。

暗号化されたシークレット

おわりに

以上でさくらのVPSにNuxt.jsの静的サイトをデプロイまでの手順を紹介しました。

1記事にまとめましたがかなり手順があって大変です。前回NetlifyからVercelへの移行を検討した話という話を書いた。Netlifyが遅い点をディスったが、Netlifyの無料で手軽にデプロイできるのは凄いことだなと改めて感じた。

マルチドメインで使えるようなNginxのバーチャルホストの設定や、Let’s EncriptでのSSL対応もしたのでこちらも記事にします。

とうとう自分のサーバを契約してブログだけでは持て余してしまう。 アプリを作りたいが、リソース管理やセキュリティの方面も学んでいきたい。 来年は使い倒していく。