Блог "Дети ОИТа"
/поиск

Автоматизация развёртывания LAMP-стека с помощью Ansible

Пошаговое руководство по автоматизации развёртывания LAMP-стека с помощью Ansible. Роли, шаблоны, секреты и production-ready практики.

Автоматизация развёртывания LAMP-стека с помощью Ansible

Современная инфраструктура требует воспроизводимости и скорости развёртывания. Ручная настройка серверов — источник ошибок и дрейфа конфигураций. Ansible, как агентлесс-система управления конфигурациями, идеально подходит для автоматизации рутинных задач. В этой статье мы рассмотрим создание playbook для развёртывания LAMP-стека (Linux, Apache, MySQL, PHP) на серверах под управлением RHEL/CentOS и Debian/Ubuntu.

Архитектура решения

Мы будем использовать роли для модульности:

  • common: базовые настройки безопасности и времени
  • apache: установка и настройка веб-сервера
  • mysql: установка СУБД и создание базы
  • php: установка интерпретатора и модулей

Inventory

Файл inventory/prod.yml:

all:
  hosts:
    webserver01:
      ansible_host: 192.168.10.10
      ansible_user: deploy
    webserver02:
      ansible_host: 192.168.10.11
      ansible_user: deploy
  vars:
    ansible_become: true
    php_version: '8.2'
    mysql_root_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          38376238313534323039373831313033383435313732363433353839303132343637383930313234
          353637383930313234353637383930313234353637383930

Playbook: site.yml

---
- name: Deploy LAMP stack
  hosts: all
  vars_files:
    - vars/secrets.yml
  roles:
    - common
    - apache
    - mysql
    - php

Роль: common

roles/common/tasks/main.yml:

---
- name: Set timezone to UTC
  timezone:
    name: UTC

- name: Install basic packages
  package:
    name:
      - vim
      - wget
      - curl
      - git
      - htop
    state: present

- name: Ensure firewalld is running (RHEL)
  ansible.builtin.service:
    name: firewalld
    state: started
    enabled: true
  when: ansible_facts.os_family == 'RedHat'

- name: Open HTTP/HTTPS ports
  ansible.posix.firewalld:
    service: "{{ item }}"
    permanent: true
    state: enabled
    immediate: true
  loop:
    - http
    - https
  when: ansible_facts.os_family == 'RedHat'

Роль: apache

roles/apache/tasks/main.yml:

---
- name: Install Apache (RHEL)
  package:
    name: httpd
    state: present
  when: ansible_facts.os_family == 'RedHat'

- name: Install Apache (Debian)
  package:
    name: apache2
    state: present
  when: ansible_facts.os_family == 'Debian'

- name: Start and enable Apache
  service:
    name: "{{ 'httpd' if ansible_facts.os_family == 'RedHat' else 'apache2' }}"
    state: started
    enabled: true

- name: Deploy virtual host
  template:
    src: vhost.conf.j2
    dest: "{{ '/etc/httpd/conf.d/' if ansible_facts.os_family == 'RedHat' else '/etc/apache2/sites-available/' }}{{ inventory_hostname }}.conf"
  notify: Restart Apache

Роль: mysql

roles/mysql/tasks/main.yml:

---
- name: Install MySQL server (RHEL)
  package:
    name: mysql-server
    state: present
  when: ansible_facts.os_family == 'RedHat'

- name: Install MariaDB (Debian)
  package:
    name: mariadb-server
    state: present
  when: ansible_facts.os_family == 'Debian'

- name: Start и enable MySQL
  service:
    name: mysqld if ansible_facts.os_family == 'RedHat' else mysql
    state: started
    enabled: true

- name: Set root password
  mysql_user:
    name: root
    password: "{{ mysql_root_password }}"
    host_all: true
    check_implicit_admin: true

- name: Create application database
  mysql_db:
    name: app_db
    state: present
    encoding: utf8mb4
    collation: utf8mb4_unicode_ci

- name: Create DB user
  mysql_user:
    name: app_user
    password: "{{ mysql_app_password }}"
    priv: 'app_db.*:ALL'
    state: present

Роль: php

roles/php/tasks/main.yml:

---
- name: Install PHP и modules (RHEL)
  package:
    name:
      - php
      - php-mysqlnd
      - php-gd
      - php-xml
      - php-mbstring
      - php-intl
    state: present
  when: ansible_facts.os_family == 'RedHat'

- name: Install PHP и modules (Debian)
  package:
    name:
      - php{{ php_version }}
      - php{{ php_version }}-mysql
      - php{{ php_version }}-gd
      - php{{ php_version }}-xml
      - php{{ php_version }}-mbstring
      - php{{ php_version }}-intl
    state: present
  when: ansible_facts.os_family == 'Debian'

- name: Set PHP timezone
  lineinfile:
    path: /etc/php.ini if ansible_facts.os_family == 'RedHat' else /etc/php/{{ php_version }}/apache2/php.ini
    regexp: '^date.timezone'
    line: 'date.timezone = UTC'

Templates

roles/apache/templates/vhost.conf.j2:

<VirtualHost *:80>
    ServerName {{ inventory_hostname }}
    DocumentRoot /var/www/{{ inventory_hostname }}

    <Directory /var/www/{{ inventory_hostname }}>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog /var/log/httpd/{{ inventory_hostname }}-error.log
    CustomLog /var/log/httpd/{{ inventory_hostname }}-access.log combined
</VirtualHost>

Запуск

ansible-playbook -i inventory/prod.yml site.yml --ask-vault-pass

Заключение

Данный playbook обеспечивает идемпотентное развёртывание LAMP-стека. Вы можете расширить его, добавив роли для Redis, мониторинга или SSL/TLS (через Let’s Encrypt). Хранение секретов в Ansible Vault и использование переменных для различия ОС делает решение production-ready.


Рубрика: Автоматизация
Хештеги: #ansible #automation #lamp #devops #linux #apache #mysql #php #инфраструктура #best-practices