<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>helm on QL&#39;s blog</title>
    <link>/tags/helm/</link>
    <description>Recent content in helm on QL&#39;s blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 01 Jul 2020 15:00:00 +0200</lastBuildDate><atom:link href="/tags/helm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Options to migrate from Swarm to K8s</title>
      <link>/posts/options-to-migrate-from-swarm-to-k8s/</link>
      <pubDate>Wed, 01 Jul 2020 15:00:00 +0200</pubDate>
      
      <guid>/posts/options-to-migrate-from-swarm-to-k8s/</guid>
      <description>Back in 2017, I decided that our applications should be deployed in a Swarm cluster. Fast forward to 2020, we have more than 50 stacks running and the number is still counting on.
I have no regret of that decision: (1) The simplicity of docker-compose file format allows us to deploy a new stack very quickly, skipping the ops part. Any developer can have a look at the stack file and say exactly what&amp;rsquo;s going on.</description>
      <content>&lt;p&gt;Back in 2017, I decided that our applications should be deployed in a &lt;code&gt;Swarm&lt;/code&gt; cluster. Fast forward to 2020, we have more than 50 &lt;code&gt;stacks&lt;/code&gt; running and the number is still counting on.&lt;/p&gt;
&lt;p&gt;I have no regret of that decision: (1) The simplicity of &lt;code&gt;docker-compose&lt;/code&gt; file format allows us to deploy a new stack very quickly, skipping the &lt;code&gt;ops&lt;/code&gt; part. Any developer can have a look at the &lt;code&gt;stack&lt;/code&gt; file and say exactly what&amp;rsquo;s going on. (2) The declarative nature of config files (minimal) and volumes allow us to backup and update the application with ease, basically create a new snapshot, try to change the image version and re-deploy the stack. With the help of network drive (and advance FS) like NFS with ZFS we, don&amp;rsquo;t have to worry about volume provision and applications can be scaled quickly within the limit of the stateful part (DB and Storage.)&lt;/p&gt;
&lt;p&gt;As of 2020, the war between &lt;code&gt;Swarm&lt;/code&gt; and &lt;code&gt;K8s&lt;/code&gt; seems to be settled. Swarm seems to be more appropriate for the hobbyists (single node cluster) and/or RPi cluster etc. If I&amp;rsquo;m looking for the auto-scale ability of the cloud provider to prepare for bigger applications, the only solution seems to be K8s.&lt;/p&gt;
&lt;h2 id=&#34;current-structure&#34;&gt;Current structure&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Swarm&lt;/code&gt; cluster of a bunch of VPSes for memory and CPU&lt;/li&gt;
&lt;li&gt;A pool of NFS servers served as storage for application&lt;/li&gt;
&lt;li&gt;Traefik as &lt;code&gt;Ingress&lt;/code&gt; controller&lt;/li&gt;
&lt;li&gt;Let&amp;rsquo;s Encrypt as TLS provider&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;a-typical-docker-compose-file&#34;&gt;A typical &lt;code&gt;docker-compose&lt;/code&gt; file&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3.1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;redmine&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redmine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;/path/to/nfs/volume/tld.redmine.www/redmine/config/configuration.yml:/usr/src/redmine/config/configuration.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;/path/to/nfs/volume/tld.redmine.www/redmine/files:/usr/src/redmine/files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;REDMINE_DB_MYSQL&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;db&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;REDMINE_DB_PASSWORD&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;web&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;placement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;constraints&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#ae81ff&#34;&gt;node.hostname == prod2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;labels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;traefik.frontend.rule=Host:www.redmine.tld&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;traefik.port=3000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;db&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql:5.7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;/path/to/nfs/volume/tld.redmine.www/mysql:/var/lib/mysql&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;MYSQL_DATABASE&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redmine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;placement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;constraints&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#ae81ff&#34;&gt;node.hostname == prod2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;external&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;idea&#34;&gt;Idea&lt;/h2&gt;
&lt;p&gt;The idea of this post is to evaluate the possibility target K8s deployment, basically transform the above &lt;code&gt;docker-compose&lt;/code&gt; file to a bunch of &lt;code&gt;Service&lt;/code&gt;, &lt;code&gt;Deployement&lt;/code&gt; and &lt;code&gt;Ingress&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;Imagine a new structure of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;K8s&lt;/code&gt; cluster with&lt;/li&gt;
&lt;li&gt;NFS provisioner (that should be changed to cloud provider if necessary)&lt;/li&gt;
&lt;li&gt;Traefik as &lt;code&gt;Ingress&lt;/code&gt; controller&lt;/li&gt;
&lt;li&gt;Let&amp;rsquo;s Encrypt as TLS provider&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to migrate, each of the above two services should generate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A static &lt;code&gt;PV&lt;/code&gt; with &lt;code&gt;Retain&lt;/code&gt; policy point to the correct location beneath the NFS mount point and a &lt;code&gt;claimRef&lt;/code&gt; point to&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;PVC&lt;/code&gt;, should be bound automatically to the above &lt;code&gt;PV&lt;/code&gt;, serve as the volume for the &lt;code&gt;Pod&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;Ingress&lt;/code&gt; that exposes &lt;code&gt;www.redmine.tld&lt;/code&gt; from the above &lt;code&gt;labels&lt;/code&gt; via the port defined by (no for the &lt;code&gt;db&lt;/code&gt; service: &lt;code&gt;port-forward&lt;/code&gt; if we need to access the DB from outside)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;ClusterIP&lt;/code&gt; &lt;code&gt;Service&lt;/code&gt; that expose the port defined by the &lt;code&gt;label&lt;/code&gt; to the cluster&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Deployment&lt;/code&gt; to deploy the container. It should be able to handle at least &lt;code&gt;replicas&lt;/code&gt; and &lt;code&gt;placment&lt;/code&gt; properties of the &lt;code&gt;yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Ideally this process should be done automatically from the above &lt;code&gt;yaml&lt;/code&gt; file, allows us to use more advance features of &lt;code&gt;K8s&lt;/code&gt; while keeping the simplicity of &lt;code&gt;docker-compose&lt;/code&gt; file format.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I can go ahead and list all of them here (2 services * 5 files = 10 files in total) but let&amp;rsquo;s skip them for the sake of brevity.&lt;/p&gt;
&lt;h2 id=&#34;research&#34;&gt;Research&lt;/h2&gt;
&lt;h3 id=&#34;write-it-manually&#34;&gt;Write it manually&lt;/h3&gt;
&lt;p&gt;Too much, should be 10 times longer, 400 lines of code for a simple service, with a lot of duplication. We will compare the final result if one of anything below works&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;generator-from-stack-dot-yaml&#34;&gt;Generator from &lt;code&gt;stack.yaml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Sound like&amp;hellip; &lt;a href=&#34;#helm&#34;&gt;Helm&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;helm&#34;&gt;Helm&lt;/h3&gt;
&lt;p&gt;Two possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;stack&lt;/code&gt; file directly as &lt;code&gt;values.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A big helper file to extract data from each service of &lt;code&gt;yaml&lt;/code&gt; file and extends the templates (10 files only 1, 2 line with per service/deployment etc from these values, or they can be merged into one single file)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking at a Helm chart example, it seems unimaginable but imagine we use only one chart for every stack, the trade of worth it. The helper file can be versioned and symlinked to the chart.&lt;/p&gt;
&lt;h4 id=&#34;resouces&#34;&gt;Resouces&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://itnext.io/publishing-symfony-application-with-helm-ecb525b34289&#34;&gt;Let’s Migrate Symfony Project to Kubernetes! Part 2: Publishing the Application with Helm.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;kustomization&#34;&gt;Kustomization&lt;/h3&gt;
&lt;p&gt;Heard about it, but how to extract variables from a &lt;code&gt;docker-compose&lt;/code&gt; file?&lt;/p&gt;
&lt;h3 id=&#34;compose-on-kubernetes&#34;&gt;Compose-on-kubernetes&lt;/h3&gt;
&lt;p&gt;Looks like the holy grail. But&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ingress&lt;/code&gt; seems to be missing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;evaluation&#34;&gt;&lt;!-- raw HTML omitted --&gt;DONE&lt;!-- raw HTML omitted --&gt; Evaluation&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s go with &lt;a href=&#34;#helm&#34;&gt;Helm&lt;/a&gt; first&amp;hellip;&lt;/p&gt;
&lt;p&gt;(Somehow) it works for above &lt;code&gt;docker-compose.yml&lt;/code&gt;. The final output of all the deployment object is about 240 lines, only 6 times bigger than the stack.yml.&lt;/p&gt;
&lt;p&gt;The repo can be found here: &lt;a href=&#34;https://github.com/linktohack/helm-stack&#34;&gt;linktohack/helm-stack: Deploy your `docker-compose` `stack` with Helm.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;next-step&#34;&gt;Next step&lt;/h2&gt;
&lt;p&gt;The next step is to evaluate other stack files. There are still some points need to be worked on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input checked=&#34;&#34; disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Handle more &lt;code&gt;labels&lt;/code&gt; property (custom header etc&amp;hellip;)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&#34;&#34; disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Handle &lt;code&gt;node&lt;/code&gt; constraint (&lt;code&gt;nodeSelector&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&#34;&#34; disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Less assumption about the stack file: i.e. normalize it
&lt;ul&gt;
&lt;li&gt;&lt;input checked=&#34;&#34; disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Separated &lt;code&gt;volumes&lt;/code&gt; section&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input checked=&#34;&#34; disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Network &lt;code&gt;section&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
  </channel>
</rss>
