Overriding Docker ENTRYPOINT of a Base Image
Take a look at how this team overrode the Docker command and copied multiple configuration files conditionally.
Join the DZone community and get the full member experience.
Join For FreeRecently my DevOps team and I decided to bring all the dev tools of all engineering teams (backend, frontend, mobile, operations) in a Kubernetes cluster. We have many reasons to do so. One of them is that we need to have centralized management of all those tools that are configured and upgraded manually sometimes by our IT department and some other times by our DevOps department. Another important reason is that we need to scale Jenkins jobs, especially when many releases or automated procedures occur by many different teams. Last, but not least, is that we need a playground with real-life issues and problems to cope with before we use a K8s cluster in production.
There are several things that we need to do before moving our apps to Kubernetes, like Dockerizing components, applications, etc.
One of the last experiments was to migrate our HornetQ servers to ActiveMQ ones. You may find more details here. In order to move ActiveMQ servers to Kubernetes, we would like to create a custom Dockerfile with specific configuration files. Unfortunately, we do not have one set of configuration files but many. Therefore, we should invent a way to copy configuration files conditionally based on Docker runtime environment variables.
To be more precise, let’s see a basic Dockerfile that would copy the configuration files we need into some specific folders in the Docker image:
FROM vromero/activemq-artemis:latest
MAINTAINER Marios Karagiannopoulos <mkaragiannopoulos@zulutrade.com>
ENV MODE_PARAMS_FOLDER /var/lib/artemis/
RUN mkdir -p ${MODE_PARAMS_FOLDER}
ADD int ${MODE_PARAMS_FOLDER}/etc-override-int
ADD ext ${MODE_PARAMS_FOLDER}/etc-override-ext
We could build an image with etc-override-int configuration and another one with etc-override-ext. But why waste disk space with too many images? There is no reason to do it since we can create one image and select our configuration based on a runtime environment variable.
Problem
We have a problem here. The base image: “vromero/activemq-artemis:latest” has an ENTRYPOINT
that does not take into account our configuration files at all.
One idea is to override ENTRYPOINT
by adding your CMD
command following by an ENTRYPOINT
["/usr/bin/env"] like:
# trick to override base image's ENTRYPOINT
ENTRYPOINT ["/usr/bin/env"]
CMD ["bash", "/sed_broker_files.sh /var/lib/artemis/etc/broker-05.xml"]
Nevertheless, if you want to use container’s runtime environment variables this is not going to work.
So, what I’ve been thinking of is to look at how the ENTRYPOINT of a base image is written and try to inject other scripts or modifying script code into it during the Docker image build time. The base image ENTRYPOINT
script is located here. If you see carefully the variable we’re interested of is:
OVERRIDE_PATH=$BROKER_HOME/etc-override
Based on the selected configuration in runtime of the container, it should be etc-override-int
or etc-override-ext
.
A sed
command could do the job during the build time of the Docker image. However, we also need to run some other commands before the startup of the application. We may put these commands in another script and inject the execution of this script inside the ENTRYPOINT
of the base image. Let’s see the final Dockerfile:
FROM vromero/activemq-artemis:latest
MAINTAINER Marios Karagiannopoulos <mkaragiannopoulos@zulutrade.com>
# trick to override artemis user when entering the container
USER root
ENV MODE_PARAMS_FOLDER /var/lib/artemis/
RUN mkdir -p ${MODE_PARAMS_FOLDER}
ADD int ${MODE_PARAMS_FOLDER}/etc-override-int
ADD ext ${MODE_PARAMS_FOLDER}/etc-override-ext
# trick to override base image's ENTRYPOINT
COPY activate_mode_param.sh /
RUN head -2 /docker-entrypoint.sh > /docker-entrypoint.sh.tmp
RUN echo "/activate_mode_param.sh" >> /docker-entrypoint.sh.tmp
RUN all_lines=`wc -l /docker-entrypoint.sh | cut -d' ' -f1` && \
new_lines=`expr $all_lines - 3` && \
tail -$new_lines /docker-entrypoint.sh >> /docker-entrypoint.sh.tmp && \
mv /docker-entrypoint.sh.tmp /docker-entrypoint.sh
RUN sed -i "s/etc-override/etc-override-\${MODE_PARAM}/g" /docker-entrypoint.sh
RUN chmod 777 /docker-entrypoint.sh
RUN chown -R artemis:artemis ${MODE_PARAMS_FOLDER}
USER artemis
As you can see, we copy the activate_mode_param.sh
script under root folder and then we inject its call inside /docker-entrypoint.sh
. Also, we change the etc-override presence
to etc-override-${MODE_PARAM}
where $MODE_PARAM
is a runtime environment. Take a look here:
docker build -t 10.0.8.171:5000/amq:latest -f Dockerfile .
docker run -it --name='amq-master-int-206' \
-v /opt/amq/sharedstore:/var/lib/artemis/data \
-e 'MODE_PARAM=int' \
-e 'AMQ_MASTER_IP=10.0.9.206' \
-e 'AMQ_MASTER_PORT=61616' \
-e 'AMQ_SLAVE_IP=10.0.9.206' \
-e 'AMQ_SLAVE_PORT=61617' \
-e 'ARTEMIS_PERF_JOURNAL=ALWAYS' \
-e 'ARTEMIS_USERNAME=admin' \
-e 'ARTEMIS_PASSWORD=admin' \
-e 'ARTEMIS_MIN_MEMORY=512M' \
-e 'ARTEMIS_MAX_MEMORY=1024M' \
-e 'ENABLE_JMX=true' \
-e 'JAVA_OPTS=-Dorg.apache.activemq.SERIALIZABLE_PACKAGES=*' \
-p 8161:8161 \
-p 61616:61616 \
-d 10.0.8.171:5000/amq:latest
Deploying to Kubernetes
After building the image with the command above, we need to push it to our local registry:
docker login 10.0.8.171:5000 --username zulu --password ********
docker push 10.0.8.171:5000/amq:latest
alias k='kubectl'
k create namespace jms
k -n jms apply -f regced.yaml
k -n jms apply -f deployment-amq-master-int-206.yaml
k -n jms apply -f svc-amq-master-int-206.yaml
With contents:
regced.yaml (secret file to read images from our private registry)
apiVersion v1
kind Secret
metadata
namespace jms
name regcred
data
.dockerconfigjson"HIDDEN_HASH"
type kubernetes.io/dockerconfigjson
deployment-amq-master-int-206.yaml
apiVersion extensions/v1beta1
kind Deployment
metadata
name amq-master-int-206
namespace jms
spec
replicas1
template
metadata
labels
k8s-app amq-master-int-206
spec
imagePullSecrets
name regcred
containers
name amq-master-int-206
image 10.0.8.171 5000/amq latest
ports
name http
containerPort8161
name jnp
containerPort61616
env
name MODE_PARAM
value"int"
name AMQ_MASTER_IP
value"10.0.8.170"
name AMQ_MASTER_PORT
value"6116"
name AMQ_SLAVE_IP
value"10.0.8.170"
name AMQ_SLAVE_PORT
value"6117"
name ARTEMIS_PERF_JOURNAL
value always
name ARTEMIS_USERNAME
value"admin"
name ARTEMIS_PASSWORD
value"admin"
name ARTEMIS_MIN_MEMORY
value"512M"
name ARTEMIS_MAX_MEMORY
value"1024M"
name ENABLE_JMX
value"true"
name JAVA_OPTS
value"-Dorg.apache.activemq.SERIALIZABLE_PACKAGES=*"
volumeMounts
name nfs-amq
mountPath /var/lib/artemis/data
volumes
name nfs-amq
nfs
server10.0.8.64
path /volume1/Storage/YB/k8s/amq206
svc-amq-master-int-206.yaml
apiVersion v1
kind Service
metadata
namespace jms
name amq-master-int-206
spec
type NodePort
ports
port8161
name http
targetPort8161
nodePort8161
port6116
name jnp
targetPort61616
nodePort6116
selector
k8s-app amq-master-int-206
Opinions expressed by DZone contributors are their own.
Comments