Минимальные требования для Master server- Control server:
1. Linux (Red Hat, Debian, CentOS, OS X)
2. Python 2.6+ или 3.5+
Требования для управляемых серверов (Controlled Servers):
1 Linux: админский username/pass или SSH key и Python 2.6+
2 Windows: админский username/pass, PowerShell 3.0 и запустить скрипт
/ConfigureRemoting forAnsible.ps1
Порты используемые в работе Ansible:
Linux: SSH Port 22
Windows: WinRM Port 5986
Установка Ansible:
CentOS:
>sudo yum install epel-realease
>sudo yum install ansible
Check version:
> ansible —version
Установка на Ubuntu 16.04:
>sudo apt-add-repository ppa:ansible/ansible
>sudo apt-get update
>sudo apt-get install ansible
Установка на Amazon Linux через PIP
>sudo pip install ansible
В корне создаем папку ansible а в ней файл hosts.txt. Содержание /ansible/hosts.txt:
[servers1]
ubuntu1 ansible_host=192.168.56.109 ansible_ssh_user=admin1 ansible_ssh_pass=123
[servers2]
suse1 ansible_host=192.168.56.105 ansible_ssh_user=root ansible_ssh_pass=123
где [servers1], [servers2]- группы серверов,
ubuntu1 ansible_host=192.168.56.109 ansible_ssh_user=admin1 ansible_ssh_pass=123 -
адрес сервера, логин и пароль для доступа по ssh. Вместо параметра ansible_ssh_pass рекомендуется использовать ansible_ssh_private_key_file=/home/mykey1.pem,
то есть вместо пароля в открытом виде нужно пользоваться ключом.
Пинг для группы серверов (модуль пинг -m ping):
>ansible -i hosts.txt servers1 -m ping
Пинг для всех групп серверов (модуль пинг -m ping):
>ansible -i hosts.txt all -m ping
Создадим конфиг файл в той же папке /ansible/ansible.cfg для облегчения работы. Содержание файла:
[defaults]
host_key_checking = False
inventory = ./hosts.txt
Теперь команду пинг можно выполнять проще:
>ansible servers1 -m ping
или
>ansible all -m ping
перед выполнением команды ansible читает конфиги и берет всю недостающую инфорацию из нашего конфигурационного файла /ansible/ansible.cfg
Подключение к Windows серверам
Для этого необходимо на master server установить библиотеку Python "pywinrm" питоновским менеджером пакетов pip.
> sudo pip install "pywinrm>=0.2.2"
В конфиге, то есть inventory файле hosts.txt добавляем настройки для Windows систем:
[windows_servers]
win10 ansible_host=192.168.56.101
[windows_servers:vars]
ansible_user = user
ansible_password = 123
ansible_port = 5986
ansible_connection = winrm
ansible_winrm_server_cert_validation = ignore
[windows_servers:vars] - переменные для группы [windows_servers]
На Windows через PowerShell нам нужно выполнить скрипт который скачивается по линку https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
На Windows10 по умолчанию выполнение скриптов запрещено. Для этого запускаем PowerShell с правами администратора. Далее выполняем команду
set-executionpolicy unrestricted
Далее выполняем скрипт для Ansible в том же PowerShell с правами админа из папки со скриптом:
.\ConfigureRemotingForAnsible.ps1
На этом преднастройка для Windows завершена и хосты из группы [windows_servers] подготовленные таким образом готовы для работы с Ansible.
Для выполнения пинга набираем команду:
>ansible windows_servers -m win_ping
Чтобы явно не указывать пароль в inventory файле hosts.txt в параметре ansible_password, можно выполнить команду со следующими параметрами:
>ansible windows_servers -m win_ping --ask-pass
Создание children group в файле hosts.txt
[servers_linux:children]
servers1
servers2
Также можно делать вложенность групп.
Просмотреть inventory:
>ansible-inventory --list
Ad-Hoc команды в Ansible
Модуль setup. Информация о хостах и их параметрах
>ansible servers1 -m setup
>ansible servers1 -m setup | grep os_family
>ansible windows_servers -m setup --ask-pass
Ввод shell команд (к примеру для Linux это bash команды)
>ansible servers1 -m shell -a 'uptime'
>ansible servers1 -m shell -a 'df'
Модуль command подобен модулю shell. Но в модуле command не работают переменные окружения и > < |
>ansible servers1 -m command -a 'df'
Копирование файла (модуль copy)
>ansible linux -m copy -a "src=/ansible/1.txt dest=/home mode=777" -b
где -b -запустить команду с привилегиями root
-a - аргумент команды copy
при повторном выполнении той же команды
>ansible linux -m copy -a "src=/ansible/1/txt dest=/home mode=777" -b
никаких изменений не произойдет, т.е. скрипт проверит существование файла в директории назначения, и никаких действий не произойдет.
Удаление файла (модуль copy)
>ansible linux -m file -a "path=/home/1.txt state=absent" -b
Скачать файл (модуль get_url)
>ansible linux -m get_url -a "url=https://collectors.sumologic.com/rest/download/linux/64 dest=/home" -b
Инсталлировать пакеты:
Для Red Hat подобных ОС модуль yum
>ansible servers2 -m yum -a "name=stress state=installed" -b
Для Debian подобных ОС модуль apt
>ansible linux -m apt -a "name=stress state=installed" -b
Деинсталирвать пакет:
>ansible linux -m apt -a "name=stress state=removed" -b
Проверка коннекта c url (модуль uri):
>ansible linux -m uri -a "url=https://google.com"
Проверка коннекта и получение контента:
>ansible linux -m uri -a "url=https://google.com return_content=yes"
Установка apache:
>ansible servers1 -m apt -a "name=httpd state=latest" -b
Запустить и автоматически стартовать сервис httpd (модуль service):
>ansible servers1 -m service -a "name=httpd state=started enabled=yes" -b
Удаление apache:
>ansible servers1 -m apt -a "name=httpd state=removed" -b
Дебаг, информация о подключении:
>ansible servers1 -m shell -a 'uptime' -vvv
>ansible servers1 -m shell -a 'uptime' -vvvv
Список модулей ansible:
>ansible-doc -l
>ansible-doc -l | grep apt
Перенос переменных из файла inventory в отдельный файл:
Необходимо создать папку group_vars в католге ansible, т.е. /ansible/group_vars/
Далее создаем файл по имени группы серверов формата .yml.
Создаем файл windows_servers со следующим содержимым:
---
ansible_user : user
#ansible_password = 123
ansible_port : 5986
ansible_connection : winrm
ansible_winrm_server_cert_validation : ignore
Теперь данная информация находится в файле переменных /ansible/group_vars/windows_servers, из файла iventory hosts.txt ее можно удалить.
Командой ansible-inventory --list можно проверить изменилось ли что либо.
Ansible playbook
Playbook пишем в формате yml. Создаем файл playbook1.yml со следующим содержимым:
(Файл yml ansible всегда должен начинаться с трех минусов "---")
---
- name: Test connection
hosts: servers1
become: yes
tasks:
- name: ping a servers
ping:
где
name - имя playbook'а
hosts- имя хоста описанного в нашем файле hosts.txt
become- используем логирование от root`а
tasks- наменования задач
ping- модуль который используем в playbook'е, в данном случае без параметров
Запуск playbook'а:
>ansible-playbook playbook1.yml
результат выполнения:
PLAY [Test connection] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ubuntu1]
TASK [ping a servers] *****************************************************************************
ok: [ubuntu1]
PLAY RECAP ****************************************************************************************
ubuntu1 : ok=2 changed=0 unreachable=0 failed=0
Создадим playbook2.yml для установки Apache:
---
- name: Install Apache
hosts: servers1
become: yes
tasks:
- name: Install Apache
apt: name=httpd state=latest
- name: Start Apache and Enabled startup
service: name=httpd state=started enabled=yes
Создадим playbook3.yml для установки Apache и копирования файла из источника в конечную директорию на серверах:
---
- name: Install Apache
hosts: servers1
become: yes
vars:
source_file: ./Mysite/index.html
destin_file: /var/www/html
tasks:
- name: Install Apache
apt: name=httpd state=latest
- name: Copy file to servers
copy: src={{ source_file }} dest={{ destin_file }} mode=0555
notify: Restart Apache
- name: Start Apache and Enabled startup
service: name=httpd state=started enabled=yes
handlers:
- name: Restart Apache
service: name=httpd state=restarted
где source_file, destin_file- переменные, используемые в плейбуке
notify- вызов задачи в handlers при определенном условии (в данном случае изменения файла)
Печать переменных в playbook'е:
---
- name: Playbook print variables
hosts: linux
become: yes
vars:
message1: Privet
message2: Hello
secret: 01010101010
tasks:
- name: Print secret variable
debug:
var: message1
Команда debug печатает переменные.
Печать сообщений с переменными(команда msg):
---
- name: Playbook print variables
hosts: linux
become: yes
vars:
message1: Privet
message2: Hello
secret: 01010101010
tasks:
- name: Print secret variable
debug:
var: secret
- debug:
msg: "Secret word is {{secret}}"
Вывести данные всех серверов:
> ansible all -m setup (модуль setup)
Вывести результат команды в шелле (команду исполняем в плейбуке):
- shell: uptime
register: results
Сначала результат команды uptime заносим в переменную results. Затем, выводим переменную results
с помощью команды debug
- debug:
var: results
В результате наш плейбук имеет вид:
---
- name: Playbook print variables
hosts: linux
become: yes
vars:
message1: Privet
message2: Hello
secret: 01010101010
tasks:
- name: Print secret variable
debug:
var: secret
- debug:
msg: "Secret word is {{secret}}"
- set_fact: full_me="{{ msg }} {{ secret }}"
- debug:
var: full_me
- debug:
var: ansible_distribution
- shell: uptime
register: results
- debug:
var: results
Также можно вывести конкретную переменную из набора переменных выдаваемых командой uptime.
Например переменная stdout_lines results.stdout_lines.
- debug:
var: results.stdout_lines
Условия в Ansible.
Директива when. Выполнение задания по условию.
В предыдущем плейбуке в запись
- shell: uptime
register: results
добавим условие для выполнения:
- shell: uptime
register: results
when: ansible_os_family == "Suse"
Если условие ansible_os_family == "Suse" то есть переменная ansible_os_family (из модуля >ansible all -m setup) равна Suse, то условие
выполняется.
Деление плейбука на блоки по условию. Выполняемые строки можно объединять в блоки директивой - block.
- block: #Suse executable df
- shell: df
register: results
- debug:
var: results
when: ansible_os_family == "Suse"
- block: #Ununtu executable Uptime
- shell: uptime
register: results2
- debug:
var: results2
when: ansible_os_family == "Debian"
В примере выше выполняем шелл команду df если наша система семейства Suse и выполняем команду uptime если наша система семейства Debian.
Данные в переменную ansible_os_family можно посмотреть в модуле setup (>ansible all -m setup).
Наш плейбук теперь имеет вид:
---
- name: Playbook print variables
hosts: linux
become: yes
vars:
message1: Privet
message2: Hello
secret: 01010101010
tasks:
- name: Print secret variable
debug:
var: secret
- debug:
msg: "Secret word is {{secret}}"
# - set_fact: full_me="{{ msg }} {{ secret }}"
# - debug:
# var: full_me
# - debug:
# var: ansible_distribution
- block: #Suse executable df
- shell: df
register: results
- debug:
var: results
when: ansible_os_family == "Suse"
- block: #Ununtu executable Uptime
- shell: uptime
register: results2
- debug:
var: results2
when: ansible_os_family == "Debian"
Символом # можем комментировать строки. Буква w слова when должна быть строго под b слова block. Можно делать вложенные блоки.
Шаблонизатор Jinja2
Генерирует страницу html. Точнее подставляет нужные нам переменные в выводимую html страницу. В файле index.j2 хранится
шаблон формата Jinja2 который имеет следующий вид:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
Test page
Host address is:
{{ansible_host}}
</body>
</html>
где {{ansible_host}} - переменная которую можно посмотреть в модуле setup (ansible -m setup)
В результате наш плейбук будет выглядеть следующим образом:
---
- name: Generate html
hosts: servers1
become: yes
vars:
source_folder: /ansible/www_source # переменная из которой мы берем путь к папке источнику (на компьютере мастере!)
destin_folder: /var/www/html # переменная в которой хранится папка веб сервера, в нашем случае nginx (apt get nginx) на целевом комьютере
tasks:
- name: Generate html file from j2 file
template: src={{source_folder}}/index.j2 dest={{destin_folder}}/index.html mode=0555
Циклы Loop.
С помщью цикла можно установить на сервере сразу несколько утилит. К примеру
- name: Install many pakages
apt: name= {{item}} state=installed
loop:
- python
- tree
- mysql-client
или
- name: Install many pakages use (with_items)
apt: name= {{item}} state=installed
with_items:
- python
- tree
- mysql-client
with_items используется в более старой версии Ansible, эта команда идентична loop. Переменная {{item}} это зарезервированная переменная для циклов.
Создание роли.
Наш плейбук playbook5.yml на данный момент имеет вид:
---
- name: Generate html
hosts: servers1
become: yes
vars:
source_folder: /ansible/www_source
destin_folder: /var/www/html
tasks:
- name: Generate html file from j2 file
template: src={{source_folder}}/index.j2 dest={{destin_folder}}/index.html mode=0555
Роли создаются командой ansible-galaxy. Эта команда создаст каталог с подкаталогами.
$ ansible-galaxy init deploy_apache_web
Эта команда создаст роль (то есть папку с подпапками) deploy_apache_web. Список подпапок:
tasks - contains the main list of tasks to be executed by the role.
handlers - contains handlers, which may be used by this role or even anywhere outside this role.
defaults - default variables for the role (see Variables for more information).
vars - other variables for the role (see Variables for more information).
files - contains files which can be deployed via this role.
templates - contains templates which can be deployed via this role.
meta - defines some meta data for this role. See below for more details.
Описание назначений создаваемых папок можно посмотреть по адресу:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
Переносим переменную из нашего плейбука в нашу роль. Переменные в роли хранятся в defaults/main.yml.
destin_folder: /var/www/html копируем в этот файл.
Файл index.j2 можно пернести из папки www_source в наш католог templates в роли(каталоге) deploy_apache_web.
Блок tasks переносим из нашего плейбука в tasks/main.yml
Переменная source_folder нам не нужна. Запись template: src={{source_folder}}/index.j2 dest={{destin_folder}}/index.html mode=0555
можно записать как
template: src=index.j2 dest={{destin_folder}}/index.html mode=0555
В плейбуке вместо tasks пишем название роли roles. В итоге наш плейбук будет выглядеть следующим образом:
---
- name: Generate html
hosts: servers1
become: yes
roles:
- deploy_apache_web
можно роли делать с условиями:
roles:
- {role: deploy_apache_web, when: ansible_system == 'Linux'}
Запускаем наш playbook:
> ansible-playbook playbook5.yml
Использование Include, Import.
В плейбуке мы можем использовать подключение внешних файлов с расширением .yml. Для этого существует директива include.
Также можно использовать директиву import. Пример:
---
- name: Playbook "Include"
hosts: linux
become: yes
vars:
mytext1: "Hello Petya!"
tasks:
- name: Ping test
ping:
#comment - name: Create folder
- include: create_folder_for_playbook6.yml
#comment - name: Create file
- include: create_file_for_playbook6.yml
static: yes
В данном плейбуке мы создаем папку с файлом на серверах linux. Также мы имеем переменную mytext1, в которую подставляем значения.
Содержание файла create_folder_for_playbook6.yml:
---
- name: Create folder1
file:
path: /home/secret/folder1
state: directory
mode: 0755
Содержание файла create_file_for_playbook6.yml:
---
- name: Create file
copy:
content: "{{mytext1}}"
dest: /home/secret/folder1/text1.txt
owner: root
mode: 0777
Таким образом, если нам понадобится в будущем создать не одну, а несколько папок и несколько файлов, мы не будем загромождать плейбук.
Мы сможем красиво внести правки в соответсвующие файлы.
Delegate. Выполнение задач только на определенный сервер.
---
- name: Playbook "Delegate"
hosts: linux
become: yes
vars:
mytext1: "Hello delegate!"
tasks:
- name: Ping test
ping:
- name: Create file2
copy:
dest: /home/file2.txt
content: |
File2
Text of file2
- name: Create file3
copy:
dest: /home/file3.txt
content: |
File3
Text of file3
greetings {{mytext1}}
delegate_to: ubuntu1
- name: Write hostname to file
shell: echo {{ inventory_hostname }} >> /ansible/massages/shell.txt
delegate_to: 127.0.0.1
В данном плейбуке мы создаем файлы file2.txt и file3.txt на хостах linux (описано в hosts.txt). Но так как во втором таске
"Create file3" применена директива delegate_to, то создание файла file3.txt будет только на хосте ubuntu1 (описано в hosts.txt).
Также задача "Write hostname to file" записывает имя хоста из переменной {{ inventory_hostname }} на локальную машину (с которой
запускаем плейбуки) с помощью делегирования: delegate_to: 127.0.0.1.
---
- name: Playbook "Reboot and wait for server"
hosts: servers2
# become_user: yes
# become: yes
- name: Reboot servers
shell: sleep 3 && reboot
async: 1
poll: 0
- name: Wait server will come up online
wait_for:
host: "{{ inventory_hostname }}"
state: started
delay: 5
timeout: 50
delegate_to: 127.0.0.1
В данном плейбуке мы перезагружаем удаленный сервер. В задаче "Reboot servers" мы ждем 3 секунды и даем команду перезагрузки.
Параметры async: 1 и poll: 0 это рекомендуемые параметры в мануале Ansible для данной операции. Затем задача
"Wait server will come up online" то есть ожидание серверов онлайн. Ожидаем хост из переменной {{ inventory_hostname }} который берется
из нашего хоста описанного в servers2. Задержка ожидания 5 секунд и максимальное время ожидания 50 секунд. Так как мы ожидаем перезагрузки сервера на локальной машине, сервере-мастере, то задачу делегируем ему же (самому себе) delegate_to: 127.0.0.1. Вход на сервер мы не используем, "become" у нас закомментирован.
Перехват и контроль ошибок.
---
- name: Playbook "Error in playbook"
hosts: servers2
become: yes
tasks:
- name: Task Number 1
yum: name=treeeee state=latest
ignore_errors: yes
- name: Task Number 2
shell: echo HelloWorld >> /root/shell.txt
- name: Task Number 3
shell: echo Privet! >> /root/shell.txt
В данном плейбуке мы пытаемся установить пакет "treeeee" которого не существует. Этот таск нам выдаст ошибку. Для игнорирования данной
ошибки и продолжения выполнения плейбука пишем директиву ignore_errors: yes.
---
- name: Playbook "Error in playbook"
hosts: servers2
become: yes
tasks:
- name: Task Number 1
yum: name=treeeee state=latest
ignore_errors: yes
- name: Task Number 2
shell: echo Hello World!
register: results
- debug:
var: results
- name: Task Number 3
shell: echo Privet!
В данном плейбуке мы регистрируем выполнение таска "Task Number 2" и регистрируем результат в переменную results (register: results).
Затем выводим результат на экран с помощью директивы debug.
---
- name: Playbook "Error in playbook"
hosts: servers2
# any_errors_fatal: true
become: yes
tasks:
- name: Task Number 1
yum: name=treeeee state=latest
ignore_errors: yes
- name: Task Number 2
shell: echo Hello World!
register: results
failed_when: "'World' in results.stdout"
# failed_when: results.rc==0
- debug:
var: results
- name: Task Number 3
shell: echo Privet!
Мы можем выполнять или не выполнять таск в зависимости от результатов выполнения таска. К примеру в таске "Task Number 2"
мы выводим на екран надпись "Hello World!". Далее мы регистрируем результат в переменную results. И директивой failed_when
мы останавливаем выполнение плейбука при условии что в нашей переменной в выводе есть слово 'World' ("'World' in results.stdout").
Как вариант можно записать по result code. То есть если results.rc==0, то есть команда выполнена успешно, мы делаем failed.
Директива "any_errors_fatal: true" говорит нам о том, что любая ошибка в любом таске приводит к прекращению выполнения плейбука.
Ansible vault. Хранение секретов.
Создаем запароленный файл:
> ansible-vault create mysecret.txt
После выполнения команды откроется наш файл в текстовом редакторе. Мы его можем править как обычный текстовый файл. Если мы попытаемся посмотреть содержимое командой cat, отобразятся цифровые символы. Для просмотра, редактирования нужно воспользоваться командами
> ansible-vault view mysecret.txt
> ansible-vault edit mysecret.txt
Смена пароля:
> ansible-vault rekey mysecret.txt
Таким же образом мы можем криптовать наши плейбуки. Например нам нужно создать конфиг-файл который содержит пароли. И нам нежелательно хранить его в открытом виде.
---
- name: Ansible Vault
hosts: servers1
become: yes
vars:
admin_pass: passwd
tasks:
- name: Create config file
copy:
dest: "/root/myconfig.conf"
content: |
password= {{ admin_pass }}
В этом плейбуке мы создаем конфиг файл на удаленноом сервере. Закриптуем наш плейбук:
> ansible-vault encrypt playbook9_vault.yml
Расшифровать файл:
> ansible-vault decrypt playbook9_vault.yml
Для запуска зашифрованного плейбука нужно запустить плейбук с ключом:
> ansible-playbook playbook9_vault.yml --ask-vault-pass
Ansible спросит пароль и его нужно будет ввести с клавиатуры. Также можно записать пароль в файл mypass.txt (только пароль) и запустить плейбук с ключом:
> ansible-playbook playbook9_vault.yml --vault-password-file mypass.txt
Можно зашифровать отдельное слово или пароль в плейбуке командой ansible-vault encrypt_string
> ansible-vault encrypt_string
Эта директива попросит пароль (пароль на процедуру шифрования) и выдаст на экран нашу строку в шифрованном виде:
!vault |
$ANSIBLE_VAULT;1.1;AES256
62353636343164636533643330313131303131613135643039626364346537353639656637326363
3135306234616636353430363362323833643433313863660a336361336666373661666238653866
33633230643530303339643761653366336236646666366462653035353134396563396639626163
3365373166613239360a366235383036306238386435646637353731373265666335333834373334
3731
Копируем эти строки и вставляем в наш плейбук вместо какого-то текста (пароля).
Получился плейбук вида:
---
- name: Ansible Vault
hosts: servers1
become: yes
vars:
admin_pass: !vault |
$ANSIBLE_VAULT;1.1;AES256
62353636343164636533643330313131303131613135643039626364346537353639656637326363
3135306234616636353430363362323833643433313863660a336361336666373661666238653866
33633230643530303339643761653366336236646666366462653035353134396563396639626163
3365373166613239360a366235383036306238386435646637353731373265666335333834373334
3731
tasks:
- name: Create config file
copy:
dest: "/root/myconfig.conf"
content: |
password= {{ admin_pass }}
Далее запускаем наш плейбук с паролем (который был запрошен при криптовании нашего слова)
> ansible-playbook playbook9_vault.yml --ask-vault-pass
[последнее обновление 30.08.2018]
Чтобы войти на удаленный сервер с правами обычноого пользователя, применяем в плейбуке директиву become_user: yes. Следовательно в нашей папке group_vars пишем кредентиалы нашего пользователя.
Список модулей для Ansible:
http://docs.ansible.com/ansible/latest/modules/modules_by_category.html
>ansible-doc -l
|