静的サイトジェネレーター Pelican と Firebase Hosting で始める開発ブログ

Pelican x Firebase Hosting

みなさんこんにちは。いつも 絵文字ジェネレーター をご利用いただき、ありがとうございます!絵文字ジェネレーターの開発者です。

このたび、絵文字ジェネレーターのアップデートの告知、技術情報の発信を目的として、開発者ブログを立ち上げることになりました。このブログを通して、絵文字ジェネレーターのことをよりよく知っていただけますと幸いです。どうぞ、よろしくお願いします。

このブログはじめの記事は、このブログ自体の技術的な話となります。この開発者ブログは WordPress などの CMS 等は用いず、Python 製の静的サイトジェネレーター Pelican を用いて構築しています。記事は Markdown で記述し、生成された HTML は Firebase Hosting を用いてホスティングしています。

この記事では、これら技術選定の理由や、全体の構成、Pelican のテーマの作成方法までを順に説明していけたらと思います。

ブログの全体構成

この開発ブログは、静的サイトジェネレーターである Pelican をメインに、以下のような技術で構成されています。

アーキテクチャ アーキテクチャ

静的サイトジェネレーターは、Ruby 製の Jekyll、Go 製の HUGO、JavaScript 製の Hexo などが有名かなと思いますが、今回は Python 製の Pelican を選択しました。

絵文字ジェネレーター は、ウェブサイト本体と周辺ツールがほぼ全て Python によって開発されており、開発言語を合わせたかったのが選択の理由です。事実、Pelican は普段 Python でウェブサービスを作っている人にとって、非常に使いやすいツールでした。

プロダクションで配信している HTML ファイルは、CircleCI 上でビルドし、Firebase Hosting でホスティングしています。Firebase Hosting は、CLI ツール群が CI と連携することを念頭に置いて設計されているため、CI との相性は抜群でした。

最終的には Firebase Hosting 上の HTML ファイルは CloudFront を経由し配信されます。絵文字ジェネレーターのドメイン emoji-gen.ninja は、全体が CloudFront を経由する設計になっています。このブログは本サイトのサブディレクトリで運用したかったため、その経路にする必然性がありました。 (※補足: 2020年1月現在、この部分は変更されています。)

このブログのソースコードは、GitHub 上で emoji-gen/blog として公開されています。よろしければ、そちらもご覧ください。

Pelican とは?

Pelican は Python 製の静的サイトジェネレーターで、Python 2.7 もしくは 3.4 以降で動作します。公式ドキュメント が案外充実しており、使い方・設定項目・テンプレートの作成方法などが解説されています。よほど凝ったことをしなければ、公式ドキュメントを読むだけで問題なく利用できます。

記事は reStructuredText か Markdown かどちらかで記述できます。このブログでは Markdown を使っています。記事は固定記事 (ページ) と投稿 (時系列の記事) のどちらかになります。Pelican では記事の URL やディレクトリ構成を簡単にカスタマイズでき、便利です。

記事は最終的に HTML に変換され、テーマのテンプレートエンジンを通して最終的な HTML が出力されます。テーマのテンプレートエンジンには Jinja2 が採用されています。Jinja2 のカスタムフィルタが設定できるので、やりたいことはだいたい何でもできます。

テーマは標準で組み込まれているもの以外にも、有志によって複数公開されています。このブログでは、デザインにこだわりたかったため、独自のテーマを作成しそれを利用しています。

Pelican のテーマ作成ノウハウ

Pelican は 公式ドキュメント が充実しているので、テーマを作る際はまずそこに目を通すことをおすすめします。

テンプレート構成

トップページ (記事一覧)、記事のページ、タグ一覧のページなど、ページごとに呼ばれるテンプレートが決まっており、作成したいページのテンプレートはすべて作成する必要があります。設定で特定のテンプレートを無効化することができるので、必要がないページは無効化しておくと、テンプレートの開発が楽になります。

このブログでは、本来 11 ページ分のテンプレートが必要な所、記事一覧と記事詳細の 2 ページ分のみのテンプレートで済ませています。汎用的なテーマを作るのでなければ、必要になったら作る運用で十分です。

固定ページを多様するような使い方をする場合、記事ごとにテンプレートを設定することもできるので、その手法を取るとカスタマイズの幅が広がります。このブログは、固定ページは絵文字ジェネレーター本体側へ移譲するため、使ってません。

CSS (スタイル) の作成

テーマの CSS は Webpack を用いてビルドしています。Pelican の assets プラグイン を使うと、SASS を直接ビルドし minify することも可能なのですが、正直いつも使い慣れている Webpack を直接叩いた方が痒い所に手が届きます。

Pelican には、PC 版とスマフォ版で HTML を出力仕分けるという機能はありません (仮に分けれたとしても、配信側での対応が必要ですが...)。そのため、複数の画面サイズに最適化したデザインにしたいのであれば、同一の HTML に対してメディアクエリで分岐する手法を取ります。

テーマで使っている画像や CSS などの静的ファイルは、規定のディレクトリに配置しておくと、勝手に出力ディレクトリへコピーされます。

OGP / 構造化データ対応

Pelican は OGP (Open Graph Protocl) や構造化データについて、特にサポートはしていません。テーマ側でそれぞれの実装が必要になります。人気のある汎用的なテーマは、標準で対応していることが多いと思います。

このブログのテンプレートでは、OGP は meta 要素を愚直に出力することで対応しています。Jinja2 のテンプレート継承を使うと、シンプルに記述できます。

構造化データは JSON-LD 形式で埋め込んでいます。たとえば、テンプレートへ以下のように書くことで、Organization の構造化データを埋め込むことができます。

<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "Organization",
    "name": "{{ AUTHOR }}",
    "sameAs": [
      "https://twitter.com/emoji_gen"
    ],
    "logo": {
      "@type": "ImageObject",
      "height": 512,
      "url": "{{ SITEURL }}/{{ THEME_STATIC_DIR }}/images/logo-512.png",
      "width": 512
    },
    "url": "https://emoji-gen.ninja/"
  }
</script>

他には BlogPosting (ブログ記事) と BreadcrumbList (パンくずリスト) の構造化データを記事ページへ埋め込んでいます。

パフォーマンスチューニング

このブログは技術ブログですので、良いパフォーマンスとなるよう意識してテーマを作成していました。具体的には、以下の様は施策を行っています。

  • 適切な Cache-Control ヘッダの付与
  • CSS / JavaScript / HTML の最小化
  • CSS の HTML への埋め込み (style 要素)
  • SNS シェア系ボタンの JavaScript の排除
  • 動的に幅・高さが確定する要素の排除

以下の施策は行っていません。

  • AMP (対応していない理由は後述)
  • WebP 形式の利用

Twitter などの SNS シェア系ボタンの JavaScript を外したのが、一番パフォーマンスが向上しました。

Cache-Control ヘッダは、Firebase Hosting の機能で付与しています。現在、キャッシュ時間はあまり長い秒数を指定していませんが、ブログを公開して落ち着いたら、画像系を中心に長めに変更したいと思っています。

論より証拠だと思いますので、結果が気になる方は Google の PageSpeed Insights よりスコアを確認してみていただければと思います。

その他 Pelican の開発 Tips

プラグインの活用

このブログでは、以下の Pelican プラグインを利用しています。

  • minify (HTML の最小化)
  • sitemap (sitemap.xml の生成)
  • summary (記事の概要の境目を指定できるようにする)

これ以外にも、多数のプラグインが公開されています。ブログでよくある関連記事を表示するプラグインなどもあります。 プラグインを上手く活用すると、よりリッチなウェブページを簡単に構築でき、便利です。

AMP 対応

AMP は Google が推進しているモバイル向けウェブページを高速表示するためのプロジェクトです。AMP に対応するためは、通常の HTML より仕様が制限された AMP 対応の HTML を出力する必要があります。

Pelican は AMP 対応の HTML を、通常の HTML に追加して別ファイルに出力することに対応していません。出力する HTML をそもそも AMP 対応のものにすることは、テーマが対応していれば可能です。Pelican の開発者は AMP 対応に対し、issue 内 (#379) で『 AMP は Web を後退させる 』と否定的な見解を示しています。

Pelican を使い AMP 対応するには、AMP 対応の HTML を最初から出力するか、テーマを複数作成して切り替えて出力するしか手法がありません。AMP 対応の HTML をはじめから出力するのは、AMP の制限を大きく受けるので避けたく、かと言って複数テーマを用意するのはかなり面倒です。

このブログは AMP 対応せずとも十分に表示が早いので、AMP 対応は見送りました。

Firebase Hosting とは?

Firebase Hosting は静的サイトをホスティングするサービスで、無料で広告なしで利用できます。同様のサービスとしては、言わずと知れた GitHub PagesNetlify などがあります。

GitHub Pages はビルドを自動化する場合 CI 向けにデプロイキーを発行して Git でコミットしなければいけず、少々煩雑です。一方 Firebase Hosting は CLI ツールも CI から叩かれる前提に設計にされており、任意のヘッダも付けられるのでこちらにしました。

Firebase Hosting の CLI ツールは JavaScript で書かれているため、Node が含まれた Docker コンテナで動作させる場合の相性も良いです。

Firebase Hosting の開発 Tips

任意のヘッダの付与

以下のように firebase.json という設定ファイルへ記載すると、Cache-Control などの任意のヘッダを付与することができます。

{
  "hosting": {
    "public": "public",
    "headers": [
      {
        "source": "/blog/",
        "headers" : [
          {
            "key": "Cache-Control",
            "value": "public, max-age=10, stale-while-revalidate=60"
          }
        ]
      }
    ]
  }
}

source の部分にはパターンが指定できるので、/blog/**/*.@(png|jpg) のように拡張子で対象にすることもできます。

まとめ

この記事では、この開発者ブログの技術要素について説明をしてきました。

  • Pelican は Python 製の使いやすい静的サイトジェネレーター
  • Pelican は公式ドキュメントが充実しており、Python に慣れた人にとってはカスタマイズが容易
  • AMP 対応を考えている場合、Pelican は不向き

静的サイトジェネレーターには、他にもさまざまな選択肢があるので、あなたにあった静的サイトジェネレーターをぜひ探してみてください。 絵文字ジェネレーター、および開発者ブログを今後ともどうぞよろしくお願いいたします!