Пару недель назад я написал сообщение в блоге, объясняющее, как я создал причинный кластер Neo4j с использованием докер-контейнеров напрямую, и для моего следующего домашнего проекта я хотел использовать Kubernetes в качестве уровня оркестровки, чтобы я мог декларативно изменять количество серверов в мой кластер.
Я никогда не использовал Kubernetes прежде, но я видел презентацию, показывающую, как использовать его для создания кластера Elastic на встрече GDG Cloud пару месяцев назад .
В этой презентации я познакомился с идеей PetSet, которая представляет собой абстракцию, представленную Kubernetes, которая позволяет нам управлять набором контейнеров (контейнеров), которые имеют фиксированную идентичность. Документация объясняет это лучше:
PetSet гарантирует, что определенное количество «домашних животных» с уникальными личностями работает в любой момент времени. Личность домашнего животного состоит из:
- стабильное имя хоста, доступное в DNS
- порядковый номер
- стабильное хранилище: связано с порядковым номером и именем хоста
В моем случае мне нужно иметь стабильное имя хоста, поскольку каждому члену кластера Neo4j предоставляется список других членов кластера, с помощью которых он может создать новый кластер или присоединиться к уже существующему. Это первый вариант использования, описанный в документации:
PetSet также помогает в решении 2 наиболее распространенных проблем, возникающих при управлении такими кластерными приложениями:
- открытие пиров для кворума
- запуск / демонтаж заказа
Поэтому первое, что нам нужно сделать, — это создать некоторое стабильное хранилище для использования нашими модулями.
Мы создадим кластер из 3 членов, поэтому нам нужно создать один PersistentVolume для каждого из них. Следующий скрипт выполняет эту работу:
volumes.sh
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
for i in $(seq 0 2 ); do cat <<EOF | kubectl create -f - kind: PersistentVolume apiVersion: v1 metadata: name: pv${i} labels: type: local app: neo4j spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce hostPath: path: "/tmp/${i}" EOF cat <<EOF | kubectl create -f - kind: PersistentVolumeClaim apiVersion: v1 metadata: name: datadir-neo4j-${i} labels: app: neo4j spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi EOF done; |
Если мы запустим этот скрипт, он создаст 3 тома, которые мы можем увидеть, выполнив следующую команду:
1
2
3
4
5
|
$ kubectl get pv NAME CAPACITY ACCESSMODES STATUS CLAIM REASON AGE pv0 1Gi RWO Bound default /datadir-neo4j-0 7s pv1 1Gi RWO Bound default /datadir-neo4j-1 7s pv2 1Gi RWO Bound default /datadir-neo4j-2 7s |
1
2
3
4
5
|
$ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESSMODES AGE datadir-neo4j-0 Bound pv0 1Gi RWO 26s datadir-neo4j-1 Bound pv1 1Gi RWO 26s datadir-neo4j-2 Bound pv2 1Gi RWO 25s |
Далее нам нужно создать шаблон PetSet. После многих итераций я получил следующее:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# Headless service to provide DNS lookup apiVersion: v1 kind: Service metadata: labels: app: neo4j name: neo4j spec: clusterIP: None ports: - port: 7474 selector: app: neo4j ---- # new API name apiVersion: "apps/v1alpha1" kind: PetSet metadata: name: neo4j spec: serviceName: neo4j replicas: 3 template: metadata: annotations: pod.alpha.kubernetes.io/initialized: "true" pod.beta.kubernetes.io/init-containers: '[ { "name" : "install" , "image" : "gcr.io/google_containers/busybox:1.24" , "command" : [ "/bin/sh" , "-c" , "echo \" unsupported.dbms.edition=enterprise\n dbms.mode=CORE\n dbms.connectors.default_advertised_address=$HOSTNAME.neo4j. default .svc.cluster.local\n dbms.connectors.default_listen_address= 0.0 . 0.0 \n dbms.connector.bolt.type=BOLT\n dbms.connector.bolt.enabled= true \n dbms.connector.bolt.listen_address= 0.0 . 0.0 : 7687 \n dbms.connector.http.type=HTTP\n dbms.connector.http.enabled= true \n dbms.connector.http.listen_address= 0.0 . 0.0 : 7474 \n causal_clustering.raft_messages_log_enable= true \n causal_clustering.initial_discovery_members=neo4j- 0 .neo4j. default .svc.cluster.local: 5000 ,neo4j- 1 .neo4j. default .svc.cluster.local: 5000 ,neo4j- 2 .neo4j. default .svc.cluster.local: 5000 \n causal_clustering.leader_election_timeout=2s\n \ " > /work-dir/neo4j.conf" ], "volumeMounts" : [ { "name" : "confdir" , "mountPath" : "/work-dir" } ] } ]' labels: app: neo4j spec: containers: - name: neo4j image: "neo4j/neo4j-experimental:3.1.0-M13-beta3-enterprise" imagePullPolicy: Always ports: - containerPort: 5000 name: discovery - containerPort: 6000 name: tx - containerPort: 7000 name: raft - containerPort: 7474 name: browser - containerPort: 7687 name: bolt securityContext: privileged: true volumeMounts: - name: datadir mountPath: /data - name: confdir mountPath: /conf volumes: - name: confdir volumeClaimTemplates: - metadata: name: datadir annotations: volume.alpha.kubernetes.io/storage- class : anything spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi |
Главное, с чем у меня были проблемы, — это заставить членов кластера разговаривать друг с другом. Конфигурация докера по умолчанию использует имена хостов, но я обнаружил, что модули не могли связываться друг с другом, пока я не указал полное доменное имя в файле конфигурации. Мы можем запустить следующую команду для создания PetSet:
1
2
3
|
$ kubectl create -f neo4j.yaml service "neo4j" created petset "neo4j" created |
Мы можем проверить работоспособность модулей, выполнив следующую команду:
1
2
3
4
5
|
$ kubectl get pods NAME READY STATUS RESTARTS AGE neo4j-0 1 /1 Running 0 2m neo4j-1 1 /1 Running 0 14s neo4j-2 1 /1 Running 0 10s |
И мы можем привязать лог-файлы neo4j следующим образом:
1
2
3
4
5
6
7
8
9
|
$ kubectl logs neo4j-0 Starting Neo4j. 2016-11-25 16:39:50.333+0000 INFO Starting... 2016-11-25 16:39:51.723+0000 INFO Bolt enabled on 0.0.0.0:7687. 2016-11-25 16:39:51.733+0000 INFO Initiating metrics... 2016-11-25 16:39:51.911+0000 INFO Waiting for other members to join cluster before continuing... 2016-11-25 16:40:12.074+0000 INFO Started. 2016-11-25 16:40:12.428+0000 INFO Mounted REST API at: /db/manage 2016-11-25 16:40:13.350+0000 INFO Remote interface available at http: //neo4j-0 .neo4j.default.svc.cluster. local :7474/ |
1
2
3
4
5
6
7
8
9
|
$ kubectl logs neo4j-1 Starting Neo4j. 2016-11-25 16:39:53.846+0000 INFO Starting... 2016-11-25 16:39:56.212+0000 INFO Bolt enabled on 0.0.0.0:7687. 2016-11-25 16:39:56.225+0000 INFO Initiating metrics... 2016-11-25 16:39:56.341+0000 INFO Waiting for other members to join cluster before continuing... 2016-11-25 16:40:16.623+0000 INFO Started. 2016-11-25 16:40:16.951+0000 INFO Mounted REST API at: /db/manage 2016-11-25 16:40:17.607+0000 INFO Remote interface available at http: //neo4j-1 .neo4j.default.svc.cluster. local :7474/ |
1
2
3
4
5
6
7
8
9
|
$ kubectl logs neo4j-2 Starting Neo4j. 2016-11-25 16:39:57.828+0000 INFO Starting... 2016-11-25 16:39:59.166+0000 INFO Bolt enabled on 0.0.0.0:7687. 2016-11-25 16:39:59.176+0000 INFO Initiating metrics... 2016-11-25 16:39:59.329+0000 INFO Waiting for other members to join cluster before continuing... 2016-11-25 16:40:19.216+0000 INFO Started. 2016-11-25 16:40:19.675+0000 INFO Mounted REST API at: /db/manage 2016-11-25 16:40:21.029+0000 INFO Remote interface available at http: //neo4j-2 .neo4j.default.svc.cluster. local :7474/ |
Я хотел войти на серверы из браузера хост-машины, чтобы настроить переадресацию портов для каждого из серверов:
1
|
$ kubectl port-forward neo4j-0 7474:7474 7687:7687 |
Затем мы можем получить обзор кластера, выполнив следующую процедуру:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
CALL dbms.cluster.overview() ╒════════════════════════════════════╤═════════════════════════════════════════════════════╤════════╕ │ id │addresses │role │ ╞════════════════════════════════════╪═════════════════════════════════════════════════════╪════════╡ │81d8e5e2-02db-4414-85de-a7025b346e84│[bolt: //neo4j-0 .neo4j.default.svc.cluster. local :7687,│LEADER │ │ │ http: //neo4j-0 .neo4j.default.svc.cluster. local :7474]│ │ ├────────────────────────────────────┼─────────────────────────────────────────────────────┼────────┤ │347b7517-7ca0-4b92-b9f0-9249d46b2ad3│[bolt: //neo4j-1 .neo4j.default.svc.cluster. local :7687,│FOLLOWER│ │ │ http: //neo4j-1 .neo4j.default.svc.cluster. local :7474]│ │ ├────────────────────────────────────┼─────────────────────────────────────────────────────┼────────┤ │a5ec1335-91ce-4358-910b-8af9086c2969│[bolt: //neo4j-2 .neo4j.default.svc.cluster. local :7687,│FOLLOWER│ │ │ http: //neo4j-2 .neo4j.default.svc.cluster. local :7474]│ │ └────────────────────────────────────┴─────────────────────────────────────────────────────┴────────┘ |
Все идет нормально. Что если мы хотим иметь 5 серверов в кластере вместо 3? Мы можем запустить следующую команду, чтобы увеличить размер нашей реплики:
1
2
|
$ kubectl patch petset neo4j -p '{"spec":{"replicas":5}}' "neo4j" patched |
Давайте снова запустим эту процедуру:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
CALL dbms.cluster.overview() ╒════════════════════════════════════╤═════════════════════════════════════════════════════╤════════╕ │ id │addresses │role │ ╞════════════════════════════════════╪═════════════════════════════════════════════════════╪════════╡ │81d8e5e2-02db-4414-85de-a7025b346e84│[bolt: //neo4j-0 .neo4j.default.svc.cluster. local :7687,│LEADER │ │ │ http: //neo4j-0 .neo4j.default.svc.cluster. local :7474]│ │ ├────────────────────────────────────┼─────────────────────────────────────────────────────┼────────┤ │347b7517-7ca0-4b92-b9f0-9249d46b2ad3│[bolt: //neo4j-1 .neo4j.default.svc.cluster. local :7687,│FOLLOWER│ │ │ http: //neo4j-1 .neo4j.default.svc.cluster. local :7474]│ │ ├────────────────────────────────────┼─────────────────────────────────────────────────────┼────────┤ │a5ec1335-91ce-4358-910b-8af9086c2969│[bolt: //neo4j-2 .neo4j.default.svc.cluster. local :7687,│FOLLOWER│ │ │ http: //neo4j-2 .neo4j.default.svc.cluster. local :7474]│ │ ├────────────────────────────────────┼─────────────────────────────────────────────────────┼────────┤ │28613d06-d4c5-461c-b277-ddb3f05e5647│[bolt: //neo4j-3 .neo4j.default.svc.cluster. local :7687,│FOLLOWER│ │ │ http: //neo4j-3 .neo4j.default.svc.cluster. local :7474]│ │ ├────────────────────────────────────┼─────────────────────────────────────────────────────┼────────┤ │2eaa0058-a4f3-4f07-9f22-d310562ad1ec│[bolt: //neo4j-4 .neo4j.default.svc.cluster. local :7687,│FOLLOWER│ │ │ http: //neo4j-4 .neo4j.default.svc.cluster. local :7474]│ │ └────────────────────────────────────┴─────────────────────────────────────────────────────┴────────┘ |
Ухоженная! И так же легко вернуться к 3:
1
2
|
$ kubectl patch petset neo4j -p '{"spec":{"replicas":3}}' "neo4j" patched |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
CALL dbms.cluster.overview() ╒════════════════════════════════════╤══════════════════════════════════════════════════════╤════════╕ │ id │addresses │role │ ╞════════════════════════════════════╪══════════════════════════════════════════════════════╪════════╡ │81d8e5e2-02db-4414-85de-a7025b346e84│[bolt: //neo4j-0 .neo4j.default.svc.cluster. local :7687, │LEADER │ │ │http: //neo4j-0 .neo4j.default.svc.cluster. local :7474] │ │ ├────────────────────────────────────┼──────────────────────────────────────────────────────┼────────┤ │347b7517-7ca0-4b92-b9f0-9249d46b2ad3│[bolt: //neo4j-1 .neo4j.default.svc.cluster. local :7687, │FOLLOWER│ │ │http: //neo4j-1 .neo4j.default.svc.cluster. local :7474] │ │ ├────────────────────────────────────┼──────────────────────────────────────────────────────┼────────┤ │a5ec1335-91ce-4358-910b-8af9086c2969│[bolt: //neo4j-2 .neo4j.default.svc.cluster. local :7687, │FOLLOWER│ │ │http: //neo4j-2 .neo4j.default.svc.cluster. local :7474] │ │ └────────────────────────────────────┴──────────────────────────────────────────────────────┴────────┘ |
Далее мне нужно посмотреть, как мы можем добавить реплики чтения в кластер. Они не участвуют в алгоритме членства / кворума, поэтому я думаю, что смогу использовать для них более распространенную архитектуру ReplicationController / Pod.
Если вы хотите поиграть с этим, код доступен в виде сути . Я использую библиотеку minikube для всех своих экспериментов, но я надеюсь, что скоро попробую сделать это на GCE или AWS.
Ссылка: | Kubernetes: раскручивание кластера причинности Neo4j 3.1 от нашего партнера по JCG Марка Нидхэма в блоге Марка Нидхэма . |