====== Сервис Ansible ====== * [[https://www.goncharov.xyz/it/make-cm-not-bash-ru.html|Вот рассмотрим банальный пример. выбрать все файлы в текущей директории и скопировать в другое место]] * Управление инфраструктурой на примере [[https://ru.wikipedia.org/wiki/Ansible|Аnsible - wikipedia]] * [[https://habrahabr.ru/company/express42/blog/254959/|Ansible — давайте попробуем]] * [[https://habrahabr.ru/post/305400/|Пособие по Ansible]] * [[https://habr.com/ru/post/508762/|Основы Ansible, без которых ваши плейбуки — комок слипшихся макарон]] * [[https://www.cisco.com/c/dam/m/ru_ru/training-events/2019/cisco-connect/pdf/introduction_automation_with_ansible_idrey.pdf|Введение в автоматизацию с помощью Ansible (Cisco)]] * [[https://nwmichl.net/2020/02/24/first-simple-ansible-playbooks/|First simple Ansible playbooks Cisco IOS]] ===== Установка на управляющей системе ===== ==== Из репозитория дистрибутива ==== node1# apt install ansible node1# ansible --version ==== Из репозитория производителя ==== * [[https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-debian|Installing Ansible on Debian]] ===== Установка на управляемых системах ==== nodeN# apt install python python-apt debian11/ubuntu20# apt install python python3-apt debian12# apt install python3 python3-apt ===== Настройка групп управляемых систем ===== * [[https://nklya.medium.com/%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5-%D0%B8%D0%BD%D0%B2%D0%B5%D0%BD%D1%82%D0%BE%D1%80%D0%B8-%D0%B2-ansible-9ee880d540d6|Динамическое инвентори в Ansible]] 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#Парольная аутентификация]] в ssh # cat /etc/ansible/ansible.cfg [defaults] #... host_key_checking = False #... * [[Сервис SSH#Аутентификация с использованием ключей ssh]] (!!! не забыть подключение ansible узла к себе !!!) 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 ===== * [[Формат YAML]] * [[https://stackoverflow.com/questions/17188147/how-to-run-ansible-without-specifying-the-inventory-but-the-host-directly|How to run Ansible without specifying the inventory but the host directly?]] * [[https://stackoverflow.com/questions/33222641/override-hosts-variable-of-ansible-playbook-from-the-command-line|Override hosts variable of Ansible playbook from the command line]] ==== Пример 1 ==== * [[Технология Docker]] 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 * Технология Vagrant: [[Технология Vagrant#Provision с использованием ansible]] 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 ==== * [[Управление учетными записями в Linux#Назначение пароля]] * [[https://docs.ansible.com/ansible/2.5/modules/user_module.html|Ansible module user - Manage user accounts]] node1# 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 ==== * [[https://docs.ansible.com/ansible/2.3/ios_config_module.html|ios_config - Manage Cisco IOS configuration sections]] 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 ===== Использование шаблонов ===== * [[https://ru.wikipedia.org/wiki/%D0%98%D0%B4%D0%B5%D0%BC%D0%BF%D0%BE%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D1%81%D1%82%D1%8C|Идемпотентность]] * [[https://cryptic-cliffs-32040.herokuapp.com/|How can I test jinja2 templates in ansible?]] node1# ansible -m setup corpX node1# ansible -m setup corpX | grep ansible_fqdn node1# cat index.html.j2

Hello from {{ ansible_fqdn }}

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 ==== * [[Сервис HTTP#Использование домашних каталогов]] 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 ===== Использование ролей ===== * [[https://rtfm.co.ua/ansible-roli-roles-primer/|Ansible: роли (roles) – пример]] * [[https://andreyex.ru/linux/ansible-roli-v-ansible/|Ansible. Роли в Ansible]] * [[Настройка стендов слушателей#Ansible конфигурация]] ==== Роль настроенного через 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# * В текущем каталоге выполняем и сохраняем файлы из тем [[Пакет OpenSSL#Создание параметра DH]] и [[Пакет OpenSSL#Создание самоподписанного сертификата]] (не указываем AltName, Common Name: server - достаточно) 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" * [[Сервисы Gateway и routing#Управление таблицей маршрутизации]] ==== Фрагмент роли с условиями и отладкой ==== # 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 ==== * [[https://medium.com/splunkuserdeveloperadministrator/using-ansible-pull-in-ansible-projects-ac04466643e8|Using Ansible Pull In Ansible Projects]] * [[Инсталяция системы в конфигурации Desktop]] * [[Переменные окружения]] === Вариант 1 === * [[https://habr.com/ru/articles/732736/|GPO для Linux из подручных материалов]] (help.desktop) 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 * [[Инструмент GitLab]] (Создать публичный проект без readme и скопировать подсказки) 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 * Инструмент GitLab [[Инструмент GitLab#Подключение через API]] === Вариант 2 === * [[Средства программирования shell#Использование диалоговых окон]] $ 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 * [[Планирование выполнения заданий в Linux#Сервис cron]] ====== Дополнительные материалы ====== ===== Вместо 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