自动化运维-Ansible Role综合应用案例-基于LNMP部署wordpress 📅 2026/8/23 8:49:36 前言作为一名运维工程师相信大家都经历过这样的场景新项目启动需要在数台甚至数十台服务器上手动安装 Nginx、MySQL、PHP然后逐个配置、调优、测试。这个过程不仅耗时还极易出错——漏装一个 PHP 扩展、写错一个配置文件参数、权限设置不当都可能让服务直接“趴窝”。在微服务和多云架构时代这种“手工作坊”式的部署方式显然已经跟不上节奏。今天我就来和大家聊聊如何用 Ansible Role 来标准化、自动化地部署 LNMP 架构并基于LNMP部署wordpress。一、为什么选择 Ansible Role1.1 手动部署的痛点手动部署 LNMP 主要面临几个问题环境不一致开发、测试、生产环境配置各有差异出了问题难以复现配置漂移随着时间推移各服务器的配置逐渐偏离初始状态效率低下重复劳动一台台服务器手工操作回滚困难出了问题很难快速回退到稳定状态1.2 Ansible 的三大优势Ansible 之所以成为自动化运维的首选工具主要得益于它的三大核心特性无 Agent只需在控制节点安装 Ansible通过 SSH 管理所有主机无需在目标机器安装额外客户端声明式 YAML用人类可读的 YAML 描述目标状态而非编写冗长的过程式脚本幂等性同一个 Playbook 可以安全地重复执行如果目标状态已达成Ansible 会自动跳过1.3 为什么要用 Role如果说 Playbook 是 Ansible 的“脚本”那 Role 就是 Ansible 的“函数”。我可以预先构建出 nginx、mysql、php 等一系列的角色当需要部署 LNMP 时直接调用这些角色即可当需要部署 LAMP 时把 nginx 角色换成 apache 角色就行。角色就像代码里的函数一次编写到处复用。二、实验环境准备在开始之前先准备好实验环境角色主机名系统IP 地址控制节点ansible-controllerCentOS 7192.168.32.135目标节点ansible-node1CentOS 7192.168.32.132控制节点需要安装 Ansible目标节点只需确保 SSH 可用且配置好免密登录。三、拆分设计将 LNMP 拆分为多个 Role在实际生产中我们通常不会把 Nginx、MySQL、PHP 全部塞进一个 Role 里而是将它们拆分成独立的 Role这样更便于维护和复用。我建议的目录结构如下roles/ ├── nginx/ # Nginx 角色 ├── mysql/ # MySQL/MariaDB 角色 ├── php/ # PHP-FPM 角色 └── wordpress/ # 应用部署角色3.1 Nginx RoleNginx Role 的核心任务是安装 Nginx、下发配置模板、启动服务。tasks/main.yml示例---# tasks file for roles/nginx-name:install nginxyum:name:nginx-name:start nginxservice:name:nginxstate:startedenabled:yes-name:configure nginxtemplate:src:nginx.conf.j2dest:/etc/nginx/nginx.confnotify:reload nginxhandlers/main.yml示例---# handlers file for roles/nginx-name:reload nginxservice:name:nginxstate:reloaded通过notify机制当配置文件发生变化时自动触发 Nginx 重启。4.2 MySQL/MariaDB RoleMySQL Role 负责安装数据库、安全初始化、创建应用所需的数据库和用户。tasks/main.yml关键任务---# tasks file for roles/mysql-name:install PyMySQLyum:name:python2-PyMySQLstate:present-name:install MariaDByum:name:mariadb-serverstate:present-name:start mariadbsystemd:name:mariadbstate:startedenabled:yes-name:set password for mariadbmysql_user:name:rootpassword:{{ mariadb_password }}host_all:true#login_user: root#login_password: {{mariadb_password}}login_unix_socket:/var/lib/mysql/mysql.sockhost:{{ item }}state:presentloop:-localhost-127.0.0.1-::1-{{ansible_default_ipv4.address}}ignore_errors:yes-name:delete anonymous usermysql_user:name:host_all:yeslogin_user:rootlogin_password:{{mariadb_password}}state:absent-name:create wordpress databasemysql_db:name:{{ wordpress_db_name }}login_user:rootlogin_password:{{mariadb_password}}state:present-name:set privilegesmysql_user:name:{{ wordpress_user_name }}password:{{ wordpress_user_password }}host:localhostlogin_user:rootlogin_password:{{mariadb_password}}priv:{{ wordpress_db_name }}.*:ALLstate:present关于密码安全生产环境中的敏感信息如数据库密码建议使用ansible-vault加密存储。4.3 PHP-FPM RolePHP Role 负责安装 PHP 及相关扩展、配置 php-fpm。tasks/main.yml示例---# tasks file for roles/php-name:install php sourceshell:yum install-y http://rpms.remirepo.net/enterprise/remi-release-7.rpmyum-config-manager--enable remi-php74ignore_errors:yes-name:install php and extensionyum:name:-php-php-fpm-php-mysqlnd-php-gd-php-mbstring-php-xml-php-opcachestate:latest-name:update configuration of php-fpmtemplate:src:www.conf.j2dest:/etc/php-fpm.d/www.confnotify:restart php-fpm-name:start php-fpmsystemd:name:php-fpmstate:startedenabled:yes4.4 wordpress Rolewordpress role 负责wordpress下载、解压与部署。---# tasks file for roles/wordpress# roles/wordpress/tasks/main.yml# 1. 创建安装目录如果不存在-name:创建 WordPress 安装目录file:path:{{ wordpress_install_dir }}state:directoryowner:nginxgroup:nginxmode:0755# 2. 下载 WordPress 安装包-name:下载 WordPressget_url:url:{{ wordpress_download_url }}dest:/tmp/wordpress-{{ wordpress_version }}.tar.gzmode:0644# 可以添加校验和提高安全性# checksum: sha256:https://wordpress.org/wordpress-{{ wordpress_version }}.tar.gz.sha256# 3. 解压 WordPress 到安装目录-name:解压 WordPressunarchive:src:/tmp/wordpress-{{ wordpress_version }}.tar.gzdest:{{ wordpress_install_dir }}remote_src:yes# 关键声明源文件在目标机器上owner:nginxgroup:nginxcreates:{{ wordpress_install_dir }}/wp-config-sample.php# 幂等性保障# 4. (可选) 清理下载的压缩包-name:删除临时压缩包file:path:/tmp/wordpress-{{ wordpress_version }}.tar.gzstate:absent-name:copy wordpresscopy:src:/usr/share/nginx/html/wordpressdest:/data/owner:nginxgroup:nginxremote_src:true五、整合编写 Playbook 调用 Roles有了各个独立的 Role 之后编写一个 Playbook 来调用它们就非常简单了。创建deploy_lnmp.yml----hosts:lnmproles:-nginx-mysql-php-wordpress执行部署$ ansible-playbook deploy_lnmp.yml一条命令全自动完成 基于LNMP在一台centos7服务器上 搭建wordpress。完整的部署代码可使用如下命令克隆进行部署git clone https://gitee.com/zhanglj1991/ansible_role_lnmp.git。部署时只需要修改资源清单inventory中的ip地址与密码即可部署。[rootansible-controller ansible_role_lnmp]# ansible-playbook deploy_lnmp.yamlPLAY[lnmp]***************************************************************************************************************************** TASK[Gathering Facts]****************************************************************************************************************** ok:[192.168.32.136]TASK[nginx:installnginx]************************************************************************************************************ changed:[192.168.32.136]TASK[nginx:start nginx]************************************************************************************************************** changed:[192.168.32.136]TASK[nginx:configure nginx]********************************************************************************************************** changed:[192.168.32.136]TASK[mysql:installPyMySQL]********************************************************************************************************** changed:[192.168.32.136]TASK[mysql:installMariaDB]********************************************************************************************************** changed:[192.168.32.136]TASK[mysql:start mariadb]************************************************************************************************************ changed:[192.168.32.136]TASK[mysql:setpasswordformariadb]************************************************************************************************* changed:[192.168.32.136](itemlocalhost)failed:[192.168.32.136](item127.0.0.1){ansible_loop_var:item,changed:false,item:127.0.0.1,msg:unable to connect to database, check login_user and login_password are correct or /root/.my.cnf has the credentials. Exception message: (1045, u\Access denied for user rootlocalhost (using password: NO)\)}failed:[192.168.32.136](item::1){ansible_loop_var:item,changed:false,item:::1,msg:unable to connect to database, check login_user and login_password are correct or /root/.my.cnf has the credentials. Exception message: (1045, u\Access denied for user rootlocalhost (using password: NO)\)}failed:[192.168.32.136](item192.168.32.136){ansible_loop_var:item,changed:false,item:192.168.32.136,msg:unable to connect to database, check login_user and login_password are correct or /root/.my.cnf has the credentials. Exception message: (1045, u\Access denied for user rootlocalhost (using password: NO)\)}...ignoring TASK[mysql:delete anonymous user]**************************************************************************************************** changed:[192.168.32.136]TASK[mysql:create wordpress database]************************************************************************************************ changed:[192.168.32.136]TASK[mysql:setprivileges]*********************************************************************************************************** changed:[192.168.32.136]TASK[php:installphp source]********************************************************************************************************* changed:[192.168.32.136]TASK[php:installphp and extension]************************************************************************************************** changed:[192.168.32.136]TASK[php:update configuration of php-fpm]******************************************************************************************** changed:[192.168.32.136]TASK[php:start php-fpm]************************************************************************************************************** changed:[192.168.32.136]TASK[wordpress:创建 WordPress 安装目录]********************************************************************************************** changed:[192.168.32.136]TASK[wordpress:下载 WordPress]******************************************************************************************************* changed:[192.168.32.136]TASK[wordpress:解压 WordPress]******************************************************************************************************* changed:[192.168.32.136]TASK[wordpress:删除临时压缩包]******************************************************************************************************* changed:[192.168.32.136]TASK[wordpress:copy wordpress]******************************************************************************************************* changed:[192.168.32.136]RUNNING HANDLER[nginx:reload nginx]************************************************************************************************** changed:[192.168.32.136]RUNNING HANDLER[php:restart php-fpm]************************************************************************************************* changed:[192.168.32.136]PLAY RECAP ******************************************************************************************************************************192.168.32.136:ok22changed21unreachable0failed0skipped0rescued0ignored1正常部署完成后浏览器访问http://192.168.32.168/即可访问新部署的wordpress.如果报错如下可先将firewalld和selinux关闭测试是否可以访问。[rootlocalhost html]# systemctl status firewalld● firewalld.service - firewalld - dynamic firewall daemon Loaded: loaded(/usr/lib/systemd/system/firewalld.service;enabled;vendor preset: enabled)Active: active(running)since Sat2026-08-2215:08:03 CST;11s ago Docs: man:firewalld(1)Main PID:5242(firewalld)CGroup: /system.slice/firewalld.service └─5242 /usr/bin/python2-Es/usr/sbin/firewalld--nofork--nopidAug2215:08:03 localhost.localdomain systemd[1]: Starting firewalld - dynamic firewall daemon... Aug2215:08:03 localhost.localdomain systemd[1]: Started firewalld - dynamic firewall daemon. Aug2215:08:03 localhost.localdomain firewalld[5242]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure con...t now. Hint: Some lines were ellipsized, use-lto showinfull.[rootlocalhost html]# systemctl stop firewalld[rootlocalhost html]# getenforceEnforcing[rootlocalhost html]# setenforce 0[rootlocalhost html]# getenforcePermissive[rootlocalhost html]#然后只需按下图设置一下数据库、用户名密码等即可登录wordpress.至此完成了基于LNMP使用ansible role实现一键部署wordpress。六、变量管理的最佳实践在 Role 中变量管理是一个需要特别注意的环节。defaults/main.yml存放默认值优先级最低适合给角色设置“出厂设置”vars/main.yml存放角色内部使用的变量优先级高于 defaultsgroup_vars/按主机组存放变量适合管理环境差异host_vars/按单台主机存放变量优先级最高建议将 Nginx 的虚拟主机配置变量、PHP 的扩展配置变量等分类存放这样后期维护会更清晰。七、生产环境部署的几点建议7.1 关于执行顺序LNMP 的部署顺序是有讲究的。一般来说应该按照Web 服务器 → 数据库→ PHP → 应用的顺序执行。7.2 关于幂等性充分利用 Ansible 的幂等性特性。同一个 Playbook 可以反复执行不会产生意外结果。比如Nginx 已经安装的情况下再次执行 PlaybookAnsible 会检测到并跳过安装步骤。7.3 关于配置模板使用 Jinja2 模板可以让配置文件更加灵活。比如 Nginx 的虚拟主机配置可以根据不同的域名、根目录动态生成server { listen 80; server_name {{ domain_name }}; root {{ document_root }}; index index.php index.html; location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }7.4 关于安全加固生产环境部署时别忘了加入安全加固步骤数据库安全初始化删除匿名用户、删除 test 数据库配置防火墙规则使用 ansible-vault 加密敏感信息遵循最小权限原则配置目录权限八、总结通过 Ansible Role 部署 LNMP本质上是将“部署”这件事从手动操作变成了代码编排。这样做的好处是显而易见的标准化每次部署的结果都是一致的消除了人为差异可复用写好的 Role 可以在不同项目、不同环境中反复使用可追溯所有配置变更都记录在代码仓库中出了问题可以快速定位高效率一条命令完成全部部署相比手动操作可节省大量的时间成本当然本文只是一个入门级的综合实践。在实际生产环境中你可能还需要考虑负载均衡、高可用、监控告警、CI/CD 集成等更复杂的需求。但无论如何从 Role 开始迈出自动化运维的第一步总是一个不错的选择。希望这篇文章能对你有所帮助。如果有任何问题欢迎在评论区交流讨论