Пару недель назад я написал сообщение в блоге, объясняющее, как я создал причинный кластер 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: PersistentVolumeapiVersion: v1metadata: name: pv${i} labels: type: local app: neo4jspec: capacity: storage: 1Gi accessModes: - ReadWriteOnce hostPath: path: "/tmp/${i}"EOF cat <<EOF | kubectl create -f -kind: PersistentVolumeClaimapiVersion: v1metadata: name: datadir-neo4j-${i} labels: app: neo4jspec: accessModes: - ReadWriteOnce resources: requests: storage: 1GiEOFdone; |
Если мы запустим этот скрипт, он создаст 3 тома, которые мы можем увидеть, выполнив следующую команду:
|
1
2
3
4
5
|
$ kubectl get pvNAME CAPACITY ACCESSMODES STATUS CLAIM REASON AGEpv0 1Gi RWO Bound default/datadir-neo4j-0 7spv1 1Gi RWO Bound default/datadir-neo4j-1 7spv2 1Gi RWO Bound default/datadir-neo4j-2 7s |
|
1
2
3
4
5
|
$ kubectl get pvcNAME STATUS VOLUME CAPACITY ACCESSMODES AGEdatadir-neo4j-0 Bound pv0 1Gi RWO 26sdatadir-neo4j-1 Bound pv1 1Gi RWO 26sdatadir-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 lookupapiVersion: v1kind: Servicemetadata: labels: app: neo4j name: neo4jspec: clusterIP: None ports: - port: 7474 selector: app: neo4j----# new API nameapiVersion: "apps/v1alpha1"kind: PetSetmetadata: name: neo4jspec: 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" createdpetset "neo4j" created |
Мы можем проверить работоспособность модулей, выполнив следующую команду:
|
1
2
3
4
5
|
$ kubectl get podsNAME READY STATUS RESTARTS AGEneo4j-0 1/1 Running 0 2mneo4j-1 1/1 Running 0 14sneo4j-2 1/1 Running 0 10s |
И мы можем привязать лог-файлы neo4j следующим образом:
|
1
2
3
4
5
6
7
8
9
|
$ kubectl logs neo4j-0Starting 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/manage2016-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-1Starting 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/manage2016-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-2Starting 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/manage2016-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 Марка Нидхэма в блоге Марка Нидхэма . |