ansible.builtin.get_url module – Downloads files from HTTP, HTTPS, or FTP to node
This module is part of ansible-core and included in all Ansible installations. In most cases, you can use the short module name get_url even without specifying the collections keyword . However, we recommend you use the Fully Qualified Collection Name (FQCN) ansible.builtin.get_url for easy linking to the module documentation and to avoid conflicting with other collections that may have the same module name.
Synopsis
- Downloads files from HTTP, HTTPS, or FTP to the remote server. The remote server must have direct access to the remote resource.
- By default, if an environment variable _proxy is set on the target host, requests will be sent through that proxy. This behaviour can be overridden by setting a variable for this task (see setting the environment ), or by using the use_proxy option.
- HTTP redirects can redirect from HTTP to HTTPS so you should be sure that your proxy environment for both protocols is correct.
- From Ansible 2.4 when run with —check , it will do a HEAD request to validate the URL but will not download the entire file or verify it against hashes and will report incorrect changed status.
- For Windows targets, use the ansible.windows.win_get_url module instead.
Parameters
attributes
The attributes the resulting filesystem object should have.
To get supported flags look at the man page for chattr on the target system.
This string should contain the attributes in the same order as the one displayed by lsattr.
The = operator is assumed as default, otherwise + or — operators need to be included in the string.
backup
Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly.
Choices:
checksum
If a checksum is passed to this parameter, the digest of the destination file will be calculated after it is downloaded to ensure its integrity and verify that the transfer completed successfully. Format: :
If you worry about portability, only the sha1 algorithm is available on all platforms and python versions.
The Python «hashlib« module is responsible for providing the available algorithms. The choices vary based on Python version and OpenSSL version.
On systems running in FIPS compliant mode, the «md5« algorithm may be unavailable.
Additionally, if a checksum is passed to this parameter, and the file exist under the dest location, the destination_checksum would be calculated, and if checksum equals destination_checksum, the file download would be skipped (unless force is true). If the checksum does not equal destination_checksum, the destination file is deleted.
Default: «»
ciphers
added in ansible-core 2.14
SSL/TLS Ciphers to use for the request
When a list is provided, all ciphers are joined in order with :
The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions
client_cert
PEM formatted certificate chain file to be used for SSL client authentication.
This file can also include the key as well, and if the key is included, client_key is not required.
client_key
PEM formatted file that contains your private key to be used for SSL client authentication.
If client_cert contains both the certificate and key, this option is not required.
decompress
added in ansible-core 2.14
Whether to attempt to decompress gzip content-encoded responses
Choices:
dest
Absolute path of where to download the file to.
If dest is a directory, either the server provided filename or, if none provided, the base name of the URL on the remote server will be used. If a directory, force has no effect.
If dest is a directory, the file will always be downloaded (regardless of the force and checksum option), but replaced only if the contents changed.
force
If true and dest is not a directory, will download the file every time and replace the file if the contents change. If false , the file will only be downloaded if the destination does not exist. Generally should be true only for small local files.
Prior to 0.6, this module behaved as if true was the default.
Choices:
force_basic_auth
Force the sending of the Basic authentication header upon initial request.
httplib2, the library used by the uri module only sends authentication information when a webservice responds to an initial request with a 401 status. Since some basic auth services do not properly send a 401, logins will fail.
Choices:
group
Name of the group that should own the filesystem object, as would be fed to chown.
When left unspecified, it uses the current group of the current user unless you are root, in which case it can preserve the previous ownership.
headers
Add custom HTTP headers to a request in hash/dict format.
The hash/dict format was added in Ansible 2.6.
Previous versions used a «key:value,key:value» string format.
The «key:value,key:value» string format is deprecated and has been removed in version 2.10.
http_agent
Header to identify as, generally appears in web server logs.
Default: «ansible-httpget»
mode
The permissions the resulting filesystem object should have.
For those used to /usr/bin/chmod remember that modes are actually octal numbers. You must give Ansible enough information to parse them correctly. For consistent results, quote octal numbers (for example, ‘644’ or ‘1777’ ) so Ansible receives a string and can do its own conversion from string into number. Adding a leading zero (for example, 0755 ) works sometimes, but can fail in loops and some other circumstances.
Giving Ansible a number without following either of these rules will end up with a decimal number which will have unexpected results.
As of Ansible 1.8, the mode may be specified as a symbolic mode (for example, u+rwx or u=rw,g=r,o=r ).
If mode is not specified and the destination filesystem object does not exist, the default umask on the system will be used when setting the mode for the newly created filesystem object.
If mode is not specified and the destination filesystem object does exist, the mode of the existing filesystem object will be used.
Specifying mode is the best way to ensure filesystem objects are created with the correct permissions. See CVE-2020-1736 for further details.
owner
Name of the user that should own the filesystem object, as would be fed to chown.
When left unspecified, it uses the current user unless you are root, in which case it can preserve the previous ownership.
Specifying a numeric username will be assumed to be a user ID and not a username. Avoid numeric usernames to avoid this confusion.
selevel
The level part of the SELinux filesystem object context.
This is the MLS/MCS attribute, sometimes known as the range .
When set to _default , it will use the level portion of the policy if available.
serole
The role part of the SELinux filesystem object context.
When set to _default , it will use the role portion of the policy if available.
setype
The type part of the SELinux filesystem object context.
When set to _default , it will use the type portion of the policy if available.
seuser
The user part of the SELinux filesystem object context.
By default it uses the system policy, where applicable.
When set to _default , it will use the user portion of the policy if available.
timeout
Timeout in seconds for URL request.
Default: 10
tmp_dest
Absolute path of where temporary file is downloaded to.
When run on Ansible 2.5 or greater, path defaults to ansible’s remote_tmp setting
When run on Ansible prior to 2.5, it defaults to TMPDIR , TEMP or TMP env variables or a platform specific value.
unredirected_headers
added in ansible-core 2.12
A list of header names that will not be sent on subsequent redirected requests. This list is case insensitive. By default all headers will be redirected. In some cases it may be beneficial to list headers such as Authorization here to avoid potential credential exposure.
Default: []
unsafe_writes
Influence when to use atomic operation to prevent data corruption or inconsistent reads from the target filesystem object.
By default this module uses atomic operations to prevent data corruption or inconsistent reads from the target filesystem objects, but sometimes systems are configured or just broken in ways that prevent this. One example is docker mounted filesystem objects, which cannot be updated atomically from inside the container and can only be written in an unsafe manner.
This option allows Ansible to fall back to unsafe methods of updating filesystem objects when atomic operations fail (however, it doesn’t force Ansible to perform unsafe writes).
IMPORTANT! Unsafe writes are subject to race conditions and can lead to data corruption.
Choices:
url
HTTP, HTTPS, or FTP URL in the form (http|https|ftp)://[user[:pass]]@host.domain[:port]/path
url_password
The password for use in HTTP basic authentication.
If the url_username parameter is not specified, the url_password parameter will not be used.
Since version 2.8 you can also use the ‘password’ alias for this option.
url_username
The username for use in HTTP basic authentication.
This parameter can be used without url_password for sites that allow empty passwords.
Since version 2.8 you can also use the username alias for this option.
use_gssapi
added in ansible-core 2.11
Use GSSAPI to perform the authentication, typically this is for Kerberos or Kerberos through Negotiate authentication.
Requires the Python library gssapi to be installed.
Credentials for GSSAPI can be specified with url_username/url_password or with the GSSAPI env var KRB5CCNAME that specified a custom Kerberos credential cache.
NTLM authentication is not supported even if the GSSAPI mech for NTLM has been installed.
Choices:
use_netrc
added in ansible-core 2.14
Determining whether to use credentials from «~/.netrc« file
By default .netrc is used with Basic authentication headers
When set to False, .netrc credentials are ignored
Choices:
use_proxy
if false , it will not use a proxy, even if one is defined in an environment variable on the target hosts.
Choices:
validate_certs
If false , SSL certificates will not be validated.
This should only be used on personally controlled sites using self-signed certificates.
Choices:
Attributes
check_mode
Support: partial
the changed status will reflect comparison to an empty source file
Can run in check_mode and return changed status prediction without modifying target
diff_mode
Support: none
Will return details on what has changed (or possibly needs changing in check_mode), when in diff mode
platform
Platform: posix
Target OS/families that can be operated against
Notes
- For Windows targets, use the ansible.windows.win_get_url module instead.
See Also
Interacts with webservices.
Downloads file from HTTP, HTTPS, or FTP to node.
Examples
- name: Download foo.conf ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf mode: '0440' - name: Download file and force basic auth ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf force_basic_auth: yes - name: Download file with custom HTTP headers ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf headers: key1: one key2: two - name: Download file with check (sha256) ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf checksum: sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c - name: Download file with check (md5) ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf checksum: md5:66dffb5228a211e61d6d7ef4a86f5758 - name: Download file with checksum url (sha256) ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf checksum: sha256:http://example.com/path/sha256sum.txt - name: Download file from a file path ansible.builtin.get_url: url: file:///tmp/afile.txt dest: /tmp/afilecopy.txt - name: < Fetch file that requires authentication.username/password only available since 2.8, in older versions you need to use url_username/url_password ansible.builtin.get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf username: bar password: ' mysecret >>'
Return Values
Common return values are documented here , the following are the fields unique to this module:
Копирование файлов посредством 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
Обновлено: 10.10.2023 Опубликовано: 17.09.2021
Используемые термины: Ansible. Инструкция представляет из себя шпаргалку по работе с Ansible. У автора не стоит задачи подробного пояснения всех операций — только описание задачи и пример того, как ее можно решить с помощью ansible. Для более подробного описания я постараюсь указать ссылки на официальную документацию. Также в данную шпаргалку не войдут все возможные действия, которые можно выполнить с помощью данной системы — только популярные и те, с которыми приходилось сталкиваться самому автору. По мере возможности, их список будет пополняться. Для удобства, мы попробуем разбить примеры на операции, которые логически можно объединить в одну группу.
Получение информации
Сюда войдут примеры, которые позволят собирать информацию, выводить ее на экран, помогать в отладке и всякое такое. 1. Сбор общей информации о системе. Чтобы собрать информацию о системе, на которой выполняется сценарий ansible, нужно использовать модуль setup, например:
— name: Get service facts
setup:
Или можно применить фильтр, чтобы сбор выполнялся быстрее:
— name: Get service facts
setup:
filter: ‘ansible_os_family’
Данная информация будет записана в переменную ansible_facts. При желании, ее значение можно отобразить с помощью модуля debug:
— name: Print all available facts
debug:
var: ansible_facts
Также мы можем обратиться к конкретному элементу массива ansible_facts, получив информацию о конкретной настройке или опции:
.
ansible_facts.hostname
О setup: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html. 2. Получить определенную информацию о компьютере. Выполняется с помощью различных модулей с окончанием _facts. Рассмотрим примеры. а) список сервисов. Для этого существует service_facts:
— name: Populate service facts
ansible.builtin.service_facts:
— name: Print all available services
debug:
var: ansible_facts.services
* цель достигается двумя задачами. В первой мы собираем информацию о сервисах с помощью service_facts, второй — выводим на экран содержимое. О service_facts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_facts_module.html. б) установленные пакеты. Используем package_facts:
— name: Gather the package facts
package_facts:
manager: auto
— name: Print all available packages
debug:
var: ansible_facts.packages
О package_facts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_facts_module.html. 3. Отображение на экран переменной. Выше мы уже использовали debug для отображения переменной — принцип тот же:
— name: Show Value of Variable
debug:
msg: «>»
* при выполнении задачи на экране мы увидим значение переменной variable. Обратите внимание, что запись ansible.builtin.debug и debug — это одно и то же, то есть, ansible.builtin можно не писать. О debug: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html. 4. Сохранение результата при выполнении задания. Создать и использовать переменную можно в процессе выполнения работы с помощью директивы register:
— name: Run a shell command and register its output as a variable
shell: command
register: command_result
Также нам может понадобиться сохранить в переменную результат обработки переменной:
— name: Set variable
set_fact:
my_hosts: «>»
* в данном случае мы взяли переменную, которую получили в примере выше и разбили строку по знаку «:». Таким образом, мы получим массив данных, который будет сохранен в переменную my_hosts.
Работа с переменными
В сценариях нам постоянно нужно будет использовать переменные. Их можно передавать при запуске, получать в процессе выполнения задач или описать в самом сценарии заранее. Рассмотрим эти действия на некоторых примерах. 1. Переменные в ролях. Для описания переменных в ролях существуют два каталога — vars и defaults. Последние имеют самый низкий приоритет, это значит, что их можно переопределить. Независимо от каталога, в котором идет описание переменной, синтаксис один:
hostname: mail.dmosk.local
* в данном примере определена переменная hostname со значением mail.dmosk.local. 2. Переменные в файле inventory. Инвентарный файл используется для описания хостов, которые могут быть задействованы при работе сценариев ansible. Переменная может быть определена на уровне всех хостов или на уровне конкретного хоста:
all:
vars:
tower_user_name: master
hosts:
192.168.0.10:
human_name: mail
192.168.0.11:
human_name: www
- tower_user_name для всех машин.
- human_name для 192.168.0.10.
- human_name для 192.168.0.11.
3. Переменные в плейбуке.
Задаются в файле плейбука:
— hosts: all
.
vars:
vm_name: test-vm
* в данном примере это переменная vm_name.
4. При запуске плейбука.
Когда мы запускаем на выполнение наш плейбук, мы можем передать переменные с помощью опции extra-vars:
* передаем переменную address.
Проверки, условия и действия на основе этих проверок
В данную группу войдут действия, которые помогут нам ограничить выполнение задач или изменить ход выполнения.
1. Проверка на пустую папку.
Задачи сводится к двум операциям:
- получении списка файлов в целевом каталоге (например, с помощью команды ls) и регистрации полученного значения в переменную с помощью register.
- проверка содержимого переменной, которую мы получили на шаге 1 с помощью when.
Пример будет таким:
— name: Register Contents of PGDATA Folder
shell: ls /var/lib/postgresql/11/main
register: pg_contents
— name: Init PostgreSQL DB
shell: /opt/pgpro/std-11/bin/pg-setup initdb
environment:
PGDATA: «/var/lib/postgresql/11/main»
when: pg_contents[«stdout_lines»] | length == 0
* в данном примере мы в первой задаче выводим содержимое каталога /var/lib/postgresql/11/main и помещаем его в переменную pg_contents. Во второй задаче мы уже проверяем с помощью when количество строк — если их 0, то тогда выполняем команду initdb. На практике, это важно, так как инициализация базы PostgreSQL при непустом каталоге выводит сообщение об ошибке.
2. Проверить, определена ли переменная.
Для этого используется опция is defined (определена) или is not defined (не определена):
when: pgpro is defined
when: pgpro is not defined
* в данном примере мы проверим наличие переменной pgpro. На практике такая проверка имеет значение, так как если мы попробуем выполнить действия с несуществующей переменной, Ansible нам вернет ошибку.
В официальной документации про это сказано в статье о when (ссылка выше).
3. Выполнение команды, если сервис в рабочем состоянии.
Нам необходимо получить информацию о службах с помощью service_facts, после чего можно уже делать проверку с помощью when:
— name: Populate service facts
ansible.builtin.service_facts:
— name: Stop Service If Running One
shell: systemctl stop apache2
when: ansible_facts.services[«apache2.service»] is defined and ansible_facts.services[«apache2.service»].state == «running»
* в данном примере мы проверим, есть ли служба apache2 и запущена ли она. Если это так, то мы ее останавливаем.
Подробнее о service_facts можно прочитать в документации (ссылка выше в разделе 4. Получить список сервисов).
4. Существует ли файл.
Проверка может быть выполнена с помощью stat:
— name: Register File Stat
stat:
path: /etc/nginx/nginx.conf
register: stat_result
— name: Cat file if exists
shell: cat /etc/nginx/nginx.conf
when: stat_result.stat.exists
* в данном примере будет выполнено чтение файла /etc/nginx/nginx.conf, если он существует.
5. Операционная система.
С помощью переменных ansible_os_family и ansible_distribution_major_version мы можем получить информацию об операционной системы, которая работает на целевом компьютере. Это можно использовать для выполнения проверок и задания условий.
а) Проверка по семейству:
— name: Run task1 if OS is Debian
.
when: ansible_os_family == «Debian»
— name: Run task2 if OS is Red Hat
.
when: ansible_os_family == «RedHat»
* в данном примере мы запустим задачу task1 в системах на основе Debian и task2 — на основе RedHat.
б) Проверка по версии системы:
— name: Run task1 if OS is Debian 9
.
when: ansible_os_family == «Debian» and ansible_distribution_major_version == «9»
— name: Run task2 if OS is Red Hat 8
.
when: ansible_os_family == «RedHat» and ansible_distribution_major_version == «8»
— name: Run task3 if OS is Red Hat less 8
.
when: ansible_os_family == «RedHat» and ansible_distribution_major_version | int < 8
* в данном примере мы запустим задачу task1 в системах на основе Debian версии 9, task2 — на основе RedHat версии 8 и task3 на системах ниже 8 версии.
6. Выдать ошибку и остановить выполнение.
Для этого предусмотрен модуль fail или опция задания failed_when. Рассмотрим примеры использования обоих.
а) Модуль fail. Является отдельным заданием. Его можно выполнить просто так, но без условия его применение нелогично. Пример:
— name: Stop the executing if variable is empty
fail:
msg: The variable srv_ip is not defined or its empty.
when: srv_ip is not defined or srv_ip == «»
* в нашем примере, если переменная srv_ip окажется пустой или неопределенной, мы получим ошибку с сообщением, определенным в опции msg.
б) Опция failed_when. Задает условие, при соблюдении которого задание выполняется с ошибкой, например:
— name: Fail task when the command error output prints FAILED
ansible.builtin.command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: «‘FAILED’ in command_result.stderr»
* данный пример с официального сайта ansible (ссылка ниже). Задание выдаст ошибку, если в результате выполнения скрипта /usr/bin/example-command будет текст FAILED.
7. Ожидания с помощью wait_for.
Мы можем создать задание, которое остановит выполнение плейбука, пока не будет выполнено определенное условие:
- Просто ждать определенное время.
- Удачное соединение по сети с узлом.
- Определенное содержимое в файле.
- Пока не появится или удалится файл или папка.
— name: Wait rpm file
wait_for:
path: /tmp/build/my_package.rpm
* в данном примере выполнение плейбука остановится, пока по пути /tmp/build/my_package.rpm не появится файл.
8. Проверка условий с выводом ошибки или успеха.
Предположим, нам нужно сделать какую-либо проверку, в результате которой ansible должен вернуть ошибку или продолжить выполнение. Для этого хорошо подходит модуль assert . Рассмотрим несколько примеров.
а) Значение переменной my_key должно быть от 0 до 100:
— name: Значение переменной my_key должно быть от 0 до 100
assert:
that:
— my_key — my_key >= 0
fail_msg: «Неправильное значение my_key — должно быть от 0 до 100»
success_msg: «Значение my_key верное»
б) Значение my_login не должно быть только из цифр:
— name: Проверяем название датасета
assert:
that:
— my_login is not regex(«^[0-9]+$»)
fail_msg: «Имя my_login не может состоять только из цифр»
success_msg: «Имя my_login задано верно»
Установки пакетов, модулей и расширений
В данном разделе мы коснемся всего, что приводит к установке чего бы то ни было. А именно:
- Установки пакетов в систему.
- Загрузки исходников.
- Установке дополнительных модулей.
- Распаковке архивов.
Рассмотрим это подробнее.
1. Установка/удаление пакетов в систему.
Выполняется с помощью универсального модуля package или с помощью специализированных yum и apt. Последние имеют больше полезных опций.
— name: Install NTP-client
package:
name: chrony
state: latest
* по данной инструкции в нашей системе должен быть установлен пакет chrony последней версии.
Для установки пакета конкретной версии, ее нужно указать в названии пакета:
— name: Install NTP-client
package:
name: chrony-3.4
state: present
— name: Install NTP-client
yum:
name: chrony
state: present
— name: Install NTP-client
apt:
name: chrony
state: present
Если нам нужно установить пакет из файла deb, синтаксис будет следующий:
— name: «Install lib for postgresql»
apt:
deb: http://ftp.ru.debian.org/debian/pool/main/l/llvm-toolchain-7/libllvm7_7.0.1-8+deb10u2_amd64.deb
state: present
* в данном примере мы устанавливаем пакет libllvm7 из файла deb, который доступен по url http://ftp.ru.debian.org/debian/pool/main/l/llvm-toolchain-7/libllvm7_7.0.1-8+deb10u2_amd64.deb.
С помощью опции state мы можем управлять поведением установки:
- present — пакет должен быть установлен.
- latest — должна быть установлена последняя версия.
- absent — пакет должен быть удален.
2. Работа с репозиториями RPM.
Ведется с помощью модуля yum_repository.
а) Добавление репозитория:
— name: «Add YUM repository»
yum_repository:
name: new_yum_repo
description: «New Yum Repo Description»
file: new_repo_file
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
enabled: yes
gpgcheck: no
validate_certs: no
б) Для удаления репозитория также используем yum_repository:
— name: «Remove YUM repositories»
yum_repository:
name: pgsql95
state: absent
* в нашем примере будет удален репозиторий с названием pgsql95.
Ansible может не распозновать некоторые репозитории, которые настроена в пользовательских файлах. В таком случае необходимо указать на эти файлы с помощью опции file:
— name: «Remove YUM repositories»
yum_repository:
name: pgsql95
state: absent
file: pgsql
в) Установка пакетов из dnf-модулей. Начиная с версии 8 дистрибутивов на основе RPM пакеты могут устанавливаться из модульных репозиториев, в которых идет четкое разграничение между версиями пакетов. В ansible, если необходимо установить пакет из определнного модуля, используем сценарий на подобие:
— name:
dnf:
name:
— @postgresql:13/client
— @postgresql:13/server
state: present
* в данном примере мы установим пакеты postgresql-client и postgresql-server из модульного репозитория postgresql:13.
3. Работа с репозиториями DEB.
а) Добавление репозитория:
— name: «Add DEB repository»
apt_repository:
repo: deb http://dl.google.com/linux/chrome/deb/ stable main
filename: new_deb_repo
б) Для удаления репозитория нужно воспользоваться директивой state со значением absent, например:
— name: «Remove Debian default repository»
apt_repository:
repo: deb https://ftp.debian.org/debian stretch main contrib
state: absent
* в данном примере будет удален репозиторий, который идет по умолчанию для Debian Stretch.
Рассмотрим примеры с добавлением gpg-ключа по URL и с сервера ключей:
— name: Import postgresql repo key
apt_key:
url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
state: present
* импорт ключа из файла https://www.postgresql.org/media/keys/ACCC4CF8.asc.
— name: Import keyserver.ubuntu.com repo keys
apt_key:
keyserver: keyserver.ubuntu.com
id: 648ACFD622F3D138
state: present
* импорт ключа с идентификатором 648ACFD622F3D138 из сервера ключей keyserver.ubuntu.com.
г) Обновление кэша репозитория.
Выполняется с помощью модуля apt и опции update_cache:
— name: Update repositories cache
apt:
update_cache: yes
4. Установка модуля в nodejs.
Установка модулей в nodejs выполняется с помощью npm. Для него в ansible есть отдельная функция:
— name: Install nodejs modules.
npm:
name: newman
global: yes
* в данном примере будет выполнена установка newman, которая будет доступна всем проектам (опция global).
5. Установка расширений для python.
Используем модуль pip. Рассмотрим несколько примеров.
а) установка пакета python:
— name: Pip install psycopg2
pip:
name: psycopg2
state: present
* в данном примере будет установлен psycopg2.
б) обновление пакетов:
— name: Upgrade pip and wheel
pip:
name: «>»
extra_args: —upgrade
executable: pip3
loop:
— pip
— wheel
* в нашем примере будут обновлены пакеты pip и wheel.
в) использовать определенную версию pip:
— name: Install python modules with pip3
pip:
name: patroni[consul]
executable: pip3
state: present
6. Распаковка архива.
Выполняется с помощью unarchive:
— name: Unpacking Nginx Source
unarchive:
src: «http://nginx.org/download/nginx->.tar.gz»
dest: /tmp/
remote_src: yes
creates: /tmp/nginx->.tar.gz
* в данном примере мы распакуем исходник для nginx в каталог /tmp. Обратите внимание на две вещи:
- Мы используем переменную nginx_ver. Данная переменная должна быть определена при запуске плейбука, или в инвентарном файле, или в var, или в default. Подробнее в соответствующем разделе выше.
- Опция creates позволит не выполнять операцию, если существует файл /tmp/nginx->.tar.gz.
Настройка системы
В данном разделе мы рассмотрим процессы, которые больше подходят для категории настройки системы.
1. Добавить задание в cron.
Выполняется с помощью модуля cron:
— name: Add Job for Run Command
cron:
name: Start Script
job: «/scripts/command.sh»
user: root
minute: «0»
hour: «*/6»
day: «*»
month: «*»
weekday: «*»
* в данном примере мы создадим задание для запуска команды /scripts/command.sh каждый день, каждые 6 часов.
2. Создание учетной записи.
Для этого есть модуль user. У него много опций, рассмотрим некоторые из них.
а) Простая учетная запись:
— name: Create User1
user:
name: user1
shell: /bin/bash
create_home: yes
* в данном примере мы создадим пользователя user1 с домашней директорией. Также мы указали для использования командную оболочку /bin/bash.
б) Для создания системной учетной записи нам достаточно:
— name: Create User Consul
user:
name: consul
system: yes
comment: «Consul Agent»
* в данном примере будет создана учетная запись consul.
в) Создаем пользователя с паролем:
* будет создан пользователь user2 с паролем my_passw0rd.
3. Работа с systemd.
Для данной настройки есть одноименный модуль systemd. Рассмотрим варианты его использования.
а) перечитать конфигурацию (необходимо делать каждый раз, когда мы меняем настройки юнита):
— name: systemd reload
systemd:
daemon_reload: yes
б) разрешить сервис (автозапуск):
— name: mysql enable
systemd:
name: mysql
enabled: yes
* для сервиса mysql.
в) перезапустить сервис:
— name: mysql reload
systemd:
name: mysql
state: restarted
г) остановить сервис:
— name: mysql stoped
systemd:
name: mysql
state: stopped
4. Настройка брандмауэра.
Выполняется разными модулями в зависимости от используемой системы управления netfilter:
Рассмотрим небольшие примеры.
— name: permit traffic in default zone for https service
firewalld:
service: https
permanent: yes
state: enabled
— name: Block specific IP
iptables:
chain: INPUT
source: 8.8.8.8
jump: DROP
Добавить 80 порт:
— name: Allow all access to tcp port 80
ufw:
rule: allow
port: ’80’
proto: tcp
Добавить порты с циклом:
5. Имя компьютера.
Для указания имени компьютера можно использовать модуль hostname:
— name: Задаем имя компьютера
hostname:
name: myweb
use: systemd
* в данном примере мы задаем имя myweb. Обратите внимание на опцию use — в зависимости от операционной системы или ее версии, ее значение должно отличаться.
6. Часовой пояс.
Часовой пояс можно настроить с помощью модуля timezone, например:
— name: Задаем часовой пояс
timezone:
name: Europe/Moscow
Работа с папками и файлами
Рассмотрим задачи, которые помогут нам создавать, копировать и работать с файлами.
1. Создание каталогов и файлов.
Создание файлов и каталогов выполняется с помощью модуля file.
а) для каталога в качестве state указываем directory:
— name: Create Directories
file:
path: «>»
state: directory
owner: www-data
group: www-data
mode: 0755
loop:
— ‘/var/www/site1’
— ‘/var/www/site2’
* в данном примере мы создадим 2 каталога: site1 и site2 в каталоге /var/www.
б) для создания файла убираем опцию state (или даем ей значение touch):
— name: Create File
file:
path: «/var/www/site1/index.php»
state: touch
owner: www-data
group: www-data
mode: 0644
* в данном примере мы созданим файл index.php в каталоге /var/www/site1.
в) для создания симлинка используем state со значением link:
— name: Create a symbolic link from foo to bar
file:
src: /usr/bin/foo
dest: /usr/sbin/bar
state: link
2. Задать права.
Это можно выполнить с помощью модуля создания файла или каталога:
— name: Set File Rights
file:
path: «/var/www/site1/index.php»
owner: www-data
group: www-data
mode: 0664
* обратите внимание, это пример из предыдущено раздела. Для созданного файла мы просто немного изменили права.
3. Копирование файлов из каталога.
Для копирования данных мы используем модуль copy:
— name: Copy Cert File If Different
copy:
src: «>»
dest: /etc/ssl/dmosk
remote_src: no
mode: 0644
owner: root
group: root
with_fileglob:
— files/*
* в данном примере мы прочитаем все содержимое каталога files на компьютере с ansible, и скопируем его в каталог /etc/ssl/dmosk на целевом компьютере.
4. Используем шаблон.
Копирование из шаблона отличается от копирования из файла тем, что в шаблоне могут использоваться переменные, которые будет заменяться их значениями в момент копирования. Для самого процесса копирования из шаблона используется модуль template:
— name: Create Config for Consul Agent
template:
src: templates/consul/config.json.j2
dest: /etc/consul.d/config.json
* в данном примере мы возьмом шаблон templates/consul/config.json.j2 на компьютере ansible и разместим его в по пути /etc/consul.d/config.json на целевом компьютере.
Мы можем вывести в консоль результат обработки шаблона следующим образом:
5. Архивирование.
Создать архив из файла или каталога можно с помощью модуля archive:
— name: «Use gzip to compress folder»
archive:
path: /etc/raddb
dest: «/tmp/raddb.gz»
format: gz
* в данном примере мы создадим архив из каталога /etc/raddb и сохраним его в файл /tmp/raddb.gz.
Для распаковки архивов используется модуль unarchive, о котором мы говорили выше.
6. Поиск файлов и папок.
Выполняется с помощью модуля find. Особый интерес представляет в контексте поиска файлов и выполнение над ними определенных действий. Разберем несколько примеров.
а) Удалить последние 30 файлов. Задача решается в два этапа:
- ищем содержимое целевого каталога.
- сотритуем список найденных по времени изменения файлов и удаляем все, что идут после определенного числа объектов.
Поиск выполняем с помощью модуля find, удаление — file:
— name: «Get list of backup files»
find:
paths: «/backup»
file_type: file
register: founds
* в данном примере мы ищем файлы в каталоге /backup, после чего сортируем найденное и удаляем по списку все файлы, которые идут после 30-го.
б) Удаление архивов для логов. Также выполняем в два этапа — более сложный поиск и удаление с помощью file:
— name: «Get a list of logs to be deleted»
find:
paths: «/var/log»
file_type: file
patterns: ‘*.gz,*.log-*,*.old,*.[0-9].log,*.log.[0-9],*-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]’
recurse: yes
register: logs_to_delete
* в данном примере мы применили регулярные выражения для поиска различных вариантов логов, которые являются архивными. После мы сохраняем результат поиска в переменную logs_to_delete, которую используем для получения списка путей до найденных файлов.
7. Скачать файл с помощью curl.
Для этого используется модуль uri. Простой пример:
— name: CURL simple download file
uri:
url: https://www.dmosk.ru/files/winsetupfromusb.zip
dest: /tmp
* в данном примере мы загрузим файл https://www.dmosk.ru/files/winsetupfromusb.zip в каталог /tmp.
— name: CURL download file with token auth
uri:
url: https://gitlab.dmosk.ru/api/v4/projects/555/repository/files/folder%2Fpath%2Fdata.sql/raw?ref=master
dest: /tmp/data.sql
owner: dmosk
group: dmosk
mode: 640
headers:
PRIVATE-TOKEN: access-token
* в данном примере мы скачаем файл с ресурса, где требуется аутентификация по токену, который передается в заголовке. Заголовки мы передаем с помощью параметра headers. Также мы задаем права на загруженный файл и делаем в качестве владельца пользователя и группу dmosk.
8. Создание временных файлов и папок.
Иногда, для работы нужно временное хранилище файлов, которое после работы можно будет удалить. Для работы с данным хранилишем в ansible можно использовать модуль tempfile.
Пример создания каталога:
— name: Create temporary ansible directory
tempfile:
state: directory
suffix: ansible
register: tmp
Путь до созданного каталога будет записан в переменную tmp.path.
После не забываем удалить созданную папку:
— name: Remove temporary ansible directory
file:
path: «>»
state: absent
when: tmp.path is defined
9. Работа с GIT.
а) Клонирование проекта из гита.
Выполняется с помощью модуля git.
— name: Clone docker-compose
git:
repo: «https://github.com/docker/compose.git»
dest: /tmp/docker-compose
* в данном примере мы сделаем клон репозитория в каталог /tmp/docker-compose.
б) Изменение содержимого для файла.
Модуль ansible не поддерживает отпавку изменений в git, но можно воспользоваться API. Например, для gitlab обновить контент файла можно с помощью модуля uri:
— name:
uri:
url: «https://gitlab.dmosk.ru/api/v4/projects/666/repository/files/folder%2Ffilename»
method: PUT
return_content: false
body_format: json
body:
branch: «main»
author_email: «master@dmosk.ru»
author_name: «Dmitriy Mosk»
content: «Text for file»
commit_message: «update filename»
headers:
PRIVATE-TOKEN: 00000_1111111_33333
Content-Type: application/json
validate_certs: no
- url — полный путь до файла. Обратите внимание на:
- gitlab.dmosk.ru — адрес нашего сервера gitlab.
- 666 — идентификатор проекта. Его можно посмотреть в настройках самого проекта.
- folder%2Ffilename — путь до файла. В нормальном формате, это folder/filename.
Содержимое файла
С помощью Ansible мы можем менять содержимое строк как в файлах — вставлять, удалять и редактировать, так и полученных результатах. Рассмотрим несколько примеров.
1. Просмотр содержимого файла.
Содержимое файлов на удаленном сервере и на стороне ansible просматривается по-разному. Рассмотрим оба варианта.
Чтобы увидеть содержимое файла, который лежит на стороне ansible, используем плагин lookup:
* в данном примере мы просто покажем содержимое файла /etc/ntp.conf.
б) На удаленном сервере.
Выполняется в два этапа — просмотр содержимого с сохранением результата в переменную, и вывод содержимого переменной:
— name: Read file content
shell: cat /path/to/file
register: file_content— name: Show file content
debug:
var: file_content.stdout2. Замены строки в файлах.
Замены выполняются с помощью модуля replace. Рассмотрим несколько примеров.
а) Простой поиск и замена строки:
— name: Check configs (comment server address 127.0.0.1)
replace:
path: «/etc/nginx/nginx.conf»
regexp: ‘^server.address=127.0.0.1$’
replace: ‘#server.address=127.0.0.1’* в данном примере мы добавляем комментарий к строке server.address=127.0.0.1.
б) Замена с подстановкой найденного содержимого:
— name: Check configs (comment server address 127.0.0.1)
replace:
path: «/etc/nginx/nginx.conf»
regexp: ‘^server.address=(.*)$’
replace: ‘# commented for \1’* в данном примере мы находим строку server.address с любым значением и меняем ее на строку, в которой будет прописано это значение.
3. Добавление и удаление строк в файле.
Мы можем менять содержимое файла с помощью модуля lineinfile. Рассмотрим некоторые примеры работы с ним.
а) Удалить строку:
— name: Remove server strings
lineinfile:
path: «/etc/chrony.conf»
regexp: ‘^server .*’
state: absent* в данном примере мы удалим все строки из файла /etc/chrony.conf, которые начинаются на server.
б) Добавить строку:
— name: Add server strings
lineinfile:
path: «/etc/chrony.conf»
line: ‘server ntp.server.local’* в данном примере мы добавим строку server ntp.server.local в файл /etc/chrony.conf. Если данная запись уже есть в файле, ansible ничего не станет менять.
в) Добавить строку с использованием регулярного выражения:
— name: Ensure SELinux is set to enforcing mode
lineinfile:
path: /etc/selinux/config
regexp: ‘^SELINUX=’
line: SELINUX=enforcing* пример взят с официального сайта. В данном случае мы гарантируем наличие строки SELINUX=enforcing — либо для директивы SELINUX будет задано определенное значение, либо строка будет полностью вставлена в конфигурационный файл /etc/selinux/config.
4. Разбить строку.
Выполняется с помощью метода split. Рассмотрим пример, когда мы регистрируем результат при выполнении команды, после чего мы разобьем его по предложениям:
— name: Run a shell command and register its output as a variable
shell: command
register: command_result— name: Parse string for dot and show results
debug:
var: command_result.stdout.split(«.»)* в данном примере мы получим массив данных из предложений.
5. Добавить блок текста.
Можно сделать с помощью модуля blockinfile:
— name: Simple config for PDF converter
blockinfile:
path: /opt/app/conf/app.yml
block: |
# Simple config
option:
key1: value1
key2: value2
key3: value36. Сохранить содержимое переменной в файл.
Выше мы рассматривали модуль copy для копирования файлов. Мы также можем его использовать для помещения содержимого переменной в файл:
— name: Save my_variable to data.txt file
copy:
content: «>»
dest: ‘/tmp/data.txt’* в данном примере содержимое переменной my_variable будет сохранено в файле /tmp/data.txt.
Работа с SSL
В данном разделе рассмотрим задачи управления сертификатами и шифрованием при передачи данных.
1. Добавить публичный ключ хоста в known_hosts.
Делается с помощью known_hosts. Пример из официальной документации:
— name: Tell the host about our servers it might want to ssh to
known_hosts:
path: /etc/ssh/ssh_known_hosts
name: foo.com.invalid
key: «>»* в данном примере мы добавим ключ из файла pubkeys/foo.com.invalid в /etc/ssh/ssh_known_hosts.
2. Создание новых SSH-ключей для сервера.
Создание ключей реализуется с помощью модуля openssh_keypair:
* в данном примере мы создадим 4 ключа разных типов: dsa, ecdsa, ed25519, rsa. Так как у каждого из них свои требования к размеру, перечень представлен в виде двумерного массива. Ключи будут созданы в каталоге /etc/ssh/.
3. Работа с SSH authorized_key.
Данный файл содержит публичный ключ для подключения по SSH. Работа с ним через Ansible выполняется с помощью модуля authorized_key.
Пример добавления ключа:
— name: Set authorized key took from file
authorized_key:
user: root
state: present
key: ‘>’
with_file:
— files/key.pub* в данном примере мы берем содержимое файла files/key.pub и устанавливаем его для пользователя root.
4. Создание самоподписанного сертификата.
Чтобы получить, более или менее, корректный самоподписанный сертификат, нам нужно будет воспользоваться тремя модулями ansible:
- openssl_privatekey — создаст приватный ключ.
- openssl_csr — создаст запрос для выпуска открытого сертификат по заданным параметрам на основе закрытого ключа.
- openssl_certificate — сгенерирует открытый сертификат на основе ключа запроса.
Рассмотрим использование данных модулей на конкретном примере:
— name: Создаем закрытый ключ (key)
openssl_privatekey:
path: /etc/ssl/mycert.key
size: 2048— name: Создаем файл запроса (csr)
openssl_csr:
path: /etc/ssl/mycert.csr
privatekey_path: /etc/ssl/mycert.key
country_name: RU
locality_name: Russian Federation
organization_name: Dmosk
email_address: master@dmosk.ru
common_name: ansible.dmosk.ru
subject_alt_name:
— «DNS:ansible.dmosk.ru»
— «DNS:test.dmosk.ru»— name: Создаем самоподписанный сертификат (crt)
openssl_certificate:
path: /etc/ssl/mycert.crt
csr_path: /etc/ssl/mycert.csr
privatekey_path: /etc/ssl/mycert.key
provider: selfsigned* в нашем запросе мы в итоге получим ключи mycert.key и mycert.crt в каталоге /etc/ssl. Результат будет похожим, если бы мы ввели команду openssl req -nodes -new -x509 -keyout /etc/ssl/mycert.key -out /etc/ssl/mycert.crt -subj ‘/C=RU/L=Russian Federation/O=Dmosk/CN=ansible.dmosk.ru’.
Виртуализация VMware
Работа с виртуальными машинами на платформе VMware выполняется с помощью большого количества различных модулей vmware. С полным их списком можно ознакомиться на странице https://docs.ansible.com/ansible/latest/collections/community/vmware/index.html.
Мы рассмотрим несколько примеров. В них мы будем использовать следующие переменные:
- vcenter_hostname — имя сервера vcenter или его IP-адрес.
- vcenter_username — логин для подключения к vcenter.
- vcenter_password — пароль для подключения. Лучше хранить с использованием vailt.
- vcenter_datacenter — имя датацентра, к которому нужно подключаться.
Данные переменные необходимо определить заранее. Это можно сделать при вызове плейбука, описать в самом сценарии или роли. Подробнее о работе с переменными смотрите раздел выше.
1. Модуль vmware_guest
С его помощью мы можем взаимодействовать с гостевой операционной системой виртуальной машины. Необходимо позаботиться, чтобы на последней были установлены VMware Tools.
а) Базовое подключение.
Для выполнения действий над виртуальными машинами мы должны подключиться к хосту VMware. Для этого используем данные строки:
* параметр validate_certs, выставленный в no, позволит избежать ошибки, если у нас на хосте используется самоподписанный сертификат (как правило, так и есть).
б) Переименовать виртуальную машину.
Для выполнения действия нам нужно знать идентификатор виртуальной машины:
* где uuid — идентификатор виртуальной машины; name — новое имя виртуальной машины.
в) Конвертировать виртуальную машину в шаблон.
Для этого нужно просто задать признак is_template:
2. Модуль vmware_vm_info
Позволяет собирать информацию о виртуальных машинах на хосте VMware.
а) Получение информации по всем виртуальным машинам на хосте:
— name: Gather all registered virtual machines
vmware_vm_info:
hostname: ‘>’
username: ‘>’
password: ‘>’
validate_certs: no
register: vminfo— debug:
var: vminfo.virtual_machines* всю информацию мы запишем в переменную vminfo и выведем ее на экран.
б) Чтобы получить информацию не о всех машинах, а о конкретной, то добавляем опцию vm_name:
— name: Gather all registered virtual machines
vmware_vm_info:
hostname: ‘>’
username: ‘>’
password: ‘>’
validate_certs: no
vm_name: mail-vm
register: vminfo3. Модуль vmware_guest_info
Позволяет получить подробную информацию о виртуальной машине.
а) Получение информации о конкретной виртуальной машине:
— debug:
var: vminfo.instance* информация будет собрана для виртуальной машины с именем, которое определено в переменной vm_name.
4. Модуль vmware_guest_powerstate
Позволяет включать, выключать и перезагружать виртуальные машины.
а) Включение виртуальной машины:
б) Выключение виртуальной машины:
— name: ‘Shutdown >’
vmware_guest_powerstate:
hostname: «>»
username: «>»
password: «>»
name: «>»
state: shutdown-guest
state_change_timeout: 200
validate_certs: no— name: Set the state of a virtual machine to poweroff
vmware_guest_powerstate:
hostname: «>»
username: «>»
password: «>»
name: «>»
state: powered-off
validate_certs: noв) Перезагрузка:
— name: ‘Restart >’
vmware_guest_powerstate:
hostname: «>»
username: «>»
password: «>»
name: «>»
state: reboot-guest
state_change_timeout: 200
validate_certs: no— name: Set the state of a virtual machine to restarted
vmware_guest_powerstate:
hostname: «>»
username: «>»
password: «>»
name: «>»
state: restarted
validate_certs: noВиртуализация Proxmox
Рассмотрим немного примеров по работе с системой виртуализации proxmox.
Для работы с данной системой виртуализации есть в Ansible специализированные модули, но, пока, их мало. Поэтому часть задачь решается с помощью API.
1. Сбор информации
В данном разделе мы рассмотрим примеры для получения информации с Proxmox.
а) Список виртуальных машин на хосте. Для получения информации о виртуальных машинах, которые находятся на хосте виртуализации нужно использовать API Proxmox. Для этого в ansible мы будем применять модуль URI:
— name: Get vms from pve
uri:
url: «https://pve.dmosk.local:8006/api2/json/cluster/resources?type=vm»
headers:
Authorization: PVEAPIToken=ansible@pve!Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7
follow_redirects: all
register: vm_list- pve.dmosk.local — адрес веб-интерфейса сервера виртуализации.
- 8006 — порт для подключения в веб-интерфейсу.
- ansible@pve — имя учетной записи, для которой создан токен доступа. Он создается в консоле управления Proxmox (Датацентр — Разрешения — API Tokens).
- Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7 — имя токена и сам токен.
В результате мы сохраним список виртуальных машин в переменной vm_list.
б) Подробная информация о виртуальной машине или хосте виртуализации. Если мы хотим собрать побольше информации, нам также понадобится API:
— name: Get vm info
uri:
url: «https://pve.dmosk.local:8006/api2/json/»
headers:
Authorization: PVEAPIToken=ansible@pve!Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7
follow_redirects: all
register: vm_infoДля получения различной информации о виртуальной машине или сервере мы используем различные запросы (отмечено как ). С полным списком того, что мы можем получить предлагаю ознакомиться на странице pve.proxmox.com/pve-docs/api-viewer.
Например, для получения конфигурации виртуальной машины, используем запрос:
Чтобы узнать hostname:
Работа с Docker
В данном разделе мы рассмотрим работу с контейнерами Docker.
Для корректной работы модуля, на целевом хосте должен быть установлен компонент python docker:
pip3 install docker
Обратите внимание, что должен использоваться python версии 3. Для версии 2 уже возникают ошибки при установке и работе модуля.
1. Работа с контейнером.
С помощью модуля docker_container мы можем работать с контейнерами. Создавать их, удалять, перезапускать и так далее. Рассмотрим несколько примеров.
а) Создать новый контейнер можно с помощью сценария:
— name: Create a new container
docker_container:
name: new_container
image: nginx* в данном примере будет создан контейнер с именем new_container из образа nginx.
Создание контейнера с большим количиством опций:
— name: Create a new container
docker_container:
name: new_container
image: nginx
auto_remove: yes
volumes:
— «/tmp:/tmp»
ports:
— «80:80»- auto_remove — удалить контейнер после завершения его работы.
- volumes — создаем volume. В данном случае, проброс каталога /tmp внутрь контейнера тоже в /tmp.
- ports — на каком порту должна слушать хостовая система и на какой порт пробрасывать запросы внутрь контейнера.
б) Для удаления используем:
— name: Delete container
docker_container:
name: new_container
state: absent* важно указать имя контейнера и state со значением absent.
2. Запуск команды внутри контейнера.
Данное действие эквивалентно команде docker exec. В ansible можно выполнить с помощью модуля docker_container_exec или ранее рассмотренного docker_container. Рассмотрим оба варианта.
Мы можем использовать данный модуль, когда нужно выполнить команду внутри запущенного контейнера. По умолчанию, его нет в системе и требуется отдельная установка командой:
ansible-galaxy collection install community.docker
— name: Download wp-cli utility
community.docker.docker_container_exec:
container: «wordpress»
command: curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chdir: /root* в нашем примере мы скачаем утилиту wp-cli в контейнере wordpress. Команда будет запущена в директории chdir.
Данный способ подойдет в случае, когда нам нужно создать контейнер, запустить его и выполнить команду. Например:
— name: Create a new container
docker_container:
name: new_container
image: nginx
command: whoamiОблачная платформа OpenStack
В данном разделе рассмотрим модуль работы с платформой виртуализации OpenStack. Данный модуль не входит в стандартный комплект и необходимо выполнить предварительные настройки для системы.
Устанавливаем коллекцию openstack.cloud на компьютер, где запускается ansible:
ansible-galaxy collection install openstack.cloud
Ознакомиться со всеми модулями данной коллекции можно на странице с документацией Ansible.
Также необходим python версии 3 с модулем openstacksdk. Установим данный модуль на компьютере, где будет запускаться сценарий:
python3 -m pip install —upgrade pip
python3 -m pip install openstacksdk
Для подключения и работы с OpenStack необходимы данные, характерные для хостинг-провайдера облачной услуги. Для этого изучите документацию по работе с OpenStack CLI на официальном сайте последнего. Также немного о работе с OpenStack CLI на моем сайте.
Для работы с модулями группы openstack нам нужно будет проходить авторизацию. Для этого мы будем использовать переменные, например:
cloud:
auth_url: https://infra.mail.ru:35357/v3/
username: cloud-user-name
password: cloud-user-password
project_id: cloud-project-id
user_domain_name: cloud-user-domain- auth_url — ссылка для подключения к облаку по openstack api.
- username — учетная запись для прохождения аутентификации.
- password — пароль от учетной записи.
- project_id — идентификатор проекта.
- user_domain_name — домен пользователей.
И так, рассмотрим простые примеры. Для удобства, попробуем разбить информацию по разделам.
Работа с виртуальными машинами
Используем модуль openstack.cloud.server.
1. Создание инстанса:
— name: Создать виртуальную машину в облаке VK Cloud
openstack.cloud.server:
state: present
auth:
auth_url: «>»
username: «>»
password: «>»
project_id: «>»
user_domain_name: «>»
name: vm-name
region_name: cloud-region-name
image: cloud-image-id
key_name: key-ssh-name
timeout: 200
flavor: cloud-vm-template
availability_zone: MS1
nics:
— net-id: cloud-network-id
security_groups:
— default
— ssh
meta:
hostname: my-cloud-vm-01* обратите внимание, что некоторые опции не будут работать для вашего поставщика облачной услуги. Их нужно заменить на другие, а какие именно, придется изучить соответсвующую документацию, представленную на сайте последнего.
2. Удаление виртуальной машины:
— name: Удалить виртуальную машину в облаке openstack
openstack.cloud.server:
state: absent
auth:
auth_url: «>»
username: «>»
password: «>»
project_id: «>»
user_domain_name: «>»
name: vm-name3. Используем cloud-config. Большинство облачных провайдеров используют образы операционных систем с установленным cloud-init. Данный компонент позволяет автоматизировать некоторые операции по администрированию системы. Например, мы можем при создании виртуальной машины выполнить дополнительные настройки:
— name: Создать виртуальную машину в облаке VK Cloud + выполнить предварительные настройки
openstack.cloud.server:
state: present
.
config_drive: true
userdata: |
#cloud-config
users:
— name: dmosk
groups: [ sudo ]
shell: /bin/bash
lock_passwd: false
passwd: «$6$ssBWwe7wxa.lPToS$P5gm0y13m3X8VsK.czScN0ilTxYrB86KNhv7YvD4.4f0CdTDvfAV3W95pYQCPUdF2iRtIXBSVIw/ZYnxtBvOA.»
ssh_pwauth: true
hostname: «my-cloud-vm-01»* в данном примере нужно обратить внимание на следующие моменты:
- config_drive — по умолчанию, cloud-init берет информацию для настройки с сервера. Данный параметр указывает, что должен использоваться пользовательский конфиг.
- users — создаем пользователей в системе.
- Некоторые провайдеры не позволяют создавать учетные записи с логином admin.
- Хэш для passwd можно получить командой mkpasswd -m sha-512.
Получение информации
В данном разделе мы будем использовать следующие модули:
- server_info — позволит получить информацию о виртуальной машине.
- resources — сведения о различных ресурсах в облаке.
- image_info — информация об образах для виртуальных дисков.
Рассмотрим следующие примеры.
1. Получение информации о виртуальной машине. Выполняем с помощью модуля server_info:
— name: Получаем информацию о виртуальной машине vm1
openstack.cloud.server_info:
auth:
auth_url: «>»
username: «>»
password: «>»
project_id: «>»
user_domain_name: «>»
name: vm1
register: openstack— debug:
var: openstack2. Список виртуальных машин. Используем модуль resources:
— name: Получаем список виртуальных машин
openstack.cloud.resources:
auth:
auth_url: «>»
username: «>»
password: «>»
project_id: «>»
user_domain_name: «>»
service: compute
type: server
register: vm_list3. Список образов для виртуальных дисков. Используем image_info.
б) конкретный образ:
— name: Получаем информацию об образе «Ubuntu-22.04-202208»
openstack.cloud.image_info:
auth:
auth_url: «>»
username: «>»
password: «>»
project_id: «>»
user_domain_name: «>»
image: Ubuntu-22.04-202208
register: image_infoРабота с базами данных
В данном блоке рассмотрим примеры работы с различными базами данных.
MySQL / MariaDB
Работа с базой данных возможна с помощью различных коллекций:
- mysql.mysql_db — позволяет редактировать базу данных или создавать дамп.
- mysql_query — создание запросов.
Они не идут в комплекте к ansible и нам необходимо установить их командой:
ansible-galaxy collection install community.mysql
Теперь рассмотрим несколько примеров для работы с MySQL / MariaDB через ansible.
1. Резервное копирование:
— name: Dump mysql databases
community.mysql.mysql_db:
state: dump
name:
— db1
— db2
target: /tmp/dump.sql* в данном примере мы создадим 2 дампа из баз db1 и db2 и сохраним результат в файл /tmp/dump.sql.
PostgreSQL
Рассмотрим разные примеры работы с СУБД PostgreSQL. Мы будем использовать следующие модули:
- postgresql_db. Работа с базами данных.
- postgresql_query. Создание запросов.
- postgresql_set. Внесение изменений в настройки СУБД.
Обратите внимание, что в некоторых случаях понадобится установка модуля командой:
ansible-galaxy collection install
Подробнее можно найти инструкцию по ссылке выше.
1. Резервное копирование:
— name: Create dump
postgresql_db:
name: DATABASE
login_host: SERVER
port: PORT
login_user: LOGIN
login_password: PASSWORD
state: dump
target: ‘/tmp/dump.sql’2. Восстановление из дампа:
Сценарий напоминает резервное копирование, за исключением опции state:
— name: Create dump
postgresql_db:
name: DATABASE
login_host: SERVER
port: PORT
login_user: LOGIN
login_password: PASSWORD
state: restore
target: ‘/tmp/dump.sql’3. Выполнение запроса:
Запрос в базу можно сделать с помощью модуля postgresql_query:
— name: Select query
postgresql_query:
query: «SELECT * FROM users»
db: DATABASE
login_host: SERVER
port: PORT
login_user: LOGIN
login_password: PASSWORDРассмотрим конкретный пример с сохранением результата в переменную:
— name: Select query
postgresql_query:
query: «SELECT * FROM users WHERE name = ‘%s’ or family = ‘%s'»
db: clients
login_host: localhost
port: 5432
login_user: dbuser
login_password: dbpassword
positional_args:
— Andrey
— Ivanov
register: myclients— name: Set variable
set_fact:
my_clients: myclients.query_result- подключились к серверу баз данных на локальном сервере под пользователем dbuser с паролем dbpassword.
- сделали запрос к базе clients. Запрос должен получить список всех записей из таблицы users, где в поле name есть значение Andrey или в поле family — Ivanov. Обратите внимание, что мы использовали паттерны с применением positional_args.
- результат работы задачи мы зафиксировали в переменной myclients.
- создали переменную my_clients, куда занесли результыты выборки.
4. Конфигурирование PostgreSQL. С помощью модуля postgresql_set можно вносить изменения в конфигурацию СУБД на подобие команды ALTER SYSTEM.
— name: Включаем SSL в PostgreSQL
postgresql_set:
name: ssl
value: ‘on’
login_unix_socket: »
become: yes
become_user: postgres* в данном примере мы разрешим SSL на PostgeSQL. Обратите внимание на директиву login_unix_socket — она указывает на папку, где находится сокет подключения к СУБД. Его использует встроенная учетная запись postgres, чтобы подключаться к базе. По умолчанию, ansible ищет сокетный файл .s.PGSQL.5432 в каталоге /var/run/postgresql, но в некоторых случаях, например, при работе с Postgres Pro, данный путь может отличаться. У меня он был /tmp — в таком случае путь нужно указать явно, в противном случае мы получим ошибку unable to connect to database: connection to server on socket \»/var/run/postgresql/.s.PGSQL.5432\» failed: No such file or directory\n\tIs the server running locally and accepting connections on that socket?\n.
Запуск плейбука
Рассмотрим отдельно некоторые возможности при запуске плейбука.
1. Начинать выполнение с определенной задачи.
При выполнении отладки, полезно запустить плейбук, но начать выполнение с определенной задачи. Остальные пропустить.
Это можно сделать с помощью опции —start-at-task:
ansible-playbook . —start-at-task=»Start Job»
* в данном примере плейбук начнет выполнять задания с задачи Start Job.
2. Передача переменных.
Мы можем определить переменные при запуске плейбука с помощью опции —extra-vars. Возможна передача одной или нескольких переменных:
3. Использование тегов.
Теги позволяют пропускать или выполнять определенные задания. Предположим, у нас есть задание:
— name: Install postfix
package:
name: postfix
state: present
tags:
— init postfix* в данном примере мы навесили тег init postfix на задание Install postfix.
Теперь мы можем запустить плейбук:
ansible-playbook . —tags «init postfix»
И задание будет выполнено.
ansible-playbook . —skip-tags «init postfix»
И задание будет проигнорировано.
Обработка ошибок
В процессе работы мы сталкиваемся с проблемами в отработки сценариев. Можно настроить поведение при выполнении заданий, если что-то пошло не так.
1. Игнорировать ошибки.
Если ansible столкнется с ошибкой при выполнении задачи, работа плейбука будет завершена. Иногда, нужно пропустить ошибку при выполнении определенной задачи, чтобы выполнение было продолжено. Для этого существует опция ignore.
а) чтобы пропустить ошибки выполнения, в настройка задачи используем:
— name: Bad Task
.
ignore_errors: yesб) чтобы игнорировать ошибки при подключении к хосту:
— name: Bad Task
.
ignore_unreachable: yesв) также мы можем проигнорировать ошибку по условию, например:
— name: Выполняем shell
shell: .
register: result
failed_when: >
result.rc != 0 and
‘already exists’ not in result.stderr* в данном примере мы выполняем какую-то каманду, и если она закончится с ошибкой, в сообщении которой есть фраза «already exists», ansible не воспримет это за ошибку и продолжит выполнение сценария.
2. Выбрасывать ошибку по своему условию.
Мы можем задать свое условие, когда Ansible должен считать, что задача отработана неправильно и нужно остановить работу. Это делается с помощью модуля fail.
а) выкинуть ошибку, если переменная не равна определенному значению:
— name: Fail if my data not loaded
fail:
msg: My data has not been loaded completely.
when: my_dataload_status != «complete»* в данном примере если переменная my_dataload_status не равна complete, системв вернет ошибку.
б) ошибка, если переменная содержит запрещенный символ:
— name: Fail if * in var
fail:
msg: «Ошибка — в переменной login есть символ *»
when: ‘»*» in login’3. Действия в случае ошибки.
Рассмотрим 2 варианта:
- Мы, в любом случае, хотим выполнить чать задачь, даже если сценарий прекратит работу из-за ошибки.
- Мы хотим выполнить определенное действие, если сценарий прекратит работу из-за ошибки.
Это возможно реализовать с помощью block + rescue/always.
а) Выполнять задачу после rescue, если основной сценарий выполнился с ошибкой:
— name: Основной сценарий
block:
.
rescue:
— name: Выполнить, если произошла ошибка
debug:
msg: ‘Show error’б) Выполнять задачу после always независимо от результата выполнения основного сценария:
— name: Основной сценарий
block:
.
always:
— name: Выполнить в любом случае
command: rm -f /tmp/testв) И то и другое:
— name: Основной сценарий
block:
.
rescue:
— name: Выполнить, если произошла ошибка
debug:
msg: ‘Show error’
always:
— name: Выполнить в любом случае
command: rm -f /tmp/test* в данном примере мы отобразим текст Show error только при наличии ошибки, а также выполним команду rm -f /tmp/test независимо от исхода работы сценария.
Работа с выводом и строками
В данном разделе будут рассмотрены варианты манипуляции данными — парсинг, фильтры, обработки и подобные действия. Так как ansible является декларативным языком, описанные операции лучше свести к минимуму — автору показалась такая работа не совсем удобной.
Конвертация типов данных
От типа данных зависят операции и способы работы с ними. Приведу пример типов данных, с которыми мы можем столкнуться:
- Строка — набор символов. Заключаются в кавычки.
- Число — набор цифр. Записываются без кавычек.
- Список (list) — массив данных. В качестве ключа используется пронумерованный индекс. Записываются в квадратных скобках.
- Словарь (dict) — массив данных. В качестве ключа используется символный индекс. Записываются в фигурных скобках.
- JSON — строка, имеющая формат записи в виде словаря. Считается удобным для передачи данных между системами.
- YAML — строка, в которой данные разделяются переносами, а вложенность определяется отступами. Формат строго зависит от последних — лишний пробел нарушает его обработку. Удобно использовать для наглядного вывода информации.
Рассмотрим примеры преобразований данных из одного типа в другой.
1. Строку в число. Преобразуем с помощью int:
2. Список в json. Можно выполнить с помощью to_json:
или в json, который будет удобно читать:
Преобразование строк
Полученные строки могут требовать дальнейшего разбора, например, парсинга. Рассмотрим несколько примеров.
1. Получение массива с помощью split.
Метод split позволяет разбить строку на части по определенному символу. Полученные части станут значениями массива:
* в данном примере мы разобьем строку из переменной results на части по двоеточию и сохраним все это в переменную newvar.
2. Замены.
Мы рассмотрим несколько способов.
а) C помощью replace.
Позволяет в нашем выводе заменить одни символы другими:
* тут в переменную newvar будет записана строка results, из которой мы уберем все пробелы.
б) с помощью regex_replace.
Если нам нужно использовать регулярные выражения, то используем regex_replace. Например:
. позволит искать слово test в начале строки.
3. Использование фильтров.
С помощью regex_search мы можем оставить определенную часть строки:
В данном примере мы создадим переменную my_var, значением которой будет чисо 12 (из строки my-chars-12 мы получим число).
Работа с массивами
В данном подразделе будем работать со списками и словарями.
1. Объединение.
Предположим, что у нас есть два списка из словарей:
vars:
list1:
— name: name1
value1: value11
— name: name2
value1: value12
list2:
— name: name1
value2: value21
— name: name3
value2: value22Мы можем их объединить по одному из полей, например, name:
* для lists_mergeby обязательно полное написание — community.general.lists_mergeby.
В результате, мы получим такой массив:
name: name1
value1: value11
value2: value21
>,
name: name2
value1: value12
>,
name: name3
value2: value22
>* как видим, два массива слились в один. Для ключа name1 также добавлено поле value2.
2. Преобразование элемента списка в словарь.
Предположим, у нас есть:
vars:
list:
— key: key1
value: value1
— key: key2
value: value2
— key: key3
value: value3Мы можем его преобразовать в словарь по имеющимся ключам (key).
а) Если имя ключа для ключа key, а имя ключа для значения value:
Мы должны увидеть что-то на подобие:
б) Если имя ключа для ключа НЕ key, а имя ключа для значения НЕ value:
* в данном примере предполагается, что у нас есть массив с ключами name и model. Мы хотим, чтобы в качестве ключа для создаваемого словаря использовался первый, а для значения второй.
Формат JSON
Посмотрим, что мы можем сделать с json.
1. Фильтр данных. С помощью json_query мы можем выбирать только те данные, которые соответствуют критерию поиска.
а) один критерий:
* в данном примере мы сделаем выборку всех данных, где значение поля state равно running.
б) два критерия:
— name: Display json filtered data
debug:
var: item
loop: «>»
vars:
json_query_string: >-
[?state==’running’ && code==`200`]* в данном примере мы добавили критерий code==`200`. Обратите внимание, что в нем мы используем другой тип кавычек. Это сделано не просто так — для строк используются кавычки » или ‘, для цифр — `.
в) фильтр с выбором конкретных полей:
— name: Display json filtered data
debug:
var: item
loop: «>»
vars:
json_query_string: >-
[?state==’running’ && code==`200`].* в этои примере мы взяли ранее использовавшейся фильр и перечислили конкретные поля, которые мы хотим показать при выводе информации.
Шаблоны Jinja2
Помимо сценария с заданиями, при автоматизации сильно помогает шаблонизатор Jinja2. С его помощью можно на лету формировать файл с любым содержимым, которое должно быть сформировано динамически. Также шаблоны jinja можно применять в самих заданиях. Рассмотрим это на нескольких примерах.
1. Перебор массива.
Предположим, нам нужно перебрать все элементы массива в шаблоне. Это можно сделать конструкцией:
* в данном примере мы сделаем перебор по переменной my_hosts. Для каждого элемента массива будет создана строка со значением server .
2. Задаем значение переменной с IF.
Допустим, наша переменная должна иметь значение, в зависимости от значения другой переменной. Для этого можно применить один из следующих синтаксисов:
* мы рассмотрели следующие варианты:
- var1 — если есть переменная hostname, то добавляем к test еще и -value. В итоге, получится test-value.
- var2 — если есть переменная hostname, значение будет value1, в противном случае — value2.
- var3 — если переменная hostname равна myhost, задаем для var3 значение value1, в противном случае — value2.
- var4 — другой формат записи. Если есть переменная hostname, значение будет value1, в противном случае — value2.
Разное
В данном разделе будет рассказано о дополнительных опциях, которые позволяют менять поведение выполнения задач, добавляет функциональности или все то, для чего не найдена отдельная подходящая категория.
1. Шифрование строки.
С помощью ansible-vault мы можем шифровать файлы и папки. Это позволит нам хранить секреты не в открытом виде. Данные расшифровываются в момент выполнения задач.
Данной командой мы получаем шифрованную строку:
Система запросит ввести дважды пароль и предложит ввести строку, которую нужно зашифровать. После мы должны нажать 2 раза Ctrl + D — мы получим строку, которая начинается с !Vault и различные символы.
Для того, чтобы в момент выполнения задачи ansible расшифровал данные, при запуске плейбука мы должны указать ключ —ask-vault-pass:
Для дешифровки строки можно создать файл, в который добавить строку типа:
Допустим, создан файл с названием decrypt.txt. Тогда для дешифровки вводим команду:
cat decrypt.txt | ansible-vault decrypt
Система запросит пароль, который мы указывали, когда шифровали данную строку.
2. Завершить выполнение плейбука после определенной задачи.
С помощью данной конструкции:
— name: Stop Play
meta: end_playМы можем полностью остановить выполнение задач для хоста:
— name: Stop Play for Host
meta: end_host3. Зависимые роли.
С помощью файла meta/main.yml в роли мы можем определить пред-роль, от которой зависит выполнение текущей роли. Для этого настраивается опция dependencies:
dependencies:
— role: pred4. Вставка роли и ее задач.
Позволяет в процессе выполнения задачи подключить роль. Делается при помощи include_role:
— name: «Include Other Role»
include_role:
name: other_roleА это пример, как подключить роль и сделать так, чтобы все ее задачи выполнились на определенном хосте:
— name: «Include Other Role»
include_role:
name: other_role
apply:
delegate_to: «>»Еще один способ включить роль, но выполнить задания из другого файла:
— name: «Include Other Role From new_roles File»
include_role:
name: other_role
tasks_from: new_roles* в данном примере будут использовать файл не main.yml, а new_roles.yml.
5. Повторы и циклы при выполнении задач.
Мы можем управлять цикличностью выполнения задач с помощью различных модулей ansible. Рассмотрим их на примерах.
а) Повторный запуск задачи.
Выполняется с помощью retries (количиство повторов) и delay (задержка в секундах). Например, можно еще раз запустить задачу при возникновении ошибки:
— name: Run anything command
command: /foo/bar/cmd
register: result
retries: 3
delay: 60
until: result is not failed* в данном примере мы будем выполнять команду /foo/bar/cmd пока ее выполнение не закончится без ошибок. Количество повторов будет равен 3 с интервалом в 60 секунд.
б) Использование циклов.
Выше в примерах мы часто упоминали loop для цикличного запуска задач в разными вариантами выполнения, например:
— name: Create Directories with loop
file:
path: «>»
state: directory
loop:
— ‘/var/log/prometheus’
— ‘/var/log/grafana’* в данном примере задача будет выполнена два раза — для создания каталогов /var/log/prometheus и /var/log/grafana.
в) Циклы из списков:
* подразумевается, что у нас есть переменная nginxcfg в виде списка (массива). В данном примере ansible пробежит по каждой записи данного списка и сделает соответствующую замену в конфигурационном файле nginx.conf.
г) Повтор задачи несколько раз.
Мы можем задать переменную, с помощью которой будем контролировать число повторов при выполнении задачи. Это может быть удобным, если нам нужно создать, например, несколько виртуальных машин.
vars:
var_count: 5* в данном примере мы создаем цикл от 1 до 5 (значение переменной var_count). Чтобы ключ для цикла item не конфликтовал с ключами других циклов, которые могут использоваться внутри задачи, меняем имя item (по умолчанию) на var_count_item с помощью директивы loop_var.
6. Объединение задач в блоки.
Это позволит установить общие свойства и условие для нескольких задач. Такая форма записи уменьшит количиство строк и упростит восприятие.
— name: Block Name
block:
— name: Task 1
.— name: Task 3
.
when: ansible_facts[‘distribution’] == ‘CentOS’
become: true
become_user: root
ignore_errors: yes* в данном примере будет выполнены 3 задачи, если выполнится одно условие, которое описывается не для задач, а для блока.
7. Обращения к DNS.
При помощи модуля dig мы можем отправлять запросы на серверы имен, получая сведения о доменных записях. Сценарии использования могут быть различные.
а) простой запрос на получение IP-адреса для А-записи:
* в данном примере мы получим адрес для узла www.dmosk.ru.
б) запрос на получение IP-адреса для А-записи через определенный DNS-сервер:
8. Отправка POST запроса.
Ранее мы рассматривали модуль uri для скачивания файла. Однако, данный модуль может быть более полезным. Например, мы можем отправлять запросы к API:
— name: Send Post Request
uri:
url: https://api.dmosk.ru/api/v2/send_request
method: POST
body_format: form-urlencoded
return_content: false
body:
id: 1
name: Test
action: Read
headers:
Authorization: Bearer 577f573d09a1949436e3a07f7e9de6c5* в данном примере будет отправлен POST-запрос на адрес https://api.dmosk.ru/api/v2/send_request. В POST-данных мы отправим id, name и action. Также дополнительно мы отправили заголовок Authorization.
9. Дата и время.
Мы можем использовать встроенную переменную ansible_date_time для отображения даты и времени в разных форматах. Чтобы переменная не была пустой, ansible должен собрать факты о хосте. Для этого используем инструкцию gather_facts: yes.
Чтобы увидеть все форматы, напишем такой сценарий:
— name: Ansible fact — ansible_date_time
debug:
var: ansible_date_timeМы должны увидеть что-то на подобие:
ok: [localhost] => «ansible_date_time»: «date»: «2022-11-11»,
«day»: «11»,
«epoch»: «1668182701»,
«hour»: «19»,
«iso8601»: «2022-11-11T16:05:01Z»,
«iso8601_basic»: «20221111T190501847269»,
«iso8601_basic_short»: «20221111T190501»,
«iso8601_micro»: «2022-11-11T16:05:01.847269Z»,
«minute»: «05»,
«month»: «11»,
«second»: «01»,
«time»: «19:05:01»,
«tz»: «MSK»,
«tz_offset»: «+0300»,
«weekday»: «Пятница»,
«weekday_number»: «5»,
«weeknumber»: «45»,
«year»: «2022»
>
>Чтобы получить только дату, используем:
— name: Get date
debug:
var: ansible_date_time.date10. Генерация случайных строк
Очень полезно, если нам нужно сгенерировать пароль. Выполняется с помощью модулей password и lookup.
— debug:
var: password_var* в данном примере мы получим строку из 12-и символов:
- первые 2 и последние 2 символа не будут содержать спецсимволов. Я не люблю специальные символы на концах.
- специальные символы перечислены в переменной spec_chars. Намеренно, исключены некоторые из них, которые могут вызывать проблемы, например звездочка (*).
- после получения строки мы заменим некоторые символы, которые могу вызывать путаницу. Например, большая буква O похожа на ноль, большая I (и) похожа на маленькую l (эль). И так далее.
- Результат будет записан в переменную password_var. С помощью модуля debug мы отобразим полученную строку на экране.
11. Добавить хост в инвентаризацию.
Ansible проходит по компьютерам согласно списку, который задается в инвентарном файле. Данный список не обязан быть статичным — мы можем добавлять в него хосты с помощью модуля add_host:
— name: Добавим хост mail в группу ‘servers’ с переменной foo
add_host:
name: mail
ansible_ssh_host: 192.168.0.11
ansible_ssh_port: 22
groups: servers
foo: barЧитайте также
Другая информация по Ansible: