ローカルで動かしていたNode.js + MySQLのアプリケーションをDocker上で動かせるようにしました。またSequelizeを使ってマイグレーションを実行するところまでメモしています。
目次
docker-compose.yml、Dockerfileの作成
・docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | version: '3.4' services: db: image: mysql environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: sample_db ports: - '3306:3306' volumes: - data_volume:/var/lib/mysql command: --default-authentication-plugin=mysql_native_password api: image: api env_file: ./app.env build: context: . dockerfile: ./Dockerfile ports: - 5000:5000 volumes: - .:/usr/src/app depends_on: - db volumes: data_volume: |
・Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | FROM node:16 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . EXPOSE 5000 CMD sleep 1; npm run dev |
docker-compose.ymlではMySQLの「db」とNode.jsの「api」の2つのサービスを定義しています。
「db」サービスの定義
公式からMySQL 8.0のバージョンのイメージを使用します。
environmentでMySQLに接続する際のパスワードと生成するDBを指定します。portsでポートフォワーディングの設定をしており、今回はホスト側のポート3306からコンテナ側のポート3306に繋がります。
volumesで「data_volume」というボリュームをコンテナの/var/lib/mysql/ディレクトリにマウントすることで、コンテナが削除されてもデータが保持されます。
また、MySQL8.0からはログイン認証方式のデフォルトがcaching_sha2_passwordになったことにより、接続時にエラーが発生するため、旧来の認証方法にするためのコマンドを追加しています。
「api」サービスの定義
apiというimageを指定しています。env_fileでapiコンテナからdbコンテナに接続する際の環境変数が定義されているapp.envファイルを読み込むようにしています。
buildでapiを起動するためのビルドを実行します。今回はDockerfileを指定し、そこでNode.jsのアプリケーションをDocker化しています。
参考:https://nodejs.org/ja/docs/guides/nodejs-docker-webapp/
depends_onは起動順を担保しているだけで、稼働順を担保していない
少しハマったのは、docker-compose upでコンテナの作成・起動を成功するが、APIからDBに接続する際にエラーになったことです。
depend_onでDBが起動が終わったタイミングで接続すると思っていたのですが、よくよく調べてみるとdepends_onは起動順を担保しているだけで、稼働順を担保していないことがわかりました。
つまり、DBから起動していたのですが、DBの起動が完了する前にAPIがDBに接続しようとしていたのです。
そのため、確実にDBが起動完了してからAPIが接続するようにするために「CMD sleep 1; npm run dev」で1秒するスリープさせるようにしました。色々調べてみると公式とかですとシェルを使ってみるとかかいてあったのですが、とりあえずこの方法でやっています。
Sequelizeでマイグレーションを実行する
docker-compose upで問題なくコンテナ作成・起動・接続できたらSequelizeを導入していきます。
SequelizeはNode.jsのORMでマイグレーション機能があり、これを使ってマイグレーションしていきます。以下のコマンドでSequelizeとその依存パッケージをインストールと初期化を行います。
1 2 | $ docker-compose run --rm api npm i mysql2 sequelize sequelize-cli $ docker-compose run --rm api npx sequelize-cli init |
次に以下のコマンドでモデルとマイグレーションファイルを作成します。今回はToDoアプリを想定しており、tasksというテーブルをつくります。
1 | $ docker-compose run --rm app npx sequelize-cli model:generate --name Task --attributes content:string,status:integer |
実行するとモデルとマイグレーションファイルが作成されます。マイグレーションファイルを少し修正しておきます。具体的にstatusにデフォルト値を追加したりcreated_atをCURRENT_TIMESTAMP、updated_atをCURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPにしました。
・xxxxxxxx-create-task.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 'use strict' module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('tasks', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, content: { type: Sequelize.STRING }, status: { type: Sequelize.INTEGER, defaultValue: 1 }, created_at: { allowNull: false, type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, updated_at: { allowNull: false, type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') } }) }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('tasks') } } |
先ほど起動したMySQLのコンテナの接続情報をconfigファイルに設定して、マイグレーションを実行していきます。正常に実行されれば完了です。
1 | $ docker-compose run --rm app npx sequelize-cli db:migrate |
あとはseedersフォルダで初期データを投入することができます。シードファイルを作成して投入したいデータをセットし、以下のコマンドを実行することでシードできます。
1 | $ docker-compose run --rm app npx sequelize-cli db:seed:all |