😸

docker-compose.yml を用いたコンテナ起動時の Volume の権限エラーの解説・解決方法

に公開

【Docker】 コンテナが Permission denied で起動しない時の対処法と仕組み

MySQLデータベースのコンテナ構築をすると、権限設定のエラーが発生したので、解決策とその原因を備忘録です。

同じエラーに遭遇した方の助けになれば幸いです。

使用技術

  • M3 MacBook
  • Rancher Desktop

🚨 発生したエラー

以下の docker-compose.yml を書き、コンテナを立ち上げようと docker compose up -d を叩いたところ、コンテナがすぐに終了(Exited)してしまいました。
以下のコマンドでログを確認すると、以下のエラーが出ていました。

$ docker logs <container_id>

[Entrypoint]: Entrypoint script for MySQL Server 8.0 started.
chown: changing ownership of '/var/lib/mysql/': Permission denied
chown: changing ownership of '/var/lib/mysql': Permission denied

原因は?

Permission Denied つまり、権限がないことはわかりました。

chown: changing ownership of '/var/lib/mysql/': Permission denied

ログに記載されているこの chown コマンドは、ファイルやディレクトリの所有権を変更するコマンドです。

MySQLコンテナは、この chown コマンドを使い、起動時にデータ保存先フォルダの所有者を自分(mysqlユーザー)に変更しようとします。

ただ、ホスト(Mac)側も、勝手に所有権を変えられるほどヤワではありません。
ホストのファイルシステムが、「勝手に所有権を変えないで!」と、ブロックします。

ここで、解決策は大きく分けて2つあります。

1. ホストのユーザーIDと、コンテナのユーザーIDを同じにする
2. Named Volume を使い、そもそも権限問題を発生させないようにする

何をいってるの? と思うかもしれませんが、一旦みていきましょう。

1番の「ホストのユーザーIDと、コンテナのユーザーIDを同じにする」については、こちらの記事で解説されているので、割愛しようと思います。

https://qiita.com/houchiey/items/ef0321956821c05b4b6a

もっと手軽に行える2番の記事について、今回は深掘りして解説します。

✅ 解決策:Named Volume を使う

まず、結論から。
ローカルのフォルダを直接マウントする(Bind Mount)のをやめ、Dockerの「Named Volume(名前付きボリューム)」 を使う構成に変えることで解決しました。

❌ うまくいかなかった設定 (Bind Mount)

ローカルの ./mysql_data フォルダを直接コンテナに見せる設定です。

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      # ...省略
    volumes:
      # 👇 ここが原因(Macのフォルダを直接指定している)
      - ./mysql_data:/var/lib/mysql

⭕️ 解決した設定 (Named Volume)

Docker管理下のボリューム領域を使う設定に変更します。

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      # ...省略
    volumes:
      # 👇 パスではなく「名前」を指定する
      - db_data:/var/lib/mysql

# 👇 ファイルの末尾にボリューム定義を追加
volumes:
  db_data:

一旦コンテナを削除し、再起動します。

docker compose down

以前のエラーで作成されたフォルダがあれば削除(紛らわしいので)

rm -rf mysql_data

再起動

docker compose up -d

これで無事、MySQLが起動しました!🎉

なぜこれで解決するのか? (仕組みの解説)

「なぜフォルダ指定だとダメで、ボリュームだとOKなのか?」 ここが直感的に分かりにくかったので、前提から仕組みを整理します。

Docker の構造

Docker の仕組みとして、よく取り上げられるのはこんな感じの図だと思います。

https://qiita.com/etaroid/items/b1024c7d200a75b992fc#コンテナ型仮想化

Dockerコンテナは、ホストOS の「カーネル」というものを使います。
まぁ、OSの「核」というイメージですね。
でもこれ、なんのカーネルでもいいわけではなく、「Linux OSのカーネル」を期待しているんです。

MacOSのカーネルは、「Linux OS のカーネル」とは違うので、Mac上で動くDockerコンテナは、macOS 上に Linux仮想マシン (Linux VM) を欲しています。

そこで、Rancher Desktop (Docker Desktop)は、裏で Linux 仮想マシンを立ち上げているのです。

そして、その Linux 仮想マシンの上でコンテナが作動しているんですね。

1. ❌ Bind Mount(フォルダ指定) の場合

これは、「Macのフォルダ」をコンテナに貸し出す 状態です。

OSには ユーザー という概念があり、各操作はユーザーによって行われます。
そして、各ユーザーには ユーザーID と 権限 が割り当てられています。

MySQL: 「データの所有者を ID:999(コンテナ内のMySQLユーザー)に書き換えたい!」
Mac: 「ダメ。ここはMacのファイルシステム(APFS)だ。ID:501(Macのユーザー)のもの!」

結果、 Macのセキュリティが働き、「部外者が勝手に所有権を変えるな」 と chown をブロックします。

✅ Named Volume(ボリューム指定)の場合

これは、「Linux VMの中にあるDocker専用倉庫」にデータを置く 状態です。

場所: データの実体はMac上のフォルダとして直接見える場所ではなく、Linux VM の中(仮想ディスク内) に作られます。

結果: MySQL が chown を実行しても、そこは Linux のテリトリーなので、Linux のルール通りに所有権の変更が許可されます。

つまり、Volume を ホストOS ではなく、Linux の管理下におくことで、権限問題を解決しているのです。

【補足】 docker compose down したら volume のデータは消えないの?

先ほどの説明で、

「VolumeがDockerの管理領域にある」と聞くと、コンテナを消したらデータも消えそうな気がしなくもないのですが、そこは問題ないです。

コンテナ (docker compose down) = データ処理等を行う実体

ボリューム (db_data) = データ保管倉庫

処理する実体を壊しても、倉庫は地面(VM)に残ります。

docker compose down でコンテナを削除しても、ボリュームは保持され続けます。
次に docker compose up した際、同じボリューム名を指定していれば、前回のデータを引き継いで起動します。

※ データを完全に消してやり直したい場合だけ、docker compose down -v(ボリュームも削除)を使えばOKです。

まとめ

Mac等の環境で MySQL の Permission denied が出たら、ファイル権限の衝突を疑う。

ホストのフォルダを直接マウントせず、Named Volume を使うと解決する。

Named Volume は Linux VM の中 にデータを持つため、権限周りのトラブルが起きにくい。

ボリューム内のデータは、コンテナを消しても永続化されるので安心。

Discussion