简介:面向Linux运维初学者、后端开发者与高校学生的环境部署脚本合集,聚焦JDK、MySQL、Redis、Nginx等Web服务基础组件的一键安装,旨在减少多组件手工部署时的依赖缺失、配置分散与启动顺序混乱等问题。压缩包共27个文件,约930.56MB,以11个sh脚本负责安装编排与环境变量配置,9个gz压缩包提供各组件安装介质,另含2个cnf、2个conf配置模板、2个rpm系统依赖包和1个tar归档,类型搭配覆盖了从依赖补齐到服务配置的主要环节。已有299人学习,说明其对快速搭建实验环境具有一定参考价值。目录按组件分文件夹组织,脚本把安装、配置与启动任务拆分,并结合密码重置、服务注册等辅助脚本,读者可据此理解组件间的依赖关系、配置文件覆盖方式与服务管理思路,也便于按需取用和二次修改,适合用作学习验证与日常运维练习的模板。
1. 一键脚本不是万能钥匙:四个组件同装时最先翻车的是版本顺序
Linux 一键式安装 JDK、MySQL、Redis、Nginx 听起来像把四条命令塞进一个脚本,真到机器上跑,最先翻车的往往不是脚本语法,而是版本顺序和系统仓库优先级。我见过一台测试机,JDK 用系统包、MySQL 走额外仓库、Redis 又编译安装,脚本前半段一路绿灯,到 Nginx 启动时 80 端口被旧服务占着,四个组件只起来两个。
这套环境常见于新购云主机、内网测试机、临时演示环境,适合想用一套入口脚本把依赖检查、配置落点、服务注册和开机自启串起来的开发者。装不好不会立刻报大错,常见结果是 MySQL 目录权限不对、Redis 只监听本机、Nginx 配置被覆盖,后面排查时间比安装还长。
2. 装前把环境钉死:JDK、MySQL、Redis、Nginx 的版本与依赖清单
一键脚本最容易给人一种错觉:只要以 root 执行,四个组件就会按顺序装好。实际落地时,脚本第一段就应该做基线检查,而不是直接install。JDK 的包名、MySQL 的数据目录、Redis 的编译依赖、Nginx 的监听端口,这四件事只要有一件和机器现状冲突,后面就会以“服务起不来”的形式集中爆发。
我一般会把这套安装拆成“基线检查、版本取舍、目录约定”三步,先让脚本知道机器上有什么、缺什么、哪些端口不能用,再决定每个组件走系统仓库、官方二进制还是源码编译。
2.1 系统基线检查:内存、磁盘、端口、SELinux 与防火墙
先跑一个只读检查脚本,不改任何配置。重点看内存是否够 MySQL 初始化、/var和/usr/local是否有空间、3306/6379/80 是否已经被旧实例或容器占住。SELinux 和 firewalld 的状态也要记录,后面服务起不来时能少走一圈弯路。
#!/usr/bin/env bash set -euo pipefail # 1. 资源基线:内存、根分区、数据目录所在分区 free -h df -h / /var /usr/local # 2. 端口占用:3306/6379/80 任一被占就先处理 ss -lntp | grep -E ':(80|3306|6379)\b' || true # 3. 安全模块与防火墙状态,只记录不修改 getenforce 2>/dev/null || true systemctl is-active firewalld 2>/dev/null || trueset -euo pipefail让脚本在命令失败、未定义变量、管道中任一环节失败时退出,避免“前面报错、后面继续装”的半成品状态。ss -lntp用来列出监听端口和对应进程,|| true是为了让 grep 没匹配到时不要中断检查。getenforce和systemctl is-active只做状态记录,不在这里直接关防火墙或关 SELinux,因为很多测试机的安全策略不是安装脚本该擅自改的。
| 检查项 | 关注点 | 处理建议 |
|---|---|---|
| 内存 | MySQL 初始化、Redis 编译 | 测试机至少留出 2GB 可用内存 |
| 磁盘 | /var/lib/mysql、/var/lib/redis、/usr/local | 数据目录和安装目录分开看 |
| 端口 | 80、3306、6379 | 被占先停旧服务或改端口 |
| SELinux | 数据目录上下文 | 启用时准备restorecon |
| firewalld | 本机访问与内网访问 | 只放行必要端口 |
提示:先跑基线检查再执行安装脚本,端口被占时不要硬改脚本里的端口号,否则 Nginx 配置、MySQL 客户端、Redis 连接串都要跟着改。
2.2 四个组件的版本取舍:系统仓库版、官方二进制版、编译版怎么选
版本选择不要只看“新不新”,而要看三件事:机器上已有客户端能不能连、你需要哪些模块、后续谁来维护。JDK 走系统仓库最省事,适合快速交付;需要多版本并存时,用官方压缩包解压到/opt/java更清楚。MySQL 走发行版仓库还是额外仓库,要看仓库里给的包名和认证插件,不要脚本写死一个包名就在所有机器上跑。Redis 编译安装适合需要指定编译参数或模块的场景,系统包适合只做缓存验证。Nginx 系统包适合常规入口转发,源码编译适合需要额外模块的场景。
| 组件 | 可选安装形态 | 适用场景 | 需要提前确认 |
|---|---|---|---|
| JDK | 系统仓库包 / 官方压缩包 | 快速交付 / 多版本并存 | JAVA_HOME、alternatives |
| MySQL | 系统仓库包 / 额外仓库包 | 测试机 / 需要特定大版本 | 数据目录、认证插件、字符集 |
| Redis | 源码编译 / 系统仓库包 | 需要指定模块 / 快速验证 | 编译依赖、持久化目录、保护模式 |
| Nginx | 系统仓库包 / 源码编译 | 常规入口 / 需要额外模块 | 编译参数、运行用户、日志目录 |
我的习惯是:一键脚本里不写死具体版本号,只写“包名变量”和“安装形态”。JDK 用JDK_PKG,MySQL 用MYSQL_PKG,Redis 用源码目录变量,Nginx 用NGINX_PREFIX。这样换机器时只改config.env,不改主脚本。版本号从机器仓库或你手里的安装包来,脚本负责检查、安装、注册服务,不负责猜版本。
2.3 一键脚本的目录约定与配置文件落点
目录不统一,后面回滚和排查都会难受。我一般把入口脚本、日志、临时包、模板分开放:/opt/oneclick放脚本,/opt/oneclick/logs放安装日志,/opt/oneclick/tmp放下载或上传的包,/opt/oneclick/templates放配置模板。数据目录不放在/opt/oneclick下面,避免回滚时误删。
# /opt/oneclick/config.env JDK_PKG="" MYSQL_PKG="mysql-server" MYSQL_DATA_DIR="/var/lib/mysql" REDIS_PREFIX="/usr/local/redis" REDIS_PASSWORD="" NGINX_PREFIX="/usr/local/nginx"JDK_PKG留空时,脚本先执行yum search openjdk或apt-cache search openjdk让人确认包名,不自动猜。MYSQL_PKG按发行版仓库实际包名改,有些仓库给的是mysql-server,有些需要额外仓库。REDIS_PASSWORD留空时由脚本生成一次并写回配置文件,避免每次source都重新生成。MYSQL_DATA_DIR、REDIS_PREFIX、NGINX_PREFIX是后续服务注册和排查时要反复用到的路径,提前固定下来。
| 配置文件 | 常见落点 | 脚本里要做什么 |
|---|---|---|
| MySQL 主配置 | /etc/my.cnf或/etc/mysql/my.cnf | 写字符集、数据目录、socket |
| Redis 主配置 | /etc/redis/redis.conf | 写监听地址、密码、持久化目录 |
| Nginx 主配置 | /etc/nginx/nginx.conf或$NGINX_PREFIX/conf/nginx.conf | 写运行用户、入口 server |
| systemd 单元 | /etc/systemd/system/*.service | 注册 Redis、Nginx 等源码安装服务 |
3. 先装 JDK 与 MySQL:脚本里的依赖顺序和初始化参数
JDK 和 MySQL 放在同一段,不是因为它们互相依赖,而是因为它们最容易被“装上了”骗过去。java -version有输出不代表JAVA_HOME写对,MySQL 服务 active 也不代表 root 能登录。一键脚本要把“安装、配置、初始化、设密码”拆成可检查的步骤,任何一步失败都能定位到具体动作。
3.1 JDK 安装:包管理器与手动解压两条路
系统仓库安装适合快速把 JDK 装到机器默认路径,手动解压适合需要固定JAVA_HOME或多版本并存的场景。脚本里不要同时跑两条路,用JDK_PKG是否为空来决定。
# 路径 A:发行版仓库安装,包名先搜索确认 if command -v yum >/dev/null 2>&1; then yum search openjdk | head -n 20 yum install -y "$JDK_PKG" elif command -v apt-get >/dev/null 2>&1; then apt-cache search openjdk | head -n 20 apt-get update && apt-get install -y "$JDK_PKG" fi # 路径 B:官方压缩包解压到统一目录 mkdir -p /opt/java tar -xzf /opt/oneclick/tmp/jdk.tar.gz -C /opt/java # 找到解压后的目录名,写入 profile echo 'export JAVA_HOME=/opt/java/REPLACE_WITH_JDK_DIR' | tee /etc/profile.d/jdk.sh echo 'export PATH=$JAVA_HOME/bin:$PATH' >> /etc/profile.d/jdk.sh路径 A 里先search再install,是为了把“本机仓库到底提供哪个包”暴露出来,避免JDK_PKG写错后脚本只报一句“没有可用软件包”。路径 B 的tar -xzf解压到/opt/java,JAVA_HOME指向实际解压目录,/etc/profile.d/jdk.sh让新登录的 shell 自动加载。REPLACE_WITH_JDK_DIR要替换成解压后的真实目录名,脚本里可以用find /opt/java -maxdepth 1 -mindepth 1 -type d取到。
装完不要只看java -version,还要看echo $JAVA_HOME和which java。有些机器上alternatives会把java链到另一个版本,JAVA_HOME和PATH不一致时,应用启动脚本会拿到错误的 JDK。
3.2 MySQL 安装:仓库初始化、数据目录与 root 认证方式
MySQL 的坑大多不在安装包,而在初始化。数据目录属主不对、socket 路径不一致、root 认证插件和旧客户端不匹配,都会让服务显示 active 但客户端连不上。
# 安装 MySQL 服务端;包名按发行版仓库实际名称替换 yum install -y "$MYSQL_PKG" || apt-get install -y "$MYSQL_PKG" # 初始化前确认数据目录属主 install -d -o mysql -g mysql -m 750 "$MYSQL_DATA_DIR" # 首次初始化,测试机可用 --initialize-insecure,初始化后立刻设密码 mysqld --initialize-insecure --user=mysql --datadir="$MYSQL_DATA_DIR" systemctl enable --now mysqldinstall -d同时建目录、设属主、设权限,比mkdir后补chown更稳。--initialize-insecure会生成空密码 root,只适合测试机;初始化完马上用ALTER USER设密码。--initialize会生成临时密码,通常写在错误日志里,脚本要能定位日志路径。systemctl enable --now把开机自启和立即启动一起做,失败时直接看systemctl status mysqld和journalctl -u mysqld。
MySQL 主配置里先把字符集和数据目录写清楚,认证插件不要一上来就改。旧客户端连不上时,先确认客户端版本,再评估是否在测试环境调整认证插件。
[mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci # 旧客户端连接多时再评估认证插件,不要一上来就改 # default_authentication_plugin=mysql_native_password [client] default-character-set=utf8mb4character-set-server和collation-server关系到建库建表默认字符集,socket要和客户端配置一致,否则本机mysql命令可能走另一个 socket。认证插件那一行默认注释掉,是为了避免“为了兼容旧客户端把新装环境改回旧插件”这种后悔药吃得太早。
3.3 把 MySQL 初始化参数写进一键脚本
一键脚本必须能重复执行。数据目录非空时跳过初始化,避免第二次跑脚本把已有数据覆盖。root 密码从环境变量读取,不要在脚本里写明文。
# 只在数据目录为空时初始化,避免重跑把旧数据覆盖 if [ -z "$(ls -A "$MYSQL_DATA_DIR" 2>/dev/null)" ]; then mysqld --initialize-insecure --user=mysql --datadir="$MYSQL_DATA_DIR" else echo "数据目录非空,跳过初始化" fi # 启动后设置 root 密码,密码从脚本环境变量读取 mysql -uroot <<SQL ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; FLUSH PRIVILEGES; SQLls -A会列出隐藏文件,数据目录里只要有ibdata1、mysql目录等内容就不会为空。<<SQL是 here-doc,${MYSQL_ROOT_PASSWORD}在 shell 层替换;密码里如果带单引号或特殊字符,要先转义或改用客户端参数传入。FLUSH PRIVILEGES在ALTER USER后不是必须,但很多运维脚本会保留,便于和旧流程兼容。
注意:
--initialize-insecure只适合测试机,初始化完立刻设密码;正式环境用--initialize并从错误日志取临时密码。
4. 再装 Redis 与 Nginx:编译参数、服务注册与反向入口配置
Redis 和 Nginx 放在后面,是因为它们更容易被“源码编译”拖时间。Redis 编译依赖不全时,make会在 jemalloc 或 TLS 相关步骤失败;Nginx 编译参数少写一个模块,后面配 HTTPS 或状态页时又得重编。一键脚本要先把依赖装齐,再把服务注册好,最后才做入口验证。
4.1 Redis 安装:编译依赖、守护进程配置与持久化取舍
Redis 源码编译前先装开发工具和常用依赖。编译参数不需要花哨,PREFIX固定到/usr/local/redis,后面 systemd 单元和配置文件都按这个路径写。
# 安装编译依赖,包名按发行版实际仓库调整 yum groupinstall -y "Development Tools" yum install -y tcl jemalloc-devel openssl-devel # 进入源码目录后编译安装 make -j"$(nproc)" make PREFIX=/usr/local/redis installmake -j"$(nproc)"用 CPU 核数并行编译,PREFIX决定redis-server、redis-cli的安装位置。tcl用于跑测试,jemalloc-devel和openssl-devel按你启用的功能取舍。编译失败时先看最后 30 行输出,通常是缺头文件或编译器版本不匹配。
Redis 配置里要明确监听地址、保护模式、密码和持久化目录。单机应用同机访问时绑定127.0.0.1最省心;需要内网访问时再改绑定地址,并同步防火墙规则。
daemonize no bind 127.0.0.1 protected-mode yes port 6379 dir /var/lib/redis appendonly yes requirepass REPLACE_WITH_SCRIPT_GENERATED_PASSWORDdaemonize no配合 systemd 使用,让 systemd 管进程。protected-mode yes在没密码、没绑定地址时会拒绝外部连接,这是保护不是障碍。appendonly yes开启 AOF,写盘更频繁;纯缓存测试机可以关掉,只留 RDB 或完全不持久化。requirepass的占位符由安装脚本替换成config.env里的密码,不要把明文密码提交到模板里。
4.2 Nginx 安装:编译模块与系统包的选择
Nginx 如果只是做入口转发和静态文件,系统包足够;需要额外模块时再源码编译。编译参数里常用的有 SSL、状态页和真实 IP 模块,按实际需要加,不要为了“以后可能用”把所有模块都开上。
./configure --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-http_realip_module make -j"$(nproc)" make install--prefix决定 Nginx 安装目录,--with-http_ssl_module用于 HTTPS,--with-http_stub_status_module用于状态页,--with-http_realip_module用于取真实客户端地址。make install之后要先检查$NGINX_PREFIX/conf/nginx.conf里的user、pid、error_log路径是否和 systemd 单元一致。
user nginx; worker_processes auto; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; server { listen 80; server_name _; location / { return 200 "nginx ok\n"; } } }worker_processes auto按 CPU 核数起工作进程,worker_connections按机器承载调整。server_name _是兜底入口,先用一个返回固定文本的 location 验证 Nginx 本身能启动,再替换成你的静态目录或后端转发规则。include mime.types的路径要跟--prefix对齐,源码安装时常见错误是配置里还写着系统包的/etc/nginx/mime.types。
4.3 服务注册与开机自启:systemd 单元文件怎么写
源码安装的 Redis 和 Nginx 不会自动注册服务。systemd 单元里要写清User、ExecStart、Restart和文件描述符限制。Redis 的LimitNOFILE建议显式写,避免高并发连接时被默认值卡住。
[Unit] Description=Redis Server After=network.target [Service] Type=simple User=redis Group=redis ExecStart=/usr/local/redis/bin/redis-server /etc/redis/redis.conf ExecStop=/bin/kill -s TERM $MAINPID Restart=on-failure LimitNOFILE=65535 [Install] WantedBy=multi-user.targetType=simple表示ExecStart启动的就是主进程,User和Group要求数据目录属主一致。ExecStop用TERM信号让 Redis 自己收尾,比直接kill -9安全。Restart=on-failure在异常退出时拉起,但配置错误导致的反复重启会刷日志,排查时先systemctl stop。写完单元文件后执行systemctl daemon-reload,再enable --now。
4.4 一键脚本串联:四个组件的执行顺序与失败回滚
入口脚本只做三件事:加载配置、按顺序调用子脚本、失败时触发回滚。顺序上 JDK 先装,MySQL 次之,Redis 和 Nginx 放后面;每装完一个组件就做一次轻量检查,不要等四个全装完再统一验证。
#!/usr/bin/env bash set -euo pipefail source /opt/oneclick/config.env trap 'echo "安装失败,执行回滚"; /opt/oneclick/rollback.sh' ERR /opt/oneclick/install_jdk.sh /opt/oneclick/install_mysql.sh /opt/oneclick/install_redis.sh /opt/oneclick/install_nginx.sh /opt/oneclick/verify.shsource把config.env里的包名、目录、密码加载进来。trap ... ERR在任一子脚本返回非零时执行回滚。回滚脚本不要删数据目录,只停服务、删临时文件、删本次新建的 systemd 单元,把“是否保留数据”交给执行人决定。verify.sh放在最后,但每个子脚本内部也应该有systemctl is-active或端口检查,避免错误累积到最后才暴露。
5. 避坑与排查:一键脚本跑完服务没起来时先看这五处
一键脚本最怕“最后一行打印 done,但服务没起来”。排查顺序我一般按端口、权限、认证、配置覆盖、回滚残留走,这五处能覆盖大部分翻车现场。每一条都先看现象,再定位原因,最后给可执行的解决命令。
5.1 端口冲突:3306、6379、80 被旧服务或容器占住
现象:systemctl status mysqld显示 failed,日志里有bind: Address already in use;Nginx 启动时报bind() to 0.0.0.0:80 failed。
原因:机器上已有旧 MySQL、Redis 或 Nginx 实例,或者容器做了端口映射,脚本又装了一遍。也可能是上一次安装失败后服务没停干净。
解决:先用ss找进程,再看 systemd 和容器。确认是旧服务就停掉并禁用,确认要保留就改新实例端口,同时同步改客户端连接串和脚本配置。
ss -lntp | grep -E ':(80|3306|6379)\b' systemctl status mysqld --no-pager journalctl -u mysqld -n 50 --no-pagerss -lntp的-p能显示进程名和 PID,journalctl -u mysqld -n 50只看最近 50 行,避免日志太长。改端口不是只改一个文件:MySQL 要改my.cnf和客户端配置,Redis 要改redis.conf和连接串,Nginx 要改listen和入口检查命令。
5.2 权限与目录:数据目录属主、SELinux 上下文、日志权限
现象:MySQL 启动失败,错误日志里出现Can't create/write to file;Redis 报Permission denied,但配置文件路径明明存在。
原因:脚本用 root 建了目录,服务却用mysql或redis用户跑;或者目录属主对了,SELinux 上下文还是旧的。日志目录权限不对也会让服务启动到一半退出。
解决:数据目录属主和权限按服务用户改,SELinux 启用时补上下文。不要直接chmod 777,那只是把问题藏起来。
chown -R mysql:mysql /var/lib/mysql chown -R redis:redis /var/lib/redis restorecon -Rv /var/lib/mysql /var/lib/redis 2>/dev/null || truechown -R递归改属主,restorecon在 SELinux 启用时恢复默认上下文。日志目录也要检查,比如/var/log/nginx、/var/log/redis,确保运行用户可写。源码安装时$NGINX_PREFIX/logs的属主也要跟user指令一致。
5.3 认证与连接:MySQL root 插件、Redis 保护模式、Nginx 用户
现象:MySQL 客户端报Authentication plugin 'caching_sha2_password' cannot be loaded;Redis 客户端报DENIED Redis is running in protected mode;Nginx 启动报getpwnam("nginx") failed。
原因:MySQL 新默认认证插件和旧客户端不匹配;Redis 没设密码或绑定地址不对,保护模式拒绝连接;Nginx 配置里的user在系统里不存在。
解决:MySQL 先查mysql.user里的插件,能升级客户端就升级,测试环境再评估改插件;Redis 设密码、绑定本机或内网地址,并确认protected-mode的取舍;Nginx 的user改成系统里真实存在的用户,或先创建对应用户。
mysql -uroot -p -e "SELECT user,host,plugin FROM mysql.user WHERE user='root';" redis-cli -a "$REDIS_PASSWORD" ping id nginx 2>/dev/null || trueSELECT user,host,plugin直接看 root 的认证方式,redis-cli -a验证密码是否生效。id nginx确认 Nginx 运行用户是否存在。认证问题不要靠反复重装解决,先看客户端和服务端各自用什么插件、什么密码。
5.4 配置覆盖:重跑脚本把 nginx.conf 和 redis.conf 改回默认
现象:第一次配好的入口规则、Redis 密码,第二次跑脚本后失效;nginx -t能过,但访问入口返回默认页。
原因:安装脚本直接cp模板覆盖目标配置,没有判断文件是否已存在,也没有备份。重跑一次就把手工改的内容冲掉。
解决:模板只在目标文件不存在时渲染,已存在时先备份再提示人工合并。关键配置项用脚本变量替换,不要把整份配置硬覆盖。
if [ ! -f /etc/nginx/nginx.conf ]; then cp /opt/oneclick/templates/nginx.conf /etc/nginx/nginx.conf else cp /etc/nginx/nginx.conf "/etc/nginx/nginx.conf.bak.$(date +%s)" fi[ ! -f ... ]判断目标配置是否存在,date +%s生成时间戳备份名。Redis 配置同理,密码和持久化目录用脚本变量替换,其他手工项保留。重跑脚本前先看备份目录,确认没有把上一次的入口规则覆盖掉。
5.5 回滚残留:装到一半失败,重跑提示用户已存在、端口占用
现象:第二次执行脚本报useradd: user 'mysql' already exists,或者端口仍被上次半启动的服务占着,systemctl里还有残留单元。
原因:第一次失败后trap只打印了错误,没有停服务、没有删临时单元,也没有清理本次创建的临时文件。重跑时脚本以为自己是第一次装。
解决:回滚脚本先停服务、禁用开机自启、删本次新建的 systemd 单元,再daemon-reload。数据目录保留,删不删由执行人确认。
systemctl stop mysqld redis nginx 2>/dev/null || true systemctl disable mysqld redis nginx 2>/dev/null || true rm -f /etc/systemd/system/redis.service /etc/systemd/system/nginx.service <p> <a href="https://download.csdn.net/download/FanZaiYo/89036168" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>