Сервис Ansible
Установка на управляющей системе
Из репозитория дистрибутива
node1# apt install ansible
node1# ansible --version
Из репозитория производителя
Установка на управляемых системах
nodeN# apt install python python-apt
debian11/ubuntu20# apt install python python3-apt
debian12# apt install python3 python3-apt
Настройка групп управляемых систем
deb11_12_ub24# mkdir /etc/ansible/
node1# cat /etc/ansible/hosts
[all:vars]
#ansible_python_interpreter="/usr/bin/python3"
#[corp]
#server.corp13.un
#server.corp[1:12].un ansible_ssh_user=root ansible_ssh_pass=strongpassword
#mail.corp[1:12].un ansible_ssh_user=root ansible_ssh_pass=strongpassword
#192.168.[1:25].10
[corpX]
node[1:2]
[addnodes]
192.168.X.[3:9] ansible_ssh_user=root ansible_ssh_pass=strongpassword
[sws]
switch[1:3] ansible_ssh_user=root ansible_ssh_pass=cisco #ansible_network_os=ios
[nodes]
##192.168.X.[210:230:10]
##192.168.X.[201:203]
node[1:3]
[nodes:vars]
ansible_ssh_user=vagrant
ansible_ssh_pass=strongpassword
#ansible_sudo_pass=strongpassword
ansible_become=yes
Настройка транспорта ssh
Для автоматизации подключения к новым системам может потребоваться отключение проверки их публичного ключа и
Парольная аутентификация в ssh
# cat /etc/ansible/ansible.cfg
[defaults]
#...
host_key_checking = False
#...
node1# ssh-keygen
node1# ssh-copy-id node1
node1# ssh-copy-id node2
Использование модулей
$ ansible-doc -l
$ ansible-doc ping
node1# ansible corpX -m ping
node1# ansible localhost -m ping
node1# ansible all -m ping
node1# ansible all -m ping -i inv_file.ini
node1# ansible all -m ping -i node2:2222, -e "ansible_python_interpreter=/usr/bin/python3"
node1# ansible corpX -m command -a 'uname -a'
kube1# ansible kubes -a 'sed -i"" -e "/swap/s/^/#/" /etc/fstab'
kube1# ansible kubes -a 'swapoff -a'
ИЛИ
(venv1) server# ansible all -a 'sed -i"" -e "/swap/s/^/#/" /etc/fstab' -i /root/kubespray/inventory/mycluster/hosts.yaml #--limit=kube4,kube5
(venv1) server# ansible all -a 'swapoff -a' -i /root/kubespray/inventory/mycluster/hosts.yaml #--limit=kube4
node1# ansible corpX -f 2 -m apt -a 'pkg=apache2 state=present update_cache=true'
node1# ansible addnodes -vv -f 5 -m apt -a 'pkg=ceph,tgt-rbd state=present update_cache=true' #-e 'https_proxy=http://radio.specialist.ru:3128/' -e 'http_proxy=http://radio.specialist.ru:3128/'
server# ansible nodes -f 3 -m apt -a 'pkg=openvpn state=present update_cache=true'
server# ansible nodes -f 3 -m apt -a 'pkg=docker.io state=present update_cache=true'
ubu20_24_deb12# apt install python3-paramiko
server# ansible sws -m ios_command -a "commands='show cdp nei'" -c local
ubuntu24# ansible sws -m ios_command -a "commands='show cdp nei'" -c network_cli #-e "ansible_network_os=ios"
Использование playbook
Пример 1
server# cat provision_docker.yml
или
λ touch provision_docker.yml
или
student@node1:~$ cat /vagrant/provision_docker.yml
- hosts: "{{ variable_host | default('all') }}"
become: yes
user: vagrant
tasks:
- name: Install Docker's prequirement
apt:
pkg:
- apt-transport-https
- ca-certificates
- curl
- gnupg2
- software-properties-common
state: present
update_cache: true
- name: Add Docker's official GPG key
apt_key:
# url: https://download.docker.com/linux/debian/gpg
# url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker's repository into sources list
apt_repository:
# repo: deb [arch=amd64] https://download.docker.com/linux/debian bookworm stable
# repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu noble stable
state: present
- name: Install Docker
apt:
pkg:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
- docker-buildx-plugin
state: present
update_cache: true
server# ansible-playbook provision_docker.yml --syntax-check
server# ansible-playbook provision_docker.yml
server# ansible-playbook provision_docker.yml --extra-vars "variable_host=nodes"
server# ansible-playbook provision_docker.yml --extra-vars "variable_host=localhost"
server# ansible-playbook provision_docker.yml -i inv_file.ini
server# ansible-playbook provision_docker.yml -e "ansible_python_interpreter=/usr/bin/python3" -i 192.168.X.1:2222,
Пример 2
-
Ansible module user - Manage user accountsnode1# cat addusers.yml
- hosts: corpX
# - hosts: all
name: Add Users
tasks:
- name: Add user1
user:
name: user1
uid: 10001
shell: /bin/bash
comment: "Ivan Ivanovitch Ivanov,RA1,401,499-239-45-23"
password: $6$3Gz1ZuH3yHckA$wQNZbfU/9G6bYx08owpn7CoFP//2WbB4cmDDOgwDYBbwEyHxB0QQyCuMrOiPOLv3JF5RFtIv/r/kxoPPYFCsx1
- name: Add user2
ansible.builtin.user:
name: user2
uid: 10002
shell: /bin/bash
comment: "Petr Petrovitch Petrov,RA7,402,499-323-55-53"
password: $6$x/AU/p9Dgi/ZiNF$6Xb8J4fsGuTi5IR0LaZe5pSgRX8vp54sfQGWJZZwKX.KFVpUL9m2PJNDh/d/l0rocueIvVjdQTzEAYPMmTm991
node1# ansible-playbook addusers.yml --syntax-check
node1# apt install ansible-lint
node1# ansible-lint addusers.yml
node1# ansible-playbook addusers.yml
Пример 3
server# cat cisco_change_conf.yml
- hosts: sws
# connection: local
connection: network_cli
gather_facts: no
tasks:
- name: configure top level configuration
ios_config:
lines:
- snmp-server community write RW
# - ip host server 192.168.X.10
# - snmp-server host server writetrap
# - snmp-server enable traps config
# - snmp-server enable traps config-copy
# - snmp-server enable traps snmp linkdown linkup
# - ip scp server enable
# - logging facility local0
## - logging host server
# - logging host server transport udp port 8514
# - ntp server server
# - clock timezone MSK 3
# - service timestamps log datetime localtime year
# - aaa new-model
# - aaa authentication login CONSOLE none
# - aaa authorization exec CONSOLE none
# - enable secret cisco
# - aaa authorization console
# - aaa authentication login default local
# - aaa authorization exec default local
# - username root privilege 15 secret cisco
# - radius-server host server auth-port 1812 acct-port 1813
# - radius-server key testing123
# - aaa authentication login default group radius enable
# - aaa authorization exec default group radius none
# - tacacs-server host server
# - tacacs-server key tackey123
# - aaa authentication login default group tacacs+ enable
# - aaa authorization exec default group tacacs+ none
# - aaa accounting commands 15 default start-stop group tacacs+
# - aaa authentication dot1x default group radius
## - aaa accounting dot1x default start-stop group radius
# - name: configure line con 0
# ios_config:
# lines:
# - login authentication CONSOLE
# - authorization exec CONSOLE
# - privilege level 15
# parents: line con 0
server# ansible-playbook cisco_change_conf.yml
server# ansible-playbook cisco_change_conf.yml --limit @/root/cisco_change_conf.retry
Использование шаблонов
node1# ansible -m setup corpX
node1# ansible -m setup corpX | grep ansible_fqdn
node1# cat index.html.j2
<html>
<body>
<h1>
Hello from {{ ansible_fqdn }}
</h1>
</body>
</html>
node1# cat inst_apache.yml
- hosts: corpX
tasks:
- name: Installs apache web server
apt: pkg=apache2 state=present update_cache=true
- name: Create index.html file
template: src=index.html.j2 dest=/var/www/html/index.html
node1# ansible-playbook inst_apache.yml
# cat iax.conf.j2
[general]
disallow=all
allow=alaw
{% for Y in YS %}
[corp{{Y}}]
type=user
host=dynamic
secret=apassword{{Y}}
auth=md5
[corp{{Y}}]
type=peer
host=server.corp{{Y}}.un
username=corp{{X}}
secret=apassword{{X}}
auth=md5
{% endfor %}
# cat ast_iax_corps.yml
- hosts: corp
tasks:
- name: Create iax.conf file
template: src=iax.conf.j2 dest=/etc/asterisk/iax.conf
- name: Reload asterisk confs
service: name=asterisk state=reloaded
# ansible-playbook ast_iax_corps.yml --extra-vars '{"X":"{{ ansible_eth0.ipv4.address.split(\".\")[3] }}","YS":[1,2,3,4,5,6,7,8,9,10,11,12,13]}'
Использование handlers
Пример 1
node1# cat conf_apache.yml
- hosts: corpX
tasks:
- name: Add userdir to apache
apache2_module:
state: present
# state: absent
name: userdir
notify:
- restart apache
handlers:
- name: restart apache
service: name=apache2 state=restarted
node1# ansible-playbook conf_apache.yml
Пример 2
server# cat za.conf
ListenIP=0.0.0.0
StartAgents=0
ServerActive=server
UserParameter=listinstalledsoft,ls /usr/share/applications | awk -F '.desktop' ' { print $1}' -
node1# cat za.yml
- hosts: lin_ws
tasks:
- name: Install zabbix agent
apt: pkg=zabbix-agent state=present update_cache=true
- name: Create conf file
copy: src=za.conf dest=/etc/zabbix/zabbix_agentd.conf.d/za.conf
notify:
- restart za
handlers:
- name: restart za
service: name=zabbix-agent state=restarted
server# ansible-playbook za.yml
Использование ролей
Роль настроенного через ifupdown узла сети
# ###cd /root/conf/
# ###git pull origin master
# ###cd /root/conf/ansible/roles/
# cat nodes.yml
- name: Network config for nodes
hosts: addnodes
# hosts: kubes
# hosts: all
roles:
- node
# ansible-galaxy init node # не обязательно
# cat node/vars/main.yml
name_prefix: node
#name_prefix: kube
X: "{{ ansible_eth0.ipv4.address.split('.')[2] }}"
N: "{{ ansible_eth0.ipv4.address.split('.')[3][-1] }}"
# cat node/tasks/main.yml
- name: Create hosts file
template: src=hosts.j2 dest=/etc/hosts
- name: Create resolv.conf file
template: src=resolv.conf.j2 dest=/etc/resolv.conf
- name: Create hostname file
template: src=hostname.j2 dest=/etc/hostname
notify:
- restart system
- name: Create interfaces file
template: src=interfaces.j2 dest=/etc/network/interfaces
notify:
- restart system
- name: Set timezone to Europe/Moscow
timezone:
name: Europe/Moscow
# cat node/handlers/main.yml
- name: restart system
reboot:
debian11# mkdir node/templates
# cat node/templates/hostname.j2
{{ name_prefix }}{{ N }}.corp{{ X }}.un
# cat node/templates/hosts.j2
127.0.0.1 localhost
{{ ansible_eth0.ipv4.address }} {{ name_prefix }}{{ N }}.corp{{ X }}.un {{ name_prefix }}{{ N }}
# cat node/templates/resolv.conf.j2
search corp{{ X }}.un
nameserver 192.168.{{ X }}.1
nameserver 192.168.{{ X }}.2
#nameserver 192.168.{{ X }}.10
# cat node/templates/interfaces.j2
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address {{ ansible_eth0.ipv4.address }}
netmask 255.255.255.0
gateway 192.168.{{ X }}.254
# gateway 192.168.{{ X }}.1
# ansible-playbook -f 5 nodes.yml
ИЛИ
# ansible-playbook -f 5 /root/conf/ansible/roles/nodes.yml
ИЛИ
(venv1) server# ansible-playbook -f 5 /root/conf/ansible/roles/nodes.yml -i /root/kubespray/inventory/mycluster/hosts.yaml #--limit=kube4
Роль OpenVPN сервера
server:~# mkdir openvpn1 && cd openvpn1
server:~/openvpn1# ansible-galaxy init openvpn1
server:~/openvpn1# cd openvpn1/files/
server:~/openvpn1/openvpn1/files#
server:~/openvpn1/openvpn1/files# ls
dh2048.pem server.crt server.key
server:~/openvpn1/openvpn1/files# cd ../../
server:~/openvpn1# cat openvpn1/templates/openvpn1.conf.j2
dev tun
keepalive 10 120
server {{node_nets[ansible_hostname]}} 255.255.255.0
push "route 192.168.{{X}}.0 255.255.255.0"
#push "dhcp-option DNS 192.168.{{X}}.10"
#push "block-outside-dns"
#push "dhcp-option DOMAIN corp{{X}}.un"
dh /etc/openvpn/dh2048.pem
key /etc/ssl/private/server.key
ca /etc/ssl/certs/server.crt
cert /etc/ssl/certs/server.crt
verify-client-cert none
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so login
username-as-common-name
#duplicate-cn
status /var/log/openvpn1-status.log
management 0.0.0.0 7505
server:~/openvpn1# cat openvpn1/tasks/main.yml
- name: Install OpenVPN
apt: pkg=openvpn state=present update_cache=true
# when: node_nets[ansible_hostname] is defined
- name: Create openvpn1.conf file
template: src=openvpn1.conf.j2 dest=/etc/openvpn/openvpn1.conf
notify:
- restart openvpn1
- name: Copy file server.key
copy:
src: server.key
dest: /etc/ssl/private/server.key
mode: '0600'
notify:
- restart openvpn1
- name: Copy many files
copy:
src: '{{item.0}}'
dest: '{{item.1}}'
loop:
- [ 'dh2048.pem', '/etc/openvpn/dh2048.pem' ]
- [ 'server.crt', '/etc/ssl/certs/server.crt' ]
notify:
- restart openvpn1
- name: Enable service OpenVPN
service:
name: openvpn@openvpn1
enabled: yes
# state: started
server:~/openvpn1# cat openvpn1/handlers/main.yml
- name: restart openvpn1
service:
name: openvpn@openvpn1
state: restarted
server:~/openvpn1# cat inventory.yaml
all:
vars:
X: "{{ ansible_eth1.ipv4.address.split('.')[2] }}"
ansible_python_interpreter: "/usr/bin/python3"
ansible_ssh_user: vagrant
ansible_ssh_pass: strongpassword
ansible_become: yes
node_nets:
node1: 192.168.110.0
node2: 192.168.120.0
node3: 192.168.130.0
prod_nodes:
hosts:
node1:
node2:
test_nodes:
hosts:
node3:
server:~/openvpn1# cat openvpn1.yaml
- name: Run openvpn1 on nodes
hosts: "{{ variable_host | default('prod_nodes') }}"
roles:
- role: openvpn1
when: node_nets[ansible_hostname] is defined
server:~# wget https://val.bmstu.ru/unix/conf.git/conf/ansible/roles/openvpn1.tgz && tar -xvzf openvpn1.tgz && cd openvpn1
server:~/openvpn1# ansible-playbook openvpn1.yaml -i inventory.yaml -e "variable_host=test_nodes"
server:~/openvpn1# ###ansible-playbook openvpn1.yaml -i inventory.yaml # лучше через GitLab CI/CD
server:~/openvpn1# ###ansible-playbook openvpn1.yaml -i inventory.yaml -e "variable_host=all"
Фрагмент роли с условиями и отладкой
# cat conf/ansible/roles/host/vars/main.yml
...
octet4: "{{ ansible_eth0.ipv4.address.split('.')[3] }}"
...
# cat conf/ansible/roles/host/tasks/main.yml
- name: set vars for server
set_fact:
hostname: "mail"
X: "{{ octet4 | int - 100 }}"
when: octet4 | int >= 100
- name: set vars for mail
set_fact:
hostname: "server"
X: "{{ octet4 }}"
when: octet4 | int < 100
- name: echo variables
debug:
msg: octet4 is {{ octet4 }}, X is {{ X }}, hostname is {{hostname}}
#- meta: end_play
...
ansible-pull
Вариант 1
client1:~/ansible-pull-gpo# cat thunderbird/tasks/main.yml
- name: Install Thunderbird
apt: pkg=thunderbird state=present update_cache=true
client1:~/ansible-pull-gpo# cat proxy/files/etc/environment
#http_proxy=http://gate.corpX.un:3128
https_proxy=http://gate.corpX.un:3128
no_proxy=localhost,127.0.0.1,isp.un,corpX.un
client1:~/ansible-pull-gpo# cat proxy/tasks/main.yml
- name: Copy file environment
copy:
src: etc/environment
dest: /etc/environment
client1:~/ansible-pull-gpo# cat local.yml
- hosts: localhost
roles:
- role: proxy
- role: thunderbird
client1:~/ansible-pull-gpo# ansible-playbook local.yml
client3:~# ###ansible-pull -U http://gate.corpX.un/user1/ansible-pull-gpo.git
client1:~/ansible-pull-gpo# cat start.sh
#!/bin/bash
apt update
apt install -y git ansible
echo -e "0 */2 * * * \
/usr/bin/ansible-pull -s 120 -U http://gate.corpX.un/user1/ansible-pull-gpo.git -C $BR 2>&1 | /usr/bin/logger -t ansible-pull\n\
@reboot sleep 1m; /usr/bin/ansible-pull -U http://gate.corpX.un/user1/ansible-pull-gpo.git -C $BR 2>&1 | /usr/bin/logger -t ansible-pull" | crontab -
init 6
Вариант 2
$ cat ansible-pull-gpo\local.yml
- hosts: localhost
tasks:
- name: Set timezone to Europe/Moscow
timezone:
name: Europe/Moscow
- name: Russian Interface
shell: |
echo 'ru_RU.UTF-8 UTF-8' > /etc/locale.gen
locale-gen
echo LANG=ru_RU.UTF-8 > /etc/default/locale
when: CONF_RUS_INT is defined
- name: Install Firefox in Debian
apt: pkg=firefox-esr state=present update_cache=true
# debug: msg="Install Firefox in Debian"
when: ansible_distribution == 'Debian'
- name: Install Firefox in Ubuntu
apt: pkg=firefox state=present update_cache=true
# debug: msg="Install Firefox in Ubuntu"
when: ansible_distribution == 'Ubuntu'
- name: Install Thunderbird
apt: pkg=thunderbird state=present update_cache=true
when: PROG_THBIRD is defined
roles:
- role: zabbix_agent
when: ROLE_ZAB_AG is defined
- role: openvpn1_client
when: ROLE_OVPN1_CL is defined
client1:~# cat /usr/local/etc/gpo_options.yml
CONF_RUS_INT:
PROG_THBIRD:
ROLE_ZAB_AG:
client1:~# /usr/bin/ansible-pull -U http://server.corp13.un/student/ansible-pull-gpo.git -C test -e @/usr/local/etc/gpo_options.yml
Дополнительные материалы
Вместо ansible
for i in 1 2 3; do ssh node$i "apt update && apt install apache2; done
выполнение команд на цисках через ансибл
1. добавить в /etc/ansible/group_vars/all.yml строки
ansible_connection: network_cli
ansible_network_os: ios
2. создать файл playbook формата
- name: Run show commands on routers
hosts: cisco-routers
tasks:
- name: run show commands
ios_command:
commands: # перечисление команд
- show ip int br
- sh ip route
register: show_result