<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="http://juan-medina.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://juan-medina.com/" rel="alternate" type="text/html" /><updated>2026-04-11T08:11:43+01:00</updated><id>http://juan-medina.com/feed.xml</id><title type="html">Juan Medina Personal Blog</title><subtitle>Personal blog from Juan Medina</subtitle><entry><title type="html">Optimizing Kubernetes Services - Part 3 &amp;amp;#58 Spring WebFlux</title><link href="http://juan-medina.com/2020/01/29/optimizing-k8s-sv-03/" rel="alternate" type="text/html" title="Optimizing Kubernetes Services - Part 3 &amp;amp;#58 Spring WebFlux" /><published>2020-01-29T23:00:00+00:00</published><updated>2020-01-29T23:00:00+00:00</updated><id>http://juan-medina.com/2020/01/29/optimizing-k8s-sv-03</id><content type="html" xml:base="http://juan-medina.com/2020/01/29/optimizing-k8s-sv-03/"><![CDATA[<p>Some days ago we did an optimized version of our series with <a href="/2020/01/22/optimizing-k8s-sv-02/" target="_blank">Spring Web</a>, now we will try to improve further the numbers using a reactive platform, for this we will use <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html" target="_blank">Spring WebFlux</a> and <a href="https://r2dbc.io/" target="_blank">R2DBC</a>.</p>

<p>Here, as a reference, are the numbers that we got so far :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
        <th>Service</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">39.16</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">25.49</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">403</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">474</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">34.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">28.85</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">209</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">252</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">111.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">85.53</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">350</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1615.82</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.72</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1049</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1454</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">266.03</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">89.44</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">686</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">369</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">5</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3190.98</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.89</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1351</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">2499</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1982.03</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">23.95</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">774</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1060</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>01 Base = Java 8 - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC default settings, unoptimized <br />02 Spring Web = Java 11 - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC optimized <br /></p>

<p><strong>Bootstrapping a Spring Boot application</strong></p>

<p>We are going to use Spring Initializr <a href="https://start.spring.io/" target="_blank">https://start.spring.io/</a> to quickly bootstrap our application.</p>

<p>We will create a <em>Maven Project</em> using <em>Java 1</em> and <em>Spring Boot</em> version <em>2.2.4</em>, we will set the <em>Group</em> to be <em>org.learning.by.example.movies</em>, and the <em>Artifact</em> to <em>spring-webflux</em>, the rest of the values should be automatically populated.</p>

<p><a href="/assets/img/captures/movies_spring_webflux_01.jpg" target="_blank"><img src="/assets/img/captures/movies_spring_webflux_01.jpg" alt="Spring Initializr setup" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>For dependencies we will search and add :</p>
<ul>
  <li>Spring Reactive Web</li>
  <li>Spring Data R2DBC [Experimental]</li>
  <li>PostgreSQL Driver</li>
  <li>Spring Boot Actuator</li>
</ul>

<p><a href="/assets/img/captures/movies_spring_webflux_02.jpg" target="_blank"><img src="/assets/img/captures/movies_spring_webflux_02.jpg" alt="Spring Initializr dependencies" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now we will click on Generate to download our zip file : <em>spring-webflux.zip</em>, we will uncompress it and leave it ready to be opened with our favorite IDE.</p>

<p><strong>Creating a Reactive repository</strong></p>

<p>First we will create our POJO that we will use to retrive a movie.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.model</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Objects</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Movie</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Integer</span> <span class="n">id</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">title</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Integer</span> <span class="n">year</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">genres</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">Movie</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Integer</span> <span class="n">id</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">title</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">Integer</span> <span class="n">year</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">genres</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">title</span> <span class="o">=</span> <span class="n">title</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">year</span> <span class="o">=</span> <span class="n">year</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">genres</span> <span class="o">=</span> <span class="n">genres</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getTitle</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">title</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getYear</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">year</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="nf">getGenres</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">genres</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>This is identical as we did in our JDBC example.</p>

<p>Now we will create our reactive repository.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.repositories</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.springwebflux.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.r2dbc.repository.Query</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.repository.reactive.ReactiveCrudRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>

<span class="nd">@Repository</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">MoviesRepository</span> <span class="kd">extends</span> <span class="nc">ReactiveCrudRepository</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="nd">@Query</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"SELECT \n"</span> <span class="o">+</span>
            <span class="s">"  m.id, m.title, m.genres \n"</span> <span class="o">+</span>
            <span class="s">" FROM \n"</span> <span class="o">+</span>
            <span class="s">"  movies as m \n"</span> <span class="o">+</span>
            <span class="s">" WHERE :genre = ANY(string_to_array(LOWER(m.genres),'|')) \n"</span><span class="o">)</span>
    <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">&gt;</span> <span class="nf">findByGenre</span><span class="o">(</span><span class="nc">String</span> <span class="n">genre</span><span class="o">);</span>
<span class="o">}</span></code></pre></figure>

<p>This is very similar of the JDBC repository that we create before.</p>

<p>Now we need to create a converter.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.converter</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">io.r2dbc.spi.Row</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.springwebflux.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.convert.converter.Converter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Objects</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.regex.Matcher</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.regex.Pattern</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.stream.Collectors</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MovieReadConverter</span> <span class="kd">implements</span> <span class="nc">Converter</span><span class="o">&lt;</span><span class="nc">Row</span><span class="o">,</span> <span class="nc">Movie</span><span class="o">&gt;</span> <span class="o">{</span>

    <span class="c1">// Without escape characters this pattern ins : (.*) \((\d{4})\)</span>
    <span class="c1">//  (       = START GROUP 1</span>
    <span class="c1">//  .*      = any set of characters</span>
    <span class="c1">//  )       = END GROUP 1</span>
    <span class="c1">//          = space</span>
    <span class="c1">//  \(      = the character (</span>
    <span class="c1">//  (       = START GROUP 2</span>
    <span class="c1">//  d{4}    = four digits, ex 1945</span>
    <span class="c1">//  )       = END GROUP 2</span>
    <span class="c1">//  \)      = the character )</span>
    <span class="kd">final</span> <span class="kd">static</span> <span class="nc">Pattern</span> <span class="no">TITLE_YEAR_PATTERN</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"(.*) \\((\\d{4})\\)"</span><span class="o">);</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="nc">Movie</span> <span class="nf">convert</span><span class="o">(</span><span class="nc">Row</span> <span class="n">row</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">String</span> <span class="n">rowTile</span> <span class="o">=</span> <span class="n">row</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"title"</span><span class="o">,</span> <span class="nc">String</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>

        <span class="kd">final</span> <span class="nc">String</span> <span class="n">realTitle</span><span class="o">;</span>
        <span class="kd">final</span> <span class="kt">int</span> <span class="n">year</span><span class="o">;</span>

        <span class="kd">final</span> <span class="nc">Matcher</span> <span class="n">matcher</span> <span class="o">=</span> <span class="no">TITLE_YEAR_PATTERN</span><span class="o">.</span><span class="na">matcher</span><span class="o">(</span><span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">rowTile</span><span class="o">));</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">matcher</span><span class="o">.</span><span class="na">find</span><span class="o">())</span> <span class="o">{</span>
            <span class="n">realTitle</span> <span class="o">=</span> <span class="n">matcher</span><span class="o">.</span><span class="na">group</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
            <span class="n">year</span> <span class="o">=</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">matcher</span><span class="o">.</span><span class="na">group</span><span class="o">(</span><span class="mi">2</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">realTitle</span> <span class="o">=</span> <span class="n">rowTile</span><span class="o">;</span>
            <span class="n">year</span> <span class="o">=</span> <span class="mi">1900</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">final</span> <span class="nc">String</span><span class="o">[]</span> <span class="n">genres</span> <span class="o">=</span> <span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">row</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"genres"</span><span class="o">,</span> <span class="nc">String</span><span class="o">.</span><span class="na">class</span><span class="o">)).</span><span class="na">split</span><span class="o">(</span><span class="s">"\\|"</span><span class="o">);</span>
        <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">genresList</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">genres</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span>
                <span class="n">genre</span> <span class="o">-&gt;</span> <span class="o">!</span><span class="n">genre</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">()</span>
        <span class="o">).</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>

        <span class="kd">final</span> <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">row</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"id"</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>

        <span class="k">return</span> <span class="k">new</span> <span class="nf">Movie</span><span class="o">(</span><span class="n">id</span><span class="o">,</span> <span class="n">realTitle</span><span class="o">,</span> <span class="n">year</span><span class="o">,</span> <span class="n">genresList</span><span class="o">);</span>

    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>This is doing almost what our mapper was doing for JDBC.</p>

<p>Next we will need to create a <em>ConfigurationProperties</em> to have our database configuration.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.datasource</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.springframework.boot.context.properties.ConfigurationProperties</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="nd">@ConfigurationProperties</span><span class="o">(</span><span class="s">"movies-datasource"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">DataSourceProperties</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">protocol</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">host</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">Integer</span> <span class="n">port</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">database</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">Boolean</span> <span class="n">readOnly</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">PoolConfig</span> <span class="n">pool</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getProtocol</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">protocol</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setProtocol</span><span class="o">(</span><span class="nc">String</span> <span class="n">protocol</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">protocol</span> <span class="o">=</span> <span class="n">protocol</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getCredentials</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCredentials</span><span class="o">(</span><span class="nc">String</span> <span class="n">credentials</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">credentials</span> <span class="o">=</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getHost</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">host</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setHost</span><span class="o">(</span><span class="nc">String</span> <span class="n">host</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">host</span> <span class="o">=</span> <span class="n">host</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getPort</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">port</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setPort</span><span class="o">(</span><span class="nc">Integer</span> <span class="n">port</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">port</span> <span class="o">=</span> <span class="n">port</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getDatabase</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">database</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setDatabase</span><span class="o">(</span><span class="nc">String</span> <span class="n">database</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">database</span> <span class="o">=</span> <span class="n">database</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Boolean</span> <span class="nf">getReadOnly</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">readOnly</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setReadOnly</span><span class="o">(</span><span class="nc">Boolean</span> <span class="n">readOnly</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">readOnly</span> <span class="o">=</span> <span class="n">readOnly</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">PoolConfig</span> <span class="nf">getPool</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">pool</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setPool</span><span class="o">(</span><span class="nc">PoolConfig</span> <span class="n">pool</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">pool</span> <span class="o">=</span> <span class="n">pool</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">PoolConfig</span> <span class="o">{</span>
        <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getMinConnections</span><span class="o">()</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">minConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setMinConnections</span><span class="o">(</span><span class="kt">int</span> <span class="n">minConnections</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">minConnections</span> <span class="o">=</span> <span class="n">minConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getMaxConnections</span><span class="o">()</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">maxConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setMaxConnections</span><span class="o">(</span><span class="kt">int</span> <span class="n">maxConnections</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">maxConnections</span> <span class="o">=</span> <span class="n">maxConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">private</span> <span class="kt">int</span> <span class="n">minConnections</span><span class="o">;</span>
        <span class="kd">private</span> <span class="kt">int</span> <span class="n">maxConnections</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>This is very similar to our JDBC example however we are not use a JDBC string so will be set the host, port and database in separate variables.</p>

<p>Finally we will setup our data base configuration.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.datasource</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">io.r2dbc.spi.ConnectionFactories</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.r2dbc.spi.ConnectionFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.r2dbc.spi.ConnectionFactoryOptions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.r2dbc.spi.Option</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.springwebflux.converter.MovieReadConverter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Bean</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.convert.converter.Converter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.r2dbc.convert.R2dbcCustomConversions</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.nio.file.Files</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Path</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Paths</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.ArrayList</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.HashMap</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Map</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">static</span> <span class="n">io</span><span class="o">.</span><span class="na">r2dbc</span><span class="o">.</span><span class="na">pool</span><span class="o">.</span><span class="na">PoolingConnectionFactoryProvider</span><span class="o">.*;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">io</span><span class="o">.</span><span class="na">r2dbc</span><span class="o">.</span><span class="na">spi</span><span class="o">.</span><span class="na">ConnectionFactoryOptions</span><span class="o">.*;</span>

<span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MoviesDataSource</span> <span class="kd">extends</span> <span class="nc">AbstractR2dbcConfiguration</span> <span class="o">{</span>
    <span class="kd">final</span> <span class="nc">DataSourceProperties</span> <span class="n">dataSourceProperties</span><span class="o">;</span>
    <span class="kd">final</span> <span class="nc">MovieReadConverter</span> <span class="n">movieReadConverter</span><span class="o">;</span>
    <span class="kd">final</span> <span class="nc">String</span> <span class="n">userName</span><span class="o">;</span>
    <span class="kd">final</span> <span class="nc">String</span> <span class="n">password</span><span class="o">;</span>

    <span class="nc">MoviesDataSource</span><span class="o">(</span><span class="kd">final</span> <span class="nc">DataSourceProperties</span> <span class="n">dataSourceProperties</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">MovieReadConverter</span> <span class="n">movieReadConverter</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">dataSourceProperties</span> <span class="o">=</span> <span class="n">dataSourceProperties</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">movieReadConverter</span> <span class="o">=</span> <span class="n">movieReadConverter</span><span class="o">;</span>
        <span class="kd">final</span> <span class="nc">String</span> <span class="n">credentials</span> <span class="o">=</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getCredentials</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">userName</span> <span class="o">=</span> <span class="n">getCredentialValue</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="s">"username"</span><span class="o">);</span>
        <span class="k">this</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">getCredentialValue</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="s">"password"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="nf">getCredentialValue</span><span class="o">(</span><span class="kd">final</span> <span class="nc">String</span> <span class="n">credentials</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="nc">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="nc">Files</span><span class="o">.</span><span class="na">readAllBytes</span><span class="o">(</span><span class="n">path</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"error getting data source credential value : "</span> <span class="o">+</span> <span class="n">path</span><span class="o">.</span><span class="na">toString</span><span class="o">(),</span> <span class="n">ex</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>


    <span class="nd">@Override</span>
    <span class="nd">@Bean</span>
    <span class="kd">public</span> <span class="nc">R2dbcCustomConversions</span> <span class="nf">r2dbcCustomConversions</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Converter</span><span class="o">&lt;?,</span> <span class="o">?&gt;&gt;</span> <span class="n">converterList</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span>
        <span class="n">converterList</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">movieReadConverter</span><span class="o">);</span>
        <span class="k">return</span> <span class="k">new</span> <span class="nf">R2dbcCustomConversions</span><span class="o">(</span><span class="n">getStoreConversions</span><span class="o">(),</span> <span class="n">converterList</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="nd">@Bean</span>
    <span class="kd">public</span> <span class="nc">ConnectionFactory</span> <span class="nf">connectionFactory</span><span class="o">()</span> <span class="o">{</span>

        <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;();</span>
        <span class="n">options</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"default_transaction_read_only"</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getReadOnly</span><span class="o">().</span><span class="na">toString</span><span class="o">());</span>

        <span class="k">return</span> <span class="nc">ConnectionFactories</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">ConnectionFactoryOptions</span><span class="o">.</span><span class="na">builder</span><span class="o">()</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">PROTOCOL</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getProtocol</span><span class="o">())</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">DRIVER</span><span class="o">,</span> <span class="no">POOLING_DRIVER</span><span class="o">)</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">HOST</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getHost</span><span class="o">())</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">PORT</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getPort</span><span class="o">())</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">USER</span><span class="o">,</span> <span class="n">userName</span><span class="o">)</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">PASSWORD</span><span class="o">,</span> <span class="n">password</span><span class="o">)</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">SSL</span><span class="o">,</span> <span class="kc">true</span><span class="o">)</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="nc">Option</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="s">"sslMode"</span><span class="o">),</span> <span class="s">"allow"</span><span class="o">)</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">DATABASE</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getDatabase</span><span class="o">())</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">INITIAL_SIZE</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getPool</span><span class="o">().</span><span class="na">getMinConnections</span><span class="o">())</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">MAX_SIZE</span><span class="o">,</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getPool</span><span class="o">().</span><span class="na">getMaxConnections</span><span class="o">())</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="no">VALIDATION_QUERY</span><span class="o">,</span> <span class="s">"SELECT 1"</span><span class="o">)</span>
                <span class="o">.</span><span class="na">option</span><span class="o">(</span><span class="nc">Option</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="s">"options"</span><span class="o">),</span> <span class="n">options</span><span class="o">)</span>
                <span class="o">.</span><span class="na">build</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>This is how setup a connection to our database with a connection pool and set our converter.</p>

<p><strong>Creating our Routes</strong></p>

<p>We wil continue creating our router</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.router</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.springwebflux.handler.MovieHandler</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Bean</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.reactive.config.EnableWebFlux</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.reactive.function.server.RouterFunction</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.reactive.function.server.ServerResponse</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">web</span><span class="o">.</span><span class="na">reactive</span><span class="o">.</span><span class="na">function</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">RequestPredicates</span><span class="o">.</span><span class="na">GET</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">web</span><span class="o">.</span><span class="na">reactive</span><span class="o">.</span><span class="na">function</span><span class="o">.</span><span class="na">server</span><span class="o">.</span><span class="na">RouterFunctions</span><span class="o">.</span><span class="na">route</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="nd">@EnableWebFlux</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MovieRouter</span> <span class="o">{</span>

    <span class="kd">final</span> <span class="nc">MovieHandler</span> <span class="n">movieHandler</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">MovieRouter</span><span class="o">(</span><span class="kd">final</span> <span class="nc">MovieHandler</span> <span class="n">movieHandler</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">movieHandler</span> <span class="o">=</span> <span class="n">movieHandler</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Bean</span>
    <span class="nc">RouterFunction</span><span class="o">&lt;</span><span class="nc">ServerResponse</span><span class="o">&gt;</span> <span class="nf">movies</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nf">route</span><span class="o">(</span><span class="no">GET</span><span class="o">(</span><span class="s">"/movies/{genre}"</span><span class="o">),</span> <span class="nl">movieHandler:</span><span class="o">:</span><span class="n">getMovies</span><span class="o">);</span>
    <span class="o">}</span>

<span class="o">}</span></code></pre></figure>

<p>As we could see this is a simple RouterFunction for our /movies/genre URL.</p>

<p>Next will be the handler.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.springwebflux.handler</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.springwebflux.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.springwebflux.repositories.MoviesRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.reactive.function.server.ServerRequest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.reactive.function.server.ServerResponse</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Flux</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">reactor.core.publisher.Mono</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MovieHandler</span> <span class="o">{</span>
    <span class="kd">final</span> <span class="nc">MoviesRepository</span> <span class="n">moviesRepository</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">MovieHandler</span><span class="o">(</span><span class="kd">final</span> <span class="nc">MoviesRepository</span> <span class="n">moviesRepository</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">moviesRepository</span> <span class="o">=</span> <span class="n">moviesRepository</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Mono</span><span class="o">&lt;</span><span class="nc">ServerResponse</span><span class="o">&gt;</span> <span class="nf">getMovies</span><span class="o">(</span><span class="kd">final</span> <span class="nc">ServerRequest</span> <span class="n">request</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">String</span> <span class="n">genre</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="na">pathVariable</span><span class="o">(</span><span class="s">"genre"</span><span class="o">);</span>
        <span class="kd">final</span> <span class="nc">Flux</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">&gt;</span> <span class="n">movies</span> <span class="o">=</span> <span class="n">moviesRepository</span><span class="o">.</span><span class="na">findByGenre</span><span class="o">(</span><span class="n">genre</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">());</span>
        <span class="k">return</span> <span class="nc">ServerResponse</span><span class="o">.</span><span class="na">ok</span><span class="o">().</span><span class="na">body</span><span class="o">(</span><span class="n">movies</span><span class="o">,</span> <span class="nc">Movie</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>The handler is using the repository to retrieve the movies and return the response back to the router.</p>

<p>Finally we will setup our configuration in our application.yml</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">movies-datasource</span><span class="pi">:</span>
  <span class="na">protocol</span><span class="pi">:</span> <span class="s2">"</span><span class="s">postgresql"</span>
  <span class="na">host</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${MOVIES_DB_CLUSTER_SERVICE_HOST}"</span>
  <span class="na">port</span><span class="pi">:</span> <span class="s">${MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL}</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s2">"</span><span class="s">movies"</span>
  <span class="na">credentials</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
  <span class="na">read-only</span><span class="pi">:</span> <span class="kc">true</span>
  <span class="na">pool</span><span class="pi">:</span>
    <span class="na">min-connections</span><span class="pi">:</span> <span class="m">1</span>
    <span class="na">max-connections</span><span class="pi">:</span> <span class="m">3</span>
<span class="na">logging</span><span class="pi">:</span>
  <span class="na">level</span><span class="pi">:</span>
    <span class="na">ROOT</span><span class="pi">:</span> <span class="s">ERROR</span>
<span class="na">spring</span><span class="pi">:</span>
  <span class="na">main</span><span class="pi">:</span>
    <span class="na">banner-mode</span><span class="pi">:</span> <span class="s">off</span>
  <span class="na">jmx</span><span class="pi">:</span>
    <span class="na">enabled</span><span class="pi">:</span> <span class="kc">false</span></code></pre></figure>

<p><strong>Configuring our deployment</strong></p>

<p>First we will copy our Dockerfile, deployment.yml, JMeter scripts and bash scripts for our previous example and rename <code class="language-plaintext highlighter-rouge">movies-spring-web</code> to <code class="language-plaintext highlighter-rouge">movies-spring-webflux</code> on them.</p>

<p>Them we will modify our deployment.yml</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
  <span class="na">strategy</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movies-spring-webflux:0.0.1</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
          <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
          <span class="na">env</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">JAVA_OPTS"</span>
              <span class="na">value</span><span class="pi">:</span> <span class="pi">&gt;-</span>
                <span class="s">-XX:+UseParallelGC</span>
                <span class="s">-Xms100m</span>
                <span class="s">-Xmx325m</span>
                <span class="s">--add-opens java.base/jdk.internal.misc=ALL-UNNAMED</span>
                <span class="s">--add-opens java.base/java.nio=ALL-UNNAMED</span>
                <span class="s">--add-exports java.base/jdk.internal.misc=ALL-UNNAMED</span>
                <span class="s">-Dio.netty.tryReflectionSetAccessible=true</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/health</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/info</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesuser.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">8080-8080</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-webflux</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>We are setting up additional JAVA_OPTS that are require to use native buffers in Netty on Java 9+.</p>

<p>Them we will edit our jlink.sh script to include an additional module :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nv">BASE_DIR</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>
  <span class="nb">cd</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">pwd</span> <span class="nt">-P</span>
<span class="si">)</span><span class="s2">"</span>

<span class="nv">DEPS_DIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">/deps"</span>

<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">"extracting jar"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>
jar <span class="nt">-xf</span> ../<span class="k">*</span>.jar
<span class="nb">echo</span> <span class="s2">"jar extracted"</span>

<span class="nb">echo</span> <span class="s2">"generate JVM modules list"</span>
<span class="nv">EX_JVM_DEPS</span><span class="o">=</span><span class="s2">"jdk.crypto.ec,jdk.unsupported"</span>
<span class="nv">JVM_DEPS</span><span class="o">=</span><span class="si">$(</span>jdeps <span class="nt">-s</span> <span class="nt">--multi-release</span> 11 <span class="nt">-recursive</span> <span class="nt">-cp</span> BOOT-INF/lib/<span class="k">*</span>.jar BOOT-INF/classes | <span class="se">\</span>
  <span class="nb">grep</span> <span class="nt">-Po</span> <span class="s1">'(java|jdk)\..*'</span> | <span class="se">\</span>
  <span class="nb">sort</span> <span class="nt">-u</span> | <span class="se">\</span>
  <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">','</span><span class="si">)</span>
<span class="nv">MODULES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$JVM_DEPS$EX_JVM_DEPS</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"JVM modules list generated"</span>

<span class="nb">echo</span> <span class="s2">"doing jlink"</span>
jlink <span class="se">\</span>
    <span class="nt">--verbose</span> <span class="se">\</span>
    <span class="nt">--module-path</span> <span class="s2">"</span><span class="nv">$JAVA_HOME</span><span class="s2">/jmods"</span>, <span class="se">\</span>
    <span class="nt">--add-modules</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--compress</span> 2 <span class="se">\</span>
    <span class="nt">--no-header-files</span> <span class="se">\</span>
    <span class="nt">--no-man-pages</span> <span class="se">\</span>
    <span class="nt">--strip-debug</span> <span class="se">\</span>
    <span class="nt">--output</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">/jre-jlink"</span>

<span class="nb">echo</span> <span class="s2">"jlink done"</span>

<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">"</span></code></pre></figure>

<p>Finally we could build and deploy our application :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh
<span class="nv">$ </span>./deploy.sh</code></pre></figure>

<p><strong>Running the complete set</strong></p>

<p>With this we are ready to run our final test following the procedure that we stablish in the <a href="/2020/01/11/optimizing-k8s-sv-01/" target="_blank">first part</a> of the series, and we will get this numbers :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
        <th>Service</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">39.16</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">25.49</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">403</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">474</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">34.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">28.85</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">209</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">252</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">31.21</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">31.99</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">335</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">310</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">03 Spring WebFlux</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">111.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">85.53</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">350</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">100.93</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">94.59</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1036</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">355</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">03 Spring WebFlux</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1615.82</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.72</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1049</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1454</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">266.03</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">89.44</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">686</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">369</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">368.0</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">76.33</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1046</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">511</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">03 Spring WebFlux</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">5</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3190.98</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.89</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1351</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">2499</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1982.03</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">23.95</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">774</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1060</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">631.65</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">75.24</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1063</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">515</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">03 Spring WebFlux</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>01 Base = Java 8 - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC default settings, unoptimized <br />02 Spring Web = Java 11 - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC optimized <br />03 Spring WebFlux = Java 11 - Spring Boot 2.2.4 - Spring WebFlux - Spring Data R2DBC optimized</p>

<p><strong>Conclusions</strong></p>

<p>As we could see the performance of the reactive solution is great, we could even handle 50 concurrent users with just one Pod, and that is because we are not so bound to threads in this implementation.</p>

<p>However we could see that with 25 concurrent users we are not doing so great, and that is probably because we setup very a very small heap and the GC is cleaning more often that it should, but I’ve setup this to minimize memory consumption, we could increase a bit the heap this will cause less GC runs, better ART but more MEM usage, and this is key in our overall optimization strategy, if we care more about ART of use of MEM.</p>

<p>Finally, Spring Data R2DBC still on experimental stage, numbers will change with future relases.</p>

<p><em>Note: The full code of this service is available at this <a href="https://github.com/LearningByExample/movies-spring-webflux/" target="_blank">repository</a>.</em></p>

<p><strong>Resources</strong></p>

<ul>
  <li><a href="https://www.postgresql.org/docs/current/runtime-config-client.html" target="_blank">https://www.postgresql.org/docs/current/runtime-config-client.html</a></li>
  <li><a href="https://github.com/r2dbc/r2dbc-postgresql" target="_blank">https://github.com/r2dbc/r2dbc-postgresql</a></li>
  <li><a href="https://github.com/netty/netty/issues/7838" target="_blank">https://github.com/netty/netty/issues/7838</a></li>
</ul>]]></content><author><name>Juan Medina</name></author><category term="Cloud" /><category term="Programming" /><summary type="html"><![CDATA[Some days ago we did an optimized version of our series with Spring Web, now we will try to improve further the numbers using a reactive platform, for this we will use Spring WebFlux and R2DBC.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/graph.jpg" /><media:content medium="image" url="http://juan-medina.com/assets/img/graph.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Optimizing Kubernetes Services - Part 2 &amp;amp;#58 Spring Web</title><link href="http://juan-medina.com/2020/01/22/optimizing-k8s-sv-02/" rel="alternate" type="text/html" title="Optimizing Kubernetes Services - Part 2 &amp;amp;#58 Spring Web" /><published>2020-01-22T23:00:00+00:00</published><updated>2020-01-22T23:00:00+00:00</updated><id>http://juan-medina.com/2020/01/22/optimizing-k8s-sv-02</id><content type="html" xml:base="http://juan-medina.com/2020/01/22/optimizing-k8s-sv-02/"><![CDATA[<p>Some days ago we start this series of article with our <a href="/2020/01/11/optimizing-k8s-sv-01/" target="_blank">base example</a>, now we have baseline that we could use to optimize the service.</p>

<p>These are the number that we got, the ones that we will like to improve :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">39.16</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">25.49</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">403</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">474</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1615.82</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.72</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1049</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1454</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">5</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3190.98</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.89</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1351</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">2499</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>Java 8  - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC default settings, unoptimized</p>

<p><strong>Moving from Java 8 to Java 11</strong></p>

<p>For this example we will first clone our <a href="https://github.com/LearningByExample/movies-base-service" target="_blank">original project</a> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>git clone https://github.com/LearningByExample/movies-spring-web.git movies-spring-web</code></pre></figure>

<p>Now we will rename all references in code, scripts, yaml and packages from <em>movies-base-service</em> to <em>movies-spring-web</em></p>

<p>Them we will modify our <em>pom.xml</em>  to use Java 11 :</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">    <span class="nt">&lt;properties&gt;</span>
        <span class="nt">&lt;java.version&gt;</span>11<span class="nt">&lt;/java.version&gt;</span>
    <span class="nt">&lt;/properties&gt;</span></code></pre></figure>

<p>Finally we will change our Dockerfile to use the Java 11 base image :</p>

<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="s"> openjdk:11-jdk</span>

<span class="k">COPY</span><span class="s"> target/*.jar /usr/app/app.jar</span>
<span class="k">WORKDIR</span><span class="s"> /usr/app</span>

<span class="k">CMD</span><span class="s"> ["java", "-jar", "app.jar"]</span></code></pre></figure>

<p>With this changes we will build and deploy our new service :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh
<span class="nv">$ </span>./deploy.sh</code></pre></figure>

<p>We could now check that is deployed in our local cluster :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get all <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>movies-spring-web
NAME                                     READY   STATUS    RESTARTS   AGE
pod/movies-spring-web-66b94f8479-mmmxw   1/1     Running   0          17s

NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>    AGE
service/movies-spring-web   ClusterIP   10.152.183.195   &lt;none&gt;        8080/TCP   17s

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/movies-spring-web   1/1     1            1           17s

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/movies-spring-web-66b94f8479   1         1         1       17s</code></pre></figure>

<p>We could check that our service is running correctly with <a href="https://httpie.org/" target="_blank">HTTPie</a> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>http 10.152.183.195:8080/movies/sci-fi
HTTP/1.1 200
Connection: keep-alive
Content-Type: application/json
Date: Sat, 18 Jan 2020 12:20:31 GMT
Keep-Alive: <span class="nb">timeout</span><span class="o">=</span>60
Transfer-Encoding: chunked

<span class="o">[</span>
    <span class="o">{</span>
        <span class="s2">"genres"</span>: <span class="o">[</span>
            <span class="s2">"Drama"</span>,
            <span class="s2">"Sci-Fi"</span>
        <span class="o">]</span>,
        <span class="s2">"id"</span>: 24,
        <span class="s2">"title"</span>: <span class="s2">"Powder"</span>,
        <span class="s2">"year"</span>: 1995
    <span class="o">}</span>,
    <span class="o">{</span>
        <span class="s2">"genres"</span>: <span class="o">[</span>
            <span class="s2">"Adventure"</span>,
            <span class="s2">"Drama"</span>,
            <span class="s2">"Fantasy"</span>,
            <span class="s2">"Mystery"</span>,
            <span class="s2">"Sci-Fi"</span>
        <span class="o">]</span>,
        <span class="s2">"id"</span>: 29,
        <span class="s2">"title"</span>: <span class="s2">"City of Lost Children, The (Cité des enfants perdus, La)"</span>,
        <span class="s2">"year"</span>: 1995
    <span class="o">}</span>,
........
........
........
<span class="o">]</span></code></pre></figure>

<p>With this we could do our load test following exactly the procedure that we describe in our <a href="/2020/01/11/optimizing-k8s-sv-01/" target="_blank">previous example</a>, but this time only for 10 concurrent users.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./load-test.sh 10 600 60</code></pre></figure>

<p>Doing this we will get this results from the JMeter report and Grafana :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>Step</th>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">01 base</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Java 11</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">517.01</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">918</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>Java 11</p>

<p>With this we could see that we have a better ART, TPS and Max CPU but we have increase the memory usage.</p>

<p><strong>Creating a optimal JRE</strong></p>

<p>One thing that we haven’t check so far is the size of our image, that may not be important for a performance perspective but it is for how fast our cluster could get install the image, let’s check it out with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>docker image <span class="nb">ls </span>localhost:32000/movies-spring-web:0.0.1
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
localhost:32000/movies-spring-web   0.0.1               491c3e0b1fdf        37 minutes ago      628MB</code></pre></figure>

<p>In java 11 we could use <a href="Jlink" target="_blank">jlink</a> to generate a optimize JRE distribution that will contains only what our application needs, for this we will create an script named jlink.sh :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nv">BASE_DIR</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>
  <span class="nb">cd</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">pwd</span> <span class="nt">-P</span>
<span class="si">)</span><span class="s2">"</span>

<span class="nv">DEPS_DIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">/deps"</span>

<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">"extracting jar"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>
jar <span class="nt">-xf</span> ../<span class="k">*</span>.jar
<span class="nb">echo</span> <span class="s2">"jar extracted"</span>

<span class="nb">echo</span> <span class="s2">"generate JVM modules list"</span>
<span class="nv">EX_JVM_DEPS</span><span class="o">=</span><span class="s2">"jdk.crypto.ec"</span>
<span class="nv">JVM_DEPS</span><span class="o">=</span><span class="si">$(</span>jdeps <span class="nt">-s</span> <span class="nt">--multi-release</span> 11 <span class="nt">-recursive</span> <span class="nt">-cp</span> BOOT-INF/lib/<span class="k">*</span>.jar BOOT-INF/classes | <span class="se">\</span>
  <span class="nb">grep</span> <span class="nt">-Po</span> <span class="s1">'(java|jdk)\..*'</span> | <span class="se">\</span>
  <span class="nb">sort</span> <span class="nt">-u</span> | <span class="se">\</span>
  <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">','</span><span class="si">)</span>
<span class="nv">MODULES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$JVM_DEPS$EX_JVM_DEPS</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"JVM modules list generated"</span>

<span class="nb">echo</span> <span class="s2">"doing jlink"</span>
jlink <span class="se">\</span>
    <span class="nt">--verbose</span> <span class="se">\</span>
    <span class="nt">--module-path</span> <span class="s2">"</span><span class="nv">$JAVA_HOME</span><span class="s2">/jmods"</span>, <span class="se">\</span>
    <span class="nt">--add-modules</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--compress</span> 2 <span class="se">\</span>
    <span class="nt">--no-header-files</span> <span class="se">\</span>
    <span class="nt">--no-man-pages</span> <span class="se">\</span>
    <span class="nt">--strip-debug</span> <span class="se">\</span>
    <span class="nt">--output</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">/jre-jlink"</span>

<span class="nb">echo</span> <span class="s2">"jlink done"</span>

<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">"</span></code></pre></figure>

<p>In this script we will uncompress our jar, use <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jdeps.html" target="_blank">jdeps</a> to find witch modules are in use, we will add additionally <em>jdk.crypto.ec</em> since we require to connect via ssl to our PostgreSQL cluster, and then produce a optimize JRE image using jlink.</p>

<p>Now we will modify our Dockerfile to perform a multi-stage build :</p>

<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="w"> </span><span class="s">openjdk:11</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>

<span class="k">COPY</span><span class="s"> target/*.jar /app/app.jar</span>
<span class="k">COPY</span><span class="s"> jlink.sh /app/jlink.sh</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">RUN </span>./jlink.sh

<span class="k">FROM</span><span class="s"> openjdk:11-jre-slim</span>

<span class="k">RUN </span><span class="nb">rm</span> <span class="nt">-rf</span> /usr/local/openjdk-11
<span class="k">COPY</span><span class="s"> --from=builder /app/deps/jre-jlink /usr/local/openjdk-11</span>
<span class="k">COPY</span><span class="s"> --from=builder /app/app.jar /app/app.jar</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="k">CMD</span><span class="s"> ["java", "-jar", "app.jar"]</span></code></pre></figure>

<p>In this docker file we create a intermediate container named builder base on the openjdk 11 image, we will use this container to extract our application jar and invoke our jlink.sh script.</p>

<p>Them our Dockerfile will create a image base on the JRE 11 slim and will add our application and replace the provide JRE with the one that we created with jlink in the builder image. We use the base JRE image since our custom JRE requires libraries and configuration that are already present in that image.</p>

<p>We could build our container as before and the check the image size :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>docker image <span class="nb">ls </span>localhost:32000/movies-spring-web:0.0.1
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
localhost:32000/movies-spring-web   0.0.1               3fc95b166643        10 seconds ago      279MB</code></pre></figure>

<p>Now we will run our performance test as before to get  :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>Step</th>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">01 base</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Java 11</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">517.01</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">918</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">03 jlink</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">518.97</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.41</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">787</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>jlink</p>

<p>We could see that there is not major changes using a JRE build with jlink, our docker is just smaller, that will help when we need to install a image into our cluster but will not benefit in overall to response time or even the memory that we use.</p>

<p><strong>Checking the Memory Usage</strong></p>

<p>The things that sound a bit odd and we will try to explain next is why we are using so much memory, so we need a tool to understand this futher so we will use <a href="https://visualvm.github.io/" target="_blank">VisualVM</a>, but first we will need to modify our deployment in order been able to pass parameters to the JVM when we start our service, and we will to enable the JMX port that we will use.</p>

<p>First we will modify our Dockerfile :</p>

<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="w"> </span><span class="s">openjdk:11</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>

<span class="k">COPY</span><span class="s"> target/*.jar /app/app.jar</span>
<span class="k">COPY</span><span class="s"> jlink.sh /app/jlink.sh</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">RUN </span>./jlink.sh

<span class="k">FROM</span><span class="s"> openjdk:11-jre-slim</span>

<span class="k">RUN </span><span class="nb">rm</span> <span class="nt">-rf</span> /usr/local/openjdk-11
<span class="k">COPY</span><span class="s"> --from=builder /app/deps/jre-jlink /usr/local/openjdk-11</span>
<span class="k">COPY</span><span class="s"> --from=builder /app/app.jar /app/app.jar</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="k">ENTRYPOINT</span><span class="s"> ["sh", "-c", "java ${JAVA_OPTS} -jar /app/app.jar"]</span></code></pre></figure>

<p>We will have now an environment variable named <em>JAVA_OPTS</em> to set additional parameters when running our java service.</p>

<p>Now we will modify our deployment.yml :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">strategy</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movies-spring-web:0.0.1</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
          <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
          <span class="na">env</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">JAVA_OPTS"</span>
              <span class="na">value</span><span class="pi">:</span> <span class="pi">&gt;-</span>
                <span class="s">-Dcom.sun.management.jmxremote</span>
                <span class="s">-Dcom.sun.management.jmxremote.port=12345</span>
                <span class="s">-Dcom.sun.management.jmxremote.authenticate=false</span>
                <span class="s">-Dcom.sun.management.jmxremote.ssl=false</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/health</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/info</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesuser.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">8080-8080</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>But in order to use JMX we need to add to our jlink.sh the <em>jdk.management.agent</em> module :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nv">BASE_DIR</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>
  <span class="nb">cd</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">pwd</span> <span class="nt">-P</span>
<span class="si">)</span><span class="s2">"</span>

<span class="nv">DEPS_DIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">/deps"</span>

<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">"extracting jar"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>
jar <span class="nt">-xf</span> ../<span class="k">*</span>.jar
<span class="nb">echo</span> <span class="s2">"jar extracted"</span>

<span class="nb">echo</span> <span class="s2">"generate JVM modules list"</span>
<span class="nv">EX_JVM_DEPS</span><span class="o">=</span><span class="s2">"jdk.crypto.ec,jdk.management.agent"</span>
<span class="nv">JVM_DEPS</span><span class="o">=</span><span class="si">$(</span>jdeps <span class="nt">-s</span> <span class="nt">--multi-release</span> 11 <span class="nt">-recursive</span> <span class="nt">-cp</span> BOOT-INF/lib/<span class="k">*</span>.jar BOOT-INF/classes | <span class="se">\</span>
  <span class="nb">grep</span> <span class="nt">-Po</span> <span class="s1">'(java|jdk)\..*'</span> | <span class="se">\</span>
  <span class="nb">sort</span> <span class="nt">-u</span> | <span class="se">\</span>
  <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">','</span><span class="si">)</span>
<span class="nv">MODULES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$JVM_DEPS$EX_JVM_DEPS</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"JVM modules list generated"</span>

<span class="nb">echo</span> <span class="s2">"doing jlink"</span>
jlink <span class="se">\</span>
    <span class="nt">--verbose</span> <span class="se">\</span>
    <span class="nt">--module-path</span> <span class="s2">"</span><span class="nv">$JAVA_HOME</span><span class="s2">/jmods"</span>, <span class="se">\</span>
    <span class="nt">--add-modules</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--compress</span> 2 <span class="se">\</span>
    <span class="nt">--no-header-files</span> <span class="se">\</span>
    <span class="nt">--no-man-pages</span> <span class="se">\</span>
    <span class="nt">--strip-debug</span> <span class="se">\</span>
    <span class="nt">--output</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">/jre-jlink"</span>

<span class="nb">echo</span> <span class="s2">"jlink done"</span>

<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">"</span></code></pre></figure>

<p>Not we will and deploy our application.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh
<span class="nv">$ </span>./deploy.sh</code></pre></figure>

<p>Now we will forward our port 12345 for the pod to our localhost :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pod <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>movies-spring-web
NAME                                 READY   STATUS    RESTARTS   AGE
movies-spring-web-6554d5fd68-kxvxg   1/1     Running   0          7m52s

<span class="nv">$ </span>microk8s.kubectl port-forward movies-spring-web-6554d5fd68-kxvxg 12345:12345
Forwarding from 127.0.0.1:12345 -&gt; 12345
Forwarding from <span class="o">[</span>::1]:12345 -&gt; 12345</code></pre></figure>

<p>Now we should open VisualVM and add a JMX connection to our service on <em>locahost:12345</em> :</p>

<p><a href="/assets/img/captures/movies_spring_web_01.jpg" target="_blank"><img src="/assets/img/captures/movies_spring_web_01.jpg" alt="VisualVM " style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now we could click on our application and the select the tab monitor to see some graphs, I choose to only see CPU and threads :</p>

<p><a href="/assets/img/captures/movies_spring_web_02.jpg" target="_blank"><img src="/assets/img/captures/movies_spring_web_02.jpg" alt="VisualVM " style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>As we could see our heap is about 1GB, with a maximun of 16GB however we are only using 80M at peak, we could see in the CPU that our garbage collector is almost doing nothing until he enter a cleaning cycle the an small CPU usage and our heap usage drops.</p>

<p>In this moment we do not have any traffic on the service, lest run our load test for a couple of minutes.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./load-test.sh 10 120 30</code></pre></figure>

<p>I’ll leave a couple of minutes after the test to wait that the garbage collector start again and grab more data from VisualVM :</p>

<p><a href="/assets/img/captures/movies_spring_web_03.jpg" target="_blank"><img src="/assets/img/captures/movies_spring_web_03.jpg" alt="VisualVM " style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>As we could see know we use around 435M of heap, and the gc busy during our test, them we could see how drops afterwards.</p>

<p>Let’s make a bit of sense on what we have seen so far.</p>

<p>First we do not specify any memory limit on the deployment of our containers so that’s the reason that our maximum heap is set to 16G, them even without using much initial heap but the JVM is prepare to use a full G if need, up to 16G if needs more.</p>

<p>Our garbage collector is trying to do it best to keep up and clean memory when he could.</p>

<p>To just test some limit we could run more test with different loads but I think that with 450M should be ok and starting with 250M, so I going to change our VM options in our deployment :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">strategy</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movies-spring-web:0.0.1</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
          <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
          <span class="na">env</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">JAVA_OPTS"</span>
              <span class="na">value</span><span class="pi">:</span> <span class="pi">&gt;-</span>
                <span class="s">-Xms250m</span>
                <span class="s">-Xmx450m</span>
                <span class="s">-Dcom.sun.management.jmxremote</span>
                <span class="s">-Dcom.sun.management.jmxremote.port=12345</span>
                <span class="s">-Dcom.sun.management.jmxremote.authenticate=false</span>
                <span class="s">-Dcom.sun.management.jmxremote.ssl=false</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/health</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/info</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesuser.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">8080-8080</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>We do not need to build our application again, since we are not changing our docker just deploy it :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./deploy.sh</code></pre></figure>

<p>Now we will forward the port again them connect with VisualVM and repeat the test for get a new graph :</p>

<p><a href="/assets/img/captures/movies_spring_web_04.jpg" target="_blank"><img src="/assets/img/captures/movies_spring_web_04.jpg" alt="VisualVM " style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now we could see that our memory is better utilize, let’s now restart our cluster and run our performance test again to check how it perform in comparison with the previous tests, first we modify our deployment to remove the jmx port but keep the heap configuration:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">strategy</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movies-spring-web:0.0.1</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
          <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
          <span class="na">env</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">JAVA_OPTS"</span>
              <span class="na">value</span><span class="pi">:</span> <span class="pi">&gt;-</span>
                <span class="s">-Xms250m</span>
                <span class="s">-Xmx450m</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/health</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/info</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesuser.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">8080-8080</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>We will remove the jdk.management.agent module as well from our jlink.sh :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nv">BASE_DIR</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>
  <span class="nb">cd</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">pwd</span> <span class="nt">-P</span>
<span class="si">)</span><span class="s2">"</span>

<span class="nv">DEPS_DIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">/deps"</span>

<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">"extracting jar"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">"</span>
jar <span class="nt">-xf</span> ../<span class="k">*</span>.jar
<span class="nb">echo</span> <span class="s2">"jar extracted"</span>

<span class="nb">echo</span> <span class="s2">"generate JVM modules list"</span>
<span class="nv">EX_JVM_DEPS</span><span class="o">=</span><span class="s2">"jdk.crypto.ec"</span>
<span class="nv">JVM_DEPS</span><span class="o">=</span><span class="si">$(</span>jdeps <span class="nt">-s</span> <span class="nt">--multi-release</span> 11 <span class="nt">-recursive</span> <span class="nt">-cp</span> BOOT-INF/lib/<span class="k">*</span>.jar BOOT-INF/classes | <span class="se">\</span>
  <span class="nb">grep</span> <span class="nt">-Po</span> <span class="s1">'(java|jdk)\..*'</span> | <span class="se">\</span>
  <span class="nb">sort</span> <span class="nt">-u</span> | <span class="se">\</span>
  <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">','</span><span class="si">)</span>
<span class="nv">MODULES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$JVM_DEPS$EX_JVM_DEPS</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"JVM modules list generated"</span>

<span class="nb">echo</span> <span class="s2">"doing jlink"</span>
jlink <span class="se">\</span>
    <span class="nt">--verbose</span> <span class="se">\</span>
    <span class="nt">--module-path</span> <span class="s2">"</span><span class="nv">$JAVA_HOME</span><span class="s2">/jmods"</span>, <span class="se">\</span>
    <span class="nt">--add-modules</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">--compress</span> 2 <span class="se">\</span>
    <span class="nt">--no-header-files</span> <span class="se">\</span>
    <span class="nt">--no-man-pages</span> <span class="se">\</span>
    <span class="nt">--strip-debug</span> <span class="se">\</span>
    <span class="nt">--output</span> <span class="s2">"</span><span class="nv">$DEPS_DIR</span><span class="s2">/jre-jlink"</span>

<span class="nb">echo</span> <span class="s2">"jlink done"</span>

<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$BASE_DIR</span><span class="s2">"</span></code></pre></figure>

<p>Then we build &amp; deploy our service :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh
<span class="nv">$ </span>./deploy.sh</code></pre></figure>

<p>And now we follow the procedure, including scaling and restart to perform a clean test :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>Step</th>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">01 base</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Java 11</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">517.01</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">918</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">03 jlink</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">518.97</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.41</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">787</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">04 heap</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">451.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.13</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">816</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">633</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>heap changes</p>

<p><strong>Changing the garbage collector</strong></p>

<p>All the test that we have done so far are using the G1 garbage collector, this is the default since Java 9, however we could change the one that we use changing in our deployment the environment variables to tell the JVM which one to use.</p>

<p>For enabling the parallel garbage collector we will edit our deployment.yml :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">strategy</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movies-spring-web:0.0.1</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
          <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
          <span class="na">env</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">JAVA_OPTS"</span>
              <span class="na">value</span><span class="pi">:</span> <span class="pi">&gt;-</span>
                <span class="s">-XX:+UseParallelGC</span>
                <span class="s">-Xms250m</span>
                <span class="s">-Xmx450m</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/health</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/info</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesuser.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">8080-8080</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-spring-web</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>If we run our load test again this is the number that we get :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>Step</th>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">01 base</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Java 11</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">517.01</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">918</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">03 jlink</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">518.97</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.41</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">787</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">04 heap</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">451.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.13</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">816</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">633</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">05 parallel GC</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">450.85</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.17</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">799</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>parallel gc</p>

<p>We could see that parallel is even better than G1 as overall, but may need to understand this.</p>

<p>G1 is really good to have a predictable pauses when doing garbage collection, and he is continuously working on do part of the work without producing pauses. parallel in other hand is just waiting to certain thresholds to actually start doing the cleaning and them pause unpredictable, however the overall performance is better. Finally G1 is really for bigger heaps and that’s is not the case in in our service.</p>

<p>This number will vary a lot depending or what your service does, for example if you have tons of static data G1 may be better fit.</p>

<p><em>Connection pool</em></p>

<p>So far we haven take into account how our service is connected to the database, to check it out let’s connect to our database and find it out.</p>

<p>First we will forward the master of our database to a local port with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl port-forward movies-db-cluster-0 6432:5432
Forwarding from 127.0.0.1:6432 -&gt; 5432
Forwarding from <span class="o">[</span>::1]:6432 -&gt; 5432</code></pre></figure>

<p>Now we will get the password from our moviesdba user :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get secret moviesdba.movies-db-cluster.credentials <span class="nt">-o</span> <span class="s1">'jsonpath={.data.password}'</span> | <span class="nb">base64</span> <span class="nt">-d</span>
k1lYa8DJ6SeXSBY8lsvacEP1BmCRAYTrAk6HuGC9wP8aK1va7Q5voX1Ih6VrWNUA</code></pre></figure>

<p>Finally we will use PSQL to connect to our database :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span> psql <span class="nt">-h</span> localhost <span class="nt">-p</span> 6432 <span class="nt">-U</span> moviesdba movies
Password <span class="k">for </span>user moviesdba: 
psql <span class="o">(</span>11.6 <span class="o">(</span>Ubuntu 11.6-1.pgdg18.04+1<span class="o">))</span>
SSL connection <span class="o">(</span>protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off<span class="o">)</span>
Type <span class="s2">"help"</span> <span class="k">for </span>help.

<span class="nv">movies</span><span class="o">=</span><span class="c"># </span></code></pre></figure>

<p>For knowing how many active connection for our moviesuser we have we could do :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">movies</span><span class="o">=</span><span class="c"># select count(*) from pg_stat_activity where pg_stat_activity.usename='moviesuser';</span>
 count 
<span class="nt">-------</span>
     0
<span class="o">(</span>1 row<span class="o">)</span></code></pre></figure>

<p>If we run our load test and when is running we execute the same command we will see something like :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">movies</span><span class="o">=</span><span class="c"># select count(*) from pg_stat_activity where pg_stat_activity.usename='moviesuser';</span>
 count 
<span class="nt">-------</span>
    10
<span class="o">(</span>1 row<span class="o">)</span></code></pre></figure>

<p>First we will modify our application.yml to define our pool :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">movies-datasource</span><span class="pi">:</span>
  <span class="na">driver</span><span class="pi">:</span> <span class="s2">"</span><span class="s">org.postgresql.Driver"</span>
  <span class="na">connection-string</span><span class="pi">:</span> <span class="s2">"</span><span class="s">jdbc:postgresql://</span><span class="se">\
</span>    <span class="s">${MOVIES_DB_CLUSTER_SERVICE_HOST}:${MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL}</span><span class="se">\
</span>    <span class="s">/movies"</span>
  <span class="na">credentials</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
  <span class="na">read-only </span><span class="pi">:</span> <span class="kc">true</span>
  <span class="na">pool </span><span class="pi">:</span>
    <span class="na">min-connections </span><span class="pi">:</span> <span class="m">1</span>
    <span class="na">max-connections </span><span class="pi">:</span> <span class="s">3</span></code></pre></figure>

<p>Now we will modify our DataSourceProperties class :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.spring.web.datasource</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.springframework.boot.context.properties.ConfigurationProperties</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="nd">@ConfigurationProperties</span><span class="o">(</span><span class="s">"movies-datasource"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">DataSourceProperties</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getDriver</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">driver</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setDriver</span><span class="o">(</span><span class="nc">String</span> <span class="n">driver</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">driver</span> <span class="o">=</span> <span class="n">driver</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getConnectionString</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">connectionString</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setConnectionString</span><span class="o">(</span><span class="nc">String</span> <span class="n">connectionString</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">connectionString</span> <span class="o">=</span> <span class="n">connectionString</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getCredentials</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCredentials</span><span class="o">(</span><span class="nc">String</span> <span class="n">credentials</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">credentials</span> <span class="o">=</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Boolean</span> <span class="nf">getReadOnly</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">readOnly</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setReadOnly</span><span class="o">(</span><span class="nc">Boolean</span> <span class="n">readOnly</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">readOnly</span> <span class="o">=</span> <span class="n">readOnly</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">PoolConfig</span> <span class="nf">getPool</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">pool</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setPool</span><span class="o">(</span><span class="nc">PoolConfig</span> <span class="n">pool</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">pool</span> <span class="o">=</span> <span class="n">pool</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="n">driver</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">connectionString</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">Boolean</span> <span class="n">readOnly</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">PoolConfig</span> <span class="n">pool</span><span class="o">;</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">PoolConfig</span> <span class="o">{</span>
        <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getMinConnections</span><span class="o">()</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">minConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setMinConnections</span><span class="o">(</span><span class="kt">int</span> <span class="n">minConnections</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">minConnections</span> <span class="o">=</span> <span class="n">minConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getMaxConnections</span><span class="o">()</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">maxConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setMaxConnections</span><span class="o">(</span><span class="kt">int</span> <span class="n">maxConnections</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">maxConnections</span> <span class="o">=</span> <span class="n">maxConnections</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">private</span> <span class="kt">int</span> <span class="n">minConnections</span><span class="o">;</span>
        <span class="kd">private</span> <span class="kt">int</span> <span class="n">maxConnections</span><span class="o">;</span>
    <span class="o">}</span>

<span class="o">}</span></code></pre></figure>

<p>And finally our MoviesDataSource class</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.spring.web.datasource</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.zaxxer.hikari.HikariDataSource</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.logging.Log</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.logging.LogFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.nio.file.Files</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Path</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Paths</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MoviesDataSource</span> <span class="kd">extends</span> <span class="nc">HikariDataSource</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Log</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LogFactory</span><span class="o">.</span><span class="na">getLog</span><span class="o">(</span><span class="nc">MoviesDataSource</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>

    <span class="nc">MoviesDataSource</span><span class="o">(</span><span class="kd">final</span> <span class="nc">DataSourceProperties</span> <span class="n">dataSourceProperties</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setDriverClassName</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getDriver</span><span class="o">());</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setJdbcUrl</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getConnectionString</span><span class="o">());</span>
        <span class="kd">final</span> <span class="nc">String</span> <span class="n">credentials</span> <span class="o">=</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getCredentials</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setUsername</span><span class="o">(</span><span class="n">getCredentialValue</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="s">"username"</span><span class="o">));</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setPassword</span><span class="o">(</span><span class="n">getCredentialValue</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="s">"password"</span><span class="o">));</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setReadOnly</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getReadOnly</span><span class="o">());</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setMinimumIdle</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getPool</span><span class="o">().</span><span class="na">getMinConnections</span><span class="o">());</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setMaximumPoolSize</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getPool</span><span class="o">().</span><span class="na">getMaxConnections</span><span class="o">());</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setConnectionTestQuery</span><span class="o">(</span><span class="s">"SELECT 1"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="nf">getCredentialValue</span><span class="o">(</span><span class="kd">final</span> <span class="nc">String</span> <span class="n">credentials</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="nc">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="nc">Files</span><span class="o">.</span><span class="na">readAllBytes</span><span class="o">(</span><span class="n">path</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"error getting data source credential value : "</span> <span class="o">+</span> <span class="n">path</span><span class="o">.</span><span class="na">toString</span><span class="o">(),</span> <span class="n">ex</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>No we could build and deploy our service again to repeat our test :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>Step</th>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">01 base</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Java 11</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">517.01</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">918</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">03 jlink</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">518.97</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.41</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">787</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">04 heap</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">451.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.13</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">816</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">633</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">05 parallel GC</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">450.85</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.17</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">799</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">06 connection pool</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">129.0</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">85.49</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">625</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">363</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>connection pool</p>

<p>We have improve our service further since we now reuse our connections.</p>

<p><em>Final Touches</em></p>

<p>Now we will finalize our optimizing we will change some more settings, first we will modify our POM to disable Logback and enable log4j2 that have better performance.</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt">&lt;dependency&gt;</span>
    <span class="nt">&lt;groupId&gt;</span>org.springframework.boot<span class="nt">&lt;/groupId&gt;</span>
    <span class="nt">&lt;artifactId&gt;</span>spring-boot-starter<span class="nt">&lt;/artifactId&gt;</span>
    <span class="nt">&lt;exclusions&gt;</span>
        <span class="nt">&lt;exclusion&gt;</span>
            <span class="nt">&lt;groupId&gt;</span>org.springframework.boot<span class="nt">&lt;/groupId&gt;</span>
            <span class="nt">&lt;artifactId&gt;</span>spring-boot-starter-logging<span class="nt">&lt;/artifactId&gt;</span>
        <span class="nt">&lt;/exclusion&gt;</span>
    <span class="nt">&lt;/exclusions&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
<span class="nt">&lt;dependency&gt;</span>
    <span class="nt">&lt;groupId&gt;</span>org.springframework.boot<span class="nt">&lt;/groupId&gt;</span>
    <span class="nt">&lt;artifactId&gt;</span>spring-boot-starter-log4j2<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;/dependency&gt;</span></code></pre></figure>

<p>We will set log to error disable the spring banner an disable JMX adding to our application.yaml :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">logging</span><span class="pi">:</span>
  <span class="na">level</span><span class="pi">:</span>
    <span class="na">ROOT</span><span class="pi">:</span> <span class="s">ERROR</span>
<span class="na">spring</span><span class="pi">:</span>
  <span class="na">main</span><span class="pi">:</span>
    <span class="na">banner-mode</span><span class="pi">:</span> <span class="s">off</span>
  <span class="na">jmx</span><span class="pi">:</span>
    <span class="na">enabled</span><span class="pi">:</span> <span class="kc">false</span></code></pre></figure>

<p>Now let’s run a new run with our final changes :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>Step</th>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">01 base</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Java 11</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">517.01</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">918</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">03 jlink</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">518.97</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">18.41</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">875</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">787</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">04 heap</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">451.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.13</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">816</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">633</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">05 parallel GC</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">450.85</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">21.17</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">799</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">06 connection pool</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">129.0</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">85.49</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">625</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">363</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-left">07 final</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">111.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">85.53</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">350</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>final</p>

<p><strong>Running the complete set</strong></p>

<p>With this we are ready to run our final test running the full set, let’s do it so :</p>

<table class="display data-table compact cell-border">
    <thead>
      <tr>
        
        <th>C. Users</th>
        
        <th>Pods</th>
        
        <th>ART</th>
        
        <th>TPS</th>
        
        <th>Max CPU</th>
        
        <th>Max MEM</th>
        
        <th>Service</th>
        
      </tr>
    </thead>
    <tbody> 
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">39.16</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">25.49</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">403</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">474</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">34.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">28.85</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">209</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">252</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">570.47</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">16.73</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">916</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">487</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">10</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">111.61</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">85.53</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">626</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">350</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1615.82</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.72</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1049</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1454</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">25</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">266.03</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">89.44</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">686</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">369</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">5</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3190.98</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">14.89</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1351</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">2499</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">01 Base</td>
            
        
                
      </tr>
      
      <tr>
        

                        
            
            
                   

            
            <td class="dt-body-right">50</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">3</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1982.03</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">23.95</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">774</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-right">1060</td>
            
        
        

                        
            
            
                   

            
            <td class="dt-body-left">02 Spring Web</td>
            
        
                
      </tr>
      
    </tbody>
  </table>
<p>01 Base = Java 8 - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC default settings, unoptimized <br />02 Spring Web = Java 11 - Spring Boot 2.2.2 - Spring Web - Spring Data JDBC optimized</p>

<p><strong>Conclusions</strong></p>

<p>With this optimization we have drastically improve our service, it use less CPU and Memory and we need to have less pods running for been able to support the same amount of concurrent users with an improve response time.</p>

<p>In the next chapters of this series we will start to use other frameworks to measure them against our results.</p>

<p><em>Note: The full code of this service is available at this <a href="https://github.com/LearningByExample/movies-spring-web" target="_blank">repository</a>.</em></p>

<p><strong>Resources</strong></p>

<ul>
  <li><a href="https://spring.io/guides/topicals/spring-boot-docker" target="_blank">https://spring.io/guides/topicals/spring-boot-docker</a></li>
  <li><a href="https://docs.gigaspaces.com/xap/14.0/rn/java11-guidelines.html">https://docs.gigaspaces.com/xap/14.0/rn/java11-guidelines.html</a></li>
  <li><a href="https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/" target="_blank">https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/</a></li>
  <li><a href="https://spring.io/blog/2015/12/10/spring-boot-memory-performance">https://spring.io/blog/2015/12/10/spring-boot-memory-performance</a></li>
  <li><a href="https://stackoverflow.com/a/30070428" target="_blank">https://stackoverflow.com/a/30070428</a></li>
  <li><a href="https://www.optaplanner.org/blog/2015/07/31/WhatIsTheFastestGarbageCollectorInJava8.html" target="_blank">https://www.optaplanner.org/blog/2015/07/31/WhatIsTheFastestGarbageCollectorInJava8.html</a></li>
  <li><a href="https://spring.io/blog/2018/12/12/how-fast-is-spring" target="_blank">https://spring.io/blog/2018/12/12/how-fast-is-spring</a></li>
</ul>]]></content><author><name>Juan Medina</name></author><category term="Cloud" /><category term="Programming" /><summary type="html"><![CDATA[Some days ago we start this series of article with our base example, now we have baseline that we could use to optimize the service.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/graph.jpg" /><media:content medium="image" url="http://juan-medina.com/assets/img/graph.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Optimizing Kubernetes Services - Part 1 &amp;amp;#58 Base service</title><link href="http://juan-medina.com/2020/01/11/optimizing-k8s-sv-01/" rel="alternate" type="text/html" title="Optimizing Kubernetes Services - Part 1 &amp;amp;#58 Base service" /><published>2020-01-11T23:00:00+00:00</published><updated>2020-01-11T23:00:00+00:00</updated><id>http://juan-medina.com/2020/01/11/optimizing-k8s-sv-01</id><content type="html" xml:base="http://juan-medina.com/2020/01/11/optimizing-k8s-sv-01/"><![CDATA[<p>Much have been said about the importance of optimizing services that are going to run in a cloud, and they are plenty of resources about the topic, from start up times, memory consumption or resource usage and dozen of data points of your applications.</p>

<p>But even with this amount of information it get exponentially complex to understand why we are doing this?, and what actually means?. So I decided that is going to be simpler to just <a href="https://github.com/LearningByExample" target="_blank">learn by example</a>, so this is the first part of a series of articles that will help on this matter.</p>

<p>We are going to create a series of services that we will deploy in our kubernetes (k8s) cluster, and we will learn how we could measure them. Them we will optimizing our service to perform better, so we could measure if what we have done improves using data we could use to understand why.</p>

<p>In this first part on the series we will create our base service that will be use as a baseline for comparing the improvements that will be doing down the line, for this our service will be developed with default values, including configuration, forgetting things that we usually do for optimizing an application in order to understand what those optimizations actually do.</p>

<p>We will use the Microk8s cluster that <a href="/2019/12/02/microk8s/" target="_blank">we recently setup</a> and the movies database that have been loaded with the <a href="/2019/12/16/jobs-k8s/" target="_blank">previous k8s job</a>.</p>

<p><strong>Bootstrapping a Spring Boot application</strong></p>

<p>We are going to use Spring Initializr <a href="https://start.spring.io/" target="_blank">https://start.spring.io/</a> to quickly bootstrap our application.</p>

<p>We will create a <em>Maven Project</em> using <em>Java 8</em> and <em>Spring Boot</em> version <em>2.2.2</em>, we will set the <em>Group</em> to be <em>org.learning.by.example.movies</em>, and the <em>Artifact</em> to <em>base-service</em>, the rest of the values should be automatically populated.</p>

<p><a href="/assets/img/captures/base_movies_services_01.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_01.jpg" alt="Spring Initializr setup" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>For dependencies we will search and add :</p>
<ul>
  <li>Spring Web</li>
  <li>Spring Boot Actuator</li>
  <li>Spring Data JDBC</li>
  <li>PostgreSQL Driver</li>
</ul>

<p><a href="/assets/img/captures/base_movies_services_02.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_02.jpg" alt="Spring Initializr dependencies" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now we will click on Generate to download our zip file : <em>base-service.zip</em>, we will uncompress it and leave it ready to be opened with our favorite IDE.</p>

<p><strong>Designing the Service</strong></p>

<p>Before starting to code our application we will design what our application will do, so first lest just look at our movies table loaded in our database, for
this we will do as been doing before using just the PSQL client to explore the data.</p>

<p>Lest explore the data using PSQL, we will login in our server with our <em>moviesuser</em> user so we need to get it password, we could get it with:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get secret moviesuser.movies-db-cluster.credentials <span class="nt">-o</span> <span class="s1">'jsonpath={.data.password}'</span> | <span class="nb">base64</span> <span class="nt">-d</span> 
9oYUFcamSKwjB5Yrg099glLHdqg8C1IkScRfd5TeHTisiuj23FQrx3YEW6fB3ctJ</code></pre></figure>

<p>We will forward the PostgreSQL port 5432 on our master to our localhost port 6432, this will run until we do <em>ctrl+c</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl port-forward movies-db-cluster-0 6432:5432                        
Forwarding from 127.0.0.1:6432 -&gt; 5432
Forwarding from <span class="o">[</span>::1]:6432 -&gt; 5432</code></pre></figure>

<p>Finally we can connect to our database with with the provide user and password using PSQL in another
shell :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>psql <span class="nt">-h</span> localhost <span class="nt">-p</span> 6432 <span class="nt">-U</span> moviesuser movies
Password <span class="k">for </span>user moviesuser: 
psql <span class="o">(</span>11.6 <span class="o">(</span>Ubuntu 11.6-1.pgdg18.04+1<span class="o">)</span>, server 11.5 <span class="o">(</span>Ubuntu 11.5-1.pgdg18.04+1<span class="o">))</span>
SSL connection <span class="o">(</span>protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off<span class="o">)</span>
Type <span class="s2">"help"</span> <span class="k">for </span>help.

<span class="nv">movies</span><span class="o">=</span><span class="c"># SELECT * FROM movies LIMIT 10;</span>
 <span class="nb">id</span> |               title                |                   genres                    
<span class="nt">----</span>+------------------------------------+---------------------------------------------
  1 | Toy Story <span class="o">(</span>1995<span class="o">)</span>                   | Adventure|Animation|Children|Comedy|Fantasy
  2 | Jumanji <span class="o">(</span>1995<span class="o">)</span>                     | Adventure|Children|Fantasy
  3 | Grumpier Old Men <span class="o">(</span>1995<span class="o">)</span>            | Comedy|Romance
  4 | Waiting to Exhale <span class="o">(</span>1995<span class="o">)</span>           | Comedy|Drama|Romance
  5 | Father of the Bride Part II <span class="o">(</span>1995<span class="o">)</span> | Comedy
  6 | Heat <span class="o">(</span>1995<span class="o">)</span>                        | Action|Crime|Thriller
  7 | Sabrina <span class="o">(</span>1995<span class="o">)</span>                     | Comedy|Romance
  8 | Tom and Huck <span class="o">(</span>1995<span class="o">)</span>                | Adventure|Children
  9 | Sudden Death <span class="o">(</span>1995<span class="o">)</span>                | Action
 10 | GoldenEye <span class="o">(</span>1995<span class="o">)</span>                   | Action|Adventure|Thriller
<span class="o">(</span>10 rows<span class="o">)</span>

<span class="nv">movies</span><span class="o">=</span><span class="c"># \q</span></code></pre></figure>

<p>As we can see we have a in our <em>movies</em> table a column id, as the key of each movie, a column name <em>title</em> that sometimes contains a year and a list of <em>genres</em>, separate with the the character |.</p>

<p>We will do a REST API that returns all the movies from a given genre, but since they are just in a column we need to prepare an special query for it.</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="k">SELECT</span>
    <span class="n">id</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">genres</span>
<span class="k">FROM</span>
    <span class="n">movies</span>
<span class="k">WHERE</span> 
    <span class="s1">'sci-fi'</span> <span class="o">=</span> <span class="k">ANY</span><span class="p">(</span><span class="n">string_to_array</span><span class="p">(</span><span class="k">LOWER</span><span class="p">(</span><span class="n">genres</span><span class="p">),</span><span class="s1">'|'</span><span class="p">))</span></code></pre></figure>

<p>We will use the PostgreSQL function <a href="https://www.postgresql.org/docs/current/functions-array.html" target="_blank">string_to_array</a> and the <a href="https://www.postgresql.org/docs/9.2/functions-subquery.html#FUNCTIONS-SUBQUERY-ANY-SOME" target="_blank">ANY</a> operator for getting all the movies from a given genre, converted to lowercase using the string function <a href="https://www.postgresql.org/docs/9.1/functions-string.html" target="_blank">lower</a>.</p>

<p>This could return thousands of row, for example for sci-fi will be around 3.5k but that is what we like to do in our service.</p>

<p>Let’s check if with just 10 rows in PSQL</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>psql <span class="nt">-h</span> localhost <span class="nt">-p</span> 6432 <span class="nt">-U</span> moviesuser movies
Password <span class="k">for </span>user moviesuser: 
psql <span class="o">(</span>11.6 <span class="o">(</span>Ubuntu 11.6-1.pgdg18.04+1<span class="o">)</span>, server 11.5 <span class="o">(</span>Ubuntu 11.5-1.pgdg18.04+1<span class="o">))</span>
SSL connection <span class="o">(</span>protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off<span class="o">)</span>
Type <span class="s2">"help"</span> <span class="k">for </span>help.

<span class="nv">movies</span><span class="o">=&gt;</span> SELECT
movies-&gt;     <span class="nb">id</span>, title, genres
movies-&gt; FROM
movies-&gt;     movies
movies-&gt; WHERE 
movies-&gt;     <span class="s1">'sci-fi'</span> <span class="o">=</span> ANY<span class="o">(</span>string_to_array<span class="o">(</span>LOWER<span class="o">(</span>genres<span class="o">)</span>,<span class="s1">'|'</span><span class="o">))</span>
movies-&gt; LIMIT 10<span class="p">;</span>
 <span class="nb">id</span>  |                  title                   |                 genres                 
<span class="nt">-----</span>+------------------------------------------+------------------------------------------
  24 | Powder <span class="o">(</span>1995<span class="o">)</span>                            | Drama|Sci-Fi
  32 | Twelve Monkeys <span class="o">(</span>a.k.a. 12 Monkeys<span class="o">)</span> <span class="o">(</span>1995<span class="o">)</span>| Mystery|Sci-Fi|Thriller
  66 | Lawnmower Man 2: Beyond Cyberspace <span class="o">(</span>1996<span class="o">)</span>| Action|Sci-Fi|Thriller
  76 | Screamers <span class="o">(</span>1995<span class="o">)</span>                         | Action|Sci-Fi|Thriller
 103 | Unforgettable <span class="o">(</span>1996<span class="o">)</span>                     | Mystery|Sci-Fi|Thriller
 160 | Congo <span class="o">(</span>1995<span class="o">)</span>                             | Action|Adventure|Mystery|Sci-Fi
 172 | Johnny Mnemonic <span class="o">(</span>1995<span class="o">)</span>                   | Action|Sci-Fi|Thriller
 173 | Judge Dredd <span class="o">(</span>1995<span class="o">)</span>                       | Action|Crime|Sci-Fi
 196 | Species <span class="o">(</span>1995<span class="o">)</span>                           | Horror|Sci-Fi
 198 | Strange Days <span class="o">(</span>1995<span class="o">)</span>                      | Action|Crime|Drama|Mystery|Sci-Fi|Thriller
<span class="o">(</span>10 rows<span class="o">)</span>

<span class="nv">movies</span><span class="o">=&gt;</span> <span class="se">\q</span></code></pre></figure>

<p>The query seems to work, now lets think on our API, we will create an endpoint in the url <em>/movies/genre</em> that will return all movies under that category as an array of JSON object, but this can not be directly the row in our database it should a bit better, we will design a movie JSON like this :</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="p">,</span><span class="w">
    </span><span class="nl">"year"</span><span class="p">:</span><span class="w"> </span><span class="mi">1995</span><span class="p">,</span><span class="w">
    </span><span class="nl">"genres"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"Drama"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"Sci-Fi"</span><span class="w">
    </span><span class="p">],</span><span class="w">        
    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Powder"</span><span class="p">,</span><span class="w">        
</span><span class="p">}</span></code></pre></figure>

<p><strong>Implementing our Service</strong></p>

<p>Back to our Service that was generated before we will open it in our favorite IDE and start adding the code require for our service to work.</p>

<p>First we will create a Plain Old Java Object, POJO, class that represents our Movie JSON Object, we will place it in <em>src/main/java/org/learning/by/example/movies/baseservice/model/Movie.java</em> :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.model</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Movie</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Integer</span> <span class="n">id</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">title</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Integer</span> <span class="n">year</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">genres</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">Movie</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Integer</span> <span class="n">id</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">title</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">Integer</span> <span class="n">year</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">genres</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">title</span> <span class="o">=</span> <span class="n">title</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">year</span> <span class="o">=</span> <span class="n">year</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">genres</span> <span class="o">=</span> <span class="n">genres</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getTitle</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">title</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getYear</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">year</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="nf">getGenres</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">genres</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>This class when deserialize will be just as our designed JSON, now we need we will create our repository that will query database on <em>src/main/java/org/learning/by/example/movies/baseservice/repositories/MoviesRepository.java</em> :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.repositories</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.jdbc.repository.query.Query</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.repository.CrudRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Repository</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="nd">@Repository</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">MoviesRepository</span> <span class="kd">extends</span> <span class="nc">CrudRepository</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="nd">@Query</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"SELECT \n"</span> <span class="o">+</span>
            <span class="s">"  m.id, m.title, m.genres \n"</span> <span class="o">+</span>
            <span class="s">" FROM \n"</span> <span class="o">+</span>
            <span class="s">"  movies as m \n"</span> <span class="o">+</span>
            <span class="s">" WHERE :genre = ANY(string_to_array(LOWER(m.genres),'|')) \n"</span><span class="o">)</span>
    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">&gt;</span> <span class="nf">findByGenre</span><span class="o">(</span><span class="nc">String</span> <span class="n">genre</span><span class="o">);</span>
<span class="o">}</span></code></pre></figure>

<p>In this simple repository we will extend from <em>CrudRepository</em> to return a Movie List giving a genre, we will use the query that we design before.</p>

<p>But the output of this query does not mach our <em>Movie Classs</em> so we need to create a mapper that will convert a row from our database, we will place on     src/main/java/org/learning/by/example/movies/baseservice/mapper/MovieMapper.java :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.mapper</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.jdbc.core.RowMapper</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.sql.ResultSet</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.sql.SQLException</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.regex.Matcher</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.regex.Pattern</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.stream.Collectors</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MovieMapper</span> <span class="kd">implements</span> <span class="nc">RowMapper</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="c1">// Without escape characters this pattern ins : (.*) \((\d{4})\)</span>
    <span class="c1">//  (       = START GROUP 1</span>
    <span class="c1">//  .*      = any set of characters</span>
    <span class="c1">//  )       = END GROUP 1</span>
    <span class="c1">//          = space</span>
    <span class="c1">//  \(      = the character (</span>
    <span class="c1">//  (       = START GROUP 2</span>
    <span class="c1">//  d{4}    = four digits, ex 1945</span>
    <span class="c1">//  )       = END GROUP 2</span>
    <span class="c1">//  \)      = the character ) </span>
    <span class="kd">final</span> <span class="kd">static</span> <span class="nc">Pattern</span> <span class="no">TITLE_YEAR_PATTERN</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"(.*) \\((\\d{4})\\)"</span><span class="o">);</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="nc">Movie</span> <span class="nf">mapRow</span><span class="o">(</span><span class="kd">final</span> <span class="nc">ResultSet</span> <span class="n">resultSet</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">i</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">SQLException</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">String</span> <span class="n">rowTile</span> <span class="o">=</span> <span class="n">resultSet</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">"title"</span><span class="o">);</span>

        <span class="kd">final</span> <span class="nc">String</span> <span class="n">realTitle</span><span class="o">;</span>
        <span class="kd">final</span> <span class="kt">int</span> <span class="n">year</span><span class="o">;</span>

        <span class="kd">final</span> <span class="nc">Matcher</span> <span class="n">matcher</span> <span class="o">=</span> <span class="no">TITLE_YEAR_PATTERN</span><span class="o">.</span><span class="na">matcher</span><span class="o">(</span><span class="n">rowTile</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">matcher</span><span class="o">.</span><span class="na">find</span><span class="o">())</span> <span class="o">{</span>
            <span class="n">realTitle</span> <span class="o">=</span> <span class="n">matcher</span><span class="o">.</span><span class="na">group</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
            <span class="n">year</span> <span class="o">=</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">matcher</span><span class="o">.</span><span class="na">group</span><span class="o">(</span><span class="mi">2</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">realTitle</span> <span class="o">=</span> <span class="n">rowTile</span><span class="o">;</span>
            <span class="n">year</span> <span class="o">=</span> <span class="mi">1900</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">final</span> <span class="nc">String</span><span class="o">[]</span> <span class="n">genres</span> <span class="o">=</span> <span class="n">resultSet</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">"genres"</span><span class="o">).</span><span class="na">split</span><span class="o">(</span><span class="s">"\\|"</span><span class="o">);</span>
        <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">genresList</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">genres</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span>
                <span class="n">genre</span> <span class="o">-&gt;</span> <span class="o">!</span><span class="n">genre</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">()</span>
        <span class="o">).</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>

        <span class="kd">final</span> <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">resultSet</span><span class="o">.</span><span class="na">getInt</span><span class="o">(</span><span class="s">"id"</span><span class="o">);</span>

        <span class="k">return</span> <span class="k">new</span> <span class="nf">Movie</span><span class="o">(</span><span class="n">id</span><span class="o">,</span> <span class="n">realTitle</span><span class="o">,</span> <span class="n">year</span><span class="o">,</span> <span class="n">genresList</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>This simple <em>RowMapper</em> will first use a regular expression to get our movie title and year, if it is available, parse the genres and produce a Movie object, however is not enough to have a mapper we need to tell Spring Data JDBC to use it when query for our Movies so we will set this in a new configuration class on src/main/java/org/learning/by/example/movies/baseservice/repositories/MappingConfiguration.java :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.repositories</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.mapper.MovieMapper</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration</span><span class="o">;</span>

<span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MappingConfiguration</span> <span class="kd">extends</span> <span class="nc">DefaultQueryMappingConfiguration</span> <span class="o">{</span>
    <span class="nc">MappingConfiguration</span><span class="o">(</span><span class="kd">final</span> <span class="nc">MovieMapper</span> <span class="n">movieMapper</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">();</span>
        <span class="n">registerRowMapper</span><span class="o">(</span><span class="nc">Movie</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">movieMapper</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>In this <em>MappingConfiguration</em> we are registering our MovieMapper for mapping <em>Movies</em> objects, note that we use dependency constructor injection for obtain the
bean for our <em>MovieMapper</em>.</p>

<p>We will create now a service that will provide movies to who ever request them, to been able to change the implementation of our repository if needed and we will place it on <em>src/main/java/org/learning/by/example/movies/baseservice/service/MovieService.java</em> :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.service</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.repositories.MoviesRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Service</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="nd">@Service</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MovieService</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">MoviesRepository</span> <span class="n">repository</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">MovieService</span><span class="o">(</span><span class="kd">final</span> <span class="nc">MoviesRepository</span> <span class="n">repository</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">repository</span> <span class="o">=</span> <span class="n">repository</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">&gt;</span> <span class="nf">getMoviesByGenre</span><span class="o">(</span><span class="kd">final</span> <span class="nc">String</span> <span class="n">genre</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">repository</span><span class="o">.</span><span class="na">findByGenre</span><span class="o">(</span><span class="n">genre</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>The <em>MovieService class</em> will as well convert to lowercase the genre, since our repository will be perform the query on lowercase genres.</p>

<p>Now we will create our api using a <em>RestController</em> and we will place it on src/main/java/org/learning/by/example/movies/baseservice/controller/MoviesController.java :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.controller</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.model.Movie</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.learning.by.example.movies.baseservice.service.MovieService</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.GetMapping</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.PathVariable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="nd">@RestController</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MoviesController</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">MovieService</span> <span class="n">movieService</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">MoviesController</span><span class="o">(</span><span class="kd">final</span> <span class="nc">MovieService</span> <span class="n">movieService</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">movieService</span> <span class="o">=</span> <span class="n">movieService</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/movies/{genre}"</span><span class="o">)</span>
    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Movie</span><span class="o">&gt;</span> <span class="nf">getMovies</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">String</span> <span class="n">genre</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">movieService</span><span class="o">.</span><span class="na">getMoviesByGenre</span><span class="o">(</span><span class="n">genre</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>Our <em>MovieController</em> will answer HTTP GET request on /movies/{genre} and return the movies using the <em>MoviesService</em> that will use our <em>MoviesRepository</em>, however we need to stablish the connection to our database but first let thinks how this will run on our k8s cluster.</p>

<p>When the service is running we need to connect to our database we could use the environment variables provide by kubernetes to connect to find the IP address and port, as we did in the <a href="/2019/12/16/jobs-k8s/" target="_blank">last example</a>, this are <em>MOVIES_DB_CLUSTER_SERVICE_HOST</em> and <em>MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL</em>, but we need as well the credentials that we could inject in our kubernetes deployment, as we did before, in a directory containing a username and password. Finally we need to tell spring witch database driver to use, and the JDBC connection string, for this let’s add to our src/main/resources/application.yml some entries :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">movies-datasource</span><span class="pi">:</span>
  <span class="na">driver</span><span class="pi">:</span> <span class="s2">"</span><span class="s">org.postgresql.Driver"</span>
  <span class="na">connection-string</span><span class="pi">:</span> <span class="s2">"</span><span class="s">jdbc:postgresql://</span><span class="se">\
</span>    <span class="s">${MOVIES_DB_CLUSTER_SERVICE_HOST}:${MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL}</span><span class="se">\
</span>    <span class="s">/movies"</span>
  <span class="na">credentials</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span></code></pre></figure>

<p>Let’s get those values using a <em>ConfigurationProperties</em> that will be on src/main/java/org/learning/by/example/movies/baseservice/datasource/DataSourceProperties.java :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.datasource</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.springframework.boot.context.properties.ConfigurationProperties</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="nd">@ConfigurationProperties</span><span class="o">(</span><span class="s">"movies-datasource"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">DataSourceProperties</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getDriver</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">driver</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setDriver</span><span class="o">(</span><span class="nc">String</span> <span class="n">driver</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">driver</span> <span class="o">=</span> <span class="n">driver</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getConnectionString</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">connectionString</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setConnectionString</span><span class="o">(</span><span class="nc">String</span> <span class="n">connectionString</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">connectionString</span> <span class="o">=</span> <span class="n">connectionString</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getCredentials</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCredentials</span><span class="o">(</span><span class="nc">String</span> <span class="n">credentials</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">credentials</span> <span class="o">=</span> <span class="n">credentials</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="n">driver</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">connectionString</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">credentials</span><span class="o">;</span>
<span class="o">}</span></code></pre></figure>

<p>Now that we have our <em>DataSourceProperties</em> we could create a <em>DataSource</em> for our connection on src/main/java/org/learning/by/example/movies/baseservice/datasource/MoviesDataSource.java :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice.datasource</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.apache.commons.logging.Log</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.logging.LogFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.jdbc.datasource.DriverManagerDataSource</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.nio.file.Files</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Path</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.nio.file.Paths</span><span class="o">;</span>

<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MoviesDataSource</span> <span class="kd">extends</span> <span class="nc">DriverManagerDataSource</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Log</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LogFactory</span><span class="o">.</span><span class="na">getLog</span><span class="o">(</span><span class="nc">MoviesDataSource</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>

    <span class="nc">MoviesDataSource</span><span class="o">(</span><span class="kd">final</span> <span class="nc">DataSourceProperties</span> <span class="n">dataSourceProperties</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setDriverClassName</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getDriver</span><span class="o">());</span>
        <span class="n">setUrl</span><span class="o">(</span><span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getConnectionString</span><span class="o">());</span>
        <span class="kd">final</span> <span class="nc">String</span> <span class="n">credentials</span> <span class="o">=</span> <span class="n">dataSourceProperties</span><span class="o">.</span><span class="na">getCredentials</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setUsername</span><span class="o">(</span><span class="n">getCredentialValue</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="s">"username"</span><span class="o">));</span>
        <span class="k">this</span><span class="o">.</span><span class="na">setPassword</span><span class="o">(</span><span class="n">getCredentialValue</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="s">"password"</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="nf">getCredentialValue</span><span class="o">(</span><span class="kd">final</span> <span class="nc">String</span> <span class="n">credentials</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="nc">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">credentials</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="nc">Files</span><span class="o">.</span><span class="na">readAllBytes</span><span class="o">(</span><span class="n">path</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"error getting data source credential value : "</span> <span class="o">+</span> <span class="n">path</span><span class="o">.</span><span class="na">toString</span><span class="o">(),</span> <span class="n">ex</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>We are creating a DriverManagerDataSource with the settings in our application.yml and reading the username and password from our credentials directory.</p>

<p>Finally we will modify our application on src/main/java/org/learning/by/example/movies/baseservice/BaseServiceApplication.java :</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">package</span> <span class="nn">org.learning.by.example.movies.baseservice</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.springframework.boot.SpringApplication</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.boot.autoconfigure.SpringBootApplication</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.jdbc.repository.config.EnableJdbcRepositories</span><span class="o">;</span>

<span class="nd">@SpringBootApplication</span>
<span class="nd">@EnableJdbcRepositories</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">BaseServiceApplication</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">SpringApplication</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="nc">BaseServiceApplication</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>We just enable JDBC repositories in our application.</p>

<p><em>Note: For make this article brief we haven’t include the unit and integrations tests created for this code, but they are available on the <a href="https://github.com/LearningByExample/movies-base-service" target="_blank">repository</a> for this article.</em></p>

<p><strong>Running our Service in Local</strong></p>

<p>Before start working on deploying the service into our k8s cluster we will run it locally, but since our configuration use a couple of environments variables and some credentials files for this.</p>

<p>First let’s get our user password with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get secret moviesuser.movies-db-cluster.credentials <span class="nt">-o</span> <span class="s1">'jsonpath={.data.password}'</span> | <span class="nb">base64</span> <span class="nt">-d</span> 
9oYUFcamSKwjB5Yrg099glLHdqg8C1IkScRfd5TeHTisiuj23FQrx3YEW6fB3ctJ</code></pre></figure>

<p>Now will we save it to a file named <em>/etc/movies-db/password</em>, we will create as well a file name <em>/etc/movies-db/username</em> that just contains our user : <em>moviesuser</em>.</p>

<p>We will build our microservice with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./mvnw clean package</code></pre></figure>

<p>We will forward the PostgreSQL port 5432 on our master to our localhost port 6432, this will run until we do <em>ctrl+c</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl port-forward movies-db-cluster-0 6432:5432
Forwarding from 127.0.0.1:6432 -&gt; 5432
Forwarding from <span class="o">[</span>::1]:6432 -&gt; 5432</code></pre></figure>

<p>In a new shell window we could run out service, this will run until we do <em>ctrl+c</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">export </span><span class="nv">MOVIES_DB_CLUSTER_SERVICE_HOST</span><span class="o">=</span><span class="s2">"localhost"</span>
<span class="nv">$ </span><span class="nb">export </span><span class="nv">MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL</span><span class="o">=</span><span class="s2">"6432"</span>
<span class="nv">$ </span>java <span class="nt">-jar</span> target/base-service-0.0.1-SNAPSHOT.jar</code></pre></figure>

<p>Now we could test our service using any HTPP client, such wget, curl, etc., I going to use <a href="https://httpie.org/" target="_blank">HTTPie</a> instead :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>http :8080/movies/sci-fi       
HTTP/1.1 200 
Connection: keep-alive
Content-Type: application/json
Date: Wed, 01 Jan 2020 12:13:46 GMT
Keep-Alive: <span class="nb">timeout</span><span class="o">=</span>60
Transfer-Encoding: chunked

<span class="o">[</span>
    <span class="o">{</span>
        <span class="s2">"genres"</span>: <span class="o">[</span>
            <span class="s2">"Drama"</span>,
            <span class="s2">"Sci-Fi"</span>
        <span class="o">]</span>,
        <span class="s2">"id"</span>: 24,
        <span class="s2">"title"</span>: <span class="s2">"Powder"</span>,
        <span class="s2">"year"</span>: 1995
    <span class="o">}</span>,
    <span class="o">{</span>
        <span class="s2">"genres"</span>: <span class="o">[</span>
            <span class="s2">"Adventure"</span>,
            <span class="s2">"Drama"</span>,
            <span class="s2">"Fantasy"</span>,
            <span class="s2">"Mystery"</span>,
            <span class="s2">"Sci-Fi"</span>
        <span class="o">]</span>,
        <span class="s2">"id"</span>: 29,
        <span class="s2">"title"</span>: <span class="s2">"City of Lost Children, The (Cité des enfants perdus, La)"</span>,
        <span class="s2">"year"</span>: 1995
    <span class="o">}</span>,
........
........
........
<span class="o">]</span></code></pre></figure>

<p>This will output thousands of records, these where just some of them.</p>

<p>But since we have as well include <em>Spring Actuator</em> in our dependencies we have two more URLs in our service :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>http :8080/actuator/info
HTTP/1.1 200 
Connection: keep-alive
Content-Type: application/vnd.spring-boot.actuator.v3+json
Date: Thu, 02 Jan 2020 16:34:58 GMT
Keep-Alive: <span class="nb">timeout</span><span class="o">=</span>60
Transfer-Encoding: chunked

<span class="o">{}</span>

<span class="nv">$ </span>http :8080/actuator/health
HTTP/1.1 200 
Connection: keep-alive
Content-Type: application/vnd.spring-boot.actuator.v3+json
Date: Thu, 02 Jan 2020 16:35:09 GMT
Keep-Alive: <span class="nb">timeout</span><span class="o">=</span>60
Transfer-Encoding: chunked

<span class="o">{</span>
    <span class="s2">"status"</span>: <span class="s2">"UP"</span>
<span class="o">}</span></code></pre></figure>

<p><strong>Building our Docker Image</strong></p>

<p>Now that we have test that our application runs correctly let’s create a docker image and push it to our local repository that we have in our cluster, first we will create a Dockerfile :</p>

<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="s"> openjdk:8-jdk</span>

<span class="k">COPY</span><span class="s"> target/*.jar /usr/app/app.jar</span>
<span class="k">WORKDIR</span><span class="s"> /usr/app</span>

<span class="k">CMD</span><span class="s"> ["java", "-jar", "app.jar"]</span></code></pre></figure>

<p>Now we will create a small script that build our docker and publish it to the registry, we will name it <em>build.sh</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nb">echo</span> <span class="s2">"doing maven build"</span>
./mvnw clean package
<span class="nb">echo</span> <span class="s2">"maven build done"</span>

<span class="nb">echo</span> <span class="s2">"building docker"</span>
docker build <span class="nb">.</span> <span class="nt">-t</span> localhost:32000/movies-base-service:0.0.1
<span class="nb">echo</span> <span class="s2">"docker builded"</span>

<span class="nb">echo</span> <span class="s2">"publishing docker"</span>
docker push localhost:32000/movies-base-service
<span class="nb">echo</span> <span class="s2">"docker published"</span></code></pre></figure>

<p>Running this script we will build our docker image and push to our local docker registry, but for deploying the image into our cluster we will create a deployment descriptor name <em>deployment.yml</em> :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-base-service</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-base-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">movies-base-service</span>
  <span class="na">strategy</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">movies-base-service</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movies-base-service:0.0.1</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">movies-base-service</span>
          <span class="na">resources</span><span class="pi">:</span> <span class="pi">{}</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/health</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/actuator/info</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/movies-db"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp"</span>
              <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesuser.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">tmp</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">status</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">creationTimestamp</span><span class="pi">:</span> <span class="kc">null</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-base-service</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-base-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">8080-8080</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">movies-base-service</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
<span class="na">status</span><span class="pi">:</span>
  <span class="na">loadBalancer</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>In this descriptor we are first deploying our service, we declare our liveness and readiness probes to use our actuator endpoint, we inject our credentials and we mount a temporary directory for our service to use. Them we create a k8s service with a load balancer to been able to call our service and balance between the pods that is available.</p>

<p>Finally we will create another script to deploy our service deploy.sh :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nv">KUBECMD</span><span class="o">=</span><span class="s2">"kubectl"</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-x</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">command</span> <span class="nt">-v</span> microk8s.kubectl<span class="si">)</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nv">KUBECMD</span><span class="o">=</span><span class="s2">"microk8s.kubectl"</span>
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"deleting previous versions"</span>
<span class="nv">$KUBECMD</span> delete all <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>movies-base-service
<span class="nb">echo</span> <span class="s2">"previous version deleted"</span>

<span class="nb">echo</span> <span class="s2">"create deployment"</span>
<span class="nv">$KUBECMD</span> create <span class="nt">-f</span> deployment.yml
<span class="nb">echo</span> <span class="s2">"deployment created"</span></code></pre></figure>

<p>This script is a bit different we have create in other tu use the standard kubectl or microk8.kubectl if its available, it will delete the deployment and them deploy our service.</p>

<p>We can now build and deploy our service with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh
<span class="nv">$ </span>./deploy.sh</code></pre></figure>

<p>Now we could check what we have in our cluster for our service with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get all <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>movies-base-service
NAME                                       READY   STATUS    RESTARTS   AGE
pod/movies-base-service-84cf9b8746-pj4xn   1/1     Running   0          4m40s

NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>    AGE
service/movies-base-service   ClusterIP   10.152.183.244   &lt;none&gt;        8080/TCP   4m40s

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/movies-base-service   1/1     1            1           4m40s

NAME                                             DESIRED   CURRENT   READY   AGE
replicaset.apps/movies-base-service-84cf9b8746   1         1         1       4m40s</code></pre></figure>

<p><strong>Creating a load test</strong></p>

<p>We have our service but in other to understand how it perform we will create an load test, we will start download <a href="https://jmeter.apache.org/" target="_blank">Apache JMeter</a> visiting the download page : <a href="https://jmeter.apache.org/download_jmeter.cgi" target="_blank">https://jmeter.apache.org/download_jmeter.cgi</a></p>

<p>Now let’s unzip it and set in path that we could use :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo mkdir</span> /opt/jmeter
<span class="nv">$ </span><span class="nb">cd</span> /opt/jmeter
<span class="nv">$ </span><span class="nb">sudo </span>unzip ~/Downloads/apache-jmeter<span class="k">*</span>.zip
<span class="nv">$ </span><span class="nb">sudo ln</span> <span class="nt">-s</span> apache-jmeter-5.2.1 default
<span class="nv">$ </span><span class="nb">sudo ln</span> <span class="nt">-s</span> /opt/jmeter/default/bin/jmeter /usr/local/bin/jmeter
<span class="nv">$ </span><span class="nb">sudo chmod</span> <span class="nt">-R</span> ugo+rw /opt/jmeter</code></pre></figure>

<p>With this now we have available jmeter to be use, however if you are using are high dpi screen, such a 4k or a retina display the JMeter UI may be to small to read but you could edit the file <em>/opt/jmeter/default/bin/user.properties</em> and add this:</p>

<figure class="highlight"><pre><code class="language-properties" data-lang="properties"><span class="py">jmeter.hidpi.mode</span><span class="p">=</span><span class="s">true</span>
<span class="py">jmeter.hidpi.scale.factor</span><span class="p">=</span><span class="s">2.0</span>
<span class="py">jmeter.toolbar.icons.size</span><span class="p">=</span><span class="s">48x48</span>
<span class="py">jmeter.tree.icons.size</span><span class="p">=</span><span class="s">32x32</span>
<span class="py">jsyntaxtextarea.font.family</span><span class="p">=</span><span class="s">Hack</span>
<span class="py">jsyntaxtextarea.font.size</span><span class="p">=</span><span class="s">14</span></code></pre></figure>

<p>Now we could execute jmeter, it will open we a empty test plan, lest just on <em>Name</em> : <em>k8s service load test</em></p>

<p><a href="/assets/img/captures/base_movies_services_03.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_03.jpg" alt="JMeter test plan" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>We will add a new thread group, right click on the tree panel on <em>k8s service load test</em> and select : <em>Add &gt; Thread (Users) -&gt; Thread Group</em>. Them we will set <em>Number of Threads (Users)</em> = <em>${__P(NUM_USERS)}</em>, <em>Ramp-up period (seconds)</em> = <em>${__P(RAMP_UP)}</em>, <em>Loop Count</em> = <em>Infinite</em>, <em>Specify Thread lifetime</em> = <em>Checked</em>, <em>Duration (Seconds)</em> = <em>${__P(DURATION)}</em>.</p>

<p><a href="/assets/img/captures/base_movies_services_04.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_04.jpg" alt="JMeter thread group" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>No we need to add a HTTP request, right click on the tree pane on <em>Thread Group</em> : <em>Add &gt; Sampler &gt; HTTP Request</em>. Them we will set <em>Name</em> = <em>${__P(TEST_URL)}</em>, <em>Server Name or IP</em> = <em>${__P(SERVICE_CLUSTER_IP)}</em>, <em>Port Number</em> = <em>${__P(SERVICE_PORT)}</em>, <em>Method</em> = <em>Get</em>, <em>Path</em> = <em>${__P(TEST_URL)}</em>.</p>

<p><a href="/assets/img/captures/base_movies_services_05.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_05.jpg" alt="JMeter http request" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now we will save our plan on k8s-sv-load-test/load_test.jmx.</p>

<p>For launching our load test we wil create a new script on k8s-sv-load-test/k8s-sv-load-test.sh :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="k">if</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-ne</span> 5 <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Illegal number of parameters, usage : "</span>
  <span class="nb">echo</span> <span class="s2">" "</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="nv">$0</span><span class="s2"> &lt;service&gt; &lt;path&gt; &lt;users&gt; &lt;duration&gt; &lt;ramp up&gt;"</span>
  <span class="nb">echo</span> <span class="s2">" "</span>
  <span class="nb">echo</span> <span class="s2">" example: "</span>
  <span class="nb">echo</span> <span class="s2">"  - </span><span class="nv">$0</span><span class="s2"> k8s-service /get/something 20 300 60"</span>
  <span class="nb">exit </span>2
<span class="k">fi

</span><span class="nv">KUBECMD</span><span class="o">=</span><span class="s2">"kubectl"</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-x</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">command</span> <span class="nt">-v</span> microk8s.kubectl<span class="si">)</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nv">KUBECMD</span><span class="o">=</span><span class="s2">"microk8s.kubectl"</span>
<span class="k">fi

</span><span class="nv">LOAD_DIR</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>
  <span class="nb">cd</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">pwd</span> <span class="nt">-P</span>
<span class="si">)</span><span class="s2">"</span>
<span class="nv">REPORT_DIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$LOAD_DIR</span><span class="s2">/report"</span>

<span class="k">if</span> <span class="o">[</span> <span class="nt">-d</span> <span class="s2">"</span><span class="nv">$REPORT_DIR</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"deleting report directory"</span>
  <span class="nb">rm</span> <span class="nt">-Rf</span> <span class="s2">"</span><span class="nv">$REPORT_DIR</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"report directory deleted"</span>
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"creating report directory"</span>
<span class="nb">mkdir</span> <span class="s2">"</span><span class="nv">$REPORT_DIR</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"report directory created"</span>

<span class="nv">SERVICE</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">TEST_URL</span><span class="o">=</span><span class="nv">$2</span>
<span class="nv">SERVICE_CLUSTER_IP</span><span class="o">=</span><span class="si">$(</span><span class="nv">$KUBECMD</span> get <span class="s2">"service/</span><span class="nv">$SERVICE</span><span class="s2">"</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.clusterIP}'</span><span class="si">)</span>
<span class="nv">SERVICE_PORT</span><span class="o">=</span><span class="si">$(</span><span class="nv">$KUBECMD</span> get <span class="s2">"service/</span><span class="nv">$SERVICE</span><span class="s2">"</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.ports.*.targetPort}'</span><span class="si">)</span>
<span class="nv">NUM_USERS</span><span class="o">=</span><span class="nv">$3</span>
<span class="nv">DURATION</span><span class="o">=</span><span class="nv">$4</span>
<span class="nv">RAMP_UP</span><span class="o">=</span><span class="nv">$5</span>

<span class="nb">echo</span> <span class="s2">"launching a load test on http://</span><span class="nv">$SERVICE_CLUSTER_IP</span><span class="s2">:</span><span class="nv">$SERVICE_PORT$TEST_URL</span><span class="s2"> with </span><span class="nv">$NUM_USERS</span><span class="s2"> "</span><span class="se">\</span>
  <span class="s2">"user(s) during </span><span class="nv">$DURATION</span><span class="s2"> seconds, ramping up during </span><span class="nv">$RAMP_UP</span><span class="s2"> seconds"</span>

<span class="nv">HEAP</span><span class="o">=</span><span class="s2">"-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m"</span>
jmeter <span class="nt">-n</span> <span class="nt">-t</span> <span class="s2">"</span><span class="nv">$LOAD_DIR</span><span class="s2">/load_test.jmx"</span> <span class="nt">-l</span> <span class="s2">"</span><span class="nv">$REPORT_DIR</span><span class="s2">/run.jtl"</span> <span class="nt">-e</span> <span class="nt">-o</span> <span class="s2">"</span><span class="nv">$REPORT_DIR</span><span class="s2">"</span> <span class="nt">-j</span> <span class="s2">"</span><span class="nv">$LOAD_DIR</span><span class="s2">/jmeter.log"</span> <span class="se">\</span>
  <span class="nt">-JSERVICE_CLUSTER_IP</span><span class="o">=</span><span class="s2">"</span><span class="nv">$SERVICE_CLUSTER_IP</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-JSERVICE_PORT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$SERVICE_PORT</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-JNUM_USERS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$NUM_USERS</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-JDURATION</span><span class="o">=</span><span class="s2">"</span><span class="nv">$DURATION</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-JRAMP_UP</span><span class="o">=</span><span class="s2">"</span><span class="nv">$RAMP_UP</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-JTEST_URL</span><span class="o">=</span><span class="s2">"</span><span class="nv">$TEST_URL</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">"load test done"</span>

<span class="nv">WEB_REPORT_PATH</span><span class="o">=</span><span class="s2">"file:///</span><span class="nv">$REPORT_DIR</span><span class="s2">/index.html"</span>
<span class="nb">echo</span> <span class="s2">"report available on </span><span class="nv">$WEB_REPORT_PATH</span><span class="s2">"</span></code></pre></figure>

<p>This is a very generic script that allow to test any k8s that we have in our cluster, for example if we like to launch a load test against our movies service in the actuator/info endpoint during 30s that will ramp up in 1s :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./k8s-sv-load-test/k8s-sv-load-test.sh movies-base-service /actuator/info 1 30 1
deleting report directory
report directory deleted
creating report directory
report directory created
launching a load <span class="nb">test </span>on http://10.152.183.244:8080/actuator/info with 1  user<span class="o">(</span>s<span class="o">)</span> during 30 seconds, ramping up during 1 seconds
Creating summariser &lt;summary&gt;
Created the tree successfully using /home/jamedina/Sources/movies-base-service/k8s-sv-load-test/load_test.jmx
Starting standalone <span class="nb">test</span> @ Thu Jan 02 18:25:14 GMT 2020 <span class="o">(</span>1577989514103<span class="o">)</span>
Waiting <span class="k">for </span>possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
Warning: Nashorn engine is planned to be removed from a future JDK release
summary +  88707 <span class="k">in </span>00:00:16 <span class="o">=</span> 5632.2/s Avg:     0 Min:     0 Max:    16 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span> Active: 1 Started: 1 Finished: 0
summary +  91647 <span class="k">in </span>00:00:14 <span class="o">=</span> 6427.8/s Avg:     0 Min:     0 Max:     2 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span> Active: 0 Started: 1 Finished: 1
summary <span class="o">=</span> 180354 <span class="k">in </span>00:00:30 <span class="o">=</span> 6010.0/s Avg:     0 Min:     0 Max:    16 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span>
Tidying up ...    @ Thu Jan 02 18:25:44 GMT 2020 <span class="o">(</span>1577989544259<span class="o">)</span>
... end of run
load <span class="nb">test </span><span class="k">done
</span>report available on file:////home/jamedina/Sources/movies-base-service/k8s-sv-load-test/report/index.html</code></pre></figure>

<p>When the test is complet we get a nice report in html, in this example was on <em>file:////home/jamedina/Sources/movies-base-service/k8s-sv-load-test/report/index.html</em></p>

<p><a href="/assets/img/captures/base_movies_services_06.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_06.jpg" alt="JMeter report" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>But this is a generic script so lets create a more specific to test our movies/genre endpoint on <em>load-test.sh</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="k">if</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-ne</span> 3 <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Illegal number of parameters, usage : "</span>
    <span class="nb">echo</span> <span class="s2">" "</span>
    <span class="nb">echo</span> <span class="s2">"  </span><span class="nv">$0</span><span class="s2"> &lt;users&gt; &lt;duration&gt; &lt;ramp up&gt;"</span>
    <span class="nb">echo</span> <span class="s2">" "</span>
    <span class="nb">echo</span> <span class="s2">" example: "</span>
    <span class="nb">echo</span> <span class="s2">"  - </span><span class="nv">$0</span><span class="s2"> 20 300 60"</span>
    <span class="nb">exit </span>2
<span class="k">fi

</span><span class="nv">SERVICE</span><span class="o">=</span><span class="s2">"movies-base-service"</span>
<span class="nv">TEST_URL</span><span class="o">=</span><span class="s2">"/movies/sci-fi"</span>
<span class="nv">NUM_USERS</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">DURATION</span><span class="o">=</span><span class="nv">$2</span>
<span class="nv">RAMP_UP</span><span class="o">=</span><span class="nv">$3</span>

./k8s-sv-load-test/k8s-sv-load-test.sh <span class="nv">$SERVICE</span> <span class="nv">$TEST_URL</span> <span class="nv">$NUM_USERS</span> <span class="nv">$DURATION</span> <span class="nv">$RAMP_UP</span></code></pre></figure>

<p>Now that we have our script ready we could launch it with</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"> ./load-test.sh 1 30 1
deleting report directory
report directory deleted
creating report directory
report directory created
launching a load <span class="nb">test </span>on http://10.152.183.252:8080/movies/sci-fi with 1  user<span class="o">(</span>s<span class="o">)</span> during 30 seconds, ramping up during 1 seconds
Creating summariser &lt;summary&gt;
Created the tree successfully using /home/jamedina/src/movies-base-service/k8s-sv-load-test/load_test.jmx
Starting standalone <span class="nb">test</span> @ Sat Jan 11 08:46:22 GMT 2020 <span class="o">(</span>1578732382783<span class="o">)</span>
Waiting <span class="k">for </span>possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
Warning: Nashorn engine is planned to be removed from a future JDK release
summary +    164 <span class="k">in </span>00:00:07 <span class="o">=</span>   23.2/s Avg:    42 Min:    40 Max:    65 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span> Active: 1 Started: 1 Finished: 0
summary +    570 <span class="k">in </span>00:00:23 <span class="o">=</span>   24.8/s Avg:    40 Min:    38 Max:    59 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span> Active: 0 Started: 1 Finished: 1
summary <span class="o">=</span>    734 <span class="k">in </span>00:00:30 <span class="o">=</span>   24.4/s Avg:    40 Min:    38 Max:    65 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span>
Tidying up ...    @ Sat Jan 11 08:46:52 GMT 2020 <span class="o">(</span>1578732412977<span class="o">)</span>
... end of run
load <span class="nb">test </span><span class="k">done
</span>report available on file:////home/jamedina/src/movies-base-service/k8s-sv-load-test/report/index.html</code></pre></figure>

<p>And this is the report that we get</p>

<p><a href="/assets/img/captures/base_movies_services_07.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_07.jpg" alt="JMeter report on sci-fi movies" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p><strong>Getting data from Grafana</strong></p>

<p>Now that we have done a simple test we could go to Grafana and look at the graph for this small test</p>

<p><a href="/assets/img/captures/base_movies_services_08.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_08.jpg" alt="Grafana first test" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>We could see in this test that initial we have a small increase of CPU and them a peak, this was quite rapidly because our ramp up period was just one second, 
them we see a dropdown after our test.</p>

<p>In terms of memory we see how slightly increase and remain unmodified during the test.</p>

<p><strong>Scaling</strong></p>

<p>For the next test we will need to increase and decrease the replicas of our service, we will use an small script for this mater name scale.sh</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="k">if</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-ne</span> 2 <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Illegal number of parameters, usage : "</span>
  <span class="nb">echo</span> <span class="s2">" "</span>
  <span class="nb">echo</span> <span class="s2">"  </span><span class="nv">$0</span><span class="s2"> &lt;k8-deployment-name&gt; &lt;replicas&gt;"</span>
  <span class="nb">echo</span> <span class="s2">" "</span>
  <span class="nb">echo</span> <span class="s2">" examples: "</span>
  <span class="nb">echo</span> <span class="s2">"  - </span><span class="nv">$0</span><span class="s2"> my-deployment 5"</span>
  <span class="nb">echo</span> <span class="s2">"  - </span><span class="nv">$0</span><span class="s2"> my-deployment 0"</span>
  <span class="nb">exit </span>2
<span class="k">fi

</span><span class="nv">KUBECMD</span><span class="o">=</span><span class="s2">"kubectl"</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-x</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">command</span> <span class="nt">-v</span> microk8s.kubectl<span class="si">)</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nv">KUBECMD</span><span class="o">=</span><span class="s2">"microk8s.kubectl"</span>
<span class="k">fi

</span><span class="nv">DEPLOYMENT_NAME</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
<span class="nv">WANTED_REPLICAS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
<span class="nv">REPLICAS</span><span class="o">=</span><span class="s2">"0"</span>
<span class="nv">PREVIOUS_REPLICAS</span><span class="o">=</span><span class="s2">"-1"</span>

replicas<span class="o">()</span> <span class="o">{</span>
  <span class="nv">REPLICAS</span><span class="o">=</span><span class="si">$(</span><span class="nv">$KUBECMD</span> get <span class="s2">"deployment/</span><span class="nv">$DEPLOYMENT_NAME</span><span class="s2">"</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.status.readyReplicas}'</span><span class="si">)</span>
  <span class="k">if </span><span class="nb">test</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$REPLICAS</span><span class="s2">"</span><span class="p">;</span> <span class="k">then
    </span><span class="nv">REPLICAS</span><span class="o">=</span><span class="s2">"0"</span>
  <span class="k">fi</span>
<span class="o">}</span>

<span class="nb">echo</span> <span class="s2">"checking deployment: </span><span class="nv">$DEPLOYMENT_NAME</span><span class="s2">"</span>

replicas
<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$REPLICAS</span><span class="s2">"</span> <span class="nt">-eq</span> <span class="s2">"</span><span class="nv">$WANTED_REPLICAS</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"we dont need to scale allready got </span><span class="nv">$REPLICAS</span><span class="s2"> replicas ready"</span>
  <span class="nb">exit </span>0
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"scaling deployment: </span><span class="nv">$DEPLOYMENT_NAME</span><span class="s2"> to </span><span class="nv">$WANTED_REPLICAS</span><span class="s2"> replicas"</span>

<span class="nv">$KUBECMD</span> scale deployment/movies-base-service <span class="nt">--replicas</span> <span class="s2">"</span><span class="nv">$WANTED_REPLICAS</span><span class="s2">"</span>

<span class="nv">START</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s.%N<span class="si">)</span>

<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
  if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$REPLICAS</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"</span><span class="nv">$PREVIOUS_REPLICAS</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"waiting for scaling: got </span><span class="nv">$REPLICAS</span><span class="s2"> replicas, want </span><span class="nv">$WANTED_REPLICAS</span><span class="s2">"</span>
    <span class="nv">PREVIOUS_REPLICAS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$REPLICAS</span><span class="s2">"</span>
  <span class="k">fi
  if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$REPLICAS</span><span class="s2">"</span> <span class="nt">-eq</span> <span class="s2">"</span><span class="nv">$WANTED_REPLICAS</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nv">END</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s.%N<span class="si">)</span>
    <span class="nv">DIFF</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$END</span><span class="s2"> - </span><span class="nv">$START</span><span class="s2">"</span> | bc<span class="si">)</span>
    <span class="nb">echo</span> <span class="s2">"scaling complete, </span><span class="nv">$REPLICAS</span><span class="s2"> ready, in </span><span class="nv">$DIFF</span><span class="s2"> seconds"</span>

    <span class="nb">exit </span>0
  <span class="k">fi
  </span>replicas
<span class="k">done</span></code></pre></figure>

<p>This script will tell K8s to scale our replica to a number and wait until we have the number of replicas that we ask for.</p>

<p>First let’s scale our application to 0 replicas.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./scale.sh movies-base-service 0
checking deployment: movies-base-service
scaling deployment: movies-base-service to 0 replicas
deployment.apps/movies-base-service scaled
waiting <span class="k">for </span>scaling: got 1 replicas, want 0
waiting <span class="k">for </span>scaling: got 0 replicas, want 0
scaling <span class="nb">complete</span>, 0 ready, <span class="k">in</span> .057501722 seconds</code></pre></figure>

<p>Not let’s scale our service to 5 replicas.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./scale.sh movies-base-service 5
checking deployment: movies-base-service
scaling deployment: movies-base-service to 5 replicas
deployment.apps/movies-base-service scaled
waiting <span class="k">for </span>scaling: got 0 replicas, want 5
waiting <span class="k">for </span>scaling: got 1 replicas, want 5
waiting <span class="k">for </span>scaling: got 2 replicas, want 5
waiting <span class="k">for </span>scaling: got 3 replicas, want 5
waiting <span class="k">for </span>scaling: got 4 replicas, want 5
waiting <span class="k">for </span>scaling: got 5 replicas, want 5
scaling <span class="nb">complete</span>, 5 ready, <span class="k">in </span>15.111852160 seconds</code></pre></figure>

<p>And now we will repeat our test but with <em>10</em> concurrent users :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./load-test.sh 10 30 1          
deleting report directory
report directory deleted
creating report directory
report directory created
launching a load <span class="nb">test </span>on http://10.152.183.252:8080/movies/sci-fi with 10  user<span class="o">(</span>s<span class="o">)</span> during 30 seconds, ramping up during 1 seconds
Creating summariser &lt;summary&gt;
Created the tree successfully using /home/jamedina/src/movies-base-service/k8s-sv-load-test/load_test.jmx
Starting standalone <span class="nb">test</span> @ Sat Jan 11 09:36:12 GMT 2020 <span class="o">(</span>1578735372194<span class="o">)</span>
Waiting <span class="k">for </span>possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
Warning: Nashorn engine is planned to be removed from a future JDK release
summary +    486 <span class="k">in </span>00:00:18 <span class="o">=</span>   27.5/s Avg:   347 Min:    44 Max:  1118 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span> Active: 10 Started: 10 Finished: 0
summary +    327 <span class="k">in </span>00:00:13 <span class="o">=</span>   25.1/s Avg:   401 Min:   108 Max:  1204 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span> Active: 0 Started: 10 Finished: 10
summary <span class="o">=</span>    813 <span class="k">in </span>00:00:31 <span class="o">=</span>   26.5/s Avg:   369 Min:    44 Max:  1204 Err:     0 <span class="o">(</span>0.00%<span class="o">)</span>
Tidying up ...    @ Sat Jan 11 09:36:43 GMT 2020 <span class="o">(</span>1578735403018<span class="o">)</span>
... end of run
load <span class="nb">test </span><span class="k">done
</span>report available on file:////home/jamedina/src/movies-base-service/k8s-sv-load-test/report/index.html</code></pre></figure>

<p>And this is the report that we got :</p>

<p><a href="/assets/img/captures/base_movies_services_09.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_09.jpg" alt="Report 10 users 5 pods" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>We like to get now the data from Grafana but we will create a copy of the <em>Pods</em> dashboard and name “movies” then we will edit the template to get our al movies pods :</p>

<p><a href="/assets/img/captures/base_movies_services_10.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_10.jpg" alt="Edit movies dashboard" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Finally we will get from Grafana the graph for our 5 pods 10 users test :</p>

<p><a href="/assets/img/captures/base_movies_services_11.jpg" target="_blank"><img src="/assets/img/captures/base_movies_services_11.jpg" alt="Edit movies dashboard" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p><strong>Getting our measure</strong></p>

<p>Now that we have this tools ready we will defined how we are going to measure this base service, and hopefuly futher services on the nexst parts for the series. We know that we have dozen of different metrics to get from our tools but we are going some very simple with just few data points, and we are not focus on a particular runtime, so they will be not JVM metrics, because we will not haven in all the parts of this series.</p>

<p>We will do this procedure with 1, 10, 25 and 50 concurrent users and 1 replica :</p>

<ul>
  <li>Scale the replicas to 0</li>
  <li>Stop our cluster</li>
  <li>Start our cluster</li>
  <li>Scale to desired number replicas</li>
  <li>Run the test for the desire set of concurrent users during 10 minutes with a ramp up of 1 minute</li>
  <li>If all request are OK (HTTP response code is 200) :
    <ul>
      <li>Get from the JMeter report :
        <ul>
          <li>Average response time</li>
          <li>Transactions per second</li>
        </ul>
      </li>
      <li>Get from Grafana
        <ul>
          <li>Max CPU Usage</li>
          <li>Max Memory Usage</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>If some request are not OK (HTTP response code is not 200)
    <ul>
      <li>We will increase the desire number of replicas to one more</li>
    </ul>
  </li>
</ul>

<p>Then we will repeat this procedure until we got the results for 100 concurrent users.</p>

<p>We will scale down our replicas and restart our cluster to guarantee that each test is a fresh run, including the database load and connections.</p>

<p><strong>The Results</strong></p>

<p>This are the results for this first example in the series, if you repeat the steps you may get different numbers because they depend on the cluster and the computer that is launching the tests.</p>

<table class="display data-table compact cell-border">
  <thead>
    <tr>
      <th>C. Users</th>
      <th>Pods</th>
      <th>ART</th>
      <th>TPS</th>
      <th>Max CPU</th>
      <th>Max MEM</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="dt-body-right">1</td>
      <td class="dt-body-right">1</td>
      <td class="dt-body-right">39.16</td>
      <td class="dt-body-right">25.49</td>
      <td class="dt-body-right">403</td>
      <td class="dt-body-right">474</td>
    </tr>
    <tr>
      <td class="dt-body-right">10</td>
      <td class="dt-body-right">1</td>
      <td class="dt-body-right">570.47</td>
      <td class="dt-body-right">16.73</td>
      <td class="dt-body-right">916</td>
      <td class="dt-body-right">487</td>
    </tr>
    <tr>
      <td class="dt-body-right">25</td>
      <td class="dt-body-right">3</td>
      <td class="dt-body-right">1615.82</td>
      <td class="dt-body-right">14.72</td>
      <td class="dt-body-right">1049</td>
      <td class="dt-body-right">1454</td>
    </tr>
    <tr>
      <td class="dt-body-right">50</td>
      <td class="dt-body-right">5</td>
      <td class="dt-body-right">3190.98</td>
      <td class="dt-body-right">14.89</td>
      <td class="dt-body-right">1351</td>
      <td class="dt-body-right">2499</td>
    </tr>
  </tbody>
</table>
<p>Java 8  - Spring Boot 2.0 - Spring Web - Spring Data JDBC	default settings, unoptimized</p>

<p>With this will close this first article, next we will try to optimize this first service and compare the results with the base.</p>

<p><em>Note: The full code of this service is available at this <a href="https://github.com/LearningByExample/movies-base-service" target="_blank">repository</a>.</em></p>

<p><strong>Resources</strong></p>

<ul>
  <li><a href="https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#jdbc.query-methods" target="_blank">https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#jdbc.query-methods</a></li>
  <li><a href="https://www.baeldung.com/spring-jdbc-jdbctemplate" target="_blank">https://www.baeldung.com/spring-jdbc-jdbctemplate</a></li>
  <li><a href="https://hub.docker.com/_/openjdk" target="_blank">https://hub.docker.com/_/openjdk</a></li>
  <li><a href="https://spring.io/guides/gs/spring-boot-docker/" target="_blank">https://spring.io/guides/gs/spring-boot-docker/</a></li>
  <li><a href="https://spring.io/guides/gs/spring-boot-kubernetes/" target="_blank">https://spring.io/guides/gs/spring-boot-kubernetes/</a></li>
  <li><a href="https://www.baeldung.com/spring-boot-kubernetes-self-healing-apps" target="_blank">https://www.baeldung.com/spring-boot-kubernetes-self-healing-apps</a></li>
  <li><a href="http://jmeter.apache.org/usermanual/hints_and_tips.html#hidpi" target="_blank">http://jmeter.apache.org/usermanual/hints_and_tips.html#hidpi</a></li>
  <li><a href="https://jmeter.apache.org/usermanual/best-practices.html" target="_blank">https://jmeter.apache.org/usermanual/best-practices.html</a></li>
</ul>]]></content><author><name>Juan Medina</name></author><category term="Cloud" /><category term="Programming" /><summary type="html"><![CDATA[Much have been said about the importance of optimizing services that are going to run in a cloud, and they are plenty of resources about the topic, from start up times, memory consumption or resource usage and dozen of data points of your applications.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/graph.jpg" /><media:content medium="image" url="http://juan-medina.com/assets/img/graph.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Creating Jobs in Kubernetes</title><link href="http://juan-medina.com/2019/12/16/jobs-k8s/" rel="alternate" type="text/html" title="Creating Jobs in Kubernetes" /><published>2019-12-16T00:00:00+00:00</published><updated>2019-12-16T00:00:00+00:00</updated><id>http://juan-medina.com/2019/12/16/jobs-k8s</id><content type="html" xml:base="http://juan-medina.com/2019/12/16/jobs-k8s/"><![CDATA[<p><a href="/2019/12/02/microk8s/" target="_blank">We recently configure</a> our own Kubernetes (K8s), cluster using MicroK8s and learn how we could use an operator for <a href="/2019/12/12/postgresql-k8s/" target="_blank">deploying a PostgreSQL Server</a> within our cluster. Now we will learn how we could create k8s jobs that populate a database within our cluster.</p>

<p><strong>Creating our Database</strong></p>

<p>This exercise assume that we have the PostgreSQL operator installed in our cluster, if not we may need to follow the steps that we did in the <a href="/2019/12/12/postgresql-k8s/" target="_blank">previous post</a>.</p>

<p>First we will delete the database that we created previously.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl delete postgresql movies-db-cluster
postgresql.acid.zalan.do <span class="s2">"movies-db-cluster"</span> deleted
<span class="nv">$ </span>microk8s.kubectl get postgresql                     
No resources found <span class="k">in </span>default namespace.</code></pre></figure>

<p>We will put everything that we are going to use in a directory that latter we could push to a repository, so we will start with a similar yaml that we original did <em>movies-db.yml</em> :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">acid.zalan.do/v1"</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">postgresql</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-db-cluster</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">default</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">teamId</span><span class="pi">:</span> <span class="s2">"</span><span class="s">movies"</span>
  <span class="na">volume</span><span class="pi">:</span>
    <span class="na">size</span><span class="pi">:</span> <span class="s">2Gi</span>
  <span class="na">numberOfInstances</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">users</span><span class="pi">:</span>
    <span class="na">moviesdba</span><span class="pi">:</span>  <span class="c1"># database owner</span>
    <span class="pi">-</span> <span class="s">superuser</span>
    <span class="pi">-</span> <span class="s">createdb</span>
    <span class="na">moviesuser</span><span class="pi">:</span> <span class="pi">[]</span>  <span class="c1"># roles</span>
  <span class="na">databases</span><span class="pi">:</span>
    <span class="na">movies</span><span class="pi">:</span> <span class="s">moviesdba</span>  <span class="c1"># dbname: owner</span>
  <span class="na">postgresql</span><span class="pi">:</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">11"</span></code></pre></figure>

<p>We are requesting 2 Gibibytes for the volume on our database named <em>movies</em> and a admin user named <em>moviesdba</em> and normal user name <em>moviesuser</em>, and we like to have to instance, one will be our master and the other our replica.</p>

<p><em>For those not familiar with the Gibibytes (Gi) instead of Gigabytes (G) you may check the IEEE 1541-2002 by the International Electrotechnical Commission (IEC) or <a href="https://en.wikipedia.org/wiki/Gibibyte" target="_blank">this article</a> in the Wikipedia</em></p>

<p>Now we could create the database executing :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> movies-db.yml
postgresql.acid.zalan.do/movies-db-cluster created</code></pre></figure>

<p>Now we we need to wait that our database is running, we could check until we get this output :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get postgresql
NAME                TEAM     VERSION   PODS   VOLUME   CPU-REQUEST   MEMORY-REQUEST   AGE    STATUS
movies-db-cluster   movies   11        2      2Gi                                     106s   Running</code></pre></figure>

<p>And the inspect the nodes four our database with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pods <span class="nt">-l</span> <span class="nv">version</span><span class="o">=</span>movies-db-cluster <span class="nt">-L</span> spilo-role
NAME                  READY   STATUS    RESTARTS   AGE     SPILO-ROLE
movies-db-cluster-0   1/1     Running   0          3m49s   master
movies-db-cluster-1   1/1     Running   0          3m6s    replica</code></pre></figure>

<p><strong>Creating a simple Job</strong></p>

<p>Now that we have our database we could start creating our Job but first we need to learn how Jobs work in k8s. A k8s job is a task that run
until completion, that means if the job doesn’t end it will run forever.</p>

<p>We could tell k8s to deploy an image, base on a existing image, and run it until it’s end, for this we will use a yaml file to
describe it, in this example will be : database-load.yml</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-load-run</span> <span class="c1"># the name of our job</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
<span class="na">spec</span><span class="pi">:</span>  
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">database-load-container</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">perl</span>
        <span class="na">command</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">perl"</span> <span class="pi">,</span><span class="s2">"</span><span class="s">-wle"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">print</span><span class="nv"> </span><span class="s">'hello</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">Job'"</span><span class="pi">]</span>
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span></code></pre></figure>

<p>For running this job we could just do  :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> database-load.yml
job.batch/movies-load-run created</code></pre></figure>

<p>To check the job that we have create we could do :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get job/movies-load-run
NAME              COMPLETIONS   DURATION   AGE
movies-load-run   0/1           3s         3s</code></pre></figure>

<p>We see that our job run during 3s, completions show 0/1 because is not running anymore. Let’s check the pods for our job</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pod <span class="nt">-l</span><span class="o">=</span>job-name<span class="o">=</span>movies-load-run
NAME                    READY   STATUS      RESTARTS   AGE
movies-load-run-rgrz8   0/1     Completed   0          6m4s</code></pre></figure>

<p>We could see that we have a pod for our job that is not running and is complete, we could check the log for our run with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl logs movies-load-run-rgrz8
hello from a Job</code></pre></figure>

<p><strong>Rerun our Job</strong></p>

<p>Our job is complete but we may want to run it again, we could try doing :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> database-load.yml
Error from server <span class="o">(</span>AlreadyExists<span class="o">)</span>: error when creating <span class="s2">"database-load.yml"</span>: jobs.batch <span class="s2">"movies-load-run"</span> already exists</code></pre></figure>

<p>And this is because that job already exist, so we could not create again, however we could delete it first :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl delete <span class="nt">-f</span> database-load.yml
job.batch <span class="s2">"movies-load-run"</span> deleted
<span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> database-load.yml
job.batch/movies-load-run created</code></pre></figure>

<p>And we could get the log as before :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pod <span class="nt">-l</span><span class="o">=</span>job-name<span class="o">=</span>movies-load-run
NAME                    READY   STATUS      RESTARTS   AGE
movies-load-run-t8lqw   0/1     Completed   0          3m13s
<span class="nv">$ </span>microk8s.kubectl logs movies-load-run-t8lqw
hello from a Job</code></pre></figure>

<p>But this is not great, there is a different way to do this, first we will delete our job:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl delete <span class="nt">-f</span> database-load.yml
job.batch <span class="s2">"movies-load-run"</span> deleted</code></pre></figure>

<p>We will modify our database-load.yml to use <em>generateName</em> instead of <em>name</em> :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">generateName</span><span class="pi">:</span> <span class="s">movies-load-run-</span> <span class="c1"># the name of our job</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
<span class="na">spec</span><span class="pi">:</span>  
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">database-load-container</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">perl</span>
        <span class="na">command</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">perl"</span> <span class="pi">,</span><span class="s2">"</span><span class="s">-wle"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">print</span><span class="nv"> </span><span class="s">'hello</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">Job'"</span><span class="pi">]</span>
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span></code></pre></figure>

<p>And now let’s do a couple of runs :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> database-load.yml
job.batch/movies-load-run-ps4nn created
<span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> database-load.yml
job.batch/movies-load-run-8hm9h created</code></pre></figure>

<p>Now we could get our jobs with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get <span class="nb">jobs</span> <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
NAME                    COMPLETIONS   DURATION   AGE
movies-load-run-8hm9h   1/1           3s         90s
movies-load-run-ps4nn   1/1           3s         92s</code></pre></figure>

<p>And the pods that they run with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pods <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
NAME                          READY   STATUS      RESTARTS   AGE
movies-load-run-8hm9h-m7rd5   0/1     Completed   0          3m22s
movies-load-run-ps4nn-dswxg   0/1     Completed   0          3m24s</code></pre></figure>

<p>We are doing this using the custom label that we have add named <em>job-group</em>, and we could use it even for get the logs :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl logs <span class="nt">--timestamps</span> <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
2019-12-18T07:52:07.434994661Z hello from a Job
2019-12-18T07:52:05.793251225Z hello from a Job</code></pre></figure>

<p>Ne could delete our jobs using as well the group :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl delete <span class="nb">jobs</span> <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
job.batch <span class="s2">"movies-load-run-8hm9h"</span> deleted
job.batch <span class="s2">"movies-load-run-ps4nn"</span> delete</code></pre></figure>

<p>One interesting fact is that when we run a job a pod is created and remain on hour cluster until is restarted, we
may not want to do that for freeing cluster resources so we want may want to delete the jobs.</p>

<p>There is a alpha feature of k8s, <a href="https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/" target="_blank">TTL Controller for Finished Resources</a>, that allow that, but since is alpha is may change in the future so we are not going to use it, for now.</p>

<p><strong>Visualizing our Jobs in Kibana</strong></p>

<p>Since we want to delete our job if we do it so we could not use kubectl to view our past logs, however we could use Kibana, as it was <a href="/2019/12/02/microk8s/" target="_blank">setup</a> in our MicroK8s, for visualizing our past jobs.</p>

<p>For example we could just filter using the simple expression : <em>kubernetes.labels.job-group: “movie-load”</em></p>

<p><a href="/assets/img/captures/kibana_jobs_01.jpg" target="_blank"><img src="/assets/img/captures/kibana_jobs_01.jpg" alt="kibana job by group" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Or we could explore a particular run with : <em>kubernetes.labels.job-name: “movies-load-run-mrn7f”</em></p>

<p><a href="/assets/img/captures/kibana_jobs_02.jpg" target="_blank"><img src="/assets/img/captures/kibana_jobs_02.jpg" alt="kibana job by name" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p><strong>Creating our own image</strong></p>

<p>For creating our job so far we have use an existing image, however we will need to create our own in order to add the dependencies that we will need, 
such the PostgreSQL client, but we need a tool for creating images and we are going to use <a href="https://buildah.io/" target="_blank">docker</a>.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="se">\</span>
    apt-transport-https <span class="se">\</span>
    ca-certificates <span class="se">\</span>
    curl <span class="se">\</span>
    gnupg-agent <span class="se">\</span>
    software-properties-common

<span class="nv">$ </span>curl <span class="nt">-fsSL</span> https://download.docker.com/linux/ubuntu/gpg | <span class="nb">sudo </span>apt-key add -

<span class="nv">$ </span><span class="nb">sudo </span>add-apt-repository <span class="s2">"deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"</span>

<span class="nv">$ </span><span class="nb">sudo </span>apt-get update

<span class="nv">$ </span><span class="nb">sudo </span>apt-get <span class="nb">install </span>docker-ce docker-ce-cli containerd.io</code></pre></figure>

<p><em>Note : there is a bug in linux mint 19.1 that the add-apt-repository does not work, you could add it manually as describe <a href="https://forums.linuxmint.com/viewtopic.php?t=300469" target="_blank">here</a>.</em></p>

<p>We will need as well to add our user to the docker group so we do not need to sudo :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>usermod <span class="nt">-a</span> <span class="nt">-G</span> docker <span class="nv">$USER</span></code></pre></figure>

<p>After this we should restart our system.</p>

<p>Them we to configure in docker the registry that we have in our MicroK8s cluster modifying the file <em>/etc/docker/daemon.json</em> as superuser :</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
  </span><span class="nl">"insecure-registries"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"localhost:32000"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>After that we will need to restart the docker engine :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>systemctl restart docker</code></pre></figure>

<p>Now we will start to create our <em>Dockerfile</em> :</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> ubuntu:latest</span>

<span class="k">ADD</span><span class="s"> job.sh /usr/src/job.sh</span>
<span class="k">RUN </span><span class="nb">chmod</span> +x /usr/src/job.sh

<span class="k">CMD</span><span class="s"> ["/usr/src/job.sh"]</span></code></pre></figure>

<p>Now we will could build our container with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>docker build <span class="nb">.</span> <span class="nt">-t</span> localhost:32000/movie-load:registry</code></pre></figure>

<p>If we like to test our docker we could do :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>docker run localhost:32000/movie-load:registry       
hello from a shell script <span class="k">in </span>a docker</code></pre></figure>

<p>Now we will push our docker into the registry</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>docker push localhost:32000/movie-load</code></pre></figure>

<p>We need to modify the <em>database-load.yml</em> for our job to refer to the new image:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">generateName</span><span class="pi">:</span> <span class="s">movies-load-run-</span> <span class="c1"># the name of our job</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
<span class="na">spec</span><span class="pi">:</span>  
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">database-load-container</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movie-load:registry</span>
        <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span></code></pre></figure>

<p>Now we could run our job as before :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> database-load.yml </code></pre></figure>

<p>And get our logs as we did previously :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl logs <span class="nt">--timestamps</span> <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
2019-12-21T10:48:37.625914657Z hello from a shell script <span class="k">in </span>a docker</code></pre></figure>

<p>To make the things simple lets create an script name <em>build.sh</em> to build and push our docker :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

docker build <span class="nb">.</span> <span class="nt">-t</span> localhost:32000/movie-load:registry
docker push localhost:32000/movie-load</code></pre></figure>

<p>Lets make it executable with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">chmod</span> +x build.sh</code></pre></figure>

<p>Now for build and push our docker we could just do :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh</code></pre></figure>

<p><strong>Adding a PostgreSQL Client</strong></p>

<p>Since our job will load data from a database we will add the PostgreSQL client our <em>Dockerfile</em> :</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> ubuntu:latest</span>

<span class="k">RUN </span>apt-get update
<span class="k">RUN </span>apt-get <span class="nb">install </span>postgresql-client <span class="nt">-y</span>

<span class="k">ADD</span><span class="s"> job.sh /usr/src/job.sh</span>
<span class="k">RUN </span><span class="nb">chmod</span> +x /usr/src/job.sh

<span class="k">CMD</span><span class="s"> ["/usr/src/job.sh"]</span></code></pre></figure>

<p>And we will change our job to test the client :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

psql <span class="nt">--version</span>

<span class="nb">echo</span> <span class="s2">"job completed"</span>

<span class="k">return </span>0</code></pre></figure>

<p>For making the thing simpler we will create a new script named <em>run.sh</em> ::</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

microk8s.kubectl delete <span class="nb">jobs</span> <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load

microk8s.kubectl create <span class="nt">-f</span> database-load.yml</code></pre></figure>

<p>Lets make it executable as well :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">chmod</span> +x run.sh</code></pre></figure>

<p>Now we could easily build and run our changes with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh
Sending build context to Docker daemon  7.168kB
Step 1/6 : FROM ubuntu:latest
 <span class="nt">---</span><span class="o">&gt;</span> 549b9b86cb8d
Step 2/6 : RUN apt-get update
 <span class="nt">---</span><span class="o">&gt;</span> Using cache
 <span class="nt">---</span><span class="o">&gt;</span> dcad669c2dc7
Step 3/6 : RUN apt-get <span class="nb">install </span>postgresql-client <span class="nt">-y</span>
 <span class="nt">---</span><span class="o">&gt;</span> Using cache
 <span class="nt">---</span><span class="o">&gt;</span> 653870d02d92
Step 4/6 : ADD job.sh /usr/src/job.sh
 <span class="nt">---</span><span class="o">&gt;</span> Using cache
 <span class="nt">---</span><span class="o">&gt;</span> f5eb8c9877f7
Step 5/6 : RUN <span class="nb">chmod</span> +x /usr/src/job.sh
 <span class="nt">---</span><span class="o">&gt;</span> Using cache
 <span class="nt">---</span><span class="o">&gt;</span> 2f1342e99195
Step 6/6 : CMD <span class="o">[</span><span class="s2">"/usr/src/job.sh"</span><span class="o">]</span>
 <span class="nt">---</span><span class="o">&gt;</span> Using cache
 <span class="nt">---</span><span class="o">&gt;</span> 2c93a1f139ef
Successfully built 2c93a1f139ef
Successfully tagged localhost:32000/movie-load:registry
The push refers to repository <span class="o">[</span>localhost:32000/movie-load]
a05f1dc51a49: Layer already exists 
0cabfb9c787c: Layer already exists 
422cf4890fa0: Layer already exists 
918efb8f161b: Layer already exists 
27dd43ea46a8: Layer already exists 
9f3bfcc4a1a8: Layer already exists 
2dc9f76fb25b: Layer already exists 
registry: digest: sha256:a017e6185297983a583d9e4caf08524e961e75672e3a98cde959e8b3c675010a size: 1990

<span class="nv">$ </span>./run.sh 
job.batch <span class="s2">"movies-load-run-llftf"</span> deleted
job.batch/movies-load-run-9qrfd created

<span class="nv">$ </span>./microk8s.kubectl get pods <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
2019-12-21T11:29:41.615865967Z psql <span class="o">(</span>PostgreSQL<span class="o">)</span> 10.10 <span class="o">(</span>Ubuntu 10.10-0ubuntu0.18.04.1<span class="o">)</span>
2019-12-21T11:29:41.616010932Z job completed</code></pre></figure>

<p><strong>Connecting to our database</strong></p>

<p>In order to connect to our database we require credentials so first we will inject them in our job changing our <em>database-load.yml</em> :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">generateName</span><span class="pi">:</span> <span class="s">movies-load-run-</span> <span class="c1"># the name of our job</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
<span class="na">spec</span><span class="pi">:</span>  
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">database-load-container</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movie-load:registry</span>
        <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
            <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/db-credentials"</span>
            <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesdba.movies-db-cluster.credentials</span></code></pre></figure>

<p>Now we will modify our script <em>job.sh</em> to use it :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="c1">#!/bin/sh -</span>

<span class="s">set -o errexit</span>

<span class="s">cd /usr/src</span>

<span class="s">export PGHOST=$MOVIES_DB_CLUSTER_SERVICE_HOST</span>
<span class="s">export PGPORT=$MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL</span>
<span class="s">export PGDATABASE="movies"</span>
<span class="s">export PGUSER=`cat /etc/db-credentials/username`</span>
<span class="s">export PGPASSWORD=`cat /etc/db-credentials/password`</span>

<span class="s">psql &lt; init.sql</span>                                                           

<span class="s">echo "job completed"</span>

<span class="s">return 0</span></code></pre></figure>

<p>This script will run a file <em>init.sql</em> into our database, let’s create it :</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="k">SELECT</span> <span class="s1">'hello from a SQL'</span> <span class="k">as</span> <span class="n">message</span><span class="p">,</span> <span class="k">current_timestamp</span> <span class="k">as</span> <span class="nb">date</span><span class="p">;</span></code></pre></figure>

<p>But we need to add this file into our <em>Dockerfile</em> :</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> ubuntu:latest</span>

<span class="k">RUN </span>apt-get update
<span class="k">RUN </span>apt-get <span class="nb">install </span>postgresql-client <span class="nt">-y</span>

<span class="k">ADD</span><span class="s"> job.sh /usr/src/job.sh</span>
<span class="k">RUN </span><span class="nb">chmod</span> +x /usr/src/job.sh

<span class="k">ADD</span><span class="s"> init.sql /usr/src/init.sql</span>

<span class="k">CMD</span><span class="s"> ["/usr/src/job.sh"]</span></code></pre></figure>

<p>Finally we could build and run our job :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh

<span class="nv">$ </span>./run.sh  

<span class="nv">$ </span>microk8s.kubectl get pods <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
2019-12-21T12:16:28.063280527Z      message      |             <span class="nb">date              
</span>2019-12-21T12:16:28.063298709Z <span class="nt">------------------</span>+-------------------------------
2019-12-21T12:16:28.063301164Z  hello from a SQL | 2019-12-21 12:16:28.062985+00
2019-12-21T12:16:28.063302989Z <span class="o">(</span>1 row<span class="o">)</span>
2019-12-21T12:16:28.063304716Z 
2019-12-21T12:16:28.063652329Z job completed</code></pre></figure>

<p>With this running we could add to our <em>init.sql</em> the creation of the tables for our database :</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="k">DROP</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">EXISTS</span> <span class="n">movies</span><span class="p">;</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">movies</span> <span class="p">(</span>
  <span class="n">id</span> <span class="nb">serial</span> <span class="k">primary</span> <span class="k">key</span><span class="p">,</span>
  <span class="n">title</span> <span class="nb">text</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
  <span class="n">genres</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="p">);</span>

<span class="k">DROP</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">EXISTS</span> <span class="n">ratings</span><span class="p">;</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">ratings</span> <span class="p">(</span>
  <span class="n">id</span> <span class="nb">serial</span> <span class="k">primary</span> <span class="k">key</span><span class="p">,</span>
  <span class="n">user_id</span> <span class="nb">int</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
  <span class="n">movie_id</span> <span class="nb">int</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
  <span class="n">rating</span> <span class="nb">float</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
  <span class="n">created</span> <span class="nb">bigint</span> <span class="k">not</span> <span class="k">null</span>
<span class="p">);</span>

<span class="k">vacuum</span> <span class="k">full</span><span class="p">;</span></code></pre></figure>

<p>We will create some tables from our jobs using this SQL statements, let’s build and run our job :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh

<span class="nv">$ </span>./run.sh  

<span class="nv">$ </span>microk8s.kubectl get pods <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
NOTICE:  table <span class="s2">"movies"</span> does not exist, skipping
CREATE TABLE
NOTICE:  table <span class="s2">"ratings"</span> does not exist, skipping
CREATE TABLE
VACUUM
job <span class="nb">complete</span></code></pre></figure>

<p>We got some messages because our tables didn’t exist but if we run our job again :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./run.sh  

<span class="nv">$ </span>microk8s.kubectl get pods <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load
DROP TABLE
CREATE TABLE
DROP TABLE
CREATE TABLE
VACUUM
job completed</code></pre></figure>

<p><strong>Download a file with movies data</strong></p>

<p>Now that we are able to create our tables we will download movies data from <a href="https://grouplens.org/datasets/movielens/" target="_blank">movielens</a>, but
first let’s modify our <em>database-load.yml</em> to have a volume to temporary store this data using <a href="https://kubernetes.io/docs/concepts/storage/volumes/#emptydir" target="_blank">emptyDir</a> :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">generateName</span><span class="pi">:</span> <span class="s">movies-load-run-</span> <span class="c1"># the name of our job</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
<span class="na">spec</span><span class="pi">:</span>  
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">job-group</span><span class="pi">:</span> <span class="s">movie-load</span> <span class="c1"># logical grouping</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">database-load-container</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">localhost:32000/movie-load:registry</span>
        <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">Always</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
            <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/etc/db-credentials"</span>
            <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">true</span>
          <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">download</span>
            <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/tmp/download"</span>
            <span class="na">readOnly</span><span class="pi">:</span> <span class="kc">false</span>            
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">moviesdba.movies-db-cluster.credentials</span>
        <span class="pi">-</span> <span class="na">name </span><span class="pi">:</span> <span class="s">download</span>
          <span class="na">emptyDir</span><span class="pi">:</span> <span class="pi">{}</span></code></pre></figure>

<p>An empty dir is able to store information for our pod at long at is runs, and disappear when our pod/job ends,
but will we need more tools in our container so we will first modify our <em>Dockerfile</em> adding wget and unzip:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">FROM ubuntu:latest

RUN apt-get update
RUN apt-get <span class="nb">install </span>postgresql-client <span class="nt">-y</span>
RUN apt-get <span class="nb">install </span>wget <span class="nt">-y</span>
RUN apt-get <span class="nb">install </span>unzip

ADD job.sh /usr/src/job.sh
RUN <span class="nb">chmod</span> +x /usr/src/job.sh

ADD init.sql /usr/src/init.sql

CMD <span class="o">[</span><span class="s2">"/usr/src/job.sh"</span><span class="o">]</span></code></pre></figure>

<p>Now we will modify our job to download our movies file updating our <em>job.sh</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh -</span>

<span class="nb">set</span> <span class="nt">-o</span> errexit

<span class="nb">echo</span> <span class="s2">"downloading file.."</span>
<span class="nb">cd</span> /tmp/download
wget <span class="nt">-q</span> <span class="nt">-c</span> http://files.grouplens.org/datasets/movielens/ml-latest.zip 
<span class="nb">echo</span> <span class="s2">"file downloaded"</span>

<span class="nb">echo</span> <span class="s2">"unzip file.."</span>
unzip <span class="nt">-o</span> ml-latest.zip
<span class="nb">echo</span> <span class="s2">"file unziped"</span>

<span class="nb">echo</span> <span class="s2">"running init script.."</span>
<span class="nb">export </span><span class="nv">PGHOST</span><span class="o">=</span><span class="nv">$MOVIES_DB_CLUSTER_SERVICE_HOST</span>
<span class="nb">export </span><span class="nv">PGPORT</span><span class="o">=</span><span class="nv">$MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL</span>
<span class="nb">export </span><span class="nv">PGDATABASE</span><span class="o">=</span><span class="s2">"movies"</span>
<span class="nb">export </span><span class="nv">PGUSER</span><span class="o">=</span><span class="sb">`</span><span class="nb">cat</span> /etc/db-credentials/username<span class="sb">`</span>
<span class="nb">export </span><span class="nv">PGPASSWORD</span><span class="o">=</span><span class="sb">`</span><span class="nb">cat</span> /etc/db-credentials/password<span class="sb">`</span>

psql &lt; /usr/src/init.sql                                                           
<span class="nb">echo</span> <span class="s2">"init script completed"</span>

<span class="nb">echo</span> <span class="s2">"job completed"</span>

<span class="k">return </span>0</code></pre></figure>

<p>Now we could run our job but we will follow the log since will take longer :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh

<span class="nv">$ </span>./run.sh  

<span class="nv">$ </span>microk8s.kubectl logs <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load <span class="nt">--timestamps</span> <span class="nt">-f</span>
2019-12-21T17:10:02.282464543Z downloading file..
2019-12-21T17:11:39.597312604Z Archive:  ml-latest.zip
2019-12-21T17:11:39.597515143Z    creating: ml-latest/
2019-12-21T17:11:39.632152585Z   inflating: ml-latest/links.csv     
2019-12-21T17:11:39.842122169Z   inflating: ml-latest/tags.csv      
2019-12-21T17:11:39.842264413Z   inflating: ml-latest/genome-tags.csv  
2019-12-21T17:11:43.479114191Z   inflating: ml-latest/ratings.csv   
2019-12-21T17:11:43.479184778Z   inflating: ml-latest/README.txt    
2019-12-21T17:11:45.081158518Z   inflating: ml-latest/genome-scores.csv  
2019-12-21T17:11:45.096730053Z   inflating: ml-latest/movies.csv    
2019-12-21T17:11:45.096873562Z file downloaded
2019-12-21T17:11:45.096880746Z running init script..
2019-12-21T17:11:45.139973302Z DROP TABLE
2019-12-21T17:11:45.156706409Z CREATE TABLE
2019-12-21T17:11:45.164718047Z DROP TABLE
2019-12-21T17:11:45.177663221Z CREATE TABLE
2019-12-21T17:11:46.481417622Z VACUUM
2019-12-21T17:11:46.484035911Z init script completed
2019-12-21T17:11:46.484089699Z job completed</code></pre></figure>

<p><strong>Upload csv data into our database</strong></p>

<p>Now that we have the data download we will create an script that insert the csv data into our database, we will name <em>load.sql</em> :</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="c1">-- movies.csv: movieId,title,genres</span>
<span class="err">\</span><span class="k">copy</span> <span class="n">movies</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">genres</span><span class="p">)</span> <span class="k">FROM</span> <span class="s1">'ml-latest/movies.csv'</span> <span class="k">WITH</span> <span class="p">(</span><span class="k">DELIMITER</span> <span class="s1">','</span><span class="p">,</span> <span class="n">FORMAT</span> <span class="n">CSV</span><span class="p">,</span> <span class="n">HEADER</span> <span class="k">true</span><span class="p">,</span> <span class="k">ESCAPE</span> <span class="s1">'"'</span><span class="p">,</span> <span class="k">ENCODING</span> <span class="s1">'UTF-8'</span><span class="p">);</span>

<span class="c1">-- ratings.csv: userId,movieId,rating,timestamp</span>
<span class="err">\</span><span class="k">copy</span> <span class="n">ratings</span> <span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="n">movie_id</span><span class="p">,</span> <span class="n">rating</span><span class="p">,</span> <span class="n">created</span><span class="p">)</span> <span class="k">FROM</span> <span class="s1">'ml-latest/ratings.csv'</span> <span class="k">WITH</span> <span class="p">(</span><span class="k">DELIMITER</span> <span class="s1">','</span><span class="p">,</span> <span class="n">FORMAT</span> <span class="n">CSV</span><span class="p">,</span> <span class="n">HEADER</span> <span class="k">true</span><span class="p">,</span> <span class="k">ESCAPE</span> <span class="s1">'"'</span><span class="p">,</span> <span class="k">ENCODING</span> <span class="s1">'UTF-8'</span><span class="p">);</span></code></pre></figure>

<p>We need to add this new SQL script into our <em>Dockerfile</em> :</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> ubuntu:latest</span>

<span class="k">RUN </span>apt-get update
<span class="k">RUN </span>apt-get <span class="nb">install </span>postgresql-client <span class="nt">-y</span>
<span class="k">RUN </span>apt-get <span class="nb">install </span>wget <span class="nt">-y</span>
<span class="k">RUN </span>apt-get <span class="nb">install </span>unzip

<span class="k">ADD</span><span class="s"> job.sh /usr/src/job.sh</span>
<span class="k">RUN </span><span class="nb">chmod</span> +x /usr/src/job.sh

<span class="k">ADD</span><span class="s"> init.sql /usr/src/init.sql</span>
<span class="k">ADD</span><span class="s"> load.sql /usr/src/load.sql</span>

<span class="k">CMD</span><span class="s"> ["/usr/src/job.sh"]</span></code></pre></figure>

<p>After this we need to modify our <em>job.sh</em> to run our new sql :</p>

<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="o">#!/</span><span class="n">bin</span><span class="o">/</span><span class="n">sh</span> <span class="o">-</span>

<span class="k">set</span> <span class="o">-</span><span class="n">o</span> <span class="n">errexit</span>

<span class="n">echo</span> <span class="nv">"downloading file.."</span>
<span class="n">cd</span> <span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">download</span>
<span class="n">wget</span> <span class="o">-</span><span class="n">q</span> <span class="o">-</span><span class="k">c</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">files</span><span class="p">.</span><span class="n">grouplens</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">datasets</span><span class="o">/</span><span class="n">movielens</span><span class="o">/</span><span class="n">ml</span><span class="o">-</span><span class="n">latest</span><span class="p">.</span><span class="n">zip</span> 
<span class="n">echo</span> <span class="nv">"file downloaded"</span>

<span class="n">echo</span> <span class="nv">"unzip file.."</span>
<span class="n">unzip</span> <span class="o">-</span><span class="n">o</span> <span class="n">ml</span><span class="o">-</span><span class="n">latest</span><span class="p">.</span><span class="n">zip</span>
<span class="n">echo</span> <span class="nv">"file unziped"</span>

<span class="n">export</span> <span class="n">PGHOST</span><span class="o">=</span><span class="err">$</span><span class="n">MOVIES_DB_CLUSTER_SERVICE_HOST</span>
<span class="n">export</span> <span class="n">PGPORT</span><span class="o">=</span><span class="err">$</span><span class="n">MOVIES_DB_CLUSTER_SERVICE_PORT_POSTGRESQL</span>
<span class="n">export</span> <span class="n">PGDATABASE</span><span class="o">=</span><span class="nv">"movies"</span>
<span class="n">export</span> <span class="n">PGUSER</span><span class="o">=</span><span class="nv">`cat /etc/db-credentials/username`</span>
<span class="n">export</span> <span class="n">PGPASSWORD</span><span class="o">=</span><span class="nv">`cat /etc/db-credentials/password`</span>

<span class="n">echo</span> <span class="nv">"running init script.."</span>
<span class="n">psql</span> <span class="o">&lt;</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">init</span><span class="p">.</span><span class="k">sql</span>                                                           
<span class="n">echo</span> <span class="nv">"init script completed"</span>

<span class="n">echo</span> <span class="nv">"running load script.."</span>
<span class="n">psql</span> <span class="o">&lt;</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="k">load</span><span class="p">.</span><span class="k">sql</span>
<span class="n">echo</span> <span class="nv">"load script completed"</span>

<span class="n">echo</span> <span class="nv">"job completed"</span>

<span class="k">return</span> <span class="mi">0</span></code></pre></figure>

<p>Now we could run our job to load our database:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>./build.sh

<span class="nv">$ </span>./run.sh  

<span class="nv">$ </span>microk8s.kubectl logs <span class="nt">--selector</span><span class="o">=</span>job-group<span class="o">=</span>movie-load <span class="nt">--timestamps</span> <span class="nt">-f</span>
2019-12-21T17:28:33.973743423Z downloading file..
2019-12-21T17:30:12.031149113Z file downloaded
2019-12-21T17:30:12.031203659Z unzip file..
2019-12-21T17:30:12.034132763Z Archive:  ml-latest.zip
2019-12-21T17:30:12.03426501Z    creating: ml-latest/
2019-12-21T17:30:12.043329834Z   inflating: ml-latest/links.csv     
2019-12-21T17:30:12.241631499Z   inflating: ml-latest/tags.csv      
2019-12-21T17:30:12.241778497Z   inflating: ml-latest/genome-tags.csv  
2019-12-21T17:30:15.845676942Z   inflating: ml-latest/ratings.csv   
2019-12-21T17:30:15.845749788Z   inflating: ml-latest/README.txt    
2019-12-21T17:30:17.440092559Z   inflating: ml-latest/genome-scores.csv  
2019-12-21T17:30:17.455591045Z   inflating: ml-latest/movies.csv    
2019-12-21T17:30:17.455835049Z file unziped
2019-12-21T17:30:17.456391292Z running init script..
2019-12-21T17:30:17.482454366Z DROP TABLE
2019-12-21T17:30:17.492437203Z CREATE TABLE
2019-12-21T17:30:17.495135958Z DROP TABLE
2019-12-21T17:30:17.502169062Z CREATE TABLE
2019-12-21T17:30:18.880631358Z VACUUM
2019-12-21T17:30:18.883385958Z init script completed
2019-12-21T17:30:18.883437157Z running load script..
2019-12-21T17:30:19.025907717Z COPY 58098
2019-12-21T17:32:29.037196091Z COPY 27753444
2019-12-21T17:32:29.037863671Z load script completed
2019-12-21T17:32:29.037873093Z job completed</code></pre></figure>

<p>This job has load 58k movies and 2.7M ratings, in a 2GB file into our database in about 4 minutes, 2 minutes just to import the data, not bat at all.</p>

<p><strong>Exploring the loaded data</strong></p>

<p>Lest explore the data using PSQL, we will login in our server with our moviesdba user so we need to get it password, we could get it with:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get secret moviesdba.movies-db-cluster.credentials <span class="nt">-o</span> <span class="s1">'jsonpath={.data.password}'</span> | <span class="nb">base64</span> <span class="nt">-d</span> 
9oYUFcamSKwjB5Yrg099glLHdqg8C1IkScRfd5TeHTisiuj23FQrx3YEW6fB3ctJ</code></pre></figure>

<p>We will forward the postgres port 5432 on our master to our localhost port 6432, this will run until we do <em>ctrl+c</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl port-forward movies-db-cluster-0 6432:5432                        
Forwarding from 127.0.0.1:6432 -&gt; 5432
Forwarding from <span class="o">[</span>::1]:6432 -&gt; 5432</code></pre></figure>

<p>Finally we can connect to our database with with the provide user and password using psql in another
shell :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>psql <span class="nt">-h</span> localhost <span class="nt">-p</span> 6432 <span class="nt">-U</span> moviesdba movies
Password <span class="k">for </span>user moviesdba: 
psql <span class="o">(</span>11.6 <span class="o">(</span>Ubuntu 11.6-1.pgdg18.04+1<span class="o">)</span>, server 11.5 <span class="o">(</span>Ubuntu 11.5-1.pgdg18.04+1<span class="o">))</span>
SSL connection <span class="o">(</span>protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off<span class="o">)</span>
Type <span class="s2">"help"</span> <span class="k">for </span>help.

<span class="nv">movies</span><span class="o">=</span><span class="c"># SELECT * FROM movies LIMIT 10;</span>
 <span class="nb">id</span> |               title                |                   genres                    
<span class="nt">----</span>+------------------------------------+---------------------------------------------
  1 | Toy Story <span class="o">(</span>1995<span class="o">)</span>                   | Adventure|Animation|Children|Comedy|Fantasy
  2 | Jumanji <span class="o">(</span>1995<span class="o">)</span>                     | Adventure|Children|Fantasy
  3 | Grumpier Old Men <span class="o">(</span>1995<span class="o">)</span>            | Comedy|Romance
  4 | Waiting to Exhale <span class="o">(</span>1995<span class="o">)</span>           | Comedy|Drama|Romance
  5 | Father of the Bride Part II <span class="o">(</span>1995<span class="o">)</span> | Comedy
  6 | Heat <span class="o">(</span>1995<span class="o">)</span>                        | Action|Crime|Thriller
  7 | Sabrina <span class="o">(</span>1995<span class="o">)</span>                     | Comedy|Romance
  8 | Tom and Huck <span class="o">(</span>1995<span class="o">)</span>                | Adventure|Children
  9 | Sudden Death <span class="o">(</span>1995<span class="o">)</span>                | Action
 10 | GoldenEye <span class="o">(</span>1995<span class="o">)</span>                   | Action|Adventure|Thriller
<span class="o">(</span>10 rows<span class="o">)</span>

<span class="nv">movies</span><span class="o">=</span><span class="c"># SELECT * FROM ratings LIMIT 10;</span>
 <span class="nb">id</span> | user_id | movie_id | rating |  created   
<span class="nt">----</span>+---------+----------+--------+------------
  1 |       1 |      307 |    3.5 | 1256677221
  2 |       1 |      481 |    3.5 | 1256677456
  3 |       1 |     1091 |    1.5 | 1256677471
  4 |       1 |     1257 |    4.5 | 1256677460
  5 |       1 |     1449 |    4.5 | 1256677264
  6 |       1 |     1590 |    2.5 | 1256677236
  7 |       1 |     1591 |    1.5 | 1256677475
  8 |       1 |     2134 |    4.5 | 1256677464
  9 |       1 |     2478 |      4 | 1256677239
 10 |       1 |     2840 |      3 | 1256677500
<span class="o">(</span>10 rows<span class="o">)</span>

<span class="nv">movies</span><span class="o">=</span><span class="c"># \q</span></code></pre></figure>

<p><strong>Conclusions</strong></p>

<p>We this we have complete this article, we have learn tons of new concepts, tools and information to create our own jobs that could run in our cluster and
now we have a database populated with data that we will be used in further examples.</p>

<p>If you like to download the scripts for this example you could grab it from <a href="https://github.com/LearningByExample/jobs-k8s" target="_blank">this github repository</a>.</p>

<p><em>Note : 22 Dec 2019 : I’ve update the SQL scripts to add some foreign keys and indexes to speed up queries on the database, they are updated in the repository</em></p>

<p><strong>Resources</strong></p>

<ul>
  <li><a href="https://grouplens.org/datasets/movielens/" target="_blank">https://grouplens.org/datasets/movielens/</a></li>
  <li><a href="https://doi.org/10.1145/2827872" target="_blank">F. Maxwell Harper and Joseph A. Konstan. 2015. The MovieLens Datasets: History and Context. ACM Transactions on Interactive Intelligent Systems (TiiS) 5, 4: 19:1–19:19.</a></li>
  <li><a href="https://github.com/bozaro/tech-db-lectures/tree/master/scripts/movielens" target="_blank">https://github.com/bozaro/tech-db-lectures/tree/master/scripts/movielens</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/" target="_blank">https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/</a></li>
  <li><a href="https://github.com/containers/buildah/blob/master/install.md" target="_blank">https://github.com/containers/buildah/blob/master/install.md</a></li>
  <li><a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/" target="_blank">https://docs.docker.com/install/linux/docker-ce/ubuntu/</a></li>
  <li><a href="https://forums.linuxmint.com/viewtopic.php?t=300469" target="_blank">https://forums.linuxmint.com/viewtopic.php?t=300469</a></li>
  <li><a href="https://microk8s.io/docs/registry-built-in" target="_blank">https://microk8s.io/docs/registry-built-in</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/configuration/secret/" target="_blank">https://kubernetes.io/docs/concepts/configuration/secret/</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/storage/volumes/" target="_blank">https://kubernetes.io/docs/concepts/storage/volumes/</a></li>
</ul>]]></content><author><name>Juan Medina</name></author><category term="Cloud" /><summary type="html"><![CDATA[We recently configure our own Kubernetes (K8s), cluster using MicroK8s and learn how we could use an operator for deploying a PostgreSQL Server within our cluster. Now we will learn how we could create k8s jobs that populate a database within our cluster.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/gears.jpg" /><media:content medium="image" url="http://juan-medina.com/assets/img/gears.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">PostgreSQL in Kubernetes</title><link href="http://juan-medina.com/2019/12/12/postgresql-k8s/" rel="alternate" type="text/html" title="PostgreSQL in Kubernetes" /><published>2019-12-12T00:00:00+00:00</published><updated>2019-12-12T00:00:00+00:00</updated><id>http://juan-medina.com/2019/12/12/postgresql-k8s</id><content type="html" xml:base="http://juan-medina.com/2019/12/12/postgresql-k8s/"><![CDATA[<p><a href="/2019/12/02/microk8s/" target="_blank">Last week</a> we manage to configure MicroK8s for having our own Kubernetes (k8s) cluster, so for learning a bit more on it we are going to learn how to install a <a href="https://www.postgresql.org/" target="_blank">PostgreSQL</a> server that will be run natively in our cloud.</p>

<p>For installing PostgreSQL we are going to use a k8s <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/" target="_blank">operator</a>, but first let’s understand what an operator is.</p>

<p>Operators are software extensions to k8s that make use of custom resources to manage applications and their components. K8s’ controllers concept lets you extend the cluster’s behaviour without modifying the code of k8s itself. Operators are clients of the k8s API that act as controllers for a Custom Resource.</p>

<p>So basically we extended the functionality of our cluster to easily deploy applications that could automatize how they are deployed and configure.</p>

<p>Why use a operator to deploy a database server? Because databases in a cloud are really hard, we need to provision storage, manage replication, 
credentials for the clients, and a dozen of complicated and delicate details, an operator will automatize all of this for us.</p>

<p>For this we are going to use this <a href="https://github.com/zalando/postgres-operator" target="_blank">postgres-operator</a>.</p>

<p><strong>Adding the Operator</strong></p>

<p>First we need to clone the repo:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>git clone git@github.com:zalando/postgres-operator.git</code></pre></figure>

<p><em>Note: Currently on version 1.2.0 of the operator there is a bug that is fixed but not released, we will patch the installation before.</em> Edit the file <em>manifests/postgres-operator.yaml</em> :</p>

<p>change</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml">        <span class="na">image</span><span class="pi">:</span> <span class="s">registry.opensource.zalan.do/acid/postgres-operator:v1.2.0</span></code></pre></figure>

<p>to:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml">        <span class="na">image</span><span class="pi">:</span> <span class="s">registry.opensource.zalan.do/acid/postgres-operator:latest</span></code></pre></figure>

<p><em>End Note</em></p>

<p>Now we will install the operator using the manifests:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">cd </span>postgres-operator
<span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> manifests/configmap.yaml  <span class="c"># configuration</span>
<span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> manifests/operator-service-account-rbac.yaml  <span class="c"># identity</span>
<span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> manifests/postgres-operator.yaml  <span class="c"># deployment</span></code></pre></figure>

<p>Now to check that the operator has started we could do:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pod <span class="nt">-l</span> <span class="nv">name</span><span class="o">=</span>postgres-operator
NAME                                 READY   STATUS    RESTARTS   AGE
postgres-operator-66cc575d9c-jfnlj   1/1     Running   0          20s</code></pre></figure>

<p><strong>Creating a Database</strong></p>

<p>Operators work with resource, when we provide to k8s a resource with the data for a register operator it
will use it to install it.</p>

<p>For example les create a database named <em>movies</em> with and admin user name <em>moviesdba</em> and a  user named <em>moviesuser</em>.
For this we will create a file that will could name movies-db.yml :</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">apiVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">acid.zalan.do/v1"</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">postgresql</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">movies-db-cluster</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">default</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">teamId</span><span class="pi">:</span> <span class="s2">"</span><span class="s">movies"</span>
  <span class="na">volume</span><span class="pi">:</span>
    <span class="na">size</span><span class="pi">:</span> <span class="s">1Gi</span>
  <span class="na">numberOfInstances</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">users</span><span class="pi">:</span>
    <span class="na">moviesdba</span><span class="pi">:</span>  <span class="c1"># database owner</span>
    <span class="pi">-</span> <span class="s">superuser</span>
    <span class="pi">-</span> <span class="s">createdb</span>
    <span class="na">moviesuser</span><span class="pi">:</span> <span class="pi">[]</span>  <span class="c1"># roles</span>
  <span class="na">databases</span><span class="pi">:</span>
    <span class="na">movies</span><span class="pi">:</span> <span class="s">moviesdba</span>  <span class="c1"># dbname: owner</span>
  <span class="na">postgresql</span><span class="pi">:</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">11"</span></code></pre></figure>

<p>Now let’s create our database using :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create <span class="nt">-f</span> movies-db.yml 
postgresql.acid.zalan.do/movies-db-cluster created</code></pre></figure>

<p>Now we we need to wait that our database is running, we could check until we get this output :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get postgresql
NAME                TEAM     VERSION   PODS   VOLUME   CPU-REQUEST   MEMORY-REQUEST   AGE    STATUS
movies-db-cluster   movies   11        2      1Gi                                     106s   Running</code></pre></figure>

<p>And the inspect the nodes four our database with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pods <span class="nt">-l</span> <span class="nv">application</span><span class="o">=</span>spilo <span class="nt">-L</span> spilo-role
NAME                  READY   STATUS    RESTARTS   AGE     SPILO-ROLE
movies-db-cluster-0   1/1     Running   0          6m56s   master
movies-db-cluster-1   1/1     Running   0          6m15s   replica</code></pre></figure>

<p>As we could see our database has a master and a replica running.</p>

<p><strong>Accessing our database</strong></p>

<p>For testing our database lest just use the postgresql client, we could install it with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>apt-get <span class="nb">install </span>postgresql-client  </code></pre></figure>

<p>We will login in our server with our moviesdba user so we need to get it password, we could get it with:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get secret moviesdba.movies-db-cluster.credentials <span class="nt">-o</span> <span class="s1">'jsonpath={.data.password}'</span> | <span class="nb">base64</span> <span class="nt">-d</span> 
9oYUFcamSKwjB5Yrg099glLHdqg8C1IkScRfd5TeHTisiuj23FQrx3YEW6fB3ctJ</code></pre></figure>

<p>The operator has configured our database server to only be accessed within the cluster so in order to test it 
we will forward the postgres port 5432 on our master to our localhost port 6432, this will run until we do <em>ctrl+c</em> :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl port-forward movies-db-cluster-0 6432:5432                        
Forwarding from 127.0.0.1:6432 -&gt; 5432
Forwarding from <span class="o">[</span>::1]:6432 -&gt; 5432</code></pre></figure>

<p>Finally we can connect to our database with with the provide user and password using psql in another
shell :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>psql <span class="nt">-h</span> localhost <span class="nt">-p</span> 6432 <span class="nt">-U</span> moviesdba movies                                                            
Password <span class="k">for </span>user moviesdba: 
psql <span class="o">(</span>11.6 <span class="o">(</span>Ubuntu 11.6-1.pgdg18.04+1<span class="o">)</span>, server 11.5 <span class="o">(</span>Ubuntu 11.5-1.pgdg18.04+1<span class="o">))</span>
SSL connection <span class="o">(</span>protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off<span class="o">)</span>
Type <span class="s2">"help"</span> <span class="k">for </span>help.

<span class="nv">movies</span><span class="o">=</span><span class="c"># \list</span>
                                  List of databases
   Name    |   Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
<span class="nt">-----------</span>+-----------+----------+-------------+-------------+-----------------------
 movies    | moviesdba | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres  | postgres  | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres  | UTF8     | en_US.UTF-8 | en_US.UTF-8 | <span class="o">=</span>c/postgres          +
           |           |          |             |             | <span class="nv">postgres</span><span class="o">=</span>CTc/postgres
 template1 | postgres  | UTF8     | en_US.UTF-8 | en_US.UTF-8 | <span class="o">=</span>c/postgres          +
           |           |          |             |             | <span class="nv">postgres</span><span class="o">=</span>CTc/postgres
<span class="o">(</span>4 rows<span class="o">)</span>

<span class="nv">movies</span><span class="o">=</span><span class="c"># \du</span>
                                          List of roles
 Role name  |                         Attributes                         |       Member of        
<span class="nt">------------</span>+------------------------------------------------------------+------------------------
 admin      | Create DB, Cannot login                                    | <span class="o">{</span>moviesdba,moviesuser<span class="o">}</span>
 moviesdba  | Superuser, Create DB                                       | <span class="o">{}</span>
 moviesuser |                                                            | <span class="o">{}</span>
 postgres   | Superuser, Create role, Create DB, Replication, Bypass RLS | <span class="o">{}</span>
 robot_zmon | Cannot login                                               | <span class="o">{}</span>
 standby    | Replication                                                | <span class="o">{}</span>
 zalandos   | Create DB, Cannot login                                    | <span class="o">{}</span>

<span class="nv">movies</span><span class="o">=</span><span class="c"># \q</span></code></pre></figure>

<p>We have verify that our users are setup in our database, now we are ready for more steps that I’ll try to cover
in the following days creating an application that run on our k8s cluster and connect to our database.</p>

<p>Finally if we look to look more about how this operator works, how we could scale our replicas and repair our 
database we should take a look to the operator <a href="https://postgres-operator.readthedocs.io/en/latest/#overview-of-involved-entities" target="_blank">documentation</a>.</p>

<p><strong>References</strong></p>

<ul>
  <li><a href="https://github.com/zalando/postgres-operator/blob/master/docs/quickstart.md" target="_blank">https://github.com/zalando/postgres-operator/blob/master/docs/quickstart.md</a></li>
  <li><a href="https://github.com/ubuntu/microk8s/issues/695" target="_blank">https://github.com/ubuntu/microk8s/issues/695</a></li>
  <li><a href="https://grouplens.org/datasets/movielens/" target="_blank">https://grouplens.org/datasets/movielens/</a></li>
  <li><a href="https://github.com/helm/helm/issues/6359" target="_blank">https://github.com/helm/helm/issues/6359</a></li>
  <li><a href="https://webcloudpower.com/use-kubernetics-locally-with-microk8s/" target="_blank">https://webcloudpower.com/use-kubernetics-locally-with-microk8s/</a></li>
  <li><a href="https://postgres-operator.readthedocs.io" target="_blank">https://postgres-operator.readthedocs.io</a></li>
</ul>]]></content><author><name>Juan Medina</name></author><category term="Cloud" /><summary type="html"><![CDATA[Last week we manage to configure MicroK8s for having our own Kubernetes (k8s) cluster, so for learning a bit more on it we are going to learn how to install a PostgreSQL server that will be run natively in our cloud.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/postgresql_k8s.png" /><media:content medium="image" url="http://juan-medina.com/assets/img/postgresql_k8s.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Configuring MicroK8s</title><link href="http://juan-medina.com/2019/12/02/microk8s/" rel="alternate" type="text/html" title="Configuring MicroK8s" /><published>2019-12-02T00:00:00+00:00</published><updated>2019-12-02T00:00:00+00:00</updated><id>http://juan-medina.com/2019/12/02/microk8s</id><content type="html" xml:base="http://juan-medina.com/2019/12/02/microk8s/"><![CDATA[<p><a href="https://kubernetes.io/" target="_blank">Kubernetes (K8s)</a> has become the defacto standard for containers orchestration, and since I like to do 
some more example about it I was needed to setup it on my system.</p>

<p>In the past I’ve use <a href="https://minikube.sigs.k8s.io/" target="_blank">minikube</a> for running a local installation of k8s on my environment but since I recently switched to <a href="https://linuxmint.com/" target="_blank">Linux Mint</a> for most of my development I decided to to use <a href="https://microk8s.io/" target="_blank">MicroK8s</a>.</p>

<p>MicroK8s is a small, fast and very simple to install, even for clustering, k8s in a single package, created by <a href="https://canonical.com/" target="_blank">Canonical</a> the company behind <a href="https://ubuntu.com/" target="_blank">Ubuntu</a>.</p>

<p>One the benefits for using Microk8s on Linux is that its run natively (without virtualization), and since Mint is base on Ubuntu this installation is even easer. On Windows and Mac you will need to use <a href="https://multipass.run/" target="_blank">Micropass</a> to run a virtual machine with Ubuntu first.</p>

<p>Minikube uses virtualization in all of the OS that runs, even in linux, and for this is more resource intensive, however is developed by the same team that develop k8s, regardless both are <a href="https://www.cncf.io/certification/software-conformance/" target="_blank">CNF certified</a> k8s solutions. MicroK8s tracks upstream releases of k8s to keep itself up to date with k8s development.</p>

<p><strong>Installing Snap</strong></p>

<p>We will install microk8s using an <a href="https://snapcraft.io/" target="_blank">Snap</a>.</p>

<p><a href="https://snapcraft.io/" target="_blank">Snaps</a> are linux app packages for desktop, cloud and IoT that are easy to install, secure and cross-platform, they are self-contained, including dependencies, and they could be uninstalled without affecting the rest of your system.</p>

<p>From Ubuntu 14.04 LTS onwards snap is already installed, for previous versions we need to install it with apt :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>apt update
<span class="nv">$ </span><span class="nb">sudo </span>apt <span class="nb">install </span>snapd</code></pre></figure>

<p>After installed we could just check the version that we have using :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>snap version 
snap       2.42.1+18.04
snapd      2.42.1+18.04
series     16
linuxmint  19.2
kernel     4.15.0-72-generic</code></pre></figure>

<p><strong>Installing MicroK8s</strong></p>

<p>Now that we have snap we could install MicroK8s just doing :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>snap <span class="nb">install </span>microk8s <span class="nt">--classic</span>
microk8s v1.16.3 from Canonical✓ installed</code></pre></figure>

<p>In order tu use MicroK8s we need either to use sudo or add our <strong>username</strong> to the microk8s group:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo </span>usermod <span class="nt">-a</span> <span class="nt">-G</span> microk8s username</code></pre></figure>

<p>After login in back again we could not check the status of our k8s cluster with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.status
microk8s is running</code></pre></figure>

<p>If we want to stop or start our cluster we could do it using <code class="language-plaintext highlighter-rouge">microk8s.stop</code> or <code class="language-plaintext highlighter-rouge">microk8s.start</code> whenever we need.</p>

<p>MicroK8s provide several commands to manage our cluster, but embed within the installation for example if we want
to see what is running in our cluster using <a href="https://kubernetes.io/docs/reference/kubectl/overview/" target="_blank">kubectl</a> we need to do:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get all <span class="nt">--all-namespaces</span>
NAMESPACE   NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>   AGE
default     service/kubernetes   ClusterIP   10.152.183.1   &lt;none&gt;        443/TCP   17m</code></pre></figure>

<p><strong>Adding addons</strong></p>

<p>Microk8s installation is very minimal if we like to have a dashboard we need to enable as an <a href="https://microk8s.io/docs/addons" target="_blank">addon</a>, 
they are several of them so we will enable some basics ones :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.enable dashboard dns registry fluentd</code></pre></figure>

<p><strong>Using the dashboard</strong></p>

<p>For using the dashboard we need first to find the IP address in our cluster, for this we could just do :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get service kubernetes-dashboard <span class="nt">-n</span> kube-system

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>   AGE
kubernetes-dashboard   ClusterIP   10.152.183.75   &lt;none&gt;        443/TCP   28m</code></pre></figure>

<p>No we know that our dashboard is running at <a href="https://10.152.183.75" target="_blank">https://10.152.183.75</a>, however we need a access token for using it, 
we could get the token with the following commands:</p>

<p>Get from secrets the default-token secret name:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get secret <span class="nt">-n</span> kube-system | <span class="nb">grep </span>default-token
default-token-gkjsl                              kubernetes.io/service-account-token</code></pre></figure>

<p>Get the token, that is the value for the secret name:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl describe secret default-token-gkjsl <span class="nt">-n</span> kube-system </code></pre></figure>

<p>Now with this token we can logon into our dashboard:</p>

<p><a href="/assets/img/captures/kubernetes_dashboard_logon.jpg" target="_blank"><img src="/assets/img/captures/kubernetes_dashboard_logon.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>We will choose to use a token and we will use the one that we just get, and finally we will see our dashboard:</p>

<p><a href="/assets/img/captures/kubernetes_dashboardjpg" target="_blank"><img src="/assets/img/captures/kubernetes_dashboard.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p><strong>Adding a demo application</strong></p>

<p>For configuring further our MicroK8s we will deploy a new application with two replicas:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create deployment microbot <span class="nt">--image</span><span class="o">=</span>dontrebootme/microbot:v1
deployment.apps/microbot created
<span class="nv">$ </span>microk8s.kubectl scale deployment microbot <span class="nt">--replicas</span><span class="o">=</span>2</code></pre></figure>

<p>Now we could check that we have actually two pods runing with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get pod <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>microbot
NAME                        READY   STATUS    RESTARTS   AGE
microbot-5bb7fd7f5d-dshtf   1/1     Running   0          2m30s
microbot-5bb7fd7f5d-mdcvs   1/1     Running   0          3m30s</code></pre></figure>

<p>To access to our applicacion we will expose it trough a NodePort :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl expose deployment microbot <span class="nt">--type</span><span class="o">=</span>NodePort <span class="nt">--port</span><span class="o">=</span>80 <span class="se">\</span>
    <span class="nt">--name</span><span class="o">=</span>microbot-service
service/microbot-service exposed</code></pre></figure>

<p>We could check our services with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl get service <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>microbot                                      
NAME               TYPE       CLUSTER-IP       EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>        AGE
microbot-service   NodePort   10.152.183.136   &lt;none&gt;        80:31771/TCP   2m27s</code></pre></figure>

<p>And since is a NodePort we could use with the IP of our local machine at http://localhost :</p>

<p><a href="/assets/img/captures/microbot.jpg" target="_blank"><img src="/assets/img/captures/microbot.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>If we refresh the page invalidating cache, shift+ctrl+r on Firefox, we will get different containers for this application.</p>

<p><strong>Configuring Kibana</strong></p>

<p>Since we original add the addon fluentd we have a Elasticsearch + Kibana for our pods logs, but we need the URL Kibana, 
listing the cluster info will display urls for the addons that we have installed:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl cluster-info

Kubernetes master is running at https://127.0.0.1:16443
Elasticsearch is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
Heapster is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/heapster/proxy
Kibana is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/kibana-logging/proxy
CoreDNS is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Grafana is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
InfluxDB is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/monitoring-influxdb:http/proxy</code></pre></figure>

<p>However before accessing we need the user and password for managing this cluster this could obtain with the command</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://127.0.0.1:16443
  name: microk8s-cluster
contexts:
- context:
    cluster: microk8s-cluster
    user: admin
  name: microk8s
current-context: microk8s
kind: Config
preferences: <span class="o">{}</span>
<span class="nb">users</span>:
- name: admin
  user:
    password: aRZbT45aNLB3AYUaz2rVa2RtzACoUR3zOGZoeWQ4c016SjlJSUt6bXNlRT0K
    username: admin</code></pre></figure>

<p>Now we could visit our Kibana in the url https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/kibana-logging/proxy and use our
admin user and password.</p>

<p><a href="/assets/img/captures/kibana_main.jpg" target="_blank"><img src="/assets/img/captures/kibana_main.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>However if we like to browse the data when clicking on Discover we will get redirect to create an index, the pattern will be <code class="language-plaintext highlighter-rouge">logstash*</code>:</p>

<p><a href="/assets/img/captures/kibana_index.jpg" target="_blank"><img src="/assets/img/captures/kibana_index.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>And finally we will set our <em>Time Filter field name</em> to be <code class="language-plaintext highlighter-rouge">@timestamp</code>:</p>

<p><a href="/assets/img/captures/kibana_index_2.jpg" target="_blank"><img src="/assets/img/captures/kibana_index_2.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now we could click on <em>Create index pattern</em> to complete our index creation.</p>

<p>For testing our index lets deploy another service to our cluster, one application that output on the logs random name into the stdout :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl create deployment random-generator <span class="nt">--image</span><span class="o">=</span>chriscmsoft/random-generator:latest
deployment.apps/random-generator created
<span class="nv">$ </span>microk8s.kubectl scale deployment random-generator <span class="nt">--replicas</span><span class="o">=</span>2
<span class="nv">$ </span>microk8s.kubectl get all <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>random-generator                                       
NAME                                    READY   STATUS    RESTARTS   AGE
pod/random-generator-7b6c47d877-plbsg   1/1     Running   0          54s
pod/random-generator-7b6c47d877-wh4jj   1/1     Running   0          11s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/random-generator   2/2     2            2           54s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/random-generator-7b6c47d877   2         2         2       54</code></pre></figure>

<p>We could check the log of this service with :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl logs <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>random-generator
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Gara, The Magnificent"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Gairvol, The Puny"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Zyvrieliar, The Swift"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Gara, The Magnificent"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xaymmynth, Champion Of The Blue"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Chynnyrth, Champion Of Men"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xaerlulth, The Warm"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xarsot, The Tyrant"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xaerlulth, The Warm"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xaerlulth, The Warm"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Siovaeloi, Protector Of The Weak"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Siovaeloi, Protector Of The Weak"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Zaellyt, The Gifted One"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Gorsolun, Warmheart"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Vugeo, The Youngling"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Vugeo, The Youngling"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xughus, The Tall"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Xaerlulth, The Warm"</span><span class="o">}</span>
<span class="o">{</span><span class="s2">"name"</span>: <span class="s2">"Gorsolun, Warmheart"</span><span class="o">}</span></code></pre></figure>

<p>Now back into Kibana we could just do a simple filter <code class="language-plaintext highlighter-rouge">kubernetes.container_name: random-generator</code> and select the <code class="language-plaintext highlighter-rouge">docker</code> and <code class="language-plaintext highlighter-rouge">log</code> fields to view the logs
of this application.</p>

<p><a href="/assets/img/captures/kibana_search.jpg" target="_blank"><img src="/assets/img/captures/kibana_search.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p><strong>Configuring Grafana</strong></p>

<p>As we did for Kibana we could grab the url for Grafana for the cluster info, this case is https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy , we will need our admin user and password as we needed before.</p>

<p><a href="/assets/img/captures/grafana_login.jpg" target="_blank"><img src="/assets/img/captures/grafana_login.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>MicroK8s has already configure some dashboard we could choose <em>Pods</em> and then select our <em>default</em> namespace an any of our random-generator pods to look some of
the graphs.</p>

<p><a href="/assets/img/captures/grafana_pod.jpg" target="_blank"><img src="/assets/img/captures/grafana_pod.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>The defaul Pod dashboard allow us to just select one pod let modify it to allow to choose several.</p>

<p>Click on the gear icon on Grafana and choose <em>Templating</em> :</p>

<p><a href="/assets/img/captures/grafana_templating.jpg" target="_blank"><img src="/assets/img/captures/grafana_templating.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Now on the field <em>$podname</em> we click on the <em>Edit</em> button.</p>

<p><a href="/assets/img/captures/grafana_podname.jpg" target="_blank"><img src="/assets/img/captures/grafana_podname.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>We need to activate <em>Multi-value</em> on the <em>Selection Options</em> and then click now <em>Update</em>, sometimes the field Data Source get set to blank we need to set it to <em>influxdb-dataset</em>.</p>

<p>With this now could close the Templating and choose all of the pods for our random-generator to get an update graph with the sum of both pods.</p>

<p><a href="/assets/img/captures/grafana_all_pods.jpg" target="_blank"><img src="/assets/img/captures/grafana_all_pods.jpg" alt="d" style="width:80%; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p><strong>Clean up</strong></p>

<p>Now that we have test some of the tools in our MicroK8s installation we could delete the applications that we do not need anymore :</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>microk8s.kubectl delete all <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>microbot
pod <span class="s2">"microbot-5bb7fd7f5d-dshtf"</span> deleted
pod <span class="s2">"microbot-5bb7fd7f5d-mdcvs"</span> deleted
service <span class="s2">"microbot-service"</span> deleted
deployment.apps <span class="s2">"microbot"</span> deleted
replicaset.apps <span class="s2">"microbot-5bb7fd7f5d"</span> deleted

<span class="nv">$ </span>microk8s.kubectl delete all <span class="nt">--selector</span><span class="o">=</span><span class="nv">app</span><span class="o">=</span>random-generator 
pod <span class="s2">"random-generator-7b6c47d877-plbsg"</span> deleted
pod <span class="s2">"random-generator-7b6c47d877-wh4jj"</span> deleted
deployment.apps <span class="s2">"random-generator"</span> deleted
replicaset.apps <span class="s2">"random-generator-7b6c47d877"</span> deleted</code></pre></figure>

<p><strong>Conclusions</strong></p>

<p>MicroK8s is very easy to install it took just seconds to download and get it ready, it provide many already config addons that allows use it easily and
kickstart a k8s cluster very fast with everything that we need, and is really slim and fast so a really good candidate for IoT deployments.</p>

<p>I’ve enjoy a lot this installation and I’m looking forward to learn more of k8s using MicroK8s, they are several addons that I’ven’t tryout yet such <a href="https://istio.io/" target="_blank">Istio</a> and <a href="https://knative.dev/" target="_blank">Knative</a>, maybe in a nearby future.</p>

<p><strong>Reference</strong></p>

<ul>
  <li><a href="https://tutorials.ubuntu.com/tutorial/install-a-local-kubernetes-with-microk8s" target="_blank">https://tutorials.ubuntu.com/tutorial/install-a-local-kubernetes-with-microk8s</a></li>
  <li><a href="https://kubernetes.io/docs/reference/kubectl/cheatsheet/" target="_blank">https://kubernetes.io/docs/reference/kubectl/cheatsheet/</a></li>
  <li><a href="https://medium.com/@chris_linguine/how-to-monitor-distributed-logs-in-kubernetes-with-the-efk-stack-1218a565ce0c" target="_blank">https://medium.com/@chris_linguine/how-to-monitor-distributed-logs-in-kubernetes-with-the-efk-stack-1218a565ce0c</a></li>
</ul>]]></content><author><name>Juan Medina</name></author><category term="Cloud" /><summary type="html"><![CDATA[Kubernetes (K8s) has become the defacto standard for containers orchestration, and since I like to do some more example about it I was needed to setup it on my system.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/microk8s.png" /><media:content medium="image" url="http://juan-medina.com/assets/img/microk8s.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Microservices Error Handling</title><link href="http://juan-medina.com/2018/02/10/microservices-errors/" rel="alternate" type="text/html" title="Microservices Error Handling" /><published>2018-02-10T00:00:00+00:00</published><updated>2018-02-10T00:00:00+00:00</updated><id>http://juan-medina.com/2018/02/10/microservices-errors</id><content type="html" xml:base="http://juan-medina.com/2018/02/10/microservices-errors/"><![CDATA[<p>Some days ago I was discusing with my colleage <a href="https://twitter.com/juanantoniobm">Juan Antonio Breña</a> how you could end breaking your control flow when handling errors in a Microservice, specially relaying in mechanism such <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html" target="_blank"><code class="language-plaintext highlighter-rouge">ControllerAdvice</code></a> and <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html" target="_blank"><code class="language-plaintext highlighter-rouge">ExceptionHandler</code></a> when create Spring Boot Microservices in Java.</p>

<p>I told Juan that we may need to handle this probllem differently, so I decide to create a new project in our <a href="https://github.com/LearningByExample" target="_blank">Learning By Example</a> GitHub meta group: <a href="https://github.com/LearningByExample/MicroservicesErrorHandling" target="_blank">Microservices Error Handling</a></p>

<p>The idea behind that project is to check how we could avoid to use <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html"><code class="language-plaintext highlighter-rouge">ControllerAdvice</code></a> and <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html"><code class="language-plaintext highlighter-rouge">ExceptionHandler</code></a>, and even checked exceptions when handling errors in our Microservices.</p>

<p>Checked exceptions some times could leak the implementation details of our components, and they could even break our control flow.</p>

<p>Spring <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html"><code class="language-plaintext highlighter-rouge">ControllerAdvice</code></a> or <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html"><code class="language-plaintext highlighter-rouge">ExceptionHandler</code></a> could become the ultimate GOTOs, that some people my use to get errors and sent back to the consumers of the Microservice, jumping out on the execution of our business logic.</p>

<p>The approach described in the example is to handle errors as part of our business logic, avoiding unnecessary lost of our control flow.</p>

<p>This example is not about stop using <a href="https://docs.spring.io/spring/docs/4.3.15.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html"><code class="language-plaintext highlighter-rouge">ControllerAdvice</code></a>, we may need to still using it to handle unexpected Runtime Exceptions, however relaying on it for handling our business errors isn’t ideal.</p>

<p>Approach like the one in this example may become a better way to do it, however this is not the unique, neither definitive, solution.</p>

<p>We have created one wrapper class to support this concept named <code class="language-plaintext highlighter-rouge">Result&lt;type&gt;</code>, this class has two creation methods.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Result.create(object)</code> : That will create a wrapper containing the data to be returned.</li>
  <li><code class="language-plaintext highlighter-rouge">Result.error(object)</code> : That will create a wrapper containing an error.</li>
</ul>

<p>How to use this class is simple, for example:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java">  <span class="kd">public</span> <span class="nc">Result</span> <span class="nf">get</span><span class="o">(</span><span class="kt">int</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span><span class="o">)</span> <span class="k">return</span> <span class="nc">Result</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="k">new</span> <span class="nc">Customer</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="s">"super customer"</span><span class="o">));</span>
    <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">id</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span>
      <span class="k">return</span> <span class="nc">Result</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="k">new</span> <span class="nc">BadParameters</span><span class="o">(</span><span class="s">"bad parameters"</span><span class="o">));</span>
    <span class="k">else</span>
      <span class="k">return</span> <span class="nc">Result</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="k">new</span> <span class="nc">NotFound</span><span class="o">(</span><span class="s">"customer not found"</span><span class="o">));</span>
  <span class="o">}</span></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">BadParameters</code> and <code class="language-plaintext highlighter-rouge">NotFound</code> are entities for our business logic that contain the desired information for handling these responses.</p>

<p>For handling the result we could just use the methods <code class="language-plaintext highlighter-rouge">isError()</code> and <code class="language-plaintext highlighter-rouge">getData()</code>.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java">  <span class="nc">Result</span> <span class="n">result</span> <span class="o">=</span> <span class="n">operation</span><span class="o">();</span>

  <span class="k">if</span><span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">isError</span><span class="o">()){</span>
    <span class="c1">//do something with the error</span>
  <span class="o">}</span><span class="k">else</span><span class="o">{</span>
    <span class="nc">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="na">getValue</span><span class="o">();</span>
  <span class="o">}</span></code></pre></figure>

<p>Finally we use in our mapping a generic ResponseEntity we could return the wrapper value without needing to understand what contains.</p>

<p>For example:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java">  <span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/customer/{id}"</span><span class="o">)</span>
    <span class="kd">public</span> <span class="nc">ResponseEntity</span><span class="o">&lt;?&gt;</span> <span class="n">get</span><span class="o">(</span><span class="nd">@PathVariable</span><span class="o">()</span> <span class="kt">int</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">final</span> <span class="nc">Result</span> <span class="n">result</span> <span class="o">=</span> <span class="n">customerService</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
    <span class="kd">final</span> <span class="nc">HttpStatus</span> <span class="n">status</span> <span class="o">=</span> <span class="n">getStatus</span><span class="o">(</span><span class="n">result</span><span class="o">);</span>

    <span class="k">return</span> <span class="k">new</span> <span class="nc">ResponseEntity</span><span class="o">&lt;&gt;(</span><span class="n">result</span><span class="o">.</span><span class="na">getValue</span><span class="o">(),</span> <span class="n">status</span><span class="o">);</span>
  <span class="o">}</span></code></pre></figure>

<p>Since we may like to return different HTTP status, based on the error contained, we could create a helper that use the type of the class held on the wrapper.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java">  <span class="kd">private</span> <span class="nc">HttpStatus</span> <span class="nf">getStatus</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Result</span> <span class="n">result</span><span class="o">){</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">isError</span><span class="o">())</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">getValue</span><span class="o">()</span> <span class="k">instanceof</span> <span class="nc">NotFound</span><span class="o">)</span>
        <span class="k">return</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">NOT_FOUND</span><span class="o">;</span>
      <span class="k">else</span>
        <span class="k">return</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">BAD_REQUEST</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="k">return</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">OK</span><span class="o">;</span>
  <span class="o">}</span></code></pre></figure>

<p>To just run the example microservice you could just do from the command line:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">  <span class="nv">$ </span>mvnw spring-boot run</code></pre></figure>

<p>To test the output when sending a correct customer id:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">  <span class="nv">$ </span>curl <span class="nt">-i</span> http://localhost:8080/customer/1
  HTTP/1.1 200
  X-Application-Context: application
  Content-Type: application/json<span class="p">;</span><span class="nv">charset</span><span class="o">=</span>UTF-8
  Transfer-Encoding: chunked
  Date: Wed, 31 Jan 2018 09:33:01 GMT

  <span class="o">{</span>
    <span class="s2">"data"</span> : <span class="s2">"super customer"</span>,
    <span class="s2">"id"</span> : 1
  <span class="o">}</span></code></pre></figure>

<p>To test the output when sending a invalid customer id</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">  <span class="nv">$curl</span> <span class="nt">-i</span> http://localhost:8080/customer/-1
  HTTP/1.1 400
  X-Application-Context: application
  Content-Type: application/json<span class="p">;</span><span class="nv">charset</span><span class="o">=</span>UTF-8
  Transfer-Encoding: chunked
  Date: Wed, 31 Jan 2018 09:34:03 GMT
  Connection: close

  <span class="o">{</span>
    <span class="s2">"message"</span> : <span class="s2">"bad parameters"</span>
  <span class="o">}</span></code></pre></figure>

<p>To test the output when sending a not found customer id</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">  <span class="nv">$ </span>curl <span class="nt">-i</span> http://localhost:8080/customer/2
  HTTP/1.1 404
  X-Application-Context: application
  Content-Type: application/json<span class="p">;</span><span class="nv">charset</span><span class="o">=</span>UTF-8
  Transfer-Encoding: chunked
  Date: Wed, 31 Jan 2018 09:34:48 GMT

  <span class="o">{</span>
    <span class="s2">"message"</span> : <span class="s2">"customer not found"</span>
  <span class="o">}</span></code></pre></figure>

<p>The full example could be found under this repository:</p>

<p><a href="https://github.com/LearningByExample/MicroservicesErrorHandling" target="_blank">https://github.com/LearningByExample/MicroservicesErrorHandling</a></p>

<p>I don’t know if we definetly will be using this approach however I think I may rewrite it in Kotlin in somepoint.</p>]]></content><author><name>Juan Medina</name></author><category term="Programming" /><summary type="html"><![CDATA[Some days ago I was discusing with my colleage Juan Antonio Breña how you could end breaking your control flow when handling errors in a Microservice, specially relaying in mechanism such ControllerAdvice and ExceptionHandler when create Spring Boot Microservices in Java.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/glass-1818065_1920.jpg" /><media:content medium="image" url="http://juan-medina.com/assets/img/glass-1818065_1920.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">My First Book is Published</title><link href="http://juan-medina.com/2018/02/09/book/" rel="alternate" type="text/html" title="My First Book is Published" /><published>2018-02-09T08:00:00+00:00</published><updated>2018-02-09T08:00:00+00:00</updated><id>http://juan-medina.com/2018/02/09/book</id><content type="html" xml:base="http://juan-medina.com/2018/02/09/book/"><![CDATA[<p>I’m very proud to announce that my first book, Hands-On Microservices with Kotlin,  have been published by <a href="https://www.packtpub.com/web-development/microservices-kotlin" target="_blank">Packt</a>.</p>

<p>A book where I try to explain how to design, build and deploy Microservices using Spring Framework 5.0 and Spring Boot 2.0.</p>

<p>From understanding the architecture principles, to testing, monitoring or even scaling Microservices and including up-to-date topics such cloud native and reactive Microservices.</p>

<p><a href="https://www.packtpub.com/web-development/microservices-kotlin" target="_blank"><img src="/assets/img/B08531_Cover_0_small.png" alt="d" style="width:300px; display:block; margin-left:auto; margin-right:auto;" /></a></p>

<p>Writing a book has been one of the more complicated things that I’ve ever did, and at the same time one of the most grateful experiences.</p>

<p>This is one of my biggest personal achievements, especially considering that three years ago I was a manager that was disconnected from technology. It’s been a really big journey that finally drive me back where I love to be in.</p>

<p>For that reason I’ve created a presentation for a tech-talk that I did at work, covering some of my history and some of the technologies on the book, this presentation is available, and the example source code, on this GitHub repository:</p>

<p>https://github.com/juan-medina/ReactiveKotlin</p>

<p>I don’t know what else I’ll do in the future, but I’ll try again to enjoy each moment of it, as I did meanwhile I wrote the book.</p>]]></content><author><name>Juan Medina</name></author><category term="Programming" /><summary type="html"><![CDATA[I’m very proud to announce that my first book, Hands-On Microservices with Kotlin, have been published by Packt.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/B08531_Cover_0.png" /><media:content medium="image" url="http://juan-medina.com/assets/img/B08531_Cover_0.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Native Serverless Kotlin</title><link href="http://juan-medina.com/2017/07/30/kotlin-native-serverless/" rel="alternate" type="text/html" title="Native Serverless Kotlin" /><published>2017-07-30T09:00:00+01:00</published><updated>2017-07-30T09:00:00+01:00</updated><id>http://juan-medina.com/2017/07/30/kotlin-native-serverless</id><content type="html" xml:base="http://juan-medina.com/2017/07/30/kotlin-native-serverless/"><![CDATA[<p>Last week we do an <a href="https://juan-medina.com/2017/07/21/kotlin-serverless/" target="_blank">example</a> of a Kotlin Serverless Function using Apache OpenWhisk, however since we using Kotlin we could make our example a native application that will not run in the JVM.</p>

<p>You could find this complete example in <a href="https://github.com/LearningByExample/KotlinNativeServerless" target="_blank">this repository</a>.</p>

<p>We will need as the week before to have OpenWhisk, and we will require again <a href="http://vagrantup.com/" target="_blank">Vagrant</a> and <a href="https://www.virtualbox.org/wiki/Downloads" target="_blank">Virtual Box</a>, so install those first.</p>

<p>To setup the environment just:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>git clone <span class="nt">--depth</span><span class="o">=</span>1 https://github.com/apache/incubator-openwhisk.git openwhisk
  <span class="nv">$ </span><span class="nb">cd </span>openwhisk/tools/vagrant
  <span class="nv">$ </span>./hello</code></pre></figure>

<p>Now we could ssh to the machine with:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>vagrant ssh</code></pre></figure>

<p>To test the action directly just using the docker that I’ve upload to <a href="https://hub.docker.com/r/jamedina/kotlin-native-fibonacci/" target="_blank">docker hub</a>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>wsk action create native-fibonacci <span class="nt">--docker</span> jamedina/kotlin-native-fibonacci</code></pre></figure>

<p>To test the new action just run:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>wsk action invoke <span class="nt">--result</span> native-fibonacci <span class="nt">--param</span> numbers 5</code></pre></figure>

<p>This will output something like:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="w">  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="mi">1</span><span class="p">,</span><span class="w">
        </span><span class="mi">1</span><span class="p">,</span><span class="w">
        </span><span class="mi">2</span><span class="p">,</span><span class="w">
        </span><span class="mi">3</span><span class="p">,</span><span class="w">
        </span><span class="mi">5</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span></code></pre></figure>

<p>OpenWhisk allow us to use a docker that contain a binary that will be execute when an action is invoke, to do it so the binary will receive 1 argument with a JSON string and it need to return a JSON string in a single line.</p>

<p>So to create our function first we need to have <a href="https://github.com/JetBrains/kotlin-native" target="_blank">kotlin-native</a> installed and configured:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>git clone <span class="nt">--depth</span> 1 https://github.com/JetBrains/kotlin-native.git /kotlin-native
  <span class="nv">$ </span><span class="nb">cd</span> /kotlin-native
  <span class="nv">$ </span>./gradlew dependencies:update
  <span class="nv">$ </span>./gradlew dist
  <span class="nv">$ </span><span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"/kotlin-native/dist/bin:</span><span class="nv">$PATH</span><span class="s2">"</span>
  <span class="nv">$ </span>kotlinc</code></pre></figure>

<p>The last line will raise and error since we haven provide any parameters to the compiler but we just do that in order to download the compiler dependencies and toolchain.</p>

<p>Now we will create our project, kotlin native use a gradle plugin that we initial setup as:</p>

<figure class="highlight"><pre><code class="language-gradle" data-lang="gradle">  <span class="k">buildscript</span> <span class="o">{</span>
      <span class="k">repositories</span> <span class="o">{</span>
          <span class="n">mavenCentral</span><span class="o">()</span>
          <span class="n">maven</span> <span class="o">{</span>
              <span class="n">url</span> <span class="s2">"https://dl.bintray.com/jetbrains/kotlin-native-dependencies"</span>
          <span class="o">}</span>
      <span class="o">}</span>

      <span class="k">dependencies</span> <span class="o">{</span>
          <span class="n">classpath</span> <span class="s2">"org.jetbrains.kotlin:kotlin-native-gradle-plugin:+"</span>
      <span class="o">}</span>
  <span class="o">}</span>

  <span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">'konan'</span>

  <span class="n">konanArtifacts</span> <span class="o">{</span>
      <span class="n">fibonacci</span> <span class="o">{</span>
      <span class="o">}</span>
  <span class="o">}</span></code></pre></figure>

<p>We need as well to specify where is kotlin-native installed using the gradle.properties:</p>

<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="w">  </span><span class="py">konan.home</span><span class="p">=</span><span class="s">/kotlin-native/dist</span></code></pre></figure>

<p>now lets just create one simple main in the folder src/main.kt</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">numbers</span><span class="p">:</span> <span class="nc">Long</span><span class="p">):</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">Long</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span> <span class="p">==</span> <span class="mi">0L</span><span class="p">)</span> <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">0L</span><span class="p">)</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span> <span class="p">==</span> <span class="mi">1L</span><span class="p">)</span> <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">1L</span><span class="p">)</span>

    <span class="kd">var</span> <span class="py">previous</span> <span class="p">=</span> <span class="mi">1L</span>
    <span class="kd">var</span> <span class="py">current</span> <span class="p">=</span> <span class="mi">1L</span>
    <span class="kd">var</span> <span class="py">temp</span><span class="p">:</span> <span class="nc">Long</span>

    <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">1L</span><span class="p">,</span> <span class="mi">1L</span><span class="p">)</span> <span class="p">+</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="p">(</span><span class="n">numbers</span> <span class="p">-</span> <span class="mi">2</span><span class="p">)).</span><span class="nf">map</span> <span class="p">{</span>
      <span class="n">temp</span> <span class="p">=</span> <span class="n">current</span> <span class="p">+</span> <span class="n">previous</span>
      <span class="n">previous</span> <span class="p">=</span> <span class="n">current</span>
      <span class="n">current</span> <span class="p">=</span> <span class="n">temp</span>
      <span class="n">current</span>
    <span class="p">}.</span><span class="nf">toList</span><span class="p">().</span><span class="nf">toTypedArray</span><span class="p">()</span>
  <span class="p">}</span>

  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="mi">5</span><span class="p">).</span><span class="nf">forEach</span><span class="p">(</span><span class="o">::</span><span class="n">println</span><span class="p">)</span></code></pre></figure>

<p>Now to compile simple :</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>gradlew clean build</code></pre></figure>

<p>We use clean because sometimes gradle will not compile the files even with the files changed.</p>

<p>Then we could run it with:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>./build/konan/bin/fibonacci.kexe
  1
  1
  2
  3
  5</code></pre></figure>

<p>Now we need to use JSON but since we don’t have the JVM neither we could use any Java code we are just going to use a C JSON library, I’m choose <a href="https://github.com/kgabis/parson" target="_blank">parson</a> since is perfect for the task.</p>

<p>First clone it, or create a submodule in your git:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span><span class="nb">mkdir </span>src/cpp
  <span class="nv">$ </span><span class="nb">cd </span>src/cpp
  <span class="nv">$ </span>git clone <span class="nt">--depth</span> 1 https://github.com/kgabis/parson.git</code></pre></figure>

<p>No I’ve create a shell script to compile the C code into a library that we could latter link:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">  <span class="nv">DEPS</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="sb">`</span><span class="nb">type</span> <span class="nt">-p</span> konanc<span class="sb">`</span><span class="si">)</span>/../dependencies

  <span class="k">if</span> <span class="o">[</span> x<span class="nv">$TARGET</span> <span class="o">==</span> x <span class="o">]</span><span class="p">;</span> <span class="k">then
  case</span> <span class="s2">"</span><span class="nv">$OSTYPE</span><span class="s2">"</span> <span class="k">in
    </span>darwin<span class="k">*</span><span class="p">)</span>  <span class="nv">TARGET</span><span class="o">=</span>macbook <span class="p">;;</span>
    linux<span class="k">*</span><span class="p">)</span>   <span class="nv">TARGET</span><span class="o">=</span>linux <span class="p">;;</span>
    <span class="k">*</span><span class="p">)</span>        <span class="nb">echo</span> <span class="s2">"unknown: </span><span class="nv">$OSTYPE</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nb">exit </span>1<span class="p">;;</span>
  <span class="k">esac</span>
  <span class="k">fi

  </span><span class="nv">CLANG_linux</span><span class="o">=</span><span class="nv">$DEPS</span>/clang-llvm-3.9.0-linux-x86-64/bin/clang++
  <span class="nv">CLANG_macbook</span><span class="o">=</span><span class="nv">$DEPS</span>/clang-llvm-3.9.0-darwin-macos/bin/clang++

  <span class="nv">var</span><span class="o">=</span>CLANG_<span class="k">${</span><span class="nv">TARGET</span><span class="k">}</span>
  <span class="nv">CLANG</span><span class="o">=</span><span class="k">${</span><span class="p">!var</span><span class="k">}</span>

  <span class="nb">mkdir</span> <span class="nt">-p</span> build/clang/

  <span class="nv">$CLANG</span> <span class="nt">-x</span> c <span class="nt">-c</span> src/main/cpp/parson/parson.c <span class="nt">-o</span> build/clang/parson.bc <span class="nt">-emit-llvm</span> <span class="o">||</span> <span class="nb">exit </span>1</code></pre></figure>

<p>This script will create a library in <a href="https://llvm.org/" target="_blank">LLVM</a> format, the architecture used by Kotlin Native.</p>

<p>Now we need to update our gradle script to compile the C code, and to link it to our program.</p>

<figure class="highlight"><pre><code class="language-gradle" data-lang="gradle">  <span class="k">buildscript</span> <span class="o">{</span>
      <span class="k">repositories</span> <span class="o">{</span>
          <span class="n">mavenCentral</span><span class="o">()</span>
          <span class="n">maven</span> <span class="o">{</span>
              <span class="n">url</span> <span class="s2">"https://dl.bintray.com/jetbrains/kotlin-native-dependencies"</span>
          <span class="o">}</span>
      <span class="o">}</span>

      <span class="k">dependencies</span> <span class="o">{</span>
          <span class="n">classpath</span> <span class="s2">"org.jetbrains.kotlin:kotlin-native-gradle-plugin:+"</span>
      <span class="o">}</span>
  <span class="o">}</span>

  <span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">'konan'</span>

  <span class="n">konanInterop</span> <span class="o">{</span>
      <span class="n">Parson</span> <span class="o">{</span>
          <span class="n">includeDirs</span> <span class="s2">"${project.projectDir}/src/main/cpp/parson"</span>
      <span class="o">}</span>
  <span class="o">}</span>

  <span class="n">konanArtifacts</span> <span class="o">{</span>
      <span class="n">fibonacci</span> <span class="o">{</span>
          <span class="n">useInterop</span> <span class="s2">"Parson"</span>
          <span class="n">nativeLibrary</span> <span class="s2">"${project.buildDir.canonicalPath}/clang/parson.bc"</span>
      <span class="o">}</span>
  <span class="o">}</span>

  <span class="n">task</span> <span class="nf">compileCpp</span><span class="o">(</span><span class="nl">type:</span> <span class="n">Exec</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">dependsOn</span> <span class="s1">'genParsonInteropStubs'</span>
      <span class="n">workingDir</span> <span class="n">project</span><span class="o">.</span><span class="na">getProjectDir</span><span class="o">()</span>
      <span class="n">commandLine</span> <span class="s1">'./buildCpp.sh'</span>
  <span class="o">}</span>

  <span class="n">compileKonanFibonacci</span> <span class="o">{</span>
      <span class="n">dependsOn</span> <span class="s1">'compileCpp'</span>
  <span class="o">}</span></code></pre></figure>

<p>Finally, we need to add a definition file for the C code so Kotlin could create and stub for it.</p>

<p>The file should be place in src/c_interop/Parson.def :</p>

<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="w">  </span><span class="py">headers</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">parson.h</span>
<span class="w">  </span><span class="py">headerFilter</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">parson.h</span></code></pre></figure>

<p>Now we could just build as before to check that everything is ok:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>gradlew clean build</code></pre></figure>

<p>This should generate some files including:</p>

<ul>
  <li>build/clang/parson.bc : The library that could be linked.</li>
  <li>build/konan/interopStubs/genParsonInteropStubs/Parson/Parson.kt : The Kotin Stub for calling the library.</li>
</ul>

<p>No we modify our program to use parson:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  import kotlinx.cinterop.<span class="k">*</span>
  import Parson.<span class="k">*</span>

  fun fibonacci<span class="o">(</span>numbers: Long<span class="o">)</span>: Array&lt;Long&gt; <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span>numbers <span class="o">==</span> 0L<span class="o">)</span> <span class="k">return </span>arrayOf<span class="o">(</span>0L<span class="o">)</span>
    <span class="k">if</span> <span class="o">(</span>numbers <span class="o">==</span> 1L<span class="o">)</span> <span class="k">return </span>arrayOf<span class="o">(</span>1L<span class="o">)</span>

    var previous <span class="o">=</span> 1L
    var current <span class="o">=</span> 1L
    var temp: Long

    <span class="k">return </span>arrayOf<span class="o">(</span>1L, 1L<span class="o">)</span> + <span class="o">(</span>1..<span class="o">(</span>numbers - 2<span class="o">))</span>.map <span class="o">{</span>
      temp <span class="o">=</span> current + previous
      previous <span class="o">=</span> current
      current <span class="o">=</span> temp
      current
    <span class="o">}</span>.toList<span class="o">()</span>.toTypedArray<span class="o">()</span>
  <span class="o">}</span>

  fun Array&lt;String&gt;.paramOrElse<span class="o">(</span>name: String, elseValue: Long<span class="o">)</span>: Long <span class="o">{</span>
    var result <span class="o">=</span> elseValue
    <span class="k">if</span> <span class="o">(</span>this.size <span class="o">&gt;</span> 0<span class="o">)</span> <span class="o">{</span>
      val json <span class="o">=</span> this[0]
      memScoped <span class="o">{</span>
        val schema <span class="o">=</span> json_parse_string<span class="o">(</span>json<span class="o">)</span>
        <span class="k">if</span> <span class="o">(</span>schema <span class="o">!=</span> null<span class="o">)</span> <span class="o">{</span>
          val root <span class="o">=</span> json_object<span class="o">(</span>schema<span class="o">)</span>
          <span class="k">if</span> <span class="o">(</span>root <span class="o">!=</span> null<span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span>json_object_has_value<span class="o">(</span>root, name<span class="o">)</span> <span class="o">==</span> 1<span class="o">)</span> <span class="o">{</span>
              result <span class="o">=</span> json_object_get_number<span class="o">(</span>root, name<span class="o">)</span>.toLong<span class="o">()</span>
            <span class="o">}</span>
          <span class="o">}</span>
          json_value_free<span class="o">(</span>schema<span class="o">)</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>
    <span class="k">return </span>result
  <span class="o">}</span>

  fun Long.throughFunction<span class="o">(</span>operation: <span class="o">(</span>Long<span class="o">)</span> -&gt; Array&lt;Long&gt;<span class="o">)</span>: String <span class="o">{</span>
    var result <span class="o">=</span> <span class="s2">"{}"</span>
    val elements <span class="o">=</span> operation<span class="o">(</span>this<span class="o">)</span>
    memScoped <span class="o">{</span>
      val schema <span class="o">=</span> json_value_init_object<span class="o">()</span>
      val root <span class="o">=</span> json_value_get_object<span class="o">(</span>schema<span class="o">)</span>

      json_object_set_value<span class="o">(</span>root, <span class="s2">"result"</span>, json_value_init_array<span class="o">())</span>
      val array <span class="o">=</span> json_object_get_array<span class="o">(</span>root, <span class="s2">"result"</span><span class="o">)</span><span class="p">;</span>

      elements.forEach <span class="o">{</span>
        json_array_append_number<span class="o">(</span>array, it.toDouble<span class="o">())</span>
      <span class="o">}</span>

      result <span class="o">=</span> json_serialize_to_string<span class="o">(</span>schema<span class="o">)</span>?.toKString<span class="o">()!!</span>
      json_value_free<span class="o">(</span>schema<span class="o">)</span>
    <span class="o">}</span>
    <span class="k">return </span>result
  <span class="o">}</span>

  fun main<span class="o">(</span>args: Array&lt;String&gt;<span class="o">)</span> <span class="o">{</span>

    val result <span class="o">=</span> args.paramOrElse<span class="o">(</span><span class="s2">"numbers"</span>, 10L<span class="o">)</span>.throughFunction<span class="o">(</span>::fibonacci<span class="o">)</span>

    println<span class="o">(</span>result<span class="o">)</span>
  <span class="o">}</span></code></pre></figure>

<p>We have create a couple of functions that uses the Kotlin/Native <a href="https://github.com/JetBrains/kotlin-native/blob/master/INTEROP.md" target="_blank">interoperability</a> so we could invoke Parson.</p>

<p>After building the program again we could just use it like this:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>./build/konan/bin/Fibonacci.kexe <span class="s2">"{ </span><span class="se">\"</span><span class="s2">numbers</span><span class="se">\"</span><span class="s2"> : 5 }"</span>
  <span class="o">{</span><span class="s2">"result"</span>:[1,1,2,3,5]<span class="o">}</span></code></pre></figure>

<p>No we need to create a docker for it, OpenWhisk have a docker base image that is based on alpine linux, I’ve not been able to make Kotlin-Native to work on alpine so I’ve create a base <a href="https://hub.docker.com/r/jamedina/openwhisk-kotlin-native/" target="_blank">docker image</a> that any one could use for making Kotlin Native Serverless functions.</p>

<p>The docker file for that image is:</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile">  <span class="c"># Dockerfile for runing a openwishk Kotlin native action</span>
  FROM openjdk:8-jdk

  <span class="c">#install kotlin native</span>
  RUN git clone --depth 1 https://github.com/JetBrains/kotlin-native.git
  WORKDIR kotlin-native
  RUN ./gradlew dependencies:update
  RUN ./gradlew dist

  ENV PATH /kotlin-native/dist/bin:$PATH

  <span class="c">#install python</span>
  RUN apt-get update
  RUN apt-get install python3-setuptools -y
  RUN easy_install3 pip
  RUN pip3 install --upgrade pip setuptools six
  RUN pip3 install --no-cache-dir gevent==1.2.1 flask==0.12

  <span class="c">#install c libraries</span>
  RUN apt-get update
  RUN apt-get install libc-dev -y

  <span class="c">#preparing the action proxy</span>
  ADD actionProxy/ /actionProxy
  WORKDIR /actionProxy
  ENV FLASK_PROXY_PORT 8080

  CMD ["/bin/bash"]</code></pre></figure>

<p>I’ve use openjdk 8 since we are going to use gradle in our build and it use the actionProxy python3 script that is provide by OpenWhisk to create a flask server that will serve our action trough HTTP, our action should be place in the folder /action and be named exec.</p>

<p>So now we could create a docker image for our fibonacci function:</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile">  <span class="c"># Dockerfile for runing a Kotlin native fibonacci action</span>
  FROM jamedina/openwhisk-kotlin-native

  <span class="c">#add basic gradle files</span>
  ADD .gradle/ /temp-build/.gradle
  ADD gradle/ /temp-build/gradle
  ADD build.gradle /temp-build
  ADD gradlew /temp-build
  ADD gradle.properties /temp-build

  <span class="c">#get the compiler</span>
  WORKDIR /temp-build
  RUN ./gradlew

  <span class="c"># add our action code</span>
  ADD src/ /temp-build/src
  ADD buildCpp.sh /temp-build

  <span class="c">#build our action</span>
  RUN ./gradlew build

  <span class="c">#copy the action as the default OpenWhisk docker actions</span>
  RUN mkdir /action
  RUN cp /temp-build/build/konan/bin/fibonacci.kexe /action/exec

  <span class="c">#clean up</span>
  RUN rm -rf /temp-build
  RUN rm -rf /kotlin-native

  CMD ["/bin/bash", "-c", "cd /actionProxy &amp;&amp; python3 -u actionproxy.py"]</code></pre></figure>

<p>Finally, I create a simple scripts to publish the images in docker hub, remember to login before.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">  <span class="c">#!/usr/bin/env bash</span>
  <span class="nb">echo</span> <span class="s2">"creating base image jamedina/openwhisk-kotlin-native"</span>
  docker build <span class="nt">-t</span> jamedina/openwhisk-kotlin-native docker/

  <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$?</span><span class="s2">"</span> <span class="nt">-eq</span> <span class="s2">"0"</span> <span class="o">]</span>
  <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"pushing jamedina/openwhisk-kotlin-native"</span>
    docker push jamedina/openwhisk-kotlin-native
    <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$?</span><span class="s2">"</span> <span class="nt">-eq</span> <span class="s2">"0"</span> <span class="o">]</span>
    <span class="k">then
      </span><span class="nb">echo</span> <span class="s2">"creating image jamedina/kotlin-native-fibonacci"</span>
      docker build <span class="nt">-t</span> jamedina/kotlin-native-fibonacci <span class="nb">.</span>
      <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$?</span><span class="s2">"</span> <span class="nt">-eq</span> <span class="s2">"0"</span> <span class="o">]</span>
      <span class="k">then
        </span><span class="nb">echo</span> <span class="s2">"pushing"</span>
        docker push jamedina/kotlin-native-fibonacci
        <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$?</span><span class="s2">"</span> <span class="nt">-eq</span> <span class="s2">"0"</span> <span class="o">]</span>
        <span class="k">then
          </span><span class="nb">echo</span> <span class="s2">"done"</span>
        <span class="k">else
          </span><span class="nb">echo</span> <span class="s2">"fail to push fibonacci docker"</span>
        <span class="k">fi
      else
        </span><span class="nb">echo</span> <span class="s2">"fail to build fibonacci docker"</span>
      <span class="k">fi
    else
      </span><span class="nb">echo</span> <span class="s2">"fail to push base docker"</span>
    <span class="k">fi
  else
    </span><span class="nb">echo</span> <span class="s2">"fail to build base docker"</span>
  <span class="k">fi</span></code></pre></figure>

<p>In the way that the files are added to the docker we can just run that command to get update the image only doing the parts that actually change.</p>

<p>So now we could put all things together to do:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>./build.sh
  <span class="nv">$ </span>wsk action create native-fibonacci <span class="nt">--docker</span> jamedina/kotlin-native-fibonacci
  <span class="nv">$ </span>wsk action invoke <span class="nt">--result</span> native-fibonacci <span class="nt">--param</span> numbers 5
  <span class="o">{</span>
    <span class="s2">"result"</span>: <span class="o">[</span>
        1,
        1,
        2,
        3,
        5
    <span class="o">]</span>
  <span class="o">}</span></code></pre></figure>

<p>This has been a great example to learn Kotlin Native, so sure I’ll do more things with it in a future.</p>

<p><em>Note: Many things could be change in this example, for example I could create a builder image that generate the binary and use for creating another image that has only that binary, since we do not need the whole compiler infrastructure and the image could be much smaller, but for demonstration is just OK.</em></p>]]></content><author><name>Juan Medina</name></author><category term="Programming" /><summary type="html"><![CDATA[Last week we do an example of a Kotlin Serverless Function using Apache OpenWhisk, however since we using Kotlin we could make our example a native application that will not run in the JVM.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/kotlin_native.png" /><media:content medium="image" url="http://juan-medina.com/assets/img/kotlin_native.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Serverless Kotlin</title><link href="http://juan-medina.com/2017/07/21/kotlin-serverless/" rel="alternate" type="text/html" title="Serverless Kotlin" /><published>2017-07-21T22:00:00+01:00</published><updated>2017-07-21T22:00:00+01:00</updated><id>http://juan-medina.com/2017/07/21/kotlin-serverless</id><content type="html" xml:base="http://juan-medina.com/2017/07/21/kotlin-serverless/"><![CDATA[<p>Right now there is a lot of traction on the Serverless architecture so I decide to give a try and do some examples using Kotlin.</p>

<p>The full source of this post it’s available in this <a href="https://github.com/LearningByExample/KotlinServerless" target="_blank">repository</a>.</p>

<p>First I recommend to read this <a href="https://martinfowler.com/articles/serverless.html" target="_blank">article</a> by <a href="https://twitter.com/mikebroberts" target="_blank">Mike Roberts</a> from Martin Fowler website to get a bit more understanding on what is a Serverless architecture.</p>

<p>Them we should take a loot to the <a href="https://github.com/apache/incubator-openwhisk/blob/master/docs/about.md" target="_blank">System Overview</a> of <a href="http://openwhisk.incubator.apache.org/" target="_blank">Apache Openwhisk</a>.</p>

<p>So now let starting to preparing our platform, we will require <a href="http://vagrantup.com/" target="_blank">Vagrant</a> and <a href="https://www.virtualbox.org/wiki/Downloads" target="_blank">Virtual Box</a>, so install those first.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>git clone <span class="nt">--depth</span><span class="o">=</span>1 https://github.com/apache/incubator-openwhisk.git openwhisk
  <span class="nv">$ </span><span class="nb">cd </span>openwhisk/tools/vagrant
  <span class="nv">$ </span>./hello</code></pre></figure>

<p>This will take a considerable amount of time so wait until everything is completed.</p>

<p>Now to test that the installation work we could just:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>vagrant ssh <span class="nt">--</span> wsk action invoke /whisk.system/utils/echo <span class="nt">-p</span> message hello <span class="nt">--result</span></code></pre></figure>

<p>This will output:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="w">  </span><span class="p">{</span><span class="w">
      </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hello"</span><span class="w">
  </span><span class="p">}</span></code></pre></figure>

<p>Now we could stop our Whisk server anytime with:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>vagrant <span class="nb">suspend</span></code></pre></figure>

<p>And bring it back with:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>vagrant up</code></pre></figure>

<p>Now let’s create a simple Kotlin function:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">numbers</span><span class="p">:</span> <span class="nc">Long</span><span class="p">):</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">Long</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span> <span class="p">==</span> <span class="mi">0L</span><span class="p">)</span> <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">0L</span><span class="p">)</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span> <span class="p">==</span> <span class="mi">1L</span><span class="p">)</span> <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">1L</span><span class="p">)</span>

    <span class="kd">var</span> <span class="py">previous</span> <span class="p">=</span> <span class="mi">1L</span>
    <span class="kd">var</span> <span class="py">current</span> <span class="p">=</span> <span class="mi">1L</span>
    <span class="kd">var</span> <span class="py">temp</span><span class="p">:</span> <span class="nc">Long</span>

    <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">1L</span><span class="p">,</span> <span class="mi">1L</span><span class="p">)</span> <span class="p">+</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="p">(</span><span class="n">numbers</span> <span class="p">-</span> <span class="mi">2</span><span class="p">)).</span><span class="nf">map</span> <span class="p">{</span>
      <span class="n">temp</span> <span class="p">=</span> <span class="n">current</span> <span class="p">+</span> <span class="n">previous</span>
      <span class="n">previous</span> <span class="p">=</span> <span class="n">current</span>
      <span class="n">current</span> <span class="p">=</span> <span class="n">temp</span>
      <span class="n">current</span>
    <span class="p">}.</span><span class="nf">toList</span><span class="p">().</span><span class="nf">toTypedArray</span><span class="p">()</span>
  <span class="p">}</span></code></pre></figure>

<p>This function just calculate a set of Fibonacci numbers, let try it out:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="mi">5</span><span class="p">).</span><span class="nf">forEach</span><span class="p">(</span><span class="o">::</span><span class="n">println</span><span class="p">)</span></code></pre></figure>

<p>This should output:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="mi">1</span>
  <span class="mi">1</span>
  <span class="mi">2</span>
  <span class="mi">3</span>
  <span class="mi">5</span></code></pre></figure>

<p>But how we could convert this into a Serverless function?</p>

<p>First we need to create a main function as OpenWhisk will be able to understand :</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">JsonObject</span><span class="p">)</span> <span class="p">:</span> <span class="nc">JsonObject</span> <span class="p">{</span>

  <span class="p">}</span></code></pre></figure>

<p>OpenWhisk use <a href="https://github.com/google/gson" target="_blank">Google GSon</a> so we will use in the final project maven to package our jar with dependencies, but for now let’s concentrate in the code, but the pom could be see <a href="https://github.com/LearningByExample/KotlinServerless/blob/master/pom.xml" target="_blank">here</a>.</p>

<p>OpenWhisk will sent to our function as many parameters as we define when we create our function, so for this I’ll need a parameters called <em>numbers</em>.</p>

<p>We could get the value with a simple extension function:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nc">JsonObject</span><span class="p">.</span><span class="nf">paramOrElse</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">elseValue</span><span class="p">:</span> <span class="nc">Long</span><span class="p">):</span> <span class="nc">Long</span> <span class="p">=</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nf">has</span><span class="p">(</span><span class="n">name</span><span class="p">))</span>
    <span class="k">this</span><span class="p">.</span><span class="nf">getAsJsonPrimitive</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="n">asLong</span> <span class="k">else</span> <span class="n">elseValue</span></code></pre></figure>

<p>So now in our main function we could do something like :</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">JsonObject</span><span class="p">)</span> <span class="p">:</span> <span class="nc">JsonObject</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">numbers</span> <span class="p">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">paramOrElse</span><span class="p">(</span><span class="s">"numbers"</span><span class="p">,</span> <span class="mi">10L</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">results</span> <span class="p">=</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span>
  <span class="p">}</span></code></pre></figure>

<p>All so now we need to get a result so we could do something like:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">JsonObject</span><span class="p">)</span> <span class="p">:</span> <span class="nc">JsonObject</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">numbers</span> <span class="p">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">paramOrElse</span><span class="p">(</span><span class="s">"numbers"</span><span class="p">,</span> <span class="mi">10L</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">results</span> <span class="p">=</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">response</span> <span class="p">=</span> <span class="nc">JsonObject</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">elements</span> <span class="p">=</span> <span class="nc">JsonArray</span><span class="p">()</span>
    <span class="n">results</span><span class="p">.</span><span class="nf">forEach</span><span class="p">{</span> <span class="n">elements</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
    <span class="n">response</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"result"</span><span class="p">,</span> <span class="n">elements</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">response</span>
  <span class="p">}</span></code></pre></figure>

<p>This look OK but I think we could improve adding just one extension function:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nc">Long</span><span class="p">.</span><span class="nf">throughFunction</span><span class="p">(</span><span class="n">operation</span><span class="p">:</span> <span class="p">(</span><span class="nc">Long</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">Long</span><span class="p">&gt;):</span> <span class="nc">JsonObject</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">result</span> <span class="p">=</span> <span class="nc">JsonObject</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">elements</span> <span class="p">=</span> <span class="nc">JsonArray</span><span class="p">()</span>
    <span class="nf">operation</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">forEach</span><span class="p">(</span><span class="n">elements</span><span class="o">::</span><span class="n">add</span><span class="p">)</span>
    <span class="n">result</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"result"</span><span class="p">,</span> <span class="n">elements</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">result</span>
  <span class="p">}</span></code></pre></figure>

<p>With this our main could become just:</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">JsonObject</span><span class="p">)</span> <span class="p">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">paramOrElse</span><span class="p">(</span><span class="s">"numbers"</span><span class="p">,</span> <span class="mi">10L</span><span class="p">).</span><span class="nf">throughFunction</span><span class="p">(</span><span class="o">::</span><span class="n">fibonacci</span><span class="p">)</span></code></pre></figure>

<p>Let put all the parts together :</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin">  <span class="k">package</span> <span class="nn">org.learning.by.example.serverless.kotlin</span>

  <span class="k">import</span> <span class="nn">com.google.gson.JsonArray</span>
  <span class="k">import</span> <span class="nn">com.google.gson.JsonObject</span>

  <span class="k">fun</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">numbers</span><span class="p">:</span> <span class="nc">Long</span><span class="p">):</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">Long</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span> <span class="p">==</span> <span class="mi">0L</span><span class="p">)</span> <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">0L</span><span class="p">)</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span> <span class="p">==</span> <span class="mi">1L</span><span class="p">)</span> <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">1L</span><span class="p">)</span>

    <span class="kd">var</span> <span class="py">previous</span> <span class="p">=</span> <span class="mi">1L</span>
    <span class="kd">var</span> <span class="py">current</span> <span class="p">=</span> <span class="mi">1L</span>
    <span class="kd">var</span> <span class="py">temp</span><span class="p">:</span> <span class="nc">Long</span>

    <span class="k">return</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="mi">1L</span><span class="p">,</span> <span class="mi">1L</span><span class="p">)</span> <span class="p">+</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="p">(</span><span class="n">numbers</span> <span class="p">-</span> <span class="mi">2</span><span class="p">)).</span><span class="nf">map</span> <span class="p">{</span>
      <span class="n">temp</span> <span class="p">=</span> <span class="n">current</span> <span class="p">+</span> <span class="n">previous</span>
      <span class="n">previous</span> <span class="p">=</span> <span class="n">current</span>
      <span class="n">current</span> <span class="p">=</span> <span class="n">temp</span>
      <span class="n">current</span>
    <span class="p">}.</span><span class="nf">toList</span><span class="p">().</span><span class="nf">toTypedArray</span><span class="p">()</span>
  <span class="p">}</span>

  <span class="k">fun</span> <span class="nc">JsonObject</span><span class="p">.</span><span class="nf">paramOrElse</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">elseValue</span><span class="p">:</span> <span class="nc">Long</span><span class="p">):</span> <span class="nc">Long</span> <span class="p">=</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nf">has</span><span class="p">(</span><span class="n">name</span><span class="p">))</span>
    <span class="k">this</span><span class="p">.</span><span class="nf">getAsJsonPrimitive</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="n">asLong</span> <span class="k">else</span> <span class="n">elseValue</span>

  <span class="k">fun</span> <span class="nc">Long</span><span class="p">.</span><span class="nf">throughFunction</span><span class="p">(</span><span class="n">operation</span><span class="p">:</span> <span class="p">(</span><span class="nc">Long</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">Long</span><span class="p">&gt;):</span> <span class="nc">JsonObject</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">result</span> <span class="p">=</span> <span class="nc">JsonObject</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">elements</span> <span class="p">=</span> <span class="nc">JsonArray</span><span class="p">()</span>
    <span class="nf">operation</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">forEach</span><span class="p">(</span><span class="n">elements</span><span class="o">::</span><span class="n">add</span><span class="p">)</span>
    <span class="n">result</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"result"</span><span class="p">,</span> <span class="n">elements</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">result</span>
  <span class="p">}</span>

  <span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="nc">JsonObject</span><span class="p">)</span> <span class="p">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">paramOrElse</span><span class="p">(</span><span class="s">"numbers"</span><span class="p">,</span> <span class="mi">10L</span><span class="p">).</span><span class="nf">throughFunction</span><span class="p">(</span><span class="o">::</span><span class="n">fibonacci</span><span class="p">)</span></code></pre></figure>

<p>With this prototype we could create many functions, in fact with a couple of generics could get even better but let’s do that another day.</p>

<p>So now we need to compile our function, since latter we will use vagrant is better that we put our project in the OpenWhisk folder created at the beginning of the post
since it will be map into the OpenWhisk machine.</p>

<p>I’ve create mine under openwhisk/projects/KotlinServerless and I’ll use maven wrapper</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span><span class="nb">cd </span>openwhisk
  <span class="nv">$ </span><span class="nb">cd </span>projects/KotlinServerless
  <span class="nv">$ </span>mvnw package</code></pre></figure>

<p>Now get back into the vagrant directory and ssh into the OpenWhisk machine:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span><span class="nb">cd </span>openwhisk
  <span class="nv">$ </span><span class="nb">cd </span>tools/vagrant
  <span class="nv">$ </span>vagrant ssh</code></pre></figure>

<p>Now from the OpenWhisk machine we will get into our function directory:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span><span class="nb">cd </span>openwhisk/projects/KotlinServerless/target
  <span class="nv">$ </span>wsk action create Fibonacci KotlinServerless-1.0-SNAPSHOT-jar-with-dependencies.jar <span class="nt">--main</span> <span class="se">\</span>
  org.learning.by.example.serverless.kotlin.FibonacciKt

  ok: created action Fibonacci</code></pre></figure>

<p>We need to specify the full location of our class, and remember than an static method in Kotlin get created in a class named _FileName_Kt.class</p>

<p>Now let’s run our function:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>wsk action invoke <span class="nt">--result</span> Fibonacci <span class="nt">--param</span> numbers 5</code></pre></figure>

<p>And we will get as output something like:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="w">  </span><span class="p">{</span><span class="w">
      </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="mi">1</span><span class="p">,</span><span class="w">
          </span><span class="mi">1</span><span class="p">,</span><span class="w">
          </span><span class="mi">2</span><span class="p">,</span><span class="w">
          </span><span class="mi">3</span><span class="p">,</span><span class="w">
          </span><span class="mi">5</span><span class="w">
      </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span></code></pre></figure>

<p>And of course we could running without parameters as:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>wsk action invoke <span class="nt">--result</span> Fibonacci</code></pre></figure>

<p>And the result will be:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="w">  </span><span class="p">{</span><span class="w">
      </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="mi">1</span><span class="p">,</span><span class="w">
          </span><span class="mi">1</span><span class="p">,</span><span class="w">
          </span><span class="mi">2</span><span class="p">,</span><span class="w">
          </span><span class="mi">3</span><span class="p">,</span><span class="w">
          </span><span class="mi">5</span><span class="p">,</span><span class="w">
          </span><span class="mi">8</span><span class="p">,</span><span class="w">
          </span><span class="mi">13</span><span class="p">,</span><span class="w">
          </span><span class="mi">21</span><span class="p">,</span><span class="w">
          </span><span class="mi">34</span><span class="p">,</span><span class="w">
          </span><span class="mi">55</span><span class="w">
      </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span></code></pre></figure>

<p>Finally don’t forget to suspend the vagrant machine after your done for the day, if not the OpenWhisk server sometimes get in bad state.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>vagrant <span class="nb">suspend</span></code></pre></figure>

<p>You could get it back simply with:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>vagrant up</code></pre></figure>

<p>I think this is enough for today, let’s see what else we do another day with this interesting topic.</p>]]></content><author><name>Juan Medina</name></author><category term="Programming" /><summary type="html"><![CDATA[Right now there is a lot of traction on the Serverless architecture so I decide to give a try and do some examples using Kotlin.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://juan-medina.com/assets/img/openwhiskkotlin.png" /><media:content medium="image" url="http://juan-medina.com/assets/img/openwhiskkotlin.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>