<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Helm - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Helm - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 14 Jun 2026 17:07:57 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/helm/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Run Database Migrations in Kubernetes – Different Approaches with Examples ]]>
                </title>
                <description>
                    <![CDATA[ In the era of Microservices and Kubernetes, managing database migrations has become more complex than ever. Traditional methods of running migrations during application startup are no longer sufficient. This article explores various approaches to han... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-run-database-migrations-in-kubernetes/</link>
                <guid isPermaLink="false">66fd623ecac98bddbe3cc1ee</guid>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Helm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 02 Oct 2024 15:09:50 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727685813983/f4672022-9a38-49c9-a252-96f40181fac1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the era of Microservices and Kubernetes, managing database migrations has become more complex than ever. Traditional methods of running migrations during application startup are no longer sufficient.</p>
<p>This article explores various approaches to handling database migrations in a Kubernetes environment, with a focus on Go tooling. You'll get the most out of this article if you already have some experience with Go, Kubernetes, and relational databases.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-challenge-of-migrations-in-kubernetes">The challenge of migrations in Kubernetes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-popular-migration-tools-for-golang">Popular migration tools for Golang</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-migrations-inside-the-application">Run migrations inside the application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-migrations-in-initcontainers">Run migrations in initContainers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-migrations-as-a-kubernetes-job">Run migrations as a Kubernetes Job</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-helm-hooks">Helm hooks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-for-kubernetes-migrations">Best practices for Kubernetes migrations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-the-challenge-of-migrations-in-kubernetes">The Challenge of Migrations in Kubernetes</h2>
<p>Kubernetes introduces new challenges for database migrations:</p>
<ul>
<li><p>Multiple replicas starting simultaneously. These can span the same migration twice which may introduce some database locks.</p>
</li>
<li><p>Separation of concerns between application and migration logic. This means it’s good to be able to run or rollback migrations without redeploying your application.</p>
</li>
</ul>
<h2 id="heading-popular-migration-tools-for-golang">Popular Migration Tools for Golang</h2>
<p>As I mentioned in another <a target="_blank" href="https://packagemain.tech/i/149097592/database-migrations">post</a>, there are a few different tools you can use to manage your migrations. They are quite similar, so I personally don’t have a strong preference between once or another. I just wanted to provide a few options so you know what the popular tools are.</p>
<ol>
<li><a target="_blank" href="https://github.com/golang-migrate/migrate"><strong>golang-migrate</strong></a></li>
</ol>
<ul>
<li><p>Widely used and supports numerous databases.</p>
</li>
<li><p>Simple CLI and API.</p>
</li>
<li><p>Supports various migration sources (local files, S3, Google Storage).</p>
</li>
</ul>
<ol start="2">
<li><a target="_blank" href="https://github.com/pressly/goose"><strong>goose</strong></a></li>
</ol>
<ul>
<li><p>Supports main SQL databases.</p>
</li>
<li><p>Allows migrations written in Go for complex scenarios.</p>
</li>
<li><p>Flexible versioning schemas.</p>
</li>
</ul>
<ol start="3">
<li><a target="_blank" href="https://atlasgo.io/"><strong>atlas</strong></a></li>
</ol>
<ul>
<li><p>Powerful database schema management tool.</p>
</li>
<li><p>Supports declarative and versioned migrations.</p>
</li>
<li><p>Offers integrity checks and migration linting.</p>
</li>
<li><p>Provides GitHub Actions and Terraform provider.</p>
</li>
</ul>
<h2 id="heading-run-migrations-inside-the-application">Run Migrations Inside the Application</h2>
<p>A naïve implementation would be to run the code of the migration directly inside your main function before you start your server.</p>
<p><strong>Example using golang-migrate:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>

    <span class="hljs-string">"github.com/golang-migrate/migrate/v4"</span>
    <span class="hljs-string">"github.com/golang-migrate/migrate/v4/database/postgres"</span>
    _ <span class="hljs-string">"github.com/golang-migrate/migrate/v4/source/file"</span>
    _ <span class="hljs-string">"github.com/lib/pq"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Database connection parameters</span>
    url := <span class="hljs-string">"postgres://user:pass@localhost:5432/dbname"</span>

    <span class="hljs-comment">// Connect to the database</span>
    db, err := sql.Open(<span class="hljs-string">"postgres"</span>, url)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatalf(<span class="hljs-string">"could not connect to database: %v"</span>, err)
    }
    <span class="hljs-keyword">defer</span> db.Close()

    <span class="hljs-comment">// Run migrations</span>
    <span class="hljs-keyword">if</span> err := runMigrations(db); err != <span class="hljs-literal">nil</span> {
        log.Fatalf(<span class="hljs-string">"could not run migrations: %v"</span>, err)
    }

    <span class="hljs-comment">// Run the application, for example start the server</span>
    <span class="hljs-keyword">if</span> err := http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>); err != <span class="hljs-literal">nil</span> {
        log.Fatalf(<span class="hljs-string">"server failed to start: %v"</span>, err)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">runMigrations</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">error</span></span> {
    driver, err := postgres.WithInstance(db, &amp;postgres.Config{})
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"could not create database driver: %w"</span>, err)
    }

    m, err := migrate.NewWithDatabaseInstance(
        <span class="hljs-string">"file://migrations"</span>, <span class="hljs-comment">// Path to your migration files</span>
        <span class="hljs-string">"postgres"</span>,          <span class="hljs-comment">// Database type</span>
        driver,
    )
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"could not create migrate instance: %w"</span>, err)
    }

    <span class="hljs-keyword">if</span> err := m.Up(); err != <span class="hljs-literal">nil</span> &amp;&amp; err != migrate.ErrNoChange {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"could not run migrations: %w"</span>, err)
    }

    log.Println(<span class="hljs-string">"migrations completed successfully"</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>However, these could cause different issues like your migrations being slow and Kubernetes considering that the pod didn’t start successfully and therefore killing it. You could run those migrations in a Go routine, but how do you handle failures then?</p>
<p>In cases when multiple pods are created at the same time, you would have a potential concurrency problem.</p>
<p>It also means your migrations need to be inside your Docker image.</p>
<p>Even with its downsides, this approach might work well for quick and stable database changes and small projects.</p>
<h2 id="heading-run-migrations-in-initcontainers"><strong>Run Migrations in initContainers</strong></h2>
<p>By using <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/">initContainers</a> in your Kubernetes Deployment, it will run the migration before the main application container starts. This is a good first solution for when scaling is not a problem yet.</p>
<p>If the initContainer fails, the blue/green deployment from Kubernetes won’t go further and your previous pods stay where they are. This prevents having a newer version of the code without the planned migration.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrations</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">migrate/migrate:latest</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'/migrate'</span>]
    <span class="hljs-attr">args:</span> [<span class="hljs-string">'-source'</span>, <span class="hljs-string">'file:///migrations'</span>, <span class="hljs-string">'-database'</span>,<span class="hljs-string">'postgres://user:pass@db:5432/dbname'</span>, <span class="hljs-string">'up'</span>]
</code></pre>
<p>This approach might work well for quick and stable database changes for deployments with a single Pod. And it already separates the application and migration layers.</p>
<h2 id="heading-run-migrations-as-a-kubernetes-job"><strong>Run Migrations as a Kubernetes Job</strong></h2>
<p>You could create a <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/controllers/job/">Kubernetes Job</a> that runs your migrations, and trigger that job during the deployment process before rolling out the application.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Job</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">db-migrate</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrate</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">your-migration-image:latest</span>
        <span class="hljs-attr">command:</span> [<span class="hljs-string">'/app/migrate'</span>]
</code></pre>
<p>You can also combine it with initContainers, making sure that the pod starts only when the job is successful.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrations-wait</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">ghcr.io/groundnuty/k8s-wait-for:v2.0</span>
    <span class="hljs-attr">args:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"job"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"my-migration-job"</span>
</code></pre>
<p>This approach can solve the problems related to multiple replicas mentioned above.</p>
<h2 id="heading-helm-hooks">Helm Hooks</h2>
<p>If you use Helm, it has <a target="_blank" href="https://helm.sh/docs/topics/charts_hooks/">hooks</a> that you can use for running migrations during chart installation/upgrade. You just define a pre-install or pre-upgrade hook in your Helm chart.</p>
<p><strong>pre-install-hook.yaml:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Job</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">include</span> <span class="hljs-string">"mychart.fullname"</span> <span class="hljs-string">.</span> }}<span class="hljs-string">-migrations</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">"helm.sh/hook":</span> <span class="hljs-string">pre-install,pre-upgrade</span>
    <span class="hljs-attr">"helm.sh/hook-weight":</span> <span class="hljs-string">"-5"</span>
    <span class="hljs-attr">"helm.sh/hook-delete-policy":</span> <span class="hljs-string">hook-succeeded</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrations</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">your-migrations-image:tag</span>
          <span class="hljs-attr">command:</span> [<span class="hljs-string">"./run-migrations.sh"</span>]
</code></pre>
<p>In this example, the pre-install hook executes after templates are rendered, but before any resources are created in Kubernetes.</p>
<p>This of course works only when you use Helm, meaning you need to find something else if you decide not to use Helm.</p>
<h2 id="heading-best-practices-for-kubernetes-migrations"><strong>Best Practices for Kubernetes Migrations</strong></h2>
<p>Decouple migrations from application code:</p>
<ol>
<li><p>Create a separate Docker image for migrations. This ensures that migration logic is encapsulated and doesn't interfere with the application codebase.</p>
</li>
<li><p>Use tools like Atlas to manage migrations independently. Tools like Atlas provide features for automating migration processes, scheduling, and rollback.</p>
</li>
</ol>
<p>Use version control for migrations:</p>
<ol>
<li><p>Store migration files in your Git repository. This ensures a complete history of migration changes, making it easier to track and revert changes.</p>
</li>
<li><p>Use sequential or timestamp-based versioning. Sequential versioning guarantees the correct order of migrations which is very important for relational databases.</p>
</li>
</ol>
<p>Ensure idempotent migrations:</p>
<ol>
<li>Ensure migrations can be run multiple times without side effects. Idempotent migrations prevent accidental data corruption or inconsistencies if a migration is run multiple times.</li>
</ol>
<p>Have a rollback strategy</p>
<ol>
<li>Implement and test rollback procedures for each migration. Having a rollback strategy ensures that you can revert changes if a migration fails or causes unexpected issues.</li>
</ol>
<p>Perform monitoring and logging</p>
<ol>
<li>Use tools like Atlas Cloud for visibility into migration history. Atlas Cloud provides detailed logs and history of migrations, making it easy to track changes and troubleshoot issues.</li>
</ol>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Managing database migrations in a Kubernetes environment requires careful planning and execution.</p>
<p>By leveraging tools like golang-migrate, goose, or atlas, and following best practices, you can create robust, scalable, and maintainable migration strategies.</p>
<p>Remember to decouple migrations from application code, use version control, and implement proper monitoring to ensure smooth database evolution in your Kubernetes-based architecture.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<ul>
<li><p><a target="_blank" href="https://packagemain.tech/p/database-migrations-in-kubernetes">Discover more articles from packagemain.tech</a></p>
</li>
<li><p><a target="_blank" href="https://helm.sh/docs/topics/charts_hooks/">Helm Hooks</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is a Helm Chart? A Tutorial for Kubernetes Beginners ]]>
                </title>
                <description>
                    <![CDATA[ By Lucas Santos Kubernetes is a very helpful tool for cloud-native developers. But it doesn't cover all the bases on its own – there are some things that Kubernetes cannot solve or that are outside its scope. This is one of the reasons why open sourc... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-a-helm-chart-tutorial-for-kubernetes-beginners/</link>
                <guid isPermaLink="false">66d419ac5f4570de9ac0c420</guid>
                
                    <category>
                        <![CDATA[ charts ]]>
                    </category>
                
                    <category>
                        <![CDATA[ containers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Helm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 09 Mar 2021 18:44:46 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/604640a8a7946308b768453d.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Lucas Santos</p>
<p><a target="_blank" href="https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a> is a very helpful tool for cloud-native developers. But it doesn't cover all the bases on its own – there are some things that Kubernetes cannot solve or that are outside its scope.</p>
<p>This is one of the reasons why open source projects are so great. They help amazing tools become even more amazing when we combine them with other awesome open-source tools. And often these tools were developed for the sole purpose of filling the gaps. One of these tools is Helm.</p>
<h2 id="heading-what-is-helm">What is Helm?</h2>
<p><a target="_blank" href="https://helm.sh">Helm</a> is widely known as "the package manager for <a target="_blank" href="https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a>". Although it presents itself like this, its scope goes way beyond that of a simple package manager. However, let's start at the beginning.</p>
<p>Helm is an open-source project which was originally created by <a target="_blank" href="https://deislabs.io/">DeisLabs</a> and donated to <a target="_blank" href="https://azure.microsoft.com/blog/announcing-cncf/?WT.mc_id=containers-19838-ludossan">CNCF</a>, which now maintains it. The original goal of Helm was to provide users with a better way to manage all the <a target="_blank" href="https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a> YAML files we create on <a target="_blank" href="https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a> projects.</p>
<p>The path <a target="_blank" href="https://docs.microsoft.com/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Helm</a> took to solve this issue was to create Helm <strong><a target="_blank" href="https://docs.microsoft.com/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Charts</a></strong>. Each chart is a bundle with one or more <a target="_blank" href="https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a> manifests – a chart can have child charts and dependent charts as well. </p>
<p>This means that Helm installs the whole dependency tree of a project if you run the install command for the top-level chart. You just a single command to install your entire application, instead of listing the files to install via <code>kubectl</code>.</p>
<p><a target="_blank" href="https://docs.microsoft.com/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Charts</a> allow you to version your manifest files too, just like we do with Node.js or any other package. This lets you install specific chart versions, which means keeping specific configurations for your infrastructure in the form of code. </p>
<p>Helm also keeps a release history of all deployed charts, so you can go back to a previous release if something went wrong.</p>
<p><a target="_blank" href="https://docs.microsoft.com/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Helm</a> supports <a target="_blank" href="https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a> natively, which means you don't have to write any complex syntax files or anything to start using Helm. Just drop your template files into a new chart and you're good to go.</p>
<p>But why should we use it? Managing application manifests can be easily done with a few combinations of commands.</p>
<h2 id="heading-why-should-you-use-helm">Why Should You Use Helm?</h2>
<p>Helm really shines where Kubernetes didn't go. For instance, templating. The scope of the Kubernetes project is to deal with your containers for you, not your template files. </p>
<p>This makes it overly difficult to create truly generic files to be used across a large team or a large organization with many different parameters that need to be set for each file. </p>
<p>And also, how do you version sensitive information using Git when template files are plain text?</p>
<p>The answer: Go templates. Helm allows you to add variables and use functions inside your template files. This makes it perfect for scalable applications that'll eventually need to have their parameters changed. Let's look at an example.</p>
<p>I have an open-source project called <a target="_blank" href="https://github.com/khaosdoctor/zaqar/">Zaqar</a>, a simple email microservice for Node.js which communicates with SendGrid. The project is basically composed of a service, a deployment and an autoscaler. </p>
<p>Let's take the deployment file as the example. I'd have something like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">zaqar</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">zaqar</span>
    <span class="hljs-attr">version:</span> <span class="hljs-string">v1.0.0</span>
    <span class="hljs-attr">env:</span> <span class="hljs-string">production</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">zaqar</span>
      <span class="hljs-attr">env:</span> <span class="hljs-string">production</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">zaqar</span>
        <span class="hljs-attr">version:</span> <span class="hljs-string">v1.0.0</span>
        <span class="hljs-attr">env:</span> <span class="hljs-string">production</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">zaqar</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">"khaosdoctor/zaqar:v1.0.0"</span>
          <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span>
          <span class="hljs-attr">env:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SENDGRID_APIKEY</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"MY_SECRET_KEY"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_FROM_ADDRESS</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"my@email.com"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_FROM_NAME</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"Lucas Santos"</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
              <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>
              <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
          <span class="hljs-attr">resources:</span>
            <span class="hljs-attr">requests:</span>
              <span class="hljs-attr">cpu:</span> <span class="hljs-string">100m</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">128Mi</span>
            <span class="hljs-attr">limits:</span>
              <span class="hljs-attr">cpu:</span> <span class="hljs-string">250m</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">256Mi</span>
</code></pre>
<p>If I wanted to use this template on a <a target="_blank" href="https://docs.microsoft.com/learn/modules/aks-deployment-pipeline-github-actions/?WT.mc_id=containers-19838-ludossan">CI pipeline</a>, or publish it on my <a target="_blank" href="https://github.com/khaosdoctor">GitHub</a>, I'd need to replace the variable parts with placeholders. So we can replace these texts with the required information. </p>
<p>In this case, both the version tag, the <code>env</code> label, and the environment variables would be replaced by placeholders, like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">zaqar</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">zaqar</span>
    <span class="hljs-attr">version:</span> <span class="hljs-comment">#!VERSION!#</span>
    <span class="hljs-attr">env:</span> <span class="hljs-comment">#!ENV!#</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">zaqar</span>
      <span class="hljs-attr">env:</span> <span class="hljs-comment">#!ENV!#</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">zaqar</span>
        <span class="hljs-attr">version:</span> <span class="hljs-comment">#!VERSION!#</span>
        <span class="hljs-attr">env:</span> <span class="hljs-comment">#!ENV!#</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">zaqar</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">"khaosdoctor/zaqar:#!VERSION!#"</span>
          <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span>
          <span class="hljs-attr">env:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SENDGRID_APIKEY</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"#!SENDGRID_KEY!#"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_FROM_ADDRESS</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"#!FROM_ADDR!#"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_FROM_NAME</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"#!FROM_NAME!#"</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
              <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>
              <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
          <span class="hljs-attr">resources:</span>
            <span class="hljs-attr">requests:</span>
              <span class="hljs-attr">cpu:</span> <span class="hljs-string">100m</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">128Mi</span>
            <span class="hljs-attr">limits:</span>
              <span class="hljs-attr">cpu:</span> <span class="hljs-string">250m</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">256Mi</span>
</code></pre>
<p>We can now run our <a target="_blank" href="https://docs.microsoft.com/learn/modules/aks-deployment-pipeline-github-actions/?WT.mc_id=containers-19838-ludossan">CI pipeline</a>. But before we do that, we need to replace our placeholders with the actual values. </p>
<p>For this we can use <code>sed</code> and its "super easy" syntax <code>sed 's/#!PLACEHOLDER!#/replacement/g'</code>, and pipe this down until we finish all the placeholders. The final command would be something like this:</p>
<pre><code class="lang-bash">cat deploy.yaml | \
    sed <span class="hljs-string">'s/#!ENV!#/production/g'</span> | \
    sed <span class="hljs-string">'s/#!VERSION!#/v1.0.0/g'</span> | \
    sed <span class="hljs-string">'s/#!SENDGRID_KEY!#/MyKey/g'</span> | \
    sed <span class="hljs-string">'s/#!FROM_ADDR!#/my@email.com/g'</span> | \
    sed <span class="hljs-string">'s/#!FROM_NAME!#/Lucas Santos/g'</span>
</code></pre>
<p>By default, sed outputs everything to the <code>stdout</code>, so we can add another pipe to <code>kubectl -f</code>, like <code>&lt;all the command from before&gt; | kubectl -f -</code>. Then we'll have our deployment in place. The only problem is that we need to do the same for <strong>all</strong> the other files. </p>
<p>Now imagine a larger project, with lots of other variables and placeholders. You'd probably write a script to do it for you, wouldn't you? That script is Helm.</p>
<p>When you create a Chart (more on this later on), we have a specific directory tree that we must follow so Helm understands what we want to do. Inside the <code>templates</code> directory, we can add our manifest files, <strong>with native go templating,</strong> like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Values.name</span> }}
  <span class="hljs-attr">namespace:</span> {{ <span class="hljs-string">default</span> <span class="hljs-string">.Release.Namespace</span> <span class="hljs-string">.Values.namespace</span> }}
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> {{ <span class="hljs-string">.Values.name</span> }}
    <span class="hljs-attr">version:</span> {{ <span class="hljs-string">.Values.image.tag</span> }}
    <span class="hljs-attr">env:</span> {{ <span class="hljs-string">.Values.env</span> }}
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> {{ <span class="hljs-string">.Values.replicaCount</span> }}
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> {{ <span class="hljs-string">.Values.name</span> }}
      <span class="hljs-attr">env:</span> {{ <span class="hljs-string">.Values.env</span> }}
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> {{ <span class="hljs-string">.Values.name</span> }}
        <span class="hljs-attr">version:</span> {{ <span class="hljs-string">.Values.image.tag</span> }}
        <span class="hljs-attr">env:</span> {{ <span class="hljs-string">.Values.env</span> }}
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Chart.Name</span> }}
          <span class="hljs-attr">image:</span> <span class="hljs-string">"khaosdoctor/zaqar:<span class="hljs-template-variable">{{ .Values.image.tag }}</span>"</span>
          <span class="hljs-attr">imagePullPolicy:</span> {{ <span class="hljs-string">.Values.image.pullPolicy</span> }}
          <span class="hljs-attr">env:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SENDGRID_APIKEY</span>
              <span class="hljs-attr">value:</span> {{ <span class="hljs-string">required</span> <span class="hljs-string">"You must set a valid Sendgrid API key"</span> <span class="hljs-string">.Values.environment.SENDGRID_APIKEY</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_FROM_ADDRESS</span>
              <span class="hljs-attr">value:</span> {{ <span class="hljs-string">required</span> <span class="hljs-string">"You must set a default from address"</span> <span class="hljs-string">.Values.environment.DEFAULT_FROM_ADDRESS</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_FROM_NAME</span>
              <span class="hljs-attr">value:</span> {{ <span class="hljs-string">required</span> <span class="hljs-string">"You must set a default from name"</span> <span class="hljs-string">.Values.environment.DEFAULT_FROM_NAME</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
              <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>
              <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
          <span class="hljs-attr">resources:</span>
            {{<span class="hljs-bullet">-</span> <span class="hljs-string">toYaml</span> <span class="hljs-string">.Values.resources</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">12</span> }}
</code></pre>
<p>All those values can be obtained from a <code>Values.yaml</code> file (for default values), or you can set them in the CLI using the <code>--set &lt;path&gt; value</code> flag. </p>
<p>If we want to install our chart we can issue the following command:</p>
<pre><code class="lang-bash">helm upgrade --install --create-namespace myChart ./path/to/my/chart \
  --<span class="hljs-built_in">set</span> image.tag=v1.0.0 \
  --<span class="hljs-built_in">set</span> env=production \
  --<span class="hljs-built_in">set</span> environment.SENDGRID_APIKEY=myKey \
  --<span class="hljs-built_in">set</span> environment.DEFAULT_FROM_ADDRESS=<span class="hljs-string">"my@email.com"</span> \
  --<span class="hljs-built_in">set</span> environment.DEFAULT_FROM_NAME=<span class="hljs-string">"Lucas Santos"</span>
</code></pre>
<p>Helm also allows us to use functions inside our deployments. So we can have <code>default</code> functions to fallback to default values if they're not filled, like the namespace. Or we can have <code>required</code> which displays a message and fails to install the chart if the value is not provided, which is the case of our environment variables.</p>
<p>There are a lot of other useful functions in their <a target="_blank" href="https://helm.sh/docs/chart_template_guide/">docs</a> - check 'em out.</p>
<p>Now we are able to not only more efficiently manage our application's resources, but also to publish these resources in an open-source version system without any hassle or security issue.</p>
<h2 id="heading-how-to-create-a-helm-chart">How to Create a Helm Chart</h2>
<p>It's pretty easy to create a chart in Helm. First, you need to have <a target="_blank" href="https://helm.sh/docs/intro/quickstart/">Helm installed</a>. Then, just type in <code>helm create &lt;chart name&gt;</code> and it will create a directory filled with files and other directories. Those files are required for Helm to create a chart. </p>
<p>Let's take a closer look at what this file tree looks like and what the files are within it:</p>
<ul>
<li><strong>chart.yaml:</strong> This is where you'll put the information related to your chart. That includes the chart version, name, and description so you can find it if you publish it on an open repository. Also in this file you'll be able to set external <a target="_blank" href="https://helm.sh/docs/topics/charts/#chart-dependencies">dependencies</a> using the <code>dependencies</code> key.</li>
<li><strong>values.yaml</strong>: Like we saw before, this is the file that contains defaults for variables.</li>
<li><strong>templates (dir):</strong> This is the place where you'll put all your manifest files. Everything in here will be passed on and created in Kubernetes.</li>
<li><strong>charts:</strong> If your chart depends on another chart you own, or if you don't want to rely on Helm's default library (the default registry where Helm pull charts from), you can bring this same structure inside this directory. Chart dependencies are installed from the bottom to the top, which means if chart A depends on chart B, and B depends on C, the installation order will be C -&gt;B -&gt;A.</li>
</ul>
<p>There are other fields, but these are the most common, and they're the required ones. You can take a quick look at <a target="_blank" href="https://github.com/khaosdoctor/zaqar/tree/master/helm">Zaqar's repository</a> to check on how we can publish open source charts.</p>
<p>A quick note: When installing Helm, make sure you're installing version 3. Version 2 still works, but it needs a server-side component called Tiller, which ties your helm installation to a single cluster. Helm 3 removed this need with the addition of several CRDs, but it's not supported in all Kubernetes versions.</p>
<h2 id="heading-how-to-host-a-helm-chart">How to Host a Helm Chart</h2>
<p>Ok, you created your chart, now what? Do we have to download the entire repository to install those charts? No! Helm has a <a target="_blank" href="https://artifacthub.io/">public library for the most used charts</a>, which kind of works like Docker Hub. </p>
<p>You can also create your own chart repository and <a target="_blank" href="https://helm.sh/docs/topics/chart_repository/">host it online</a>. Helm drinks from the same fountain as HomeBrew, or Linux. You can tap these repositories to download charts contained in them. </p>
<p>Since a chart repository is basically an <code>index.yaml</code> file served from a static web server, you can pretty much create a chart repository out of anywhere.</p>
<p>Take <a target="_blank" href="https://github.com/khaosdoctor/zaqar/tree/master/helm">Zaqar</a>, for example – it's hosted on GitHub Pages and is accessible through <a target="_blank" href="https://lsantos.me/zaqar/helm/index.yaml">my domain</a>. When Helm looks for an <code>index.yaml</code> file it's actually looking for the list of available versions of that chart, their SHA256 digests, and the location of the packaged <code>.tgz</code> file to download the chart itself. This is pretty much what NPM does under the hood (overly simplified).</p>
<p>This means you don't need to have your repository cloned forever, and your charts can be private as well. You only need to create a chart repository. </p>
<p>You can even use <a target="_blank" href="https://docs.microsoft.com/azure/container-registry/container-registry-helm-repos?WT.mc_id=containers-19838-ludossan">hosted services like Azure CR</a> to do the job, or you can have a full solution called <a target="_blank" href="https://github.com/helm/chartmuseum">Chart Museum</a>, which allows you to store your charts and provides you with a neat UI.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Helm is here to stay. It has helped and will help a lot of Kubernetes developers out there for a long time. </p>
<p>If you want to know how to use Helm, you can refer to their <a target="_blank" href="https://helm.sh">docs</a>, or you can take this <a target="_blank" href="https://docs.microsoft.com/learn/modules/aks-app-package-management-using-helm/?WT.mc_id=containers-19838-ludossan">free learn module</a> on how to deploy your applications on Kubernetes the easy way with Helm.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Helm Charts Tutorial: The Kubernetes Package Manager Explained ]]>
                </title>
                <description>
                    <![CDATA[ By Sebastian Sigl There are different ways of running production services at a high scale. One popular solution for running containers in production is Kubernetes. But interacting with Kubernetes directly comes with some caveats.  Helm tries to solve... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/helm-charts-tutorial-the-kubernetes-package-manager-explained/</link>
                <guid isPermaLink="false">66d460f63a8352b6c5a2ab0b</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Helm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 31 Dec 2020 19:28:44 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/12/helm-blog-logo.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sebastian Sigl</p>
<p>There are different ways of running production services at a high scale. One popular solution for running containers in production is Kubernetes. But interacting with Kubernetes directly comes with some caveats. </p>
<p>Helm tries to solve some of the challenges with useful features that increase productivity and reduce maintenance efforts of complex deployments. </p>
<p>In this post you will learn:</p>
<ul>
<li>What Helm is</li>
<li>The most common use-cases of Helm</li>
<li>How to configure and deploy a publicly available Helm package</li>
<li>How to deploy a custom application using Helm</li>
</ul>
<p>Every code example in this post requires a Kubernetes cluster. The easiest way to get a cluster to play with is to install Docker and activate its Kubernetes cluster feature. Also, you need to <a target="_blank" href="https://kubernetes.io/docs/tasks/tools/install-kubectl/">install kubectl</a> and <a target="_blank" href="https://helm.sh/docs/intro/install/">Helm</a> to interact with your cluster.</p>
<p><em>Please note: When you try the examples, be patient. If you are too fast then the containers are not ready. It might take a few minutes until the containers can receive requests.</em></p>
<h2 id="heading-what-is-helm">What is Helm?</h2>
<p>Helm calls itself ”The Kubernetes package manager”. It is a command-line tool that enables you to create and use so-called Helm Charts. </p>
<p>A Helm Chart is a collection of templates and settings that describe a set of Kubernetes resources. Its power spans from managing a single node definition to a highly scalable multi-node cluster.</p>
<p>The architecture of Helm has changed over the last years. The current version of Helm communicates directly to your Kubernetes cluster via Rest. If you read something about Tiller in the context of Helm, then you're reading an old article. Tiller was removed in Helm 3. </p>
<p>Helm itself is stateful. When a Helm Chart gets installed, the defined resources are getting deployed and meta-information is stored in Kubernetes secrets.</p>
<h2 id="heading-how-to-deploy-a-simple-helm-application">How to Deploy a Simple Helm Application</h2>
<p>Let’s get our hands dirty and make sure Helm is ready to use. </p>
<p>First, we need to be connected to a Kubernetes cluster. In this example, I will concentrate on a Kubernetes cluster that comes with your Docker setup. So if you use some other Kubernetes cluster, configurations and outputs might differ.</p>
<pre><code class="lang-shell">$ kubectl config use-context docker-desktop

  Switched to context "docker-desktop".

$ kubectl get node

   NAME             STATUS   ROLES    AGE   VERSION
   docker-desktop   Ready    master   20d   v1.19.3
</code></pre>
<p>Let’s deploy an Apache webserver using Helm. As a first step, we need to tell Helm what location to search by adding a Helm repository:</p>
<pre><code class="lang-shell">$ helm repo add bitnami https://charts.bitnami.com/bitnami
</code></pre>
<p>Let’s install the actual container:</p>
<pre><code class="lang-shell">$ helm install my-apache bitnami/apache --version 8.0.2
</code></pre>
<p>After a few minutes your deployment is ready. We can check the state of the containers using kubectl:</p>
<pre><code class="lang-shell">$ kubectl get pods

  NAME                               READY   STATUS    RESTARTS   AGE
  my-apahe-apache-589b8df6bd-q6m2n   1/1     Running   0          2m27s
</code></pre>
<p>Now, open <a target="_blank" href="http://localhost">http://localhost</a> to see the default Apache exposed website locally. Also, Helm can show us information about current deployments:</p>
<pre><code class="lang-shell">$ helm list

 NAME         REVISION    STATUS      CHART           VERSION
 my-apache    1        deployed      apache-8.0.2    2.4.46
</code></pre>
<h3 id="heading-how-to-upgrade-a-helm-application">How to Upgrade a Helm Application</h3>
<p>We can upgrade our deployed application to a new version like this:</p>
<pre><code class="lang-shell">$ helm upgrade my-apache bitnami/apache --version 8.0.3

$ helm list

NAME         REVISION    STATUS      CHART          VERSION
my-apache    2           deployed    apache-8.0.3    2.4.46
</code></pre>
<p>The column Revision indicates that this is the 2nd version we've deployed. </p>
<h3 id="heading-how-to-rollback-a-helm-application">How to Rollback a Helm Application</h3>
<p>So let’s try to rollback to the first deployed version:</p>
<pre><code class="lang-shell">$ helm rollback my-apache 1                    

Rollback was a success! Happy Helming!

$ helm list

NAME         REVISION    STATUS      CHART        VERSION
my-apache    3           deployed    apache-8.0.2    2.4.46
</code></pre>
<p>This is a very powerful feature that allows you to roll back changes in production quickly.</p>
<p>I mentioned that Helm stores deployment information in secrets – here they are:</p>
<pre><code class="lang-shell">$ kubectl get secret

NAME                    TYPE                         DATA   AGE
default-token-nc4hn               kubernetes.io/sat        3      20d
sh.helm.release.v1.my-apache.v1   helm.sh/release.v1        1      1m
sh.helm.release.v1.my-apache.v2   helm.sh/release.v1        1      1m
sh.helm.release.v1.my-apache.v3   helm.sh/release.v1        1      1m
</code></pre>
<h3 id="heading-how-to-remove-a-deployed-helm-application">How to Remove a Deployed Helm Application</h3>
<p>Let’s clean up our Kubernetes by removing the my-apache release:</p>
<pre><code class="lang-shell">$ helm delete my-apache

  release "my-apache" uninstalled
</code></pre>
<p>Helm gives you a very convenient way of managing a set of applications that enables you to deploy, upgrade, rollback and delete.</p>
<p>Now, we are ready to use more advanced Helm features that will boost your productivity!</p>
<h2 id="heading-how-to-access-production-ready-helm-charts">How to Access Production-Ready Helm Charts</h2>
<p>You can search public hubs for Charts that enable you to quickly deploy your desired application with a customizable configuration. </p>
<p>A Helm Chart doesn't just contain a static set of definitions. Helm comes with capabilities to hook into any lifecycle state of a Kubernetes deployment. This means during the installation or upgrade of an application, various actions can be executed like creating a database update before updating the actual database.</p>
<p>This powerful definition of Helm Charts lets you share and improve an executable description of a deployment setup that spans from initial installation and version upgrades to rollback capabilities. </p>
<p>Helm might be heavy for a simple container like a single node web server, but it’s very useful for more complex applications. For example it works great for a distributed system like Kafka or Cassandra that usually runs on multiple distributed nodes on different datacenters. </p>
<p>We've already leveraged Helm to deploy a single Apache container. Now, we will deploy a production-ready WordPress application that contains:</p>
<ul>
<li>Containers that serve WordPress,</li>
<li>Instances of MariaDB for persistence and</li>
<li>Prometheus sidecar containers for each WordPress container to expose health metrics.</li>
</ul>
<p>Before we deploy, it’s recommended to increase your Docker limits to at least 4GB of memory.</p>
<p>Setting everything up sounds like a job that would take weeks. To make it resilient and scale, probably a job that would take months. In these areas, Helm Charts can really shine. Due to the growing community, there might already be a Helm Chart that we can use.</p>
<h3 id="heading-how-to-deploy-wordpress-and-mariadb">How to Deploy WordPress and MariaDB</h3>
<p>There are different public hubs for Helm Charts. One of them is <a target="_blank" href="https://artifacthub.io">artifacthub.io</a>. We can search for “WordPress” and find an interesting <a target="_blank" href="https://artifacthub.io/packages/helm/bitnami/wordpress">WordPress Chart</a>. </p>
<p>On the right side, there is an install button. If you click it, you get clear instructions about what to do:</p>
<pre><code class="lang-shell">$ helm repo add bitnami https://charts.bitnami.com/bitnami

$ helm install my-wordpress bitnami/wordpress --version 10.1.4
</code></pre>
<p>You will also see some instructions that tell you how to access the admin interface and the admin password after installation.</p>
<p>Here is how you can get and decode the password for the <strong>admin</strong> user on Mac OS:</p>
<pre><code class="lang-shell">$ echo Username: user
$ echo Password: $(kubectl get secret --namespace default my-wordpress-3 -o jsonpath="{.data.wordpress-password}" | base64 --decode)

Username: user
Password: sZCa14VNXe
</code></pre>
<p>On windows, you can get the password for the <strong>user</strong> user in the powershell:</p>
<pre><code class="lang-powershell"><span class="hljs-variable">$pw</span>=kubectl get secret -<span class="hljs-literal">-namespace</span> default my<span class="hljs-literal">-wordpress</span> <span class="hljs-literal">-o</span> jsonpath=<span class="hljs-string">"{.data.wordpress-password}"</span>
[<span class="hljs-type">System.Text.Encoding</span>]::UTF8.GetString([<span class="hljs-type">System.Convert</span>]::FromBase64String(<span class="hljs-variable">$pw</span>))
</code></pre>
<p>Our local development will be available at: <a target="_blank" href="http://localhost">http://localhost</a>.<br>Our admin interface will be available at: <a target="_blank" href="https://localhost/admin">https://localhost/admin</a>.</p>
<p>So we have everything to run it locally. But in production, we want to scale some parts of it to serve more and more visitors. We can scale the number of WordPress services. We also want to expose some health metrics like the usage of our CPU and memory.</p>
<p>We can <a target="_blank" href="https://raw.githubusercontent.com/bitnami/charts/master/bitnami/wordpress/values-production.yaml">download the example configuration for production</a> from the maintainer of the WordPress Chart. The most important changes are:</p>
<pre><code class="lang-yaml"><span class="hljs-comment">### Start 3 WordPress instances that will all receive </span>
<span class="hljs-comment">### requests from our visitors. A load-balancer will distribute calls </span>
<span class="hljs-comment">### to all containers evenly.</span>
<span class="hljs-attr">replicaCount:</span> <span class="hljs-number">3</span>

<span class="hljs-comment">### start a sidecar container that will expose metrics for your wordpress container</span>
<span class="hljs-attr">metrics:</span>
  <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">image:</span>
    <span class="hljs-attr">registry:</span> <span class="hljs-string">docker.io</span>
    <span class="hljs-attr">repository:</span> <span class="hljs-string">bitnami/apache-exporter</span>
    <span class="hljs-attr">tag:</span> <span class="hljs-number">0.8</span><span class="hljs-number">.0</span><span class="hljs-string">-debian-10-r243</span>
</code></pre>
<p>Let’s stop the default application:</p>
<pre><code class="lang-shell">$ helm delete my-wordpress

  release "my-wordpress" uninstalled
</code></pre>
<h3 id="heading-how-to-start-a-multi-instance-wordpress-and-mariadb-deployment">How to Start a Multi-instance WordPress and MariaDB Deployment</h3>
<p>Deploy a new release using the production values:</p>
<pre><code class="lang-shell">$ helm install my-wordpress-prod bitnami/wordpress --version 10.1.4 -f values-production.yaml
</code></pre>
<p>This time, we have more containers running:</p>
<pre><code class="lang-shell">$ kubectl get pods
NAME                                 READY   STATUS    RESTARTS   AGE
my-wordpress-prod-5c9776c976-4bs6f   2/2     Running   0          103s
my-wordpress-prod-5c9776c976-9ssmr   2/2     Running   0          103s
my-wordpress-prod-5c9776c976-sfq84   2/2     Running   0          103s
my-wordpress-prod-mariadb-0          1/1     Running   0          103s
</code></pre>
<p>We see 4 lines: 1 line for MariaDB, and 3 lines for our actual WordPress pods. </p>
<p>A pod in Kubernetes is a group of containers. Each group contains 2 containers, one for WordPress and one for an exporter for Prometheus that exposes valuable metrics in a special format.</p>
<p>As in the default setup, we can <a target="_blank" href="http://localhost">open localhost</a> and play with our WordPress application. </p>
<h3 id="heading-how-to-access-exposed-health-metrics">How to Access Exposed Health Metrics</h3>
<p>We can check the exposed health metrics by proxying to one of the running pods:</p>
<pre><code class="lang-shell">kubectl port-forward my-wordpress-prod-5c9776c976-sfq84 9117:9117
</code></pre>
<p><em>Make sure to replace the pod-id with your own pod ID when you execute the port-forward command.</em> </p>
<p>Now, we are connected to port 9117 of the WordPress Prometheus exporter and map the port to our local port 9117. Open <a target="_blank" href="http://localhost:9117/metrics">http://localhost:9117</a> to check the output. </p>
<p>If you are not used to the Prometheus format, it might be a little bit confusing in the beginning. But it’s actually pretty easy to read. Each line without <em>‘#’</em> contains a metric key and a value behind it:</p>
<pre><code class="lang-prometheus">apache_cpuload 1.2766
process_resident_memory_bytes 1.6441344e+07
</code></pre>
<p>If you are not used to such metrics, don't worry – you will get used to them quickly. You can Google each of the keys and find out what it means. After some time, you will identify what metrics are the most valuable for you and how they behave as soon as your containers receive more and more production traffic.</p>
<p>Let’s tidy up our setup by:</p>
<pre><code class="lang-shell">$ helm delete my-wordpress-prod

  release "my-wordpress-prod" uninstalled
</code></pre>
<p>We touched on a lot of deployment areas and features. We deployed multiple WordPress instances and scaled it up to more containers for production. You could even go one step further and activate auto-scaling. Check out the documentation of the Helm Chart and play around with it!</p>
<h3 id="heading-mariadb-helm-chart">MariaDB Helm Chart</h3>
<p>The persistence of the helm Chart for WordPress depends on MariaDB. It builds on another <a target="_blank" href="https://artifacthub.io/packages/helm/bitnami/mariadb">Helm Chart for MariaDB</a> that you can configure and scale to your needs by, for example, starting multiple replicas.</p>
<p>The possibilities that you have when running containers in production using Kubernetes are enormous. The definition of the WordPress Chart is publicly available. </p>
<p>In the next section, we will create our own Helm Chart with a basic application to understand the fundamentals of creating a Helm Chart and to make a static container deployment more dynamic.</p>
<h2 id="heading-how-to-create-a-template-for-custom-applications">How to Create a Template for Custom Applications</h2>
<p>Helm adds a lot more flexibility to your Kubernetes deployment files. Kubernetes deployment files are static by their nature. This means, adjustments like </p>
<ul>
<li>desired container count, </li>
<li>environment variables or</li>
<li>CPU and memory limit</li>
</ul>
<p>are not adjustable by using plain Kubernetes deployment files. Either you solve this by duplicating configuration files or you put placeholders in your Kubernetes deployment files that are replaced at deploy-time. </p>
<p>Both of these solutions require some additional work and will not scale well if you deploy a lot of applications with different variations.</p>
<p>But for sure, there is a smarter solution that is based on Helm that contains a lot of handy features from the Helm community. Let’s create a custom Chart for a blogging engine, this time for a NodeJS based blog called <a target="_blank" href="https://ghost.org/">ghost blog</a>. </p>
<h3 id="heading-how-to-start-a-ghost-blog-using-docker">How to Start a Ghost Blog Using Docker</h3>
<p>A simple instance can be started using pure Docker:</p>
<pre><code class="lang-shell">docker run --rm -p 2368:2368 --name my-ghost ghost
</code></pre>
<p>Our blog is available at: <a target="_blank" href="http://localhost:2368/">http://localhost:2368</a>.</p>
<p>Let's stop the instance to be able to launch another one using Kubernetes:</p>
<pre><code class="lang-shell">$ docker rm -f my-ghost

  my-ghost
</code></pre>
<p>Now, we want to deploy the ghost blog with 2 instances in our Kubernetes cluster. Let’s set up a plain deployment first:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># file 'application/deployment.yaml'</span>

<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">ghost-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">ghost-app</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">2</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">ghost-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">ghost-app</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">ghost</span>

          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">2368</span>
</code></pre>
<p>and put a load balancer before it to be able to access our container and to distribute the traffic to both containers:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># file 'application/service.yaml'</span>

<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">my-service-for-ghost-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">LoadBalancer</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">ghost-app</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">2368</span>
</code></pre>
<p>We can now deploy both resources using kubectl:</p>
<pre><code>$ kubectl apply -f ./appplication/deployment.yaml -f ./appplication/service.yaml

  deployment.apps/ghost-app created
  service/my-service-<span class="hljs-keyword">for</span>-ghost-app created
</code></pre><p>The ghost application is now available via <a target="_blank" href="http://localhost">http://localhost</a>. Let's again stop the application:</p>
<pre><code>$ kubectl <span class="hljs-keyword">delete</span> -f ./appplication/deployment.yaml -f ./appplication/service.yaml

  deployment.apps/ghost-app <span class="hljs-keyword">delete</span>
  service/my-service-<span class="hljs-keyword">for</span>-ghost-app <span class="hljs-keyword">delete</span>
</code></pre><p>So far so good, it works with plain Kubernetes. But what if we need different settings for different environments?</p>
<p>Imagine that we want to deploy it to multiple data centers in different stages (non-prod, prod). You will end up duplicating your Kubernetes files over and over again. It will be hell for maintenance. Instead of scripting a lot, we can leverage Helm. </p>
<p>Let’s create a new Helm Chart from scratch:</p>
<pre><code class="lang-shell">$ helm create my-ghost-app

  Creating my-ghost-app
</code></pre>
<p>Helm created a bunch of files for you that are usually important for a production-ready service in Kubernetes. To concentrate on the most important parts, we can remove a lot of the created files. Let’s go through the only required files for this example.</p>
<p>We need a project file that is called Chart.yaml:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Chart.yaml</span>

<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v2</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">my-ghost-app</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">Helm</span> <span class="hljs-string">chart</span> <span class="hljs-string">for</span> <span class="hljs-string">Kubernetes</span>
<span class="hljs-attr">type:</span> <span class="hljs-string">application</span>
<span class="hljs-attr">version:</span> <span class="hljs-number">0.1</span><span class="hljs-number">.0</span>
<span class="hljs-attr">appVersion:</span> <span class="hljs-number">1.16</span><span class="hljs-number">.0</span>
</code></pre>
<p>The deployment template file:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># templates/deployment.yaml</span>

<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">ghost-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">ghost-app</span>
  <span class="hljs-attr">replicas:</span> {{ <span class="hljs-string">.Values.replicaCount</span> }}
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">ghost-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">ghost-app</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">ghost</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">2368</span>
          <span class="hljs-attr">env:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">url</span>
              {{<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">.Values.prodUrlSchema</span> }}
              <span class="hljs-attr">value:</span> <span class="hljs-string">http://{{</span> <span class="hljs-string">.Values.baseUrl</span> <span class="hljs-string">}}</span>
              {{<span class="hljs-bullet">-</span> <span class="hljs-string">else</span> }}
              <span class="hljs-attr">value:</span> <span class="hljs-string">http://{{</span> <span class="hljs-string">.Values.datacenter</span> <span class="hljs-string">}}.non-prod.{{</span> <span class="hljs-string">.Values.baseUrl</span> <span class="hljs-string">}}</span>
              {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
</code></pre>
<p>It looks very similar to our plain Kubernetes file. Here, you can see different placeholders for the replica count, and an if-else condition for the environment variable called url. In the following files, we will see all the values defined.</p>
<p>The service template file:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># templates/service.yaml</span>

<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">my-service-for-my-webapp</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">LoadBalancer</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">ghost-app</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">2368</span>
</code></pre>
<p>Our Service configuration is completely static.</p>
<p>The values for the templates are the last missing parts of our Helm Chart. Most importantly, there is a default values file required called values.yaml:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># values.yaml</span>

<span class="hljs-attr">replicaCount:</span> <span class="hljs-number">1</span>
<span class="hljs-attr">prodUrlSchema:</span> <span class="hljs-literal">false</span>
<span class="hljs-attr">datacenter:</span> <span class="hljs-string">us-east</span>
<span class="hljs-attr">baseUrl:</span> <span class="hljs-string">myapp.org</span>
</code></pre>
<p>A Helm Chart should be able to run just by using the default values. Before you proceed, make sure that you have deleted:</p>
<ul>
<li>my-ghost-app/templates/tests/test-connection.yaml</li>
<li>my-ghost-app/templates/serviceaccount.yaml</li>
<li>my-ghost-app/templates/ingress.yaml</li>
<li>my-ghost-app/templates/hpa.yaml</li>
<li>my-ghost-app/templates/NOTES.txt.</li>
</ul>
<p>We can get the final output that would be sent to Kubernetes by executing a “dry-run”:</p>
<pre><code class="lang-shell">$ helm template --debug my-ghost-app

install.go:159: [debug] Original chart version: ""
install.go:176: [debug] CHART PATH: /helm/my-ghost-app

---
# Source: my-ghost-app/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service-for-my-webapp
spec:
  type: LoadBalancer
  selector:
    app: my-example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 2368
---
# Source: my-ghost-app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ghost-app
spec:
  selector:
    matchLabels:
      app: ghost-app
  replicas: 1
  template:
    metadata:
      labels:
        app: ghost-app
    spec:
      containers:
        - name: ghost-app
          image: ghost
          ports:
            - containerPort: 2368
          env:
            - name: url
              value: us-east.non-prod.myapp.org
</code></pre>
<p>Helm inserted all the values and also set url to <em><code>us-east.non-prod.myapp.org</code></em> because in the <em><code>values.yaml</code></em>, <code>prodUrlSchema</code> is set to false and the datacenter is set to us-east.</p>
<p>To get some more flexibility, we can define some override value files. Let’s define one for each datacenter:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># values.us-east.yaml</span>
<span class="hljs-attr">datacenter:</span> <span class="hljs-string">us-east</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-comment"># values.us-west.yaml</span>
<span class="hljs-attr">datacenter:</span> <span class="hljs-string">us-west</span>
</code></pre>
<p>and one for each stage:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># values.nonprod.yaml</span>
<span class="hljs-attr">replicaCount:</span> <span class="hljs-number">1</span>
<span class="hljs-attr">prodUrlSchema:</span> <span class="hljs-literal">false</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-comment"># values.prod.yaml</span>
<span class="hljs-attr">replicaCount:</span> <span class="hljs-number">3</span>
<span class="hljs-attr">prodUrlSchema:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>We can now use Helm to combine them as we want and check the result again:</p>
<pre><code class="lang-shell">$ helm template --debug my-ghost-app -f my-ghost-app/values.nonprod.yaml  -f my-ghost-app/values.us-east.yaml 

install.go:159: [debug] Original chart version: ""
install.go:176: [debug] CHART PATH: /helm/my-ghost-app

---
# Source: my-ghost-app/templates/service.yaml
# templates/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service-for-my-webapp
spec:
  type: LoadBalancer
  selector:
    app: my-example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 2368
---
# Source: my-ghost-app/templates/deployment.yaml
# templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ghost-app
spec:
  selector:
    matchLabels:
      app: ghost-app
  replicas: 1
  template:
    metadata:
      labels:
        app: ghost-app
    spec:
      containers:
        - name: ghost-app
          image: ghost
          ports:
            - containerPort: 2368
          env:
            - name: url
              value: http://us-east.non-prod.myapp.org
</code></pre>
<p>And for sure, we can do a final deployment:</p>
<pre><code class="lang-shell">$ helm install -f my-ghost-app/values.prod.yaml my-ghost-prod ./my-ghost-app/

NAME: my-ghost-prod
LAST DEPLOYED: Mon Dec 21 00:09:17 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
</code></pre>
<p>As before, our ghost blog is available via <a target="_blank" href="http://localhost">http://localhost</a>.</p>
<p>We can delete this deployment and deploy the application with us-east and non prod settings like this:</p>
<pre><code class="lang-shell">$ helm delete my-ghost-prod                                                 
  release "my-ghost-prod" uninstalled

$ helm install -f my-ghost-app/values.nonprod.yaml -f my-ghost-app/values.us-east.yaml my-ghost-nonprod ./my-ghost-app
</code></pre>
<p>We finally clean up our Kubernetes deployment via Helm:</p>
<pre><code class="lang-shell">$ helm delete my-ghost-nonprod
</code></pre>
<p>So we can combine multiple override value files as we want. We can automate deployments in a flexible way that we need for many use-cases of deployment pipelines. </p>
<p>Especially for companies, this means defining Chart Skeletons once to ensure the required criteria are fulfilled. Later, you can copy them and adjust them to the needs of your application.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The power of a great templating engine and the possibility of executing releases, upgrades, and rollbacks makes Helm great. On top of that comes the publicly available Helm Chart Hub that contains thousands of production-ready templates. This makes Helm a must-have tool in your toolbox if you work with Kubernetes on a bigger scale!</p>
<p>I hope you enjoyed this hands-on tutorial. Motivate yourself to Google around, check out other examples, deploy containers, connect them, and use them.</p>
<p>You will learn many cool features in the future that enable you to ship your application to production in an effortless, reusable and scalable way.</p>
<p>As always, I appreciate any feedback and comments. I hope you enjoyed the article. If you like it and feel the need for a round of applause, <a target="_blank" href="https://twitter.com/sesigl">follow me on Twitter</a>.  I work at eBay Kleinanzeigen, one of the biggest classified companies globally. By the way, <a target="_blank" href="https://jobs.ebayclassifiedsgroup.com/ebay-kleinanzeigen">we are hiring</a>!</p>
<p>References:</p>
<ul>
<li><a target="_blank" href="https://helm.sh/docs/chart_template_guide/getting_started/">https://helm.sh/docs/chart_template_guide/getting_started/</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ An Introduction to the Helm Package Manager for Kubernetes ]]>
                </title>
                <description>
                    <![CDATA[ By Jillian Rowe Before we dive into the Helm package manager, I'm going to explain some key concepts to deploying any application anywhere. I'll also give you a brief introduction to Kubernetes terminology. What is Kubernetes? Kubernetes (K8s) is an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/an-introduction-to-the-helm-package-manager-for-kubernetes/</link>
                <guid isPermaLink="false">66d45f6351f567b42d9f845b</guid>
                
                    <category>
                        <![CDATA[ Helm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 10 Jul 2020 11:10:49 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c99c6740569d1a4ca21a6.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jillian Rowe</p>
<p>Before we dive into the <a target="_blank" href="https://helm.sh/">Helm package manager</a>, I'm going to explain some key concepts to deploying any application anywhere. I'll also give you a brief introduction to <a target="_blank" href="https://kubernetes.io/">Kubernetes terminology</a>.</p>
<h2 id="heading-what-is-kubernetes">What is Kubernetes?</h2>
<blockquote>
<p><a target="_blank" href="https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/">Kubernetes (K8s)</a> is an open-source system for automating deployment, scaling, and management of containerized applications<br><a target="_blank" href="https://kubernetes.io/#kubernetes-k8s-docs-concepts-overview-what-is-kubernetes-is-an-open-source-system-for-automating-deployment-scaling-and-management-of-containerized-applications">kubernetes.io</a></p>
</blockquote>
<p>Now you might be asking yourself, "Well, what does that mean?"</p>
<p>Kubernetes is essentially a very nice set of APIs for deploying, managing, and scaling applications. </p>
<p>The applications are packaged with Docker, and then the logic surrounding the deployment of the application is expressed using Helm templates. The templates themselves are instructions that are then run using the Kubernetes API. </p>
<p>There are <a target="_blank" href="https://bitnami.com/stacks/helm">a ton of Helm packages</a> already created to take care of your application deployment needs!</p>
<p>I like to think of Kubernetes + Helm as a one stop shop for my application DevOps needs.</p>
<h2 id="heading-fun-fact-time">Fun Fact Time!</h2>
<p>The entire container ecosystem, including Docker, has a very fun nautical theme. Docker has whales, Kubernetes has pods (of whales), and its logo looks like the steering portion of a ship, and Helm is the helm of a ship.</p>
<p>Aren't they cute?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/docker-swarm-logo.png" alt="Image" width="600" height="400" loading="lazy">
<em>[https://hub.docker.com/</em>/swarm](https://hub.docker.com/<em>/swarm)</em></p>
<h2 id="heading-deploying-an-application-on-kubernetes">Deploying an Application on Kubernetes</h2>
<p>First of all, no matter where you deploy an application there are going to be some things that remain the same anywhere<em>,</em> and I do mean <em>anywhere!</em> ;-) Whether you are deploying to your laptop, a remote server, an AWS EC2 instance, High Performance Computing systems, or Kubernetes, the underlying concepts do not change.</p>
<p>I think of pretty much everything, tech concepts in particular, as a series of layers. Once you understand what those fundamental layers are you can get cooking.</p>
<h3 id="heading-application-layers">Application Layers</h3>
<p>These are:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>General</td><td>Kubernetes</td></tr>
</thead>
<tbody>
<tr>
<td>Data Layer</td><td>PVC or Persistent Volume Claims</td></tr>
<tr>
<td>Application Layer</td><td>Pods</td></tr>
<tr>
<td>Services</td><td>SVC</td></tr>
</tbody>
</table>
</div><p>Let's take these on one at a time.</p>
<p><strong>Data Layer / Persistent Volume Claims (PVCs)</strong></p>
<p>This is nice and straight forward. When you need to persist data you persist it to a filesystem. This can be local storage or some sort of networked file system (NFS). If you are using a database the database also eventually persists to a filesystem.</p>
<p><strong>Application Layer / Pods</strong></p>
<p>The application layer is what we typically think of in a deployment. Its the part we <code>apt-get install</code>, <code>npm run</code> or <code>docker run</code>. An application could be an NGINX web server, a Python or Node.js app, or a Spark application to name a few. </p>
<p>Applications are either Kubernetes <strong>Deployments</strong> or <strong>Stateful Set</strong>, depending on whether or not they persist data (or have a state). </p>
<p>A MySQL database would be an example of a <em>Stateful</em> application. It needs to keep track of information about itself.</p>
<p>An NGINX server would be a Kubernetes Deployment, because it does not need to keep track of any information about itself — it is <em>stateless</em>.</p>
<p><strong>Services Layer / SVC</strong></p>
<p>The services layer is where we expose our <em>Application</em> to the outside world. This is generally accomplished by saying "Hey, I have an app running on this port". You might have run these directly, or done something like a proxy pass in NGINX or Apache.</p>
<h3 id="heading-site-reliability-layers"><strong>Site Reliability Layers</strong></h3>
<p>Site reliability is our ability to confidently say our application is up, running, and will <em>probably</em> stay up and running!</p>
<p>To be real, we want an API to essentially do this. ? </p>
<p><em><a target="_blank" href="https://xkcd.com/1495/">XKCD</a> - Hard Reboot</em></p>
<p><img src="https://imgs.xkcd.com/comics/hard_reboot.png" alt="XKCD Hard Reboot" width="465" height="226" loading="lazy"></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>General</td><td>Kubernetes</td></tr>
</thead>
<tbody>
<tr>
<td>Monitoring</td><td>metrics-server</td></tr>
<tr>
<td>Scaling (or Load Balancing)</td><td>Horizontal Pod Autoscaler (HPA)</td></tr>
<tr>
<td>Service Rules</td><td>Container specs</td></tr>
</tbody>
</table>
</div><p><strong>Monitoring Layer / Metrics Server</strong></p>
<p>The monitoring layer answers the question "How is our app doing"? Ideally it would answer questions like "How much CPU is left on that machine?" and "Are we out of memory yet"? </p>
<p><strong>Scaling Layer / HPA</strong></p>
<p>Have you ever had an application that worked great until too many people started using it at once? You take care of this by scaling the instances of your application up or down. </p>
<p>With web applications, you'll often see the term load balancing, too. This functionality is built into many process managers and https servers such as <a target="_blank" href="https://pm2.keymetrics.io/">PM2</a> and <a target="_blank" href="https://gunicorn.org/">Gunicorn</a>. </p>
<p>In Kubernetes you accomplish this with a Horizontal Pod Autoscaler, or HPA, which you give specific rules for how to scale up or down.</p>
<p><strong>Service Rules Layer</strong></p>
<p>Have you ever wanted to automate when / how your application should restart? Maybe you want it to restart 3 times and then give up. Or maybe you want it to restart, but not right away. </p>
<p>Give it some time! You may also want some objective measure to test whether or not you application is up and running. </p>
<h2 id="heading-deploying-applications-on-kubernetes">Deploying Applications on Kubernetes</h2>
<p>Kubernetes applications can be deployed either through the CLI, or by writing YAML templates that describe the various PVCs, Pods (whether they are Deployments or Stateful sets), and Service (SVC) layers. </p>
<h2 id="heading-the-helm-package-manager">The Helm Package Manager</h2>
<blockquote>
<p>Helm is the best way to find, share, and use software built for <a target="_blank" href="https://kubernetes.io/">Kubernetes</a>.<br><a target="_blank" href="https://helm.sh/">https://helm.sh/</a></p>
</blockquote>
<p>The Helm package manager allows us to wire up complex Kubernetes deployments into a single package, that can be installed with a single command.</p>
<p>Helm uses a templating language on top of Kubernetes YAML definitions to allow more versatility to our deployments. </p>
<p>Probably the most important point to note with Helm is that it has been widely accepted by the community. This means that there are lots of resources for using Helm, getting started, and also oodles of preconfigured Helm Charts! </p>
<p>It's very rare for me to ever have to create a Helm package completely from scratch. I can nearly always find a good starting point from one or more of the <a target="_blank" href="https://bitnami.com/stacks/helm">Helm charts that are already available</a>.</p>
<h2 id="heading-deploy-nginx-on-kubernetes">Deploy NGINX on Kubernetes</h2>
<p>First, let's talk about a base NGINX deployment without Helm. </p>
<p>As you can see, there is a lot of stuff to keep track of and we probably wouldn't be typing this by hand. That is where the Helm package manager comes in, but it's good to take a look to know what is happening first! ;-)</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Source: nginx/templates/deployment.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">helm.sh/chart:</span> <span class="hljs-string">nginx-6.0.1</span>
    <span class="hljs-attr">app.kubernetes.io/instance:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">app.kubernetes.io/managed-by:</span> <span class="hljs-string">Helm</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">nginx</span>
      <span class="hljs-attr">app.kubernetes.io/instance:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">helm.sh/chart:</span> <span class="hljs-string">nginx-6.0.1</span>
        <span class="hljs-attr">app.kubernetes.io/instance:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">app.kubernetes.io/managed-by:</span> <span class="hljs-string">Helm</span>
    <span class="hljs-attr">spec:</span>      
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">docker.io/bitnami/nginx:1.19.0-debian-10-r2</span>
          <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">"IfNotPresent"</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
              <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>

          <span class="hljs-attr">livenessProbe:</span>
            <span class="hljs-attr">failureThreshold:</span> <span class="hljs-number">6</span>
            <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">30</span>
            <span class="hljs-attr">tcpSocket:</span>
              <span class="hljs-attr">port:</span> <span class="hljs-string">http</span>
            <span class="hljs-attr">timeoutSeconds:</span> <span class="hljs-number">5</span>
          <span class="hljs-attr">readinessProbe:</span>
            <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>
            <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">5</span>
            <span class="hljs-attr">tcpSocket:</span>
              <span class="hljs-attr">port:</span> <span class="hljs-string">http</span>
            <span class="hljs-attr">timeoutSeconds:</span> <span class="hljs-number">3</span>
</code></pre>
<p>Now let's break down the different parts of the Kubernetes Deployment definition.</p>
<p><strong>Metadata</strong></p>
<p>I want to very briefly touch on the <code>labels</code>. Only briefly, because chances are you will be fine with the defaults and not need to touch them. </p>
<p>One of the goals of Kubernetes is that it should abstract the actual physical server away. You shouldn't <em>usually</em> have to care if your app is running on <code>node1</code> or <code>node2</code>. Of course at some point you do care, and then you will start to get into labels. </p>
<p>Until then, don't worry about them and just stick with the defaults.</p>
<p><strong>Containers</strong></p>
<p>This is the part of the application that will be most relevant to you when you are deploying applications. You need to define your containers. </p>
<p>A single Deployment Pod can have many containers. That container has, at the very least a <code>name</code>, a <code>repo</code>, and a <code>tag</code>:</p>
<pre><code class="lang-yaml">      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
          <span class="hljs-comment">#image:  "repo:tag"</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">docker.io/bitnami/nginx:1.19.0-debian-10-r2</span>
</code></pre>
<p>Once you have the base you need to define the ports that will get picked up by the service. See that separation of concerns?:</p>
<pre><code class="lang-yaml">          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
              <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>
</code></pre>
<p><strong>App Rules</strong></p>
<p>Then, at some point, we want to know if our app is running. We can even determine exactly where it is in it's lifecycle with the various hooks:</p>
<pre><code class="lang-yaml">         <span class="hljs-attr">livenessProbe:</span>
            <span class="hljs-attr">failureThreshold:</span> <span class="hljs-number">6</span>
            <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">30</span>
            <span class="hljs-attr">tcpSocket:</span>
              <span class="hljs-comment"># This corresponds to the ports[0].name</span>
              <span class="hljs-attr">port:</span> <span class="hljs-string">http</span>
            <span class="hljs-attr">timeoutSeconds:</span> <span class="hljs-number">5</span>
          <span class="hljs-attr">readinessProbe:</span>
            <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>
            <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">5</span>
            <span class="hljs-comment"># This corresponds to the ports[0].name</span>
            <span class="hljs-attr">tcpSocket:</span>
              <span class="hljs-attr">port:</span> <span class="hljs-string">http</span>
            <span class="hljs-attr">timeoutSeconds:</span> <span class="hljs-number">3</span>
</code></pre>
<p><strong>Names</strong></p>
<p>This is more of a general concept, but I do want to point out that giving things names is very important in the Kubernetes ecosystem. Notice that we gave our <code>container</code> and <code>port</code> a <code>name</code>. Later on when we need to refer to them we use that <code>name</code>.</p>
<h2 id="heading-deploy-nginx-on-kubernetes-with-a-helm-chart">Deploy NGINX on Kubernetes with a Helm Chart</h2>
<p>## </p>
<p>The Helm package manager creates a series of templates that can be modified through the Helm CLI. Each of these templates corresponds to one of our Kubernetes types that we discussed earlier. </p>
<p>Here is an example of the <a target="_blank" href="https://github.com/bitnami/charts/blob/master/bitnami/nginx/templates/deployment.yaml">bitnami/nginx</a> helm chart:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/helm-chart-view.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Bitnami/NGINX Helm Chart Templates</em></p>
<p>Here is that same block with the Helm templating language. For the sake of brevity I have omitted some parts of the template. If you would like to see the whole thing you can take a look at it in <a target="_blank" href="https://github.com/bitnami/charts/blob/master/bitnami/nginx/templates/deployment.yaml">the GitHub repo</a>.</p>
<p>(This is for demonstration purposes and is not a fully functional Helm chart. Please don't use this. Grab the actual Helm chart instead.)</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Source: nginx/templates/deployment.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">template</span> <span class="hljs-string">"nginx.fullname"</span> <span class="hljs-string">.</span> }}
  <span class="hljs-attr">labels:</span> {{<span class="hljs-bullet">-</span> <span class="hljs-string">include</span> <span class="hljs-string">"nginx.labels"</span> <span class="hljs-string">.</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">4</span> }}
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span> {{<span class="hljs-bullet">-</span> <span class="hljs-string">include</span> <span class="hljs-string">"nginx.matchLabels"</span> <span class="hljs-string">.</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">6</span> }}
  <span class="hljs-attr">replicas:</span> {{ <span class="hljs-string">.Values.replicaCount</span> }}
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span> {{<span class="hljs-bullet">-</span> <span class="hljs-string">include</span> <span class="hljs-string">"nginx.labels"</span> <span class="hljs-string">.</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">8</span> }}
      <span class="hljs-comment"># Omitted the annotation labels!</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
          <span class="hljs-attr">image:</span> {{ <span class="hljs-string">template</span> <span class="hljs-string">"nginx.image"</span> <span class="hljs-string">.</span> }}
          <span class="hljs-attr">imagePullPolicy:</span> {{ <span class="hljs-string">.Values.image.pullPolicy</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
              <span class="hljs-attr">containerPort:</span> {{ <span class="hljs-string">.Values.containerPort</span> }}
            {{ <span class="hljs-string">if</span> <span class="hljs-string">.Values.containerTlsPort</span> }}
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">https</span>
              <span class="hljs-attr">containerPort:</span> {{ <span class="hljs-string">.Values.containerTlsPort</span> }}
            {{ <span class="hljs-string">end</span> }}
          {{<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">.Values.livenessProbe</span> }}
          <span class="hljs-attr">livenessProbe:</span> {{<span class="hljs-bullet">-</span> <span class="hljs-string">toYaml</span> <span class="hljs-string">.Values.livenessProbe</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">12</span> }}
          {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
          {{<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">.Values.readinessProbe</span> }}
          <span class="hljs-attr">readinessProbe:</span> {{<span class="hljs-bullet">-</span> <span class="hljs-string">toYaml</span> <span class="hljs-string">.Values.readinessProbe</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">12</span> }}
          {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
          {{<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">.Values.resources</span> }}
          <span class="hljs-attr">resources:</span> {{<span class="hljs-bullet">-</span> <span class="hljs-string">toYaml</span> <span class="hljs-string">.Values.resources</span> <span class="hljs-string">|</span> <span class="hljs-string">nindent</span> <span class="hljs-number">12</span> }}
          {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
</code></pre>
<h3 id="heading-where-do-the-helm-template-values-come-from">Where do the Helm Template values come from?</h3>
<p>Now, this is what I really like about Helm. The values that are exposed in the template come from one of two places. </p>
<p><strong>Templated Functions</strong></p>
<p>They come from the template itself, as shown here.</p>
<pre><code class="lang-yaml">{{ <span class="hljs-string">template</span> <span class="hljs-string">"nginx.fullname"</span> <span class="hljs-string">.</span> }}
</code></pre>
<p>We can find that this is defined in our <code>templates/_helpers.tpl</code>, which is a way of getting more complex functions that we could get with just a <code>yaml</code> file.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># templates/_helpers.tpl</span>
{{<span class="hljs-string">/*</span>
<span class="hljs-string">Create</span> <span class="hljs-string">a</span> <span class="hljs-string">default</span> <span class="hljs-string">fully</span> <span class="hljs-string">qualified</span> <span class="hljs-string">app</span> <span class="hljs-string">name.</span>
<span class="hljs-string">We</span> <span class="hljs-string">truncate</span> <span class="hljs-string">at</span> <span class="hljs-number">63</span> <span class="hljs-string">chars</span> <span class="hljs-string">because</span> <span class="hljs-string">some</span> <span class="hljs-string">Kubernetes</span> <span class="hljs-string">name</span> <span class="hljs-string">fields</span> <span class="hljs-string">are</span> <span class="hljs-string">limited</span> <span class="hljs-string">to</span> <span class="hljs-string">this</span> <span class="hljs-string">(by</span> <span class="hljs-string">the</span> <span class="hljs-string">DNS</span> <span class="hljs-string">naming</span> <span class="hljs-string">spec).</span>
<span class="hljs-string">*/</span>}}
<span class="hljs-comment"># Here is the nginx.fullname</span>
{{<span class="hljs-bullet">-</span> <span class="hljs-string">define</span> <span class="hljs-string">"nginx.fullname"</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">.Values.fullnameOverride</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">.Values.fullnameOverride</span> <span class="hljs-string">|</span> <span class="hljs-string">trunc</span> <span class="hljs-number">63</span> <span class="hljs-string">|</span> <span class="hljs-string">trimSuffix</span> <span class="hljs-string">"-"</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">else</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">$name</span> <span class="hljs-string">:=</span> <span class="hljs-string">default</span> <span class="hljs-string">.Chart.Name</span> <span class="hljs-string">.Values.nameOverride</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">contains</span> <span class="hljs-string">$name</span> <span class="hljs-string">.Release.Name</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">.Release.Name</span> <span class="hljs-string">|</span> <span class="hljs-string">trunc</span> <span class="hljs-number">63</span> <span class="hljs-string">|</span> <span class="hljs-string">trimSuffix</span> <span class="hljs-string">"-"</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">else</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">printf</span> <span class="hljs-string">"%s-%s"</span> <span class="hljs-string">.Release.Name</span> <span class="hljs-string">$name</span> <span class="hljs-string">|</span> <span class="hljs-string">trunc</span> <span class="hljs-number">63</span> <span class="hljs-string">|</span> <span class="hljs-string">trimSuffix</span> <span class="hljs-string">"-"</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> <span class="hljs-string">-</span>}}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> <span class="hljs-string">-</span>}}
</code></pre>
<p><strong>Values exposed in the Values.yaml</strong></p>
<p>This is actually a very neat feature and what makes Helm so powerful and configurable. </p>
<p>Each Helm chart comes along with a <code>values.yaml</code>. You can put whatever you want in the <code>values.yaml</code>, and then use it throughout your Helm chart, and it's exposed through the CLI!</p>
<pre><code class="lang-yaml"><span class="hljs-comment">## Bitnami NGINX image version</span>
<span class="hljs-comment">## ref: https://hub.docker.com/r/bitnami/nginx/tags/</span>
<span class="hljs-comment">##</span>
<span class="hljs-attr">image:</span>
  <span class="hljs-attr">registry:</span> <span class="hljs-string">docker.io</span>
  <span class="hljs-attr">repository:</span> <span class="hljs-string">bitnami/nginx</span>
  <span class="hljs-attr">tag:</span> <span class="hljs-number">1.19</span><span class="hljs-number">.1</span><span class="hljs-string">-debian-10-r0</span>
  <span class="hljs-comment">## Specify a imagePullPolicy</span>
  <span class="hljs-comment">## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'</span>
  <span class="hljs-comment">## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images</span>
  <span class="hljs-comment">##</span>
  <span class="hljs-attr">pullPolicy:</span> <span class="hljs-string">IfNotPresent</span>
</code></pre>
<p>Then we see this referred to in our templates as:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># templates/deployment.yaml </span>

<span class="hljs-comment"># ...</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
          <span class="hljs-attr">image:</span> {{ <span class="hljs-string">template</span> <span class="hljs-string">"nginx.image"</span> <span class="hljs-string">.</span> }}
          <span class="hljs-attr">imagePullPolicy:</span> {{ <span class="hljs-string">.Values.image.pullPolicy</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
</code></pre>
<p>Everything in the <code>values.yaml</code> can also be modified through the Helm CLI:</p>
<pre><code>helm upgrade --install nginx bitnami/nginx \
    --set image.tag=<span class="hljs-string">"my-new-tag"</span>
</code></pre><p>It would then render the <code>containers[0].image</code> as <code>image: docker.io/bitnami/nginx:my-new-tag</code></p>
<h2 id="heading-wrap-up">Wrap Up</h2>
<p>That's it! I hope that you've learned a little about Kubernetes and its package manager Helm. Hopefully it isn't quite as scary as it once was.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to simplify the deployment of containerized apps ]]>
                </title>
                <description>
                    <![CDATA[ By Omer Levi Hevroni I asked myself this question when I started to learn Kubernetes: how can we simplify the deployment of containerized apps? At first look, deploying containerized apps seems pretty simple. All you need is a bunch of YAML files, an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-can-we-simplify-deployment-of-containarized-apps-a2effd01a662/</link>
                <guid isPermaLink="false">66c34ce00fa3812cdd5ea9e8</guid>
                
                    <category>
                        <![CDATA[ continuous deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Helm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 16 Apr 2018 05:11:19 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*QzKmrPtCm5SvCklDccppkQ.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Omer Levi Hevroni</p>
<p>I asked myself this question when I started to learn <a target="_blank" href="https://kubernetes.io/">Kubernetes</a>: how can we simplify the deployment of containerized apps? At first look, deploying containerized apps seems pretty simple. All you need is a bunch of YAML files, and by using <code>[kubectl](https://kubernetes.io/docs/reference/generated/kubectl/kubectl/)</code> (the Kubernetes command line utility), you’ll have your service up and running in your Kubernetes cluster.</p>
<p>But, Although deploying one app is an easy task, how do you deploy hundreds of apps? At <a target="_blank" href="https://www.solutotlv.com">Soluto</a>, we have more than 100 live microservices and this number keeps growing. So as we started thinking about shifting workload to our Kubernetes cluster, we faced a few challenges:</p>
<p>First, Kubernetes deployment is actually more complex. There are many moving parts that you need to set up correctly: pod autoscaler, pod resources, ingress, and so on. Those parts require some experience with how Kubernetes works, and not setting it up correctly might cause issues in production. Ideally we’d have a way to simplify this, so developers can focus on writing their code and worry less about deployment.</p>
<p>Second, security is also a challenge. All services in production should have certain things, like Transport Layer Security (TLS). These are not necessarily complex things, but need to be taken care of nonetheless. We would like to pre-configure them so that any new deployment will be secured by default.</p>
<h3 id="heading-finding-a-solution">Finding a solution</h3>
<p>To solve these challenges and speed up and ease the adoption process, we looked for a way to create a template for Kubernetes. Something that any developer would be able to use, and would require just a few parameters (for example, the Docker image of the service) to get the service up and running in production.</p>
<p>On the other hand, we needed to be careful not to hide too much — developers must be able to understand what’s going on so they can handle production issues. We had to find the right level of abstraction that makes it easier to deploy to Kubernetes, without hiding too much detail.</p>
<p>With that in mind, we started to look for a solution. After trying a few things, we found <a target="_blank" href="https://helm.sh/">Helm</a>. Helm is a package manager for Kubernetes.You can use it to install any app on your cluster, and Helm will take care of getting all the required configuration files and installing them on your cluster. Helm also support updating deployments, rollbacks, and many other cool features. Each Helm package is called a “Chart”, and the charts are stored in a repository. With helm, installing <a target="_blank" href="https://github.com/kubernetes/charts/tree/master/stable/mongodb">Mongo</a>, for instance, is as easy as <code>helm install stable/mongodb</code>.</p>
<p>Sound like an excellent solution! We can define a chart for each type of service — like one for all our web APIs, which will handle things like load balancer and TLS — and the developer simply needs to specify the required parameters using Helm’s configuration files.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/4P0D8IGfVHiObVNHgAdPTrrhcNFOZh2irhoj" alt="Image" width="800" height="533" loading="lazy">
<em>Me and my teammate, when we found Helm</em></p>
<h3 id="heading-helm-lets-see-how-its-done">Helm: Let’s see how it’s done</h3>
<p>In order to use Helm, we first need to <a target="_blank" href="https://docs.helm.sh/using_helm/#installing-helm">install</a> it. Helm has two components: Helm client running on your computer, and Tiller, a server-side component running on your cluster. Then, we need to create the chart by using this simple Helm CLI command: <code>helm create web-api</code></p>
<p>After running this command, you’ll notice the creation of a new folder named “web-api”. Within this folder, you’ll find all the familiar Kubernetes configuration files: deployment, service, ingress, and so on. Now it’s time to customize a bit: we can add a <a target="_blank" href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">horizontal pod autoscaler</a>, define the default resources the pod requires, and of course, enable TLS by default.</p>
<p>Everything is highly customizable based on the <a target="_blank" href="https://docs.helm.sh/chart_template_guide/">Go templating mechanism</a>. So anything we add can be overridden later by the developer, in case the default configuration doesn’t work as needed.</p>
<p>So now we have a chart — but how can we consume it? The chart has to exist in a <a target="_blank" href="https://github.com/kubernetes/helm/blob/master/docs/chart_repository.md">Helm repository</a>, which is basically a server with a few Zip archives (which are all the charts in the repository) and one index file that is consumed by the CLI. You can manually set up your repo using any storage service like Azure Blob or AWS S3, but the simplest option is <a target="_blank" href="https://github.com/kubernetes-helm/chartmuseum">Chart Museum</a>.</p>
<p>Chart museum is a Helm repository with a CRUD API to manage your charts. It supports basic authentication so you can restrict who can push new charts to your Helm repository. Helm doesn’t supply any museum-as-a-service solution, so you’re gonna have to roll your own. But it’s dead simple — <a target="_blank" href="https://hub.docker.com/r/chartmuseum/chartmuseum/">simply use its docker image.</a></p>
<p>Now we can build a CI/CD pipeline for our web-api chart, to ease the process of modifying it:</p>
<ul>
<li>Run some kind of tests, to make sure that the new version is not broken. I’ll discuss how in the next paragraph.</li>
<li>Pack the new chart, using Helm CLI.</li>
<li>Push the new package to our Chart Museum instance, using Chart Museum’s API.</li>
</ul>
<h3 id="heading-testing-it-out">Testing it out</h3>
<p>Now our chart is ready to be used by developers! But wait… how can we know that the chart actually works? And how can we make sure it will continue to work? This is why we need to write tests for our chart (like we write for anything else).</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/WGGYmgftYuRQOqzlognwNV8yF4IFx6etjAF2" alt="Image" width="475" height="547" loading="lazy"></p>
<p>There are basically two things we want to test.</p>
<ol>
<li>We want to test our template — for example, if an ingress is supposed to exist with a TLS and specific rules (defined by the developer), we should test the generated template and make sure the ingress was created correctly.</li>
<li>We want to test that the files are valid Kubernetes configurations and that they work as expected.</li>
</ol>
<p>Testing the first thing is relatively simple — check out this <a target="_blank" href="https://github.com/omerlh/helm-chart-tests-demo">sample repo</a> to see just how simple it is.</p>
<p>This allows us to test the generated Kubernetes files, by using <code>[kubetest](https://github.com/garethr/kubetest)</code>. This is great, but can be complex and messy, especially when having a lot of branching in your template files.</p>
<p>A better solution is required — one that will allow us to do unit testing for the templates, without generating Kubernetes files. This wasn’t a problem until recently when we started to have a lot of branching in our templates, and we’re now looking for options.</p>
<p>The second thing — testing that Kubernetes files are valid — is a bit more tricky. For now, at Soluto we’re using Helm’s version mechanism: each chart has a version, and all of our services will use the latest stable version. When a new chart version is pushed, we can test this version on a specific service. If it works correctly, update the rest of the services. Another option is to test that using <code>minikube</code>, but it was too complex for our needs.</p>
<h3 id="heading-finally-deploying">Finally: Deploying!</h3>
<p>So now we have a CI/CD pipeline for our Helm charts, and we have prepared a Helm chart that the developers can use. Now, when a new developer wants to deploy a new service to production, all they have to do is:</p>
<ol>
<li>Add our repository to their local Helm using <code>helm repo add chartmuseum http://&lt;chart-museum-u</code>rl&gt;  </li>
<li>Create a new Helm config file and specify the required parameters (for example, docker image of the service)<br>3<code>. Run helm upgrade —install &lt;service-name&gt; chartmusuem/web-api -f&lt;path_</code>to_config_file&gt; and that’s it. The service is alive.</li>
</ol>
<p>And to make it even easier to understand, I’ve created a sample <a target="_blank" href="https://github.com/Soluto/kubernetes-deployment-demo">repository</a>. The repository contains all the things that I’ve discussed in this blog post: a generic chart for a web app that can be deployed with this chart and chart museum. Check it out to better understand what’s going on — and there is even a walkthrough to help you get started.</p>
<p>Thank you for reading along. If you have any questions, or you need help getting started with Helm, feel free to reach out either via the comments here or via <a target="_blank" href="https://twitter.com/intent/tweet?text=.%20%40omerlh%2C%20I%20have%20a%20question%20about%20%40Helm&amp;via=SolutoEng">Twitter</a>.</p>
<p>Happy Helming!</p>
<p>Originally posted on <a target="_blank" href="https://blog.solutotlv.com/?utm_source=medium">Soluto’s Blog</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
