注意: この記事はLLMによって英語から翻訳されたものです。正確性については保証いたしかねますので、あらかじめご了承ください。英語の原文はこちら。
このブログは開設以来、専用サーバー上でGhostを使って運営してきました。しかし、次第に不満が増えてきたため、変更の時期だと判断し、オープンソースの静的サイトジェネレーターであるHugoを試すことにしました。
この記事では、なぜ移行したのか、そしてブログをホスティングするためにAWS上でS3とCloudFrontを(Terraformを使って)どのようにセットアップしたかを説明します。
なぜ移行したのか?#
Ghostには気になる点がいくつかありました。その一部を挙げると:
- メンテナンスされているテーマやモジュールの不足(例:AWSイメージホスティングモジュールは何年もコミットがない。Ghost導入時に購入してインストールしたテーマは更新されなくなり、すべてが壊れてしまったため使用を中止せざるを得なかった、など)
- ある時点で、公式テーマのCasperを少し修正して使い始めたが、テーマを更新するたびにこれらの修正を再適用する必要があった
- メディア管理システムが良くなく、エディタも使い心地が良くない(特にインデントされたリストなどのシンプルなMarkdownを書く場合)
- ページの表示が遅かった
- システム管理の観点から、Ghostは独自のユーザーで動作しているにもかかわらず、インストールディレクトリの権限が少なくとも
chmod 755に設定されていないと警告を出す(最後の5は不要であり、また、私はJS系の技術に対して強い嫌悪感がある)
決定打となったのは、ブログを新しいサーバーに移行しようとした時でした。エクスポートにはすべてが含まれておらず、Node.jsをアップグレードした後にGhostが正常に動作しなくなりました(修正できたはずですが、この時点で、これらの小さな問題の積み重ねで別のものを試したくなりました)。誤解のないように言うと、Ghostは今でもかなり良いプラットフォームだと思いますが、私のやりたいことに対して現時点で最適なツールではないと考えています。
そこから、WordPressを検討しました。インストールとメンテナンスが簡単で、多くのモジュールやテーマが利用可能です。しかし、RSSフィードでWordPress関連のCVEをしょっちゅう目にします。また、このプラットフォームは速度面でも知られていません。
以前からHugoというオープンソースの静的サイトジェネレーターについて聞いており、Ghostの非静的機能をほとんど活用していないことを知っていたので、試してみることにしました。Ghostと比較して:
- より高速(もちろん、サイトが静的なので当然)
- ホスティングが容易(まず静的ウェブサイトであること、次にHugoがAWS、Azure、Cloudflareなどのさまざまなサービスに自動的にデプロイできること)、そしてコストも低い
- メンテナンスがほぼ不要(サーバーの定期更新の心配が不要、バックアップも簡単、など)
- ウェブサイトとテーマのカスタマイズが容易(ただし、テーマの選択肢はGhostとそれほど変わらない)
- もちろん、サイトが静的になることで利便性は一部失われる(例:読者にコメント機能を提供したい場合、Disqusなどを使用する必要がある)
AWSのセットアップ#
先ほど述べたように、私のHugoブログはAWS上でS3とCloudFrontを使ってホスティングされています。すべてTerraformで構成され、Hugoの統合デプロイ機能を使ってプッシュされます。ブログをオンラインにするために行った手順を見ていきましょう。
AWS Certificate Manager#
最初のステップは、ACMを使ってixonae.comとwww.ixonae.comの証明書を作成することでした。基本的なDNS検証方法とRSA 2048を使用しました。その後、所有権を証明するために適切なCNAMEエントリをDNS設定に追加しました。Terraformからも実行可能でしたが、DNSプロバイダーがAWSではないため、(検証ステップがあるので)手動で証明書を生成する方が簡単だと判断しました。
CloudFormationで使用するには、証明書をus-east-1リージョンで作成する必要があることに注意してください。
TerraformによるS3とCloudFrontのセットアップ#
設定は100行以上あるため、ここには貼り付けませんが、GitHubで確認できます。この設定が行うことは以下の通りです:
example-websiteという名前のバケットを作成- ファイルの変更履歴を30日間保持し、その後は最新の変更のみを保持
- アクセスは非公開に設定し、コンテンツはCloudFrontを通じてのみリクエスト可能
- バケット暗号化キーを使用(「バケットキーはAWS KMSへの呼び出しを削減することで暗号化コストを下げます」)
- CloudFrontディストリビューションを作成
- 先ほど作成したS3バケットをデータソースとして使用
- IPv6を許可
- キャッシュポリシーを
Managed-CachingOptimizedに設定(AWS推奨) - TLSv1.2_2021を使用
- HTTP/2をサポート(HTTP/1はデフォルト、HTTP/3も追加可能)
- ハードコードされたARN IDでSSL証明書を設定し、代替ドメイン名を追加
- オブジェクトを自動的に圧縮
- HTTPをHTTPSにリダイレクト
- GETとHEADのHTTPメソッドを許可
- ビューワーリクエストに適用されるCloudFront関数を設定(デフォルトではクライアントは
ixonae.com/blog/のようなURLにアクセスするが、CloudFrontがixonae.com/blog/index.htmlにアクセスするようにする。クライアントには「きれいな」URLのみが表示される)
example-website-logsというS3バケットを作成し、CloudFrontがこのバケットのlogsディレクトリにログを書き込むように設定- CloudFrontディストリビューションが
example-websiteS3バケットにアクセスできるようにするS3ポリシーを作成して適用
この設定を再利用する場合、以下を変更する必要があります:
- バケット名はユニークでなければならず、
example-websiteは既に誰かが使用している可能性が非常に高い - リージョンを選択する必要がある(現在は
xx-xxxx-xxに設定) - 前のステップで作成したACM証明書のARNで
acm_certificate_arnを設定し、aliasesを適切な値に置き換える必要がある
このTerraformを適用した後、残る作業はDNSレコードにCNAMEエントリを設定して、CloudFrontが生成したディストリビューションドメイン名(例:abcd.cloudfront.net)を指すようにすることだけです。
Hugoの設定#
Hugoの設定は非常に簡単です。設定ファイルに以下を含めるだけです(ただし、他にも多くの最適化オプションがあります)。
[deployment.targets]
name = "mydeployment"
url = "s3://example-website?region=xx-xxxx-x"
cloudFrontDistributionID = "xxxxxxxx"デプロイは以下のコマンドで行えます(適切な認証情報が含まれた~/.aws/credentialsファイルがすでにある場合)。
hugo # To compile the website
hugo deploy # To upload everything and invalidate the CloudFront cacheまとめと今後の課題#
ブログはHugoとAWSを使って公開され、サーバーのメンテナンスを気にしたり、障害発生時にウェブサイトを素早く復旧させることを心配する必要がなくなりました。また、PageSpeed Insightsでのスコアも100/100を達成しました。

次に、いくつかのワークフロー改善を計画しています:
- gitリポジトリのmasterブランチにプッシュされた際に、本番環境のAWSスタックへのデプロイをトリガーする
- CI/CDパイプラインにcronジョブを追加して、Hugoが定期的にウェブサイトのコンパイルとデプロイを試みるようにする(
dateが未来に設定された記事が、適切なタイミングで自動的にプッシュされるように) - devブランチに同様の操作が行われた場合、認証が必要なテスト環境へのデプロイをトリガーする
- AWSの設定を小改善する(ログの監視とストレージの改善、クロスリージョンレプリケーション、など)
クレジット#
- カバー写真: Glenn Carstens-Peters(Unsplash)