用Ansible剧本管理Ubuntu上的LNMP环境状态,核心就是通过YAML编写的Playbook,把Nginx、MySQL/MariaDB、PHP这三大组件的安装、配置、启动、状态监控全部自动化。你不需要SSH登录到每台服务器手动敲命令,只需要一台控制机、一个剧本文件,就能批量部署和持续维护整个LNMP集群。下面我从环境准备、剧本编写、模块调用、状态管理、实战优化五个维度,把这套方案彻底讲透。

一、为什么要用Ansible管理LNMP而不是Shell脚本

很多运维同学习惯写Shell脚本来部署LNMP,但Shell脚本有个致命问题:它是线性执行的,没有幂等性保证。你跑两次脚本,第二次可能报错或者覆盖配置。Ansible的Playbook天然支持幂等操作,也就是说同一个剧本跑一百次,结果都是一致的——已经装好的不会重复装,已经启动的不会重复启动。而且Ansible基于SSH协议,不需要在目标机器上安装任何Agent,Ubuntu默认就支持,开箱即用。对于LNMP这种需要精确控制版本、配置文件、服务状态的场景,Ansible的模块化设计比Shell脚本高效太多。

二、控制机环境准备与主机清单配置

首先在你的Ubuntu控制机上安装Ansible。Ubuntu 22.04可以直接用apt安装,但建议用pip安装最新稳定版:

sudo apt update
sudo apt install python3-pip python3-venv -y
python3 -m venv ~/ansible-env
source ~/ansible-env/bin/activate
pip install ansible

然后创建主机清单文件,假设你有三台Ubuntu服务器要部署LNMP:

[lnmp_servers]
192.168.1.101
192.168.1.102
192.168.1.103

[lnmp_servers:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3

这段配置的关键点:ansible_user指定SSH登录用户,ansible_ssh_private_key_file指定密钥路径免密登录,ansible_python_interpreter指定目标机Python解释器路径,Ubuntu 22.04自带Python3,但有些精简镜像可能没有,这个参数能避免报错。

三、LNMP安装剧本的完整编写

下面是一个生产级的LNMP部署剧本,我把Nginx、MariaDB、PHP三个组件拆成独立的role来写,结构清晰、可复用:

---
- name: Deploy LNMP Environment on Ubuntu
  hosts: lnmp_servers
  become: yes
  vars:
    nginx_version: "1.24"
    mysql_root_password: "YourSecurePass123!"
    php_version: "8.2"
    php_extensions:
      - php8.2-fpm
      - php8.2-mysql
      - php8.2-cli
      - php8.2-curl
      - php8.2-gd
      - php8.2-mbstring
      - php8.2-xml
      - php8.2-zip

  roles:
    - role: nginx_install
    - role: mariadb_install
    - role: php_install

这个主剧本非常简洁,实际工作都下放到各个role里。下面逐个讲每个role怎么写。

四、Nginx角色剧本:安装、配置与状态管理

创建roles/nginx_install/tasks/main.yml:

- name: Install Nginx
  apt:
    name: nginx
    state: present
    update_cache: yes

- name: Ensure Nginx is running and enabled
  service:
    name: nginx
    state: started
    enabled: yes

- name: Deploy custom nginx.conf
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify: Restart Nginx

- name: Deploy site configuration
  template:
    src: templates/site.conf.j2
    dest: /etc/nginx/sites-available/default
  notify: Restart Nginx

- name: Enable site configuration
  file:
    src: /etc/nginx/sites-available/default
    dest: /etc/nginx/sites-enabled/default
    state: link
  notify: Restart Nginx

handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted

这里有几个硬核细节要注意。第一,apt模块的update_cache: yes等价于先apt update再apt install,省了一步。第二,template模块用Jinja2模板渲染配置文件,你可以在templates目录下放nginx.conf.j2和site.conf.j2,里面用变量占位符实现差异化部署。第三,notify和handler配合,只有配置文件真正发生变化时才重启Nginx,没变化就跳过,这就是幂等性的体现。

五、MariaDB角色剧本:安全初始化与状态监控

创建roles/mariadb_install/tasks/main.yml:

- name: Install MariaDB server
  apt:
    name:
      - mariadb-server
      - python3-pymysql
    state: present
    update_cache: yes

- name: Ensure MariaDB is running and enabled
  service:
    name: mariadb
    state: started
    enabled: yes

- name: Set MariaDB root password
  mysql_user:
    name: root
    host: localhost
    password: "{{ mysql_root_password }}"
    login_unix_socket: /var/run/mysqld/mysqld.sock

- name: Remove anonymous users
  mysql_user:
    name: ''
    host_all: yes
    state: absent
    login_unix_socket: /var/run/mysqld/mysqld.sock

- name: Remove test database
  mysql_db:
    name: test
    state: absent
    login_unix_socket: /var/run/mysqld/mysqld.sock

- name: Create application database
  mysql_db:
    name: appdb
    state: present
    login_unix_socket: /var/run/mysqld/mysqld.sock

- name: Create application user
  mysql_user:
    name: appuser
    password: "AppPass456!"
    priv: 'appdb.*:ALL'
    state: present
    login_unix_socket: /var/run/mysqld/mysqld.sock

MariaDB的安全初始化非常重要。mysql_user和mysql_db模块需要python3-pymysql这个依赖,所以apt任务里一起装了。login_unix_socket参数指定通过本地socket连接而不是TCP,更安全也更快。这套操作跑完,MariaDB就完成了从裸装到生产安全配置的全过程。

六、PHP角色剧本:FPM配置与版本管理

创建roles/php_install/tasks/main.yml:

- name: Add Ondřej Surý PHP PPA
  apt_repository:
    repo: ppa:ondrej/php
    state: present

- name: Install PHP packages
  apt:
    name: "{{ php_extensions }}"
    state: present
    update_cache: yes

- name: Configure PHP-FPM
  template:
    src: templates/php-fpm.conf.j2
    dest: /etc/php/{{ php_version }}/fpm/pool.d/www.conf
    owner: root
    group: root
    mode: '0644'
  notify: Restart PHP-FPM

- name: Ensure PHP-FPM is running and enabled
  service:
    name: "php{{ php_version }}-fpm"
    state: started
    enabled: yes

handlers:
  - name: Restart PHP-FPM
    service:
      name: "php{{ php_version }}-fpm"
      state: restarted

Ubuntu自带的apt源里PHP版本通常比较旧,要装PHP 8.2就必须加Ondřej Surý的PPA源。apt_repository模块自动处理了add-apt-repository的工作。PHP-FPM的www.conf模板里你可以调整pm.max_children、pm.start_servers这些参数,根据服务器内存做调优。一般4G内存的机器,pm.max_children设20-30比较合理。

七、状态管理与健康检查剧本

部署完不是终点,持续监控状态才是运维自动化的核心价值。创建一个独立的check_lnmp.yml:

---
- name: Check LNMP Environment Status
  hosts: lnmp_servers
  become: yes
  tasks:
    - name: Check Nginx service status
      service_facts:

    - name: Verify Nginx is running
      assert:
        that:
          - "'nginx.service' in services"
          - "services['nginx.service'].state == 'running'"
        fail_msg: "Nginx is not running!"
        success_msg: "Nginx is running properly."

    - name: Check Nginx response
      uri:
        url: http://localhost
        status_code: 200
      register: nginx_check
      failed_when: nginx_check.status != 200

    - name: Check MariaDB service status
      service_facts:

    - name: Verify MariaDB is running
      assert:
        that:
          - "'mariadb.service' in services"
          - "services['mariadb.service'].state == 'running'"
        fail_msg: "MariaDB is not running!"

    - name: Check PHP-FPM socket
      stat:
        path: /run/php/php{{ php_version }}-fpm.sock
      register: php_socket

    - name: Verify PHP-FPM socket exists
      assert:
        that:
          - php_socket.stat.exists
        fail_msg: "PHP-FPM socket not found!"

这个检查剧本用了service_facts收集所有服务状态,用assert做断言验证,用uri模块直接发HTTP请求测试Nginx响应,用stat模块检查PHP-FPM的socket文件是否存在。任何一项检查失败,Ansible会立即报错并停止,你可以把它接入定时任务或者CI/CD流水线,实现自动化巡检。

八、实战优化建议与常见坑

第一,变量分离。不要把密码、IP写死在剧本里,用group_vars/lnmp_servers/vars.yml或者Ansible Vault加密敏感数据。第二,标签控制。给任务加tags,比如nginx_config、db_security,紧急情况下可以只跑某一类任务:ansible-playbook deploy.yml --tags nginx_config。第三,重试机制。网络不稳定时apt安装可能失败,在任务里加retries: 3和delay: 10能自动重试。第四,日志输出。用-vvv参数运行能看到详细调试信息,排错时非常有用。第五,Ubuntu版本差异。20.04和22.04的包名、服务名有细微差别,比如PHP 8.1在22.04里叫php8.1-fpm,在20.04里可能需要从PPA装,剧本里要做版本判断或者统一用变量控制。

最后说一个很多人忽略的点:Ansible剧本本身也需要版本管理。把整个项目放到代码仓库里,每次修改都有记录、可回滚。配合GitLab CI或者Jenkins,代码合并后自动触发Ansible部署,这才是真正的基础设施即代码(IaC)实践。LNMP环境的状态不再是黑盒,每一行配置都有据可查、可复现。

九、总结

Ansible管理Ubuntu上的LNMP环境,本质上就是把运维经验固化成可执行的YAML代码。从安装、配置、安全加固到状态巡检,每个环节都有对应的Ansible模块支撑。关键在于:结构清晰的role划分、Jinja2模板实现配置差异化、幂等操作保证多次执行安全、assert和健康检查实现自动化监控。掌握这套方法,你管理10台服务器和管理1000台服务器,工作量几乎没有区别,这就是自动化运维的真正威力。