<?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: hatem ben tayeb</title>
    <description>The latest articles on DEV Community by hatem ben tayeb (@hatembentayeb).</description>
    <link>https://dev.to/hatembentayeb</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%2F412563%2F4f45f7b2-8aa1-49c9-b81a-7681bdb5e99e.jpg</url>
      <title>DEV Community: hatem ben tayeb</title>
      <link>https://dev.to/hatembentayeb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hatembentayeb"/>
    <language>en</language>
    <item>
      <title>Looking for recommendations for provisioning a production AKS cluster</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Mon, 18 Jan 2021 18:03:18 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/looking-for-recommendations-for-provisioning-a-production-aks-cluster-1m7g</link>
      <guid>https://dev.to/hatembentayeb/looking-for-recommendations-for-provisioning-a-production-aks-cluster-1m7g</guid>
      <description></description>
      <category>discuss</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Scale your containers with ansible on a non kubernetes/swarm environment</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Sat, 16 Jan 2021 19:23:35 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/scale-your-containers-with-ansible-on-a-non-kubernetes-swarm-environment-4g78</link>
      <guid>https://dev.to/hatembentayeb/scale-your-containers-with-ansible-on-a-non-kubernetes-swarm-environment-4g78</guid>
      <description>&lt;p&gt;Scaling containers on Kubernetes with Replicatsets or scale services with docker swarm is a very easy task! but what about scaling containers in a non managed environment, when you have high traffic, absolutely you need to scale and load balance the traffic among multiple containers. In this article, we will use Ansible and nginx to scale containers.&lt;/p&gt;

&lt;p&gt;Original post: &lt;a href="https://hatembentayeb.hashnode.dev/scale-your-containers-with-ansible-on-a-non-kubernetesswarm-environment"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>ansible</category>
      <category>nginx</category>
    </item>
    <item>
      <title>Bash: writing a simple pod checker</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Fri, 15 Jan 2021 18:52:52 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/bash-writing-a-simple-pod-checker-49dc</link>
      <guid>https://dev.to/hatembentayeb/bash-writing-a-simple-pod-checker-49dc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I was working with Kubernetes and I just want to check my pods in the current workspace, in a funny way  😅, I will use bash for just one reason, it's the Linux native languages no need for other languages and libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Actually, I was inspired by a cool tool called &lt;strong&gt;popeye&lt;/strong&gt;, you can find it &lt;a href="https://github.com/derailed/popeye" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Make sure you have &lt;strong&gt;Kubectl&lt;/strong&gt; installed and an existing cluster as well, here is my implementation &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Define your favorite colors&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;blanc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;37m"&lt;/span&gt;
&lt;span class="nv"&gt;gray&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[0;37m"&lt;/span&gt;
&lt;span class="nv"&gt;magento&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[0;35m"&lt;/span&gt;
&lt;span class="nv"&gt;red&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;31m"&lt;/span&gt;
&lt;span class="nv"&gt;green&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;32m"&lt;/span&gt;
&lt;span class="nv"&gt;amarillo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;33m"&lt;/span&gt;
&lt;span class="nv"&gt;azul&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;34m"&lt;/span&gt;
&lt;span class="nv"&gt;rescolor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[0m"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find more about colors in bash &lt;a href="https://misc.flogisoft.com/bash/tip_colors_and_formatting" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Get list pods into an Array&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;listPods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get po | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;#echo "$listPods"&lt;/span&gt;
readarray  arr &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;  &lt;span class="nv"&gt;$listPods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NR&amp;gt;1&lt;/code&gt; will skip the first line and &lt;code&gt;print $1&lt;/code&gt; will print the first words (separated by a space) on all lines.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Looping over the array and check the status&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;notok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Sit Down and Wait  &lt;/span&gt;&lt;span class="se"&gt;\U&lt;/span&gt;&lt;span class="s2"&gt;1F602 :&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;do 
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; ... "&lt;/span&gt; 
&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get po &lt;span class="nv"&gt;$i&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $3}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^Running&lt;span class="nv"&gt;$|&lt;/span&gt;^Completed&lt;span class="nv"&gt;$ &lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;  &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[1;31mOh Shit !"&lt;/span&gt;&lt;span class="nv"&gt;$rescolor&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
        notify-send &lt;span class="s2"&gt;"Pods Health"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; was  FUCKED"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 10000 
        &lt;span class="nb"&gt;let &lt;/span&gt;&lt;span class="nv"&gt;notok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notok+1
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[1;32mOK!"&lt;/span&gt;&lt;span class="nv"&gt;$rescolor&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="c"&gt;#notify-send "Pod $i Is Good :)"&lt;/span&gt;
      &lt;span class="nb"&gt;let &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ok+1
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ok&lt;/code&gt; and &lt;code&gt;notok&lt;/code&gt; are used to count the number of the running/not running pods , the &lt;code&gt;${arr[@]}&lt;/code&gt; prints out the whole array, the &lt;code&gt;notify-send&lt;/code&gt; will create a notification on your system is one of the pods are &lt;strong&gt;f&lt;/strong&gt;ked up** .&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Print out the summary&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;STATS:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"+---------------+---------------+"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt;  &lt;span class="s2"&gt;"|&lt;/span&gt;&lt;span class="nv"&gt;$green&lt;/span&gt;&lt;span class="s2"&gt;%-15s&lt;/span&gt;&lt;span class="nv"&gt;$rescolor&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;$red&lt;/span&gt;&lt;span class="s2"&gt;%-15s&lt;/span&gt;&lt;span class="nv"&gt;$rescolor&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"Healthy Pods"&lt;/span&gt; &lt;span class="s2"&gt;"Unhealthy Pods"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"+---------------+---------------+"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt;  &lt;span class="s2"&gt;"|%-15s|%-15s|&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ok&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$notok&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"+---------------+---------------+"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run the script&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Download the scripts with &lt;code&gt;CURL&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://raw.githubusercontent.com/hatembentayeb/podschecker/master/podschecker.sh &lt;span class="nt"&gt;--output&lt;/span&gt; podschecker.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x podschecker.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Demo&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610725608108%2FyrhW96uAh.gif" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610725608108%2FyrhW96uAh.gif" alt="PodHealth.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/hatembentayeb/podschecker" rel="noopener noreferrer"&gt;https://github.com/hatembentayeb/podschecker&lt;/a&gt;&lt;br&gt;
Original blog: &lt;a href="https://hatembentayeb.hashnode.dev/bash-writing-a-simple-pod-checker" rel="noopener noreferrer"&gt;https://hatembentayeb.hashnode.dev/bash-writing-a-simple-pod-checker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scripts don't do much but it was a result of a boring 3 hours on this pandemic&lt;/strong&gt;  🥲&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>bash</category>
      <category>linux</category>
      <category>funny</category>
    </item>
    <item>
      <title>Heroku: Deploy a dockerized Flask ML app</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Tue, 12 Jan 2021 19:49:43 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/heroku-deploy-a-dockerized-flask-ml-app-b2p</link>
      <guid>https://dev.to/hatembentayeb/heroku-deploy-a-dockerized-flask-ml-app-b2p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In this post we will deploy a simple dockerized Machine learning application written with the Flask micro-framework to the Heroku (PaaS), using one of the leading CI/CD tools, which is Gitlab!, all used tools are free and no fees in achieving this tutorial.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Original article: &lt;a href="https://hatembentayeb.hashnode.dev/heroku-deploy-a-dockerized-flask-ml-app"&gt;https://hatembentayeb.hashnode.dev/heroku-deploy-a-dockerized-flask-ml-app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>flask</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Naniiiiiiiiii !!</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Tue, 12 Jan 2021 13:17:24 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/my-github-readme-profile-1258</link>
      <guid>https://dev.to/hatembentayeb/my-github-readme-profile-1258</guid>
      <description>&lt;p&gt;I was inspired by several readme profiles on Github and especially this awesome repository: &lt;a href="https://github.com/coderjojo/creative-profile-readme"&gt;https://github.com/coderjojo/creative-profile-readme&lt;/a&gt;&lt;br&gt;
and finally, I created my own &lt;a href="https://github.com/hatembentayeb"&gt;https://github.com/hatembentayeb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share your readme profile as a comment 😋😋😋&lt;/p&gt;

</description>
      <category>github</category>
      <category>discuss</category>
      <category>profile</category>
      <category>creative</category>
    </item>
    <item>
      <title>Hiding my nodejs application code within a docker container</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Mon, 11 Jan 2021 02:50:39 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/hiding-my-nodejs-application-code-within-a-docker-container-46kh</link>
      <guid>https://dev.to/hatembentayeb/hiding-my-nodejs-application-code-within-a-docker-container-46kh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Building binaries on compiled languages like Rust and Go as Static or dynamic linking makes a huge step on code security, i mean no one can access your code, reading it !, but in many use cases they can by doing some complicated reverse engineering methods ... actually it's hard ! it become more harder when you built your app as a static linking nd bundle all the shit 💩 together. Nodejs applications can be read it easly ... all your source code is accessible, In this post we will hide our node app and "COMPILE" it ! seems owesome right ! let's go folks 😇 !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Original post : &lt;a href="https://hatembentayeb.hashnode.dev/hiding-my-nodejs-application-code-within-a-docker-container"&gt;https://hatembentayeb.hashnode.dev/hiding-my-nodejs-application-code-within-a-docker-container&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>node</category>
      <category>security</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Mailcow: Setting up a full featured self hosted mail server</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Sun, 10 Jan 2021 15:10:02 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/mailcow-setting-up-a-full-featured-self-hosted-mail-server-4511</link>
      <guid>https://dev.to/hatembentayeb/mailcow-setting-up-a-full-featured-self-hosted-mail-server-4511</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Setting up a full featured mail server is an actual &lt;strong&gt;PAIN&lt;/strong&gt; for a system admin, you have to interconnect many software pieces and a lot of testing espacially security and spam if you plan to use it for your company ... In this Post i will take you in my journey of setting up  a  full featured  mail server, i was searching a lot on the internet and i didn't get a sweetable answer to move on .. but finally i successfully made it ! .. It works with multiple domains, secure and no spam problems !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  For the reader
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;I assume you have knowledge about how email works, Mail DNS records, linux, docker and SSL of course. This guide is not for beginners&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you dont have enough knowledge, take a seat and take a look here : &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mail server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is a computer system that sends and receives email &lt;a href="https://techterms.com/definition/mail_server" rel="noopener noreferrer"&gt;&lt;em&gt;source&lt;/em&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MX&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CNAME&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Used to alias one name to another,CNAME stands for Canonical Name. &lt;a href="https://support.dnsimple.com/articles/cname-record/" rel="noopener noreferrer"&gt;&lt;em&gt;source&lt;/em&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DKIM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is an email authentication technique that allows the receiver to check that an email was indeed sent and authorized by the owner of that domain &lt;a href="https://www.dmarcanalyzer.com/dkim/" rel="noopener noreferrer"&gt;&lt;em&gt;source&lt;/em&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SPF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is an email-authentication technique which is used to prevent spammers from sending messages on behalf of your domain  &lt;a href="https://www.dmarcanalyzer.com/spf/" rel="noopener noreferrer"&gt;&lt;em&gt;source&lt;/em&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DMARC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;is an email validation system designed to protect your company’s email domain from being used for email spoofing &lt;a href="https://www.dmarcanalyzer.com/dmarc/" rel="noopener noreferrer"&gt;&lt;em&gt;source&lt;/em&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Mailcow
&lt;/h1&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610109314178%2FO0QJMagRl.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610109314178%2FO0QJMagRl.png" alt="256.png"&gt;&lt;/a&gt;&lt;br&gt;
Mailcow is a free, open source software project. A Mailcow server is a collection of Docker containers running different mail server applications, SOGo, Postfix, Dovecot etc. Mailcow provides a modern and easy to use web interface to create and manage email accounts. You can visit the &lt;a href="https://mailcow.github.io/mailcow-dockerized-docs/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ill not make a comparison about different opensource self hosted mail servers, rather then that i will give you a great repo that includes owesome softwares marked as self hosted, you can check the Email  section  &lt;a href="https://github.com/awesome-selfhosted/awesome-selfhosted#complete-solutions" rel="noopener noreferrer"&gt;come here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want some thoughts about mailcow from real users check this reddit discussions : &lt;a href="https://www.reddit.com/r/selfhosted/comments/hdgau0/whats_the_current_take_on_selfhosting_mail/" rel="noopener noreferrer"&gt;come here&lt;/a&gt; and &lt;a href="https://www.reddit.com/r/selfhosted/comments/4ct285/iredmail_vs_mailcow_vs_mailinabox/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Server Requirements
&lt;/h1&gt;

&lt;p&gt;The recommended OS to run mailcow is &lt;code&gt;ubuntu 18.04&lt;/code&gt;, also don't use cento7 packages on cento8  because the maintainers said : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not use CentOS 8 with Centos 7 Docker packages. You may create an open relay.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the resources i bought a VPS from OVH cloud provider with : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 VCPU &lt;/li&gt;
&lt;li&gt;4 GB of RAM &lt;/li&gt;
&lt;li&gt;80 GB storage &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker installed : &lt;/p&gt;

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

curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://get.docker.com/ | &lt;span class="nv"&gt;CHANNEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stable sh
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker.service
systemctl start docker.service


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

&lt;/div&gt;

&lt;p&gt;Docker-compose installed : &lt;/p&gt;

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

curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/docker/compose/releases/download/&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-Ls&lt;/span&gt; https://www.servercow.de/docker-compose/latest.php&lt;span class="si"&gt;)&lt;/span&gt;/docker-compose-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/docker-compose


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

&lt;/div&gt;

&lt;p&gt;If you have a firewall, you should allow those ports : &lt;/p&gt;

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

netstat &lt;span class="nt"&gt;-tulpn&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'25|80|110|143|443|465|587|993|995|4190'&lt;/span&gt;


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : you must have a clean server means no other applications or a reverse proxy becaue mailcow has every thing in place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay now every thing is good ! but there is another thing to verify which is our IP !!! &lt;br&gt;
we have to test it if it is &lt;strong&gt;blacklisted&lt;/strong&gt;, if yes it's a huge problem ... we can't proceed anymore because your  mails will not be delivered, it's all about your host &lt;strong&gt;reputation&lt;/strong&gt;!. &lt;/p&gt;

&lt;p&gt;We can check it with an online service called &lt;strong&gt;mxtoolbox&lt;/strong&gt; by visiting this url &lt;a href="https://mxtoolbox.com/SuperTool.aspx" rel="noopener noreferrer"&gt;mxtoolbox&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Basic DNS configuration
&lt;/h1&gt;

&lt;p&gt;Before we proceed we have to get a domain name from any provider, i got one from &lt;code&gt;OVH&lt;/code&gt;, and i delegated to &lt;code&gt;AZURE DNS SERVICE&lt;/code&gt;, it dosen't matter actually, we will have the same configuration in any provider.&lt;/p&gt;

&lt;p&gt;I pretend that i have : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Root Domain name&lt;/strong&gt; : mymailserver.com&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server IP address&lt;/strong&gt; : 1.2.3.4 &lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Create an &lt;strong&gt;A&lt;/strong&gt; record
&lt;/h2&gt;

&lt;p&gt;In your provider DNS pannel add an &lt;code&gt;A&lt;/code&gt; record like this &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : mail &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; : A&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt; : 1.2.3.4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can confirm with the &lt;code&gt;dig&lt;/code&gt; command : &lt;/p&gt;

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

 dig mail.mymailserver.com +noall +answer


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Create the &lt;strong&gt;CNAME&lt;/strong&gt; records
&lt;/h2&gt;

&lt;p&gt;In your provider DNS pannel add a &lt;code&gt;CNAME&lt;/code&gt; record for the &lt;code&gt;autoconfig&lt;/code&gt; like this &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : autoconfig &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; : CNAME&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alias&lt;/strong&gt; : mail.mymailserver.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test it with : &lt;code&gt;dig autoconfig.mymailserver.com +noall +answer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add another &lt;code&gt;CNAME&lt;/code&gt; record for the &lt;code&gt;autodiscover&lt;/code&gt; like this :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : autodiscover &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; : CNAME&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alias&lt;/strong&gt; : mail.mymailserver.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test it with : &lt;code&gt;dig autodiscover.mymailserver.com +noall +answer&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Create the an MX record
&lt;/h2&gt;

&lt;p&gt;In your root domain add an MX domain to point to your mail server domain : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : @/empty
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; : MX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefenerence/periorty&lt;/strong&gt; : 10 or 0 &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mail Exchange&lt;/strong&gt; : mail.mymailserver.com&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  rDNS configuration
&lt;/h2&gt;

&lt;p&gt;Get more information about &lt;code&gt;rDNS&lt;/code&gt; &lt;a href="https://help.returnpath.com/hc/en-us/articles/360019088671-How-to-set-up-reverse-DNS-zone-and-PTR-records-" rel="noopener noreferrer"&gt;here&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You can configure your rDNS on the provider of your &lt;code&gt;server&lt;/code&gt; and change the generated &lt;code&gt;domain name&lt;/code&gt; to your mail domain &lt;code&gt;mail.mymailserver.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and test it with &lt;code&gt;dig -x 1.2.3.4 +noall +answer&lt;/code&gt;, should get &lt;code&gt;mail.mymailserver.com&lt;/code&gt; as responce.&lt;/p&gt;
&lt;h1&gt;
  
  
  Security DNS Records
&lt;/h1&gt;

&lt;p&gt;Authenticate your mail server and protect it from &lt;a href="https://www.serversmtp.com/what-is-dkim/" rel="noopener noreferrer"&gt;Fake identities&lt;/a&gt;  and  &lt;a href="https://www.barracuda.com/glossary/domain-spoofing" rel="noopener noreferrer"&gt;Domain spoofing attacks&lt;/a&gt; we have to setup those records &lt;/p&gt;
&lt;h2&gt;
  
  
  SPF record
&lt;/h2&gt;

&lt;p&gt;In your root domain add a &lt;code&gt;TXT&lt;/code&gt; record like this : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : @/empty
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; : TXT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;value&lt;/strong&gt; : v=spf1 ip4:1.2.3.4 -all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test it with &lt;code&gt;dig mymailserver.com TXT&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  DKIM record
&lt;/h2&gt;

&lt;p&gt;Setting up the dkim record needs a public key to be inserted in the record here but we can't now because we have to install mailcow and get the public key given by our mail server, we will leave it empty for now. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : dkim._domainkey&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; : TXT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;value&lt;/strong&gt; : v=DKIM1;k=rsa;t=s;s=email;&lt;strong&gt;p=&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  DMARC record
&lt;/h2&gt;

&lt;p&gt;The best thing you can do for dmarc is to make a free account on &lt;a href="https://dmarcian.com/" rel="noopener noreferrer"&gt;dmarcian&lt;/a&gt;, it will give you a record like this : &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DMARC1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;reject&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;rua&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mailto:&amp;lt;dmarc email1&amp;gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;ruf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;dmarc email2&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;Just add it to your domain DNS pannel like this : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : _dmarc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; : default &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;value&lt;/strong&gt; : v=DMARC1; p=reject; rua=mailto: dmarc email1 ; ruf=dmarc email2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test is with : &lt;code&gt;dig _dmarc.mymailserver.com TXT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's it for the DNS configurations, all records are in place execpt the dkim value we ill add it later&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Install mailcow
&lt;/h1&gt;

&lt;p&gt;You need &lt;code&gt;git&lt;/code&gt; installed to clone the repo in your server : &lt;/p&gt;

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

 git clone https://github.com/mailcow/mailcow-dockerized
 &lt;span class="nb"&gt;cd &lt;/span&gt;mailcow-dockerized


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

&lt;/div&gt;

&lt;p&gt;Your in the repo now, so run the script &lt;code&gt;./generate_config.sh&lt;br&gt;
&lt;/code&gt; to generate your config, for the hostname add &lt;code&gt;mail.mymailserver.com&lt;/code&gt;, it will request a certificate from &lt;code&gt;Let'svEncrypt&lt;/code&gt; automatically. &lt;/p&gt;

&lt;p&gt;To run your mail server just use this command : &lt;/p&gt;

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

docker-compose pull
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;You need to wait some time to download all the containers, run them and provision a SSL certificate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The default credentials to access are &lt;code&gt;admin&lt;/code&gt; &amp;amp; &lt;code&gt;moohoo&lt;/code&gt; (you must change it with your own)&lt;/p&gt;

&lt;p&gt;Verify that all containers are healthy with the &lt;code&gt;green icon&lt;/code&gt; like this :&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610120906694%2FXD99OD4X3.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610120906694%2FXD99OD4X3.png" alt="Screenshot_2021-01-08 mailcow UI.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well done ! Let's proceed to add domains and mailboxes&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Configure Mailcow
&lt;/h1&gt;

&lt;p&gt;Our mail server is working now but there is neither domains or mailboxs configured. in this section we will go step by step .. let's GO !&lt;/p&gt;

&lt;h2&gt;
  
  
  Add your domain
&lt;/h2&gt;

&lt;p&gt;Login to your mailcow admin panel :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;code&gt;configuration&lt;/code&gt; on the top bar then select &lt;code&gt;mail setup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;add domain&lt;/code&gt; then add your root domain &lt;code&gt;mymailserver.com&lt;/code&gt; and click on &lt;code&gt;Add domain and restart SoGo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;configuration&lt;/code&gt; again then click on &lt;code&gt;configuration &amp;amp; details&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In the hosizontal menu click &lt;code&gt;configuration&lt;/code&gt; and click on &lt;code&gt;ARC/DKIM keys&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Scroll to bottom and fill the &lt;code&gt;domain/s&lt;/code&gt; form with your domain &lt;code&gt;mymailserver.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On the selector type &lt;code&gt;dkim&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;2048&lt;/code&gt; on the &lt;code&gt;DKIM key length&lt;/code&gt; and click &lt;code&gt;add&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the generated public key that starts with &lt;code&gt;v=DKIM1;k=rsa;t=s;s=email;p= ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to your DNS pannel and modify the &lt;code&gt;TXT&lt;/code&gt; record with the generated value&lt;/li&gt;
&lt;li&gt;Wait until your DNS modification propagate
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can validate your configuration for &lt;code&gt;SPF&lt;/code&gt;, &lt;code&gt;DKIM&lt;/code&gt; and &lt;code&gt;DMARC&lt;/code&gt; with this        &lt;a href="https://dmarcian.com/" rel="noopener noreferrer"&gt;site&lt;/a&gt;, you should get result like this (don't forget to put &lt;code&gt;dkim&lt;/code&gt; as a selector` :&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610122571096%2FeV1_YdWK8.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610122571096%2FeV1_YdWK8.png" alt="Screenshot_2021-01-08 Free DMARC Domain Check Is Your Domain Protected - dmarcian.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Mailbox for mymailserver.com
&lt;/h2&gt;

&lt;p&gt;By default mailcow give &lt;code&gt;10GB&lt;/code&gt; of storage to every domain and every mailbox under that domain has &lt;code&gt;3GB&lt;/code&gt; of storage .. so feel free to modify them to your needs, &lt;/p&gt;

&lt;p&gt;To create a mailbox folow those steps : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Under &lt;code&gt;configuration&lt;/code&gt; on the to bar click &lt;code&gt;mail setup&lt;/code&gt; and select mailboxes`&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;add mailbox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Choose a &lt;code&gt;username&lt;/code&gt; , your domain (it will be loaded automatically) and then add a password and click &lt;code&gt;add&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On the top bar click &lt;code&gt;apps&lt;/code&gt; and then &lt;code&gt;webmail&lt;/code&gt;, you will be redirected to the &lt;code&gt;SoGo&lt;/code&gt; UI , login wiyh your newly created mail and password&lt;/li&gt;
&lt;li&gt;on the bottom click on the green icon, you will get an interface of sending a mail &lt;/li&gt;
&lt;li&gt;In your browser open another tab and go to this site : mail-tester.com and copy the mail address.&lt;/li&gt;
&lt;li&gt;Return to your &lt;code&gt;SoGo&lt;/code&gt; interface and send a mail with that mail, on the body make sure to add some text with a least two paragraphs.&lt;/li&gt;
&lt;li&gt;Wait 10 seconds and go the mail-tester.com and click on &lt;code&gt;then check your score&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And &lt;strong&gt;BOOM&lt;/strong&gt; you should get this result &lt;strong&gt;You Can Send&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610123568730%2FQfI2NwVUH.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1610123568730%2FQfI2NwVUH.png" alt="Screenshot_2021-01-07 Spam Test Result.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to send a mail to your friends and your own Gmail address, don't worry Gmail still don't know your mail server, so he will placed as spam, just &lt;code&gt;unspam&lt;/code&gt; it !, try to send it to zoho mail for example. The test again with other tools i suggest to use : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://mxtoolbox.com/SuperTool.aspx" rel="noopener noreferrer"&gt;Mxtoolbox SuperTool&lt;/a&gt; and select &lt;code&gt;test mail server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dkimvalidator.com/" rel="noopener noreferrer"&gt;Dkimvalidator&lt;/a&gt; and send an email to the given address, make sure that all tests are oassed like the &lt;code&gt;dmarcian.com&lt;/code&gt; tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add another domain to mailcow
&lt;/h2&gt;

&lt;p&gt;I assume we have a second domain : myseconddomain.com.&lt;br&gt;
I will not repeat the same steps it's quite easy so let's go :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create A record : &lt;code&gt;mail.myseconddomain.com&lt;/code&gt; that points to your Host &lt;code&gt;1.2.3.4&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Create a CNAME record : &lt;code&gt;autoconfig&lt;/code&gt; that points to &lt;code&gt;mail.myseconddomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a CNAME record : &lt;code&gt;autodiscover&lt;/code&gt; that points to &lt;code&gt;mail.myseconddomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create an MX record: on the root domain add &lt;code&gt;10&lt;/code&gt; as periority and &lt;code&gt;mail.myseconddomain.com&lt;/code&gt; as target &lt;/li&gt;
&lt;li&gt;Create SPF record like we did earlier&lt;/li&gt;
&lt;li&gt;Create DMARC record as we did earlier &lt;/li&gt;
&lt;li&gt;Create DKIM record and in the same time add your new domain as we did earlier and copy the generated &lt;code&gt;DKIM key&lt;/code&gt; to your &lt;code&gt;DKIM&lt;/code&gt; record. &lt;/li&gt;
&lt;li&gt;Validate your records &lt;/li&gt;
&lt;li&gt;Add a mailbox under your new domain and send an email to mail-tester.com and dkimvalidator.com, you should get 10/10 sweetheart :)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;p&gt;Don't send a lot of mails directly you will be blocked ! ... so start step by step  by sending 20 mails per day for the first week then try sending 80 the next week ... after 2 months you can send a lot of mails like 1000 mails, but take in mind that you need to add the &lt;code&gt;list unsubscribe headers&lt;/code&gt; to your postfix to allow users to unsubscribe against your newsletters/subscriptions. &lt;/p&gt;

&lt;p&gt;please follow me on twitter &lt;a href="https://twitter.com/hatembentayeb" rel="noopener noreferrer"&gt;@hatem ben tayeb&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>selfhosted</category>
      <category>mailcow</category>
      <category>email</category>
    </item>
    <item>
      <title>Delivering React .. The hard way !</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Thu, 31 Dec 2020 15:40:47 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/delivering-react-the-hard-way-k86</link>
      <guid>https://dev.to/hatembentayeb/delivering-react-the-hard-way-k86</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In  this Post  we will setup a React pipeline using Gitlab,Ansible and docker. we will go throught the whole process from nothing to a fast, reliable and hightly customizable pipeline with multi-environment deployment. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;center&gt; &lt;h2&gt;**Daah ! let's start i can't wait 😍 !**&lt;/h2&gt;
&lt;/center&gt;




&lt;center&gt;&lt;h1&gt;😜&lt;/h1&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Tools&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we start we need to define the damn 🔥  tech stack :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gitlab&lt;/strong&gt; : GitLab is a web-based DevOps lifecycle tool that provides a Git-repository manager providing wiki, issue-tracking and continuous integration and deployment pipeline features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ansible&lt;/strong&gt; : Ansible is the simplest way to automate apps and IT infrastructure. Application Deployment + Configuration Management + Continuous Delivery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt;: Docker is a tool designed to make it easier to create, deploy, and run applications by using containers.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: if you dont know nothing about those tools ... No problem .... aaah actually it's a problem 😐 ... we are diving into advanced topic here 😝 ...&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;center&gt;&lt;h2&gt;😕Come on ! i was kidding 😄 &lt;/h2&gt;&lt;/center&gt;

&lt;center&gt;&lt;h6&gt; Actually no ... &lt;br&gt;contact me if you need some support&lt;/h6&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;yoo .. we have to draw the global environment architecture  to get the whole picture about what we will do here 👌 ... dont start coding directly. Daaah 😤 ... you have to think by compiling the whole process in mind 😎&lt;/p&gt;

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

&lt;p&gt;Of course we will create a repository ( i will not explain that 😏) on gitlab with a hello world react app  ( i will not explain that 😏) and push it there. &lt;/p&gt;

&lt;p&gt;Let's break down the architecture now : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Block 1&lt;/strong&gt; : this where our code application resides and the whole gitlab eco-system also, all configuration to start a pipeline must be there, actually you can install gitlab on your own servers .. but it's not the aim of this post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Block 2&lt;/strong&gt;: this is the important block for now (The CI environment)  .. actually it is the server when all the dirty 💩 work resides like buiding docker containers .. saving cache ... testing code and so on ... we must configure this environment with love ❤️ haha yeah with love ... it's is the base of the pipeline speed and low level configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Block 3&lt;/strong&gt; : the target environments where we will deploy our application using ansible playbooks via a secure tunnel .. &lt;strong&gt;SSH&lt;/strong&gt; ... BTW i love you SSH 😄 because we will not install any runners on those targets servers we will interact with them only with ansible to ensure a clean deployment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;center&gt;&lt;h1&gt;😙&lt;/h1&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;CI environment&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this section we will connect our gitlab repo to the &lt;strong&gt;CI environment machine&lt;/strong&gt; and install the gitlab runner on it of course. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to your repo ... under &lt;code&gt;settings --&amp;gt;  CI/CD --&amp;gt; runners&lt;/code&gt; and get the gitlab url and the token associeted to ... dont loose it 😑&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You should have a VPS or a virtual machine on the cloud ... i will work on an azure virtual machine with ubuntu 18.04 installed &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install docker of course ... it's simple &lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;come here&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installing the gitlab runner :&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LJO&lt;/span&gt; &lt;span class="s2"&gt;"https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_&amp;lt;arch&amp;gt;.deb"&lt;/span&gt;

dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; gitlab-runner_&amp;lt;&lt;span class="nb"&gt;arch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Gitlab will be installed as service on your machine but i don't you can encounter a problem when starting it ... (don't ask me i don't know 😑 ) so you can start it as follow :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gitlab runner run &amp;amp; &lt;span class="c"&gt;# it will work on background &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now register the runner with &lt;code&gt;gitlab-runner register&lt;/code&gt; and follow the instructions ... dont loose the token or reset it ... if you reset the token you have to re-register the runner again. i will make things easier ...  here is my &lt;code&gt;config.toml&lt;/code&gt; under &lt;code&gt;/etc/gitlab-runner/config.toml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;concurrent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; 
&lt;span class="py"&gt;check_interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="nn"&gt;[session_server]&lt;/span&gt;
  &lt;span class="py"&gt;session_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;

&lt;span class="nn"&gt;[[runners]]&lt;/span&gt;
  &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"runner-name"&lt;/span&gt;
  &lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://gitlab.com/"&lt;/span&gt;
  &lt;span class="py"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"runner-token"&lt;/span&gt;
  &lt;span class="py"&gt;executor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"docker"&lt;/span&gt;
  &lt;span class="py"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nn"&gt;[runners.custom_build_dir]&lt;/span&gt;
  &lt;span class="nn"&gt;[runners.cache]&lt;/span&gt;
    &lt;span class="nn"&gt;[runners.cache.s3]&lt;/span&gt;
    &lt;span class="nn"&gt;[runners.cache.gcs]&lt;/span&gt;
    &lt;span class="nn"&gt;[runners.cache.azure]&lt;/span&gt;
  &lt;span class="nn"&gt;[runners.docker]&lt;/span&gt;
    &lt;span class="py"&gt;pull_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"if-not-present"&lt;/span&gt;
    &lt;span class="py"&gt;tls_verify&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"alpine"&lt;/span&gt;
    &lt;span class="py"&gt;privileged&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="py"&gt;disable_entrypoint_overwrite&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;oom_kill_disable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;disable_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;volumes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"/cache:/cache"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="py"&gt;shm_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;let's make a breakdown here ... &lt;/p&gt;

&lt;p&gt;This runner will run 9 concurent jobs on a docker containers (docker in docker)  based on the alpine container (&lt;em&gt;to make a clean build&lt;/em&gt;) ... The runner will pull new versions of images if they are not present ... This is optional you can turn it to &lt;strong&gt;always&lt;/strong&gt; but we need to speed up the build ... No need to pull the same image again and again if there is no updates ... The runner will save the cache  on the current machine under &lt;code&gt;/cache&lt;/code&gt; on the host and pass it in use as a docker volume  to save some minutes when gitlab by default upload the zipped cache to it's own storage and download it again ... It's painfull when the cache is becoming huge. At some point on time the cache will be so big .. So you can make your hand dirty and delete the shit 💩&lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**We are almost done 😍**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;p&gt;Now you can go the repository under &lt;code&gt;settings --&amp;gt;  CI/CD --&amp;gt; runners&lt;/code&gt;  and verify that the runner was registred successfully ( &lt;em&gt;the green icon&lt;/em&gt; ) &lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;h2&gt;
  
  
  &lt;strong&gt;The react pipeline&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;let's code the pipeline now 😄 .... wait a second !!! we need the architecture as the previous section ... so here is how the pipeline will look like ... &lt;/p&gt;

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

&lt;p&gt;This pipeline aims to support the folowing features : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching node modules for faster build&lt;/li&gt;
&lt;li&gt;Docker for shiping containers&lt;/li&gt;
&lt;li&gt;Gitlab private registry linked to the repo &lt;/li&gt;
&lt;li&gt;Ship only &lt;code&gt;/build&lt;/code&gt; on the container with nginx web server&lt;/li&gt;
&lt;li&gt;Tag containers with the git SHA-COMMIT&lt;/li&gt;
&lt;li&gt;Deploy containers with an ansible playbook&lt;/li&gt;
&lt;li&gt;SSH configuration as a gitlab secret to secure the target IP&lt;/li&gt;
&lt;li&gt;Only ssh keypairs used for authentication with the target server ... no damn passwords 💩 ... &lt;/li&gt;
&lt;/ul&gt;

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;h2&gt;
  
  
  &lt;strong&gt;Defining Secrets&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This pipeline needs some variables to be placed in gitlab as secrets on &lt;code&gt;settings --&amp;gt; CI/CD --&amp;gt; Variables&lt;/code&gt; : &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ANSIBLE_KEY&lt;/td&gt;
&lt;td&gt;The target server ssh private key 😐&lt;/td&gt;
&lt;td&gt;file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GITLAB_REGISTRY_PASS&lt;/td&gt;
&lt;td&gt;Gitlab registry password (your account password 😐)&lt;/td&gt;
&lt;td&gt;variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GITLAB_REGISTRY_USER&lt;/td&gt;
&lt;td&gt;Gitlab registry login  (your account user 😐)&lt;/td&gt;
&lt;td&gt;variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH_CFG&lt;/td&gt;
&lt;td&gt;The regular ssh config that contains the target IP&lt;/td&gt;
&lt;td&gt;file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;SSH_CFG&lt;/code&gt; looks like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;Host&lt;/span&gt; *
   &lt;span class="k"&gt;StrictHostKeyChecking&lt;/span&gt; &lt;span class="no"&gt;no&lt;/span&gt;

&lt;span class="k"&gt;Host&lt;/span&gt; dev 
    HostName &amp;lt;IP&amp;gt;
    &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ./keys/keyfile
    &lt;span class="k"&gt;User&lt;/span&gt; root

&lt;span class="k"&gt;Host&lt;/span&gt; staging 
    HostName &amp;lt;IP&amp;gt;
    &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ./keys/keyfile
    &lt;span class="k"&gt;User&lt;/span&gt; root

&lt;span class="k"&gt;Host&lt;/span&gt; prod 
    HostName &amp;lt;IP&amp;gt;
    &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ./keys/keyfile
    &lt;span class="k"&gt;User&lt;/span&gt; root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will not explain this 😭 ... &lt;a href="https://linuxize.com/post/using-the-ssh-config-file/" rel="noopener noreferrer"&gt;come here&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;center&gt;&lt;h2&gt;**[KNOCK KNOCK](https://bongo.cat/) ... are you still here 😺**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;center&gt;Thank god 😄 ! his here 👶 ... let's continue  then be ready 🔥  ... &lt;/center&gt; 

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;h2&gt;
  
  
  &lt;strong&gt;Preparing Dockerfile&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before writing the &lt;code&gt;dockerfile&lt;/code&gt; take in mind that the steup should be compatible with the pipeline architecture ... if you remember we have a separate jobs for :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing node modules &lt;/li&gt;
&lt;li&gt;Run the build process &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the Dockerfile must contain only the builded assets only to be served by nginx 😄 &lt;/p&gt;

&lt;p&gt;Here is our sweet 🐭 Dockerfile :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:1.16.0-alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build/  /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt;  /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.old
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dockerfile does not do too much work, it just take the &lt;code&gt;/build directory&lt;/code&gt; and copy it under &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; to be served.&lt;/p&gt;

&lt;p&gt;Also we need a basic nginx config like follow to be under &lt;code&gt;/etc/nginx/conf.d&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;index&lt;/span&gt;  &lt;span class="s"&gt;index.html&lt;/span&gt; &lt;span class="s"&gt;index.htm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kn"&gt;error_page&lt;/span&gt;   &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;  &lt;span class="n"&gt;/50x.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/50x.html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html&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;You see ! 👀 its simple let's proceed to setup the &lt;code&gt;ansible playbook&lt;/code&gt; for the deployment process ... hurry up 😐&lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;h2&gt;
  
  
  &lt;strong&gt;Deployment with ansible&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We are almost done ! the task now is to write the ansible playbook that will do the folowing :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a docker network and specify the the gateway address&lt;/li&gt;
&lt;li&gt;Authenticate the gitlab registry &lt;/li&gt;
&lt;li&gt;Start the container with the suitable configurations &lt;/li&gt;
&lt;li&gt;Clean the unsed containers and volumes &lt;/li&gt;
&lt;li&gt;Most setup will be in the &lt;code&gt;inventory file&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look at the &lt;code&gt;inventory_file&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dev]&lt;/span&gt;
&lt;span class="err"&gt;devserver&lt;/span&gt; &lt;span class="py"&gt;ansible_ssh_host&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;dev&lt;/span&gt; &lt;span class="py"&gt;ansible_ssh_user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;root&lt;/span&gt; &lt;span class="py"&gt;ansible_python_interpreter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;/usr/bin/python&lt;/span&gt;

&lt;span class="nn"&gt;[dev:vars]&lt;/span&gt;
&lt;span class="py"&gt;c_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;lookup(&lt;/span&gt;&lt;span class="s"&gt;'env'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'CI_PROJECT_NAME'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}-dev&lt;/span&gt; &lt;span class="c"&gt;#container name&lt;/span&gt;
&lt;span class="py"&gt;h_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;lookup(&lt;/span&gt;&lt;span class="s"&gt;'env'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'CI_PROJECT_NAME'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}-dev&lt;/span&gt; &lt;span class="c"&gt;#host name&lt;/span&gt;
&lt;span class="py"&gt;subnet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;172.30&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c"&gt;# network gateway                                         &lt;/span&gt;
&lt;span class="py"&gt;network_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;project_name_dev&lt;/span&gt;
&lt;span class="py"&gt;registry_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;lookup(&lt;/span&gt;&lt;span class="s"&gt;'env'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'CI_REGISTRY'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;                          
&lt;span class="py"&gt;registry_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;lookup(&lt;/span&gt;&lt;span class="s"&gt;'env'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'GITLAB_REGISTRY_USER'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;    
&lt;span class="py"&gt;registry_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;lookup(&lt;/span&gt;&lt;span class="s"&gt;'env'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'GITLAB_REGISTRY_PASS'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;  
&lt;span class="py"&gt;image_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;lookup(&lt;/span&gt;&lt;span class="s"&gt;'env'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'CI_REGISTRY_IMAGE'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}:{{&lt;/span&gt; &lt;span class="err"&gt;lookup('env','CI_COMMIT_SHORT_SHA')&lt;/span&gt; &lt;span class="err"&gt;}}-dev&lt;/span&gt; 

&lt;span class="nn"&gt;[project_network:children]&lt;/span&gt;
&lt;span class="err"&gt;dev&lt;/span&gt;
&lt;span class="nn"&gt;[project_clean:children]&lt;/span&gt;
&lt;span class="err"&gt;dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ansible_ssh_host=dev&lt;/code&gt; refers to the &lt;code&gt;SSH_CFG&lt;/code&gt; configuration.&lt;/p&gt;

&lt;p&gt;Gitlab by default exports many useful environment variables like :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CI_PROJECT_NAME&lt;/code&gt; : the repo name &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CI_COMMIT_SHORT_SHA&lt;/code&gt; : the sha commit ID to tag the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can explore all variables &lt;a href="https://docs.gitlab.com/ee/ci/variables/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's move now to the playbook ... i'm tired damn it haha .. it is a long post ... okay nevermind come on .. &lt;/p&gt;

&lt;p&gt;Here is the ansible playbook :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project_network&lt;/span&gt;
  &lt;span class="c1"&gt;#become: yes # for previlged user&lt;/span&gt;
  &lt;span class="c1"&gt;#become_method: sudo   # for previlged user&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                                                     
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create docker network&lt;/span&gt;
    &lt;span class="na"&gt;docker_network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;network_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;ipam_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;subnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;subnet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}.0/16"&lt;/span&gt;
          &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;subnet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}.1"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;gather_facts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
  &lt;span class="c1"&gt;#become: yes # for previlged user&lt;/span&gt;
  &lt;span class="c1"&gt;#become_method: sudo   # for previlged user&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Log into gitlab registry and force re-authorization&lt;/span&gt;
    &lt;span class="na"&gt;docker_login&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;registry_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;registry_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;registry_password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;reauthorize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start the container&lt;/span&gt;
    &lt;span class="na"&gt;docker_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;c_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;image &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;image_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;pull&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;restart_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;h_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="c1"&gt;# volumes:&lt;/span&gt;
      &lt;span class="c1"&gt;#   - /some/path:/some/path&lt;/span&gt;
      &lt;span class="na"&gt;exposed_ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80"&lt;/span&gt;
      &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;network_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;ipv4_address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;subnet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}.2"&lt;/span&gt;
      &lt;span class="na"&gt;purge_networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project_clean&lt;/span&gt;
  &lt;span class="c1"&gt;#become: yes # for previlged user&lt;/span&gt;
  &lt;span class="c1"&gt;#become_method: sudo   # for previlged user&lt;/span&gt;
  &lt;span class="na"&gt;gather_facts &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt; 
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Removing exited containers&lt;/span&gt;
    &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker ps -a -q -f status=exited | xargs --no-run-if-empty docker rm --volumes&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Removing untagged images&lt;/span&gt;
    &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker images | awk '/^&amp;lt;none&amp;gt;/ { print $3 }' | xargs --no-run-if-empty docker rmi -f&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Removing volume directories&lt;/span&gt;
    &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker volume ls -q --filter="dangling=true" | xargs --no-run-if-empty docker volume rm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This playbook is a life saver because we configure the container automatically before starting it ... no setup on the remote host ... we can deploy the same in any other servers based on linux. the container update is quite simple .. ansible will take care of stopping the container and starting new one with different tag and then clean up the shit 💩&lt;/p&gt;

&lt;p&gt;We can also make a &lt;code&gt;rollback&lt;/code&gt; to the previous container by going to the previous pipeline history on gitlab and restart the lastest job &lt;code&gt;the deploy job&lt;/code&gt; because we have already an existing container on the registry 😄 &lt;/p&gt;

&lt;p&gt;The setup is for &lt;code&gt;dev&lt;/code&gt; environment you can copy paste the two files for the &lt;code&gt;prod&lt;/code&gt; &amp;amp; &lt;code&gt;staging&lt;/code&gt; environment ... &lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

&lt;h2&gt;
  
  
  &lt;strong&gt;Setting up the Pipeline&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The pipeline will deploy to the three environments as i mentioned on the top of this post ... &lt;/p&gt;

&lt;p&gt;Here is the full pipeline code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;DOCKER_IMAGE_PRODUCTION &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE&lt;/span&gt; 
  &lt;span class="na"&gt;DOCKER_IMAGE_TEST &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE&lt;/span&gt;   
  &lt;span class="na"&gt;DOCKER_IMAGE_DEV &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE&lt;/span&gt;


&lt;span class="c1"&gt;#caching node_modules folder for later use  &lt;/span&gt;
&lt;span class="na"&gt;.example_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;example_cache&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;


&lt;span class="na"&gt;stages &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prep&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build_dev&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;push_registry_dev&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy_dev&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build_test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;push_registry_test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy_test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build_production&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;push_registry_production&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy_production&lt;/span&gt;


&lt;span class="c1"&gt;########################################################&lt;/span&gt;
&lt;span class="c1"&gt;##                                                                                                                                 ##&lt;/span&gt;
&lt;span class="c1"&gt;##     Development: autorun after a push/merge                                               ## &lt;/span&gt;
&lt;span class="c1"&gt;##                                                                                                                                 ##&lt;/span&gt;
&lt;span class="c1"&gt;########################################################&lt;/span&gt;

&lt;span class="na"&gt;install_dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:12.2.0-alpine&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prep&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*example_cache&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci --log-level=error&lt;/span&gt; 

  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;  
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;  &lt;span class="s"&gt;runner_name&lt;/span&gt; 
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;refs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prod_branch&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;staging_branch&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dev_branch&lt;/span&gt;
    &lt;span class="na"&gt;changes &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.json"&lt;/span&gt;

&lt;span class="na"&gt;build_react_dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:12.2.0-alpine&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build_dev&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*example_cache&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CI &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat .env.dev &amp;gt; .env&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build/&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;  &lt;span class="s"&gt;runner_name&lt;/span&gt; 
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"dev_branch"'&lt;/span&gt;


&lt;span class="na"&gt;build_image_dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push_registry_dev&lt;/span&gt;
  &lt;span class="na"&gt;image &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:19&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:19-dind&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp://docker:2375/&lt;/span&gt;
    &lt;span class="na"&gt;DOCKER_DRIVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overlay2&lt;/span&gt;
    &lt;span class="na"&gt;DOCKER_TLS_CERTDIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# docker login asks for the password to be passed through stdin for security&lt;/span&gt;
  &lt;span class="c1"&gt;# we use $CI_JOB_TOKEN here which is a special token provided by GitLab&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo -n $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build  --tag $DOCKER_IMAGE_DEV:$CI_COMMIT_SHORT_SHA-dev .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $DOCKER_IMAGE_DEV:$CI_COMMIT_SHORT_SHA-dev&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;runner_name&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"dev_branch"'&lt;/span&gt;


&lt;span class="na"&gt;deploy_dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy_dev&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;willhallonline/ansible:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ${SSH_CFG} &amp;gt; "$CI_PROJECT_DIR/ssh.cfg"&lt;/span&gt;               
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mkdir -p "$CI_PROJECT_DIR/keys"&lt;/span&gt;                            
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ${ANSIBLE_KEY} &amp;gt; "$CI_PROJECT_DIR/keys/keyfile"&lt;/span&gt;      
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chmod og-rwx "$CI_PROJECT_DIR/keys/keyfile"&lt;/span&gt;    
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $CI_PROJECT_DIR &amp;amp;&amp;amp; ansible-playbook  -i deployment/inventory_dev --ssh-extra-args="-F $CI_PROJECT_DIR/ssh.cfg -o ControlMaster=auto -o ControlPersist=30m" deployment/deploy_container_dev.yml&lt;/span&gt;
  &lt;span class="na"&gt;after_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm -r "$CI_PROJECT_DIR/keys" || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;                              
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm "$CI_PROJECT_DIR/ssh.cfg" || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"branch_dev"'&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;runner_name&lt;/span&gt;

&lt;span class="c1"&gt;########################################################&lt;/span&gt;
&lt;span class="c1"&gt;##                                                                                                                                 ##&lt;/span&gt;
&lt;span class="c1"&gt;##     pre-production: autorun after a push/merge                                            ## &lt;/span&gt;
&lt;span class="c1"&gt;##                                                                                                                                 ##&lt;/span&gt;
&lt;span class="c1"&gt;########################################################&lt;/span&gt;

&lt;span class="na"&gt;build_react_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:12.2.0-alpine&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build_test&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*example_cache&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CI &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat .env.test &amp;gt; .env&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build/&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;  &lt;span class="s"&gt;runner_name&lt;/span&gt; 

  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"staging_branch"'&lt;/span&gt;


&lt;span class="na"&gt;build_image_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push_registry_test&lt;/span&gt;
  &lt;span class="na"&gt;image &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:19&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:19-dind&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp://docker:2375/&lt;/span&gt;
    &lt;span class="na"&gt;DOCKER_DRIVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overlay2&lt;/span&gt;
    &lt;span class="na"&gt;DOCKER_TLS_CERTDIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# docker login asks for the password to be passed through stdin for security&lt;/span&gt;
  &lt;span class="c1"&gt;# we use $CI_JOB_TOKEN here which is a special token provided by GitLab&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo -n $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build  --tag $DOCKER_IMAGE_TEST:$CI_COMMIT_SHORT_SHA-test .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $DOCKER_IMAGE_TEST:$CI_COMMIT_SHORT_SHA-test&lt;/span&gt;

  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"staging_branch"'&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;runner_name&lt;/span&gt;



&lt;span class="na"&gt;deploy_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy_test&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;willhallonline/ansible:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ${SSH_CFG} &amp;gt; "$CI_PROJECT_DIR/ssh.cfg"&lt;/span&gt;               
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mkdir -p "$CI_PROJECT_DIR/keys"&lt;/span&gt;                            
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ${ANSIBLE_KEY} &amp;gt; "$CI_PROJECT_DIR/keys/keyfile"&lt;/span&gt;      
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chmod og-rwx "$CI_PROJECT_DIR/keys/keyfile"&lt;/span&gt;    
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $CI_PROJECT_DIR &amp;amp;&amp;amp; ansible-playbook  -i deployment/inventory_test --ssh-extra-args="-F $CI_PROJECT_DIR/ssh.cfg -o ControlMaster=auto -o ControlPersist=30m" deployment/deploy_container_test.yml&lt;/span&gt;
  &lt;span class="na"&gt;after_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm -r "$CI_PROJECT_DIR/keys" || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;                              
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm "$CI_PROJECT_DIR/ssh.cfg" || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"staging_branch"'&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;runner_name&lt;/span&gt;

&lt;span class="c1"&gt;########################################################&lt;/span&gt;
&lt;span class="c1"&gt;##                                                                                                                                 ##&lt;/span&gt;
&lt;span class="c1"&gt;##     Production: must be deployed manually                                                    ## &lt;/span&gt;
&lt;span class="c1"&gt;##                                                                                                                                 ##&lt;/span&gt;
&lt;span class="c1"&gt;########################################################&lt;/span&gt;

&lt;span class="na"&gt;build_react_production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:12.2.0-alpine&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build_production&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*example_cache&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CI &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat .env.prod &amp;gt; .env&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build/&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;  &lt;span class="s"&gt;runner_name&lt;/span&gt; 
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"prod_branch"'&lt;/span&gt;
      &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manual&lt;/span&gt;

&lt;span class="na"&gt;build_image_production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push_registry_production&lt;/span&gt;
  &lt;span class="na"&gt;image &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:19&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:19-dind&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp://docker:2375/&lt;/span&gt;
    &lt;span class="na"&gt;DOCKER_DRIVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overlay2&lt;/span&gt;
    &lt;span class="na"&gt;DOCKER_TLS_CERTDIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# docker login asks for the password to be passed through stdin for security&lt;/span&gt;
  &lt;span class="c1"&gt;# we use $CI_JOB_TOKEN here which is a special token provided by GitLab&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo -n $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build  --tag $DOCKER_IMAGE_PRODUCTION:$CI_COMMIT_SHORT_SHA .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $DOCKER_IMAGE_PRODUCTION:$CI_COMMIT_SHORT_SHA&lt;/span&gt;

  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"prod_branch"'&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;runner_name&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;build_react_production&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;



&lt;span class="na"&gt;deploy_production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy_production&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;willhallonline/ansible:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ${SSH_CFG} &amp;gt; "$CI_PROJECT_DIR/ssh.cfg"&lt;/span&gt;               
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mkdir -p "$CI_PROJECT_DIR/keys"&lt;/span&gt;                            
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ${ANSIBLE_KEY} &amp;gt; "$CI_PROJECT_DIR/keys/keyfile"&lt;/span&gt;      
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chmod og-rwx "$CI_PROJECT_DIR/keys/keyfile"&lt;/span&gt;    
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $CI_PROJECT_DIR &amp;amp;&amp;amp; ansible-playbook  -i deployment/inventory --ssh-extra-args="-F $CI_PROJECT_DIR/ssh.cfg -o ControlMaster=auto -o ControlPersist=30m" deployment/deploy_container.yml&lt;/span&gt;
  &lt;span class="na"&gt;after_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm -r "$CI_PROJECT_DIR/keys" || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;                              
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm "$CI_PROJECT_DIR/ssh.cfg" || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;!=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"prod_branch"'&lt;/span&gt;
  &lt;span class="na"&gt;tags &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;runner_name&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;build_image_production&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is some notes about this pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The pipeline is protected by default to not be started with the trigger token ( Gitlab pipeline trigger)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;prep&lt;/code&gt; stage will start if there is any modifications in any json file including the &lt;code&gt;package.json&lt;/code&gt; file &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The pipeline jobs runs on docker alpine image (DinD) so we need some variables to connect to the docker host by using &lt;code&gt;DOCKER_HOST: tcp://docker:2375/&lt;/code&gt; and &lt;code&gt;DOCKER_TLS_CERTDIR: ""&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The production deployment depends on the staging jobs to be succeeded and tested by the testing team. by default no auto deploy to prod ... it's manual !&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I used some files to store application environment variables using &lt;code&gt;.env.dev&lt;/code&gt; , &lt;code&gt;env.test&lt;/code&gt; and &lt;code&gt;.env.prod&lt;/code&gt; you can use what you want !&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure to use a good docker image for the job based images .. for node i always work with &lt;code&gt;LTS&lt;/code&gt; versions. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;deployment&lt;/code&gt; folder to store the ansible playbooks and inventory files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;Cron Job&lt;/code&gt; to delete the cache every three months to clean the cache on the &lt;code&gt;CI environment&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the target server make sure to install &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;nginx&lt;/code&gt;, &lt;code&gt;certbot&lt;/code&gt; and &lt;code&gt;docker python package&lt;/code&gt;&lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**. . .**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final thoughts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can make this pipeline as template to deliver other kinds of projects like :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Rust &lt;/li&gt;
&lt;li&gt;Node &lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this post was helpful ! thanks for reading 😄 it was great to share this with you, if you have any problems in setting this just let me know !&lt;/p&gt;

&lt;center&gt;&lt;h2&gt;**Thanks 😍**&lt;h2&gt;&lt;/h2&gt;
&lt;/h2&gt;&lt;/center&gt; 

</description>
      <category>devops</category>
      <category>ansible</category>
      <category>docker</category>
      <category>react</category>
    </item>
    <item>
      <title>Optimizing CI/CD Pipeline for Rust Projects (Gitlab &amp; Docker)</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Sun, 27 Dec 2020 20:42:33 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/optimizing-ci-cd-pipeline-for-rust-projects-gitlab-docker-hc9</link>
      <guid>https://dev.to/hatembentayeb/optimizing-ci-cd-pipeline-for-rust-projects-gitlab-docker-hc9</guid>
      <description>&lt;h2&gt;
  
  
  What is Rust ?
&lt;/h2&gt;

&lt;p&gt;Rust is a programming language ( general purpose) C-like, which mean it is a compiled language and it comes with new strong features in managing memory and more. The cool thing ! rust does not have a garbage collector and that is awesome 😅 .&lt;/p&gt;

&lt;h2&gt;
  
  
  What is DevOps ?
&lt;/h2&gt;

&lt;p&gt;In short, Devops is the key feature that helps the dev team and the ops team to be friends 😃 without a work conflicts , It is the ART of automation. It increase the velocity of delivering a better softwares !&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the problem
&lt;/h2&gt;

&lt;p&gt;we can make a lot of things with rust like web apps , system drivers ans much more but there is one problem which is the time that rust takes to make a binary by downloading dependencies and compile them.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;cargo&lt;/strong&gt; command helps us to download packages ( crates in the rust world) , The Rustc is our compiler. Now we need to make a pipeline using the Gitlab CI/CD and docker to make the deployment faster.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is our challenge and the Goal of this article ! 👊&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Static linking Vs Dynamic linking
&lt;/h2&gt;

&lt;p&gt;Rust by default uses a Dynamic linking method to build the binary, so what is dynamic linking ?. &lt;/p&gt;

&lt;p&gt;The Dynamic linking uses shared libraries , so the lib is loaded into the memory and only the address is integrated into the binary. In this case the &lt;code&gt;libc&lt;/code&gt; is used.&lt;/p&gt;

&lt;p&gt;The Static linking uses static libraries which is integrated physically into the binary, no addresses are used and the binary size will be more bigger. In this case the &lt;code&gt;musl libc&lt;/code&gt; is used.&lt;/p&gt;

&lt;p&gt;You want to know more ? Then check this : &lt;a href="https://medium.com/@dkwok94/the-linking-process-exposed-static-vs-dynamic-libraries-977e92139b5f" rel="noopener noreferrer"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing the CI/CD pipeline
&lt;/h2&gt;

&lt;p&gt;The CI/CD pipeline is a set a steps that allow us to make :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;build → test → deploy&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article i will focus on the build stage because in my opinion it is very sensitive phase and it will affect the “&lt;em&gt;Time to market&lt;/em&gt;” approach !&lt;/p&gt;

&lt;p&gt;So the first thing is to optimize the size of our docker images to make the deployment faster. Before we begin, i will use a simple rust project for the demo.&lt;/p&gt;

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

&lt;p&gt;let’s understand the project structure :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;src&lt;/strong&gt; : This dir contains all source code of the app (*.rs files).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cargo.toml&lt;/strong&gt; : This file contain the package meta-data and the dependencies required by the app and some other features … .&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cargo.lock&lt;/strong&gt; : Ct contains the exact information about your dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rocket.toml&lt;/strong&gt; : With this file we specify the app status ( development , staging or production) and the required configuration for each mode, for example the port configuration for each environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dockerfile&lt;/strong&gt; : This is the docker file configuration to build the image with the specific environment that is configured already in Rocket.toml.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are you prepared 👊 😈 !!! , let’s begin the show !! 🎉 🎉 🎉&lt;/p&gt;

&lt;p&gt;We will begin by building the app image locally , so let’s see how the docker file look like :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;rustdocker/rust:nightly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;cargo-build &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update 
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;musl-tools &lt;span class="nt"&gt;-y&lt;/span&gt; 
&lt;span class="k"&gt;RUN &lt;/span&gt;/root/.cargo/bin/rustup target add x86_64-unknown-linux-musl 
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root /root/.cargo/bin/cargo new &lt;span class="nt"&gt;--bin&lt;/span&gt; material 
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /material &lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./Cargo.toml ./Cargo.toml &lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./Cargo.lock ./Cargo.lock &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Clinker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;musl-gcc /root/.cargo/bin/cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_64-unknown-linux-musl &lt;span class="nt"&gt;--features&lt;/span&gt; vendored 
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; target/x86_64-unknown-linux-musl/release/deps/material&lt;span class="k"&gt;*&lt;/span&gt; 
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;src/&lt;span class="k"&gt;*&lt;/span&gt;.rs 
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./src ./src &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Clinker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;musl-gcc /root/.cargo/bin/cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_64-unknown-linux-musl &lt;span class="nt"&gt;--features&lt;/span&gt; vendored 
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest &lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=cargo-build /auth/target/x86_64-unknown-linux-musl/release/material . &lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./material"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile is splitted into two sections :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The builder section ( a temporary container)&lt;/li&gt;
&lt;li&gt;The final image (Reduced in size)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The builder section:&lt;/strong&gt;&lt;br&gt;
In order to use rust we have to get a pre-configured images that contains the Rustc compiler and the Cargo tool. the image have the rust nightly build version and this is a real challenge because it’s not stable 😠.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We will use the static linking to get fully functional binary that doesn’t need any shared libraries from the host image !!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;let’s breakdown the code :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First we import the base image.&lt;/li&gt;
&lt;li&gt;We need the &lt;code&gt;MUSL&lt;/code&gt; support : &lt;code&gt;musl-tool&lt;/code&gt; after updating the source.list of your packages &lt;code&gt;apt-get update&lt;/code&gt; , MUSL is an easy-to-deploy static and minimal dynamically linked programs.&lt;/li&gt;
&lt;li&gt;Now we have to specify the target , if you don’t know ! no problem ! you can use &lt;code&gt;x86_64-unknown-linux-musl&lt;/code&gt; , run with Rustup (the rust toolchain installer)&lt;/li&gt;
&lt;li&gt;To define the project structure on the container we use cargo new &lt;code&gt;--bin&lt;/code&gt; material (material is the project name), it’s much like the structure that we see earlier.&lt;/li&gt;
&lt;li&gt;Making the material directory as a default we use the &lt;code&gt;WORKDIR&lt;/code&gt; Dockerfile command.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Cargo.toml&lt;/code&gt; and Cargo.lock are required for deps. installation&lt;/li&gt;
&lt;li&gt;Setting up the &lt;code&gt;RUST_FLAGS with -Clinker=musl-gcc&lt;/code&gt; : this flag tell cargo to use the musl gcc to compile the source code , the &lt;code&gt;--release&lt;/code&gt; argument is used to prepare the code for a release ( final binary optimization).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--target&lt;/code&gt; specify the target compilation 64 or 32 bit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--feature&lt;/code&gt; vendored thsi command is an angle 😄 ! it helps to solve any ssl problem by finding the SSL resources automatically without specifying the SSL lib directory and the SSL include directory. It saves me a lot of time, this command is associated with some configurations in the &lt;code&gt;Cargo.toml&lt;/code&gt; file under the &lt;code&gt;feature&lt;/code&gt; section.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Until now we only build the dependencies in Cargo.toml and we make the clean ( removing unnecessary files)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;After downloading and compiling required packages, it’s the time to get the source code into the container and make the final build to produce the final binary ( &lt;strong&gt;standalone&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The builder stage has complete ! congrats 😙 🎉 yeah !!. Now let’s use alpine as a base image to get the binary from the build stage , but ! wait a second ! what is alpine ???&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Alpine is a Linux distribution, it’s characterized in the docker world by his size ! it is a very small image (4MB) and it contains only the base commands (busybox)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;--from=cargo-build ..../material&lt;/strong&gt; now we will copy the final binary to the alpine and the intermediate container (cargo-build) will be destroyed and we get as a result a very tiny image (12–20MB) ready to use 😃 😃 😃&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You know how to build a docker image right 😲 ? okay 😃&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The CI/CD pipeline
&lt;/h2&gt;

&lt;p&gt;After testing the image locally, it seems good 😃, we resolve the docker image size, but in CI system the velocity is very important than size !! so let’s take this challenge and reduce the compilation time of this rust project !!&lt;/p&gt;

&lt;p&gt;let’s look at the .gitlab-ci.yml file ( our CI configuration):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;.caching_rust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;caching_rust&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cache/sccache&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/x86_64-unknown-linux-musl/release/material&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build_binary&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build_docker&lt;/span&gt;


&lt;span class="na"&gt;prepare_deps_for_cargo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build_binary&lt;/span&gt;
   &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hatembt/rust-ci:latest&lt;/span&gt;
   &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*caching_rust&lt;/span&gt;
   &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export CARGO_HOME="${PWD}/.cargo"&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo $CARGO_HOME&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export SCCACHE_DIR="${PWD}/.cache/sccache"&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo $SCCACHE_DIR&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export PATH="/builds/Astrolab-devops/material/.cargo/bin:$PATH"&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export RUSTC_WRAPPER="$CARGO_HOME/bin/sccache"&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo $RUSTC_WRAPPER&lt;/span&gt;

   &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt;  &lt;span class="s"&gt;cargo build --release --target=x86_64-unknown-linux-musl --features vendored&lt;/span&gt;
   &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cache/sccache&lt;/span&gt;
   &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/x86_64-unknown-linux-musl/release/material&lt;/span&gt;


&lt;span class="na"&gt;build_docker_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build_docker&lt;/span&gt;
   &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:latest&lt;/span&gt;
   &lt;span class="na"&gt;&amp;lt;&amp;lt; &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*caching_rust&lt;/span&gt;
   &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:dind&lt;/span&gt;
   &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build -t registry.gitlab.com/astrolab-devops/material:0.2.1 .&lt;/span&gt;  
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push registry.gitlab.com/astrolab-devops/material:0.2.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a tip in this file , i just splitted the docker file into two stages in this .gitlab-ci.yml :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The builder stage (rustdocker/rust..)→ build dependencies and binary&lt;/li&gt;
&lt;li&gt;The final stage (Alpine) → the build stage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the CI work i prepared a ready-to-use docker image that contains all i need to make a reliable and fast pipeline for rust project , this image is hosted in my &lt;strong&gt;$docker hub&lt;/strong&gt; .&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hatembt/rust-ci:latest&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This image contains the following packages installed and configured :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;sccache&lt;/code&gt; command : this command caches the compiled dependencies ! so by making this action to our build we can compile deps only one time !! 😅 , and we gained much more time.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;cargo-audit&lt;/code&gt; : it’s a helpful command let’s us to scan dependencies security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s breakdown the code and understand what’s going on !!&lt;/p&gt;

&lt;p&gt;In the first job : &lt;strong&gt;prepare_deps_for_cargo&lt;/strong&gt; we need our base image &lt;strong&gt;hatembt/rust-ci&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;In this job some setting are required to make a successful build are placed in the &lt;strong&gt;before_script&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defining the cargo home in the path variable.&lt;/li&gt;
&lt;li&gt;Defining the cache directory that s generated by &lt;code&gt;sccache&lt;/code&gt; (it contains the compilation cache ).&lt;/li&gt;
&lt;li&gt;Adding cargo and rustup ( they are under &lt;code&gt;.cargo/bin&lt;/code&gt;) in the path.&lt;/li&gt;
&lt;li&gt;Specifying the &lt;code&gt;RUSTC_WRAPPER&lt;/code&gt; variable in order to use the &lt;code&gt;sccache&lt;/code&gt; command with the &lt;code&gt;rustc&lt;/code&gt; or &lt;code&gt;MUSL&lt;/code&gt; in our case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now all thing are ready ! so let’s make the build in the &lt;strong&gt;script&lt;/strong&gt; section, you are already now what we should do 😃 , let’s skip it 👇.&lt;/p&gt;

&lt;p&gt;The cache and artifacts sections are very important ! its saves the data under :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.cargo/&lt;/li&gt;
&lt;li&gt;.cache/sccache&lt;/li&gt;
&lt;li&gt;target/x86_64-unknown-linux-musl/release/material (this is our final binary ).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To know more about caching and artifacts flow &lt;a href="https://gitlab.com/gitlab-org/gitlab-runner/issues/1232" rel="noopener noreferrer"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All data that is created in the first run of the CI jobs will be now saved and uploaded to the Gitlab coordinator. On the next build (new codes are pushed), we will not start the build from scratch, we just build the new packages , the old data will be injected with &lt;strong&gt;&amp;lt;&amp;lt;:*caching_rust&lt;/strong&gt; after the &lt;strong&gt;image&lt;/strong&gt; keyword.&lt;/p&gt;

&lt;p&gt;let’s move on the next JOB : &lt;strong&gt;build_docker_image&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;I made a new Dockerfile for the docker build stage, it’s based on the alpine image and it contain only the &lt;strong&gt;binary&lt;/strong&gt; from the previous stage.&lt;/p&gt;

&lt;p&gt;The new Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; target/x86_64-unknown-linux-musl/release/material .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./material"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we need a docker in docker image (dind) → to get the docker command and let’s make the steps below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login to the Gitlab registry&lt;/li&gt;
&lt;li&gt;Build the image with the new Dockerfile&lt;/li&gt;
&lt;li&gt;Push the image to Gitlab registry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And Now the results ! 😧&lt;/p&gt;

&lt;p&gt;The image size is :&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The CI Time :&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%2Fi%2Fb6vjlkmekfw5shqynx79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb6vjlkmekfw5shqynx79.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NB: the time is for the hole build time , the build binary and docker_build stages. &lt;/p&gt;

&lt;p&gt;This is the power of Devops, the art of automation with some &lt;strong&gt;philosophy&lt;/strong&gt; in the configurations and the steps to flow we can make even better than these results.&lt;/p&gt;

&lt;p&gt;In business the velocity ,the quality and the necessary features (on the application) are very important to Bring the company on the hight levels of success → this is the successful &lt;strong&gt;Digital transformation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Finally, i hope that this Story helps you to move on to next steps in the CI/CD systems, you can apply these ideas into any language (mostly complied languages, but still the same steps). If you have any feedback or critiques, please feel free to share them with me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you&lt;/strong&gt; 😄&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>rust</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>Deploying a Dockerized Angular App with Github Actions
</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Sat, 26 Dec 2020 19:41:02 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/deploying-a-dockerized-angular-app-with-github-actions-59oo</link>
      <guid>https://dev.to/hatembentayeb/deploying-a-dockerized-angular-app-with-github-actions-59oo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In this article we will discover the devops movement step by step, we will talk about the fundamental concepts then we will make a basic pipeline with github actions to deploy an angular 6 app, so let’s go.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is devops ?
&lt;/h2&gt;

&lt;p&gt;Devops is used to remove the conflict between the developers team and the operations team to work together. This conflict is removed by adding a set of best practices, rules and tools. The devops workflow is defined with a set of setps :&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9gzvkbzx78ty5kryeooa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9gzvkbzx78ty5kryeooa.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Plan
&lt;/h3&gt;

&lt;p&gt;This is the first step, where the team defines the product goals and phases, also defining deadlines and assigning tasks to every team member, this step is the root of the hole workflow. the team uses many methodology like scrum and agile.&lt;/p&gt;
&lt;h3&gt;
  
  
  Code:
&lt;/h3&gt;

&lt;p&gt;After planning, there is the code when the team converts the ideas to code. every task must be coded and merged to the main app, here we use an SCM to organize the collaboration to make a clean code and have a full code history to make a rollback in case of failure.&lt;/p&gt;
&lt;h3&gt;
  
  
  Build:
&lt;/h3&gt;

&lt;p&gt;After coding we push the code to Github or Gitlab ( SCM ) and we make the build, usually we use docker images for packaging. also we can build the code to be a Linux package like deb , rpm … or even zip files , also there is a set of tests like unit tests and integration tests. this phase is critical !&lt;/p&gt;
&lt;h3&gt;
  
  
  Test:
&lt;/h3&gt;

&lt;p&gt;The build was succeeded, no it’s time to deploy the build artifacts to the staging server when we apply a set of manual and automated tests ( UAT ).&lt;/p&gt;
&lt;h3&gt;
  
  
  Release:
&lt;/h3&gt;

&lt;p&gt;it’s the final step for the code work, so we make a release and announce a stable version of our code that is fully functional ! also we can tag it with a version number .&lt;/p&gt;
&lt;h3&gt;
  
  
  Deploy:
&lt;/h3&gt;

&lt;p&gt;A pre-prod or a production server is the target now, to make our app up and running&lt;/p&gt;
&lt;h3&gt;
  
  
  Operate:
&lt;/h3&gt;

&lt;p&gt;It’s all about infrastructure preparation and environment setup with some tools like terraform for IaaC, ansible for configuration management and security stuff configurations …&lt;/p&gt;
&lt;h3&gt;
  
  
  Monitor:
&lt;/h3&gt;

&lt;p&gt;The performance is very important, so we install and configure some monitoring tools like ELK, nagios and datadog to get all information about the applications like CPU and memory usage …&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying an angular app
&lt;/h2&gt;

&lt;p&gt;In this example we will deploy a simple angular app on two environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On VPS ( OVH provider) as a development server.&lt;/li&gt;
&lt;li&gt;on heroku as a staging server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you must have a VPS and a heroku account to continue with me.&lt;br&gt;
The application repository is here : &lt;a href="https://github.com/hatembentayeb/angular-devops" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the project with git clone &lt;code&gt;https://github.com/hatembentayeb/angular-devops&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;run npm install &amp;amp;&amp;amp; ng serve to run the app locally&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Preparing the deployment for heroku
&lt;/h2&gt;

&lt;p&gt;Nginx is a popular and powerful web server can be used to serve a large variety of apps based on python, angular and react …&lt;/p&gt;

&lt;p&gt;I will go through an optimization process to produce a clean and a lightweight docker container with the best practices as much as i can.&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing the Dockerfile
&lt;/h2&gt;

&lt;p&gt;First we will prepare the Dockerfile to be deployed to the heroku cloud,so there is some tricks to make it work smoothly, make sure that you have an account and simply click new to create an app :&lt;/p&gt;

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

&lt;p&gt;Make sure to give a valid name for your app, then go to your accout settings and get your API_KEY that we will use it in the pipeline 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%2Fi%2Fc5slegxld9ydujtopvj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc5slegxld9ydujtopvj6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;let’s take a look at the dockerfile of the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;trion/ng-cli&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package.json&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package-lock.json package-lock.json&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci  &lt;span class="nt"&gt;--debug&lt;/span&gt; 
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;ng build &lt;span class="nt"&gt;--prod&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:1.17.5&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; default.conf.template /etc/nginx/conf.d/default.conf.template&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/nginx.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder  /app/dist/my-first-app /usr/share/nginx/html &lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; /bin/bash -c "envsubst '\$PORT' &amp;lt; /etc/nginx/conf.d/default.conf.template &amp;gt; /etc/nginx/conf.d/default.conf" &amp;amp;&amp;amp; nginx -g 'daemon off;'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This Dockerfile is splitted into two stages :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Builder stage&lt;/strong&gt; : The name of the stage is builder, it is a temporary docker container that produces an artifact which is the &lt;code&gt;dist/&lt;/code&gt; folder created by &lt;code&gt;ng build --prod&lt;/code&gt; that compiles our project to produce a single html page and some *js &amp;amp; *.css . The base images that is used here is &lt;code&gt;trion/ng-cli&lt;/code&gt; that containes all requirements to run an angular up and it’s accessible for public use in the Docker-hub, the public docker registry.&lt;br&gt;
Make sure to install all app requirement packages with &lt;code&gt;npm ci&lt;/code&gt; , the &lt;code&gt;ci&lt;/code&gt; command is used often in the continues integration environments because it is faster than npm install.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Final stage&lt;/strong&gt;: The base image for this stage is &lt;code&gt;nginx:1.17.5&lt;/code&gt; and simply we copy the &lt;code&gt;dist/&lt;/code&gt; folder from the builder stage to the &lt;code&gt;/var/share/nginx/html&lt;/code&gt; folder in the nginx container with the command &lt;code&gt;COPY --from=builder&lt;/code&gt; ...&lt;br&gt;
There is additional configurations required to run the app, we need to configure nginx, there is a file named &lt;code&gt;default.conf.template&lt;/code&gt; that contains a basic nginx configurations so we copy it to the container under &lt;code&gt;/etc/nginx/conf.d/default.conf.template&lt;/code&gt; , this file have the &lt;code&gt;$PORT&lt;/code&gt; variable that have to be changed when building the docker image in the heroku environment.&lt;br&gt;
The &lt;code&gt;default.conf.template&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                         
&lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="nv"&gt;$PORT&lt;/span&gt; &lt;span class="s"&gt;default_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                           
&lt;span class="kn"&gt;include&lt;/span&gt;  &lt;span class="n"&gt;/etc/nginx/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                                      
&lt;span class="kn"&gt;root&lt;/span&gt;   &lt;span class="n"&gt;/usr/share/nginx/html/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;index&lt;/span&gt;  &lt;span class="s"&gt;index.html&lt;/span&gt; &lt;span class="s"&gt;index.htm&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;Also make sure to copy the nginx.conf under the &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; , you are free to change and modify 😃, but for now i will use the default settings.&lt;br&gt;
The last command is a little bit confusing so let’s break it down :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; /bin/bash -c “envsubst ‘\$PORT’ &amp;lt; /etc/nginx/conf.d/default.conf.template &amp;gt; /etc/nginx/conf.d/default.conf” &amp;amp;&amp;amp; nginx -g ‘daemon off;’&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;→ &lt;strong&gt;/bin/bash -c ‘ command’&lt;/strong&gt; : This command will run a linux command with the bash shell.&lt;br&gt;
→ &lt;strong&gt;envsubst&lt;/strong&gt; : It is a program substitutes the values of environment variables, so it will replace the &lt;code&gt;$PORT&lt;/code&gt; from the heroku environment and replace it in the &lt;code&gt;default.conf.template&lt;/code&gt; file with it’s value, this variable is given by heroku and attached to your app name, then we rename the template with default.conf which is recognized by nginx.&lt;br&gt;
→ &lt;strong&gt;nginx -g ‘daemon off;’&lt;/strong&gt;: The &lt;code&gt;daemon off;&lt;/code&gt; directive tells Nginx to stay in the foreground. For containers this is useful as best practice is for one container = one process. One server (container) has only one service.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparing the deployment for the VPS on OVH
&lt;/h2&gt;

&lt;p&gt;We will use the VPS as a development server so no need for a docker now we will use ssh for this, after all make sure to have a VPS , ssh credentials and a public IP.&lt;/p&gt;

&lt;p&gt;I assume you have &lt;strong&gt;nginx&lt;/strong&gt; installed , if not try to do it, it is simple 😙&lt;/p&gt;

&lt;p&gt;In this tutorial i will be using the &lt;code&gt;sshpass&lt;/code&gt; command, it is powerful and suitable for CI environments.&lt;/p&gt;

&lt;p&gt;You can install it with : &lt;code&gt;apt-get install sshpass -y&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;lets deploy the app to our server from the local machine, navigate to the repo and run &lt;code&gt;ng build --prod&lt;/code&gt; , then navigate to &lt;code&gt;dist/my-first-app&lt;/code&gt; folder and type this command :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sshpass  scp &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;password&amp;gt;  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;stricthostkeychecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt; root@&amp;lt;vps-ip&amp;gt;:/usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t want to hardcode the password in the command line try to set the &lt;code&gt;SSHPASS&lt;/code&gt; variable with your password like this export &lt;code&gt;SSHPASS="password"&lt;/code&gt;and replace &lt;code&gt;-p&lt;/code&gt; with &lt;code&gt;-e&lt;/code&gt; to use the environment variable.&lt;/p&gt;

&lt;p&gt;Now all things almost done ! great 😃 ! let’s prepare the &lt;strong&gt;pipeline&lt;/strong&gt; in the github actions which is a fast and powerful ci system provided by github inc.&lt;/p&gt;

&lt;p&gt;Under the project root folder create the file main.yml in the github/wokflows folder, this directory is hidden so must start with a point like this : &lt;code&gt;.github/workflows/main.yml&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the pipeline
&lt;/h2&gt;

&lt;p&gt;let’s take a look at the pipeline steps and configurations :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build an angular project&lt;/span&gt; 
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;12.x&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache node modules&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.npm&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-node-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Node ${{ matrix.node-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push to staging server with ssh&lt;/span&gt;
        &lt;span class="na"&gt;env &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;SSHPASS &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSHPASS }}&lt;/span&gt;
            &lt;span class="na"&gt;SERVER &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER}}&lt;/span&gt;
        &lt;span class="na"&gt;run &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;sudo apt-get update &lt;/span&gt;
            &lt;span class="s"&gt;sudo apt-get install sshpass -y&lt;/span&gt;
            &lt;span class="s"&gt;npm install -g @angular/cli@6.2&lt;/span&gt;
            &lt;span class="s"&gt;npm ci --debug&lt;/span&gt;
            &lt;span class="s"&gt;ng build --prod&lt;/span&gt;
            &lt;span class="s"&gt;cd dist/my-first-app/&lt;/span&gt;
            &lt;span class="s"&gt;sudo sshpass  -p ${SSHPASS}   -v  ssh -o StrictHostKeyChecking=no root@${SERVER} 'rm -rf /usr/share/nginx/html/*'&lt;/span&gt;
            &lt;span class="s"&gt;sudo sshpass -p ${SSHPASS} scp -v  -o stricthostkeychecking=no -r *.* root@${SERVER}:/usr/share/nginx/html&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push to heroku&lt;/span&gt;
        &lt;span class="na"&gt;env &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="na"&gt;HEROKU_REGISTRY_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.heroku.com&lt;/span&gt;
            &lt;span class="na"&gt;HEROKU_TOKEN &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.HEROKU_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;docker login --username=_ --password $HEROKU_TOKEN $HEROKU_REGISTRY_URL&lt;/span&gt;
            &lt;span class="s"&gt;export HEROKU_API_KEY=$HEROKU_TOKEN&lt;/span&gt;
            &lt;span class="s"&gt;heroku container:login&lt;/span&gt;
            &lt;span class="s"&gt;heroku container:push web --app angulardevops&lt;/span&gt;
            &lt;span class="s"&gt;heroku container:release web --app angulardevops&lt;/span&gt;
            &lt;span class="s"&gt;heroku ps:scale web=1 --app angulardevops&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block 1&lt;/strong&gt;: In this block we define the the workflow name and the actions that must be performed to start the build , test and the deployment. and of course you have to specify the branch of your repo (by default master ).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block 2&lt;/strong&gt; : The &lt;code&gt;jobs&lt;/code&gt; keyword has to sub keywords &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;steps&lt;/code&gt; , the build define the base os for the continues integration environment, in this case we will use &lt;code&gt;ubuntu-latest&lt;/code&gt; , also we define the &lt;code&gt;node-version&lt;/code&gt; as a matrix that allow us to use multiple node versions in the list, in this case we need only &lt;code&gt;12.x&lt;/code&gt; . The steps allow us to define the wokflow steps and configurations ( build,test,deploy...).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block 3&lt;/strong&gt; : &lt;code&gt;actions/checkout@v1&lt;/code&gt; is used to clone the app code in the ci env. this action is provided by github.
Lets define a cache action with the name &lt;code&gt;cache node modules&lt;/code&gt; , the name is up to you 😃, then we use a predefined action &lt;code&gt;called actions/cache@v1&lt;/code&gt; and specify the folders that we want to cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block 4&lt;/strong&gt; : Installing and configuring the node run-time with an action called &lt;code&gt;actions/node-setup@v1&lt;/code&gt; and pass to it the desired node version that we already defined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block 5&lt;/strong&gt; : The show will begin now ! let’s configure the build and the deployment to the VPS. Create two environment variables &lt;code&gt;SSHPASS&lt;/code&gt; for the sshpass command and define the &lt;code&gt;server&lt;/code&gt; address , make sure to define these values on the github secrets under setting on the top of your repo files. Under &lt;code&gt;run&lt;/code&gt; keyword put your deployment logic. so we need the sshpass command and and the angular cli to be installed, then install all required packages and build the app with the production mode &lt;code&gt;--prod&lt;/code&gt; , next, navigate to the &lt;code&gt;dist/my-first-app&lt;/code&gt; folder and run the sshpass command with a set of arguments to remove older app in the server and deploy the new code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block 6&lt;/strong&gt; : Now heroku is our target, so define also two env. variables, the heroku registry url and the &lt;strong&gt;API KEY&lt;/strong&gt; to gain access to the registry using docker , next we need to define a special variable &lt;code&gt;HEROKU_API_KEY&lt;/code&gt; that is used by heroku cli, next, we login to the heroku container and build the docker image then we pushed to the registry. we need to specify the target app in my case i named it &lt;code&gt;angulardevops&lt;/code&gt; . After deploying the docker image we need to release it and tell the heroku dynos to run our app on a heroku server, using 1 server &lt;code&gt;web=1&lt;/code&gt; , note that &lt;code&gt;web&lt;/code&gt; is the name of the docker image that we already pushed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are almost done! now try to make a change in the app code and push it to GitHub , the workflow will start automatically 🎉 🎉 😄 !&lt;/p&gt;

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

&lt;p&gt;You can view the app here: &lt;a href="https://angulardevops.herokuapp.com/" rel="noopener noreferrer"&gt;https://angulardevops.herokuapp.com/&lt;/a&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%2Fi%2F48suxnw3czd4avayfmw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F48suxnw3czd4avayfmw6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, this tutorial is aimed to help developers and DevOps engineers to deploy an Angular app, I hope it is helpful 😍. for any feedback please contact me!&lt;/p&gt;

&lt;p&gt;If this post was helpful click the clap button as much as possible 😃.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you&lt;/strong&gt; 😃&lt;/p&gt;

</description>
      <category>angular</category>
      <category>github</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Hello Docker 😄 </title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Fri, 25 Dec 2020 18:58:54 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/hello-docker-31e8</link>
      <guid>https://dev.to/hatembentayeb/hello-docker-31e8</guid>
      <description>&lt;p&gt;In this story i will share my docker experience, so, be ready ! to know the most used container technologies in the hole world ! . I will help you to get started in docker also we will deep dive into cool features … , and by the end of this story you will be able to make your own projects using docker&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Docker commands
&lt;/h2&gt;

&lt;p&gt;In this section I am going to show you the most used docker commands so let’s begin :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To get info about your docker environment use : &lt;code&gt;docker info&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To remove a container use : &lt;code&gt;docker rm &amp;lt;name | ID&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To remove an entire image use : &lt;code&gt;docker rmi &amp;lt;name | ID&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To remove a container after run use : &lt;code&gt;docker run --rm &amp;lt;name|ID&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To view current running containers use : &lt;code&gt;docker ps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To view all running and exited containers use: d&lt;code&gt;ocker ps -a&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To view all and only the IDs of containers use : &lt;code&gt;docker ps -a -q&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To get all images use : &lt;code&gt;docker images&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To remove all images use : &lt;code&gt;docker rm $(docker ps -a -q)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To view the  images use : &lt;code&gt;docker images -f"dangling=true"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To remove them use : &lt;code&gt;docker rmi $(docker images -f"dangling=true" -q)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To run the container in background use : &lt;code&gt;docker run -d ... &amp;lt;name|ID&amp;gt;&lt;/code&gt;
That’s enough for now 😆, no lets build a simple python project with flask&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Flask app
&lt;/h2&gt;

&lt;p&gt;Flask is a simple and powerful Framework for python like apache or tomcat... So let's begin :&lt;/p&gt;

&lt;p&gt;app.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello My Name is Hatem"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&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;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to install &lt;code&gt;flask&lt;/code&gt; with &lt;code&gt;pip install flask&lt;/code&gt; and run the script with python &lt;code&gt;app.py&lt;/code&gt; and open your browser on &lt;code&gt;localhost:8080/&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Let’s generate the &lt;code&gt;requirements.txt&lt;/code&gt; , we will not use &lt;code&gt;pip freeze&lt;/code&gt; here (we are not in a virtual environment) but we will use pipreqs and make sure to install it with &lt;code&gt;pip install pipreqs&lt;/code&gt; .&lt;br&gt;
use &lt;code&gt;pipreqs &amp;lt;path to the python project&amp;gt;&lt;/code&gt; to generate it.&lt;br&gt;
Now let’s write the Dockerfile :&lt;/p&gt;

&lt;p&gt;Dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:latest&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; python app.py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FROM python:latest&lt;/code&gt; : using python as a base image.&lt;br&gt;
&lt;code&gt;WORKDIR /app&lt;/code&gt; : using /app as a default directory.&lt;br&gt;
&lt;code&gt;COPY requirements.txt .&lt;/code&gt; copy this file to /app&lt;br&gt;
&lt;code&gt;RUN pip install -r req..txt&lt;/code&gt; : install the required dependencies&lt;br&gt;
&lt;code&gt;COPY app.py .&lt;/code&gt; : copy the app script to /app&lt;br&gt;
&lt;code&gt;CMD python app.py&lt;/code&gt; : The container entry point when we run it&lt;/p&gt;

&lt;p&gt;Run the build process with docker build -t hello_flask .&lt;br&gt;
Run the container &lt;code&gt;docker rn --rm -p 8080: 8080 hello_flask&lt;/code&gt; , the &lt;code&gt;-p 8080:8080&lt;/code&gt; : will map the port 8080 on your host to the port 8080 in your container → this is the port mapping in docker, you can change the host port to any port you want and must be &amp;gt; 1024 ( ports &amp;lt; 1024 are reserved by the system).&lt;/p&gt;
&lt;h2&gt;
  
  
  Environment variables in Docker
&lt;/h2&gt;

&lt;p&gt;An environment variable is a variable whose value is set outside the program, typically through the functionality built into the operating system or microservice. An environment variable is made up of a name/value pair, and any number may be created and available for reference at a point in time.&lt;br&gt;
→ &lt;a href="https://medium.com/chingu/an-introduction-to-environment-variables-and-how-to-use-them-f602f66d15fa"&gt;source&lt;/a&gt; for more information.&lt;br&gt;
let’s use them in our example :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt; 
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello My Name is {}"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'NAME'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;os.environ['NAME']&lt;/code&gt; will fetch the NAME variable and get its value and return it in the browser.&lt;br&gt;
make sure to build the container again and run it like this :&lt;br&gt;
&lt;code&gt;docker run --rm -p 8080:8080 -e "NAME=steve" hello_flask&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Volumes with Docker
&lt;/h2&gt;

&lt;p&gt;In order to be able to save (&lt;code&gt;persist&lt;/code&gt;) data and also to share data between containers, Docker came up with the concept of volumes. Quite simply, volumes are directories (or files) that are outside of the default Union File System and exist as normal directories and files on the host filesystem.&lt;br&gt;
→ &lt;a href="https://blog.container-solutions.com/understanding-volumes-docker"&gt;source&lt;/a&gt;&lt;br&gt;
let’s make some change to our script :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/name_from_file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name_from_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"files/name.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello My Name is {}"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to add a directory named “files” and add a file named “name.txt” that contains your name or whatever you want.&lt;br&gt;
Now make the build again ! and run it with this command :&lt;br&gt;
&lt;code&gt;docker run --rm -v ${PWD}/files:/app/files -e "NAME=steve" -p 8080:8080 hello_flask&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now open your browser and type &lt;code&gt;localhost:8080/name_from_file&lt;/code&gt; , you should see the name that you already add it in the name.txt file. In this case, you can change the content of the file in real-time and reload the page, you should see the changes 😆.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving and publishing images
&lt;/h2&gt;

&lt;p&gt;After finalizing your work there are two things you should do :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saving images to tarballs or compressed archives&lt;/li&gt;
&lt;li&gt;Publishing images to registries to be used in public or private…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's begin by saving our example image to a tarball by running this command &lt;code&gt;docker save --output hello_flask.tar hello_flask&lt;/code&gt; , now, check your current directory and type in the terminal &lt;code&gt;ls -sh hello_flask.tar&lt;/code&gt; to get the archive size. if you want to reduce the archive size use the gzip command which is compression tools. Execute this command :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker save hello_flask | gzip &amp;gt; hello_flask.tar.gz&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and check the size again 😃. you can now upload them to your cloud storage or wherever you want. Now if you want to load the tar file to the docker engine simply run :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker image load -i=hello_flask.tar&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Docker hub is our target now, to publish images in it you have to make an account first. the image name must be with this syntax &lt;code&gt;account_name/image_name:image_tag&lt;/code&gt; so let's rename our image by running this command :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker tag hello_flask hatembt/hello_flask:latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now you have to log in to your account and push the image to the public :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker login&lt;/code&gt; &lt;br&gt;
&lt;code&gt;docker push hatembt/hello_flask:latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To pull the image just run &lt;code&gt;docker pull hatembt/hello_flask:latest&lt;/code&gt; , no need for credentials here 😃 .&lt;/p&gt;

&lt;p&gt;Finally, I hope that this tutorial is helpful to everyone who wants to know docker.&lt;br&gt;
Thank you 😃&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>bash</category>
      <category>linux</category>
    </item>
    <item>
      <title>Automate your Documentation with Gitlab and Mkdocs</title>
      <dc:creator>hatem ben tayeb</dc:creator>
      <pubDate>Fri, 25 Dec 2020 11:17:11 +0000</pubDate>
      <link>https://dev.to/hatembentayeb/automate-your-documentation-with-gitlab-and-mkdocs-2blf</link>
      <guid>https://dev.to/hatembentayeb/automate-your-documentation-with-gitlab-and-mkdocs-2blf</guid>
      <description>&lt;p&gt;Producing documentation may be painful and need a lot of time to write and operate. In this story, i will share with you, my way of generating docs using the devops approach. To make life easier, we will explore the art of automation 😃.&lt;/p&gt;

&lt;p&gt;Let’s go folks 😙&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Gitlab repo
&lt;/h2&gt;

&lt;p&gt;This is straightforward, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to your GitLab&lt;/li&gt;
&lt;li&gt;Click new project&lt;/li&gt;
&lt;li&gt;Give it a name: auto_docs&lt;/li&gt;
&lt;li&gt;Initialize it with a README.md file&lt;/li&gt;
&lt;li&gt;Make it public or private&lt;/li&gt;
&lt;li&gt;Hit create&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now clone the project by copying the URL and run this command :&lt;br&gt;
&lt;code&gt;$ git clone https://gitlab.com/auto_docs.git&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up the environment
&lt;/h2&gt;

&lt;p&gt;I’m using a Linux environment but it is possible to reproduce the same steps on a Windows machine.&lt;br&gt;
In order to follow me, you need a set of tools that must be available on your machine … make sure to to have python3 installed, I have python 3.8 (latest).&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a virtual environment
&lt;/h2&gt;

&lt;p&gt;The easiest way to set up a virtual environment is to install virtualenv python package by executing pip install virtualenv .&lt;/p&gt;

&lt;p&gt;Navigate to your local GitLab repository and create a new virtual environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ cd auto_docs/&lt;br&gt;
$ virtualenv autodocs&lt;br&gt;
$ source autodocs/bin/acivate&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Mkdocs Material
&lt;/h2&gt;

&lt;p&gt;Make sure that the virtual environment is active.&lt;br&gt;
Install the mkdocs material with this command : pip install mkdocs-material.&lt;br&gt;
This package needs some dependencies to work .. install them by using a &lt;code&gt;requirement.txt&lt;/code&gt; file, copy-paste the dependencies list to filename requirements.txt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Babel==2.8.0
click==7.1.1
future==0.18.2
gitdb==4.0.4
GitPython==3.1.1
htmlmin==0.1.12
Jinja2==2.11.2
joblib==0.14.1
jsmin==2.2.2
livereload==2.6.1
lunr==0.5.6
Markdown==3.2.1
MarkupSafe==1.1.1
mkdocs==1.1
mkdocs-awesome-pages-plugin==2.2.1
mkdocs-git-revision-date-localized-plugin==0.5.0
mkdocs-material==5.1.1
mkdocs-material-extensions==1.0b1
mkdocs-minify-plugin==0.3.0
nltk==3.5
Pygments==2.6.1
pymdown-extensions==7.0
pytz==2019.3
PyYAML==5.3.1
regex==2020.4.4
six==1.14.0
smmap==3.0.2
tornado==6.0.4
tqdm==4.45.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install them all with one command : &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now it’s time to create a new mkdocs project 😅.&lt;/p&gt;

&lt;p&gt;Run this command : &lt;code&gt;mkdocs new&lt;/code&gt; . and verify that you have this structure :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|--auto_docs
    |--- docs
    |--- mkdocs.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;docs&lt;/code&gt; folder contains the structure of your documentation, it contains subfolders and markdown files.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;mkdocs.yml&lt;/code&gt; file defines the configuration of the generated site.
Let's test the installation by running this command : mkdocs serve . The site will be accessible on &lt;code&gt;http://locahost:8000&lt;/code&gt; and you should see the initial look of the docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up the CI/CD
&lt;/h2&gt;

&lt;p&gt;let’s enable le CI/CD to automate the build and the deployment of the docs. Notice that GitLab offers a feature called gitlab pages that can serve for free a static resource (HTML, js, CSS). The repo path is converted to an URL to your docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the CI/CD file
&lt;/h2&gt;

&lt;p&gt;Gitlab uses a YAML file — it holds the pipeline configuration.&lt;br&gt;
The CI file content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;squidfunk/mkdocs-material&lt;/span&gt;
  &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mkdocs build&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mv site public&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gitlab-org-docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pipeline uses a docker executor with an image that contains mkdocs already installed… mkdocs build the project and put the build assets on a folder called site … to be able to use GitLab pages you have to name your job pages and put the site assets into a new folder called public.&lt;br&gt;
For tags: check the runner's section under settings → CI/CD →Runners and pick one of the shared runners that have a tag GitLab-org-docker.&lt;br&gt;
All things were done 🎉 🎉 😸 !&lt;/p&gt;

&lt;p&gt;Oh ! just one thing … we forgot the virtual environment files .. they are big and not needed on the pipeline … they are for the local development only. The mkdocs image on the pipeline is already shipped with the necessary packages.&lt;br&gt;
So … create a new file called .gitignore and add these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auto_docs/ 
requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The auto_docs folder has the same name as the virtual environment .. don't forget 😠! you will be punished by pushing +100Mi 😝 and you will wait your whole life to complete the process haha 😢.&lt;/p&gt;

&lt;p&gt;Now run &lt;code&gt;git add . &amp;amp;&amp;amp; git commit -m "initial commit" a &amp;amp;&amp;amp; git push&lt;/code&gt;… go to your GitLab repo and click CI/CD → pipelines, click on the blue icon and visualize the logs .. once the job succeeded, navigate to settings -&amp;gt; pages and click the link of your new documentation site (you have to wait for 10m~ to be accessible)&lt;/p&gt;

&lt;p&gt;Finally, I hope this was helpful ! thanks for reading 😺 😍!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>python</category>
      <category>docker</category>
      <category>github</category>
    </item>
  </channel>
</rss>
