Strapi を開発環境と本番環境のDockerで動かしたい【その3】
前回までで、Strapi 本体の開発環境と本番環境の Dockerfile を用意することができました。
今回は、Strapi に接続するデータベースについて考えていきます。
Strapi とデータベース
Strapi は、ヘッドレスCMS(Contents Management System)です。コンテンツを管理します。
そのコンテンツはデータベースに保存されることになります。
データベース選び
SQLite
Strapi はデフォルトではSQLite
が使われています。
これは開発目的であり、SQLite – Strapi Developer Docsにも、ローカルで素早く起動するためにオススメと書かれています。
Docker で構築する場合には、コンテナ内にデータベースの中身を持ち、コンテナを破棄するとデータが消えてしまいます。Strapi + Docker では少し相性が悪そうです。
MySQL
MySQLはオープンソースのリレーショナルデータベース管理システムです。
わかりやすく直感的で、MySQL はデータベースの読み取り操作で高いパフォーマンスを発揮するように設計されているそうです。
なんとなく DB といえば MySQL という印象です。WordPress では MySQL を利用しているので馴染み深いです。
MariaDB
MariaDBはMySQLから派生したリレーショナルデータベース管理システムです。
MySQLとの互換性を保ちつつ、性能/堅牢性を高めるための独自機能を備えているそうです。
PostgreSQL
PostgreSQLはオープンソースのリレーショナルデータベース管理システムです。
PostgreSQL は MySQLより厳格で、書き込みの多い操作や同時読み取り/書き込みの操作に適しているそうです。
PostgreSQLはより厳密に標準に準拠しているため、PostgreSQL用に書いたコードは他のSQLバージョンに移植しやすいメリットがあるそうです。いいですね。
MySQLとMariaDBとPostgreSQLの選択
それぞれの違いについて調べてみました。
- データベースの選択: MySQLかPostgreSQLか
- PostgreSQLとMySQLのデータベースとしての機能の違い
- MariaDB vs MySQL vs PostgreSQL比較:最適なRDBは?ベンチマーク付選択フローチャート
総合的なパフォーマンス
データベースが読み込みと書き込みのどちらの操作に対応するか、あるいは最適化する必要があるかを検討することが重要です。MySQLはデータベースの読み取り操作で高いパフォーマンスを発揮するように設計されていますが、PostgreSQLは書き込みの多い操作や同時読み取り/書き込みの操作に適しています。
PostgreSQLは高いスケーラビリティを持ち、高度なクエリをサポートしていることから、企業向けソリューションで高い評価を得ています。PostgreSQLは、データベースの更新を並列化し、ビジネスに対応した顧客にアピールするための工業的に強力な追加機能を提供します。PostgreSQLは、書き込みと同時の読み書き操作に最適化されています。構文の習得はより困難ですが、高度なクエリ機能を備えています。PostgreSQLはより厳密に標準に準拠しているため、PostgreSQL用に書いたコードは他のSQLバージョンに移植しやすくなっています。
PostgreSQL の厳格さには惹かれるものがあります。性能面で実感することがあるかは微妙なので、今後移植する可能性と好みで選択しようと思います。
前回の記事までは MySQL を選択していましたが、PostgreSQL へ乗り換えてみようと思います。
様々なサービス
Strapi のDeployment – Strapi Developer Docsに様々なサービスへのデプロイ方法が書かれています。
Strapi はそれぞれのサービス上のデータベースと接続して利用することになります。
AWS
AWSへのデプロイではAmazon RDSとの紐付けを行なっています。ドキュメントのサンプルではPostgreSQL
が選択されています。
Azure
AzureへのデプロイではAzure Database for PostgreSQLやAzure Database for MariaDB等への紐付けを行なっています。ドキュメントでのサンプルではMariaDB
が選択されています。
DigitalOcean
DigitalOcean App PlatformへのデプロイでもDigitalOceanの管理画面上でデータベースを作成します。ドキュメントでのサンプルではPostgreSQL
が選択されています。同様にDigitalOcean DropletsへのデプロイもPostgreSQL
が選択されています。
Google App Engine
Google App EngineへのデプロイではCloud SQL databaseを利用しています。これはPostgreSQL
で構築されます。
Heroku
HerokuへのデプロイではHeroku Postgresを利用しています。
今回は、特にこういったサービスは利用せずにVPS上のUbuntuに直接構築していくことになります。
利用されているデータベースもPostgreSQL
が多いですね。
PostgreSQLに移行する
前回の記事のMySQL
で構築されている Strapi をPostgreSQL
に移行していきます。
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# postgreSQLを追加する
$ yarn add pg
package.json
からMySQLを削除します
{
...
"dependencies": {
"@strapi/plugin-i18n": "4.3.8",
"@strapi/plugin-users-permissions": "4.3.8",
"@strapi/strapi": "4.3.8",
"pg": "^8.8.0"
},
...
}
Strapi のサーバ設定が行われているファイルの編集をします。./config/database.ts
をpostgres
に変更して、ポート番号を変えます。
export default ({ env }) => ({
connection: {
client: 'postgres',
connection: {
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', 'strapi'),
},
debug: false,
},
});
上記で用いられる環境変数も.env
にて変更しておきます。
DATABASE_HOST=donuts-strapi-postgres
DATABASE_PORT=5432
DATABASE_NAME=donuts-strapi
DATABASE_USERNAME=donuts-strapi
DATABASE_PASSWORD=donuts-strapi
./config/database.ts
が以下の場合にはSSLをサポートしていないというエラーが表示されました。
# 開発環境でStrapiを起動する
$ docker run -p 1337:1337 --net=donuts-strapi-network --name donuts-strapi donuts-strapi-image
yarn run v1.22.19
$ strapi develop
Starting the compilation for TypeScript files in /opt/app
[2022-09-22 21:49:04.133] debug: ⛔️ Server wasn't able to start properly.
[2022-09-22 21:49:04.135] error: The server does not support SSL connections
Error: The server does not support SSL connections
export default ({ env }) => ({
connection: {
client: 'postgres',
connection: {
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', 'strapi'),
ssl: {
rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates
},
},
debug: false,
},
});
PostgreSQL
のDocker イメージを用いて、コンテナを起動します。
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# ネットワークを作成する
$ docker network create donuts-strapi-network
# ネットワークが作成されたことを確認する
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
2bb3e3e77243 donuts-strapi-network bridge local
# PostgreSQLのコンテナを起動する
# docker run --name コンテナ名 -dit --net=ネットワーク名 -p ポートの指定 環境の設定
$ docker run --name donuts-strapi-postgres -dit --net=donuts-strapi-network -p 5432:5432 -e POSTGRES_DB=donuts-strapi -e POSTGRES_USER=donuts-strapi -e POSTGRES_PASSWORD=donuts-strapi postgres
# コンテナを確認する
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b11563ca8bf3 postgres "docker-entrypoint.s…" 45 seconds ago Up 44 seconds 0.0.0.0:5432->5432/tcp donuts-strapi-postgres
# 開発環境のイメージを作成する
$ docker build -f dev.Dockerfile -t donuts-strapi-image .
# 開発環境でStrapiを起動する
$ docker run -p 1337:1337 --net=donuts-strapi-network --name donuts-strapi donuts-strapi-image
## 起動を確認
同様にステージング環境用の Dockerfile で起動することも確認します。
# ステージング環境のイメージを作成する
$ docker build -f stg.Dockerfile -t donuts-strapi-image-staging .
# ステージング環境でStrapiを起動する
$ docker run -p 1337:1337 --net=donuts-strapi-network --name donuts-strapi-staging donuts-strapi-image-staging
どちらも起動することが確認できました。
ボリュームをマウントする
このままでは PostgreSQL のコンテナを削除した場合に、データが消えてしまいます。
ボリュームを外部ファイルとリンクさせるためにvolumes
を設定します。
docker run
をするときに-v 実際の記憶領域パス:コンテナの記憶領域パス
で設定します。
コンテナ側のパスはPostgreSQLのDockerイメージの説明によると/var/lib/postgresql/data
がデフォルトで指定されています。
This optional variable can be used to define another location – like a subdirectory – for the database files. The default is /var/lib/postgresql/data. If the data volume you’re using is a filesystem mountpoint (like with GCE persistent disks) or remote folder that cannot be chowned to the postgres user (like some NFS mounts), Postgres initdb recommends a subdirectory be created to contain the data.
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# 先ほど作ったコンテナを削除しておく
$ docker container rm -f donuts-strapi-postgres
# PostgreSQLのコンテナを起動する
# docker run --name コンテナ名 -dit --net=ネットワーク名 -p ポートの指定 -v 実際の記憶領域パス:コンテナの記憶領域パス 環境の設定
$ docker run --name donuts-strapi-postgres -dit --net=donuts-strapi-network -p 5432:5432 -v data:/var/lib/postgresql/data -e POSTGRES_DB=donuts-strapi -e POSTGRES_USER=donuts-strapi -e POSTGRES_PASSWORD=donuts-strapi postgres
Changing SQLite to Postgres / MySQLを参考にしました。
また、.dockerignore
にdata
が書かれていることも確認します。
最後に Strapi のコンテナを再構築します。
# コンテナを停止する
$ docker container rm -f donuts-strapi-staging
# あたらしくコンテナを起動する
$ docker run -p 1337:1337 --net=donuts-strapi-network --name donuts-strapi-staging donuts-strapi-image-staging
正しくマウントされているかを確認しましょう。
http://localhost:1337/adminにアクセスして、Userを追加してみます。
Content Manager
> User
> 右上のCreate new entry
を選択
新しいユーザを追加してSAVE
します。
Content Manager
> User
で追加されたことを確認します。
この状態でコンテナを止めて再開してみましょう。
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# Strapi のコンテナを止めて削除する
$ docker container rm -f donuts-strapi-staging
# http://localhost:1337/にアクセスできないことを確認
# 再度実行する
$ docker run -p 1337:1337 -d --net=donuts-strapi-network --name donuts-strapi-staging donuts-strapi-image-staging
http://localhost:1337/adminにアクセスして User を確認しましょう。
先ほどと同じで消えていません。
次に、PostgreSQLのコンテナ
も削除してしまいましょう。
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# Strapi のコンテナを止めて削除する
$ docker container rm -f donuts-strapi-staging
# http://localhost:1337/にアクセスできないことを確認
# Postgres のコンテナを止めて削除する
$ docker container rm -f donuts-strapi-postgres
# ネットワークも削除しちゃう
$ docker network rm donuts-strapi-network-staging
# ネットワークから作り直し
$ docker network create donuts-strapi-volume-confirm
# Postgres のコンテナを起動
$ docker run --name donuts-strapi-postgres -dit --net=donuts-strapi-volume-confirm -p 5432:5432 -v data:/var/lib/postgresql/data -e POSTGRES_DB=donuts-strapi -e POSTGRES_USER=donuts-strapi -e POSTGRES_PASSWORD=donuts-strapi postgres
# Strapi のコンテナを起動
$ docker run -p 1337:1337 --net=donuts-strapi-volume-confirm --name donuts-strapi-staging donuts-strapi-image-staging
http://localhost:1337/adminにアクセスして User を確認して、データが残っていることを確認できました😊
メディアの保存先
最後にメディアの保存先を考えます。
Media Library
> Add new assets
より画像を追加してみます。
この画像が配置された場所をコンテナの中に入って確認してみます。
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# コンテナの中を確認してみる
$ docker container exec -it donuts-strapi-staging sh
% ls
build config database favicon.ico node_modules package.json public src tsconfig.tsbuildinfo
% cd public
% ls
robots.txt uploads
% cd uploads
% ls
large_strapi_95516f0cf4.png medium_strapi_95516f0cf4.png small_strapi_95516f0cf4.png strapi_95516f0cf4.png thumbnail_strapi_95516f0cf4.png
% exit;
Strapi のコンテナを止めて、再度起動してみましょう。
# Strapi のコンテナを止めて削除する
$ docker container rm -f donuts-strapi-staging
# http://localhost:1337/にアクセスできないことを確認
# 再度実行する
$ docker run -p 1337:1337 -d --net=donuts-strapi-network --name donuts-strapi-staging donuts-strapi-image-staging
Media Library
> Add new assets
を確認してみると、画像が破損してしまっているのがわかります。
データベース側には参照が残っていますが、画像本体はpublic
フォルダの中に入っていて、public
フォルダはコンテナを削除したときに消えてしまっています。
なのでpublic
ディレクトリも外付けのボリュームにしていきます。
dev.Dockerfile
の方は特に編集しません。
stg.Dockerfile
とprod.Dockerfile
のCOPY --from=builder /opt/app/public ./public
を削除します。
### deps ステージ ###
FROM node:16 AS deps
RUN apt-get update && apt-get install libvips-dev -y
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY ./package.json ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g && yarn install
### builder ステージ ###
FROM node:16 AS builder
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/app
# deps ステージでインストールしたライブラリをコピーする
COPY --from=deps /opt/node_modules ./node_modules
COPY . .
RUN yarn build
### runner ステージ ###
FROM node:16 AS runner
WORKDIR /opt/app
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 strapi
COPY --from=builder /opt/app/package.json ./package.json
COPY --from=builder /opt/app/favicon.ico ./favicon.ico
COPY --from=builder --chown=strapi:nodejs /opt/app/dist ./
COPY --from=builder --chown=strapi:nodejs /opt/app/node_modules ./node_modules
COPY --from=builder --chown=strapi:nodejs /opt/app/.env ./.env
EXPOSE 1337
ENV PORT 1337
CMD ["yarn", "start"]
Docker イメージを再作成後、-v public:/opt/app/public
を追加してコンテナを起動します。
# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi
# ステージング環境のイメージを作成する
$ docker build -f stg.Dockerfile -t donuts-strapi-image-staging .
# ボリュームを指定して起動する
$ docker run -p 1337:1337 -d --net=donuts-strapi-network -v public:/opt/app/public --name donuts-strapi-staging donuts-strapi-image-staging
再度別のファイルをアップロードして同様の手順で試します。
コンテナを止めて再度起動します。
# Strapi のコンテナを止めて削除する
$ docker container rm -f donuts-strapi-staging
# http://localhost:1337/にアクセスできないことを確認
# 再度実行する
$ docker run -p 1337:1337 -d --net=donuts-strapi-network -v public:/opt/app/public --name donuts-strapi-staging donuts-strapi-image-staging
コンテナを作り直しても画像が正しく表示されることを確認します。
おわりに
Strapi で管理しているコンテンツは、PostgreSQLのデータであるdata
ディレクトリと、アップロードしたメディアが格納されるpublic
ディレクトリの2つであることがわかりました。
これらのファイルをバックアップできれば、リストアもできそうです。
次回は、GitHub Actions を用いて 本番環境の VPS へのデプロイを目指します。