【Linux从入门到精通】第40篇:LAMP/LNMP环境一键部署脚本实战

目录

一、引言:为什么自己写部署脚本?

二、主流一键包的内部分析

[2.1 它们做了什么?](#2.1 它们做了什么?)

[2.2 成熟的实践可以借鉴](#2.2 成熟的实践可以借鉴)

三、编写自己的LNMP部署脚本

[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的内存参数依赖此值)、磁盘剩余空间(编译需要大量临时空间),以及检测必要的命令是否可用(wgettarmake等)。

第二步:参数解析

支持命令行参数定制安装选项:

bash

复制代码
# ./install.sh --nginx-version=1.24.0 --php-version=8.1 --mysql-password=MyPass123

如果用户没有提供参数,脚本使用默认值(通常是较保守的稳定版本)。

第三步:依赖安装

自动安装编译工具链(gccmake等)和开发库(libpcre3-devlibssl-dev等)。这一步最耗时,通常占总运行时间的50%以上。

第四步:源码编译安装

按顺序执行每个软件的编译三部曲(./configure && make && make install),这和我们第10篇讲的完全一致。

第五步:配置与整合

安装完成后,自动生成基础配置文件,确保Nginx能找到PHP-FPM、PHP能连接MySQL。这一步是一键包差异最大的地方------不同脚本的默认配置风格差异明显。

2.2 成熟的实践可以借鉴

  • 日志保留每一个步骤echotee双输出,让用户能看到进度,同时所有操作写入日志文件

  • 失败不继续 :用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上实现同样的功能,需要做哪些修改?提示:aptdnfdebconf-set-selections机制不存在需要换用其他方式预设密码,php-fpm的Socket路径也不同。将这些差异抽象成函数(如install_package()),根据/etc/os-release判断系统类型后调用不同的实现,这就是主流一键包做跨平台适配的核心思路。

相关推荐
‎ദ്ദിᵔ.˛.ᵔ₎7 小时前
Linux 基础指令
linux
计算机安禾7 小时前
【Linux从入门到精通】第46篇:SELinux与AppArmor——Linux的安全增强模块
linux·运维·安全
speop7 小时前
Reasoning kingdom chapter13
android·java·python
落羽的落羽7 小时前
【网络】计算机网络世界的基础概念
linux·服务器·网络·c++·人工智能·计算机网络·机器学习
xxjj998a7 小时前
Laravel7.x核心特性全解析
数据库·mysql·adb
xxjj998a7 小时前
Laravel9.x新特性全面解析
android
计算机安禾7 小时前
【Linux从入门到精通】第41篇:Linux内核编译初体验——裁剪属于你自己的内核
linux·运维·服务器
木木_王8 小时前
嵌入式Linux学习 | 数据结构 (Day03)顺序表与单链表 超详细解析(含 C 语言实现 + 作业 + 避坑指南)
linux·c语言·数据结构·学习
vortex58 小时前
HackMyVm靶机Artig复盘
linux·渗透测试·靶机·hmv