Dockerの volumes
についてあまり知らなかったゆえにハマったのだが……
次のようにdocker-composeでホスト上の任意のディレクトリにマウントさせるようなVolumeを設定すると、環境によっては コンテナ内のユーザーが持っている権限とマウントされたディレクトリの権限が一致せずコンテナが立ち上がらないことがあります。
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のデータのバックアップなど「データの移動」は別の方法を考えましょう。
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 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のコンテナを使う場合は次のとおりです
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
に追記する形に変更します。
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'
LINUX_MYSQL_UID=501
LINUX_MYSQL_GID=501
実行環境のuid, gidを取得して.env
に追記するシェルスクリプトを同梱することで立ち上げも簡単になります(その代わり実行するのを忘れると設定すべきuid, gidが空っぽになります)
#!/bin/sh
echo "LINUX_MYSQL_UID=$(id -u $USER)" >> .env
echo "LINUX_MYSQL_GID=$(id -g $USER)" >> .env
./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