Docker 装 MySQL 最大的好处是版本干净、随起随删;最大的坑是默认没有持久化——容器一删数据就没。这篇按「能跑 → 不丢数据 → 好用」三步把 MySQL 8 容器配到生产可用的程度。
一、最短命令(只用来本地试试)
docker run -d --name mysql-test -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:8.4
30 秒能连上,但容器删了数据就没了。正式用请往下看。
二、标准命令:带数据卷和配置
docker run -d \
--name mysql8 \
--restart unless-stopped \
-p 3306:3306 \
-v /data/mysql/data:/var/lib/mysql \
-v /data/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD='你的root密码' \
-e TZ=Asia/Shanghai \
mysql:8.4
逐个解释挂载:
/data/mysql/data:/var/lib/mysql—— 核心。数据落在宿主机,容器升级、重建都不丢。目录不存在 Docker 会自动建,但要确认属主能被容器内 mysql 用户(uid 999)写入。/data/mysql/conf:/etc/mysql/conf.d—— 把自定义 my.cnf 放宿主机这个目录,容器自动加载。
自定义配置 /data/mysql/conf/my.cnf:
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
max_connections = 500
innodb_buffer_pool_size = 512M
slow_query_log = 1
long_query_time = 1
改完 docker restart mysql8 生效,docker exec mysql8 mysql -uroot -p -e "show variables like 'character%'" 验证。
三、建远程账号(别直接暴露 root)
docker exec -it mysql8 mysql -uroot -p
CREATE USER 'app'@'%' IDENTIFIED BY '强密码';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX ON `appdb`.* TO 'app'@'%';
FLUSH PRIVILEGES;
MySQL 8 默认认证插件是 caching_sha2_password,老客户端(PHP 5.x、旧版 Navicat)连不上报 2059 时,要么升级客户端,要么建号时指定 IDENTIFIED WITH mysql_native_password BY '...'。
四、compose 写法(推荐)
services:
mysql:
image: mysql:8.4
container_name: mysql8
restart: unless-stopped
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "你的root密码"
TZ: Asia/Shanghai
volumes:
- ./data:/var/lib/mysql
- ./conf:/etc/mysql/conf.d
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p你的root密码"]
interval: 10s
timeout: 5s
retries: 5
healthcheck 配上之后,depends_on: { condition: service_healthy } 才有意义——否则应用容器比 MySQL 先起,连接失败。
五、常见问题
| 现象 | 原因 | 解法 |
|---|---|---|
容器反复重启,日志 Access denied for root |
数据卷里已有旧数据,环境变量的密码只在首次初始化时生效 | 删掉数据卷目录重新初始化,或进容器手改密码 |
外部连不上,ERROR 2003 |
-p 3306:3306 没加 / 云安全组没放行 |
docker port mysql8 确认映射,再查安全组 |
中文变 ??? |
服务端字符集不是 utf8mb4 | 挂载 conf 按第二节配置 |
| 磁盘被日志写满 | binlog 或容器日志无限增长 | my.cnf 加 binlog_expire_logs_seconds=604800;daemon.json 配 log-opts |
| 时间差 8 小时 | 容器默认 UTC | -e TZ=Asia/Shanghai |
六、备份怎么办
数据卷不等于备份。每天一条 cron 最省事:
docker exec mysql8 sh -c 'exec mysqldump -uroot -p"密码" --all-databases' | \
gzip > /data/backup/mysql-$(date +%F).sql.gz
配合 find /data/backup -mtime +7 -delete 保留最近 7 天。
相关工具:Docker 命令速查 · JSON 格式化 · Linux 命令速查