Follow along with this article as we take a guided tour of containerizing Zookeeper using Docker. This guide will show how to install Zookeeper to the container, how to configure the Zookeeper application, and how to share data volumes between the host and container.
This tutorial makes use of a Dockerfile
for specifying our container’s
contents. If you need more information on writing a Dockerfile, refer to the
official documentation.
Version 1 - Specifying a Base Image
A Docker container is built off of a base Linux image. These images provide core
functionality for your container and are specified using the FROM
command.
FROM
allows you to specify both an image and a tag with the tag being a
version of the image. In the following Dockerfile, we are building a container
using the jessie
version of the debian
image.
FROM debian:jessie
This is enough to build your Docker image. You can exercise it by running a
container in interactive mode and starting a bash prompt. When you are done,
type exit
at the container’s prompt to return to your host machine.
> docker build -t sookocheff/docker-zookeeper:1 .
...
> docker run -it sookocheff/docker-zookeeper:1 /bin/bash
...
> exit
Version 2 - Installing Zookeeper
Now that we have a base image, we can install Zookeeper on it using the RUN
command. RUN
allows you to execute arbitrary commands on the image. In this
example, we install Zookeeper version 3.4.7 to /opt/zookeeper
.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz
RUN tar -xzf - -C /opt
RUN mv /opt/zookeeper-3.4.7 /opt/zookeeper \
Each command executed within a Dockerfile creates an additional image layer. Each layer replicates the contents of the layers before it and so each layer ultimately increases the size of your Docker image. It is therefore considered best practice to minimize the number of layers by combining statements in your Dockerfile. You will need to balance readability with performance and adjust your Dockerfile to your needs.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
Version 3 - Expose Environment Variables
Java applications often times require the JAVA_HOME
environment variable to be
set. The ENV
keyword creates environment variables in your image.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
Version 4 - Expose Ports
Zookeeper requires several ports to be open to communicate with other Zookeeper
nodes and with clients. You can tell the container to listen to network ports
using the EXPOSE
keyword.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
EXPOSE 2181 2888 3888
To connect your host to the ports the container is listening on, you run your container with the -p command. For example, to expose ports from the container and connect those same ports to your host machine you can specify multiple ports to bind to.
docker run -d -p 2181:2181 -p 2888:2888 -p 3888:3888 sookocheff/zookeeper-docker:4
Version 5 - Setting a Working Directory
We installed Zookeeper to /opt/zookeeper
. Since we will only be running
Zookeeper on this container it makes sense to also set our working directory to
the install location. This is accomplished with the WORKDIR
keyword.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
EXPOSE 2181 2888 3888
WORKDIR /opt/zookeeper
Version 6 - Setting Configuration Files
The VOLUME
keyword is used to mount data into a Docker container. To configure
the Zookeeper instance we declare our configuration files locally and have
Docker read those configuration files using the mounted volumes specified with
the VOLUME
command.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
EXPOSE 2181 2888 3888
WORKDIR /opt/zookeeper
VOLUME ["/opt/zookeeper/conf"]
To map your local directory to the volume you created, you use the -v
option
of the docker run
command. Place your zoo.cfg
file in a local directory and
run the container. In the following example, any files in the directory ./conf
will be mapped to the directory /opt/zookeeper/conf
on the container.
docker run -it -v conf:/opt/zookeeper/conf sookocheff/zookeeper-docker:7 /bin/bash
OS X users will need to qualify the local directory with the full path rather than a relative directory.
Version 7 - Sharing Data
Zookeeper also keeps local data in a file system directory. By default that
directory is /tmp/zookeeper
. We can also mount that volume through Docker.
FROM debian:jesse
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
EXPOSE 2181 2888 3888
WORKDIR /opt/zookeeper
VOLUME ["/opt/zookeeper/conf", "/tmp/zookeeper"]
If your Zookeeper configuration is different from the default, use the appropriate mount point for the data volume.
Volumes are a complex subject in Docker. For more details, refer to the official Docker documentation.
Version 8 - Running Zookeeper
At this point, your Dockerfile installs Zookeeper, exposes the appropriate ports
to the host filesystem, and mounts volumes for configuration and data files. The
last thing we need is to actually run Zookeeper. For this we use the
ENTRYPOINT
and CMD
keywords.
FROM debian:jessie
RUN apt-get update && apt-get install -y openjdk-7-jre-headless wget
RUN wget -q -O - http://mirror.csclub.uwaterloo.ca/apache/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz | tar -xzf - -C /opt \
&& mv /opt/zookeeper-3.4.7 /opt/zookeeper \
&& cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
EXPOSE 2181 2888 3888
WORKDIR /opt/zookeeper
VOLUME ["/opt/zookeeper/conf", "tmp/zookeeper"]
ENTRYPOINT ["/opt/zookeeper/bin/zkServer.sh"]
CMD ["start-foreground"]
The ENTRYPOINT
and CMD
keywords provide a default executable that is run
whenever the container is started. In this example, we start the Zookeeper
server in the foreground.
Wrapping Up
At this point, we have a working Dockerfile for building a Docker image to run a Zookeeper instance. To build the Docker image, run:
> docker build -t sookocheff/docker-zookeeper:3.4.7 .
And to run it:
> docker run -p 2181:2818 -p 2888:2888 -p 3888:3888 sookocheff/docker-zookeeper:3.4.7