GitLab Pagesと本番サイトの双方にJekyllで生成されたページをデプロイする
GitLab CI/CD を使って、GitLab.com 上で管理しているリポジトリから Jekyll を使って静的なサイトを構築し、ドラフト版(master
以外)は GitLab Pages で、本番(master
)は外部のサイト (FTPS接続を受け付けている) で公開するという運用をしています。
この場合に注意することをメモします。
.gitlab-ci.yml
の内容
使っている image: ruby:3.2
variables:
JEKYLL_ENV: production
LC_ALL: C.UTF-8
# DST_DIR is used to set root directory for HTTP(S) service.
DST_ROOT: www
# DST_DIR is used as base-url, it might be '/'.
DST_DIR: /
cache:
paths:
- vendor/
- dist/
- .jekyll-metadata
before_script:
- bundle install --path vendor
- apt-get update -qy
- apt-get install -y git-restore-mtime
pages:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH != "master"'
script:
- /usr/lib/git-core/git-restore-mtime
- bundle exec jekyll build -I -d public
artifacts:
paths:
- public
deploy_pages:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
script:
- /usr/lib/git-core/git-restore-mtime
- bundle exec jekyll build -V -I -d dist --baseurl "$DST_DIR"
- lftp -e "
set ftp:ssl-allow yes;
open $FTP_SERVER;
user $FTP_USERNAME $FTP_PASSWORD;
mirror -X .* -X .*/ --reverse --ignore-time --verbose
dist/ $DST_ROOT$DST_DIR;
bye"
内容の解説
本番サイトと GitLab Pages とのパスの違いの吸収
GitLab Pages で公開される URL はプロジェクト名から自動生成されるため、本番サイトとルートの場所が違う構成になる場合があります。その場合はジョブの script
のコマンドライン引数で baseurl を設定する(例: --baseurl "/"
) ことで対応しています。この際、コマンドライン引数による設定は、_config.yml
による設定よりも優先されるのがポイントです。
利用する Docker Image
Docker Image としては、Ruby 公式の内、Jekyll がサポートしているバージョン[1]に対応する Ruby 公式イメージを使えば良いでしょう。
Ruby 公式の Docker Image では apt
が使えるので、必要なライブラリなどは apt-get
でインストールしていきます。
必要なプログラムは基本的に before_script
の部分で指定しています。
Bundler キャッシュの活用
毎回、Bundler を再構成していると無駄が多いので、Bundler を --path
引数付きで実施し、インストール先のパスを cache
として指定しています。
テスト用ビルドと本番ビルドの切り分け
ジョブの設定として rules
以下の if
文でジョブの実行条件を指定し、コミットされたブランチにより挙動を変えています。
この際、ブランチは GitLab により自動的に設定される変数 $CI_COMMIT_BRANCH
を利用して判定します。
本番サイトのビルド
ジョブ deploy_pages
では本番サイト向けのビルド、およびアップロードを行っています。
以下の処理を行います:
- jekyll で静的サイトのデータを生成します。
- 生成されたデータを lftp で本番環境へアップロードします。
lftp による本番サイトへのアップロード
本番サイトへのアップロード手段としてFTP, FTPS, SFTPなどが使えるのであれば、lftp を使うのが比較的簡単です。このパッケージはデフォルトでは入っていないので、CIのレシピ中でインストールします。
lftp 用パスワードの秘匿
この際、.gitlab-ci.yml
に直接FTPパスワードを書くのはセキュリティ上推奨できないので、GitLab のUIから設定できる(一般ユーザーには見えない)環境変数を使うと良いでしょう。
例の場合、$FTP_PASSWORD
はUI設定の環境変数として定義しています。
ミラーリング効率化のためのオプション
lftp でミラーする際、通常はファイルのタイムスタンプ[2]とサイズを比較して差があった場合にファイルが差し替えられます。しかし、タイムスタンプの比較は正常に動作しないことが多いため[3]、--ignore-time
というオプションをつけておくと良さそうです[4]。ただし、--ignore-time
が指定された場合、ファイルサイズの比較のみで変更の有無が判別され、サイズが変わらない変更の場合にアップロードが行われなくなるので[5]、パフォーマンスと確実性のバランスを考えて設定するかどうか判断してください。
FTPS をサポートしていない場合
対象のサーバが FTPS に対応していない場合は、 set ftp:ssl-allow no
に変更してご利用下さい。
テスト用ビルドは GitLab Pages へ
ジョブ pages
は GitLab Pages を使った公開です。
master
以外のブランチでは Job pages
で GitLab Pages 向けのデータをビルドします。結果を public
というフォルダに格納し、それを artifacts
として指定することで、ビルド結果が gitlab.io で公開されます。
TODO: インクリメンタルビルドを使ってJekyllのサイト構築の時間を減らせる可能性があるが、キャッシュを使っても今の所想定通りの動作になっていないので、引き続き検討が必要です。
Discussion