☰
Linux服务器环境一键部署:JDK、MySQL、Redis、Nginx自动化安装脚本实战
2026/10/21 21:02:21 网站建设 项目流程

简介:面向Linux服务器运维与开发人员,解决在裸机或云服务器上手动安装JDK、MySQL、Redis、Nginx等Web组件步骤繁琐、依赖易出错的问题,提供一键式部署方案。资源共27个文件,压缩包约930.56MB,主要包含Shell安装脚本、tar/tar.gz源码包、配置文件和rpm依赖包,覆盖安装、配置与依赖处理环节。已有298人学习下载。内含各组件独立安装脚本(jdk_install.sh、mysql_install.sh、redis_install.sh、nginx_install.sh)及init.conf、nginx.conf等配置,可自动化完成JAVA_HOME环境变量配置、MySQL密码重置与安全优化、Redis服务守护进程设置、Nginx反向代理及systemd服务管理;同时附带pcre、zlib、openssl等编译依赖,适合希望提升Linux自动化部署运维效率的初学者与中级工程师参考实战。

1. 为什么还在手动装 JDK、MySQL、Redis、Nginx:这五个软件的好与坏

拿到一台新 Linux 服务器,最耗时间的往往不是业务代码,而是先把 Java、MySQL、Redis、Nginx 这套基础环境装齐。手动操作过的人都有体会:JDK 要配环境变量还要验证版本,MySQL 初始化会随机生成密码,Redis 编译时缺依赖得先解决 gcc 和 tcl,Nginx 的 configure 参数漏一个后面就得重新编译。整个流程走下来,慢则两三个小时,快也要四十分钟,而且每一步都得靠百度。这套一键式安装脚本正是为了把这个过程压缩到十分钟以内:下载资源包、预检环境、安装四个组件、输出验证结果,全部自动化处理。适合刚接触 Linux 的开发者,也适合需要频繁交付环境的运维人员,把它当作一份可以直接复用的部署模板。

2. 自动化部署的核心思路:目录布局、幂等设计与主控流程

写一键安装脚本,最容易翻车的地方不是单个软件的安装,而是脚本的组织方式。如果所有逻辑都堆在一个文件里,版本升级、组件增删、出错排查都会变成灾难。我一般会把脚本拆成稳定的主控流程和可替换的模块文件两个部分,同时引入幂等机制,确保脚本重复执行不会把系统搞坏。

2.1 脚本目录布局:五个目录各管一件事

先看目录结构,这是一切设计的基础:

deploy/ ├── scripts/ # 存放所有安装脚本 │ ├── install_jdk.sh │ ├── install_mysql.sh │ ├── install_redis.sh │ ├── install_nginx.sh │ └── common.sh # 公共函数库 ├── packages/ # 存放下载好的 tar 包和 rpm 包 ├── config/ # 各组件的配置文件模板 ├── logs/ # 安装日志 └── control.env # 版本号、端口、密码等全局变量

这个布局的核心思想是:脚本只负责流程,所有可变的东西都收敛到control.env和config/里。换一个 MySQL 版本,只需要改control.env里的一个变量;改 Redis 密码,也只需要改配置模板。scripts/里的每个安装脚本都独立可执行,方便单独重装某个组件。

control.env的关键变量大概长这样:

# 版本相关 JDK_VERSION="17.0.8" MYSQL_VERSION="8.0.34" REDIS_VERSION="7.0.12" NGINX_VERSION="1.25.3" # 安装路径 INSTALL_BASE="/opt/software" JDK_HOME="${INSTALL_BASE}/jdk-${JDK_VERSION}" MYSQL_HOME="${INSTALL_BASE}/mysql-${MYSQL_VERSION}" REDIS_HOME="${INSTALL_BASE}/redis-${REDIS_VERSION}" NGINX_HOME="${INSTALL_BASE}/nginx-${NGINX_VERSION}" # 服务端口 MYSQL_PORT="3306" REDIS_PORT="6379" NGINX_PORT="80"

参数说明:版本变量独立维护,升级软件时只改这一处;安装路径统一放在/opt/software下,方便管理;端口在安装前就固定,避免后续配置混乱。common.sh里则放了几个最常用的公共函数,例如打印日志和检查命令是否存在。

2.2 幂等设计与重跑安全:重复执行不出二次坑

一键脚本最怕什么?最怕装到一半失败,修复问题后重新执行,结果之前的残留把新安装破坏了。我见过最典型的情况是:JDK 解压到一半中断,第二次执行时目录已存在,脚本没有清空就直接覆盖,最后java -version报错说找不到主类。

解决这个问题的通用方案是引入标记文件机制。每个组件安装完成后,在固定位置生成一个.done标记文件,脚本执行时先检查标记文件是否存在:

# 公共函数:检查组件是否已安装 check_done() { local component=$1 if [ -f "/opt/software/.${component}.done" ]; then echo "[SKIP] ${component} 已安装,跳过" return 0 fi return 1 } # 公共函数:执行系统命令并记录执行状态 run_cmd() { local step=$1 local cmd=$2 echo "[RUN] ${step}: ${cmd}" if eval "${cmd}"; then echo "[OK] ${step}" touch "/opt/software/.${step}.done" else echo "[FAIL] ${step},请查看日志 logs/install.log" exit 1 fi }

逻辑说明:check_done在每组件的安装脚本开头调用,如果发现/opt/software/下存在对应的.done文件,就直接跳过组件安装步骤,这样脚本无论执行多少次都是安全的。run_cmd则封装了每一步的执行业务:成功了留下标记,失败了立刻终止并提示错误位置。

参数说明:.done标记文件放在安装目录而不是/tmp下,避免系统重启后标记丢失;组件名就是标记文件名的一部分,例如install_jdk.sh完成后生成/opt/software/.install_jdk.done。这个设计虽然简单,但确实解决了一个核心痛点:重跑安全。

2.3 主控脚本:阶段化执行与整体进度控制

有了模块脚本和公共函数库,主控脚本反而很简单,它的职责是编排而不是实现:

#!/bin/bash # 主控脚本:deploy.sh set -euo pipefail source ./scripts/common.sh source ./control.env mkdir -p logs echo "================== 环境预检 ==================" ./scripts/precheck.sh echo "================== 安装 JDK ==================" ./scripts/install_jdk.sh echo "================== 安装 MySQL =================" ./scripts/install_mysql.sh echo "================== 安装 Redis ================" ./scripts/install_redis.sh echo "================== 安装 Nginx ================" ./scripts/install_nginx.sh echo "================== 安装后验证 =================" ./scripts/postcheck.sh echo "全部组件安装完成。日志路径: logs/install.log"

逻辑说明:主控脚本按固定顺序执行六个子脚本,每个子脚本运行前会先执行precheck.sh做环境检查——包括磁盘空间是否足够、端口是否被占用、是否有可用的包管理器。set -euo pipefail三个参数组合起来,保证脚本在任何一条命令失败时立即退出,不会带着错误继续执行。最后一个postcheck.sh会遍历检查四个组件的启动状态和版本信息,结果输出到终端。

这里有一个经验:主控脚本刻意不做任何安装细节判断,所有的业务逻辑都下沉到子脚本里。这么做的好处是,当你想单独重装 Redis 时,可以直接执行./scripts/install_redis.sh而不会影响其他组件。

3. 逐个拆解四个组件的安装逻辑:从解压到 systemd 托管

这一章是整套脚本的核心,四个组件的安装方式各有特点。JDK 是纯二进制解压,MySQL 涉及初始化和密码策略,Redis 需要编译但参数不多,Nginx 的 configure 参数直接影响后续扩展能力。我按照安装顺序逐个拆解。

3.1 JDK 安装脚本:软链接与 /etc/profile.d 的配合

JDK 的安装本质上就是解压加配环境变量,但环境变量这一步如果写在/etc/profile里,会污染全局配置且不易维护。我偏向使用/etc/profile.d/下的独立脚本:

#!/bin/bash # scripts/install_jdk.sh source "$(dirname "$0")/common.sh" source "$(dirname "$0")/../control.env" if check_done "install_jdk"; then exit 0 fi # 检查压缩包是否存在 if [ ! -f "packages/jdk-${JDK_VERSION}-linux-x64.tar.gz" ]; then echo "[ERROR] 未找到 packages/jdk-${JDK_VERSION}-linux-x64.tar.gz" exit 1 fi # 解压安装 mkdir -p "${INSTALL_BASE}" tar -xzf "packages/jdk-${JDK_VERSION}-linux-x64.tar.gz" -C "${INSTALL_BASE}" ln -sfn "${INSTALL_BASE}/jdk-${JDK_VERSION}" "${INSTALL_BASE}/jdk" # 写入环境变量 cat > /etc/profile.d/jdk.sh <<EOF export JAVA_HOME=${INSTALL_BASE}/jdk export PATH=\$JAVA_HOME/bin:\$PATH export CLASSPATH=.:\$JAVA_HOME/lib:\$JAVA_HOME/jre/lib EOF source /etc/profile.d/jdk.sh java -version touch "/opt/software/.install_jdk.done"

逻辑说明:先检查 packages 目录下是否有对应版本的 JDK 压缩包,不存在就直接退出;解压到/opt/software后,创建了一个jdk软链接指向具体的版本目录,这个软链接的作用是隔离版本号——升级 JDK 时只需要解压新版本然后改一下软链接指向,不需要改环境变量文件。环境变量写入/etc/profile.d/jdk.sh,每次登录 shell 都会自动加载。

参数说明:ln -sfn中-f表示强制覆盖旧软链接,-n表示把软链接当作普通文件处理而非进入其目录。CLASSPATH里的.:表示当前目录优先,这是很多 Java 程序运行时的潜在依赖。这个脚本执行完毕后,新开一个终端java -version就能看到版本信息;如果你在脚本所在终端直接用java命令,可能因为环境变量尚未刷新而失效,需要source /etc/profile.d/jdk.sh或者重新登录,这是新手最容易困惑的地方。

3.2 MySQL 安装脚本:初始化密码与安全配置的自动化

MySQL 的安装比 JDK 复杂得多,最让人头疼的是初始随机密码:8.0 版本在初始化时会生成临时密码,并写入错误日志文件。脚本要做的就是解析这个密码,然后通过 SQL 语句把它改成自定义密码:

#!/bin/bash # scripts/install_mysql.sh source "$(dirname "$0")/common.sh" source "$(dirname "$0")/../control.env" if check_done "install_mysql"; then exit 0 fi # 创建 mysql 用户和目录 id mysql &>/dev/null || useradd -r -s /sbin/nologin mysql mkdir -p ${MYSQL_HOME}/data ${MYSQL_HOME}/logs chown -R mysql:mysql ${MYSQL_HOME} # 解压二进制包 tar -xzf "packages/mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.gz" -C "${INSTALL_BASE}" ln -sfn ${INSTALL_BASE}/mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64 ${INSTALL_BASE}/mysql # 初始化数据目录 ${MYSQL_HOME}/bin/mysqld --initialize-insecure \ --basedir=${MYSQL_HOME} \ --datadir=${MYSQL_HOME}/data \ --user=mysql

初始化参数这里有两个选择:--initialize会生成随机密码,--initialize-insecure则会生成一个空密码的 root 账号。脚本里我选择后者,安装完成后再通过 SQL 立即设置密码。理由很简单:随机密码的解析依赖对错误日志文本的精确匹配,一旦 MySQL 版本更新日志格式变化,脚本就废了。空密码虽然不安全,但只在初始化后到设置密码之间短暂存在,风险可控。

接下来用 SQL 脚本完成安全设置和 systemd 托管:

# 安全设置:改密码、删除匿名用户、禁止 root 远程登录 ${MYSQL_HOME}/bin/mysql -uroot --skip-password <<SQL ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; DELETE FROM mysql.user WHERE User=''; DROP DATABASE IF EXISTS test; FLUSH PRIVILEGES; SQL # 创建 systemd 服务文件 cat > /etc/systemd/system/mysqld.service <<EOF [Unit] Description=MySQL Server After=network.target [Service] Type=forking User=mysql Group=mysql ExecStart=${MYSQL_HOME}/bin/mysqld --defaults-file=${MYSQL_HOME}/etc/my.cnf --daemonize --pid-file=${MYSQL_HOME}/data/mysqld.pid ExecStop=/bin/sh -c 'kill \\\$(cat ${MYSQL_HOME}/data/mysqld.pid)' Restart=on-failure [Install] WantedBy=multi-user.target EOF # 配置文件 cat > ${MYSQL_HOME}/etc/my.cnf <<EOF [mysqld] basedir=${MYSQL_HOME} datadir=${MYSQL_HOME}/data port=${MYSQL_PORT} socket=${MYSQL_HOME}/data/mysql.sock character-set-server=utf8mb4 collation-server=utf8mb4_general_ci innodb_buffer_pool_size=1G log-bin=${MYSQL_HOME}/logs/mysql-bin server-id=1 EOF systemctl daemon-reload systemctl enable mysqld systemctl start mysqld touch /opt/software/.install_mysql.done

逻辑说明:mysql客户端通过--skip-password方式免密登录,执行四条 SQL 完成基本安全设置。DELETE FROM mysql.user WHERE User=''删除匿名账号,DROP DATABASE IF EXISTS test移除默认测试库,最后刷新权限。systemd 服务使用Type=forking,因为 mysqld 在--daemonize模式下会先启动再后台化,父进程退出后 systemd 认为服务启动完成。

参数说明:character-set-server=utf8mb4是必须的,很多装在默认字符集上的库遇到中文写入乱码,事后改字符集非常麻烦;innodb_buffer_pool_size=1G是一个通用值,物理机内存小于 2G 时可以调小到 512M;log-bin开启后每写一条数据都会产生 binlog,对后续主从复制很重要,但如果只是单机测试,不想要额外的磁盘开销,可以注释掉这一行。

3.3 Redis 安装脚本:编译参数与 systemd 守护方式

Redis 的安装过程有两条路:一条是make && make install的快路径,另一条是 configure 式的慢路径。Redis 6.0 之后默认用make编译,我需要强调的是make install的PREFIX参数,它决定了二进制文件的最终安装位置:

#!/bin/bash # scripts/install_redis.sh source "$(dirname "$0")/common.sh" source "$(dirname "$0")/../control.env" if check_done "install_redis"; then exit 0 fi # 安装编译依赖 yum install -y gcc make tcl >/dev/null 2>&1 || apt-get install -y gcc make tcl >/dev/null 2>&1 # 解压编译 tar -xzf "packages/redis-${REDIS_VERSION}.tar.gz" -C /tmp cd /tmp/redis-${REDIS_VERSION} # 跳过测试可以避免 tcl 版本不一致导致的假失败 make -j$(nproc) >/dev/null make install PREFIX=${REDIS_HOME} # 配置文件模板 mkdir -p ${REDIS_HOME}/etc cp redis.conf ${REDIS_HOME}/etc/redis.conf

config 目录里准备一份修改过的 redis.conf 模板,脚本只需做变量替换:

# 把模板中的占位符替换为实际值 sed -i "s/@REDIS_PORT@/${REDIS_PORT}/g" ${REDIS_HOME}/etc/redis.conf sed -i "s/@REDIS_PASSWORD@/${REDIS_PASSWORD}/g" ${REDIS_HOME}/etc/redis.conf # 生成 systemd 服务文件 cat > /etc/systemd/system/redis.service <<EOF [Unit] Description=Redis Server After=network.target [Service] Type=simple ExecStart=${REDIS_HOME}/bin/redis-server ${REDIS_HOME}/etc/redis.conf ExecStop=${REDIS_HOME}/bin/redis-cli -p ${REDIS_PORT} -a ${REDIS_PASSWORD} shutdown Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable redis systemctl start redis touch /opt/software/.install_redis.done

逻辑说明:Redis 的服务类型是Type=simple,与 MySQL 不同,因为 redis-server 默认在前台运行直到收到关闭信号,systemd 只需直接跟踪这个前台进程。编译时加上-j$(nproc)可以让 make 并行编译,CPU 核数多的机器速度提升明显。make install PREFIX=${REDIS_HOME}会把redis-server、redis-cli等二进制文件安装到指定目录的bin/下,后续升级版本时不需要重装系统级路径。

参数说明:make test这一步我在脚本里注释掉了,原因是 Redis 的测试套件对 tcl 版本非常敏感,版本不匹配会产生大量假失败,与其在这里卡壳,不如直接跳过测试进入安装阶段。模板配置文件里需要重点修改的项是daemonize no——不要设置成 yes,否则 systemd 会认为主进程退出导致服务状态异常;protected-mode yes在设置了密码后可以保留开启,它的作用是禁止未授权的外部访问,这是遇到过血泪教训才加上的设置。

3.4 Nginx 安装脚本:configure 参数决定后续扩展空间

Nginx 的安装核心在于 configure 阶段的参数选择。很多人图省事编译时只加--prefix,等后面需要 HTTPS 或者监控模块时才发现--with-http_ssl_module没编译进去,重新编译又要折腾一遍。脚本里我会把常用模块一次全带上:

#!/bin/bash # scripts/install_nginx.sh source "$(dirname "$0")/common.sh" source "$(dirname "$0")/../control.env" if check_done "install_nginx"; then exit 0 fi # 安装依赖库 yum install -y pcre-devel zlib-devel openssl-devel >/dev/null 2>&1 \ || apt-get install -y libpcre3-dev zlib1g-dev libssl-dev >/dev/null 2>&1 # 解压 tar -xzf "packages/nginx-${NGINX_VERSION}.tar.gz" -C /tmp cd /tmp/nginx-${NGINX_VERSION} # configure 参数:模块按需选择 ./configure \ --prefix=${NGINX_HOME} \ --user=nginx \ --group=nginx \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-http_gzip_static_module \ --with-pcre \ --with-http_realip_module make -j$(nproc) >/dev/null make install >/dev/null # 创建 nginx 用户 id nginx &>/dev/null || useradd -r -s /sbin/nologin nginx chown -R nginx:nginx ${NGINX_HOME} # systemd 服务 cat > /etc/systemd/system/nginx.service <<EOF [Unit] Description=nginx - high performance web server After=network.target [Service] Type=forking ExecStart=${NGINX_HOME}/sbin/nginx -c ${NGINX_HOME}/conf/nginx.conf ExecReload=${NGINX_HOME}/sbin/nginx -s reload ExecStop=${NGINX_HOME}/sbin/nginx -s quit PIDFile=${NGINX_HOME}/logs/nginx.pid Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable nginx systemctl start nginx touch /opt/software/.install_nginx.done

逻辑说明:configure 生成的 Makefile 决定了哪些模块会被编译进 nginx 二进制文件。--with-http_stub_status_module提供了/nginx_status端点,可以输出连接数、请求数等关键指标,这是运维监控 Nginx 的必备模块。--with-http_realip_module的作用是在 Nginx 位于反向代理之后时,从请求头中还原客户端真实 IP,没有这个模块,后面做日志分析时看到的全是代理地址。

参数说明:--prefix指定安装根目录,后续所有路径都基于这个目录;--user=nginx和--group=nginx让 worker 进程以低权限用户运行,避免安全风险。这里有个细节我补充一下:make install完成后,Nginx 的 pid 文件路径默认在--prefix下的logs/目录,systemd 里写的PIDFile必须与 nginx.conf 中的pid指令路径一致,否则systemctl reload时会报错找不到主进程号。

4. 避坑手册:五个高频失败场景与修复方案

再好的脚本也会遇到环境差异导致的失败。这一章记录我在实际部署中遇到的五个最常见问题,按「现象 → 原因 → 解决」的格式整理,其中前三条几乎每个新环境都会遇到至少一个。

4.1 MySQL 初始化失败:glibc 版本与二进制包不匹配

现象:执行mysqld --initialize-insecure时报错,日志里提示./bin/mysqld: /lib64/libc.so.6: version GLIBC_2.18 not found或者类似的动态链接错误。

原因:MySQL 官方提供的预编译二进制包是按特定 glibc 版本编译的,例如mysql-8.0.34-linux-glibc2.17-x86_64.tar.gz需要系统的 glibc 版本不低于 2.17。CentOS 7 默认是 2.17 没问题,但 CentOS 6 和一些精简版系统只到 2.12。

解决:先执行ldd --version查看当前系统 glibc 版本,如果低于压缩包文件名里的最低版本要求,有两种做法:一是找对应低版本的 MySQL 二进制包(官网提供了 glibc2.12 版本),二是改用 yum/apt 的官方仓库源安装。脚本无法替你做这个决定,但precheck.sh里可以加入 glibc 版本检查,不满足条件时直接报错提示换包。

4.2 Redis 编译通过却启动失败:daemonize 与 systemd 的冲突

现象:systemctl start redis执行后,systemctl status redis显示 active (running),但过了几秒变成 failed,日志里没有任何明显的错误信息。

原因:配置文件里如果设置了daemonize yes,redis-server 启动后会 fork 出一个后台进程然后父进程退出,systemd 的Type=simple认为主进程已经结束,判定服务失败。我在前面章节的配置里强调过daemonize no,但很多人从网上下载的 redis.conf 模板里默认就是 yes,装上就直接翻车。

解决:把daemonize yes改为daemonize no,然后systemctl daemon-reload && systemctl restart redis。这也是为什么脚本里我坚持用 sed 覆盖占位符而不是直接使用下载的原版配置文件的原因——原版配置是为手工启动设计的,不是为 systemd 设计的。

4.3 Nginx 启动时报错:地址已被占用或 pid 文件路径不一致

现象:systemctl start nginx报错nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)。

原因:两种情况,最常见的是服务器上已经跑着 Apache 或其他 Web 服务占用了 80 端口;另一种是旧版本 Nginx 进程还残留着,nginx -s quit没有正常退出,新进程 bind 不上端口。

解决:先执行ss -tlnp | grep :80查出占用端口的进程,如果确认是残留 Nginx,执行kill $(cat ${NGINX_HOME}/logs/nginx.pid)结束旧进程再启动。如果 80 端口被其他业务占用,一个省事的方案是改用 8080 端口,修改nginx.conf里的listen 8080;即可。这里的教训是:precheck.sh里检查端口占用是必须的,不要心存侥幸。

4.4 JDK 环境变量失效:shell 缓存导致 java -version 显示旧版本

现象:脚本安装完 JDK 后,当前终端执行java -version显示的还是老的版本,或者提示command not found,但重新开一个终端就正常了。

原因:当前 shell 的 PATH 变量是在登录时从/etc/profile读取并缓存到内存里的,source /etc/profile.d/jdk.sh只会更新当前 shell 的环境变量,其他已经打开的终端不受影响。还有一个隐藏问题——系统里原本存在/usr/bin/java的符号链接,它指向旧的 JRE 路径,而环境变量里 PATH 的顺序是/usr/bin在前,覆盖了你新加的$JAVA_HOME/bin。

解决:执行source /etc/profile.d/jdk.sh刷新当前终端,然后which java确认路径是否为$JAVA_HOME/bin/java。如果系统自带旧的 java,脚本里还应该加一个步骤:rm -f /usr/bin/java /usr/bin/javac删除系统级链接,确保新版本java命令生成唯一来源。

4.5 脚本中断后重跑:端口残留与脏数据让安装失败

现象:安装 MySQL 到一半脚本报错退出,修复问题后重新执行deploy.sh,结果在初始化数据目录时报错[ERROR] --initialize specified but data directory exists。

原因:第一次执行时mkdir -p已经创建了数据目录,虽然初始化没完成,但 mysqld 检测到目录非空就拒绝执行。这就是没有完全幂等导致的坑——只检查了.done标记文件,没有检查中间状态。

解决:在初始化数据目录之前,脚本必须判断data目录是否为空,不空则先清空再初始化:

if [ -d "${MYSQL_HOME}/data" ] && [ "$(ls -A ${MYSQL_HOME}/data)" ]; then rm -rf ${MYSQL_HOME}/data/* fi

逻辑说明:ls -A会列出目录下所有内容但不包括.和..,返回空就说明目录是干净的。这条检查应该加在check_done之后、任何写操作之前。从那以后,我写安装脚本时每加一个步骤都会问自己:如果这一步执行了一半失败,重跑时会不会出问题?这个检查习惯帮我规避了至少五次生产级别的翻车现场。

5. 装完不算结束:一键验证脚本与离线部署改造

脚本跑完只是前半场,真正判断安装是否成功,需要一套快速的验证方案。我习惯在deploy/scripts/里放一个postcheck.sh,它的作用是遍历四个组件的端口、进程和版本信息,把结果用表格样式输出到终端。验证目标很简单:端口在监听、进程活着、命令能返回版本号。

验证脚本的粗框架长这样:

#!/bin/bash # scripts/postcheck.sh source "$(dirname "$0")/common.sh" source "$(dirname "$0")/../control.env" declare -A SERVICES=( [jdk]="java -version" [mysql]="${MYSQL_HOME}/bin/mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e 'SELECT VERSION();'" [redis]="${REDIS_HOME}/bin/redis-cli -p ${REDIS_PORT} -a ${REDIS_PASSWORD} ping" [nginx]="${NGINX_HOME}/sbin/nginx -v" ) for service in jdk mysql redis nginx; do echo "===== ${service} 状态 =====" eval "${SERVICES[$service]}" || echo "[FAIL] ${service} 验证未通过" done echo "===== 端口监听状态 =====" ss -tlnp | grep -E ":(${MYSQL_PORT}|${REDIS_PORT}|${NGINX_PORT})\b"

逻辑说明:declare -A定义了一个关联数组,key 是组件名,value 是验证命令。Redis 的验证用redis-cli ping,正常返回PONG;MySQL 验证直接用 SQL 查询版本号,这一步也同时验证了账号密码的正确性;Nginx 用-v输出版本。ss -tlnp的\b是正则中的单词边界,防止3306被33060匹配到。

这套验证流程的价值在改完配置后尤其明显:我每次改完nginx.conf或者redis.conf,都会在 reload 之后强制跑一遍postcheck.sh,确认服务和端口都健康再离开终端。这个习惯帮我挡掉了不少低级错误。

最后说一个离线部署的细节——很多服务器环境不允许直接访问外网下载软件包,脚本运行时如果去联网拉取依赖,就会卡在网络超时上。我在脚本里加入了本地包优先的逻辑:

# 包检查:本地 packages/ 目录优先,缺失时才尝试下载 if [ ! -f "packages/nginx-${NGINX_VERSION}.tar.gz" ]; then wget -P packages/ "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" fi

也就是说,只要把四个 tar 包提前放到packages/目录里,整套脚本完全可以脱离外网运行。这在实际交付中很常用:我一般会在有网的机器上把包全部下好,打成一个压缩包传到目标服务器,解压后直接跑deploy.sh,十分钟内完成全部环境交付。从那以后,我每次接手新环境的第一件事就是看一眼 packages 目录是否齐全、control.env 里的版本是否匹配——这两处是整套脚本最核心的人肉检查点,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询