みなさんこんにちは。
ソリューション事業部の大藪と申します。
この度、strapiのメジャーバージョンを3から4へのバージョンアップを実施しましたので、その時の対応について記事を書かせていただきました。これからバージョンアップを検討している方に参考にしていただけますと幸いです。
strapiとは?
・strapiとは?
Strapi - Open source Node.js Headless CMS 🚀
strapiとはオープンソースで提供されている、Node.jsベースの代表的なヘッドレス CMSです。Strapiは100%JavaScriptベースであり、完全なカスタマイズが可能です。APIも素早く設計することができ、開発者ファーストのCMSとして高い支持を得ています。
・ヘッドレスCMSとは?
WebサイトやWebサービスを表示するための機能を持たない、コンテンツを管理することに特化したCMSです。
・strapiのメリット
①オープンソースで公開されており、無料で使用できる
一般的なヘッドレスCMSは有償のものが多いですが、strapiは無料で使用できるため、コスト面を抑えられます。
②自動でやってくれることが多い
起動時にcontent-typeをもとにテーブルを自動生成してくれるので、テーブル定義などを自分で作成しなくてすみます。
create:登録、update:更新、delete:削除、find:参照、findOne:参照(1件)
はデフォルト機能として使用できるため、これらの処理を行いたい場合、デフォルトの書き方を記載するだけで使用できます。
統括すると、手に取りやすいこと、システム自体で賄ってくれる機能が多いことがメリットとして挙げられます!
バージョンアップでどのように変わる?
・APITOKENSALTの追加
セキュリティ面の強化。V3にはなかった新機能です。トークン生成時に有効期限も設定でき、定期的に新しくすることでよりセキュリティ面の強化にもつながっています。
・APIフォルダの構成
configフォルダの名称がroutesに変更
ファイル名もroute.jsonから<api-name>.jsonに変更
各APIのmodelsフォルダがcontent-typesフォルダに名称が変更。
配下のファイル名も「<api-name>.settings.json」から「<api-name>/schema.json」に変更、フォルダの階層が1つ深くなります。
・デフォルトメソッドの書き方
デフォルトメソッドの記載方法が変更されました。
// path: ./src/api//controllers/ .js const { createCoreController } = require("@strapi/strapi").factories; module.exports = createCoreController("api::api-name.content-type-name");
上記のような記載方法になります。
こちらはそのまま採用し、api-name部分を実際に使用しているAPI名に置き換えればOKです。
また、デフォルトメソッドからcountがなくなりました。V3使用時にcountメソッドを使用していた場合は、独自に作成が必要となります。
・テーブル構成の変更
起動時に自動生成してくれるテーブルの構成がガラッと変更されました。
コンポーネント間でrelation関係があると、「○○□□links」のように、relation関係のある者同士のテーブルが新たに作成されるようになります。
移行の為にどんなことをした?
①ソース側の修正
・設定ファイルの変更
・上記変更に伴うフォルダ名、ファイル名の修正
こちらについてはstrapi側で「Codemods」というスクリプトが用意されており、こちらを起動することで自動的に実行してくれます!
修正自体が単純な作業なので、これも自動で実行してくれるのはありがたいですね。
npx @strapi/codemods migrate
コマンドは上記を実行します。
あとはオプションの選択とアプリケーションのルートパスを選択すれば実行してくれます。
...簡単!
? What do you want to migrate? ❯ Application Plugin Only Dependencies ? What do you want to migrate? Application ? Enter the path to your Strapi application ./
・countメソッドの作成と水平展開
②データベース移行
・データ移行用のスクリプトの設定
スクリプトのフォルダに、各DB(MySQL、PostgreSQL、sqlite)の記載例があるので、それに倣って記載すればOKでした。
・移行前と移行先のDB情報を.envファイルに入力
移行前のデータベースは頭に「old_」などをつけてリネームする必要があります。
・実行の為にyarnコマンドが動かせるようにしておく
・strapiV4の起動→テーブルの自動生成
・移行スクリプトの実行
yarn start
実行コマンドもこれだけなので、実行はすごく簡単でした!
・移行スクリプトで上手く移行できなかった箇所の手動修正
V3で「component名id」のような名称のカラムがすでにあった場合、V4で新たにされる「○○□□links」テーブルのカラム名と重複してしまいます。
V4のテーブル自動生成時、重複名称を避けるため、カラム名に「inv」をつけて作成されるため、テーブル定義として重複は起こらないですが、データ移行自体はこの部分は上手くいかないので、手動での修正が必要になります。
テーブル自体が変わってしまうのと、データ量も少なくはないので、strapi側で移行スクリプトを用意してくれていたことは助かりました!
また、使用していたデータベースもMySQLであったため、そのまま一度動かすだけで実行できました。
※MongoDBを使用していた場合:V4からは非対応となるため対応しているDBに移行してからV4に移行するという形となり、もうひと手間作業が必要でした
③AWSに載せる
・タスクCPU/タスクメモリの値の変更
推奨値、もしくは最小値のスペックが設定されているので、そちらに合わせてECSのタスク定義から値を変更します。
・環境変数にAPITOKENSALTの設定
こちらもECSで保持するようにします。
実際やってみて躓いた点
①移行スクリプトが正しく動かない
移行スクリプトを流す手順は難しくはなかったですが、検証環境での実行時は途中で失敗しておりました…。
$ node index.js Migrating Core Store Migrating 108/215 items from core_store to strapi_core_store_settings /home/user/v3-sql-v4-sql/migrate/migrateCoreStore.js:28 const { id: _id1, ...apiTokenEntry } = await dbV4(resolveDestTableName(destination)) ^ TypeError: Cannot destructure property 'id' of '(intermediate value)' as it is undefined. at Object.migrateTables (/home/user/v3-sql-v4-sql/migrate/migrateCoreStore.js:28:15) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async migrate (/home/user/v3-sql-v4-sql/migrate/index.js:67:5) at async f (/home/user/v3-sql-v4-sql/index.js:6:3) error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
上記が実行時のエラー表示となります。
core_storeテーブルはstrapi起動時自動的に生成され、値も自動的に入力されるものなので、手動で何かしていたわけではないのですが、このテーブルのカラムの値で、改行がエスケープシーケンスの「\n」でなく、エンターキーによる改行で登録されており、そこが引っかかっているようでした。
上記問題を解消すると無事スクリプトは動いてくれました。
それにしても、自分でデータを編集する必要のない箇所が原因だったとは…。
②メモリ関連
ECSのメモリ不足でビルドができない問題もありました。
検証環境に反映する段階ではありましたが、AWSのCodePipelineを使用し、strapiの設定ファイルを変更したソースをAWS上に反映し、ビルド→デプロイしていたところ、ビルドは通過するが、デプロイが上手くいかずに苦戦してしまいました。
※CodePipeline上では成功した扱いになっていましたが、ECSのログを確認するとエラーログが出続けている状態なので、なかなかにタチの悪いエラーの出方でした
strapiV3では「タスク CPU/タスクメモリ」が「256 ユニット (0.25 vCPU)/512 MiB (0.5 GB)」でも稼働していましたが、V4になると、メモリとGBの推奨値と最小値が変更されていました。
AWSのECSで「タスク CPU/タスクメモリ」が「256 ユニット (1024 ユニット (1 vCPU)/2048 MiB (2 GB)」を変更したところ、なんとか成功!
デプロイ時のログを確認しても、ログメッセージとメモリ不足であることが結びつかず、この問題は解決に時間が掛かりました。
③移行スクリプト起動時にMySQLのfreeBufferが足りずデータベースが落ちたこと
データを移行する際、strapi側が推奨していた移行スクリプトがあり、そちらを起動してデータ移行を実行していたところ、
V3→V4移行、切り戻しでV4→V3をリハーサルとして一度実行した際は問題なく成功していただけに、発生した際はなかなか焦りました…。
移行スクリプトの仕組みとして、大量のCREATE分を発行してデータを移し替えていましたので、メモリが足りなくなることが起こることは言われてみれば納得できました。
MySQLのコマンドで
show engine innodb status \G
を入力すると、データベースのステータスを確認することができ、その中の「BUFFER POOL AND MEMORY」 の「Free buffers」(バッファプールの空き)を確認すると、値が著しく低かったことが確認でき、ここが原因になったとわかりました。
Buffer pool size(バッファプールの総量)に対し、Free buffersが著しく少ないです。
---------------------- BUFFER POOL AND MEMORY ---------------------- Total large memory allocated 0 Dictionary memory allocated 3971515 Buffer pool size 40406 Free buffers 1024 Database pages 39382 Old database pages 14517 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 11493, not young 63010 0.00 youngs/s, 0.00 non-youngs/s Pages read 21047, created 36727, written 1170194 0.00 reads/s, 0.00 creates/s, 0.00 writes/s Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 39382, unzip_LRU len: 0 I/O sum[0]:cur[0], unzip sum[0]:cur[0]
AWSに上げた状態でしたので、インスタンスタイプを一時的に変更してバッファ量を確保した状態で再度実行することで解決できました。
スクリプトのような便利なツールはどんどん使用していきたいですが、ツールに頼りっきりになってしまうと今回のように何か起こった時に焦ってしまうので、ある程度スクリプトの仕組みについて把握しておく必要があると身をもって実感させられました。
まとめ
今回実際にstrapiのバージョンアップをやってみましたが、インパクトが大きなバージョンアップであったため率直に大変だったという感想です。
手軽にバージョンアップできるようにと、いくつかスクリプトが用意されていましたが、やはりある程度は何を実行しているか内容を把握していないと問題が起こった際の対応も難しかったので、方法調査の段階でその辺りまで調べないといけないなと痛感させられました。
この記事が、少しでも皆さんのウェブ開発に役に立ってくれれば幸いです。
最後までお読みいただきありがとうございました。
参照サイト
・strapi 移行ガイド
Migration Guides | Strapi Documentation
・strapi データ移行
SQL v3 to v4 migration | Strapi Documentation
・How To Migrate From Strapi v3 to v4 Walkthrough
Strapi, the leading open-source headless CMS
アドグローブでは、さまざまなポジションで一緒に働く仲間を募集しています!
詳細については下記からご確認ください。みなさまからのご応募お待ちしております。