Deployment guide

Apache ZooKeeper 3.9

Overview

Apache ZooKeeper is the industry-standard distributed coordination service. It provides a service registry, leader election, distributed locks and a configuration store. It is also the backing store for systems like Kafka (legacy mode), HBase, NiFi, Druid and SolrCloud.

This image installs ZooKeeper 3.9.5 from the official Apache CDN with SHA-512 verification, alongside OpenJDK 17. It runs as a single-node standalone ensemble. Per-VM SecureAnyCloud credentials are generated at first boot. They are used for ZooKeeper's native digest ACL scheme and for the ZooNavigator 2.0.0 web UI, which is bundled in a Docker container behind an nginx HTTP basic auth wall on TCP 80.

What you get:

  • Apache ZooKeeper 3.9.5 at /opt/zookeeper
  • OpenJDK 17 JRE headless from Ubuntu 24.04 noble main
  • Standalone single-node ensemble: zookeeper.service (one JVM, with both server and client roles)
  • ZooKeeper client port 2181 (TCP) and admin server 127.0.0.1:8080 (loopback only)
  • Four-letter-word (4LW) commands enabled (stat, ruok, conf, mntr, srvr and others)
  • ZooNavigator 2.0.0 Docker container bound to 127.0.0.1:9000
  • nginx reverse proxy on TCP 80 with HTTP basic auth. A single per-VM SecureAnyCloud credential covers nginx auth, ZooNavigator container auth and ZooKeeper digest ACLs.
  • Heap tuned for Standard_B2s (256m to 512m JVM)
  • Credentials at /stage/scripts/zookeeper-credentials.log
  • 24/7 SecureAnyCloud support

Prerequisites

  • An active Azure subscription, an SSH key, and a VNet and subnet.
  • Standard_B2s (4 GB RAM) is suitable for dev, test, single-tenant production coordination, an embedded service registry and PoC workloads.
  • NSG inbound rules that allow 22/tcp from your management CIDR, 2181/tcp from your ZooKeeper client CIDR (the application tier), and 80/tcp from your operator CIDR (ZooNavigator web UI).

The security model does not use ZooKeeper's sessionRequireClientSASLAuth=true, because Java 17+ has deprecated DIGEST-MD5 SASL. Instead, the image relies on three layers:

  • NSG perimeter as the primary access control on :2181
  • ZooKeeper digest ACLs for per-znode authorization once a client has connected
  • nginx HTTP basic auth on :80 for the ZooNavigator web UI

Restrict NSG :2181 inbound to your application tier IPs.

Step 1-3: Deploy and connect over SSH

Deploy the VM from the Azure Marketplace or the Azure CLI, then connect over SSH:

ssh azureuser@<vm-ip>
4

Service status and version

Check the service and the ZooKeeper version:

sudo systemctl is-active zookeeper.service zoonavigator.service nginx.service /opt/zookeeper/bin/zkServer.sh version 2>&1 | tail -1
5

Read per-VM credentials

Read the credentials file:

sudo cat /stage/scripts/zookeeper-credentials.log

Note ZK_CONNECT, ZK_ADMIN_USER and ZK_ADMIN_PASSWORD. The same password is used for the ZooNavigator web UI basic auth.

6

4LW health check and auth roundtrip

Run a health check and an authenticated request:

echo "stat" | nc -q 2 127.0.0.1 2181 | head -10
PASS=$(sudo grep '^ZK_ADMIN_PASSWORD=' /stage/scripts/zookeeper-credentials.log | cut -d= -f2-) printf "addauth digest sac:%s\nls /\n" "$PASS" | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>&1 | tail -5

The first call uses ZooKeeper's "stat" four-letter-word command to confirm Mode: standalone. The second call authenticates as SecureAnyCloud via digest ACL and lists the root znodes.

7

ZooNavigator web UI: connect

Browse to http://<vm-ip>/ and authenticate as SecureAnyCloud with the password from Step 5. ZooNavigator auto-connects to the local ZooKeeper ensemble and lands on the znode browser.

8

Create a znode from the UI

Click Create node in the ZooNavigator left pane. Name the node /myapp and set the mode to Persistent. The new znode appears in the tree.

9

Inspect a znode

Click any znode, for example the newly created /myapp, to see its data, ACL list, stat (czxid, mzxid, version and so on) and any children. The Edit tab lets operators set data inline.

10

Create and protect a znode from the CLI

Create a protected znode with the ZooKeeper CLI:

PASS=$(sudo grep '^ZK_ADMIN_PASSWORD=' /stage/scripts/zookeeper-credentials.log | cut -d= -f2-) printf "addauth digest sac:%s\ncreate /sac/data hello digest:sac:cdrwa\nls /sac\nget /sac/data\n" "$PASS" | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>&1 | tail -10

This creates /sac/data with an ACL that grants the SecureAnyCloud digest user full CRUD permissions, then reads it back. For public-readable znodes, use the ACL world:anyone:r instead.

11

Tune the heap

Edit /etc/zookeeper/conf/java.env and restart the service:

sudo systemctl restart zookeeper.service

Defaults on Standard_B2s:

  • JVMFLAGS: -Xms256m -Xmx512m
  • Snapshot retention: 5 snapshots, autopurge every 24 hours
  • Data dir: /var/lib/zookeeper/data
  • Transaction log dir: /var/lib/zookeeper/log

For production with a busy ensemble, raise -Xmx to 1024m–2048m and place transaction logs on a separate fast disk (dataLogDir in /etc/zookeeper/conf/zoo.cfg).

12

Add a second ZooNavigator user

Replace <new-password> with the operator's chosen password:

sudo htpasswd -bB /etc/nginx/zoonavigator.htpasswd alice <new-password>
sudo systemctl reload nginx

Each operator authenticates with their own basic-auth credential against the same nginx vhost. The underlying ZooKeeper digest ACL is still SecureAnyCloud.

13

Add a second ZooKeeper digest user

Generate the digest hash and create a znode owned by the second user:

PASS=$(sudo grep '^ZK_ADMIN_PASSWORD=' /stage/scripts/zookeeper-credentials.log | cut -d= -f2-) NEWUSER=alice NEWPASS=<new-password>
HASH=$(echo -n "$NEWUSER:$NEWPASS" | openssl dgst -binary -sha1 | base64) printf "addauth digest sac:%s\nsetAcl / digest:sac:cdrwa,digest:%s:%s:cdrwa\nls /\n" "$PASS" "$NEWUSER" "$HASH" | /opt/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 2>&1 | tail -5
14

Backups

ZooKeeper writes snapshots to /var/lib/zookeeper/data/version-2 and transaction logs to /var/lib/zookeeper/log/version-2. Take a consistent backup while the service is running by copying the latest snapshot and all transaction logs since:

sudo tar czf /var/backups/zookeeper-$(date +%F).tgz -C /var/lib zookeeper

Periodically copy /var/backups to Azure Blob Storage (az storage blob upload-batch) for off-VM retention.

To restore, stop ZooKeeper, replace /var/lib/zookeeper and restart the service.

15

Logs and troubleshooting

Use these commands to inspect the logs:

sudo journalctl -u zookeeper.service --no-pager -n 80
sudo journalctl -u zoonavigator.service --no-pager -n 30
sudo journalctl -u nginx.service --no-pager -n 30
sudo ls /var/lib/zookeeper/log/

ZooKeeper writes its main log to /var/lib/zookeeper/log/zookeeper-zookeeper-server-*.out and its data log to /var/lib/zookeeper/log/version-2/. ZooNavigator's stdout goes to the systemd journal. The nginx access log is at /var/log/nginx/access.log.

Security

  • ZooKeeper client port :2181 is not auth-walled at the network layer, because SASL DIGEST-MD5 is deprecated in Java 17+. Restrict NSG inbound to your application tier IPs.
  • ZooKeeper digest ACLs gate per-znode access for connected clients. Use them to isolate workloads.
  • The admin server is bound to 127.0.0.1:8080 (loopback only) and is not exposed to the network.
  • The ZooNavigator web UI is bound to 127.0.0.1:9000 and is not exposed directly.
  • The nginx auth wall on :80 enforces HTTP basic authentication for the ZooNavigator web UI.
  • One per-VM SecureAnyCloud password is used for nginx, the ZooNavigator container and the default znode digest ACL, so a single rotation covers all three.
  • Terminate TLS at an upstream Application Gateway or Front Door, or enable ZooKeeper's own SSL listener (zoo.cfg secureClientPort=2281).

Licensing: ZooNavigator (AGPL-3.0)

Apache ZooKeeper itself is Apache License 2.0 (permissive). The ZooNavigator web UI bundled in this image is AGPL-3.0-or-later, which has a network-copyleft clause. If you modify ZooNavigator and run the modified version on a network where users can interact with it, you must offer those users the source code of your modified version.

Unmodified deployments satisfy AGPL section 13, because the upstream source is publicly available. This is the SecureAnyCloud default: we ship elkozmon/zoonavigator:2.0.0 from Docker Hub without modification. The upstream locations are:

  • Source: https://github.com/elkozmon/zoonavigator
  • Image: https://hub.docker.com/r/elkozmon/zoonavigator
  • License: https://github.com/elkozmon/zoonavigator/blob/master/LICENSE.txt

If you modify the ZooNavigator container in your own deployment, for example with custom theming or additional plugins, AGPL section 13 requires you to provide your modified source to your users.

The SecureAnyCloud packaging, support and CLI scripts around ZooNavigator are not modifications to ZooNavigator and remain under SecureAnyCloud's commercial terms.

If you prefer a strictly Apache-licensed image without ZooNavigator, contact sales@secureanycloud.com and we will build a CLI-only ZooKeeper variant.

Support

SecureAnyCloud provides 24/7/365 expert technical support, with a guaranteed response within 24 hours and a one-hour average for critical issues. Contact sales@secureanycloud.com.

Deploy on Azure

Launch Apache ZooKeeper 3.9 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure