Docker MySQL实例root用户host配置及API数据库连接问题咨询
1. 为什么API实例的IP发生了变化?
Docker Compose默认会给每个项目创建专属的bridge网络,容器在这个网络里的IP是Docker动态分配的——简单说就是每次容器重启(不管是服务器重启、docker-compose down/up操作,还是容器意外崩溃重启),Docker都可能给API容器分配新的IP,这是它的默认行为,IP本来就不是固定不变的。
至于你之前能正常连接,大概率是两种情况之一:要么你之前手动修改过MySQL root用户的host,允许了当时API容器的IP,但后来DB容器重启时因为没挂载数据卷,导致MySQL的用户配置被重置回初始的localhost;要么你之前API用的是docker-compose.yml里定义的MYSQL_USER(这个用户默认允许从任何IP连接),后来改成用root连接才触发了这个报错。
2. 如何通过Docker Compose/Makefile可持续解决这个问题?
当然可以,绝对不推荐手动修改DB用户这种一次性操作,给你几个靠谱的方案:
方案一:用容器名连接数据库(最推荐)
Docker Compose的默认网络自带DNS解析功能,你直接在API的数据库配置里把主机地址改成容器名db就行,不用管IP怎么变。Docker会自动把db解析成对应容器的当前IP,而且docker-compose.yml里定义的MYSQL_USER默认允许从任何IP(%)连接,完美避开IP变动的问题。
比如你的Django数据库配置要改成这样:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '你的数据库名', 'USER': '你在docker-compose里设的MYSQL_USER', 'PASSWORD': '对应的MYSQL_PASSWORD', 'HOST': 'db', # 这里写容器名,不是IP! 'PORT': '3306', } }
方案二:挂载数据卷持久化MySQL配置
如果一定要用root用户连接,那你得给DB容器挂载一个数据卷,这样MySQL的用户配置和数据就不会因为容器重启而丢失。修改docker-compose.yml的db服务:
services: db: container_name: db image: mysql:5.7 ports: - "3336:3306" environment: MYSQL_ROOT_PASSWORD: *** MYSQL_USER: *** MYSQL_PASSWORD: *** MYSQL_DATABASE: *** restart: always command: [ "mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci", ] volumes: - mysql_data:/var/lib/mysql # 新增这行,挂载命名数据卷 # 在文件末尾新增数据卷定义 volumes: mysql_data:
启动服务后,进入DB容器修改一次root用户的host为%(允许所有IP连接),之后这个配置就会一直保存在数据卷里:
docker exec -it db mysql -uroot -p # 输入root密码后执行下面的SQL ALTER USER 'root'@'%' IDENTIFIED BY '你的root密码'; FLUSH PRIVILEGES;
方案三:用初始化脚本自动配置MySQL
你还可以写一个SQL初始化脚本,让MySQL第一次启动时自动配置root用户的权限。创建一个init-mysql.sql文件:
ALTER USER 'root'@'%' IDENTIFIED BY '你的root密码'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;
然后修改docker-compose.yml的db服务,挂载这个脚本到MySQL的初始化目录:
services: db: # ... 其他配置不变 volumes: - ./init-mysql.sql:/docker-entrypoint-initdb.d/init-mysql.sql - mysql_data:/var/lib/mysql
这样第一次启动DB容器时,脚本会自动执行,后续重启也因为数据卷的存在不会丢失配置。
如果用Makefile的话,你可以把启动、初始化的命令封装起来,比如:
# 启动所有服务 up: docker-compose up -d # 初始化MySQL root用户权限 init-db: docker exec -it db mysql -uroot -p$(DB_ROOT_PWD) -e "ALTER USER 'root'@'%' IDENTIFIED BY '$(DB_ROOT_PWD)'; FLUSH PRIVILEGES;"
执行make up启动服务,make init-db完成初始化(记得替换$(DB_ROOT_PWD)为你的root密码)。
3. 能否将API实例的IP改为localhost?
这行不通的。在Docker容器里,localhost指的是容器自己的网络接口,不是宿主机也不是DB容器。如果API容器把数据库主机设为localhost,它会尝试连接自己内部的MySQL服务,但你的MySQL是在另一个独立容器里,根本连不上。
如果你非要通过宿主机的localhost来连接,可以用Docker提供的host.docker.internal这个特殊域名(指向宿主机),然后用宿主机映射的端口3336,但这种方式不如直接用容器名连接可靠,还多了一层端口转发,性能也会受影响,不推荐。
内容的提问来源于stack exchange,提问作者Tuma

