Мы продолжаем цикл обучающих статей для начинающих системных администраторов. В этом материале мы разберем Ansible, Ansible-Playbook, и как поднять полноценный веб-сервер с помощью системы автоматизации. Отметим, что если вы являетесь опытным администратором, можете смело пропускать данный материал.

Вот о чем мы сегодня расскажем:

  1. Знакомство.

  2. Разновидности инструментов автоматизации.

  3. Как работает Ansible?

  4. Установка Ansible.

  5. Подключаемся к удаленному сервер с Master.

  6. Конфигурация.

  7. Командные модули Ansible.

  8. Ansible-playbooks.

  9. Ansible Roles.

  10. Итог.

Знакомство

Ansible - это инструмент автоматизации процессов администрирования.

Пример: вышло обновление вашего приложения. Поставлена задача изменить версию php с 7.4 на 8.1. Как поступить?

  1. Сделать вручную.

    Зайти на каждый сервер и обновить версию. Этот вариант подходит, если у вас 2-3 сервера. А если серверов будет 100? Можно также сделать это вручную. Но зачем? Это займет очень много времени.

  2. С помощью инструментов автоматизации.

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

Понятия, которые нам помогут в дальнейшем:

  • Master - это главный сервер, на котором пишется код автоматизации (чаще всего в формате yml).

  • Удаленный сервер - это управляемые сервера.

Разновидности инструментов автоматизации

  1. Pull - на удаленных серверах устанавливается пакет, который делает выгрузку настроек с Master.

    Пример: Пакет автоматизации будет применять настройки только в том случае, если на удаленном сервере установлен точно такой же пакет и он готов принимать соединение с master сервера.

  2. Push - на серверах не нужно устанавливать никакое ПО, требуется только доступ по SSH и master-сервер.

    Пример: у нас есть master-сервер и 40 серверов. Заходить на новые сервера нет необходимости, достаточно будет запустить конфигурацию на master. После этого master сам запустит конфигурации на удаленный сервер.

Как работает Ansible?

Представьте себе master-сервер: на нем мы пишем конфигурацию, указываем ip-адреса серверов и полностью управляем процессом. 

Пример: Мы написали конфигурацию по обновлению php7.4 до 8.1. Выполнили Push, после чего все настройки начали устанавливаться на всех удаленных серверах, которые были указаны в Ansible.


Установка Ansible

Установку делаем на master-сервере.

Добавим репозиторий ansible:

mcedit /etc/apt/sources.list

Вставляем:

deb http://ppa.launchpad.net/ansible/ansible/ubuntu focal main

Добавляем ключ репозитория:

apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367

Обновляем репозитории:

apt update

Установка:

apt install ansible

Проверяем:

ansible --version
ansible [core 2.12.5]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
  jinja version = 2.10.1
  libyaml = True

Если у вас другая ОС, можете обратиться на официальный мануал.

Ansible установлен и готов к работе!

Подключаемся к удаленному серверу с Master

Создаем директорию для управления Ansible:

mkdir /var/ansible/

Переходим:

cd /var/ansbile/

Создаем inventory-файл. Файл, в котором будут перечислены все ip-адреса удаленных серверов. Формат файла можно использовать обычный txt либо yml. Название тоже любое.

touch hosts.txt

Основные ключи:

  1. [ ] - В данные скобки мы указываем название группы серверов.
    Пример: [dev]

  2. ansible_host - ip адрес.
    Пример: ansible_host = 0.0.0.0

  3. ansible_user - имя пользователя для подключения на удаленный сервер.
    Пример: ansible_user - test

  4. ansible_pass - Пароль пользователя.
    Пример: ansible_pass = 12345

  5. ansible_ssh_private_key_file - путь до закрытого ключа. Для входа без участия пароля.
    Пример: ansible_ssh_private_key_file = /home/test/.ssh/id_rsa

  6. ansible_port - порт подключения ssh.
    Пример: ansible_port = 2222

Данные ключи будут основными. Стоит иметь в виду, что здесь перечислены не все ключи, остальные можно посмотреть в официальном мануале.

Прокидываем ваш ключ ssh на удаленный сервер.

На master генерируем ключ:

ssh-keygen -t rsa

Далее выводим ключ:

cat ~/.ssh/id_rsa.pub

Получится примерно такой ключ:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDO2w+l5l0PM2cq8WAFWK3dJCAHuqYR5tno9aIQ1hoqMGdsZWRNntDQvIsId/uvXcqUZddK33QbhDjb4k5yq1IqkoClY/z5Ps3V/xvA3uWALFzB8SuFRB+OtJjbvqGO9QHct4RKEiIAdDdMWNhPHBoa4KDJszhs1+0j5DXp3N96BatO4sE4X6AzUWDb+YD3Lb3g2pPrr4YRvEDGRgE6YZLANV3nYmAaqVMqznWKnnXkbx4ccAEkmND4L/6FuJ+lv3mXpaSnLkDr3NhKjiCCH88BV+Nh3KD+dpp76hWvrgp5yrWvmJ6kpZU0jbgb4RXW0HkLb9TpVkyZgRV96RdWtr9JkQ0eSCgvNN+mtAOmDHmogijhrv0Eq54LINNsSNUjeeSPM51MiZRmXW68WgXjKbKKKpTzH0vj6E6p9vznlexRB2FulX+1fMj/toG8Js75GZXejpQ2XI9BWgrzZwsfhYx8m7jDa4/HOpsl6IqSm2ZYTRAydH+YhJjMWsMYnzXugmc= test@ansible2

Копируем ключ. Подключаемся к удаленному серверу и сохраняем этот ключ в authorized_keys. По адресу ~/.ssh/authorized_keys

touch ~/.ssh/authorized_keys
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDO2w+l5l0PM2cq8WAFWK3dJCAHuqYR5tno9aIQ1hoqMGdsZWRNntDQvIsId/uvXcqUZddK33QbhDjb4k5yq1IqkoClY/z5Ps3V/xvA3uWALFzB8SuFRB+OtJjbvqGO9QHct4RKEiIAdDdMWNhPHBoa4KDJszhs1+0j5DXp3N96BatO4sE4X6AzUWDb+YD3Lb3g2pPrr4YRvEDGRgE6YZLANV3nYmAaqVMqznWKnnXkbx4ccAEkmND4L/6FuJ+lv3mXpaSnLkDr3NhKjiCCH88BV+Nh3KD+dpp76hWvrgp5yrWvmJ6kpZU0jbgb4RXW0HkLb9TpVkyZgRV96RdWtr9JkQ0eSCgvNN+mtAOmDHmogijhrv0Eq54LINNsSNUjeeSPM51MiZRmXW68WgXjKbKKKpTzH0vj6E6p9vznlexRB2FulX+1fMj/toG8Js75GZXejpQ2XI9BWgrzZwsfhYx8m7jDa4/HOpsl6IqSm2ZYTRAydH+YhJjMWsMYnzXugmc= test@ansible2" >> ~/.ssh/authorized_keys

Для проверки подключения можете попробовать подключится по ssh.

ssh USER@HOST

Возвращаемся на master. Открываем inventory-файл.

cd /var/ansible
mcedit hosts.txt
  1. Добавляем группу.
    [test]

  2. Добавляем удаленный сервер в данную группу.
    Указываем название сервера: ansible2

  3. Указываем ip-адрес удаленного сервера:
    ansible_host=0.0.0.0

  4. Указываем порт:
    ansible_port=22

  5. Указываем пользователя:
    ansible_user=root

  6. Указываем путь к закрытому ключу на master-сервере:
    ansible_ssh_private_key_file=/root/.ssh/id_rsa

Вот что получилось:

[test]

 ansible2 ansible_host=0.0.0.0 ansible_port=22 ansible_user=root ansible_ssh_private_key_file=/root/.ssh/id_rsa

Для проверки подключения нам необходимо запустить команду ansible с ключами.

ansible -i hosts.txt all -m ping
  1. -i путь к файлу inventory.

  2. all - это запуск на всех серверах, указанных в данном inventory-файле. Можно запускать вместо all название группы.
    Пример: test

  3. -m модули ansible. В данном случае мы используем модуль ping, для проверки соединения.

Проверяем:

ansible -i hosts.txt all -m ping
The authenticity of host '192.168.0.24 (192.168.0.24)' can't be established.
ECDSA key fingerprint is SHA256:YejdpawsmOZjAh8M518r+lk7eRPTETdCEPsPzQezNd8.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

Соглашаемся с сохранением отпечатка и получаем следующее (это делается единоразово; в дальнейшем мы проверку отпечатка отключим):

ansible2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

На наш запрос ping нам пришел ответ pong. Это значит, что соединение установлено, и мы можем делать с сервером все, что нам необходимо.

Конфигурация

Для дальнейшего удобства редактируем файл конфигурации Ansible, в котором можно будет указать все нужные параметры для запуска.

Ключи:

  1. ask_pass - Может быть либо TRUE либо FALSE. Включить/выключить запрос пароля. По умолчанию стоит false. Если вы используете ключ для подключения, то менять нет необходимости.

  2. ask_sudo_pass - Значение либо TRUE, либо FALSE. По умолчанию указано TRUE. Это запрос sudo-пароля на удаленном сервере. Если на удаленном сервере отключен запрос sudo-пароля, то менять нет необходимости. Если запрос пароля необходим, измените данное значение на FALSE.

  3. forks - Количество параллельных процессов ansible. По умолчанию 5. Если у вас много серверов, то данное значение можно изменить. Тогда ansible будет выполнять настройки параллельно не на пяти серверах, а, например, на двадцати. Однако, нагрузка на сервер и сеть вырастет.

  4. host_ket_checking - проверка отпечатка hosts. По умолчанию указано TRUE. FALSE отключает проверку отпечатка.

  5. inventory - адрес inventory-файла по умолчанию.

  6. log_path - адрес log-файла по умолчанию.

Больше ключей в официальном мануале.

Открываем конфигурацию:

mcedit /etc/ansible/ansible.cfg

Добавляем настройки по умолчанию:

[defaults]

Отключаем проверку отпечатка:

host_key_checking = false

Указываем файл inventory, чтобы не писать всегда ключ -i при запуске ansible:

inventory = /var/ansible/hosts.txt

Указываем log-файл:

log_path = /var/log/ansible/ansible.log

Получается:

[defaults]
host_key_checking = false
inventory                  = /var/ansible/hosts.txt
log_path          = /var/log/ansible/ansible.log

Проверяем:

ansible all -m ping
ansible2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Как видим, без указания ключа -i все работает.

Командные модули ansible

Все командные модули вызывается ключом -m.

command - выполняет команды на выбранных группах без участие оболочки shell. Команда для выполнение вызывается ключом -a (аргумент) и помещается в кавычки.
Пример: ansible test -m command -a "date"

# ansible test -m command -a "date"
ansible2 | CHANGED | rc=0 >>
Вс 22 мая 2022 08:28:23 UTC

У этого модуля есть минус: не будут работать переменные "<", ">", "|", ";", "&". Например, вы не сможете делать grep с помощью данного модуля.

Более подробно: Официальный мануал.

shell - Делает все тоже самое, что и модуль command, только с участием оболочки shell.

Пример

ansible test -m shell -a "cat /var/log/syslog | tail -5"
ansible2 | CHANGED | rc=0 >>
May 22 08:27:30 ansible2 python3[7211]: ansible-ansible.legacy.command Error Executing CMD:'“time”' Exception:[Errno 2] No such file or directory: b'\xe2\x80\x9ctime\xe2\x80\x9d'
May 22 08:28:23 ansible2 python3[7238]: ansible-ansible.legacy.command Invoked with _raw_params=date _uses_shell=False warn=False stdin_add_newline=True strip_empty_ends=True argv=None chdir=None executable=None creates=None removes=None stdin=None
May 22 08:29:23 ansible2 systemd[1]: session-16.scope: Succeeded.
May 22 08:32:58 ansible2 systemd[1]: Started Session 17 of user nik.
May 22 08:32:59 ansible2 python3[7381]: ansible-ansible.legacy.command Invoked with _raw_params=cat /var/log/syslog | tail -5 _uses_shell=True warn=False stdin_add_newline=True strip_empty_ends=True argv=None chdir=None executable=None creates=None removes=None stdin=None

Более подробно: Официальный мануал.

copy - модуль позволяет скопировать файл с master на удаленный сервер.

Аргументы: 

  1. scr - адрес файла на master;

  2. dest - адрес куда сохранить файл на удаленный сервер;

  3. owner - имя пользователя, кому будет принадлежать файл;

  4. group - группа, кому будет принадлежать файл;

  5. mode - права на файл.

Пример

ansible test -m copy -a "src=/var/ansible/test.txt dest=/var owner=test group=test mode=644"
ansible2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "dest": "/var/test.txt",
    "gid": 1000,
    "group": "test",
    "md5sum": "d41d8cd98f00b204e9800998ecf8427e",
    "mode": "0644",
    "owner": "test",
    "size": 0,
    "src": "/root/.ansible/tmp/ansible-tmp-1653209819.5195355-7928-33699130649859/source",
    "state": "file",
    "uid": 1000
}

Если делаете не из под root пользователя, то необходимо добавить -b (sudo) в конце.

ansible test -m copy -a "src=/var/ansible/test.txt dest=/var owner=nik group=nik mode=644" -b

В ином случае прав для записи в /var будет недостаточно, и будет выдана ошибка.

Более подробно: Официальный мануал.

file - выполняет действие с файлом. Удаляет, изменяет права пользователя и много чего еще.

Аргументы:

  1. path - путь до файла на удаленном хранилище;

  2. state - состояние.

Пример: Удалим файл, который мы скачали с помощью предыдущего модуля:

ansible test -m file -a "path=/var/test.txt state=absent"
ansible2 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "path": "/var/test.txt",
    "state": "absent"
}

Более подробно: Официальный мануал.

apt - модуль для установки пакетов на удаленном сервере при условии использования операционных системах Debian и основанных на них (UbuntuLinux Mint и т. п.). При использовании Red Hat ОС можно использовать модуль yum.

Аргументы:

  1. name - имя сервиса, пакета;

  2. state - команда установить, удалить, обновить.

Пример:

ansible test -m apt -a "name=htop state=latest"
ansible2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "cache_update_time": 1653211045,
    "cache_updated": false,
    "changed": false
}

Более подробно: Официальный мануал.

service - позволяет запускать/останавливать/перезагружать сервисы на удаленном сервере.

Аргументы:

  1. name - "название сервиса";

  2. state - команда для изменения состояния;

  3. enabled - когда необходимо добавить в автозагрузку.

Пример

test -m service -a "name=nginx state=started"
ansible2 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "msg": "Could not find the requested service nginx: host"
}

В данном случае возвращается ошибка, т.к. nginx не установлен на удаленном сервере. 

Более подробно: Официальный мануал.

Ansible-playbooks

Ansible Playbooks - это система управления конфигурацией и развертыванием на нескольких удаленных серверах. Формат файла: yml.

Пример: выше мы писали все команды вручную. С помощью playbook мы сможем все это объединить в один файл и запускать с помощью одной команды.

Запуск:

ansible-playbook и Адрес playbook.

Пример:

ansible-playbook playbook.yml

Как пример напишем playbook, который будет отправлять ping на все удаленные сервера.

Для начала создадим файл:

touch playbook.yml

Далее открываем:

mcedit playbook.yml

Напоминаем, что это формат yml, который не любит TAB, поэтому никогда его там не используйте.

Ключи:

  1. name - присваивание названия группе/команде.
    Пример: name: Test PING

  2. hosts - указание группы серверов, на которых необходимо выполнить данную команду.
    Пример: hosts: all

  3. become - запуск sudo (если вы делаете настройки не под root-пользователем).

  4. tasks - указываем, что дальше будет идти исполняемая команда.

  5. name - присваивание названия команде.

Получается так:

- name: Test PING.  
  hosts: all  
  become: yes
  tasks:  
    - name: ping
    ping:

ping: - это название модуля, который мы просим использовать.

Пробуем запустить:

ansible-playbook playbook.yml
PLAY [Test PING.] ************************************************************************************TASK [Gathering Facts] *******************************************************************************ok: [ansible2]
TASK [ping] ******************************************************************************************ok: [ansible2]
PLAY RECAP *******************************************************************************************ansible2                   : 
ok=2    
changed=0    
unreachable=0    
failed=0    
skipped=0    
rescued=0    
ignored=0

playbook вернул ok. Значит ping прошел, и playbook написан без ошибок.

Красиво написанные ansible playbook не обходятся без так называемых Roles:

Ansible Roles

Ansible Roles - это роли, позволяющие автоматически загружать связанные переменные, файлы, задачи, обработчики и другие артефакты Ansible на основе известной файловой структуры. После того, как вы сгруппируете свой контент по ролям, вы сможете легко использовать его повторно и загружать в репозитории - для использования его в дальнейшем на других хостах. Оф. мануал.

Roles  позволяет создать удобную и понятную структуру директорий. Это очень упрощает администрирование в дальнейшем и позволяет понимать чужие playbook.

Для создания структуры директорий используется команда:

ansible-galaxy init <Название>

# ansible-galaxy init test
- Role test was created successfully

Получаем такую структуру:

.└── test    
     ├── defaults    
     │   └── main.yml    
     ├── files    
     ├── handlers    
     │   └── main.yml    
     ├── meta    
     │   └── main.yml    
     ├── README.md    
     ├── tasks    
     │   └── main.yml    
     ├── templates    
     ├── tests    
     │   
     ├── inventory    
     │   └── test.yml
         └── vars
         └── main.yml

defaults - присваиваются переменные по умолчанию. Данные переменные имеют низкий приоритет. Использоваться будут только в том случае, если до этого данная переменная не была объявлена ранее. В данной директории мы присваиваем defaults-переменные.
Пример: DOC_ROOT: /var/www/DOMAIN_NAME.com/

files - хранятся файлы, которые необходимо будет передать на удаленные сервера с помощью ролей. В данную директорию мы складываем файлы, которые необходимо перенести и использовать на удаленных серверах.
Пример: В данную директорию помещается код площадки для копирования на удаленный сервер.

handlers - это обработчик, который выполняется только при запуске через notify -директиву. Обработчик выполняется в самом конце playbook, когда все задачи завершены без ошибок. В данную директорию мы помещаем обработчики, которые необходимо запустить в самом конце.

Пример:

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

meta - метаданные для роли, включая ролевые зависимости. В данной директории мы указываем так называемые метаданные для проекта. Название компании, название репозиторий, название проекта.

По умолчанию main-файл в данной директории выглядит так:

galaxy_info:
  author: your name
  description: your role description
  company: your company (optional)
  license: license (GPL-2.0-or-later, MIT, etc)
  min_ansible_version: 2.1
  galaxy_tags: []
dependencies: []
root@php:/var/ansible/test/meta#
root@php:/var/ansible/test/meta# cat main.yml
galaxy_info:
  author: your name
  description: your role description
  company: your company (optional)
  license: license (GPL-2.0-or-later, MIT, etc)
  min_ansible_version: 2.1
  galaxy_tags: []
dependencies: []

tasks - основной список задач для Ansible. В данной директории мы указываем все задачи, которые необходимо выполнить на удаленном сервере.
Пример: установить nginx и открыть 80 порт.

templates - в данной директории хранятся файлы шаблоны для развертывания на удаленном сервере. 
Пример: Мы помещаем default страницу nginx и default index.php, после чего передаем на удаленный сервер

tests - это название проекта, который был создан при использовании команды ansible-galaxy init <Название>. В данной директории будет создан inventory-файл, в котором будут перечислены хосты удаленных серверов. В test.yml помещаются переменные для подключения к удаленному серверу (пользователь, порт и т.д.).

vars - аналог defaults. В данной директории присваиваются переменные для запуска ролей. Эта директория имеет приоритет выше, чем defaults.

Итог

В этой статье мы с вами рассмотрели теоретическую часть, которая нам будет необходима в будущем. В следующей статье мы будем использовать теорию на практике: построим план действий и развернем LEMP на удаленном сервере с помощью инструмента автоматизации.

Если у вас остались вопросы, можете задавать их в комментариях. Ставьте лайки и подписывайтесь на нас - в дальнейшем будем публиковать еще больше обучающих статьей.

Также подписывайтесь на наш telegram-канал DevOps FM.

Рекомендации для чтения:

Зашита от dos/ddos.

Обучение docker.

10 частых ошибок в настройке nginx.

Настройка LEMP сервера с нуля.

Комментарии (4)


  1. Ul3ainee
    31.05.2022 15:02
    +2

    echo ~/.ssh/authorized_keys >> ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDO2w+l5l0PM2cq8WAFWK3dJCAHuqYR5tno9aIQ1hoqMGdsZWRNntDQvIsId/uvXcqUZddK33QbhDjb4k5yq1IqkoClY/z5Ps3V/xvA3uWALFzB8SuFRB+OtJjbvqGO9QHct4RKEiIAdDdMWNhPHBoa4KDJszhs1+0j5DXp3N96BatO4sE4X6AzUWDb+YD3Lb3g2pPrr4YRvEDGRgE6YZLANV3nYmAaqVMqznWKnnXkbx4ccAEkmND4L/6FuJ+lv3mXpaSnLkDr3NhKjiCCH88BV+Nh3KD+dpp76hWvrgp5yrWvmJ6kpZU0jbgb4RXW0HkLb9TpVkyZgRV96RdWtr9JkQ0eSCgvNN+mtAOmDHmogijhrv0Eq54LINNsSNUjeeSPM51MiZRmXW68WgXjKbKKKpTzH0vj6E6p9vznlexRB2FulX+1fMj/toG8Js75GZXejpQ2XI9BWgrzZwsfhYx8m7jDa4/HOpsl6IqSm2ZYTRAydH+YhJjMWsMYnzXugmc= test@ansible2

    А вы уверены что ничего не напутали?


    1. thisprogame Автор
      31.05.2022 17:48

      Спасибо, исправил.


  1. edo1h
    01.06.2022 02:58
    +1

    Установка Ansible

    можно (и нужно) было сократить до apt install ansible


  1. SolarisSun
    02.06.2022 18:06
    +1

    Напоминаем, что это формат yml, который не любит TAB, поэтому никогда его там не используйте.

    Просто так случайным ТАБом на 1518 строке нас не испугаешь :) Для того чтобы не бояться клавиши TAB в YAML-конфигах рекомендуется поставить linter. Он много что еще умеет проверять и подсказывать