【Docker】在尝试执行MariaDB的初始化SQL时遇到了困难的故事

这篇帖子是《格倫吉(Glenji)2021年圣诞日历》的第7天文章。

你好,我在株式会社格伦吉担任服务器端工程师。
我是新毕业的岛田(@konnyaku256)。

最近,我有机会使用Docker版MariaDB,并在此过程中遇到了初始化SQL的问题。在本文中,我将总结一下这个故事。

关于MariaDB的初始化

当容器首次启动时,将创建一个具有指定名称的新数据库,并使用提供的配置变量进行初始化。此外,它将执行在/docker-entrypoint-initdb.d目录中找到的具有扩展名.sh,.sql,.sql.gz,.sql.xz和.sql.zst的文件。文件将按字母顺序执行。没有文件执行权限的.sh文件将被源码而不是执行。您可以通过将SQL转储挂载到该目录并提供贡献数据的自定义映像来轻松填充您的mariadb服务。默认情况下,SQL文件将导入到由MARIADB_DATABASE / MYSQL_DATABASE变量指定的数据库中。

引用自 https://hub.docker.com/_/mariadb

根据Docker Hub的说明,Docker版MariaDB可以在/docker-entrypoint-initdb.d目录下放置.sql、.sh、.sql.gz文件,在容器启动时会读取并执行这些文件。

如果使用这个机制,似乎可以执行一些初始化处理,例如创建数据库和用户等。

然而… ‘ér…)

SQL的初始化没有被执行。

使用以下配置创建映像并启动容器。

目录结构

$ tree
.
├── Dockerfile
└── init
    └── init.sql

初始化SQL(最后一行是换行符)

CREATE DATABASE `test`;

Dockerfile 的中文翻译选项:容器文件。

FROM mariadb:5.5.40

COPY init/* /docker-entrypoint-initdb.d/

CMD ["mysqld"]

乍一看,看起来似乎很不错。

我会构建映像并启动容器来尝试。

$ docker build -t mariadb-example ./ && docker run -d -e MYSQL_ROOT_PASSWORD=password --name mariadb-example mariadb-example

我会检查容器的状态。

$ docker ps -a --no-trunc -f name=mariadb-example

CONTAINER ID                                                       IMAGE             COMMAND                          CREATED         STATUS         PORTS      NAMES
6395ad036dfa74597853a2cf1e62d73f30b7446547d2a262a887dc1614c47b2c   mariadb-example   "/docker-entrypoint.sh mysqld"   6 minutes ago   Up 6 minutes   3306/tcp   mariadb-example

从状态来看,似乎能够成功启动。

登录容器并查看详细信息。

$ docker exec -it mariadb-example /bin/bash

我要登录MariaDB数据库。

root@0b4910572e26:/# mysql -ppassword

获取数据库列表,并确认是否创建了名为test的数据库。

MariaDB [(none)]> show databases;

+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

喔!? 初始化SQL中没有写入数据库!

原因是docker-entrypoint.sh

从”docker ps”的结果中可以看出,我们创建的MariaDB容器在启动时执行了”docker-entrypoint.sh”。

COMMAND
"/docker-entrypoint.sh mysqld"

简而言之,查看 docker-entrypoint.sh 可以了解为什么初始化 SQL 未被执行。

我要检查Shell脚本的内容。

root@0b4910572e26:/# cat docker-entrypoint.sh
#!/bin/bash
set -e

if [ ! -d '/var/lib/mysql/mysql' -a "${1%_safe}" = 'mysqld' ]; then
    if [ -z "$MYSQL_ROOT_PASSWORD" ]; then
        echo >&2 'error: database is uninitialized and MYSQL_ROOT_PASSWORD not set'
        echo >&2 '  Did you forget to add -e MYSQL_ROOT_PASSWORD=... ?'
        exit 1
    fi

    mysql_install_db --user=mysql --datadir=/var/lib/mysql

    # These statements _must_ be on individual lines, and _must_ end with
    # semicolons (no line breaks or comments are permitted).
    # TODO proper SQL escaping on ALL the things D:
    TEMP_FILE='/tmp/mysql-first-time.sql'
    cat > "$TEMP_FILE" <<-EOSQL
        DELETE FROM mysql.user ;
        CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
        GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ;
        DROP DATABASE IF EXISTS test ;
    EOSQL

    if [ "$MYSQL_DATABASE" ]; then
        echo "CREATE DATABASE IF NOT EXISTS $MYSQL_DATABASE ;" >> "$TEMP_FILE"
    fi

    if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
        echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" >> "$TEMP_FILE"

        if [ "$MYSQL_DATABASE" ]; then
            echo "GRANT ALL ON $MYSQL_DATABASE.* TO '$MYSQL_USER'@'%' ;" >> "$TEMP_FILE"
        fi
    fi

    echo 'FLUSH PRIVILEGES ;' >> "$TEMP_FILE"

    set -- "$@" --init-file="$TEMP_FILE"
fi

chown -R mysql:mysql /var/lib/mysql
exec "$@"

我理解了之前配置的初始化SQL语句为何没有执行。
在/docker-entrypoint-initdb.d目录下没有找到任何处理。
虽然我想说官方文档是在撒谎,但很可能在这个版本发布时还未实现使用/docker-entrypoint-initdb.d目录进行初始化的功能。

替换docker-entrypoint.sh

由于发现无法使用`/docker-entrypoint-initdb.d`,我们决定考虑替代方案。
这次,我们决定快速替换`docker-entrypoint.sh`。

目录结构

$ tree
.
├── Dockerfile
├── docker-entrypoint.sh
└── init
    └── init.sql

我們將添加以下類似的docker-entrypoint.sh。

#!/bin/bash
set -e

if [ ! -d '/var/lib/mysql/mysql' -a "${1%_safe}" = 'mysqld' ]; then
    if [ -z "$MYSQL_ROOT_PASSWORD" ]; then
        echo >&2 'error: database is uninitialized and MYSQL_ROOT_PASSWORD not set'
        echo >&2 '  Did you forget to add -e MYSQL_ROOT_PASSWORD=... ?'
        exit 1
    fi

    mysql_install_db --user=mysql --datadir=/var/lib/mysql

    # These statements _must_ be on individual lines, and _must_ end with
    # semicolons (no line breaks or comments are permitted).
    # TODO proper SQL escaping on ALL the things D:
    TEMP_FILE='/tmp/mysql-first-time.sql'
    cat > "$TEMP_FILE" <<-EOSQL
        DELETE FROM mysql.user ;
        CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
        GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ;
        DROP DATABASE IF EXISTS test ;
    EOSQL

    if [ "$MYSQL_DATABASE" ]; then
        echo "CREATE DATABASE IF NOT EXISTS $MYSQL_DATABASE ;" >> "$TEMP_FILE"
    fi

    if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
        echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" >> "$TEMP_FILE"

        if [ "$MYSQL_DATABASE" ]; then
            echo "GRANT ALL ON $MYSQL_DATABASE.* TO '$MYSQL_USER'@'%' ;" >> "$TEMP_FILE"
        fi
    fi

    # 初期化SQLを実行
    cat ./initdb/init.sql >> "$TEMP_FILE"

    echo 'FLUSH PRIVILEGES ;' >> "$TEMP_FILE"

    set -- "$@" --init-file="$TEMP_FILE"
fi

chown -R mysql:mysql /var/lib/mysql
exec "$@"

与替换前的不同在于以下一行。
在现有的Shell脚本中,似乎使用变量$TEMP_FILE进行数据库的初始化工作,因此我将初始化SQL的内容追加到$TEMP_FILE的末尾。

# 初期化SQLを実行
cat ./initdb/init.sql >> "$TEMP_FILE"

将文件的权限更改为755。

$ chmod 755 docker-entrypoint.sh

我們將Dockerfile進行以下修正。

FROM mariadb:5.5.40

COPY init/* /initdb/
COPY docker-entrypoint.sh /

CMD ["mysqld"]

重新执行从构建镜像到登录MariaDB的步骤。

获取数据库列表,并检查是否创建了名为”test”的数据库。

MariaDB [(none)]> show databases;

+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)

这次制作得很棒!?

总结

    • Docker版 MariaDB 5.5.40(かなり古い)では /docker-entrypoint-initdb.d ディレクトリにsqlファイルを配置しても初期化を実行できなかった

 

    代わりに docker-entrypoint.sh を置き換えて、初期化SQLが実行されるようにした

因为结构相似,所以即使在MySQL的旧版本中也可能会遇到类似的问题。
虽然很少有机会遇到这种情况,但如果能对某人有所帮助那就很幸运了。

明天gamu先生的文章将会发布。敬请期待!

给出的参考

    • https://hub.docker.com/_/mariadb

 

    https://qiita.com/NagaokaKenichi/items/ae037963b33a85df33f5
广告
将在 10 秒后关闭
bannerAds