Статьи

Kubernetes: имитация сетевого раздела

Пару недель назад я написал пост, объясняющий, как создать причинный кластер Neo4j с использованием Kubernetes и… я хотел разработать, как имитировать сетевой раздел, который бы поставил лидера на сторону меньшинства и вынудил провести выборы.

Мы сделали это с помощью нашего внутреннего инструментария в AWS с помощью команды iptables, но, к сожалению, этого нет в моем контейнере, который имеет только утилиты, предоставляемые BusyBox .

К счастью, одним из них является команда маршрута, которая позволит нам достичь того же.

Напомним, у меня есть 3 Neo4j и работает:

1
2
3
4
5
$ kubectl get pods
NAME      READY     STATUS    RESTARTS   AGE
neo4j-0   1/1       Running   0          6h
neo4j-1   1/1       Running   0          6h
neo4j-2   1/1       Running   0          6h

И мы можем проверить, доступна ли команда route :

1
2
$ kubectl exec neo4j-0 -- ls -alh /sbin/route
lrwxrwxrwx    1 root     root          12 Oct 18 18:58 /sbin/route -> /bin/busybox

Давайте посмотрим, какую роль в настоящее время играет каждый сервер:

1
2
3
4
5
$ kubectl exec neo4j-0 -- bin/cypher-shell "CALL dbms.cluster.role()"
role
"FOLLOWER"
  
Bye!
1
2
3
4
5
$ kubectl exec neo4j-1 -- bin/cypher-shell "CALL dbms.cluster.role()"
role
"FOLLOWER"
  
Bye!
1
2
3
4
5
$ kubectl exec neo4j-2 -- bin/cypher-shell "CALL dbms.cluster.role()"
role
"LEADER"
  
Bye!

Немного в стороне: я могу вызвать cypher-shell без имени пользователя и пароля, потому что я отключил авторизацию, поместив в файл conf / neo4j.conf следующее :

1
dbms.connector.bolt.enabled=true

Вернемся к разделу сети … нам нужно отделить neo4j-2 от двух других серверов, что мы можем сделать, выполнив следующие команды:

1
2
3
4
$ kubectl exec neo4j-2 -- route add -host neo4j-0.neo4j.default.svc.cluster.local reject && \
  kubectl exec neo4j-2 -- route add -host neo4j-1.neo4j.default.svc.cluster.local reject && \
  kubectl exec neo4j-0 -- route add -host neo4j-2.neo4j.default.svc.cluster.local reject && \
  kubectl exec neo4j-1 -- route add -host neo4j-2.neo4j.default.svc.cluster.local reject

Если мы посмотрим журналы neo4j-2, то увидим, что он вышел из строя после отключения от двух других серверов:

1
2
3
4
$ kubectl exec neo4j-2 -- cat logs/debug.log
...
2016-12-04 11:30:10.186+0000 INFO  [o.n.c.c.c.RaftMachine] Moving to FOLLOWER state after not receiving heartbeat responses in this election timeout period. Heartbeats received: []
...

Кто стал лидером?

1
2
3
4
5
$ kubectl exec neo4j-0 -- bin/cypher-shell "CALL dbms.cluster.role()"
role
"LEADER"
  
Bye!
1
2
3
4
5
$ kubectl exec neo4j-1 -- bin/cypher-shell "CALL dbms.cluster.role()"
role
"FOLLOWER"
  
Bye!
1
2
3
4
5
$ kubectl exec neo4j-2 -- bin/cypher-shell "CALL dbms.cluster.role()"
role
"FOLLOWER"
  
Bye!

Похоже, neo4j-0! Давайте поместим некоторые данные в базу данных:

1
2
3
4
$ kubectl exec neo4j-0 -- bin/cypher-shell "CREATE (:Person {name: 'Mark'})"
Added 1 nodes, Set 1 properties, Added 1 labels
  
Bye!

Давайте проверим, добрался ли этот узел до двух других серверов. Мы ожидаем, что он будет на neo4j-1, но не на neo4j-2:

1
2
3
4
5
$ kubectl exec neo4j-1 -- bin/cypher-shell "MATCH (p:Person) RETURN p"
p
(:Person {name: "Mark"})
  
Bye!
1
2
3
4
$ kubectl exec neo4j-2 -- bin/cypher-shell "MATCH (p:Person) RETURN p"
  
  
Bye!

На neo4j-2 мы неоднократно будем видеть эти типы записей в журнале, поскольку время его ожидания срабатывает, но не получает никаких ответов на запросы голосования, которые оно отправляет:

1
2
3
4
5
$ kubectl exec neo4j-2 -- cat logs/debug.log
...
2016-12-04 11:32:56.735+0000 INFO  [o.n.c.c.c.RaftMachine] Election timeout triggered
2016-12-04 11:32:56.736+0000 INFO  [o.n.c.c.c.RaftMachine] Election started with vote request: Vote.Request from MemberId{ca9b954c} {term=11521, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467} and members: [MemberId{484178c4}, MemberId{0acdb8dd}, MemberId{ca9b954c}]
...

Мы можем увидеть эти запросы голосования, посмотрев raft-messages.log, который можно включить, установив следующее свойство в conf / neo4j.conf :

1
causal_clustering.raft_messages_log_enable=true
01
02
03
04
05
06
07
08
09
10
11
$ kubectl exec neo4j-2 -- cat logs/raft-messages.log
...
11:33:42.101 -->MemberId{484178c4}: Request: Vote.Request from MemberId{ca9b954c} {term=11537, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467}
11:33:42.102 -->MemberId{0acdb8dd}: Request: Vote.Request from MemberId{ca9b954c} {term=11537, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467}
  
11:33:45.432 -->MemberId{484178c4}: Request: Vote.Request from MemberId{ca9b954c} {term=11538, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467}
11:33:45.433 -->MemberId{0acdb8dd}: Request: Vote.Request from MemberId{ca9b954c} {term=11538, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467}
  
11:33:48.362 -->MemberId{484178c4}: Request: Vote.Request from MemberId{ca9b954c} {term=11539, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467}
11:33:48.362 -->MemberId{0acdb8dd}: Request: Vote.Request from MemberId{ca9b954c} {term=11539, candidate=MemberId{ca9b954c}, lastAppended=68, lastLogTerm=11467}
...

Чтобы «вылечить» сетевой раздел, нам просто нужно удалить все команды, которые мы запускали ранее:

1
2
3
4
$ kubectl exec neo4j-2 -- route delete neo4j-0.neo4j.default.svc.cluster.local reject && \
  kubectl exec neo4j-2 -- route delete neo4j-1.neo4j.default.svc.cluster.local reject && \
  kubectl exec neo4j-0 -- route delete neo4j-2.neo4j.default.svc.cluster.local reject && \
  kubectl exec neo4j-1 -- route delete neo4j-2.neo4j.default.svc.cluster.local reject

Теперь давайте проверим, что у neo4j-2 теперь есть узел, который мы создали ранее:

1
2
3
4
5
$ kubectl exec neo4j-2 -- bin/cypher-shell "MATCH (p:Person) RETURN p"
p
(:Person {name: "Mark"})
  
Bye!

На этом пока все!

Ссылка: Kubernetes: имитация сетевого раздела от нашего партнера по JCG Марка Нидхэма в блоге Марка Нидхэма .