- 投稿日:2020-07-31T23:59:40+09:00
Docker上のMySQLのデータをvolumeでホストのディレクトリにマウントすると権限周りで面倒なことになる
Dockerの
volumes
についてあまり知らなかったゆえにハマったのだが……次のようにdocker-composeでホスト上の任意のディレクトリにマウントさせるようなVolumeを設定すると、環境によってはコンテナ内のユーザーが持っている権限とマウントされたディレクトリの権限が一致せずコンテナが立ち上がらないことがあります。
docker-compose.ymlversion: "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.ymlversion: "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 inspect
のMountpoint
に記載されている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.ymlcommand: 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.ymlversion: "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'.envLINUX_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/938781ff5898e654fd7cDockerのまとめ - コンテナとボリューム編 - Qiita
https://qiita.com/kompiro/items/7474b2ca6efeeb0df80fGetting a Shell in the Docker for Mac Moby VM · GitHub
https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389
- 投稿日:2020-07-31T23:59:40+09:00
Docker上のMySQLのデータをVolumeでホストのディレクトリにマウントすると権限周りで面倒なことになる
Dockerの
volumes
についてあまり知らなかったゆえにハマったのだが……次のようにdocker-composeでホスト上の任意のディレクトリにマウントさせるようなVolumeを設定すると、環境によっては コンテナ内のユーザーが持っている権限とマウントされたディレクトリの権限が一致せずコンテナが立ち上がらないことがあります。
docker-compose.ymlversion: "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.ymlversion: "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 inspect
のMountpoint
に記載されている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.ymlcommand: 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.ymlversion: "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/938781ff5898e654fd7cDockerのまとめ - コンテナとボリューム編 - Qiita
https://qiita.com/kompiro/items/7474b2ca6efeeb0df80fGetting a Shell in the Docker for Mac Moby VM · GitHub
https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389
- 投稿日:2020-07-31T23:11:17+09:00
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": []} | +---------------+参考
- 投稿日:2020-07-31T20:04:44+09:00
【戒め】すぐ忘れちゃう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;
LIMIT句はMySQLとPostgreSQLぐらいでしか使えないそうなのでタイトル変えました ↩
- 投稿日:2020-07-31T01:24:03+09:00