一台不能连外网的RockyLinux 8服务器,要装MySQL 8。很多刚接触服务器运维的朋友,第一次遇到这种“离线环境”都会懵:yum装不了,源码编译缺工具链,想找离线安装包又不知道从哪里下手。我前阵子刚好在物理隔离的内网环境里部署了一套MySQL 8,从依赖包准备到初始化完成,踩了一路坑,也沉淀了一套可以“照抄”的流程。这篇文章就把整个过程完整记录下来,覆盖mysql8离线安装的核心要点、依赖包准备方法、RPM安装顺序、初始化配置、常见报错排查,希望能帮到同样被离线环境折腾的人。
1. 离线安装的整体思路与方案选型
1.1 什么场景下会用到离线安装
先聊清楚“离线”到底指什么。它不是指你本地电脑没网,而是指目标服务器处于内网隔离环境,访问不了外网yum仓库,甚至整个网段默认就不通外部DNS。常见的场景有这几种:
- 生产内网有安全要求,服务器不允许连接公网,安装软件只能靠离线包。
- 等保合规或项目交付时,需要把整套软件版本固定下来,不允许线上yum随意升级。
- 批量交付多台同配置服务器,离线包可以一次准备好,在每台机器上重复执行,版本完全一致。
在这类场景下,直接用 yum install mysql-server 基本是行不通的。即使你的机器能访问外网,也不建议在生产环境直接yum安装,因为你无法控制它装出来的版本和依赖,一旦后续需要迁移、重建环境,版本漂移会让你非常痛苦。
1.2 三种离线安装方案,我为什么选了RPM
离线装MySQL 8,业内比较常见的路子有三条:RPM离线包安装、Docker离线镜像安装、源码编译安装。三条路我都在实际环境里试过,先做个直观对比:
| 方案 | 安装速度 | 依赖处理 | 适合场景 | 坑点 |
|---|---|---|---|---|
| RPM离线包 | 快,10分钟内完成 | 需提前备齐依赖包 | 常规生产环境首选 | 依赖遗漏时会装到一半卡住 |
| Docker镜像 | 快,镜像加载即可 | 无需额外依赖 | 已经用了容器体系的团队 | 需要额外维护容器网络、数据卷 |
| 源码编译 | 慢,编译耗时长 | 需要完整工具链 | 需要定制编译参数的特殊场景 | 对新手极不友好,后期升级麻烦 |
我个人强烈推荐RPM方案。理由很直接:RockyLinux 8和RHEL 8同源,官方yum仓库里就提供MySQL 8的RPM包,这些包针对系统做了适配,安装后的服务管理、日志路径、环境配置都是标准化的。相比源码编译,RPM方案省掉了gcc、make、cmake这一大堆开发工具,也避免了编译参数踩坑;相比Docker,RPM方案不需要额外维护容器运行时,对传统运维栈更友好。
1.3 需要提前准备的物料清单
离线安装最忌讳“装到一半发现少个包”。我踩过一次这种坑,在客户内网装MySQL,传到一半rpm报缺perl,现场又没有离线仓库,差点让整个部署计划延期。所以物料清单必须先理清楚。
我这次用到的MySQL 8 RPM包如下:
- mysql-community-server
- mysql-community-client
- mysql-community-common
- mysql-community-libs
- mysql-community-libs-compat
- mysql-community-icu-data-files
其中 mysql-community-icu-data-files 是很多新手容易忽略的包,它提供ICU字符集数据,MySQL 8的utf8mb4排序规则依赖它,漏装会导致启动时直接报错。
除了MySQL自身的包,还要准备一些系统依赖:libaio、perl、net-tools、ncurses-libs。这些依赖通常会被MySQL 8的RPM包自动检查,如果系统里原本没有,rpm安装就会报依赖缺失。最稳妥的做法是:在一台能联网的RockyLinux 8机器上用 yum install --downloadonly 把这些依赖连同MySQL包一起下载下来,再传到目标机器上。这样版本匹配度最高,也最不容易出幺蛾子。
2. 安装前的环境检查与物料准备
2.1 系统环境确认
拿到一台干净的RockyLinux 8服务器,别急着传包,先把系统信息确认一遍。这一步看起来简单,但能避免很多后面才发现的问题。
先看系统和架构:
cat /etc/os-release uname -m确保目标机器是RockyLinux 8.x版本,架构是x86_64。虽然MySQL 8的RPM也提供aarch64版本,但x86_64下的包最全,遇到问题也最好排查。
再看内存和磁盘:
free -h df -hMySQL 8运行起来后占用内存不少,尤其是InnoDB缓冲池。建议内存至少2GB,数据盘剩余空间至少10GB。我这里有一台4GB内存的测试机,默认配置下运行MySQL 8完全没问题;如果你机器内存只有1GB,后面my.cnf里就得把 innodb_buffer_pool_size 调小,否则启动后会面临OOM风险。
接着检查repo状态。目标机器上可能残留了系统自带的repo文件,如果服务器完全离线,这些repo其实没有意义,但有些repo会在执行 yum 命令时报错或者卡在超时上。建议把 /etc/yum.repos.d/ 目录下的非必要repo文件统一备份后移走:
mkdir -p /root/repo_backup mv /etc/yum.repos.d/*.repo /root/repo_backup/同时还要确认AppStream模块的默认状态。RockyLinux 8自带的mysql模块可能是8.0版本,也可能是mariadb版本。如果不关心,可以不管它;但如果你后面想用 yum localinstall 来装rpm包,模块冲突会直接导致安装中止。处理方式很简单,把默认模块禁用掉:
dnf module disable -y mysql dnf module disable -y mariadb2.2 处理系统自带的MariaDB和残留文件
RockyLinux 8默认自带的是MariaDB,而不是MySQL。这两个数据库虽然名字相近,但文件目录、服务管理方式有差别。如果直接用rpm安装MySQL 8,很可能会和系统自带的MariaDB-libs冲突。
先检查系统里是否已经有相关包:
rpm -qa | grep -iE 'mysql|mariadb'如果查出 mariadb-libs 之类的包,不要犹豫,先备份配置再卸载。数据库里的数据如果有用,一定要先备份到安全位置。我在实践中见过不少同事,卸载前不备份,事后发现系统根目录下还有旧库文件,悔之晚矣。
卸载命令:
rpm -e --nodeps mariadb-libs加 --nodeps 是因为系统中其他组件可能对这个库有依赖,直接正常卸载会报错。这种操作有风险吗?有。但对于刚装好的系统,MariaDB通常没有实际业务使用,风险可控。如果服务器上已经跑了MariaDB服务并且有业务在写,那就必须先停服务、备份数据,再走迁移流程,不能直接卸载。
卸载完后,检查 /var/lib/mysql 目录是否存在。如果存在且是空的,直接清掉;如果里面有旧数据文件,先重命名备份:
mv /var/lib/mysql /var/lib/mysql_backup这一步很关键。MySQL初始化时如果发现数据目录里已有文件,会直接跳过初始化,导致你后续找不到临时密码,服务也无法启动。
2.3 在联网机器上准备离线安装包
这是整个离线安装里最考验“情报收集能力”的环节。准备安装包有两条路子,我建议两条都走一遍,互相补充。
第一条路子:从镜像站直接下载RPM包。国内常用的清华TUNA镜像、阿里云开源镜像站都收录了MySQL官方仓库。以清华镜像为例,在浏览器里访问镜像站上的MySQL yum仓库路径,找到 el8 目录,文件列表里就能看到 mysql-community-server-8.0.x.x86_64.rpm 等包。用 wget 逐个下载:
wget https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql-8.0-el8/x86_64/mysql-community-server-8.0.36-1.el8.x86_64.rpm第二条路子:用 yum install --downloadonly 把MySQL包和依赖包一次拉全。在一台能联网、系统版本和目标机器一致的机器上执行:
dnf install --downloadonly --downloaddir=/root/mysql8_offline mysql-community-server这条命令会把 mysql-community-server 以及它依赖的所有软件包全部下载到指定目录。注意:执行这条命令之前,需要先安装MySQL官方yum仓库:
dnf install https://dev.mysql.com/get/mysql80-community-release-el8-4.noarch.rpm或者手动把官方repo文件放到 /etc/yum.repos.d/ 下。下载完成后,把整个 /root/mysql8_offline 目录打包带走,里面既包括MySQL自己的RPM包,也包括perl、ncurses等系统依赖。
我提倡两条路子都准备,是因为镜像站下载适合精确控制版本,而 --downloadonly 适合保证依赖完整。两者结合,基本可以覆盖所有场景。
2.4 上传文件与校验完整性
离线包准备好了,接下来就是传到目标机器。传输方式取决于现场条件,常见的有:scp/rsync走内网、U盘拷贝、带外管理口挂载、或者通过堡垒机的文件分发功能。
我这次用的是scp:
scp /root/mysql8_offline/*.rpm root@内网IP:/opt/mysql8rpm/传完后,务必做完整性校验。rpm包在传输过程中如果损坏,虽然不至于完全装不了,但会让安装行为变得很诡异,比如安装到一半报校验错误,或者装完启动时才发现二进制文件有问题。校验方式很简单,用md5sum或sha256sum和源文件比对:
cd /opt/mysql8rpm sha256sum *.rpm比对源机器上同样命令的输出结果,一致再继续。这个习惯我强烈建议保留,它不仅是离线安装的问题,也是任何二进制交付场景的基础素养。
3. 离线安装MySQL 8的完整实操步骤
3.1 按依赖顺序安装RPM包
物料到齐、环境检查做完,就可以正式安装了。RPM包之间的安装顺序有讲究,不按顺序装会报依赖缺失,但按正确顺序装就能顺利通过。
推荐安装顺序:
- mysql-community-common
- mysql-community-libs
- mysql-community-client
- mysql-community-icu-data-files
- mysql-community-libs-compat
- mysql-community-server
实际命令行如下:
cd /opt/mysql8rpm rpm -ivh mysql-community-common-8.0.36-1.el8.x86_64.rpm rpm -ivh mysql-community-libs-8.0.36-1.el8.x86_64.rpm rpm -ivh mysql-community-client-8.0.36-1.el8.x86_64.rpm rpm -ivh mysql-community-icu-data-files-8.0.36-1.el8.x86_64.rpm rpm -ivh mysql-community-libs-compat-8.0.36-1.el8.x86_64.rpm rpm -ivh mysql-community-server-8.0.36-1.el8.x86_64.rpm如果安装过程中报缺失perl或其他系统依赖,用你之前下载的依赖RPM包手动装上,再回来继续:
rpm -ivh perl-*.rpm ncurses-libs-*.rpm libaio-*.rpm有一个更省事的替代方案:用 yum localinstall 安装本地rpm目录,它会自动解析目录内可行的依赖关系,还需要联网做额外的仓库解析时会失败,但目录内的包能自动处理。命令如下:
dnf localinstall ./mysql-community-*.rpm我这里还是建议逐个 rpm -ivh 安装。原因很简单:逐个装,每步的成功失败你都能明确知道,排查时定位更快。而 localinstall 一旦中途报错,它究竟解析到哪一步、缺了什么,有时看输出都费劲。
另外,如果在生产环境里之前装过MySQL的老版本,rpm安装时可能会报文件冲突。比如 /etc/my.cnf 已存在,rpm默认不会覆盖,会报“conflicts with file”错误。处理方式是把旧配置文件备份改名,再重新执行安装命令:
mv /etc/my.cnf /etc/my.cnf.bak rpm -ivh mysql-community-server-8.0.36-1.el8.x86_64.rpm3.2 初始化MySQL数据目录
RPM包安装完成后,mysqld命令已经就位,但此时数据目录还是空的。MySQL 8不能像5.7以前那样直接 service mysqld start 由脚本自动初始化,需要手动执行初始化命令。有两种方式:
第一种,随机临时密码方式:
mysqld --initialize --user=mysql执行完成后,临时密码会写到 /var/log/mysqld.log 里。用 grep 查一下:
grep 'temporary password' /var/log/mysqld.log第二种,空密码方式,适合内网测试环境:
mysqld --initialize-insecure --user=mysql这样初始化出的root账号没有密码,登录后需要立即设置。我在生产环境推荐第一种,至少临时密码能保证首次登录前没有人能直接用root空密码进入数据库。
初始化过程中有几点容易踩坑。第一,执行用户必须是mysql,这个用户是rpm包安装时自动创建的。如果你用root执行初始化,数据目录的文件属主会变成root,后续mysqld启动时以mysql用户运行,没有权限访问,直接报错。第二,数据目录默认在 /var/lib/mysql,如果之前残留过文件,初始化会被跳过或失败,必须确保目录是空的。第三,日志目录 /var/log/mysql 或 /var/log 需要有mysql用户的写权限,否则初始化过程不会产出日志,你也找不到临时密码。
初始化完成后,可以先根据自己的业务需求调整 /etc/my.cnf。MySQL 8默认配置已经比较合理,但有几个参数我是常调的:
[mysqld] port=3306 character-set-server=utf8mb4 collation-server=utf8mb4_general_ci max_connections=500 innodb_buffer_pool_size=1G default-time-zone='+8:00'innodb_buffer_pool_size的设定需要注意:经验值是物理内存的50%到70%,但别超过实际可用内存。机器内存2GB时,我通常设为512M;内存4GB时设1G;内存8GB以上再考虑更大的值。这个参数直接影响MySQL的读写性能,设太大容易触发OOM,设太小则索引和数据频繁刷盘,查询会明显变慢。
3.3 启动服务并配置开机自启
初始化完成后,就可以启动服务了:
systemctl start mysqld启动后先确认状态,再确认进程和端口:
systemctl status mysqld ss -lntp | grep 3306如果服务没有正常起来,第一时间看日志,MySQL 8的默认日志路径有两个:
tail -100 /var/log/mysqld.log tail -100 /var/log/mysql/mysqld.log启动成功后,顺手配置开机自启:
systemctl enable mysqld这样每次服务器重启后,MySQL服务会自动拉起,不需要人工干预。这一步看着不起眼,但运维工作中经常有人忘记,服务器一重启数据库不在了,业务报警才发现问题。
如果要修改数据目录位置,需要做更复杂的处理:新建目录、复制文件、调整权限,并在my.cnf里指定 datadir,同时还要处理SELinux上下文。这个操作不建议在离线部署中做,因为一旦SELinux上下文没配置好,MySQL反复启动失败,排查起来比较费时间。默认的 /var/lib/mysql 在根分区上,生产环境可以把数据盘挂载到 /var/lib/mysql 或者用软链接方式调整,总之数据目录单独挂载到数据盘更稳妥。
3.4 修改root密码与创建业务账号
服务启动后,接下来就是首次登录和密码设置。如果你用的是临时密码方式,先登录:
mysql -uroot -p输入临时密码进入MySQL命令行。MySQL 8默认安装了密码校验插件validate_password,所以设置密码时不能太简单。先看一下当前密码策略:
SHOW VARIABLES LIKE 'validate_password%';默认策略要求密码至少8位,包含大小写字母、数字和特殊字符。如果这是生产库,建议保持默认强度;如果只是内网测试环境,想简化密码,可以临时调低策略:
SET GLOBAL validate_password.policy = LOW; SET GLOBAL validate_password.length = 6;但注意,这两个设置只对当前实例生效,重启后恢复默认。要持久化,需要把对应的配置项写进my.cnf的[mysqld]段。
有部分版本的validate_password组件还需要在配置文件里显式声明,否则修改全局变量时报“Variable 'validate_password.policy' doesn't exist”。如果遇到这种情况,说明你的实例还没加载该组件,需要手动安装:
INSTALL COMPONENT 'file://component_validate_password';修改root密码的标准姿势如下:
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码'; FLUSH PRIVILEGES;密码设置完成后,再为业务单独创建一个账号,避免业务代码直接用root连库。这是最基本的权限隔离概念:
CREATE USER 'appuser'@'%' IDENTIFIED BY '业务密码'; GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'%'; FLUSH PRIVILEGES;这里 'appuser'@'%' 表示允许任意主机通过该账号连接数据库,适合业务服务器和数据库服务器分离的场景。如果业务代码只和数据库在同一台机器上,建议把 '%' 改成 'localhost',安全面会小很多。远程登录时还需要注意,MySQL 8默认的 bind-address 是 0.0.0.0,也就是监听所有网卡,不用额外改;但如果你在my.cnf里把 bind-address 设成了 127.0.0.1,远程连接一定会失败,这个参数需要提前确认。
4. 常见问题与排查技巧实录
4.1 依赖缺失类报错
离线安装MySQL,碰到最多的报错就是rpm安装时提示 Failed dependencies。典型的输出长这样:
error: Failed dependencies: libaio.so.1()(64bit) is needed by mysql-community-server-8.0.36-1.el8.x86_64这个问题的根源在准备工作做得不够。如果你严格按照2.3节用 yum install --downloadonly 拉取了依赖包,这种报错基本不会出现。但万一目标机器上还有其他软件包缺失,rpm会明确告诉你缺什么,你只要去离线包里找到对应的rpm文件装上即可。
优先排查顺序:
rpm -qa | grep libaio rpm -qa | grep perl rpm -qa | grep ncurses缺哪个装哪个。如果下载的依赖包里没有对应文件,就得想其他办法,比如换一台联网的同版本机器重新执行 --downloadonly,或者直接从官网下载对应依赖包。
有没有必要用 --nodeps 强制安装?我的建议是:不到万不得已,不要用。--nodeps 会让rpm跳过依赖检查,包本身能装上,但缺失的系统库会导致mysqld启动时直接报错,比如找不到libaio.so.1,这种错误比安装时缺依赖更难排查。宁可多花十分钟把依赖备齐,也别让服务起不来再返工。
关于如何判断一个rpm包依赖哪些库,可以用这个命令提前检查:
rpm -qpR mysql-community-server-8.0.36-1.el8.x86_64.rpm它会列出这个包所有依赖的库和rpm包名,拿到这个清单,对照系统里已有的包,就能提前发现缺什么。这个技巧在离线安装中非常实用。
4.2 初始化或启动失败的典型原因
MySQL 8启动失败,90%的原因集中在三个地方:数据目录权限、SELinux、日志目录问题。
数据目录权限问题最常见的表象是:
[ERROR] failed to set datadir to /var/lib/mysql [ERROR] Can't open the mysql.plugin table排查方法很简单:
ls -ld /var/lib/mysql如果属主不是mysql用户和mysql组,就改回去:
chown -R mysql:mysql /var/lib/mysqlSELinux导致的启动失败也很常见,尤其在你移动过数据目录、改了端口、或者自定义了日志路径之后。先看当前SELinux状态:
getenforce如果返回 Enforcing,可以临时关闭验证:
setenforce 0然后再启动MySQL。如果关闭SELinux后能正常启动,说明确实是SELinux上下文的问题。稳妥的解决方案是恢复SELinux上下文,而不是直接关掉SELinux:
restorecon -Rv /var/lib/mysql但如果这是内网隔离环境、对安全等级要求没到强制SELinux的程度,我个人的处理习惯是直接把SELinux设为disabled,因为内网环境的SELinux带来的管理成本远大于它的安全收益。修改 /etc/selinux/config 里的 SELINUX=enforcing 改为 disabled,重启后生效。需要注意,setenforce 0 只对当前运行态生效,重启后会恢复成Enforcing,一定要把配置文件和运行态一起处理。
还有一个细节:MySQL 8的socket文件默认在 /var/lib/mysql/mysql.sock,运行时的pid文件在 /var/run/mysqld/ 目录。如果 /var/run/mysqld 目录不存在或权限不对,mysqld无法创建pid文件,启动也会失败。手动创建并授权:
mkdir -p /var/run/mysqld chown mysql:mysql /var/run/mysqld4.3 忘记root密码的应急处理
生产环境最糟心的问题之一就是忘记了MySQL的root密码。尤其在离线内网环境,没有外部工具可以介入,只能本机处理。MySQL 8的密码重置思路是:先跳过授权表启动服务,再更新root账号。
具体步骤:
先在my.cnf里加一行基础配置,或者直接用--skip-grant-tables参数手动启动:
mysqld --skip-grant-tables --user=mysql &注意,MySQL 8的skip-grant-tables模式有个限制:你进入MySQL后不能直接用ALTER USER修改密码,因为该模式下权限加载不完全。需要先让授权表在内存中重新生效:
FLUSH PRIVILEGES;然后再执行密码修改:
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码'; FLUSH PRIVILEGES;改完密码后,退出MySQL,把后台启动的mysqld进程杀掉,再用 systemctl start mysqld 正常启动。这个流程我在MySQL 8.0.36上验证过,可行。
另一种办法是用 --init-file 指定一个包含SQL语句的文件,启动时自动执行。这个方式更优雅,适合在服务能正常登录但密码丢失的场景下使用。先写一个sql文件:
echo "ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';" > /tmp/reset_pwd.sql然后以init-file启动:
mysqld --init-file=/tmp/reset_pwd.sql --user=mysql &启动完成后删除该文件,防止SQL语句里的密码留在磁盘上。
4.4 远程登录连不上的排查路径
MySQL能启动,数据也能写入,但业务服务器连数据库时提示无法连接。这种问题通常不是MySQL本身故障,而是网络链路上的配置问题。
按顺序排查:
第一步,确认MySQL服务监听地址:
ss -lntp | grep 3306看到 0.0.0.0:3306 说明监听所有网卡;如果看到 127.0.0.1:3306,说明server配置了 bind-address=127.0.0.1,外部无法连接,改回 0.0.0.0 后重启服务。
第二步,确认防火墙放行3306端口:
firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload firewall-cmd --list-ports内网环境有时候会有多道防火墙,应用防火墙、宿主防火墙、云安全组,每一层都要检查。我遇到过最匪夷所思的情况是:系统firewalld没开、云安全组也放行了,最后发现是交换机ACL挡了端口,这种只能在现场根据网络拓扑一层层排查,没有捷径。
第三步,确认账号host范围。如果你创建账号时用的是 'appuser'@'localhost',那业务服务器肯定连不上。改用 '%' 或者指定业务服务器的IP网段:
CREATE USER 'appuser'@'192.168.%' IDENTIFIED BY '密码';然后授权并刷新。这个操作可以在MySQL运行时直接调整,不需要重启。
第四步,注意网络时延和TCP连接超时。内网环境通常没有问题,但如果业务服务器和数据库隔着多跳网络,连接超时也会表现为“无法连接”,需要用 ping、telnet 等工具通断检测后再判断。
4.5 一些让你少走弯路的经验
最后补几条纯粹来自现场的经验,不写进常规文档但非常有用:
- RPM包版本号和镜像站不一致时,优先选择镜像站能下到的版本。宁可版本旧一个patch,也不要手忙脚乱去官网找旧包,因为官网的仓库通常只保留最新版本。
- 离线安装前,先把 /etc/my.cnf 备份一份。RPM安装和后续修复过程中很有可能会被覆盖或者被修改,备份能让你快速回溯。
- 使用 systemctl start mysqld 前,先确认 /var/lib/mysql 目录的属主。这一步常被人忽略,但重要性我前面已经强调过。
- 建议把离线包整理成固定的“软件交付目录”:包含RPM目录、依赖目录、README文件(写明安装顺序和已知问题)。团队里多人操作时,这个习惯能省去大量重复沟通成本。
- 所有rpm包安装完成后,用 mysql --version 确认客户端版本和服务端版本一致。版本不一致也会引起连接时的鉴权兼容问题,这在RHEL8系系统上时有发生。
离线安装MySQL 8这件事,只要物料准备齐全、顺序不搞错,成功率其实很高。我个人在实际操作中的体会是:最难的不是命令本身,而是那些看不见的前提条件——SELinux状态、已有包冲突、数据目录残留。宁可在前期把这些检查做足、做细,也不要等安装报错后救火。版本锁定、物料清单、日志路径,这几样东西心中有数,离线部署就能从“老运维才能干”变成“照着清单就能复现”的标准化流程。