ghost(CMS)からHugo + Firebase Hostingに乗り換えた
Posted Jul 23, 2023 | Updated Dec 23, 2023
かねてからサーバーのコストダウンを進めていました。が、RDBを使うCMSを使い続ける限りサーバー費用をゼロにすることはほぼ不可能だと感じていました。GCE(Google Cpmpute Engine)で動かしていたghost(CMS)からHugo+Firebase Hostingに乗り換えるまでの記録です。
GCEで6年以上動いていたghost(CMS)
残っている限り、ブログを最初に書いたのは2011年3月からのようです。
"[Python][Google App Engine]為替レートを取得、保存する"
当時はSEとして働いていたため、プログラミングの勉強も兼ねてシステムトレードのプログラムや便利ツールを作ったりした記録をメモしていました。Pythonの実行環境やNoSQL、タスクキューなどが一体になっていたGoogle App Engineにのめり込んでいたのを覚えています。
そして、ちょうど6年後の2017年3月にghostに乗り換えたことになります。当時から自分で稼働させるブログといえばWordpressが主流だった気がしますが、どこで見つけてきたのかghostを採用しました。
Google Compute Engineの無償枠を使って気が向いた時にメモをする用途で構築し、そこから少しずつUpdateを繰り返して使ってきました。
ホストOSにインストールしたnginxからDockerコンテナで構築したghostにリクエストを転送する構成でした。稼働当初はSSLは使っていませんでしたが、Let’s Encryptoが登場して今後はSSLが標準となる、という話を小耳にはさんで設定したりもしました。
技術のことを書いたり、食事の内容を書いたり、投資の方針を書いたりしているうちにコロナの流行期になり気付けばghostも稼働から6年が経ちました。
趣味としてのITコストを見直す
2023年5月に独立し、会社を設立したのを機に趣味としてのITコストを見直すことにしました。Google Cloudで動いているプロジェクトをどんどん削除していきます。
なぜGoogle Cloudなのかは覚えていません。AWSやAzureの方が人気なようですが、個人的な感覚として、どちらも各サービスのネーミングがあまり好きではありません。
まずはFXの自動売買プロジェクト。2011年にブログを書き始めた当初からまさか12年間も同じテーマを続けているとは思いませんでした(笑)GCEで価格フィードを取得し、Pub/Sub、Cloud Functionsを経由してBigQueryへの蓄積やトレードプログラムに流す仕組みです。
次にFlutterで一儲けしようとした時の残骸であるFirebaseプロジェクト2つくらいを消します。これもお金と時間をかけましたが、甘かった。ひっそりとサービス停止です。アプリプラットフォーム(Apple、Google)の審査が面倒くさく、リリース頻度を高く保ちたい事業化初期にアプリリリースを目指すのは悪手と理解しました。
Wireguardを使って作ったVPN用のサーバーや実験で使ったプロジェクトも削除し、そして最後に残ったのがghostのサーバーです。
ghostをCloudRunで動かせばいいんじゃない?
bot以外は誰も訪れず、リクエストがほとんどないサービスやブログに常時稼働サーバーを使うなどというのはサーバーレスの時代にはそぐわない構成です。
サーバーはリクエストを処理することが仕事であって、リクエストを待つのは仕事ではありません。Cloud FunctionsをはじめとするFaaS(Function as a Service)が個人的に好きな理由でもあります。
こんな考えからWebアプリ全体をリクエストベースで起動できるCloud Runには注目していました。RailsのアプリやNext.js(だっけ?)を動かす記事を読んだりもしました。
前から存在は知っていて、使う機会のなかったCloud Run、今回のGCEインスタンス削除にも使えるのではないか?ghostをそのままCloud Runに載せれば良いのでは?と思ったのです。幸いghostはすでにコンテナで動いています。やったろ。
Cloud Run + Litestreamでやってみる
ghostはSQLiteを使っており、インスタンスはf1-micro 1台で動いています。これをCloud Run用に書き換えていきます。
Cloud Runはコンテナが起動してリクエストを処理して、その後削除されるためこのままでは記事を作成しても永続化は難しいだろうと考えました。Cloud SQLと繋ぐ選択肢もありますが、インスタンスを減らすのが目的なので、Cloud SQLでサーバーを追加するのは本末転倒です。
そこでSQLiteの同期ツールであるLitestreamを使うことにしました。GCS(Google Cloud Storage)をバックアップ用にしてコンテナ起動時にrestore(復元)し、継続的にreplicate(バックアップ)します。
やったこと
あまり詳しくない自分でもできたので本職のエンジニアにとっては簡単なことなのでしょう。
- ghostを80番ポートで動かす
- ghost公式のDockerイメージにLitestreamをインストールしたコンテナイメージを作成
- ghost公式のDockerイメージのdocker-entrypoint.shにLitestreamの起動コマンド追加
- Cloud Buildでコンテナイメージの自動ビルドを仕込む
- 出来上がったサービスに独自ドメインを紐づける
結果
結論から言えば失敗でした。
- 約18〜200秒かかってghostが起動
- その間「もう少しで準備できるから待ってね」のページが表示され続ける
- GoogleほかさまざまなbotがURLを叩きに来る
- 最後のリクエスト処理後15分でようやくコンテナ停止
ほとんど(人間による)アクセスのないブログのコストを下げるのが目的なのに、見にきた人が記事を読めないまま離脱、botのためにリソースを使い、金を払う。そんな状態になり兼ねません。
Hugoで静的サイトにしてみる
JAMStackというのは動的サイトを静的サイトにする技術(?)と理解していますが、詳しい定義は知りません。Cloud Runでダメならこれしかない。
いくつか調べてみると日本語の情報はGatsbyやNext.jsなどが多いようでした。腰を据えてプログラミングする余裕があればNext.jsに挑戦してみたいのですが、挫折しそうな気がしたのでサクッといけそうなHugoにしました。
今回はrocinanteというテーマを使っています。非常にシンプルでスマホで見た時のUIも自分の好みでした。
ありがたいことにghostのデータからHugo用のデータに変換してくれるツールも提供されていましたので、こちらで既存のghostから6年間の成果を移行します。
静的サイトのホストサービスにはFirebase Hostingを採用しました。何よりもGoogle Cloudですし、Github Actionsとの統合が楽という記事も見かけたからです。
やったこと
- ghostからコンテンツをエクスポート
- ghostToHugoでエクスポートしたコンテンツからHugoプロジェクト作成
- テーマ「rocinante」を適用
- ローカルでテスト
- プロジェクトにFirebase設定追加(firebase init)
- Github Actions Workflowを作成(firebase initの続き)
- Githubにコミット
- Github Actionsのエラー解消
- 独自ドメインの設定
結果
- GCEインスタンス停止・削除で7ドルくらい削減
- 記事のリポジトリ管理
- 静的サイト化で読み込み早い
- ghostでhostしていた記事内の画像リンク切れ
- ghost記事間のリンク切れ
移行としてはダメな部分もありますが、自分だけが見返すブログだと思えばリンク切れは許せそうです。今後使っていく中で気になるところは直すかもしれません。
振り返り
今回のghost(CMS)からHugoへの移行は、本業なら1.5人日かけて月7ドル削減てどういうことすか?優先順位おかしくないすか?と自分でも言いたくなる内容でした。疲れました。
今回のghostからHugoへの移行、実は2023年5月に設立した会社の採用に向けた情報発信の検討の一環でもありました。会社のパーパス、ミッション、バリューなどを発信するためにブログを始めようと考えています。
Wantedlyさんやその他の採用メディアは素晴らしい機能やプレゼンスがあるのですが、いかんせんコストが高いので現時点では始められませんし、サーバー運用を意識せず、記事の作成に集中できる環境を探していました。
個人ブログを題材にして、上記のようなブログプラットフォームの選定をやっていたというわけです。普段使っているVisual Studio Codeが使えるので記事の内容に集中でき、Firebase Hostingでサーバーを意識せず使えます。これでなんとか始められそうです。
個人のブログも短くても書いていかないとな。
久しぶりにいろいろ調べながらやったので今日はよく寝られそうです。明日はジムに行ってから出勤です。それではおやすみなさい。
- 朝食:The City Bakery Bistro Rubin
- 昼食:鶏もも肉に片栗粉をまぶしてカリカリに焼いたやつ
- 夕食:タラのアクアパッツァ(ミニトマト、ズッキーニ、きのこ、シーフードMix)