はじめに
みなさん「タコス」食べていますか?
こんにちは、tacosDB 開発責任者です。
タコスが好きすぎてタコスに特化したサイト「tacos DB」を開発しています。
食べログなど既存のグルメサイトでよくない?と最初は私も思っていたのですが、場所や値段、評価で絞ることはできてもトルティーヤの種類や具材などで絞ることは既存のサービスでは叶いません。
私はコーントルティーヤで、チキンティンガ、ワカモレトッピングのタコスが好きなのですが、このタコスを提供するお店をピンポイントで探すことって結構難しいんですよね、、、
開発を進める中で、出先でもスマホからレビューできる環境が欲しいと思うようになり、 PR ごとにプレビュー環境が自動で立ち上がる仕組みを作りました!
この記事では、GitHub Actions と Cloudflare を使って構築した CI/CD の配備構成を紹介します。
技術スタック
| 領域 | 技術 |
|---|---|
| Backend | Rust |
| Frontend (Web) | Astro |
| Frontend (CMS) | Next.js |
| Infrastructure | Cloudflare (Pages / Workers / R2 / D1 / Zero Trust / DNS / etc...) / Terraform / GitHub Actions |
Cloudflare の構成
今回扱うのは次の 4 つです。dev / prod それぞれに同じ構成が 1 セットずつあります。
- Cloudflare Pages — 公開 Web
- Cloudflare Workers — API サーバー
- Cloudflare D1 — 店舗・記事などのデータ
- Cloudflare R2 — 画像
実際には公開 Web と CMS のアプリが分かれていたり、API も用途ごとに分けていたりします。ディレクトリ構成は別の記事でまとめます。
dev と prod の出しどころを決める
最初に決めたのは「どのタイミングで、どこに配備するか」です。
-
developへのマージ → 即時 dev へ配備 - GitHub Release / tag の作成 → prod へ配備
- 手動再配備 →
workflow_dispatchで対応
本番は develop → main の release PR をマージし、GitHub Release が作られたタイミングで配備されます。バージョンはセマンティックバージョニングで管理し、PR につけたラベルに応じて patch / minor / major を自動インクリメントします。
手動再配備時は、migration はデフォルトで実行しない設計にしています(後述)。
on:
push:
branches:
- develop
release:
types:
- published
workflow_dispatch:
inputs:
release_tag:
description: "Release tag to deploy to prod (example: v0.1.0)"
required: true
type: string
apply_migrations:
description: "Apply remote D1 migrations before deploy"
required: false
default: false
type: boolean
PR ごとにプレビュー環境を作る
開発中に意外と手間だったのが画面のレビューです。コードを読めば変更内容は把握できますが、店舗一覧や CMS の入力画面は実際に開いて確認したほうが早い。
そこで、Web または CMS に差分がある PR では自動でプレビュー URL を生成し、PR コメントに貼り付けるようにしました。
差分検知
web_changed="false"
cms_changed="false"
while IFS= read -r file_path; do
case "$file_path" in
src/ts/tacos-web/*)
web_changed="true"
;;
esac
case "$file_path" in
src/ts/tacos-cms/*)
cms_changed="true"
;;
esac
done < /tmp/cloudflare-preview-changed.txt
Web のプレビュー(Cloudflare Pages)
- name: Deploy tacos-web preview to Pages
working-directory: src/ts/tacos-web
run: >
npx --yes wrangler@${WRANGLER_VERSION}
pages deploy dist
--project-name "${WEB_PROJECT_NAME}"
--branch "${WEB_BRANCH_ALIAS}"
CMS のプレビュー(Cloudflare Workers)
CMS 側は Workers の preview alias を使っています。
Backend と D1 は PR ごとに複製していません。 プレビューの主な用途は画面確認なので、dev 環境のバックエンドを共有する判断にしました。バックエンドは I/O と DB を検証する CI テストでカバーしています。この判断はアプリの規模や同時開発人数によって変わってくる部分だと思います。
D1 migration をいつ流すか
最も慎重に設計したのが D1 migration です。Web の再配備と違い、DB の変更は気軽に巻き戻せません。
ルール:
- dev への通常配備 — migration ファイルに差分があれば適用する
- prod への配備(Release 起点) — 常に適用する
- 手動再配備(同一 tag) — デフォルトでは実行しない。必要なときだけ明示的にオンにする
if: >-
${{
(
needs.resolve-target.outputs.deploy_env == 'dev' &&
needs.detect-changes.outputs.migration_changed == 'true'
) ||
(
needs.resolve-target.outputs.deploy_env == 'prod' &&
needs.resolve-target.outputs.apply_migrations == 'true'
)
}}
適用は Wrangler の D1 migration コマンドで行います。
- name: Apply remote D1 migrations
working-directory: src/rs/bo-neko-api-server
run: >
npx --yes wrangler@${WRANGLER_VERSION}
d1 migrations apply ITEM_DB
--remote
--config wrangler.client-api.generated.toml
機能開発より先に migration の運用方針を決めておいたことは正解でした。後から「schema が変わるアプリ」として考えるより、最初から設計に組み込んだほうが安心して開発を進められます。
Terraform と GitHub Actions の分担
| 担当 | 役割 |
|---|---|
| Terraform | Pages project / D1 / R2 / ドメインなど、インフラの土台 |
| GitHub Actions | コード変更に伴う build / deploy |
「土台はコードで管理、アプリを届ける部分は自動化」という分担です。
おわりに
この CI/CD 構成を整備したことで、開発開始から約 2 週間で dev / prod にデプロイできる状態まで持っていけました。早い段階でパイプラインを固めておくと、その後の開発リズムが格段に上がります。
リリースした tacos DB はまだ掲載店舗数が少ないのが課題です。
- 店舗情報・写真・紹介文を提供してくれる方
- タコスが好きで一緒に整えてくれる方
を募集しています!知っているお店があれば、ぜひお問い合わせフォームから投稿してください 🌮



