MySQLとDockerとRailsの備忘録


## はじめに

一つ前の記事でDocker上でMySQLを使うことが出来なかったので、リベンジです。

https://qiita.com/kajiyai/items/46958afbecb0a4f941a7


mysqlの公式を参考にします

https://hub.docker.com/_/mysql

## キーワード

MySQL, Docker, docker compose, docker network

## 目次

1. コンテナのシェルから入る
2. MySQL コマンド ライン クライアントから MySQL に接続する
3. docker-compose経由でMySQLに接続する
4. まとめ
5. 参考

## コンテナのシェルから入る

`docker run` はイメージを`create`と`start`を同時に行います。

```ruby
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:latest
```

引数は以下の通りです

- —name
    - コンテナの名前を付ける
- -e
    - 環境変数の定義
- - d
    - バックグラウンドで実行し、実行後も通常のターミナルとして使える
    - detachとatachは対義語
    - 中身を見たいときは`docker logs`

-dオプションについて

- [https://qiita.com/Mi_tsu_ya/items/49a5e983f774f1e932e7](https://qiita.com/Mi_tsu_ya/items/49a5e983f774f1e932e7)
- [https://qiita.com/R_R/items/40d256389b59b66eaf6b](https://qiita.com/R_R/items/40d256389b59b66eaf6b)
- [https://maku77.github.io/p/dmpsvz3/](https://maku77.github.io/p/dmpsvz3/)

先ほど動かしたdockerコンテナのシェルに入るためのコマンド

```ruby
$ docker exec -it some-mysql bash
```

引数は以下の通りです

- exec
    - 現在動いているコンテナに対して行うコマンドを渡すコマンド
    - [https://docs.docker.com/engine/reference/commandline/exec/](https://docs.docker.com/engine/reference/commandline/exec/)
- -it
    - **標準入出力をコンテナに結びつける(キー入力する場合)**。が一番しっくり
    - 色々調べた
        - -iと-tの二つのコマンドを含む
        - -i or -interactiveと -t or -tty
            - ttyはUNIXのコマンドで、標準入出力先のデバイスの事をさすみたいです
                - [https://zenn.dev/hohner/articles/43a0da20181d34](https://zenn.dev/hohner/articles/43a0da20181d34)
        - [https://qiita.com/TaaaZyyy/items/4ecf21f23e6730faf696](https://qiita.com/TaaaZyyy/items/4ecf21f23e6730faf696)
- some-mysql
    - さきほど作成したコンテナの名前です
- bash
    - bashはLinuxの標準シェル
    - なんのコマンドを実行してもいい
        - `docker exec some-mysql ls`とか打つと、配下のディレクトリが見れる
        - [https://www.memotansu.jp/docker/591/#toc4](https://www.memotansu.jp/docker/591/#toc4)

シェルに入り、mysqlのCLIに入るコマンドを打ちました

```bash
bash-4.4# mysql -u root -pmy-secret-pw
```

成功しました

## MySQL コマンド ライン クライアントから MySQL に接続する

```bash
$ docker run -it --network some-network --rm mysql mysql -hsome-mysql -uexample-user -p
```

引数は以下の通りです(前述のものは省略)

- —network
    - ネットワークを指定する
    - (という事はネットワークが作成されている必要がある)

[https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/commandline/run/#ネットワークへのコンテナーの接続---network](https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/commandline/run/#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%B8%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%83%BC%E3%81%AE%E6%8E%A5%E7%B6%9A---network)

dockerのnetworkについていろいろ調べた

- [https://qiita.com/TsutomuNakamura/items/ed046ee21caca4a2ffd9](https://qiita.com/TsutomuNakamura/items/ed046ee21caca4a2ffd9)
- [https://knowledge.sakura.ad.jp/23899/](https://knowledge.sakura.ad.jp/23899/)

- -rm
    - 終了時に中間コンテナを自動で削除する
- -h or -hostname
    - ホストとなるコンテナ名を指定する
- -u or -user
    - ユーザー名またはUID
- -p
    - パスワードの要求

some-networkという名前のネットワークを作ってみます

正直、bridge,host,noneの違いは説明できないですが、bridgeがdockerの特徴なのかなと思いました。

```bash
docker network create -d bridge some-network
```

``docker network ls``を打つとsome-networkが表示されました。

- 参考

[https://docs.docker.com/engine/reference/commandline/network/](https://docs.docker.com/engine/reference/commandline/network/)

では、もともとのコマンドを打ってみます

```bash
ERROR 2005 (HY000): Unknown MySQL server host 'some-mysql' (-2)
```

上記のようなエラーが出たのでググると、良さそうなissueが見つかりました

[https://github.com/docker-library/mysql/issues/644#issuecomment-664107416](https://github.com/docker-library/mysql/issues/644#issuecomment-664107416)

僕はまだネットワークとコンテナを関連付けられていないのかな、という事で下記のコマンドを打ってみました

```bash
$ docker network connect some-network some-mysql
```

`docker network inspect some-network`コマンドを打ってみたら、コンテナが含まれています

さて、元のコマンドを再度打ってみます

```bash
ERROR 1045 (28000): Access denied for user 'example-user'@'192.168.48.3' (using password: YES)
```

PWを要求されたので、`my-secret-pw`を打ってみたら見覚えのあるエラーが返ってきました！！

rootにすればいいのかな、と思い、-urootに変更して、PWを打ってみます

```bash
$ docker run -it --network some-network --rm mysql mysql -hsome-mysql -uroot -p
```

成功しました

## docker-compose経由でMySQLに接続する

- mysql_docker_testフォルダを作成、配下にdocker-compose.ymlファイルを置く

内容は公式の通りにする

```bash
# Use root/example as user/password credentials
version: '3.1'

services:

  db:
    image: mysql
    # NOTE: use of "mysql_native_password" is not recommended: https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html#upgrade-caching-sha2-password
    # (this is just an example, not intended to be a production configuration)
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

```

adminerとは

phpで単一ファイルに書かれたデータベース管理ツールのことらしいです

> Adminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consist of a single file ready to deploy to the target server. Adminer is available for MySQL, PostgreSQL, SQLite, MS SQL, Oracle, Firebird, SimpleDB, Elasticsearch and MongoDB.
> 

[https://hub.docker.com/_/adminer/](https://hub.docker.com/_/adminer/)

なにはともあれ　`docker compose up` コマンドを打ってみました

ユーザー: root

パスワード: example

adminerコンテナ

[http://localhost:8080/](http://localhost:8080/)を訪れるとまさにデータベース管理ツールが起動されていました。

sequel proとかの一番簡易なものですかね。

ここで上記のユーザー、パスワードを入力すると、ログインできました。

dbコンテナ

docker desktopからターミナルに行き、

`mysql -u root -pexample`

とmysqlのコマンドラインに入ろうとすると、普通にログイン出来ました。

ここで、前の記事のdocker-compose.ymlファイルと比較して異なるのは以下の指定です。そのため、少し調べてみました

```bash
    command: --default-authentication-plugin=mysql_native_password
    restart: always
```

docker-compose.ymlにおいて

- command
    - デフォルトのコマンドを上書きする
    - [https://docs.docker.jp/v1.12/compose/compose-file.html#id20](https://docs.docker.jp/v1.12/compose/compose-file.html#id20)

--default-authentication-plugin=mysql_native_passwordについて

- 古いハッシュ化アルゴリズムみたいです
    - 現在は非推奨で、ed25519というプラグインが推奨されています

[https://mariadb.com/kb/en/authentication-plugin-mysql_native_password/](https://mariadb.com/kb/en/authentication-plugin-mysql_native_password/)

[https://mariadb.com/kb/en/authentication-plugin-ed25519/](https://mariadb.com/kb/en/authentication-plugin-ed25519/)

- restart
    - docker run のコマンドオプションに対応している
    - コンテナ終了時の再起動の振る舞いについて規定している
        - alwaysは終了ステータスに関わらず再起動する
    - [https://docs.docker.com/engine/reference/run/#restart-policies---restart](https://docs.docker.com/engine/reference/run/#restart-policies---restart)

これらを前記事のdocker-compose.ymlファイルに追加したらmysqlが動くようになる・・・？(そんな気はしないですが…)

上記を追加し、以下の手順を試してみました

```bash
$ docker volume ls
$ docker volume rm xxx
$ docker compose up -d -build
```

`docker volume ls`でNAMEが表示されなかったのは不便だなと思いました。docker-compose.ymlファイルでvolumeの名前を指定することもできたはずなので指定すべきだなと思いました

docker desktopで調べてみると、関係ないvolumeでした。(volume作成されてない…??)

再度試してみてもやはりうまくいかなかったです。

もはや公式のみを参照するというルールを一度棚に上げておき、ブログ記事の通りにやってみた

- [https://kiyobl.com/rails-docker-basic/](https://kiyobl.com/rails-docker-basic/)

普通にうまくいきました。

異なるのはDockerfileぽいですね。

以前のDockerfile

```docker
# syntax=docker/dockerfile:1
FROM ruby:3.2
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Configure the main process to run when running the image
CMD ["rails", "server", "-b", "0.0.0.0"]
```

参考にさせて頂いたブログ記事のDockerfile

```docker
FROM ruby:3.1

WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN bundle install

CMD ["rails", "server", "-b", "0.0.0.0"]
```

ちなみに、postgresqlを使用して成功した時のDockerfileも載せておきます

```docker
# syntax=docker/dockerfile:1
FROM ruby:3.2
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3001

# Configure the main process to run when running the image
CMD ["rails", "server", "-b", "0.0.0.0"]
```

これらを踏まえると、entrypoint.shが悪さをしているわけではなさそうで、恐らく以下のコードがおかしいのかと予想が付きます

```docker
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client
```

`postgresql-client`ではうまくいき、`default-mysql-client`ではうまくいかない。

nodejsはDBとは全く関係なさそうなので無視してます。

psqlコマンドやmysqlコマンドを使用できるように、クライアント-サーバー通信を実現するためのアプリケーションをインストールしよう、というコマンドだと理解してます

もう一度初めから、手順を行ってみました。

なぜかうまくいきました。結局コードは悪くなく、何らかの手順を飛ばしていた可能性がありそうです。

変更した点としては、以下のようにバージョンを明示しました

```docker
docker-compose.yml
 mysql:8.0.3

Gemfile
 gem "rails", "~> 7.0.4"

```

また、`docker compose run --no-deps web rails new . --force --database=mysql`コマンドを打った時に待ちきれずCtrl+cを打つなどをしてしまったかもしれないです

## まとめ

一度やり直した方が早いこともある。

回り道をしながら知識を拾っていこう

ではまた。

今度こそmakeツールを使いたい！

## 参考

- [https://hub.docker.com/_/mysql](https://hub.docker.com/_/mysql)