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

6 min read読了の目安(約5500字

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

2020/11/03 追記: 2020/11/02 にリリースされた 2.5.0.0 で修正されていないことも確認しました...
2021/02/04 追記: 2020/01/14 にリリースされた 3.1.0 で修正されていないことも確認しました...

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

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

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

この記事は現象と解決策の紹介です。直接の原因の特定には至っていませんのでご容赦ください。

環境

Key Value
Mac OS X 10.15.7
Docker 19.03.13
Docker Desktop 2.4.0.0 以降

詳細

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H2
$ docker version
Client: Docker Engine - Community
 Cloud integration  0.1.18
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 16:58:31 2020
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46d9d
  Built:            Wed Sep 16 17:07:04 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

現象

準備

単純な 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

少し深堀り

実際にエラーが出ている部分の ソースコード を見てみます。
下記、該当の関数のコードとなります。

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 が有効な場合の不具合事例