返回

Docker Compose MySQL 认证未知?3招解决报错

mysql

掰扯明白 Docker Compose 里 MySQL 报 “server requested authentication method unknown to the client”

哥们儿,姐们儿,用 Docker Compose 搭 MySQL,结果一连接就跳出来个 “server requested authentication method unknown to the client”?别急,这事儿不罕见。这篇咱就好好说道说道这到底是咋回事,以及咋给它摆平。

直接看问题,你可能用的是类似下面这样的 docker-compose.yml 文件(这里假设文件名是 stack.yml):

# Use root/example as user/password credentials
version: '3.1'

services:

  db:
    image: mysql # 默认会拉取最新的 MySQL 镜像
    restart: always
    environment:
       MYSQL_ROOT_PASSWORD: 'pass'
       MYSQL_DATABASE: 'db'
       MYSQL_USER: 'user'
       MYSQL_PASSWORD: 'pass'

  adminer:
    image: adminer
    restart: always
    ports:
      - 8888:8080

然后你在 stack.yml 文件所在的目录敲了命令:

docker-compose -f stack.yml up

容器跑起来了,看着挺美。可当你想用 Adminer (通过 http://localhost:8888)或者别的数据库客户端连上去,输入用户名 user 密码 pass,Duang!报错了:

错误截图
(截图显示 "server requested authentication method unknown to the client")

糟心不?下面咱就来分析分析这背后的门道。

问题根源在哪?

这锅主要得 MySQL 8.0 以上版本背。

MySQL 从 8.0 版本开始,把默认的用户身份验证插件从 mysql_native_password 改成了 caching_sha2_password。这新插件安全性更高,但问题就出在“新”上。

好多老的数据库客户端、驱动程序(比如某些版本的 PHP PDO MySQL 驱动、老的 Navicat、DBeaver 版本,甚至是一些框架自带的库)压根儿还不认识这个 caching_sha2_password。当 MySQL 服务端用这个新插件“热情地”跟客户端打招呼时,客户端一脸懵逼:“哥们儿,你说的啥‘暗号’?我听不懂啊!” 于是,就报了上面那个错。

你的 docker-compose.yml 文件里 image: mysql 没有指定版本标签,Docker 默认会去拉最新的 MySQL 镜像,这通常就是 MySQL 8.x 系列的。所以,你环境里的 MySQL 服务端就用了 caching_sha2_password 作为默认验证方式。而你的 Adminer 或者其他客户端,可能因为版本不够新,或者依赖的连接库不支持这个新插件,就出问题了。

怎么解决这事儿?

别慌,有好几种法子能解决这个问题。挑一个你觉得合适的就行。

方案一:让 MySQL 服务端迁就一下,改回老验证插件

这是最直接,也通常是最省事儿的办法。既然客户端不认识新插件,那就让服务端用回老插件 mysql_native_password

1. 修改 Docker Compose 配置

你可以在 docker-compose.yml 文件里给 MySQL 服务加个启动参数,强制它使用 mysql_native_password 作为默认的身份验证插件。

原理和作用

通过在 MySQL 服务启动时传递 command 参数 --default-authentication-plugin=mysql_native_password,MySQL 在初始化或者创建新用户时,就会默认使用 mysql_native_password 插件。这样,即使用户 user 是在容器启动时通过环境变量创建的,也会使用这个旧插件。

操作步骤

修改你的 stack.yml 文件,在 db 服务下添加 command 指令:

version: '3.1'

services:

  db:
    image: mysql
    # 新增下面这行
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
       MYSQL_ROOT_PASSWORD: 'pass'
       MYSQL_DATABASE: 'db'
       MYSQL_USER: 'user'
       MYSQL_PASSWORD: 'pass'
    volumes: # 建议加上数据持久化,否则容器一删数据就没了
      - mysql_data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8888:8080

# 声明一个具名数据卷用于持久化
volumes:
  mysql_data:
注意事项和解释
  1. 重新构建和启动: 修改完 stack.yml 后,需要停止并移除旧的容器(如果已经启动过),然后重新启动。

    docker-compose -f stack.yml down # 停止并移除旧容器(如果之前跑过)
    docker-compose -f stack.yml up -d # -d 表示后台运行
    

    如果之前没有加 volumes 来持久化数据,那旧容器一删,里面的数据(包括已经用 caching_sha2_password 创建的用户)就没了,MySQL 会重新初始化。如果加了持久化并且之前MySQL已经成功初始化了,直接改 command 可能不会对已经存在的用户立即生效(除非删掉旧的volume数据让它重新初始化)。所以,最好是在第一次部署,或者可以接受MySQL重新初始化数据时用这个方法。

  2. MYSQL_USER 的创建: 当 MySQL 容器启动时,它会检查 MYSQL_ROOT_PASSWORDMYSQL_DATABASEMYSQL_USERMYSQL_PASSWORD 这些环境变量。如果设置了 MYSQL_USERMYSQL_PASSWORD,它会尝试创建一个普通用户。有了 --default-authentication-plugin=mysql_native_password 这个启动参数,这个新创建的 user 就会使用 mysql_native_password 插件。

安全建议

虽然 mysql_native_password 解决了兼容性问题,但 caching_sha2_password 在安全性上确实更胜一筹。如果你的客户端条件允许(比如可以升级),长远来看,还是尽量适配新的验证插件比较好。不过,对于开发环境或者一些内部系统,为了方便,用回老插件也问题不大,但生产环境要谨慎评估。

进阶使用技巧:针对已存在用户修改认证插件

如果你不想重新初始化整个数据库,只是想修改某个已经存在的用户的认证插件(比如之前没加 command,MySQL 已经启动并创建了用户 user),你可以连接到 MySQL 容器内部,用 SQL 命令修改。

  1. 首先,用 root 用户(它在初始创建时可能因为你的客户端兼容性问题也登不上,如果你的客户端只认 native password,那 root 也得改)或者一个能登录的账户(如果root一开始就能用pass通过你当前客户端登录,那就好办)。
    假设你的容器名叫 yourproject_db_1(可以通过 docker ps 查看):

    docker exec -it yourproject_db_1 mysql -u root -p
    

    输入 MYSQL_ROOT_PASSWORD(也就是 pass)。

  2. 进入 MySQL 命令行后,执行:

    -- 修改 'user'@'%' 用户的认证插件和密码
    ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'pass';
    FLUSH PRIVILEGES;
    

    这里的 'user'@'%' 表示 user 用户可以从任何主机 (%) 连接。你的实际用户和主机配置可能不同,按需修改。'pass' 应该是你为 user 设置的密码。

    如果 root 用户也需要改(比如你的 Adminer 最开始连 root 都连不上):

    ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'pass'; -- 假设root的密码也是'pass'
    FLUSH PRIVILEGES;
    

    注意: 生产环境中不建议 root 用户使用弱密码和允许任意主机连接。

这个进阶技巧在你无法或不想通过修改 docker-compose.yml 中的 command 来解决问题时很有用。但对于通过环境变量初始创建的用户,还是直接用 command 更方便。

方案二:升级你的客户端或连接驱动

治标不如治本。如果条件允许,把你的数据库客户端工具或者应用程序里的 MySQL 连接驱动升级到支持 caching_sha2_password 的新版本是更好的选择。

原理和作用

新版本的客户端/驱动程序内置了对 caching_sha2_password 插件的支持。当 MySQL 8+ 服务端要求使用此插件时,客户端能够正确响应,完成身份验证。

操作步骤

这个就得看你具体用的是什么客户端了。

  • 图形化客户端 (如 Navicat, DBeaver, Sequel Pro/Ace, DataGrip 等):
    去它们的官网看看有没有新版本,下载安装最新的。通常新版本都会跟进主流数据库的更新。

  • Adminer:
    你的 docker-compose.yml 中用的是 image: adminer,这会拉取 Adminer 的 latest 标签,通常包含了最新的稳定版 Adminer。理论上,最新的 Adminer 应该支持 caching_sha2_password
    如果 Adminer latest 仍然有问题(虽然可能性较小),你可能需要检查 Adminer 镜像的构建时间和 MySQL 8 发布的时间。不过一般官方 latest 都会是比较新的。另一种可能是 Adminer 依赖的 PHP 环境里的 MySQL 扩展版本问题,但这个通常在 Docker 镜像里会处理好。
    确认一下: 你的 Adminer 容器是新拉取的吗?如果不是,尝试 docker pull adminer 更新一下本地的 adminer:latest 镜像,然后 docker-compose downdocker-compose up

  • 代码中的连接驱动 (如 PHP 的 PDO_MYSQL, Python 的 mysqlclient/PyMySQL, Node.js 的 mysql/mysql2 等):

    • PHP: 确保你的 PHP 版本足够新(PHP 7.2.8+ 对 caching_sha2_password 有更好的默认支持,PHP 7.4+ 基本没问题),并且 php_mysqlnd (MySQL Native Driver) 扩展是启用的。如果用的是老的 php_mysql 扩展(已废弃),那肯定不行。
    • Python: mysqlclient >= 1.4.0,PyMySQL >= 0.9.3 支持。更新你的库:pip install --upgrade mysqlclientpip install --upgrade PyMySQL
    • Node.js: mysql2 库比老的 mysql 库支持得更好。用 npm install mysql2yarn add mysql2,然后代码里 require('mysql2')
    • Java (JDBC): Connector/J 驱动版本 8.0.11 或更高。

安全建议

保持软件最新是基本的安全实践。升级不仅能解决兼容性问题,通常也能修复已知的安全漏洞,获得性能改进和新功能。

进阶使用技巧

有些连接库即使支持 caching_sha2_password,也可能需要特定的连接参数来显式启用或处理它。具体查阅你所用语言/框架的 MySQL 连接库文档。但多数情况下,只要版本够新,它会自动协商处理。

方案三:在 Docker Compose 中指定旧版 MySQL 镜像

如果你对 MySQL 8 的新特性没啥特殊需求,或者就是想快速解决问题,而且你的应用在 MySQL 5.7 上跑得好好的,那干脆就用回 MySQL 5.7 版本的镜像。

原理和作用

MySQL 5.7 及更早版本默认使用 mysql_native_password 作为身份验证插件。通过指定使用这些旧版本的镜像,你就从源头上避开了 caching_sha2_password 带来的麻烦。

操作步骤

修改你的 stack.yml 文件,在 db 服务的 image 处明确指定 MySQL 5.7 的标签:

version: '3.1'

services:

  db:
    image: mysql:5.7 # 把 image: mysql 修改为 image: mysql:5.7
    restart: always
    environment:
       MYSQL_ROOT_PASSWORD: 'pass'
       MYSQL_DATABASE: 'db'
       MYSQL_USER: 'user'
       MYSQL_PASSWORD: 'pass'
    volumes:
      - mysql_data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8888:8080

volumes:
  mysql_data:
注意事项
  1. 重新构建和启动: 同样,修改完 stack.yml 后,执行:

    docker-compose -f stack.yml down
    docker-compose -f stack.yml up -d
    

    如果之前已经用了 MySQL 8 的镜像并产生了数据,并且这些数据跟 MySQL 8 的特性强相关,直接换成 5.7 可能会有数据不兼容的问题(虽然常见场景下,如果应用本身没用8的新特性,直接换通常问题不大,但还是要注意)。新的空数据库用 5.7 初始化肯定没问题。

  2. 版本选择: mysql:5.7 是一个常用的选择。你也可以在 Docker Hub (hub.docker.com/_/mysql/tags) 上查找其他 MySQL 5.x 系列的具体版本标签。

安全建议

使用旧版本的软件意味着你可能错过了新版本中的安全补丁和功能改进。MySQL 5.7 虽然稳定且广泛使用,但官方对它的主流支持已经结束,扩展支持也快到期了。如果不是临时过渡或者有特殊原因,长远来看,还是建议迁移到更新的 MySQL 版本,并解决客户端兼容性问题。

进阶使用技巧

如果你团队有多个项目,有的需要 MySQL 8,有的需要 5.7,可以在不同的 docker-compose.yml 项目里指定不同的 MySQL 版本镜像,互不干扰。这就是 Docker 的魅力所在嘛。

小结一下哪个方案好?

  • 快速解决,图省事(尤其开发环境): 方案一,修改 docker-compose.yml 添加 command,让 MySQL 8 用回老插件。这是最常见的折中办法。
  • 追求最佳实践,提升安全性: 方案二,升级你的客户端或连接驱动。这是长远来看最好的路子,拥抱新技术,安全有保障。
  • 特定场景,或者对 MySQL 8 没需求: 方案三,直接用 MySQL 5.7 镜像。简单粗暴,但要注意旧版本软件的生命周期问题。

针对你提供的 docker-compose.yml 和场景(通过 Adminer 访问新搭建的 MySQL),方案一可能是最快见效的。如果你的 Adminer 是通过 image: adminer 拉取最新的,理论上它应该兼容 MySQL 8 的新验证插件,但有时候实际情况复杂,可能是 Adminer 镜像内部环境(比如PHP版本或扩展)有细微滞后。所以,强制MySQL服务端使用老插件能最快绕过这个问题。

选哪个,看你的具体情况和偏好了。希望这几招能帮你把这个烦人的 "unknown to the client" 给彻底拿下!