ADR-0005: Cloudflare Workers Static Assets を採用し、基盤を Terraform、deploy を Wrangler が管理する¶
Status¶
承認
Context¶
- このサイトは完全に静的で、実行時のサーバーコンポーネントを持たない。 Worker はハンドラを持たず、静的アセットだけを配信する。
- Cloudflare は、Pages から Workers Static Assets への移行を推奨の方法にしている。
wrangler pages deployの失敗メッセージも "Workers are the recommended way to deploy all new projects" と Workers を勧める。 - 別のプロジェクトでは、稼働中の Pages を続け、Workers への移行を後に回した。 このプロジェクトは新規なので、その分岐を経ずに始められる。
- 同じプロジェクトでは、infra code を別のリポジトリへ置くと決めた。
3 か月の間
.tfが 1 つも書かれず、後の判断で monorepo へ集め直した。 分離は、そこへ変更を書き込む頻度と担当があって初めて機能する。 - 運用者は 1 名なので、レビューによる職務の分離が成り立たない。
- Terraform の provider
cloudflare/cloudflareの latest は 5.24.0 で、認証はCLOUDFLARE_API_TOKENを使う。 - provider 5.11.0 以降は、
cloudflare_workers_scriptのassets = { directory = ... }で静的アセットを Terraform から配れる。
Decision¶
1. Cloudflare Workers Static Assets を採用する¶
Pages を採らない。
Workers を選ぶと、Pages にある次の 2 つの問題が最初からなくなる。
| Pages の問題 | Workers での状況 |
|---|---|
wrangler pages deploy は project を自動では作らず、非対話では UserError で失敗する。初回に運用者が wrangler pages project create を手で実行する必要がある |
wrangler deploy が Worker を作るので、手で事前に作る手順が要らない |
cloudflare_pages_project の deployment_configs が secret と binding を内包し、Terraform の管理と Wrangler による secret の投入が衝突する |
Worker script を Terraform で管理しないので、衝突しない |
将来 /api/* にハンドラを足すときは、同じ Worker にハンドラを足すだけで済む。
Pages では、Pages Functions という別の仕組みを経由する。
2. infra code をこのリポジトリに置く¶
infra/ をリポジトリのルートの直下に置き、packages/ に入れない。
packages/ は app から import される場所で、infra はどこからも import されないため。
modules/を作らない。- Cloudflare Terraform Best Practices は、モジュールを避け、使うとしても控えめにすると定める。
- 対象は 1 アカウント、1 zone、1 サイトで、括り出す重複がない。
- 同じ形が 2 箇所以上に実在するまで作らない。
- 環境をディレクトリで分けない。 staging を持たない。
3. Terraform と Wrangler で管轄を分ける¶
基盤の設定は Terraform が、アプリの deploy と runtime の設定は Wrangler が管理する。1 つのリソースを両方から管理しない。
管轄を判断できない新しいリソースは、次の順に判定する。
- データを保持するなら、Terraform にする。 消えると戻せないリソースを、deploy のたびに動く workflow から切り離す。
- deploy のたびに変わるなら、Wrangler にする。
- 変わらない場合、その変更が本番の可用性かドメインへの到達に影響するなら、Terraform にする。
- どれにも当たらなければ、Terraform をデフォルトにする。
| リソース | 管轄 |
|---|---|
| Zone、DNS レコード、TLS | Terraform |
Workers custom domain(cloudflare_workers_custom_domain) |
Terraform |
| Terraform の state 用の R2 バケット | Terraform(infra/bootstrap/) |
| Worker script と静的アセット | Wrangler |
| deployment | Wrangler |
| runtime secret | Wrangler |
レスポンスヘッダ(_headers) |
Wrangler(配信物の一部) |
| binding とデータ本体(D1、KV、R2) | binding は Wrangler、データ本体は Terraform |
cloudflare_workers_script を使わない。
使うと deploy が Terraform を経由し、apply の権限が日常の CI へ降りる。
4. runtime secret を Terraform の state に載せない¶
値は wrangler secret put で投入し、手で起動する特権の workflow か、運用者の手元での実行に限る。
Terraform は secret の名前だけを扱う。
5. credential を 2 系統に分ける¶
- 通常の deploy workflow は、push を起点に自動で動き、deploy の範囲に絞った token だけを持つ。
- 本番の Worker(
fukuemon-dev)の deploy workflow は、本番を新しいサイトに切り替えるまで、手で起動したときだけ動く。この間も、deploy の範囲に絞った token だけを持つ。切り替えまでの扱いは、ADR-0031 の 6 が決める。
- 本番の Worker(
- apply workflow は、手で起動し(
workflow_dispatch)、GitHub environment の protection rules を通し、apply 用の token を持つ。
1 名の体制でも意味を持つ分離は 2 つある。
apply が自動では動かず、手で起動するという明示的な行為を伴うこと。
通常の CI が apply 用の credential を持たないこと。
レビューによる職務の分離は成り立たないので、CODEOWNERS を設定しない。
Verification¶
cloudflare_workers_custom_domainの必須の項目は、account_id、hostname、serviceの 3 つだった。environmentは Optional で、Deprecated になっていた。- provider 5.4.0 で報告された、Wrangler で assets 付きの Worker を deploy すると
environmentの不一致で 404 になる問題は、この schema の変更で前提がなくなった。
serviceは Worker の名前を文字列で参照する。- Pages の
cloudflare_pages_domain.project_nameと同じ構造なので、script を Terraform で管理せずに custom domain だけを管理できる。
- Pages の
- 初回の apply で、
cloudflare_workers_custom_domainが Wrangler で deploy した Worker に紐付いた。- apply の後、
https://fukuemon.devが Workerfukuemon-devを配信した。
- apply の後、
- R2 backend は、
skip_s3_checksumを含む設定で state を読み書きでき、use_lockfileのロックも取れた。- S3 互換のキーは、R2 の画面で発行したものに限る。通常の API Tokens の画面で発行した token では、署名が一致しない。
Considered Options¶
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: Workers Static Assets を使い、基盤を Terraform、deploy と runtime の設定を Wrangler が管理し、infra code をこのリポジトリに置く | Cloudflare の推奨の方法。Astro は Workers 上で GA で、wrangler.jsonc を使う。state に機密が入らない。日常の CI が apply の権限を持たない |
管理ツールが 2 つになる。初回に Wrangler の deploy から Terraform の apply への順序の制約がある |
すべてを Terraform で管理する(cloudflare_workers_script を使う) |
管理ツールが 1 つに揃う。すべての変更が terraform plan の差分に出る |
deploy のたびに Terraform を動かすことになり、apply の権限が日常の CI に降り、credential を分ける目的を損なう。Worker の binding と secret を Terraform が持ち、state が機密になる。provider 5.11.0 の assets への対応には、既知の不具合の報告がある |
| すべてを Wrangler と Dashboard で管理する | 初期のセットアップが最も速い。Terraform の学習と state の運用が要らない | DNS と custom domain がコードで管理されず、変更の履歴、レビュー、再現性を失う。成功条件「公開に関わる Cloudflare 設定が Dashboard 上の属人設定として残らない」を満たさない |
| Cloudflare Pages を使う | 静的サイトの標準的な選択肢で、情報が多い。廃止の告知はない | Decision の「1. Cloudflare Workers Static Assets を採用する」の表にある 2 つの問題を最初から抱える。将来 /api/* を足すとき Pages Functions という別の仕組みが要る。Pages Functions は Secrets Store に対応しない。Cloudflare 自身が新規には Workers を勧める |
| infra code を別のリポジトリへ分ける | app の開発の導線から、apply の権限と credential を物理的に遠ざけられる。UI の変更と infra の変更のレビューの観点を分けられる | 別のプロジェクトで同じ判断をした結果、3 か月の間 .tf が 1 つも書かれず、分離のコストだけを払って利益を受け取らない状態が続いた。運用者が 1 名で書き込む頻度が低いこのプロジェクトでも、同じ結末になる可能性が高い。分離で守ろうとした、apply の権限を日常の CI から遠ざけることは、credential の 2 系統でリポジトリを分けずに達成できる |
Consequences¶
Positive¶
- 初回の構築で手で行う手順が、Pages より 1 つ少ない。
/api/*を足すときは、同じ Worker にハンドラを足すだけで済む。- Terraform の state に機密が入らないので、state backend を守る要件が下がる。
- 日常の deploy が Terraform を経由しないので、UI の変更の PR に infra の差分が入らない。
Negative¶
- 管理ツールが 2 つになり、どちらの管轄かを都度判断する必要がある。
- 判定の手順と管轄の表を保つ運用のコストがかかる。
- Worker の設定(compatibility date、binding)が Terraform の差分に出ない。
- Worker の設定を持つのは実質的に
wrangler.jsoncだけになるので、レビューの対象として扱う必要がある。
- Worker の設定を持つのは実質的に
cloudflare_workers_custom_domainは Worker の存在を前提にするので、初回は Wrangler の deploy を Terraform の apply より先に行う必要がある。- secret の投入は自動化されず、更新のときに手で操作する必要がある。
- 将来
/api/*にハンドラを足すと、その変更は通常の PR に含まれ、merge で自動的に deploy される。- apply workflow のような明示的な行為を挟まない。
- Worker のコードは app の側にあるので、リポジトリをどう分けても解けない。
- 緩和は、通常の PR のレビューと自動テストに頼る。