🐳

Docker DesktopのgRPC FUSE / VirtioFSが有効だとMySQLのentrypoint.shでエラーになる

2020/10/03に公開

Docker Desktop for Mac を 2.4.0.0 に更新したところ、突然 MySQL のコンテナ初期化時に /entrypoint.sh がエラーになったのでメモ。

手っ取り早く解決策(?)

  • gRPC FUSE / VirtioFS を無効化して Docker Desktop を再起動

以下、筆者が遭遇した事例のご紹介です。

環境

M1 Macを購入したので、改めて確認しました

Key Value
Mac OS X 12.4
Docker 20.10.16
Docker Desktop 2.4.0.0 〜 4.8.2

詳細

$ sw_vers
ProductName:    macOS
ProductVersion: 12.4
BuildVersion:   21F79
$ docker version
Client: Docker Engine - Community
 Version:           20.10.16
 API version:       1.41
 Go version:        go1.18.2
 Git commit:        aa7e414fdc
 Built:             Wed May 11 16:22:17 2022
 OS/Arch:           darwin/arm64
 Context:           default
 Experimental:      true

Server: Docker Desktop 4.8.2 (79419)
 Engine:
  Version:          20.10.14
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.15
  Git commit:       87a90dc
  Built:            Thu Mar 24 01:45:44 2022
  OS/Arch:          linux/arm64
  Experimental:     true
 containerd:
  Version:          1.5.11
  GitCommit:        3df54a852345ae127d1fa3092b95168e4a88e2f8
 runc:
  Version:          1.0.3
  GitCommit:        v1.0.3-0-gf46b6ba
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

現象

準備

単純な SQL 文をわざわざシェルを介して実行させていますが、実際は他の処理も挟んでいます。
試してみましたが、 SQL を直接実行する場合はこのエラーは起きませんでした。

.
└── docker-entrypoint-initdb.d
    ├── entry.sh
    └── files
        └── create_db.sql
docker-entrypoint-initdb.d/entry.sh
#!/bin/bash

docker_process_sql < /tmp/files/create_db.sql
docker-entrypoint-initdb.d/files/create_db.sql
CREATE SCHEMA IF NOT EXISTS `sample`;
USE `sample`;
CREATE TABLE IF NOT EXISTS `user` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB;

MySQL のコンテナを起動

すると、謎の Permission denied が起こります。
尚、ボリュームの :ro オプションの有無では特に変化がありませんでした。

$ docker run --rm \
  -v $(PWD)/docker-entrypoint-initdb.d/entry.sh:/docker-entrypoint-initdb.d/entry.sh:ro \
  -v $(PWD)/docker-entrypoint-initdb.d/files:/tmp/files:ro \
  -e MYSQL_ROOT_PASSWORD=password \
  mysql:latest

(略)
[Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/entry.sh
/usr/local/bin/docker-entrypoint.sh: /docker-entrypoint-initdb.d/entry.sh: /bin/bash: bad interpreter: Permission denied

軽く調査

bad interpreter って、あなたさっきまで動いてたじゃない...」と思いつつ、Permission denied とあるのでまずは権限を確認。問題ありません。

# コンテナ内

$ ls -l /tmp/files
total 4
-rw-r--r-- 1 root root 198 Oct  2 17:11 create_db.sql

$ ls -l /docker-entrypoint-initdb.d
total 4
-rw-r--r-- 1 root root 59 Oct  2 17:11 entry.sh

# そんなはずないと思いつつ一応...
$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1168776 Apr 18  2019 /bin/bash

ファイルの権限を変えて実行

一度コンテナから出て権限を変更し、再度実行。エラーが変わります。
シェルの実行はできますが、 docker_process_sql が見つからないと言われてしまいます。

$ chmod -R 777 docker-entrypoint-initdb.d

$ docker run --rm \
  -v $(PWD)/docker-entrypoint-initdb.d/entry.sh:/docker-entrypoint-initdb.d/entry.sh:ro \
  -v $(PWD)/docker-entrypoint-initdb.d/files:/tmp/files:ro \
  -e MYSQL_ROOT_PASSWORD=password \
  mysql:latest

(略)
[Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/entry.sh
/docker-entrypoint-initdb.d/entry.sh: line 3: docker_process_sql: command not found

権限を確認。問題ありません。

# コンテナ内

$ ls -l /tmp/files
total 4
-rwxrwxrwx 1 root root 198 Oct  2 17:11 create_db.sql

$ ls -l /docker-entrypoint-initdb.d
total 4
-rwxrwxrwx 1 root root 59 Oct  2 17:11 entry.sh

$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1168776 Apr 18  2019 /bin/bash

少し深堀り

実際にエラーが出ている部分のソースコードを見てみます。

https://github.com/docker-library/mysql/blob/285fd39122/8.0/docker-entrypoint.sh#L65

下記、該当の関数のコードとなります。

docker_process_init_files()
docker_process_init_files() {
  # mysql here for backwards compatibility "${mysql[@]}"
  mysql=( docker_process_sql )

  echo
  local f
  for f; do
    case "$f" in
      *.sh)
        # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
        # https://github.com/docker-library/postgres/pull/452
        if [ -x "$f" ]; then
          mysql_note "$0: running $f"
          "$f"
        else
          mysql_note "$0: sourcing $f"
          . "$f"
        fi
        ;;
      *.sql)    mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
      *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
      *.sql.xz) mysql_note "$0: running $f"; xzcat "$f" | docker_process_sql; echo ;;
      *)        mysql_warn "$0: ignoring $f" ;;
    esac
    echo
  done
}

すると、下記の不明点が見えてきます。しかしながら、実働環境への影響が無いことを踏まえてこれ以上の調査は断念しました。

  • if [ -x "$f" ]; ... で実行権限のチェックをしているにも関わらず、1回目の実行で ... running ... のログが出ている( 644 にも関わらず)。gRPC FUSE を無効化して実行するとログは ... sourcing ... になり、こちらは期待通りの動作

最後に

2.4.0.0 以降は 他にも色々つらい...

Linux や Docker のファイルシステムは詳しくないので大きな見落としがあるかもしれません。もし何かあればご指摘いただければ幸いです。

参考リンク

現象は異なるものの、同様に gRPC FUSE が有効な場合の不具合事例

https://stackoverflow.com/questions/64146845/mysql-not-starting-in-a-docker-container-on-macos-after-docker-update

Discussion