多台 Ubuntu 服务器在隔离内网环境下的离线部署与运维排错
前言:空气隔离内网的“战役”
在金融、政企、国企或军工等高安全等级的项目中,“空气隔离(Air-gapped)” 是一道无法愈越的物理红线。当进入客户的内网机房时,你面对的是几台彻底无法访问外网的物理服务器或虚拟机。这里没有 apt install,没有 Docker Hub,没有网络镜像源,甚至连找个搜索引擎查看报错都做不到。
手头仅有的一件武器,就是你在外部联网环境下下载好、并通过跳板机或安全 U 盘带入内网的“离线安装包”。
本文将真实还原某政企项目离线部署的完整全过程。我们将从 3 台纯内网 Ubuntu 22.04 LTS 服务器的规划开始,手把手完成 5 大核心中间件的离线底座构建,发布前后端服务,并深度剖析可以直接投产的 Nginx 完整配置。最后,我们将现场复盘四个经典的线上“救火”排错场景,并附赠两个企业级黄金运维脚本,助你掌握真正的生产线第一线部署与运维能力。
一、 架构拓扑与内网安全准入规则
1.1 生产主机矩阵与角色分配
为了实现高并发、高可用和数据隔离,我们规划了 3 台物理隔离的 Ubuntu 22.04 LTS 服务器,具体的职责划分和内网 IP 分配如下表所示:
| 主机名称 | 内网 IP 地址 | 部署的服务与角色 | 开放的端口白名单 |
|---|---|---|---|
| Server A (网关节点) | 192.168.10.11 |
Nginx (网关/负载均衡/静态资源托管), Keepalived | 外部访问: 80, 443运维访问: 50022 (SSH) |
| Server B (应用节点一) | 192.168.10.12 |
Java 业务应用 Node-01, Redis (Master), MinIO | 仅对 Server A: 8080 (Java)仅对内网互信: 6379, 9000 (MinIO)运维: 50022 |
| Server C (应用节点二) | 192.168.10.13 |
Java 业务应用 Node-02, MySQL 8.0 (主库) | 仅对 Server A: 8080 (Java)仅对内网互信: 3306 (MySQL)运维: 50022 |
1.2 内网严苛安全组(UFW 防火墙)配置
在企业安全规范中,绝不允许数据库或缓存端口暴露在公网,甚至不允许非关联的内网主机随意探测。我们将通过 Ubuntu 自带的 ufw 工具进行精细化准入限制。
在网关节点 Server A 执行:
1 | # 默认拒绝所有入站流量,允许所有出站流量 |
在应用节点 Server B 执行:
1 | ufw default deny incoming |
在应用节点 Server C 执行:
1 | ufw default deny incoming |
二、 系统级安全加固与高并发性能调优
在正式安装任何软件之前,必须对 Ubuntu 操作系统进行全面的系统初始化和加固调优。这关系到线上系统的安全性和高并发承载能力。
2.1 SSH 生产连接与堡垒机密码代填规范
在金融和政企项目中,客户通常规定严禁随意在虚拟机上分发和注入自定义的 SSH 密钥对。所有的虚机账户必须通过统一的**堡垒机(如 JumpServer、行云管家等)**进行托管、审计与账号密码代填登录。各虚机分配的都是高强度的 root 账户与复杂随机密码。
因此,为了确保堡垒机能够正常代填密码并登录目标主机,我们绝不能粗暴地禁用 root 登录与密码验证。我们需要保留它们,而将安全红线收缩在堡垒机的准入限制和内网 UFW 防火墙上。
在 3 台服务器上修改默认的 /etc/ssh/sshd_config 配置文件,加入以下切合堡垒机环境的安全加固参数:
1 | # 修改 SSH 默认端口,防范局域网大范围暴力扫描 |
修改完成后,重启 SSH 服务以使配置生效:
1 | sudo systemctl restart sshd |
[!IMPORTANT]
真实落地避坑指南:在政企交付现场,千万不要为了盲目追求所谓的“禁用密码/禁用 root”而修改配置。一旦修改并重启服务,堡垒机与目标虚机的底层通信和代填凭证将瞬间失效,你将被直接锁在系统之外!必须根据客户的安全规范(堡垒机白名单代填),保留PermitRootLogin yes。
2.2 彻底解决“Too many open files”高并发隐患
Linux 默认的最大文件打开数(nofile)通常是 1024,在流量稍大的生产环境,Nginx 或 SpringBoot 会在瞬间抛出 java.io.IOException: Too many open files 导致服务宕机。
在 3 台主机上编辑 /etc/security/limits.conf 文件,在文件末尾追加:
1 | * soft nofile 65535 |
同时在 /etc/pam.d/common-session 中确保加入:
1 | session required pam_limits.so |
2.3 生产级 Linux 内核参数调优 (sysctl.conf)
针对高并发 Web 场景下大量 TCP 短连接产生的 TIME_WAIT 堆积,以及半连接队列溢出问题,在 3 台主机上编辑 /etc/sysctl.conf:
1 | # 开启 TCP 连接重用,允许将 TIME_WAIT sockets 重新用于新的 TCP 连接 |
执行以下命令,无须重启直接刷新内核参数:
1 | sudo sysctl -p |
2.4 企业级标准目录规范
为了防范混乱的运维操作,我们必须在多台主机上统一规范目录架构:
1 | sudo mkdir -p /var/tmp/offline_packages # 离线安装包暂存区 |
三、 5大核心中间件完全离线安装
由于处于纯离线环境,所有中间件安装包必须由跳板机通过安全通道上传至各机器的 /var/tmp/offline_packages/ 目录下。
3.1 JDK 17 完全离线安装(部署于 Server B、Server C)
后端服务基于 Java 17 构建。
- 将
openjdk-17.0.2_linux-x64_bin.tar.gz上传至目标服务器。 - 解压并移动到规范目录:
1
2
3cd /var/tmp/offline_packages
tar -zxvf openjdk-17.0.2_linux-x64_bin.tar.gz
sudo mv jdk-17.0.2 /usr/local/services/ - 创建版本软链接(这样做的好处是方便日后无缝平滑升级 JDK,业务脚本无须改动):
1
2cd /usr/local/services
sudo ln -s jdk-17.0.2 jdk - 配置全局环境变量,在
/etc/profile底部追加:1
2export JAVA_HOME=/usr/local/services/jdk
export PATH=$JAVA_HOME/bin:$PATH - 使配置立即生效并进行真实校验:
1
2
3source /etc/profile
java -version
# 验证输出:openjdk version "17.0.2" 2022-01-18
3.2 MySQL 8.0 完全离线安装(GLIBC 二进制免编译版,部署于 Server C)
对于离线环境,选用 GLIBC 版二进制包(mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz)是最优解,比 deb 包更纯净且免除底层动态库缺失导致的无法安装问题。
- 创建 MySQL 运行专属低权限用户与组:
1
2sudo groupadd mysql
sudo useradd -r -g mysql -s /bin/false mysql - 解压并规整安装目录:
1
2
3cd /var/tmp/offline_packages
tar -xvf mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz
sudo mv mysql-8.0.28-linux-glibc2.12-x86_64 /usr/local/services/mysql - 编写符合真实生产环境参数的配置文件
/etc/my.cnf:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24[mysqld]
user=mysql
port=3306
basedir=/usr/local/services/mysql
datadir=/data/mysql
socket=/tmp/mysql.sock
log-error=/data/mysql/mysql-error.log
pid-file=/data/mysql/mysql.pid
# 生产核心性能参数
max_connections=1000
max_user_connections=800
# 根据 Server C 物理内存的 60% 动态配置(例如 16G 内存配置为 9G)
innodb_buffer_pool_size=9G
innodb_log_file_size=1G
innodb_log_buffer_size=16M
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
# 字符集与时区
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default-time-zone='+8:00'
lower_case_table_names=1 - 数据目录授权并执行核心初始化命令:
1
2
3
4sudo chown -R mysql:mysql /data/mysql
cd /usr/local/services/mysql
# 执行数据库初始化,注意记下输出最后一行随机生成的 MySQL 初始 root 密码!
sudo ./bin/mysqld --defaults-file=/etc/my.cnf --initialize --user=mysql - 将 MySQL 注册为 Systemd 服务。编写
/etc/systemd/system/mysqld.service:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[Unit]
Description=MySQL Server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/services/mysql/support-files/mysql.server start
ExecStop=/usr/local/services/mysql/support-files/mysql.server stop
User=mysql
Group=mysql
Restart=on-failure
PrivateTmp=true
[Install]
WantedBy=multi-user.target - 启动并配置开机自启:
1
2
3sudo systemctl daemon-reload
sudo systemctl enable mysqld
sudo systemctl start mysqld - 登录数据库修改初始密码,并开通局域网内物理隔离的内网访问权限:在交互命令行中执行:
1
2# 使用刚刚记下的临时随机密码登录
/usr/local/services/mysql/bin/mysql -uroot -p -S /tmp/mysql.sock1
2
3
4
5
6
7
8
9
10
11-- 修改本地 root 密码为强密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Company@Secure#MySQL2026';
-- 禁止 root 外部远程访问,仅允许本地访问以保证物理安全
-- 为 Server B 的 Java 后端节点创建专属的内网通信账号,实现细粒度控制
CREATE USER 'app_user'@'192.168.10.12' IDENTIFIED BY 'AppCon#Secure@2026';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE ON *.* TO 'app_user'@'192.168.10.12';
-- 刷新权限并退出
FLUSH PRIVILEGES;
EXIT;
3.3 Redis 7.2 完全离线安装(部署于 Server B)
- 上传
redis-7.2.4.tar.gz到 Server B。 - 源码解压并编译安装:
1
2
3
4cd /var/tmp/offline_packages
tar -zxvf redis-7.2.4.tar.gz
cd redis-7.2.4
make PREFIX=/usr/local/services/redis install - 复制默认配置文件,进行生产安全加固修改:编辑
1
2mkdir -p /usr/local/services/redis/conf
cp redis.conf /usr/local/services/redis/conf//usr/local/services/redis/conf/redis.conf:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# 严禁绑定 0.0.0.0。只监听本机回环以及 Server B 的内网 IP,防止外界探测
bind 127.0.0.1 192.168.10.12
port 6379
# 以后台进程守护模式运行
daemonize no
# 设置极其强悍的访问密码
requirepass Redis@Secure#AuthPassword2026
# 数据库数据目录
dir /data/redis
# 生产级持久化策略配置:RDB 配合 AOF 双防灾方案
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# 安全加固:禁用高危命令以防黑客提权或误操作清空数据
rename-command FLUSHALL "SECURE_FLUSHALL"
rename-command FLUSHDB "SECURE_FLUSHDB"
rename-command KEYS "SECURE_KEYS" - 将其注册为 Systemd 服务统一托管。编写
/etc/systemd/system/redis.service:1
2
3
4
5
6
7
8
9
10
11
12
13[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
User=ops
ExecStart=/usr/local/services/redis/bin/redis-server /usr/local/services/redis/conf/redis.conf
ExecStop=/usr/local/services/redis/bin/redis-cli -a Redis@Secure#AuthPassword2026 shutdown
Restart=always
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target - 启动服务:
1
2
3sudo systemctl daemon-reload
sudo systemctl enable redis
sudo systemctl start redis
3.4 MinIO 文件存储完全离线安装(部署于 Server B)
MinIO 是极具生产代表性的单二进制文件架构,非常适合在无公网的私有化环境单机快速拉起。
- 上传可执行二进制包
minio和客户端工具mc到/usr/local/services/minio/bin/。 - 授予可执行权限并进行目录规整:
1
2
3
4
5sudo mkdir -p /usr/local/services/minio/bin
sudo mv /var/tmp/offline_packages/minio /usr/local/services/minio/bin/
sudo mv /var/tmp/offline_packages/mc /usr/local/services/minio/bin/
sudo chmod +x /usr/local/services/minio/bin/*
sudo chown -R ops:ops /usr/local/services/minio /data/minio - 建立环境变量配置文件
/etc/default/minio以实现凭证与启动逻辑的解耦:1
2
3
4
5# 绑定内网 IP 端口与控制台端口
MINIO_OPTS="--address 192.168.10.12:9000 --console-address 192.168.10.12:9001 /data/minio"
# 超强管理员账户与密码
MINIO_ROOT_USER=minio_admin_user
MINIO_ROOT_PASSWORD=MinIO@Secure#AccessKey2026 - 编写 Systemd 服务托管文件
/etc/systemd/system/minio.service:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[Unit]
Description=MinIO Object Storage
Documentation=https://docs.min.dev
After=network.target
[Service]
User=ops
Group=ops
EnvironmentFile=/etc/default/minio
ExecStart=/usr/local/services/minio/bin/minio server $MINIO_OPTS
Restart=always
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target - 启动 MinIO 并开启自启:
1
2
3sudo systemctl daemon-reload
sudo systemctl enable minio
sudo systemctl start minio
3.5 Nginx 1.24 完全离线安装(源码级静态链接编译,部署于 Server A)
离线编译 Nginx 最大的痛点是由于缺失网络,系统通常没有预装 PCRE (用于正则表达式)、Zlib (用于 Gzip 压缩)、OpenSSL (用于 SSL 证书加密) 的库。如果直接执行 ./configure 会疯狂报错。
黄金避坑技巧:将三大底层库的源码包打包带进内网,不安装它们,而是在 Nginx 编译时,直接静态绑定编译到 Nginx 的二进制文件中。这能创造一个完全无视目标环境依赖的、纯净的单体 Nginx 二进制!
- 确保上传了以下四个源码包到
/var/tmp/offline_packages/:nginx-1.24.0.tar.gzpcre-8.45.tar.gzzlib-1.2.13.tar.gzopenssl-1.1.1w.tar.gz
- 一键解压所有源码包:
1
2
3
4
5cd /var/tmp/offline_packages
tar -zxvf nginx-1.24.0.tar.gz
tar -zxvf pcre-8.45.tar.gz
tar -zxvf zlib-1.2.13.tar.gz
tar -zxvf openssl-1.1.1w.tar.gz - 进入 Nginx 源码目录,利用
--with-xxx=参数静态绑定解压出的源码路径,执行极度优雅的高阶静态编译:1
2
3
4
5
6
7
8
9
10
11
12cd nginx-1.24.0
./configure --prefix=/usr/local/services/nginx \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-pcre=/var/tmp/offline_packages/pcre-8.45 \
--with-zlib=/var/tmp/offline_packages/zlib-1.2.13 \
--with-openssl=/var/tmp/offline_packages/openssl-1.1.1w
# 执行编译与安装
make -j$(nproc)
sudo make install - 编译安装完成后,验证 Nginx 二进制并查阅其内置绑定的库:
1
2/usr/local/services/nginx/sbin/nginx -V
# 验证输出,你会发现 OpenSSL、PCRE、Zlib 全部以静态链接的形式内嵌在 Nginx 中! - 托管为 Systemd 服务
/etc/systemd/system/nginx.service:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[Unit]
Description=The Nginx HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/usr/local/services/nginx/logs/nginx.pid
ExecStartPre=/usr/local/services/nginx/sbin/nginx -t
ExecStart=/usr/local/services/nginx/sbin/nginx
ExecReload=/usr/local/services/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target - 启动并允许开机自启:
1
2
3sudo systemctl daemon-reload
sudo systemctl enable nginx
sudo systemctl start nginx
四、 前后端发布流程与低权限安全托管
在 5 大中间件底座稳固搭建好之后,开始进入核心的前后端服务打包和部署工作。
4.1 离线制品的完整性 SHA256 检验
在跳板机上将前端制品打包压缩为 frontend_v1.0.tar.gz,后端 SpringBoot 打包为 app.jar。为了防止网络传输过程中由于磁盘块损坏或局域网抖动引起的文件损坏问题,必须在源端计算并校验 SHA256:
1 | # 在跳板机执行计算 |
4.2 拒绝使用 root!SpringBoot 应用低权限 Systemd 系统服务托管
在线上绝对不允许使用 root 用户运行 Java 后端服务,一旦应用被黑客挖掘出反序列化等命令执行漏洞,攻击者将直接拿到系统底层最高控制权。
- 在 Server B 和 Server C 上分别创建低权限隔离组和账号:
1
2sudo groupadd appgroup
sudo useradd -r -g appgroup -s /bin/false -d /usr/local/services/app apprun - 将后端程序放置在
/usr/local/services/app/目录下,并进行专属低权限授权:1
2
3
4sudo mkdir -p /usr/local/services/app
sudo cp /var/tmp/offline_packages/app.jar /usr/local/services/app/
sudo chown -R apprun:appgroup /usr/local/services/app
sudo chmod 500 /usr/local/services/app/app.jar # 仅允许只读执行,禁止写入防范被篡改 - 编写专属的 Systemd 应用服务托管单元
/etc/systemd/system/app.service。我们在这里融合了内存硬限制以及启动 JVM 参数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28[Unit]
Description=Java SpringBoot Application
After=network.target mysql.service redis.service
[Service]
# 指定以低权限账户和组运行
User=apprun
Group=appgroup
WorkingDirectory=/usr/local/services/app
# JVM 堆大小等调优参数显式配置,防止内存无限蔓延
Environment="JAVA_OPTS=-Xms512m -Xmx1G -XX:+UseG1GC -Dspring.profiles.active=prod"
ExecStart=/usr/local/services/jdk/bin/java $JAVA_OPTS -jar /usr/local/services/app/app.jar
# 发生非正常退出时 10 秒后自动拉起
Restart=on-failure
RestartSec=10s
# 限制输出到标准输出的系统日志格式
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=app-backend
# 安全限制:拒绝写入临时文件等
PrivateTmp=true
[Install]
WantedBy=multi-user.target - 刷新并加载服务:
1
2
3sudo systemctl daemon-reload
sudo systemctl enable app.service
sudo systemctl start app.service
4.3 生产发布与自动回滚黄金运维脚本 (deploy.sh)
这是一个真正可以直接放在生产上使用的一键升级发布与容灾脚本。每次升级只需将最新的 app.jar 放入 /var/tmp/offline_packages/ 即可自动执行:
1 |
|
五、 生产级可直接投产的完整的 nginx.conf 剖析
作为整个集群对外唯一的顶级七层网关,网关节点 Server A 的 nginx.conf 承载了安全性、高并发吞吐量以及静态前端托管的核心使命。以下是精心提炼且可直接覆盖到生产环境的完整配置文件:
1 | # 运行账号,以 Linux 低权限的 www-data 组和用户运行,防止提权 |
六、 线上运维“救火”:日志分析与四大经典故障排错实战
真实环境的升级和发版,绝不会是一帆风顺的。当发版出现异常、请求全部报错时,作为资深运维或技术大拿,必须迅速祭出日志和连通性排查工具,完成精准“救火”。
6.1 线上日志诊断武器库
- 实时追踪业务服务(Systemd 托管下的 Java)日志:
1
2
3
4
5# 实时监视后端最近 100 行日志输出,不产生分页
journalctl -u app.service -f -n 100 --no-pager
# 搜索特定时段或者包含 ERROR 字眼的异常崩溃栈
journalctl -u app.service --since "2026-05-30 00:00:00" | grep -i -E "error|exception" - 实时追踪网关负载日志:
1
2# 观察错误日志,定位网关和后端的通信错误
tail -f /usr/local/services/nginx/logs/error.log
6.2 经典事故 1:调用 API 时前端一直转圈,最终报 502 Bad Gateway
- 故障现象:在浏览器访问系统一切正常,但点击登录或加载表格时系统转圈,控制台狂报
502 Bad Gateway。
第一现场排查链条:
查看 Nginx 的
error.log。在 Server A 网关发现类似如下报错:1
2026/05/30 23:56:01 [error] 1405#0: *8 connect() failed (111: Connection refused) while connecting to upstream, client: 220.181.10.45, server: 192.168.10.11, request: "POST /api/login HTTP/1.1", upstream: "http://192.168.10.12:8080/login"
专业诊断:这代表 Nginx 向后端
192.168.10.12的8080发起 TCP 握手时,直接被应用节点Connection refused(连接拒绝)。代表后端服务可能根本没有监听该端口。去应用主机 Server B 排查端口存活:
1
2ss -ntlp | grep 8080
# 发现输出一片空白!代表 Server B 的 8080 没有被监听。使用 journalctl 排查 Java 业务进程的日志:
1
journalctl -u app.service -n 150 --no-pager
发现数据库相关的报错栈:
1
2
3
4Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet successfully received from the server was 0 milliseconds ago. The driver has not received any packets from the server.
...
Caused by: java.net.ConnectException: Connection refused (Connection refused)破案结论:SpringBoot 在启动时因为连不上 Server C 上的 MySQL 数据库,抛出底层通信异常,直接导致初始化失败、服务自动终止退出,从而引起 Nginx 返回 502。接下来需要按照事故 3的步骤去排查 Server C 上的数据库。
6.3 经典事故 2:静态网页加载失败,打开一片空白,报 403 Forbidden / Permission denied
- 故障现象:刚把前端
dist.tar.gz解压覆盖到/var/www/html/dist下,清理缓存刷新浏览器,结果直接显示403 Forbidden。
第一现场排查链条:
查看网关 Server A 上 Nginx 错误日志:
1
2026/05/30 23:56:10 [error] 1405#0: *12 open() "/var/www/html/dist/index.html" failed (13: Permission denied), client: 220.181.10.45, server: 192.168.10.11, request: "GET / HTTP/1.1"
专业诊断:Permission denied(权限被拒绝) 代表操作系统的文件权限控制链失效了。Nginx 的 Worker 进程由于无权读取物理磁盘上的
index.html导致直接抛出 403。排查文件的 Linux 所有者与权限级别:
1
2ls -ld /var/www/html/dist
# 输出显示:drwx------ 3 root root 4096 May 30 23:00 /var/www/html/dist核心原理拆解:
- 看到没有?
dist目录的用户所有者和组所有者都是root,且其权限为700(即除 root 之外的任何账户彻底无权读取甚至进入该目录)! - 而我们在
nginx.conf顶部指定的 Worker 进程账户是www-data,非 root 账号在700面前被操作系统无情挡下!
- 看到没有?
完美解决命令:
规范静态网页目录的归属,并将权限恢复为行业标准的755(目录) 与644(文件):1
2
3
4
5
6# 将所有权正确递归变更为 www-data
sudo chown -R www-data:www-data /var/www/html/dist
# 将目录权限恢复为 755 (必须允许执行 x 权限,用户才能进入目录!)
sudo find /var/www/html/dist -type d -exec chmod 755 {} \;
# 将文件权限恢复为 644
sudo find /var/www/html/dist -type f -exec chmod 644 {} \;刷新网页,瞬间成功渲染!
6.4 经典事故 3:后端启动崩溃,抛出数据库连接拒绝(最小化赋权纠偏)
- 故障现象:如事故 1 中,SpringBoot 后端在拉起时抛出
Host '192.168.10.12' is not allowed to connect to this MySQL server并自崩。
第一现场排查链条:
测试内网主机间底层的网络与端口可达性:
在 Server B 上执行nc,探针 Server C 的数据库端口是否开启:1
2nc -zv 192.168.10.13 3306
# 输出:Connection to 192.168.10.13 3306 port [tcp/mysql] succeeded!专业诊断:网络连通性完美无损,且没有被 UFW 防火墙拦截。那么问题只可能出现在 MySQL 自身的授权机制上。
去 Server C 登录数据库,排查 MySQL 系统库的账号白名单:
1
/usr/local/services/mysql/bin/mysql -uroot -p -S /tmp/mysql.sock
1
SELECT user, host FROM mysql.user;
输出可能显示:
1
2
3
4
5
6+----------+-----------+
| user | host |
+----------+-----------+
| root | localhost |
| app_user | 127.0.0.1 |
+----------+-----------+破案结论:
app_user账号的白名单限制了只有来自127.0.0.1才能建立会话!现在后端是从 Server B(192.168.10.12)跨机器访问的,自然直接被拦截。防灾避坑警告:很多缺乏经验的运维图方便会直接把 host 设为
%(允许全球连接),这在严格审查的企业级内网是绝对不合格的,极易被内网渗透攻击一窝端。安全纠偏授权:
1
2
3
4-- 创建精准只服务于 192.168.10.12 的账号
CREATE USER 'app_user'@'192.168.10.12' IDENTIFIED BY 'AppCon#Secure@2026';
GRANT SELECT, INSERT, UPDATE, DELETE ON `company_prod_db`.* TO 'app_user'@'192.168.10.12';
FLUSH PRIVILEGES;完成纠偏授权后,Server B 重启后端服务,直接平滑启动上线。
6.5 经典事故 4:单页面应用(Vue/React)在点击子页面刷新时,浏览器直接报 404
- 故障现象:系统发布上线后,点击页面导航菜单跳转流畅,但在某些子页面(例如
/user/settings)按下浏览器刷新键 F5 时,网页没有加载,直接弹出了裸露的 Nginx 默认404 Not Found错误。
第一现场排查链条:
分析原理:
- 这是由于现代 Vue/React 的单页面应用(SPA)采用的是 前端路由模型。
- 当点击前端导航栏时,实际上只是前端 JS(如 Vue-Router)截获了路由请求并进行了虚拟页面渲染,完全没有请求过后端网关服务器,因原是平滑的。
- 但是当你按下 F5 刷新时,浏览器会向 Nginx 服务器真实发起一个
GET /user/settings的物理请求。而 Nginx 静态托管目录下根本没有/var/www/html/dist/user/settings这个物理目录或物理文件!最终抛出 404。
解决手段:
在网关配置server块托管静态资源的location /下,通过配置try_files进行静默转发拦截:1
2
3
4
5
6
7
8location / {
root /var/www/html/dist;
index index.html index.htm;
# 试探查找物理文件是否存在。如果都不存在,则静默重写请求至 index.html
# 此时 index.html 会载入入口 JS,前端路由会自动识别 "/user/settings" 路径并完成业务渲染
try_files $uri $uri/ /index.html;
}修改
nginx.conf后,热加载 Nginx 使配置平滑生效,故障彻底根除:1
/usr/local/services/nginx/sbin/nginx -s reload
七、 企业生产运维黄金脚本(彩蛋福利)
真正有含金量的系统管理员,不仅会部署和修复故障,更有一套高效的日常巡检和备份体系。
7.1 三台服务器日常巡检黄金脚本 (cluster_check.sh)
由于物理隔离服务器禁用了自定义密钥分发,只允许 root + 密码通过堡垒机连接,那我们在局域网的运维管理机上该如何实现一键批量巡检?
实战终极方案:
- 在管理机上离线安装
sshpass命令行工具(在互联网环境提前下载好sshpass_1.09-1_amd64.deb,放入 U 盘,在内网执行sudo dpkg -i sshpass_*.deb一键离线安装)。 - 为了不将高度机密的 root 密码硬编码在脚本文件中(防止越权泄露),脚本在启动时将交互式提示运维人员手动输入各主机的 root 密码,并临时缓存在脚本的内存变量中。既安全又实现了全自动一键批量巡检!
将此脚本部署在局域网内的运维管理机上:
1 |
|
7.2 MySQL 物理/逻辑备份并自动离线上传 MinIO 的脚本 (mysql_backup.sh)
此脚本部署在 MySQL 主机 Server C 上。每天深夜自动执行,逻辑导出数据库并利用二进制客户端安全备份到 Server B 的 MinIO 桶中,且自动轮转保留 7 天:
1 |
|
结语:稳固在实操里的力量
真正的部署与运维不是一成不变的教科书。在空气隔离的“深水内网”中,没有辅助工具,一切依靠你的基础功底。
通过掌握系统级内核调优,从源码彻底静态编译无任何依赖的 Nginx,利用标准的低权限 Systemd 安全托管 Java 服务,并通过严谨的故障日志链进行逻辑自洽的火速排错,你将彻底在脑海中构建出一个庞大系统的生产图景。
这,才是从一名开发走向顶级 DevOps/系统架构师的蜕变之路。