{"id":334,"date":"2017-08-02T00:55:20","date_gmt":"2017-08-01T16:55:20","guid":{"rendered":"https:\/\/blog.jsjs.org\/?p=334"},"modified":"2017-08-02T00:55:20","modified_gmt":"2017-08-01T16:55:20","slug":"building-dockers-with-maven-for-continuous-integration","status":"publish","type":"post","link":"https:\/\/blog.jsjs.org\/?p=334","title":{"rendered":"Building Dockers with Maven for Continuous Integration"},"content":{"rendered":"<p>At Alooma, we\u00a0<em>loooove<\/em>\u00a0<a href=\"https:\/\/www.docker.com\/\" target=\"_blank\" rel=\"noopener\">dockers<\/a>.<\/p>\n<p>It&#8217;s true. We try to run as much as we can inside docker containers. While there are plenty of benefits to packaging modules in containers, we are not here to persuade you to use Docker. We will, however, just assume that you\u00a0<em>loooove<\/em>\u00a0dockers as much as we do.<\/p>\n<p>Given that, let&#8217;s talk about how Alooma uses dockers in production to streamline development and push code quickly.<\/p>\n<h2 id=\"overview\">Overview<\/h2>\n<p>Docker allows you to treat your infrastructure as code. This code is your\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>s.<\/p>\n<p>Like any code, we want to get into a tight change-&gt;commit-&gt;build-&gt;test cycle (a full\u00a0<em>continuous integration<\/em>\u00a0solution). To achieve this, we need to build a smooth DevOps pipeline.<\/p>\n<p>Let&#8217;s break it down into more detailed requirements:<\/p>\n<ul>\n<li>Manage\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>s in a VCS<\/li>\n<li>Build\u00a0<em>docker images<\/em>\u00a0on a CI server upon each commit<\/li>\n<li>Upload and tag the artifacts (which should be ready for easy deployment)<\/li>\n<\/ul>\n<h3 id=\"our-workflow\">Our Workflow<\/h3>\n<p>At a high level, our DevOps pipeline is built around GitHub, Jenkins and Maven.<\/p>\n<p>Here&#8217;s how it works:<\/p>\n<ol>\n<li>GitHub notifies Jenkins about each push to the repository<\/li>\n<li>Jenkins then triggers a Maven build<\/li>\n<li>Maven builds everything, including docker images<\/li>\n<li>Finally, the Maven build finishes up by pushing the docker images into our private docker registry<\/li>\n<\/ol>\n<p>The benefit of this workflow is that it allows us to easily tag a release version (all commits are built and ready in our docker registry). Then, we can very easily deploy by pulling and running the docker images.<\/p>\n<p>In fact this deployment is so simple, we initiate it via a command to our trusted Slackbot: &#8220;Aloominion&#8221; (more on our bot friend in a future post).<\/p>\n<p>You are probably fairly familiar with the other elements of this workflow, as they are quite common. Therefore, let&#8217;s specifically dive into building docker images with Maven.<\/p>\n<h2 id=\"docker-building-in-depth\">Docker Building In Depth<\/h2>\n<p>Alooma is a Java shop. We already have Maven as a central tool in our build pipeline, so it was natural to add the process of building dockers to our Maven build as well.<\/p>\n<p>When searching for Maven plugins to interact with docker, 3 options came up. We chose to use Spotify&#8217;s\u00a0<a href=\"https:\/\/github.com\/spotify\/docker-maven-plugin\" target=\"_blank\" rel=\"noopener\">maven-docker-plugin<\/a>\u00a0&#8212; although\u00a0<a href=\"https:\/\/github.com\/rhuss\/docker-maven-plugin\" target=\"_blank\" rel=\"noopener\">rhuss&#8217; one<\/a>\u00a0and\u00a0<a href=\"https:\/\/github.com\/alexec\/docker-maven-plugin\" target=\"_blank\" rel=\"noopener\">alexec&#8217;s<\/a>\u00a0seemed like decent options as well.<\/p>\n<p>Another Maven plugin, on which our build scheme relies, is the\u00a0<a href=\"https:\/\/github.com\/ktoso\/maven-git-commit-id-plugin\" target=\"_blank\" rel=\"noopener\">maven-git-commit-id-plugin<\/a>. We use this so our dockers are tagged by the git commit ID &#8211; this comes in very handy in the deployment process and allows us to understand which version is running.<\/p>\n<h3 id=\"show-me-the-code\">Show Me The Code<\/h3>\n<p>Each of our docker images has its own Maven module (all the above mentioned docker-maven plugins work smoothly with a single\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>\u00a0in a module).<\/p>\n<p>Let&#8217;s start with a simple configuration for the Spotify plugin:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;plugin&gt;<\/span>\n    <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.spotify<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n    <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">docker-maven-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n    <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">0.2.3<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n    <span class=\"tag\">&lt;executions&gt;<\/span>\n        <span class=\"tag\">&lt;execution&gt;<\/span>\n            <span class=\"tag\">&lt;phase&gt;<\/span><span class=\"pln\">package<\/span><span class=\"tag\">&lt;\/phase&gt;<\/span>\n            <span class=\"tag\">&lt;goals&gt;<\/span>\n                <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">build<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n            <span class=\"tag\">&lt;\/goals&gt;<\/span>\n        <span class=\"tag\">&lt;\/execution&gt;<\/span>\n    <span class=\"tag\">&lt;\/executions&gt;<\/span>\n    <span class=\"tag\">&lt;configuration&gt;<\/span>\n        <span class=\"tag\">&lt;dockerDirectory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/dockerDirectory&gt;<\/span>\n        <span class=\"tag\">&lt;imageName&gt;<\/span><span class=\"pln\">alooma\/${project.artifactId}<\/span><span class=\"tag\">&lt;\/imageName&gt;<\/span>\n    <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n<span class=\"tag\">&lt;\/plugin&gt;<\/span><\/code><\/pre>\n<p>What we see here is that we&#8217;ve bound the plugins\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">build<\/span><\/code>\u00a0goal to the Maven\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">package<\/span><\/code>\u00a0phase. We&#8217;ve instructed it to look for the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>\u00a0in our modules base directory (using the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">dockerDirectory<\/span><\/code>element), and name the image by its artifactId (prefixed with &#8220;alooma\/&#8221;).<\/p>\n<p>This works very well for a simple docker build.<\/p>\n<p>The first thing we&#8217;ll notice is that this image doesn&#8217;t get pushed anywhere. We can fix this by adding\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;pushImage&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/pushImage&gt;<\/span><\/code>\u00a0to the configuration. Great.<\/p>\n<p>But now the image will be pushed to the default docker hub registry. Not great.<\/p>\n<p>To fix this, we define a new Maven property\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;docker.registry&gt;<\/span><span class=\"pln\">docker-registry.alooma.com:5000\/<\/span><span class=\"tag\">&lt;\/docker.registry&gt;<\/span><\/code>\u00a0and change the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">imageName<\/span><\/code>\u00a0to\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}<\/span><\/code>. You might be thinking,\u00a0<em>&#8220;why is a property needed for the docker registry?&#8221;<\/em>, and you would be right! But having this will make it a lot easier to modify it in case our registry URL changes.<\/p>\n<p>There&#8217;s one more important thing which we haven&#8217;t handled yet &#8211; we want each image to be tagged by its git commit ID. This is achieved by changing the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">imageName<\/span><\/code>\u00a0to\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}:${git.commit.id.abbrev}<\/span><\/code>.<\/p>\n<p>The\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">${git.commit.id.abbrev}<\/span><\/code>\u00a0property is set using the maven-git-commit-id-plugin I mentioned above.<\/p>\n<p>So, now our plugin configuration looks like this:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;plugin&gt;<\/span>\n    <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.spotify<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n    <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">docker-maven-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n    <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">0.2.3<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n    <span class=\"tag\">&lt;executions&gt;<\/span>\n        <span class=\"tag\">&lt;execution&gt;<\/span>\n            <span class=\"tag\">&lt;phase&gt;<\/span><span class=\"pln\">package<\/span><span class=\"tag\">&lt;\/phase&gt;<\/span>\n            <span class=\"tag\">&lt;goals&gt;<\/span>\n                <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">build<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n            <span class=\"tag\">&lt;\/goals&gt;<\/span>\n        <span class=\"tag\">&lt;\/execution&gt;<\/span>\n    <span class=\"tag\">&lt;\/executions&gt;<\/span>\n    <span class=\"tag\">&lt;configuration&gt;<\/span>\n        <span class=\"tag\">&lt;dockerDirectory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/dockerDirectory&gt;<\/span>\n        <span class=\"tag\">&lt;imageName&gt;<\/span><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}:${git.commit.id.abbrev}<\/span><span class=\"tag\">&lt;\/imageName&gt;<\/span>\n        <span class=\"tag\">&lt;pushImage&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/pushImage&gt;<\/span>\n    <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n<span class=\"tag\">&lt;\/plugin&gt;<\/span><\/code><\/pre>\n<p>Our next challenge was to express our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfiles<\/span><\/code>&#8216; dependencies in our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">pom.xml<\/span><\/code>. Some of our docker images are built\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">FROM<\/span><\/code>\u00a0other docker images (which are built in the same build cycle). For example, our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">webgate<\/span><\/code>\u00a0image (which is our Tomcat based WebApp) is based on our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">base<\/span><\/code>\u00a0image (which contains Java 8, up-to-date apt-get, etc.).<\/p>\n<p>Having the images built in the same build procedure means we can&#8217;t just have\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">FROM docker-registry.alooma.com:5000\/alooma\/base:some-tag<\/span><\/code>\u00a0because we need the tag to be changed to the tag of the current build (i.e. the git commit ID).<\/p>\n<p>To gain access to these properties from within the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>, we use Maven&#8217;s\u00a0<em>resource filtering<\/em>. This replaces Maven properties in a resource file.<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;resource&gt;<\/span>\n    <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n    <span class=\"tag\">&lt;filtering&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/filtering&gt;<\/span>\n    <span class=\"tag\">&lt;includes&gt;<\/span>\n        <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">**\/Dockerfile<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n    <span class=\"tag\">&lt;\/includes&gt;<\/span>\n<span class=\"tag\">&lt;\/resource&gt;<\/span><\/code><\/pre>\n<p>And inside the Dockerfile we have a\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">FROM<\/span><\/code>\u00a0as follows:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">FROM ${docker.registry}alooma\/base:${git.commit.id.abbrev}<\/span><\/code><\/pre>\n<p>A couple more things&#8230; We need to make our configuration look at the correct\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>\u00a0(the one after filtering), which is found inside the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">target\/classes<\/span><\/code>\u00a0folder. So we change the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">dockerDirectory<\/span><\/code>\u00a0to\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">${project.build.directory}\/classes<\/span><\/code>.<\/p>\n<p>Meaning that now our configuration looks like this:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;resources&gt;<\/span>\n    <span class=\"tag\">&lt;resource&gt;<\/span>\n        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n        <span class=\"tag\">&lt;filtering&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/filtering&gt;<\/span>\n        <span class=\"tag\">&lt;includes&gt;<\/span>\n            <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">**\/Dockerfile<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n        <span class=\"tag\">&lt;\/includes&gt;<\/span>\n    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n<span class=\"tag\">&lt;\/resources&gt;<\/span>\n<span class=\"tag\">&lt;pluginManagement&gt;<\/span>\n    <span class=\"tag\">&lt;plugins&gt;<\/span>\n        <span class=\"tag\">&lt;plugin&gt;<\/span>\n            <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.spotify<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n            <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">docker-maven-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n            <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">0.2.3<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n            <span class=\"tag\">&lt;executions&gt;<\/span>\n                <span class=\"tag\">&lt;execution&gt;<\/span>\n                    <span class=\"tag\">&lt;phase&gt;<\/span><span class=\"pln\">package<\/span><span class=\"tag\">&lt;\/phase&gt;<\/span>\n                    <span class=\"tag\">&lt;goals&gt;<\/span>\n                        <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">build<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n                    <span class=\"tag\">&lt;\/goals&gt;<\/span>\n                <span class=\"tag\">&lt;\/execution&gt;<\/span>\n            <span class=\"tag\">&lt;\/executions&gt;<\/span>\n            <span class=\"tag\">&lt;configuration&gt;<\/span>\n                <span class=\"tag\">&lt;dockerDirectory&gt;<\/span><span class=\"pln\">${project.build.directory}\/classes<\/span><span class=\"tag\">&lt;\/dockerDirectory&gt;<\/span>\n                <span class=\"tag\">&lt;pushImage&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/pushImage&gt;<\/span>\n                <span class=\"tag\">&lt;imageName&gt;<\/span><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}:${git.commit.id.abbrev}<\/span><span class=\"tag\">&lt;\/imageName&gt;<\/span>\n            <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n        <span class=\"tag\">&lt;\/plugin&gt;<\/span>\n    <span class=\"tag\">&lt;\/plugins&gt;<\/span>\n<span class=\"tag\">&lt;\/pluginManagement&gt;<\/span><\/code><\/pre>\n<p>Additionally, we&#8217;ll add the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">base<\/span><\/code>\u00a0artifact as a Maven dependency of the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">webgate<\/span><\/code>\u00a0module in order to ensure the correct Maven build order.<\/p>\n<p>But we still have another challenge: how do we insert our compiled &amp; packaged sources to our docker images? Many of our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">Dockerfile<\/span><\/code>s depend on some other files, inserted with an\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">ADD<\/span><\/code>\u00a0or\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">COPY<\/span><\/code>\u00a0command. (You can read about more Dockerfile instructions\u00a0<a href=\"https:\/\/docs.docker.com\/reference\/builder\/\" target=\"_blank\" rel=\"noopener\">here<\/a>)<\/p>\n<p>To make these files accessible, we need to use the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">resources<\/span><\/code>\u00a0tag of the plugin configuration.<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;resources&gt;<\/span>\n    <span class=\"tag\">&lt;resource&gt;<\/span>\n        <span class=\"tag\">&lt;targetPath&gt;<\/span><span class=\"pln\">\/<\/span><span class=\"tag\">&lt;\/targetPath&gt;<\/span>\n        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n        <span class=\"tag\">&lt;excludes&gt;<\/span>\n            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">target\/**\/*<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">pom.xml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">*.iml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n        <span class=\"tag\">&lt;\/excludes&gt;<\/span>\n    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n<span class=\"tag\">&lt;\/resources&gt;<\/span><\/code><\/pre>\n<p>Notice that we&#8217;ve excluded some files (which are not actually required by the docker).<\/p>\n<p>Keep in mind that this\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">resources<\/span><\/code>\u00a0tag should not be confused with the general Maven\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">resources<\/span><\/code>\u00a0tag. Take a look at the following example, which is a part of our pom.xml:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;resources&gt;<\/span>            <span class=\"com\">&lt;!-- general Maven resources --&gt;<\/span>\n    <span class=\"tag\">&lt;resource&gt;<\/span>\n        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n        <span class=\"tag\">&lt;filtering&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/filtering&gt;<\/span>\n        <span class=\"tag\">&lt;includes&gt;<\/span>\n            <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">**\/Dockerfile<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n        <span class=\"tag\">&lt;\/includes&gt;<\/span>\n    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n<span class=\"tag\">&lt;\/resources&gt;<\/span>\n<span class=\"tag\">&lt;pluginManagement&gt;<\/span>\n    <span class=\"tag\">&lt;plugins&gt;<\/span>\n        <span class=\"tag\">&lt;plugin&gt;<\/span>\n            <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.spotify<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n            <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">docker-maven-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n            <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">0.2.3<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n            <span class=\"tag\">&lt;executions&gt;<\/span>\n                <span class=\"tag\">&lt;execution&gt;<\/span>\n                    <span class=\"tag\">&lt;phase&gt;<\/span><span class=\"pln\">package<\/span><span class=\"tag\">&lt;\/phase&gt;<\/span>\n                    <span class=\"tag\">&lt;goals&gt;<\/span>\n                        <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">build<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n                    <span class=\"tag\">&lt;\/goals&gt;<\/span>\n                <span class=\"tag\">&lt;\/execution&gt;<\/span>\n            <span class=\"tag\">&lt;\/executions&gt;<\/span>\n            <span class=\"tag\">&lt;configuration&gt;<\/span>\n                <span class=\"tag\">&lt;dockerDirectory&gt;<\/span><span class=\"pln\">${project.build.directory}\/classes<\/span><span class=\"tag\">&lt;\/dockerDirectory&gt;<\/span>\n                <span class=\"tag\">&lt;pushImage&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/pushImage&gt;<\/span>\n                <span class=\"tag\">&lt;imageName&gt;<\/span><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}:${git.commit.id.abbrev}<\/span><span class=\"tag\">&lt;\/imageName&gt;<\/span>\n                <span class=\"tag\">&lt;resources&gt;<\/span>        <span class=\"com\">&lt;!-- Dockerfile building resources --&gt;<\/span>\n                    <span class=\"tag\">&lt;resource&gt;<\/span>\n                        <span class=\"tag\">&lt;targetPath&gt;<\/span><span class=\"pln\">\/<\/span><span class=\"tag\">&lt;\/targetPath&gt;<\/span>\n                        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n                        <span class=\"tag\">&lt;excludes&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">target\/**\/*<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">pom.xml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">*.iml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                        <span class=\"tag\">&lt;\/excludes&gt;<\/span>\n                    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n                <span class=\"tag\">&lt;\/resources&gt;<\/span>\n            <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n        <span class=\"tag\">&lt;\/plugin&gt;<\/span>\n    <span class=\"tag\">&lt;\/plugins&gt;<\/span>\n<span class=\"tag\">&lt;\/pluginManagement&gt;<\/span><\/code><\/pre>\n<p>The previous addition works when we want to add static resources to the image, but requires a little more finesse if we want to add an artifact which is built in the same build.<\/p>\n<p>For example, our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">webgate<\/span><\/code>\u00a0docker image contains our\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">webgate.war<\/span><\/code>, which is built by another module.<\/p>\n<p>To add this .war as a resource, we first must add it as a Maven dependency and\u00a0<em>then<\/em>\u00a0use the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">copy<\/span><\/code>\u00a0goal of the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">maven-dependency-plugin<\/span><\/code>\u00a0to add it to our current build folder.<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;plugin&gt;<\/span>\n    <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">org.apache.maven.plugins<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n    <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">maven-dependency-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n    <span class=\"tag\">&lt;executions&gt;<\/span>\n        <span class=\"tag\">&lt;execution&gt;<\/span>\n            <span class=\"tag\">&lt;goals&gt;<\/span>\n                <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">copy<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n            <span class=\"tag\">&lt;\/goals&gt;<\/span>\n            <span class=\"tag\">&lt;configuration&gt;<\/span>\n                <span class=\"tag\">&lt;artifactItems&gt;<\/span>\n                    <span class=\"tag\">&lt;artifactItem&gt;<\/span>\n                        <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.alooma<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n                        <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">webgate<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n                        <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">${project.parent.version}<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n                        <span class=\"tag\">&lt;type&gt;<\/span><span class=\"pln\">war<\/span><span class=\"tag\">&lt;\/type&gt;<\/span>\n                        <span class=\"tag\">&lt;outputDirectory&gt;<\/span><span class=\"pln\">${project.build.directory}<\/span><span class=\"tag\">&lt;\/outputDirectory&gt;<\/span>\n                        <span class=\"tag\">&lt;destFileName&gt;<\/span><span class=\"pln\">webgate.war<\/span><span class=\"tag\">&lt;\/destFileName&gt;<\/span>\n                    <span class=\"tag\">&lt;\/artifactItem&gt;<\/span>\n                <span class=\"tag\">&lt;\/artifactItems&gt;<\/span>\n            <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n        <span class=\"tag\">&lt;\/execution&gt;<\/span>\n    <span class=\"tag\">&lt;\/executions&gt;<\/span>\n<span class=\"tag\">&lt;\/plugin&gt;<\/span><\/code><\/pre>\n<p>And now this allows us to simply add this file to the resources of the docker plugin:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;resources&gt;<\/span>\n    <span class=\"tag\">&lt;resource&gt;<\/span>\n        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n        <span class=\"tag\">&lt;filtering&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/filtering&gt;<\/span>\n        <span class=\"tag\">&lt;includes&gt;<\/span>\n            <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">**\/Dockerfile<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n        <span class=\"tag\">&lt;\/includes&gt;<\/span>\n    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n<span class=\"tag\">&lt;\/resources&gt;<\/span>\n<span class=\"tag\">&lt;pluginManagement&gt;<\/span>\n    <span class=\"tag\">&lt;plugins&gt;<\/span>\n        <span class=\"tag\">&lt;plugin&gt;<\/span>\n            <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.spotify<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n            <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">docker-maven-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n            <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">0.2.3<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n            <span class=\"tag\">&lt;executions&gt;<\/span>\n                <span class=\"tag\">&lt;execution&gt;<\/span>\n                    <span class=\"tag\">&lt;phase&gt;<\/span><span class=\"pln\">package<\/span><span class=\"tag\">&lt;\/phase&gt;<\/span>\n                    <span class=\"tag\">&lt;goals&gt;<\/span>\n                        <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">build<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n                    <span class=\"tag\">&lt;\/goals&gt;<\/span>\n                <span class=\"tag\">&lt;\/execution&gt;<\/span>\n            <span class=\"tag\">&lt;\/executions&gt;<\/span>\n            <span class=\"tag\">&lt;configuration&gt;<\/span>\n                <span class=\"tag\">&lt;dockerDirectory&gt;<\/span><span class=\"pln\">${project.build.directory}\/classes<\/span><span class=\"tag\">&lt;\/dockerDirectory&gt;<\/span>\n                <span class=\"tag\">&lt;pushImage&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/pushImage&gt;<\/span>\n                <span class=\"tag\">&lt;imageName&gt;<\/span><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}:${git.commit.id.abbrev}<\/span><span class=\"tag\">&lt;\/imageName&gt;<\/span>\n                <span class=\"tag\">&lt;resources&gt;<\/span>\n                    <span class=\"tag\">&lt;resource&gt;<\/span>\n                        <span class=\"tag\">&lt;targetPath&gt;<\/span><span class=\"pln\">\/<\/span><span class=\"tag\">&lt;\/targetPath&gt;<\/span>\n                        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n                        <span class=\"tag\">&lt;excludes&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">target\/**\/*<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">pom.xml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">*.iml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                        <span class=\"tag\">&lt;\/excludes&gt;<\/span>\n                    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n                    <span class=\"tag\">&lt;rescource&gt;<\/span>\n                        <span class=\"tag\">&lt;targetPath&gt;<\/span><span class=\"pln\">\/<\/span><span class=\"tag\">&lt;\/targetPath&gt;<\/span>\n                        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.build.directory}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n                        <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">webgate.war<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n                    <span class=\"tag\">&lt;\/rescource&gt;<\/span>\n                <span class=\"tag\">&lt;\/resources&gt;<\/span>\n            <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n        <span class=\"tag\">&lt;\/plugin&gt;<\/span>\n    <span class=\"tag\">&lt;\/plugins&gt;<\/span>\n<span class=\"tag\">&lt;\/pluginManagement&gt;<\/span><\/code><\/pre>\n<p>The last thing we need to do is have our CI server (Jenkins) actually push images to the docker registry. Keep in mind that local builds, by default, should not push images.<\/p>\n<p>To push the images, we change the\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;pushImage&gt;<\/span><\/code>\u00a0tag from\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">true<\/span><\/code>\u00a0to a\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">${push.image}<\/span><\/code>\u00a0property which will by default be set to\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">false<\/span><\/code>, and only be set to\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">true<\/span><\/code>\u00a0in the CI build.<\/p>\n<p>That&#8217;s it! Let&#8217;s see the final code:<\/p>\n<pre><code class=\"prettyprint lang-xml prettyprinted\"><span class=\"tag\">&lt;resources&gt;<\/span>\n    <span class=\"tag\">&lt;resource&gt;<\/span>\n        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n        <span class=\"tag\">&lt;filtering&gt;<\/span><span class=\"pln\">true<\/span><span class=\"tag\">&lt;\/filtering&gt;<\/span>\n        <span class=\"tag\">&lt;includes&gt;<\/span>\n            <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">**\/Dockerfile<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n        <span class=\"tag\">&lt;\/includes&gt;<\/span>\n    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n<span class=\"tag\">&lt;\/resources&gt;<\/span>\n<span class=\"tag\">&lt;pluginManagement&gt;<\/span>\n    <span class=\"tag\">&lt;plugins&gt;<\/span>\n        <span class=\"tag\">&lt;plugin&gt;<\/span>\n            <span class=\"tag\">&lt;groupId&gt;<\/span><span class=\"pln\">com.spotify<\/span><span class=\"tag\">&lt;\/groupId&gt;<\/span>\n            <span class=\"tag\">&lt;artifactId&gt;<\/span><span class=\"pln\">docker-maven-plugin<\/span><span class=\"tag\">&lt;\/artifactId&gt;<\/span>\n            <span class=\"tag\">&lt;version&gt;<\/span><span class=\"pln\">0.2.3<\/span><span class=\"tag\">&lt;\/version&gt;<\/span>\n            <span class=\"tag\">&lt;executions&gt;<\/span>\n                <span class=\"tag\">&lt;execution&gt;<\/span>\n                    <span class=\"tag\">&lt;phase&gt;<\/span><span class=\"pln\">package<\/span><span class=\"tag\">&lt;\/phase&gt;<\/span>\n                    <span class=\"tag\">&lt;goals&gt;<\/span>\n                        <span class=\"tag\">&lt;goal&gt;<\/span><span class=\"pln\">build<\/span><span class=\"tag\">&lt;\/goal&gt;<\/span>\n                    <span class=\"tag\">&lt;\/goals&gt;<\/span>\n                <span class=\"tag\">&lt;\/execution&gt;<\/span>\n            <span class=\"tag\">&lt;\/executions&gt;<\/span>\n            <span class=\"tag\">&lt;configuration&gt;<\/span>\n                <span class=\"tag\">&lt;dockerDirectory&gt;<\/span><span class=\"pln\">${project.build.directory}\/classes<\/span><span class=\"tag\">&lt;\/dockerDirectory&gt;<\/span>\n\n                <span class=\"tag\">&lt;pushImage&gt;<\/span><span class=\"pln\">${push.image}<\/span><span class=\"tag\">&lt;\/pushImage&gt;<\/span>      <span class=\"com\">&lt;!-- true when Jenkins builds, false otherwise --&gt;<\/span>\n\n                <span class=\"tag\">&lt;imageName&gt;<\/span><span class=\"pln\">${docker.registry}alooma\/${project.artifactId}:${git.commit.id.abbrev}<\/span><span class=\"tag\">&lt;\/imageName&gt;<\/span>\n                <span class=\"tag\">&lt;resources&gt;<\/span>\n                    <span class=\"tag\">&lt;resource&gt;<\/span>\n                        <span class=\"tag\">&lt;targetPath&gt;<\/span><span class=\"pln\">\/<\/span><span class=\"tag\">&lt;\/targetPath&gt;<\/span>\n                        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.basedir}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n                        <span class=\"tag\">&lt;excludes&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">target\/**\/*<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">pom.xml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                            <span class=\"tag\">&lt;exclude&gt;<\/span><span class=\"pln\">*.iml<\/span><span class=\"tag\">&lt;\/exclude&gt;<\/span>\n                        <span class=\"tag\">&lt;\/excludes&gt;<\/span>\n                    <span class=\"tag\">&lt;\/resource&gt;<\/span>\n                    <span class=\"tag\">&lt;rescource&gt;<\/span>\n                        <span class=\"tag\">&lt;targetPath&gt;<\/span><span class=\"pln\">\/<\/span><span class=\"tag\">&lt;\/targetPath&gt;<\/span>\n                        <span class=\"tag\">&lt;directory&gt;<\/span><span class=\"pln\">${project.build.directory}<\/span><span class=\"tag\">&lt;\/directory&gt;<\/span>\n                        <span class=\"tag\">&lt;include&gt;<\/span><span class=\"pln\">webgate.war<\/span><span class=\"tag\">&lt;\/include&gt;<\/span>\n                    <span class=\"tag\">&lt;\/rescource&gt;<\/span>\n                <span class=\"tag\">&lt;\/resources&gt;<\/span>\n            <span class=\"tag\">&lt;\/configuration&gt;<\/span>\n        <span class=\"tag\">&lt;\/plugin&gt;<\/span>\n    <span class=\"tag\">&lt;\/plugins&gt;<\/span>\n<span class=\"tag\">&lt;\/pluginManagement&gt;<\/span><\/code><\/pre>\n<h3 id=\"performance\">Performance<\/h3>\n<p>There are two additions to this process which will improve the performance of your builds and deploys:<\/p>\n<ul>\n<li>Have your base machine image (AMI in case of EC2) contain some basic versions of your docker images. This will cause a\u00a0<code class=\"prettyprint lang-xml prettyprinted\"><span class=\"pln\">docker pull<\/span><\/code>\u00a0to pull only the layers that have changed, the delta (which are much smaller than the entire image).<\/li>\n<li>Put a Redis cache in front of the docker registry. This caches the tags and meta-data and reduces roundtrips to the actual storage (S3 in our case).<\/li>\n<\/ul>\n<p>We have been using this build process for some time now and are quite happy with it. However, there&#8217;s always room for improvement&#8230; if you have any tips on smoothing this out even further, I&#8217;d be happy to hear your thoughts in the comments.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>At Alooma, we\u00a0loooove\u00a0dockers. It&#8217;s true. We try to run as much as we can inside docker containers. While there are [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-334","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/posts\/334","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=334"}],"version-history":[{"count":0,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/posts\/334\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=334"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=334"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=334"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}