dockerでexpressとmysqlをつなげたい

# お品書き
[1.はじめに](#1-はじめに)
[2.expresss-generatorでアプリの準備](#2-expresss-generatorでアプリの準備)
[3.Dockerfileとdocker-composeを準備](#3-Dockerfileとdocker-composeを準備)
[4.mysqlのコンテナを追加する](#4-mysqlのコンテナを追加)
[5.おわりに](#5-おわりに)

#1. はじめに
dockerを使ってexpressとMySQLをつなげようとしたときにハマってしまったのでメモしておきます。
dockerを使うと簡単に環境構築ができまた潰すのも簡単です。
つまり初学者こそdockerを使いいろんな環境を構築すべきなんです！
しかしdockerと聞くと難しそうな技術だなと尻込みしてしまいます。
そんな人のためにこの記事を書きました。
難しいことはわかりませんが、dockerを使って開発できるようになりましょう。

##1-1 目標
dockerを使ってexpress(のコンテナ)とMySQL(のコンテナ)をつなげる
完成コードは[こちら](https://github.com/cedrictarou/docker_sample)

##1-2 そもそもdockerってなに
パソコン内に仮想環境を作る技術の１つで最近人気になっています。
dockerの良いところは気軽に様々な環境をつくることができることです。
コンテナという仮想環境を作ってそこでやりたい放題できる。
コンテナ内に好きなOSを入れてそれを好きなように操作するって感じです。
基本的は軽量のlinuxベースのもが多いのでlinuxコマンドを知っていると便利です。
そして要らなくなったらそのコンテナを捨てばOKってな感じです。

##1-3 dockerがわかりにくい理由
とはいったものの、dockerを初めて触ったときに？？？がいっぱい浮かびました。
仮想環境やコンテナなどの概念などはわかるのですが、実際にどう使えばいいの？？的な。
なぜならdockerを扱う上で似たようなコマンドが出てくるし、オプションも多いからです。
特にdockerとdocker-composeが初学者にはややこしい。
一通り調べた結果、とりあえずdocker-composeを使えばなんとかなりそうという結論になりました。（笑）
こちらの動画がかなり参考になりました。
[docker-compose入門](https://www.youtube.com/watch?v=M3h_z-9DoMQ&t=643s)をチェックしてください。
難しいことは考えずに気軽に開発環境をつくっていきましょう。

##1-4 今回使うdocker-composeのコマンドは２つだけ
- docker-compose up　複数のコンテナを起動する
- docker-compose down　起動しているコンテナを停止する
とりあえずこれだけ知っていれば今回は大丈夫です。
その他のコマンドは後で覚えればOKです。

#2. expresss-generatorでアプリの準備
まずはお好きな方法でディレクトリーを作ってください。
今回はdocker_express_mysqlという名前にしておきます。

```:terminal
mkdir docker_express_mysql
```
今回はexpress-generatorを使ってサクッとアプリを作っていきます。
アプリの名前はdockerExpressにします。
express-generatorの使い方はこちらを参考にしてください。
[express-generatorを使いたい](https://qiita.com/cedrictarou/items/e975b6ede5abe1429a9f)

```:terminal
#ディレクトリーへの移動
cd docker_express_mysql

#express-generatorを使ってdockerExpressというアプリを作る今回はテンプレートエンジンにejsをつかいます。
express --view=ejs dockerExpress

#dockerExpressに移動し立ち上げまで
cd dockerExpress
npm install
npm start
```
以下のように表示されれば成功です。
![スクリーンショット 2021-02-27 10.20.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618705/825c41b8-7a55-95bc-9289-7f003021a029.png)

次にdockerExpressの中身をいじっていきます。
することは
- nodemonのインストール 保存時に自動で更新してくれるスグレモノ
- mysql2のインストール　mysqlに接続するのに必要
- sequelizeのインストール　クエリーを簡単にするためのもの。ORMとよばれるもの

```:terminal
npm install nodemon mysql2 sequelize
``` 
```json:package.json
"scripts": {
    // "start": "node ./bin/www" <-を下のように変える
    "start": "nodemon ./bin/www"
  }
```
これで準備が整いました。

#3. Dockerfileとdocker-composeを準備
これはめっちゃ簡単です。というかvscodeの"Docker"というエクステンションを使えば簡単にできます。
詳しいことは省略しますが、Dockerfileはコンテナのもとになるものを作るときに必要なものです。
docker-composeは複数のコンテンなを立ち上げるときに必要になるものです。
この辺の書き方が、初心者には難しいと感じるので、是非本記事で感覚だけでも掴んでください。
とりあえず最低限必要な設定のみにしています。
拡張機能のDockerについて[詳しくはこちら](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker)
##3-1 vscode拡張機能"Docker"を導入する
"Docker"という拡張機能を使って作業します。もしvscodeを使っていな人や拡張機能を使いたくない人はあとに出てくるDockerfileとdocker-compose.ymlを自分で作成し、コードをコピーしてください。

導入方法は至って簡単です。
拡張機能から"Docker"と検索してインストールするだけです。
![スクリーンショット 2021-02-27 10.42.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618705/9de142d3-4b3f-ca58-f2ef-046c7fc44ac2.png)

##3-2 拡張機能を使う
インストールできたら、拡張機能を使いましょう。
vscodeでcmd+shift+pを押してひたすらenterをおしてください。（詳しく知りたい人は[こちら](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker)）
[![Image from Gyazo](https://i.gyazo.com/eb5f7583becbcf6e94ec1f7692d47cd7.gif)](https://gyazo.com/eb5f7583becbcf6e94ec1f7692d47cd7)
そうすると自動でDockerfileとdocker-composeのファイルをができます。
これらの自動生成されたファイルを多少変更していきます。

##3-3 Dockerfileの編集
まずDockerfileから編集していきます。
いらない設定を削除していきます。

```yml:Dockerfile
FROM node:12.18-alpine
# ENV NODE_ENV=production ←を削除
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
# RUN npm install --production --silent && mv node_modules ../ ←を下のよう変更する
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
```
##3-4 docker-compose.ymlを編集 
続いてdocker-compose.ymlを編集していきます。
こちらは不要な設定を消しておきます。

```yml:docker-compose.yml
version: '3.4'

services:
  dockerexpress:
    image: dockerexpress
    build:
      context: dockerExpress
      dockerfile: ./Dockerfile
    #environment: ←を削除
      #NODE_ENV: production　←を削除
    ports:
      - 3000:3000 #接続するポート番号。お好きな番号でいけます。
```
これでdockerを使う準備が整いました。

##3-5 コンテナを立ち上げてみる
試しにコンテナを動かして見ましょう。
注意点は**docker-compose.ymlがあるディレクトリーで**docker-compose upというコマンドを打つことです。

```terminal:terminal
#docker-compose.ymlが入っているフォルダに移動してから
#コンテナを立ち上げるコマンド
docker-compose up
```
![スクリーンショット 2021-02-27 11.41.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618705/664886e2-fc4a-e19c-fbe2-a13f425b367f.png)
問題なければhttp://localhost:3000/にアクセスできるはずです。
*もし`docker-compose up`をしてエラーが出た場合はコードを確認して`docker-compose build`を行ってから`docker-compose up`を行ってください。

ここまででdockerを使ってnodeのアプリをコンテナ内に立ち上げることができました。

#4. mysqlのコンテナを追加
ここまで来たらあとはmysqlのコンテナを立ち上げるだけです。
基本的にすることはdocker-compose.ymlにmysqlのコンテナの設定を書くだけです。
今回は最小限の設定でコードを書いていきます。
dockerでコンテナをつくるときには１つのコンテナにすべての機能を入れるのではなくて、**最小限の機能に分けて作るのがベストプラクティス**らしいです。
なので今回はアプリ部分のコンテナ(expressで作ったもの)とDB部分のコンテナ(今から作るmyslqのもの)に分けて作っています。

##4-1 docker-compose.ymlにmysqlのコンテナ設定を書く
docker-compose.ymlに次のように追記してください。
ポイントはDBのデータを永続化させるために第３の場所にデータの置き場を作ることです。
詳しくは別の記事で説明は別の記事でできれば。。

```yml:docker-compose.yml
version: '3.4'

services:
# ここから
  my_mysql:
    image: mysql #もととなるイメージ。ここではmysql
    environment: #最小限の記述にしています。他にも環境設定は可能です。
      MYSQL_ROOT_PASSWORD: root #rootパスワード。今回はrootにしました。
      MYSQL_DATABASE: my_mysql_db #作成するデータベース。今回はmy_mysql_dbにしました。
    ports:
      - '3300:3300' #接続するポート番号。お好きな番号でいけます。
    volumes:
      - my_volume:/var/lib/mysql　    #第３の場所my_volumeをコンテナ内に同期するコード
# ここまで追記　下の部分も忘れずに追記してください。

  dockerexpress:
    image: dockerexpress
    build:
      context: dockerExpress
      dockerfile: ./Dockerfile
    ports:
      - 3000:3000

#第3の場所にデータを置いておく場所をつくる。こちらも忘れずに追記してください。
volumes: 
  my_volume:
```

これでmysqlのコンテナを起動する準備ができたので起動させてみましょう。

```terminal:terminal
# docker-composeがあるディレクトリーで
docker-compose up
```
これでエラーがでなかれば起動しているはずです。

##4-2 mysqlコンテナ内に入って操作してみよう
一応myslqのコンテナが起動しているか確認しましょう。
イメージとしては先程立ち上げたコンテナ内に入ってmysqlを使うというかたちです。
ここがちょっとややこしいです。とりあえずコマンドを打ちましょう。

```terminal:別のterminalを開く
# my_mysqlコンテナ内に入るためのコマンド
docker-compose exec my_mysql /bin/bash
```
```sql:my_mysqlのコンテナ内
--プロンプトが変わったらコンテナ内に入れた証拠です。続いてmysqlを起動させていきます。
mysql -u root -p 
--docker-composeで設定したパスワードを入力
```
下のような感じになれば成功です。
![スクリーンショット 2021-02-27 17.23.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618705/7e93b100-d4e8-6714-3d65-c98e6f372350.png)

```sql:my_mysqlのコンテナ内

show databases;
```
![スクリーンショット 2021-02-27 17.28.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618705/5d32d5f6-191f-7398-805d-f3aa59a69f09.png)
データベースが作られていることを確認！
＊他にもdocker-composeでいろんな指定ができますが省略します。
あとはdockerexpressからmy_mysqlにデータを取りにいけるようにしましょう。

##4-3 コンテナ間の接続を確認する
expressのコンテナとmysalのコンテナはそれぞれ別のコンテナとして立ち上げました。
なので今は２つのコンテナ立ち上がっているわけです。
それらのコンテナをつなげることに挑戦します。とはいってもとても簡単です。
**host名はローカルではlocalhostになっていますが、これをdocker-compose.ymlで設定したDBの名前(今回はmy_mysql)にするだけです。**
これを知らなくてかなり時間を無駄にしてしまいました。。。

mysql2を使ってクエリを書いても確認できますが、今回はsequelizeを使ってみます。
[sequelizeの本家サイトはこちら](https://sequelize.org/master/manual/getting-started.html)

usersのページに遷移したときにdbにつながっているかをしらべてみます。
なのでroutes内のusers.jsを開き次のようにsequelizeの設定と確かめるためのコードを追記しましょう。

```javascript:users.js
var express = require('express');
var router = express.Router();

// sequelizeの設定を追加
const { Sequelize } = require('sequelize');
// databaseやuser, passwordをdcoker-compose.ymlで設定したものを使う↓
const sequelize = new Sequelize('my_mysql_db', 'root', 'root', {
  host: 'my_mysql', // hostの名前をdocker-compose.ymlで設定したmy_mysqlに変更する
  dialect: 'mysql',
});

/* GET users listing. */
router.get('/', async (req, res, next) => {
  // 忘れずに上に"async"を追加する。
  // my_mysqlに接続されているかテスト
  try {
    await sequelize.authenticate();
    console.log('Connection has been established successfully.');
  } catch (error) {
    console.error('Unable to connect to the database:', error);
  }
  res.send('respond with a resource');
});

module.exports = router;
```
URLをhttp://localhost:3000/usersとして、usersページに遷移したときに以下のようなメッセージがターミナルに出たら成功です。

```terminal:terminal
dockerexpress_1  | Executing (default): SELECT 1+1 AS result
dockerexpress_1  | Connection has been established successfully.
dockerexpress_1  | GET /users 304 45.706 ms - -
```
とりあえずこれでdockerを使ってexpressとmyqlのコンテナ同士をつなげることができました。
あとはアプリを作り込んだり、DBにデータをどんどん追加していくだけです！

#5. おわりに

githubに[コード](https://github.com/cedrictarou/docker_sample)を載せておきます。
初学者のため、不十分な理解やコードのミス等があるとおもいます。もしあれば優しく教えてくいただければ幸いです。

sequelizeの使い方に関しては後日別の記事を書きたいと思います。
もう少しい詳しいdockerの解説も後日別の記事を書きたいと思います。