https://www.netlify.com/pdf/oreilly-modern-web-development-on-the-jamstack.pdf
Netlify社が2019年に公開した本?PDFです。
せっかくJamstackの会社に入ったので、読んでおかないといけない気がして。
あとJamstackは人によって解釈が違ったりするとし、Jamstackの真髄について知っておきたいですよね?と思い。
ただこれなんと127ページもあるんですよね〜。
全編もちろん英語なので、読むのも中々に大変ですよね〜。
てなわけで、ざっくり訳してまとめまておきました。(それでも長いけど)
はじめに
- ここ最近のWebの進化はすさまじい
- ブラウザもJavaScriptもパワフルになった
- その分ユーザーの要求も増える
- やることが増えると処理は遅くなる
- 遅いページは見向きもされないモバイル当たり前の世界だ
- Googleですら、表示速度を検索アルゴリズムに反映する
- そこで紹介したいのがJAMstack(今はJamstack表記)
- より高速でスケールでき、負荷耐性もあって安全
- モノリシックなシステムに疲弊したことあるよね
- Jamstackの意義がわかるはず
- 開発者だけでなく、ユーザーにもメリットがある
- いわゆるサーバーはいらない
- HTMLは事前に生成され、CDNに配置される
- APIはサーバーではなく専用のマイクロサービスで提供される
- 新しいアイデアに懐疑的な人もいるだろう
- 今までもWebにはいろんなアイデアがあった
- しかしそれらのアイデアは、また新たな目標を生み出してきた
- たとえばWordpressでコンテンツを生み出せるようになった
- しかしパフォーマンスとセキュリティの問題が生まれた
- それを回避するためのキャッシュの指定に奮闘した
- それによるパイプラインの肥大化が起きた
- 以下ループ
- そういう過去を踏まえ、簡素化して再構築したのがJamstackである
JAMとは
- JavaScript / API / Markup
- Jamstackとは
- 特定の技術を指す言葉ではない
- いわばムーブメントである
- いろいろなガイドラインがあり、その中で選択をすることになる
- ブラウザのためのJavaScriptは必須になるが、それ以外の言語も使える
- いろいろなOSSを選択できる
- 新しいアイデアもあれば、古いものもある
- 最近になってやっと選べるようになった技術もある
開発効率とパフォーマンス
- Jamstackなサイトとは
- 世界中にデプロイされ、負荷耐性がある
- Gitベースで開発者フレンドリー
- APIによってサービスを利用する
- 事前にビルドされ、最適化されている
- 最後の項目が特に重要
- クライアントが動的に画面を組み上げるべきではないし
- サーバーもリクエストの度にコンテンツを生成すべきではない
- Jamstackたるもの
- コードとコンテンツは同じリポジトリで管理され
- そこに変更があればビルドが実行され、HTMLや他のデータを生成する
- それらはCDNで、ユーザーに最も近いところへ配置される
- こうすることでネットワークのレイテンシを極限まで下げられる
- リポジトリに対する変更にはじまり、CDNのファイル更新に終わる
- この流れが新しいポイント
- キャッシュのパージを待つ必要もない
バージョン管理によるアトミックなデプロイ
- Jamstackでは、コンテンツすらもGitで管理する
- つまりデータベースが不要になる
- この考え方で、常にデプロイがアトミックなものになる
- Gitのブランチを切り替えれば、それがステージングになる
- そういう意味では、もはや静的ではなく、日に数百回も更新される
Jamstackをやっていく
- ワークフローに参加するのはエンジニアだけではない
- Gitに詳しくない人もいるだろう
- そのためのCMSなどもある
- Gitを意識することなく、コンテンツの作成や修正ができる
- ビルドのタイミングでそれを自動化することもできる
- 特定のソースから情報を取ってくるなど
- 全てを巻き込むことで、すべての履歴を管理できるようになる
- 必要な単位・タイミングで、Gitのリポジトリに入れればいい
API
- 単なるHTMLよりも複雑なことをやる場合に必要
- 従来はセッションを保持して、サーバーが状態を保っていた
- これらは、専用のマイクロサービスによって代替される
- 支払いはStripe
- 検索はAlgolia
- etc...
- すべてを1回呼びきりで副作用のないAPIにする
- スケーラブルにするため
- とはいえ認証などはあるだろう
- JWTを作成して、ブラウザに保存するとよい
Smashing Magazineのケース
- 歴史あるWebマガジン
- 2017年の末、Jamstackに移行した
- 後の章でそれを解説する
では本編へ
- Jamstackを知るためには、実際にやってみるのがよい
- 特別なスキルは必要なく、既にできることの組み合わせである
- ただしサーバーなしで
- 最初にWebに触れたあの頃を思い出してほしい
- いかに負担をかけずにコンテンツを制作するか
- それが魅力あるコンテンツ、UI作りにつながる
1. モダンWeb開発への挑戦
モノリスアーキテクチャ
- 誰もが通った歴史
- しかしサーバーがクライアントの形まで決めてしまう
- 分離されていないので不必要に複雑化していく
- 現世においては、オーバーエンジニアリングなところすらある
失われた柔軟性
- ここ数年、パフォーマンスへの認識は高まっている
- しかしモノリスなままでは、これ以上の改善がむずかしい
- 開発ワークフローなどにも制限を課されてしまう
パフォーマンスへの関心
- パフォーマンスとコンバージョンには関連がある
- 1秒遅いと売上が7%下がる
- モノリシックなサーバーが、リクエストごとにHTMLを生成するから遅い
- スピードだけがパフォーマンスの指標ではないけども
- キャッシュはパフォーマンスにとって重要
- しかしモノリシックなアプリの場合は、単一のURLから複数のコンテンツが返ることもある
- ログイン状態などにより
- これもユーザーが予測できるようになっているべき
スケールの問題
- リクエストのたびにページを生成すると、予期せぬトラフィック増大の問題がある
- 生成コストの問題だけがあるわけではない
- サイトのためのコンテンツを生成する要件と、サイトをホスティングする要件が混ざっているのも問題
- コンテンツ管理者も、一般の閲覧者も、インフラがいっしょくたになる
- コンテンツに対するREADとWRITEは、分離されているべき
- 性質が違うから
- ここを切り離せないと、スケールできない
セキュリティの問題
- モノリシックなアーキテクチャを、全否定したいわけではない
- ただし、セキュリティの問題があるならばそれを推奨はできない
- Webの29%は、WordpressなどのCMSでできているという報告がある
- それだけに、攻撃者の標的にもなってしまっている
- ホスティングしなければならないゆえに、攻撃手法の選択肢も豊富である
- プラグインなどで機能を拡張できるようになっているが、そこも悪意を向けられやすい構造
- つい最近も、1200万ものDurupalを使ったサイトに脆弱性のパッチが充てられる事案があった
現状維持のリスク
- 変化よりも現状維持のほうが、得てして簡単である
- その変化のためにあらゆるリソースが必要になるから
- しかし今こそ、変化の時がきたのだと思っている
- 現状維持のほうがコスパが悪くなるラインがきたということ
- 既に前に進む選択をしている人たちもいる
- ブログやOSSのドキュメント、イベント告知ページなど
- この流れがやがて大きくなって、ECやサブスク系にも広がっていくはず
- オンライン人口がこれからもずっと増えていく
- そのために、トラフィック増大にも耐えられ
- あらゆるネットワークでも快適に閲覧でき
- あらゆる言語、デバイスで利用できる
- セキュリティも万全な
- そんなWebを、Jamstackで作りたい
2. Jamstackとは
その名の由来
- 既にあるものを組み合わせて名付けただけ
- 名前というものは便利
- その背景にあるトレンドとなっている技術たち
- 静的サイトジェネレータ
- SPA
- ビルドツール
- 用途に特化したAPIのエコシステム
- PWA
- etc...
- J: ブラウザのランタイムとしてのJavaScript
- A: データベースを意識しない、個別のHTTPのAPI
- M: 事前にビルド済のHTML
JavaScript
- 1995年に10日ぽっちで作られたとは思えないほど成熟した
- ほかの言語からコンパイルされることもあるほどに
- ランタイムのVMとして、Jamstackでも利用する
- ブラウザはもはやWebにおけるOSである
API
- もともとのJavaScriptには、外部のサーバーとやり取りする機能はなかった
- それが今やCORS、WebSocketなどなど
- OAuth2やJWTなど認証の仕組みまである
- APIは、Webの可能性をそのまま広げる
- 今やだいたいのものはAPIでまかなえる
- これを利用しない手はないという意味で、Jamstackでも利用する
Markup
- HTMLはもともと文書を読むためのものだった
- いうなればWebサイトは、ただのフォルダだった
- やがて、データベースの情報を取得して返すものになった
- しかしこれが遅い
- 静的なサイトに比べると複雑になる
- けど、あの頃はそうするしかなかった
- しかし今は違う
- JavaScriptもあるし、クロスドメインでAPIも利用できる
事前ビルド
- リクエストを受けてからビルドするのではない
- 事前にビルドしておいて、CDNに配置しておく
- これにより、フロントエンドとバックエンドを分離できる
- バックエンドに縛られない = パフォーマンスもUXも改善できる
- この考え方自体は、ネイティブアプリの開発と同じかも
マイクロサービス、サーバーレス関数
- フロントエンドとバックエンドの分離を推し進めた要因
- バックエンドの開発者たちも、時を同じくしてモノリシックなアーキテクチャを見直した
- SPAも単一のエンドポイントではなく、複数のAPIを利用するようになった
- そのため、OAuthやJWTのような画一的な認可認証のフローが必要にもなった
Jamstackのプロジェクト
- 世界中で様々なタイプのプロジェクトがデプロイされてる
- 数ページのものから、数千ページのものまで
- その種類について見ていく
HTMLコンテンツ
- これももちろんJamstackといえる
- バージョン管理下において、テキストを修正するのみ
- 少し大きな規模になるなら、ビルドツールなどを使えばよい
- コードを変更し、それをプッシュすると、CI/CDで配信される
CMS
- Headless CMSの隆盛
- CMSもGitベースのものがでてきている
- APIからデータを利用するためのものへ
Webアプリ
- XHRやAjaxからその流れは加速した
- 新たな概念を生み出したという意味で、Jamstackにも親しい
- ページを読み込まずにデータが取得できるというのは革命的だった
- ここ数年であらゆる認識を変えた
*** フロントエンドの分離
- JavaScriptのパフォーマンスが向上したので、SPAができるようになった
- しかし元来のSPAは、単にデータベースを利用するモノリシックなものだった
- RailsでEmberを埋め込んで返すような
- モダンなSPAは、そこが分離している
- もはやバックエンドがいらない
巨大なサイト
- 事前にビルドする際の問題は、そのページ数
- ビルド時間をいかに短くするかが問題
- サイトによっては、10分すらも待てないことがある
- ビルドツールの高速化も進んではいるが・・
- Jamstackを採用できるかどうかは、このサイクルをどう捉えるか
- コンテンツの公開スケジュールに縛られない場合は、多少はマシ
- 数千ではなく、数百万くらいのページがある場合のみ、要検討になると考えている
ハイブリッド
- サイト、ブログ、アプリなど、様々なユースケースがある
- 従来のアーキテクチャでは、フロントエンドはバックエンドと密結合していた
- Jamstackなら、そのどんなケースにも対応できる
- バックエンドに縛られないから
まとめ
- Jamstackがハマらないプロジェクトは、そんなに多くないはず
- ツールやエコシステムが成熟していくにつれて、もっと広まっていくはず
3. Jamstackの優位性
物事を単純化する
- 数多の抽象化によって、我々はいろんなことができる
- しかし抽象化は時に危うい
- 我々の手が届かないところで何が起きているかわからない
よりよい解釈とメンタルモデル
- コンテンツをサーバーサイドで生成するのではなく事前にビルドする
- これによって、ランタイムの環境のことを考えなくてよくなる
- 少なくとも、同時には考えなくてよい
- メンタルモデルがクリアになり、何にフォーカスすべきかが明らかになる
すべてに通ずる必要がない
- フロントエンドとバックエンドを分離することで、分業もできる
- すべてを1人で覚える必要がなくなる
- 環境変数ひとつで任意のAPIを使うことができる
- 開発用・ステージング・本番環境
- APIとしても、用途が明確で再利用性も高い
ランタイムの負担を減らす
- ランタイムで実行するコードも、事前にビルドできる
- 全てではないが
- 何も実行しない = 最速であり、最適化である
- ランタイムでやることが減るということは、障害点も減るということ
- ビルド時のデバッグは、誰にも悪い影響を与えない
- 本当に避けられないもの以外は、サーバーサイドで動的に実行するべきではない
コスト
- コストにはいろんな面がある
- そしてそれはアーキテクチャに大きく依存する
おカネ
- これはトラフィックの量によって決まることが多い
- たくさんのトラフィックを捌くために、たくさんのリソースが必要になる
- それを計算して、インフラを組む必要があった
- そしてそのインフラが複数必要になる(開発・テスト・本番など)
- Jamstackでは、トラフィックの問題はCDNが保証する
チーム効率
- 複雑なスタックは、高度な技術力や人材を要する
- シンプルに作れば、属人化も回避できるし、少ない人数で済む
- CDNの設定は、インフラの設定+デプロイよりも簡単
- 求められるスキルのレベルも下げられるし、一層注力もできる
- この敷居を下げることがイノベーションには重要
イノベーションの価値
- Jamstackでは、小さなパーツをゆるくつなげていく
- パーツはいつでも交換可能である
- リファクタもできるし入れ替えることもできる
- もちろんこれに反して、がっちり接続することもできる
- しかしそれは避けるべき方向性
- 加えた変更による影響が、ビルド時に確認できるのもよい
- ランタイムに影響がない(ことも確認できる)
- トータルで、あらゆるコストが下がるのである
スケールさせるために
- スケールについてもっと詳しく
- どれだけ緻密に見積もっても、サイトのトラフィックは突然跳ねる
- それに備えて監視したり、スケールできるように構成することをしていたはず
- これがそもそもコストであり、好ましい状態ではないということ
そのための静的サイト
- スケールさせるためには、やはりキャッシュが重要
- 動的なサイトは、スケールさせるために、後から静的なキャッシングレイヤーを追加する
- CDNを動的に扱う仕組みを用意するはず
- Jamstackの場合は、そもそもCDNが前提になってる
地理の問題
- トラフィックの量だけでなく、どこからのアクセスかも問題になる
- CDNはもとよりこの問題を解決しようとするもの
- よいCDN事業者は、世界中にノードを持っているはず
冗長性
- CDNにデプロイすることで、冗長性を確保できる
- ビルドとデプロイを分けて考えられるメリットでもある
パフォーマンス
- 開発者なら誰でも、パフォーマンスを気にする必要がある
- ネットワークの信頼性の違いや、回線速度なども
- デバイスの違いについても
- パフォーマンスの指標がある
- FirstMeaningfulPaint / TimeToInteractive
- WPO: WebPerformanceOptimizaion
- https://wpostats.com/
- たくさんの事例が公開されてる
どこから改善するか
- 一昔前は、パフォーマンスのすべてはサーバーサイドにあった
- しかし状況が変わり、フロントエンドでも数々の最適化が成されている
- HTTPリクエストの数を減らす
- メディアの最適化
- etc...
- ブラウザの進歩も相まって改善が見られる
- しかしサーバーサイドはどうか
- 最適化の余地はいたるところにある
- しかし、従来のサーバー構成だと、全てを改善する必要がある
- すべての要素がつながっているから
- Jamstackであれば、そのつながりを断ち、短くすることはできる
- それぞれのエリアで、それぞれが最適化を行うから
- ともあれ、コードを書かないことが最速であることに変わりはない
- サーバーサイドで動的にコンテンツをレンダリングするより
- 事前にビルドされたものをCDNから返すほうが当然早い
必要なものを最適化しておく
- 事前ビルドするからこそ、CDNから即レスポンスができる
- そしてこうすると、ビルド時、デプロイ時のエラーに悩むことはない
- それはユーザーには気付かれないので
セキュリティ
- Jamstackによってシンプルな構成になると、セキュリティも強固になるおまけがつく
露出する面を減らす
- この考え方はセキュリティを考えるときも重要
- 攻撃できる箇所を減らすことができる
- Jamstackに則りスタックをシンプルにするだけで、それが達せられる
読み取り専用にする
- 例えばWordpressは、POSTリクエストで受け付けたデータをデータベースに格納する
- ここが攻撃者の標的によくなる
- もちろん対策は講じられるが、常に勝利し続けることは容易でない
- Jamstackであれば、そもそも標的にならない
- そんな口がないから
- Jamstackでも、もちろん他の仕組みと連携することはできる
- しかしそこでも確固たる手法により、安全にやり取りできる
外部サービス
- Jamstackでも動的にしたい部分はある
- そういうときにどうするか
*** 複雑性を切り出す
- JamstackのAは、APIのAである
- 外部サービスは、APIを介して利用する
- 注意点はベンダー選び
- as a Serive市場は急成長している
- 自分たちのビジネスを任せられるかどうかがポイント
- ともあれAPIに分離しておくことがセキュリティ向上にもつながる
*** 抽象化して切り離す
- APIでまかなえない要件もあるはず
- 外部サービスが要求を満たせない場合
- 外部に預けたくないデータの場合など
- そういう場合は自作するしかない
- それでもJamstackでビルドとデプロイを分けることでメリットがある
- 重要なのはビルドプロセスからのみアクセスできるようにしておくこと
- Jamstackの原則
- 基本的に読み取り専用で露出する面を減らす
- ビルド環境からのみサービスは利用し、外からは見えないようにしておく
- 独立して使える分離されたセキュアなサービスを利用する
どうやって勝つか
- 複雑性を減らすことの意義について学んだ
- 基本的には受け入れられるべき方針だが、もしかしたら開発効率が気になるかもしれない
- 開発効率や開発体験は重要である
- それによってプロジェクトの質も左右される
- 長期的に見ればなおさら
- しかし開発者体験よりも、ユーザー体験のほうが重要
- 開発体験もそこまで悪化しないと思っている
- 馴染みのツールを組み合わせて使うだけなので
- Jamstackは、どちらの体験も良くすることができるはず
4. Jamstackをはじめる
- Jamstackをはじめるにあたってのガイド
- どのようにプロジェクトとコードを管理するか
- どこにコンテンツを保存するか
- なにを使ってビルドするか
- ビルドプロセスの自動化
- どこにサイトを公開するか
- どんなAPIやサービスを使うか
プロジェクトのセットアップ
- サーバーもデータベースも不要な場合、Jamstackはただのフォルダである
- しかしコンテンツはGitで管理されているほうがよい
- たとえ個人プロジェクトだとしても
コンテンツマネジメント
- コンテンツがないとサイトは成り立たない
- そんなコンテンツをどのように管理するか
テキストファイル
- 最も単純なやり方
- ディレクトリを用意すればそれがURLのパスになる
- 中身はHTMLでもいいが、Markdownなどがよく使われる
- HTMLをそのまま書くのは大変なので
- YAMLのメタデータを埋めることもできる
- サイトジェネレータで、汎用レイアウトに埋めたりする
GitベースCMS
- Gitに不慣れな人がコンテンツを制作することもあるはず
- その場合は、それを意識させないCMSがあるとよい
- これで開発者とコンテンツ編集者が同じGitフローに乗れる
- 代表的な例
- Netlify CMS
- Forestry.io
ヘッドレスCMS
- 専用のサービスで、リッチなエディタがついてたりする
- APIでコンテンツを取得し、表示は自分たちでやる
- ビルドのときにそのAPIを利用してサイトを生成する
- 代表的な例
- Contentful
CMSを自分でホストする
- 既存のWordpressなどをそのままバックエンドにする
- 最近のCMSはそれ用のAPIが生えてたりもする
- コンテンツの制作フローはそのままに、セキュリティを向上できる
サイトジェネレータ
- コンテンツを実際のHTMLに変換するところ
- サイトジェネレータはいろいろある
- 昔はJekyll一強だったが今はたくさんある
- https://www.staticgen.com/
- その中からどれを選ぶかが重要
- 選ぶポイントを見ていく
1: サイトの種類
- どんなサイト向けのジェネレータかが暗黙的に決まってる
- ブログ向け、ドキュメントサイト向け、アプリ向けなど
- それぞれのサンプルを実際に触ってみて判断するとよい
2: プログラミング言語
- だいたいのジェネレータは、その書かれた言語を知らずとも利用できる
- ただし独自のプラグインを書きたいなどの場合は、その知識が必要になる
3: テンプレートのシンタックス
- Jamstackにおける開発のほとんどは、テンプレートの構築になるはず
- なのでそのシンタックスが開発効率に関わる
- ここは好みが分かれるところのはず
- Jade, Pug, Handlebars, Liquid, etc...
4: ビルドの速度
- コンテンツの変更は即ビルドにつなげたい
- Go製のHugoは、その速さで人気を博している
- クライアントで動くJavaScriptのバンドルを用意する場合などは、どうしても時間がかかる
5: 開発ツール
- サイトはHTMLだけでは作れない
- CSSや画像やJavaScriptのファイルも必要
- それらは圧縮やコンパイルなどの最適化が必要
- ジェネレータによっては、そこまでカバーしているものもある
- ビルドの過程でLintをかけてくれるものもある
- 事前にエラー検知できるので重要
- ローカルで変更をプレビューできることも重要なポイント
- これらを踏まえた上で、自分たちにとってのベストを選ぶ必要がある
- パソコン選びのようなもので、正解はない
自動化する
- Jamstackにおいて、ビルドの自動化は必須
- ビルドして自分でデプロイすることも不可能ではない
- ただすぐにスケールしないことに気づく
- GitのリポジトリからWebHookでビルドを実行するのがよい
- GitHubやGitLabやBitbucketなどにはある
- Gitのpushに対応して、ビルドプロセスを起動する
GiHub PagesやGitLab Pages
- GitHubやGitLabを使っているなら、そのままホスティングもできる
- カスタムドメインも使える
- GitHub Pagesは、もとはOSSのドキュメントを置くためのものだった
- ジェネレータとしては限定されたJekyllしか使えない
- GitLab Pagesは、いろんなジェネレータが使える
- 独自のCIパイプラインでデプロイもできる
自分たちのインフラを使う
- もちろん自分たちのサーバー、VMでビルドしてもよい
- CI/CDに慣れてるなら、Jamstackのビルドは容易い
ホスティングサービスを使う
- AWSなどでもよいが、一時的なビルド環境を用意して、そこでビルドさせる
- 各ビルドごとに依存関係はそれ用のファイルにまとまっているはず
- `Gemfile`や`package.json`など
CDNを選ぶ
- どれだけ技術が進歩しても、時間と空間はまだ克服できない
- コンテンツの提供時間は、ビルドを事前に済ませることでほぼ0にできる
- しかしコンテンツとの空間的な距離は埋められない
- そこで、全世界に配置されたCDNを使う
- CDN事業者の選び方を3つ紹介する
エッジのロケーション
- サイトの利用者に近いところで提供しているものを選ぶ
- いわゆる事業者なら、少なくとも10くらいの地域にはエッジがあるはず
即時アップデート
- CDNのキャッシュは、TTLによって制限される
- これが長いと、コンテンツを更新しても、反映されない
- これをどう調整するかの戦略を決める必要があった
- ただし最近のCDNには、即時アップデートできる仕組みがある
- キャッシュをミリ秒の速さでパージできる
- この機能があれば、TTLの設定を調整する必要はない
マルチクラウド
- 特定のクラウド事業者がダウンすることが稀にある
- 特定の地域のネットワークが不通になることもある
- これらの備えた事業者のほうがよいケースもあるかも
APIを提供する
- あらゆるものがAPIで提供される昨今
- しかし、それらのAPIが本当に信頼できるのかは悩み
- ただこれは、自作する場合でも同じ悩みになる
- ただ言えるのは、APIドリブンのアーキテクチャや開発手法のほうが柔軟であるということ
- たとえばECサイト
- まず負荷への体制だがこれは問題ない
- そして画面レイアウトなどの柔軟性は、従来のECパッケージを使ったサイトとは比較にならない
- しかしECサイトは動的である
- 検索やセール情報、在庫の確保など
- これらはすべて専用のコンポーネントでまかなう
- ECサイトのビルドの例
- CMSを更新すると、ビルドがはじまる
- 同時に、検索エンジンへメタデータを送る
- ECサイトのランタイムで動くJavaScript
- 検索フィールドに文字を入れたら検索APIをたたく
- カートに商品を入れたら、購入用のAPIをたたく
- etc...
- このように、APIを組み合わせてサイトを形成していく
5. メンタルモデルの移行
マインドセットとアプローチ
- Jamstackでプロジェクトを進めるためには、まずそれが何かを知る必要がある
- 静的にサイトを配信すればそれはJamstackだ、というのは間違い
- 間違いというか、端折りすぎた説明
- 真に静的なサイトには、動的な部分は存在しない
- Jamstackでできたサイトには、動的な部分も組み込める
- そして、それを支えるビルドの自動化や、外部APIやサービスまでがセット
- ベストプラクティスを6つ紹介する
1. CDNを使う
- CDNを利用することで得られるメリット
- 1. 物理的に近いサーバーからコンテンツを配信できてパフォーマンスがよくなる
- 2. 冗長化が自動的に成される
- 3. 自分たちのインフラがSPoFにならない
- 突然のトラフィック増にも、CDN事業者が対応してくれる
- これを自分たちでやるのはすごく大変
- そして動的なコンテンツの場合は、さらに難易度が上がる
CDNを使い倒すには
- UIは事前に静的に作っておき、クライアントのJSで動的にするパターンがよい
- 静的にプリレンダリングしておける部分は、多いほどよい
- Webアプリを設計するときに用いられる方法はだいたい3つ
- しばしば混同されるので、区別しておくべき
- 1. クライアントサイドレンダリング
- ブラウザがJSでDOMを構築するパターン(いわゆるSPA)
- しかしそこにはエラーの可能性を秘めている
- できれば事前にレンダリングしておくほうがよい
- 2. サーバーレンダリング
- リクエストに応じて、コンテンツを生成するパターン
- 3. プリレンダリング
- サーバーレンダリングを、リクエストがくる前にやっておくパターン
2. イミュータブルでアトミックにデプロイ
- 事前に静的なコンテンツを用意することで、改善速度もあげられる
- ただしそのためには、たくさんの素材を、すばやくデプロイできなければならない
- リリースサイクルをいかに効率的にするかが、コンテンツが増えると課題になる
イミュータブルにする
- たとえばFTPでサーバーにアップロードする場合を考える
- アップロードの途中で、サーバーの状態が変わることになる(ミュータブル)
- 何か問題があったときに、切り戻しもできない
- 過去のどこで問題が起きたのかもわからない
- よって、変更とデプロイを同じ単位で考える必要がある
イミュータブルにデプロイする利点
- バージョン管理システムの、特定の瞬間を切り出す
- 何にも依存しない、それだけで完結する塊
- それこそがイミュータブルの正体で、それをそのままデプロイする
*** デプロイしたものを把握する
- つまり、何がデプロイされるのかを把握すること
- 何がその環境に配置され、ユーザーに届けられるのか
*** いつでもバージョン切り替え
- ちょっとした変更でも、すべてビルドをやり直す
- 無駄にも思えるが、これが重要
- これにより、ビルドされたものの信頼性があがる
- ビルドプロセスが正常に機能していることの証明でもある
- いつでもロールバックできる
- ランタイムにエラーが発生しても、その影響を最小限にできる
イミュータブルなデプロイの位置づけ
- Jamstackに移行することは、イミュータブルなデプロイを行うに等しい
- ビルド時に利用するデータと、その後で利用するAPIも分離されてる
- これらの性質があるからこそ、イミュータブルなデプロイが可能になるとも言える
アトミックなデプロイ
- イミュータブルに生成されたコンテンツは、アトミックにデプロイされる
- アトミックなデプロイとは、すべてをまとめてデプロイすること
- 変更した一部分の差分だけを更新するのではなく
- CDNはファイルの差分をハッシュなどでチェックでき、無駄なファイル転送を省いてくれる
- Jamstackは、こうした前提に基づいて、あらゆる複雜性を排除するデザイン
3. すべてをバージョン管理する
- コードベースの管理に、Gitを採用することは一般的
- だがその考え方をデプロイにまで活かすことは、あまりされていない
- Herokuはそれをやっている例で、人気を博した
- デプロイまでバージョン管理下に置くことで、不慮の事故を防ぐことができる
- ステートフルなデータベースがあるとそれも難しいが、Jamstackにデータベースは存在しない
オンボーディングのコストを減らす
- バージョン管理されていれば、新人が歴史をたどることもできる
- 新人が仕事をはじめるまでの理想の流れはこう
- コードへのアクセス権をもらう
- `README`などを読む
- ローカルにコードをクローンする
- 依存関係を1つのコマンドでダウンロード
- 1つのコマンドでビルドして確認
- 1つのコマンドで任意の環境にデプロイ
- `README`のほか、`package.json`なども昨今は便利な道標になる
バージョン管理は背骨
- すべてをバージョン管理することが受け入れられたのは最近のこと
- Gitの概念が大衆化したのも最近のこと
- ブランチ、コミット、プルリクエストなど
- いわゆるモノリシックなアーキテクチャで、Gitがやってることを実装してきたのも事実
- すべてをバージョン管理するのではなく、特別なワークフローに載せる
- ただこれは学習コストがかかる
- CI/CDツールも、最近は基本的にGitによってトリガーされる
- それができるCI/CDサービスを選ぶべき
4. 自動化とツール
- 単なる静的なサイトでなく、ビルドとデプロイの自動化までやるのがJamstackのカギ
- 静的サイトジェネレータを使うことは、そもそもその自動化への第一歩でもある
サイト生成のパターン
- ビルドを自動化するためのツールは、好きに選んでよい
- GulpでもWebpackでも、BashスクリプトやMakeでもよい
- 一般的なJamstackのサイト生成の手順はこの通り
- APIを使って外部からデータを集める
- サイトジェネレータでそのデータを正規化
- 正規化したデータを使ってページを生成
- アセットの最適化など
- テストの実行
- デプロイ
- この一連の流れを、どこからでも実行できるようにしておく
- Webhookなどを使って実行することが多い
リスク削減のために人間にやらせない
- 自動化することで、人間のミスを減らすこともできる
- 繰り返しの処理を、後から自動化することはよくある
- ただし後からではなく早い段階でやってしまうことをおすすめする
5. エコシステムを利用する
- 価値があり、利用できるツールは、どんどん利用していく
- 便利なツールやサービスが急速に生まれている
- Jamstackを、単なる静的なサイトのためのアプローチと言えなくなりつつある
- これらのツールやサービスを利用することで、機能追加し動的にできるから
- そんなサービスを、Jamstackのプロジェクトで採用するためのポイントを紹介する
フォーム
- これのために、静的にサイトを作ることはできないとよく思われている
- しかしそんなことはない
- 実は内容をHTTPでリクエストするだけでいい
- それができるサービスはたくさんある
- Wufoo, Formkeep, Formcarry, Google Forms, ...etc
検索
- 簡単な検索であれば問題にならない
- ただ、場合によっては複雜になり得る
- 静的サイトジェネレータは、出力フォーマットを変えられるはず
- それで検索用のインデックスを生成して、検索用のサービスに送る
- そしてそれを、クライアントのJSで索引すればよい
- それだけでなく、付加価値を提供するサービスもある
- Algoliaなど
- そのほか、GoogleやDuck Duck Goで、サイト内検索を提供することもできる
- JSが使えない環境でも利用できるし、実装コストも軽い
通知
- ユーザーに連絡するためには、しばしば複雜な仕組みが必要
- できればこれもサービスでなんとかしたい
- メール
- SendGrid, Mailgun, ...etc
- Twilioは、SMSや音声通話などもAPIで提供している
認証
- 認証と認可をどうするか
- 特に個人情報を扱うためには、細心の注意が必要
- しかし場合によってはアウトソースできない事情もあるはず
- ただベストプラクティスが確立されつつある
- IDベースで認証や認可をする仕組みを使う
- これにより直接ユーザーの個人情報を管理する必要がなくなる
- そんなOAuthのサービスとしては、Auth0などがある
6. FaaSで最後の穴を埋める
- これまで見てきたように、たくさんのサービスがある
- 基本的にそれらを再実装する必要はない
- とは言え、どうしても実装する必要がある場合もある
- そこで使えるのが、Functionのサービス
- サーバーレス関数とも
脱モノリスするために
- Web開発者は、その速度を落とすことなく学び続けなければならない
- ただ、すべてのことに精通する必要はない
- 外部サービスを利用するということは、責任を預けるということ
- 最初はそれが落ち着かないかもしれない
- ただし、そうすることで得られるものも多い
- 安心感、開放感なども
- 彼らも素人ではない
- 自分たちの領域にその分だけ集中できる
- どの領域を3rdパーティのサービスに任せるかは、一考の余地がある
- 利用規約は適切か
- サービスレベルの保証はあるか
- 金銭的なコスト、実装のコストはどうなるか
- ただ基本的な考え方として、事業者もビジネスでやっていて、常に進歩している
- 自分たちで実装するよりも、任せてしまうほうが、往々にして良い結果になる
- 次の章では、それを実践した経験について触れる
6. Jamstackを適用する
ケーススタディ: Smashing Magazineの場合
- Smashing Magazineは歴史あるフロントエンドの情報サイト
- 2017年の末、Jamstackに移行した
- この章は、そのショーケースである
チャレンジ
- サイトは10年も運用されていた
- 数千もの記事、20万のコメントがあるWordpressの本体
- 本やワークショップを販売するShopifyのEC
- Ruby on Railsでできたジョブボード
- カンファレンス用のPHPのスタック
- ビジネスの成長にあわせて様々な機能が追加されてきた
- しかし、もうこれ以上は厳しいという段階だった
キーポイント
- パフォーマンスと安定性、セキュリティを改善したい
- 同時に、メンバーシップの概念を導入したかった
重複するテンプレート
- WordpressやRailsなど、技術スタックごとにテンプレが分散していた
- 内容は同じだが、分散せざるを得ない状況だった
- これらをまとめあげる必要があった
パフォーマンス
- 同様に、パフォーマンスへの取り組みも分散していた
- 各言語ごとのライブラリやモジュールとして
- 管理する対象が多くて、セキュリティ的な不安もあった
メンバーシップ
- 新たな機能であり、リデザインのモチベーションでもあった
- メンバーシップによって得られる機能
- 広告無し
- ディスカウント
- 相応のUI表示など
ツール選び
- それらを実現するため、選ばれたのはJamstackでした
- そのコアとなる静的サイトジェネレータや、フロントエンドのフレームワークを選ぶ
静的サイトジェネレータ
- 生成するページは、様々なタイプがある
- 記事、イベント、著者、カテゴリ、などなど
- RSSを生成する必要もあった
- あとはページ数がとにかく多い
- GatsbyやReactStaticを検討した
- ページ数が増えるとパフォーマンスが悪かった
- パフォーマンスを考慮した結果、Hugoに決めた
- 数千ページを数秒で生成できた
- インクリメンタルビルドができるJS製のジェネレータより、Hugoが速かった
- これを書いている時点では
- 事前にビルドするものを限定し、あとはクライアントで生成するという手もあった
- しかしコンテンツの性質上、事前ビルドは必須の要件だった
アセット管理
- ブラウザに最適化されたアセットを届ける必要がある
- だいたいの静的サイトジェネレータは、その機能を含んでいたりする
- しかしHugoにはない
- 必要だったのは、JSのバンドル、コード分割、SCSSのコンパイル
- webpackを検討した
- JavaScriptの扱いは十分だった
- しかしCSSのそれは、静的サイトというよりSPA向けの機能だった
- HTML/CSSに長けたメンバーがいたので、その開発体験は損ねたくなかった
- 結果、Victor Hugoというフレームワークを自作することにした
- https://github.com/netlify-templates/victor-hugo
- GulpでSassとHugoをコンパイルし、JSはwebpackで更新する
フロントエンドのフレームワーク
- 静的なサイトの場合は、プログレッシブ・エンハンスメントでよいと思うかもしれない
- そういうページも多いが、そうでもないページもある
- 機能ごとにJSを書いてもいいが、UIコンポーネントごとにそれを管理したい
- SPAでもないので、バンドルするよりも個別に管理したい
- そういうところには、Preactを採用した
- ECのチェックアウトやログインなど
コンテンツの移行
- まずは既存コンテンツをエクスポートすることからはじめた
- いろんなデータベースに別個に入っていたものを、テキストファイルとしてディレクトリに整理
- WordpressからHugoへ移行するためのちょっとしたツールを使った
- RSSフィードを使ったツールも
- コンテンツの移行は、それぞれのプロジェクトごとにやり方を変える必要がある
コンテンツの構造
- それぞれの記事はテキストファイル
- YAMLのfrontmatterがあって、本文が続く
- 10年分の記事はかなりの数があるが、それでもGitで管理できる
- Lunuxカーネルなんかに比べれば小さい
- しかし画像などのアセットはGitに入れられなかった
- サイズが大きすぎて、cloneするのが大変
- それらは新しいCDNに置き、パスなどの構造だけをGitで管理するようにした
- 軽量なサイトを作るためには、これがベストな選択
大規模サイトであるがゆえ
- Victor Hugoに移行してからの開発で問題になったことが1つある
- テンプレを変更した際の自動リビルドが遅いこと
- それを回避するため、Gulpを導入してスクリプト化した
- 作業時には、すべてではなく100くらいのビルド済の記事で構成されるサイトとした
- こうすることで、ローカルでの開発にHugoで毎回ビルドする必要がなくなった
コアを作る
- だいたいのスケルトンができてきたので、コア部分を作ることにした
- 頻出パターンなど、サイトのテーマのメイン部分のこと
- テンプレで使えるパーシャルなど
- 事前にビルドするがゆえに、全記事をイテレーションして抽出するなどの処理もできる
- ランタイムでこれをやると、パフォーマンスがすごく悪い
- Hugoはテンプレのフィルタが優秀で助かった
検索
- 検索は事前にビルドできない
- ここではSaaSとしてのAlgoliaを利用した
- 競合としては、Lunrがあった
- こっちはインデックスを生成して、静的なファイルとして利用できる
- ただ大規模サイトには向かないと判断
- ビルドの過程で、Algoliaへインデックスを送信するようになっている
- フロントエンドはPreactとReduxで構成
- `data`属性がついた要素に、Preactのアプリを描画する
- JavaScriptが使えない環境では、Googleのサイト内検索にフォールバック
コンテンツ管理
- Jamstackでのコンテンツ管理は、いろいろなやり方がある
- しかし中でも、ヘッドレスCMSの利用が革新的
- https://headlesscms.org/
- 種類としては、APIベースのCMSと、GitベースのCMSに大別される
- APIベース
- 専用のGUIによってコンテンツの編集をするもの
- 事前にコンテンツの型やリレーションを定義する
- ContentfulやDato CMS、GraphCMSなどが有名
- Gitベース
- シンプルにGitHubなどの連携するもの
- Netlify CMSやCloudcannon、Forestryなどが有名
- APIベースの利点は、さまざまなクライアントに、さまざまなデータを返せること
- ただし、データの整形の手間が必要で、事前ビルドの時間が増える
- Gitベースの利点は、ディレクトリがそのままソースになるところ
- 普段使っているツールやスクリプトがそのまま使える
- Smashing Magazineでは、Netlify CMSを選んだ
- ビルドの時間が短くなる
- 普段使いのエディタでコードのようにコンテンツを編集できるのがよい
Netlify CMS
- 従来のCMSは、CMSを使ってサイトを構成するというものだった
- Netlify CMSでは、サイトがCMSを使える(データだけを引き出す)
- 特定のディレクトリに、同じ種類のコンテンツを格納し、コレクションとして扱う
- コンテンツを作成する画面は、ReactでできたSPAになっている
- プレビュー画面などは、プラグインで機能を拡張できる
- 自分でコンポーネントを用意すれば、独自のデザインでのプレビューも可能
大規模サイトのためのCMS
- NetlifyはGitHubのAPIを使ってコンテンツを取得する
- ただし大規模サイトにおいては、画像などのアセットは同様に管理しないほうがよい
- そのような場合のに備えて、Algoliaとの連携や、CDNへのファイルアップロードができる
- Netlify CMSでは、この拡張性によって、Gitベースでありながら大規模サイトにも対応できた
ユーザーとロール
- モノリシックな構成の場合、ユーザーの認証ももちろん組み込まれているはず
- Smashing Magazineでも、複数のサイト向けに別々のユーザーの概念があった
JWTを使ったステートレスな認証
- 従来の認証は、セッションIDをCookieに含めて利用することが多かった
- それによりサーバーでステートフルにユーザーを管理していた
- ステートレスな認証フローでは、代わりにユーザーの情報を送信する
- これにより、各サービス側が、独自に必要な情報をチェックして認証することができる
- このユーザー情報をどのように画一的に扱うかが問題
- そのために存在するのがJWTである
- 特定のシークレットによって署名されたトークンを使う
- `exp`プロパティで、期限を設定できるが、これが短めになっている
- そのために、リフレッシュトークンを別途用意するのが一般的
- Auth0がこの領域には詳しく、説明用のサイトもある
*** APIゲートウェイとトークン
- 各サービスがトークンを検証するということは、検証機能を各サービスが持つということ
- 検証用の設定値などが分散することで、セキュリティリスクになるかもしれない
- 公開鍵と秘密鍵の組み合わせで署名を作る方法で、そのリスクは低減できる
- クライアントとのゲートウェイとして、単一のエンドポイントを用意するという手もある
*** GoTrueによる認証
- Smashing Magazineでは、GoTrueというOSSのマイクロサービスを使った
- https://github.com/netlify/gotrue
- `/signup`などのエンドポイントが立てられる
- クライアントではJavaScriptからこのSDKを利用する
- ログインユーザーは`LocalStorage`に保存した
EC
- Smashing Magazineの収入を支えるのがEC
- これまではShopifyを使っていたが、今回の移行で、GoCommerceというOSSを作った
- https://github.com/netlify/gocommerce
- 商品カタログのパスを渡して購入状態を作るAPIと、その購入に対する決済トークンを用意するAPI
- 決済方法はPayPalかStripeから選べる
- ビルド時に商品のカタログをHugoで生成し、それを利用する
- Preactのフロントエンドでカート情報を保持しつつ、裏でそのAPIを叩く
- 購買フローを自由に設計できるのが強み
- この仕組みはデモのリポジトリもある
ユーザーと注文
- GoCommerceも、他のサービスと同じで、JWTによって認証できる
- これによって、認証済のユーザーのみが購買できる
- 購買履歴を返すこともできる
メンバーシップとサブスク
- Smashing Magazineには、3段階の購読プランがある
- それによって、今まで紹介してきた各機能が協調するようになる
- そのための橋渡しをするために、AWSのLambda関数がある
- 購読のタイミングで、ユーザーを認証する
- そしてメールの購読サービスに登録し、Slackに通知
- Stripeで定期購読するよう設定する
- 最後に、認証サービスのユーザー情報も更新する
- これらの処理はシークレットの問題で、クライアントサイドではできない
GoCommerceでメンバー割引
- GoCommerceにも、JWTトークンのメタデータとして、購読プランを送るようにする
- GoCommerce側では、割引設定が書かれた設定ファイルを見ている
- それによって、金額の割引が可能になる
- GoCommerceと、割引設定はあくまで疎結合であることが重要
ジョブボードとチケット
- ジョブボードも、メンバー割引と似たようなイメージでGoCommerceで対応した
- 仕事を投稿する用の商品をGoCommerceで購入する
- すると、WebHookにリクエストが発行されて、ジョブボードを更新する
- WebHookはAWSのLambdaで受けるようになってて、それによってMarkdownファイルがGitHubにコミットされる
- そしてそれが新たなビルドのきっかけとなり、ジョブボードのページが更新される
- 同様の仕組みで、イベントチケットを購入した参加者一覧は、Spreadsheetに転載されるようになってる
ワークフローとAPIゲートウェイ
- このケーススタディで学んだこと
- 事前ビルドされたフロントエンド
- いくつものサービスを疎結合で利用する
- サービス間の取りまとめには、サーバーレス関数を使う
- これがJamstackでサイトを構成することの強みである
- そしてこれを支えるのが、確立されたワークフローとメンテナブルな構成要素
- サーバーレス関数での取りまとめは、ワークフローのためには必須であるともいえる
- NetlifyのCDNは、エッジでJWTを検証できる
- そこで検証をパスしたら、各サービス用のトークンをスワップしたりもできる
- フロントエンドと同様に、各サービスも一緒にデプロイされるワークフローが、自動化されているというのが重要
サービスのデプロイと運用
- それまではHerokuにすべてをデプロイし、DBもそこに1つだけあった
- この構成に変えてからは、各サービスがそれぞれのデータを抱える
- マイクロサービスの連携において、共通のDBを持つことは推奨されない
- それぞれのサービスを橋渡しするサービスも、最終的にAWSのLambda関数になった
- それより大きな規模であったり、永続化層が必要な場合は、Kubernetesなどに移行するのがよさそうである
まとめ
- 静的なフロントエンドと、そこから複数のサービスを利用する構成へ移行した
- 自分たちで書いたサーバーサイドのコードは、たったの575行(3つのLambda関数)だった
- アクセス急増に備える必要もなくなり、本来のサービスと向き合う時間が増えた
- GitベースのCMSもうまくマッチしていて、日々の運用でもスクリプトで簡単にファイルを処理できる
- 急ごしらえのOSSにはまだ粗があるけど、これからに十分に期待できる
7. まとめ
最後にもう1つ
- Jamstackの利点だけを並べて説明するのは容易い
- しかしそそれ以上に、大事な哲学があることに触れておきたい
- Jamstackは、真にWebのためのものである
- iPhoneが世に出たあとで、ジョブズはFlashをHTML5に置き換えた
- そのときに、Adobeに向けた手紙を書いている
- だいたいこんな内容
- Flashは現在ひろく普及しているが、あれはAdobeでしか動かない
- Adobe専用のものになってしまっている
- AppleもOSなど専用なものは作っているが、Webはオープンであるべき
- なのでFlashは、HTML5やその代替技術で置き換えられるべき
- Webは誰の手も借りずに、コンテンツを公開できる唯一の場所
- しかしそこで十分な品質やUXを提供できない場合
- またFlashの二の舞になってしまう可能性がある
- GoogleのAMPなどの取り組みもあるが、あるべき姿ではない
- Webの競争力を、健全なWebを保つために、過去の問題を解決しつつ、前に進む
- そのためのベストプラクティス、ガイドラインであり制約であるのが、このJamstackである
現場からは以上です!
ちなみに、Jamstackな会社のサイトでもわかりやすく説明されてたりするので、よければそちらもどうぞ。(宣伝)