目录
[2.1 它们做了什么?](#2.1 它们做了什么?)
[2.2 成熟的实践可以借鉴](#2.2 成熟的实践可以借鉴)
[3.1 脚本头部:安全与可维护性](#3.1 脚本头部:安全与可维护性)
[3.2 通用工具函数](#3.2 通用工具函数)
[3.3 安装Nginx](#3.3 安装Nginx)
[3.4 安装MySQL](#3.4 安装MySQL)
[3.5 安装PHP及扩展](#3.5 安装PHP及扩展)
[3.6 验证部署](#3.6 验证部署)
[3.7 主流程](#3.7 主流程)
[5.1 这个脚本串联了哪些知识?](#5.1 这个脚本串联了哪些知识?)
[5.2 第四阶段总结](#5.2 第四阶段总结)
一、引言:为什么自己写部署脚本?
在服务器上搭建LNMP环境,标准流程是这样的:
bash
sudo apt update
sudo apt install nginx -y
sudo apt install mysql-server -y
sudo mysql_secure_installation
sudo apt install php-fpm php-mysql -y
sudo vim /etc/nginx/sites-available/default
sudo nginx -t
sudo systemctl reload nginx
这套流程手动操作一遍,大约需要15到20分钟。如果你管理着多台服务器,或者在测试环境中需要反复重建,这无疑是一种折磨。
网上有不少成熟的"一键安装包"(如军哥LNMP、OneinStack),它们确实省时省力,但有几个问题让你不敢在生产环境放心使用:
-
黑盒操作:你不知道它具体装了什么,配置是怎么改的,出问题时排查如同瞎子摸象
-
版本锁定:它决定装哪个版本就装哪个版本,你想换版本?自己改脚本,而那脚本往往上千行,改一处牵动全身
-
不可预测的副作用 :有些脚本会修改系统级的
/etc/profile、替换仓库源、添加自己定制的systemd服务
自己写部署脚本的核心价值,是让每一行配置你都清楚其来龙去脉。这份脚本不需要炫技,不需要"一行搞定所有",而是用清晰的结构、充分的分步日志和可验证的中间步骤,让你在凌晨三点被报警叫醒时,能冷静地定位问题。
二、主流一键包的内部分析
2.1 它们做了什么?
市面上的主流一键包(如军哥LNMP、OneinStack),虽然各自实现不同,但核心逻辑大同小异:
第一步:环境检测
脚本启动后先判断操作系统类型和版本:
bash
# 典型的检测逻辑(伪代码)
if [ -f /etc/debian_version ]; then
OS="debian"
elif [ -f /etc/redhat-release ]; then
OS="rhel"
fi
然后检查内存大小(MySQL的内存参数依赖此值)、磁盘剩余空间(编译需要大量临时空间),以及检测必要的命令是否可用(wget、tar、make等)。
第二步:参数解析
支持命令行参数定制安装选项:
bash
# ./install.sh --nginx-version=1.24.0 --php-version=8.1 --mysql-password=MyPass123
如果用户没有提供参数,脚本使用默认值(通常是较保守的稳定版本)。
第三步:依赖安装
自动安装编译工具链(gcc、make等)和开发库(libpcre3-dev、libssl-dev等)。这一步最耗时,通常占总运行时间的50%以上。
第四步:源码编译安装
按顺序执行每个软件的编译三部曲(./configure && make && make install),这和我们第10篇讲的完全一致。
第五步:配置与整合
安装完成后,自动生成基础配置文件,确保Nginx能找到PHP-FPM、PHP能连接MySQL。这一步是一键包差异最大的地方------不同脚本的默认配置风格差异明显。
2.2 成熟的实践可以借鉴
-
日志保留每一个步骤 :
echo和tee双输出,让用户能看到进度,同时所有操作写入日志文件 -
失败不继续 :用
set -e或手动检查返回值,确保编译失败时不会强行安装下一个软件,能立即定位问题 -
备份不覆盖 :修改系统原有配置文件前自动备份(加
.bak后缀),出问题时直接回滚
三、编写自己的LNMP部署脚本
我们不编译源码------直接使用系统包管理器安装。编译虽然可定制版本,但耗时长、容易出错,而包管理器稳定、快速、自动处理依赖。对于90%的生产环境,用包管理器安装的版本完全够用。如果需要特定版本(如Nginx的新特性),在第10篇的源码编译基础上自行替换即可。
3.1 脚本头部:安全与可维护性
借鉴一键包的成熟实践,部署脚本从规范的开头开始:
bash
#!/bin/bash
# LNMP 自动部署脚本
# 支持: Ubuntu 20.04+ / Debian 11+
# 用法: bash install_lnmp.sh
set -euo pipefail
# 颜色输出
readonly RED='\033[31m'
readonly GREEN='\033[32m'
readonly YELLOW='\033[33m'
readonly RESET='\033[0m'
# 日志文件
readonly LOG_FILE="/var/log/lnmp_install_$(date +%Y%m%d_%H%M%S).log"
# 配置变量(可根据需要修改)
readonly MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-LNMProot123!}"
readonly PHP_VERSION="${PHP_VERSION:-8.1}"
要点解读:
-
set -euo pipefail:严格模式。-e遇错退出,-u未定义变量报错,-o pipefail管道中任何命令失败都算整体失败 -
readonly LOG_FILE:日志文件带时间戳,每次安装都有独立的日志记录 -
${MYSQL_ROOT_PASSWORD:-LNMProot123!}:支持通过环境变量自定义密码,不设置则用默认值
3.2 通用工具函数
bash
# 记录日志
log() {
echo -e "[$(date '+%H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log_success() {
echo -e "${GREEN}[✓] $1${RESET}" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[✗] $1${RESET}" | tee -a "$LOG_FILE"
exit 1
}
# 检测是否为root用户
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "请使用 root 用户运行此脚本"
fi
}
# 检测操作系统
check_os() {
if [[ ! -f /etc/debian_version ]]; then
log_error "此脚本仅支持 Debian/Ubuntu 系统"
fi
log_success "操作系统检测通过"
}
# 备份文件(覆盖前自动保留原文件)
backup_file() {
local file="$1"
if [[ -f "$file" ]] && [[ ! -f "${file}.bak" ]]; then
cp "$file" "${file}.bak"
log "已备份: ${file} → ${file}.bak"
fi
}
backup_file函数借鉴了一键包的良好实践------修改系统配置文件前自动保留.bak备份,出问题时可以快速回滚。
3.3 安装Nginx
bash
install_nginx() {
log "开始安装 Nginx..."
apt install nginx -y >> "$LOG_FILE" 2>&1 && \
log_success "Nginx 安装成功" || \
log_error "Nginx 安装失败"
# 备份原始配置
backup_file /etc/nginx/sites-available/default
# 创建默认站点配置
cat > /etc/nginx/sites-available/default << 'NGINX_EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
# PHP-FPM 配置
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
}
NGINX_EOF
# 测试配置
nginx -t >> "$LOG_FILE" 2>&1
systemctl restart nginx
systemctl enable nginx >> "$LOG_FILE" 2>&1
log_success "Nginx 配置完成"
}
关键细节 :fastcgi_pass使用Unix Socket(unix:/var/run/php/php8.1-fpm.sock),这是我们第32篇未深入的部分。Nginx和PHP-FPM之间可以通过TCP端口(127.0.0.1:9000)或Unix Socket通信。Unix Socket不走网络协议栈,性能更高,适合Nginx和PHP-FPM在同一台机器上的场景(也是我们的默认配置)。
3.4 安装MySQL
bash
install_mysql() {
log "开始安装 MySQL..."
# 预设root密码,避免安装过程中弹出交互式对话框
echo "mysql-server mysql-server/root_password password $MYSQL_ROOT_PASSWORD" | debconf-set-selections
echo "mysql-server mysql-server/root_password_again password $MYSQL_ROOT_PASSWORD" | debconf-set-selections
apt install mysql-server -y >> "$LOG_FILE" 2>&1 && \
log_success "MySQL 安装成功" || \
log_error "MySQL 安装失败"
# 执行安全配置(非交互式)
mysql -u root -p"$MYSQL_ROOT_PASSWORD" << MYSQL_EOF
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;
MYSQL_EOF
systemctl enable mysql >> "$LOG_FILE" 2>&1
log_success "MySQL 配置完成(root 密码: ${MYSQL_ROOT_PASSWORD})"
}
这部分用一个技巧跳过了mysql_secure_installation的交互式问答------通过SQL直接执行四步操作(删除匿名用户、限制root远程登录、删除test数据库、刷新权限),效果完全一致。其中debconf-set-selections是Debian/Ubuntu特有的"预回答安装问题"机制,让包管理器的安装过程完全静默、无需人工干预。
3.5 安装PHP及扩展
bash
install_php() {
log "开始安装 PHP ${PHP_VERSION}..."
# 添加PHP仓库并安装
apt install software-properties-common -y >> "$LOG_FILE" 2>&1
add-apt-repository ppa:ondrej/php -y >> "$LOG_FILE" 2>&1
apt update >> "$LOG_FILE" 2>&1
# 安装PHP及常用扩展
apt install -y \
php${PHP_VERSION}-fpm \
php${PHP_VERSION}-mysql \
php${PHP_VERSION}-curl \
php${PHP_VERSION}-gd \
php${PHP_VERSION}-mbstring \
php${PHP_VERSION}-xml \
php${PHP_VERSION}-zip \
php${PHP_VERSION}-bcmath \
>> "$LOG_FILE" 2>&1 && \
log_success "PHP ${PHP_VERSION} 安装成功" || \
log_error "PHP ${PHP_VERSION} 安装失败"
systemctl restart php${PHP_VERSION}-fpm
systemctl enable php${PHP_VERSION}-fpm >> "$LOG_FILE" 2>&1
log_success "PHP-FPM 配置完成"
}
扩展说明:
-
php-fpm:FastCGI进程管理器,让PHP能作为独立服务运行,Nginx通过它调用PHP -
php-mysql:PHP连接MySQL的驱动 -
php-curl:发起HTTP请求(调用API、发送Webhook等) -
php-gd:图像处理库(验证码、缩略图等) -
php-mbstring:多字节字符串支持(处理中文的关键) -
php-xml:XML/JSON解析 -
php-zip:压缩文件操作 -
php-bcmath:高精度数学运算(支付系统的金额计算)
3.6 验证部署
bash
verify() {
log "验证部署..."
# 创建测试页面
echo "<?php phpinfo(); ?>" > /var/www/html/index.php
chown -R www-data:www-data /var/www/html
# 验证各组件
systemctl is-active --quiet nginx && log_success "Nginx 运行中" || log_error "Nginx 未运行"
systemctl is-active --quiet mysql && log_success "MySQL 运行中" || log_error "MySQL 未运行"
systemctl is-active --quiet php${PHP_VERSION}-fpm && log_success "PHP-FPM 运行中" || log_error "PHP-FPM 未运行"
# HTTP测试
if curl -s http://localhost | grep -q phpinfo; then
log_success "LNMP 环境验证成功!"
log "请访问 http://$(hostname -I | awk '{print $1}') 查看 phpinfo 页面"
fi
# 安全提醒
log "${YELLOW}安全提醒: 请及时删除 /var/www/html/index.php 测试文件${RESET}"
log "MySQL root 密码: ${MYSQL_ROOT_PASSWORD}"
log "完整安装日志: ${LOG_FILE}"
}
3.7 主流程
bash
main() {
log "=========================================="
log " LNMP 自动部署脚本"
log " MySQL Root 密码: ${MYSQL_ROOT_PASSWORD}"
log " PHP 版本: ${PHP_VERSION}"
log " 日志文件: ${LOG_FILE}"
log "=========================================="
check_root
check_os
apt update >> "$LOG_FILE" 2>&1
install_nginx
install_mysql
install_php
verify
log_success "LNMP 环境部署完成!"
}
main
四、完整脚本与使用
将以上所有代码合并为一个install_lnmp.sh文件,执行:
bash
# 赋予执行权限
chmod +x install_lnmp.sh
# 默认安装(MySQL密码为预设值)
sudo bash install_lnmp.sh
# 自定义MySQL密码和PHP版本
sudo MYSQL_ROOT_PASSWORD="MyCustomPass456!" PHP_VERSION="8.2" bash install_lnmp.sh
部署完成后,浏览器访问http://服务器IP,应该看到phpinfo的输出页面。
部署耗时参考:在1核1G的云服务器上,整个安装过程约5-8分钟(视网络速度和软件源响应时间略有浮动)。
五、回顾与展望
5.1 这个脚本串联了哪些知识?
| 篇目 | 知识 | 在脚本中的体现 |
|---|---|---|
| 第10篇 | 软件包管理 | apt install xxx -y |
| 第12篇 | 退出码与exit |
log_error中的exit 1 |
| 第15篇 | systemd服务管理 | systemctl enable --now |
| 第16篇 | 计划任务 | 为后续的备份脚本预留crontab集成点 |
| 第22篇 | 变量 | ${MYSQL_ROOT_PASSWORD:-默认值} |
| 第23篇 | 条件判断 | if [[ $EUID -ne 0 ]] |
| 第25篇 | 循环 | 可用于批量安装PHP扩展 |
| 第26篇 | 函数与模块化 | log()、backup_file()等工具函数 |
| 第29篇 | awk | 提取hostname -I输出中的IP |
| 第32篇 | Nginx配置 | fastcgi_pass、location块 |
| 第33篇 | MySQL | 安全初始化SQL、debconf-set-selections |
5.2 第四阶段总结
至此,第四阶段"服务器应用与生产环境实战"(第31-40篇)全部完成。我们走过了防火墙防御、Web服务搭建、数据库配置、文件共享、容器化入门、DNS服务、网络文件系统、数据同步、版本控制和Web环境部署的完整旅程。
你已经具备了独立搭建和运维Linux服务器环境的能力。但应用部署只是第一步------当服务上线后,CPU飙升了怎么查?内存泄漏了怎么定位?磁盘I/O拖慢了响应怎么办?
第五阶段将进入Linux运维的最高阶领域:内核原理与高阶性能优化。我们将从内核编译、内存管理、I/O调度、网络协议栈、动态追踪等底层机制出发,让你不仅会"用"Linux,更能"理解"Linux的每一个行为背后的原理。
动手练习
bash
# 1. 将本文的脚本片段组装成完整的install_lnmp.sh
# 2. 在虚拟机中测试运行
sudo bash install_lnmp.sh
# 3. 验证部署
curl http://localhost | grep phpinfo
systemctl status nginx mysql php8.1-fpm
# 4. 查看安装日志
sudo cat /var/log/lnmp_install_*.log
延伸思考 :这个脚本只在Debian/Ubuntu上运行。如果要在CentOS/RHEL上实现同样的功能,需要做哪些修改?提示:apt→dnf,debconf-set-selections机制不存在需要换用其他方式预设密码,php-fpm的Socket路径也不同。将这些差异抽象成函数(如install_package()),根据/etc/os-release判断系统类型后调用不同的实现,这就是主流一键包做跨平台适配的核心思路。