
简介一个面向Linux运维与Web开发人员的ApachePHP7Mariadb一键安装包专门解决在Ubuntu 24、麒麟V10、CentOS 7等系统上手动搭建Web环境时脚本整理繁琐、源码编译耗时、配置项众多的痛点。作者特别针对CentOS停止维护后无法正常安装的问题重新配置了YUM源老系统也能顺利安装部署。压缩包共7个文件整体仅108KB包含4个RPM依赖包、2个PHP配置文件和1个主安装脚本结构精简清晰运行脚本即可自动完成依赖检查、服务安装与基础配置。目前已有557人学习下载适合需要快速搭建开发或测试环境、又不想在编译和调参上浪费大量时间的技术用户。通过该资源可直接获得一套经过整理与验证的部署方案显著减少环境搭建与排错时间也可为后续维护提供明确的配置参考。 做了大半年 Linux 服务器运维手头最烦的事就是给新机器部署一套 Web 环境。尤其最近接到一个需求要在 Ubuntu 24、麒麟 V10、CentOS 7 这三种完全不同的系统上统一部署 Apache PHP7 MariaDB 这套经典组合。系统差异大、依赖版本乱、指令还各不相同每次手动装都像是一场赌博。后来我干脆花了几个晚上写了一套一键安装资源把三套系统全部跑通测试过了这里直接把脚本思路、细节实现和踩坑记录分享出来。这套资源的核心价值很直接拿到一台新机器只需要上传一个压缩包解压后执行一条命令剩下的 Apache、PHP-FPM、MariaDB 安装和初始化全部自动完成。它既解决了不同发行版之间包管理器的差异问题也把 PHP 版本锁定在了兼容性较好的 PHP 7.4Ubuntu 和 CentOS 系都有现成渠道同时还不碰系统自带的 Python、OpenSSL 等基础组件相对干净。适合做内网项目交付、客户环境初始化、或者自己实验室快速建站的运维同学参考。1. 为什么我决定写一套自己的 LAMP 一键脚本先说背景。我做项目交付时最常遇到的就是客户给了台服务器系统版本五花八门有的是 CentOS 7 的老机器有的是新采购的麒麟 V10还有测试环境的 Ubuntu 24。我最早是用 Ansible 写过一次 playbook但 Ansible 本身需要在控制机和目标机两边装 Python 环境CentOS 7 默认的 Python 2.7 兼容性虽然没问题可异地交付时客户不一定愿意装 agent 类工具。所以第二版我就换成了纯 shell 脚本目标很简单cp上去、bash install.sh收工不依赖任何额外运行时。这套脚本真正让我下决心重写的原因是三个系统之间几个非常恼人的差异点Ubuntu 24 的 apt 源默认只提供 PHP 8.3但客户的旧项目代码只保证 PHP 7.2–7.4 下运行稳定直接 apt 装 8.3 会有一堆 deprecated 告警甚至直接白屏。麒麟 V10 有好几个版本部分版本底层是 CentOS 8 的生态dnf 指令部分又是 CentOS 7 的生态yum 指令不检测清楚就写命令脚本跑到一半就会中断。CentOS 7 默认的 yum 源里 PHP 版本停留在 5.4要装 PHP 7.4 必须引入 EPEL 和 Remi 源而 Remi 源又依赖 EPEL这里面的依赖顺序一旦错了安装直接失败。所以这套一键脚本真正解决的不是敲三条命令的问题而是跨发行版做环境适配的工程问题。它比单纯的复制粘贴命令多了一层系统检测和源切换的逻辑这也是它能在三个系统上保持一致体验的原因。1.1 三个目标系统一个都不能少在设计时我没有按主攻一个系统的思路来做而是把三个系统当成三个独立的目标档位脚本启动时先跑一段系统识别逻辑再分流执行对应分支。这么做的好处是后续如果还需要适配 openEuler、Debian 之类的系统只需要增加一个分支函数不需要改动主流程。系统识别主要靠/etc/os-release文件里面会有ID和VERSION_ID字段。例如 Ubuntu 24 会返回IDubuntu、VERSION_ID24.04麒麟 V10 会返回IDkylinCentOS 7 则返回IDcentos、VERSION_ID7。这几个关键字段就足够让脚本决定后续走哪套逻辑了。1.2 一键脚本的设计边界不该自动化的坚决不自动化写自动化脚本最忌讳的是大包大揽。我见过不少一键脚本把防火墙关闭、SELinux 禁用、甚至系统内核参数都改了最后出了问题根本不知道是哪一步动的手。我的原则是只做安装必需组件 启动服务 创建基础配置文件这三件事不碰系统安全策略不修改内核参数不删除任何已有软件包。这样一来脚本在客户现场的接受度会高很多。特别是 CentOS 7 上SELinux 默认是 enforcing 状态如果脚本直接 setenforce 0某些客户的安全巡检会亮红灯。我的做法是安装 httpd 后用semanage port -a -t http_port_t -p tcp 8080这类命令单独放行 Apache 监听端口既满足功能又保持系统安全策略的完整性。2. 资源包整体结构设计与分发方案先放一下最终的目录结构这是整个资源包的核心骨架lamp-onekey/ ├── install.sh # 统一入口自动识别系统并分发 ├── conf/ │ ├── apache-vhost.conf # Apache 虚拟主机模板 │ ├── php.ini.production # PHP 生产环境常用配置 │ └── my.cnf.mariadb # MariaDB 免初始化配置模板 ├── scripts/ │ ├── ubuntu_install.sh # Ubuntu 24 专属安装逻辑 │ ├── kylin_install.sh # 麒麟 V10 专属安装逻辑 │ ├── centos7_install.sh # CentOS 7 专属安装逻辑 │ ├── mariadb_secure.sh # MariaDB 安全初始化脚本 │ └── common_lib.sh # 公共函数库日志、打包、校验 ├── src/ │ └── php7.4-tarball/ # 可选源码包缓存内网离线时使用 └── README.md这种一个入口 多个分支脚本 公共函数库的结构是我试了好几版以后确定下来的。早期我图省事把所有逻辑写在了一个 800 行的 install.sh 里后来发现改一处要反复翻上下文维护成本很高。拆成独立脚本以后每个系统的问题只需要去对应文件里定位调试效率提升非常明显。2.1 统一入口 install.sh 的真实逻辑install.sh 做的事情其实不多检查是否为 root 用户、识别系统类型、加载公共函数库、然后调用对应系统的安装脚本。这里有个关键点就是检查命令执行环境。很多脚本直接用#!/bin/bash但有些精简版系统的/bin/sh指向的是 dash不是 bash所以第一行解释器必须写清楚否则语法会报错。install.sh 开头部分我这样处理#!/bin/bash set -euo pipefail source ./scripts/common_lib.sh if [ $(id -u) -ne 0 ]; then log_error 请使用 root 用户执行 exit 1 fi detect_os case $OS_ID in ubuntu) bash ./scripts/ubuntu_install.sh ;; kylin) bash ./scripts/kylin_install.sh ;; centos) bash ./scripts/centos7_install.sh ;; *) log_error 不支持的发行版: $OS_ID exit 1 ;; esacset -euo pipefail这三行是我这次踩坑后硬性加上去的。set -e表示任何一条命令返回非零状态脚本立即终止防止某些步骤明明失败了还在继续往下跑最后产生一个半残环境。set -u则保证变量未被定义时就报错避免拼写错误导致静默问题。pipefail防止管道前一个命令失败但管道整体退出码为 0 的情况。这些设置对于跨系统脚本来说非常关键。2.2 为什么用 shell 而不是 Ansible 或者 Docker我最初也考虑过 Docker 方案毕竟一条docker run确实省事。但实际交付时发现问题很多首先是内网环境没有 Docker Hub 镜像源pull 不下来其次是客户的生产机器可能不允许运行 Docker daemon安全管控比较严格最后是镜像里跑的进程和宿主机之间有时区、日志路径、文件权限不一致的坑排查起来比直装还费劲。所以最终选了纯 shell只在目标机器上调用系统原生的包管理工具。这也是我这几年下来比较坚定的一个结论交付给客户的运维资源依赖越少越好。系统自带的 bash、curl、tar、sed 就足够了没有任何额外运行时依赖任何一台能跑 Linux 的机器都能执行。2.3 在线安装与离线安装的取舍这套资源默认走在线安装也就是调用 apt/yum 从云镜像源下载软件包。但客户现场经常是内网隔离环境所以我额外做了离线包缓存机制在src/目录里预留了 php7.4 源码包、rpm 包和 deb 包的位置。如果是离线部署把对应系统架构的包提前放到src/目录脚本检测到本地有匹配文件时优先走本地安装。离线安装的处理逻辑不复杂但是在 CentOS 7 上有一点特别容易踩坑rpm 包的依赖顺序。MariaDB 的 rpm 包依赖libaio、perl等基础库如果直接一条rpm -ivh MariaDB-server-*.rpm拍下去大概率会因为前置依赖缺失而失败。我的脚本里封装了一段rpm_install_multi函数它会循环扫描当前目录下所有 rpm 包并逐个尝试安装直到某一轮没有任何一个新包被安装成功为止这样能最大程度自动解决依赖顺序问题。3. 三个发行版的差异处理与实测记录这部分是整套资源里最有含金量的因为每套系统都有自己独特的脾气处理不好就会翻车。3.1 Ubuntu 24干净但也有坑Ubuntu 24 的 apt 源默认 PHP 是 8.3常规安装 Apache2 和 MariaDB 都很简单apt update apt install -y apache2 mariadb-server但 PHP 这里必须处理版本回退。我给 Ubuntu 分支指定的方案是使用 ondrej/php PPA这是社区维护的 PHP 版本仓库可以稳定获取 php7.4。添加 PPA 后执行add-apt-repository -y ppa:ondrej/php apt update apt install -y php7.4 php7.4-fpm php7.4-mysql php7.4-mbstring php7.4-curl php7.4-gd php7.4-xml安装完成后要特别注意 PHP-FPM 的 socket 配置。Ubuntu 下 php7.4-fpm 默认监听/run/php/php7.4-fpm.sock而 Apache 的 mod_php 模块并不存在必须走 FastCGI 代理方式。所以我的 Apache 虚拟主机配置里专门加了这一段FilesMatch \.php$ SetHandler proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost /FilesMatch如果不写这一句Apache 会把 PHP 文件当纯文本输出浏览器直接看到源码。3.2 麒麟 V10兼容 CentOS 生态但细节不同麒麟 V10 是我这次适配中最需要小心的一环。它有两种形态一种基于 CentOS 7 的 rpm 生态一种基于 CentOS 8 的 dnf 生态。我实测的那台是迁移自 CentOS 7 的版本所以走了 yum 分支。麒麟默认的 yum 源里没有 php7.4需要先安装 EPEL 源再安装 Remi 源。但麒麟的/etc/yum.repos.d/下自带的仓库文件名和 CentOS 有差异直接照搬 CentOS 的epel-release命令会报错。后来我手动确认了系统的 baseurl 指向的是麒麟官方镜像然后改用 remi-release 的 rpm 包直接安装这样 Remi 源会写入/etc/yum.repos.d/remi.repo和系统自带的仓库互不干扰。安装 PHP 时需要明确启用 remi-php74 模块流yum install -y epel-release rpm -Uvh https://rpms.remirepo.net/enterprise/remi-release-7.rpm yum --enablereporemi,remi-php74 install -y php php-fpm php-mysqlnd php-mbstring php-curl php-gd php-xml这里有一个容易忽略的地方如果只写yum install phpyum 还是会把系统自带仓库里的 PHP 5.4 装进来必须显式指定--enablereporemi-php74才会命中目标版本。我最初就是因为没加这个参数装了好几次都得到 PHP 5.4排查了大半天。麒麟分支还有一个坑是systemctl对 MariaDB 服务的命名。CentOS 7 官方源里的 MariaDB 是 5.5服务名是mariadb但如果系统里同时装了 mysql 的兼容包服务名可能会变成mysql。我的处理方式是一个兜底函数尝试启动 mariadb失败则尝试 mysql再失败则输出手动检查提示。3.3 CentOS 7老而稳最需要小心CentOS 7 虽然主流生命周期已经过了但它作为存量服务器系统占比依然很高。给 CentOS 7 做一键脚本最大的挑战不是功能而是源。默认 yum 源里的 httpd 是 2.4.6、PHP 是 5.4、MariaDB 是 5.5全都太旧了。我的组合是Apache 使用 CentOS 7 自带 httpd2.4.6功能足够不额外升级PHP 使用 Remi 源装 7.4MariaDB 使用官方 MariaDB 镜像源装 10.4官方源里同时提供了mariadb-server、mariadb-client等完整包。配置 MariaDB 官方源时需要先创建/etc/yum.repos.d/mariadb.repo。注意 MariaDB 官方源地址里带了版本号和系统标识系统标识必须写rhel7而不是centos7写错了会 404[mariadb] name MariaDB baseurl https://mirror.mariadb.org/yum/10.4/rhel7-amd64/ gpgkey https://mirror.mariadb.org/yum/RPM-GPG-KEY-MariaDB gpgcheck 1CentOS 7 还有一个所有 CentOS 系脚本都必须面对的问题SELinux。如果开着 enforcingApache 默认不能连数据库、不能写目录光是一个网站能打开但 PHP 连不上 MariaDB的问题就能耗掉一晚上。我的做法是在 MariaDB 安全初始化脚本里单独执行两条 SELinux 策略调整命令setsebool -P httpd_can_network_connect_db 1 setsebool -P httpd_can_network_connect 1这两条命令只影响 httpd 域的网络访问权限不会关闭 SELinux 本身安全上可接受也容易向客户解释。4. 安装后的初始配置与安全加固一键安装完成后脚本会留下一些基础配置模板同时顺手做掉必要的安全初始化。这个环节不是可选项尤其是 MariaDB 的空密码问题这是很多新装环境被入侵的直接原因。4.1 PHP-FPM 与 Apache 的整合细节把 PHP-FPM 接入 Apache除了上一节提到的 FilesMatch 代理配置还要注意DirectoryIndex里要包含 index.php否则访问根路径时不会自动去找 PHP 入口文件。Apache 的默认配置文件在三个系统里位置不同Ubuntu 是/etc/apache2/apache2.confCentOS/麒麟是/etc/httpd/conf/httpd.conf。脚本里我直接写了一个函数用 grep 检查是否已经有配置没有再追加避免重复安装时产生重复指令。还有一个细节是 PHP 的listen地址。为了性能和权限隔离我让 php-fpm 监听 Unix Socket 而不是 TCP 端口这样外部网络无法直接探测到 PHP 服务。配置文件在/etc/php/7.4/fpm/pool.d/www.conf关键参数如下listen /run/php/php7.4-fpm.sock listen.owner www-data listen.group www-data listen.mode 0660其中listen.mode 0660保证 Socket 只允许属主和属组访问Apache 进程用户如果是 www-data就能正常转发请求。如果 Apache 进程用户是 apache则需要把 owner/group 改成 apache或者把 Apache 的 User 指令改成 www-data。4.2 MariaDB 初始化与审计插件新装的 MariaDB 默认 root 用户没有密码而且只能通过 Unix socket 登录。我的安全初始化脚本做了这些事设置 root 密码从环境变量读取如果没有则生成随机密码并写入脚本同目录的密码文件删除匿名用户移除 test 数据库强制刷新权限表。关于 audit plugin我在 CentOS 7 和麒麟上额外启用了 MariaDB 的审计日志插件因为这两个系统的安全合规要求比较高。MariaDB 从 10.1 开始自带了server_audit插件启用方法很简单INSTALL SONAME server_audit; SET GLOBAL server_audit_logging ON; SET GLOBAL server_audit_file_path /var/log/mysql/audit.log; SET GLOBAL server_audit_file_rotate_size 104857600;这个插件会把所有连接、查询、权限变更事件写入审计日志对等保测评很有帮助。Ubuntu 24 上的 MariaDB 版本更新自带的插件兼容性也更好直接安装即可。4.3 服务自启与状态检查脚本最后一步是设置开机自启并验证服务状态systemctl enable httpd php-fpm mariadb systemctl start httpd php-fpm mariadb systemctl --no-pager status httpd php-fpm mariadb这里要检查服务是否真的起来了而不仅仅是设置 enable。我遇到过几次enable 成功但服务实际没起来的情况典型的错误是 php-fpm 的 pid 目录不存在或者权限错误导致启动失败但 systemctl 没有返回明显错误提示。所以脚本里在启动完成后会接着执行一次 curl 探测curl -I http://127.0.0.1/ 2/dev/null | head -n 1如果返回 HTTP/1.1 200 或 403说明 Apache 至少起来了如果连接被拒绝脚本会提示用户手动查看日志而不是假装安装成功。5. 常见问题与排查技巧实录这里整理一下我在这三个系统上测试时遇到频率最高的几个问题每个都配上排查思路和解决方案后续抄作业时能少走很多弯路。问题现象系统范围排查思路解决方式浏览器访问 PHP 文件显示源码三个系统都有概率说明 Apache 没有把 .php 交给 PHP-FPM 处理确认 FilesMatch 代理配置已写入重启 Apache 后再试php -v 显示版本正常但页面报 502 Bad Gateway主要出现在 Ubuntu 24php-fpm 服务没起来或 socket 路径与配置不一致检查 php-fpm 状态确认代理配置里的 socket 路径与实际完全对应yum 安装 php 时得到的还是 5.4麒麟 V10 / CentOS 7Remi 源未启用或未指定 php74 模块流安装 remi-release 后安装命令必须加--enablereporemi-php74PHP 页面连接数据库失败报 permission deniedCentOS 7 / 麒麟SELinux 拦截了 Apache 到数据库的网络访问执行setsebool -P httpd_can_network_connect_db 1MariaDB root 登录失败三个系统都有概率安装后未正确初始化 root 密码socket 认证与实际密码不一致使用mysql -uroot -p直接登录或sudo mysql进 root 后再改密码Apache 启动报 Address already in use三个系统均有已有 nginx 或者其他 Web 服务占用 80/443 端口用ss -lntp查看占用进程临时停掉旧服务后重启 Apache5.1 一个最容易忽略的权限问题我在 Ubuntu 24 测试时遇到过一个问题PHP-FPM 和 Apache 都能起来但网站传文件到/var/www/html时提示没有写权限。排查了半天发现是/var/www目录的属主是 rootHTML 子目录没有给 Apache 用户的写入权限。这个不是脚本的问题是 Linux 权限的经典场景。我的建议是如果站点需要上传文件应该单独挂一个 upload 目录并设置好属主而不是直接把整个 web 根目录开放写权限避免被上传恶意文件。5.2 关于一键脚本的自更新和幂等性最后分享一个关于脚本设计的小心得。我在执行脚本前加了set -e理论上只要某一步失败脚本就会停下来。但为了处理脚本跑了一半失败修复后重新执行的场景脚本里所有关键步骤都设置了幂等特性比如配置文件存在时会先备份再覆盖用户已存在时不会重复创建软件包已安装时跳过安装。这样一来客户现场即使第一次执行因为网络原因失败了第二次重跑也不会有副作用这个体验对运维人员来说非常重要。我个人的体会是一键安装脚本这件事难点从来不在安装本身而在于怎么优雅地处理系统差异和异常情况。做这套资源前前后后花了几天时间真正的时间都花在了版本检测、源配置、SELinux 这类旁支问题上。如果你只是在统一版本的系统上做部署其实没必要写这么复杂但如果你和我一样需要面对多个发行版的交付那套检测分发、幂等处理、安全初始化的框架就非常值得花时间搭建了。本文还有配套的精品资源点击获取