Docker Compose 部署 MySQL 数据库指南
版本: 1.0
更新时间: 2025-06-06
维护者: guobin
前言
什么是 Docker Compose?
想象一下:你要开一家餐厅(部署服务),需要同时安排厨师(MySQL)、服务员(Nginx)、收银员(Redis)各司其职。如果一个个招聘、培训、安排工位,太麻烦了。
Docker Compose 就像一个"团队经理",你只需要写一份"排班表"(
docker-compose.yml),它就能一次性把所有员工(容器)安排好,让它们互相配合,同时开工。用 Docker Compose 部署 MySQL,你不需要手动安装数据库、配置环境变量、创建数据目录——一条命令
docker-compose up -d,一切就绪。
本文档会告诉你:
- 部署前的环境准备要求
- Docker 及 Docker Compose 的安装步骤
- 完整的 docker-compose.yml 配置示例
- 部署、验证、排查的完整流程
- 安全最佳实践和数据持久化方案
- 服务停止与卸载步骤
目录
一、环境准备要求
1.1 硬件要求
| 资源 | 最低要求 | 推荐配置 | 说明 |
|---|---|---|---|
| CPU | 2 核 | 4 核+ | MySQL 是 CPU 密集型应用 |
| 内存 | 2 GB | 4 GB+ | MySQL 至少需要 1GB 运行内存 |
| 磁盘 | 10 GB | 50 GB+ | 取决于数据量,建议 SSD |
| 网络 | 100 Mbps | 1 Gbps | 数据库读写频繁时需要高速网络 |
通俗解释:如果 CPU 是厨师,内存是厨房操作台,磁盘是仓库。厨房太小(内存不足),厨师就施展不开;仓库太小(磁盘不足),食材(数据)就没地方放。
1.2 软件要求
| 软件 | 版本要求 | 说明 |
|---|---|---|
| 操作系统 | Linux (CentOS 7+ / Ubuntu 18.04+ / Debian 10+) | 推荐使用 Linux 生产环境 |
| Docker | 20.10+ | 容器运行环境 |
| Docker Compose | 2.0+ | 多容器编排工具 |
1.3 端口要求
| 端口 | 用途 | 说明 |
|---|---|---|
| 3306 | MySQL 默认端口 | 数据库连接端口,确保未被占用 |
# 检查 3306 端口是否被占用
netstat -tuln | grep 3306
# 或
ss -tuln | grep 3306
# 参数解释:
# -tuln:
# -t:TCP 连接
# -u:UDP 连接
# -l:只显示监听的端口
# -n:以数字形式显示(不解析域名,加快速度)
# | grep 3306:过滤包含 3306 的行
# 输出示例(端口空闲):
# (无输出)→ 说明 3306 端口空闲,可以使用 ✅
# 输出示例(端口被占用):
# tcp6 0 0 :::3306 :::* LISTEN
# 说明已有程序占用了 3306 端口,需要先关闭该程序或修改 MySQL 端口
二、安装 Docker 和 Docker Compose
2.1 安装 Docker
Ubuntu / Debian
# 🚀 一键安装 Docker(官方脚本)
curl -fsSL https://get.docker.com | sh
# 参数解释:
# curl:下载文件
# -f:失败时不显示错误页面
# -s:静默模式(不显示进度条)
# -S:出错时显示错误信息
# -L:跟随重定向
# | sh:将下载的内容交给 shell 执行
# 启动 Docker 服务
sudo systemctl start docker
# 设置开机自启
sudo systemctl enable docker
# 验证安装
docker --version
# 输出示例:
# Docker version 24.0.7, build afdf5e4
# 解读:看到版本号 = 安装成功 ✅
CentOS / RHEL
# 🚀 安装 Docker(yum 方式)
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
# 启动并设置自启
sudo systemctl start docker
sudo systemctl enable docker
# 验证
docker --version
2.2 安装 Docker Compose
什么是 Docker Compose?
Docker 是单个集装箱(容器),Docker Compose 是指挥多个集装箱协同工作的"船长"。
你只需要写一份"航海计划"(docker-compose.yml),它就能同时启动多个集装箱,让它们互相配合。
# 🚀 下载 Docker Compose 二进制文件(推荐方式)
sudo curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 参数解释:
# -L:跟随重定向(GitHub 的下载链接会重定向)
# $(uname -s):获取操作系统名称(Linux)
# $(uname -m):获取 CPU 架构(x86_64)
# -o:输出到指定路径
# 添加执行权限
sudo chmod +x /usr/local/bin/docker-compose
# 验证安装
docker-compose --version
# 输出示例:
# Docker Compose version v2.23.0
# 解读:看到版本号 = 安装成功 ✅
2.3 配置 Docker 用户权限(可选)
# 将当前用户加入 docker 组(这样就不用每次都 sudo 了)
sudo usermod -aG docker $USER
# 参数解释:
# usermod:修改用户属性
# -aG:追加到组(a=append, G=group)
# docker:组名
# $USER:当前用户名
# ⚠️ 注意:需要重新登录才能生效
# 重新登录后验证:
docker ps
# 如果不报错,说明配置成功 ✅
三、编写 docker-compose.yml 配置文件
3.1 创建项目目录
# 创建 MySQL 部署目录(幂等操作)
mkdir -p /opt/mysql-docker
# 参数解释:
# -p:父目录不存在时自动创建(幂等,不会报错)
# 进入目录
cd /opt/mysql-docker
# 创建配置文件
touch docker-compose.yml
3.2 完整的 docker-compose.yml 配置
version: '3.8'
services:
mysql:
# 是什么:使用的 Docker 镜像及其版本
# 为什么:8.0 是 MySQL 的长期支持版本,稳定且功能完善
# 怎么用:可以改成其他版本,如 mysql:5.7 或 mysql:8.0
image: mysql:8.0
# 是什么:容器的名称
# 为什么:方便在 docker 命令中引用,如 docker logs mysql-db
# 怎么用:改成你想要的容器名
container_name: mysql-db
# 是什么:容器重启策略
# 为什么:除非手动停止,否则总是重启(保证服务高可用)
# 怎么用:可选值:no(不重启)/ always(总是重启)/ on-failure(失败时重启)/ unless-stopped(除非手动停止)
restart: unless-stopped
# 是什么:端口映射(宿主机端口:容器端口)
# 为什么:让外部能通过 3306 端口访问 MySQL
# 怎么用:如果 3306 被占用,可以改成其他端口,如 13306:3306
ports:
- "3306:3306"
# 是什么:环境变量配置
# 为什么:MySQL 容器启动时读取这些变量进行初始化配置
# 怎么用:修改为你自己的密码和数据库名
environment:
# 是什么:MySQL root 用户的密码(必须设置)
# 为什么:root 是数据库管理员,没有密码容器不会启动
# ⚠️ 生产环境建议使用 .env 文件或 Docker Secret
MYSQL_ROOT_PASSWORD: "MyRootPassword@123"
# 是什么:创建一个普通数据库用户
# 为什么:应用不应该用 root 连接数据库,应该用普通用户
# 怎么用:改成你想要的用户名
MYSQL_USER: "appuser"
# 是什么:普通用户的密码
# 怎么用:改成你想要的密码
MYSQL_PASSWORD: "AppPassword@456"
# 是什么:创建一个初始数据库
# 为什么:MySQL 容器启动时会自动创建这个数据库,并授权给 MYSQL_USER
# 怎么用:改成你想要的数据库名
MYSQL_DATABASE: "myapp_db"
# 是什么:MySQL 服务器的字符集配置
# 为什么:支持中文、emoji 等特殊字符(utf8mb4 是 utf8 的超集)
# 怎么用:一般不需要改
MYSQL_CHARSET: "utf8mb4"
# 是什么:数据卷挂载(宿主机目录:容器内目录)
# 为什么:容器删除后数据不会丢失(数据持久化)
# 怎么用:冒号左边是宿主机路径,右边是容器内路径
volumes:
# 是什么:MySQL 数据目录
# 为什么:存放数据库文件、表数据、索引等(最重要的数据)
# 怎么用:./data 表示当前目录下的 data 文件夹
- ./data:/var/lib/mysql
# 是什么:MySQL 配置文件挂载
# 为什么:自定义 MySQL 配置(如最大连接数、日志格式等)
# 怎么用:先创建 ./conf/my.cnf,然后挂载
- ./conf/my.cnf:/etc/mysql/conf.d/my.cnf
# 是什么:MySQL 初始化脚本目录
# 为什么:容器首次启动时会自动执行 /docker-entrypoint-initdb.d/ 下的 .sql/.sh 脚本
# 怎么用:把你的初始化脚本放在 ./init/ 目录下
- ./init:/docker-entrypoint-initdb.d
# 是什么:资源限制
# 为什么:防止 MySQL 占用过多资源影响其他服务
# 怎么用:根据你的服务器配置调整
deploy:
resources:
limits:
# 是什么:容器最多使用 512MB 内存
# 为什么:防止内存泄漏导致系统崩溃
memory: 512M
# 是什么:容器最多使用 1.5 个 CPU 核心
# 为什么:防止 CPU 占用过高
cpus: '1.5'
reservations:
# 是什么:容器至少保证 256MB 内存
# 为什么:保证 MySQL 有足够内存运行
memory: 256M
# 是什么:健康检查
# 为什么:让 Docker 知道 MySQL 什么时候才算真正启动成功
# 怎么用:一般不需要改
healthcheck:
# 是什么:检查命令
# 为什么:用 mysqladmin ping 检查 MySQL 是否存活
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$MYSQL_ROOT_PASSWORD"]
# 是什么:容器启动后等多久再开始检查
# 为什么:MySQL 启动需要时间,太早检查会误报
start_period: 30s
# 是什么:每隔多久检查一次
interval: 10s
# 是什么:超时时间
timeout: 5s
# 是什么:连续失败 3 次才认为不健康
retries: 3
# 是什么:容器日志配置
# 为什么:防止日志文件无限增长占满磁盘
# 怎么用:根据你的需求调整大小和数量
logging:
driver: "json-file"
options:
# 是什么:每个日志文件最大 10MB
max-size: "10m"
# 是什么:最多保留 3 个日志文件
max-file: "3"
# 是什么:网络配置
# 为什么:让 MySQL 和其他服务(如应用服务器)在同一个内网中通信
# 怎么用:一般不需要改
networks:
- mysql-network
# 是什么:自定义网络
# 为什么:让容器之间能互相访问,同时和外部隔离
# 怎么用:其他服务也加入这个网络,就能用容器名互相访问
networks:
mysql-network:
# 是什么:网络驱动
# 为什么:bridge 是 Docker 默认的网络驱动(桥接模式)
# 怎么用:一般用 bridge 就够了
driver: bridge
3.3 MySQL 配置文件(可选)
创建 ./conf/my.cnf 文件:
[mysqld]
# 是什么:默认字符集
# 为什么:支持中文和 emoji
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# 是什么:最大连接数
# 为什么:限制同时连接数据库的客户端数量,防止资源耗尽
# 怎么用:根据你的应用并发量调整
max_connections = 200
# 是什么:慢查询日志
# 为什么:记录执行超过指定时间的 SQL,帮助优化性能
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # 超过 2 秒的查询记录
# 是什么:InnoDB 缓冲池大小
# 为什么:MySQL 最重要的性能参数,建议设置为内存的 50%-70%
# 怎么用:根据你的服务器内存调整
innodb_buffer_pool_size = 256M
# 是什么:SQL 模式
# 为什么:严格模式可以避免数据 truncation 等问题
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
[client]
default-character-set = utf8mb4
3.4 初始化 SQL 脚本(可选)
创建 ./init/01-init.sql 文件:
-- 创建示例表(容器首次启动时自动执行)
-- 使用指定的数据库
USE myapp_db;
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID(自动增长)',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名(唯一)',
email VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱(唯一)',
password_hash VARCHAR(255) NOT NULL COMMENT '密码哈希值',
status TINYINT DEFAULT 1 COMMENT '状态:1=启用,0=禁用',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- 插入测试数据
INSERT INTO users (username, email, password_hash) VALUES
('admin', 'admin@example.com', '$2b$12$example_hash'),
('testuser', 'test@example.com', '$2b$12$example_hash');
四、部署流程
4.1 启动服务
# 🚀 启动 MySQL 服务(后台运行)
docker-compose up -d
# 参数解释:
# up:启动服务
# -d:detached 模式(后台运行,不阻塞终端)
# 输出示例:
# [+] Running 2/0
# ✔ Network mysql-docker_mysql-network Created 0.0s
# ✔ Container mysql-db Started 0.5s
# 解读:
# - Network ... Created:网络创建成功
# - Container ... Started:容器启动成功
4.2 检查服务状态
# 👀 查看容器运行状态
docker-compose ps
# 输出示例:
# NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
# mysql-db mysql:8.0 "docker-entrypoint.s…" mysql 2 minutes ago Up 2 minutes (healthy) 0.0.0.0:3306->3306/tcp
# 解读:
# - STATUS:Up 2 minutes (healthy) = 容器运行中且健康检查通过 ✅
# - STATUS:Up 2 minutes (starting) = 容器运行中但健康检查还没通过(MySQL 启动中)
# - STATUS:Exited = 容器已停止(需要排查)
# - PORTS:0.0.0.0:3306->3306/tcp = 宿主机 3306 端口映射到容器 3306 端口
# 👀 查看更详细的容器信息
docker ps -a
# 参数解释:
# -a:包括已停止的容器
# 输出示例:
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# a1b2c3d4e5f6 mysql:8.0 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes (healthy) 3306/tcp mysql-db
# 解读:
# - CONTAINER ID:容器的唯一标识
# - CREATED:容器创建时间
# - STATUS:运行状态
# - NAMES:容器名称
4.3 查看日志
# 📋 查看实时日志
docker-compose logs -f mysql
# 参数解释:
# -f:follow 模式(实时滚动,类似 tail -f)
# mysql:指定服务名
# 按 Ctrl+C 退出日志查看
# 📋 查看最近 50 行日志
docker-compose logs --tail=50 mysql
# 参数解释:
# --tail=50:只显示最后 50 行
# 📋 查看完整的启动日志(判断是否启动成功)
docker-compose logs mysql | grep -i "ready for connections"
# 参数解释:
# grep -i:不区分大小写搜索
# "ready for connections":MySQL 启动成功的标志
# 输出示例(启动成功):
# mysql-db | 2025-06-06T10:00:00.000000Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.35' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
# 解读:
# - ready for connections = MySQL 启动成功 ✅
# - port: 3306 = 监听 3306 端口
4.4 连接 MySQL 验证
# 🔌 通过 Docker exec 进入容器内连接 MySQL
docker exec -it mysql-db mysql -uroot -pMyRootPassword@123
# 参数解释:
# exec:在容器内执行命令
# -it:
# -i:交互式(interactive)
# -t:分配伪终端(tty)
# mysql-db:容器名称
# mysql:MySQL 客户端命令
# -uroot:以 root 用户登录
# -pMyRootPassword@123:密码(-p 和密码之间没有空格)
# 进入 MySQL 命令行后:
# 查看数据库列表
SHOW DATABASES;
# 输出:
# +--------------------+
# | Database |
# +--------------------+
# | information_schema |
# | myapp_db | ← 这是我们创建的数据库
# | mysql |
# | performance_schema |
# | sys |
# +--------------------+
# 解读:能看到 myapp_db = 数据库创建成功 ✅
# 使用应用数据库
USE myapp_db;
# 查看表
SHOW TABLES;
# 输出(如果有初始化脚本):
# +--------------------+
# | Tables_in_myapp_db |
# +--------------------+
# | users |
# +--------------------+
# 查询数据
SELECT * FROM users;
# 退出 MySQL
EXIT;
4.5 外部连接测试
# 🔌 从宿主机或其他机器连接 MySQL
mysql -h 127.0.0.1 -P 3306 -u root -pMyRootPassword@123
# 参数解释:
# -h 127.0.0.1:主机地址(本机)
# -P 3306:端口号(大写的 P)
# 如果提示 "command not found",说明宿主机没装 MySQL 客户端
# 可以改用:
docker exec -it mysql-db mysql -uroot -pMyRootPassword@123
五、安全最佳实践
5.1 密码管理
❌ 不推荐:明文写在 docker-compose.yml 中
# 不推荐:密码直接写在文件中,容易被 git 提交
environment:
MYSQL_ROOT_PASSWORD: "MyRootPassword@123"
✅ 推荐方案一:使用 .env 文件
创建 .env 文件:
# .env 文件(不要提交到 git)
MYSQL_ROOT_PASSWORD=MyRootPassword@123
MYSQL_USER=appuser
MYSQL_PASSWORD=AppPassword@456
MYSQL_DATABASE=myapp_db
修改 docker-compose.yml:
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
# ⚠️ 重要:将 .env 加入 .gitignore,防止密码泄露
echo ".env" >> .gitignore
✅ 推荐方案二:使用 Docker Secret(生产环境推荐)
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
secrets:
- mysql_root_password
secrets:
mysql_root_password:
file: ./secrets/mysql_root_password.txt
# 创建密码文件
mkdir -p ./secrets
echo "MyRootPassword@123" > ./secrets/mysql_root_password.txt
# ⚠️ 设置严格的文件权限(只有 owner 可读)
chmod 600 ./secrets/mysql_root_password.txt
5.2 网络隔离
# 限制只有特定网络才能访问 MySQL
networks:
mysql-network:
driver: bridge
# 是什么:IP 地址管理
# 为什么:限制网络范围,提高安全性
ipam:
config:
- subnet: 172.20.0.0/16 # 只允许这个子网访问
5.3 不要用 root 运行应用
-- 创建应用专用用户(最小权限原则)
CREATE USER 'app_user'@'%' IDENTIFIED BY 'AppPassword@456';
-- 只授予必要的权限(而不是 ALL)
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'app_user'@'%';
-- 刷新权限
FLUSH PRIVILEGES;
-- 解读:
# SELECT, INSERT, UPDATE, DELETE:只允许增删改查
# 不允许 CREATE, DROP, ALTER 等危险操作
# ON myapp_db.*:只能访问 myapp_db 数据库
5.4 定期备份
# 📋 创建备份脚本
cat > /opt/mysql-docker/backup.sh << 'EOF'
#!/bin/bash
# 备份目录
BACKUP_DIR="/opt/mysql-docker/backups"
mkdir -p "$BACKUP_DIR"
# 备份文件名(带时间戳)
BACKUP_FILE="$BACKUP_DIR/mysql_backup_$(date +%Y%m%d_%H%M%S).sql"
# 执行备份
docker exec mysql-db mysqldump -uroot -pMyRootPassword@123 --all-databases > "$BACKUP_FILE"
# 压缩备份文件
gzip "$BACKUP_FILE"
# 删除 7 天前的旧备份
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
echo "✅ 备份完成:${BACKUP_FILE}.gz"
EOF
chmod +x /opt/mysql-docker/backup.sh
# 设置定时任务(每天凌晨 2 点备份)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/mysql-docker/backup.sh") | crontab -
六、数据持久化方案
6.1 数据卷挂载方式对比
| 方式 | 语法 | 优点 | 缺点 |
|---|---|---|---|
| Bind Mount | ./data:/var/lib/mysql |
文件直接在宿主机可见 | 依赖宿主机路径 |
| Named Volume | mysql_data:/var/lib/mysql |
Docker 管理,跨平台 | 不方便直接访问文件 |
| tmpfs | tmpfs:/tmp |
速度快 | 容器停止后数据丢失 |
6.2 推荐方案:Bind Mount
volumes:
# 数据目录(最重要)
- ./data:/var/lib/mysql
# 配置文件
- ./conf/my.cnf:/etc/mysql/conf.d/my.cnf
# 初始化脚本(只在首次启动时执行)
- ./init:/docker-entrypoint-initdb.d
# 日志目录(可选)
- ./logs:/var/log/mysql
6.3 数据备份与恢复
备份
# 📋 备份整个数据目录(冷备份,需要先停止容器)
docker-compose down
tar -czf mysql_data_backup_$(date +%Y%m%d).tar.gz ./data/
docker-compose up -d
# 参数解释:
# tar:打包压缩
# -c:创建新档案
# -z:用 gzip 压缩
# -f:指定文件名
# 📋 使用 mysqldump 备份(热备份,不需要停止容器)
docker exec mysql-db mysqldump -uroot -pMyRootPassword@123 --all-databases > backup_$(date +%Y%m%d).sql
# 参数解释:
# mysqldump:MySQL 逻辑备份工具
# --all-databases:备份所有数据库
# >:重定向输出到文件
恢复
# 📋 从 mysqldump 恢复
cat backup_20250606.sql | docker exec -i mysql-db mysql -uroot -pMyRootPassword@123
# 参数解释:
# cat:读取文件
# |:管道,把内容传给下一个命令
# -i:interactive 模式(从标准输入读取数据)
# 📋 从数据目录恢复(冷恢复)
docker-compose down
rm -rf ./data/
tar -xzf mysql_data_backup_20250606.tar.gz
docker-compose up -d
# ⚠️ 危险操作:rm -rf 会删除数据目录
# 操作前请确保备份文件完整可用
七、常见问题排查
问题 1:容器启动后立即退出
现象:
docker-compose ps
# 输出:STATUS = Exited (1)
通俗解释:就像一个厨师(MySQL)还没开始炒菜就辞职了,通常是环境配置有问题。
排查步骤:
# 📋 第一步:查看日志(最重要)
docker-compose logs mysql
# 常见错误及解决方案:
# 错误 1:[ERROR] [MY-010457] --initialize specified but the data directory has files in it
# 原因:数据目录不为空
# 解决:
rm -rf ./data/*
docker-compose up -d
# 错误 2:[ERROR] [MY-013276] Failed to initialize authentication
# 原因:密码不符合复杂度要求(MySQL 8.0)
# 解决:使用更复杂的密码(包含大小写、数字、特殊字符)
# 错误 3:Port 3306 is already in use
# 原因:端口被占用
# 解决:见问题 2
问题 2:端口被占用
现象:
Error: Port 3306 is already in use
通俗解释:就像你要开一家新店,但门牌号已经被别人用了。
排查步骤:
# 👀 查看哪个程序占用了 3306 端口
netstat -tuln | grep 3306
# 或
ss -tuln | grep 3306
# 输出示例:
# tcp6 0 0 :::3306 :::* LISTEN
# 👀 查看是哪个进程
sudo lsof -i :3306
# 输出:
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# mysqld 1234 mysql 34u IPv6 12345 0t0 TCP *:3306 (LISTEN)
# 解读:
# - COMMAND:mysqld(另一个 MySQL 实例)
# - PID:1234
解决方案:
- 方案一:关闭占用端口的程序
sudo kill 1234
# 或
sudo systemctl stop mysql # 如果是系统 MySQL 服务
- 方案二:修改映射端口
# 修改 docker-compose.yml 中的 ports 部分
ports:
- "13306:3306" # 用 13306 映射到容器的 3306
问题 3:无法从外部连接 MySQL
现象:
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.1.100:3306' (111)
通俗解释:就像你给餐厅打电话,但电话线被切断了。
排查步骤:
# 1. 检查 MySQL 是否在运行
docker-compose ps
# 确认 STATUS = Up (healthy)
# 2. 检查端口是否在监听
netstat -tuln | grep 3306
# 应该能看到 0.0.0.0:3306 或 :::3306
# 3. 检查防火墙
# Ubuntu/Debian
sudo ufw status
sudo ufw allow 3306/tcp
# CentOS/RHEL
sudo firewall-cmd --list-ports
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload
# 4. 检查 MySQL 是否允许远程连接
docker exec -it mysql-db mysql -uroot -pMyRootPassword@123 -e "SELECT host, user FROM mysql.user;"
# 输出:
# +-----------+------------------+
# | host | user |
# +-----------+------------------+
# | % | root | ← % 表示允许所有主机
# | localhost | root |
# +-----------+------------------+
# 如果 root 的 host 只有 localhost,说明不允许远程连接
解决方案:
-- 允许 root 从任何主机连接(⚠️ 生产环境不推荐)
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'MyRootPassword@123';
FLUSH PRIVILEGES;
-- 或者创建专门的远程用户(推荐)
CREATE USER 'remote_user'@'%' IDENTIFIED BY 'RemotePassword@789';
GRANT ALL PRIVILEGES ON myapp_db.* TO 'remote_user'@'%';
FLUSH PRIVILEGES;
问题 4:数据丢失
现象:重启容器后数据不见了。
通俗解释:就像你把东西放在临时宿舍里,退房后东西被清理了。
原因:没有配置数据卷挂载,数据存在容器内部,容器删除就没了。
解决方案:
# 确保 docker-compose.yml 中有 volumes 配置
volumes:
- ./data:/var/lib/mysql # 这行必须有!
# 验证数据是否在宿主机
ls -la ./data/
# 应该能看到 mysql 的数据文件
问题 5:内存不足导致 MySQL 被杀
现象:
Killed
# 或
docker-compose ps 显示 Exited (137)
通俗解释:就像餐厅雇不起厨师了(内存不够),只能把人辞退。
排查步骤:
# 检查系统内存
free -h
# 检查是否有 OOM Killer 记录
dmesg | grep -i "killed process"
# 输出示例:
# [12345.678] Out of memory: Killed process 1234 (mysqld) total-vm:1234567kB, anon-rss:567890kB
# 解读:
# - mysqld 进程被 OOM Killer 杀掉了
# - 说明系统内存不足
解决方案:
- 增加服务器内存
- 减少 MySQL 的内存配置:
# my.cnf
[mysqld]
innodb_buffer_pool_size = 128M # 减少缓冲池
max_connections = 100 # 减少最大连接数
八、服务停止与卸载
8.1 停止服务
# ⏹️ 停止服务(容器和数据卷保留)
docker-compose stop
# 参数解释:
# stop:停止容器(类似关机,数据还在)
# 验证:
docker-compose ps
# STATUS 应该显示为 Exited
8.2 停止并删除容器
# ⏹️ 停止并删除容器、网络(数据卷保留)
docker-compose down
# 参数解释:
# down:停止容器并删除容器和网络(数据还在)
# 验证:
docker-compose ps
# 应该看不到容器了
# 验证数据还在:
ls -la ./data/
8.3 彻底清理(⚠️ 危险操作)
⚠️ 危险操作:以下操作会删除所有数据,操作前务必备份!
# 🗑️ 停止容器并删除数据卷(数据会丢失!)
docker-compose down -v
# 参数解释:
# -v:删除数据卷(包括所有数据)
# ⚠️ 操作前请确保已备份重要数据!
# 🗑️ 删除项目目录(彻底清理)
docker-compose down -v
cd ..
rm -rf mysql-docker/
# ⚠️ 再次提醒:这会删除所有 MySQL 数据!
# 操作前请执行备份:
# ./backup.sh 或
# docker exec mysql-db mysqldump -uroot -p --all-databases > backup.sql
8.4 卸载 Docker(可选)
# Ubuntu/Debian
sudo apt-get purge docker-ce docker-ce-cli containerd.io
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
# CentOS/RHEL
sudo yum remove docker-ce docker-ce-cli containerd.io
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
九、总结
核心操作速查表
| 操作 | 命令 | 说明 |
|---|---|---|
| 启动服务 | docker-compose up -d |
后台启动 |
| 查看状态 | docker-compose ps |
检查运行状态 |
| 查看日志 | docker-compose logs -f mysql |
实时日志 |
| 进入容器 | docker exec -it mysql-db bash |
进入容器终端 |
| 连接数据库 | docker exec -it mysql-db mysql -uroot -p |
MySQL 客户端 |
| 停止服务 | docker-compose stop |
停止容器 |
| 删除容器 | docker-compose down |
删除容器(保留数据) |
| 彻底清理 | docker-compose down -v |
删除所有数据(⚠️ 危险) |
黄金法则
- 先备份,后操作 - 任何涉及数据的操作前先备份
- 数据卷必须挂载 - 没有 volumes 配置,重启就丢数据
- 不要用 root 跑应用 - 创建专用用户,最小权限原则
- 日志要限制大小 - 防止日志占满磁盘
- 密码不要硬编码 - 使用 .env 文件或 Docker Secret
文件结构
mysql-docker/
├── docker-compose.yml # 核心配置文件
├── .env # 环境变量(不要提交 git)
├── data/ # MySQL 数据目录(自动创建)
├── conf/
│ └── my.cnf # MySQL 自定义配置
├── init/
│ └── 01-init.sql # 初始化脚本(首次启动执行)
├── logs/ # 日志目录(可选)
└── backup.sh # 备份脚本
文档版本:1.0
最后更新:2025-06-06
维护者:guobin
评论区