Stateless "12-factor" applications are all the rage, but there are some kinds of services that are inherently stateful. Good examples are things like relational databases (Postgres, MySQL) and NoSQL databases (Cassandra, etc).
These services are difficult to containerize, because the default docker model favours ephemeral containers where the data disappears when the container is destroyed.
These services also have a strong need for identity. A database "primary" server is different than the "slave". In Cassandra, certain nodes are designated as seed nodes, and so on.
OpenDJ is an open source LDAP directory server from ForgeRock. LDAP servers are inherently "pet like" insomuch as the directory data must persist beyond the container lifetime. OpenDJ nodes also replicate data between themselves to provide high-availability and therefore need some kind of stable network identity.
Kubernetes 1.3 introduces a feature called "Pet Sets" that is designed specifically for these kinds of stateful applications. A Kubernetes PetSet provides applications with:
- Permanent hostnames that persist across restarts
- Automatically provisioned persistent disks per container that live beyond the life of a container
- Unique identities in a group to allow for clustering and leader election
- Initialization containers which are critical for starting up clustered applications
These features are exactly what we need to deploy OpenDJ instances. If you want to give this a try, read on...
You will need access to a Kubernetes 1.3 environment. Using minikube is the recommended way to get started on the desktop.
You will need to fork and clone the ForgeRock Docker repository to build the OpenDJ base image. The repository is on our stash server:
To build the OpenDJ image, you will do something like:
docker build -t forgerock/opendj:latest .
If you are using minikube, you should connect your docker client to the docker daemon running in your minikube cluster (use minikube docker-env). Kubernetes will not need to "pull" the image from a registry - it will already be loaded. For development this approach will speed things up considerably.
Take a look at the README for the OpenDJ image. There are a few environment variables that the container uses to determine how it is bootstrapped and configured. The most important ones:
- BOOTSTRAP: Path to a shell script that will initialize OpenDJ. This is only executed if the data/config directory is empty. Defaults to /opt/opendj/boostrap/setup.sh
- BASE_DN: The base DN to create. Used in setup and replication
- DJ_MASTER_SERVER: If set, run.sh will call bootstrap/replicate.sh to enable replication to this master. This only happens if the data/config directory does not exist
There are sample bootstrap setup.sh scripts provided as part of the container, but you can override these and provide your own script.
Next, fork and clone the ForgeRock Kubernetes project here:
The opendj directory contains the Pet Set example. You must edit the files to suit your needs, but as provided, the artifacts do the following:
- Configures two OpenDJ servers (opendj-0 and opendj-1) in a pet set.
- Runs the cts/setup.sh script provided as part of the docker image to configure OpenDJ as an OpenAM CTS server.
- Optionally assigns persistent volumes to each pet, so the data will live across restarts
- Assigns "opendj-0" as the master. The replicate.sh script provided as part of the Docker image will replicate each node to this master. The script ignores any attempt by the master to replicate to itself. As each pet is added (Kubernetes creates them in order) replication will be configured between that pet and the opendj-0 master.
- Creates a Kubernetes service to access the OpenDJ instances. Instances can be addressed by their unique name (opendj-1), or by a service name (opendj) which will go through a primitive load balancing function (at this time round robin). Applications can also perform DNS lookup on the opendj SRV record to obtain a list of all the OpenDJ instances in the cluster.
The replication topology is quite simple. We simply replicate each OpenDJ instance to opendj-0. This is going to work fine for small OpenDJ clusters. For more complex installations you will need to enhance this example.
To create thet petset:
kubectl create -f opendj/
If you bring up the minikube dashboard:
You should see the two pets being created (be patient, this takes a while).
Take a look at the pod logs using the dashboard or:
kubectl logs opendj-0 -f
Now try scaling up your PetSet. In the dashboard, edit the Pet Set object, and change the number of replicas from 2 to 3:
You should see a new OpenDJ instance being created. If you examine the logs for that instance, you will see it has joined the replication topology.
Note: Scaling down the Pet Set is not implemented at this time. Kubernetes will remove the pod, but the OpenDJ instances will still think the scaled down node is in the replication topology.