Ansible - удаленная настройка конфигураций

Представим, что у вас есть 10 серверов под разные или похожие задачи. Каждую машину нужно периодически обновлять, изменять ее конфигурацию. Часто необходимо поднять и настроить сразу несколько серверов в короткие сроки. Конечно, можно подключаться к каждому по SSH и вручную вносить необходимые изменения, но это очень долго и увеличивает возможность совершения ошибок из-за человеческого фактора.

Для эффективного решения подобных задач DevOps-специалисты используют систему удаленного управления конфигурацией Ansible. Ansible автоматизирует поставку ПО, управляет конфигурацией и развёртыванием приложений. В облачной среде, Ansible может использоваться для автоматизации конфигурирования и управления облачными инфраструктурами. Например, для развертывания и управления виртуальными машинами, контейнерами, сетевыми объектами и облачными сервисами.

Главные преимущества Ansible:

1) Работа по SSH. На управляемые серверы не нужно устанавливать дополнительный софт, Ansible должен быть установлен лишь на управляющем сервере.

2) Простота. Код написан на Python, поэтому написание дополнительных модулей под ваши задачи не составит труда. Конфигурационные файлы Ansible создаются в формате YAML - формат сериализованных данных (т.е. перевод структуры данных в битовую последовательность), намного проще и читабельнее, чем JSON и XML.

3) Декларативность. Разработчику нужно писать не действия программы, а результат, которого она должна достичь. Ansible сам спланирует как именно он будет достигнут. Это значительно сокращает время настройки.

4) Push и Pull. Ansible может работать в обоих режимах: главный сервер может "вытягивать" информацию из управляемых, или наоборот, "проталкивать" ее в зависимые сервера.

Недостатки:

1) Проблемы с Windows. С версии 1.7 Ansible поддерживает Windows, но работать приходится с оболочкой PowerShell, необходимо установить Linux-модуль.

2) Контроль состояния. В Ansible нет постоянного мониторинга зависимых серверов, он лишь выполняет задачи и наблюдает за состоянием хоста. В других системах управления есть более широкие возможности для контроля состояния серверов.

Структура Ansible

Модули

Это программы, выполняющие работу на сервере. Например, вместо запуска команды на машине:

$ apt install nginx

Мы можем использовать модуль apt и установить nginx :

- name: Install htop
apt: name=htop

Хосты

Чтобы предоставить перечень хостов, нам нужно обозначить список, находящийся в файле инвентаризации.

Playbooks

Playbooks — основа работы Ansible. Это способ отправки команд на удалённые компьютеры с помощью скриптов в формате YAML. Вместо того, чтобы индивидуально использовать команды для удалённой настройки компьютеров из командной строки, вы можете настраивать целые сложные среды, передавая скрипт одной или нескольким серверам.

Vars

Файл содержит набор переменных, например имя пользователя и пароль базы данных.

Роли

Это способ сгруппировать несколько задач в один контейнер, чтобы эффективно автоматизировать работу с помощью понятной структуры каталогов.

Файлы конфигураций Ansible делятся на блоки, начинающиеся с “- name”. Сначала указывается название операции, а потом модуль, который нужен для ее выполнения. После этого нужно указать переменные модуля.

На первый взгляд это может показаться сложным, но структура Ansible очень проста, а модули быстро запоминаются или находятся в интернете. Существует полная документация, помимо этого, вы можете найти огромное количество самописные модулей под самые разные задачи.

Использование Ansible на практике

Рассмотрим удобство Ansible на конкретном примере - с помощью Ansible мы установим сервисы на машины и отредактируем их конфигурации:

  1. Ubuntu22, 192.168.1.2 – MariaDB
  2. Ubuntu23, 192.168.1.3 – MySQL
  3. Debian12, 172.16.1.4 – стек LAMP + WordPress

ВНИМАНИЕ! В конфигурациях мы используем шаблонные пароли для наглядности, обязательно измените их.

Установка

На управляемых узлах должен быть установлен Python версии 2.4 и выше, по умолчанию он вшит в большинство дистрибутивов Linux-систем. На сервер ansible-controller необходимо установить Ansible, рассмотрим пример для Ubuntu:

$ sudo apt update
$ sudo apt install ansible

При установке будет создан каталог /etc/ansible с конфигурационными файлами: hosts и ansible.cfg - файл с настройками Ansible.

В простейшем виде файл hosts может содержать одну строку (адрес управляемого сервера): 192.168.1.2

Вы можете указать только ip-адреса серверов, но будет удобнее сразу записать номера портов ssh, пользователей, пароли и т.д. Укажем информацию о хостах:

$ nano hosts

all:
    hosts:
        server-1:
            ansible_host: 192.168.1.2
            ansible_connection: ssh
            ansible_user: root
            ansible_password: b1Ge62qUzIZhmtrH
        server-2:
            ansible_host: 192.168.1.3
            ansible_connection: ssh
            ansible_user: root
            ansible_password: h1FF8LQUmapS5uj0
        server-3:
            ansible_host: 172.16.1.4
            ansible_connection: ssh
            ansible_user: root
            ansible_password: Byu0M1E7vJyxQ2m6

Теперь мы можем проверить доступность серверов:

root@ubuntu:/etc/ansible# ansible all -i hosts -m ping
server-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
server-3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
server-2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Отлично, Ansible-controller имеет доступ ко всем необходимым серверам.

Начнем с MariaDB, для этого создадим новую конфигурацию Ansible:

$ nano mariadb-management.yml
 
Разберем из чего состоит этот файл конфигурации:

Сначала мы указываем информацию об управляемых серверах и переменные.

---

 - name: MariaDB Management using Ansible
   hosts: server-1
   gather_facts: true   become: true   vars:
     mysql_root_password: "StrongRootPassword!"
     python_mysql: "{{ 'python3-mysqldb' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'python3-PyMySQL' }}"

Устанавливаем и включаем сервисы:
   tasks:
     - name: Installing MariaDB Server
       package:
         name: mariadb-server
         state: latest
         update_cache: yes     - name: Installing python_mysql
       package:
         name: "{{ python_mysql }}"
         state: present
         update_cache: yes     - name: start and enable mariadb server
       service:
         name: mariadb
         enabled: true         state: started

 

Обновляем пароль СУБД:

     - name: update mysql root password for all root accounts
       mysql_user:
         name: root
         host: "{{ item }}"
         password: "{{ mysql_root_password }}"
         login_user: root
         login_password: "{{ mysql_root_password }}"
         check_implicit_admin: yes         priv: "*.*:ALL,GRANT"
       with_items:
       - "{{ ansible_hostname }}"
       - 127.0.0.1
       - ::1
       - localhost

 

Создаем новую БД и пользователя с полными привилегиями:

     - name: Create a new database with name 'testdb'
       mysql_db:
         name: testdb
         state: present
         login_user: root
         login_password: "{{ mysql_root_password }}"

     - name: Create database user & grant all privileges on the testdb
       mysql_user:
         name: testuser1
         password: Passw0rd!
         priv: 'testdb.*:ALL'
         host: '%'
         state: present
         login_user: root
         login_password: "{{ mysql_root_password }}"

 

Это все, что нужно для установки и настройки MariaDB на нашем сервере, пора запустить Ansible:

По выводу программы понятно, что все необходимые сервисы установлены и настроены.

 

Теперь поднимем MySQL-сервер, добавим необходимую базу данных и пользователя с привилегиями:

Создадим конфигурацию Ansible для MySQL:

$ nano mysql-management.yml

---
 - name: MySQL Server Management Using Ansible
   become: yes   hosts: server-2
   vars:

     mysql_root_password: "StrongRootPassword!"
     service_name: "{{ 'mysql' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'mysqld' }}"
     python_mysql: "{{ 'python3-mysqldb' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'python3-PyMySQL' }}"

   tasks:
     - name: Determine package manager
       ansible.builtin.set_fact:
         package_manager: "{{ 'apt' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'yum' }}"

     - name: Installing MySQL and Required dependencies
       package:
         name: "{{ item }}"
         state: present
         update_cache: yes       loop:
         - mysql-server
     - name: Installing python_mysql
       package:
         name: "{{ python_mysql }}"
         state: present
         update_cache: yes     - name: Start and enable mysql service
       service:
         name: "{{ service_name }}"
         state: started
         enabled: yes     - name: update mysql root password for all root accounts
       mysql_user:
         name: root
         host: "{{ item }}"
         password: "{{ mysql_root_password }}"
         login_user: root
         login_password: "{{ mysql_root_password }}"
         check_implicit_admin: yes         priv: "*.*:ALL,GRANT"
       with_items:
       - "{{ ansible_hostname }}"
       - 127.0.0.1
       - ::1
       - localhost
     - name: Create a new database with name 'testdb'
       mysql_db:
         name: testdb
         state: present
         login_user: root
         login_password: "{{ mysql_root_password }}"
     - name: Create database user & grant all privileges on the testdb
       mysql_user:
         name: testuser1
         password: Passw0rd!
         priv: 'testdb.*:ALL'
         host: '%'
         state: present
         login_user: root
         login_password: "{{ mysql_root_password }}"

Запускаем!

И теперь можно понять, что все сработало. Теперь у нас есть конфигурации, которые позволяют создавать СУБД с необходимыми параметрами за 5 минут сразу на нескольких машинах.

Наконец, попробуем что-то более серьезное: стек LAMP и WordPress с готовыми конфигурациями сервисов.

Так будет выглядеть структура нашего проекта:

  1. playbook.yml – конфигурация Ansible, в которой будет описано, какой результат нам необходим;
  2. hosts – список хостов;
  3. apache.conf.j2 и wp-config.php.j2 – конфигурации сервисов LAMP+WP;
  4. default.yml – здесь буду указаны пароли и пользователи СУБД, хост и порт веб-сервера.
$ nano playbook.yml
---
- hosts: server-3
  become: true  vars_files:
    - vars/default.yml
  tasks:
    - name: Install prerequisites
      apt: name=aptitude update_cache=yes state=latest force_apt_get=yes
      tags: [ system ]
    
     - name: Install LAMP Packages
      apt: name={{ item }} update_cache=yes state=latest
      loop: [ 'apache2', 'mariadb-server', 'python3-pymysql', 'php', 'php-mysql', 'libapache2-mod-php' ]
      tags: [ system ]

    - name: Install PHP Extensions
      apt: name={{ item }} update_cache=yes state=latest
      loop: "{{ php_modules }}"
      tags: [ system ]


  # Apache Configuration
    - name: Create document root
      file:
        path: "/var/www/{{ http_host }}"
        state: directory
        owner: "www-data"
        group: "www-data"
        mode: '0755'
      tags: [ apache ]
    - name: Set up Apache VirtualHost
      template:
        src: "files/apache.conf.j2"
        dest: "/etc/apache2/sites-available/{{ http_conf }}"
      notify: Reload Apache
      tags: [ apache ]
    - name: Enable rewrite module
      shell: /usr/sbin/a2enmod rewrite
      notify: Reload Apache
      tags: [ apache ]
    - name: Enable new site
      shell: /usr/sbin/a2ensite {{ http_conf }}
      notify: Reload Apache
      tags: [ apache ]
    - name: Disable default Apache site
      shell: /usr/sbin/a2dissite 000-default.conf
      notify: Restart Apache
      tags: [ apache ]

  # MySQL Configuration
    - name: Set the root password
      mysql_user:
        name: root
        password: "{{ mysql_root_password }}"
        login_unix_socket: /var/run/mysqld/mysqld.sock
      tags: [ mysql, mysql-root ]
    - name: Remove all anonymous user accounts
      mysql_user:
        name: ''
        host_all: yes        state: absent
        login_user: root
        login_password: "{{ mysql_root_password }}"
      tags: [ mysql ]
    - name: Remove the MySQL test database
      mysql_db:
        name: test
        state: absent
        login_user: root
        login_password: "{{ mysql_root_password }}"
      tags: [ mysql ]
    - name: Creates database for WordPress
      mysql_db:
        name: "{{ mysql_db }}"
        state: present
        login_user: root
        login_password: "{{ mysql_root_password }}"
      tags: [ mysql ]
    - name: Create MySQL user for WordPress
      mysql_user:
        name: "{{ mysql_user }}"
        password: "{{ mysql_password }}"
        priv: "{{ mysql_db }}.*:ALL"
        state: present
        login_user: root
        login_password: "{{ mysql_root_password }}"
      tags: [ mysql ]

  # UFW Configuration
    - name: "UFW - Allow HTTP on port {{ http_port }}"
      ufw:
        rule: allow
        port: "{{ http_port }}"
        proto: tcp
      tags: [ system ]

  # WordPress Configuration
    - name: Download and unpack latest WordPress
      unarchive:
        src: https://wordpress.org/latest.tar.gz
        dest: "/var/www/{{ http_host }}"
        remote_src: yes        creates: "/var/www/{{ http_host }}/wordpress"
      tags: [ wordpress ]
    - name: Set ownership
      file:
        path: "/var/www/{{ http_host }}"
        state: directory
        recurse: yes        owner: www-data
        group: www-data
      tags: [ wordpress ]
    - name: Set permissions for directories
      shell: "/usr/bin/find /var/www/{{ http_host }}/wordpress/ -type d -exec chmod 750 {} \\;"
      tags: [ wordpress ]
    - name: Set permissions for files
      shell: "/usr/bin/find /var/www/{{ http_host }}/wordpress/ -type f -exec chmod 640 {} \\;"
      tags: [ wordpress ]
    - name: Set up wp-config
      template:
        src: "files/wp-config.php.j2"
        dest: "/var/www/{{ http_host }}/wordpress/wp-config.php"
      tags: [ wordpress ]

  handlers:
    - name: Reload Apache
      service:
        name: apache2
        state: reloaded
    - name: Restart Apache
      service:
        name: apache2
        state: restarted

----

$ mkdir vars
$ nano vars/default.yml

----

#System Settings
php_modules: [ 'php-curl', 'php-gd', 'php-mbstring', 'php-xml', 'php-xmlrpc', 'php-soap', 'php-intl', 'php-zip' ]

#MySQL Settings
mysql_root_password: "mysql_root_password"
mysql_db: "wordpress"
mysql_user: "sammy"
mysql_password: "mysql_password"

#HTTP Settings
http_host: "my_site.com"
http_conf: "my_site.conf"
http_port: "80"

----

$ mkdir files
$ nano files/apache.conf.j2

----

<VirtualHost *:{{ http_port }}>
    ServerAdmin webmaster@localhost    ServerName {{ http_host }}    ServerAlias www.{{ http_host }}    DocumentRoot /var/www/{{ http_host }}/wordpress    ErrorLog ${APACHE_LOG_DIR}/error.log    CustomLog ${APACHE_LOG_DIR}/access.log combined    <Directory /var/www/{{ http_host }}>
          Options -Indexes          AllowOverride All    </Directory>
     <IfModule mod_dir.c>
        DirectoryIndex index.php index.html index.cgi index.pl  index.xhtml index.htm    </IfModule> 
 </VirtualHost>

----

$ nano files/wp-config.php.j2
<?php
define( 'DB_NAME', '{{ mysql_db }}' );
define( 'DB_USER', '{{ mysql_user }}' );
define( 'DB_PASSWORD', '{{ mysql_password }}' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
define('FS_METHOD', 'direct');
define( 'AUTH_KEY',         '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'SECURE_AUTH_KEY',  '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'LOGGED_IN_KEY',    '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'NONCE_KEY',        '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'AUTH_SALT',        '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'SECURE_AUTH_SALT', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'LOGGED_IN_SALT',   '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'NONCE_SALT',       '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
$table_prefix = 'wp_';
define( 'WP_DEBUG', false );
if ( ! defined( 'ABSPATH' ) ) {
        define( 'ABSPATH', dirname( __FILE__ ) . '/' );
}
require_once( ABSPATH . 'wp-settings.php' );

Все файлы созданы, можно запускать ansible-playbook:

Все установлено, проверяем:

Отлично, наш сервер работает. Теперь вы можете использовать эту конфигурацию Ansible, чтобы быстро устанавливать и настраивать свои веб-серверы.

Подведем итоги:

Для работы с Ansible не нужны агенты на управляемых серверах. Он использует Python и Yaml, простые и удобные языки для новичка. Так же, программа предоставляет набор надежных функций и встроенных модулей, которые облегчают написание сценариев автоматизации.

Это эффективный и расширяемый Open Source инструмент, который позволяет управлять конфигурациями удаленных серверов и сохранять их состояние. Мы лишь поверхностно прошлись по его возможностям, показали как пишутся сценарии для решения различных задач.

Все варианты использования Ansible в рамках одной статьи охватить невозможно и не нужно, а для желающих узнать больше мы подготовили несколько ссылок:

  1. Официальный аккаунт на github c исходным кодом проекта и хорошим набором примеров проектов.
  2. Документация Ansible.
  3. Готовые конфигурации Ansible под разные задачи.
  4. Управление Windows-сервером.

 

 

 

Наш телеграм-канал
Пишем про облака, кейсы, вебинары
Подписаться