こんにちは〜。チーフエンジニアの
id:cockscombです。
最近、同僚の
id:yashigani_wとともに、はてなのコーポレートサイトを静的サイトとして再構築しました。ちょっとした仕事ではありますが、経験によって得られた暗黙知を形式知へ昇華するため、ここに紹介します。
弊社のコーポレートサイトは一般的なそれと同様に、株式会社としての「はてな」に関心を持っていただいた方々とコミュニケーションするための窓口です。会社情報を提供したり、あるいはプレスリリースなどの情報を公開したり、またあるいは採用に関する窓口になっています。必然的に社内でも関係する部署が多く、会社の規模の拡大とともに運用を改善するインセンティブが高まっていました。
これまでコーポレートサイトは、普通のWebアプリケーションとして構築されていました。Webアプリケーションといってもそれほどアプリケーションしておらず、テンプレートエンジンでHTMLを作って返すくらいのものです。作られた頃の背景などを考えると、リーズナブルな選択だったように思われます。
ここでいう静的化とは、Webサーバーがリクエストに応答する際に「単にファイルを返せばよい」とすることです。単にファイルを返せばよいということは、Webアプリケーションが動的な処理をする必要がなくなり、なんなら大半のリクエストにCDNだけで応答できるということです。
CDNでレスポンスを返せるので、Round-Trip Timeは短くなり、また突発的なアクセスの増加に対しても堅牢です。放っておいても壊れにくいのは大きなポイントです。
一方で、静的化による制約もあります。当然のことながら動的にコンテンツが増えても対応できません。あるいは、User-Agentに応じて異なるページを返すようなことも、ふつうはできません。
最近では、サーバーサイドで静的ファイルを生成する手法も出てきました。これならコンテンツが動的に増えても、最初のリクエストで静的ファイルが作られるので問題ありません。二度目のリクエストからは静的ファイルとして応答できます。Webアプリケーションと静的サイトのいいとこ取りになっています。
とはいえ我々のコーポレートサイトでは、動的にページが増えるようなことはないため、単に静的化するのが適していると考えられます。ページを増やしたり書き換えるのは我々なので、そのたびにデプロイすればよいはずです。
それでは具体的にどのような作業を行ったかを振り返ってみます。
静的サイトを作るため、既存のページを静的サイトジェネレータに移植します。テンプレートエンジンからの移植になりますが、ページ数的には十分に現実的です。ただし、今後もほとんど変更されないであろうページは、単にHTMLとして取り込みました。
静的サイトジェネレータにはNext.jsを選びました。我々はReactのエコシステムに慣れていて、Next.jsを使った案件も経験しており、社内にもノウハウが蓄積されています。
Next.jsは、今回利用する静的サイトへのエクスポート(Static HTML Export)にも、Server Side Rendering(SSR)にも対応しています。さらに、前述したような「サーバーサイドで静的ファイルを生成する手法」にあたるStatic Site Generation(SSG)のIncremental Static Generation機能も持っています。
Next.jsひとつでさまざまな状況に対応でき、非常に便利です。
先に述べたようにコーポレートサイトは関係する部署が多いため、運用を改善する必要があります。運用負荷を低減するために、真っ先に思いつくのは自動化です。
継続的デプロイを志向して、Gitでメインブランチにマージしたら自動的にプロダクション環境へ反映されるようにできるとよさそうです。合わせてGitHubの機能を使って、メインブランチを保護ブランチに指定し、権限を制約します。
さらに、GitHubのプルリクエストひとつひとつにプレビュー環境が作成されると便利そうです。関係者が複数の場合には、合意を取りながら作業を進めやすく、マージ可能かどうかの判断もできます。特に現在のような、リモートワークを前提とした働き方の中では、URLを共有するだけで状況を伝えられるのは便利です。
静的サイトをホスティングするには、いろいろな方法があります。nginxで配信してもいいし、S3に置いてCloudFrontを通してもよいでしょう。
しかし最近では、静的サイトやSingle-Page Application(SPA)のためのホスティングサービスがよく使われています。そのうち以下の3つを検討しました。
結論から書くと、我々はAWS Amplify Consoleを選択しました。
Gitと連携した継続的デプロイ(上記2.を参照)について、AWS Amplify Consoleでは(Netlifyでも)よくサポートされています。ブランチごと、あるいはプルリクエストごとにプレビュー環境を作ることも容易です。プレビュー環境へのアクセス制限もでき、リダイレクトの柔軟さもあります。
NetlifyとAmplify Consoleはどちらも要件を満たせそうでしたが、普段からAWSを使っているため、Amplify Consoleを選びました。AWSのサポートプランにも加入しているので、いざというときも安心です。
Firebase Hostingは、BaaSとしてFirebaseとの連携はよいものの、プレビュー環境を作るのが容易ではなく、候補から外れました。
コーポレートサイトでは、いろいろな新着情報を動的に表示する箇所があります。一部はJSON API経由で取得して表示し、また一部ははてなブログMediaをサブディレクトリで運用しています。
JSON APIは、AWS Amplifyを使えば簡単に用意できます。これはAmplifyの面白いところで、GraphQLのスキーマを書いておくと、それをもとにLambdaやDynamoDBを(内部的にCloudFormationを使って)プロビジョニングできます。これによって、LambdaをバックエンドとしたGraphQL APIをとても簡単に作れます。
またAmplify Consoleのリダイレクト機能には、リダイレクトだけでなくプロキシとして動作させるモードがあります。これを使って、特定のパスを別にデプロイしたnginxへプロキシして、はてなブログMediaを活用しています。
2020年5月中頃、DNSの設定を切り替えて、静的化されたコーポレートサイトをリリースしました。
静的化によって、内容はそのままに表示速度が改善されたはずです。CDNで配信しているのに加えて、Next.jsの機能で2ページ目以降はClient-Side Renderingされるため、プリフェッチを行っていることも含めて、ほとんど一瞬で(DOMの書き換えのみで)遷移が終わります。
ただ残念なことに、リリース後しばらく検索していたのですが「はてなのコーポレートサイト、表示が速くなった?」といったツイートは見つけられませんでした。
その後、インターンシップの募集開始や採用サイトのリニューアルがありましたが、運用の改善という当初の目論見もある程度は達成されています。自動化する際に考えるべきことが少なくなり、リリースも簡単でした。
最後に、私とともに尽力したid:yashigani_wからのメッセージをお読みください。
「テンプレートエンジンでうまいことコンポーネント化できてなかったレガシーサイトも、Reactでチョチョイのチョイやで!
静的サイトエクスポートのおかげでアプリケーションとの密結合も解消されたし、いいことづくしやからみんなも試してみてな!」
余談ですが、静的化に際して調査を行った中で、特にホスティングサービスに関してはそれぞれに特色があり、興味深く思いました。
BaaSとしては、GraphQLのような汎用的なインターフェース上に構築を試みるAmplifyを好ましく思う一方で、良くも悪くも玄人好みな感じもあります。SDKに寄せているFirebaseの方が、多くの人にとって取っつきやすいかもしれません。
あるいは、Netlifyのような比較的新興のプラットフォームは静的サイトのようなユースケースに特化して、使いやすさの点でリードしているようにも思います。
いずれにせよ、ホスティングサービスが自分たちのニーズを満たしてくれるかどうかが肝要です。
ということで、速くなったコーポレートサイトをお楽しみください。内容も新しくなったエンジニア採用ページはこちらです。
id:koudenpa
id:hatenatech
id:yunagi_n
id:motemen
id:rskmm0chang
id:lufiabb
id:daiksy
id:onk
id:yigarashi
id:cohalz
id:tokizuoh
id:kk__777
id:s-shiro
id:yutailang0119
id:fxwx23
id:CNaan
id:walnuts1018
id:todays_mitsui
id:MysticDoll
id:taxintt
id:kouki_dan
id:vilagia
id:masayoshi
id:gano-k
id:handat
id:KashEight
id:r4wxii
id:cateiru
id:matsudamper
id:mangano-ito
id:gurrium
id:k1s1eee
id:chaya2z
id:onishi
id:heleeen
id:cockscomb
id:masayosu
id:Furutsuki
id:hagihala
id:maku693
id:blog-media
id:d-haru
id:kmuto
id:arthur-1
id:nabe1216
id:tkzwtks
id:momochi29
id:mizdra
id:KGA
id:miki_bene
id:astj
id:nanto_vi
id:do-su-0805
id:nakiwo
id:wtatsuru
id:missasan
id:hogashi
id:polamjag
id:susisu
id:hitode909
id:Pasta-K
id:chris4403
id:yashigani_w
id:ueday
id:pokutuna引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。