今まで、「食べたリンゴの割合をグラフ化するアプリ」をGoogle Cloud Runで動かしてきました。
Python + Django + Highcharts + Coogle Cloud Cloud Run + Cloud Storage + Litestream で食べたリンゴの割合をグラフ化してみた - メモ的な思考的な
運用する中で、
- コールドスタートがやや気になる
- めったに使わないアプリとはいえ、使うときにはさっと起動すると嬉しい
- DB(Litestream + SQLite)をCloud Storageに置いているので、テーブルの中身を確認しづらい
- Cloud Runはほとんど費用負担が発生していない一方で、Cloud Storageで費用がかさんできた
などを感じることがありました。
そんな中、Cloudflareがデータベース機能のD1を正式リリースしました。
データベースがあるならば移行先として使えるかもしれないと考え、必要そうな機能を検証してきました。
- Hono + React + TanStack Router + TanStack Query + Chart.js + Drizzle ORMなアプリを、Cloudflare Pages と D1 に乗せてみた - メモ的な思考的な
- React + react-chartjs-2 + Chart.js を使って、デフォルトの Legend の代わりに HTML Legend を表示してみた - メモ的な思考的な
- CloudflareのService Bindings RPC を使って、Bun + Hono な Pages と Workers を連携してみた - メモ的な思考的な
また、 d1-jdbc-driver
を使うことで、D1にあるテーブルをJetBrains DataGripから確認することもできました。
JetBrains IDEで外部キーを表示できるよう、Cloudflare D1向けJDBC driver「d1-jdbc-driver」を修正するプルリクを作った - メモ的な思考的な
以上より、移行しても問題なさそうと判断しました。
そこで、今までの検証に加えて移行作業を行ったため、内容をメモしておきます。
なお、移行後のWebアプリのURLは以下です。
https://ringosky.thinkami.dev/
目次
- 環境
- ルートディレクトリでのセットアップ
- ringo-db Workersの作成
- ringo-web Pagesの作成
- ringo-bsky Workersの作成
- データ移行について
- その他
- 動作確認
- ソースコード
環境
- Windows 11 WSL2
- Wrangler 3.60.3
- Bun 1.1.13
- Bunのworkspaceを使ってモノレポで管理
- フロントエンド
- React 18.3.1
- Chart.js 4.4.2
- react-chartjs-2 5.2.0
- TanStack Router 1.38.1
- TanStack Query 5.45.1
- バックエンド
- Hono 4.4.12
- Drizzle ORM 0.31.2
- Drizzle Kit 0.22.7
- @atproto/api 0.12.23
- Bluesky API用ライブラリ
- 開発向けツール
- Wrangler 3.63.1
- Biome 1.8.1
- Linter & Formatter
ちなみに、これらのソースコードは、Bunのworkspaceを使ってモノレポで管理しています。
また、移行前後で使っている機能は以下の通りです。
Google Cloudだと | 役割 | Cloudflareでは |
---|---|---|
Cloud Run | アプリをホスト | Pages + Workers + Service Binding(RPC) |
SQLite + Litestream + Cloud Storage | アプリのデータベース | D1 |
Cloud Scheduler | SNSへの投稿を定期的に収集 | WorkersのCron Trigger + KV |
また、こんな感じで Cloudflare を使っています。 Pages と Workers は Service Binding (RPC) で連携しています。
なお、上図の各種アイコンは以下のページよりお借りしています。
Brand Icons - Cloudflare Datamining
以降では、どんな感じでアプリを作成・移行したのかをメモしておきます。
なお、記事中ではソースコードを必要な部分だけ明示しています。
ソースコード全体は以下のリポジトリで公開しているため、必要に応じてそちらを確認してください。
https://github.com/thinkAmi/cf_ringo_sky
ルートディレクトリでのセットアップ
モノレポ内のPagesやWorkersが共通で使うものをセットアップします。
ライブラリのインストールと設定ファイルの用意
wranglerとconcurrentlyをインストールします。
$ bun add -d wrangler concurrently
Linter & Formatterとして Biome をインストールします。
$ bun add --dev --exact @biomejs/biome
Biomeの設定ファイルへコメントを書けるよう biome.jsonc
として用意します。
https://github.com/thinkAmi/cf_ringo_sky/blob/main/biome.jsonc
Bun workspace を使うために package.jsonを編集
今回のアプリをモノレポで開発できるよう、Bunのworkspace機能を使います。
Workspaces – Package manager | Bun Docs
また、 concurrently
を使ってPagesやWorkersを一括起動できるように script
も設定します。
{ "private": true, "scripts": { "dev": "concurrently \"bun run --filter=\"ringo-db\" dev:db\" \"bun run --filter=\"ringo-web\" dev:web\"" }, "workspaces": ["packages/*"], // ... }
ringo-db Workersの作成
前述の通り、Service Binding RPCを使ってPagesとWorkersを連携します。
そこで、まずは
- D1と接続し、SQLを発行する
- Service Binding RPC向けに、メソッドを公開する
を担当する ringo-db
Workersを作成します。
ライブラリのインストール
Workersの雛形を作るには、Cloudflare C3で作成するのが便利です。ただ、今回のWorkers向けには不要なものもできてしまいます。
そこで、今回は bun init
により最小限のものだけ生成しました。
bun init – Templating | Bun Docs
# packages ディレクトリの中で作業 $ mkdir ringo-db $ cd ringo-db # ringo-db アプリを生成 $ bun init ... package name (ringo-db): entry point (index.ts): ./src/index.ts Done! A package.json file was saved in the current directory. + ./src/index.ts + .gitignore + tsconfig.json (for editor auto-complete) + README.md ...
続いて、D1接続に必要な Drizzle ORM まわりをインストールします。
$ bun add drizzle-orm ... installed drizzle-orm@0.31.2
合わせて、開発向けに Drizzle Kit もインストールします。
$ bun add -D drizzle-kit ... installed drizzle-kit@0.22.7 with binaries: - drizzle-kit
Cloudflare D1を作成
次に、Cloudflare D1を wrangler で作成します。database leaderは明示的に apac
を指定しておきます。
create | D1 | Commands - Wrangler · Cloudflare Workers docs
$ wrangler d1 create ringodb --location apac ... ✅ Successfully created DB 'ringodb' in region APAC Created your new D1 database. [[d1_databases]] binding = "DB" # i.e. available in your Worker on env.DB database_name = "ringodb" database_id = "01febb3d-148f-4f3c-8fea-e32445da1ae1"
wrangler.toml の編集
D1の情報のほか、 compatibility_date
や port
などを指定しておきます。
Configuration - Wrangler · Cloudflare Workers docs
name = "ringo-db" main = "./src/index.ts" compatibility_date = "2024-06-18" compatibility_flags = [ "nodejs_compat" ] [dev] port = 8788 [[d1_databases]] binding = "DB" # i.e. available in your Worker on env.DB database_name = "ringodb" database_id = "01febb3d-148f-4f3c-8fea-e32445da1ae1"
drizzle.config.ts の作成
Drizzle ORMの設定ファイルを作成します。
なお、今回のマイグレーション適用は、Drizzle Kit ではなく Cloudflare D1 の Migrationを使います。
Migrations · Cloudflare D1 docs
そのため、Cloudflare D1のMigrationの仕様に合わせて、 out
は migrations
にしておきます。
import type {Config} from "drizzle-kit" export default { dialect: "sqlite", schema: "./db/schema/*", out: "./migrations", } satisfies Config
データベースのスキーマを作成
Blueskyの投稿を保存するためのテーブル feeds
向けのスキーマとして、ringo-db/db/schema/feeds.ts
を作成します。
import { sql } from 'drizzle-orm' import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core' export const feeds = sqliteTable('feeds', { id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }), name: text('name'), content: text('content'), createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`), snsId: text('sns_id'), })
マイグレーションファイルを生成
マイグレーションファイルを drizzle-kit
を使って生成します。
Generate migrations | Drizzle ORM - List of commands
$ bun drizzle-kit generate drizzle-kit: v0.22.7 drizzle-orm: v0.31.2 No config path provided, using default 'drizzle.config.ts' Reading config file 'path/to/ringo_sky/packages/ringo-db/drizzle.config.ts' 1 tables feeds 5 columns 0 indexes 0 fks [✓] Your SQL migration file ➜ migrations/0000_wild_pride.sql 🚀
ローカルのD1にマイグレーションを適用
ローカルのCloudflare D1対してマイグレーションを適用する場合、drizzle-kit の機能は使えません。
そこで、今回は wrangler
を使ってマイグレーションを適用します。
ちなみに、ローカルに適用する --local
オプションも明示的に付けておきます。
https://developers.cloudflare.com/workers/wrangler/commands/#migrations-apply
$ wrangler d1 migrations apply ringodb --local ... Migrations to be applied: ┌─────────────────────┐ │ name │ ├─────────────────────┤ │ 0000_wild_pride.sql │ └─────────────────────┘ ✔ About to apply 1 migration(s) Your database may not be available to serve requests during the migration, continue? … yes 🌀 Executing on local database ringodb (01febb3d-148f-4f3c-8fea-e32445da1ae1) from .wrangler/state/v3/d1: 🌀 To execute on your remote database, add a --remote flag to your wrangler command. ┌─────────────────────┬────────┐ │ name │ status │ ├─────────────────────┼────────┤ │ 0000_wild_pride.sql │ ✅ │ └─────────────────────┴────────┘
Cloudflare D1にマイグレーションを適用
続いて、本番環境である Cloudflare D1 にマイグレーションを適用します。
$ wrangler d1 migrations apply ringodb --remote ... Migrations to be applied: ┌─────────────────────┐ │ name │ ├─────────────────────┤ │ 0000_wild_pride.sql │ └─────────────────────┘ ✔ About to apply 1 migration(s) Your database may not be available to serve requests during the migration, continue? … yes 🌀 Executing on remote database ringodb (01febb3d-148f-4f3c-8fea-e32445da1ae1): 🌀 To execute on your local development database, remove the --remote flag from your wrangler command. 🚣 Executed 2 commands in 0.4022ms ┌─────────────────────┬────────┐ │ name │ status │ ├─────────────────────┼────────┤ │ 0000_wild_pride.sql │ ✅ │ └─────────────────────┴────────┘
実装に関するメモ
あとは、 src/index.ts
などに必要な機能を実装します。
https://github.com/thinkAmi/cf_ringo_sky/blob/main/packages/ringo-db/src/index.ts
実装で迷ったことは以下です。
batch APIを使ってSQLのトランザクション相当を実装
以下の記事にある通り、Cloudflare D1にはSQLのトランザクションが実装されていないようです。
- Cloudflare D1でのトランザクションと、Drizzle ORMの対応 | Memory ice cubes
- Cloudflare D1でトランザクションが使えない問題
- Kysely + Cloudflare D1 でトランザクションを実行するヘルパー関数を書いた話
そこで、SQLのトランザクションが必要になったときは batch APIを使って実装しました。
- 実装
- ドキュメント
Drizzle ORMでGroup By や Count する
Drizzle ORMのドキュメントに従い実装しました。
- 実装
- ドキュメント
なお ringo-db
Workers は Service Binding RPC で利用することを前提にしているため
WorkerEntrypoint
を継承したクラスに、公開するメソッドを定義- 環境変数などの値は、
this.env
で参照可能
- 環境変数などの値は、
- エラーになるのを防ぐため、default export するオブジェクトにはダミーの
fetch
を定義
としています。
ringo-web Pagesの作成
次に、Pages である ringo-web
を作成します。
このPagesでは
- ringo-db Workers と Service Binding RPC して、D1のデータを受け取る
- D1のデータを元に、ブラウザで描画する
を担当します。
ライブラリのインストール
packages
ディレクトリの中で、 create hono
によりHonoアプリの雛形を作成します。
# インストール $ bun create hono ringo-web create-hono version 0.7.1 ✔ Using target directory … ringo-web ? Which template do you want to use? cloudflare-pages ✔ Cloning the template ? Do you want to install project dependencies? yes ? Which package manager do you want to use? bun ✔ Installing project dependencies 🎉 Copied project files Get started with: cd ringo-web # ringo-webディレクトリへ移動 $ cd ringo-web
次に必要なライブラリをインストールします。
Reactまわりです。
$ bun add react react-dom ... installed react@18.3.1 installed react-dom@18.3.1 $ bun add -d @types/react @types/react-dom ... installed @types/react@18.3.3 installed @types/react-dom@18.3.0
Chart.js まわりです。
$ bun add chart.js react-chartjs-2 ... installed chart.js@4.4.3 installed react-chartjs-2@5.2.0
TanStack Routerまわりです。
$ bun add @tanstack/react-router ... installed @tanstack/react-router@1.38.1 $ bun add -D @tanstack/router-vite-plugin @tanstack/router-devtools @tanstack/router-cli ... installed @tanstack/router-vite-plugin@1.38.0 installed @tanstack/router-devtools@1.38.1 installed @tanstack/router-cli@1.37.0 with binaries: - tsr
TanStack Queryまわりです。
$ bun add @tanstack/react-query ... installed @tanstack/react-query@5.45.1
wrangler.toml の編集
compatibility_date
などを設定しておきます。
なお、今回の compatibility_date
には、開発を始めた頃の日付 2024-06-18
を設定してあります。
name = "ringo-web" pages_build_output_dir = "./dist" compatibility_date = "2024-06-18"
実装に関するメモ
YAMLをJSONへ変換するのに yq を使った
今までりんごに関する情報は apples.yaml
というファイルで管理していました。
https://github.com/thinkAmi/dj_ringo_tabetter/blob/development/apples.yaml
今回、Cloudflareへ移行するにあたり、ソースコードはすべてTypeScriptになりました。そこで、YAMLでの管理をやめ、TypeScriptで管理することを考えました。
YAMLをJSONへ変換するツールを探したところ、 yq
があったため、使ってみることにしました。
https://github.com/mikefarah/yq
まずはインストールします。
$ sudo snap install yq yq v4.40.5 from Mike Farah (mikefarah) installed
続いて、JSONファイルへと変換します。
$ yq -o json ./old_data/apples.yml > ./src/apples.json
あとはファイルに保存されたJSONをTypeScriptファイルへと移植・修正して使います。
デプロイしようとするとviteが途中でハングするので、それぞれ分ける
過去記事でも書きましたが、Pagesをデプロイしようとするとハングしてしまいます。
そこで、フロントエンド・バックエンド・デプロイの3段階に分けてビルド・デプロイします。
ただ、各ビルドでは途中でハングするので、途中でキャンセルしています。
まずはフロントエンドのビルド
$ bun run build:fe ... dist/static/index.lazy-B-SIKAr-.js 1.01 kB │ gzip: 0.60 kB dist/static/month.lazy-mKjT0B0Q.js 1.06 kB │ gzip: 0.64 kB dist/static/appleLegendPlugin-C9-uDFHk.js 201.77 kB │ gzip: 70.46 kB dist/static/client.js 213.00 kB │ gzip: 68.13 kB ✓ built in 1.33s ^C # ハングしたのでキャンセル
続いてバックエンドです。
$ bun run build:be $ vite build vite v5.3.1 building SSR bundle for production... ✓ 21 modules transformed. dist/_worker.js 22.64 kB ✓ built in 196ms ^C # ハングしたのでキャンセル
最後にデプロイします。
$ bun run deploy $ wrangler pages deploy dist The project you specified does not exist: "ringo-web". Would you like to create it?" ❯ Create a new project ✔ Enter the production branch name: … main ✨ Successfully created the 'ringo-web' project. 🌏 Uploading... (5/5) ✨ Success! Uploaded 5 files (2.38 sec) ✨ Compiled Worker successfully ✨ Uploading Worker bundle ✨ Uploading _routes.json 🌎 Deploying... ✨ Deployment complete! Take a peek over at https://578e4ac8.ringo-web.pages.dev
ringo-bsky Workersの作成
最後に、 Blueskyからリンゴの投稿を定期的に取得する ringo-sky
Workersを作成します。
ライブラリのインストール
packages
ディレクトリの中に ringo-bsky
を作成し、その中で bun init
します。
$ bun init bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit package name (ringo-bsky): entry point (index.ts): Done! A package.json file was saved in the current directory. + index.ts + .gitignore + tsconfig.json (for editor auto-complete) + README.md To get started, run: bun run index.ts
続いて、Blueskyにアクセスするためのライブラリ @atproto/api
をインストールします。
https://www.npmjs.com/package/@atproto/api
$ bun add @atproto/api bun add v1.1.13 (bd6a6051) installed @atproto/api@0.12.23 11 packages installed [3.82s]
Blueskyのクレデンシャルをローカル・本番環境へ設定
Blueskyの投稿を取得するために、クレデンシャルを設定します。
Workersではクレデンシャルは
- ローカル環境は
.dev.env
ファイル - 本番環境は wrangler のコマンド
を使います。
Secrets · Cloudflare Workers docs
まず、ローカル環境向けには ringo-bsky
ディレクトリの直下に .dev.env
ファイルを生成します。中身には以下のクレデンシャル情報を設定しておきます。
IDENTIFIER=自分のDID APP_PASSWORD=アプリパスワードの値
次に、wranglerを使って本番環境用のクレデンシャルを設定します。
まずは IDENTIFIER
を設定します。
$ wrangler secret put IDENTIFIER ... ✔ Enter a secret value: … ******************** 🌀 Creating the secret for the Worker "ringo-bsky" ✨ Success! Uploaded secret IDENTIFIER
次に APP_PASSWORD
を設定します。
$ wrangler secret put APP_PASSWORD ... ✔ Enter a secret value: … ******************* 🌀 Creating the secret for the Worker "ringo-bsky" ✨ Success! Uploaded secret APP_PASSWORD
なお、 wrangler secret put
すると、wrangler.toml の情報をもとに Cloudflare Workersが自動で作成されます。
このときに作成される Workers を確認したところ
という状態でした。
KV へ処理済の投稿に関する情報を記録
Blueskyから投稿を取得・保存する際、重複してデータを保存しないよう「前回どこまで保存したか」を記録しておく必要があります。
今まではDBへ保存していました。ただ、Cloudflareには KV
というキーバリューストアがあります。
Cloudflare Workers KV · Cloudflare Workers KV
「前回どこまで保存したか」という情報は1個だけ存在していればよいので、今回は KV を使って保存することにします。
まずは wrangler でグローバルなKVを作成します。
なお、wrangler kv:namespace
コマンドのwarningが出ていますが、まだドキュメントには反映されていないようです。また、warningなので、KVはできているようです。
https://developers.cloudflare.com/workers/wrangler/commands/#create-3
$ wrangler kv:namespace create LAST_CURSOR_KV ▲ [WARNING] The `wrangler kv:namespace` command is deprecated and will be removed in a future major version. Please use `wrangler kv namespace` instead which behaves the same. ... 🌀 Creating namespace with title "ringo-bsky-LAST_CURSOR_KV" ✨ Success! Add the following to your configuration file in your kv_namespaces array: [[kv_namespaces]] binding = "LAST_CURSOR_KV" id = "f6f608a043
続いて、同じコマンドに --preview
オプションを付けて、ローカルのKVを作成します。
なお、warningで指示されたコマンド wrangler kv namespace
を使ったところ、warningが消えました。
$ wrangler kv namespace create LAST_CURSOR_KV --preview ... 🌀 Creating namespace with title "ringo-bsky-LAST_CURSOR_KV_preview" ✨ Success! Add the following to your configuration file in your kv_namespaces array: [[kv_namespaces]] binding = "LAST_CURSOR_KV" preview_id = "54ad69fc39bc429597e55ed0fe7acdd9"
最後に、wrangler.toml に KV の設定を追加します。
kv_namespaces = [ { binding = "LAST_SEARCH_KV", id = "f6f608a04340492d87d3a10b0210cfa8", preview_id = "54ad69fc39bc429597e55ed0fe7acdd9" } ]
あとは、
// 読み込み await env.LAST_SEARCH_KV.get(BSKY_KV_KEY) // 書き込み await env.LAST_SEARCH_KV.put(BSKY_KV_KEY, latestCreateAt)
のような感じで使います。
Cron Trigger による定期実行を設定
Cloudflare Workerでは Cron Trigger を使って定期実行を実現できます。
Cron Triggers · Cloudflare Workers docs
そこで、wrangler.toml へ定期実行タイミングの設定を行います。
なお、タイムゾーンは UTC であることに注意します。以下の例では、毎日、日本時間の午前3時に起動します。
[triggers] crons = [ "0 18 * * *" ]
あとは、 scheduled
ハンドラを持ったオブジェクトを default export します。
import type { ExportedHandler } from 'cloudflare:workers' export default { async scheduled(_event: any, env: Env) { const bsky = new Bsky(env) await bsky.run() }, } as ExportedHandler<Env>
ローカルで動作確認するには、 ringo-bsky
を
$ wrangler dev --test-scheduled
にて起動します。
それに加え、別のターミナルから curl
を使って
$ curl "http://localhost:8787/__scheduled?cron=*+*+*+*+*"
のようにアクセスします。
Test Cron Triggers | Cron Triggers · Cloudflare Workers docs
データ移行について
アプリを Google Cloud から Cloudflare へ移行するのに伴い、
- 移行元
- Google Cloud Storage
- 移行先
- Cloudflare D1
というデータ移行が必要になります。
今回は
- Google Cloud Storageから、ローカルのSQLiteファイルとしてリストア
- ローカルのSQLiteから、ローカルのD1へデータをリストア
- ローカルのD1から、Cloudflare D1へリストア
というステップでデータ移行を行います。
Litestreamを使い、ローカルファイルとしてリストアする
Litestreamのドキュメントに従い、 litestream restore
コマンドでローカルへSQLiteをリストアします。
Replicating to Google Cloud Storage - Litestream
リストアしたSQLiteからローカルのD1へデータを投入する
ローカルでの動作確認を可能にするため、LitestreamでリストアしたSQLiteをローカルのD1へ投入します。
データを投入するときの方針は以下です。
- Cloudflare環境では不要なデータをリストアしない
- Drizzle ORMを使って、ローカルのD1へデータを投入する
そこで、次のようなスクリプトを作成します。
なお、ローカルのD1のファイル名などについては、環境によって異なります。
// @ts-ignore import { Database } from 'bun:sqlite' import { sql } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/bun-sqlite' import { feeds } from '../../db/schema/feeds' type Tweet = { id: number name: string tweet: string tweeted_at: string tweet_id: number } const main = async () => { const fromSqlite = new Database('old_data/ringo_2024_0502.db') const fromDb = drizzle(fromSqlite) const tweets: Tweet[] = await fromDb.all( sql.raw('select * from tweets_tweets'), ) // 環境に応じてファイル名を修正する const fileName = '2073307253fd76d9e289ad074b54fc751825840a1efddf02aa13a82ecf5305f6.sqlite' const toSqlite = new Database( `.wrangler/state/v3/d1/miniflare-D1DatabaseObject/${fileName}`, ) const toDb = drizzle(toSqlite) // biome-ignore lint/complexity/noForEach: <explanation> tweets.forEach(async (t) => { await toDb.insert(feeds).values({ name: t.name, content: t.tweet, createdAt: t.tweeted_at, snsId: t.tweet_id.toString(), }) }) console.log('finished') } main()
続いて、 ringo-db
ディレクトリの中で、スクリプトを実行します。
$ bun run scripts/development/import_local_db.ts finished
ローカルのD1を確認すると、必要なデータが投入されていました。
本番のD1に対してマイグレーションを実行する
ローカルでの動作確認ができたところで、次は本番環境です。
ここで、 ringo-db
Workersを初めてデプロイしたとき、同時にD1も作成されているはずです。
$ wrangler deploy --minify ... Total Upload: 64.01 KiB / gzip: 18.33 KiB Your worker has access to the following bindings: - D1 Databases: - DB: ringodb (01febb3d-148f-4f3c-8fea-e32445da1ae1) Uploaded ringo-db (3.96 sec) Published ringo-db (4.40 sec) ...
ただ、この時点ではD1はあるもののテーブルが存在しません。
そこで、wranglerを使い、本番のD1に対してマイグレーションを実行します。
https://developers.cloudflare.com/workers/wrangler/commands/#migrations-apply
$ wrangler d1 migrations apply ringodb --remote ... Migrations to be applied: ┌─────────────────────┐ │ name │ ├─────────────────────┤ │ 0000_wild_pride.sql │ └─────────────────────┘ ✔ About to apply 1 migration(s) Your database may not be available to serve requests during the migration, continue? … yes 🌀 Executing on remote database ringodb (01febb3d-148f-4f3c-8fea-e32445da1ae1): 🌀 To execute on your local development database, remove the --remote flag from your wrangler command. 🚣 Executed 2 commands in 0.4022ms ┌─────────────────────┬────────┐ │ name │ status │ ├─────────────────────┼────────┤ │ 0000_wild_pride.sql │ ✅ │ └─────────────────────┴────────┘
ローカルのD1からデータをエクスポートする
Cloudflareの以下のドキュメントを参考に、ローカルのD1からデータをエクスポートします。
Export an existing D1 database | Import and export data · Cloudflare D1 docs
なお、ローカルのD1なため、エクスポート時には --local
フラグが必要です。
$ wrangler d1 export ringodb --local --output=./old_data/ringodb_local_2024_0706.sql ... 🌀 Exporting local database ringodb (01febb3d-148f-4f3c-8fea-e32445da1ae1) from .wrangler/state/v3/d1: 🌀 To export your remote database, add a --remote flag to your wrangler command. 🌀 Exporting SQL to ./old_data/ringodb_local_2024_0706.sql... Done!
エクスポートしたデータを見たところ、マイグレーション済な本番環境では不要なマイグレーション関係のデータが含まれていました。
そこで、エクスポートしたデータから、以下の内容を削除しました。
CREATE TABLE d1_migrations( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ); INSERT INTO d1_migrations VALUES(1,'0000_wild_pride.sql','2024-06-17 23:22:59'); CREATE TABLE `feeds` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` text, `content` text, `created_at` text DEFAULT (CURRENT_TIMESTAMP), `sns_id` text );
本番のD1に対してインポートする
次に、wranglerを使って、ローカルへエクスポートしたデータを本番のD1へインポートします。
エクスポートしたデータはSQLファイルになっているため、 wrangler d1 execute
を使います。
https://developers.cloudflare.com/workers/wrangler/commands/#execute
$ wrangler d1 execute ringodb --remote --file=./old_data/ringodb_local_2024_0706.sql ... ✔ ⚠️ This process may take some time, during which your D1 database will be unavailable to serve queries. Ok to proceed? … yes 🌀 Executing on remote database ringodb (01febb3d-148f-4f3c-8fea-e32445da1ae1): 🌀 To execute on your local development database, remove the --remote flag from your wrangler command. Note: if the execution fails to complete, your DB will return to its original state and you can safely retry. ├ 🌀 Uploading 01febb3d-148f-4f3c-8fea-e32445da1ae1.c8499ae13caabeb9.sql │ 🌀 Uploading complete. │ 🌀 Starting import... 🌀 Processed 751 queries. 🚣 Executed 751 queries in 0.04 seconds (2240 rows read, 1498 rows written) Database is currently at bookmark 00000005-00000000-00004dc6-da501f7be681ab1e632fa9c002d2fc69. ┌────────────────────────┬───────────┬──────────────┬────────────────────┐ │ Total queries executed │ Rows read │ Rows written │ Database size (MB) │ ├────────────────────────┼───────────┼──────────────┼────────────────────┤ │ 751 │ 2240 │ 1498 │ 0.19 │ └────────────────────────┴───────────┴──────────────┴────────────────────┘
インポート後、Cloudflare上のD1を確認すると、データが投入されていました。
その他
Pagesでカスタムドメインを設定
Cloudflare Pagesでは、アプリにカスタムドメインを設定できます。
Custom domains · Cloudflare Pages docs
今回は以下の流れで対応しました。
- Cloudflare Pagesでの作業
- ドメインを管理している Squarespace での作業
- カスタムレコードの
レコードを追加
をクリックし、表示されている値を設定
- カスタムレコードの
- 再度、Cloudflareでの作業
Check DNS records
をクリック- 「Your records for ringosky.thinkami.dev are being rechecked. You’ll be notified by email when your domain is activated.」と表示されるので、しばらく待つ
しばらく待つと、カスタムドメインでの運用ができるようになりました。
移行前のデータ削除
Cloudflareで運用できるようになったので、Cloud Runで管理しているデータを削除しておきます。
- Cloud Run
- Cloud Storage のすべてのバケット
- Cloud Scheduler
- サービスアカウント
- Secret Manager
- Artifact Registry
お世話になりました。ありがとうございました。
ログに TypeError: e.env.RINGO_DB_TOTAL.calculateByName is not a function のように表示されたとき
今回、
- ローカルでは、Service Binding RPC まわりは問題なく動作している
- 本番環境にデプロイすると、動作しない
- 本番環境のログに、「TypeError: e.env.RINGO_DB_TOTAL.calculateByName is not a function」が出力されて動作しない
ということが起きました。
Service Binding RPC の設定はすべてドキュメント通り行なっているはずでしたが、うまくいきませんでした。
そんな中、wrangler.toml を見たところ、 compatibility_date
の値が Pages や Workers の間で差異があることに気づきました。
そこで、 compatibility_date
の値を 2024-06-18
へ統一したところ、動作するようになりました。
本当にこの対応でよいのか自信はありませんが、手元ではこの方法で解決したため、メモとして残しておきます。
動作確認
以下のURLで動作しています。起動も速くなりました。
https://ringosky.thinkami.dev/
月ごとの表示はこちら。
https://ringosky.thinkami.dev/month