20200731のMySQLに関する記事は5件です。

Docker上のMySQLのデータをvolumeでホストのディレクトリにマウントすると権限周りで面倒なことになる

Dockerの volumes についてあまり知らなかったゆえにハマったのだが……

次のようにdocker-composeでホスト上の任意のディレクトリにマウントさせるようなVolumeを設定すると、環境によってはコンテナ内のユーザーが持っている権限とマウントされたディレクトリの権限が一致せずコンテナが立ち上がらないことがあります。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    volumes:
      - ./data:/var/lib/mysql
      - ./logs:/var/log/mysql
...

特にDocker for Macで作ったdocker-compose.ymlをLinuxホストのDockerで実行しようとしたときに発生します。

解決法1: データの永続化は名前付きボリュームにする

ホストのディレクトリにマウントするのを諦めて、名前付きボリュームを用いる方法です。
MySQLのデータのバックアップなど「データの移動」は別の方法を考えましょう。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    volumes:
      - db-store:/var/lib/mysql
      - ./logs:/var/log/mysql
    environment:

...

volumes:
  db-store:

ちなみに名前付きボリュームはどこかというと

ホストのディレクトリにVolumeをマウントする理由の一つとして「コンテナで使ったデータは自分が把握しやすい場所で管理したい」というのがあると思います。
先程の名前付きボリュームにデータを永続化させるとデータはDockerによる管理になり、一見するとどこに行ったかわからなくなります。

そこで、docker volume ls でボリュームの名前を調べて、docker volume inspectMountpointに記載されているpathで名前付きボリュームの保存先を調べることができます。

$ docker volume ls
DRIVER              VOLUME NAME
local               1fff58055cdf5d6d088b79ca40970b7cc7b74103647cd2d5cd47292ebc51037b
local               09d44f5c50e868ee1420322768a4bf5de5c6573d48eb9e534c6b2be464542f83
local               sample-docker-compose-api-flask_db-store

$ docker volume inspect sample-docker-compose-api-flask_db-store
[
    {
        "CreatedAt": "2020-02-20T16:00:20Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "sample-docker-compose-api-flask",
            "com.docker.compose.version": "1.25.4",
            "com.docker.compose.volume": "db-store"
        },
        "Mountpoint": "/var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data",
        "Name": "sample-docker-compose-api-flask_db-store",
        "Options": null,
        "Scope": "local"
    }
]

Docker for Macで名前付きボリュームにアクセスできない

Docker for Macの場合、 Mountpoint にそのままアクセスしようとしても「存在しない」とエラーになります。
macOS上でDockerを動作させてvolumeを作成しているのではなく、LinuxKitと呼ばれるVMを立ち上げてその中にvolumesを作成しているからです。

volumes内のファイルを確認するにはVMの中に入る必要があります。

$ ls /var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data
ls: /var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data: No such file or directory

次のコマンドでVMの中に入り、 Ctrl + D を入力します。Ctrl + D を入力するまでの間画面には何も表示されません。

$ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty

...
__  __

Welcome to LinuxKit

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""__/ ===
          {                       /  ===-
           _____ O           __/
                        __/
              _________/

docker-desktop login: root (automatic login)

Welcome to LinuxKit!

NOTE: This system is namespaced.
The namespace you are currently in may not be the root.
System services are namespaced; to access, use `ctr -n services.linuxkit ...`
login[5397]: root login on 'ttyS0'
esktop:~# ls /var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data/
#innodb_temp        binlog.index        mysql
auto.cnf            ca-key.pem          mysql.ibd
binlog.000001       ca.pem              performance_schema
binlog.000002       client-cert.pem     private_key.pem
binlog.000003       client-key.pem      public_key.pem
binlog.000004       homestead           server-cert.pem
binlog.000005       ib_buffer_pool      server-key.pem
binlog.000006       ib_logfile0         sys
binlog.000007       ib_logfile1         undo_001
binlog.000008       ibdata1             undo_002
docker-desktop:~#

Ctrl + A Ctrl + K と入力し、 Really kill this window [y/n] の質問に y と入力すると脱出できます。

最新のDocker for Macでは screen でDockerのVMに入れない

2020/07/31現在のDocker for Macでは、次のように操作が許可されていない旨のエラーが出てVMに入れなくなってます。 sudo で昇格してもダメです。

Cannot exec '~/Library/Containers/com.docker.docker/Data/vms/0/tty': Permission denied

そこで、VMに入るためのコンテナであるnsenter1を使います、

docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh

脱出方法も exit と入力するだけで簡単です。

Docker for Windowsでも使えるか試してみます?

解決策2: コンテナ内のユーザーID(uid, gid)をdocker-compose実行ユーザーのものに揃えておく

ちょっと脱線しましたが、この解決策は ホスト上の任意のディレクトリを指定して コンテナのデータを保存することができます。

docker-compose を実行してコンテナを立ち上げるユーザーで次のコマンドを実行してuid, gidを調べます、

id -u $USER
id -g $USER

次に、 docker-compose.ymlでVolumeを使うサービスのcommandに次を追加します。

docker-compose.yml
    command: bash -c 'usermod -o -u <UID> mysql; groupmod -o -g <GID> mysql; chown -R mysql:root /var/run/mysqld/ /var/log/mysql/ /var/lib/mysql/; /entrypoint.sh mysqld --user=mysql --console'

docker-compose.ymlに環境固有の値を入れるとGitで管理する際に支障が出るため、.env に追記する形に変更します。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    volumes:
      - ./data:/var/lib/mysql
      - ./logs:/var/log/mysql

...

    command: bash -c 'usermod -o -u $LINUX_MYSQL_UID mysql; groupmod -o -g $LINUX_MYSQL_GID mysql; chown -R mysql:root /var/run/mysqld/ /var/log/mysql/ /var/lib/mysql/; /entrypoint.sh mysqld --user=mysql --console'
.env
LINUX_MYSQL_UID=501
LINUX_MYSQL_GID=501

実行環境のuid, gidを取得して.envに追記するシェルスクリプトを同梱することで立ち上げも簡単になります(その代わり実行するのを忘れると設定すべきuid, gidが空っぽになります)

set-environ-uid.sh
#!/bin/sh
echo "LINUX_MYSQL_UID=$(id -u $USER)" >> .env
echo "LINUX_MYSQL_GID=$(id -g $USER)" >> .env

参考

docker composeでMySQLのデータ領域をローカルにマウントする | WEB EGG
https://blog.leko.jp/post/how-to-mount-data-volume-to-local-with-docker-compose/

Docker for MacのDisk Imageの場所が変わった - Qiita
https://qiita.com/amuyikam/items/938781ff5898e654fd7c

Dockerのまとめ - コンテナとボリューム編 - Qiita
https://qiita.com/kompiro/items/7474b2ca6efeeb0df80f

Getting a Shell in the Docker for Mac Moby VM · GitHub
https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker上のMySQLのデータをVolumeでホストのディレクトリにマウントすると権限周りで面倒なことになる

Dockerの volumes についてあまり知らなかったゆえにハマったのだが……

次のようにdocker-composeでホスト上の任意のディレクトリにマウントさせるようなVolumeを設定すると、環境によっては コンテナ内のユーザーが持っている権限とマウントされたディレクトリの権限が一致せずコンテナが立ち上がらないことがあります。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    volumes:
      - ./data:/var/lib/mysql
      - ./logs:/var/log/mysql
...

特にDocker for Macで作ったdocker-compose.ymlをLinuxホストのDockerで実行しようとしたときに「コンテナ内の/var/lib/mysqlにアクセスできない」旨のエラーが発生します。

解決法1: データの永続化は名前付きボリュームにする

ホストのディレクトリにマウントするのを諦めて、名前付きボリュームを用いる方法です。次の例では /var/lib/mysql の部分を名前付きボリュームにしました。
MySQLのデータのバックアップなど「データの移動」は別の方法を考えましょう。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    volumes:
      - db-store:/var/lib/mysql
      - ./logs:/var/log/mysql
    environment:

...

volumes:
  db-store:

ちなみに名前付きボリュームはどこかというと

ホストのディレクトリにコンテナのVolumeをマウントする理由の一つとして「コンテナで使ったデータは自分が把握しやすい場所で管理したい」というのがあると思います。
先程の名前付きボリュームにデータを永続化させるとデータはDockerによる管理になり、一見するとどこに行ったかわからなくなります。

そこで、docker volume ls でボリュームの名前を調べて、docker volume inspectMountpointに記載されているpathで名前付きボリュームの保存先を調べることができます。

$ docker volume ls
DRIVER              VOLUME NAME
local               1fff58055cdf5d6d088b79ca40970b7cc7b74103647cd2d5cd47292ebc51037b
local               09d44f5c50e868ee1420322768a4bf5de5c6573d48eb9e534c6b2be464542f83
local               sample-docker-compose-api-flask_db-store

$ docker volume inspect sample-docker-compose-api-flask_db-store
[
    {
        "CreatedAt": "2020-02-20T16:00:20Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "sample-docker-compose-api-flask",
            "com.docker.compose.version": "1.25.4",
            "com.docker.compose.volume": "db-store"
        },
        "Mountpoint": "/var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data",
        "Name": "sample-docker-compose-api-flask_db-store",
        "Options": null,
        "Scope": "local"
    }
]

Docker for Macで名前付きボリュームにアクセスできない

LinuxホストでDockerを動作させている場合ではMountpoint にそのままアクセスできますが、Docker for Macの場合は「存在しない」とエラーになります。
これはmacOS上でDockerを動作させてvolumeを作成しているのではなく、Dockerが自動的にLinuxKitと呼ばれるVMを立ち上げてその中にVolumeを作成しているからです。

Docker for MacでVolume内のファイルを確認するにはVMの中に入る必要があります。

$ ls /var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data
ls: /var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data: No such file or directory

次のコマンドでVMの中に入り、 Ctrl + D を入力します。Ctrl + D を入力するまでの間画面には何も表示されません。

"Welcome to LinuxKit"と表示され、操作可能になると Mountpoint で示されたpathにアクセスできます。

$ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty

...
__  __

Welcome to LinuxKit

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""__/ ===
          {                       /  ===-
           _____ O           __/
                        __/
              _________/

docker-desktop login: root (automatic login)

Welcome to LinuxKit!

NOTE: This system is namespaced.
The namespace you are currently in may not be the root.
System services are namespaced; to access, use `ctr -n services.linuxkit ...`
login[5397]: root login on 'ttyS0'
docker-desktop:~# ls /var/lib/docker/volumes/sample-docker-compose-api-flask_db-store/_data/
#innodb_temp        binlog.index        mysql
auto.cnf            ca-key.pem          mysql.ibd
binlog.000001       ca.pem              performance_schema
binlog.000002       client-cert.pem     private_key.pem
binlog.000003       client-key.pem      public_key.pem
binlog.000004       homestead           server-cert.pem
binlog.000005       ib_buffer_pool      server-key.pem
binlog.000006       ib_logfile0         sys
binlog.000007       ib_logfile1         undo_001
binlog.000008       ibdata1             undo_002
docker-desktop:~#

Ctrl + A Ctrl + K と入力し、 Really kill this window [y/n] の質問に y と入力すると脱出できます。

最新のDocker for Macでは screen でDockerのVMに入れない

2020/07/31現在のDocker for Macでは、次のように操作が許可されていない旨のエラーが出てVMに入れなくなってます。 sudo で昇格してもダメです。

Cannot exec '~/Library/Containers/com.docker.docker/Data/vms/0/tty': Permission denied

そこで、VMに入るためのコンテナであるnsenter1を使います、

docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh

脱出方法も exit と入力するだけで簡単です。

Docker for Windowsでも使えるか試してみます?

解決策2: コンテナ内のユーザーID(uid, gid)をdocker-compose実行ユーザーのものに揃えておく

ちょっと脱線しましたが、この解決策は ホスト上の任意のディレクトリを指定して コンテナのデータを保存することができます。

docker-compose を実行してコンテナを立ち上げるユーザーで次のコマンドを実行してuid, gidを調べます、

id -u $USER
id -g $USER

次に、 docker-compose.ymlでVolumeを使うサービスのcommandに次の順番でコマンドを追加します。

  • コンテナ内のuid, gidを変更するコマンド
  • ホスト上のディレクトリに紐付けたコンテナ内のディレクトリの所有権を変更するコマンド
  • 通常のコンテナ立ち上げ時に実行されるコマンド

MySQLのコンテナを使う場合は次のとおりです

docker-compose.yml
    command: bash -c 'usermod -o -u <調べたuid> mysql; groupmod -o -g <調べたgid> mysql; chown -R mysql:root /var/run/mysqld/ /var/log/mysql/ /var/lib/mysql/; /entrypoint.sh mysqld --user=mysql --console'

docker-compose.ymlに環境固有の値を入れるとGitで管理する際に支障が出るため、.env に追記する形に変更します。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    volumes:
      - ./data:/var/lib/mysql
      - ./logs:/var/log/mysql

...

    command: bash -c 'usermod -o -u $LINUX_MYSQL_UID mysql; groupmod -o -g $LINUX_MYSQL_GID mysql; chown -R mysql:root /var/run/mysqld/ /var/log/mysql/ /var/lib/mysql/; /entrypoint.sh mysqld --user=mysql --console'
.env(例)
LINUX_MYSQL_UID=501
LINUX_MYSQL_GID=501

実行環境のuid, gidを取得して.envに追記するシェルスクリプトを同梱することで立ち上げも簡単になります(その代わり実行するのを忘れると設定すべきuid, gidが空っぽになります)

set-environ-uid.sh
#!/bin/sh
echo "LINUX_MYSQL_UID=$(id -u $USER)" >> .env
echo "LINUX_MYSQL_GID=$(id -g $USER)" >> .env
この環境で初めてプロジェクトのdocker-composeを使う場合は
./set-environ-uid.sh
docker-compose up -d db

参考

docker composeでMySQLのデータ領域をローカルにマウントする | WEB EGG
https://blog.leko.jp/post/how-to-mount-data-volume-to-local-with-docker-compose/

Docker for MacのDisk Imageの場所が変わった - Qiita
https://qiita.com/amuyikam/items/938781ff5898e654fd7c

Dockerのまとめ - コンテナとボリューム編 - Qiita
https://qiita.com/kompiro/items/7474b2ca6efeeb0df80f

Getting a Shell in the Docker for Mac Moby VM · GitHub
https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL でJSON型のnull/空配列のデータをフィルターする

環境は MySQL 5.7

値が null のレコードを検索する

SELECT data FROM test WHERE JSON_EXTRACT(data, "$.value") = CAST('null' AS JSON);

+-----------------+
| data            |
+-----------------+
| {"value": null} |
+-----------------+

値が 空配列 のレコードを検索する

SELECT data FROM test WHERE JSON_EXTRACT(data, "$.value") = JSON_ARRAY();

+---------------+
| data          |
+---------------+
| {"value": []} |
+---------------+

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【戒め】すぐ忘れちゃうMySQL基本4構文【備忘録】

はじめに

SQLの構文ってすぐ記憶からDROPしませんか?(おつむ弱すぎでは? とか思った人、ちょっと前に出なさい。)
特にやっぱりUPDATEやINSERTあたりはど忘れしやすいですよね。
というわけで今更記事にすることでもないんですが、忘れないために記事にします。

SELECT文

SELECT {表示するカラム名} FROM {テーブル名} WHERE {絞り込み条件} LIMIT {表示する件数} ORDER BY {ソートしたいカラム名} {ASC(昇順)/DESC(降順)};

指定したテーブルからデータを取得してくる構文です。
一番良く使うのでなかなか忘れようがないと思います。
表示するカラム のところはテーブルのカラム名をカンマ区切りで列挙していくのですが、だいたい面倒くさいので * にすることが多いかと思います。全件取得するときは WHERE 句と LIMIT 句は要りません。1
主キーの昇順で良ければ ORDER BY 句も要りません。

下記の例では日本に住んでいる人間を取得しています。

長い例
SELECT id, name, sex, born_at, arrested_at, arrested_reason, note FROM humans WHERE country = 'japan' LIMIT 30 ORDER BY id ASC;
短い例
SELECT * FROM humans;

INSERT文

INSERT INTO {テーブル名} (カラム名) VALUES ();

指定されたテーブルにデータを挿入……というか、新規で作成する構文です。
挿入するデータのカラム名と値はカンマ区切りで記述します。その時カラム名と値は同じ順番で列挙することになります。カラム名の指定は省略することができるのですが、値は省略できないので、労力のことを考えるとカラム名を指定したほうが無難です。

下記の例では生まれたばかりの人間「太郎」を挿入しています。

INSERT INTO humans (name, sex, born_at) VALUES ('太郎', 1, NOW());

UPDATE文

UPDATE {テーブル名} SET {カラム名} = {値} WHERE {絞り込み条件};

指定されたテーブルのカラム名の値を書き換えます。
WHERE 句をすっ飛ばすと当該テーブルの当該カラムは 全部書き換わります。
よく事故るので、UPDATE文実行の前には必ずSELECT文を実行して、どれが書き換わってしまうのか確かめましょう。前職の後輩が本番でやらかして徹夜で復旧してるのを見たことがあります……

下記の例では罪を犯した人間「太郎」がどんな罪を犯したのか、いつ収監されたのかのデータを書き換えています。

UPDATE humans SET has_sin = 1, arrested_at = NOW(), arrested_reason = '不正指令電磁的記録に関する罪' WHERE name = '太郎';

DELETE文

DELETE FROM {テーブル名} WHERE {絞り込み条件};

指定されたデータを削除する構文です。SELECT文とよく似ているのが最大の特徴です。
WHERE 句をすっ飛ばすと当該テーブルの中身は 全部消えます。
よく事故るので、DELETE文実行の前には必ずSELECT文を実行して、どれが消えてしまうのか確かめましょう。やらかす率は思いのほか高いので、事前にdump取るとかした方が良いです。RDSならスナップショットから回復できますが……
何らかの事情でテーブル内を全消ししたいときは TRUNCATE 文を使った方が高速です。

下記の例では罪を抱えた人間を削除しています。

DELETE FROM humans WHERE has_sin = 1;

  1. LIMIT句はMySQLとPostgreSQLぐらいでしか使えないそうなのでタイトル変えました 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Integrity constraint violation: 1452... の解決方法

エラーメッセージ

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails 

ざっくり理解

外部キーとの関係に矛盾があるときのエラー。

解決方法

リレーションしている(外部キーを設定している)カラムの値が間違っていた(指定した値がリレーション先のテーブルに存在しない)ので、それを修正したら治りました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む