Docker ComposeとGitHubを使用して、Node.js + Express + MySQLの環境構築を行う

# 初めに
Docker Composeを使用して、Node.jsとMySQLのコンテナを作成し、Sequelzieを使用してDBにアクセスするところまでを記載する。
また、Node.jsのコンテナ上でExpressのプロジェクトを1から作成する記事は多く見かけたが、既にあるGitHubのリポジトリ(ExpressとSequelizeを含む)をcloneしてそこから環境を構築していく記事はあまり見受けられなかったので、今回はそちらの手順で行う。(実際に現場で開発する際は、既にGitHubが作成されている場合もあると思われるため)

#### 今回やりたいことの図
![SnapCrab_NoName_2022-4-1_10-29-57_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1229062/bb0c1684-1998-7692-c970-899ba13c904c.png)




# 環境
■ 使用PC
MacOS Big Sur：バージョン11.6

※既に使用PCにはgitを導入状態。
Node.js、MySQLは入っていない。

## Docker Composeとは
Docker Composeとは、複数(単数でも可)のコンテナを定義して実行するツールである。
定義ファイルは`YAML形式`で記載する。
また、Dockerのデスクトップアプリをインストールして入れば一緒にDocker Composeもインストールされている。

### Dockerfile、Kubernetesとの違い
Dockerfile・・・イメージを作る。
Docker Compose・・・コンテナ、ネットワーク、ボリュームを作る。
Kubernetes・・・コンテナを管理する。

## 使い方
ファイルの名前は以下で固定であり、1つのディレクトリにつき1つまで配置可能。
```
docker-compose.yml
```



#### よく使いそうなコマンド
設定ファイルがカレントディレクトリにあれば -f とパスは不要。

```
// コンテナの作成・起動
docker-compose -f ymlファイルのパス up
// コンテナの実行
docker-compose -f ymlファイルのパス up
// 削除
docker-compose -f ymlファイルのパス down
//停止
docker-compose -f ymlファイルのパス stop
```


# 実際にやってみる
ホストでの作業は[ホスト]コンテナでの作業は[コンテナ]と記載する。


## ファイル作成等の事前準備
###### [ホスト]
テスト用のディレクトリを作成し、その配下にDocker Composeの設定ファイルを作成。
#### 現在のディレクトリの構造

###### [ホスト]
```
% ls
docker-compose.yml
```

#### 設定ファイルの内部
```yml
% cat docker-compose.yml
version: '3'
services:
    db:
        image: mysql:5.7

        environment:
            MYSQL_DATABASE: [接続したいスキーマの名前]
            MYSQL_USER: [任意の値]
            MYSQL_PASSWORD: [任意の値]
            MYSQL_ROOT_PASSWORD: [任意の値]
            TZ: 'Asia/Tokyo'

        ports:
            - '3306:3306'

        volumes:
            - ./data:/var/lib/mysql
            - ./logs:/var/log/mysql

    app:
        image: node:11

        environment:
            - DEBUG=app:*

        tty: true

        ports:
            - '3000:3000'
            # - '3001:3001'

        volumes:
            - ./src:/app

        # working_dir: /app/[GitHubからcloneするフォルダ(npm startを実行するフォルダ]

        # command: npm run start
```

## 設定ファイルの説明
#### services
services以下にコンテナに関する情報を記載。

#### db、app
db、appという名称は任意だが、Sequelizeを初期化した際に生成されるconfig.jsonファイルのhostキーの値がこの内容となる。(今回だと`db`という名称がそれにあたる)

#### image
どのイメージを使用するかを記載。
今回は、[mysql:5.7](https://hub.docker.com/_/mysql)と[node:11](https://hub.docker.com/_/node)を使用。
#### environment
MySQLで使用する環境変数を設定。

#### ports
左側がホストのポート番号、右側がコンテナのポート番号
ホストのポート番号は任意だが、仮にDockerを導入しているOSにて3000番と3006番ポートを使用している場合は重複しないように変更が必要。
複数使用したい場合はコメントアウトしてあるコードを外すと使用可能である。

#### volumes
左側にホストの情報、右側にコンテナの情報を記載。
今回は「data」「logs」「src」というディレクトリをローカルに作成。
また、コンテナ側の`/app`配下にコードを記載していくようになる。

#### working_dir
起動時のディレクトリを指定。
GItHubからcloneしてきたリポジトリに移動するように設定するため、最初はコメントアウトしている。

#### command
起動時のコマンドを指定。
最初にコンテナを起動する際は、まだGitHubからリポジトリをcloneしていないので、コメントアウトしている。

## コンテナ起動
#### MySQLのコンテナを起動
###### [ホスト]

```bash
% docker-compose run db

// これが出たら成功
Version: '5.7.37'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
```

#### Nodeのコンテナを起動
###### [ホスト]

```bash
% docker-compose run app
```

#### コンテナの一覧を確認
###### [ホスト]

nodeとmysqlのコンテナが起動していたら成功。

```bash
% docker ps
4cb5cc823848   node:11     "docker-entrypoint.s…"   2 minutes ago   Up 7 seconds                         docker_app_run_5cea1c0c5428
e8226de1f749   mysql:5.7   "docker-entrypoint.s…"   7 minutes ago   Up 5 minutes   3306/tcp, 33060/tcp   docker_db_run_77072701880a
```

## ディレクトリ構造を再確認
###### [ホスト]

新しく「data」「logs」「src」というディレクトリが作成されている。
```
% ls
docker-compose.yml	data logs src
```

## GitHubのリポジトリを使用して環境構築
### Git Hubからclone
###### [ホスト]

srcディレクトリに移動し、任意のGitHubのリポジトリからclone。

```
src % git [任意のGitHubのリポジトリ]
```

###### [コンテナ]

#### nodeコンテナにアクセス
ホストのsrcディレクトリとコンテナのappディレクトリは同じ構造である。
```
docker exec -it 4cb5cc823848 bash
root@4cb5cc823848:/# 
```

cloneしてきたGitHubのリポジトリに移動して`npm install`を行う。
今回は、ここでExpress/Sequelizeが既にある状態。
###### [コンテナ]

```
# npm install
```

インストールが成功したら、一旦コンテナから抜ける。
###### [コンテナ]
```bash
exit
```

## `docker-compose.yml`ファイルの設定を変更
###### [ホスト]

ここで、`docker-compose.yml`ファイルの設定を変更を、Node.jsコンテナに反映するためにコンテナを一旦停止する。

```
% docker stop 4cb5cc823848
4cb5cc823848
```
`docker-compose.yml`ファイルのコメントアウトを外す。
###### [ホスト]
```:docker-compose.yml
working_dir: /app/[GitHubからcloneするフォルダ(npm startを実行するフォルダ]
command: npm run start
```

#### コンテナを起動
###### [ホスト]
起動時のコマンドを打つためにコンテナを起動。
```
% docker-compose up  
```
以下にアクセスしてExpressの初期画面が表示されれば成功。

```
http://localhost:3000/
```

## DBのスキーマをコンテナ内に作成
###### [ホスト]
スキーマを作成する前に`.env`ファイルを`docker-compose.yml`ファイルと同じ階層に作成。

```:.env
DB_ROOT_HOST=%
DB_USER=[DBユーザーの名前]
DB_PASS=[DBユーザーのパスワード]
TZ=Asia/Tokyo
```

別サーバーのMySQLをローカルにコピーする方法は以下の記事で記載。
※スキーマは必ず別サーバーから持ってくる必要はなく、状況に合わせて設定する必要がある。
https://qiita.com/shintaro_secual/private/d3aeb2e30a39d07fa399
## Node.jsコンテナとMySQLコンテナをSequelizeを使用して接続
###### [コンテナ]
### Sequelizeをインストールして、初期化
```bash
npm install sequelize sequelize-cli mysql2
npx sequelize-cli init
```

初期化したことにより、config.jsonファイルが生成されるので、値を変更する。

```:config.json
  "development": {
    "username": [docker-compose.ymlファイルの環境変数で設定したMYSQL_USERの値を記載],
    "password": [docker-compose.ymlファイルの環境変数で設定したMYSQL_PASSWORDの値を記載],
    "database": [MySQLのスキーマ名を記載],
    "host": "db", // docker-compose.ymlファイルのservicesの名称
    "dialect": "mysql"
}
```

Node.jsコンテナを再起動すると、DBに接続されている。


## Nodemon
nodeonを導入する方法を記載する。
###### [コンテナ]
nodemonをインストールする。
```
npm install nodemon
```

package.jsonに以下を追加する。

```json
  "scripts": {
    "dev": "nodemon ./bin/www",
    "start": "node ./bin/www" 
  }
```

`docker-compose.yml`ファイルのnodeコンテナの情報欄の`environment`に以下を追加
```yml
CHOKIDAR_USEPOLLING=1
```

今回だと以下のようになる。

```yml
    app:
        image: node:11

        environment:
            - DEBUG=app:*
            # 新規追加
            - CHOKIDAR_USEPOLLING=1
        tty: true

        ports:
            - '3000:3000'

        volumes:
            - ./src:/app

        working_dir: /app/[GitHubからcloneするフォルダ(npm startを実行するフォルダ]

        # npm startから変更
        command: npm run dev
```

上記設定でコンテナを再起動すると、nodemonが起動する。

## ローカルサーバー立ち上げの速度向上
nodeのパッケージの使用数が多いと、ローカルサーバー立ち上げに時間がかかる可能性がある。

`docker-compose.yml`ファイルに以下の記述を行うと解決可能である。

```yml
services:
  app:
    volumes:
         - ./src:/app
         # 追加 ↓
         - node_modules_volume:/app/[コンテナ側のnode_modelesまでのパス]/node_modules  
# 追加 ↓
volumes:
  node_modules_volume:
```

再度コンテナでnodeのパッケージをインストールし、Docker Composeを再起動すると反映される。
筆者は、約10秒ほどローカルサーバーの立ち上げに時間がかかっていたが、約2秒ほどに改善された。