DockerのMySQLコンテナで日本語を使いたい！

6月に未経験エンジニアとして入社して早いもので、3ヶ月が経ってしまいました...
日々研修に励んでいます。
まだまだ分からないことばかりですが、初投稿していきたいと思います。お手柔らかにお願いします。
では。本題に入ります。
## 誰のための記事？
* スッキリわかるSQLやゼロからはじめるデータベース操作などの教材をDockerのMySQLコンテナで使ってみたい方。(そんな方います？？)
* MySQLコンテナで日本語入力を使いたい方(時短：本命(Dockerfile)だけ見れば解決できるかと思います。)
 
## MySQLコンテナ内で日本語入力できない⁉
SQLの研修時に、Docker環境を構築して「MySQLをいじるぞー」って意気込んで作ってはみたものの、何故か日本語が入力できない。
「ナゼー？？？:thinking:」っという事で調べてみました。
結果、日本語入力できる環境ができたので、いろいろ調べたものをまとめハンズオンっぽい記事を書いてみました。

## 原因
MySQLの文字コードを日本語(utf8mb4)に設定しても入力できず...
どうやらコンテナ内のLinux カーネルのlocaleを日本語が使えるように設定しないとダメみたいです。

# docker-composeでDocker環境をつくってみた。
### リポジトリ
https://github.com/sunsound33/docker-mysql
### ディレクトリ構成図
```:ディレクトリ構成
  docker-mysql
        ├─ mysql 
        │　  ├ Dockerfile        // Dockerファイル
        │    ├ my.cnf            // MySQL設定ファイル
        │    └ prefecture.sql　  // SQLファイル(使いたいSQLファイルに差し替えてください。)
        ┝ .env                   // 環境設定ファイル
        └ docker-compose.yml
```
## Docker
```yml:docker-compose.yml
version: '3.9'
volumes:
  sql:
services:
  db:
    build:
      context: .
      dockerfile: ./mysql/Dockerfile
    ports:
      - 3306:3306
    volumes:
      - sql:/var/lib/mysql
      - ./mysql/:/docker-entrypoint-initdb.d
    environment:
      - MYSQL_DATABASE=${DB_NAME}
      - MYSQL_USER=${DB_USER}
      - MYSQL_PASSWORD=${DB_PASS}
      - MYSQL_ROOT_PASSWORD=${DB_PASS}
      - TZ=${TZ)
```
ファイルについて上から順に説明していきます。
* **version:** docker-composeで使うバージョン
* **volumes: sql:** コンテナを破壊(down)するとデータが消えてしまうので永続化のためのボリューム
永続化って何？って方はこちらに詳しい書いてくれてますね。

https://qiita.com/taiga_tech/items/d9361f10d521a55659ee

* **services:** 各コンテナを要素ごとにまとめている。今回はdbのみですが
* **build:** コンテナを作る設定
* **context:** コマンドを実行した場所 
* **dockerfile:** Dockerfileの場所
なんでコンテキストが必要なのだろうか？って方はこちらを

https://qiita.com/sam8helloworld/items/e7fffa9afc82aea68a7a

* **port:** 左側がホストのポート番号で右側がコンテナのポート番号
今回は同じ番号ですが、例えば。`3307:3306`の場合。
JavaなどのバックエンドからDB(MySQL)に接続したい時は`3307`でアクセス、コンテナ内からDB(MySQL)へ接続するときは`3306`でアクセスが必要です。
`3306`はMySQLのデフォルトのポート番号です。(ポート番号を変えたい場合はmy.cnfの項目で説明します。)
* **volumes: - sql:/var/lib/mysql**　データを永続化するためさっき用意した*volumes:sql*をバインドします。これでコンテナを破壊(down)してもデータがボリュームに保存されます。
**- ./mysql/:/docker-entrypoint-initdb.d**　エントリーポイントを指定します。
 mysqlディレクトリにある.shや.sqlファイルを起動時にdaemonが読み取って初期化を行ってくれます。
つまり、mysqlディレクトリにSQLファイルを入れれば、起動時にテーブルを作ってくれます。(便利！！)

* **environment:** 環境変数の指定です。.envの値を指定します。TZ(timezone)を指定しないとMySQLで `CURRENT_TIMESTAMP`が取れないので注意です。
```.env
DB_NAME=sql
DB_USER=user
DB_PASS=password
TZ=Asia/Tokyo
```
環境変数を設定するファイルです。
リポジトリには.envはありません。
```
cp .env.example .env
```
で.envをコピーして設定してください。
<br>
MySQLの設定ファイル　エントリーポイントを指定しているので、起動時に読み込んでくれます。
```my.cnf
[mysqld]
character_set_server = utf8mb4
collation_server = utf8mb4_general_ci

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

```
文字コードを日本語が使えるように設定してます。
設定しないと`character_set`が`latin1`のままでバックエンドから日本語データを取った時に文字化けしたりします。
[mysqld]に`port=3388`を記述するとポート番号が変更できます。
### 本命
```dockerfile:Dockerfile
# apt-get出来る様にdebian版を使う。
FROM mysql:8.0.30-debian
# コンテナ内で日本語が打てるようにlocalを変更といらないファイル削除
RUN apt-get update \
    && apt-get install -y locales \
    && sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
ENV LC_ALL ja_JP.UTF-8
```
表題の解決策はこれです。
mysql imageにlocaleを設定します。
こちらを参考にしました。

https://qiita.com/valzer0/items/db7639d8231bf5121297

<br>

* **FROM mysql:8.0.30-debian**　apt-getするにはdebian版を使う必要があります。
はじめapt-getできなくて「なぜできないの？？:sob:」ってハマってしまいました。
どうやらデフォルトのosが変わっていたらしくapt-getできなかったようです。

https://zenn.dev/manabin/articles/174ef3169dc7c7

* **apt-get update && apt-get install -y locales** パッケージを更新してlocalesをインストール
* **sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen** locale.genにある#ja_JP.UTF-8　→　ja_JP.UTF-8へ変更
コンテナ内の/etc/locale.genをcatしてみると
```: /etc/locale.gen
# it_IT.UTF-8 UTF-8
# it_IT@euro ISO-8859-15
# iu_CA UTF-8
# ja_JP.EUC-JP EUC-JP
ja_JP.UTF-8 UTF-8
# ka_GE GEORGIAN-PS
# ka_GE.UTF-8 UTF-8
# kab_DZ UTF-8
# kk_KZ PT154
```
なるほど！
* **locale-gen** localedefを呼び出すシェルスクリプトみたいです。

https://qiita.com/aosho235/items/58e2e7acd5c2ee3641ff

* **update-locale LANG=ja_JP.UTF-8** ロケールを日本語に設定。
* **apt-get clean && rm -rf /var/lib/apt/lists/** apt キャッシュをクリーンにして、/var/lib/apt/lits を削除することで、イメージのサイズを減らせるベストプラクティスみたいです。

https://docs.docker.jp/engine/articles/dockerfile_best-practice.html

***
# 作ったdocker-compose環境を使ってみる。
まずはbuildしてMySQLコンテナを作っていきます。
docker-mysqlのディレクトリ内で
```:コマンド
docker-compose build
```
```
docker-mysql> docker-compose build
Building db
[+] Building 4.4s (6/6) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 32B                                                                                0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/mysql:8.0.30-debian                                             4.3s
 => [1/2] FROM docker.io/library/mysql:8.0.30-debian@sha256:6d49fc540dac15528491aed83c7127b0c5874e87b5c81d0e89437  0.0s
 => CACHED [2/2] RUN apt-get update     && apt-get install -y locales     && sed -i -E 's/# (ja_JP.UTF-8)/\1/' /e  0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:967a578ba2ec984ed705d653fe49ae1443b02b0b054d8e5f7182512e318e68e3                       0.0s
 => => naming to docker.io/library/docker-mysql_db                                                                 0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
docker-mysql>
```
ビルドできました！
次にコンテナを建てます。
```:コマンド
docker-compose up -d
```
```
docker-mysql> docker-compose up -d
Creating network "docker-mysql_default" with the default driver
Creating docker-mysql_db_1 ... done
docker-mysql>
```
建ちましたね。
きちんと建ったか確認して見ます。
```:コマンド
docker-compose ps
```
```
docker-mysql> docker-compose ps
      Name                    Command             State                 Ports
-------------------------------------------------------------------------------------------
docker-mysql_db_1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp, 33060/tcp
docker-mysql>
```
大丈夫ですね。

***
***
### コンテナがすぐ落ちたり、立ち上がらなくなってしまったら
**SQL文が間違っているとエラーをはいてコンテナが数秒立ち上がったのに`Exit 1`になり落ちてしまうことがあります。**

```
docker-mysql> docker-compose ps
      Name                    Command             State    Ports
----------------------------------------------------------------
docker-mysql_db_1   docker-entrypoint.sh mysqld   Exit 1
docker-mysql>
```
コンテナが立ち上がるように修正していきたいと思います。
ログ見てエラーを確認します。
```:コマンド
docker-compose logs
```

```
db_1  | ERROR 1064 (42000) at line 10: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 48
```
エラーが見えれました。
SQL文が間違っているようです。
SQLファイルを修正するため、まずコンテナを破壊(down)します。
```:コマンド
docker-compose down
```
SQLファイルを修正します。(INSERT文の48項目目にいらない「,」をつけていましたので修正しました。)

このままコンテナを起動しても永続化されたボリュームがエラーの状態を保存しているため一度ボリューム削除する必要があります。
ボリュームを見てみます。
```:コマンド
docker volume ls
```
```
DRIVER    VOLUME NAME
local     c58542dcb9f8b981c3daa9cef6e2e07c9bf7807cc04f00318f7d4f149277b15a
local     db9fde8114f175621fdc22c7fd9c20697032361c735fbf0e358d451cee530ab2
local     docker-mysql_sql
```
docker-mysql_sqlがありました。(ディレクトリ名:docker-mysql, ボリューム名:sqlになるっぽいですね。)
ボリュームを削除します。
※`docker-compose down`していないと使ってる状態なのでボリュームの削除ができません。
```:コマンド
docker volume rm docker-mysql_sql
```
```
docker-mysql> docker volume rm docker-mysql_sql
docker-mysql_sql
docker-mysql>
```
```:コマンド
docker volume ls
```
ボリュームが消えました。
コンテナを起動します。
```:コマンド
docker-compose up -d
```
```:コマンド
docker-compose ps
```
```
docker-mysql> docker-compose ps
      Name                    Command             State                 Ports
-------------------------------------------------------------------------------------------
docker-mysql_db_1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp, 33060/tcp
docker-mysql>
```
これで修正完了です。
***
***
### コンテナ内のMySQLを使ってみる
```:コマンド
docker-compose up -d
```
コンテナを起動した状態にします。
```:コマンド
docker-compose exec db bash
```
execコマンドでコンテナの中に入れます。
db:コンテナの名前　bash:シェル(bashが使えないコンテナもあったりします。そういう場合はshを使います。)
```
docker-mysql> docker-compose exec db bash
root@33d333d333c3:/#
```
dbコンテナの中に入ることができました。
MySQLに接続してみます。
```:コマンド
mysql -u$MYSQL_USER -p$MYSQL_PASSWORD
```
```
root@33d333d333c3:/# mysql -u$MYSQL_USER -p$MYSQL_PASSWORD
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.30 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```
MySQLに接続できました。
接続に環境変数を使っていますが使わなくても接続できます。
日本語データが使えるか文字コードを確認します。

https://qiita.com/yukiyoshimura/items/d44a98021608c8f8a52a
```:コマンド
show variables like '%char%';
```
```
mysql> show variables like '%char%';
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.04 sec)

mysql>
```
文字コードは大丈夫ですね。

データベースを見てみます。
```:コマンド
show databases;
```
```
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| sql                |
+--------------------+
3 rows in set (0.01 sec)

mysql>
```
information_schemaとperformance_schemaはMySQLの機能用のデータベースみたいですね。

https://dev.mysql.com/doc/refman/5.6/ja/information-schema.html

https://dev.mysql.com/doc/refman/5.6/ja/performance-schema.html

MYSQL_DATABASEで設定した名前(sql)が初期化テーブルの詰まったデータベースです。
データベースを選択します。
```:コマンド
use sql;
```
```
mysql> use sql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql>
```
データベースを選択できました。
テーブルを見てみます。
```:コマンド
show tables;
```
```
mysql> show tables;
+---------------+
| Tables_in_sql |
+---------------+
| prefecture    |
+---------------+
1 row in set (0.00 sec)

mysql>
```
初期化したテーブルが見えると思います。
サンプルのprefectureテーブルのデータ一覧を取ります。
```
mysql> select * from prefecture;
+----+--------------+--------------------+---------------------+---------------------+
| id | 名前          | カナ               | create_at           | update_at           |
+----+--------------+--------------------+---------------------+---------------------+
|  1 | 北海道        | ホッカイドウ        | 2022-09-04 21:37:51 | 2022-09-04 21:37:51 |
|  2 | 青森県        | アオモリケン        | 2022-09-04 21:37:51 | 2022-09-04 21:37:51 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| 46 | 鹿児島県      | カゴシマケン        | 2022-09-04 21:37:51 | 2022-09-04 21:37:51 |
| 47 | 沖縄県        | オキナワケン        | 2022-09-04 21:37:51 | 2022-09-04 21:37:51 |
+----+--------------+--------------------+---------------------+---------------------+
47 rows in set (0.00 sec)
```
日本語表示は大丈夫ですね。
日本語入力も試してみます。
```
mysql> sselect id, 名前, カナ from prefecture;
+----+--------------+--------------------+
| id | 名前         | カナ               |
+----+--------------+--------------------+
|  1 | 北海道       | ホッカイドウ       |
|  2 | 青森県       | アオモリケン       |
|  3 | 岩手県       | イワテケン         |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| 45 | 宮崎県       | ミヤザキケン       |
| 46 | 鹿児島県     | カゴシマケン       |
| 47 | 沖縄県       | オキナワケン       |
+----+--------------+--------------------+
47 rows in set (0.00 sec)
```
日本語入力もしっかりできました。
***
### 後片付け
コンテナの終了作業をします。
`exit`(または、Ctrl＋D)を2回打ってMySQLとコンテナから抜けます。
```
mysql> exit
Bye
root@33d333d333c3:/# exit
exit
docker-mysql>
```
作業が終わったらコンテナを破壊します。
`down`しないとコンテナが起動し続けるので必ず破壊します。
`docker-compose`にはコンテナを停止するだけの`stop`というコマンドもありますがなぜ`down`を使うのか？はこちらの記事を

https://zenn.dev/fagai/articles/55c1b34172ca5a0bce09
```:コマンド
docker-compose down
```
```
docker-mysql> docker-compose down
Stopping docker-mysql_db_1 ... done
Removing docker-mysql_db_1 ... done
Removing network docker-mysql_default
docker-mysql> 
```
破壊されたか確認します。
```:コマンド
docker-compose ps
```
```
docker-mysql> docker-compose ps
Name   Command   State   Ports
------------------------------
docker-mysql>
```
片付けができました。

以上になります。

後は、好きにいじり倒してください。
***
## 終わり
日本語入力できるようになりましたが、入力が確定されるまで文字が表示されないのはちょっと使いづらいですね...
まだまだLinuxやコンテナ周り知識が全然足りなそうです。今回はとても勉強になりました。
これからDockerの研修を受けるのでしっかり勉強していきたいなと感じました。おしまい。