Описанная далее проблема случилась уже давно, примерно давно и было найдено её решение. Всё затягивал я и затягивал с выкладыванием этого материала, но довольно. Представляю вашему вниманию детектив об убийце кластера Clickhouse.
Входные данные
Начнем с входных данных. Кластер на 3 машины настроен и вполне работает. Конфиги, которые я использовал, если вам интересно, можете обнаружить в одном из предыдущих постов.
“Почему я думаю, что кластер работает?” - может возникнуть вопрос. “Потому что я угрожал его семье” - скажу я. Шутка. Я так считал и не отказываюсь от этого, у меня, в конце концов, есть основания: сервера отвечают корректно на команду stat.
Тот самый убийца кластера
Перезагружаем какой- нибудь сервер кластера и… После перезагрузки сервера кластеру становится плохо. Кластер больше не может прийти к консенсусу.
2023.12.05 09:13:48.707111 [ 11591 ] {2c49171b-944c-4bf3-a7f7-78644aa99a65} <Err
or> executeQuery: Code: 999. Coordination::Exception: Connection loss. (KEEPER_E
XCEPTION) (version xxx) (from 0.0.0.0:0) (in query: /* ddl_entry=query-000
0000003 */ CREATE TABLE IF NOT EXISTS table_name.bd_name, Stack trace (when copying this messag
e, always include the lines below):
0. Poco::Exception::Exception(String const&, int) @ 0x1494bd7a in /nix/store/lx6
scx072nzfbybn9g45l9h8mqcx3b7r-clickhouse-xxx/bin/clickhouse
1. DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0xb326a
43 in /nix/store/lxbscx213nzfbybn9g45lfg8mqcx3b7r-clickhouse-xxx
...
2023.12.05 09:13:48.768152 [ 11591 ] {2c49171b-944c-4bf3-a7f7-78644aa99a65} <Err
or> DDLWorker: Query /* ddl_entry=query-0000000003 */ CREATE TABLE IF NOT EXISTS
TABLE_NAME UUID '1981e0ac-244c-4b4c-826b-c80471e9c012' ( ... ) ENGINE = ReplicatedMergeTree
PARTITION BY (...) ORDER BY ... wasn't finished successfully: Code: 999. Coordination::Exception: Connection
loss. (KEEPER_EXCEPTION), Stack trace (when copying this message, always includ
e the lines below):
0. Poco::Exception::Exception(String const&, int) @ 0x1494bd7a in /nix/store/lxbscx213nzfbybn9g45lfg8mqcx3b7r-clickhouse-xxx/bin/clickhouse
1. DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0xb326a
43 in /nix/store/lxbscx213nzfbybn9g45lfg8mqcx3b7r-clickhouse-xxx/bin/click
house
2. Coordination::Exception::Exception(String const&, Coordination::Error, int) @
0x12359582 in /nix/store/lxbscx213nzfbybn9g45lfg8mqcx3b7r-clickhouse-xxx/
bin/clickhouse
...
ip- адреса, доменные имена, порты и названия полей в таблице я намеренно скрываю или прячу
Попытка осознать
Итак, всё, кластер разъехался, а в него даже данных никто не слал. Был лишь перезапущен сервер у нас ничего не работает. Только что успешно все работало и биты информации бегали от сервера к серверу, а тут оп и приехали…
В ответ на stat кластер говорит следующее:
root@localhost:~# echo stat | nc 172.16.128.251 9181 ; echo ; echo stat | nc 172.16.128.252 9181 ; echo ; echo stat | nc 172.16.128.253 9181
This instance is not currently serving requests
ClickHouse Keeper version: vxxx
Clients:
ip1:40815(recved=0,sent=0)
127.0.0.1:57636(recved=289966,sent=289985)
Latency min/avg/max: 0/0/65
Received: 289966
Sent: 289985
Connections: 1
Outstanding: 0
Zxid: 14798
Mode: leader
Node count: 4968
ClickHouse Keeper version: vxxx
Clients:
ip1:41747(recved=0,sent=0)
127.0.0.1:37056(recved=234648,sent=234668)
Latency min/avg/max: 0/0/41
Received: 234648
Sent: 234668
Connections: 1
Outstanding: 0
Zxid: 14798
Mode: follower
Node count: 4968
А вот ответ на lgif можно увидеть следующую картину:
root@localhost:~# echo lgif | nc ip1 9181 ; echo ; echo lgif | nc ip2 9181 ; echo ; echo lgif | nc ip3 9181
first_log_idx 1
first_log_term 1
last_log_idx 14464
last_log_term 5
last_committed_log_idx 0
leader_committed_log_idx 0
target_committed_log_idx 0
last_snapshot_idx 0
first_log_idx 1
first_log_term 1
last_log_idx 14998
last_log_term 55
last_committed_log_idx 14998
leader_committed_log_idx 14998
target_committed_log_idx 14998
last_snapshot_idx 9678
first_log_idx 1
first_log_term 1
last_log_idx 14998
last_log_term 55
last_committed_log_idx 14998
leader_committed_log_idx 14998
target_committed_log_idx 14998
last_snapshot_idx 0
На данном этапе в той ситуации можно было проводить вскрытие жертвы. Уже никто не мог её спасти, кластер разъехался окончательно. На самом деле спасти его как бы еще можно было. Но с применением средства после потери кворума (сброса прогресса на лидере и выбор нового лидера с последующей пересинхронизацией).
Следственный эксперимент
Откатываем все сервера и повторяем процедуру, заранее удостоверяемся в том, что команда stat верно обрабатывается серверами.
root@localhost:~# echo stat | nc ip1 9181 ; echo ; echo stat | nc ip2 9181 ; echo ; echo stat | nc ip3 9181
ClickHouse Keeper version: vxxx
Clients:
ip1:38371(recved=0,sent=0)
127.0.0.1:44202(recved=10,sent=10)
Latency min/avg/max: 0/1/4
Received: 10
Sent: 10
Connections: 1
Outstanding: 0
Zxid: 21
Mode: follower
Node count: 7
ClickHouse Keeper version: vxxx
Clients:
ip1:36929(recved=0,sent=0)
127.0.0.1:57856(recved=10,sent=10)
Latency min/avg/max: 0/21/135
Received: 10
Sent: 10
Connections: 1
Outstanding: 0
Zxid: 21
Mode: leader
Node count: 7
ClickHouse Keeper version: vxxx
Clients:
ip1:33281(recved=0,sent=0)
127.0.0.1:37236(recved=10,sent=10)
Latency min/avg/max: 0/22/135
Received: 10
Sent: 10
Connections: 1
Outstanding: 0
Zxid: 21
Mode: follower
Node count: 7
Все ок с кластером. Далее интересное: при кратковременном выключении одного из улов, кластер может собраться вновь. Эмпирическим путем было установлено, что для этого разница между наименьшим и наибольшим в кластере значениями last_log_idx не должна превышать примерно 30.
root@localhost:~# echo lgif | nc ip1 9181 ; echo ; echo lgif | nc ip2 9181 ; echo ; echo lgif | nc ip3 9181
first_log_idx 1
first_log_term 1
last_log_idx 88
last_log_term 1
last_committed_log_idx 88
leader_committed_log_idx 88
target_committed_log_idx 88
last_snapshot_idx 0
first_log_idx 1
first_log_term 1
last_log_idx 88
last_log_term 1
last_committed_log_idx 88
leader_committed_log_idx 88
target_committed_log_idx 88
last_snapshot_idx 0
first_log_idx 1
first_log_term 1
last_log_idx 88
last_log_term 1
last_committed_log_idx 88
leader_committed_log_idx 88
target_committed_log_idx 88
last_snapshot_idx 0
А вот записать в такой кластер не получится, даже создание таблицы оборачивается проблемами:
CREATE TABLE IF NOT EXISTS testdb.testtable ON CLUSTER test_c
(
id Int8,
) ENGINE = ReplicatedMergeTree()
ORDER BY id;
Но в ответ в веб-морде вернется такое исключение:
Code: 999. Coordination::Exception: Connection loss, path: /какой-то_путь/task_queue/ddl/query-0000000000: While executing DDLQueryStatus. (KEEPER_EXCEPTION) (version 23.3.13.1)
Но вид ошибки может быть и другим. Это зависит от того, к лидеру или к последователю отправляется запрос на создание таблицы в первую очередь.
{
"meta":
[
{
"name": "host",
"type": "String"
},
{
"name": "port",
"type": "UInt16"
},
{
"name": "status",
"type": "Int64"
},
{
"name": "error",
"type": "String"
},
{
"name": "num_hosts_remaining",
"type": "UInt64"
},
{
"name": "num_hosts_active",
"type": "UInt64"
}
],
"data":
[
["ip1", 9000, "999", "Code: 999. Coordination::Exception: Connection loss. (KEEPER_EXCEPTION) (version xxx)", "2", "2"],
["ip2", 9000, "999", "Code: 999. Coordination::Exception: Operation timeout, path: \/clickhouse\/tables. (KEEPER_EXCEPTION) (version xxx)", "1", "1"],
["ip3", 9000, "999", "Code: 999. Coordination::Exception: Operation timeout. (KEEPER_EXCEPTION) (version xxx)", "0", "0"]Code: 999. DB::Exception: There was an error on [ip3:port]: Code: 999. Coordination::Exception: Connection loss. (KEEPER_EXCEPTION) (version xxx). (KEEPER_EXCEPTION) (version xxx)
]
Ничего не понятно, но…
В какой- то момент, вводя случайные команды в терминале одного из серверов ,попадаю я на список сетевых интерфейсов и вижу, что подозрительно много пакетов отправились в DROP.
Казалось бы, из- за чего их так много?
Сделаю небольшую оговорку, а затем раскрою все карты. Ноды кластера представляли собой отдельные виртуальные машины на разных физических серверах.
Вот, что я увидел на виртуальной машине: два интерфейса с MTU 9000.
root@localhost:~# ifconfig | grep MTU
UP BROADCAST RUNNING MULTICAST MTU:9000 Metric:1
UP BROADCAST RUNNING MULTICAST MTU:9000 Metric:1
UP LOOPBACK RUNNING MTU:65536 Metric:1
А вот уже данные с физической машины, на которой хостилась эта виртуалка. Два последних интерфейса- те, которые прокинуты в виртуальную машину. Один из оставшихся сетевых интерфейсов соединяет физический сервер с остальными. На нём MTU равен 1500.
root@computer01:~# ifconfig | grep MTU
...
UP BROADCAST SLAVE MULTICAST MTU:1500 Metric:1
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
UP BROADCAST RUNNING MULTICAST MTU:9000 Metric:1
UP BROADCAST RUNNING MULTICAST MTU:9000 Metric:1
Вот она и отгадка загадки. Из- за неверного MTU, в ситуации, когда данных становилось слишком много, пакеты с данными Clickhouse Keeper отбрасывались на интерфейсе. В результате чего соединения нод отключались по таймауту, не дождавшись ответа.