【テクニカル・上級編】GitLab Pagesで静的サイトを無料でホスティング!GitHub Pagesより便利? – バージョン管理・CI/CD活用バイブル

GitLab Pagesの真髄:GitHub Pagesの枠を超えたエンタープライズ・静的ホスティングの極意

世の中の多くの開発者は、「GitLab Pages」と聞いて「GitHub PagesのGitLab版ね、無料の静的サイトホスティングでしょ?」程度にしか認識していない。もし君がそう思っているなら、DevOpsの最前線を見誤っていると言わざるを得ない。

GitHub Pagesが提供するのは、あくまで「単一リポジトリに紐づく、手軽な静的ファイルの公開場所」に過ぎない。対して、GitLab Pagesの裏側にあるのは、世界最高峰のCI/CDエンジンであるGitLab CI/CDそのものだ。

この事実を理解していれば、GitLab Pagesが単なる「静的サイトの置き場」ではなく、「あらゆるビルド成果物、ドキュメント、カバレッジレポート、そしてプレビュー環境を極限まで自動化・セキュア化して爆速で配信するための高度なインフラストラクチャ」であることが見えてくるはずだ。

今回は、HugoやJekyllを用いたモダンな静的サイト構築をベースに、GitLab CI/CDのパイプラインを骨の髄までハックし、エンタープライズレベルのセキュアかつ高速なGitLab Pages運用を実現する知見を共有しよう。

—

1. 内部アーキテクチャの理解:なぜGitLab Pagesは強力なのか?

GitLab Pagesの本質は、GitLabのストレージとリバースプロキシ、そしてCI/CDワーカー(GitLab Runner)の完璧な融合にある。

1. 成果物(Artifacts)の直接マウント:
GitLab CI/CDのパイプラインで生成された成果物は、特定のエンドポイント(通常は `public/` ディレクトリ)としてPagesのデーモンに引き渡される。サーバーレス的に、ビルドが成功した瞬間にアトミックにルーティングが切り替わる。
2. カスタムドメインとLet’s Encryptの完全自動化:
DNSレコードさえ向けておけば、GitLab自体がACMEプロトコルを叩いてSSL/TLS証明書を自動発行・更新する。証明書の期限切れに怯える日々は過去のものだ。
3. アクセス制御の柔軟性(GitLab Enterprise Editionの真価):
パブリック公開だけでなく、プロジェクトメンバー、あるいは同一グループ内、さらには特定のアクセス権を持つユーザーだけにPagesを限定公開できる(Access Control)。社内WikiやAPIドキュメントのセキュアなホスティングにおいて、これ以上の選択肢はない。

—

2. 徹底比較:なぜ「上級エンジニア」はGitHub PagesよりGitLabを選ぶのか?

| 比較項目 | GitHub Pages | GitLab Pages |
| :— | :— | :— |
| CI/CDの柔軟性 | GitHub Actionsに依存(設定が分散しがち) | GitLab CI/CDとネイティブ統合(単一YAMLで完結) |
| プレビュー環境 (Review Apps) | 原則として追加のActions/Appが必要 | 動的環境(`environment: url`)をファーストクラスでサポート |
| アクセス制限 | Publicのみ(Enterprise Cloudを除く) | Public / Internal / Privateを細やかに制御可能 |
| ビルドキャッシュ・並列処理 | アクションキャッシュに依存 | 強力なディレクトリーキャッシュ、マトリクスビルド完備 |

GitHub Actionsが悪いわけではない。しかし、リポジトリのライフサイクル全体(Issue、Merge Request、CI/CD、Security Scanning、Pages)を単一のプラットフォームで完結させる統合度において、GitLabの優位性は揺るぎない。

—

3. 実践:Hugoを用いた超高速ビルド&デプロイパイプライン

ここでは、静的サイトジェネレーターとして圧倒的な速度を誇る「Hugo」を使用し、セキュリティヘッダーの付与、キャッシュ最適化、そしてマルチステージビルドを組み込んだプロダクションレディな `.gitlab-ci.yml` を構築する。

生半可な設定ではない。セキュリティ、パフォーマンス、トレーサビリティのすべてを妥協なく詰め込んだ、至高のパイプラインだ。

完全最適化された `.gitlab-ci.yml`

stages:

  • test
  • build
  • deploy

variables:
# Hugoのバージョンを固定し、環境の再現性を担保する
HUGO_VERSION: “0.121.2”
GIT_SUBMODULE_STRATEGY: “recursive”

キャッシュ戦略: Hugoのモジュールキャッシュとビルドキャッシュを保持
cache:
key: “${CI_COMMIT_REF_SLUG}”
paths:

  • .cache/
  • resources/_gen/

1. テストステージ:Markdownの構文チェックやリンク切れ検証
lint:
image: alpine:3.19
stage: test
script:

  • echo “Running pre-flight checks…”

# ここにvaleやhtmlprooferなどのリンターを挿入可能

  • apk add –no-cache curl
  • echo “Lint passed successfully.”

rules:

  • if: ‘$CI_PIPELINE_SOURCE == “merge_request_event”‘
  • if: ‘$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH’

2. ビルドステージ:静的アセットの生成
pages:
image:
name: klakegg/hugo:${HUGO_VERSION}-ext-alpine
entrypoint: [“”]
stage: build
script:

  • mkdir -p .cache

# Hugoによるビルド実行 (Minify化、ドラフト除外、キャッシュディレクトリの指定)

  • hugo –minify –cacheDir .cache

# セキュリティヘッダー(CSP等)を適用するための _headers ファイルの配置

  • |

cat << 'EOF' > public/_headers
/
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src ‘self’ https:; style-src ‘self’ ‘unsafe-inline’ https:; script-src ‘self’ ‘unsafe-inline’ https:;
EOF
artifacts:
name: “pages-artifact-${CI_COMMIT_SHA}”
expire_in: 30 days
paths:

  • public

rules:

  • if: ‘$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH’

environment:
name: production
url: $CI_PAGES_URL

3. プレビュー環境(Review Apps):マージクエストごとの一時プレビュー
preview_pages:
image:
name: klakegg/hugo:${HUGO_VERSION}-ext-alpine
entrypoint: [“”]
stage: build
script:
# MRブランチごとにbaseURLを動的に書き換えてビルド

  • hugo –minify –baseURL “${CI_PAGES_URL}”
  • mkdir -p public
  • mv public public_preview # ※実際はhugoのdestination設定に合わせて調整

artifacts:
expire_in: 7 days
paths:

  • public

rules:

  • if: ‘$CI_PIPELINE_SOURCE == “merge_request_event”‘

environment:
name: review/$CI_MERGE_REQUEST_IID
url: https://$CI_PROJECT_NAMESPACE.pages.gitlab.io/-/$CI_PROJECT_NAME/-/environments/review/$CI_MERGE_REQUEST_IID/edit
on_stop: stop_preview_pages

Review Appのクリーンアップ
stop_preview_pages:
stage: .pre
script:

  • echo “Stopping review app…”

rules:

  • if: ‘$CI_PIPELINE_SOURCE == “merge_request_event”‘

when: manual
environment:
name: review/$CI_MERGE_REQUEST_IID
action: stop

—

4. プロが教える極限の最適化・ハック集

ここからは、一般的なドキュメントには決して載っていない、GitLab Pages運用における「現場の知見」を伝授する。

1. `_headers` と `_redirects` によるエッジ制御

Netlifyなどのモダンホスティングと同様に、GitLab Pagesでも `public/` ディレクトリの直下に `_headers` や `_redirects` ファイルを置くことで、リバースプロキシ層での挙動を制御できる。
特にSPA(Single Page Application)をホスティングする場合、すべてのルーティングを `index.html` にフォールバックさせるために `_redirects` は必須だ。

public/_redirects の例 (SPA用)
/ /index.html 200

2. GitLab APIを用いたデプロイメントの監視と自動化

大規模なドキュメントサイトや社内ポータルを運用していると、「現在のPagesがどのコミットを指しているか」をプログラムから把握したくなる瞬間がある。
そんな時は、GitLab REST APIを叩いてデプロイ状態を監視するスクリプトをCIに組み込もう。

GitLab APIでPagesのステータスを確認するスニペット
curl –header “PRIVATE-TOKEN: ${GITLAB_API_TOKEN}” \
“https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/pages”

3. パフォーマンスの限界突破: Brotli圧縮とアセットのプリコンパイル

HugoやJekyllで生成したHTML/CSS/JSは、そのままデプロイするのではなく、CIのビルドステップの最後に `brotli` や `gzip` で事前圧縮(Pre-compression)しておくことを強く推奨する。
これにより、GitLab Pagesのエッジサーバーが動的圧縮するオーバーヘッドをゼロにし、TTFB(Time to First Byte)を極限まで切り詰めることが可能だ。

ビルドスクリプトの最後に追加するBrotli圧縮の例

  • find public/ -type f -regex ‘.\.(html|css|js|json|xml|txt|svg)$’ -exec brotli -k -Z {} \;

—

5. 結び:インフラストラクチャをコードとして愛せ

GitLab Pagesは、単なる「無料のオマケ機能」ではない。
GitLab CI/CDという巨大な自動化の歯車の一部品であり、適切に調律してやれば、企業のナレッジベース、オープンソースのドキュメントサイト、あるいはモダンなWebフロントエンドの配信基盤として、有料のクラウドサービスに匹敵する堅牢性とパフォーマンスを発揮する。

設定ファイルを書き、パイプラインを走らせ、緑色のチェックマークを確認する。その瞬間に世界中へサイトが配信される快感——これこそが、私たちDevOpsエンジニアがコードを書く理由の一つではないだろうか。

さあ、今すぐ既存のパイプラインを見直し、君のGitLab Pagesを極限まで最適化してほしい。

タイトルとURLをコピーしました