<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: boncheff</title>
    <description>The latest articles on DEV Community by boncheff (@boncheff).</description>
    <link>https://dev.to/boncheff</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F302009%2Ff3b12990-07d5-4d5f-a7f1-6e9e11c04679.png</url>
      <title>DEV Community: boncheff</title>
      <link>https://dev.to/boncheff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/boncheff"/>
    <language>en</language>
    <item>
      <title>Table Driven Unit Tests in Go</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Sun, 13 Jun 2021 14:45:20 +0000</pubDate>
      <link>https://dev.to/boncheff/table-driven-unit-tests-in-go-407b</link>
      <guid>https://dev.to/boncheff/table-driven-unit-tests-in-go-407b</guid>
      <description>&lt;p&gt;Whether you have been writing code for some years, or just getting started in software engineering, table driven tests have something to offer to you.&lt;br&gt;
In this article I will introduce you to table driven tests, and show you a few ways you can incorporate them in your codebase.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;




&lt;p&gt;Testing your code, in particular with &lt;strong&gt;unit tests&lt;/strong&gt;, can save you a lot of time!&lt;/p&gt;

&lt;p&gt;As the popular expression goes, &lt;code&gt;"6 hours of debugging can save you 5 minutes of reading the documentation"&lt;/code&gt;. We have all been there! But would we really need to spend 6 hours debugging our code if we had written good tests in the first place? Probably, not. There are plenty of articles on how to write good unit tests - this article is &lt;strong&gt;not&lt;/strong&gt; one of them - so now that we got this out of the way, let me take you on a little journey.&lt;/p&gt;




&lt;p&gt;Imagine you work in a bank, and are writing the code to perform a transfer of funds from one bank account to another (i.e. checking account to savings account). For the sake of simplicity, we can only use USD as a currency:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;I want to transfer 150.99 USD from my checking account to my savings account.&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;amount&lt;/span&gt;               &lt;span class="kt"&gt;float64&lt;/span&gt;
     &lt;span class="n"&gt;currency&lt;/span&gt;             &lt;span class="kt"&gt;string&lt;/span&gt; 
     &lt;span class="n"&gt;originAccountID&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; 
     &lt;span class="n"&gt;destinationAccountID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;validateTransferRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"USD"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid currency: must be USD"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;originAccountID&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"empty origin account ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destinationAccountID&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"empty destination account ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// request is valid so we return nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;How do you go about testing this function? I can think of a few ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approach 1&lt;/strong&gt;: if you do not mind code repetition, you can write a test case to validate each request field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 2&lt;/strong&gt;: if you are in a rush, you can write an OK table test, removing some of the repetition from the first approach&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 3&lt;/strong&gt;: if you are a perfectionist, you can write a table test on steroids which makes your test code more readable and extensible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's write some code for each approach and see how it looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 1 (code duplication):
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="s"&gt;"errors"&lt;/span&gt;
       &lt;span class="s"&gt;"testing"&lt;/span&gt;

       &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;
       &lt;span class="s"&gt;"github.com/stretchr/testify/require"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error due to invalid amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;transferRequest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;

              &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;validateTransferRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
       &lt;span class="p"&gt;})&lt;/span&gt;

       &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error due to invalid currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;transferRequest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;150.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"INR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;

              &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;validateTransferRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"invalid currency: must be USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
       &lt;span class="p"&gt;})&lt;/span&gt;

       &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error due to an empty origin account ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error due to an empty destination count ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You get the idea - the test cases read nicely but there is a lot of repetition, and although readable, it can be quite overwhelming especially if your test file has hundreds or even thousands of lines of code!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqz4gt9hm4mbdszw5va1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqz4gt9hm4mbdszw5va1.png" alt="IDE output for Approach 1&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 2 (table driven tests):
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="s"&gt;"errors"&lt;/span&gt;
       &lt;span class="s"&gt;"testing"&lt;/span&gt;

       &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;
       &lt;span class="s"&gt;"github.com/stretchr/testify/require"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestValidatorShouldError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;errorTestCases&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;description&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
              &lt;span class="n"&gt;input&lt;/span&gt;         &lt;span class="n"&gt;transferRequest&lt;/span&gt;
              &lt;span class="n"&gt;expectedError&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;errorTestCases&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="p"&gt;},&lt;/span&gt;
                     &lt;span class="n"&gt;expectedError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"invalid currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;150.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"INR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="p"&gt;},&lt;/span&gt;
                     &lt;span class="n"&gt;expectedError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"invalid currency: must be USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"invalid origin account ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;150.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="p"&gt;},&lt;/span&gt;
                     &lt;span class="n"&gt;expectedError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"empty origin account ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"invalid destination account ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;150.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="p"&gt;},&lt;/span&gt;
                     &lt;span class="n"&gt;expectedError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"empty destination account ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;validateTransferRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
              &lt;span class="p"&gt;})&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is already looking &lt;strong&gt;a lot&lt;/strong&gt; better! We removed some of the boilerplate and still ended up with a readable and maintainable test file!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Approach 3 (table driven tests on steroids):
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="s"&gt;"errors"&lt;/span&gt;
       &lt;span class="s"&gt;"testing"&lt;/span&gt;

       &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;
       &lt;span class="s"&gt;"github.com/stretchr/testify/require"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestValidatorShouldError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
              &lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                 &lt;span class="n"&gt;testInvalidAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="s"&gt;"invalid currency"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="n"&gt;testInvalidCurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="s"&gt;"invalid origin account ID"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;testInvalidOriginAccountID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="s"&gt;"invalid destination account ID"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;testInvalidDestinationAccountID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;})&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;testInvalidAmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;transferRequest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;validateTransferRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;testInvalidCurrency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;transferRequest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="m"&gt;150.90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"INR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;originAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;destinationAccountID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;validateTransferRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transferRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"invalid currency: must be USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;testInvalidOriginAccountID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;testInvalidDestinationAccountID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;p&gt;Whoa! Take a moment to let it sink in before you start cursing me!&lt;/p&gt;

&lt;p&gt;It might look like all we have done is moved the duplication further down the page, but in reality this approach is a lot more powerful than the previous two.&lt;/p&gt;

&lt;p&gt;First, it allows you to quickly scan through the test case definitions and see &lt;strong&gt;what&lt;/strong&gt; we are testing, without getting burdened with the &lt;strong&gt;how&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;Second, if you have more complicated business logic with a lot of edge cases for each test case (i.e. you only allow transfers where the currency of the origin and destination accounts match, or there is only a specific list of allowed currencies) you can abstract this in the test case function without burdering yourself with how exactly it is being tested, and only dig into each function if required.&lt;/p&gt;




&lt;p&gt;So here you have it folks - here are 3 of my favourite ways of writing table driven tests in Go.&lt;/p&gt;

&lt;p&gt;Please let me know if you follow any of these approaches yourself, or if you happen to write table tests differently, please let me know so I can learn a different approach!&lt;/p&gt;

&lt;p&gt;I hope that you have learned something new or that you simply enjoyed reading it!&lt;/p&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>refactorit</category>
    </item>
    <item>
      <title>CKAD - Revision - Services &amp; Networking</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Sun, 09 Feb 2020 20:57:18 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-revision-services-networking-42kl</link>
      <guid>https://dev.to/boncheff/ckad-revision-services-networking-42kl</guid>
      <description>&lt;p&gt;A service provides an abstract way to expose an application running on a set of Pods as a network service.&lt;/p&gt;

&lt;p&gt;For example, suppose you have a set of Pods that each listen on TCP port 9376 and carry a label &lt;code&gt;app=MyApp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Services can be one of the following four types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NodePort&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExternalName&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt; is the default and only provides access &lt;strong&gt;internally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NodePort&lt;/strong&gt; is great for when static IP addresses are necessary, such as opening a specific IP through a firewall. It is a simple connection from a high-port routed to a &lt;strong&gt;ClusterIP&lt;/strong&gt; using iptables or ipvs. &lt;strong&gt;The creation of a &lt;em&gt;NodePort&lt;/em&gt; generates a &lt;em&gt;ClusterIP&lt;/em&gt; by default&lt;/strong&gt;. The traffic is routed from the &lt;em&gt;NodePort&lt;/em&gt; to the &lt;em&gt;ClusterIP&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt; was created to pass requests to cloud provider like AWS. The address is made available to public traffic and packers are spread among the Pods in the deployment automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ExternalName&lt;/strong&gt; allows the return of an alias to an external service. It is used for &lt;strong&gt;services not yet brought into the Kubernetes cluster&lt;/strong&gt; (a bit like a placeholder). Once those services are &lt;em&gt;ready&lt;/em&gt; we can then change the type and traffic would flow to the internal objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl proxy creates a local service to access a ClusterIP which can be useful for troubleshooting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exposing an application with a Service
&lt;/h2&gt;

&lt;p&gt;We can use &lt;code&gt;kubectl expose&lt;/code&gt; command which exposes a resource as a new Kubernetes service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ingress
&lt;/h2&gt;

&lt;p&gt;An ingress is an API object that manages external access to the services in a cluster, typically HTTP.&lt;/p&gt;

&lt;p&gt;Ingress can provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load balancing&lt;/li&gt;
&lt;li&gt;SSL termination&lt;/li&gt;
&lt;li&gt;name-based virtual hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Service Mesh
&lt;/h2&gt;

&lt;p&gt;We can implement a service mesh for more complex connections or resources, such as service discovery, rate limiting, traffic management and advanced metrics. &lt;/p&gt;

&lt;p&gt;A service mesh consists of edge and embedded proxies communicating with each other and handling traffic based on rules from the a control plane.&lt;/p&gt;

&lt;p&gt;Typically we use &lt;a href="https://www.envoyproxy.io/"&gt;Envoy&lt;/a&gt; and &lt;a href="https://istio.io/"&gt;Istio&lt;/a&gt; &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>linux</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Revision - Observability</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Thu, 06 Feb 2020 21:11:26 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-revision-observability-338f</link>
      <guid>https://dev.to/boncheff/ckad-revision-observability-338f</guid>
      <description>&lt;h1&gt;
  
  
  Liveness Probes
&lt;/h1&gt;

&lt;p&gt;Many applications running for long periods of time eventually transition to broken states, and cannot recover except by being restarted. Kubernetes provides liveness probes to detect and remedy such situations.&lt;/p&gt;

&lt;p&gt;Here is an example of a pod using a liveness probe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5 // how long to wait before performing first probe
      periodSeconds: 5 // how often to run the liveness probe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Readiness Probes
&lt;/h1&gt;

&lt;p&gt;Sometimes, applications are &lt;strong&gt;temporarily unable to serve traffic&lt;/strong&gt;. For example, an application might need to load large data or configuration files during startup, or depend on external services after startup. A pod with containers reporting that they are not ready &lt;strong&gt;does not receive traffic through Kubernetes Services.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Readiness probes are configured similarly to liveness probes. The only difference is that you use the &lt;strong&gt;readinessProbe&lt;/strong&gt; field instead of the livenessProbe field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kubernetes</category>
      <category>linux</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Revision - Configuration</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Wed, 05 Feb 2020 21:15:21 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-revision-configuration-1h6o</link>
      <guid>https://dev.to/boncheff/ckad-revision-configuration-1h6o</guid>
      <description>&lt;h1&gt;
  
  
  ConfigMaps
&lt;/h1&gt;

&lt;p&gt;Data in ConfigMaps in kubernetes &lt;strong&gt;is not encoded or encrypted&lt;/strong&gt; and contains &lt;em&gt;key-value&lt;/em&gt; pairs or plain configuration files in any format.&lt;/p&gt;

&lt;p&gt;Here are a few uses of ConfigMaps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pod env vars from single or multiple ConfigMaps&lt;/li&gt;
&lt;li&gt;Use ConfigMap values in Pod commands&lt;/li&gt;
&lt;li&gt;Populate Volume from ConfigMap&lt;/li&gt;
&lt;li&gt;Add ConfigMap data to specific path in Volume&lt;/li&gt;
&lt;li&gt;Set file names and access mode in Volume from ConfigMap data&lt;/li&gt;
&lt;li&gt;Can be used by system components and controllers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating ConfigMaps
&lt;/h3&gt;

&lt;p&gt;ConfigMaps can be created in one of the three following ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create configmap myconfigmap \
--from-literal=city=London \             
--from-file=./myconfigmapfile.txt \
--from-file=./myconfigmapdirectory/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which results in the following ConfigMap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k get configmap myconfigmap -o yaml

apiVersion: v1
data:
  city: London
kind: ConfigMap
metadata:
  creationTimestamp: "2020-01-12T11:22:43Z"
  name: myconfigmap
  namespace: default
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Security Context
&lt;/h1&gt;

&lt;p&gt;A security context defines privilege and access control settings for a Pod or Container so we can limit what processes running in containers can do. For example we can limit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the user ID of the process (UID)&lt;/li&gt;
&lt;li&gt;the Linux capabilities&lt;/li&gt;
&lt;li&gt;filesystem groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we want to enforce that containers cannot run their process as root user we can add &lt;strong&gt;runAsNonRoot: true&lt;/strong&gt; to the pod spec. Or we can define a PodSecurityPolicy to that effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To automate the enforcement of security contexts, we can define &lt;a href="https://kubernetes.io/docs/concepts/policy/pod-security-policy/"&gt;PodSecurityPolicies (PSP)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pod Security Policies are cluster-level rules that govern what a pod can do, what they can access, what user they run as...&lt;/p&gt;

&lt;p&gt;For a PSP to be enabled we must first configure the admission controller of the &lt;strong&gt;controller-manager&lt;/strong&gt; to contain &lt;strong&gt;PodSecurityPolicy&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Service Accounts
&lt;/h1&gt;

&lt;p&gt;Service accounts are used by processes to access the API (a service account provides an identity for processes than run in a pod)&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>linux</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Revision - Pod Design</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Wed, 29 Jan 2020 21:31:46 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-revision-pod-design-5dc7</link>
      <guid>https://dev.to/boncheff/ckad-revision-pod-design-5dc7</guid>
      <description>&lt;h1&gt;
  
  
  Labels, Selectors and Annotations
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Labels
&lt;/h2&gt;

&lt;p&gt;Labels are key/value pairs that are attached to objects, such as pods. Labels allow for efficient queries and watches and are ideal for use in UIs and CLIs. Non-identifying information should be recorded using &lt;a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/"&gt;annotations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example on how to create a pod using &lt;code&gt;kubectl&lt;/code&gt; and add a label to it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl run test_pod --image=nginx --restart=Never --labels="country=united_kingdom" --dry-run -o yaml'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which results in the following &lt;code&gt;yaml&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    country: united_kingdom
  name: test_pod
spec:
  containers:
  - image: nginx
    name: test_pod
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Selectors
&lt;/h2&gt;

&lt;p&gt;Unlike names and UIDs, labels do not provide uniqueness. In general, we expect many objects to carry the same label(s).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Via a label selector, the client/user can identify a set of objects. The label selector is the core grouping primitive in Kubernetes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of a &lt;strong&gt;nodeSelector&lt;/strong&gt; which selects nodes with the label &lt;em&gt;accelerator=nvidia-tesla-p100&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: cuda-test
spec:
  containers:
    - name: cuda-test
      image: "k8s.gcr.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Annotations
&lt;/h2&gt;

&lt;p&gt;You can use Kubernetes annotations to attach arbitrary non-identifying metadata to objects. Clients such as tools and libraries can retrieve this metadata.&lt;/p&gt;

&lt;p&gt;For example, here’s the configuration file for a Pod that has the annotation &lt;em&gt;imageregistry: &lt;a href="https://hub.docker.com/"&gt;https://hub.docker.com/&lt;/a&gt;&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Deployments
&lt;/h1&gt;

&lt;p&gt;A deployment provides declarative updates for Pods and ReplicaSets. You describe a &lt;strong&gt;desired state&lt;/strong&gt; and the deployment controller changes the &lt;strong&gt;actual state&lt;/strong&gt; to the &lt;strong&gt;desired state&lt;/strong&gt; at a controlled rate.&lt;/p&gt;

&lt;p&gt;To create a deployment you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create deployment my-dep --image=busybox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or create using a file using &lt;code&gt;kubectl create -f &amp;lt;path_to_file.yaml&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The following is the resulting &lt;em&gt;yaml&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: test
  name: test
spec:
  replicas: 1
  selector:
    matchLabels: &amp;lt;----- defines how the Deployment finds which Pods to manage
      app: test
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: test
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
status: {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rolling updates
&lt;/h3&gt;

&lt;p&gt;We can do a rolling deployment update using&lt;br&gt;
&lt;code&gt;kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1 --record&lt;/code&gt; command, which results in the following output: &lt;code&gt;deployment.apps/nginx-deployment image updated&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Another way is to run &lt;code&gt;kubectl edit deployment &amp;lt;my_dep_name&amp;gt;&lt;/code&gt; and update the image manually.&lt;/p&gt;

&lt;p&gt;Once an update command is issued, we can keep track using &lt;code&gt;kubectl rollout status deployment/&amp;lt;my_dep_name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can check status of rollout using &lt;code&gt;kubectl rollout status deployment/&amp;lt;my_dep_name&amp;gt;&lt;/code&gt; or check history using &lt;code&gt;kubectl rollout history deployment/&amp;lt;my_dep_name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rollbacks
&lt;/h3&gt;

&lt;p&gt;We can roll back a deployment using &lt;code&gt;kubectl rollout undo deployment/&amp;lt;my_dep_name&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Jobs
&lt;/h1&gt;

&lt;p&gt;A Job creates one or more pods and ensures that a specified number of them successfully terminate.&lt;/p&gt;

&lt;p&gt;We can create a job using &lt;code&gt;kubectl create job myjob --image=nginx --dry-run -o yaml -- /bin/sh -c 'echo hello;sleep 100;'&lt;/code&gt; which results in the following job being created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: batch/v1
kind: Job
metadata:
  creationTimestamp: null
  name: myjob
spec:
  template:
    metadata:
      creationTimestamp: null
    spec:
      containers:
      - command:
        - /bin/sh
        - -c
        - echo hello;sleep 100;
        image: nginx
        name: myjob
        resources: {}
      restartPolicy: Never
status: {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once created we can use &lt;code&gt;kubectl describe job myjob&lt;/code&gt; and do &lt;code&gt;kubectl get pods&lt;/code&gt; to get the list of running pods for that job.&lt;/p&gt;

&lt;p&gt;# CronJob&lt;/p&gt;

&lt;p&gt;A Cron Job creates a time-based schedule.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>linux</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Revision - Multi-Container Pods</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Tue, 28 Jan 2020 20:58:14 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-revision-multi-container-pods-4aem</link>
      <guid>https://dev.to/boncheff/ckad-revision-multi-container-pods-4aem</guid>
      <description>&lt;p&gt;See &lt;a href="https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns/"&gt;https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns/&lt;/a&gt; for mode details on this topic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sidecar Container&lt;/strong&gt; - Adds some functionality not present in the main container such as logging - this allows for a decoupled and scalable approach rather than bloating main container code. &lt;em&gt;Prometheus&lt;/em&gt;  and &lt;em&gt;Fluentd&lt;/em&gt; logging use sidecar containers to collect data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adapter Container&lt;/strong&gt; - Used to &lt;strong&gt;modify (adapt)&lt;/strong&gt; the data either on ingress or egress to match some other need. An adapter would be an efficient way to standardise the output of the main container to be ingested by the monitoring tool without having to modify the monitor of the containerised application. An adapter container transforms multiple applications to a singular view&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ambassador&lt;/strong&gt; - an ambassador allows for access to the outside world without having to implement a service or another entry in an ingress controller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proxy local connection&lt;/li&gt;
&lt;li&gt;Reverse proxy&lt;/li&gt;
&lt;li&gt;Limit HTTP requests&lt;/li&gt;
&lt;li&gt;Re route from main container to the outside world. 
(Open Source Kubernetes-Native API Gateway built on the Envoy Proxy)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Revision - Core Concepts</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Sun, 26 Jan 2020 18:10:32 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-revision-core-concepts-o7l</link>
      <guid>https://dev.to/boncheff/ckad-revision-core-concepts-o7l</guid>
      <description>&lt;h1&gt;
  
  
  Pods
&lt;/h1&gt;

&lt;p&gt;A pod is a group of &lt;strong&gt;one or more containers&lt;/strong&gt; (such as Docker containers), with shared storage/network, and a specification for how to run the containers. A Pod’s contents are always &lt;strong&gt;co-located and co-scheduled&lt;/strong&gt;, and &lt;strong&gt;run in a shared context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pods share &lt;strong&gt;networking&lt;/strong&gt; and &lt;strong&gt;storage&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;containers within a Pod share an IP address and port space, and can find each other via localhost&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a pod can specify a set of shared storage volumes, allowing containers to share data.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Restarting a container in a Pod should not be confused with restarting
the Pod. The Pod itself does not run, but is an environment the containers
run in and persists until it is deleted.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of a pod template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! &amp;amp;&amp;amp; sleep 3600']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Services
&lt;/h1&gt;

&lt;p&gt;A service is an abstraction which defines a &lt;strong&gt;logical set of Pods and a policy by which to access them&lt;/strong&gt; (sometimes this pattern is called a micro-service). The set of Pods targeted by a Service is usually determined by a &lt;strong&gt;selector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Services allow for decoupling - if a set of "frontend" pods needs to talk to a set of "backend" pods, this is possible due to the Service abstraction.&lt;/p&gt;

&lt;p&gt;Here is an example of a service template that would match the above pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:    &amp;lt;--------------------- this is what selects pods
    app: myApp &amp;lt;--------------------- 
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kubernetes assigns this Service an IP address (sometimes called the “cluster IP”), which is used by the Service proxies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes ServiceTypes allow you to specify what kind of Service you want. The default is ClusterIP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Type values and their behaviors are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt;: exposes the Service on a cluster-internal IP. Choosing this value makes the Service only reachable from within the cluster. This is the default ServiceType.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NodePort&lt;/strong&gt;: exposes the Service on each Node’s IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. You’ll be able to contact the NodePort Service, from outside the cluster, by requesting :.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;*&lt;em&gt;LoadBalancer&lt;/em&gt;: exposes the Service externally using a cloud provider’s load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ExternalName&lt;/strong&gt;: allows the return of an alias to an external service. &lt;strong&gt;It is used for services not yet brought into the Kubernetes cluster&lt;/strong&gt; (a bit like a placeholder). Once those services are ready we can then change the type and traffic would flow to the internal objects.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Volumes
&lt;/h1&gt;

&lt;p&gt;On-disk files in a Container are ephemeral, which presents some problems for non-trivial applications when running in Containers. First, &lt;strong&gt;when a Container crashes, kubelet will restart it, but the files will be lost - the Container starts with a clean state&lt;/strong&gt;. Second, &lt;strong&gt;when running Containers together in a Pod it is often necessary to share files between those Containers&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Kubernetes Volume abstraction solves both of these problems&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A volume has the same lifetime as the pod that encloses it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Namespaces
&lt;/h1&gt;

&lt;p&gt;Kubernetes supports multiple virtual clusters backed by the same physical cluster. These virtual clusters are called namespaces.&lt;br&gt;
Namespaces are intended for use in environments with many users spread across multiple teams, or projects.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deployment
&lt;/h1&gt;

&lt;p&gt;A Deployment provides declarative updates for Pods and ReplicaSets.&lt;/p&gt;

&lt;h1&gt;
  
  
  ReplicaSet
&lt;/h1&gt;

&lt;p&gt;A ReplicaSet’s purpose is to maintain a stable set of replica Pods running at any given time. As such, it is often used to guarantee the availability of a specified number of identical Pods.&lt;/p&gt;

&lt;p&gt;You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate.&lt;/p&gt;

&lt;h1&gt;
  
  
  DaemonSet
&lt;/h1&gt;

&lt;p&gt;A DaemonSet ensures that all (or some) Nodes run a copy of a Pod such as cluster storage daemon, logs collection daemon or monitoring daemon.&lt;/p&gt;

&lt;h1&gt;
  
  
  StatefulSet
&lt;/h1&gt;

&lt;p&gt;StatefulSet is the workload API object used to manage stateful applications.&lt;/p&gt;

&lt;p&gt;Manages the deployment and scaling of a set of Pods, and provides guarantees about the ordering and uniqueness of these Pods.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>linux</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Service Types</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Mon, 20 Jan 2020 20:49:02 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-service-types-dc5</link>
      <guid>https://dev.to/boncheff/ckad-service-types-dc5</guid>
      <description>&lt;p&gt;Services can be one of the following four types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NodePort&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExternalName&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt; is the default and only provides access &lt;strong&gt;internally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NodePort&lt;/strong&gt; is great for when static IP addresses are necessary, such as opening a specific IP through a firewall. It is a simple connection from a high-port routed to a &lt;strong&gt;ClusterIP&lt;/strong&gt; using iptables or ipvs. &lt;strong&gt;The creation of a &lt;em&gt;NodePort&lt;/em&gt; generates a &lt;em&gt;ClusterIP&lt;/em&gt; by default&lt;/strong&gt;. The traffic is routed from the &lt;em&gt;NodePort&lt;/em&gt; to the &lt;em&gt;ClusterIP&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt; was created to pass requests to cloud provider like AWS. The address is made available to public traffic and packers are spread among the Pods in the deployment automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ExternalName&lt;/strong&gt; allows the return of an alias to an external service. It is used for &lt;strong&gt;services not yet brought into the Kubernetes cluster&lt;/strong&gt; (a bit like a placeholder). Once those services are &lt;em&gt;ready&lt;/em&gt; we can then change the type and traffic would flow to the internal objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl proxy creates a local service to access a ClusterIP which can be useful for troubleshooting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exposing an application with a Service
&lt;/h2&gt;

&lt;p&gt;We can use &lt;code&gt;kubectl expose&lt;/code&gt; command which exposes a resource as a new Kubernetes service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ingress
&lt;/h2&gt;

&lt;p&gt;An ingress is an API object that manages external access to the services in a cluster, typically HTTP.&lt;/p&gt;

&lt;p&gt;Ingress can provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load balancing&lt;/li&gt;
&lt;li&gt;SSL termination&lt;/li&gt;
&lt;li&gt;name-based virtual hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Service Mesh
&lt;/h2&gt;

&lt;p&gt;We can implement a service mesh for more complex connections or resources, such as service discovery, rate limiting, traffic management and advanced metrics. &lt;/p&gt;

&lt;p&gt;A service mesh consists of edge and embedded proxies communicating with each other and handling traffic based on rules from the a control plane.&lt;/p&gt;

&lt;p&gt;Typically we use &lt;a href="https://www.envoyproxy.io/"&gt;Envoy&lt;/a&gt; and &lt;a href="https://istio.io/"&gt;Istio&lt;/a&gt; &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>linux</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Security Notes</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Sat, 18 Jan 2020 18:13:28 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-security-notes-4g0a</link>
      <guid>https://dev.to/boncheff/ckad-security-notes-4g0a</guid>
      <description>&lt;p&gt;To perform any action in Kubernetes cluster we need to access the API and go through three main steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorisation (RBAC or ABAC)&lt;/li&gt;
&lt;li&gt;Admission Control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the &lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/#admission-control"&gt;official documentation&lt;/a&gt; for more details:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8coS1wI5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://d33wubrfki0l68.cloudfront.net/673dbafd771491a080c02c6de3fdd41b09623c90/50100/images/docs/admin/access-control-overview.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8coS1wI5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://d33wubrfki0l68.cloudfront.net/673dbafd771491a080c02c6de3fdd41b09623c90/50100/images/docs/admin/access-control-overview.svg" alt="diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Authentication is done with certificates, tokens or basic auth&lt;/li&gt;
&lt;li&gt;Users are &lt;em&gt;not&lt;/em&gt; created by the API and should be managed by external system&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/"&gt;Service accounts&lt;/a&gt; are used by processes to access the API &lt;em&gt;(a service account provides an identity for processes than run in a pod)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Authorisation
&lt;/h2&gt;

&lt;p&gt;Once a request is authenticated, it needs to be authorised to be able to proceed through the Kubernetes system. There are three main modes of authorisation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ABAC (attribute based access control)&lt;/li&gt;
&lt;li&gt;RBAC (role based access control)&lt;/li&gt;
&lt;li&gt;WebHook&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Admission Controller
&lt;/h2&gt;

&lt;p&gt;Accesses the contents of the objects being created by the requests. It can modify the content or validate it and potentially deny the request.&lt;/p&gt;

&lt;h1&gt;
  
  
  Security Context
&lt;/h1&gt;

&lt;p&gt;A security context defines privilege and access control settings for a Pod or Container so we can limit what processes running in containers can do. For example we can limit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the user ID of the process (UID)&lt;/li&gt;
&lt;li&gt;the Linux capabilities&lt;/li&gt;
&lt;li&gt;filesystem groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we want to enforce that containers cannot run their process as root user we can add &lt;strong&gt;runAsNonRoot: true&lt;/strong&gt; to the pod spec. Or we can define a PodSecurityPolicy to that effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To automate the enforcement of security contexts, we can define &lt;a href="https://kubernetes.io/docs/concepts/policy/pod-security-policy/"&gt;PodSecurityPolicies (PSP)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pod Security Policies are cluster-level rules that govern what a pod can do, what they can access, what user they run as...&lt;/p&gt;

&lt;p&gt;For a PSP to be enabled we must first configure the admission controller of the &lt;strong&gt;controller-manager&lt;/strong&gt; to contain &lt;strong&gt;PodSecurityPolicy&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Network Security Policy
&lt;/h1&gt;

&lt;p&gt;A network policy is a specification of how groups of pods are allowed to communicate with each other and other network endpoints.&lt;/p&gt;

&lt;p&gt;NetworkPolicy resources use labels to select pods and define rules which specify what traffic is allowed to the selected pods.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>linux</category>
      <category>docker</category>
    </item>
    <item>
      <title>CKAD - Deployment Configuration Notes</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Wed, 01 Jan 2020 21:32:30 +0000</pubDate>
      <link>https://dev.to/boncheff/ckad-deployment-configuration-notes-3gi9</link>
      <guid>https://dev.to/boncheff/ckad-deployment-configuration-notes-3gi9</guid>
      <description>&lt;h1&gt;
  
  
  Deployment Configuration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Volumes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a volume is a directory (possibly pre-populated) made available to containers in a Pod&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims"&gt;Kubernetes volume&lt;/a&gt; shares at least the Pod lifetime, not the container within&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;if you want your storage lifetime to be distinct from a Pod, use Persistent Volumes. These allow for volumes to be claimed by a Pod using &lt;em&gt;PersistentVolumeClaims&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a volume can persist longer than a pod, and can be accessed by multiple pods using &lt;strong&gt;PersistentVolumeClaims&lt;/strong&gt;. This allows for state persistency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a volume can be made available to multiple pods, with each given an &lt;strong&gt;access mode&lt;/strong&gt; to write&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;we can pass &lt;strong&gt;encoded&lt;/strong&gt; data to a pod using &lt;strong&gt;Secrets&lt;/strong&gt; and &lt;strong&gt;non-encoded&lt;/strong&gt; data using a &lt;strong&gt;ConfigMap&lt;/strong&gt; - we can pass things like SSH keys, passwords etc...&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;there is no concurrency checking, which means data corruption is probable unless outside locking takes place&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;examples of volume types: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.ceph.com/docs/master/rbd/"&gt;rbd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;NFS&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/"&gt;gcePersistentDisk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are three access modes for volumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RWO(ReadWriteOnce)&lt;/strong&gt; - allows read-write by a single node&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROX(ReadOnlyMany)&lt;/strong&gt; - allow read-only by multiple nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RWX(ReadWriteMany)&lt;/strong&gt; - allows read-write by multiple nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a volume is requested, the local &lt;strong&gt;kubelet&lt;/strong&gt; uses the &lt;strong&gt;kubelet_pods.go&lt;/strong&gt; script to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;map raw devices&lt;/li&gt;
&lt;li&gt;determine and make mount point for container&lt;/li&gt;
&lt;li&gt;create symbolic link on the host filesystem to associate the storage with the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If no particular &lt;strong&gt;StorageClass&lt;/strong&gt; was requested, only &lt;em&gt;access_mode&lt;/em&gt; and &lt;em&gt;size&lt;/em&gt; are used as params to create a volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Volume types
&lt;/h2&gt;

&lt;p&gt;There are many different types of volumes such as &lt;strong&gt;GCEpersistentDisk&lt;/strong&gt; or &lt;strong&gt;awsElasticBlockStore&lt;/strong&gt; (GCE and EBS respectively)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;emptyDir&lt;/strong&gt; volume is first created when a Pod is assigned to a Node, and exists as long as that Pod is running on that node. As the name says, it is initially empty. Containers in the Pod can all read and write the same files in the emptyDir volume, though that volume can be mounted at the same or different paths in each Container. &lt;strong&gt;When a Pod is removed from a node for any reason, the data in the emptyDir is deleted forever.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;hostPath&lt;/strong&gt; volume mounts a file or directory from the host node’s filesystem into your Pod. This is not something that most Pods will need, but it offers a powerful escape hatch for some applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Secrets
&lt;/h2&gt;

&lt;p&gt;Secrets in kubernetes are not encrypted by default - only &lt;strong&gt;base64&lt;/strong&gt; encoded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Config Maps
&lt;/h2&gt;

&lt;p&gt;Data in ConfigMaps in kubernetes is not encoded or encrypted and contains &lt;em&gt;key-value&lt;/em&gt; pairs or plain configuration files in any format.&lt;/p&gt;

&lt;p&gt;Here are a few uses of ConfigMaps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pod env vars from single or multiple ConfigMaps&lt;/li&gt;
&lt;li&gt;Use ConfigMap values in Pod commands&lt;/li&gt;
&lt;li&gt;Populate Volume from ConfigMap&lt;/li&gt;
&lt;li&gt;Add ConfigMap data to specific path in Volume&lt;/li&gt;
&lt;li&gt;Set file names and access mode in Volume from ConfigMap data&lt;/li&gt;
&lt;li&gt;Can be used by system components and controllers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating ConfigMaps
&lt;/h3&gt;

&lt;p&gt;ConfigMaps can be created in one of the three following ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create configmap myconfigmap \
--from-literal=city=London \             
--from-file=./myconfigmapfile.txt \
--from-file=./myconfigmapdirectory/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which results in the following ConfigMap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k get configmap myconfigmap -o yaml

apiVersion: v1
data:
  city: London
kind: ConfigMap
metadata:
  creationTimestamp: "2020-01-12T11:22:43Z"
  name: myconfigmap
  namespace: default
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scaling and Rolling Update
&lt;/h2&gt;

&lt;p&gt;To scale a deployment we can use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl scale deploy/mydeployment --replicas=4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then to downscale&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl scale deploy/mydeployment --replicas=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment rollbacks
&lt;/h2&gt;

&lt;p&gt;Let's say we want to update a container's image using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl set image deployment/test nginx=nginx:0.9 --record
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say we now want to update to an even newer version of nginx using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl set image deployment/test nginx=nginx:66.6 --record
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now run &lt;code&gt;kubectl get pods&lt;/code&gt; you will see that the pod is stuck in &lt;code&gt;ImagePullBackoff&lt;/code&gt; state so we want to immediately rollback the changes to a previously working version. We can do this using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl rollout undo deployment/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view a history of all events we can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl rollout history deployment/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which shows the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deployment.apps/test 
REVISION  CHANGE-CAUSE
1         kubectl set image deploy/test test=nginx --record=true
4         kubectl set image deployments/test nginx=nginx:0.8 --record=true
5         kubectl set image deployments/test nginx=nginx:0.9 --record=true
6         kubectl set image deployments/test nginx=nginx:0.66 --record=true
7         kubectl set image deployments/test nginx=nginx:66.66 --record=true
8         kubectl set image deployments/test nginx=nginx:66.66 --record=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>CKAD notes</title>
      <dc:creator>boncheff</dc:creator>
      <pubDate>Fri, 27 Dec 2019 16:57:00 +0000</pubDate>
      <link>https://dev.to/boncheff/certified-kubernetes-application-developer-notes-10i1</link>
      <guid>https://dev.to/boncheff/certified-kubernetes-application-developer-notes-10i1</guid>
      <description>&lt;p&gt;This article will contain a series of notes I made while preparing for the &lt;a href="https://www.cncf.io/certification/ckad/" rel="noopener noreferrer"&gt;CKAD&lt;/a&gt; exam. &lt;br&gt;
I intend to use these notes to refresh my knowledge on the topic periodically and hope fellow engineers find them useful during their foray into Kubernetes. &lt;/p&gt;

&lt;p&gt;Please come back to read them periodically as I will add new notes as I work through the preparation material.&lt;/p&gt;

&lt;p&gt;Let's dive straight in!&lt;/p&gt;
&lt;h2&gt;
  
  
  General background information on Linux:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;systemd&lt;/strong&gt; - system startup and service management (replaces &lt;em&gt;service&lt;/em&gt; package)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;journald&lt;/strong&gt; - manages system logs (this is a &lt;em&gt;systemd&lt;/em&gt; service that collects and stores logging data - it creates and maintains &lt;em&gt;structures&lt;/em&gt;, indexed journals based on logging information that is received from a variety of sources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;firewalld&lt;/strong&gt; - firewall management daemon (provides a dynamically managed firewall with support for network / firewall zones to define the trust level of network connections or interfaces - it has support for IPv4, IPv6 firewall settings and Ethernet bridges.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ip&lt;/strong&gt; - network display and configuration tool (this is part of the networking tools package, and is designed to be a replacement for the &lt;strong&gt;ifconfig&lt;/strong&gt; command. The &lt;strong&gt;ip&lt;/strong&gt; command will show or manipulate routing, network devices, routing information and tunnels.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What is Kubernetes?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;...an open source system for automating deployment, scaling and management of containerised applications.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;started at &lt;a href="https://kubernetes.io/blog/2015/04/borg-predecessor-to-kubernetes/" rel="noopener noreferrer"&gt;Google&lt;/a&gt; 15 years ago&lt;/li&gt;
&lt;li&gt;containers provide a fine-grained solution for packing clusters efficiently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trivia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kubernetes in greek means 'the helmsman' or pilot of the ship 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kubernetes offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;continuous integration&lt;/strong&gt; - a consistent way to build and test software. Deploying new packages with code written each day or every hour instead of quarterly. Tools like &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; or &lt;a href="https://jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt; are often part of this with Kubernetes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;continuous delivery&lt;/strong&gt; - an automated way to test and deploy software into various environments. Kubernetes handles the lifecycle of containers and connection of infrastructure resources to make rolling upgrades and rollbacks easy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kubernetes approaches these software issues by deploying a large number of small web services called &lt;a href="https://martinfowler.com/articles/microservices.html" rel="noopener noreferrer"&gt;microservices&lt;/a&gt;. One way of visualising this is - instead of deploying a large &lt;em&gt;Apache&lt;/em&gt; web server running &lt;em&gt;httpd&lt;/em&gt; daemons responding to page requests, there would be many smaller &lt;em&gt;nginx&lt;/em&gt; servers handling traffic and requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd33wubrfki0l68.cloudfront.net%2F817bfdd83a524fed7342e77a26df18c87266b8f4%2F3da7c%2Fimages%2Fdocs%2Fcomponents-of-kubernetes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd33wubrfki0l68.cloudfront.net%2F817bfdd83a524fed7342e77a26df18c87266b8f4%2F3da7c%2Fimages%2Fdocs%2Fcomponents-of-kubernetes.png" alt="kube_architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt; - is used to talk to the API server (or we can write our own client)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-scheduler&lt;/strong&gt; - sees the requests for running containers coming to the API and finds a suitable node to run that container on&lt;/li&gt;
&lt;li&gt;each node in a cluster runs two processes - a &lt;strong&gt;kubelet&lt;/strong&gt; and &lt;strong&gt;kube-proxy&lt;/strong&gt;. A &lt;strong&gt;kubelet&lt;/strong&gt; receives requests to run containers, manages any necessary resources and watches over them on the local node. A &lt;strong&gt;kube-proxy&lt;/strong&gt; creates and manages networking rules to expose containers on the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resource types
&lt;/h2&gt;

&lt;p&gt;Orchestration is managed through a set of watch-loops knows as &lt;strong&gt;operators&lt;/strong&gt; or &lt;strong&gt;controllers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each &lt;em&gt;controller&lt;/em&gt; interrogates the &lt;strong&gt;kube-apiserver&lt;/strong&gt; for a particular object state, modifying the object until the declared state matches the current state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a &lt;strong&gt;Pod&lt;/strong&gt; is a group of one or more containers (such as Docker containers), with shared storage/network, and a specification for how to run the containers. A Pod’s contents are always co-located and co-scheduled, and run in a &lt;em&gt;shared context&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;strong&gt;Deployment&lt;/strong&gt; provides declarative updates for Pods and ReplicaSets. You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;strong&gt;ReplicaSet&lt;/strong&gt; is a controller which deploys and restarts Docker containers until the requested number of containers is running&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;strong&gt;DaemonSet&lt;/strong&gt; ensures that a single pod is deployed on every node (often used for logging and metrics)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;strong&gt;StatefulSet&lt;/strong&gt; can be used to deploy pods in a particular order, such that following pods are only deployed if previous pods report a &lt;em&gt;ready&lt;/em&gt; status&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;we can use &lt;strong&gt;labels&lt;/strong&gt; which are part of object metadata&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;nodes can have &lt;a href="https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/" rel="noopener noreferrer"&gt;taints&lt;/a&gt; to ensure pods are not scheduled on inappropriate nodes unless the pod has a &lt;strong&gt;toleration&lt;/strong&gt; in its metadata&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl describe nodes | grep -i taint
Taints:             node-role.kubernetes.io/master:NoSchedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that no pod will be able to schedule onto this node.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Annotations&lt;/strong&gt; are used by third party agents and other tools nut not by Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Kubernetes Master Node
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;runs various server and manager processes for the cluster&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;kube-apiserver&lt;/strong&gt;, &lt;strong&gt;kube-scheduler&lt;/strong&gt; and &lt;strong&gt;etcd&lt;/strong&gt; database are all components of the master node&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kube-apiserver&lt;/strong&gt; is central to the operations of a kubernetes cluster.&lt;br&gt;
All calls are handled via this agent. All actions are accepted and validated by this agent and it is the only agent that connects to the &lt;em&gt;etcd&lt;/em&gt; database. It acts as a master process for the cluster. Each API call goes through authentication, authorisation and several admission controllers. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kube-scheduler&lt;/strong&gt; uses an algorithm to determine which node will host a Pod of containers. Views available resources such  volumes and then deploys based on availability and success.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Etcd&lt;/strong&gt; database stores the state of the cluster, networking and other persistent info. Values are always appended to the end. Previous copies of a data are marked for future removal by compaction process. There is a master database along with several followers. Starting from v1.15.1 &lt;a href="https://github.com/kubernetes/kubeadm" rel="noopener noreferrer"&gt;kubeadm&lt;/a&gt; allows easy deployment of multi master clusters with stacked &lt;em&gt;etcd&lt;/em&gt; clusters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kube-controller-manager&lt;/strong&gt; is a core control loop daemon which interacts with the &lt;em&gt;kube-apiserver&lt;/em&gt; to determine the state of the cluster. If it does not match, the manager will contact the necessary controllers to match the desired state. There are several controllers to use such as &lt;em&gt;endpoints, namespaces&lt;/em&gt; and &lt;em&gt;replication.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Worker Nodes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;all worker nodes run a &lt;strong&gt;kubelet&lt;/strong&gt; and &lt;strong&gt;kube-proxy&lt;/strong&gt; as well as a container engine such as Docker&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;kubelet&lt;/strong&gt; interacts with underlying Docker engine to ensure containers that need to run are actually running - it works to configure the local node until the specifications (in PODSPEC) has been met. Also sends back status to &lt;em&gt;kube-apiserver&lt;/em&gt; for persistence&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;kube-proxy&lt;/strong&gt; manages network connectivity to the containers using IPTABLES entries. Also has &lt;em&gt;userspace&lt;/em&gt; mode, in which it monitors &lt;em&gt;Services&lt;/em&gt; and &lt;em&gt;Endpoints&lt;/em&gt; using a random high number port to proxy traffic&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Pods
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;smallest unit of work - we do not (or rarely) directly interact with containers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;represents a group of co-located containers with some associated data volumes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;containers in a pod share the same network &lt;em&gt;namespace&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;sidecar&lt;/strong&gt; containers perform helper tasks like logging&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each pod is made up of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one or more containers&lt;/li&gt;
&lt;li&gt;one or more volumes&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;pause&lt;/strong&gt; container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;pause&lt;/strong&gt; container is used to get an IP address and then ensure all containers in a pod use its network namespace - containers can die and come back and they still use the same network namespace.&lt;/p&gt;
&lt;h2&gt;
  
  
  Services
&lt;/h2&gt;

&lt;p&gt;A Service is an abstraction which defines a logical set of Pods and a policy by which to access them (sometimes this pattern is called a micro-service)&lt;/p&gt;
&lt;h2&gt;
  
  
  Controllers
&lt;/h2&gt;

&lt;p&gt;In Kubernetes, controllers are control loops that watch the state of a cluster, then make or request changes where needed. Each controller tries to move the current cluster state closer to the desired state. &lt;em&gt;Endpoints, namespace&lt;/em&gt; and &lt;em&gt;serviceaccounts&lt;/em&gt; are examples of controllers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Kubernetes Networking
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Every pod gets its own IP address (Pods can be treated much like VMs or physical hosts from the perspectives of port allocation, naming, service discovery, load balancing, application configuration, and migration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All containers within a pod behave as if they are on the same host with regard to networking. They can all reach each other's ports on &lt;strong&gt;localhost&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pods are assigned IP address prior to container being started. The service object is used to connect pods within the network using &lt;strong&gt;CLUSTERIP&lt;/strong&gt; addresses. From outside the cluster it uses &lt;strong&gt;NODEPORT&lt;/strong&gt; addresses and using a load balancer if configured with a &lt;strong&gt;LOADBALANCER&lt;/strong&gt; service. See &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more details&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ClusterIP&lt;/strong&gt;: Exposes the Service on a cluster-internal IP. Choosing this value makes the Service only reachable from within the cluster. This is the default ServiceType.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NodePort&lt;/strong&gt;: Exposes the Service on each Node’s IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. You’ll be able to contact the NodePort Service, from outside the cluster, by requesting :.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoadBalancer&lt;/strong&gt;: Exposes the Service externally using a cloud provider’s load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ingress&lt;/strong&gt; exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource. - An Ingress can be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; internet
     |
[ Ingress ]
--|-----|--
[ Services ]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;kubeadm&lt;/strong&gt; - kubernetes cluster bootstrapping tool - uses CNI (container network interface) as the default network interface mechanism.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CNI&lt;/strong&gt; is an emerging specification that configure container networking and remove allocated resources when the container is deleted. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Creating a simple pod / service
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create -f &amp;lt;service.yaml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Services and Pods are "linked" as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;pods use labels (key:value pairs) as metadata i.e. labels -&amp;gt; type -&amp;gt; webserver&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the service uses a selector (with same key:value pairs) to match those pods i.e. spec -&amp;gt; selector -&amp;gt; webserver&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a &lt;strong&gt;single container per pod&lt;/strong&gt; allows for the most &lt;strong&gt;granularity&lt;/strong&gt; and &lt;strong&gt;decoupling&lt;/strong&gt;. There are still some reasons to deploy multiple containers, sometimes called composite containers, in a single pod. The secondary containers can handle logging or enhance the primary, the sidecar concept, or acting as a proxy to the outside, the ambassador concept, or modifying data to meet an external format such as an adapter. All three concepts are secondary containers to perform a function the primary container does not.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a simple deployment
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create deployment &amp;lt;deployment_name&amp;gt; --image=&amp;lt;image_name&amp;gt;

kubectl create deployment example --image=busybox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Creating a pod does not take advantage of orchestrating abilities of Kubernetes. &lt;strong&gt;Deployments&lt;/strong&gt; on the other hand give us &lt;strong&gt;scalability&lt;/strong&gt;, &lt;strong&gt;reliability&lt;/strong&gt; and &lt;strong&gt;updates&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Container runtime
&lt;/h2&gt;

&lt;p&gt;A container runtime is the component which runs the containerised application upon request. Docker Engine remains the default for Kubernetes, though cri-o or rkt, and others are gaining community support. &lt;/p&gt;

&lt;p&gt;The Open Container Initiative (OCI) was created to help with portability across systems and environments. Docker donated their lib container project to form a new codebase called runC to support these goals. &lt;/p&gt;

&lt;p&gt;Here is how to create a a Dockerfile&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start Dockerfile with FROM i.e. &lt;code&gt;FROM busybox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then build it with &lt;code&gt;docker build -t myapp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check that image built successfully with &lt;code&gt;docker images&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then run the container with &lt;code&gt;docker run myapp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then push to the registry with &lt;code&gt;docker push&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can also build a local repo to push images to - this adds privacy and reduces bandwidth &lt;br&gt;
Once it is configured, we can docker build, tag and push&lt;/p&gt;

&lt;p&gt;To create a deployment with image version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create deployment &amp;lt;name&amp;gt; —image=&amp;lt;repo&amp;gt;/&amp;lt;app_name&amp;gt;:&amp;lt;version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;i.e.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create deployment test-img —image=1.2.3.4:5001/myapp:v2.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Probes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;LIVENESS PROBE&lt;/strong&gt; -  when to restart a container (deadlocked etc) (if under a controller, it is restarted, else terminated)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;READINESS PROBE&lt;/strong&gt; -  when it is available to accept traffic&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations for good design
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;decoupled resources&lt;/strong&gt; - instead of hard coding a resource in an application, an &lt;em&gt;intermediary&lt;/em&gt;, such as a &lt;strong&gt;Service&lt;/strong&gt;, enables connection and reconnection of other resources, providing flexibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;transience&lt;/strong&gt; - each object should be developed with the expectation that other components will die and be rebuilt. (This allows us to terminate and deploy new versions easily)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;flexible framework&lt;/strong&gt; - many independent resources work together, but decoupled from each other, and without expectations of individual or permanent relationship. This allows for greater flexibility, higher availability and easy scalability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resource Usage
&lt;/h2&gt;

&lt;p&gt;Pods usually use as much CPU and memory as the workload requires. By using &lt;strong&gt;resource requests&lt;/strong&gt; the scheduler will only schedule a pod to a node if the resources exist to meet all requests on that node.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CPU&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(spec.containers[].resources.[ limits | requests ] .cpu&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If pod uses more CPIU than allowed it WON’T be evicted&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;RAM&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(spec.containers[].resources.[ limits | requests ] .memory)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If pod uses more memory than required, it may be restarted or evicted.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Ephemeral Storage - when a container crashes, &lt;em&gt;kubelet&lt;/em&gt; will restart it and all the files will be lost. (Container starts with a clean state). When running containers in a pod, it is often necessary to share files between containers (this is where VOLUMES come in). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;If it uses more storage then specified it will be EVICTED.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Patterns for composite containers
&lt;/h2&gt;

&lt;p&gt;See &lt;a href="https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns/" rel="noopener noreferrer"&gt;https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns/&lt;/a&gt; for mode details on this topic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sidecar Container&lt;/strong&gt; - Adds some functionality not present in the main container such as logging - this allows for a decoupled and scalable approach rather than bloating main container code. &lt;em&gt;Prometheus&lt;/em&gt;  and &lt;em&gt;Fluentd&lt;/em&gt; logging use sidecar containers to collect data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adapter Container&lt;/strong&gt; - Used to &lt;strong&gt;modify (adapt)&lt;/strong&gt; the data either on ingress or egress to match some other need. An adapter would be an efficient way to standardise the output of the main container to be ingested by the monitoring tool without having to modify the monitor of the containerised application. An adapter container transforms multiple applications to a singular view&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ambassador&lt;/strong&gt; - an ambassador allows for access to the outside world without having to implement a service or another entry in an ingress controller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proxy local connection&lt;/li&gt;
&lt;li&gt;Reverse proxy&lt;/li&gt;
&lt;li&gt;Limit HTTP requests&lt;/li&gt;
&lt;li&gt;Re route from main container to the outside world. 
(Open Source Kubernetes-Native API Gateway built on the Envoy Proxy)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Kubernetes Jobs
&lt;/h2&gt;

&lt;p&gt;Jobs are part of the &lt;strong&gt;batch&lt;/strong&gt; API group. They are used to run a set number of pods to completion. If a pod fails, it will be restarted until the number of completion is reached.&lt;br&gt;
A job spec has a &lt;strong&gt;PARALLELISM&lt;/strong&gt; (# of pods to run) and a &lt;strong&gt;COMPLETION&lt;/strong&gt; ( # of pods to run successfully for the job to be considered done).&lt;/p&gt;

&lt;h1&gt;
  
  
  Deployment Configuration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Volumes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a volume is a directory (possibly pre-populated) made available to containers in a Pod&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims" rel="noopener noreferrer"&gt;Kubernetes volume&lt;/a&gt; shares at least the Pod lifetime, not the container within&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;if you want your storage lifetime to be distinct from a Pod, use Persistent Volumes. These allow for volumes to be claimed by a Pod using &lt;em&gt;PersistentVolumeClaims&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a volume can persist longer than a pod, and can be accessed by multiple pods using &lt;strong&gt;PersistentVolumeClaims&lt;/strong&gt;. This allows for state persistency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a volume can be made available to multiple pods, with each given an &lt;strong&gt;access mode&lt;/strong&gt; to write&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;we can pass &lt;strong&gt;encoded&lt;/strong&gt; data to a pod using &lt;strong&gt;Secrets&lt;/strong&gt; and &lt;strong&gt;non-encoded&lt;/strong&gt; data using a &lt;strong&gt;ConfigMap&lt;/strong&gt; - we can pass things like SSH keys, passwords etc...&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;there is no concurrency checking, which means data corruption is probable unless outside locking takes place&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;examples of volume types: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.ceph.com/docs/master/rbd/" rel="noopener noreferrer"&gt;rbd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;NFS&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/" rel="noopener noreferrer"&gt;gcePersistentDisk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;There are three access modes for volumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RWO(ReadWriteOnce)&lt;/strong&gt; - allows read-write by a single node&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROX(ReadOnlyMany)&lt;/strong&gt; - allow read-only by multiple nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RWX(ReadWriteMany)&lt;/strong&gt; - allows read-write by multiple nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a volume is requested, the local &lt;strong&gt;kubelet&lt;/strong&gt; uses the &lt;strong&gt;kubelet_pods.go&lt;/strong&gt; script to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;map raw devices&lt;/li&gt;
&lt;li&gt;determine and make mount point for container&lt;/li&gt;
&lt;li&gt;create symbolic link on the host filesystem to associate the storage with the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If no particular &lt;strong&gt;StorageClass&lt;/strong&gt; was requested, only &lt;em&gt;access_mode&lt;/em&gt; and &lt;em&gt;size&lt;/em&gt; are used as params to create a volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Volume types
&lt;/h2&gt;

&lt;p&gt;There are many different types of volumes such as &lt;strong&gt;GCEpersistentDisk&lt;/strong&gt; or &lt;strong&gt;awsElasticBlockStore&lt;/strong&gt; (GCE and EBS respectively)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;emptyDir&lt;/strong&gt; volume is first created when a Pod is assigned to a Node, and exists as long as that Pod is running on that node. As the name says, it is initially empty. Containers in the Pod can all read and write the same files in the emptyDir volume, though that volume can be mounted at the same or different paths in each Container. &lt;strong&gt;When a Pod is removed from a node for any reason, the data in the emptyDir is deleted forever.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;hostPath&lt;/strong&gt; volume mounts a file or directory from the host node’s filesystem into your Pod. This is not something that most Pods will need, but it offers a powerful escape hatch for some applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>linux</category>
      <category>docker</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
