The plugins image
You may have noticed that while we called the previous image
we never did drop the war in it. In fact, the only reference to that war we’ve
seen is the very base image which sets a version, path, and a checksum. What’s the
reason for this madness?
The answer is that the images we have built up until now are only a runtime environment for this image. The master image (the one we just built) will almost never change. When doing an upgrade the war never has new system requirements and rarely changes the directory structure or anything like that.
What does change from deployment to deployment is the set of plugins, version of the Jenkins war, and the configuration that interacts with those things. For this reason I choose to run a vanilla Jenkins master container (with a few environment variable configs passed in) and a highly customized plugin container. This plugin container is where the binaries live and is volume mounted by the master to provide the software itself.
Let’s create it now and we can talk more about it after.
You can see from the Dockerfile that this image is where the action is. We have
a similar set of metadata at the top like the other images, then we add a file
install-plugins.sh This file is from the upstream Jenkins Docker image and
it’s purpose is to install a set of plugins as well as any depdendencies they
have. It can be downloaded from the link provided in the Dockerfile 1.
Then we go on to download the jenkins war and check it’s SHA. If the SHA does not match what we have in the base image, this step will fail and you know that something amiss is going on. Since the version and the SHA are both set in the very base image they should always match. There is never a scenario in which those two do not match up.
Once the war and tools are installed we can install our set of plugins. The
install-plugins.sh script needs the war to run so now we should be ready.
What this script is doing in the background is interacting with the Jenkins
Update Center to attempt to install each plugin that is listed in
It will reach out to download the plugin and check for any depdendencies the
plugin may have. If there are any, it will download those, resolve transitive
deps and so on until the full set of plugins defined by us are installed along
with any deps they need to function.
NOTE: This is different than the
plugins.sh file that is out there. That
script will not resolve dependencies and makes it very hard to audit which
plugins you actually need.
If you recall we discussed that this image is only going to provide the software
itself and the Jenkins master image will provde the runtime. How that works is
that we will export our plugins and warfile via the
VOLUME statements at the
bottom of this Dockerfile and mount them into the master via
This makes our plugins image an fully contained and versionable bundle of the
master war and any plugins we need. A little later on, we will talk about how
to include your configuration as well.
Finally we have the
ENTRYPOINT. This version is farily simple:
What this does is keeps the container running even though we do not have a
running process in it. Since this is only a container for our wars and JPIs,
it doesn’t need to run the JVM or anything like that. It only needs to provide
it’s exported volumes. If we were to omit the
ENTRYPOINT, everything would
still work as expected but for the fact that the
would not be running.
It would appear to be in a stopped state which for me is very confusing. The container is being used by the master (by way of volumes) and so it is indeed in use. The fact that Docker shows it as stopped is misleading IMO and so this just props up the container for clarity.
Building the image
Well, we’ve got another image to build and I think by this time you know what we’re going to do and it’s not DRY out our builders :P
Testing the image
As us rafters say, the proof is at the put-in. Let’s give it a whirl!
Would you look at that? Jenkins seems to be starting up swimmingly! If it is not for you, try to debug what exactly is going wrong. There are a lot of moving parts and this is a fairly complex system so don’t feel bad. It happens to all of us. Except us writing blog tutorials. We are always 100% right and our instructions work 11/10 times so you’re wrong and you should feel bad :P Seriously though, if something is jacked up in these instructions please use your super PR skills and help a brotha out by submitting a PR to the repo.
After running tests like these, we definitely need to begin thinking about
cleanup. What would happen if we tried to run the same tests again right now?
Feel free to try it, but the secret is that it won’t work. We need to delete
the remnants from the previous test before starting another so I make it a habit
to ensure a clean environmnet before I run a test and attempt to cleanup
afterwards. The command I normally use for this is the equivalent of “nuke it
‘till it glows”:
docker container rm -fv $(docker ps -qa).
This little gem will remove all containers, running or not as well as any
volumes they may have created (you may want to read more about that, volumes
not in the state you thought they were can ruin your day in lots of ways).
One other thing you may be noticing is that no matter how diligent you are,
you’re developing a stack of
<none> images, weirdly named volumes, and orphaned
networks. This is normal cruft left behind while doing Docker development and it
can be removed by using
docker system prune . This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all dangling images (
NOTE: If you really want to clean up, add a
-a and it will also remove
images not attached to a running container. I find that to be annoying except
when we’re in prod, but it is handy there.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 (~) ------------------------------------------------------------------------- 🐳 unset (matt.bajor) % docker rm -fv $(docker ps -qa) 34b9692447f6 59b24f290270 (~) ------------------------------------------------------------------------- 🐳 unset (matt.bajor) % docker system prune WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all build cache Are you sure you want to continue? [y/N] y Deleted Networks: master_default Deleted Images: untagged: modernjenkins/jenkins-master@sha256:8f4b3bcad8f8aa3a26da394ce0075c631d311ece10cf7c23ce60058a9e47f6ed deleted: sha256:96c78f549467f8b4697b73eddd9da299d8fd686696b45190a2bba24ad810529a deleted: sha256:d1f38cb683287825bbf1856efdfaa87e2a7c279ceb793f9831b88b850ae1c9a0 deleted: sha256:5371c45cef2d3c5c468aae4fd5e93c335e8e681f2aa366f6122902c45e8ec9cb deleted: sha256:079be452ec3e99b51a35b76e67b1bb3af649c3357e3ba05d2b6bd2a8127804b4 deleted: sha256:87baad26b39521ddd0d7b12ac46b2f92344f2f8ad34f0f35c524d5c0c566b409 deleted: sha256:c348763948964e1f63c427bea6b4d38c3a34403b61aee5e7b32059a3c095af32 deleted: sha256:6f92439bdac179e8c980dc6a7eb4f9647545e9c6d34d28edbba3c922efa9ea1e deleted: sha256:edd5cbd4dc3cb3e9ab54bb1d7f446d5638c4543f04f2b63ae1a3e87a661be7a2 deleted: sha256:7890def677cf6649567c4355ef8f10c359f71c0ac9ca6ab94d8f359a5d57f84d deleted: sha256:2704ec820811576ee2c60b8a660753939457f88fbe6938c2039489a6047ec59c deleted: sha256:202acc3c794ce58a5e0b0e6b3285ab5ae27c641804c905a50b9ca7d5c601b2b3 deleted: sha256:70e19603643ce03f9cbff3a8837f1ebfb33fe13df7fba66c2501be96d9a2fb93 deleted: sha256:8e757cb858613c81e5fa8fb2426d22584539c163ce4ab66d6b77bd378ee2817a deleted: sha256:18d1a064d790f3be371fef00813efe1c78996eab042977b952f4cbf067b846e8 deleted: sha256:bddcbf75436ff49e435fe3c371337b6b12ae125e68e0d833ac6180ffd82f34d9 deleted: sha256:f4dae60dcb2542e532eb05c94abba2da00d5a36360cb1d79cb32f87bf9b9c909 deleted: sha256:12f7c2589fdbb6e8b9ac78983511df70e9613c8da42edf23ee1cdb3599437233 deleted: sha256:26b155d41fabd6881f871945586c623a485688fc67f08223df288522f7aeed87 deleted: sha256:3a7c393698419b8f4f7a1464264459d2662d9015b3d577ad8cb12e0b4ae069a5 deleted: sha256:53794a3680b75ae98f70ab567db86a1b0e262615a8194bad534edfd5d8acc2f2 deleted: sha256:13449dedb3ec5df1f1b969aa2f1f93bb2a3bed2fb3ebe7279cce52b750696031 deleted: sha256:55aae84cda94b4611f73ec70b4cc1ea7ce4bbb77a1999b585fcc46c6239fe2a5 deleted: sha256:b41674288931c4e4bcd43e9fcc0d0af8d9ddd9a31f04506050ce0f0dfc59e3e3 Total reclaimed space: 313.9MB
Commit, push, PR
You know the drill. Integrate early, integrate often. Make sure you actually are looking at the work you’re merging. Afterall, it has your public name on it twice.
If you did get lost (I know I had to make a minor change to my base image), take
a look at the
unit2-part4 tag here: