<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Javier Romero</title>
    <description>The latest articles on DEV Community by Javier Romero (@jromero).</description>
    <link>https://dev.to/jromero</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F435539%2F32d59662-d192-40e8-8082-70417d0e254d.png</url>
      <title>DEV Community: Javier Romero</title>
      <link>https://dev.to/jromero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jromero"/>
    <language>en</language>
    <item>
      <title>Creating an s2i builder for Go (and a runtime image)</title>
      <dc:creator>Javier Romero</dc:creator>
      <pubDate>Thu, 23 Jul 2020 01:03:37 +0000</pubDate>
      <link>https://dev.to/jromero/creating-an-s2i-builder-for-go-and-a-runtime-image-5d56</link>
      <guid>https://dev.to/jromero/creating-an-s2i-builder-for-go-and-a-runtime-image-5d56</guid>
      <description>&lt;p&gt;This post is a continuation of:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jromero" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F435539%2F32d59662-d192-40e8-8082-70417d0e254d.png" alt="jromero"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jromero/building-an-app-image-using-s2i-5gcp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Build an app image with s2i&lt;/h2&gt;
      &lt;h3&gt;Javier Romero ・ Jul 19 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#openshift&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#s2i&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Objective
&lt;/h1&gt;

&lt;p&gt;I will try to go through the process of creating an s2i builder for... &lt;code&gt;go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The goals of this builder are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The builder should compile &lt;code&gt;go&lt;/code&gt; programs.&lt;/li&gt;
&lt;li&gt;The app image should not contain the source code.&lt;/li&gt;
&lt;li&gt;The app image should not contain the &lt;code&gt;go&lt;/code&gt; compiler.&lt;/li&gt;
&lt;li&gt;The app image should not run as &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/l4FGkfXrTl9qxV24U/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l4FGkfXrTl9qxV24U/giphy.gif" alt="aye aye"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  App
&lt;/h2&gt;

&lt;p&gt;First, we need an app to use as our test subject.&lt;/p&gt;

&lt;p&gt;This is a simple http server app that uses &lt;a href="https://github.com/gorilla/mux" rel="noopener noreferrer"&gt;mux&lt;/a&gt; for routing. This server could be written without the use of this library but I wanted to show how dependencies would be handled.&lt;/p&gt;

&lt;p&gt;Let's look at the app contents...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree test-app

test-app/
├── app.go    # app source
├── go.mod    # dependency declarations
└── go.sum    # dependency checksums
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;app.go:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gorilla/mux"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8080"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting app on port:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"This is a test app!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hostname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"This request was processed by host: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello/{object}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;vars&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello %v!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutting down"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;go.mod:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module github.com/jromero/learning-s2i/s2i-golang/test/test-app

go 1.14

require github.com/gorilla/mux v1.7.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;go.sum:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Builder
&lt;/h2&gt;

&lt;p&gt;The builder as we learned &lt;a href="https://dev.to/jromero/building-an-app-image-using-s2i-5gcp#builder"&gt;previously&lt;/a&gt; is an image that knows how to build a specific type of application. In this case, we will write a builder for a &lt;code&gt;go&lt;/code&gt; app.&lt;/p&gt;

&lt;p&gt;In order to do this we'll need the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree builder/

builder/
├── Dockerfile               # docker instructions to create builder
└── s2i
    └── bin
        ├── assemble         # script to build application
        ├── run              # script to run application
        ├── save-artifacts   # script to package cached items
        └── usage            # script that displays usage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go into more detail about each one...&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;s2i/bin/usage&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;usage&lt;/code&gt; script is very straight forward. We will use it as a way to assist users when they try to run the builder using &lt;code&gt;docker run&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Contents
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash -e&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
This is the s2i-golang S2I image:
To use it, install S2I: https://github.com/openshift/source-to-image

Sample invocation:

s2i build &amp;lt;source code path/URL&amp;gt; s2i-golang &amp;lt;application image&amp;gt;

You can then run the resulting image via:
docker run &amp;lt;application image&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;s2i/bin/save-artifacts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;save-artifacts&lt;/code&gt; script is a special script that is called as part of the build process directly into a tar stream. We will use it to specify what data we want cached so that users can leverage &lt;code&gt;--incremental&lt;/code&gt; builds.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;go&lt;/code&gt;, we care about caching the following directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;${GOPATH}/src&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;${GOPATH}/pkg&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; Due to the way this script is used, no output other than the cache contents should be present.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  Contents
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh -e&lt;/span&gt;
&lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; src &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; +w src
    &lt;span class="nb"&gt;tar &lt;/span&gt;cf - src
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; pkg &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; +w pkg
    &lt;span class="nb"&gt;tar &lt;/span&gt;cf - pkg
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;popd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;s2i/bin/assemble&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;assemble&lt;/code&gt; script is responsible for the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;restoring cache&lt;/li&gt;
&lt;li&gt;compiling code&lt;/li&gt;
&lt;li&gt;relocating contents&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  Contents
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If the 's2i-golang' assemble script is executed with the '-h' flag, print the usage.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"-h"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; /usr/libexec/s2i/usage
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Restore artifacts from the previous build (if they exist).&lt;/span&gt;
&lt;span class="nb"&gt;echo
echo&lt;/span&gt; &lt;span class="s2"&gt;"---&amp;gt; Checking for cache..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /tmp/artifacts 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; /tmp/artifacts &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"-----&amp;gt; Pulling cache..."&lt;/span&gt;
  &lt;span class="nb"&gt;shopt&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; dotglob
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; src &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Restoring cache &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/src/..."&lt;/span&gt;
    &lt;span class="nb"&gt;mv &lt;/span&gt;src &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/src
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; pkg &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Restoring cache &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/pkg/..."&lt;/span&gt;
    &lt;span class="nb"&gt;mv &lt;/span&gt;pkg &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/pkg 
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;shopt&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; dotglob
  &lt;span class="nb"&gt;popd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Compile app to final location&lt;/span&gt;
&lt;span class="nb"&gt;echo
echo&lt;/span&gt; &lt;span class="s2"&gt;"---&amp;gt; Building application from source..."&lt;/span&gt;
&lt;span class="nb"&gt;pushd&lt;/span&gt; /tmp/src/ &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
go build &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin/app
&lt;span class="nb"&gt;popd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;s2i/bin/run&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; script will be what is executed on the produced app image. In our case it will simply call the compiled executable.&lt;/p&gt;

&lt;h5&gt;
  
  
  Contents
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh -e&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin/app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Dockerfile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, we'll put it all together by composing our builder image using a standard &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Contents
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# s2i-golang&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openshift/base-centos7&lt;/span&gt;

      &lt;span class="c"&gt;# the maintainer&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="Javier Romero &amp;lt;root@jromero.codes&amp;gt;" \&lt;/span&gt;
      &lt;span class="c"&gt;# specify where s2i scripts are located&lt;/span&gt;
      io.openshift.s2i.scripts-url="image:///usr/libexec/s2i/bin"

# configuration
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GO_VERSION=1.14&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GO_INSTALL_DIR=/usr/local/&lt;/span&gt;

&lt;span class="c"&gt;# env vars&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; APP_ROOT=/opt/app-root&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; BUILDER_VERSION 1.0&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GOPATH ${APP_ROOT}/src/go&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH=${PATH}:${GOPATH}/bin:${GO_INSTALL_DIR}/go/bin&lt;/span&gt;

&lt;span class="c"&gt;# build dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://dl.google.com/go/go&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GO_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.linux-amd64.tar.gz &lt;span class="nt"&gt;-o&lt;/span&gt; go&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GO_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.linux-amd64.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GO_INSTALL_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; go&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GO_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.linux-amd64.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; go&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GO_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.linux-amd64.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# s2i scripts&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; s2i /usr/libexec/s2i&lt;/span&gt;

&lt;span class="c"&gt;# set permissions&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1001:1001 &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1001:1001 /usr/libexec/s2i

&lt;span class="c"&gt;# default user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; 1001&lt;/span&gt;

&lt;span class="c"&gt;# default CMD for the image&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/usr/libexec/s2i/usage"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: user &lt;code&gt;1001&lt;/code&gt; and group &lt;code&gt;1001&lt;/code&gt; have been created by the base image &lt;code&gt;openshift/base-centos7&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Build!
&lt;/h2&gt;

&lt;p&gt;Now that we've got all the pieces together let's test out our builder.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build our builder image
&lt;/h4&gt;

&lt;p&gt;Building the image using docker should be as easy as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; s2i-golang ./builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Build our app using the builder
&lt;/h4&gt;

&lt;p&gt;To build our app we will use our builder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;s2i build &lt;span class="nt"&gt;--copy&lt;/span&gt; test-app/ s2i-golang my-go-app &lt;span class="nt"&gt;--incremental&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Checking &lt;span class="k"&gt;for &lt;/span&gt;cache...

&lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Building application from source...
go: downloading github.com/gorilla/mux v1.7.4
Build completed successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Because we are caching &lt;code&gt;go&lt;/code&gt; dependencies, if we re-ran the same build command (with &lt;code&gt;--incremental&lt;/code&gt;) we should see that &lt;code&gt;mux&lt;/code&gt; is not downloaded again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Run our app
&lt;/h4&gt;

&lt;p&gt;With the app image built, we can run it using &lt;code&gt;docker&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -it -p 8080:8080 my-go-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and verify that it works by going to &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s http://localhost:8080

This is a test app!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  App Image
&lt;/h2&gt;

&lt;p&gt;Let's take a look at what we've ended up with.&lt;/p&gt;

&lt;p&gt;Checking what user we are running as yields &lt;code&gt;default&lt;/code&gt;. 👍&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; my-go-app /bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"whoami"&lt;/span&gt;

default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our app ended up being &lt;strong&gt;7.5MBs&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; my-go-app /bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"ls -lh /opt/app-root/bin/app"&lt;/span&gt;

&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 default root 7.5M Jul 22 15:53 /opt/app-root/bin/app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our image... is &lt;strong&gt;725MBs&lt;/strong&gt;!!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;docker images | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker images | &lt;span class="nb"&gt;grep &lt;/span&gt;my-go-app&lt;span class="o"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1, $NF}'&lt;/span&gt;

REPOSITORY SIZE
my-go-app 725MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we &lt;a href="https://github.com/wagoodman/dive" rel="noopener noreferrer"&gt;&lt;code&gt;dive&lt;/code&gt;&lt;/a&gt; into our app image we can see that this is because of a few things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The base image of the app image is the builder image which in itself has a lot of stuff our app doesn't care about.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;go&lt;/code&gt; compiler and build tools are present in the final app image.&lt;/li&gt;
&lt;li&gt;All the intermittent artifacts such as cache and source code are on the final app image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In addition to the size, all this &lt;em&gt;extra&lt;/em&gt; stuff has the additional negative side-effect of increasing our attack surface. 😈&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnidclb2rnf87myprm9wz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnidclb2rnf87myprm9wz.png" alt="dive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we review our goals, it's clear that we haven't satisfied them all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ The builder should compile &lt;code&gt;go&lt;/code&gt; programs.&lt;/li&gt;
&lt;li&gt;❌ The app image should not contain the source code.&lt;/li&gt;
&lt;li&gt;❌ The app image should not contain the &lt;code&gt;go&lt;/code&gt; compiler.&lt;/li&gt;
&lt;li&gt;✅ The app image should not run as &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7qDWoeN6PaJ6Z6RW/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7qDWoeN6PaJ6Z6RW/giphy.gif" alt="wat"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Runtime Image
&lt;/h2&gt;

&lt;p&gt;We can try to optimize our app image by using a &lt;code&gt;runtime&lt;/code&gt; image to meet our initial goals.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;runtime&lt;/code&gt; image is an image that will be used &lt;em&gt;instead&lt;/em&gt; of the builder as the base for the app image.&lt;/p&gt;

&lt;p&gt;In order to make this runtime image work with &lt;code&gt;s2i&lt;/code&gt; we need the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree runtime/

runtime/
├── Dockerfile                 # docker instructions to create image
└── s2i
    └── bin
        ├── assemble-runtime   # script to relocate assets
        └── run                # script to run our application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;s2i/bin/run&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Just like the &lt;code&gt;run&lt;/code&gt; script for the builder this script will simply execute our binary. In this case we are executing it the current working directory since that's where we'll be placing it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh -e&lt;/span&gt;
./app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;s2i/bin/assemble-runtime&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;assemble-runtime&lt;/code&gt; is analogous to &lt;code&gt;assemble&lt;/code&gt; but for the &lt;code&gt;runtime&lt;/code&gt; image. In our case, there is nothing additional to do so we'll just &lt;code&gt;echo&lt;/code&gt;. Unfortunately this script is required even if we don't actually need it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh -e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Nothing special to do here..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Dockerfile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Lastly, but most importantly, we'll define our image with the right permissions on the minimal base image we need. In this case we are going to go with the base cento7 image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# s2i-golang-runtime&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; centos:7&lt;/span&gt;

      &lt;span class="c"&gt;# the maintainer&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="Javier Romero &amp;lt;root@jromero.codes&amp;gt;" \&lt;/span&gt;
      &lt;span class="c"&gt;# specify where s2i scripts are located&lt;/span&gt;
      io.openshift.s2i.scripts-url="image:///usr/libexec/s2i/bin"

# s2i scripts
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; s2i /usr/libexec/s2i&lt;/span&gt;

&lt;span class="c"&gt;# create user/group&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;groupadd &lt;span class="nt"&gt;-g&lt;/span&gt; 1001 app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;-u&lt;/span&gt; 1001 &lt;span class="nt"&gt;-g&lt;/span&gt; 1001 default

&lt;span class="c"&gt;# default user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; 1001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build! (w Runtime Image)
&lt;/h2&gt;

&lt;p&gt;Let's build our app again but this time providing a separate runtime image in an effort to improve the final app image.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build our runtime image
&lt;/h4&gt;

&lt;p&gt;Again, building the image using docker should be as easy as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; s2i-golang-runtime ./runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Build our app using the builder and runtime
&lt;/h4&gt;

&lt;p&gt;To build our app we will use our builder and newly build runtime image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;s2i build &lt;span class="nt"&gt;--copy&lt;/span&gt; test-app/ s2i-golang my-go-app &lt;span class="nt"&gt;--runtime-image&lt;/span&gt; s2i-golang-runtime &lt;span class="nt"&gt;--runtime-artifact&lt;/span&gt; /opt/app-root/bin/app

&lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Checking &lt;span class="k"&gt;for &lt;/span&gt;cache...

&lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Building application from source...
go: downloading github.com/gorilla/mux v1.7.4
Build completed successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A few things to note:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can no longer use &lt;code&gt;--incremental&lt;/code&gt; 😞. See &lt;a href="https://github.com/openshift/source-to-image/issues/824" rel="noopener noreferrer"&gt;issue #824&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We must provide the location of what artifacts we want to "transfer" over to the runtime image. This requires the end user to know more than they should about the internals of the builder. 😥&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Run our app
&lt;/h4&gt;

&lt;p&gt;Now that we've built our app (again) we can run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -it -p 8080:8080 my-go-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool it still works!&lt;/p&gt;

&lt;h2&gt;
  
  
  App Image (Redux)
&lt;/h2&gt;

&lt;p&gt;Let's hope we have a &lt;em&gt;better&lt;/em&gt; app image. 🤞&lt;/p&gt;

&lt;p&gt;Checking the running user...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; my-go-app /bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"whoami"&lt;/span&gt;

default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still good.&lt;/p&gt;

&lt;p&gt;Image size?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;docker images | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker images | &lt;span class="nb"&gt;grep &lt;/span&gt;my-go-app&lt;span class="o"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1, $NF}'&lt;/span&gt;

REPOSITORY SIZE
my-go-app 212MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! We've saved &lt;strong&gt;&amp;gt;500MBs&lt;/strong&gt;!!! This is data that we don't have to store or transfer. 🙌&lt;/p&gt;

&lt;p&gt;Plus, much less of an attack surface. 👿&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://github.com/wagoodman/dive" rel="noopener noreferrer"&gt;&lt;code&gt;dive&lt;/code&gt;&lt;/a&gt; into the image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff9kgga5nmxcii75maba6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff9kgga5nmxcii75maba6.png" alt="dive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks &lt;em&gt;much&lt;/em&gt; better!&lt;/p&gt;

&lt;p&gt;... and we've met all our original goals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ The builder should compile &lt;code&gt;go&lt;/code&gt; programs.&lt;/li&gt;
&lt;li&gt;✅ The app image should not contain the source code.&lt;/li&gt;
&lt;li&gt;✅ The app image should not contain the &lt;code&gt;go&lt;/code&gt; compiler.&lt;/li&gt;
&lt;li&gt;✅ The app image should not run as &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7qDON1FYhi5eoqgU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7qDON1FYhi5eoqgU/giphy.gif" alt="awesome"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So what did we learn? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Builders are fairly easy to construct and can be generally used with multiple applications (unlike pure &lt;code&gt;Dockerfiles&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Built-in caching can be used to improve local development via &lt;code&gt;--incremental&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--incremental&lt;/code&gt; does not currently work with &lt;code&gt;--runtime-image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To use &lt;code&gt;--runtime-image&lt;/code&gt; you must have intimate knowledge about what artifacts will be produced.&lt;/li&gt;
&lt;li&gt;We gain a performance and security boost from using a seperate runtime image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you'd like to look at the source used in this tutorial, you may find it here: &lt;a href="https://github.com/jromero/learning-s2i" rel="noopener noreferrer"&gt;https://github.com/jromero/learning-s2i&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o6ozomjwcQJpdz5p6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o6ozomjwcQJpdz5p6/giphy.gif" alt="cheers"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  Additional Reading
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.openshift.com/blog/create-s2i-builder-image" rel="noopener noreferrer"&gt;OpenShift "create s2i builder image" post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openshift/source-to-image/blob/master/docs/runtime_image.md" rel="noopener noreferrer"&gt;Runtime Image docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>openshift</category>
      <category>s2i</category>
    </item>
    <item>
      <title>Build an app image with s2i</title>
      <dc:creator>Javier Romero</dc:creator>
      <pubDate>Sun, 19 Jul 2020 17:45:15 +0000</pubDate>
      <link>https://dev.to/jromero/building-an-app-image-using-s2i-5gcp</link>
      <guid>https://dev.to/jromero/building-an-app-image-using-s2i-5gcp</guid>
      <description>&lt;h2&gt;
  
  
  What is &lt;code&gt;s2i&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;source-to-image&lt;/code&gt;, or &lt;code&gt;s2i&lt;/code&gt; as commonly abbreviated, is a build tool that allows an app developer and/or operator the ability to take the app source code and construct an OCI image (aka docker image, aka container image).&lt;/p&gt;

&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;h5&gt;
  
  
  Builder
&lt;/h5&gt;

&lt;p&gt;A builder is an image that knows how to build a specific type of source code. Although it would be possible for a single builder to support multiple languages it is common that a builder only builds one language family.&lt;/p&gt;

&lt;p&gt;The following publically available builders are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sclorg/?q=s2i--container&amp;amp;type=&amp;amp;language=shell"&gt;OpenShift Builders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openshift-s2i/"&gt;Community Builders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fabric8io-images/s2i"&gt;fabric8 Builders&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Build image
&lt;/h5&gt;

&lt;p&gt;The base image in which the build process is executed.&lt;/p&gt;

&lt;h5&gt;
  
  
  Run image
&lt;/h5&gt;

&lt;p&gt;The base image in which the built application runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;h5&gt;
  
  
  Separation of concerns
&lt;/h5&gt;

&lt;p&gt;By using a tool such as &lt;code&gt;s2i&lt;/code&gt;, the app developer can focus on adding features and maintaining the application. On the other side, DevOps can ensure that the application is built in a secure and reproducible manner by providing builders that should be used by developers.&lt;/p&gt;

&lt;h5&gt;
  
  
  Minimal to no configuration
&lt;/h5&gt;

&lt;p&gt;In most cases, no additional &lt;code&gt;s2i&lt;/code&gt; configuration is necessary and it's able to build the application. In the cases where a more complex application needs to be built there are various configuration points (to be covered in a separate tutorial).&lt;/p&gt;

&lt;h3&gt;
  
  
  Process
&lt;/h3&gt;

&lt;p&gt;The process, as well defined in the project &lt;a href="https://github.com/openshift/source-to-image#build-workflow"&gt;README.md&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;s2i build&lt;/code&gt; workflow is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;s2i&lt;/code&gt; creates a container based on the build image and passes it a tar file that contains:

&lt;ul&gt;
&lt;li&gt;The application source in src, excluding any files selected by &lt;code&gt;.s2iignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The build artifacts in &lt;code&gt;artifacts&lt;/code&gt; (if applicable - see incremental builds)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s2i&lt;/code&gt; sets the environment variables from &lt;code&gt;.s2i/environment&lt;/code&gt; (optional)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s2i&lt;/code&gt; starts the container and runs its assemble script&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s2i&lt;/code&gt; waits for the container to finish&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s2i&lt;/code&gt; commits the container, setting the CMD for the output image to be the run script and tagging the image with the name provided.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Now that we've got some idea on what &lt;code&gt;s2i&lt;/code&gt; is and why we might want to use it let's start by installing it.&lt;/p&gt;

&lt;p&gt;You may find the latest releases &lt;a href="https://github.com/openshift/source-to-image/releases"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These are the command I ran to install it in &lt;code&gt;/usr/local/bin/&lt;/code&gt; on a macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://github.com/openshift/source-to-image/releases/download/v1.3.0/source-to-image-v1.3.0-eed2850f-darwin-amd64.tar.gz &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/s2i.tgz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvf&lt;/span&gt; /tmp/s2i.tgz &lt;span class="nt"&gt;-C&lt;/span&gt; /usr/local/bin/
s2i version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Build
&lt;/h2&gt;

&lt;p&gt;Next, we'll build the app. &lt;/p&gt;

&lt;p&gt;Let's use Spring's sample project, &lt;a href="https://github.com/spring-projects/spring-petclinic"&gt;petclinic&lt;/a&gt;, as the source.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;s2i build https://github.com/spring-projects/spring-petclinic fabric8/s2i-java pet-clinic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Breakdown
&lt;/h4&gt;

&lt;p&gt;If we breakdown the command, this is what it all means:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;s2i                                             
build                                                 &lt;span class="c"&gt;# command&lt;/span&gt;
https://github.com/spring-projects/spring-petclinic   &lt;span class="c"&gt;# source (in)&lt;/span&gt;
fabric8/s2i-java                                      &lt;span class="c"&gt;# builder image (in)&lt;/span&gt;
pet-clinic                                            &lt;span class="c"&gt;# image name (out)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Output
&lt;/h4&gt;

&lt;p&gt;The [truncated] output is as follows. Nothing spectacular to look at but we do see that it built our image.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;==================================================================
Starting S2I Java Build .....
S2I source build for Maven detected
Using MAVEN_OPTS '-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:+ExitOnOutOfMemoryError'
Found pom.xml ...
Running 'mvn -Dmaven.repo.local=/tmp/artifacts/m2 package -DskipTests -Dmaven.javadoc.skip=true -Dmaven.site.skip=true -Dmaven.source.skip=true -Djacoco.skip=true -Dcheckstyle.skip=true -Dfindbugs.skip=true -Dpmd.skip=true -Dfabric8.skip=true -e -B '
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T18:33:14Z)
Maven home: /opt/maven
Java version: 1.8.0_252, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "4.19.76-linuxkit", arch: "amd64", family: "unix"
[INFO] Error stacktraces are turned on.
[INFO] Scanning for projects...
...
(Downloading a bunch of dependencies)
...
[INFO] Building jar: /tmp/src/target/spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.3.1.RELEASE:repackage (repackage) @ spring-petclinic ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 06:43 min
[INFO] Finished at: 2020-07-19T16:00:45Z
[INFO] ------------------------------------------------------------------------
Copying Maven artifacts from /tmp/src/target to /deployments ...
Running: cp -v *.jar /deployments
'spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar' -&amp;gt; '/deployments/spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar'
Checking for fat jar archive...
Found spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar...
... done
Build completed successfully
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Pro-tip: Incremental Build
&lt;/h4&gt;

&lt;p&gt;If you were to run the &lt;code&gt;build&lt;/code&gt; command again you would notice that the build does EVERYTHING again. This includes downloading dependencies. By default, &lt;code&gt;s2i&lt;/code&gt; aims to provide a safe reproducible build. If you'd like to leverage caching of such resources (dependant on builder implementation) you may enable incremental builds via &lt;code&gt;--incremental&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;s2i build https://github.com/spring-projects/spring-&amp;gt; petclinic fabric8/s2i-java pet-clinic &lt;span class="nt"&gt;--incremental&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Run
&lt;/h2&gt;

&lt;p&gt;Now that we've built our image we can run it just like we normally would any other image:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 pet-clinic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;We are binding port &lt;code&gt;8080&lt;/code&gt; since that's the port our app uses by default.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Output
&lt;/h4&gt;

&lt;p&gt;Here's the output of the petclinic app starting...&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting the Java application using /opt/run-java/run-java.sh ...
exec java -javaagent:/opt/jolokia/jolokia.jar=config=/opt/jolokia/etc/jolokia.properties -javaagent:/opt/prometheus/jmx_prometheus_javaagent.jar=9779:/opt/prometheus/prometheus-config.yml -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:+ExitOnOutOfMemoryError -cp . -jar /deployments/spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar
I&amp;gt; No access restrictor found, access to any MBean is allowed
Jolokia: Agent started with URL http://172.17.0.2:8778/jolokia/


              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/

:: Built with Spring Boot :: 2.3.1.RELEASE


2020-07-19 16:06:40.218  INFO 1 --- [           main] o.s.s.petclinic.PetClinicApplication     : Starting PetClinicApplication v2.3.1.BUILD-SNAPSHOT on 4920727ddb55 with PID 1 (/deployments/spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar started by jboss in /deployments)
2020-07-19 16:06:40.226  INFO 1 --- [           main] o.s.s.petclinic.PetClinicApplication     : No active profile set, falling back to default profiles: default
2020-07-19 16:06:42.947  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFERRED mode.
2020-07-19 16:06:43.168  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 195ms. Found 4 JPA repository interfaces.
2020-07-19 16:06:44.925  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-07-19 16:06:44.951  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-07-19 16:06:44.952  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.36]
2020-07-19 16:06:45.140  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-07-19 16:06:45.140  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4797 ms
2020-07-19 16:06:46.331  INFO 1 --- [           main] org.ehcache.core.EhcacheManager          : Cache 'vets' created in EhcacheManager.
2020-07-19 16:06:46.375  INFO 1 --- [           main] org.ehcache.jsr107.Eh107CacheManager     : Registering Ehcache MBean javax.cache:type=CacheStatistics,CacheManager=urn.X-ehcache.jsr107-default-config,Cache=vets
2020-07-19 16:06:46.386  INFO 1 --- [           main] org.ehcache.jsr107.Eh107CacheManager     : Registering Ehcache MBean javax.cache:type=CacheStatistics,CacheManager=urn.X-ehcache.jsr107-default-config,Cache=vets
2020-07-19 16:06:46.498  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-07-19 16:06:47.133  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-07-19 16:06:47.609  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-07-19 16:06:48.127  INFO 1 --- [         task-1] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-07-19 16:06:49.586  INFO 1 --- [         task-1] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.17.Final
2020-07-19 16:06:50.482  INFO 1 --- [         task-1] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-07-19 16:06:51.609  INFO 1 --- [         task-1] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2020-07-19 16:06:54.376  INFO 1 --- [         task-1] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-07-19 16:06:54.432  INFO 1 --- [         task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-07-19 16:06:54.523  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 13 endpoint(s) beneath base path '/actuator'
2020-07-19 16:06:54.605  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-07-19 16:06:54.608  INFO 1 --- [           main] DeferredRepositoryInitializationListener : Triggering deferred initialization of Spring Data repositories?
2020-07-19 16:06:56.055  INFO 1 --- [           main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-07-19 16:06:56.073  INFO 1 --- [           main] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 16.736 seconds (JVM running for 18.197)
^C2020-07-19 16:09:43.051  INFO 1 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-07-19 16:09:43.056  INFO 1 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2020-07-19 16:09:43.057  INFO 1 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-07-19 16:09:43.074  INFO 1 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2020-07-19 16:09:43.085  INFO 1 --- [extShutdownHook] org.ehcache.core.EhcacheManager          : Cache 'vets' removed from EhcacheManager.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Verify
&lt;/h4&gt;

&lt;p&gt;Lastly, let's verify that our app is running by opening our browser to &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ey9CGoRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9qsl90nzqc7tg9qau2sz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ey9CGoRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9qsl90nzqc7tg9qau2sz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. With one simple command we've converted our java app source code to an OCI image.&lt;/p&gt;

&lt;p&gt;This OCI image can then be pushed to any container registry (&lt;a href="https://hub.docker.com/"&gt;DockerHub&lt;/a&gt;, &lt;a href="https://aws.amazon.com/ecr/"&gt;ECR&lt;/a&gt;, &lt;a href="https://cloud.google.com/container-registry"&gt;GCR&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/en-us/services/container-registry/"&gt;ACR&lt;/a&gt;, etc) and ran in any platform that supports OCI images such as #kubernetes.&lt;/p&gt;


&lt;h2&gt;
  
  
  Pitfalls
&lt;/h2&gt;

&lt;p&gt;Below are a few pitfalls to consider when using &lt;code&gt;s2i&lt;/code&gt;. These may very well be moot points if they are resolved using the &lt;code&gt;--runtime-image&lt;/code&gt; option. Something I plan to explore further in another post.&lt;/p&gt;
&lt;h3&gt;
  
  
  Source leak
&lt;/h3&gt;

&lt;p&gt;Depending on your requirements this may or not be an issue but given the way the basic process works the source code is persisted on the final app image. This not only increases the size unnecessarily but also exposes information that you otherwise might not have liked to be exposed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MBgOSB8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z9nrnnmwwc8ldzo0faj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MBgOSB8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z9nrnnmwwc8ldzo0faj6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Build tools leak
&lt;/h3&gt;

&lt;p&gt;This is very well dependent on the builder to a certain extent. &lt;code&gt;s2i build&lt;/code&gt; without providing a separate &lt;strong&gt;run image&lt;/strong&gt; from the &lt;strong&gt;build image&lt;/strong&gt; means that all the tools necessary for building the application are carried over to the app image.&lt;/p&gt;

&lt;p&gt;There are two immediate concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From a security perspective, the additional build tools may increase the potential attack vectors.&lt;/li&gt;
&lt;li&gt;From an optimization perspective, the image size is unnecessarily larger increasing storage space usage and transport data and time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0XEg9vvh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bhl263y0yv4la77rwofw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0XEg9vvh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bhl263y0yv4la77rwofw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Update
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;07/22/2020:&lt;/strong&gt; A new post is up that goes into more detail about how to resolve these pitfalls:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jromero" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0jnXwVn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--QQIveiGR--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/435539/32d59662-d192-40e8-8082-70417d0e254d.png" alt="jromero image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jromero/creating-an-s2i-builder-for-go-and-a-runtime-image-5d56" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Creating an s2i builder for Go (and a runtime image)&lt;/h2&gt;
      &lt;h3&gt;Javier Romero ・ Jul 23 ・ 8 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#openshift&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#s2i&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



</description>
      <category>docker</category>
      <category>devops</category>
      <category>openshift</category>
      <category>s2i</category>
    </item>
  </channel>
</rss>
