Недавно выпала задача по развертке копий основного Zabbix-server на несколько машин, дабы хранить конфиги на разных серверах, да и еще всунуть это в CI/CD GitLab. Взял Ansible и начал писать плейбук.
Работа с установками на хосты Zabbix'а через Ansible
Сначала написал файл хостов с переменными, чтобы разделять установщики на разных семействах Linux(Debian, Ubuntu, RedHat):
[remote_zabbix:children] os_family_Debian os_family_RedHat os_family_Ubuntu py_script ## current_host - переменная, значение = самому хосту(да, хост два раза прописать) [py_script] #Скрипт, на котором будет запускаться скрипт для экспорта/импорта гита# current_host= [os_family_Debian] #Хост на ОС Debian# current_host= [os_family_RedHat] #Хост на ОС ReHat# current_host= [os_family_Ubuntu] #Хост на ОС Ubuntu# current_host= [os_family_Debian:vars] os_family_type=debian [os_family_RedHat:vars] os_family_type=redhat [os_family_Ubuntu:vars] os_family_type=ubuntu
Написал обычный пинг серверов:
- name: Test Connection hosts: remote_zabbix become: yes tasks: - name: Ping server ping: tags: - test_connection
После того, как я удостоверился, что хосты доступны, создал типичный шаблон для развертывания Zabbix (web, server и БД timescaledb на PostgreSQL) и вызвал его с ролью(run_docker-compose), где стопятся контейнеры со старым Zabbix'ом и БД, если они были подняты, и поднимаются заново в docker-compose:
- name: rm zabbix-containers ansible.builtin.shell: docker ps --filter name=zabbix* -aq | xargs docker stop | xargs docker rm ignore_errors: True - name: rm timescale container ansible.builtin.shell: docker ps --filter name=timescale* -aq | xargs docker stop | xargs docker rm ignore_errors: True - name: run zabbix-server ansible.builtin.shell: cd /home && docker-compose up -d
*Дабы не занимать много вашего времени и не делать длиннопост, прикреплю ссылку на репозиторий с файлами.
Далее я накатываю Zabbix-agent2, скачивая его пакеты с репозиториев, роль - install_agent2.service:
- name: Download agent2.service debian get_url: url: https://repo.zabbix.com/zabbix/6.2/debian/pool/main/z/zabbix-release/zabbix-release_6.2-4+debian10_all.deb dest: ./zabbix-release_6.2-4+debian10_all.deb when: os_family_type == "debian" - name: Download agent2.service debian shell: | dpkg -i ./zabbix-release_6.2-4+debian10_all.deb apt update when: os_family_type == "debian" - name: Install agent2.service debian apt: name: zabbix-agent2 update_cache: yes when: os_family_type == "debian" - name: Download agent2 redhat yum: name: https://repo.zabbix.com/zabbix/6.2/rhel/7/x86_64/zabbix-release-6.2-3.el7.noarch.rpm when: os_family_type == "redhat" - name: Yum clean all shell: yum clean all when: os_family_type == "redhat" - name: Install agent2.service redhat yum: name: zabbix-agent2 when: os_family_type == "redhat"
Накидываю уже шаблон для конфига агента вместо дефолтного файла конфигурации (тег configure_zabbix-agent2) и перезапустил службу этого агента ролью restart_agent2.service.
Скрипт для импорта/экспорта конфигурации
Далее наступила интересная часть, где я думал, как все-таки можно сделать импорт/экспорт конфигурации самого Zabbix, когда уже все поднято. Поискав в интернете, я уже почти расстроился, что придется делать либо запросами, либо писать самодельный API. Но, к счастью, я отыскал библиотеку для Python pyzabbix, где можно спокойно сделать экспорт конфига (как потом, спустя время, оказалось, что можно и импорт).
На вход два аргумента: host_from(откуда берем конфиг) и host_to(куда импортируем, подставляется в роли Ansible).
Написал функции для аутентификации (логин-пароль или токен):
# Auth Zabbix API to export def zabbix_auth_from(host_from): zapi = ZabbixAPI("http://" + host_from) zapi.login("Admin", "zabbix") print("Connected to Zabbix API Version %s" % zapi.api_version()) return zapi # Auth Zabbix API to import def zabbix_auth_to(url_link): zapi = ZabbixAPI('http://' + url_link + ':8080') zapi.login("Admin", "zabbix") print("Connected to Zabbix API Version %s" % zapi.api_version()) return zapi
При успешном выполнении скрипта нам выведется в консоль отчет об успешном входе и выведет версию Zabbix:
Connected to Zabbix API Version %version%
Все хорошо, далее пишем функции для экспорта хостов, групп хостов и шаблонов:
# Func for make export files def write_export(name, config, export_format): if config is not None: print("Writing %s.%s" % (name, export_format)) with open("%s.%s" % (name, export_format), "w") as f: f.write(config) # Func for export hosts from main zabbix def export_hosts(zapi, namefile = "export_zabbix_hosts"): hosts_ids = [] for item in zapi.host.get(output="extend"): hosts_ids.append(int(item['hostid'])) config = zapi.configuration.export( format="yaml", prettyprint=True, options={"hosts": hosts_ids} ) write_export(namefile, config, "yml") # Func for export templates from main zabbix def export_templates(zapi, namefile = "export_zabbix_templates"): hosts_ids = [] for item in zapi.template.get(output="extend"): hosts_ids.append(int(item['templateid'])) config = zapi.configuration.export( format="yaml", prettyprint=True, options={"templates": hosts_ids} ) write_export(namefile, config, "yml") # Func for export host groups from main zabbix def export_host_groups(zapi, namefile = "export_zabbix_hostgroups"): hosts_groups_ids = [] for item in zapi.hostgroup.get(output="extend"): hosts_groups_ids.append(int(item["groupid"])) config = zapi.configuration.export( format="yaml", prettyprint=True, options={"groups": hosts_groups_ids} ) write_export(namefile, config, "yml")
Далее мы просто вызываем поочередно функции экспорта, куда передаем объект с аутентифицированным Zabbix по API:
def export_zabbix_conf(zapi): export_templates(zapi) export_hosts(zapi) export_host_groups(zapi)
Все, что осталось - это импортировать созданные файлы экспорта в другие хосты Zabbix(здесь zapi - уже хост, на который копируем):
# Func for import config to copy of zabbix def import_config(zapi, filename, rules): print("Import " + filename.split('_')[-1].split('.')[0]) export_file = open(filename, "r").read() zapi.configuration['import'](format="yaml", source=export_file, rules=rules) def import_zabbix_conf(zapi): import_config(zapi, "export_zabbix_templates.yml", { "templates": { "createMissing": True, "updateExisting": True }}) import_config(zapi, "export_zabbix_hostgroups.yml", { "host_groups": {"createMissing": True }}) import_config(zapi, "export_zabbix_hosts.yml", {"hosts": { "createMissing": True, "updateExisting": True }})
Все хорошо, скопировался конфиг, но остается последняя проблема - агент не видит свой же хост и нам нужно в настройках заббикса переименовать наш хост с "Zabbix Server" на наш DNS, IP (я переключил на чтение DNS, мне так удобнее было):
# Func for rename localhost on zabbix-server, for find it by zabbix-agent2 def rename_local_host(zapi, remote_host): try: hid=zapi.host.get(search={'host': remote_host})[0]['hostid'] except: hid=zapi.host.get(search={'host': 'Zabbix server'})[0]['hostid'] print('Change name') zapi.host.update(hostid=hid, host=remote_host, name=remote_host) print('Change DNS name') iid = zapi.hostinterface.get(hostids=hid)[0]['interfaceid'] zapi.hostinterface.update(interfaceid=iid, dns=remote_host, useip=0, ip='сюда вписать наш IP')
Внедрение в CI/CD GitLab
Ну здесь уже просто. Мы при создании заданий в плейбуке прописываем теги, которые уже вызываем в .gitlab-ci.yml:
image: ### Ansible 6.5.1 image stages: - install/update_zabbix - configure_zabbix-agent2 - run_script Install/update_Zabbix: stage: install/update_zabbix script: - ansible-playbook ansible/zabbix_playbook.yml -i ansible/hosts/hosts.txt -vvvv -t install/update_zabbix when: manual Configure_Zabbix-Agent2: stage: configure_zabbix-agent2 script: - ansible-playbook ansible/zabbix_playbook.yml -i ansible/hosts/hosts.txt -vvvv -t configure_zabbix-agent2 when: manual Export/import_zabbix_config: stage: run_script script: - ansible-playbook ansible/zabbix_playbook.yml -i ansible/hosts/hosts.txt -vvvv -t run_sript -t restart-agent2 when: manual
Итоги
Мы смогли через Ansible развернуть много копий Zabbix-server и Zabbix-agent2 со своими настройками, а также скопировать конфиг с основного Zabbix на другие хосты с помощью одной библиотеки pyzabbix.
