云服务器上部署MySQL 8.0:安全组、防火墙与远程连接全解
把 MySQL 部署在云服务器上和在本机装一个 MySQL完全是两码事。本机装好以后只要客户端和服务端网络能通你自然就能连上但放到云服务器上以后多了一层云平台的安全组操作系统还有一层防火墙MySQL 安装完以后默认还只监听本机地址再加上用户授权、端口放行这些环节任何一个地方卡住别人就连不上你的数据库。凡是做过一次的人基本都体会过这种“明明服务已经启动远程却始终连不上”的崩溃感。这篇文章以阿里云 ECS 为例按照实际操作的顺序把部署一套 MySQL 数据库并对外服务需要做的动作完整串一遍从云服务器规格选型、MySQL 8.0 的安装、初始化密码、修改监听地址、放行安全组、配置系统防火墙、创建远程账号再到参数调优、自动备份和故障排查。即便你换到其他云平台思路也完全一致只是控制台入口和按钮叫法会稍有差异。适合刚接触云上数据库部署的同学也适合已经部署好但一直没彻底搞懂链路的人回来查漏。1. 部署方案选型ECS 自建和托管 RDS 怎么取舍1.1 先想清楚为什么要自己搭建一套 MySQL在云上部署数据库表面上看只有一句“我要装个 MySQL”但实际动手时会遇到第一个分岔口用云厂商托管的数据库服务还是买一台云服务器自己装托管的数据库服务比如阿里云的 RDS MySQL是平台帮你做好了一整套运维购买之后只需要填好库名、账号、白名单马上就能连接使用。自动备份、高可用切换、监控告警这些能力基本是开箱即有的。对于没有专职 DBA 小团队来说这种模式确实省了很多事。但它也有明显的短板价格通常比同等规格的 ECS 高出不少而且一些底层参数、插件、字符集、系统层面的优化受管控限制比较大。你想在配置上做更细的调优或者装一些特殊组件往往需要提工单申请而不是自己改个配置文件就能解决。自己买一台 ECS 来装 MySQL成本上更可控操作自由度也更高。熟悉 Linux 基本命令就能搞定。不过这意味着备份、高可用、安全加固、日常巡检这些工作也要自己承担。我的建议是先看场景如果是正式且偏核心的业务团队没有足够运维人力选 RDS 更稳妥如果是测试环境、学习环境或者早期预算紧张想对数据库底层有更深的了解那 ECS 自建是非常合适的路线。这篇文章主讲的就是后者的完整落地过程。1.2 自建 MySQL 对外服务必须打通的四个环节自建 MySQL 安装完成以后默认只会监听在 127.0.0.1。也就是说无论这台 ECS 分配了多少公网 IP其他机器都是访问不到的。要让数据库真的对外提供服务至少要在四个环节上做动作。第一修改 MySQL 的监听地址让它从 127.0.0.1 变成 0.0.0.0或者绑定到具体的内网 IP。第二创建一个允许远程登录的 MySQL 账号并授出合适的库表权限不要用 root 暴露在网络上。第三放行操作系统自带的防火墙端口比如 firewalld 里的 3306。第四在云平台的安全组规则里放行入方向的 3306 端口。这四件事缺一不可而且很多人会忽略“云平台安全组”和“系统防火墙”是两层独立规则这个关键点。你只改了其中一层另一层没动数据库照样连不上。1.3 RDS 和 ECS 自建的对比速查对比维度ECS 自建 MySQL托管 RDS MySQL费用只付 ECS 费用ECS 费用加数据库服务费用运维工作自己负责备份、监控、高可用平台托管自动备份和高可用自定义程度高可改各种参数和配置文件低部分参数受平台限制上手难度需要熟悉 Linux 和 MySQL 管理控制台点击配置即可适合场景测试、学习、成本敏感项目核心业务、运维人力有限实际项目里也有人会选择“先用 ECS 自建跑起来等数据量上来后再迁移到 RDS”的过渡方案。这个思路没问题只要从第一天就把备份做好后面迁移会轻松很多。2. 动手前的资源准备地域、规格、系统镜像2.1 购买 ECS 实例时这几个选项不要乱选在云平台新建实例时第一个要面对的就是地域。地域和可用区一旦确定后续迁移成本不低而且跨地域访问数据库会有明显的延迟。我的建议是看业务用户在哪里。如果服务的是华东区域的用户数据库就放在华东区域如果是自己学习测试就近选择即可。同时数据库 ECS 和业务应用 ECS 最好放在同一个地域必要时再放进同一个 VPC 网络这样内网访问延迟最低也不消耗公网带宽。第二个是实例规格。自建 MySQL 的 ECS我强烈不建议低于 2 核 4G 内存。MySQL 8.0 安装完成之后空闲时内存占用大约在 300 到 500MB执行查询时还会产生连接缓冲、排序缓冲、InnoDB 缓冲池等额外内存开销。用 1核2G 跑简单测试可以一旦并发稍微集中很容易出现内存不足严重的会直接把 MySQL 进程杀掉。第三个是磁盘。系统盘建议只用来装系统和软件数据盘另外挂载并把 MySQL 的数据目录迁移到数据盘上。原因有两点一是避免日志和业务数据抢占系统盘 IO二是系统盘一旦重装数据盘默认不会被格式化数据安全性更高。数据盘的大小建议从 40GB 起步再按业务增长速度适当放大。2.2 操作系统和 MySQL 版本怎么选更省心操作系统方面在阿里云上我默认推荐 Alibaba Cloud Linux 3。原因很简单镜像和 yum 源都在国内访问速度快命令习惯和 CentOS 基本一致很多老 Linux 用户迁移过来几乎无感。如果团队更熟悉 Debian 系的路径选 Ubuntu 22.04 LTS 也可以只是把 yum 换成 apt把 firewalld 换成 ufw 而已。MySQL 版本目前主推 8.0。8.0 相比 5.7 改进非常大比如默认字符集已经是 utf8mb4性能、窗口函数、数据字典都有明显升级。而且 5.7 已经逐步进入生命周期尾声长期维护和安全补丁方面会有隐患。除非现有代码里有大量必须兼容 5.7 历史行为的老语法否则新项目直接上 8.0后面升级成本最低。安装之前还要想好字符集。直接在配置里把默认字符集设成 utf8mb4避免后面建库建表时才发现中文乱码。这个细节越早处理麻烦越少。3. MySQL 安装与初始化从零到能本地登录3.1 用国内镜像源安装 MySQL 的正确姿势以 Alibaba Cloud Linux 3 为例系统自带的软件源默认提供的是 MariaDB并不是官方 MySQL。MariaDB 虽然兼容大部分 MySQL 语法但底层行为和管理方式有差异不建议用它替代。所以要安装 MySQL 8.0第一步是先把 MySQL 的软件源配置好。我实际操作时会用 MySQL 官方发布的源 rpm 包安装然后把下载地址改为国内镜像避免拉包时卡在国外服务器上。示例命令如下wget https://repo.mysql.com/mysql80-community-release-el8-1.noarch.rpm rpm -ivh mysql80-community-release-el8-1.noarch.rpm安装完 rpm 以后系统里会多出一个/etc/yum.repos.d/mysql-community.repo文件。打开这个文件把里面 MySQL 官方软件源的 baseurl 改成国内镜像站的地址。“国内镜像站”的具体路径可能随版本变化最稳妥的方式是直接访问云厂商的镜像目录找到对应 el8 和 x86_64 的路径后填写。改完再执行软件源刷新sed -i s#repo.mysql.com#mirrors.aliyun.com#g /etc/yum.repos.d/mysql-community.repo yum clean all yum -y install mysql-community-server更多时候sed 只替换域名后路径结构不匹配还是要手动看一遍。如果安装时遇到长时间卡住大概率还是源连接不稳定换一个镜像路径再试。安装完成后启动并设置开机自启systemctl enable --now mysqld systemctl status mysqld看到 activerunning状态说明 MySQL 已经起来了。8.0 在首次启动时会自动初始化数据目录同时生成一个临时 root 密码这个临时密码会写到日志里下一步要用它来登录。3.2 用临时密码登录并修改 root 密码查临时密码的方式grep temporary password /var/log/mysqld.log拿到临时密码以后执行mysql -uroot -p登录后第一件事就是改 root 密码。MySQL 8.0 默认开了密码校验插件对密码强度有要求太简单的密码会直接报错。下面这条 SQL 可以完成修改ALTER USER rootlocalhost IDENTIFIED BY YourStrong_Passw0rd;注意这里的密码需要包含大小写字母、数字和特殊字符长度最好在 12 位以上。有的人会想直接把校验插件关掉用一个简单的密码我强烈不建议这么干。数据库一旦对外开放弱密码基本等于把数据送给扫描器。修改完 root 密码后建议执行一遍安全初始化mysql_secure_installation这个交互脚本会依次提示配置密码校验策略、删除匿名用户、禁止 root 远程登录、删除 test 库并刷新权限表。其中“禁止 root 远程登录”这一步务必选择 yes。我们后面会创建独立业务账号root 只用于本机管理可以大幅降低被爆破的风险。3.3 修改绑定地址让 MySQL 监听所有网络接口本地能登录只算完成了一半。MySQL 默认的监听地址是 127.0.0.1对外网不可见。先看一下当前监听情况ss -antp | grep 3306正常情况下会看到类似LISTEN 0 128 127.0.0.1:3306的行。要让数据库对外提供连接需要修改/etc/my.cnf[mysqld] port3306 bind-address0.0.0.0 mysqlx-port33060bind-address0.0.0.0表示对所有网卡开放。如果这台机器有多个网卡并且你只想对内网提供服务也可以写成具体的内网 IP例如bind-address172.16.0.5。这样公网不会直接暴露 3306安全性会更好。如果业务必须从公网访问才选择绑定0.0.0.0同时一定要把云平台安全组和账号授权收紧。配置改动后需要重启服务systemctl restart mysqld再次执行ss -antp | grep 3306看到0.0.0.0:3306就说明监听地址已经生效。4. 打通网络链路安全组、系统防火墙与端口放行4.1 云平台安全组里的 3306 规则这样加很多人修改完 MySQL 配置以后还是连不上问题往往出在云平台安全组。安全组可以理解成云服务器外的一道虚拟防火墙购买 ECS 时默认的安全组通常只放行 22、80、443、3389 这些常用端口3306 不会自动开放。进入 ECS 实例详情页找到安全组配置选择入方向规则然后添加一条规则。协议类型选 TCP端口范围填 3306/3306授权对象这里要非常慎重。网上很多教程直接让填0.0.0.0/0意思是允许所有 IP 访问但这是数据库端口一旦放开等于把数据库暴露在公网上让全世界的扫描器随意试探。正确做法是只填允许访问的来源 IP。比如你办公网络的公网出口 IP 是203.0.113.10就填203.0.113.10/32如果访问方是云上的另一台应用服务器可以填那台服务器的内网 IP或者填它的安全组 ID让同组内的机器都能访问。规则添加以后会立即生效不需要重启 ECS。4.2 系统防火墙和 iptables 也要同步放行云安全组放行之后别忘了系统里还有一层防火墙。Alibaba Cloud Linux 3 和 CentOS 默认开了 firewalld在 ECS 本机执行systemctl status firewalld firewall-cmd --permanent --add-port3306/tcp firewall-cmd --reload firewall-cmd --list-ports如果系统是 Ubuntu用的通常不是 firewalld 而是 ufw对应命令是ufw allow 3306/tcp ufw reload有些精简版系统镜像默认没有启动 firewalld但 iptables 里面可能残留了一些策略。检查方式iptables -L -n | grep 3306如果发现DROP规则拦住了 3306需要插入一条放行规则iptables -I INPUT -p tcp --dport 3306 -j ACCEPT这里最容易犯的错是觉得安全组放行就万事大吉结果系统防火墙还开着。网络包实际上要经过两层过滤只有两层都放行客户端才能真正到达 MySQL 进程。4.3 改完还不通先按这三步自查我帮别人排查远程连不上 MySQL 时一般是按从外到里的顺序走三步。第一步在 ECS 上执行ss -antp | grep 3306确认 MySQL 真的在监听 0.0.0.0第二步从本地电脑执行telnet 公网IP 3306看端口通不通超时通常说明网络层有问题需要回去查安全组和系统防火墙第三步用实际业务账号远程登录如果端口通了但报 Access denied那就是授权或者密码问题。这套顺序能解决掉大部分“数据库连不上”的问题比反复改配置高效得多。5. 创建远程账号并测试客户端连接5.1 为什么不能用 root 远程连接数据库对外提供服务后最忌讳把 root 直接暴露出去。MySQL 8.0 默认的 root 账号 host 就是 localhost即便 bind-address 改成 0.0.0.0root 也无法远程登录。有些教程会教你把 root 的 host 改成%我这里明确说一句不要这么干。root 被攻破意味着整个实例完全沦陷所有库表数据都会被拿走。合理的做法是单独创建一个业务账号并授出最小权限。如果某个业务只需要读写biz_db库就只给增删改查不给建表、删库、改账号这些高权限。示例CREATE USER app_user% IDENTIFIED BY StrongPassword_2024!; GRANT SELECT, INSERT, UPDATE, DELETE ON biz_db.* TO app_user%; FLUSH PRIVILEGES;这里的%表示允许从任意主机登录。如果访问来源相对固定强烈建议把%换成具体 IP 或网段比如app_user10.0.0.0/255.255.255.0。host 匹配范围越小安全面越窄。5.2 MySQL 8.0 认证插件不兼容的解决方案MySQL 8.0 默认的密码认证插件是caching_sha2_password比 5.7 时代的mysql_native_password更安全但一些老客户端和老驱动并不支持。如果你用的是比较旧的 Navicat或者程序里的数据库驱动还是老版本连接时会出现Authentication plugin caching_sha2_password cannot be loaded解决办法有两个。首选升级客户端或驱动这是最干净的方式实在无法升级时才创建用户时手动指定老插件CREATE USER app_user% IDENTIFIED WITH mysql_native_password BY StrongPassword_2024!;不过mysql_native_password在 MySQL 8.0 里已经标记为弃用后续版本有可能会移除。如果是新项目驱动还是尽早升级到支持 caching_sha2_password 的版本。5.3 从不同来源测试连接配置完成后先在 ECS 本机测一下账号mysql -uapp_user -p -h 127.0.0.1再用内网 IP 测mysql -uapp_user -p -h 内网IP最后再从本地客户端连公网 IP。如果报Host is not allowed to connect to this MySQL server基本是账号 host 规则没匹配上。如果报Access denied for user除了密码错误还要确认是否用了正确的认证插件。如果远程连接要等很久才返回结果很可能是 MySQL 在做反向 DNS 解析。在/etc/my.cnf里加一行[mysqld] skip-name-resolve重启后MySQL 会直接用 IP 来判断来源连接速度会明显提升。代价是授权表里不能使用域名形式的 host只能用 IP。5.4 客户端连接串中的几个易错点用 Java 中的 JDBC 连接时连接串里经常会漏掉useSSLfalse或serverTimezoneAsia/Shanghai这类参数导致报时区错误或者 SSL 握手失败。虽然在配置层面做调整可以解决但更推荐在客户端侧明确参数。比如本地 Navicat 连接时如果 MySQL 版本较高要注意 SSL 模式选为 Preferred 或 Disable避免因为证书校验失败而拒绝连接。数据库对外服务客户端并不一定只限定一种所以连接串的兼容性也要纳入测试范围。6. 部署后的参数调优与自动备份6.1 刚够用的几个关键 MySQL 配置项数据库稳定跑起来以后不能一点不管。至少有三个配置项需要根据硬件和负载调整。第一个是innodb_buffer_pool_size这是 InnoDB 的缓冲池大小一般建议设为物理内存的 50% 到 70%。比如 ECS 是 4G 内存可以设成 2G如果同一个实例上还跑着其他应用要把比例调低避免操作系统没有足够的缓存空间。第二个是max_connections。默认值是 151大部分中小项目都不够用。并发查询稍微一多就会出现Too many connections。我通常建议先调到 300如果业务验证后需要更高再配合内存情况缓慢上调。每个连接都会占用内存调太高反而容易引发内存耗尽。第三个是慢查询日志和二进制日志。配置文件里建议加上slow_query_log1 slow_query_log_file/var/log/mysql-slow.log long_query_time2 binlog_formatROW server_id100慢查询日志能帮你快速找到性能有问题的 SQLbinlog 是后面做增量备份和数据恢复的基础。server_id在单机场景也需要配因为 MySQL 8.0 默认开启了 binlog如果没有配置某些复制场景会报错。6.2 自动备份脚本与 cron 定时任务备份总是最容易“以后再说”的事情但真出问题的时候后悔往往来不及。我常用的方案是 mysqldump 全量备份gzip 压缩后保留最近 7 天。脚本大概长这样#!/bin/bash BACKUP_DIR/data/backup/mysql DATE$(date %F) MYSQL_USERbackup_user MYSQL_PASSBackup_Passw0rd mkdir -p ${BACKUP_DIR} mysqldump -u${MYSQL_USER} -p${MYSQL_PASS} --single-transaction --routines --events --all-databases | gzip ${BACKUP_DIR}/all_${DATE}.sql.gz find ${BACKUP_DIR} -name *.sql.gz -mtime 7 -delete--single-transaction可以在 InnoDB 引擎下做一致性快照避免备份过程中业务写入导致数据不一致--routines和--events用于备份存储过程和定时事件。mysqldump 使用的备份账号不能是业务账号要单独建一个 backup_user授权SELECT、LOCK TABLES、RELOAD、SHOW VIEW、PROCESS这些必要权限。把脚本放到/usr/local/bin/mysql_backup.sh并赋予执行权限然后写进 crontabchmod x /usr/local/bin/mysql_backup.sh crontab -e 0 2 * * * /usr/local/bin/mysql_backup.sh /var/log/mysql_backup.log 21备份脚本写完以后至少要验证一次恢复过程。最常见的问题是脚本一直在执行但备份文件是 0 字节或者恢复时报权限错误。真正验证过能恢复的备份才有资格叫备份。6.3 数据目录迁移到数据盘如果一开始没有把 MySQL 数据目录放在独立数据盘后续要迁移也不复杂。先把数据目录整体复制到新盘比如/data/mysqlrsync -av /var/lib/mysql/ /data/mysql/再修改/etc/my.cnf增加datadir/data/mysql然后重启 MySQL确认数据无异常后再把原目录改名留作备份。迁移时要注意目录属主和权限MySQL 进程通常以 mysql 用户运行如果目录权限不对MySQL 会拒绝启动。数据目录迁移后内核也要跟得上地做一次全量备份防患于未然。7. 对外服务时的常见故障与排查实录7.1 用端口探测来定位问题到底卡在哪一层远程连不上数据库时最忌讳的是病急乱投医来回改配置。我习惯用从外到里的方法。先在客户端电脑或其他服务器上执行telnet 公网IP 3306如果连接超时或被拒绝说明网络层不通问题大概率在安全组或系统防火墙。如果端口通了再进入数据库授权检查。接着在 ECS 本机执行mysqladmin -uroot -p ping能正常返回mysqld is alive说明服务进程没问题。然后再用实际业务账号从远程测试连接。遇到报错时直接在错误表里对照处理效率最高。7.2 高频错误对照表报错现象大概率原因处理方向连接超时 / 10060安全组未放行或公网 IP 未绑定检查安全组入方向和实例公网 IP连接被拒绝 / 10061MySQL 未监听或系统防火墙拦截看 3306 监听地址放行 firewalld/iptablesAccess denied for user密码错误、host 不匹配核对密码和授权表 hostHost is not allowed账号 host 限定过严调整%或指定来源 IPAuthentication plugin cannot be loaded客户端不兼容 caching_sha2_password升级客户端或指定 mysql_native_passwordToo many connectionsmax_connections 不够调大连接数并排查连接泄漏Unknown database库名不存在检查库名和大小写敏感配置Packet too largemax_allowed_packet 过小调大 max_allowed_packet 参数实际工作中最容易踩到的错误是 10060 和 10061。10060 是连接请求发出后超时大概率是安全组或公网 IP 的问题10061 是目标机器主动拒绝连接大概率是系统防火墙拦截或者 MySQL 本身没在监听。7.3 两个实际的排障案例第一个案例来自某项目两台 ECS 在同一个内网里应用服务器访问数据库服务器的私网 IP 始终超时。一开始我怀疑是安全组没放行内网网段后来发现安全组规则确实是对的问题出在数据库所在 ECS 的 firewalld 配置上。因为默认 zone 没有放行 3306而且内网请求也会命中这个 zone。最直接的解法是把内网网段加入 trusted zonefirewall-cmd --permanent --zonetrusted --add-source10.0.0.0/8 firewall-cmd --reload这样内网流量就会绕过端口限制外网网卡依然走默认严格的 public zone安全性和可用性都能兼顾。第二个案例是某系统在远程连接后执行查询特别慢每一条 select 都快超时。查了很久没有发现 SQL 问题因为同样的查询在数据库本机执行秒回。后来才定位到MySQL 的 DNS 反解在起作用每一次远程连接都会尝试把客户端 IP 反向解析成域名而对应网络的 DNS 响应很慢。在 my.cnf 里加上skip-name-resolve并重启后问题立即消失。7.4 防火墙 zone 和端口放行的经验再补充一个容易被忽略的细节云服务器里经常有多块网卡比如内网网卡和公网网卡。firewalld 的 zone 可以按网卡或来源地址来区别对待。生产环境比较推荐的做法是公网网卡放在 public zone只放行必要的端口内网网卡放在 trusted zone允许内网网段直接访问。这样可以避免为了一个数据库端口把公网的所有来源都放开。命令上面写过把10.0.0.0/8换成本项目的实际内网网段就行。8. 安全底线对外 MySQL 的几条实用原则8.1 从网络层到账号层都做最小暴露如果已经决定把 MySQL 对外服务那么每一层都应该做最小暴露。安全组上能填具体 IP 就不要填0.0.0.0/0MySQL 账号上能限定 host 就不要用%权限上能只授业务所需权限就不要给 all privileges。定期执行下面这条语句检查账号列表SELECT User, Host FROM mysql.user;如果发现一堆xxx%要逐个确认来源是否合理。长期不用的账号直接删除权限发生过变化的账号重新授权。大部分安全问题都来自历史账号积压和权限过度授权。8.2 临时开启 general_log 来观察访问行为当怀疑有异常访问或者连接频繁报错时可以临时打开 general_log把每一条连接和 SQL 都记录到日志里。配置文件加下面几行general_log1 general_log_file/var/log/mysql-general.log重启后生效。general_log 在高并发下增长非常快所以只能作为临时排查工具确认问题处理后立刻关掉general_log0再重启一次恢复正常状态。另外要对暴力破解有一些心理准备。数据库端口一旦暴露在公网各种扫描和弱口令尝试几乎是必然的。与其寄希望于别人不会来扫不如把基础措施做到位强密码、来源 IP 白名单、最小权限、定期检查账号。安全组只放开必要的来源日志定期看一眼这套数据库的对外服务能力才算真正合格。8.3 给以后留一条体面的升级路径一套 MySQL 部署完不是故事的终点。以后业务量上来你可能会考虑做主从复制、读写分离或者干脆迁移到托管数据库。如果从一开始就开了 binlog并且记得定期备份迁移升级时会从容很多。数据量从小到大的过程里备份策略也要跟着调整比如从每日全备变成“全备binlog 增量”。这些工作看起来是“以后再说”但前期基础打好了后面只是顺水推舟的事。我自己在实际操作中最大的体会是这套链路里最费时间的往往不是 MySQL 安装而是网络层的排查。云安全组、系统防火墙、监听地址、账号授权这四层只要养成固定的排查顺序大多数问题都能在几分钟内定位。如果你正准备做这件事建议先写一个包含上述四步的检查清单部署完照着走一遍会比反复重启服务靠谱得多。