Isolating clients with ZNodes

ZooKeeper is a dependency of many products supported by the Stackable Data Platform. To ensure that all products can use the same ZooKeeper cluster safely, it is important to isolate them which is done using ZNodes.

This guide shows you how to set up multiple ZNodes to use with different products from the Stackable Data Platform, using Kafka and Druid as an example. For an explanation of the ZNode concept, read the ZNodes concept page.

Prerequisites

To follow this guide, you should have

  • Access to a Kubernetes cluster

  • The Stackable Operator for Apache ZooKeeper installed in said cluster

  • A ZookeeperCluster already deployed on the cluster

If you have not yet set up the Operator and ZookeeperCluster, follow the getting started guide.

Steps

This guide assumes the ZookeeperCluster is called my-zookeeper and is running in a data namespace.

Setting up the ZNodes

To set up a Kafka and Druid instance to use the ZookeeperCluster, two ZNodes are required, one for each product. This guide assumes the Kafka instance is running in the same namespace as the ZooKeeper, while the Druid instance is running in its own namespace called druid-ns.

First, the Druid ZNode:

---
apiVersion: zookeeper.stackable.tech/v1alpha1
kind: ZookeeperZnode
metadata:
  name: druid-znode    (1)
  namespace: druid-ns  (2)
spec:
  clusterRef:          (3)
    name: my-zookeeper
    namespace: data
1 The name of the Druid ZNode.
2 The namespace where the ZNode should be created. This should be the same as the namespace of the product or client that wants to use the ZNode.
3 The ZooKeeper cluster reference. Since ZooKeeper is running in a different namespace, both the cluster name and namespace need to be given.

And the Kafka ZNode:

---
apiVersion: zookeeper.stackable.tech/v1alpha1
kind: ZookeeperZnode
metadata:
  name: kafka-znode  (1)
  namespace: data    (2)
spec:
  clusterRef:        (3)
    name: my-zookeeper
1 The name of the Kafka ZNode.
2 The namespace where the ZNode should be created. Since Kafka is running in the same namespace as ZooKeeper, this is the namespace of my-zookeeper.
3 The ZooKeeper cluster reference. The namespace is omitted here because the ZooKeeper is in the same namespace as the ZNode object.

The Stackable Operator for ZooKeeper watches for ZookeeperZnode objects. If one is found it creates the ZNode inside the ZooKeeper cluster and also creates a discovery ConfigMap in the same namespace as the ZookeeperZnode with the same name as the ZookeeperZnode.

In this example, two ConfigMaps are created:

  • The Druid ZNode discovery ConfigMap druid-znode in the druid-ns namespace

  • The Kafka ZNode discovery ConfigMap kafka-znode in the data namespace

Connecting the products to the ZNodes

The ConfigMaps with the name and namespaces as given above look similar to this:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: ...  (1)
  namespace: ...
data:
  ZOOKEEPER: pod-1:2181,pod-2:2181,pod-3:2181/{path} (2)
1 Name and namespaces as specified above
2 $PATH is a unique and unpredictable path that is generated by the operator

This ConfigMap can then be mounted into other Pods and the ZOOKEEPER key can be used to connect to the ZooKeeper instance and the correct ZNode.

All products that need a ZNode can be configured with a zookeeperConfigMapName property. As the name implies, this property references the discovery ConfigMap for the requested ZNode.

For Kafka:

---
apiVersion: kafka.stackable.tech/v1alpha1
kind: DruidCluster
metadata:
  name: my-druid
  namespace: druid-ns
spec:
  zookeeperConfigMapName: kafka-znode
  ...

And for Druid:

---
apiVersion: kafka.stackable.tech/v1alpha1
kind: KafkaCluster
metadata:
  name: my-kafka
  namespace: data
spec:
  zookeeperConfigMapName: kafka-znode
  ...

The Stackable Operators for Kafka and Druid use the discovery ConfigMaps to connect Kafka and Druid Pods with different ZNodes in a shared ZooKeeper cluster.

What’s next

You can find out more about the discovery ConfigMap Discovery Profiles and the ZNodes in the concepts documentation.

Restoring from backups

For security reasons, a unique ZNode path is generated every time the same ZookeeperZnode object is recreated, even if it has the same name.

If a ZookeeperZnode needs to be associated with an existing ZNode path, the field status.znodePath can be set to the desired path. Note that since this is a subfield of status, it must explicitly be updated on the status subresource, and requires RBAC permissions to replace the zookeeperznodes/status resource. For example:

kubectl get zookeeperznode/test-znode -o json -n $NAMESPACE \
        | jq '.status.znodePath = "/znode-override"' \
        | kubectl replace -f- --subresource=status
The auto-generated ZNode will still be kept, and should be cleaned up by an administrator.