<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>gee-whiz</title>
    <description></description>
    <link>https://www.gee-whiz.de/</link>
    <atom:link href="https://www.gee-whiz.de/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sat, 15 Jun 2024 08:43:00 +0000</pubDate>
    <lastBuildDate>Sat, 15 Jun 2024 08:43:00 +0000</lastBuildDate>
    <generator>Jekyll v3.9.5</generator>
    
      <item>
        <title>State of the Art CI</title>
        <description>&lt;p&gt;Here you can find the &lt;a href=&quot;/assets/state-of-the-art-ci.pdf&quot;&gt;slides&lt;/a&gt; of my talk at the JUG Saxony Day 2018. If you have any question, email us :)&lt;/p&gt;

</description>
        <pubDate>Sun, 30 Sep 2018 16:55:21 +0000</pubDate>
        <link>https://www.gee-whiz.de/2018/state-of-the-art-ci/</link>
        <guid isPermaLink="true">https://www.gee-whiz.de/2018/state-of-the-art-ci/</guid>
        
        <category>jug</category>
        
        <category>ci</category>
        
        <category>jenkins</category>
        
        <category>bitbucket</category>
        
        <category>docker</category>
        
        <category>jira</category>
        
        <category>debugShell</category>
        
        
        <category>talks</category>
        
      </item>
    
      <item>
        <title>Extending build containers: Generic caching</title>
        <description>&lt;p&gt;Declaring your build environment by using Dockerfiles and creating separate containers for each and every build is good practice. But starting in a fresh environment each time prevents various caching mechanisms from having any effect. The result is an increase in build time as well as network traffic, where especially the latter can cause some bottlenecking to occur. This affects mainly git repositories, build tools like Maven and npm, but also Docker builds itself. The git repository has to be cloned from scratch. Dependencies for Maven and npm have to be fetched from their official repositories on every build, even if the majority did not change. In case of Docker builds, at least the base image must be pulled. The Docker build cache is also empty.&lt;/p&gt;

&lt;p&gt;This post covers a couple of methods to overcome these limitation while still keeping isolation and reproducibility guarantees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;tl;dr:&lt;/em&gt;&lt;/strong&gt; We create a Docker image that contains a generic template for building caches for git repositories, Maven artifacts and npm dependencies by &lt;em&gt;(a)&lt;/em&gt; cloning relevant git repositories into a single bare reference repository and &lt;em&gt;(b)&lt;/em&gt; searching for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; files and pre-fetching their dependencies, thus presenting depending builds with an immediately available cache. That image must be build either on a regular basis or on demand in order to keep the cache up to date.&lt;/p&gt;

&lt;p&gt;The details and full implementation can be found on &lt;a href=&quot;https://github.com/gee-whiz-de/_posts-2018-09-26-caching-buildcontainer&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;building-a-generic-caching-solution-for-each-tool&quot;&gt;Building a generic caching solution for each tool&lt;/h2&gt;

&lt;p&gt;The following sections show the taken approach for git, maven and npm. The caching of Docker images and builds will be discussed in a future blog post.&lt;/p&gt;

&lt;h3 id=&quot;setup-and-strategy&quot;&gt;Setup and strategy&lt;/h3&gt;

&lt;p&gt;Let’s make some assumptions before we begin. Assume we have a Docker image &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hub.gee-whiz.de/build-env:latest&lt;/code&gt; where builds are executed in. This contains our default build environment with a set of standard tools. This would also be the starting point for new users who want to use a build container in general.&lt;/p&gt;

&lt;p&gt;There’s also a central git server where multiple projects, each with multiple repositories, reside. Those are stored in hierarchical form like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git.gee-whiz.de/project/repository&lt;/code&gt;. We’re aiming to build a generic caching solution for each project for all its repositories. To do so, we create a separate so called &lt;em&gt;project specific Docker image&lt;/em&gt; with an embedded cache by extending our default build image and creating the actual cache as part of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build&lt;/code&gt; process. Say we have a project named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;website&lt;/code&gt;, the resulting image will be named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hub.gee-whiz.de/build-env-website:latest&lt;/code&gt;. That image will contain a cache for every repository of that project.&lt;/p&gt;

&lt;p&gt;The cache is now being build as follows. Depending on your environment and requirements, you may of course deviate in certain steps or add additional mechanisms:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Select a project we want to build an image with a suitable cache for.&lt;/li&gt;
  &lt;li&gt;Determine available git repositories of said project and clone them all in a single &lt;em&gt;bare&lt;/em&gt; &lt;em&gt;reference&lt;/em&gt; (&lt;a href=&quot;https://git-scm.com/docs/git-clone&quot;&gt;git doc&lt;/a&gt;) repository. This will act as the git cache.&lt;/li&gt;
  &lt;li&gt;Temporarily checkout the default branches of every repository. On each we
    &lt;ol&gt;
      &lt;li&gt;Search for Maven projects (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;) and build them with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--fail-never&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dependency:go-offline&lt;/code&gt; goal. The fetched artifacts will be stored in the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.m2&lt;/code&gt; folder.&lt;/li&gt;
      &lt;li&gt;Search for npm projects (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;) and install them. The fetched dependencies will be stored in the local npm cache.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now dive into the implementation details of each step involved. Note that we also support to optionally ignore repositories by setting the environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IGNORE_REPOS&lt;/code&gt;. There are also a bunch of other variables being used that have to be set appropriately. We come back to those later.&lt;/p&gt;

&lt;h3 id=&quot;git&quot;&gt;Git&lt;/h3&gt;

&lt;p&gt;Determine available git repositories for the project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;website&lt;/code&gt; from a central Atlassian Bitbuck instance:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;FILTER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IGNORE_REPOS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/,/&quot;|&quot;/g'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;REPOS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BITBUCKET_HTTP_CREDENTIALS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; https://bitbucket.gee-whiz.de/rest/api/1.0/projects/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/repos?limit&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;100 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    | jq &lt;span class=&quot;nt&quot;&gt;-cM&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.values[] | {name: .name, url: .links.clone[].href} | select(.url | contains(&quot;ssh://&quot;))'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Ev&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FILTER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Considering &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPOS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cMs&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'. | length'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; repositories for buildcontainer creation process: &quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPOS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cM&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.name '&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cMs&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Clone all git repositories into a single bare reference repository:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /var/tmp/cache/git &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git init &lt;span class=&quot;nt&quot;&gt;--bare&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;REPO &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPOS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;REPO_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cMr&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.name'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;REPO_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cMr&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.url'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git remote add &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_URL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git fetch &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Temporarily checkout the default branch of every repository:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /tmp/git &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;REPO &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPOS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;REPO_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cMr&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.name'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;REPO_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-cMr&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.url'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone &lt;span class=&quot;nt&quot;&gt;--reference&lt;/span&gt; /var/tmp/cache/git &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_URL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; /tmp/git/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;prefetch-maven-dependencies&quot;&gt;Prefetch Maven dependencies&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;REPO_DIR &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /tmp/git/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Scanning for Maven project in &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/pom.xml &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Found pom.xml in &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;JAVA_HOME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;JDK_HOME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;MAVEN_HOME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/bin/mvn &lt;span class=&quot;nt&quot;&gt;-gs&lt;/span&gt; /tmp/build/maven-global-settings.xml &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-V&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--fail-never&lt;/span&gt; org.apache.maven.plugins:maven-dependency-plugin:3.0.2:go-offline &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;prefetch-npm-dependencies&quot;&gt;Prefetch npm dependencies&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODEJS_HOME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/bin &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;REPO_DIR &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /tmp/git/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Scanning for npm projects in &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/*&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE_PACKAGE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-maxdepth&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; package.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;NODE_PACKAGE_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dirname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODE_PACKAGE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Found package.json in &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODE_PACKAGE_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODE_PACKAGE_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class=&quot;nt&quot;&gt;--globalconfig&lt;/span&gt; /tmp/build/npm-global-rc &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-onbuild&quot;&gt;Using ONBUILD&lt;/h2&gt;

&lt;p&gt;Instead of copying the above bash lines in each and every project specific Dockerfile, in practice we use Dockers &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#onbuild&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ONBUILD&lt;/code&gt;&lt;/a&gt; instruction: We create a separate &lt;em&gt;template&lt;/em&gt; image called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hub.gee-whiz.de/build-env-template:latest&lt;/code&gt; which inherits from the default build image and contains instructions in the form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ONBUILD RUN build_cache.sh&lt;/code&gt;. Those instructions are then being executed when a dependent child image is built. Thus moving the generic caching implementation as some kind of template into a central place, thereby improving maintainability. The project the cache should be build for must be given as a build parameter.&lt;/p&gt;

&lt;p&gt;We’re now able to create an arbitrary amount of project specific images by just inheriting from that single template image. If no further customization is needed, the Dockerfile for the image  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hub.gee-whiz.de/build-env-website:latest&lt;/code&gt; would just be:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; hub.gee-whiz.de/build-env-template:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;creating-the-project-specific-image-on-jenkins&quot;&gt;Creating the project specific image on Jenkins&lt;/h2&gt;

&lt;p&gt;The previous bash snippets were using a couple of environment variables we’re now going to address. Included where locations for specific tools, configuration files, credentials and of course the projects name. As these kind of artifacts should not be included directly within the Docker image (to maintain a single location for configurations and for security reasons), we inject them during build time with the help of Jenkins.&lt;/p&gt;

&lt;p&gt;We also implement the pipeline as a shared library. Thus allowing a simple usage and again, maintainability. It’s implemented as a function named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildProjectSpecificDockerImage&lt;/code&gt; and looks like this:&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;buildProjectSpecificDockerImage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;triggeredBy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'../build-env-template/master'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'website'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ignoreRepos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'testing'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'old'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The build is triggered whenever the extended template-image was built. We pass the project name the cache should be build for, but also the repositories to ignore. Optionally, the function also allows to specify custom configuration files, credentials or specific tool to use for building the image. You can see the full implementation on &lt;a href=&quot;https://github.com/gee-whiz-de/_posts-2018-09-26-caching-buildcontainer/blob/master/jenkins-shared-library/vars/buildProjectSpecificDockerImage.groovy&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;On the premise these &lt;em&gt;project specific build images&lt;/em&gt; are build either on a regular basis or on demand, builds running inside those containers have immediate access to the cache for git, Maven and npm. If the image is not quite up to date, for instance when a dependency was bumped recently, only that specific artifact musst be fetched. Same principle goes for the git repository.&lt;/p&gt;

&lt;p&gt;Thanks to Dockers image layering technique, the cache can be shared by multiple in parallel running builds on the same host while still being isolated. To further improve build time, the build image might also be distributed to build nodes in advance, right after the build image is created. This alleviates possibly time consuming on demand image pulls.&lt;/p&gt;
</description>
        <pubDate>Wed, 26 Sep 2018 00:00:00 +0000</pubDate>
        <link>https://www.gee-whiz.de/2018/caching-buildcontainer/</link>
        <guid isPermaLink="true">https://www.gee-whiz.de/2018/caching-buildcontainer/</guid>
        
        <category>ci</category>
        
        <category>jenkins</category>
        
        <category>docker</category>
        
        <category>maven</category>
        
        <category>npm</category>
        
        <category>cache</category>
        
        <category>caching</category>
        
        <category>buildcontainer</category>
        
        
        <category>ci</category>
        
      </item>
    
      <item>
        <title>Remote debugging containerized Jenkins builds: debugShell()</title>
        <description>&lt;p&gt;Providing build environments by employing some kind of container technology is quite common nowadays. When looking at Docker as an example, it offers various benefits: Declaring the environment in a textual way by using a Dockerfile , you can combine it with a versioning tool like git and track every change. Each build is running inside its own fresh container and is therefore isolated and also reproducible. If you choose to use a clustering solution like Docker Swarm or Kubernetes, you’re also able to dynamically scale available build power at run time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Any downsides?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of course this comes at a cost. Besides building, maintaining and supporting additional infrastructure, from a perspective of developers and DevOps engineers, the usability is definitely lowered. If one is using a central build server whose builds are executed on that same machine, debugging problems seems way easier: When a build fails, we simply browse its work space and check on relevant files to investigate potential causes. If that’s not enough, we can always SSH into the machine and continue debugging locally.&lt;/p&gt;

&lt;p&gt;On the other hand, there are some challenges involved when executing a build inside a container on some node somewhere on a container platform. In the nature of ephemeral build environments where separate containers are created and teared down for each individual build, accessing build artifacts or work space files becomes impossible. Using SSH is also problematic: Usually there is no SSH daemon listening inside the container. It would also only be reachable from the outside, if you expose the daemons port. But even then, you don’t even know exactly on which node the container is running on.&lt;/p&gt;

&lt;h2 id=&quot;proposed-solution-and-tldr&quot;&gt;Proposed solution and &lt;strong&gt;&lt;em&gt;tl;dr&lt;/em&gt;&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;We’ll create a TCP shell server inside the container a client can connect to. To overcome the connectivity issues we’ll setup a dynamic reverse proxy on a public reachable machine that tunnels the connection inside the container. Following this approach, we’ll actually gain some additional features along the road. Using the right combination of tools enable us to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Set breakpoints within a Jenkinsfile&lt;/li&gt;
  &lt;li&gt;Gain an interactive shell inside the buildcontainer&lt;/li&gt;
  &lt;li&gt;Inherit the actual build environment within that shell: Setting a breakpoint inside  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withMaven()&lt;/code&gt; for example, would leave us with the corresponding configuration when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Modify the workspace during build time&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;tool-introduction&quot;&gt;Tool introduction&lt;/h2&gt;

&lt;p&gt;This is a brief introduction of the tools and their respective role to achieve the laid out features above.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/fatedier/frp&quot;&gt;frp&lt;/a&gt;&lt;/strong&gt; (&lt;em&gt;fast reverse proxy&lt;/em&gt;): Exposes TCP sockets behind a NAT, i.e. inside a container. There is a server part that runs on a public reachable machine. The client runs inside the container and connects to the server and thereby establishes a reverse tunnel to a socket inside the container. The socket inside the container is now reachable via the public machine.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://www.dest-unreach.org/socat/doc/socat.html&quot;&gt;socat&lt;/a&gt;&lt;/strong&gt; (SOcket CAT): Multipurpose socket relay utility. Used to let a developer connect to the buildcontainer and to establish the interactive shell session.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://jenkins.io/doc/book/pipeline/shared-libraries/&quot;&gt;Jenkins shared library&lt;/a&gt;&lt;/strong&gt;: To create a custom build step which functions as a break point that halts the running build, constructs the shell and establishes the reverse tunnel.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;The following shows the basic idea on how to implement the remote debugging functionality. For those of you who want to dive in deeper: Everything laid out here here can be fully explored by checking out the corresponding repository over at &lt;a href=&quot;https://github.com/gee-whiz-de/_posts-2018-09-24-remote-debugging&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;frp&quot;&gt;frp&lt;/h3&gt;

&lt;p&gt;As &lt;a href=&quot;https://github.com/fatedier/frp&quot;&gt;frp&lt;/a&gt; is responsible for establishing a communication link between the client and the buildcontainer, it needs to be setup first. As already mentioned, we require a public reachable node the server component (frpd) must be installed on. Let’s call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jenkins.gee-whiz.de&lt;/code&gt;. frp’s feature set is quite extensive, but a minimal server configuration that fit our needs would look like this:&lt;/p&gt;
&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[common]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;bind_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;7000&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;privilege_allow_ports&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;17000-17100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We define a listening port of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7000&lt;/code&gt; where the clients inside of buildcontainers are supposed to connect to, as well as a range of ports frpd may use on the public node for inbound tunneled connections. On the client side of things, i.e. inside the buildcontainer, a suitable frpc configuration would be:&lt;/p&gt;
&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[common]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;server_addr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jenkins.gee-whiz.de&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;server_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;7000&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[shell]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;local_ip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;local_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;22222&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;remote_port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;17000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Beside the servers address and port in the common section, we define the local endpoint that should be exposed. We’re planning to let socat listen on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:22222&lt;/code&gt;. socat will provide the interactive shell a client is able to connect to. In this case, the remote port is explicitly set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;17000&lt;/code&gt;. To let frpd automatically choose a free port within the specified range on the public node, the remote port must be set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When running the client against the server, the resulting tunnel would basically look like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[ jenkins.gee-whiz.de:17000 ] --&amp;gt; [ some_docker_server ] --&amp;gt; [ inside_container:22222 ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;socat&quot;&gt;socat&lt;/h3&gt;

&lt;p&gt;Socat will be used in two ways: First, it will act as a server inside the container that is responsible for providing the interactive shell. Second, it will be run on the client to connect to said server via the reverse tunnel created by frp.&lt;/p&gt;

&lt;p&gt;On the server side of things we’re going to spawn a TCP socket on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;22222&lt;/code&gt; and redirect incoming traffic to a bash instance. We also need to set some additional options as laid out in &lt;a href=&quot;https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/&quot;&gt;this blog post&lt;/a&gt;  to create a fully interactive &lt;a href=&quot;https://en.wikipedia.org/wiki/Pseudoterminal&quot;&gt;PTY&lt;/a&gt;. This lets you do things you would expect from a terminal, like signal handling (ctrl+c), tab-completion or using a text editor like vim. This is the command to spawn the server process:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;socat &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt;:&lt;span class=&quot;s1&quot;&gt;'bash -li'&lt;/span&gt;,pty,stderr,setsid,sigint,sane tcp-listen:22222
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once we created both the frp reverse tunnel and the socat server we’re able to establish the connection from a client to the container. This can be done from an arbitrary Linux system with socat installed, or by using something like Mintty with &lt;a href=&quot;https://www.msys2.org/&quot;&gt;MSYS2&lt;/a&gt; or &lt;a href=&quot;https://www.cygwin.com/&quot;&gt;Cygwin&lt;/a&gt; on Windows. The native Windows &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd.exe&lt;/code&gt; technically also works, but misses a few interactive shell features mentioned above. As on the server side, we also set a few additional options to make the shell fully interactive:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;socat file:&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tty&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;,raw,echo&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0,escape&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x0f tcp:jenkins.gee-whiz.de:17000
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-al&lt;/span&gt;
total 4
drwxr-xr-x 1 jenkins jenkins  30 Feb 14 08:46 &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
drwxr-xr-x 1 jenkins jenkins 300 Feb 14 08:46 ..
drwxr-xr-x 1 jenkins jenkins 136 Feb 14 08:46 .git
&lt;span class=&quot;nt&quot;&gt;-rw-r--r--&lt;/span&gt; 1 jenkins jenkins 676 Feb 14 08:46 Jenkinsfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Side note&lt;/em&gt;:  Instead of socat you may also use a real SSH daemon to allows every standard SSH client to connect. You would also automatically gain features like authentication, encryption and file transfer.&lt;/p&gt;

&lt;h3 id=&quot;jenkins-shared-library&quot;&gt;Jenkins shared library&lt;/h3&gt;

&lt;p&gt;Doing everything above manually inside a &lt;a href=&quot;https://jenkins.io/doc/pipeline/steps/workflow-durable-task-step/#sh-shell-script&quot;&gt;sh&lt;/a&gt; step would be way to cumbersome. Instead we’ll use the &lt;a href=&quot;https://jenkins.io/doc/book/pipeline/shared-libraries/&quot;&gt;Jenkins shared library&lt;/a&gt; mechanism to neatly encapsulate all of those actions into a single &lt;a href=&quot;https://jenkins.io/doc/book/pipeline/shared-libraries/#defining-custom-steps&quot;&gt;custom step&lt;/a&gt;. This enables us to halt the execution of the Jenkinsfile and start the debug shell with just a single command. We name that custom step we’re about to create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debugShell()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We’re not diving into all the details about creating shared libraries here. Also note that this version is way stripped down to bring across the basic idea. Among other things, the full version adds automatic port determination and therefore multi user capability.&lt;/p&gt;

&lt;p&gt;This is the content of the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debugShell.groovy&lt;/code&gt; inside our shared library:&lt;/p&gt;
&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groovy&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;------------------------------&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Spawning debug shell socket&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;------------------------------&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Reverse connection endpoint to localhost:22222 created on: jenkins.gee-whiz.de:17000&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Connect via: socat file:\$(tty),raw,echo=0,escape=0x0f tcp:jenkins.gee-whiz.de:17000&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Listening for incoming connection and pausing execution until connection terminates&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;------------------------------&quot;&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/tmp/debugshell/create.sh'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'frpc -c /etc/frp/frpc.ini &amp;amp;'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'sleep 1'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'socat exec:&quot;bash -li&quot;,pty,stderr,setsid,sigint,sane tcp-listen:22222'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Upon calling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debugShell()&lt;/code&gt; function, we can use socat as a client to connect to the buildcontainer. To do so, just execute the echoed command. As the created shell inherits the environment of the current build on the exact location the function is called, access to every environment variable, injected configurations and credentials is being gained. We’re also able to edit files inside the workspace with our favorite editor. As an example, we could modify the content of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt; file, and run Maven on our changes. Upon exiting the debug shell, the build resumes.&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h2&gt;

&lt;p&gt;Surly the laid out approach was suitable to understand the basic gist of the setup. Running it in production though, we add a few additional things:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Automatically determine an unused port for socats listening socket inside the container&lt;/li&gt;
  &lt;li&gt;Use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote_port&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; to let the frp server automatically determine an unused port within the specified port range on the public reachable machine&lt;/li&gt;
  &lt;li&gt;Generating a corresponding frp client configuration&lt;/li&gt;
  &lt;li&gt;Feed back the chosen port and the endpoint as a whole to the user as a log statement&lt;/li&gt;
  &lt;li&gt;Use a custom bashrc to print some useful information when a client connects:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  Connected to buildcontainer a163fc77fac0 on Swarm-a163fc77fac0 for job https://jenkins.gee-whiz.de/some_project/some_repository/master/75/ from 127.0.0.1:57964
  Building commit b6050f00bbd7c9a10ebabbd1151dad7130e32e71 on branch master originating from ssh://git@git.gee-whiz.de:2222/some_project/some_repository.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Be able to run the debugShell in the background while the build continues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go investigate all the details on &lt;a href=&quot;https://github.com/gee-whiz-de/_posts-2018-09-24-remote-debugging&quot;&gt;Github&lt;/a&gt;, if you like. Or drop us an &lt;a href=&quot;mailto:info@gee-whiz.de&quot;&gt;email&lt;/a&gt; if you have any questions or suggestions.&lt;/p&gt;
</description>
        <pubDate>Mon, 24 Sep 2018 00:00:00 +0000</pubDate>
        <link>https://www.gee-whiz.de/2018/remote-debugging/</link>
        <guid isPermaLink="true">https://www.gee-whiz.de/2018/remote-debugging/</guid>
        
        <category>ci</category>
        
        <category>jenkins</category>
        
        <category>docker</category>
        
        <category>debugging</category>
        
        <category>shell</category>
        
        <category>debugshell</category>
        
        <category>frp</category>
        
        <category>socat</category>
        
        <category>ssh</category>
        
        <category>shared-library</category>
        
        
        <category>ci</category>
        
      </item>
    
      <item>
        <title>Inhalte von Git Projekten verschlüsseln</title>
        <description>&lt;h1 id=&quot;inhalte-von-git-projekten-verschlüsseln&quot;&gt;Inhalte von Git Projekten verschlüsseln&lt;/h1&gt;
&lt;p&gt;Um z.B. auf github auch sicherheitsrelevante Sachen ablegen zu können, gibt es die Möglichkeit Inhalte eines git Projekt zu verschlüsseln. Ein Beispiel hierfür ist &lt;a href=&quot;https://github.com/AGWA/git-crypt&quot;&gt;git-crypt&lt;/a&gt;. Damit git-crypt auch unter Windows funktionert, muss man folgendes tun:&lt;/p&gt;

&lt;h1 id=&quot;git-crypt-installation-in-msys2&quot;&gt;git-crypt Installation in Msys2&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;Folgende Pakete installieren &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pacman -S make gcc openssl&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;git-crypt clonen: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone https://github.com/AGWA/git-crypt.git&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd git-crypt&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Das Makefile wie in &lt;a href=&quot;https://github.com/AGWA/git-crypt/pull/84/files&quot;&gt;Pull Request&lt;/a&gt; beschrieben anpassen (ist noch nicht germerged).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make install&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;projekt-für-git-crypt-konfigurieren&quot;&gt;Projekt für git-crypt konfigurieren&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;In bestehendes/neues git projekt wechseln&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-crypt init&lt;/code&gt; ausführen, es wird ein default Key angelegt&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim .gitattributes&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Folgendes eintragen:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      * filter=git-crypt diff=git-crypt
       .gitattributes !filter !diff
       README.md !filter !diff
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Ab diesem moment werden nun alle Dateien verschlüsselt. Außer die .gitattributes und README.md .&lt;/li&gt;
  &lt;li&gt;Key exportieren &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-crypt export-key ../exportKey&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Der Key wird von denjenigen benötigt die das Projekt clonen und es auch entschlüsseln sollen&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;encryptetes-projekt-nutzen&quot;&gt;Encryptetes Projekt nutzen&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Projekt clonen&lt;/li&gt;
  &lt;li&gt;Den Key bekommen&lt;/li&gt;
  &lt;li&gt;git-crypt installieren&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-crypt unlock path/to/exportKey&lt;/code&gt;ausführen&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Fri, 27 Apr 2018 16:10:23 +0000</pubDate>
        <link>https://www.gee-whiz.de/2018/git-crypt/</link>
        <guid isPermaLink="true">https://www.gee-whiz.de/2018/git-crypt/</guid>
        
        <category>git</category>
        
        
        <category>mentalNotes</category>
        
      </item>
    
      <item>
        <title>Hello World!</title>
        <description>&lt;p&gt;Everything starts with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello World&lt;/code&gt;… So here we go :-)&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloWorld&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
             &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;it's so geee&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See you on &lt;a href=&quot;https://github.com/gee-whiz-de&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/gee-whiz.png&quot; alt=&quot;logo&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 21 Jan 2017 23:05:23 +0000</pubDate>
        <link>https://www.gee-whiz.de/2017/hello-world/</link>
        <guid isPermaLink="true">https://www.gee-whiz.de/2017/hello-world/</guid>
        
        <category>stuff</category>
        
        
        <category>stuff</category>
        
      </item>
    
  </channel>
</rss>
