ホスティング先を XREA から GitHub Pages に変えた詳細

XREA Plus が2,514円/年もかかるのは高いなぁと思い、ホスティング先を XREA サーバから GitHub Pages に変えた。

今回はその技術的な詳細について書いておく。

目次

移行方針

このサイトは現状、静的なコンテンツしかないので、GitHub Pages に移行することで特に問題はなかった。XREA Plus は広告も除去されていて、元々広告がない GitHub Pages 向けに調整する必要はなかった。

neos21.net というドメインを取得しているので、DNS で向き先だけ変えれば、既存の URL 等には影響がないようにしたく、移行作業をしていった。

GitHub Pages に独自ドメインを割り当てる

GitHub リポジトリ自体は以前から変わらず同じ。ただし今回の移行作業の中で、リポジトリの要領削減のため過去のコミットを全て削除した。その話は後述。

まずはローカルで全量ビルドし、gh-pages ブランチに全資材を Push した。その際、neos21.net というドメイン名だけを書いた CNAME という1行のファイルをルート直下にコミットしている。

次に GitHub リポジトリの「Settings」→「Pages」画面に移動し、

を確認する。

そしたら、DNS 設定を変更していく。以下の GitHub 公式ドキュメントを参考に設定する。

neos21.net ドメインは Value Domain で管理しているので、Value Domain の DNS 設定に移る。

指定する。

Value Domain 設定画面

設定後、1・2時間待って dig コマンドで確認した時に、以下のようなレコードが取得できていれば OK。

# ネイキッドドメインに対する NS レコードはそのままにしておく
$ dig neos21.net +nostats +nocomments +nocmd -t NS
;neos21.net.                    IN      NS
neos21.net.             0       IN      NS      01.dnsv.jp.
neos21.net.             0       IN      NS      02.dnsv.jp.
neos21.net.             0       IN      NS      03.dnsv.jp.
neos21.net.             0       IN      NS      04.dnsv.jp.

# ネイキッドドメインには A レコード4つ
$ dig neos21.net +nostats +nocomments +nocmd
;neos21.net.                    IN      A
neos21.net.             0       IN      A       185.199.108.153
neos21.net.             0       IN      A       185.199.109.153
neos21.net.             0       IN      A       185.199.110.153
neos21.net.             0       IN      A       185.199.111.153

# www ドメインには CNAME のみ
$ dig www.neos21.net +nostats +nocomments +nocmd
;www.neos21.net.                        IN      A
www.neos21.net.         0       IN      CNAME   neos21.github.io.

ちなみに、Freenom で取得している無料ドメインを使って、Freenom DNS でも試してみたのだが、Freenom でも同様に、A レコード4つ、CNAME レコード1つを作れば1・2時間でしっかり反映された。

Freenom DNS も Value Domain も、若干 www ドメインに対する CNAME の反映がちょっと遅れた。ネイキッドドメインでは上手くアクセスできるのに、www サブドメインが付いているとまだ反映されていない、みたいな状態が長めに続いた。

DNS の反映が終わったら、再び GitHub リポジトリの「Settings」→「Pages」画面に戻る。

Your site is published at https://neos21.net/

と無事表示されたら、下部にある 「Enforce HTTPS」チェックボックスが押下できるようになっていると思う (なっていなかったら F5 更新などして試してみてほしい)。コレにチェックを入れることで、HTTPS 化してくれる。コレも反映には若干時間がかかるので、10分程度は気長に待とう。

コレでドメイン設定は完了。すげー簡単だった…!

GitHub Pages への CNAME ファイル設置・DNS 設定変更と反映待ち・Enforce HTTPS までが完了したら、アクセス URL は次のようになるはずだ。

$ dig コマンドの他、$ curl -I でヘッダ情報を確認してみたり、$ nslookup でも疎通確認してみたりすると良いだろう。TTL の関係で、反映には1時間はかかると思って良いだろう。どうも外部の dig サービスだと反映されているのに、ローカルからコマンド実行するとまだ反映されていない、みたいな状態は結構長めに続いた印象。

XREA サーバ側のドメイン紐付けを解除しておく

neos21.net ドメインと関わりがなくなった XREA サーバ側の設定をしておく。

XREA サーバには Freenom ドメインだけ割り当てておく

以前から neo.s21.xrea.com に対して割り当てていた neos21.tk ドメインだが、以下のように www の方は CNAME を使っても大丈夫だったのでこのように変更しておいた。

Freenom DNS 設定

$ dig neos21.tk +nostats +nocomments +nocmd -t A
;neos21.tk.                     IN      A
neos21.tk.              0       IN      A       150.95.8.121

$ dig www.neos21.tk +nostats +nocomments +nocmd -t CNAME
;www.neos21.tk.                 IN      CNAME
www.neos21.tk.          0       IN      CNAME   neo.s21.xrea.com.

# ちなみに、Freenom DNS では NS レコードが見えないがこんなのが設定されていた
$ dig neos21.tk +nostats +nocomments +nocmd -t NS
;neos21.tk.                     IN      NS
neos21.tk.              0       IN      NS      ns01.freenom.com.
neos21.tk.              0       IN      NS      ns02.freenom.com.
neos21.tk.              0       IN      NS      ns03.freenom.com.
neos21.tk.              0       IN      NS      ns04.freenom.com.
ns01.freenom.com.       0       IN      A       54.171.131.39
ns02.freenom.com.       0       IN      A       52.19.156.76
ns03.freenom.com.       0       IN      A       104.155.27.112
ns04.freenom.com.       0       IN      A       104.155.29.241

コチラも、Apex ドメインへの反映は15分くらいで確認できたが、www への CNAME レコードの反映が2時間近くかかった印象。

GitHub Pages へのデプロイパイプラインを調整する

ホスティング先の移行作業と、XREA サーバに対する後処理はコレで終わり。以降は本サイトで導入しているオレオレデプロイパイプラインを調整していく。

元々は、GitHub Actions にて Push 時のコミットを見て差分だけビルドして XREA サーバに FTP アップロードしていた。また、毎朝予約投稿するためにファイル内に書かれた更新日時を見て、同様に差分ビルドして FTP アップロードしていた。

ブログの記事数も多いし画像ファイルもあるので、毎回全量ビルドすると 500MB くらい扱うことになり無駄が多い。GitHub Pages に移行しても、差分ビルドの仕組みはそのまま活かしたかった。

つまりは、master ブランチの dist/ ディレクトリ配下に出力されたファイルを gh-pages ブランチにコミットしたいが、全量ビルドしなくても差分だけ git add できれば良いワケだ。その方法を色々検討したので紹介する。

ローカル環境では「二重 git clone」で対応

まずローカル環境で出来そうだと考えたのは、dist/ ディレクトリ自体が「gh-pages ブランチをチェックアウトしたローカルリポジトリ」になれば良いのだろう、という手法。

hail2u 様で行われている作業を参考に、要はこんなことをやってみたワケだ。

# まずは普通に `master` ブランチを Clone する
$ git clone https://github.com/Neos21/neos21.net.git
$ cd ./neos21.net/
$ pwd
/home/neo/neos21.net
$ git branch
* master

# `.gitignore` には `dist/` を書かないでいるので、ローカルのみ除外するように以下を設定する
$ echo 'dist/' >> ./.git/info/exclude

# `gh-pages` ブランチを `dist/` ディレクトリに Clone する
$ git clone -b gh-pages https://github.com/Neos21/neos21.net.git dist
$ cd ./dist/
$ pwd
/home/neo/neos21.net/dist
$ git branch
* gh-pages

Git リポジトリを Clone したディレクトリ内で、また別に Git リポジトリを Clone している。コレでローカルでは問題が起きなかった。

親ディレクトリで全量ビルドすれば、./dist/ 配下にビルドされたファイルが配置される。./dist/ ディレクトリに移動して git diff を確認しながら gh-pages ブランチに Push していけば良い。

手作業でファイル削除など込みでやるなら、コレでやるのが良さそうかなと思う。

GitHub Actions で差分ビルドし gh-pages ブランチにコミットする方法

今回の移行作業のキモはココだった。

ということで、master ブランチへの Push 時に実行される GitHub Actions では、

といった処理を行えば良いことになる。中でも「なんとかして ./dist/ を退避して gh-pages ブランチに切り替える」のが難しかった。git subtree を使う方法とか色々出てきたが、結局以下のように作業すればなんとかなった。

  1. master ブランチに、コレまでどおり差分ビルドして ./dist/ 配下にファイルを配置しておく
    • ./dist/ 配下に全量ビルドする必要はない
    • どのファイルを対象にするかなどはコレまで自分が自作したスクリプトそのまま
  2. npm install コマンドで package.json が整形されて「ファイルの更新」差分として扱われてしまうので、元に戻しておく
    • $ git checkout ./package.json
    • コレをやらないと、このあと git checkout でブランチ切替が上手くいかない (-f オプションで逃げても良いかも知れないが)
  3. ./dist/ 配下を git stash で退避する
    • $ git stash --include-untracked -- ./dist
    • この時、.gitignore./dist/ の記載があると上手く Stash できないので、.gitignore には dist/ を記載しないでおく
  4. GitHub Actions 内では直近のコミットのみ取得しており、gh-pages ブランチの存在が分からなかったりするので Fetch しておく
    • $ git fetch origin
  5. GitHub Actions 内でブランチを切り替えるには、以下のように書く
    • $ git checkout -b gh-pages --track origin/gh-pages
    • 全く gh-pages ブランチが存在しない状態では動かないので、予め gh-pages ブランチを生やす作業だけは手作業でやっておく
  6. Stash した ./dist/ ディレクトリを復元する
    • $ git stash pop
  7. 復元した ./dist/ ディレクトリ内の資材を ./ 配下に置き直す
    • $ cp -r ./dist/* .
    • mv で何とか移せないかと試行錯誤していたが、どうにも色んなところで詰まるのでコピーにした
  8. ./dist/ ディレクトリやその他 master ブランチで使用していたファイルが「ファイルの追加」差分として残っているので、これらを削除する
    • $ rm -rf ./dist ./node_modules ./package-lock.json
    • npm install 時に node_modules/package-lock.json ができていて、これらが gh-pages ブランチに git add されると死ぬので消しておく
  9. gh-pages ブランチにコミット・Push していく
    • 先にコミッタの設定が必要なのでしておく
    • $ git config --global user.email 'neos21@gmail.com'
    • $ git config --global user.name 'Neos21'
    • $ git add .
    • $ git commit -m 'Deploy gh-pages'
    • $ git push origin gh-pages
    • GitHub Actions 内では特にトークン管理などせずとも Push できた

Git Stash を使い、ブランチ切替後に復元するという手法だ。あとは gh-pages ブランチ内でコミットしたくないファイルを上手く除外して git addgit commit できていれば OK。

要は master ブランチにおける ./dist/ ディレクトリ配下が、gh-pages ブランチのルートディレクトリになるので、.gitignore がいなくなったりして扱いがダルくなるというワケだ。gh-pages ブランチ用の .gitignore を置いても良いのだろうけどな~、そうするかなー。

これらの処理を GitHub Actions YAML 内で全部シェルスクリプトで実装したのも、master ブランチにいる ./lib/ などの自作スクリプト群がブランチ切替によって使えなくなるためだ。

当初はローカル環境と同じく、./dist/ ディレクトリ部分に gh-pages ブランチを Clone しようかと思ったのだが、何か微妙そうだったので止めた。

GitHub Actions 内でブランチを切り替えるにはちょっとひと手間必要だった。

./dist/ 配下の全ファイルを ./ 配下に移したいところは、当初 mv コマンドでやろうとしていたのだが、

などなど、つらみが増えたので、諦めて cp -r で全上書きして ./dist/ ディレクトリを丸ごと消す、という処理にした。シェルスクリプトってこんな簡単なことも未だにうまくできんのかいな…。頼むで…。

あと、gh-pages ブランチへの切替はコストがかかるので、デプロイ対象のファイルがなければその処理をスキップしたい。ということで、手順 1. で差分ビルドした時に、ビルドしたファイルの情報を ./temp/upload.json に書き出すことにした。デプロイしたいファイルが1つもなければ、このファイルは生成されない仕組み。

そして、ファイルの存在チェックをしてくれる andstor/file-existence-action を使ってこのファイルの存在チェックを行い、次のステップで if 文を書いてチェックすることにした。

ということで出来上がった YAML ファイルは次のとおり。

name: Deploy To GitHub Pages On Commit
on:
  push:
    branches:
      - master
    # 手動実行
    workflow_dispatch:
jobs:
  deploy-on-commit:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: 14.x
      # 変更があったファイルを JSON ファイルに書き出す
      - name: Get Changed Files
        id  : get_changed_files
        uses: jitterbit/get-changed-files@v1
        with:
          format: json
      - name: Write Changed Files
        run : |
          mkdir -p ./temp/
          echo '${{ steps.get_changed_files.outputs.added_modified }}' > ./temp/added_modified.json
          echo '${{ steps.get_changed_files.outputs.renamed }}' > ./temp/renamed.json
          echo '${{ steps.get_changed_files.outputs.removed }}' > ./temp/removed.json
          cat ./temp/added_modified.json
          cat ./temp/renamed.json
          cat ./temp/removed.json
      # npm install 後、一部ファイルはビルドしておく (アップロードするかどうかは後で特定する)
      - name: Setup npm
        run : |
          npm install
          npm run clear-dist
          npm run build-css
          npm run build-feeds
          npm run build-sitemap
      # JSON ファイルを基にビルドする (削除されたファイルについては扱わないので手作業する・ビルドしたファイルがあれば `./temp/upload.json` を生成する)
      - name: Detect And Build
        run : |
          node ./.github/workflows/deploy-on-commit.js
      # ビルドしたアップロード対象ファイルのリストがあるかチェックする
      - name: Check File Exists
        id: check_file_exists
        uses: andstor/file-existence-action@v1
        with:
          files: './temp/upload.json'
      # ファイルがあればそれをデプロイする
      # `npm install` 時に `package.json` が整形されて差分扱いになるようなのでチェックアウトして戻しておく (コレをしないとブランチ切替に失敗する)
      # `./dist/` を Stash し `gh-pages` ブランチに切替後 Pop することで、`./dist/` ディレクトリを `gh-pages` ブランチ内に復元する
      # `./dist/` 配下の全ファイル (隠しファイルは含まれない) を再帰的にコピーし `./dist/` ディレクトリを削除する (ファイル移動だと上手く処理しきれなかったので諦めた)
      - name: Deploy To GitHub Pages
        if  : steps.check_file_exists.outputs.files_exists == 'true'
        run : |
          cat ./temp/upload.json
          rm -rf ./temp
          git checkout ./package.json
          git stash --include-untracked -- ./dist
          git fetch origin
          git checkout -b gh-pages --track origin/gh-pages
          git stash pop
          cp -r ./dist/* .
          rm -rf ./dist ./node_modules ./package-lock.json
          git config --global user.email 'neos21@gmail.com'
          git config --global user.name 'Neos21'
          git add .
          git commit -m 'Deploy gh-pages'
          git push origin gh-pages

予約投稿のための GitHub Actions についても、ほぼ同様の方法で差分のみビルドして gh-pages にコミット・Push している。うまく動いてくれてよかった。

肥大化した Git 履歴を全削除した

neos21.net リポジトリ全体の容量が爆増して、git pull に時間がかかるようになってしまったので、思い切って過去のコミットを全部削除した。もう過去には戻れない。前に前になのだ。w

一般的には上述の記事のような、git rebase -i を使って、一部のコミットだけなかったことにするやり方があるが、今回は「最新の1コミットだけを生かす」ことにした。新規リポジトリを作って最新のファイルだけ配置したのと同じような感じのことをやる。

↑ コチラが参考になった。

# `master` ブランチの最新コミットだけ生かすとする
$ git checkout master
# `--orphan` オプションを付けると、親を共有しない独立したブランチが作れる
$ git checkout --orphan temp-master
# 既に存在するファイルが全て Add された状態になっているので、そのままコミットする
$ git commit -m 'Initial Commit'
# `-B` オプションにより、既存の `master` ブランチを上書きして `temp-master` ブランチのみを引き継がせる
$ git checkout -B master
# `temp-master` ブランチを消す
$ git branch -d temp-master
# 1コミットだけとなった `master` ブランチを強制 Push する
$ git push -f

このやり方で mastergh-pages ブランチをまっさらに作り直して今に至る。ローカル作業時もかなり速くなった。

以上

こうして neos21.net ドメインは GitHub Pages で運用していくことになった。

XREA Plus の更新直後に実質的な利用を止めてしまう結果となったが、逆にいえばあと1年間は XREA Plus 環境が使えるので、何か試してみようかな。

http://neo.s21.xrea.com/ で運営してきた XREA のスペースには https://neos21.tk/ という Freenom ドメインを割り当てて HTTPS 化してあり、「Neo's World Origin」という仮題を付けてある。ドメイン違いで CGI や DB が使える環境として、何か用途を思い付けば使っていこうと思う。

↑ A8.net に XREA と Value Domain の広告があったので貼っときます。