<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/groovy/</id>
  <link href="https://www.endpointdev.com/blog/tags/groovy/"/>
  <link href="https://www.endpointdev.com/blog/tags/groovy/" rel="self"/>
  <updated>2020-05-25T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Designing flexible CI pipelines with Jenkins and Docker</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/05/flexible-ci-pipelines-jenkins-docker/"/>
      <id>https://www.endpointdev.com/blog/2020/05/flexible-ci-pipelines-jenkins-docker/</id>
      <published>2020-05-25T00:00:00+00:00</published>
      <author>
        <name>Will Plaut</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/05/flexible-ci-pipelines-jenkins-docker/pipes.jpg&#34; alt=&#34;Pipes&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/9AxFJaNySB8&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@realaxer&#34;&gt;Tian Kuan&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When deciding on how to implement continuous integration (CI) for a new project, you are presented with lots of choices. Whatever you end up choosing, your CI needs to work for you and your team. Keeping the CI process and its mechanisms clear and concise helps everyone working on the project. The setup we are currently employing, and what I am going to showcase here, has proven to be flexible and powerful. Specifically, I’m going to highlight some of the things Jenkins and Docker do that are really helpful.&lt;/p&gt;
&lt;h3 id=&#34;jenkins&#34;&gt;Jenkins&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.jenkins.io/&#34;&gt;Jenkins&lt;/a&gt; provides us with all the CI functionality we need and it can be easily configured to connect to projects on GitHub and our internal GitLab. Jenkins has support for something it calls a multibranch pipeline. A Jenkins project follows a repo and builds any branch that has a &lt;code&gt;Jenkinsfile&lt;/code&gt;. A &lt;code&gt;Jenkinsfile&lt;/code&gt; configures an individual pipeline that Jenkins runs against a repo on a branch, tag or merge request (MR).&lt;/p&gt;
&lt;p&gt;To keep it even simpler, we condense the steps that a &lt;code&gt;Jenkinsfile&lt;/code&gt; runs into shell scripts that live in &lt;code&gt;/scripts/&lt;/code&gt; at the root of the source repo to do things like test or build or deploy, such as &lt;code&gt;/scripts/test.sh&lt;/code&gt;. If a team member wants to know how the tests are run, it is right in that file to reference.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Jenkinsfile&lt;/code&gt; can be written in a declarative syntax or in plain Groovy. We have landed on the scripted Groovy syntax for its more fine-grained control of Docker containers. Jenkins also provides several ways to inspect and debug the pipelines with things like “Replay” in its GUI and using &lt;code&gt;input(&#39;wait here&#39;)&lt;/code&gt; in a pipeline to debug a troublesome step. The &lt;code&gt;input()&lt;/code&gt; function is especially useful when paired with Docker. The function allows us to pause the job and go to the Jenkins server where we use &lt;code&gt;docker ps&lt;/code&gt; to find the running container’s name. Then we use &lt;code&gt;docker exec -it {container name} bash&lt;/code&gt; to debug inside of the container with all of the Jenkins environment variables loaded. This has proven to be a great way to figure out why something isn’t working in our test stages.&lt;/p&gt;
&lt;h3 id=&#34;docker&#34;&gt;Docker&lt;/h3&gt;
&lt;p&gt;We love using &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt; for our development and deployment for a variety of reasons. First, creating a Dockerfile for a project is essentially an exercise in figuring out how a project is built with a minimum of dependencies. Once a Docker container is built, the running container provides a great place to run tests as it is a clean checkout with little to no extra cruft.&lt;/p&gt;
&lt;p&gt;Using our Jenkins pipeline, we can take builds triggered by tags and push an associated tagged Docker image up to our registry. With Docker’s layering, pushes are often the shortest stage of the Jenkins job. Deploying that tag is as simple as doing a &lt;code&gt;docker pull&lt;/code&gt; on the target system. For the application deployment, we create a basic &lt;code&gt;docker-compose.yml&lt;/code&gt; to start and serve the project from within the container, forwarding whatever ports we need on the local system.&lt;/p&gt;
&lt;h3 id=&#34;example-jenkinsfile&#34;&gt;Example Jenkinsfile&lt;/h3&gt;
&lt;p&gt;Let’s take a look at a basic scripted &lt;code&gt;Jenkinsfile&lt;/code&gt; (scripted in Groovy) that utilizes a &lt;code&gt;Dockerfile&lt;/code&gt; in the source repo to build, test, and deploy a project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;node() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  properties([gitLabConnection(&amp;#39;gitlab-connect&amp;#39;)])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  def vueImage
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  def dockerTagName
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  stage(&amp;#39;Checkout&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    checkout scm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  stage(&amp;#39;Build&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vueImage = docker.build(&amp;#34;endpoint/vue-test&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  vueImage.inside(&amp;#39;-u 0&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stage(&amp;#39;Test&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      sh &amp;#39;./scripts/test.sh&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  stage(&amp;#39;Tag/Push&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    docker.withRegistry(&amp;#39;https://registry.hub.docker.com&amp;#39;, &amp;#39;ep_dockerhub_creds&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      if (env.TAG_NAME != null) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        vueImage.push(&amp;#34;${env.TAG_NAME}&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } else {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        vueImage.push(&amp;#34;${env.BRANCH_NAME}&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The script’s first stage, &lt;code&gt;Checkout&lt;/code&gt;, checks out the repo using our &lt;code&gt;gitlab-connect&lt;/code&gt; credentials that are stored on the Jenkins server. It then moves to the &lt;code&gt;Build&lt;/code&gt; stage where it builds the image using the &lt;code&gt;Dockerfile&lt;/code&gt; in our repo and names it after the org/repo it will use on DockerHub. Then, inside of the running container we enter the &lt;code&gt;Test&lt;/code&gt; stage where we run the repo script &lt;code&gt;./scripts/test.sh&lt;/code&gt;. After the &lt;code&gt;.inside&lt;/code&gt; code block is closed the running container is stopped and removed. Finally, we get to the &lt;code&gt;Tag/Push&lt;/code&gt; stage where we push our Docker image up to DockerHub using another set of stored credentials. We tag it with either the &lt;code&gt;TAG_NAME&lt;/code&gt; or the &lt;code&gt;BRANCH_NAME&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;Jenkinsfile&lt;/code&gt; provides us with a solid base to expand on. During development as requirements change, it’s easy to modify and update the &lt;code&gt;Jenkinsfile&lt;/code&gt;. We have the ability to run steps inside and outside of the Docker. Combined with bash scripts that live in the repo, we can do almost anything. Most of the job mechanics can be tuned, down to the specific status updates GitLab receives during a run.&lt;/p&gt;
&lt;p&gt;Say we want to handle a push a bit differently if the branch is named &lt;code&gt;Master&lt;/code&gt; or we want to add another stage and break out the &lt;code&gt;Test&lt;/code&gt; stage into &lt;code&gt;Unit Tests&lt;/code&gt; and &lt;code&gt;E2E Tests&lt;/code&gt;. These things are easily changed in the &lt;code&gt;Jenkinsfile&lt;/code&gt; and then run on Jenkins when pushed. There’s no need to merge to see the pipeline change. Every branch/​tag/​MR has its own pipeline. Deploying the Docker you just built is easy; just use your &lt;code&gt;TAG_NAME&lt;/code&gt; or &lt;code&gt;BRANCH_NAME&lt;/code&gt; with &lt;code&gt;docker pull endpoint/vue-test:{}&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Although the above script is just an example script, the &lt;code&gt;Jenkinsfile&lt;/code&gt;s we use in production are not far off from this in functionality and the ideas remain the same.&lt;/p&gt;
&lt;p&gt;Jenkins is not the easiest to configure as some of the required functionality comes from plugins, and getting the correct combination of plugins can be a challenge. That being said, the functionality it provides paired with Docker is amazing and definitely worth considering when setting up CI for a new project.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
