{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/blog/2016/08/08/docker-pipeline-environments/",
    "result": {"data":{"blog":{"html":"<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<i class=\"fa icon-note\" title=\"Note\"></i>\n</td>\n<td class=\"content\">\n<div class=\"paragraph\">\n<p>This is a guest post by <a href=\"https://github.com/michaelneale\">Michael Neale</a>, long time open\nsource developer and contributor to the <a href=\"/projects/blueocean\">Blue Ocean</a>\nproject.</p>\n</div>\n</td>\n</tr>\n</table>\n</div>\n<div class=\"paragraph\">\n<p>If you are running parts of your pipeline on Linux, possibly the easiest way to\nget a clean reusable environment is to use:\n<a href=\"https://go.cloudbees.com/docs/cloudbees-documentation/cje-user-guide/chapter-docker-workflow.html\">CloudBees\nDocker Pipeline plugin</a>.</p>\n</div>\n<div class=\"paragraph\">\n<p>In this short post I wanted to show how you can avoid installing stuff on the agents, and have per project, or even per branch, customized build environments.\nYour environment, as well as your pipeline is defined and versioned alongside your code.</p>\n</div>\n<div class=\"paragraph\">\n<p>I wanted to use the <a href=\"/doc/book/blueocean\">Blue Ocean</a> project as an\n<a href=\"https://github.com/jenkinsci/jenkins-design-language/\">example</a> of a\nproject that uses the CloudBees Docker Pipeline plugin.</p>\n</div>\n<div class=\"sect2\">\n<h3 id=\"environment-and-pipeline-for-javascript-components\"><a class=\"anchor\" href=\"#environment-and-pipeline-for-javascript-components\"></a>Environment and Pipeline for JavaScript components</h3>\n<div class=\"paragraph\">\n<p>The <a href=\"/projects/blueocean\">Blue Ocean</a> project has a few moving parts, one of\nwhich is called the \"Jenkins Design Language\".  This is a grab bag of re-usable\nCSS, HTML, style rules, icons and JavaScript components (using React.js) that\nprovide the look and feel for Blue Ocean.</p>\n</div>\n<div class=\"paragraph\">\n<p>JavaScript and Web Development being what it is in 2016, many utilities are\nneed to assemble a web app.  This includes npm and all that it needs, less.js\nto convert Less to CSS, Babel to \"transpile\" versions of JavaScript to other\ntypes of JavaScript (don&#8217;t ask) and more.</p>\n</div>\n<div class=\"paragraph\">\n<p>We could spend time installling nodejs/npm on the agents, but why not just use\nthe <a href=\"https://hub.docker.com/_/node/\">official off the shelf</a> docker image\nfrom <a href=\"https://hub.docker.com\">Docker Hub</a>?</p>\n</div>\n<div class=\"paragraph\">\n<p>The only thing that has to be installed and run on the build agents is the Jenkins agent, and a docker daemon.</p>\n</div>\n<div class=\"paragraph\">\n<p>A simple pipeline using this approach would be:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight nowrap\"><code class=\"language-groovy\" data-lang=\"groovy\">node {\n        stage \"Prepare environment\"\n          checkout scm\n          docker.image('node').inside {\n            stage \"Checkout and build deps\"\n                sh \"npm install\"\n\n            stage \"Test and validate\"\n                sh \"npm install gulp-cli &amp;&amp; ./node_modules/.bin/gulp\"\n          }\n}</code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>This uses the stock \"official\" Node.js image from the Docker Hub, but doesn&#8217;t let us customize much about the environment.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"customising-the-environment-without-installing-bits-on-the-agent\"><a class=\"anchor\" href=\"#customising-the-environment-without-installing-bits-on-the-agent\"></a>Customising the environment, without installing bits on the agent</h3>\n<div class=\"paragraph\">\n<p>Being the forward looking and lazy person that I am, I didn&#8217;t want to have to\ngo and fish around for a Docker image every time a developer wanted something\nspecial installed.</p>\n</div>\n<div class=\"paragraph\">\n<p>Instead, I put a <code>Dockerfile</code> in the root of the repo, alongside the <code>Jenkinsfile</code>:</p>\n</div>\n<div class=\"imageblock center\">\n<div class=\"content\">\n<img src=\"/images/post-images/2016-08-03/environment_jenkinsfile.png\" alt=\"Environment\">\n</div>\n</div>\n<div class=\"paragraph\">\n<p>The contents of the <code>Dockerfile</code> can then define the exact environment needed\nto build the project.  Sure enough, shortly after this, someone came along\nsaying they wanted to use <a href=\"https://flowtype.org/\">Flow</a> from Facebook (A\ntypechecker for JavaScript).  This required an additional native component to\nwork (via <code>apt-get install</code>).</p>\n</div>\n<div class=\"paragraph\">\n<p>This was achieved via a\n<a href=\"https://github.com/jenkinsci/jenkins-design-language/pull/72/files\">pull\nrequest</a> to both the <code>Jenkinsfile</code> and the <code>Dockerfile</code> at the same time.</p>\n</div>\n<div class=\"paragraph\">\n<p>So now our environment is defined by a <code>Dockerfile</code> with the following contents:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight nowrap\"><code class=\"language-shell\" data-lang=\"shell\"># Lets not just use any old version but pick one\nFROM node:5.11.1\n\n# This is needed for flow, and the weirdos that built it in ocaml:\nRUN apt-get update &amp;&amp; apt-get install -y libelf1\n\nRUN useradd jenkins --shell /bin/bash --create-home\nUSER jenkins</code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>The <code>Jenkinsfile</code> pipeline now has the following contents:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight nowrap\"><code class=\"language-groovy\" data-lang=\"groovy\">node {\n    stage \"Prepare environment\"\n        checkout scm\n        def environment  = docker.build 'cloudbees-node'\n\n        environment.inside {\n            stage \"Checkout and build deps\"\n                sh \"npm install\"\n\n            stage \"Validate types\"\n                sh \"./node_modules/.bin/flow\"\n\n            stage \"Test and validate\"\n                sh \"npm install gulp-cli &amp;&amp; ./node_modules/.bin/gulp\"\n                junit 'reports/**/*.xml'\n        }\n\n    stage \"Cleanup\"\n        deleteDir()\n}</code></pre>\n</div>\n</div>\n<div class=\"admonitionblock tip\">\n<table>\n<tr>\n<td class=\"icon\">\n<i class=\"fa icon-tip\" title=\"Tip\"></i>\n</td>\n<td class=\"content\">\nEven hip JavaScript tools can emit that weird XML format that test\nreporters can use, e.g. the junit result archiver.\n</td>\n</tr>\n</table>\n</div>\n<div class=\"paragraph\">\n<p>The main change is that we have <code>docker.build</code> being called to produce the\n<code>environment</code> which is then used.  Running <code>docker build</code> is essentially a\n\"no-op\" if the image has already been built on the agent before.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"whats-it-like-to-drive\"><a class=\"anchor\" href=\"#whats-it-like-to-drive\"></a>What&#8217;s it like to drive?</h3>\n<div class=\"paragraph\">\n<p>Well, using Blue Ocean, to build Blue Ocean, yields a pipeline that visually\nlooks like this (a recent run I screen capped):</p>\n</div>\n<div class=\"imageblock center\">\n<div class=\"content\">\n<img src=\"/images/post-images/2016-08-03/JDL_pipeline.png\" alt=\"Pipeline\">\n</div>\n</div>\n<div class=\"paragraph\">\n<p>This creates a pipeline that developers can tweak on a pull-request basis,\nalong with any changes to the environment needed to support it, without having\nto install any packages on the agent.</p>\n</div>\n<div class=\"sect3\">\n<h4 id=\"why-not-use-docker-commands-directly\"><a class=\"anchor\" href=\"#why-not-use-docker-commands-directly\"></a>Why not use docker commands directly?</h4>\n<div class=\"paragraph\">\n<p>You could of course just use shell commands to do things with Docker directly,\nhowever, Jenkins Pipeline keeps track of Docker images used in a <code>Dockerfile</code>\nvia the \"Docker Fingerprints\" link (which is good, should that image need to\nchange due to a security patch).</p>\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"links\"><a class=\"anchor\" href=\"#links\"></a>Links</h3>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>The project used as as an example is <a href=\"https://github.com/jenkinsci/jenkins-design-language/\">here</a></p>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>The pipeline is defined by the <a href=\"https://github.com/jenkinsci/jenkins-design-language/blob/master/Jenkinsfile\">Jenkinsfile</a></p>\n</li>\n<li>\n<p>The environment is defined by the <a href=\"https://github.com/jenkinsci/jenkins-design-language/blob/master/Dockerfile\">Dockerfile</a></p>\n</li>\n</ul>\n</div>\n</li>\n<li>\n<p>Read more on <a href=\"https://go.cloudbees.com/docs/cloudbees-documentation/cje-user-guide/chapter-docker-workflow.html\">Docker Pipeline</a></p>\n</li>\n</ul>\n</div>\n</div>","id":"649a2e8e-4f2f-56eb-99f1-8897de882a49","title":"Don't install software, define your environment with Docker and Pipeline","date":"2016-08-08T00:00:00.000Z","slug":"/blog/2016/08/08/docker-pipeline-environments/","links":{"discourse":""},"authors":[{"avatar":{"childImageSharp":{"gatsbyImageData":{"layout":"constrained","backgroundColor":"#181818","images":{"fallback":{"src":"/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/19e71/michaelneale.jpg","srcSet":"/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/77b35/michaelneale.jpg 32w,\n/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/d4a57/michaelneale.jpg 64w,\n/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/19e71/michaelneale.jpg 128w","sizes":"(min-width: 128px) 128px, 100vw"},"sources":[{"srcSet":"/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/ef6ff/michaelneale.webp 32w,\n/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/8257c/michaelneale.webp 64w,\n/gatsby-jenkins-io/static/75c8520897a1db139d524965f5bb7ccc/6766a/michaelneale.webp 128w","type":"image/webp","sizes":"(min-width: 128px) 128px, 100vw"}]},"width":128,"height":128}}},"blog":null,"github":"michaelneale","html":"<div class=\"paragraph\">\n<p>Michael is a CD enthusiast with a interest in User Experience.\nHe is a co-founder of CloudBees and a long time OSS developer, and can often be found\nlurking around the jenkins-dev mailing list or #jenkins on irc (same nick as twitter name).\nBefore CloudBees he worked at Red Hat.</p>\n</div>","id":"michaelneale","irc":null,"linkedin":null,"name":"Michael Neale","slug":"/blog/authors/michaelneale","twitter":"michaelneale"}]}},"pageContext":{"next":"/blog/2016/08/09/ewm-beta-version/","previous":"/blog/2016/08/03/st-petersburg-jam-3-4-report/","id":"649a2e8e-4f2f-56eb-99f1-8897de882a49"}},
    "staticQueryHashes": ["1271460761","3649515864"]}