# Задания ## 0. Настройка кластера В этом задании мы сконфигурируем основу будущего кластера с беспарольным доступом по ssh c управляющей машины. Создайте виртуальную машину `studX-node` из шаблона `ubuntu-template` со свеже установленной ОС Ubuntu 18.04 в режиме `Linked Clone`. Укажите свой ресурсный пул при создании. Настройте сеть. В этой машине наиболее простым способом будет модификация конфигурационного файла `/etc/netplan/01-netcfg.yaml`. Используйте ip адрес из сети `192.168.1.0/24`, шлюз `192.168.1.1`, любой общедоступный DNS. В настройках оборудования виртуальной машины подключите сетевое устройство к `vmbr15+X`. Добавьте в `/etc/hosts` имена узлов в соответствии со схемой ниже на `studX` и `studX-node`. Сгенерируйте в управляющей машине `studX` ssh ключи без кодовой фразы (passphrase) для пользователя `stud` по алгоритму ed25519. Если нужно в машине `studX-node` отредактируйте конфигурацию `/etc/ssh/sshd_config`, разрешите доступ к машине по паролю и перезагрузите sshd `systemctl restart sshd`. Добавьте публичный ключ в `authorized_keys` машины `studX-node` с помощью `ssh-copy-id`. Превратите виртуальную машину в шаблон для узлов кластера. Создайте 3 машины `studX-node[1-3]` из шаблона `studX-node`. Настройте сеть, установив уникальные IP. Поменяйте имена хостов командой `hostnamectl set-hostname`. В итоге схема должна выглядеть следующим образом. ``` _______________ | | | studX | Управляющий узел с Ansible |_______________| | ens19 в vlan 15+X, IP 192.168.1.100/24 | +---------+---------+-------------------+ | 192.168.1.101/24 | 192.168.1.102/24 | 192.168.1.103/24 __________ __________ __________ | | | | | | | node 1 | | node 2 | | node 3 | Узлы под управлением Ansible |__________| |__________| |__________| ``` Проверьте, что доступ с `studX` по ключу работает. ``` stud@studX # ssh node1 stud@studX # ssh node2 stud@studX # ssh node3 ``` ## 1. Установка Ansible, введение в команды Далее все команды выполняются в консоли управляющего узла `studX`. Установите Ansible. ``` $ sudo apt install ansible $ ansible --version ansible [core 2.13.4] config file = None configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3/dist-packages/ansible ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections executable location = /usr/bin/ansible python version = 3.10.7 (main, Sep 8 2022, 14:34:29) [GCC 12.2.0] jinja version = 3.0.3 libyaml = True ``` Требованием для работы Ansible являются: возможность подключения к удалённому узлу по ssh и наличие установленного Python интерпретатора на каждом узле. Управление кластером с помощью Ansible может осуществляться в двух режимах ad-hoc интерактивном режиме и в режиме выполнения проекта конфигурации playbook. В первом случае все команды начинаются с вызова ansible. Документация команды `man ansible`. ### 1.1. Инвентарь Прежде чем выполнять команды, создадим кластер в терминах Ansible. В Ansible инструменте существует понятие инвентаря (Inventory), файла, который содержит список сгруппированных имён или ip адресов. Создайте файл `/etc/ansible/hosts`. Отредактируйте его так, чтобы он содержал только группу `cluster` и имена машин, входящих в кластер. В квадратных скобках указывается имя группы, ниже следуют имена машин. Вы можете использовать шаблоны для перечисления номеров (также используют квадратные скобки), которые раскрываются следующим образом: ``` [1:3] раскрывается в 1 2 3 abc[1:3] раскрывается в abc1 abc2 abc3 A[1:3]B раскрывается в A1B A2B A3B ``` Наш кластер `cluster` в `/etc/ansible/hosts` может выглядеть так ``` # cat /etc/ansible/hosts [cluster] node[1:3] ``` **Примечание.** Обратите внимание что в скобках используется двоеточие, а не знак тире. Таким образом кластер `cluster` в терминах Ansible - это группа имён машин `node1`, `node2`, `node3`. ### 1.2 Модули #### 1.2.1 ping Запустим нашу первую Ansible команду: ``` $ ansible cluster -m ping ``` В данной команде мы запустили модуль `ping` для группы узлов `cluster`. Формат ad-hoc команд: ``` $ ansible <группа или шаблон> -m <модуль> ``` Существуют и другие ключи, кроме `-m`, часть из которых будет описана далее. О них вы можете узнать в официальной документации, либо вызвав `ansible` без параметров. По умолчанию модуль выполняется параллельно на как можно большем количестве узлов. Это позволяет быстрее получить результат, но не гарантирует выполнение в том же порядке, что и порядок узлов в инвентаре. Попробуйте выполнить следующую команду: ``` $ ansible cluster -m ping -f 1 ``` Добавленный в конце ключ `-f` позволяет ограничить количество одновременно изменяемых узлов. Его также применяют для обновления компонентов распределённого приложения по частям, для избегания остановки всей системы. #### 1.2.2 shell Для ad-hoc режима естественнее всего подходит модуль `shell` (https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html). Данный модуль позволяет выполнить любую консольную команду на нескольких  узлах. Приведём ряд примеров, чтобы вы попробовали их далее на всём кластере: ```bash # узнать время на текущей машине, нам необходимо вызвать: date # узнать имена файлов в директории `~/.ssh/`: ls -la ~/.ssh/ # узнать информацию о процессорах: lscpu # узнать количество свободного места на дисках: df -h # узнать версию операционной системы (для CentOS, Red Hat, Fedora) и ядра линукс cat /etc/os-release lsb_release -a uname -a # проверить, что нужный пакет находится в списке установленных apt list installed python3 ``` Выполнение консольных команд на узлах кластера с помощью модуля `shell` выглядит следующим образом: ``` # ansible cluster -m shell -a "date" ``` Верное ли время на узлах? После ключа `-a` в `ansible` передаётся строка с командой. Попробуйте выполнить несколько вышеупомянутых команд аналогичным образом. #### 1.2.3 setup В задачах конфигурации кластера как правило требуется не только узнавать информацию о различных свойствах, конфигурациях систем, но и использовать данную информацию в качестве параметров в других командах. Для сбора информации о состоянии (Facts) узлов используется модуль `setup`. Выполните команду для одного узла и просмотрите результат. Среди этих данных есть результаты, полученные нами ранее. ``` $ ansible node1 -m setup ``` Результатом является иерархическая структура в JSON формате. https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html. Для обращения к значениям (листьям JSON структуры) указывается путь из названий, разделённых точками. Например:  `ansible_eth0.ip4.address` или `ansible_date_time.date`. #### 1.2.4 apt Для установки ПО нам потребуется модуль `apt`. Проверьте установлена ли python3. Например так: ``` $ ansible cluster -m shell -a "apt list installed python3" ``` Целью использования Ansible является перевод распределённой системы из одного состояния в другое. По этой причине в параметрах многих модулей можно встретить параметр `state`. Данных параметр для модуля apt допускает значения: `present` - присутствует, `absent` - отсутствует, `latest` - последняя версия. Кроме него нам потребуется параметр `name` - имя или шаблон имени по которому нужно искать устанавливаемое ПО. Другие параметры модуля yum доступны на официальном сайте https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html. Попробуем установить htop следующей командой: ``` $ ansible cluster -m apt -a "name=htop state=present" ``` #### 1.2.5 Эскалация прав доступа Для повышения прав доступа используется ключ `--become` или сокращенный вариант `-b`. ``` $ ansible cluster -m apt -a "name=htop state=present" -b ``` Подробнее об эскалации прав можно прочитать в https://docs.ansible.com/ansible/2.3/become.html. Таким образом мы переводим кластер из состояния без htop в состояние с htop. Можно убедиться, что при повторном запуске никаких изменений производиться не будет. ## 2. Ansible Playbook Большую часть времени в Ansible вы потратите на написание сценариев управления конфигурацией (Playbook). Playbook — это термин который Ansible использует для названия таких сценариев. В этом задании установим Greenplum на наш кластер. ### 2.1 Шаблон конфигурации В первую очередь создайте папку проекта управления конфигурацией `ansible-greenplum`, в которой будет лежать файл со сценарием. Назовите этот файл `main.yml`. Поместите в него следующие строки и попробуйте запустить с флагом `--verbose`. ```yaml --- - hosts: cluster tasks: - name: Current date shell: date ``` ``` $ ansible-playbook main.yml -v ``` ### 2.2 Создание пользователя-администратора распределённой базы данных Преступим у настройке конфигурации для Greenplum. Создайте файл 1.yml и поместите содержимое из листинга следующего ниже. Отличие от предыдущего примера заключается в добавлении блока с переменными `vars`. Все действия понадобится выполнять с правами `root`, поэтому мы добавляем параметр `become: yes`. Первая задача - создать пользователя `gpadmin` и установить ему пароль `changeme`. ```yaml --- - hosts: cluster vars: - version: "6.0.0" - greenplum_admin_user: "gpadmin" - greenplum_admin_password: "changeme" become: yes tasks: - name: create greenplum admin user user: name: "{{ greenplum_admin_user }}" password: "{{ greenplum_admin_password | password_hash('sha512', 'DvkPtCuQ9pU') }}" ``` ``` $ ansible-playbook 1.yml ``` ### 2.3 Настройка репозитория на целевых узлах Поместите содержимое файла в 2.yml и запустите. Конфигурация настроит Greenplum репозиторий для apt. ```yaml --- - hosts: cluster become: yes tasks: - name: install software-properties-common apt: name: software-properties-common state: present - name: install gnupg2 apt: name: gnupg2 state: present - name: install ppa:greenplum/db apt_repository: repo: ppa:greenplum/db state: present ``` ``` $ ansible-playbook 2.yml ``` ### 2.4 Установка пакета Установим пакет Greenplum конфигурацией `3.yml`. ```yaml --- - hosts: all vars: - version: "6.0.0" - greenplum_admin_user: "gpadmin" - greenplum_admin_password: "changeme" # - package_path: passed via the command line with: -e package_path= remote_user: root become: yes become_method: sudo connection: ssh gather_facts: yes tasks: - name: install package apt: name: greenplum-db-6 state: present - name: change install directory ownership file: path: '{{ item.path }}' owner: "{{ greenplum_admin_user }}" group: "{{ greenplum_admin_user }}" recurse: yes with_items: "{{ installed_dir.files }}" ``` ``` $ ansible-playbook 3.yml ``` ### 2.5 Настроим параметры ОС для Greenplum ```yaml - hosts: all vars: - version: "6.0.0" - greenplum_admin_user: "gpadmin" - greenplum_admin_password: "changeme" remote_user: root become: yes become_method: sudo connection: ssh gather_facts: yes tasks: - name: update pam_limits pam_limits: domain: "{{ greenplum_admin_user }}" limit_type: '-' limit_item: "{{ item.key }}" value: "{{ item.value }}" with_dict: nofile: 524288 nproc: 131072 ``` ``` $ ansible-playbook 4.yml ``` ### 2.6 Проверка установленной версии ```yaml - hosts: all vars: - version: "6.0.0" remote_user: root become: yes become_method: sudo connection: ssh gather_facts: yes tasks: - name: find installed greenplum version shell: . /usr/local/greenplum-db/greenplum_path.sh && /usr/local/greenplum-db/bin/postgres --gp-version register: postgres_gp_version - name: fail if the correct greenplum version is not installed fail: msg: "Expected greenplum version {{ version }}, but found '{{ postgres_gp_version.stdout }}'" when: "version is not defined or version not in postgres_gp_version.stdout" ``` ``` $ ansible-playbook 5.yml ``` ### 2.7 Финальная версия Соберите все предыдущие конфигурации в один файл и запустите ещё раз. Ошибок быть не должно, кластер перешёл в состояние с установленной Greenplum. ```yaml --- - hosts: all vars: - version: "6.0.0" - greenplum_admin_user: "gpadmin" - greenplum_admin_password: "changeme" # - package_path: passed via the command line with: -e package_path=./greenplum-db-6.0.0-rhel7-x86_64.rpm remote_user: root become: yes become_method: sudo connection: ssh gather_facts: yes tasks: - name: create greenplum admin user user: name: "{{ greenplum_admin_user }}" password: "{{ greenplum_admin_password | password_hash('sha512', 'DvkPtCtNH+UdbePZfm9muQ9pU') }}" - name: copy package to host copy: src: "{{ package_path }}" dest: /tmp - name: cleanup package file from host file: path: "/tmp/{{ package_path | basename }}" state: absent - name: find install directory find: paths: /usr/local patterns: 'greenplum*' file_type: directory register: installed_dir - name: change install directory ownership file: path: '{{ item.path }}' owner: "{{ greenplum_admin_user }}" group: "{{ greenplum_admin_user }}" recurse: yes with_items: "{{ installed_dir.files }}" - name: update pam_limits pam_limits: domain: "{{ greenplum_admin_user }}" limit_type: '-' limit_item: "{{ item.key }}" value: "{{ item.value }}" with_dict: nofile: 524288 nproc: 131072 - name: find installed greenplum version shell: . /usr/local/greenplum-db/greenplum_path.sh && /usr/local/greenplum-db/bin/postgres --gp-version register: postgres_gp_version - name: fail if the correct greenplum version is not installed fail: msg: "Expected greenplum version {{ version }}, but found '{{ postgres_gp_version.stdout }}'" when: "version is not defined or version not in postgres_gp_version.stdout" ``` ``` $ ansible-playbook main.yml ``` ## Релевантные источники - Nemeth E. et al. UNIX and Linux system administration handbook. Chapter 23. - Hochstein L. Ansible: Up and Running. – " O'Reilly Media, Inc.", 2014. - https://gpdb.docs.pivotal.io/6-1/install_guide/ansible-example.html