Docker部署Rails 6连接MySQL报错:Access denied for user 'user'@'%'
核心问题分析
你遇到的Mysql2::Error::ConnectionError: Access denied for user 'user'@'%' to database 'db_production'错误,核心大概率是数据库用户权限不足或配置不一致导致的,结合Ubuntu版本差异、手动连接正常但db:create失败的现象,以下是具体排查和解决步骤:
解决步骤
1. 检查数据库用户权限(最可能原因)
MySQL官方镜像通过MYSQL_USER创建的用户,默认仅拥有登录权限,没有创建数据库的权限——这就是为什么你能手动连接数据库,但执行db:create会报错的原因(db:create需要用户具备CREATE DATABASE权限)。
临时验证方法:
进入MySQL容器,给用户授予创建数据库的权限:
# 进入MySQL容器,输入环境变量中的PRODUCTION_MYSQL_PASSWORD docker exec -it database_production mysql -u user -p
在MySQL控制台执行:
GRANT CREATE ON *.* TO 'user'@'%'; FLUSH PRIVILEGES;
退出后重新执行bundle exec rails db:create,如果能成功,说明确实是权限问题。
永久解决方法:
创建MySQL初始化脚本,让容器启动时自动授权:
- 在项目根目录创建
init.sql文件:
CREATE DATABASE IF NOT EXISTS db_production; GRANT ALL PRIVILEGES ON db_production.* TO 'user'@'%'; FLUSH PRIVILEGES;
- 修改
docker-compose.yml中database_production的volumes配置,添加初始化脚本挂载:
database_production: # ...其他配置 volumes: - dbdata_production:/var/lib/mysql - mysql_socket:/var/run/mysqld - ./init.sql:/docker-entrypoint-initdb.d/init.sql # 新增这一行
这样MySQL容器第一次启动时会自动执行脚本,创建数据库并授予完整权限。
2. 修正database.yml的密码配置
你的database.yml中密码是硬编码的"some_password",但Docker环境变量中使用的是APP_DATABASE_PASSWORD=${PRODUCTION_MYSQL_PASSWORD},可能存在密码不匹配的情况(手动连接时你用的是正确的环境变量密码,但db:create用的是硬编码值)。
修改database.yml的production配置:
production: <<: *default host: database_production port: 3306 database: db_production username: user password: <%= ENV['APP_DATABASE_PASSWORD'] %> # 改为读取环境变量 socket: "/var/run/mysqld/mysqld.sock"
3. 替换sleep为健康检查,确保MySQL完全就绪
sleep 5是不可靠的,Ubuntu 20.04和22.04的MySQL启动速度可能不同,导致Rails容器启动时MySQL还没完全准备好接受连接。
修改docker-compose.yml,给MySQL添加健康检查,并让Rails容器等待MySQL就绪:
database_production: # ...其他配置 healthcheck: test: ["CMD", "mysqladmin", "ping", "-uuser", "-p${PRODUCTION_MYSQL_PASSWORD}", "-h", "localhost"] interval: 5s timeout: 5s retries: 5 app_production: # ...其他配置 depends_on: database_production: condition: service_healthy # 替换原来的depends_on配置
4. 检查MySQL Socket权限(针对Ubuntu 20.04差异)
Ubuntu 20.04的Docker环境可能导致共享socket的权限问题,验证方法:
# 查看MySQL容器内socket权限 docker exec -it database_production ls -l /var/run/mysqld/mysqld.sock # 查看Rails容器内socket权限 docker exec -it app_production ls -l /var/run/mysqld/mysqld.sock
如果Rails容器无法读取socket,可在MySQL容器的command中添加--user=mysql(确保socket属于mysql用户,而Rails容器以root运行,默认有权限读取):
database_production: command: mysqld --default-authentication-plugin=mysql_native_password --user=mysql
内容的提问来源于stack exchange,提问作者Simon Mo

