Automate LNMP Deployment with Ansible Playbooks: Step‑by‑Step Guide
This tutorial walks you through installing Ansible, preparing inventory, creating reusable variables, and writing a comprehensive playbook that automates the deployment and configuration of Nginx, MySQL, and PHP on multiple CentOS 8 hosts, complete with service setup and firewall adjustments.
Installation of Ansible
Configure the Alibaba Cloud repository, remove existing repo files, and add the official Alibaba mirror:
# cd /etc/yum.repos.d
# rm -rf *
# curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/CentOS-vault-8.5.2111.repo
# sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
# yum install -y https://mirrors.aliyun.com/epel/epel-release-latest-8.noarch.rpm
# sed -i 's|^#baseurl=https://download.example/pub|baseurl=https://mirrors.aliyun.com|' /etc/yum.repos.d/epel/*
# sed -i 's|^metalink|#metalink|' /etc/yum.repos.d/epel/*
# ansible --versionResult shows Ansible 2.9.27 and Python 3.6.8.
Basic Preparation with Ansible
Create a project directory, copy the default ansible.cfg, and define the inventory:
# mkdir playdemo && cd playdemo
# cp /etc/ansible/ansible.cfg .
# vim /etc/hosts # add entries for nginx, mysql, php hosts
# cat /etc/hosts
127.0.0.1 localhost
192.168.222.137 nginx
192.168.222.138 mysql
192.168.222.139 php
# mkdir playbook && cd playbook
# vim lnmp.yml # create the playbook fileGenerate SSH keys and copy them to the managed hosts for password‑less login, then verify connectivity:
# ssh-keygen -t rsa
# ssh-copy-id 192.168.222.137
# ssh-copy-id 192.168.222.138
# ssh-copy-id 192.168.222.139
# ansible all --list-hosts -m pingDisable the firewall and SELinux on the control host:
# systemctl stop firewalld.service
# setenforce 0
# sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/configWrite Playbook
The lnmp.yml playbook uses variables from dir.yml, hos.yml, and package.yml to download, unpack, compile, and configure each component.
---
- name: Deploy LNMP stack
hosts: all
tasks:
- name: Stop firewalld
service:
name: firewalld.service
state: stopped
enabled: no
- name: Disable SELinux
lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: SELINUX=disabled
# Nginx section
- name: Install Nginx
vars_files:
- ../var/dir.yml
- ../var/hos.yml
- ../var/package.yml
hosts: "{{ host_ip['nginx'] }}"
tasks:
- name: Create nginx user
user:
name: nginx
system: yes
shell: /sbin/nologin
create_home: no
state: present
- name: Download Nginx source
get_url:
url: "{{ url_dir }}{{ package['nginx'] }}.tar.gz"
dest: "{{ dow_dir }}"
- name: Unarchive Nginx
unarchive:
src: "{{ dow_dir }}{{ package['nginx'] }}.tar.gz"
dest: "{{ una_dir }}"
remote_src: yes
- name: Install build dependencies
yum:
name: pcre-devel,openssl,openssl-devel,gd-devel,make,gcc,gcc-c++,wget
state: present
- name: Configure Nginx
shell: |
cd {{ una_dir }}{{ package['nginx'] }} && \
./configure --prefix={{ ins_dir['nginx'] }} \
--user=nginx --group=nginx \
--with-http_ssl_module --with-http_gzip_static_module
- name: Build and install Nginx
shell: |
cd {{ una_dir }}{{ package['nginx'] }} && \
make -j$(nproc) && make install
- name: Add Nginx to PATH
copy:
dest: /etc/profile.d/nginx.sh
content: "export PATH=$PATH:{{ ins_dir['nginx'] }}/sbin"
- name: Create Nginx systemd service
copy:
dest: /usr/lib/systemd/system/nginx.service
content: |
[Unit]
Description=nginx server daemon
After=network.target
[Service]
Type=forking
ExecStart={{ ins_dir['nginx'] }}/sbin/nginx
ExecStop={{ ins_dir['nginx'] }}/sbin/nginx -s stop
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
- name: Deploy Nginx config
copy:
dest: "{{ ins_dir['nginx'] }}/conf/nginx.conf"
content: |
user nginx;
worker_processes 1;
events { worker_connections 1024; }
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.php;
}
location ~ \.php$ {
root /var/www;
fastcgi_pass {{ host_ip['php'] }}:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
}
- name: Create test index.php
copy:
dest: "{{ ins_dir['nginx'] }}/html/index.php"
content: "<?php phpinfo(); ?>"
- name: Start and enable Nginx
service:
name: nginx.service
state: restarted
enabled: yes
# MySQL section (similar structure, omitted for brevity)
# PHP section (similar structure, omitted for brevity)Variable Usage
Variables are defined in separate YAML files for directories, host IPs, and package names, then referenced in the playbook using Jinja2 syntax (e.g., {{ ins_dir['nginx'] }}).
# tree playdemo
playdemo
├── ansible.cfg
├── inventory
├── package
│ ├── mysql-5.7.38-linux-glibc2.12-x86_64.tar.gz
│ ├── nginx-1.22.0.tar.gz
│ └── php-8.1.11.tar.gz
├── playbook
│ └── lnmp.yml
└── var
├── dir.yml
├── hos.yml
└── package.yml
# cat var/package.yml
package:
nginx: nginx-1.22.0
mysql: mysql-5.7.38-linux-glibc2.12-x86_64
php: php-8.1.11
# cat var/dir.yml
url_dir: ../package/
dow_dir: /usr/local/src/
una_dir: /usr/src/
ins_dir:
nginx: /usr/local/nginx
mysql: /usr/local/mysql
php: /usr/local/php
# cat var/hos.yml
host_ip:
nginx: 192.168.222.137
mysql: 192.168.222.138
php: 192.168.222.139Running ansible-playbook playbook/lnmp.yml -vv completes the deployment with all tasks reporting ok and changed counts, confirming a successful automated LNMP setup.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
