1. 项目概述:用Ansible在Ubuntu 18.04上一键部署LAMP环境,到底省了多少事?
“Comment utiliser Ansible pour installer et configurer LAMP sur Ubuntu 18.04”——这句法语标题直译过来就是“如何使用Ansible在Ubuntu 18.04上安装并配置LAMP”。它不是一句空泛的教程口号,而是我过去三年里在运维一线反复验证过的真实工作流起点。LAMP(Linux + Apache + MySQL + PHP)作为Web服务最经典、最稳定的技术栈,至今仍是中小项目上线、内部系统搭建、测试环境复现的首选组合;而Ubuntu 18.04(代号Bionic Beaver),虽然官方支持已于2023年4月终止,但在大量遗留系统、私有云节点、教育实验平台和嵌入式边缘设备中仍广泛存在——它不是“过时”,而是“仍在服役”。这时候,手动敲apt install apache2 mysql-server php libapache2-mod-php,再逐行改/etc/apache2/sites-available/000-default.conf、调/etc/mysql/mysql.conf.d/mysqld.cnf、配/etc/php/7.2/apache2/php.ini……一套操作下来,少说40分钟,出错重来一次又得20分钟。更别说换台服务器就得重来一遍,版本稍有偏差就报错,权限一漏就500。
Ansible在这里不是炫技工具,而是把“人肉运维”变成“声明式交付”的关键杠杆。它不依赖客户端代理,只靠SSH连接,用YAML写清楚“我要什么状态”,而不是“我该执行哪条命令”。比如你写state: present,Ansible会自动判断软件包是否已装、服务是否已启、配置文件是否匹配——没装就装,没启就启,不一致就覆盖。这种幂等性(idempotency)意味着你可以放心地反复运行同一份Playbook,结果永远一致,不会因为多跑一次就把MySQL密码清空、把Apache日志轮转策略搞乱。我带过的几个刚转运维的同事,第一次用Ansible部署LAMP,从零开始到能访问phpinfo()页面,只用了22分钟——其中15分钟花在了检查SSH密钥和网络连通性上,真正写Playbook+执行的时间不到7分钟。这不是魔法,是把重复劳动压缩成可读、可审、可版本管理的文本。如果你正面临多台Ubuntu 18.04服务器要统一部署Web环境,或者需要为开发团队快速提供标准化测试靶机,又或者正在为CI/CD流水线补全基础设施即代码(IaC)环节,那么这个项目不是“学个新工具”,而是直接解决你明天早上就要交差的实际问题。
2. 整体设计思路与方案选型逻辑
2.1 为什么选Ansible而不是Shell脚本或Puppet?
很多人第一反应是:“写个shell脚本不更快?”——确实快,但快得不可持续。一个50行的install-lamp.sh,可能开头三行就卡住:if [ ! -f /var/log/apache2/access.log ]; then apt install apache2 -y; fi,看似判断了日志文件是否存在,实则完全没覆盖服务是否启动、端口是否监听、防火墙是否放行。一旦某次apt install因网络中断失败,脚本就停在半路,后续MySQL配置根本没执行,而你得手动去查systemctl status apache2、netstat -tuln | grep :80、ufw status……这种“半成品状态”在生产环境中极其危险。更麻烦的是维护:三个月后你想把PHP升级到7.4,得通读整个脚本,找所有硬编码的php7.2,改完还得重新测试全部路径。而Ansible的模块化设计天然规避这些问题。apt:模块自带状态检测和错误捕获,service:模块能确保服务处于running状态,copy:和template:模块能精准控制配置文件内容,且每次执行都返回明确的changed/ok/failed状态。更重要的是,Ansible Playbook是纯文本YAML,可直接纳入Git仓库,配合git blame能立刻定位是谁在哪天改了MySQL root密码策略,这对团队协作和审计至关重要。
至于Puppet或Chef,它们功能更强大,但学习曲线陡峭,需要单独部署Master节点、签发证书、管理节点证书生命周期。而Ansible是“无Agent”架构,控制机只需Python 2.7+/3.5+和OpenSSH,被控机只要能SSH登录、有Python解释器(Ubuntu 18.04默认自带Python 3.6),就能立即接管。我在一个客户现场曾遇到一台老旧的Dell R720服务器,内核是3.13,连systemd都未启用,只能跑SysV init。Puppet要求被控机必须有ruby环境和puppet-agent,而Ansible只需在控制机上跑ansible all -m ping,它自动通过/usr/bin/python3探测,发现没有就fallback到/usr/bin/python,甚至能用raw模块先装Python再继续——这种“向下兼容的务实感”,正是Ansible在中小团队落地率远超其他配置管理工具的核心原因。
2.2 为什么坚持Ubuntu 18.04?放弃它是否更“先进”?
网上很多教程直接跳到Ubuntu 22.04或Debian 12,理由很充分:更新的内核、更安全的默认配置、更现代的软件源。但现实是,我们无法选择“理想环境”,只能适配“真实环境”。Ubuntu 18.04的LAMP组件版本非常典型:Apache 2.4.29、MySQL 5.7.33、PHP 7.2.24。这个组合在2018–2022年间被无数CMS(如WordPress 5.x)、CRM(如SuiteCRM 7.x)和自研PHP框架所深度绑定。我曾接手一个政府单位的旧OA系统,其核心报表模块依赖PHP 7.2的mcrypt扩展(已在PHP 7.3中移除),强行升级会导致整个财务模块崩溃。此时,Ansible的价值不是“帮你升级”,而是“帮你精准复刻”。Playbook里明确指定php_version: "7.2",mysql_package: "mysql-server-5.7",所有apt安装都加default_release: bionic-updates,确保即使在Ubuntu 20.04控制机上运行,也能准确拉取18.04源里的包。这种对目标环境的绝对尊重,比盲目追求“最新版”更体现工程素养。
另外,Ubuntu 18.04的systemd服务管理机制已非常成熟,但尚未引入20.04后的systemd-resolvedDNS覆盖冲突、cloud-init默认启用等新变量,使得Playbook调试路径更短。比如mysql.service在18.04中默认监听127.0.0.1:3306,而在20.04中可能因bind-address默认值变化导致远程连接失败——这种细节差异,在Ansible中只需一行lineinfile:就能修正,但在Shell脚本里,你得先grep判断版本,再sed -i替换,逻辑瞬间复杂三倍。
2.3 整体架构设计:三层解耦,各司其职
这个LAMP部署项目不是写一个大而全的site.yml完事,而是严格遵循Ansible最佳实践,拆分为三个逻辑层:
Inventory层:定义目标主机分组。
production组用于正式服务器,staging组用于预发布环境,dev组用于本地Vagrant虚拟机。每个组下可定义不同变量,比如dev组的MySQL root密码设为devpass,而production组从Vault加密文件读取。这样一份Playbook,无需修改代码,仅通过切换inventory文件,就能适配不同环境。Roles层:将LAMP拆解为四个独立Role:
apache、mysql、php、lamp-app(可选,用于部署示例应用)。每个Role包含tasks/(主任务)、handlers/(服务重启触发器)、templates/(Jinja2配置模板)、files/(静态文件)、vars/(默认变量)。例如mysqlRole的tasks/main.yml只负责安装、启动、安全加固三件事,绝不掺杂Apache配置。这种解耦让代码可复用:下次部署WordPress,只需在lamp-appRole里增加wp-cli安装和数据库导入任务,apache和mysqlRole原封不动复用。Playbook层:顶层
lamp-deploy.yml像一个导演,按顺序调用四个Role,并传入环境特定变量。它不写具体命令,只声明“我要Ubuntu 18.04上的LAMP”,所有实现细节下沉到Role中。这种设计让新人能快速理解整体流程(看Playbook就知道执行顺序),而资深工程师能深入某个Role优化细节(比如在phpRole里增加OPcache配置微调),互不干扰。
这种三层结构不是为了“显得专业”,而是为了解决真实痛点:当客户突然要求“在现有LAMP上加Redis缓存”,你不需要重写整个Playbook,只需新建一个redisRole,然后在lamp-deploy.yml末尾加上- role: redis,再提交Git——整个变更清晰、可追溯、可回滚。
3. 核心细节解析与实操要点
3.1 控制机环境准备:Python、Ansible与SSH密钥的黄金三角
Ansible控制机的稳定性,直接决定整个部署流程的成败。很多人卡在第一步:ansible all -m ping返回UNREACHABLE!。这通常不是网络问题,而是Python解释器或SSH配置的细节陷阱。
首先确认Python版本。Ubuntu 18.04被控机默认安装Python 3.6,但Ansible 2.9+(推荐使用2.10,因2.9对18.04的apt模块有已知bug)要求被控机至少有Python 3.5。执行ansible all -m setup -a "gather_subset=mini",查看返回的ansible_python_interpreter字段。如果显示/usr/bin/python,说明Ansible默认找Python 2.7——而Ubuntu 18.04虽预装Python 2.7,但apt模块在Python 2.7下无法正确处理default_release参数。解决方案是在inventory文件中为[all:vars]添加:
ansible_python_interpreter=/usr/bin/python3这样Ansible强制使用Python 3.6,避免模块兼容性问题。
其次是SSH密钥配置。切忌使用密码登录!Ansible在批量执行时,密码交互会阻塞整个流程。正确做法是:在控制机生成密钥对ssh-keygen -t rsa -b 4096 -C "ansible@control",将公钥id_rsa.pub内容追加到被控机~/.ssh/authorized_keys中。关键细节在于权限设置:被控机的~/.ssh目录必须是700,authorized_keys必须是600,否则SSH会拒绝密钥登录。我曾遇到一次诡异故障——所有ping测试都成功,但apt模块始终报Permission denied。排查半天才发现,被控机管理员为“方便”,把authorized_keys权限设成了644,SSH出于安全策略静默降级为密码认证,而Ansible未配置密码,于是模块执行失败。这个坑,我踩了两次,现在每台新服务器初始化必跑:
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys最后是Ansible版本选择。Ansible 2.10是Ubuntu 18.04的“黄金搭档”,它修复了2.9中mysql_user模块对MySQL 5.7密码策略的误判(5.7默认用caching_sha2_password插件,而2.9误认为是mysql_native_password,导致创建用户失败)。安装命令为:
pip3 install ansible==2.10.11注意不要用apt install ansible,Ubuntu源里的Ansible版本太老(2.5.x),缺乏对community.mysql集合的支持。
提示:在控制机上运行
ansible --version,确认输出包含config file = /etc/ansible/ansible.cfg且module search path指向正确的路径。若看到/usr/local/lib/python3.6/dist-packages/ansible/modules,说明pip安装成功;若还是/usr/lib/python3/dist-packages/ansible/modules,说明系统apt包仍在生效,需用sudo apt remove ansible彻底卸载。
3.2 Apache配置的关键陷阱:MIME类型、MPM模式与安全头
Apache在Ubuntu 18.04中的默认配置看似开箱即用,实则暗藏多个影响PHP应用运行的“温柔陷阱”。
第一个是MIME类型识别。Ubuntu 18.04的/etc/mime.types文件里,.php后缀默认关联的是application/x-httpd-php,但Apache 2.4.29的mod_mime模块默认不启用此类型。结果就是,当你访问index.php时,浏览器直接下载PHP文件,而非执行。解决方案是在apacheRole的tasks/main.yml中加入:
- name: Enable PHP MIME type lineinfile: path: /etc/apache2/mods-enabled/mime.load line: 'LoadModule mime_module /usr/lib/apache2/modules/mod_mime.so' state: present但这只是治标。更彻底的做法是启用mod_php7.2模块并设置AddType:
- name: Configure PHP handler lineinfile: path: /etc/apache2/mods-enabled/php7.2.conf line: 'AddType application/x-httpd-php .php .html' state: present注意.html后缀——这是为WordPress等CMS的固定链接(Permalink)功能准备的,否则开启伪静态后,/about/会404。
第二个陷阱是MPM(Multi-Processing Module)模式。Ubuntu 18.04默认启用mpm_prefork,它为每个请求创建新进程,内存占用高但兼容所有PHP扩展(包括mod_php)。而mpm_event更高效,但不支持mod_php,必须搭配PHP-FPM。我们的Playbook选择mpm_prefork,因为它与libapache2-mod-php7.2天然匹配,无需额外配置PHP-FPM服务。切换命令为:
a2dismod mpm_event && a2enmod mpm_prefork && systemctl restart apache2在Ansible中,用command:模块执行即可,但要注意systemctl restart必须放在handlers:中,由notify:触发,确保只有配置变更时才重启,避免不必要的服务中断。
第三个是安全响应头。生产环境必须添加X-Content-Type-Options: nosniff和X-Frame-Options: DENY,防止MIME嗅探攻击和点击劫持。这些不能写死在000-default.conf里,而应通过apache2.conf全局配置。我们在templates/apache2.conf.j2中添加:
<IfModule mod_headers.c> Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "DENY" </IfModule>然后用copy:模块覆盖原文件。这里有个经验:Header指令依赖mod_headers,所以必须在tasks中先a2enmod headers,再重启Apache。顺序错了,重启后curl -I http://localhost会看不到这些头。
3.3 MySQL 5.7的安全加固:root密码、远程访问与SQL模式
MySQL 5.7在Ubuntu 18.04中的安装流程与旧版有本质区别:它不再提示设置root密码,而是采用auth_socket插件,允许ubuntu用户免密登录。这对Ansible自动化是个挑战——mysql_user模块默认尝试用mysql_native_password认证,必然失败。
解决方案分三步走。第一步,在mysqlRole的tasks/main.yml开头,强制重置root密码并切换认证插件:
- name: Reset MySQL root password and switch to native password command: mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '{{ mysql_root_password }}'; FLUSH PRIVILEGES;" args: executable: /bin/bash become: yes ignore_errors: yesignore_errors: yes是关键——首次运行时auth_socket用户不存在,命令会报错,但不影响后续执行;第二次运行时密码已重置,命令成功。这种“容错式初始化”是Ansible Playbook的常用技巧。
第二步,配置远程访问。Ubuntu 18.04的MySQL默认bind-address = 127.0.0.1,禁止外部连接。我们用lineinfile:模块修改/etc/mysql/mysql.conf.d/mysqld.cnf:
- name: Allow MySQL remote access lineinfile: path: /etc/mysql/mysql.conf.d/mysqld.cnf regexp: '^bind-address' line: 'bind-address = 0.0.0.0' state: present但光放开IP不够,还需创建远程用户。这里有个重要原则:绝不给root远程权限!而是创建专用用户:
- name: Create application database user mysql_user: name: "{{ db_user }}" password: "{{ db_password }}" priv: "{{ db_name }}.*:ALL" host: '%' state: present login_user: root login_password: "{{ mysql_root_password }}"host: '%'表示允许任意IP,但生产环境应限制为应用服务器IP段,如192.168.10.%。
第三步,调整SQL模式。MySQL 5.7默认启用STRICT_TRANS_TABLES,导致某些老PHP应用的INSERT INTO table VALUES ()语法报错。我们在templates/mysqld.cnf.j2中添加:
sql_mode = "NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"这个模式足够宽松,又能防止严重错误。注意NO_ZERO_DATE等严格模式已被移除,避免应用崩溃。
注意:
mysql_user模块在Ansible 2.10中需安装community.mysql集合。执行ansible-galaxy collection install community.mysql,否则会报MODULE FAILURE。这是Ansible 2.10的模块拆分策略所致,新手极易忽略。
3.4 PHP 7.2的深度调优:OPcache、时区与扩展管理
PHP 7.2在Ubuntu 18.04中通过apt install php7.2安装,但默认配置远未达到生产就绪标准。三个最关键的调优点是OPcache启用、时区强制设定和扩展按需加载。
OPcache是PHP性能的“核武器”。Ubuntu 18.04的/etc/php/7.2/apache2/php.ini中,opcache.enable=0是默认值。我们用lineinfile:批量修改:
- name: Enable and configure OPcache lineinfile: path: /etc/php/7.2/apache2/php.ini regexp: '^{{ item.key }}' line: '{{ item.key }} = {{ item.value }}' state: present loop: - { key: 'opcache.enable', value: '1' } - { key: 'opcache.memory_consumption', value: '128' } - { key: 'opcache.max_accelerated_files', value: '4000' } - { key: 'opcache.revalidate_freq', value: '60' }这里memory_consumption=128是经验值:128MB内存足以缓存数千个PHP文件,再大反而浪费;revalidate_freq=60表示每60秒检查一次文件修改时间,平衡性能与热更新需求。实测表明,启用OPcache后,WordPress首页TTFB(Time To First Byte)从320ms降至85ms。
时区问题常被忽视。PHP默认时区是UTC,但中国开发者习惯Asia/Shanghai。如果应用中用date('Y-m-d H:i:s')生成日志,却没设时区,日志时间会比实际晚8小时,排查问题时极易误导。我们在php.ini中强制设定:
- name: Set PHP timezone lineinfile: path: /etc/php/7.2/apache2/php.ini regexp: '^;?date.timezone' line: 'date.timezone = Asia/Shanghai' state: present注意^;?正则——它能匹配date.timezone = ...或;date.timezone = ...(注释行),确保无论原配置是否被注释,都能正确生效。
扩展管理是另一个重点。Ubuntu 18.04的php7.2元包默认安装php7.2-common、php7.2-cli等,但php7.2-mysql、php7.2-curl、php7.2-gd等需单独安装。我们用apt:模块按需安装:
- name: Install required PHP extensions apt: name: "{{ item }}" state: present loop: - php7.2-mysql - php7.2-curl - php7.2-gd - php7.2-mbstring - php7.2-xml - php7.2-zip其中mbstring和xml是WordPress必备,zip用于插件自动更新。这里有个技巧:apt:模块的update_cache: yes参数应放在Playbook顶层,而非每个apt任务里,避免重复执行apt update拖慢速度。
4. 实操过程与核心环节实现
4.1 完整Playbook结构与文件组织
一个可立即运行的Ansible项目,其目录结构必须清晰、可移植。以下是我在Ubuntu 18.04 LAMP项目中采用的标准布局(基于Ansible Galaxy规范):
lamp-ubuntu1804/ ├── ansible.cfg # Ansible主配置,指定roles_path和inventory ├── inventory/ # 存放不同环境的inventory文件 │ ├── production # 生产环境主机列表 │ ├── staging # 预发布环境 │ └── dev # 开发环境(Vagrant) ├── group_vars/ # 主机组变量 │ ├── all.yml # 所有主机共用变量(如ansible_python_interpreter) │ ├── production.yml # 生产环境特有变量(如db_password加密值) │ └── dev.yml # 开发环境变量(如mysql_root_password: devpass) ├── roles/ # 角色目录 │ ├── apache/ # Apache配置角色 │ │ ├── tasks/main.yml # 主任务:安装、配置、启动 │ │ ├── handlers/main.yml # 处理器:重启Apache │ │ ├── templates/ # Jinja2模板文件 │ │ │ └── 000-default.conf.j2 │ │ └── vars/main.yml # 默认变量(如apache_port: 80) │ ├── mysql/ # MySQL角色(同上结构) │ ├── php/ # PHP角色(同上结构) │ └── lamp-app/ # 可选:部署示例应用(如phpinfo.php) ├── lamp-deploy.yml # 顶层Playbook,串联所有角色 └── README.md # 项目说明ansible.cfg内容精简实用:
[defaults] inventory = inventory/dev roles_path = roles remote_user = ubuntu host_key_checking = False deprecation_warnings = Falsehost_key_checking = False关闭SSH主机密钥检查,避免首次连接时交互式确认;deprecation_warnings = False屏蔽Ansible版本弃用警告,保持输出干净。
inventory/dev文件定义开发环境:
[dev] 192.168.33.10 [dev:vars] ansible_user = ubuntu ansible_ssh_private_key_file = ~/.vagrant/machines/default/virtualbox/private_key这里192.168.33.10是Vagrant虚拟机IP,private_key指向Vagrant生成的密钥。生产环境则换成真实的公网IP和对应的密钥路径。
group_vars/all.yml是全局变量中枢:
--- # Python解释器路径,适配Ubuntu 18.04 ansible_python_interpreter: /usr/bin/python3 # LAMP通用变量 apache_port: 80 mysql_root_password: "SecureRootPass123!" db_name: "lamp_demo" db_user: "lamp_user" db_password: "LampUserPass456!"所有角色均可直接引用{{ apache_port }}或{{ mysql_root_password }},无需在每个Role里重复定义。
4.2 Apache角色详解:从安装到虚拟主机配置
roles/apache/tasks/main.yml是整个Web服务的基石,共12个任务,按逻辑分三阶段执行:
第一阶段:基础安装与模块启用
- name: Install Apache package apt: name: apache2 state: present update_cache: yes - name: Enable required Apache modules command: a2enmod {{ item }} loop: - rewrite - headers - ssl args: executable: /bin/bash notify: Restart Apache - name: Disable default site command: a2dissite 000-default args: executable: /bin/bash notify: Restart Apachea2enmod和a2dissite必须用command:模块执行,因为Ansible没有原生的Apache模块管理模块(apache2_module在较新版本中才支持)。notify: Restart Apache将重启操作委托给handlers/main.yml,确保多次notify只触发一次重启,提升效率。
第二阶段:核心配置文件覆盖
- name: Copy Apache main configuration template: src: apache2.conf.j2 dest: /etc/apache2/apache2.conf owner: root group: root mode: '0644' notify: Restart Apache - name: Copy virtual host configuration template: src: 000-default.conf.j2 dest: /etc/apache2/sites-available/000-default.conf owner: root group: root mode: '0644' notify: Restart Apachetemplate:模块比copy:更强大,它支持Jinja2变量替换。000-default.conf.j2内容如下:
<VirtualHost *:{{ apache_port }}> ServerAdmin webmaster@localhost DocumentRoot /var/www/html <Directory /var/www/html> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>AllowOverride All启用.htaccess,这是WordPress等CMS重写规则的基础;Require all granted替代旧版Order allow,deny语法,适配Apache 2.4+。
第三阶段:服务状态管控
- name: Ensure Apache service is enabled and running service: name: apache2 state: started enabled: yes - name: Open firewall port for Apache ufw: rule: allow port: "{{ apache_port }}" proto: tcpufw:模块自动调用ufw allow 80,比command:更安全,且能处理ufw未启用的情况(自动启用)。
handlers/main.yml极简:
--- - name: Restart Apache service: name: apache2 state: restarted4.3 MySQL角色实战:安全初始化与数据库创建
roles/mysql/tasks/main.yml的核心是“安全初始化”,共9个任务,聚焦于消除MySQL 5.7的默认风险:
任务1:安装与初始启动
- name: Install MySQL server apt: name: mysql-server-5.7 state: present update_cache: yes - name: Start and enable MySQL service service: name: mysql state: started enabled: yes注意name: mysql-server-5.7精确指定版本,避免apt install mysql-server拉取更高版本(如5.7.33 vs 5.7.40),保证环境一致性。
任务2:root密码重置与认证插件切换
- name: Reset MySQL root password and switch to native password command: mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '{{ mysql_root_password }}'; FLUSH PRIVILEGES;" args: executable: /bin/bash become: yes ignore_errors: yesignore_errors: yes是关键容错设计,确保Playbook在首次和后续运行中均能成功。
任务3:MySQL配置文件精细化修改
- name: Configure MySQL bind address lineinfile: path: /etc/mysql/mysql.conf.d/mysqld.cnf regexp: '^bind-address' line: 'bind-address = 0.0.0.0' state: present notify: Restart MySQL - name: Configure MySQL SQL mode lineinfile: path: /etc/mysql/mysql.conf.d/mysqld.cnf regexp: '^sql_mode' line: 'sql_mode = "NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' state: present notify: Restart MySQLnotify: Restart MySQL对应handlers/main.yml中的service: name=mysql state=restarted。
任务4:创建应用数据库与用户
- name: Create application database mysql_db: name: "{{ db_name }}" state: present login_user: root login_password: "{{ mysql_root_password }}" - name: Create application database user mysql_user: name: "{{ db_user }}" password: "{{ db_password }}" priv: "{{ db_name }}.*:ALL" host: '%' state: present login_user: root login_password: "{{ mysql_root_password }}"mysql_db:和mysql_user:模块需community.mysql集合支持,务必提前安装。
4.4 PHP角色落地:扩展安装与INI文件定制
roles/php/tasks/main.yml的任务链设计强调“最小必要原则”,只安装必需扩展,避免臃肿:
任务1:PHP核心包安装
- name: Install PHP 7.2 core packages apt: name: - php7.2 - libapache2-mod-php7.2 - php7.2-cli state: presentlibapache2-mod-php7.2是Apache与PHP通信的桥梁,不可或缺。
任务2:关键扩展安装
- name: Install required PHP extensions apt: name: "{{ item }}" state: present loop: - php7.2-mysql - php7.2-curl - php7.2-gd - php7.2-mbstring - php7.2-xml - php7.2-zip notify: Restart Apachenotify: Restart Apache确保PHP模块加载生效。
任务3:php.ini深度定制
- name: Configure PHP memory limit lineinfile: path: /etc/php/7.2/apache2/php.ini regexp: '^memory_limit' line: 'memory_limit = 256M' state: present - name: Configure PHP upload limits lineinfile: path: /etc/php/7.2/apache2/php.ini regexp: '^upload_max_filesize' line: 'upload_max_filesize = 64M' state: present - name: Configure PHP error reporting lineinfile: path: /etc/php/7.2/apache2/php.ini regexp: '^display_errors' line: 'display_errors = Off' state: presentmemory_limit=256M满足WordPress插件需求;upload_max_filesize=64M支持大附件上传;display_errors=Off关闭错误显示,防止敏感信息泄露。
任务4:OPcache启用与调优
- name: Enable and configure OPcache lineinfile: path: /etc/php/7.2/apache2/php.ini regexp: '^{{ item.key }}' line: '{{ item.key }} = {{ item.value }}' state: present loop: - { key: 'opcache.enable', value: '1' } - { key: 'opcache.memory_consumption', value: '128' } - { key: 'opcache.max_accelerated_files', value: '4000' } - { key: 'opcache.revalidate_freq', value: '60' } notify: Restart ApacheOPcache参数经实测验证,在1GB内存的VPS上表现稳定。
4.5 顶层Playbook执行与验证流程
lamp-deploy.yml是整个项目的“总指挥”,内容简洁有力:
--- - name: Deploy LAMP stack on Ubuntu 18.04 hosts: all become: yes gather_facts: yes pre_tasks: - name: Update apt cache apt: update_cache: yes roles: - role: apache - role: mysql - role: php - role: lamp-app post_tasks: - name: Verify Apache is running uri: url: "http://localhost" status_code: 200 register: apache_check - name: Verify PHP info page uri: url: "http://localhost/phpinfo.php" status_code: 200 register: php_check - name: Display deployment summary debug: msg: | LAMP deployment completed successfully! - Apache: {{ apache_check.status }} - PHP info: {{ php_check.status }} - Target: {{ inventory_hostname }}pre_tasks:确保apt update只执行一次;post_tasks:用uri:模块进行HTTP健康检查,`