Записки системного администратора

Ещё один блог о системном администрировании, операционных системах, СУБД, сетях, костылях-велосипедах и пр.

Настройка отказоустойчивого кластера виртуализации на базе OpenNebula, Ceph, MariaDB Galera, Open vSwitch, Paсemaker и Nginx

В этой заметке я хотел бы написать, как настроить отказоустойчивый кластер для виртаулизации из нескольких серверов с исользованием Ceph, OpenNebula, MariaDB Galera Cluster, Open vSwitch, Paсemaker и Nginx. Все эти проекты opensource и у них всех есть замечательная документация, и, в принципе, от этой заметки толку мало. Но, если честно, надоело вспоминать всякие нюансы при настройке очередного кластера. А если кто-то случайно набрел на эту статью, пусть узнает, что так тоже можно. В конце заметки обязательно укажу ссылки на офф документацию этих проектов.
Похожая заметка есть на Хабре, и идею, конечно же, я взял оттуда. Но той заметке уже три года, некоторые моменты изменились. И так, поехали.

Установка и настройка всего этого комбайна будет проходить несколько этапов. Вот они:

  • Настройка кластера распределенного хранилища Ceph
  • Настройка galera кластера MariaDB
  • Настройка программного свитча Open vSwitch
  • Настройка OpenNebula
  • Настройка отказоустойчивого кластера
  • Подключение Ceph хранилища к OpenNebula и первоначальная конфигурация
  • Настройка https на веб-интерфейсе управления кластером OpenNebula

В данном туториале будет описана установка на 3 ноды. В действительности нод может быть сколько угодно.

Желательно, чтобы между нодами кластера была 10G сеть. Она необходима для "комфортного" функционирования кластера Ceph. Об этом пишут сами разработчики в документации по проекту. Но можно обойтись и 1G, если это, например, тестовый кластер или кластер в резервной серверной. В этом случае, конечно же, можно сэкономить ввиду меньшей критичности.

В данном случае между серверами используется 1G сеть. На каждой ноде по два сетевых интерфейса - один интерфейс мы "воткнем" в openvswitch, второй интерфейс будет использоваться ceph.

Также каждая нода содержит 3 HDD по 2Tb для основного пула ceph, оперативную память, достаточную для функционирования ceph (1Gb RAM на 1Tb данных), и конечно же ресурсы, необходимые для функционирования самого облака - CPU и RAM, которые будут использоваться для запуска виртуальных машин.

На каждой ноде установлена операционная система CentOS 7 с последними обновлениями.

Также нужно отключить Selinux (да, да, я в курсе https://stopdisablingselinux.com/, но этого требуют сами разработчики):

for node in tank dozer truck; do ssh $node "sed -i s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config && setenforce 0"; done

И установить EPEL репозиторий:

for node in tank dozer truck; do ssh $node yum install -y epel-release; done

Отлючим временно firewall (я использую iptables):

for node in tank dozer truck; do ssh $node.domain.local systemctl stop iptables; done

Ближе к концу статьи я оставлю список правил для iptables.

Ceph очень привиредлив к настрокам времени - на всех нодах время должно быть одинaково. Поэтому установим демон сhrony:

for node in tank dozer truck; do ssh $node "yum install chrony -y && systemctl enable chronyd && systemctl start chronyd"; done

Схема нашего кластера

Ну и табличка с характеристиками каждой ноды:

Hostname                   | tank             | dozer            | truck
------------------------------------------------------------------------------------
Public Network Interface   | em1              | em1              | em1
Private Network Interface  | em2              | em2              | em2
Public IP Address          | 192.168.5.201/24 | 192.168.5.202/24 | 192.168.5.203/24
Private IP Address         | 10.0.1.1/29      | 10.0.1.2/29      | 10.0.1.3/29
HDD                        | sdc              | sdc              | sdc              
HDD                        | sdd              | sdd              | sdd              
HDD                        | sde              | sde              | sde

Настройка Ceph

Ceph Homepage

Здесь опишу настройку ceph для хранения блочных устройств RBD (RADOS Block Device) для наших виртуальных машин. Последний стабильный релиз - Mimic. Но сколько бы я не пытался настроить его работу мне никак не удавалось. С релизом Luminous у меня никаких проблем не возникало. Поэтому опишу установку именно Ceph Luminous.

Установка будет осуществляться с помощью ceph-deploy, а она подразумевает установку с так называемого админского сервера. В качестве админского сервера может выступить любой инстанс с установленным ceph-deploy и ssh-клиентом. В этом примере будем деплоить с одной из нод - tank.

На каждой ноде необходимо иметь пользователя ceph, который может ходить между нодами без пароля и выполнять любые команды через sudo так же без пароля. На каждой ноде выполняем:

sudo useradd ceph
sudo passwd ceph
sudo echo "ceph ALL = (root) NOPASSWD:ALL" > /etc/sudoers.d/ceph
sudo chmod 0440 /etc/sudoers.d/ceph

Сгенерируем ключ и скопируем его на остальные ноды.

sudo -u ceph mkdir /home/ceph/.ssh
sudo -u ceph ssh-keygen -f /home/ceph/.ssh/id_rsa
sudo -u ceph cat /home/ceph/.ssh/id_rsa.pub >> /home/ceph/.ssh/authorized_keys
for node in dozer truck; do scp -r /home/ceph/.ssh/ ceph@$node:/home/ceph/; done

Добавим ceph репозиторий в /etc/yum.repos.d/ceph.repo. Вместо {ceph-stable-release} нужно указать необходимы релиз (например, mimic или luminous)

[ceph-noarch]
name=Ceph noarch packages
baseurl=https://download.ceph.com/rpm-luminous/el7/noarch
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc

Устанавливаем ceph-deploy:

sudo yum install -y ceph-deploy

Теперь зайдем под пользователем ceph и создадим папку, в которой будут храниться конфиги и ключи ceph:

sudo su - ceph
mkdir ceph-admin
cd ceph-admin

Создадим кластер:

ceph-deploy new --public-network 192.168.5.0/24 --cluster-network=10.0.1.0/29 tank dozer truck        

Установим ceph на наши ноды:

ceph-deploy install tank dozer truck

Если в процессе возникла ошибка:

Traceback (most recent call last):
  File "/bin/ceph-deploy", line 18, in <module>
    from ceph_deploy.cli import main
  File "/usr/lib/python2.7/site-packages/ceph_deploy/cli.py", line 1, in <module>
    import pkg_resources
ImportError: No module named pkg_resources

то необходимо установить pip:

sudo yum install python2-pip

и повторить процесс заново:

ceph-deploy install tank dozer truck

Создадим мониторы:

ceph-deploy mon create-initial

В документации к OpenNebula говорится, что необходим строго RBD format 2. Поэтому добавим строчку в конфиг ceph.conf в секцию global:

[global]
rbd_default_format = 2

Обновим конфиг на наших нодах:

ceph-deploy admin tank dozer truck

Для релизов Ceph старше Luminous необходимо создать так же демона manager:

ceph-deploy mgr create tank.domain.local dozer.domain.local truck.domain.local

Подготовим наши диски и установим osd демоны. Сейчас это выполняется всего одной командой для каждого osd. Команду ниже нужно выполнить для всех нод и каждого диска, которые будут использоваться в пуле ceph:

ceph-deploy osd create --data /dev/sdc tank.domain.local

Посмотрим, что у нас получилось:

[root@tank ~]$sudo ceph -s

  cluster:
    id:     f5ba7ed1-de2c-4355-a7b6-27e0c22eefca
    health: HEALTH_OK

  services:
    mon: 3 daemons, quorum tank,dozer,truck
    mgr: tank.domain.local(active), standbys: truck.domain.local, dozer.domain.local
    osd: 6 osds: 6 up, 6 in

  data:
    pools:   0 pools, 0 pgs
    objects: 0  objects, 0 B
    usage:   6.0 GiB used, 10.1 TiB / 10.1 TiB avail
    pgs:
[root@tank ~]$sudo ceph osd tree

ID CLASS  WEIGHT  TYPE NAME      STATUS REWEIGHT PRI-AFF 
-1       10.91271 root default                           
-5        3.63757     host dozer                         
 2   hdd  1.81879         osd.2      up  1.00000 1.00000 
 3   hdd  1.81879         osd.3      up  1.00000 1.00000 
-7        3.63757     host truck                          
 4   hdd  1.81879         osd.4      up  1.00000 1.00000 
 5   hdd  1.81879         osd.5      up  1.00000 1.00000 
-3        3.63757     host tank                          
 0   hdd  1.81879         osd.0      up  1.00000 1.00000 
 1   hdd  1.81879         osd.1      up  1.00000 1.00000

Создадим пул для images и system. Создается комaндой:

ceph osd pool create {pool-name} pg_num

При создании пула необходимо задать значение pg_num, так как оно не может расчитываться автоматически. Можно выбрать значение из списка ниже:

  • меньше 5 osd - pg_num = 128
  • между 5 и 10 osd - pg_num = 512
  • между 10 и 50 osd - pg_num = 1024

Если количество osd больше, чем 50, то можно воспользоваться калькулятором

Назначаем им size - размер реплики, min_size - минимальный размер реплики в момент записи, то есть сколько нужно сделать реплик в момент записи, чтобы "отпустить" операцию записи, compression_algorithm, compression_mode. Полный список возможных опций можно посмотреть тут:

sudo ceph osd pool create one 512
sudo ceph osd pool set one min_size 2
sudo ceph osd pool set one size 3
sudo ceph osd pool set one compression_algorithm zlib
sudo ceph osd pool set one compression_mode force 

sudo ceph osd pool create one-system 512
sudo ceph osd pool set one min_size 2
sudo ceph osd pool set one size 3
sudo ceph osd pool set one compression_algorithm zlib
sudo ceph osd pool set one compression_mode force

Проверим:

[ceph@tank ceph-admin]$ sudo ceph osd lspools
1 one
2 one-system

Позже мы вернемся к ceph, когда необходимо будет создать пользователя для кластера OpenNebula. А пока настройку ceph можно считать завершенной.

Настройка MariaDB Galera Cluster

MariaDB Homepage

Теперь настроим отказоустойчивую MySQL базу данных на наших нодах, в которой мы и будем хранить конфигурацию нашего дата центра. MariaDB Galera Cluster — это MariaDB кластер с мастер-мастер репликацией использующий для синхронизации galera-библиотеку. Плюс ко всему он довольно прост в настройке.

На всех нодах добавим репозиторий /etc/yum.repos.d/mariadb.repo:

[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

Установим сервер:

for node in tank dozer truck; do ssh $node yum install -y MariaDB-server MariaDB-client rsync galera; done

Установим в атозагрузку на каждой ноде:

systemctl enable mariadb

и запустим:

systemctl start mariadb 

Произведем первоначальную настройку:

mysql_secure_installation

На каждой ноде создадим пользователя для репликации:

mysql -u root -p
create user 'replicator'@'%' identified by 'gK7LBHty7Eosid9gE82Obf8O';
grant all privileges on *.* to 'replicator'@'%';
flush privileges;
exit

И остановим mariadb:

systemctl stop mariadb

В my.cnf (или в /etc/my.cnf.d/server.cnf в секцию mysqld) на первой ноде нужно добавить следующее:

collation-server = utf8_general_ci
init-connect = 'SET NAMES utf8'
character-set-server = utf8
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
innodb_locks_unsafe_for_binlog=1
query_cache_size=0
query_cache_type=0
bind-address=0.0.0.0
datadir=/var/lib/mysql
innodb_log_file_size=100M
innodb_file_per_table
innodb_flush_log_at_trx_commit=2
wsrep_on=ON
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.5.202,192.168.5.203"
wsrep_cluster_name='galera_cluster'
wsrep_node_address='192.168.5.201'
wsrep_node_name='tank.domain.local'
wsrep_sst_method=rsync
wsrep_sst_auth=replicator:gK7LBHty7Eosid9gE82Obf8O

На остальных нодах:

collation-server = utf8_general_ci
init-connect = 'SET NAMES utf8'
character-set-server = utf8
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
innodb_locks_unsafe_for_binlog=1
query_cache_size=0
query_cache_type=0
bind-address=0.0.0.0
datadir=/var/lib/mysql
innodb_log_file_size=100M
innodb_file_per_table
innodb_flush_log_at_trx_commit=2
wsrep_on=ON
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.5.201,192.168.5.203"
wsrep_cluster_name='galera_cluster'
wsrep_node_address='192.168.5.202'
wsrep_node_name='dozer.domain.local'
wsrep_sst_method=rsync
wsrep_sst_auth=replicator:gK7LBHty7Eosid9gE82Obf8O
collation-server = utf8_general_ci
init-connect = 'SET NAMES utf8'
character-set-server = utf8
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
innodb_locks_unsafe_for_binlog=1
query_cache_size=0
query_cache_type=0
bind-address=0.0.0.0
datadir=/var/lib/mysql
innodb_log_file_size=100M
innodb_file_per_table
innodb_flush_log_at_trx_commit=2
wsrep_on=ON
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.5.201,192.168.5.202"
wsrep_cluster_name='galera_cluster'
wsrep_node_address='192.168.5.203'
wsrep_node_name='truck.domain.local'
wsrep_sst_method=rsync
wsrep_sst_auth=replicator:gK7LBHty7Eosid9gE82Obf8O

Запускаем кластер. На первой ноде выполняем:

galera_new_cluster

Если все прошло без ошибок, остальные ноды запускаем так:

systemctl start mariadb

Проверим кластер. На каждой ноде запустим:

mysql -u root -p
SHOW STATUS LIKE 'wsrep%';

Должны получить что-то похожее на это:

MariaDB [(none)]> SHOW STATUS LIKE 'wsrep%';
+------------------------------+----------------------------------------------------------+
| Variable_name                | Value                                                    |
+------------------------------+----------------------------------------------------------+
| wsrep_apply_oooe             | 0.000000                                                 |
| wsrep_apply_oool             | 0.000000                                                 |
| wsrep_apply_window           | 0.000000                                                 |
| wsrep_causal_reads           | 0                                                        |
| wsrep_cert_deps_distance     | 0.000000                                                 |
| wsrep_cert_index_size        | 0                                                        |
| wsrep_cert_interval          | 0.000000                                                 |
| wsrep_cluster_conf_id        | 3                                                        |
| wsrep_cluster_size           | 3                                                        |
| wsrep_cluster_state_uuid     | 332eb3dc-d08c-11e8-b23f-3ea9f81c173c                     |
| wsrep_cluster_status         | Primary                                                  |
| wsrep_cluster_weight         | 3                                                        |
| wsrep_commit_oooe            | 0.000000                                                 |
| wsrep_commit_oool            | 0.000000                                                 |
| wsrep_commit_window          | 0.000000                                                 |
| wsrep_connected              | ON                                                       |
| wsrep_desync_count           | 0                                                        |
| wsrep_evs_delayed            |                                                          |
| wsrep_evs_evict_list         |                                                          |
| wsrep_evs_repl_latency       | 0/0/0/0/0                                                |
| wsrep_evs_state              | OPERATIONAL                                              |
| wsrep_flow_control_paused    | 0.000000                                                 |
| wsrep_flow_control_paused_ns | 0                                                        |
| wsrep_flow_control_recv      | 0                                                        |
| wsrep_flow_control_sent      | 0                                                        |
| wsrep_gcomm_uuid             | 45b29422-d08c-11e8-a2ff-96d1e7471207                     |
| wsrep_incoming_addresses     | 192.168.5.201:3306,192.168.5.202:3306,192.168.5.203:3306 |
| wsrep_last_committed         | 0                                                        |
| wsrep_local_bf_aborts        | 0                                                        |
| wsrep_local_cached_downto    | 18446744073709551615                                     |
| wsrep_local_cert_failures    | 0                                                        |
| wsrep_local_commits          | 0                                                        |
| wsrep_local_index            | 1                                                        |
| wsrep_local_recv_queue       | 0                                                        |
| wsrep_local_recv_queue_avg   | 0.000000                                                 |
| wsrep_local_recv_queue_max   | 1                                                        |
| wsrep_local_recv_queue_min   | 0                                                        |
| wsrep_local_replays          | 0                                                        |
| wsrep_local_send_queue       | 0                                                        |
| wsrep_local_send_queue_avg   | 0.000000                                                 |
| wsrep_local_send_queue_max   | 1                                                        |
| wsrep_local_send_queue_min   | 0                                                        |
| wsrep_local_state            | 4                                                        |
| wsrep_local_state_comment    | Synced                                                   |
| wsrep_local_state_uuid       | 332eb3dc-d08c-11e8-b23f-3ea9f81c173c                     |
| wsrep_open_connections       | 0                                                        |
| wsrep_open_transactions      | 0                                                        |
| wsrep_protocol_version       | 9                                                        |
| wsrep_provider_name          | Galera                                                   |
| wsrep_provider_vendor        | Codership Oy <info@codership.com>                        |
| wsrep_provider_version       | 25.3.24(r3825)                                           |
| wsrep_ready                  | ON                                                       |
| wsrep_received               | 4                                                        |
| wsrep_received_bytes         | 531                                                      |
| wsrep_repl_data_bytes        | 0                                                        |
| wsrep_repl_keys              | 0                                                        |
| wsrep_repl_keys_bytes        | 0                                                        |
| wsrep_repl_other_bytes       | 0                                                        |
| wsrep_replicated             | 0                                                        |
| wsrep_replicated_bytes       | 0                                                        |
| wsrep_thread_count           | 2                                                        |
+------------------------------+----------------------------------------------------------+
61 rows in set (0.002 sec)

Настройка Open vSwitch

Open vSwitch Homepage

Так как в стандартных репозиториях нет пакета openvswitch, можно скачать исходники, скопилировать и установить, а можно воспользоваться репозиторием RDO:

for node in tank dozer truck; do ssh $node yum install https://rdoproject.org/repos/rdo-release.rpm; done
for node in tank dozer truck; do ssh $node yum install openvswitch; done

Добавим в автозагрузку и запустим демон:

for node in tank dozer truck; do ssh $node systemctl enable openvswitch; done
for node in tank dozer truck; do ssh $node systemctl start openvswitch; done

Сейчас настроим сетевой бридж, в который будут добавляться порты:

ovs-vsctl add-br ovs-br0
ovs-vsctl add-port ovs-br0 em1

Если настройка производится на удаленном сервере и кроме ssh другого доступа нет, то эти команды лучше выполнить позже, после правки конфигов сетевых интерфейсов.

Поправим конфиги сетевых интерфейсов. Для первой ноды:

/etc/sysconfig/network-scripts/ifcfg-em1
TYPE="OVSPort"
NM_CONTROLLED="no"
IPV4_FAILURE_FATAL="no"
IPV6INIT="no"
NAME="em1"
UUID="f1326702-cdc5-4743-8537-77ab270e47ab"
DEVICE="em1"
DEVICETYPE="OVSIntPort"
ONBOOT="yes"
OVS_BRIDGE="ovs-br0"

/etc/sysconfig/network-scripts/ifcfg-ovs-br0
DEVICE="ovs-br0"
NM_CONTROLLED="no"
ONBOOT="yes"
TYPE="OVSBridge"
BOOTPROTO="static"
IPADDR="192.168.5.201"
NETMASK="255.255.255.0"
GATEWAY="192.168.5.1"
DNS1="1.1.1.1"
DNS2="8.8.8.8"
HOTPLUG="no"

Не забываем поправить конфиги на остальных серверах.

Перезапустим сеть, все должно завестись:

systemctl restart network

Установка и настройка OpenNebula

OpenNebula Homepage

На всех нодах подключим репозиторий OpenNebula:

# cat << EOT > /etc/yum.repos.d/opennebula.repo
[opennebula]
name=opennebula
baseurl=https://downloads.opennebula.org/repo/5.6/CentOS/7/x86_64
enabled=1
gpgkey=https://downloads.opennebula.org/repo/repo.key
gpgcheck=1
#repo_gpgcheck=1
EOT

Устанавливаем сервер, веб-интерфейс с api и мета-пакет, который создаст в системе пользователя oneadmin и установит на ноду libvirt и kvm:

for node in tank dozer truck; do ssh $node yum install -y opennebula-server opennebula-sunstone opennebula-node-kvm; done

Некоторые компоненты opennebula требуют наличия некоторых ruby библиотек и дополнительных пакетов. Для их установки на каждой ноде нужно запустить интерактивный скрипт:

/usr/share/one/install_gems

Если во время компиляции библиотек возникла ошибка

/usr/bin/ld: cannot find -lmariadb
collect2: ошибка: выполнение ld завершилось с кодом возврата 1
make: *** [mysql2.so] Ошибка 1


Gem files will remain installed in /usr/local/share/gems/gems/mysql2-0.5.1 for inspection.
Results logged to /usr/local/share/gems/gems/mysql2-0.5.1/ext/mysql2/gem_make.out

An error occurred while installing mysql2 (0.5.1), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.1' --source 'https://rubygems.org/'` succeeds before bundling.

То нужно выполнить:

ln -s /usr/lib64/libmysqld.so /usr/lib64/libmariadb.so
ln -s /usr/lib64/libmysqld.a /usr/lib64/libmariadb.a

Проверить все ли хорошо можно командой:

ld -lmariadb --verbose

После этого скрипт нужно запустить заново.

Установщик сам создал пользователя oneadmin в системе, ssh-ключи и правила для sudo. Так же этому пользователю необходимо разрешить заходить на каждую ноду по ssh без пароля. Для этого выполним:

sudo passwd oneadmin
sudo -u oneadmin cat /var/lib/one/.ssh/id_rsa.pub >> /var/lib/one/.ssh/authorized_keys
for node in dozer truck; do scp /var/lib/one/.ssh/* oneadmin@$node:/var/lib/one/.ssh/; done

Настроим базу данных:

mysql -u root -p
create database opennebula;
create user 'oneadmin'@'%' identified by 'JKnknjkncxsgEObf8343fdd5O';
grant all privileges on opennebula.* to 'oneadmin'@'%';
flush privileges;

Теперь сообщим opennebula как подключиться к нашей базе данных. Поправим конфиг /etc/one/oned.conf.
Закомментируем строку:

DB = [ BACKEND = "sqlite" ]

и добавим:

DB = [ BACKEND = "mysql",
        SERVER  = "localhost",
        PORT    = 0,
        USER    = "oneadmin",
        PASSWD  = "JKnknjkncxsgEObf8343fdd5O",
        DB_NAME = "opennebula",
        CONNECTIONS = 50 ]

Это нужно выполнить на каждой ноде.

Так же мы должны скопировать ключ авторизации oneadmin в кластере на остальные ноды, так как все управление кластером OpenNebula осуществляется именно под ним:

for node in dozer truck; do scp /var/lib/one/.one/one_auth oneadmin@$node.domain.local:/var/lib/one/.one/; done

Для проверки на каждой ноде попробуем запустить opennebula:

systemctl start opennebula opennebula-sunstone

Проверяем:

http://192.168.5.201:9869

Проверим эти логи на наличие ошибок:

/var/log/one/oned.log /var/log/one/sched.log /var/log/one/sunstone.log

Если все ок, то выключаем:

systemctl stop opennebula opennebula-sunstone

Настройка отказоустойчивого кластера

ClusterLabs Homepage

Тут я сделал как в статье на Хабре

Будем использовать pacemaker, corosync and crmsh.

Отключим автозапуск демонов opennebula на всех нодах:

for node in tank dozer truck; do ssh $node.domain.local "systemctl disable opennebula opennebula-sunstone opennebula-novnc"; done

Добавим репозиторий на всех нодах:

cat << EOT > /etc/yum.repos.d/network\:ha-clustering\:Stable.repo
[network_ha-clustering_Stable]
name=Stable High Availability/Clustering packages (CentOS_CentOS-7)
type=rpm-md
baseurl=http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/CentOS_CentOS-7/
gpgcheck=1
gpgkey=http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/CentOS_CentOS-7/repodata/repomd.xml.key
enabled=1
EOT

Установим необходимые пакеты:

for node in tank dozer truck; do ssh $node "yum install corosync pacemaker crmsh resource-agents -y"; done

Пакет resource-agents потребовал cifs-utils, не знаю зачем он ему, но я не стал спорить и установил дополнительно и его.

Отредактируем на всех нодах конфиг /etc/corosync/corosync.conf и приведем его к следующему виду:

totem {
        version: 2
        crypto_cipher: none
        crypto_hash: none
        interface {
                ringnumber: 0
                bindnetaddr: 192.168.5.0
                mcastaddr: 226.94.1.1
                mcastport: 4000
                ttl: 1
        }
}
logging {
        fileline: off
        to_stderr: no
        to_logfile: yes
        logfile: /var/log/cluster/corosync.log
        to_syslog: yes
        debug: off
        timestamp: on
        logger_subsys {
                subsys: QUORUM
                debug: off
        }
}
quorum {
        provider: corosync_votequorum
}
service {
name: pacemaker
ver: 1
}
nodelist {
        node {
                ring0_addr: tank.domain.local
                nodeid: 1
        }
        node {
                ring0_addr: dozer.domain.local
                nodeid: 2
        }
        node {
                ring0_addr: truck.domain.local
                nodeid: 3
        }
}

Сгенерируем ключ и скопируем его на остальные ноды:

cd /etc/corosync
corosync-keygen
for node in dozer truck; do scp /etc/corosync/authkey root@$node:/etc/corosync/; done

Запустим HA-сервисы на всех нодах:

systemctl enable pacemaker corosync
systemctl start pacemaker corosync        

Проверяем статус:

[root@tank corosync]# crm status
Stack: corosync
Current DC: tank.domain.local (version 1.1.18-11.el7_5.3-2b07d5c5a9) - partition with quorum
Last updated: Tue Oct 16 13:50:33 2018
Last change: Tue Oct 16 13:50:26 2018 by hacluster via crmd on tank.domain.local

3 nodes configured
0 resources configured

Online: [ dozer.domain.local truck.domain.local tank.domain.local ]

No resources

Отключим STONITH (механизм добивания неисправной ноды):

crm configure property stonith-enabled=false

Если ноды всего две, то нужно отключить кворум, во избежании splitbrain'a:

crm configure property no-quorum-policy=stop

Теперь создадим ресурсы:

[root@tank corosync]# crm
crm(live)# configure
crm(live)configure# primitive ClusterIP ocf:heartbeat:IPaddr2 params ip="192.168.5.200" cidr_netmask="24" op monitor interval="30s"
crm(live)configure# primitive opennebula_p systemd:opennebula op monitor interval=60s timeout=20s op start interval="0" timeout="120s" op stop  interval="0" timeout="120s"
crm(live)configure# primitive opennebula-sunstone_p systemd:opennebula-sunstone op monitor interval=60s timeout=20s op start interval="0" timeout="120s" op stop  interval="0" timeout="120s"
crm(live)configure# primitive opennebula-novnc_p systemd:opennebula-novnc op monitor interval=60s timeout=20s op start interval="0" timeout="120s" op stop  interval="0" timeout="120s" 
crm(live)configure# group Opennebula_HA ClusterIP opennebula_p opennebula-sunstone_p  opennebula-novnc_p
crm(live)configure# exit
There are changes pending. Do you want to commit them (y/n)? y

Этими действиями мы создали виртуальный IP (192.168.5.200), добавили три наших сервиса в HA-кластер и объединили их в группу Opennebula_HA.

Проверяем:

[root@tank corosync]# crm status
Stack: corosync
Current DC: tank.domain.local (version 1.1.18-11.el7_5.3-2b07d5c5a9) - partition with quorum
Last updated: Tue Oct 16 13:59:05 2018
Last change: Tue Oct 16 13:57:53 2018 by root via cibadmin on tank.domain.local

3 nodes configured
4 resources configured

Online: [ dozer.domain.local truck.domain.local tank.domain.local ]

Full list of resources:

Resource Group: Opennebula_HA
      ClusterIP        (ocf::heartbeat:IPaddr2):        Started tank.domain.local
      opennebula_p        (systemd:opennebula):        Started tank.domain.local
      opennebula-sunstone_p        (systemd:opennebula-sunstone):        Started tank.domain.local
      opennebula-novnc_p        (systemd:opennebula-novnc):        Started tank.domain.local

На этом настройка HA закончена.

Подключение Ceph хранилища к OpenNebula и первоначальная конфигурация

Возвращаемся к нашему ceph хранилищу. Необходимо в ceph создать пользователя для доступа к созданным пулам one и one-system. Этот пользователь так же будет использоваться libvirt'ом для доступа к образам дисков. Создадим пользователя libvirt:

ceph auth get-or-create client.libvirt mon 'profile rbd' osd 'profile rbd pool=one'

Получим ключ и сохраним его:

ceph auth get-key client.libvirt | tee client.libvirt.key
ceph auth get client.libvirt -o /etc/ceph/ceph.client.libvirt.keyring

Файл keyring скопируем в каталог /etc/ceph на оставшихся нодах:

for node in dozer truck; do scp ceph.client.libvirt.keyring root@$node.domain.local:/etc/ceph/; done

Сохраним ключ авторизации для libvirtd:

[vadim@tank ~]$ sudo su - oneadmin

[oneadmin@tank ~]$ UUID=`uuidgen`; echo $UUID
u86d28f9-84af-49c8-87db-b5f379e26364

[oneadmin@tank ~]$ cat > secret.xml <<EOF
<secret ephemeral='no' private='no'>
  <uuid>$UUID</uuid>
  <usage type='ceph'>
          <name>client.libvirt secret</name>
  </usage>
</secret>
EOF

[oneadmin@tank ~]$ for node in tank dozer truck; do virsh --connect=qemu+ssh://oneadmin@$node.domain.local/system secret-define secret.xml; done
[oneadmin@tank ~]$ for node in tank dozer truck; do virsh --connect=qemu+ssh://oneadmin@$node.domain.local/system secret-set-value --secret $UUID --base64 $(cat client.libvirt.key); done

Файл key после этого можно удалить:

rm client.libvirt.key

Теперь подключим пул для images:

[oneadmin@tank ~]$ cat << EOT > rbd-images.conf
NAME = "ceph-images-ds"
DS_MAD = ceph
TM_MAD = ceph
DISK_TYPE = RBD
POOL_NAME = one
BRIDGE_LIST ="192.168.5.201 192.168.5.202 192.168.5.203"
CEPH_HOST ="192.168.5.201:6789 192.168.5.202:6789 192.168.5.203:6789"
CEPH_SECRET ="376d28f9-84af-49c8-87db-b5fe85e26364"
CEPH_USER = libvirt
EOT


[oneadmin@tank ~]$ onedatastore create rbd-images.conf

Пул system:

[oneadmin@tank ~]$ cat << EOT > rbd-system.conf
NAME = "ceph-system-ds"
TM_MAD = ceph
DISK_TYPE = RBD
POOL_NAME = one-system
TYPE = SYSTEM_DS
BRIDGE_LIST ="192.168.5.201 192.168.5.202 192.168.5.203"
CEPH_HOST ="192.168.5.201:6789 192.168.5.202:6789 192.168.5.203:6789"
CEPH_SECRET ="376d28f9-84af-49c8-87db-b5fe85e26364"
CEPH_USER = libvirt
EOT

[oneadmin@tank ~]$ onedatastore create rbd-system.conf

Далее заходим в веб-интерфейс. Он всегда будет доступен по адресу http://192.168.5.200:9869

login: oneadmin
pass: находится в /var/lib/one/.one/one_auth

В веб-интерфейсе создаем кластер, добавляем ноды и виртуальную сеть. Интерфейс очень понятный, поэтому описывать тут эту настройку не буду.

Настройка https на веб-интерфейсе управления кластером

Nginx Homepage

Добавим репозиторий nginx:

cat << EOT > /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/\$basearch/
gpgcheck=0
enabled=1
EOT

Установим nginx:

for node in tank dozer truck; do ssh $node.domain.local yum install -y nginx nginx-module-geoip; done

Добавим его в автозагрузку:

for node in tank dozer truck; do ssh $node.domain.local systemctl enable nginx; done

Удалим дефолтный конфиг:

for node in tank dozer truck; do ssh $node.domain.local rm -f /etc/nginx/conf.d/default.conf; rm -f /etc/nginx/nginx.conf; done

Сертификаты для https можно использовать самоподписанные или от Let's Encrypt - тут уже как душе угодно. Информации о том, как сделать любую из этих вещей, в интернете вагон и две тележки - описывать тут не буду.

На каждой ноде добавляем следующие конфиги:

less /etc/nginx/nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    ssl_session_cache   shared:SSL:100m;
    ssl_session_timeout 1h;

    include /etc/nginx/conf.d/*.conf;
    server_tokens off;
}
less /etc/nginx/conf.d/one.conf
##
## Upstreams
###

upstream one-crm {
        server 192.168.5.200:9869;
}

##
## HTTP Server
###

server {
        listen 869 default_server;
        server_name one-cluster.domain.local;
        return 301 https://one-cluster.domain.local:4869$request_uri;
}

##
## HTTPS Server
###

server {
        listen 4869 ssl;
        server_name one-cluster.domain.local;

        ssl_certificate /etc/ssl/one-cluster.domain.local/one_domain_local.crt; 
        ssl_certificate_key /etc/ssl/one-cluster.domain.local/one_domain_local.key;

        ssl_protocols TLSv1.2;
        ssl_prefer_server_ciphers on;

        ssl_dhparam /etc/ssl/certs/dhparam.pem;
        ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:AES256-GCM-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
        add_header X-Frame-Options SAMEORIGIN;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";

        location / {
                proxy_pass http://one-crm;
                proxy_store off;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                client_max_body_size 0;
                client_body_buffer_size 128k;
                proxy_connect_timeout 60;
                proxy_send_timeout 60;
                proxy_read_timeout 60;
                proxy_buffer_size 8k;
                proxy_buffers 4 32k;
                proxy_busy_buffers_size 64k;
                proxy_temp_file_write_size 64k;
                location ~ /\.ht {
                        deny all;
                }
                location ~ /\.git {
                        deny all;
                }
        }
}

Проверим, что конфиг без ошибок:

sudo nginx -t

И запустим nginx:

sudo systemctl start nginx

Теперь подключение к основному веб интерфейсу у нас шифруется tls1.2, осталось зашифровать подключение к vnc.

Правим конфиг /etc/one/sunstone-server.conf

:vnc_proxy_port: 29876
:vnc_proxy_support_wss: only
:vnc_proxy_cert: /etc/ssl/one-cluster.domain.local/one_domain_local.crt
:vnc_proxy_key: /etc/ssl/one-cluster.domain.local/one_domain_local.key

И перезапускаем:

sudo systemctl restart opennebula-sunstone

Но несмотря на то, что мы в конфиге описали расположение сертификатов, wss не хочет работать. При попытке запуска vnс подключения оно не будет устанавливаться, в консоли FF увидим сообщение "Firefox can’t establish a connection to the server at wss", а в логе /var/log/one/novnc.log будет сообщение "SSL connection but '/self.pem' not found".

Не знаю, может я плохо читал документацию к opennebula, но в ней я информации об этом не нашел, поэтому выбрал немного костыльный метод (зато работает).
В файле /usr/share/one/websockify/websockify я подправил два параметра, чтобы получилось так:

    parser.add_option("--cert", default="/etc/ssl/one-cluster.domain.local/one_domain_local.crt",
            help="SSL certificate file")
    parser.add_option("--key", default="/etc/ssl/one-cluster.domain.local/one_domain_local.key",
            help="SSL key file (if separate from cert)")

И перезапустил opennebula-novnc:

sudo systemctl restart opennebula-novnc

Пробуем подключиться:

Настройка firewall

Как я писал выше, в работе я использую iptables (firewalld мне неудобен). Ниже представлен список правил для этого firewall, необходимых для функционирования кластера:

## Allow full access from dozer
-A INPUT -s 192.168.5.202 -j ACCEPT
## Allow full access from truck
-A INPUT -s 192.168.5.203 -j ACCEPT
## Allow ceph cluster network
-A INPUT -s 10.0.1.0/28 -j ACCEPT
## Allow access to one web interface
-A INPUT -s ${admin-desktop-ip} -p tcp -m multiport --ports 869,4869,29876 -j ACCEPT

Источники

Комментарии (RSS)

Автор молодец! Спасибо, очень помог

Ответить Отменить ответ

вопрос к автору

[ceph_deploy.mon][ERROR ] Some monitors have still not reached quorum:

всё проходи ок по мануалу, фаерволы выключены, selinux=disabled

сеть 192.168.30.0/24

что могло пойти не так?

Ответить Отменить ответ

А что в логах? Это при инициализации мониторов возникает?

Ответить Отменить ответ

Я переделал всё с нуля и заработало, единственное что - это нужно использовать другого юзера вместо ceph, он зарезервирован самим сервисом. Спасибо за статью, кармы ++.

Ответить Отменить ответ

HTML Preview:

Коротко о себе:

Привет! Меня зовут Вадим. В этом блоге я пишу об интересующих меня вещах, о проблемах, с которыми сталкиваюсь во время работы, и о путях их решения.

Связаться со мной можно, написав письмо на адрес vadim@adminbook.click