当前位置: 首页 > news >正文

Ansible 自动化运维实践笔记:Jinja2 模板、LNMP+WordPress 部署与大项目管理

部署文件到受管主机

使用JINJA2模板部署文件

示例1:部署web服务器,主页内容显示为Welcome to HOSTNAME。HOSTNAME为受管主机完全主机名。

playbook内容如下:

 [yuxb@controller web 09:39:37]$ cat playbook.yml ---- name: Enable intranet serviceshosts: node1tasks:- name: ensure latest version of httpd yum:name: httpdstate: latest​- name: test html page is installedtemplate:src: index.html.j2dest: /var/www/html/index.html- name: httpd enabled and runningservice:name: httpdenabled: truestate: restarted...

index.html.j2 内容如下:

 [yuxb@controller web 09:39:40]$ vim index.html.j2[yuxb@controller web 09:40:55]$ cat index.html.j2 Welcome to {{ ansible_fqdn }}​

执行:

 [yuxb@controller web 09:41:22]$ ansible-playbook playbook.yml ​PLAY [Enable intranet services] *********************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node1]​TASK [ensure latest version of httpd] ***************************************************************ok: [node1]​TASK [test html page is installed] ******************************************************************changed: [node1]​TASK [httpd enabled and running] ********************************************************************changed: [node1]​PLAY RECAP ******************************************************************************************node1                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​

示例2:推送 ssh 服务配置文件。

sshd_config.j2内容:

 [yuxb@controller web 09:50:47]$ cat sshd_config.j2 # {{ ansible_managed }}# DO NOT MAKE LOCAL MODIFICATIONS TO THIS FILE AS THEY WILL BE LOSTPort {{ ssh_port }}ListenAddress {{ ansible_facts['default_ipv4']['address'] }}​HostKey /etc/ssh/ssh_host_rsa_keyHostKey /etc/ssh/ssh_host_ecdsa_keyHostKey /etc/ssh/ssh_host_ed25519_keySyslogFacility AUTHPRIVPermitRootLogin  {{ root_allowed }}AllowGroups {{ groups_allowed }}​AuthorizedKeysFile      /etc/.rht_authorized_keys .ssh/authorized_keysPasswordAuthentication {{ passwords_allowed }}​

playbook

 [yuxb@controller web 09:41:00]$ vim playbook.yml [yuxb@controller web 09:50:07]$ cat playbook.yml ---- name: config sshd servicehosts: node1vars:ssh_port: 1022root_allowed: "yes"groups_allowed: wheelpasswords_allowed: "yes"ansible_managed: "Ansible managed"tasks:- name: config sshd servicetemplate:src: sshd_config.j2dest: /root/sshd_config​# 执行[yuxb@controller web 09:51:00]$ ansible-playbook playbook.yml ​PLAY [config sshd service] **************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node1]​TASK [config sshd service] **************************************************************************changed: [node1]​PLAY RECAP ******************************************************************************************node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​

 [yuxb@controller web 10:32:15]$ cat playbook.yml ---- name: 部署web服务器hosts: node1tasks:- name: 安装软件包 httpdyum: name: httpdstate: latest- name: 启动并启用 httpd 服务service:name: httpdstate: started- name: 准备 web 服务器主页内容copy:src: index.htmldest: /var/www/html/index.html- name: 启动并启用 firewalld 服务service:name: firewalldstate: started- name: 配置防火墙 放行 httpfirewalld:service: httppermanent: yesstate: enabled​

Jinja2 模板语法

for 语句

Jinja2使用for语句来提供循环功能。

示例1:

 [yuxb@controller web 10:41:56]$ vim playbook.yml [yuxb@controller web 10:46:24]$ cat playbook.yml ---- name: test templatehosts: node1vars:users:- tom- jack- snoopy- lucy​tasks:- name: test templatetemplate:src: testfile.j2dest: /tmp/testfile​

testfile.j2内容

 [yuxb@controller web 10:45:16]$ cat testfile.j2 {% for user in users %}username is {{ user }}{% endfor %}​

执行并验证

 # 执行[yuxb@controller web 10:45:19]$ ansible-playbook playbook.yml ​PLAY [test template] *******************************************************************************​TASK [Gathering Facts] *****************************************************************************ok: [node1]​TASK [test template] *******************************************************************************changed: [node1]​PLAY RECAP *****************************************************************************************node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​# 验证[yuxb@node1 ~ 10:33:13]$ cat /tmp/testfile tomjacksnoopylucy​

LNMP + WordPress 自动化部署实验

一、实验目标

通过 Ansible 自动化工具,在目标服务器(lnmp 主机组)上搭建 LNMP 架构(Linux 系统 + Nginx 网页服务器 + MariaDB 数据库 + PHP 脚本语言),并部署 WordPress 博客系统,最终实现通过浏览器访问博客。

二、前置知识说明

术语 / 工具作用说明
Ansible自动化运维工具,通过 “剧本(YAML 文件)” 批量执行命令,无需在目标机装客户端
LNMPWeb 服务架构,Nginx 负责接收用户请求,PHP 处理动态代码,MariaDB 存储数据
MariaDB开源数据库(MySQL 分支),用于存储 WordPress 的文章、用户等数据
Nginx轻量级网页服务器,负责转发请求、展示静态页面、对接 PHP
PHP-FPMPHP 的进程管理器,处理 Nginx 转发的 PHP 动态请求
WordPress开源博客系统,需依赖 LNMP 架构运行
Ansible VaultAnsible 自带的加密工具,用于保护数据库密码等敏感信息

三、实验环境准备

3.1 环境说明

角色主机名系统核心软件IP 地址(示例)
控制节点(执行命令)controllerCentOS 7Ansible、LNMP 配置文件10.1.8.10(示例)
目标节点(部署服务)node1(属于 lnmp 组)CentOS 7无(Ansible 自动安装软件)10.1.8.11(示例)
客户端(访问博客)个人电脑(Windows)Windows 10/11浏览器-

3.2 控制节点前置检查

controller 主机上执行以下命令,确认 Ansible 已安装且能连接目标节点:

 # 1. 检查Ansible是否安装(出现版本号即正常)[yuxb@controller ~]$ ansible --version# 2. 检查能否连通目标节点(lnmp是主机组名,从inventory文件读取)[yuxb@controller ~]$ ansible lnmp -m ping# 若返回“SUCCESS”,说明控制节点与目标节点连通正常;若失败,需先解决SSH登录问题

四、实验步骤

阶段 1:初始化控制节点工作目录(存放配置文件)

4.1.1 复制并进入工作目录(避免干扰原有文件)
 [yuxb@controller ~ 11:21:36]$ cp -r web lnmp
  • 这步做什么?:把原有 web 目录复制一份,命名为 lnmp,作为本次实验的 “工作目录”(所有配置文件都放在这里,不影响其他文件)。

  • 为什么这么做?:工作目录独立,后续操作只在 lnmp 目录内进行,方便管理和回溯。

 [yuxb@controller ~ 13:49:03]$ cd lnmp/
  • 这步做什么?:进入 lnmp 工作目录,后续所有命令都在这个目录下执行。

4.1.2 清空工作目录(删除原有冗余文件)
 [yuxb@controller lnmp 13:49:15]$ rm -rf *
  • 这步做什么?:删除 lnmp 目录里所有原有文件(从 web 复制过来的旧文件)。

  • 为什么这么做?:避免旧文件(如旧配置、旧脚本)干扰本次实验,保证环境 “干净”。

4.1.3 拷贝 Ansible 核心配置文件(让 Ansible 知道怎么工作)
 [yuxb@controller lnmp 13:49:44]$ cp ../web/ansible.cfg .[yuxb@controller lnmp 13:49:46]$ cp ../web/inventory .
  • 这步做什么?

    :从上级目录web拷贝两个关键文件到当前lnmp目录:

    1. ansible.cfg:Ansible 的主配置文件(告诉 Ansible 用哪个用户登录目标机、敏感文件怎么解密等);

    2. inventory:主机清单文件(告诉 Ansible 目标节点是哪些,比如 lnmp 组包含 node1)。

  • 为什么这么做?:这两个文件是 Ansible 运行的基础,必须放在工作目录,否则 Ansible 不知道 “操作哪个机器”“用什么身份操作”。

阶段 2:准备变量文件(存储配置,保护敏感信息)

4.2.1 创建变量目录(Ansible 规定的目录结构)
 [yuxb@controller lnmp 14:03:33]$ mkdir host_vars[yuxb@controller lnmp 14:03:57]$ mkdir host_vars/lnmp
  • 这步做什么?

    :创建host_vars/lnmp目录:

    • host_vars:是 Ansible 约定的 “主机变量目录”,专门存放不同主机 / 主机组的配置;

    • host_vars/lnmp:专门存放 lnmp 主机组(目标节点)的变量(比如数据库密码、用户名)。

  • 为什么这么做?:变量和剧本分离,后续修改配置(如改密码)不用动核心剧本,更灵活。

4.2.2 编写敏感变量文件(存储密码等重要信息)
 [yuxb@controller lnmp 14:04:02]$ vim host_vars/lnmp/vaults.yml
  • 这步做什么?:用 vim 编辑器创建 vaults.yml 文件,内容如下(直接复制粘贴):

 mysql_root_password: 123app_user: wordpressapp_password: 123app_host: '%'app_priv: '*.*:ALL'
  • 每个配置项是什么意思?:

    配置项含义作用
    mysql_root_password: 123MariaDB 数据库的 root 密码(最高权限密码)用于初始化数据库、创建用户和库
    app_user: wordpress给 WordPress 用的数据库用户名WordPress 只能用这个用户操作数据库,避免用 root 提权风险
    app_password: 123WordPress 数据库用户的密码验证 WordPress 对数据库的访问权限
    app_host: '%'允许该用户从 “任意主机” 连接数据库这里是本地部署,% 表示目标机自身可以访问
    app_priv: '*.*:ALL'该用户拥有 “所有数据库、所有权限”让 WordPress 能创建表、插入数据(实际生产可缩小权限,实验简化用 ALL)
  • 为什么要单独放这个文件?:这些是敏感信息(密码),后续要加密保护,不能明文暴露。

4.2.3 编写普通变量文件(存储非敏感配置)
 [yuxb@controller lnmp 14:10:23]$ vim host_vars/lnmp/vars.yml
  • 这步做什么?:用 vim 创建 vars.yml 文件,内容如下:

 db_name: webapp
  • 配置项含义db_name: webapp 表示给 WordPress 专门创建的数据库名是 webapp

  • 为什么和敏感变量分开?:数据库名不是敏感信息,不用加密,单独存放更清晰。

4.2.4 加密敏感变量文件(保护密码不泄露)
 [yuxb@controller lnmp 15:03:40]$ ansible-vault encrypt host_vars/lnmp/vaults.ymlNew Vault password: Confirm New Vault password: Encryption successful
  • 这步做什么?:

    用 Ansible Vault 加密1vaults.yml文件:

    1. 输入 “加密密码”(实验用 123,生产环境用复杂密码);

    2. 再次确认密码;

    3. 提示 “Encryption successful” 表示加密成功。

  • 为什么要加密?:如果不加密,vaults.yml 里的密码是明文,别人看到就能登录数据库,不安全。

  • 加密后怎么用?:后续 Ansible 执行剧本时,需要解密密码,所以要存一个 “解密密码文件”。

4.2.5 创建解密密码文件(避免每次输入密码)
 [yuxb@controller lnmp 15:04:53]$ echo 123 > secret.txt
  • 这步做什么?:把 “加密密码(123)” 写入 secret.txt 文件。

  • 为什么这么做?:如果不存这个文件,每次执行 Ansible 剧本都要手动输入解密密码,麻烦;存了之后,Ansible 会自动从这个文件读密码解密。

4.2.6 验证加密文件能否正常解密(确保后续能用)
 [yuxb@controller lnmp 15:06:06]$ ansible-vault view host_vars/lnmp/vaults.yml
  • 这步做什么?:查看加密后的 vaults.yml 内容(验证解密是否正常)。

  • 正常结果:会显示 vaults.yml 的原始内容(和 4.2.2 里写的一致),说明解密成功;如果失败,检查 secret.txt 的密码是否和加密时一致。

阶段 3:准备核心配置文件(Ansible 剧本、Nginx/PHP 配置)

4.3.1 编写 Ansible 主配置文件(告诉 Ansible 怎么执行命令)
 [yuxb@controller lnmp 16:25:31]$ vim ansible.cfg
  • 这步做什么?:用 vim 编辑 ansible.cfg 文件,内容如下(直接复制,注释已解释每一行):

 [defaults]inventory = ./inventory          # 告诉Ansible:主机清单文件在当前目录的inventory里remote_user = yuxb               # 告诉Ansible:用“yuxb”用户登录目标节点(需提前配置该用户sudo免密)vault_password_file=./secret.txt # 告诉Ansible:解密Vault文件的密码在当前目录的secret.txt里#vault_password_file = ./password-for-vault  # 注释:备用密码文件路径(不用管)#ask_pass = True                  # 注释:是否每次登录目标机都问SSH密码(实验已配置免密,所以注释)#module_name = command            # 注释:Ansible默认执行的模块(不用改)#private_key_file = /opt/id_rsa   # 注释:如果用SSH密钥登录,填密钥路径(实验用密码免密,注释)#host_key_checking = False        # 注释:是否检查目标机的SSH密钥(新手可取消注释,避免首次登录弹确认)​[privilege_escalation]become=True          # 告诉Ansible:执行命令时需要提升权限(即sudo)become_method=sudo   # 告诉Ansible:提升权限的方式是sudobecome_user=root     # 告诉Ansible:提升权限后用root用户执行命令become_ask_pass=False # 告诉Ansible:sudo时不用问密码(需提前在目标机配置yuxb用户sudo免密)
  • 关键注意点:目标节点必须配置 yuxb 用户的 sudo 免密(否则 Ansible 执行安装命令会失败),配置方法:在目标机 node1 上执行 visudo,添加 yuxb ALL=(ALL) NOPASSWD: ALL

4.3.2 编写 PHP 配置文件(让 Nginx 能处理 PHP 请求)
 [yuxb@controller lnmp 14:11:18]$ vim php.conf
  • 这步做什么?:用 vim 创建 php.conf 文件,内容如下(Nginx 解析 PHP 的规则):

 location ~ \.php$ {try_files $uri =404;fastcgi_pass 127.0.0.1:9000;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params;}
  • 每一行是什么意思?:

    1. location ~ \.php$:匹配所有以 .php 结尾的请求(比如 index.php);

    2. try_files $uri =404:检查请求的 PHP 文件是否存在,不存在就返回 404 错误(避免恶意请求);

    3. fastcgi_pass 127.0.0.1:9000:把 PHP 请求转发到本地 9000 端口(PHP-FPM 默认端口);

    4. fastcgi_index index.php:如果请求目录(比如 http://blog.yuxb.cloud/),默认找 index.php

    5. fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name:告诉 PHP-FPM 脚本文件的实际路径(比如 /usr/share/nginx/html/index.php);

    6. include fastcgi_params:加载 FastCGI 的通用参数(比如请求方法、用户 IP 等)。

  • 为什么要这个文件?:Nginx 本身不能处理 PHP 动态代码,必须通过这个配置把 PHP 请求转发给 PHP-FPM 处理。

4.3.3 编写 WordPress 虚拟主机模板(让 Nginx 识别博客域名)
 [yuxb@controller lnmp 16:25:38]$ vim vhost-wordpress.conf.j2
  • 这步做什么?:用 vim 创建 vhost-wordpress.conf.j2 模板文件(Jinja2 格式,Ansible 会自动替换变量),内容如下:

 server {listen       80;server_name  {{ blog_vhost }};root         /usr/share/nginx/html/{{ blog_vhost }}/wordpress;index index.php;​include /etc/nginx/default.d/*.conf;access_log  /var/log/nginx/access-{{ blog_vhost }}.log;error_log /var/log/nginx/error-{{ blog_vhost }}.log;}
  • 变量和配置项解释:

    内容含义作用
    {{ blog_vhost }}模板变量,后续会被替换为 blog.yuxb.cloud(博客域名)避免硬编码,方便修改域名
    listen 80Nginx 监听 80 端口(HTTP 协议默认端口)用户访问域名时,默认走 80 端口
    server_name {{ blog_vhost }}绑定域名,Nginx 收到该域名的请求就用这个配置区分不同网站(比如一个服务器跑多个博客)
    root /usr/share/nginx/html/{{ blog_vhost }}/wordpressWordPress 代码存放的根目录Nginx 从这个目录找博客的文件
    index index.php默认首页文件,优先加载 index.php用户访问域名时,自动打开 index.php
    access_log/error_log博客的访问日志和错误日志路径后续排查问题(比如访问失败)时看日志
  • 为什么用模板(.j2 后缀)?:如果后续要改域名,只需改变量,不用改配置文件,更灵活。

4.3.4 本地安装 Nginx(仅为编辑模板,非必须但建议做)
 [yuxb@controller lnmp 14:34:30]$ sudo yum install -y nginx
  • 这步做什么?:在控制节点 controller 上安装 Nginx。

  • 为什么这么做?:不是必须的,但安装后可以本地测试 vhost-wordpress.conf.j2 模板是否正确(避免目标机部署后才发现配置错误),实验简化可跳过。

阶段 4:编写 Ansible 核心剧本(自动化部署的 “大脑”)

4.4.1 创建并编辑部署剧本
 [yuxb@controller lnmp 15:50:25]$ vim deploy_lnmp.yml
  • 这步做什么?:用 vim 创建 deploy_lnmp.yml 剧本文件(Ansible 执行的核心,按 “Play” 分阶段部署),内容如下(每个步骤都有详细注释):

 ---# ==================== Play 1:部署MariaDB数据库(LNMP的“M”)====================- name: deploy mariadb  # Play名称(描述这个阶段做什么)hosts: lnmp           # 目标主机组(从inventory文件读取,即node1)tasks:                # 该阶段要执行的具体任务(按顺序执行)​# 任务1:安装MariaDB服务和PHP连接数据库的依赖- name: install mariadb-serveryum:              # Ansible的yum模块(用于在CentOS上安装软件)name:           # 要安装的软件列表- mariadb-server  # MariaDB数据库服务(核心)- python2-PyMySQL # Python2连接MySQL的模块(Ansible操作MariaDB需要)state: present  # 确保软件“已安装”(如果没装就装,装了就不操作)​# 任务2:启动MariaDB服务,并设置开机自启- name: enable and start mariadbservice:          # Ansible的service模块(管理系统服务)name: mariadb   # 服务名(MariaDB的服务名就是mariadb)enabled: yes    # 开机自启(yes=启用,no=禁用)state: started  # 服务状态(started=启动,stopped=停止,restarted=重启)​# 任务3:首次设置MariaDB的root本地密码(忽略首次执行的错误)- name: set root@localhost passwordshell: mysqladmin password {{ mysql_root_password }}  # 执行系统命令设置密码ignore_errors: yes  # 忽略错误(首次执行时MariaDB默认无密码,执行该命令可能报错,所以忽略)​# 任务4:为不同主机地址(本机域名、回环地址)设置root密码- name: set root passwordmysql_user:        # Ansible的mysql_user模块(管理数据库用户)name: root       # 要操作的数据库用户(root)password: "{{ mysql_root_password }}"  # 密码(从vaults.yml读取,即123)host: "{{ item }}"                     # 允许登录的主机地址(循环变量)state: present                         # 确保用户“存在”login_user: root                       # 登录数据库的用户(用root登录)login_password: "{{ mysql_root_password }}"  # 登录密码(root密码)with_items:        # 循环执行(为3个地址分别设置密码)- "{{ ansible_fqdn }}"  # 目标机的完整域名(比如node1.yuxb.cloud)- 127.0.0.1             # IPv4回环地址(本机访问)- ::1                   # IPv6回环地址(本机访问)​# 任务5:删除数据库匿名用户(提升安全性,避免别人空密码登录)- name: delete user anonymousmysql_user:name: ""         # 匿名用户的名称是空字符串(MariaDB默认有匿名用户)host_all: yes    # 所有主机上的匿名用户都删除state: absent    # 确保用户“不存在”(即删除)login_user: rootlogin_password: "{{ mysql_root_password }}"#login_unix_socket: /var/lib/mysql/mysql.sock  # 注释:可选的socket登录方式(不用管)​# 任务6:删除默认的test数据库(提升安全性,test库默认任何人可访问)- name: delete database testmysql_db:          # Ansible的mysql_db模块(管理数据库)name: test       # 要删除的数据库名(test是默认测试库)state: absent    # 确保数据库“不存在”(即删除)login_user: rootlogin_password: "{{ mysql_root_password }}"​​# ==================== Play 2:为WordPress准备数据库(创建专用用户和库)====================- name: prepare db for webapp  # Play名称(为Web应用准备数据库)hosts: lnmptasks:​# 任务1:创建WordPress专用的数据库用户(避免用root提权)- name: create user {{ user }}mysql_user:name: "{{ app_user }}"          # 用户名(从vaults.yml读取,即wordpress)password: "{{ app_password }}"  # 密码(从vaults.yml读取,即123)host: "{{ app_host }}"          # 允许登录的主机(从vaults.yml读取,即%)priv: "{{ app_priv }}"          # 权限(从vaults.yml读取,即*.*:ALL)state: presentlogin_user: rootlogin_password: "{{ mysql_root_password }}"​# 任务2:创建WordPress专用的数据库(webapp)- name: create database db_namemysql_db:name: "{{ db_name }}"  # 数据库名(从vars.yml读取,即webapp)state: present         # 确保数据库“存在”(即创建)login_user: rootlogin_password: "{{ mysql_root_password }}"​​# ==================== Play 3:部署Nginx服务(LNMP的“N”)====================- name: deploy web server  # Play名称(部署网页服务器)hosts: lnmptasks:​# 任务1:安装Nginx服务- name: install nginxyum:name: nginxstate: present​# 任务2:启动Nginx服务,并设置开机自启- name: enable and start nginxservice:name: nginxenabled: yesstate: started​# 任务3:创建Nginx测试页面(验证Nginx是否正常运行)- name: prepare test file for web servercopy:  # Ansible的copy模块(复制内容到文件)content: hello world from nginx  # 文件内容(测试文本)dest: /usr/share/nginx/html/index.html  # 目标文件路径(Nginx默认首页路径)​​# ==================== Play 4:部署PHP服务(LNMP的“P”)====================- name: php  # Play名称(部署PHP)hosts: lnmptasks:​# 任务1:安装PHP及依赖(PHP-FPM、MySQL连接驱动)- name: install phpyum:name: php,php-fpm,php-mysqlnd  # 要安装的软件(用逗号分隔)state: present# 软件说明:# php:PHP核心# php-fpm:PHP进程管理器(处理Nginx转发的PHP请求)# php-mysqlnd:PHP连接MySQL/MariaDB的驱动(WordPress需要)​# 任务2:修改PHP-FPM的运行用户和组为nginx(权限一致)- name: modify running user for phplineinfile:  # Ansible的lineinfile模块(修改文件中的某一行)path: /etc/php-fpm.d/www.conf  # 要修改的文件(PHP-FPM的用户配置文件)regexp: "{{ item}} = "         # 匹配行的正则(找“user = ”或“group = ”)line: "{{ item }} = nginx"     # 替换后的内容(把用户/组改成nginx)loop:  # 循环修改两个配置项(user和group)- user- group# 为什么改?Nginx默认用nginx用户运行,PHP-FPM也用nginx用户,避免文件权限问题(比如PHP写文件时权限不够)​# 任务3:启动PHP-FPM服务,并设置开机自启(重启确保配置生效)- name: enable and start php-fpm.serviceservice:name: php-fpmenabled: yesstate: restarted  # 用restarted(重启),因为刚改了配置,需要生效​# 任务4:把本地的php.conf复制到目标机(让Nginx能解析PHP)- name: config php for nginxcopy:src: php.conf  # 本地的php.conf文件(3.2步创建的)dest: /etc/nginx/default.d/php.conf  # 目标机路径(Nginx会自动加载这个目录的配置)​# 任务5:重启Nginx(加载PHP配置,让Nginx能处理PHP请求)- name: restart nginxservice:name: nginxstate: restarted​​# ==================== Play 5:部署WordPress博客系统====================- name: deploy web app  # Play名称(部署Web应用,即WordPress)hosts: lnmpvars:  # 该Play的局部变量(只在这个Play里生效)blog_vhost: blog.yuxb.cloud  # 博客的域名(后续会替换模板里的{{ blog_vhost }})tasks:​# 任务1:用模板生成WordPress的Nginx虚拟主机配置- name: prepare vhost for wordpresstemplate:  # Ansible的template模块(处理Jinja2模板,替换变量)src: vhost-wordpress.conf.j2  # 本地模板文件(3.3步创建的)dest: /etc/nginx/conf.d/vhost-wordpress.conf  # 目标机路径(Nginx虚拟主机配置目录)​# 任务2:创建WordPress的网站根目录(按域名命名,方便管理)- name: create /usr/share/nginx/html/{{ blog_vhost }}file:  # Ansible的file模块(管理文件/目录)path: /usr/share/nginx/html/{{ blog_vhost }}  # 目录路径(替换变量后是/blog.yuxb.cloud)state: directory  # 确保目录“存在”(即创建)​# 任务3:解压WordPress压缩包到网站根目录- name: Unarchive a wordpress file unarchive:  # Ansible的unarchive模块(解压压缩包)src: wordpress-4.9.4-zh_CN.zip  # 本地WordPress压缩包(需提前下载到lnmp目录)dest: /usr/share/nginx/html/{{ blog_vhost }}/  # 解压目标路径(到博客目录下)owner: nginx  # 解压后文件的所有者(nginx,和Nginx/PHP用户一致)group: nginx  # 解压后文件的所属组(nginx)# 注意:必须提前把wordpress-4.9.4-zh_CN.zip下载到控制节点的lnmp目录,否则会失败!​# 任务4:重启Nginx(加载虚拟主机配置,让Nginx识别博客域名)- name: restart nginxservice:name: nginxstate: restarted
  • 剧本关键注意点:

    1. 必须提前下载 wordpress-4.9.4-zh_CN.zip 到控制节点的 lnmp 目录(下载地址:https://wordpress.org/wordpress-4.9.4-zh_CN.zip);

    2. 剧本按 “数据库→Web 服务器→PHP→博客” 顺序执行,不能乱序(比如先装 PHP 再装数据库,PHP 连接数据库会失败)。

阶段 5:执行自动化部署(跑剧本,让 Ansible 干活)

5.1.1 执行 Ansible 剧本
 [yuxb@controller lnmp 15:18:48]$ ansible-playbook deploy_lnmp.yml
  • 这步做什么?:执行 deploy_lnmp.yml 剧本,Ansible 会自动连接目标节点 node1,按剧本顺序执行所有任务。

  • 执行过程说明:

    • 输出中会显示每个任务的状态(ok= 已执行过,changed= 刚执行完,failed= 失败);

    • 如果某个任务失败(比如软件安装失败),先看错误信息(通常是网络问题或权限问题),解决后重新执行命令即可(Ansible 支持幂等性,重复执行不会有问题)。

5.1.2 关闭目标机冲突服务(避免端口被占)
 # 注意:这步要在目标节点node1上执行,不是控制节点![root@node1 ~ 15:32:00]# systemctl disable httpd --now
  • 这步做什么?:

    1. systemctl disable httpd:禁用 httpd 服务(CentOS 默认的网页服务器),避免开机自启;

    2. --now:立即停止 httpd 服务。

  • 为什么这么做?:httpd 和 Nginx 都默认用 80 端口,同时运行会导致 Nginx 无法使用 80 端口(用户访问博客会失败),所以必须关闭。

阶段 6:验证部署结果(确保每个组件都正常)

6.1.1 验证 WordPress 文件是否部署成功(目标节点 node1)
 # 在node1上执行,查看WordPress文件是否存在[root@node1 ~ 15:57:26]$ ls /usr/share/nginx/html/blog.yuxb.cloud/wordpress/
  • 正常结果:会显示 WordPress 的核心文件(如 index.phpwp-admin 目录),和下面一致:

 index.php        wp-admin              wp-content         wp-load.php      wp-signup.phplicense.txt      wp-blog-header.php    wp-cron.php        wp-login.php     wp-trackback.phpreadme.html      wp-comments-post.php  wp-includes        wp-mail.php      xmlrpc.phpwp-activate.php  wp-config-sample.php  wp-links-opml.php  wp-settings.php
  • 如果失败:检查 Ansible 剧本中 “解压 WordPress” 的任务是否成功,或压缩包是否在控制节点的 lnmp 目录。

 # 继续在node1上执行,查看文件权限(确保所有者是nginx)[root@node1 ~ 15:58:41]$ ll /usr/share/nginx/html/blog.yuxb.cloud/wordpress/
  • 正常结果:所有文件的所有者(owner)和所属组(group)都是 nginx(如 -rw-r--r-- 1 nginx nginx 418 9月 25 2013 index.php);

  • 如果权限不对:检查 Ansible 剧本中 “解压 WordPress” 任务的 ownergroup 是否配置为 nginx

6.2.1 验证 MariaDB 数据库是否正常(目标节点 node1)
 # 在node1上执行,用WordPress专用用户登录数据库[root@node1 ~ 16:04:31]$ mysql -uwordpress -p123
  • 登录成功标志:显示 MariaDB 命令行提示符(MariaDB [(none)]>),无报错;

  • 如果登录失败:检查 vaults.yml 中的 app_userapp_password 是否正确,或 Ansible 剧本中 “创建数据库用户” 的任务是否成功。

 # 登录后执行,查看数据库列表MariaDB [(none)]> show databases;
  • 正常结果:显示 webapp 数据库(WordPress 专用库),如下:

 +--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || webapp             |+--------------------+4 rows in set (0.00 sec)
  • 如果没有 webapp 库:检查 Ansible 剧本中 “创建数据库” 的任务是否成功。

 # 退出数据库(可选)MariaDB [(none)]> exitBye

阶段 7:浏览器访问博客(完成 WordPress 安装)

7.1.1 配置 Windows 客户端 Hosts(让浏览器识别博客域名)
  1. 打开 Windows 文件管理器,进入路径:C:\Windows\System32\drivers\etc

  2. 找到 hosts 文件,右键用 “记事本” 打开(需管理员权限,否则无法保存);

  3. 在文件末尾添加一行:10.1.8.11 blog.yuxb.cloud10.1.8.11 是目标节点 node1 的 IP,blog.yuxb.cloud 是博客域名);

  4. 保存并关闭文件。

  • 为什么这么做?:Windows 不知道 blog.yuxb.cloud 对应哪个 IP,添加后浏览器会把该域名指向 10.1.8.11(目标节点)。

7.1.2 访问 WordPress 并完成安装
  1. 打开浏览器(Chrome、Edge 等),在地址栏输入 blog.yuxb.cloud,按回车;

  2. 进入 WordPress 安装向导,选择 “中文”,点击 “继续”;

  3. 点击 “现在就开始!”,填写数据库信息(和vaults.yml、vars.yml一致):

    • 数据库名:webapp

    • 用户名:wordpress

    • 密码:123

    • 数据库主机:localhost(本地数据库,不用改)

    • 表前缀:默认 wp_(不用改)

  4. 点击 “提交”,再点击 “运行安装程序”;

  5. 填写博客信息(自定义):

    • 站点标题:比如 “我的第一个博客”

    • 用户名:自定义(比如 admin

    • 密码:自定义(比如 123456,生产环境用复杂密码)

    • 电子邮件:自定义(比如 test@123.com

  6. 点击 “安装 WordPress”,安装完成后用刚才的用户名密码登录;

  7. 登录后进入博客后台,或点击 “访问站点” 查看博客首页,至此实验完成!

五、常见问题排查

问题现象可能原因解决方法
执行剧本时 “yum 安装软件失败”目标节点网络不通,或 yum 源有问题1. 在 node1 上 ping 百度(ping baidu.com)确认网络;2. 更换 yum 源为阿里云(yum install -y wget && wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
浏览器访问 blog.yuxb.cloud 显示 “无法访问”1. 目标节点 Nginx 没启动;2. 客户端 Hosts 配置错误;3. 防火墙拦截 80 端口1. 在 node1 上执行 systemctl status nginx 确认 Nginx 已启动;2. 检查 Windows Hosts 配置的 IP 和域名是否正确;3. 在 node1 上关闭防火墙(systemctl stop firewalld && systemctl disable firewalld
WordPress 安装时 “无法连接数据库”1. 数据库密码输错;2. MariaDB 没启动;3. 数据库用户权限不够1. 确认密码和 vaults.yml 一致;2. 在 node1 上执行 systemctl status mariadb 确认数据库已启动;3. 检查剧本中 app_priv 是否为 *.*:ALL
访问 PHP 文件显示 “下载文件” 而非执行Nginx 的 PHP 配置没生效,或 PHP-FPM 没启动1. 在 node1 上执行 systemctl status php-fpm 确认 PHP-FPM 已启动;2. 检查 /etc/nginx/default.d/php.conf 文件是否存在且内容正确

六、实验总结

  1. 本次实验通过 Ansible 实现了 LNMP + WordPress 的自动化部署,核心是 “剧本驱动”,避免手动在目标机敲命令;

  2. 敏感信息用 Ansible Vault 加密,普通配置用变量文件,符合 “安全 + 灵活” 的原则;

  3. 每个组件的作用:Nginx 收请求、PHP 处理动态代码、MariaDB 存数据,三者协同支撑 WordPress 运行;

  4. 零基础同学需重点理解 “剧本的执行顺序” 和 “各组件的联动关系”,遇到问题先看日志(Nginx 日志、Ansible 执行日志)。

管理大项目

配置并行

概念

并行配置就是把原本串行执行的任务拆分,使得多个任务可以同时进行,从而加快整体完成速度。这在大项目中尤为重要,比如:

  • 软件开发:编译、测试、打包可以并行。

  • 数据处理:大数据分析任务可以按分区或模块并行。

  • 运维/部署:批量服务器操作可以并行执行。

并行策略

  1. 任务级并行

    • 不同任务之间独立执行。

    • 例:在CI/CD流水线中,同时运行单元测试和代码扫描。

  2. 数据级并行

    • 将数据拆分成多个部分,各自独立处理。

    • 例:大文件分块处理、多节点分布式计算。

  3. 混合并行

    • 结合任务级和数据级。

    • 例:多模块服务并行编译 + 各模块单元测试并行执行。

配置 forks

 [yuxb@controller web 16:47:05]$ vim inventory [yuxb@controller web 16:47:48]$ cat playbook.yml ---- name: connectionhosts: alltasks:- name: conneciton 1shell: sleep 5- name: conneciton 2debug: msg: connection 2[yuxb@controller web 16:47:52]$ cat inventory [controller]​[webs]node1node3​[dbs]node2node4​

执行

 # 两人一组执行[yuxb@controller web 16:48:10]$ ansible-playbook playbook.yml -f 2​PLAY [connection] ***********************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node1]ok: [node3]ok: [node2]ok: [node4]​TASK [conneciton 1] *********************************************************************************changed: [node3]changed: [node1]changed: [node2]changed: [node4]​TASK [conneciton 2] *********************************************************************************ok: [node1] => {"msg": "connection 2"}ok: [node3] => {"msg": "connection 2"}ok: [node2] => {"msg": "connection 2"}ok: [node4] => {"msg": "connection 2"}​PLAY RECAP ******************************************************************************************node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node4                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​

配置 serial

 [yuxb@controller web 16:49:34]$ vim playbook.yml [yuxb@controller web 16:49:57]$ cat playbook.yml ---- name: Rolling updatehosts: allserial: 2tasks:- name: latest apache httpd package is installedyum:name: httpdstate: latestnotify: restart apachehandlers:- name: restart apacheservice:name: httpdstate: restarted

执行

 [yuxb@controller web 16:50:51]$ ansible-playbook playbook.yml​PLAY [Rolling update] *******************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node3]ok: [node1]​TASK [latest apache httpd package is installed] *****************************************************ok: [node3]ok: [node1]​PLAY [Rolling update] *******************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node4]ok: [node2]​TASK [latest apache httpd package is installed] *****************************************************ok: [node2]ok: [node4]​PLAY RECAP ******************************************************************************************node1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node2                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node3                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node4                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​

配置 async

示例1: 任务执行失败,在规定时间内容任务没有执行完成。

 [yuxb@controller web 16:50:01]$ vim playbook.yml [yuxb@controller web 16:52:54]$ cat playbook.yml ---- name: connectionhosts: node1tasks:- name: conneciton shell: sleep 10async: 5poll: 2# 执行# 中间会卡一会[yuxb@controller web 16:51:14]$ ansible-playbook playbook.yml​PLAY [connection] ***********************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node1]​TASK [conneciton] ***********************************************************************************fatal: [node1]: FAILED! => {"changed": false, "msg": "async task did not complete within the requested time - 5s"}​PLAY RECAP ******************************************************************************************node1                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   ​

示例2: 放入后台下载,立刻执行下一个任务。

 [yuxb@controller web 16:52:58]$ vim playbook.yml [yuxb@controller web 16:54:11]$ cat playbook.yml ---- name: connectionhosts: node1tasks:- name: downloadget_url: url: http://192.168.48.100/ISOS/openEuler-24.03-LTS-x86_64-dvd.isodest: /home/laomaasync: 100poll: 0​# 执行# 很快的就执行完毕[yuxb@controller web 16:53:14]$ ansible-playbook playbook.yml​PLAY [connection] ***********************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node1]​TASK [download] *************************************************************************************changed: [node1]​PLAY RECAP ******************************************************************************************node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​

示例3: ansible 默认行为,等该任务执行完,再执行下一个任务。

 [yuxb@controller web 16:54:13]$ vim playbook.yml [yuxb@controller web 16:55:18]$ cat playbook.yml ---- name: connectionhosts: node1tasks:- name: conneciton shell: sleep 10async: 0poll: 2​# 执行# 中间会卡一会[yuxb@controller web 16:54:26]$ ansible-playbook playbook.yml​PLAY [connection] ***********************************************************************************​TASK [Gathering Facts] ******************************************************************************ok: [node1]​TASK [conneciton] ***********************************************************************************changed: [node1]​PLAY RECAP ******************************************************************************************node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   ​

http://www.dtcms.com/a/337309.html

相关文章:

  • 飞算JavaAI智慧校园场景实践:从校园管理到师生服务的全链路技术革新
  • 【C++✨】多种 C++ 解法固定宽度右对齐输出(每个数占 8 列)
  • 常见的光源频闪控制方式
  • GitHub 热榜项目 - 日榜(2025-08-18)
  • 为什么有些相机“即插即用”,而有些则需要采集卡?
  • 联动无影(TscanPlus)送激活码
  • 短剧小程序系统开发:推动短剧行业规范化与标准化发展
  • 【计算机网络】TCP/IP
  • 决策树简单实战
  • 【github-action 如何为github action设置secrets/environment】
  • 《软件工程导论》实验报告六 设计建模工具的使用(二)
  • 蓝牙AOA定位技术在智慧仓储中的优势与挑战
  • Python 面向对象三大特性详解(与 C++ 对比)
  • (nice!!!)(LeetCode 每日一题) 679. 24 点游戏 (深度优先搜索)
  • 华曦达港股IPO观察丨以创新研发为笔,构建AI Home智慧生活新蓝图
  • 图形自动化:pynput实现Win11系统动作点击录制与回放
  • 【HarmonyOS】应用设置全屏和安全区域详解
  • 广州曼顿智能断路器:让用电更聪明,生活更安心!
  • Java面试宝典:Redis高级特性和应用(发布 订阅、Stream)
  • Redis面试精讲 Day 25:Redis实现分布式Session与购物车
  • Redis---持久化策略
  • SSM-组件的批量扫描
  • 时、分、秒、倒计时组件
  • Redis 客户端安装方法
  • Spring Boot + Spring Kafka 集成
  • 深层语义知识图谱:提升NLP文本预处理效果的关键技术
  • 《基于改进 MobileNetV2 的轻量化茶叶病虫害检测方法》论文解析
  • Redis--day8--黑马点评--分布式锁(一)
  • HTML应用指南:利用POST请求获取全国华为旗舰店门店位置信息
  • Python函数:装饰器