Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit 9f70794

Browse files
author
Hasan Çelik
authored
Add NODE_AWARE metadata support (#278)
* Add NODE_AWARE metadata support With 3.12.11 IMDG release, new metadata introduced to define partition group based on Kubernetes node that Hazelcast cluster runs on.The discovery plugin should pass name of the node to the core SPI via using discoverLocalMetadata method like zone info. This method passes both zone and node name metadata. User can not enable tow group types at the same time so cluster will decide which metadata will be used based on cluster config.
1 parent fdd657f commit 9f70794

File tree

4 files changed

+108
-13
lines changed

4 files changed

+108
-13
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ There are 2 properties to configure the plugin:
186186
**Note**: In this README, only YAML configurations are presented, however you can achieve exactly the same effect using
187187
XML or Java-based configurations.
188188

189+
## High Availability
190+
189191
### Zone Aware
190192

191193
When using `ZONE_AWARE` configuration, backups are created in the other availability zone. This feature is available only for the Kubernetes API mode.
@@ -224,6 +226,42 @@ env:
224226
fieldPath: metadata.name
225227
```
226228

229+
### Node Aware
230+
231+
When using `NODE_AWARE` configuration, backups are created in the other Kubernetes nodes. This feature is available only for the Kubernetes API mode.
232+
233+
**Note**: Your Kubernetes cluster must orchestrate Hazelcast Member PODs equally between the nodes, otherwise Node Aware feature may not work correctly.
234+
235+
#### YAML Configuration
236+
237+
```yaml
238+
partition-group:
239+
enabled: true
240+
group-type: NODE_AWARE
241+
```
242+
243+
#### Java-based Configuration
244+
245+
```java
246+
config.getPartitionGroupConfig()
247+
.setEnabled(true)
248+
.setGroupType(MemberGroupType.NODE_AWARE);
249+
```
250+
251+
Note the following aspects of `NODE_AWARE`:
252+
* Retrieving name of the node uses Kubernetes API, so RBAC must be configured as described [here](#granting-permissions-to-use-kubernetes-api)
253+
* `NODE_AWARE` feature works correctly when Hazelcast members are distributed equally in all nodes, so your Kubernetes cluster must orchestrate PODs equally.
254+
255+
Note also that retrieving name of the node assumes that your container's hostname is the same as POD Name, which is almost always true. If you happen to change your hostname in the container, then please define the following environment variable:
256+
257+
```yaml
258+
env:
259+
- name: POD_NAME
260+
valueFrom:
261+
fieldRef:
262+
fieldPath: metadata.name
263+
```
264+
227265
## Hazelcast Client Configuration
228266

229267
Depending on whether your Hazelcast Client runs inside or outside the Kubernetes cluster, its configuration looks different.

src/main/java/com/hazelcast/kubernetes/HazelcastKubernetesDiscoveryStrategy.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public void start() {
7070
public Map<String, Object> discoverLocalMetadata() {
7171
if (memberMetadata.isEmpty()) {
7272
memberMetadata.put(PartitionGroupMetaData.PARTITION_GROUP_ZONE, discoverZone());
73+
memberMetadata.put("hazelcast.partition.group.node", discoverNodeName());
7374
}
7475
return memberMetadata;
7576
}
@@ -82,14 +83,7 @@ public Map<String, Object> discoverLocalMetadata() {
8283
private String discoverZone() {
8384
if (DiscoveryMode.KUBERNETES_API.equals(config.getMode())) {
8485
try {
85-
String podName = System.getenv("POD_NAME");
86-
if (podName == null) {
87-
podName = System.getenv("HOSTNAME");
88-
}
89-
if (podName == null) {
90-
podName = InetAddress.getLocalHost().getHostName();
91-
}
92-
String zone = client.zone(podName);
86+
String zone = client.zone(podName());
9387
if (zone != null) {
9488
getLogger().info(String.format("Kubernetes plugin discovered availability zone: %s", zone));
9589
return zone;
@@ -103,6 +97,39 @@ private String discoverZone() {
10397
return "unknown";
10498
}
10599

100+
/**
101+
* Discovers the name of the node which the current Hazelcast member pod is running on.
102+
* <p>
103+
* Note: NODE_AWARE is available only for the Kubernetes API Mode.
104+
*/
105+
private String discoverNodeName() {
106+
if (DiscoveryMode.KUBERNETES_API.equals(config.getMode())) {
107+
try {
108+
String nodeName = client.nodeName(podName());
109+
if (nodeName != null) {
110+
getLogger().info(String.format("Kubernetes plugin discovered node name: %s", nodeName));
111+
return nodeName;
112+
}
113+
} catch (Exception e) {
114+
// only log the exception and the message, Hazelcast should still start
115+
getLogger().finest(e);
116+
}
117+
getLogger().warning("Cannot fetch name of the node, NODE_AWARE feature is disabled");
118+
}
119+
return "unknown";
120+
}
121+
122+
private String podName() throws UnknownHostException {
123+
String podName = System.getenv("POD_NAME");
124+
if (podName == null) {
125+
podName = System.getenv("HOSTNAME");
126+
}
127+
if (podName == null) {
128+
podName = InetAddress.getLocalHost().getHostName();
129+
}
130+
return podName;
131+
}
132+
106133
@Override
107134
public Iterable<DiscoveryNode> discoverNodes() {
108135
return endpointResolver.resolve();

src/main/java/com/hazelcast/kubernetes/KubernetesClient.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ List<Endpoint> endpointsByPodLabel(String podLabel, String podLabelValue) {
137137
}
138138
}
139139

140-
141140
/**
142141
* Retrieves zone name for the specified {@code namespace} and the given {@code podName}.
143142
* <p>
@@ -149,13 +148,22 @@ List<Endpoint> endpointsByPodLabel(String podLabel, String podLabelValue) {
149148
* @see <a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11">Kubernetes Endpoint API</a>
150149
*/
151150
String zone(String podName) {
152-
String podUrlString = String.format("%s/api/v1/namespaces/%s/pods/%s", kubernetesMaster, namespace, podName);
153-
String nodeName = extractNodeName(callGet(podUrlString));
154-
155-
String nodeUrlString = String.format("%s/api/v1/nodes/%s", kubernetesMaster, nodeName);
151+
String nodeUrlString = String.format("%s/api/v1/nodes/%s", kubernetesMaster, nodeName(podName));
156152
return extractZone(callGet(nodeUrlString));
157153
}
158154

155+
/**
156+
* Retrieves node name for the specified {@code namespace} and the given {@code podName}.
157+
*
158+
* @param podName POD name
159+
* @return Node name
160+
* @see <a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11">Kubernetes Endpoint API</a>
161+
*/
162+
String nodeName(String podName) {
163+
String podUrlString = String.format("%s/api/v1/namespaces/%s/pods/%s", kubernetesMaster, namespace, podName);
164+
return extractNodeName(callGet(podUrlString));
165+
}
166+
159167
private static List<Endpoint> parsePodsList(JsonObject podsListJson) {
160168
List<Endpoint> addresses = new ArrayList<Endpoint>();
161169

src/test/java/com/hazelcast/kubernetes/KubernetesClientTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,28 @@ public void zone() {
361361
assertEquals("us-central1-a", zone);
362362
}
363363

364+
365+
@Test
366+
public void nodeName() {
367+
// given
368+
String podName = "pod-name";
369+
370+
//language=JSON
371+
String podResponse = "{\n"
372+
+ " \"kind\": \"Pod\",\n"
373+
+ " \"spec\": {\n"
374+
+ " \"nodeName\": \"kubernetes-node-f0bbd602-f7cw\"\n"
375+
+ " }\n"
376+
+ "}";
377+
stub(String.format("/api/v1/namespaces/%s/pods/%s", NAMESPACE, podName), podResponse);
378+
379+
// when
380+
String nodeName = kubernetesClient.nodeName(podName);
381+
382+
// then
383+
assertEquals("kubernetes-node-f0bbd602-f7cw", nodeName);
384+
}
385+
364386
@Test
365387
public void endpointsByNamespaceWithLoadBalancerPublicIp() {
366388
// given

0 commit comments

Comments
 (0)