Копирование файлов посредством ansible
Что-то не смог понять из примеров в документации — как же скопировать файл с удалённого сервера на локальный и наоборот? Вот есть модули copy и fetch, например. Вот я пишу в плейбуке:
- name: Copy file from remote host to local machine fetch: src=/tmp/somefile dest=/tmp/fetched(взято из доки)
Как указать ansible, с какого хоста я хочу скопировать файл? Ну и соответственно, при копировании на удалённый хост, где указывать этот удалённый хост? P.S. Немного разобрался: Чтобы скопировать файл с удалённого хоста на локальный:
- hosts: localhost vars_files: - config.yml tasks: - include: ../share/dev.yml - name: Get file from remote fetch: src="https://ru.stackoverflow.com/questions/506325/%3C%3C%20remote_sources_path%20%3E%3E/test.txt" dest="backup" delegate_to: '>' tags: fetch
Файл test.txt будет лежать в каталоге ./backup/localhost/<< remote_sources_path >> Скопировать с локального на удалённый:<>
- name: Send file to remote copy: src="https://ru.stackoverflow.com/questions/506325/%3C%3C%20local_sources_path%20%3E%3E/to_remote_test.txt" dest=> delegate_to: '>' tags: fetch
Подозреваю, что, если указать в начале — hosts: << remote_host >> вместо localhost, то delegate_to использовать не нужно.<>
Отслеживать
Sviderskiy Dmitriy
задан 24 мар 2016 в 15:11
Sviderskiy Dmitriy Sviderskiy Dmitriy
154 1 1 золотой знак 1 1 серебряный знак 11 11 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Ну зачем же так сложно? ))
Все эти задачи решаются и без мороки с Delegation и Local Actions, вам же самим потом сложнее будет разбираться в собственных плейбуках или читать чужие, написанные по-простому.
В каждом плейбуке вы указываете на каких хостах запускать задачи — пишете hosts: ‘>’ (если хотите из командной строки управлять списком через —extra-vars «target=10.0.100.123») или hosts: dbservers (если фиксировать плейбук на группу хостов):
--- # This playbook for quick tests. - name: quick tests hosts: '>' become: true become_user: root roles: - role1 - role2 tasks: - name: install mc yum: name=mc state=latest - name: install wget yum: name=wget state=latest
Эти хосты — «удалённый» сервер, ну а «локальный» — это само собой хост, на котором находится ваш плейбук.
- Для копирования файла с локального сервера на удалённый — используете модуль copy
- Для копирования файла с удалённого сервера на локальный — используете модуль fetch
И в copy и в fetch src — это откуда брать файл, а dest — это куда положить файл. Для copy src=local а dest=remote, для fetch — наоборот src=remote, а dest=local.
Вот и всё. Вам кстати именно об этом говорили в комментарии к вашему последнему вопросу: «В целом Ansible подразумевает то, что ты уже на удаленном хосте», не надо нарезать лишние петли вокруг локалхоста.
PS Если нужно копировать с произвольного хоста на произвольный хост — то смотрите в сторону модуля syncronyze на основе rsync. Но вообще в 90% случаев вам понадобится копирование с локального хоста на удалённый и будете использовать copy или template.
Про Ansible для новичков: Практика (часть I)

Любое написание ansible-роли сопровождается планом. В этот план необходимо будет включить все, что необходимо будет установить и настроить.
- Пункт 1. Первоначальная настройка сервера.
- Пункт 2. Установка LEMP.
- Пункт 3. Права и пользователь.
- Пункт 4. Настройка LEMP.
- Пункт 5. Перенос кода площадки и БД.
- Пункт 6. Тестирование.
- Пункт 7. Итог.
Так как если рассмотреть все пункты в одной статье, то материал получается слишком объемным, было принято решение разделить его на две части. В первой части обсудим 1, 2 и 3 пункты плана. В следующей — 4, 5, 6, 7 пункты.
На текущий момент в директории /var/ansible, которую мы создали и использовали в предыдущей статье, имеются файлы:
- hosts.txt — файл с ip адресами удаленных машин.
- playbook.yml — playbook из первой статьи.
Генерируем роль, роль будет называться LEMP (название можете использовать любое).
ansible-galaxy init LEMP
Открываем playbook.yml. Добавляем:
- name: Install LEMP server hosts: all become: yes roles: - LEMP
- name — название данного ansible проекта.
- hosts — на каких хостах запускать.
- become — использование sudo.
- roles — подключаем созданную роль.
Сохраняем файл и выходим. Больше данный файл нам не понадобится.
Структура директорий в директории ansible:
.
├── hosts.txt
├── LEMP
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── README.md
│ ├── tasks
│ │ ├── main.yml
│ ├── templates
│ ├── tests
│ │ ├── inventory
│ │ └── test.yml
│ └── vars
│ └── main.yml
└── playbook.yml
Пункт 1. Первоначальная настройка сервера.
В данном пункте необходимо настроить сервер для работы веб приложений. Выполнить установку default приложений, настроить дату, название сервера и локализацию.
Необходимо установить приложения и пакеты:
- dirmngr mc iotop htop telnet tcpdump nmap curl console-cyrillic hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog
Первое, что необходимо сделать, — зайти в директорию с заданиями по пути /var/ansible/LEMP/tasks/ .
cd /var/ansible/LEMP/tasks/
Создаем yml-файл для пункта 1. Это необходимо, чтобы в дальнейшем удобнее было администрировать ansible-роль. Потому что все будет разделено по своим файлам и загружаться только тогда, когда нам это необходимо. Для каждого пункта статьи мы будем создавать отдельный файл конфигурации.
Создаем yml файл. Название можно выбрать любое:
touch default_settings.yml
Заходим в главный файл конфигурации в заданиях ./LEMP/tasks/main.yml.
Необходимо подгрузить выше созданный файл конфигурации. Для этого используется модуль include_tasks и название файла:
- include_tasks: default_settings.yml
Сохраняем изменения в main.yml.
Теперь начинаем редактировать default_settings.yml . Для установки нам потребуется использование shell модуль. Для начала необходимо обновить все репозитории. Добавляем:
--- - name: update repo. shell: apt update
- name — название задания.
- shell — модуль, с помощью которого выполняется команда.
Сохраняем и запускаем из директории, где лежит playbook.yml (/var/ansible/):
ansible-playbook playbook.yml PLAY [Install LEMP server] ************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [ansible2] TASK [test : update repo.] ************************************************************************************************************************************************************************************** changed: [ansible2] PLAY RECAP ****************************************************************************************************************************************************************************************************** ansible2 : ok=2 changed=1 unreachable=0 failed=0
Как видим из вывода, ошибки не наблюдается.
Начинаем устанавливать приложения и пакеты.
Также будем использовать модуль shell, так как нужных нам утилит достаточно много и устанавливать все через модуль apt будет достаточно долго и проблематично.
В файл default_settings.yml добавляем:
- name: install default app. shell: cmd: "apt install -y dirmngr mc iotop htop telnet tcpdump nmap curl hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog"
cmd — ключ, в котором мы указываем команду.
# ansible-playbook playbook.yml PLAY [Install LEMP server] ************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [ansible2] TASK [test : update repo.] ************************************************************************************************************************************************************************************** changed: [ansible2] TASK [test : install default app.] ****************************************************************************************************************************************************************************** changed: [ansible2] PLAY RECAP ****************************************************************************************************************************************************************************************************** ansible2 : ok=3 changed=2 unreachable=0 failed=0
Далее необходимо настроить время, локализацию и hostname.
Нам понадобятся команды:
Для установки времени:
timedatectl set-timezone Europe/Moscow
Для установки локализации:
locale-gen ru_RU.UTF-8 update-locale LANG=en_US.UTF-8 LC_TIME="ru_RU.UTF-8"
Для установки hostname:
hostnamectl set-hostname DOMAIN_NAME
Так как у нас используются плавающие переменные, мы добавим их в директорию vars. Чтобы в дальнейшем на других серверах нам было удобно их менять.
- Europe/Moscow
- ru_RU.UTF-8
- en_US.UTF-8
- DOMAIN_NAME
Открываем файл, где указываем переменные для всего проекта. Файл находится по адресу: /etc/ansible/test/vars/main.yml
DOMAIN_NAME: domain_name locale1: ru_RU.UTF-8 locale2: en_US.UTF-8 time_zone: Europe/Moscow
В дальнейшем для вызова переменной необходимо будет использовать скобки >.
Идем в default_settings.yml и добавляем задания:
- name: time shell: cmd: "timedatectl set-timezone >" - name: locale settings shell: cmd: 'locale-gen > && update-locale LANG=> LC_TIME=">"' - name: hostname shell: cmd: "hostnamectl set-hostname >"
Запускаем и проверяем.
ansible-playbook playbook.yml PLAY [Install LEMP server] ************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [ansible2] TASK [test : update repo.] ************************************************************************************************************************************************************************************** changed: [ansible2] TASK [test : install default app.] ****************************************************************************************************************************************************************************** changed: [ansible2] TASK [test : time] ********************************************************************************************************************************************************************************************** changed: [ansible2] TASK [test : locale settings] *********************************************************************************************************************************************************************************** changed: [ansible2] TASK [test : hostname] ****************************************************************************************************************************************************************************************** changed: [ansible2] PLAY RECAP ****************************************************************************************************************************************************************************************************** ansible2 : ok=6 changed=5 unreachable=0 failed=0
Для проверки можно отправить команду с помощью модуля shell.
ansible all -m shell -a "date && cat /etc/hostname" ansible2 | CHANGED | rc=0 >> Пн июн 20 14:41:49 MSK 2022 domainname
Все работает. Пункт 1 завершён.
Итоговый файл default_settings.yml:
- name: update repo. shell: apt update - name: install default app. shell: cmd: "apt install -y dirmngr mc iotop htop telnet tcpdump nmap curl hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog" - name: time shell: cmd: "timedatectl set-timezone >" - name: locale settings shell: cmd: 'locale-gen > && update-locale LANG=> LC_TIME=">"' - name: hostname shell: cmd: "hostnamectl set-hostname >"
Пункт 2. Установка LEMP.
В данном пункте необходимо установить:
- nginx apache2 mysql exim4
Для установки этих пакетов будем использовать модуль apt.
Нам понадобится разделить установку пакетов по разным yml файлам. Так как в дальнейшем будет удобно добавлять и изменять конфигурации для каждого пакета по отдельности.
Создаем 4 файла:
cd /var/ansible/LEMP/tasks touch mysql_install.yml nginx_install.yml apache2_install.yml exim4_install.yml
Подключаем загрузку задач в main.yml:
#####install mysql - include_tasks: mysql_install.yml #####install nginx - include_tasks: nginx_install.yml #####install apache2 - include_tasks: apache2_install.yml #####install exim4 - include_tasks: exim4_install.yml
Добавляем в файл nginx_install.yml:
- name: Install nginx apt: name: nginx state: latest
- — name — название задачи.
- apt — модуль.
- name — название пакета.
- state — версия пакета; в данном случае последняя доступная версия.
Добавляем в файл apache2_install.yml:
- name: Install apache2 apt: name: apache2 state: latest
Добавляем в файл exim4_install.yml:
- name: Install exim4 apt: name: exim4 state: latest
При установке mysql необходимо будет подключить репозитории. Открываем mysql_install.yml. Используем модуль get_url для загрузки deb. файла с официального сайта.
- name: add mysql repo get_url: url: https://dev.mysql.com/get/mysql-apt-config_0.8.6-1_all.deb dest: "/tmp" mode: 0440
- get_url — модуль загрузки файла по ссылке, аналог wget.
- dest — место куда будет загружен файл.
- mode — присваиваем права загруженному файлу.
Следующим действием необходимо установить скачанный репозиторий.
- name: install mysql repo apt: "deb=/tmp/mysql-apt-config_0.8.6-1_all.deb" become: true
Добавляем ключ репозитория и обновляем репозитории:
- name: add key mysql and update repo shell: "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 && apt update"
Устанавливаем python-mysqldb для дальнейшего взаимодействия.
- name: install python-mysqldb apt: name: python-mysqldb state: present update_cache: yes
Проверяем доступную версию и выводим на экран для проверки:
- name: check latest version of mysql 5.7 command: bash -c "apt-cache showpkg mysql-server|grep 5.7|head -1|cut -d' ' -f1" register: latestmysql57 - debug: msg=">"
- name: install mysql 57 apt: name: mysql-server=> state: present update_cache: yes
ansible-playbook playbook.yml TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/mysql_install.yml for ansible2 TASK [LEMP : add mysql repo] ************************************************************************************************************************************************************************************ok: [ansible2] TASK [LEMP : install mysql repo] ********************************************************************************************************************************************************************************ok: [ansible2] TASK [LEMP : add key mysql] *************************************************************************************************************************************************************************************changed: [ansible2] TASK [LEMP : update repo] ***************************************************************************************************************************************************************************************changed: [ansible2] TASK [LEMP : check latest version of mysql 5.7] *****************************************************************************************************************************************************************changed: [ansible2] TASK [LEMP : debug] *********************************************************************************************************************************************************************************************ok: [ansible2] => < "msg": "5.7.38-1debian10" >TASK [LEMP : install mysql 57] **********************************************************************************************************************************************************************************changed: [ansible2] TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/nginx_install.yml for ansible2 TASK [LEMP : Install nginx] *************************************************************************************************************************************************************************************ok: [ansible2] TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/apache2_install.yml for ansible2 TASK [LEMP : Install apache2] ***********************************************************************************************************************************************************************************ok: [ansible2] TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/exim4_install.yml for ansible2 TASK [LEMP : Install exim4] *************************************************************************************************************************************************************************************ok: [ansible2]
Все необходимые нам 4 сервиса установлены.
Для проверки можно отправить запрос на удаленный сервер с помощью shell модуля :
ansible all -m shell -a "systemctl status nginx apache2 mysql exim4"
Пункт 3. Права и пользователь.
- Cоздать пользователя/домашнюю директорию.
- Настроить права директории /var/www.
- Создать БД и пользователя БД.
Создаем конфигурационный файл для данного пункта:
touch /var/ansible/LEMP/tasks/default_user_settings.yml
Подключаем конфигурацию в main-файле:
#####default user settings - include_tasks: default_user_settings.yml
Создаем структуру директорий по аналогии с нашей первой статьей.
На данном этапе нам необходимо добавить переменные в /var/ansible/LEMP/vars, номер пользователя/группы в системе и пароль.
User_uid: 10000 Group_GID: 10000 user_password: password
Открываем default_user_settings.yml. Для добавление пользователя необходимо будет использовать модуль user и group. Добавляем группу:
- name: add group group: name: ">" state: present gid: ">"
- state — состояние группы, если группа будет имеется на сервер, то ansible не будет ее пересоздавать или пытаться изменить.
- gid — номер группы в системе
- «>» — переменная которую мы добавляли в Пункте 1.
- name: add user user: name: ">" password: ">" uid: ">" group: ">" state: present update_password: on_create home: "/var/www/>" shell: /bin/bash
- password: «>» — так как ansible не может передавать не зашифрованный пароль, мы его шифруем с помощью password_hash(‘sha512’).
- update_password: on_create — означает, что пароль будет добавлен только один раз при первом выполнении данной команды, то есть он не будет перезаписан в случае повторного запуска роли.
- home: «/var/www/>» — домашняя директория пользователя.
Таким образом, пользователь и группы созданы. Теперь необходимо создать структуру директорий. Нам поможет модуль file:
- name: create home directory file: path: "/var/www/>" owner: ">" group: ">" mode: 0751 state: directory
- path: «/var/www/>» — адрес директории
- owner: «>» — Присваиваем директорию пользователю
- group: «>» — Присваиваем директорию группе.
- mode: 0751 — права директории
- state: directory — означает, что создается именно директория а не файл.
Следующим шагом необходимо создать базу данных и пользователя базы данных.
Так как настройка mysql ведется в определенном файле, будем использовать именно его (/var/ansible/LEMP/tasks/mysql_install.yml).
Во-первых, необходимо создать root пароль. Добавляем root пароль в переменные /var/ansible/LEMP/vars/main.yml.
mysql_root_password: password
Идем в /var/ansible/LEMP/tasks/mysql_install.yml. Добавляем:
- name: update mysql root password for all root accounts become: true mysql_user: name: root host: ">". password: ">" login_user: root login_password: '' check_implicit_admin: yes priv: "*.*:ALL,GRANT" state: present with_items: - 127.0.0.1 - ::1 - localhost
- Используется модуль mysql_user.
- name: root — имя пользователя которого используем в mysql.
- host: «>». — адреса хостов, на которых будет изменен пароль; все хосты указаны в with_items.
- password: «>» — новый пароль.
- check_implicit_admin — производит проверку входа в mysql без пароля (если, например, пароль указан в .my.cnf в home директории); Если войти без пароля не удалось, будет использован пароль указанный в login_password.
- login_user: root и login_password: » пользователь и пароль, под которым мы заходим в mysql.
- priv: «*.*:ALL,GRANT» — какие привилегии присваиваем пользователю.
- state: present — присваиваем единоразово.
Далее добавляем базу данных. Добавляем переменные название базы данных и пользователя в /var/ansible/LEMP/vars/main.yml:
name_db: domain_name_db user_db: domain_name_db password_user_db: password
Идем обратно в /var/ansible/LEMP/tasks/mysql_install.yml. Добавляем базу данных:
- name: Create a new database with name 'DOMAIN_NAME_DB' mysql_db: login_user: root login_password: ">" name: ">" state: present
- Используем модуль mysql_db.
- name: «>» — название базы данных.
Добавляем пользователя и присваиваем права на базу данных.
- name: add user DOMAIN_NAME_USR mysql_user: login_user: root login_password: ">" host: localhost name: ">" password: ">" priv: '>.*:ALL,GRANT' state: present
Пользователь базы данных и база данных созданы. Итоговые файлы в рамках данного пункта следующие:
- name: add group group: name: ">" state: present gid: ">" - name: add user user: name: ">" password: ">" uid: ">" group: ">" state: present update_password: on_create home: "/var/www/>" shell: /bin/bash - name: create home directory file: path: "/var/www/>" owner: ">" group: ">" mode: 0751 state: directory - name: create other directory file: path: "/var/www/>/data" owner: ">" group: ">" mode: 0755 state: directory - name: create other directory file: path: "/var/www/>/log" owner: ">" group: ">" mode: 0755 state: directory - name: create other directory file: path: "/var/www/>/sess" owner: ">" group: ">" mode: 0755 state: directory - name: create other directory file: path: "/var/www/>/tmp" owner: ">" group: ">" mode: 0755 state: directory - name: create other directory file: path: "/var/www/>/upload" owner: ">" group: ">" mode: 0755 state: directory - name: create other directory file: path: "/var/www/>/log/apache2" owner: ">" group: ">" mode: 0755 state: directory - name: create other directory file: path: "/var/www/>/log/nginx" owner: ">" group: ">" mode: 0755 state: directory
- name: add mysql repo get_url: url: https://dev.mysql.com/get/mysql-apt-config_0.8.6-1_all.deb dest: "/tmp" mode: 0440 - name: install mysql repo apt: "deb=/tmp/mysql-apt-config_0.8.6-1_all.deb" become: true - name: add key mysql and update repo shell: "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 && apt update" - name: install python-mysqldb apt: name: python-mysqldb state: present update_cache: yes - name: check latest version of mysql 5.7 command: bash -c "apt-cache showpkg mysql-server|grep 5.7|head -1|cut -d' ' -f1" register: latestmysql57 - debug: msg=">" - name: install mysql 57 apt: name: mysql-server=> state: present update_cache: yes - name: update mysql root password for all root accounts become: true mysql_user: name: root host: ">" password: ">" login_user: root login_password: 12345 check_implicit_admin: yes priv: "*.*:ALL,GRANT" state: present with_items: - 127.0.0.1 - ::1 - localhost - name: Create a new database with name 'DOMAIN_NAME_DB' mysql_db: login_user: root login_password: ">" name: ">" state: present - name: add user DOMAIN_NAME_USR mysql_user: login_user: root login_password: ">" host: localhost name: ">" password: ">" priv: '>.*:ALL,GRANT' state: present
DOMAIN_NAME: domain_name locale1: ru_RU.UTF-8 locale2: en_US.UTF-8 time_zone: Europe/Moscow User_uid: 10000 Group_GID: 10000 user_password: password mysql_root_password: password name_db: domain_name_db user_db: domain_name_usr password_user_db: password
Итог
В этой статье мы с вами рассмотрели первую половину практической части серии обучающих статей по ansible. Мы использовали на практике полученные знания из первой статьи. В следующей статье мы рассмотрим остальные пункты, после чего у нас будет готова полноценная ansible-роль для быстрого развертывания простых проектов. Также мы выложим данную ansible-роль на github для ознакомления.
Если у вас остались вопросы, можете задавать их в комментариях. Ставьте лайки и подписывайтесь на нас — в дальнейшем будем публиковать еще больше обучающих статьей.
Также подписывайтесь на наш telegram-канал DevOps FM.
Рекомендации для чтения:
Ansible FAQ

Мы уже ознакомились с тем, что нужно знать перед началом работы с Ansible.Теперь давайте разберем самые Часто Задаваемые Вопросы, или сокращенно FAQ. Они помогут вам глубже понять тонкости и принцип работы с Ansible. На ранних этапах можете использовать эту статью как некую шпаргалку.
- Что такое Ansible
- Для чего он используется
- Есть ли Ansible на Windows
- Ansible сложный
- Какие у него преимущества
- Ansible – язык программирования
- Или каков синтаксис сценариев
- Как работает Ansible А где этот список хостов
- Как сохранить результат команды в переменную
- Как проверить определена ли переменная
- Где можно хранить переменные и какова их приоритетность
- Как вывести содержимое исполненной команды
- Как проверить существует ли файл
- Как запускать задачи на разных ОС и различных версиях
- Как добавить репозиторий
- Как установить конкретной версию пакета используя pip3
- Как склонировать в определенную папку только ветку master из github
- Как создать системную учетную запись
- Как запустить и поставить службу на автозагрузку
- Как скопировать файл с заданными правами
- Как скачать и разархивировать файл в директорию
- Как запустить docker контейнер c healtcheck’ом в Ansible
- Как запустить playbook с определенной задачи
- Как запускать / пропускать несколько задач одной тематики
- Сталкиваюсь с ошибками. Как детальнее узнать в чем проблема
- Наверняка кто-то уже делал задачу которая мне необходима, где ее найти
- Источники изучения Ansible
Что такое Ansible?
Это ПО с открытым исходным кодом, написанное на Python.
Для чего он используется?
Для автоматизированного выполнения задач поставки программного обеспечения, управления конфигурацией и развертывания приложений.
Есть ли Ansible на Windows?
Нет, но системы с поддержкой WSL (Windows Subsystem for Linux) позволяют запускать дистрибутивы Linux без накладных расходов на виртуальные машины.
Ansible сложный?
Нет, это простой и эффективный инструмент.
Такие сложные задачи как подготовка сервера, управление конфигурацией программных и аппаратных пакетов, развертывание приложений на разных системах, выполняются посредством написания сценариев на человекочитаемом языке YAML. А сами сценарии сродни файлам README.
Какие у него преимущества?
- Безагентная модель работы. Не требуется предустановка софта на удаленные серверы. Достаточно лишь иметь SSH соединение и python 2.6+.
- Встроенные модули. Позволяют решать широкий спектр задач, так же есть возможность написать свой модуль https://habr.com/ru/company/southbridge/blog/646147/
- Идемпотентность. Свойство, которое при многократном выполнении приводит к тому же результату, что и однократное выполнение.
- Распространенность (популярность). Продукт компании RedHat. Поддерживается большим числом вендоров.
Ansible – язык программирования? Или каков синтаксис сценариев?
- Все сценарии Ansible пишутся на YAML — это формат файла, напоминающий JSON, но намного проще для восприятия человеком.
- Строки можно не заключать в кавычки, даже если они содержат пробелы, но рекомендуется в случае использования двойных фигурных скобок, которые используются для подстановки значений переменных.
Пример: «>» - Булевы выражения. Ansible достаточно гибкая система в отношении значений «истина» и «ложь».
Истина в YAML (true, Тгuе, TRUE, yes, Yes, YES, on, On, ON y, Y) Ложь в YAML (false, False, FALSE, no, No, NO, off, Off, OFF, n, N)
- Списки в YAML похожи на массивы в JSON и Ruby или списки в Python. И оформляются с помощью дефиса.
- ntp - wget - curl
- Табуляция или пробелы? Спецификация YAML обязует использовать только пробелы.
Как работает Ansible?
Он устанавливает параллельные SSH-соединения и выполняет задачи по списку на всех хостах
А где этот список хостов?
В так называемом инвентаре, файле hosts
node1 ansible_host=172.16.10.11 node2 ansible_host=172.16.10.12 node3 ansible_host=172.16.10.13 [first-group] node1 ansible_host=172.16.10.11 node2 ansible_host=172.16.10.12 [second-group] node3 ansible_host=172.16.10.13
Как сохранить результат команды в переменную?
С помощью модуля register
- name: Run some command and register output as variable shell: command register: command_result
Как проверить определена ли переменная?
Для этого используется опция is defined (определена) или is not defined (не определена):
Где можно хранить переменные и какова их приоритетность?
Основные правила приоритета выглядят так: (От наиболее приоритетного к наименьшему)
0. extra-vars (Переменные передаваемые при вызове playbook’а через параметр -e «some_var=my_value»
1. role params (Переменные описанные в роли и включениях)
2. set_facts / registered vars (Переменные описанные в playbook используя set_facts и зарегистрированные переменные.)
3. include_vars (Добавляется указанием файла с переменными и списка переменных)
4. task vars — block_vars (Переменные только для задачи / блока)
5. role vars (Описываются в папке роли roles/role/vars/main.yml)
6. play vars_files / play vars_prompt (С указанием файла переменных / ручным вводом по запросу)
7. play vars (Указываются в playbook используя vars:)
8. host facts (Факты хоста)
9. host_vars (playbook – inventory)
10. group_vars (playbook – inventory — group_vars/all)
11. role defaults (Описываются в папке роли roles/role/defaults/main.yml)
Как вывести содержимое исполненной команды?
С помощью модуля debug
- name: Show command output debug: var: command_result.stdout
Как проверить существует ли файл?
С помощью модуля stat
- name: Print a debug message debug: msg: "Path doesn't exist" when: path_var.stat.exists is not defined
Как запускать задачи на разных ОС и различных версиях?
С помощью встроенных переменных ansible_os_family и ansible_distribution_major_version
В следующем примере запустится задача task1 в системах на основе Debian версии 10 и task2 на основе RedHat версии 8.
- name: Run task1 if OS is Debian 10 … when: ansible_os_family == "Debian" and ansible_distribution_major_version == "10" - name: Run task2 if OS is Red Hat 8 … when: ansible_os_family == "RedHat" and ansible_distribution_major_version == "8"
Как добавить репозиторий?
- name: "Add chrome deb repository" apt_repository: repo: deb http://dl.google.com/linux/chrome/deb/ stable main state: present
Удалить – написать absent в state
Как установить конкретной версию пакета используя pip3?
- name: Install specific bottle version with pip3 pip: name: bottle==0.12.19 executable: pip3
Как склонировать в определенную папку только ветку master из github?
- name: Example clone of a single branch git: repo: https://github.com/ansible/ansible-examples.git dest: /src/ansible-examples single_branch: yes version: master
Как создать системную учетную запись?
- name: Create consul user user: name: consul system: yes comment: "Consul Agent"
Как запустить и поставить службу на автозагрузку?
- name: Start and Enable httpd systemd: name: httpd.service state: started enabled: yes
Как скопировать файл с заданными правами?
- name: Copy file with owner and permission copy: src: /mine/ntp.conf dest: /etc/ntp.conf owner: user group: user mode: '0644'
Как скачать и разархивировать файл в директорию?
- name: Download and Unarchive file to directory unarchive: src: https://example.com/example.zip dest: /usr/local/bin remote_src: yes
Как запустить docker контейнер c healtcheck’ом в Ansible?
- name: Start container with healthstatus docker_container: name: nginx-proxy image: nginx:1.20 state: started healthcheck: # Check if nginx server is healthy by curl'ing the server. # If this fails or timeouts, the healthcheck fails. test: ["CMD", "curl", "--fail", "http://nginx.host.com"] interval: 1m30s timeout: 10s retries: 3 start_period: 30s
Как запустить playbook с определенной задачи?
С помощью ключа —start-at-task
ansible-playbook playbook.yml --start-at-task="Configure Nginx"
Как запускать / пропускать несколько задач одной тематики?
Задать tags: в плейбуке и использовать ключ —tags при запуске
ansible-playbook playbook.yml --tags=nginx,mysql
Пропускать задачи аналогично, задав tags в плейбуке и использовав —skip-tags при запуске
ansible-playbook playbook.yml --skip-tags=mysql
Сталкиваюсь с ошибками. Как детальнее узнать в чем проблема?
Используя ключ -v при запуске. (Для более детального вывода использовать -vvvv)
ansible-playbook playbook.yml -v
Наверняка кто-то уже делал задачу которая мне необходима, где ее найти?
https://galaxy.ansible.com. Вы можете ковырять, редактировать роли под ваши нужды.
Источники изучения Ansible
- Наш вводный бесплатный курс по Ansible.
- Документация на английском.
- Книга «Запускаем Ansible» Мозер Рене, Хоштейн Лорин на русском.
- Свободные источники: Google и YouTube.
- Наш курс «Ansible: Infrastructure as Code» без сухой теории, с реальной практикой, мемами и чатом для участников. Вас ждут 8 тем, 38 уроков, 10 часов видеолекций, 78 тестовых и 46 практических заданий, 36 часов стендов для выполнения практик.
Ansible — DevOps: Автоматизация локального окружения
Сайты это не только код, но и инфраструктура для их запуска. В первую очередь, в неё входят сервера, на которых крутится код, база данных и различные вспомогательные системы. Иногда все это помещается на один сервер, в более сложных ситуациях количество серверов измеряется тысячами, а для обслуживания таких систем привлекаются целые команды инженеров (разного рода администраторов). Независимо от размера сайта, проблемы обслуживания инфраструктуры у всех очень похожие. Поговорим об одной конкретной – настройке сервера.
Существуют подходы, которые позволяют избежать прямого взаимодействия с инфраструктурой. В рамках статьи они не рассматриваются, но знать про них полезно. К ним относятся классические хостинги с предустановленным софтом, serverless, хостинги статических сайтов, PaaS решения и kubernetes (и его аналоги)
В подавляющем большинстве случаев сервера арендуются у хостинговых компаний, таких как DigitalOcean или AWS. Делается это за 5 минут нажатием буквально нескольких кнопок. Нас попросят выбрать характеристики сервера, операционную систему и датацентр, в котором он будет развернут. В результате мы получаем машину (виртуальную) с предустановленной операционной системой и ip-адресом для входа по ssh.
Новая машина содержит только основную операционную систему с небольшим набором предустановленных программ. Перед тем, как запустить на ней какой-то сервис, например, обычный сайт, понадобится установить дополнительные пакеты. Набор пакетов зависит от стека технологий, на котором он написан. Если сайт «завернут» в Docker, то настройка значительно упрощается и сводится к установке самого Docker. В остальных случаях придется потратить какое-то время на донастройку и конфигурирование. Помимо пакетов часто требуется настраивать саму систему, менять конфигурационные файлы, права на файлы и директории, создавать пользователей и так далее:
# Как это могло бы быть # Сервер на Ubuntu # Заходим на удаленную машину ssh root@ipaddress # Создание пользователя для деплоя # Где-то здесь копируются ssh ключи sudo adduser deploy sudo apt install curl # установка Node.js curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - sudo apt install nodejs # установка и настройка Nginx sudo apt install nginx vim /etc/nginx/default.conf # Формирование структуры директорий для сервиса mkdir -p /opt/hexlet/versions/
Процесс первоначальной настройки занимает часы и даже дни. Постоянно придётся что-то подкручивать, донастраивать и устанавливать. Цикл повторится снова, когда понадобится перейти на новые версии пакетов. Снова придется заходить на сервер, вспоминать, что и где настраивалось, и как мягко обновиться, ничего не сломав. В чём проблема ручной настройки?
Сервера могут умирать и делать это внезапно. Сколько времени уйдёт на «раскатку» нового сервера? Практически столько же времени, сколько было потрачено первый раз. Порядок действий и нужные настройки просто никто не вспомнит даже через неделю после настройки, что уже говорить, если прошли месяцы. Более того, вдруг тот, кто изначально это делал, уже не работает в компании или находится в отпуске. Что тогда? Придётся долго извиняться перед пользователями за длительный простой, и хорошо, если бизнес от этого пострадает не сильно.
Переустановка сервера необязательно связана с какими-то форс-мажорными обстоятельствами. В компаниях с хорошей инженерной культурой сервера меняются на регулярной основе. Как минимум это важно для безопасности. Операционные системы содержат уязвимости, которые закрываются новыми пакетами или версиями. Следить за этим довольно сложно, поэтому проще регулярно освежать инфраструктуру. С другой стороны, обновление сервера может легко сломать рабочее приложение и вызвать простой в работе. Единственный способ гарантировать беспрерывную работу во время обновления – поднимать рядом ещё один сервер и настраивать его. Затем сервис просто выкатывается на новый сервер, а старый выключается.
Автоматизация
Хорошо бы было автоматизировать настройку сервера. Для этого существует несколько подходов, которые мы рассмотрим ниже.
Bash-скрипты
В простейшем случае для этого достаточно обычного bash-скрипта, в который последовательно добавляются команды, которые ранее мы запускали руками. Затем всё сводится к копированию скрипта на сервер и запуску:
# Копирование на сервер с помощью scp scp mybashscript.sh root@ipaddress:~/ # Заходим на сервер и запускаем скрипт ssh root@ipaddress sh ~/mybashscript.sh
Если перенести команды в bash-скрипт «как есть», без модификации, то, скорее всего, нам придётся постоянно следить за выводом и не забывать подтверждать установку пакетов, так как это поведение по умолчанию:
install golang The following additional packages will be installed: golang-1.13 golang-1.13-doc golang-1.13-go golang-1.13-race-detector-runtime golang-1.13-src golang-doc golang-go Need to get 63.5 MB of archives. After this operation, 329 MB of additional disk space will be used. Do you want to continue? [Y/n] # Скрипт останавливается и ждёт ответа
Автоматическое «да» добавляется опцией -y . У других команд свои опции для подавления взаимодействия с пользователем. Придётся их всё учесть.
install -y golang
Другая проблема серьёзнее. Связана она с понятием «идемпотентность». Что будет если выполнить команду создания директории два раза?
mkdir /hexlet mkdir /hexlet # ?
Команда завершится с ошибкой, она не идемпотентна. То есть последовательные вызовы одной и той же команды приводят к разному результату. Идемпотентность для настройки сервера очень важна. Иначе повторный запуск скрипта настройки завершится с ошибкой. А повторные запуски нужны, например, в случае отладки самого скрипта, когда мы его только пишем и проверяем, как он работает. В случае с командой mkdir идемпотентности добиться легко, достаточно добавить флаг -p :
mkdir -p /hexlet mkdir -p /hexlet # ошибки не будет
Но, к сожалению, не все команды поддерживают такую возможность. Для многих ситуаций, идемпотентность нужно обеспечивать самостоятельно, что резко усложнит скрипт. Из простого набора команд он превратится в реальный код с условными конструкциями. И в какой-то момент разбираться в нём станет крайне сложно. Через это проходили многие, особенно раньше, когда не было альтернативы.
Но дело не только в идемпотентности. Часть задач, которые легко делались руками, становятся сложными в автоматизации. Представьте, что для изменения конфигурации нужно поправить конкретную строчку внутри файла. Как это легко сделать с помощью bash? Никак. Придётся либо полностью заменять файл, копируя всё его содержимое в bash-скрипт (или рядом с ним), либо использовать что-то вроде sed для точечной замены строки.
И последнее, но очень важное ограничение. Bash-скрипт нужно доставить на сервер самостоятельно. И если для одного сервера это ещё как-то можно автоматизировать, то для нескольких «раскатка» скрипта становится проблемой. Важно делать это параллельно, иначе настройка растягивается на часы даже в случае полной автоматизации. Добавьте сюда разные сервера со своими скриптами, которые отличаются от других.
На этом этапе bash-скрипты перестают помогать, нужно придумывать что-то ещё. Так стали появляться специализированные инструменты для конфигурирования серверов. Одними из первых были проекты Chef и Puppet. Сейчас же наибольшую популярность приобрел Ansible, который значительно проще в освоении и использовании.
Ansible
Система управления конфигурацией (серверов), которая решает все проблемы, описанные выше и даже больше, может использоваться не только для настройки, но и для деплоя, то есть установки и запуска сервиса. Для установки Ansible воспользуйтесь одним из предложенных способов
В минимальном виде Ansible конфигурация выглядит, как два файла, один – описание серверов, второй – команды, которые мы хотим выполнить. Ansible сам подключается к удаленным серверам и выполняет необходимые команды. Главное дать доступ к этим серверам, например, с помощью ssh-ключей. Ставить Ansible на сервера не нужно.
Описание серверов хранится в файле inventory.ini. Ansible использует его для определения машин, на которые нужно выполнить установку.
; адрес машины, которую настраиваем ; для простоты говорим Ansible использовать локальный компьютер 127.0.0.1 ansible_connection=local
Команды настройки сервера записываются в файлы, называемые плейбуками. Плейбуки создаются в формате yaml под любым именем. Например, playbook.yaml:
# hosts – означает группу машин, на которой нужно выполниться # all – означает все описанные в inventory.ini - hosts: all tasks: # набор команд которые нужно выполнить - ansible.builtin.file: # file – управляет файлами и директориями name: /tmp/ansible_was_here state: touch # выполнит команду touch, если файла не существует. И – идемпотентность
Файловая структура может выглядеть так:
# вывод содержимого директории . ├── inventory.ini └── playbook.yaml
# Запуск Ansible идет на локальной машине! # Запускать нужно в той же директории, где созданы файлы # -i означает inventory # https://github.com/hexlet-boilerplates/ansible ansible-playbook -i inventory.ini playbook.yaml PLAY [Server Setup] *********************************************************************************************************** TASK [Gathering Facts] ******************************************************************************************************** ok: [127.0.0.1] TASK [file] ******************************************************************************************************************* changed: [127.0.0.1] PLAY RECAP ******************************************************************************************************************** 127.0.0.1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Вывод говорит о том, что плейбук успешно выполнен. В результате в директории /tmp окажется файл ansible_was_here. Повторный запуск плейбука тоже закончится успешно, но из вывода будет видно, что он не сделает никаких изменений, так как Ansible сам обеспечивает идемпотентность. В данном случае, он проверит наличие файла и пропустит команду если файл существует. Если в inventory.ini указать несколько ip-адресов, то Ansible выполнит плейбук на каждом из них, причем сделает это параллельно. Единственное, о чём нужно не забыть – добавить ssh-ключи на эти машины, иначе Ansible не сможет до них достучаться.
Что из себя представляет плейбук? Главное внутри него – набор задач (tasks), которые мы хотим выполнить. В отличие от bash-скрипта, задачи это не просто bash-команды. На каждую задачу в Ansible встроен модуль для работы с определенной частью системы. Например, внутри Ansible есть модули для работы с git, пакетными менеджерами, файлами и тому подобным. Всего их сотни на все случаи жизни. Именно благодаря готовым интеграциям, Ansible знает, как работают те или иные части системы, что позволяет добавить нужные проверки для обеспечения идемпотентности. Несколько примеров:
tasks: # Установка postgresql - name: Ensure postgresql is at the latest version ansible.builtin.apt: # модуль apt name: postgresql state: latest # Запуск postgresql - name: Ensure that postgresql is started ansible.builtin.service: # модуль service name: postgresql state: started # запускаем, если не запущен
Как видите, Ansible достаточно прост для начала, при этом у него много возможностей, которые можно изучать по мере погружения и усложнения инфраструктуры.
Итого
Управление конфигурацией в современном мире выполняется с помощью специализированных программ, которые умеют подключаться к удаленным серверам, параллельно настраивать их, обеспечивая идемпотентность операций. При таком подходе важно перестать настраивать сервера напрямую. Любые изменения теперь должны делаться через инструмент автоматизации, иначе всё вернется к изначальным проблемам. Управление конфигурацией через код повышает взаимозаменяемость людей, позволяет легко отслеживать изменения, просто просматривая историю git, подключать других членов команды к управлению инфраструктурой.
Теперь, когда вы немного познакомились с Ansible, рекомендуем начать прохождение курса, где уже отрабатывается работа с ним.
Дополнительные материалы
- Список модулей Ansible
- Пример готового Ansible playbook.yaml и inventory.ini
- Курс по Ansible
![]()
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты