<?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" version="2.0"><channel><title><![CDATA[Balman's Blog]]></title><description><![CDATA[Balman's Blog]]></description><link>https://blog.balmanrawat.com.np</link><generator>RSS for Node</generator><lastBuildDate>Mon, 11 May 2026 21:38:09 GMT</lastBuildDate><atom:link href="https://blog.balmanrawat.com.np/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Official  Helm Chart Template Guide Summary]]></title><description><![CDATA[Command Cheat sheet
https://helm.sh/docs/intro/cheatsheet/
Add/Search/Install/Uninstall repo
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update bitnami
helm repo list

helm install wordpress bitnami/wordpress
helm list
helm sta...]]></description><link>https://blog.balmanrawat.com.np/official-helm-chart-template-guide-summary</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/official-helm-chart-template-guide-summary</guid><category><![CDATA[Helm]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[SRE devops]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[distributed system]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Fri, 21 Jun 2024 03:11:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Sq0L3SPWLHI/upload/c5cb23e414621b2c844fb618eb89ffe3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-command-cheat-sheet">Command Cheat sheet</h3>
<p><a target="_blank" href="https://helm.sh/docs/intro/cheatsheet/">https://helm.sh/docs/intro/cheatsheet/</a></p>
<h3 id="heading-addsearchinstalluninstall-repo">Add/Search/Install/Uninstall repo</h3>
<pre><code class="lang-bash">helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update bitnami
helm repo list

helm install wordpress bitnami/wordpress
helm list
helm status wordpress
helm get all wordpress
helm get values wordpress
helm uninstall wordpress <span class="hljs-comment">#--keep-history uninstall while keeping history</span>
</code></pre>
<h3 id="heading-search-repositories">Search Repositories</h3>
<pre><code class="lang-bash">helm search repo
helm search hub
helm search bitnami
helm search repo wordpress
helm search hub wordpress
</code></pre>
<h3 id="heading-installation-order-helm">Installation Order Helm</h3>
<ul>
<li>namespace</li>
</ul>
<ul>
<li><p>NetworkPolicy</p>
</li>
<li><p>ResourceQuota</p>
</li>
<li><p>LimitRange</p>
</li>
<li><p>PodSecurityPolicy</p>
</li>
<li><p>PodDisruptionBudget</p>
</li>
<li><p>ServiceAccount</p>
</li>
<li><p>Secret</p>
</li>
<li><p>SecretList</p>
</li>
<li><p>ConfigMap</p>
</li>
<li><p>StorageClass</p>
</li>
<li><p>PersistentVolume</p>
</li>
<li><p>PersistentVolumeClaim</p>
</li>
<li><p>CustomResourceDefinition</p>
</li>
<li><p>ClusterRole</p>
</li>
<li><p>ClusterRoleList</p>
</li>
<li><p>ClusterRoleBinding</p>
</li>
<li><p>ClusterRoleBindingList</p>
</li>
<li><p>Role</p>
</li>
<li><p>RoleList</p>
</li>
<li><p>RoleBinding</p>
</li>
<li><p>RoleBindingList</p>
</li>
<li><p>Service</p>
</li>
<li><p>DaemonSet</p>
</li>
<li><p>Pod</p>
</li>
<li><p>ReplicationController</p>
</li>
<li><p>ReplicaSet</p>
</li>
<li><p>Deployment</p>
</li>
<li><p>HorizontalPodAutoscaler</p>
</li>
<li><p>StatefulSet</p>
</li>
<li><p>Job</p>
</li>
<li><p>CronJob</p>
</li>
<li><p>Ingress</p>
</li>
<li><p>APIService</p>
</li>
</ul>
<h3 id="heading-customizing-chart-before-installing">Customizing Chart before Installing</h3>
<pre><code class="lang-bash">helm show values bitnami/wordpress
</code></pre>
<h3 id="heading-more-installation-methods"><strong>More Installation Methods</strong></h3>
<p>The <code>helm install</code> command can install from several sources:</p>
<ul>
<li><p>A chart repository (as we've seen above)</p>
</li>
<li><p>A local chart archive (<code>helm install foo foo-0.1.1.tgz</code>)</p>
</li>
<li><p>An unpacked chart directory (<code>helm install foo path/to/foo</code>)</p>
</li>
<li><p>A full URL (<code>helm install foo</code> <a target="_blank" href="https://example.com/charts/foo-1.2.3.tgz"><code>https://example.com/charts/foo-1.2.3.tgz</code></a>)</p>
</li>
</ul>
<h2 id="heading-helm-upgrade-and-helm-rollback"><strong>'helm upgrade' and 'helm rollback':</strong></h2>
<p>An upgrade takes an existing release and upgrades it according to the information you provide. Because Kubernetes charts can be large and complex, Helm tries to perform the least invasive upgrade.  </p>
<p>NOTE: <em>It will only update things that have changed since the last release.</em></p>
<p>To see new upgrade too place:</p>
<pre><code class="lang-bash">helm get values wordpress
</code></pre>
<p>To rollback:</p>
<pre><code class="lang-bash">helm rollback wordpress 1
</code></pre>
<h2 id="heading-helpful-options-for-installupgraderollback"><strong>Helpful Options for Install/Upgrade/Rollback</strong></h2>
<ul>
<li><p><code>--timeout</code></p>
</li>
<li><p><code>--wait</code> It will wait for as long as the <code>--timeout</code> value. If timeout is reached, the release will be marked as <code>FAILED</code>.</p>
</li>
<li><p><code>--no-hooks</code>: This skips running hooks for the command</p>
</li>
<li><p><code>--recreate-pods</code> (only available for <code>upgrade</code> and <code>rollback</code>)</p>
</li>
</ul>
<h3 id="heading-creating-your-own-chart">Creating your own chart</h3>
<pre><code class="lang-bash">helm create webhotel
helm package webhotel
helm install my-hotel ./webhotel

<span class="hljs-comment">## debugging chart</span>
helm install --debug --dry-run goodly-guppy ./webhotel
</code></pre>
<p><strong>NOTES:</strong></p>
<ul>
<li><p>By virtue of the fact that this file is in the <code>mychart/templates/</code> directory, it will be sent through the template engine.</p>
</li>
<li><p>We recommend using the extension <code>.yaml</code> for YAML files and <code>.tpl</code> for helpers.</p>
</li>
<li><p>The template directive <code>{{ .</code><a target="_blank" href="http://Release.Name"><code>Release.Name</code></a> <code>}}</code> injects the release name into the template.</p>
</li>
<li><p>The <code>Release</code> object is one of the built-in objects for Helm</p>
</li>
</ul>
<h1 id="heading-built-in-objects"><strong>Built-in Objects</strong></h1>
<p>Objects are passed into a template from the template engine. And your code can pass objects around. There are even a few ways to create new objects within your templates, like with the <code>tuple</code> .</p>
<p>Objects can be simple, and have just one value. Or they can contain other objects or functions. For example, the <code>Release</code> object contains several objects (like <a target="_blank" href="http://Release.Name"><code>Release.Name</code></a>) and the <code>Files</code> object has a few functions.</p>
<p>NOTE: <em>The built-in values always begin with a capital letter</em>.</p>
<p><strong>Release Object</strong></p>
<p>This object describes the release itself. It has several objects inside of it:</p>
<ul>
<li><p><a target="_blank" href="http://Release.Name"><code>Release.Name</code></a></p>
</li>
<li><p><code>Release.Namespace</code></p>
</li>
<li><p><code>Release.IsUpgrade</code>: <code>true</code> if the current operation is an upgrade or rollback.</p>
</li>
<li><p><code>Release.IsInstall</code>: <code>true</code> if the current operation is an install.</p>
</li>
<li><p><code>Release.Revision</code>: The revision number for this release. On install, this is 1, and it is incremented with each upgrade and rollback.</p>
</li>
<li><p><code>Release.Service</code>: The service that is rendering the present template. On Helm, this is always <code>Helm</code>.</p>
</li>
</ul>
<p><strong>Values Object</strong></p>
<p>Values passed into the template from the <code>values.yaml</code> file and from user-supplied files. By default, <code>Values</code> is empty.</p>
<p><strong>Chart Object</strong></p>
<p>The contents of the <code>Chart.yaml</code> file. Any data in <code>Chart.yaml</code> will be accessible here. For example <code>{{ .Chart.Name }}-{{ .Chart.Version }}</code> will print out the <code>mychart-0.1.0</code>.</p>
<p>The available fields are listed in the <a target="_blank" href="https://helm.sh/docs/topics/charts/#the-chartyaml-file">Charts Guide</a></p>
<p><strong>Subchart Object</strong></p>
<p>This provides access to the scope (.Values, .Charts, .Releases etc.) of subcharts to the parent. For example <code>.Subcharts.mySubChart.myValue</code> to access the <code>myValue</code> in the <code>mySubChart</code> chart.</p>
<p><strong>Files Object</strong></p>
<p>This provides access to all non-special files in a chart. While you cannot use it to access templates, you can use it to access other files in the chart. See the section <a target="_blank" href="https://helm.sh/docs/chart_template_guide/accessing_files/">Accessing Files</a> for more.</p>
<ul>
<li><ul>
<li><p><code>Files.Get</code> is a function for getting a file by name (<code>.Files.Get config.ini</code>)</p>
<ul>
<li><p><code>Files.GetBytes</code> is a function for getting the contents of a file as an array of bytes instead of as a string. This is useful for things like images.</p>
</li>
<li><p><code>Files.Glob</code> is a function that returns a list of files whose names match the given shell glob pattern.</p>
</li>
<li><p><code>Files.Lines</code> is a function that reads a file line-by-line. This is useful for iterating over each line in a file.</p>
</li>
<li><p><code>Files.AsSecrets</code> is a function that returns the file bodies as Base 64 encoded strings.</p>
</li>
<li><p><code>Files.AsConfig</code> is a function that returns file bodies as a YAML map.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>Capabilities Object</strong></p>
<p>This provides information about what capabilities the Kubernetes cluster supports.</p>
<ul>
<li><ul>
<li><p><code>Capabilities.APIVersions</code> is a set of versions.</p>
<ul>
<li><p><code>Capabilities.APIVersions.Has $version</code> indicates whether a version (e.g., <code>batch/v1</code>) or resource (e.g., <code>apps/v1/Deployment</code>) is available on the cluster.</p>
</li>
<li><p><code>Capabilities.KubeVersion</code> and <code>Capabilities.KubeVersion.Version</code> is the Kubernetes version.</p>
</li>
<li><p><code>Capabilities.KubeVersion.Major</code> is the Kubernetes major version.</p>
</li>
<li><p><code>Capabilities.KubeVersion.Minor</code> is the Kubernetes minor version.</p>
</li>
<li><p><code>Capabilities.HelmVersion</code> is the object containing the Helm Version details, it is the same output of <code>helm version</code>.</p>
</li>
<li><p><code>Capabilities.HelmVersion.Version</code> is the current Helm version in semver format.</p>
</li>
<li><p><code>Capabilities.HelmVersion.GitCommit</code> is the Helm git sha1.</p>
</li>
<li><p><code>Capabilities.HelmVersion.GitTreeState</code> is the state of the Helm git tree.</p>
</li>
<li><p><code>Capabilities.HelmVersion.GoVersion</code> is the version of the Go compiler used.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>Template Object</strong></p>
<p>Contains information about the current template that is being executed</p>
<ul>
<li><ul>
<li><p><a target="_blank" href="http://Template.Name"><code>Template.Name</code></a>: A namespaced file path to the current template (e.g. <code>mychart/templates/mytemplate.yaml</code>)</p>
<ul>
<li><code>Template.BasePath</code>: The namespaced path to the templates directory of the current chart (e.g. <code>mychart/templates</code>).</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-values-files">Values Files</h3>
<p>This object provides access to values passed into the chart. Its contents come from multiple sources:</p>
<ul>
<li><p>The <code>values.yaml</code> file in the chart</p>
</li>
<li><p>If this is a subchart, the <code>values.yaml</code> file of a parent chart</p>
</li>
<li><p>A values file if passed into <code>helm install</code> or <code>helm upgrade</code> with the <code>-f</code> flag (<code>helm install -f myvals.yaml ./mychart</code>)</p>
</li>
<li><p>Individual parameters passed with <code>--set</code> (such as <code>helm install --set foo=bar ./mychart</code>)</p>
</li>
</ul>
<p>NOTE: <code>values.yaml</code> is the default, which can be overridden by a parent chart's <code>values.yaml</code>, which can in turn be overridden by a user-supplied values file, which can in turn be overridden by <code>--set</code> parameters.</p>
<p><strong>Deleting a default:</strong></p>
<pre><code class="lang-bash">helm install stable/drupal --<span class="hljs-built_in">set</span> livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --<span class="hljs-built_in">set</span> livenessProbe.httpGet=null
</code></pre>
<h3 id="heading-template-functions-and-pipelines">Template Functions and Pipelines</h3>
<p>So far, we've seen how to place information into a template. But that information is placed into the template unmodified. Sometimes we want to transform the supplied data in a way that makes it more useable to us.</p>
<p><strong>Template Function</strong></p>
<p>List of available functions: <a target="_blank" href="https://helm.sh/docs/chart_template_guide/function_list/">https://helm.sh/docs/chart_template_guide/function_list/</a></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> {{ <span class="hljs-string">quote</span> <span class="hljs-string">.Values.favorite.drink</span> }}
  <span class="hljs-attr">food:</span> {{ <span class="hljs-string">quote</span> <span class="hljs-string">.Values.favorite.food</span> }}
</code></pre>
<p><strong>Template Function syntax</strong></p>
<p><code>functionName arg1 arg2...</code></p>
<p><strong>Pipelines</strong></p>
<p>Pipelines are an efficient way of getting several things done in sequence. Let's rewrite the above example using a pipeline.</p>
<pre><code class="lang-yaml">
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.Values.favorite.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">repeat</span> <span class="hljs-number">5</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  <span class="hljs-attr">food:</span> {{ <span class="hljs-string">.Values.favorite.food</span> <span class="hljs-string">|</span> <span class="hljs-string">upper</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}

<span class="hljs-comment">### above snippet will be translated to below👇 yaml</span>
<span class="hljs-comment"># Source: mychart/templates/configmap.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">melting-porcup-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> <span class="hljs-string">"coffeecoffeecoffeecoffeecoffee"</span>
  <span class="hljs-attr">food:</span> <span class="hljs-string">"PIZZA"</span>
</code></pre>
<p><strong>Using Default Function</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.Values.favorite.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">default</span> <span class="hljs-string">"tea"</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
<span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.Values.favorite.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">default</span> <span class="hljs-string">(printf</span> <span class="hljs-string">"%s-tea"</span> <span class="hljs-string">(include</span> <span class="hljs-string">"fullname"</span> <span class="hljs-string">.))</span> }}
</code></pre>
<p>In some places, an <code>if</code> conditional guard may be better suited than <code>default</code>.\</p>
<p><strong>Using the lookup function</strong></p>
<p>The <code>lookup</code> function can be used to <em>look up</em> resources in a running cluster. The synopsis of the lookup function is <code>lookup apiVersion, kind, namespace, name -&gt; resource or resource list</code></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Behavior</td><td>Lookup function</td></tr>
</thead>
<tbody>
<tr>
<td><code>kubectl get pod mypod -n mynamespace</code></td><td><code>lookup "v1" "Pod" "mynamespace" "mypod"</code></td></tr>
<tr>
<td><code>kubectl get pods -n mynamespace</code></td><td><code>lookup "v1" "Pod" "mynamespace" ""</code></td></tr>
<tr>
<td><code>kubectl get pods --all-namespaces</code></td><td><code>lookup "v1" "Pod" "" ""</code></td></tr>
<tr>
<td><code>kubectl get namespace mynamespace</code></td><td><code>lookup "v1" "Namespace" "" "mynamespace"</code></td></tr>
<tr>
<td><code>kubectl get namespaces</code></td><td><code>lookup "v1" "Namespace" "" ""</code></td></tr>
</tbody>
</table>
</div><p>Example:</p>
<p><code>(lookup "v1" "Namespace" "" "mynamespace").metadata.annotations</code></p>
<p>NOTE: To test <code>lookup</code> against a running cluster, <code>helm template|install|upgrade|delete|rollback --dry-run=server</code> should be used</p>
<p><strong>Operators are functions</strong></p>
<p>For templates, the operators (<code>eq</code>, <code>ne</code>, <code>lt</code>, <code>gt</code>, <code>and</code>, <code>or</code> and so on) are all implemented as functions. In pipelines, operations can be grouped with parentheses (<code>(</code>, and <code>)</code>).</p>
<h3 id="heading-flow-control">Flow Control</h3>
<p>Helm's template language provides the following control structures:</p>
<ul>
<li><p><code>if</code>/<code>else</code> for creating conditional blocks</p>
</li>
<li><p><code>with</code> to specify a scope</p>
</li>
<li><p><code>range</code>, which provides a "for each"-style loop</p>
</li>
</ul>
<p><strong>If/Else</strong></p>
<pre><code class="lang-yaml">{{ <span class="hljs-string">if</span> <span class="hljs-string">PIPELINE</span> }}
  <span class="hljs-comment"># Do something</span>
{{ <span class="hljs-string">else</span> <span class="hljs-string">if</span> <span class="hljs-string">OTHER</span> <span class="hljs-string">PIPELINE</span> }}
  <span class="hljs-comment"># Do something else</span>
{{ <span class="hljs-string">else</span> }}
  <span class="hljs-comment"># Default case</span>
{{ <span class="hljs-string">end</span> }}

<span class="hljs-comment">## Example</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.Values.favorite.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">default</span> <span class="hljs-string">"tea"</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  <span class="hljs-attr">food:</span> {{ <span class="hljs-string">.Values.favorite.food</span> <span class="hljs-string">|</span> <span class="hljs-string">upper</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  {{ <span class="hljs-string">if</span> <span class="hljs-string">eq</span> <span class="hljs-string">.Values.favorite.drink</span> <span class="hljs-string">"coffee"</span> }}<span class="hljs-attr">mug:</span> <span class="hljs-string">"true"</span>{{ <span class="hljs-string">end</span> }}

<span class="hljs-comment">### Values</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">eyewitness-elk-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> <span class="hljs-string">"coffee"</span>
  <span class="hljs-attr">food:</span> <span class="hljs-string">"PIZZA"</span>
  <span class="hljs-attr">mug:</span> <span class="hljs-string">"true"</span>
</code></pre>
<p><strong>Modifying scope using with</strong></p>
<p>The next control structure to look at is the <code>with</code> action. This controls variable scoping. Recall that <code>.</code> is a reference to <em>the current scope</em>. So <code>.Values</code> tells the template to find the <code>Values</code> object in the current scope.</p>
<pre><code class="lang-yaml"><span class="hljs-comment">###Syntax:</span>
{{ <span class="hljs-string">with</span> <span class="hljs-string">PIPELINE</span> }}
  <span class="hljs-comment"># restricted scope</span>
{{ <span class="hljs-string">end</span> }}

<span class="hljs-comment">### Example</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">with</span> <span class="hljs-string">.Values.favorite</span> }}
  <span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">default</span> <span class="hljs-string">"tea"</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  <span class="hljs-attr">food:</span> {{ <span class="hljs-string">.food</span> <span class="hljs-string">|</span> <span class="hljs-string">upper</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
  <span class="hljs-attr">release:</span> {{ <span class="hljs-string">.Release.Name</span> }}
</code></pre>
<p><strong>Looping with the range action</strong></p>
<pre><code class="lang-yaml"><span class="hljs-comment">##Example</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">with</span> <span class="hljs-string">.Values.favorite</span> }}
  <span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">default</span> <span class="hljs-string">"tea"</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  <span class="hljs-attr">food:</span> {{ <span class="hljs-string">.food</span> <span class="hljs-string">|</span> <span class="hljs-string">upper</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
  <span class="hljs-attr">toppings:</span> <span class="hljs-string">|-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}   
</span>
<span class="hljs-comment">## Tuple example</span>
<span class="hljs-attr">sizes:</span> <span class="hljs-string">|-
    {{- range tuple "small" "medium" "large" }}
    - {{ . }}
    {{- end }}</span>
</code></pre>
<p>In addition to lists and tuples, <code>range</code> can be used to iterate over collections that have a key and a value (like a <code>map</code> or <code>dict</code>).</p>
<h3 id="heading-variables">Variables</h3>
<p>In Helm templates, a variable is a named reference to another object. It follows the form <code>$name</code>. Variables are assigned with a special assignment operator: <code>:=</code>. We can rewrite the above to use a variable for <a target="_blank" href="http://Release.Name"><code>Release.Name</code></a>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">$relname</span> <span class="hljs-string">:=</span> <span class="hljs-string">.Release.Name</span> <span class="hljs-string">-</span>}}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">with</span> <span class="hljs-string">.Values.favorite</span> }}
  <span class="hljs-attr">drink:</span> {{ <span class="hljs-string">.drink</span> <span class="hljs-string">|</span> <span class="hljs-string">default</span> <span class="hljs-string">"tea"</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  <span class="hljs-attr">food:</span> {{ <span class="hljs-string">.food</span> <span class="hljs-string">|</span> <span class="hljs-string">upper</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  <span class="hljs-attr">release:</span> {{ <span class="hljs-string">$relname</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
</code></pre>
<p>ariables are particularly useful in <code>range</code> loops. They can be used on list-like objects to capture both the index and the value:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">toppings:</span> <span class="hljs-string">|-
    {{- range $index, $topping := .Values.pizzaToppings }}
      {{ $index }}: {{ $topping }}
    {{- end }}
</span>
<span class="hljs-comment">#result</span>
<span class="hljs-attr">toppings:</span> <span class="hljs-string">|-
      0: mushrooms
      1: cheese
      2: peppers
      3: onions</span>
</code></pre>
<p>For data structures that have both a key and a value, we can use <code>range</code> to get both. For example, we can loop through <code>.Values.favorite</code> like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">range</span> <span class="hljs-string">$key</span>, <span class="hljs-string">$val</span> <span class="hljs-string">:=</span> <span class="hljs-string">.Values.favorite</span> }}
  {{ <span class="hljs-string">$key</span> }}<span class="hljs-string">:</span> {{ <span class="hljs-string">$val</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}

<span class="hljs-comment">#result</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">eager-rabbit-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> <span class="hljs-string">"coffee"</span>
  <span class="hljs-attr">food:</span> <span class="hljs-string">"pizza"</span>
</code></pre>
<p>Variables are normally not "global". They are scoped to the block in which they are declared. Earlier, we assigned <code>$relname</code> in the top level of the template. That variable will be in scope for the entire template. But in our last example, <code>$key</code> and <code>$val</code> will only be in scope inside of the <code>{{ range... }}{{ end }}</code> block.</p>
<p>However, there is one variable that is always global - <code>$</code> -this variable will always point to the root context.</p>
<h3 id="heading-named-template">Named Template</h3>
<p>It is time to move beyond one template, and begin to create others. In this section, we will see how to define <em>named templates</em> in one file, and then use them elsewhere. A <em>named template</em> (sometimes called a <em>partial</em> or a <em>subtemplate</em>) is simply a template defined inside of a file, and given a name. We'll see two ways to create them, and a few different ways to use them.</p>
<p>NOTE: An important detail to keep in mind when naming templates: <strong>template names are global</strong>. If you declare two templates with the same name, whichever one is loaded last will be the one used. Because templates in subcharts are compiled together with top-level templates, you should be careful to name your templates with <em>chart-specific names</em>.</p>
<p><strong>Partials and _ files</strong></p>
<ul>
<li><p>Most files in <code>templates/</code> are treated as if they contain Kubernetes manifests</p>
</li>
<li><p>The <code>NOTES.txt</code> is one exception</p>
</li>
<li><p>But files whose name begins with an underscore (<code>_</code>) are assumed to <em>not</em> have a manifest inside. These files are not rendered to Kubernetes object definitions, but are available everywhere within other chart templates for use.</p>
</li>
</ul>
<p><strong>Declaring and using templates with define and template</strong></p>
<pre><code class="lang-yaml">{{<span class="hljs-bullet">-</span> <span class="hljs-string">define</span> <span class="hljs-string">"MY.NAME"</span> }}
  <span class="hljs-comment"># body of template here</span>
{{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}

<span class="hljs-comment">### Example</span>
{{<span class="hljs-bullet">-</span> <span class="hljs-string">define</span> <span class="hljs-string">"mychart.labels"</span> }}
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">generator:</span> <span class="hljs-string">helm</span>
    <span class="hljs-attr">date:</span> {{ <span class="hljs-string">now</span> <span class="hljs-string">|</span> <span class="hljs-string">htmlDate</span> }}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
</code></pre>
<p>Now we can embed this template inside of our existing ConfigMap, and then include it with the <code>template</code> action:</p>
<pre><code class="lang-yaml">{{<span class="hljs-bullet">-</span> <span class="hljs-string">define</span> <span class="hljs-string">"mychart.labels"</span> }}
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">generator:</span> <span class="hljs-string">helm</span>
    <span class="hljs-attr">date:</span> {{ <span class="hljs-string">now</span> <span class="hljs-string">|</span> <span class="hljs-string">htmlDate</span> }}
{{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">template</span> <span class="hljs-string">"mychart.labels"</span> }}
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">range</span> <span class="hljs-string">$key</span>, <span class="hljs-string">$val</span> <span class="hljs-string">:=</span> <span class="hljs-string">.Values.favorite</span> }}
  {{ <span class="hljs-string">$key</span> }}<span class="hljs-string">:</span> {{ <span class="hljs-string">$val</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}

<span class="hljs-comment">## Result</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">running-panda-configmap</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">generator:</span> <span class="hljs-string">helm</span>
    <span class="hljs-attr">date:</span> <span class="hljs-number">2016-11-02</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  <span class="hljs-attr">drink:</span> <span class="hljs-string">"coffee"</span>
  <span class="hljs-attr">food:</span> <span class="hljs-string">"pizza"</span>
</code></pre>
<p>NOTE: Note: a <code>define</code> does not produce output unless it is called with a template, as in this example.</p>
<p>Conventionally, Helm charts put these templates inside of a partials file, usually <code>_helpers.tpl</code>. Let's move this function there:</p>
<p><strong>The include function</strong></p>
<p>Because <code>template</code> is an action, and not a function, there is no way to pass the output of a <code>template</code> call to other functions; the data is simply inserted inline.</p>
<p>To work around this case, Helm provides an alternative to <code>template</code> that will import the contents of a template into the present pipeline where it can be passed along to other functions in the pipeline.</p>
<p>Here's the example above, corrected to use <code>indent</code> to indent the <a target="_blank" href="http://mychart.app"><code>mychart.app</code></a> template correctly</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
  <span class="hljs-attr">labels:</span>
{{ <span class="hljs-string">include</span> <span class="hljs-string">"mychart.app"</span> <span class="hljs-string">.</span> <span class="hljs-string">|</span> <span class="hljs-string">indent</span> <span class="hljs-number">4</span> }}
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">myvalue:</span> <span class="hljs-string">"Hello World"</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">range</span> <span class="hljs-string">$key</span>, <span class="hljs-string">$val</span> <span class="hljs-string">:=</span> <span class="hljs-string">.Values.favorite</span> }}
  {{ <span class="hljs-string">$key</span> }}<span class="hljs-string">:</span> {{ <span class="hljs-string">$val</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
{{ <span class="hljs-string">include</span> <span class="hljs-string">"mychart.app"</span> <span class="hljs-string">.</span> <span class="hljs-string">|</span> <span class="hljs-string">indent</span> <span class="hljs-number">2</span> }}
</code></pre>
<p>NOTE: It is considered preferable to use <code>include</code> over <code>template</code> in Helm templates simply so that the output formatting can be handled better for YAML documents.</p>
<h3 id="heading-accessing-files-inside-templates">Accessing Files Inside Templates</h3>
<p><a target="_blank" href="https://helm.sh/docs/chart_template_guide/accessing_files/">https://helm.sh/docs/chart_template_guide/accessing_files/</a></p>
<p>Sometimes it is desirable to import a <em>file that is not a template</em> and inject its contents without sending the contents through the template renderer. Helm provides access to files through the <code>.Files</code> object.</p>
<p>NOTES</p>
<ul>
<li><p>It is okay to add extra files to your Helm chart. These files will be bundled. Be careful, though. Charts must be smaller than 1M because of the storage limitations of Kubernetes objects.</p>
</li>
<li><p>Some files cannot be accessed through the <code>.Files</code> object, usually for security reasons.</p>
<ul>
<li><p>Files in <code>templates/</code> cannot be accessed.</p>
</li>
<li><p>Files excluded using <code>.helmignore</code> cannot be accessed.</p>
</li>
<li><p>Files outside of a Helm application <a target="_blank" href="https://helm.sh/docs/chart_template_guide/subcharts_and_globals/">subchart</a>, including those of the parent, cannot be access</p>
</li>
</ul>
</li>
</ul>
<p><strong>Basic Examples</strong></p>
<p><code>onfig1.toml</code>:</p>
<pre><code class="lang-toml"><span class="hljs-attr">message</span> = Hello from config <span class="hljs-number">1</span>
</code></pre>
<p><code>config2.toml</code>:</p>
<pre><code class="lang-toml"><span class="hljs-attr">message</span> = This is config <span class="hljs-number">2</span>
</code></pre>
<p><code>config3.toml</code>:</p>
<pre><code class="lang-toml"><span class="hljs-attr">message</span> = Goodbye from config <span class="hljs-number">3</span>
</code></pre>
<p>Each of these is a simple TOML file (think old-school Windows INI files). We know the names of these files, so we can use a <code>range</code> function to loop through them and inject their contents into our ConfigMap.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">$files</span> <span class="hljs-string">:=</span> <span class="hljs-string">.Files</span> }}
  {{<span class="hljs-bullet">-</span> <span class="hljs-string">range</span> <span class="hljs-string">tuple</span> <span class="hljs-string">"config1.toml"</span> <span class="hljs-string">"config2.toml"</span> <span class="hljs-string">"config3.toml"</span> }}
  {{ <span class="hljs-string">.</span> }}<span class="hljs-string">:</span> <span class="hljs-string">|-
        {{ $files.Get . }}
</span>  {{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}

<span class="hljs-comment">### Result</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">quieting-giraf-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">config1.toml:</span> <span class="hljs-string">|-
        message = Hello from config 1
</span>
  <span class="hljs-attr">config2.toml:</span> <span class="hljs-string">|-
        message = This is config 2
</span>
  <span class="hljs-attr">config3.toml:</span> <span class="hljs-string">|-</span>
        <span class="hljs-string">message</span> <span class="hljs-string">=</span> <span class="hljs-string">Goodbye</span> <span class="hljs-string">from</span> <span class="hljs-string">config</span> <span class="hljs-number">3</span>
</code></pre>
<h3 id="heading-creating-a-notestxt-file">Creating a NOTES.txt File</h3>
<p><a target="_blank" href="https://helm.sh/docs/chart_template_guide/notes_files/">https://helm.sh/docs/chart_template_guide/notes_files/</a></p>
<h3 id="heading-subcharts-and-global-values">Subcharts and Global Values</h3>
<p>Before we dive into the code, there are a few important details to learn about application subcharts.</p>
<ol>
<li><p>A subchart is considered "stand-alone", which means a subchart can never explicitly depend on its parent chart.</p>
</li>
<li><p>For that reason, a subchart cannot access the values of its parent.</p>
</li>
<li><p>A parent chart can override values for subcharts.</p>
</li>
<li><p>Helm has a concept of <em>global values</em> that can be accessed by all charts.</p>
</li>
</ol>
<p><strong>Creating a Subchart</strong></p>
<p>For these exercises, we'll start with the <code>mychart/</code> chart we created at the beginning of this guide, and we'll add a new chart inside of it.</p>
<pre><code class="lang-console">cd mychart/charts
helm create mysubchart
Creating mysubchart
rm -rf mysubchart/templates/*
</code></pre>
<p><strong>Overriding Values from a Parent Values.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">favorite:</span>
  <span class="hljs-attr">drink:</span> <span class="hljs-string">coffee</span>
  <span class="hljs-attr">food:</span> <span class="hljs-string">pizza</span>
<span class="hljs-attr">pizzaToppings:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">mushrooms</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">cheese</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">peppers</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">onions</span>

<span class="hljs-comment">##Chart name</span>
<span class="hljs-attr">mysubchart:</span>
  <span class="hljs-attr">dessert:</span> <span class="hljs-string">ice</span> <span class="hljs-string">cream</span>
</code></pre>
<p><strong>Global Chart Values</strong></p>
<p>Global values are values that can be accessed from any chart or subchart by exactly the same name. Globals require explicit declaration. You can't use an existing non-global as if it were a global.</p>
<p>The Values data type has a reserved section called <a target="_blank" href="http://Values.global"><code>Values.global</code></a> where global values can be set. Let's set one in our <code>mychart/values.yaml</code> file.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">favorite:</span>
  <span class="hljs-attr">drink:</span> <span class="hljs-string">coffee</span>
  <span class="hljs-attr">food:</span> <span class="hljs-string">pizza</span>
<span class="hljs-attr">pizzaToppings:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">mushrooms</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">cheese</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">peppers</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">onions</span>

<span class="hljs-attr">mysubchart:</span>
  <span class="hljs-attr">dessert:</span> <span class="hljs-string">ice</span> <span class="hljs-string">cream</span>

<span class="hljs-attr">global:</span>
  <span class="hljs-attr">salad:</span> <span class="hljs-string">caesar</span>
</code></pre>
<p>Example Config Map</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Release.Name</span> }}<span class="hljs-string">-configmap</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">salad:</span> {{ <span class="hljs-string">.Values.global.salad</span> }}
</code></pre>
<p><strong>Sharing Templates with Subcharts</strong></p>
<p>Parent charts and subcharts can share templates. Any defined block in any chart is available to other charts.</p>
<p>For example, we can define a simple template like this:</p>
<pre><code class="lang-yaml">{{<span class="hljs-bullet">-</span> <span class="hljs-string">define</span> <span class="hljs-string">"labels"</span> }}<span class="hljs-attr">from:</span> <span class="hljs-string">mychart{{</span> <span class="hljs-string">end</span> <span class="hljs-string">}}</span>
</code></pre>
<p>Recall how the labels on templates are <em>globally shared</em>. Thus, the <code>labels</code> chart can be included from any other chart.</p>
<p>While chart developers have a choice between <code>include</code> and <code>template</code>, one advantage of using <code>include</code> is that <code>include</code> can dynamically reference templates:</p>
<pre><code class="lang-yaml">{{ <span class="hljs-string">include</span> <span class="hljs-string">$mytemplate</span> }}
</code></pre>
<p>The above will dereference <code>$mytemplate</code>. The <code>template</code> function, in contrast, will only accept a string literal.</p>
]]></content:encoded></item><item><title><![CDATA[KOps Quick Bootstrapping on AWS]]></title><description><![CDATA[https://kops.sigs.k8s.io/getting_started/aws/
Pre-requisite
DOMAIN=kops.balmanrawat.com.np

aws route53 create-hosted-zone \
    --name ${DOMAIN} \
    --caller-reference kops-expriment

ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --ou...]]></description><link>https://blog.balmanrawat.com.np/kops-quick-bootstrapping-on-aws</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/kops-quick-bootstrapping-on-aws</guid><category><![CDATA[Kops]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[cluster]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Sat, 08 Jun 2024 09:32:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HFI_UM7RLoU/upload/279b53285c42009d1d6025cb34629b6c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://kops.sigs.k8s.io/getting_started/aws/">https://kops.sigs.k8s.io/getting_started/aws/</a></p>
<h3 id="heading-pre-requisite">Pre-requisite</h3>
<pre><code class="lang-bash">DOMAIN=kops.balmanrawat.com.np

aws route53 create-hosted-zone \
    --name <span class="hljs-variable">${DOMAIN}</span> \
    --caller-reference kops-expriment

ACCOUNT_ID=$(aws sts get-caller-identity --query <span class="hljs-string">'Account'</span> --output text)

aws s3api create-bucket \
    --bucket kops-<span class="hljs-variable">${ACCOUNT_ID}</span> \
    --region us-east-1

aws s3api put-bucket-versioning \
    --bucket kops-<span class="hljs-variable">${ACCOUNT_ID}</span> \
    --versioning-configuration Status=Enabled
</code></pre>
<h3 id="heading-oidc">OIDC</h3>
<pre><code class="lang-bash">aws s3api create-bucket \
    --bucket kops-oidc-<span class="hljs-variable">${ACCOUNT_ID}</span> \
    --region us-east-1 \
    --object-ownership BucketOwnerPreferred

aws s3api put-public-access-block \
    --bucket kops-oidc-<span class="hljs-variable">${ACCOUNT_ID}</span> \
    --public-access-block-configuration BlockPublicAcls=<span class="hljs-literal">false</span>,IgnorePublicAcls=<span class="hljs-literal">false</span>,BlockPublicPolicy=<span class="hljs-literal">false</span>,RestrictPublicBuckets=<span class="hljs-literal">false</span>

aws s3api put-bucket-acl \
    --bucket kops-oidc-<span class="hljs-variable">${ACCOUNT_ID}</span> \
    --acl public-read
</code></pre>
<h3 id="heading-creating-cluster">Creating Cluster</h3>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> NAME=<span class="hljs-variable">${DOMAIN}</span>
<span class="hljs-built_in">export</span> KOPS_STATE_STORE=s3://kops-<span class="hljs-variable">${ACCOUNT_ID}</span>

<span class="hljs-comment"># aws ec2 describe-availability-zones --region us-east-1</span>

kops create cluster \
    --name=<span class="hljs-variable">${NAME}</span> \
    --cloud=aws \
    --zones=us-east-1a \
    --dns-zone=kubernetes.<span class="hljs-variable">${DOMAIN}</span> \
    --discovery-store=s3://kops-oidc-<span class="hljs-variable">${ACCOUNT_ID}</span>/<span class="hljs-variable">${NAME}</span>/discovery

kops update cluster --name <span class="hljs-variable">${NAME}</span> --yes --admin
</code></pre>
<h3 id="heading-validate">Validate</h3>
<pre><code class="lang-bash">Suggestions:
 * validate cluster: kops validate cluster --<span class="hljs-built_in">wait</span> 10m
 * list nodes: kubectl get nodes --show-labels
 * ssh to a control-plane node: ssh -i ~/.ssh/id_rsa ubuntu@
 * the ubuntu user is specific to Ubuntu. If not using Ubuntu please use the appropriate user based on your OS.
 * <span class="hljs-built_in">read</span> about installing addons at: https://kops.sigs.k8s.io/addons.
</code></pre>
<h3 id="heading-cleanup">Cleanup</h3>
<pre><code class="lang-bash">kops delete cluster --name <span class="hljs-variable">${NAME}</span> --yes

aws s3 rm s3://kops-<span class="hljs-variable">${ACCOUNT_ID}</span> --recursive
aws s3api delete-bucket --bucket kops-<span class="hljs-variable">${ACCOUNT_ID}</span> --region us-east-1

aws s3 rm s3://kops-oidc-<span class="hljs-variable">${ACCOUNT_ID}</span> --recursive
aws s3api delete-bucket --bucket kops-oidc-<span class="hljs-variable">${ACCOUNT_ID}</span> --region us-east-1

HOSTEDZONE_ID=$(aws route53 list-hosted-zones-by-name --dns-name <span class="hljs-variable">${DOMAIN}</span> --query <span class="hljs-string">'HostedZones[0].Id'</span> --output text | sed <span class="hljs-string">'s/\/hostedzone\///'</span>)
aws route53 list-resource-record-sets --hosted-zone-id <span class="hljs-variable">${HOSTEDZONE_ID}</span> &gt; delete-record-set.json
<span class="hljs-comment">#delete ns/soa record form the file and run the delete</span>
aws route53 change-resource-record-sets --hosted-zone-id <span class="hljs-variable">${HOSTEDZONE_ID}</span> --change-batch file://delete-record-sets.json
aws route53 delete-hosted-zone --id <span class="hljs-variable">${HOSTEDZONE_ID}</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Summary of the Book "Container Security" by Liz Rice]]></title><description><![CDATA[This blog post is a summary of key insights from the book "Container Security" authored by Liz Rice. These notes were captured as I went through the book, so certain portions might be missing or not as detailed as in the original text. Nonetheless, t...]]></description><link>https://blog.balmanrawat.com.np/container-security-by-liz-rice</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/container-security-by-liz-rice</guid><category><![CDATA[container]]></category><category><![CDATA[Security]]></category><category><![CDATA[containersecurity]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[virtualization]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Fri, 07 Jun 2024 23:34:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/F_KCIRbsxAY/upload/ed777f58c8166eac3e503759264d669e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog post is a summary of key insights from the book "Container Security" authored by <a target="_blank" href="https://x.com/lizrice">Liz Rice</a>. These notes were captured as I went through the book, so certain portions might be missing or not as detailed as in the original text. Nonetheless, this summary highlights the main points and is intended to serve as a helpful reference. For a comprehensive understanding, I highly recommend reading the full book. The original book can be found <a target="_blank" href="https://www.oreilly.com/library/view/container-security/9781492056690/">here</a><a target="_blank" href="https://www.oreilly.com/library/view/container-security/9781492056690/">.</a></p>
<h2 id="heading-chapter-1-container-security-threats">Chapter 1 ( Container Security Threats )</h2>
<p><strong>Risks, Threats, and Mitigations</strong></p>
<p>A <em>risk</em> is a potential problem, and the effects of that problem if it were to occur.</p>
<p>A <em>threat</em> is a path to that risk occurring.</p>
<p>A <em>mitigation</em> is a countermeasure against a threat—something you can do to prevent the threat or at least reduce the likelihood of its success.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712921619599/e4d94347-35cc-4dd4-ab14-3c54c17b0034.png" alt class="image--center mx-auto" /></p>
<p><strong>Security Principles</strong></p>
<ul>
<li><p><strong>Least Privilege</strong></p>
</li>
<li><p><strong>Defense in Depth</strong></p>
<ul>
<li>have multiple layers of defense</li>
</ul>
</li>
<li><p><strong>Reducing the Attack Surface</strong></p>
<ul>
<li><p>less code and less dependency</p>
</li>
<li><p>system with less components</p>
</li>
<li><p>Limiting the users and components who can access a service</p>
</li>
</ul>
</li>
<li><p><strong>Limiting the Blast Radius</strong></p>
</li>
<li><p><strong>Segregation of Duties</strong></p>
<ul>
<li>give only subset of privilege to different components or people</li>
</ul>
</li>
</ul>
<h1 id="heading-chapter-2-linux-system-calls-permissions-and-capabilities"><strong>Chapter 2 ( Linux System Calls, Permissions, and Capabilities )</strong></h1>
<p><strong>System Calls</strong></p>
<p>The programmatic interface that the user space code uses to make these requests of the kernel is known as the <em>system call</em> or <em>syscall</em> interface.</p>
<p>There are some 300+ different system calls, with the number varying according to the version of Linux kernel.</p>
<p><strong>File Permissions</strong></p>
<ul>
<li><p><strong>setuid</strong></p>
</li>
<li><p><strong>setgid</strong></p>
</li>
</ul>
<p>Linux Capabilities: there is another way to give ping sufficient privileges to open the socket without the executable having all the privileges associated with root.</p>
<p>Because <em>setuid</em> provides a dangerous pathway to privilege escalation, some container image scanners (covered in Chapter 7) will report on the presence of files with the <em>setuid</em> bit set. You can also prevent it from being used with the --no-new-privileges flag on a docker run command.</p>
<p><strong>Linux Capabilities</strong></p>
<p>There are over 30 different capabilities in today’s Linux kernel. Capabilities can be assigned to a thread to determine whether that thread can perform certain actions. For example, a thread needs the CAP_NET_BIND_SERVICE capability in order to bind to a low-numbered (below 1024) port. CAP_SYS_BOOT exists so that arbitrary executables don’t have permission to reboot the system. CAP_SYS_MODULE is needed to load or unload kernel modules.</p>
<p>You can see the capabilities assigned to a process by using the getpcaps command. For example, a process run by a non-root user typically won’t have capabilities:</p>
<pre><code class="lang-bash">getpcaps &lt;pid&gt;
<span class="hljs-built_in">setcap</span> <span class="hljs-string">'cap_net_raw+p'</span> &lt;executable&gt;
</code></pre>
<h1 id="heading-chapter-3-control-groups"><strong>Chapter 3 (</strong> Control Groups )</h1>
<p><strong>Cgroup Hierarchies</strong></p>
<p>There is a hierarchy of control groups for each type of resource being managed, and each hierarchy is managed by a cgroup controller. Any Linux process is a member of one cgroup of each type, and when it is first created, a process inherits the cgroups of its parent.</p>
<p>The Linux kernel communicates information about cgroups through a set of pseudo‐ filesystems that typically reside at /sys/fs/cgroup.</p>
<p><strong>Assigning a Process to a Cgroup</strong></p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> 100000 &gt; memory.limit_in_bytes
<span class="hljs-built_in">echo</span> 29903 &gt; cgroup.procs
</code></pre>
<h1 id="heading-chapter-4-container-isolation">Chapter 4 (Container Isolation)</h1>
<p><strong>Linux Namespaces</strong></p>
<p>If cgroups control the resources that a process can use, <em>namespaces</em> control what it can see. By putting a process in a namespace, you can restrict the resources that are visible to that process.</p>
<p><strong>PID Namespace</strong>:</p>
<ul>
<li><p>Isolates the process ID number space, allowing processes inside the namespace to have the same PID as processes outside the namespace.</p>
</li>
<li><p>Enables the creation of containers with their own init process, providing a private process tree.</p>
</li>
</ul>
<p><strong>NET Namespace</strong>:</p>
<ul>
<li><p>Provides isolation of network interfaces, IP addresses, routing tables, and port numbers.</p>
</li>
<li><p>Allows containers to have their own network stack, separate from the host and other containers.</p>
</li>
</ul>
<p><strong>MNT Namespace</strong>:</p>
<ul>
<li><p>Isolates the filesystem mount points, so changes to mounts (e.g., adding or removing filesystems) in one namespace do not affect others.</p>
</li>
<li><p>Ensures each container can have its own file system layout.</p>
</li>
</ul>
<p><strong>IPC Namespace</strong>:</p>
<ul>
<li><p>Isolates System V IPC objects and POSIX message queues, allowing processes to communicate within the same namespace without interfering with other namespaces.</p>
</li>
<li><p>Useful for containers that need shared memory or semaphores.</p>
</li>
</ul>
<p><strong>UTS Namespace</strong>:</p>
<ul>
<li><p>Isolates the hostname and NIS domain name, allowing containers to have a different hostname and domain name from the host.</p>
</li>
<li><p>Useful for setting custom hostnames within containers.</p>
</li>
</ul>
<p><strong>USER Namespace</strong>:</p>
<ul>
<li><p>Isolates user and group IDs, enabling processes to have different user IDs and privileges inside the namespace compared to outside.</p>
</li>
<li><p>Allows running containers as non-root users while providing root privileges within the container.</p>
</li>
</ul>
<p><strong>CGROUP Namespace</strong>:</p>
<ul>
<li><p>Isolates the view of the cgroups, so each namespace sees its own cgroups.</p>
</li>
<li><p>Helps in managing and limiting the resources (CPU, memory, etc.) available to the processes within the namespace.</p>
</li>
</ul>
<p><strong>TIME Namespace</strong>:</p>
<ul>
<li><p>Isolates the system and monotonic clocks, allowing processes within the namespace to see and manipulate different time values.</p>
</li>
<li><p>Useful for testing software behavior at different times or handling time-sensitive applications in containers without affecting the host system.</p>
</li>
</ul>
<h1 id="heading-chapter-5-virtual-machines"><strong>Chapter 5 ( Virtual Machines )</strong></h1>
<p><strong>....</strong></p>
<h1 id="heading-capter-6-container-images"><strong>Capter 6 ( Container Images )</strong></h1>
<p><strong>Dockerfile Best Practices for Security</strong></p>
<p><em>Base image</em></p>
<ul>
<li><p>Refer to an image from a trusted registry</p>
</li>
<li><p>The smaller the base image, the less likely that it includes unnecessary code, and hence the smaller the attack surface.</p>
</li>
<li><p><a target="_blank" href="https://github.com/GoogleContainerTools/distroless">https://github.com/GoogleContainerTools/distroless</a></p>
</li>
<li><p>Be thoughtful about using a tag or a digest to reference the base image.</p>
</li>
</ul>
<p><em>Use multi-stage builds</em></p>
<p><em>Non-root USER</em></p>
<p><em>RUN commands</em></p>
<p><em>Volume mounts</em></p>
<ul>
<li>Be careful while running volume mounts of system directories like /etc</li>
</ul>
<p><em>Don’t include sensitive data in the Dockerfile</em></p>
<p><em>Avoid setuid binaries</em></p>
<p><em>Avoid unnecessary code</em></p>
<p><em>Include everything that your container needs</em></p>
<h1 id="heading-chapter-7-software-vulnerabilities-in-images"><strong>Chapter 7 ( Software Vulnerabilities in Images )</strong></h1>
<p><a target="_blank" href="https://github.com/aquasecurity/trivy">https://github.com/aquasecurity/trivy</a></p>
<p><a target="_blank" href="https://aws.amazon.com/blogs/containers/scanning-images-with-trivy-in-an-aws-codepipeline/">https://aws.amazon.com/blogs/containers/scanning-images-with-trivy-in-an-aws-codepipeline/</a></p>
<p><a target="_blank" href="https://bitnami.com/stack/nginx">https://bitnami.com/stack/nginx</a></p>
<h1 id="heading-chapter-8-strengthening-container-isolation"><strong>Chapter 8 ( Strengthening Container Isolation )</strong></h1>
<p><a target="_blank" href="https://blog.jessfraz.com/post/how-to-use-new-docker-seccomp-profiles/">https://blog.jessfraz.com/post/how-to-use-new-docker-seccomp-profiles/</a></p>
<p><a target="_blank" href="https://github.com/nevins-b/falco2seccomp">https://github.com/nevins-b/falco2seccomp</a></p>
<p><a target="_blank" href="https://github.com/aquasecurity/tracee">https://github.com/aquasecurity/tracee</a></p>
<p>In AppArmor, a profile can be associated with an executable file, determining what that file is allowed to do in terms of capabilities and file access permissions.</p>
<p><code>/sys/module/apparmor/parameters/enabled</code></p>
<p>AppArmor and other LSMs implement <em>mandatory access controls</em>. A mandatory access control is set by a central administrator, and once set, other users do not have any ability to modify the control or pass it on to another user.</p>
<p><a target="_blank" href="https://github.com/genuinetools/bane">https://github.com/genuinetools/bane</a> (Custom &amp; better AppArmor profile generator for Docker containers.)</p>
<p><strong>gVisor</strong></p>
<p>Google’s gVisor sandboxes a container by intercepting system calls in much the same way that a hypervisor intercepts the system calls of a guest virtual machine.</p>
<p>According to the gVisor documentation, gVisor is a “user-space kernel,” which strikes me as a contradiction in terms but is meant to describe how a number of Linux sys‐ tem calls are implemented in user space through paravirtualization. As you saw in Chapter 5, paravirtualization means reimplementing instructions that would other‐ wise be run by the host kernel.</p>
<p><strong>Kata Containers</strong></p>
<p>As you’ve seen in Chapter 4, when you run a regular container, the container runtime starts a new process within the host. The idea with Kata Containers is to run contain‐ ers within a separate virtual machine. This approach gives the ability to run applica‐ tions from regular OCI format container images, with all the isolation of a virtual machine.</p>
<p>Kata uses a proxy between the container runtime and a separate target host where the application code runs. The runtime proxy creates a separate virtual machine using QEMU to run the container on its behalf.</p>
<p>One criticism of Kata Containers is that you have to wait for a virtual machine to boot up. The folks at AWS have created a lightweight virtual machine that is specifically designed for running containers, with much faster startup times than a normal VM: Firecracker.</p>
<p><strong>Firecracker</strong></p>
<p>As you saw in “Disadvantages of Virtual Machines” on page 62, virtual machines are slow to start, making them unsuitable for the ephemeral workloads that typically run in containers. But what if you had a virtual machine that boots extremely quickly? Firecracker is a virtual machine offering the benefits of secure isolation through a hypervisor and no shared kernel, but with startup times around 100ms, it is much more suitable for containers. It has the benefit of becoming field-hardened due to its (as I understand it, gradual) adoption by AWS for its Lambda and Fargate services.</p>
<p><strong>Unikernels</strong></p>
<p>The idea of Unikernels is to create a dedicated machine image consisting of the appli‐ cation and the parts of the operating system that the app needs. This machine image can run directly on the hypervisor, giving the same levels of isolation as regular virtual machines, but with a lightweight startup time similar to what we see in Firecracker.</p>
<p>Every application has to be compiled into a Unikernel image complete with every‐ thing it needs to operate. The hypervisor can boot up this machine in just the same way that it would boot a standard Linux virtual machine image.</p>
<h1 id="heading-chapter-9-breaking-container-isolation"><strong>Chapter 9 ( Breaking Container Isolation )</strong></h1>
<p><strong>Containers Run as Root by Default</strong></p>
<p><strong>Rootless Containers</strong></p>
<p><strong>The --privileged Flag and Capabilities</strong></p>
<p><strong>Mounting Sensitive Directories</strong></p>
<p><strong>Mounting the Docker Socket</strong></p>
<p><strong>Sharing Namespaces Between a Container and Its Host</strong></p>
<p><strong>Sidecar Containers</strong></p>
<h1 id="heading-chapter-10-container-network-security"><strong>Chapter 10 ( Container Network Security )</strong></h1>
<p><a target="_blank" href="https://bitnami.com/stack/nginx">https://bitnami.com/stack/nginx</a></p>
<p><a target="_blank" href="https://rootlesscontaine.rs/">https://rootlesscontaine.rs/</a></p>
<pre><code class="lang-bash">docker run -it --rm nginx
./tracee.py -c -e cap_capable
docker run --cap-drop=all --cap-add=&lt;cap1&gt; --cap-add=&lt;cap2&gt; &lt;image&gt;
</code></pre>
<p><strong>Layer 3/4 Routing and Rules</strong></p>
<p>As you already know, routing at Layer 3 is concerned about deciding the next hop for an IP packet. This decision is based on a set of rules about which addresses are reached over which interface. But this is just a subset of things you can do with Layer 3 rules: there are also some fun things that can go on at this level to drop packets or manipulate IP addresses, for example, to implement load balancing, NAT, firewalls, and network security policies. Rules can also act at layer 4 to take into account the port number. These rules rely on a kernel feature called netfilter.</p>
<p>netfilter is a packet-filtering framework that was first introduced into the Linux kernel in version 2.4. It uses a set of rules that define what to do with a packet based on its source and destination addresses.</p>
<p>There are a few different ways that netfilter rules can be configured in user space. Let’s look at the two most common options: iptables and IPVS.</p>
<p><strong>iptables</strong></p>
<p>The iptables tool is one way of configuring IP packet–handling rules that are dealt with in the kernel using netfilter. There are several different table types; the two most interesting types in the context of container networking are filter and nat:</p>
<ul>
<li><p>filter—for deciding whether to drop or forward packets</p>
</li>
<li><p>nat—for translating addresses</p>
<p>  As a root user, you can see the current set of rules of a particular type by running iptables -t &lt;table type&gt; -L.</p>
</li>
</ul>
<p><strong>IPVS</strong></p>
<p>IP Virtual Server (IPVS) is sometimes referred to as Layer 4 load balancing or Layer 4 LAN switching. It is another rules implementation similar to iptables, but it’s opti‐ mized for load balancing by storing the forwarding rules in hash tables.</p>
<p>This optimization makes it very performant for kube-proxy’s use case, but it doesn’t necessarily mean you can draw conclusions about the performance of network plug- ins implementing network policies.</p>
<p>Whether it’s IPVS or iptables that manages netfilter rules, they act within the ker‐ nel.</p>
<h1 id="heading-chapter-11-securely-connecting-components-with-tls"><strong>Chapter 11 ( Securely Connecting Components with TLS )</strong></h1>
<p><strong>....</strong></p>
<h1 id="heading-chapter-12-passing-secrets-to-containers"><strong>Chapter 12 ( Passing Secrets to Containers )</strong></h1>
<ol>
<li><p><strong>Storing the Secret in the Container Image</strong></p>
</li>
<li><p><strong>Passing the Secret Over the Network</strong></p>
</li>
<li><p><strong>Passing Secrets in Environment Variables</strong></p>
</li>
<li><p><strong>Passing Secrets Through Files</strong></p>
</li>
</ol>
<h1 id="heading-chapter-13-container-runtime-protection"><strong>Chapter 13 ( Container Runtime Protection )</strong></h1>
<p><strong>Container Image Profiles</strong></p>
<p><strong>Network Traffic Profiles</strong></p>
<p><strong>Executable Profiles</strong></p>
<ul>
<li><strong>Observing executables with eBPF</strong></li>
</ul>
<p><strong>File Access Profiles</strong></p>
<p><strong>User ID Profiles</strong></p>
<p><strong>Other Runtime Profiles</strong></p>
<p><strong>Container Security Tools</strong></p>
<ul>
<li><p>AppArmor, SELinux, or seccomp profile.</p>
</li>
<li><p>Network traffic can be policed at runtime using network policy or a service mesh, as described in</p>
</li>
<li><p>CNCF project Falco.</p>
</li>
</ul>
<p><strong>Prevention or alerting</strong></p>
<h1 id="heading-chapter-14-containers-and-the-owasp-top-10"><strong>Chapter 14 ( Containers and the OWASP Top 10 )</strong></h1>
<p><strong>....</strong></p>
<h1 id="heading-tools-to-play-with-container-internals">Tools to play with Container Internals</h1>
<ul>
<li><p>lscgroup</p>
</li>
<li><p>lib-cgroup</p>
</li>
<li><p>setns</p>
</li>
<li><p>nsenter</p>
</li>
<li><p>unshare</p>
</li>
<li><p>chroot</p>
</li>
<li><p>lsns</p>
</li>
</ul>
<h1 id="heading-links">Links</h1>
<p><a target="_blank" href="https://www.openwall.com/lists/oss-security/2019/02/11/2">https://www.openwall.com/lists/oss-security/2019/02/11/2</a></p>
<p><a target="_blank" href="https://github.com/0xAX/linux-insides">https://github.com/0xAX/linux-insides</a></p>
<p><a target="_blank" href="https://github.com/opencontainers/umoci">https://github.com/opencontainers/umoci</a></p>
<p><a target="_blank" href="https://github.com/containers/skopeo">https://github.com/containers/skopeo</a></p>
<p><a target="_blank" href="https://medium.com/microscaling-systems/spot-the-docker-difference-9f99adcc4aaf">https://medium.com/microscaling-systems/spot-the-docker-difference-9f99adcc4aaf</a></p>
<p><a target="_blank" href="https://github.com/GoogleContainerTools/distroless">https://github.com/GoogleContainerTools/distroless</a></p>
<p><a target="_blank" href="https://github.com/aquasecurity/trivy">https://github.com/aquasecurity/trivy</a></p>
<p><a target="_blank" href="https://aws.amazon.com/blogs/containers/scanning-images-with-trivy-in-an-aws-codepipeline/">https://aws.amazon.com/blogs/containers/scanning-images-with-trivy-in-an-aws-codepipeline/</a></p>
<p><a target="_blank" href="https://www.schneier.com/blog/archives/2016/08/the_nsa_is_hoar.html">https://www.schneier.com/blog/archives/2016/08/the_nsa_is_hoar.html</a></p>
<p><a target="_blank" href="https://blog.jessfraz.com/post/how-to-use-new-docker-seccomp-profiles/">https://blog.jessfraz.com/post/how-to-use-new-docker-seccomp-profiles/</a></p>
<p><a target="_blank" href="https://bitnami.com/stack/nginx">https://bitnami.com/stack/nginx</a></p>
]]></content:encoded></item><item><title><![CDATA[Memo: Bootstrapping Kubernets using Kubeadm]]></title><description><![CDATA[Details: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
Pre-requisite

A compatible Linux host.

Verify the MAC address and product_uuid are unique for every node. Kubernetes uses these values to uniquely ident...]]></description><link>https://blog.balmanrawat.com.np/bootstrapping-kubernets-using-kubeadm</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/bootstrapping-kubernets-using-kubeadm</guid><category><![CDATA[k8s]]></category><category><![CDATA[kubeadm]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Fri, 07 Jun 2024 23:05:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xL86g_rz28M/upload/6ff2eba1dc9d350c0571860b8fc7bc3c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Details: <a target="_blank" href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/">https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/</a></p>
<h2 id="heading-pre-requisite">Pre-requisite</h2>
<ul>
<li><p>A compatible Linux host.</p>
</li>
<li><p><strong>Verify the MAC address and product_uuid</strong> are unique for every node. Kubernetes uses these values to uniquely identify the nodes in the cluster.</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># Verify the mac-address</span>
  ip link

  <span class="hljs-comment"># The product_uuid can verfied using following commands. </span>
  <span class="hljs-comment"># If these values are not unique to each node, the installation process may fail.</span>
  sudo cat /sys/class/dmi/id/product_uuid
</code></pre>
</li>
<li><p><strong>Check network adapters</strong></p>
</li>
<li><p><strong>Check required ports</strong><br />  These <a target="_blank" href="https://kubernetes.io/docs/reference/networking/ports-and-protocols/">required ports need to be op</a>en in order for Kubernetes components to communicate with each other. The pod network plugin you use may also require certain ports to be open. Since this differs with each pod network plugin</p>
</li>
</ul>
<pre><code class="lang-bash">nc 127.0.0.1 6443 -v
</code></pre>
<ul>
<li><p>2 GB or more of RAM per machine (any less will leave little room for your apps).</p>
</li>
<li><p>2 CPUs or more.</p>
</li>
<li><p>Swap configuration. The default behavior of a kubelet was to fail to start if swap memory was detected on a node. See <a target="_blank" href="https://kubernetes.io/docs/concepts/architecture/nodes/#swap-memory">Swap memory management for more details.</a></p>
<ul>
<li>You <strong>MUST</strong> disable swap if the kubelet is not properly configured to use swap. For example, <code>sudo swapoff -a</code> will disable swapping temporarily. <a target="_blank" href="https://kubernetes.io/docs/concepts/architecture/nodes/#swap-memory">To make this change p</a>ersistent across reboots, make sure swap is disabled in config files like <code>/etc/fstab</code>, <code>systemd.swap</code>, depending how it was configured on your system.</li>
</ul>
</li>
</ul>
<pre><code class="lang-bash">    sudo swapoff -a
</code></pre>
<h3 id="heading-network-configuration">Network Configuration</h3>
<p>Enable IPv4 packet forwarding:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># sysctl params required by setup, params persist across reboots</span>
cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF

<span class="hljs-comment"># Apply sysctl params without reboot</span>
sudo sysctl --system

<span class="hljs-comment">#verify</span>
sysctl net.ipv4.ip_forward
</code></pre>
<h3 id="heading-configure-cgroup-drivers">Configure cgroup drivers</h3>
<p><strong>NOTE</strong>: <code>Starting with v1.22 and later, when creating a cluster with kubeadm, if the user does not set the cgroupDriver field under KubeletConfiguration, kubeadm defaults it to systemd.</code></p>
<h3 id="heading-install-containerdhttpsgithubcomcontainerdcontainerdblobmaindocsgetting-startedmd"><strong>Install</strong> <a target="_blank" href="https://github.com/containerd/containerd/blob/main/docs/getting-started.md"><strong>Containerd</strong></a></h3>
<pre><code class="lang-bash"><span class="hljs-comment">## Containerd</span>
curl -LO https://github.com/containerd/containerd/releases/download/v1.7.18/containerd-1.7.18-linux-amd64.tar.gz
sudo tar Cxzvf /usr/<span class="hljs-built_in">local</span> containerd-1.7.18-linux-amd64.tar.gz

<span class="hljs-comment">## Systemd</span>
curl -O https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo mv containerd.service /lib/systemd/system/
sudo systemctl daemon-reload
sudo systemctl <span class="hljs-built_in">enable</span> --now containerd
</code></pre>
<h3 id="heading-runc">Runc</h3>
<pre><code class="lang-bash">curl -LO  https://github.com/opencontainers/runc/releases/download/v1.1.12/runc.amd64
sudo install -m 755 runc.amd64 /usr/<span class="hljs-built_in">local</span>/sbin/runc
</code></pre>
<h3 id="heading-install-cni-plugins">Install CNI plugins</h3>
<pre><code class="lang-bash">~&gt; curl -LO https://github.com/containernetworking/plugins/releases/download/v1.5.0/cni-plugins-linux-amd64-v1.5.0.tgz
~&gt; mkdir -p /opt/cni/bin
~&gt; sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.5.0.tgz

~&gt; ls /opt/cni/bin/
LICENSE    bandwidth  dhcp   firewall     host-local  loopback  portmap  sbr     tap     vlan
README.md  bridge     dummy  host-device  ipvlan      macvlan   ptp      static  tuning  vrf
</code></pre>
<h3 id="heading-configure-containerd">Configure containerd</h3>
<pre><code class="lang-bash">sudo mkdir -p /etc/containerd/
containerd config default | sudo tee  /etc/containerd/config.toml
<span class="hljs-comment"># Update the SystemdCgroup = false line to SystemdCgroup = true</span>

<span class="hljs-comment"># You need CRI support enabled to use containerd with Kubernetes. </span>
<span class="hljs-comment">#Make sure that cri is not included in thedisabled_plugins list</span>
<span class="hljs-comment"># within /etc/containerd/config.toml; if you made changes to that </span>
<span class="hljs-comment"># file, also restart containerd.</span>
sudo vim /etc/containerd/config.toml
sudo systemctl restart containerd
</code></pre>
<ul>
<li><a target="_blank" href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#installing-kubeadm-kubelet-and-kubectl"><strong>Installing kubeadm, kubelet and kubectl</strong></a></li>
</ul>
<p>kubeadm will not install or manage <code>kubelet</code> or <code>kubectl</code> for you, so you will need to ensure they match the version of the Kubernetes control plane you want kubeadm to install for you.</p>
<ul>
<li><h4 id="heading-network-setuphttpskubernetesiodocssetupproduction-environmenttoolskubeadmcreate-cluster-kubeadmnetwork-setup"><a target="_blank" href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#network-setup"><strong>Network setup</strong></a></h4>
</li>
</ul>
<pre><code class="lang-bash">ip route show
</code></pre>
<p><strong>Optional</strong>: <strong>Preparing the required container images</strong><br />This step is optional and only applies in case you wish <code>kubeadm init</code> and <code>kubeadm join</code> to not download the default container images which are hosted at <a target="_blank" href="http://registry.k8s.io"><code>registry.k8s.io</code></a>.</p>
<h3 id="heading-initializing-your-control-plane-node"><strong>Initializing your control-plane node</strong></h3>
<ul>
<li><p>(Recommended) If you have plans to upgrade this single control-plane <code>kubeadm</code> cluster to high availability you should specify the <code>--control-plane-endpoint</code> to set the shared endpoint for all control-plane nodes. Such an endpoint can be either a DNS name or an IP address of a load-balancer.</p>
</li>
<li><p>Choose a Pod network add-on, and verify whether it requires any arguments to be passed to <code>kubeadm init</code>. Depending on which third-party provider you choose, you might need to set the <code>--pod-network-cidr</code> to a provider-specific value. See <a target="_blank" href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#pod-network">Installing a Pod network add-on</a>.</p>
</li>
<li><p>(Optional) <code>kubeadm</code> tries to detect the container runtime by using a list of well known endpoints. To use different container runtime or if there are more than one installed on the provisioned node, specify the <code>--cri-socket</code> argument to <code>kubeadm</code>. See <a target="_blank" href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#installing-runtime">Installing a runtime</a>.</p>
</li>
</ul>
<h2 id="heading-run-init">Run init</h2>
<pre><code class="lang-bash">~&gt; sudo kubeadm init --pod-network-cidr 10.33.0.0/16
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p <span class="hljs-variable">$HOME</span>/.kube
  sudo cp -i /etc/kubernetes/admin.conf <span class="hljs-variable">$HOME</span>/.kube/config
  sudo chown $(id -u):$(id -g) <span class="hljs-variable">$HOME</span>/.kube/config

Alternatively, <span class="hljs-keyword">if</span> you are the root user, you can run:

  <span class="hljs-built_in">export</span> KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run <span class="hljs-string">"kubectl apply -f [podnetwork].yaml"</span> with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.0.8.15:6443 --token vajf1q.ahw188f33xklo8um \
        --discovery-token-ca-cert-hash sha256:58e837184a19c648286f21ccac308b2894fc0592ceee7c24f1ce0e616885bc52 

<span class="hljs-comment">### Sanity Check</span>
kubect get nodes
kubect get pods -n kube-system
</code></pre>
<h3 id="heading-deploy-cni-plugins">Deploy CNI Plugins</h3>
<pre><code class="lang-bash"><span class="hljs-comment">#for single node cluster</span>
kubectl taint nodes vagrant node-role.kubernetes.io/control-plane-
curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml -O
kubectl apply -f calico.yaml
</code></pre>
<h3 id="heading-untaint-nodes">Untaint Nodes</h3>
<pre><code class="lang-bash">kubectl taint nodes vagrant node.cilium.io/agent-not-ready-
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Memo: SSM Session and Port Forwarding]]></title><description><![CDATA[Docs

Link1

Link2


Install
Session Manger Plugin
AWS SSM Proxy. Optional
Start basic ssh connection
    aws ssm start-session \
    --target i-askfsa8asld

Port Forwarding
aws ssm start-session \
    --target i-00e68c055d1c0b087 \
    --document-na...]]></description><link>https://blog.balmanrawat.com.np/ssm-session-and-port-forwarding</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/ssm-session-and-port-forwarding</guid><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Sun, 11 Feb 2024 16:29:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/l090uFWoPaI/upload/bfcd61247b8a47e43df81fd28344f9bd.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-docs">Docs</h3>
<ul>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#start-ec2-console">Link1</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/blogs/aws/new-port-forwarding-using-aws-system-manager-sessions-manager">Link2</a></p>
</li>
</ul>
<h3 id="heading-install">Install</h3>
<p><a target="_blank" href="https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html">Session Manger Plugin</a></p>
<p><a target="_blank" href="https://github.com/qoomon/aws-ssm-ec2-proxy-command">AWS SSM Proxy. Optional</a></p>
<h3 id="heading-start-basic-ssh-connection">Start basic ssh connection</h3>
<pre><code class="lang-shell">    aws ssm start-session \
    --target i-askfsa8asld
</code></pre>
<h3 id="heading-port-forwarding">Port Forwarding</h3>
<pre><code class="lang-shell">aws ssm start-session \
    --target i-00e68c055d1c0b087 \
    --document-name AWS-StartPortForwardingSession \
    --parameters '{"portNumber":["remote-host-port"], "localPortNumber":["local-port"]}'
</code></pre>
<h3 id="heading-port-forwarding-to-remote-host">Port Forwarding to Remote host</h3>
<pre><code class="lang-shell">aws ssm start-session \
    --target i-00e68c055d1c0b087 \
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters '{"host":["remote-host-ip"],"portNumber":["remote-host-port"], "localPortNumber":["local-port"]}'
</code></pre>
]]></content:encoded></item><item><title><![CDATA[K8s and Kubectl Notes]]></title><description><![CDATA[Pod
kubectl get pods
kubectl get pods --show-labels
kubectl run nginx --image=nginx
k run nginx --image=nginx --labels app=nginx
kubectl describe pod nginx
kubectl delete pod nginx
kubectl delete pod -lapp=nginx
k delete pod nginx --force --grace-per...]]></description><link>https://blog.balmanrawat.com.np/k8s-and-kubectl-notes</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/k8s-and-kubectl-notes</guid><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Mon, 24 Jul 2023 05:40:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/6IsCeQBIPmo/upload/90bf2a985c23474c923953efc094b0cb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-pod">Pod</h2>
<pre><code class="lang-bash">kubectl get pods
kubectl get pods --show-labels
kubectl run nginx --image=nginx
k run nginx --image=nginx --labels app=nginx
kubectl describe pod nginx
kubectl delete pod nginx
kubectl delete pod -lapp=nginx
k delete pod nginx --force --grace-period=0
k delete pod nginx --now <span class="hljs-comment">#similar to --grace-period=1</span>
kubectl run nginx --image=nginx --dry-run=client -o yaml &gt; nginx-pod.yaml
kubectl edit pod nginx
kubectl get pods -o json
k run static-busybox --image=busybox -- sh -c <span class="hljs-string">"sleep 1000"</span>
k run busybox --image=busybox --restart=Never -- sh -c <span class="hljs-string">"echo sleeping.... &amp;&amp; sleep 5"</span>
kubectl run busybox-pod --image=busybox --restart=Never --<span class="hljs-built_in">command</span> -- /bin/sh -c <span class="hljs-string">"echo 'sleeping...'; sleep 5"</span>
kubectl replace nginx --force -f nginx.yaml

You cannot edit pod spec other than
- spec.containers[*].image
- spec.initContainers[*].image
- spec.activeDeadlineSeconds
- spec.tolerations
</code></pre>
<h2 id="heading-replicaset">Replicaset</h2>
<pre><code class="lang-bash">k get rs nginx
kubectl describe rs nginx
k explain rs nginx
k edit rs nginx
k scale rs nginx --replicas 2
k scale --replicas 2 -f manifest.yaml
k delete rs nginx
k delete rs nginx --force --grace-period=0
</code></pre>
<h3 id="heading-deployments">Deployments</h3>
<pre><code class="lang-bash">k get deploy
k create deploy nginx --image=nginx --replicas=3
<span class="hljs-comment"># With Deployments you can easily edit any field/property of the POD template. Since the pod template is a child of the deployment specification, since it deletes the pod and recreats it</span>
</code></pre>
<h2 id="heading-namespace">Namespace</h2>
<pre><code class="lang-bash">k get ns
k create ns myns
k get pods -n myns
k run nginx --image=nginx -n myns
k get pods -A
k get pods --all-namespaces
</code></pre>
<h2 id="heading-domain-names">Domain Names</h2>
<pre><code class="lang-bash">&lt;service-name&gt;.&lt;namespace&gt;.svc.&lt;cluster-endpoint&gt;
</code></pre>
<h2 id="heading-service">Service</h2>
<pre><code class="lang-bash">k get svc
k get svc --show-labels
k describe svc
k get ep
k run nginx --image=nginx --expose --port 8080
k expose pod nginx-pod --name nginx-service --port 80
k expose deployment webapp --<span class="hljs-built_in">type</span> NodePort --port 30082 --target-port 8080
<span class="hljs-comment">#port range =&gt; (30000 - 32767)</span>

kube-proxy --proxy-mode iptables/ipvs/userspace <span class="hljs-comment">#default is iptables</span>
kube-api-server --service-cluster-ip-range <span class="hljs-comment">## is the option to specify the ip range of the service</span>
iptables -L -t nat | grep &lt;service-name&gt; to list the iptables rules
tail -f /var/<span class="hljs-built_in">log</span>/kube-proxy.log
</code></pre>
<h2 id="heading-dns">DNS</h2>
<ul>
<li><p>before k8s <strong>1.12</strong> DNS was called <strong>kube-dns</strong> and now the recommended is <strong>core-dns</strong></p>
</li>
<li><p>configuration file for <strong>core-dns</strong> can be found in <strong>/etc/coredns/corefile</strong></p>
</li>
<li><p>Service FQDN: &lt;service-name&gt;.&lt;namespace&gt;.svc.&lt;cluster-endpoint&gt;</p>
<ul>
<li>eg: web-service.default.svc.cluster.local</li>
</ul>
</li>
<li><p>By default pod DNS is disabled.</p>
</li>
<li><p>FQDN: &lt;pod-ip&gt;.&lt;namespace&gt;.pod.&lt;cluster-endpoint&gt;</p>
<ul>
<li>eg: 10-122-5-1.default.pod.cluster.local</li>
</ul>
</li>
<li><p><strong>core-dns</strong> is accessible using the service with name <strong>kube-dns</strong></p>
</li>
<li><p>pod's DNS configurations are automatically set by the Kubernetes using <strong>kubelet</strong> process. <strong>kubelet</strong> gets the cluster domain and the DNS IP using its config.</p>
</li>
</ul>
<p>Sample Core File</p>
<pre><code class="lang-json">Corefile: |
    .:<span class="hljs-number">53</span> {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :<span class="hljs-number">9153</span>
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache <span class="hljs-number">30</span>
        loop
        reload
        loadbalance
    }
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment">## core file</span>
kubectl get cm coredns -n kube-system -o yaml | less

<span class="hljs-comment">##</span>
</code></pre>
<h2 id="heading-nodes">Nodes</h2>
<pre><code class="lang-bash">k get nodes
k get nodes -o jsonpath=<span class="hljs-string">'{.items[*].status.nodeInfo.osImage}'</span>
k taint node node01 color=blue:NoExecute
k taint node node01 color=blue:NoSchedule
k taint node node01 color=blue:PreferNoSchedule
k taint node controlplane node-role.kubernetes.io/control-plane:NoSchedule-
k label node node01 color=blue
kubectl label node node01 color-
</code></pre>
<h2 id="heading-ip-commands">Ip Commands</h2>
<pre><code class="lang-bash"><span class="hljs-comment">#see the list of interfaces</span>
ip link

<span class="hljs-comment">#see ip address</span>
ip addr
ip addr show

<span class="hljs-comment">## add ip address</span>
ip addr add 192.168.1.5/24 dev eth0

<span class="hljs-comment"># list the routes</span>
ip route
<span class="hljs-comment">#ip route add &lt;network-addr&gt; via &lt;gateway-ip&gt;</span>
ip route add 192.168.1.0/24 via 192.168.2.1
<span class="hljs-comment">#if gateway is directly linked with interface then you can </span>
ip route add 192.168.1.0/24 dev eth0

<span class="hljs-comment">#verify if the forward is enabled or not. if 0 disabled/if 1 enabled</span>
cat /proc/sys/net/ipv4/ip_forward
<span class="hljs-comment">#Modify /etc/systcl.conf to forward the request from one interface to others</span>

<span class="hljs-comment">## modify /etc/nsswitch.conf to update the priority of the dns server</span>
cat /etc/nsswitch.conf

<span class="hljs-comment">#you can add extra nameservers in resolv.conf by adding</span>
nameserver 8.8.8.8
<span class="hljs-comment">#you can append domain alias as well by using</span>
search mycompany.com, prod.mycompany.com
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Setting up elasticsearch with Operator Pattern]]></title><description><![CDATA[Overview
This guide provides the information for deploying/managing highly available Elastic search setup in EKS using Elastic Cloud on Kubernetes Operator. In this, we will see how we can deploy the Elasticsearch version 8.8.1 using the ECK Operator...]]></description><link>https://blog.balmanrawat.com.np/setting-up-elasticsearch-with-operator-pattern</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/setting-up-elasticsearch-with-operator-pattern</guid><category><![CDATA[EKS]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[high availability]]></category><category><![CDATA[statefulsets]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Mon, 19 Jun 2023 07:30:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/eQ2Z9ay9Wws/upload/f49e9899a294531b60d851783269f476.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p>This guide provides the information for deploying/managing highly available Elastic search setup in EKS using <code>Elastic Cloud on Kubernetes Operator</code>. In this, we will see how we can deploy the Elasticsearch version <code>8.8.1</code> using the ECK Operator.</p>
<p>The major source of information for this guide is the <a target="_blank" href="https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-overview.html">official ECK docs</a>.</p>
<h4 id="heading-why-eck-operator">Why ECK Operator?</h4>
<p>We will be <code>deploying/managing</code> Elasticsearch using the Elastic Cloud on Kubernetes Operator which is the latest recommended approach and managed by the Elastic team, the community itself. Please find more <a target="_blank" href="https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-overview.html">details here</a>.</p>
<h4 id="heading-architecture">Architecture</h4>
<p>The current setup will create an elastic search with 3 master nodes and 2 data nodes and make them accessible via LoadBalancer in AWS EKS. It tries to distribute the elastic search nodes evenly among the existing k8s worker nodes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687159446207/507c8be3-839f-48c6-b29b-11a5c4f27f40.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-pre-requisite">Pre-requisite</h2>
<h3 id="heading-eks-cluster">EKS Cluster ☸️</h3>
<p>The setup assumes that EKS cluster is up and running and has enough resources to provision the ES Cluster. The current setup creates <code>3 master nodes</code> and <code>2 data nodes</code> which require approx <code>2GB</code> memory and <code>1 vCPU</code> per ElasticSearch node.</p>
<h3 id="heading-install-eck-crds">Install ECK CRDs ☸️</h3>
<p>Install custom resources for provisioning the <code>kind: ElasticSearch</code> resource. It creates other Custom Resources supported by ECK as well but can be ignored. Command</p>
<pre><code class="lang-shell">kubectl create -f https://download.elastic.co/downloads/eck/2.8.0/crds.yaml
</code></pre>
<p>Output</p>
<pre><code class="lang-shell">customresourcedefinition.apiextensions.k8s.io/agents.agent.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/apmservers.apm.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/beats.beat.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/elasticmapsservers.maps.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/elasticsearchautoscalers.autoscaling.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/elasticsearches.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/enterprisesearches.enterprisesearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/kibanas.kibana.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/logstashes.logstash.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/stackconfigpolicies.stackconfigpolicy.k8s.elastic.co created
</code></pre>
<h3 id="heading-install-operator-with-its-rbac-rules">Install Operator with its RBAC rules ☸️</h3>
<p>The below command installs operator and required roles to manage various operations. By default, it creates an <code>elastic-system</code> namespace and deploys resources under it. Command</p>
<pre><code class="lang-shell">kubectl apply -f https://download.elastic.co/downloads/eck/2.8.0/operator.yaml
</code></pre>
<p>Output</p>
<pre><code class="lang-shell">namespace/elastic-system created
serviceaccount/elastic-operator created
secret/elastic-webhook-server-cert created
configmap/elastic-operator created
clusterrole.rbac.authorization.k8s.io/elastic-operator created
clusterrole.rbac.authorization.k8s.io/elastic-operator-view created
clusterrole.rbac.authorization.k8s.io/elastic-operator-edit created
clusterrolebinding.rbac.authorization.k8s.io/elastic-operator created
service/elastic-webhook-server created
statefulset.apps/elastic-operator created
validatingwebhookconfiguration.admissionregistration.k8s.io/elastic-webhook.k8s.elastic.co created
</code></pre>
<h4 id="heading-verify">Verify ☑️</h4>
<p>Verify if the operator is up and running by looking at the logs. Command</p>
<pre><code class="lang-shell">kubectl -n elastic-system logs -f statefulset.apps/elastic-operator
</code></pre>
<p>Output</p>
<pre><code class="lang-shell">{"log.level":"info","@timestamp":"2023-06-19T04:38:33.355Z","log.logger":"manager.eck-operator","message":"Starting EventSource","service.version":"2.8.0+3940cf4d","service.type":"eck","ecs.version":"1.4.0","controller":"beat-controller","source":"kind source: *v1.Secret"}
{"log.level":"info","@timestamp":"2023-06-19T04:38:33.355Z","log.logger":"manager.eck-operator","message":"Starting EventSource","service.version":"2.8.0+3940cf4d","service.type":"eck","ecs.version":"1.4.0","controller":"beat-controller","source":"kind source: *v1.Secret"}
{"log.level":"info","@timestamp":"2023-06-19T04:38:33.355Z","log.logger":"manager.eck-operator","message":"Starting Controller","service.version":"2.8.0+3940cf4d","service.type":"eck","ecs.version":"1.4.0","controller":"beat-controller"}
...
</code></pre>
<h3 id="heading-storage-class-optional">Storage Class (Optional) ☸️</h3>
<p>This is an optional step as the <code>elasticsearch.yaml</code> manifest is configured to use the default <code>gp2</code> <code>storage class</code>. If you want to override this behavior you can create your custom storage class and update its name in the <code>elasticsearch.yaml</code> file.</p>
<p><strong>NOTE:</strong> Make sure the <code>EBS CSI Plugin</code> is active and the EKS Nodes have permission to manage volumes on behalf of the provisioner. Please include the following policy if missing.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>: [
    {
      <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Action"</span>: [
        <span class="hljs-string">"ec2:AttachVolume"</span>,
        <span class="hljs-string">"ec2:CreateSnapshot"</span>,
        <span class="hljs-string">"ec2:CreateTags"</span>,
        <span class="hljs-string">"ec2:CreateVolume"</span>,
        <span class="hljs-string">"ec2:DeleteSnapshot"</span>,
        <span class="hljs-string">"ec2:DeleteTags"</span>,
        <span class="hljs-string">"ec2:DeleteVolume"</span>,
        <span class="hljs-string">"ec2:DescribeAvailabilityZones"</span>,
        <span class="hljs-string">"ec2:DescribeInstances"</span>,
        <span class="hljs-string">"ec2:DescribeSnapshots"</span>,
        <span class="hljs-string">"ec2:DescribeTags"</span>,
        <span class="hljs-string">"ec2:DescribeVolumes"</span>,
        <span class="hljs-string">"ec2:DescribeVolumesModifications"</span>,
        <span class="hljs-string">"ec2:DetachVolume"</span>,
        <span class="hljs-string">"ec2:ModifyVolume"</span>
      ],
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>
    }
  ]
}
</code></pre>
<h4 id="heading-verify-1">Verify ☑️</h4>
<p>Verify if the EBS CSI controller is actively running. Command</p>
<pre><code class="lang-shell">kubectl get pods -n kube-system -lapp=ebs-csi-controller
``
Output
```shell
NAME                                 READY   STATUS    RESTARTS   AGE
ebs-csi-controller-6876d9b86-d88kq   6/6     Running   0          5m49s
ebs-csi-controller-6876d9b86-t47wn   6/6     Running   0          5m49s
</code></pre>
<h2 id="heading-deploy-elasticsearch">Deploy Elasticsearch ☸️</h2>
<p>Now as our pre-requisite is met we can deploy the elastic search in the cluster using <code>elasticsearch.yaml</code> template</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">elasticsearch.k8s.elastic.co/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Elasticsearch</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">esearch</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">8.8</span><span class="hljs-number">.1</span>
  <span class="hljs-attr">http:</span>
    <span class="hljs-attr">service:</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">LoadBalancer</span>
    <span class="hljs-attr">tls:</span>
      <span class="hljs-attr">selfSignedCertificate:</span>
        <span class="hljs-attr">subjectAltNames:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">dns:</span> <span class="hljs-string">localhost</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">dns:</span> <span class="hljs-string">es.esearch.app</span>
  <span class="hljs-comment">#link: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-update-strategy.html</span>
  <span class="hljs-attr">updateStrategy:</span>
    <span class="hljs-attr">changeBudget:</span>
      <span class="hljs-attr">maxSurge:</span> <span class="hljs-number">1</span>
      <span class="hljs-attr">maxUnavailable:</span> <span class="hljs-number">1</span>
  <span class="hljs-comment">#link: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-pod-disruption-budget.html</span>
  <span class="hljs-attr">podDisruptionBudget:</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">minAvailable:</span> <span class="hljs-number">4</span>
      <span class="hljs-attr">selector:</span>
        <span class="hljs-attr">matchLabels:</span>
          <span class="hljs-attr">elasticsearch.k8s.elastic.co/cluster-name:</span> <span class="hljs-string">esearch</span>
  <span class="hljs-comment">#Behind the scenes, ECK translates each NodeSet specified in the Elasticsearch resource into a StatefulSet in Kubernetes</span>
  <span class="hljs-comment">#Upgrade Patterns:  #Behind the scenes, ECK translates each NodeSet specified in the Elasticsearch resource into a StatefulSet in Kubernetes</span>
  <span class="hljs-attr">nodeSets:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">masters</span>
    <span class="hljs-attr">count:</span> <span class="hljs-number">3</span>
    <span class="hljs-attr">config:</span>
      <span class="hljs-attr">node.roles:</span> [<span class="hljs-string">"master"</span>]
      <span class="hljs-comment"># node.store.allow_mmap: false</span>
      <span class="hljs-attr">xpack.ml.enabled:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">volumeClaimTemplates:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">metadata:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">elasticsearch-data</span> <span class="hljs-comment"># Do not change this name unless you set up a volume mount for the data path.</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">accessModes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ReadWriteOnce</span>
        <span class="hljs-attr">resources:</span>
          <span class="hljs-attr">requests:</span>
            <span class="hljs-attr">storage:</span> <span class="hljs-string">5Gi</span>
        <span class="hljs-attr">storageClassName:</span> <span class="hljs-string">gp2</span>
    <span class="hljs-attr">podTemplate:</span>
      <span class="hljs-attr">metadata:</span>
        <span class="hljs-attr">labels:</span>
          <span class="hljs-attr">app:</span> <span class="hljs-string">elasticsearch</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">150</span>
      <span class="hljs-comment">#link: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-advanced-node-scheduling.html#k8s-affinity-options</span>
        <span class="hljs-attr">affinity:</span>
          <span class="hljs-attr">podAntiAffinity:</span>
            <span class="hljs-attr">preferredDuringSchedulingIgnoredDuringExecution:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">weight:</span> <span class="hljs-number">100</span>
              <span class="hljs-attr">podAffinityTerm:</span>
                <span class="hljs-attr">labelSelector:</span>
                  <span class="hljs-attr">matchLabels:</span>
                    <span class="hljs-attr">elasticsearch.k8s.elastic.co/cluster-name:</span> <span class="hljs-string">esearch</span>
                <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">kubernetes.io/hostname</span>
        <span class="hljs-comment">#https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-virtual-memory.html#k8s_using_an_init_container_to_set_virtual_memory</span>
        <span class="hljs-attr">initContainers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">sysctl</span>
          <span class="hljs-attr">securityContext:</span>
            <span class="hljs-attr">privileged:</span> <span class="hljs-literal">true</span>
            <span class="hljs-attr">runAsUser:</span> <span class="hljs-number">0</span>
          <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'sysctl -w vm.max_map_count=262144'</span>]
        <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elasticsearch</span>
          <span class="hljs-attr">resources:</span>
            <span class="hljs-attr">limits:</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">3Gi</span>
            <span class="hljs-attr">requests:</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">2Gi</span>
          <span class="hljs-attr">env:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">ES_JAVA_OPTS</span>
            <span class="hljs-attr">value:</span> <span class="hljs-string">"-Xms1g -Xmx1g"</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">data</span>
    <span class="hljs-attr">count:</span> <span class="hljs-number">2</span>
    <span class="hljs-attr">config:</span>
      <span class="hljs-attr">node.roles:</span> [<span class="hljs-string">"data"</span>]
      <span class="hljs-comment"># node.store.allow_mmap: false</span>
      <span class="hljs-attr">xpack.ml.enabled:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">volumeClaimTemplates:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">metadata:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">elasticsearch-data</span> <span class="hljs-comment"># Do not change this name unless you set up a volume mount for the data path.</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">accessModes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ReadWriteOnce</span>
        <span class="hljs-attr">resources:</span>
          <span class="hljs-attr">requests:</span>
            <span class="hljs-attr">storage:</span> <span class="hljs-string">10Gi</span>
        <span class="hljs-attr">storageClassName:</span> <span class="hljs-string">gp2</span>
    <span class="hljs-attr">podTemplate:</span>
      <span class="hljs-attr">metadata:</span>
        <span class="hljs-attr">labels:</span>
          <span class="hljs-attr">app:</span> <span class="hljs-string">elasticsearch</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">150</span>
        <span class="hljs-attr">affinity:</span>
          <span class="hljs-attr">podAntiAffinity:</span>
            <span class="hljs-attr">preferredDuringSchedulingIgnoredDuringExecution:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">weight:</span> <span class="hljs-number">100</span>
              <span class="hljs-attr">podAffinityTerm:</span>
                <span class="hljs-attr">labelSelector:</span>
                  <span class="hljs-attr">matchLabels:</span>
                    <span class="hljs-attr">elasticsearch.k8s.elastic.co/cluster-name:</span> <span class="hljs-string">esearch</span>
                <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">kubernetes.io/hostname</span>
        <span class="hljs-attr">initContainers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">sysctl</span>
          <span class="hljs-attr">securityContext:</span>
            <span class="hljs-attr">privileged:</span> <span class="hljs-literal">true</span>
            <span class="hljs-attr">runAsUser:</span> <span class="hljs-number">0</span>
          <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'sysctl -w vm.max_map_count=262144'</span>]
        <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elasticsearch</span>
          <span class="hljs-attr">resources:</span>
            <span class="hljs-attr">limits:</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">3Gi</span>
            <span class="hljs-attr">requests:</span>
              <span class="hljs-attr">memory:</span> <span class="hljs-string">2Gi</span>
          <span class="hljs-attr">env:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">ES_JAVA_OPTS</span>
            <span class="hljs-attr">value:</span> <span class="hljs-string">"-Xms1g -Xmx1g"</span>
</code></pre>
<h3 id="heading-apply">Apply ⤵️</h3>
<pre><code class="lang-shell">k apply -f elasticsearch.yaml
</code></pre>
<h3 id="heading-verify-2">Verify ☑️</h3>
<p>Verify by listing the Elasticsearch resources</p>
<p><strong>Initial Status</strong></p>
<pre><code class="lang-shell">#When you create the cluster, there is unknown HEALTH status and the PHASE is ApplyingChanges. After a while, the PHASE turns into Ready, and HEALTH becomes green.
➜ kubectl get es  
NAME     HEALTH    NODES   VERSION   PHASE             AGE
esearch   unknown           8.8.1     ApplyingChanges   22s

➜ kubectl get pods
NAME                  READY   STATUS     RESTARTS   AGE
esearch-es-data-0      0/1     Init:0/3   0          23s
esearch-es-data-1      0/1     Init:0/3   0          23s
esearch-es-masters-0   0/1     Init:0/3   0          23s
esearch-es-masters-1   0/1     Init:0/3   0          23s
esearch-es-masters-2   0/1     Init:0/3   0          23s

➜ kubectl get sts 
NAME                READY   AGE
esearch-es-data      0/2     28s
esearch-es-masters   0/3     29s
➜  elastic-search git:(main) ✗
</code></pre>
<p><strong>Eventual Status</strong></p>
<pre><code class="lang-shell">➜ kubectl get es
NAME     HEALTH   NODES   VERSION   PHASE   AGE
esearch   green    5       8.8.1     Ready   3m5s

➜ kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
esearch-es-data-0      1/1     Running   0          3m10s
esearch-es-data-1      1/1     Running   0          3m10s
esearch-es-masters-0   1/1     Running   0          3m10s
esearch-es-masters-1   1/1     Running   0          3m10s
esearch-es-masters-2   1/1     Running   0          3m10s

➜ kubectl get sts
NAME                READY   AGE
esearch-es-data      2/2     3m16s
esearch-es-masters   3/3     3m17s
</code></pre>
<h3 id="heading-accessing">Accessing 🌍</h3>
<p>To access the cluster properly you will need <code>es endpoint</code>, <code>es password</code> and the <code>ca certificate</code>. Please use the below commands to get the information:</p>
<pre><code class="lang-shell">#Set Elasticsearch name
export ES_NAME=esearch

#Get Service Public endpoint
ENDPOINT=$(kubectl get svc ${ES_NAME}-es-http -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

#Get ElasticSearch User Password
PASSWORD=$(kubectl get secret ${ES_NAME}-es-elastic-user -o go-template='{{.data.elastic | base64decode}}')

#Get ES CA Certificate
kubectl get secret ${ES_NAME}-es-http-certs-public -o go-template='{{index .data "tls.crt" | base64decode }}' &gt; ca.cert
</code></pre>
<h3 id="heading-verify-3">Verify ☑️</h3>
<p>Once we have the endpoint, certificates and password we can not connect with Elasticsearch cluster using <code>https</code>. Please use the below verification command to see if everything is set properly. Command</p>
<pre><code class="lang-shell">curl -X GET --cacert ca.cert -u "elastic:${PASSWORD}" "https://${ENDPOINT}:9200"
</code></pre>
<p>Output</p>
<pre><code class="lang-shell">{
  "name" : "esearch-es-masters-2",
  "cluster_name" : "esearch",
  "cluster_uuid" : "jygZP_KOS_6ELM5WjSe9cQ",
  "version" : {
    "number" : "8.8.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "f8edfccba429b6477927a7c1ce1bc6729521305e",
    "build_date" : "2023-06-05T21:32:25.188464208Z",
    "build_snapshot" : false,
    "lucene_version" : "9.6.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}
</code></pre>
<h2 id="heading-cleanup">Cleanup ❌</h2>
<p>If we need to revert all the changes we can do it in the following order.</p>
<pre><code class="lang-shell">kubectl delete -f elasticsearch.yaml
kubectl delete -f https://download.elastic.co/downloads/eck/2.8.0/operator.yaml
kubectl delete -f https://download.elastic.co/downloads/eck/2.8.0/crds.yaml
</code></pre>
<p>Hurray 🎉 This concludes the setup of the elastic search using the ECK operator. For any further details and customization please follow the details section below.</p>
<h2 id="heading-details-on-elasticsearch-manifest">Details on elasticsearch manifest</h2>
<p>There are multiple choices and configuration settings that we made when defining the <code>elasticsearch.yaml</code> manifest. Please find details about them in the section below:</p>
<h4 id="heading-1-updateupgrade-resiliency">1. Update/Upgrade Resiliency</h4>
<p>While running any kind of updates/upgrades we want to make sure we are not under the capacity or overcapacity. <code>maxsurge</code> specifies a maximum addition number of nodes than our existing pool of es nodes, this will have us prevent excessive utilization of resources during the upgrades. <code>maxUnavailable</code> specifies a minimum number of nodes that can be available at a time.</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">updateStrategy:</span>
    <span class="hljs-attr">changeBudget:</span>
      <span class="hljs-attr">maxSurge:</span> <span class="hljs-number">1</span> <span class="hljs-comment">#creates only one new node at a time.</span>
      <span class="hljs-attr">maxUnavailable:</span> <span class="hljs-number">1</span> <span class="hljs-comment">#using 1 because we only have 2 data nodes. Can be increased if we have more data/master nodes. Low value increases stability but increases deployment time i.e we need to find the right balance.</span>
<span class="hljs-string">...</span>
</code></pre>
<h4 id="heading-2-pod-disruption-budget">2. Pod Disruption Budget</h4>
<p>During the upgrades, maintenance and failure of any nodes we still want to maintain the pool of the nodes such that the es cluster continues to serve the traffic. We are using <code>podDistributionBudget</code> where <code>minAvailable</code> is set to <code>4</code> and the selector matches the es nodes.</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">podDisruptionBudget:</span>
  <span class="hljs-attr">spec:</span>
    <span class="hljs-attr">minAvailable:</span> <span class="hljs-number">4</span> <span class="hljs-comment">#using 4 as we have 3 master nodes and 2 data nodes.</span>
    <span class="hljs-attr">selector:</span>
      <span class="hljs-attr">matchLabels:</span>
        <span class="hljs-attr">elasticsearch.k8s.elastic.co/cluster-name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{.metadata.name}}</span>"</span>
<span class="hljs-string">...</span>
</code></pre>
<h4 id="heading-3-uniform-es-nodes-distribution">3. Uniform ES nodes distribution</h4>
<p>The config tries to ensure the es nodes are distributed uniformly across the worker nodes for better availability and resiliency using <code>topologyKey:</code> <a target="_blank" href="http://kubernetes.io/hostname"><code>kubernetes.io/hostname</code></a>.</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">affinity:</span>
  <span class="hljs-attr">podAntiAffinity:</span>
    <span class="hljs-attr">preferredDuringSchedulingIgnoredDuringExecution:</span> <span class="hljs-comment">#using preferred instead of required as there always won't be unique k8s node per es node and using required might make es nodes unschedulable</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">weight:</span> <span class="hljs-number">100</span> <span class="hljs-comment">#give high priority for this rule</span>
      <span class="hljs-attr">podAffinityTerm:</span>
        <span class="hljs-attr">labelSelector:</span>
          <span class="hljs-attr">matchLabels:</span>
            <span class="hljs-attr">elasticsearch.k8s.elastic.co/cluster-name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{.metadata.name}}</span>"</span>
        <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">kubernetes.io/hostname</span> <span class="hljs-comment">#this ensures distribution of es nodes based on the hostname</span>
<span class="hljs-string">...</span>
</code></pre>
<h4 id="heading-4-es-node-rolescounts">4. ES Node Roles/Counts</h4>
<p>For this scenario the configuration specifies <code>3 master node</code>(for minimal H/A) and <code>2 data nodes</code>(for minimal H/A). For the sake of simplicity other roles are ignored.</p>
<h4 id="heading-5-memory-mapping-configuration">5. Memory mapping configuration</h4>
<p>Default values for virtual address space on Linux distributions are too low for Elasticsearch to work properly, which may result in out-of-memory exceptions. For production workloads, it is strongly recommended to increase the kernel setting vm.max_map_count to <code>262144</code>.</p>
<h4 id="heading-6-storage-class">6. Storage Class</h4>
<p>The storage class used in the <code>volumeClaimTemplates</code> template is <code>storageClassName: gp2</code> default storage class created when EKS cluster is created which can be changed if you have different requirements for different roles.</p>
<h4 id="heading-7-jvm-flags">7. JVM flags</h4>
<p>JVM flags can be overridden using the environment variables and this can largely impact the performance of the es cluster overall. It should be different for different roles for now the basic configuration is JVM start memory: <code>1GB</code> and JVM max: <code>1GB</code>.</p>
<pre><code class="lang-shell">...
env:
  - name: ES_JAVA_OPTS
    value: "-Xms1g -Xmx1g"
...
</code></pre>
<h4 id="heading-8-termination-period">8. Termination Period</h4>
<p>The termination grace period for pod is used to give enough time to gracefully exit the es node during any kinds of upgrades and maintenance tasks. Current scenario specific <code>150</code> which should be enough but can highly differ based on the node type and size of traffic/data.</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">300</span>
<span class="hljs-string">...</span>
</code></pre>
<h2 id="heading-further-configuration-and-details">Further configuration and details 📖</h2>
<p>Many more settings can be configured to tune the cluster setup to our needs. Please find more details in this <a target="_blank" href="https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-api-elasticsearch-k8s-elastic-co-v1.html">API reference</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Memo: Amazon EC2 Auto Scaling]]></title><description><![CDATA[Amazon EC2 Auto Scaling helps you ensure that you have the correct number of Amazon EC2 instances available to handle the load for your application.
https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html

Autoscali...]]></description><link>https://blog.balmanrawat.com.np/memo-amazon-ec2-auto-scaling</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/memo-amazon-ec2-auto-scaling</guid><category><![CDATA[ec2]]></category><category><![CDATA[AWS]]></category><category><![CDATA[auto-scaling-aws]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[scaling]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Tue, 06 Jun 2023 12:36:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xG8IQMqMITM/upload/5fa808d34e3cd91c26ffe6f95b132026.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Amazon EC2 Auto Scaling helps you ensure that you have the correct number of Amazon EC2 instances available to handle the load for your application.</p>
<p><a target="_blank" href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html">https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686013043834/c7acff30-84ae-4fec-bf4a-ae813c47dd35.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-autoscaling-beyond-ec2-autoscaling">Autoscaling Beyond EC2 Autoscaling</h2>
<p><a target="_blank" href="https://docs.aws.amazon.com/autoscaling/application/userguide/what-is-application-auto-scaling.html">https://docs.aws.amazon.com/autoscaling/application/userguide/what-is-application-auto-scaling.html</a></p>
<h2 id="heading-components-of-autoscaling">Components of Autoscaling</h2>
<h3 id="heading-scaling-options">Scaling Options</h3>
<h3 id="heading-autoscaling-group">Autoscaling Group</h3>
<h3 id="heading-configuration-options">Configuration Options</h3>
<h2 id="heading-benefits">Benefits</h2>
<ul>
<li><p>Adjust the workload with the varied demand</p>
</li>
<li><p>Distribute Instances across AZ for HA</p>
<ul>
<li><p>instance distribution</p>
</li>
<li><p>AZ rebalance</p>
</li>
<li><p>Capacity rebalancing for Spot instance type</p>
</li>
</ul>
</li>
<li><p>Optimum utilization of the resources</p>
</li>
</ul>
<h2 id="heading-lifecycle">Lifecycle</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686014083193/ba8a7bca-ad2f-4b91-b1ba-ccb6478be350.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-scale-out">Scale Out</h3>
<ul>
<li><p>ASG launches the required number of EC2 instances, using its assigned launch template. instances start in the Pending state.</p>
</li>
<li><p>When each instance is fully configured and passes the <code>Amazon EC2 health checks</code>, it is attached to the Auto Scaling group and enters the <code>InService state</code>.</p>
</li>
<li><p>If your ASG is configured with ELB, it will automatically register your instance with the load balancer before it marks the instance as InService.</p>
</li>
<li><p>The instance is counted against the desired capacity after it gets to <code>InService</code> stage.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686014423643/326cda35-1a44-4cab-9d5c-c4433abc602a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-scale-in">Scale In</h3>
<ul>
<li><p>The ASG uses its <strong>termination policy</strong> to determine which instances to terminate.</p>
</li>
<li><p>Instances that are in the process of terminating from the ASG and shutting down enter the Terminating state, and <strong>can't be put back into service</strong>.</p>
</li>
<li><p>If you add a lifecycle hook to your Auto Scaling group, you can perform a custom action here.</p>
</li>
<li><p>If your ASG is configured to receive traffic from an ELB, ASG automatically deregisters the terminating instance from the load balancer <strong>before running lifecycle hooks.</strong></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686015276972/50838897-20c6-4deb-a44c-0d1070d139c1.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-attach-instance">Attach Instance</h3>
<p>You can attach existing EC2 Instance which meets certain criteria to be managed by the ASG.</p>
<p>For an instance to be attached, it must meet the following criteria:</p>
<ul>
<li><p>The instance is in the <code>running</code> state with Amazon EC2.</p>
</li>
<li><p>The AMI used to launch the instance must still exist.</p>
</li>
<li><p>The instance is not a member of another Auto Scaling group.</p>
</li>
<li><p>The instance is launched into one of the Availability Zones defined in your Auto Scaling group.</p>
</li>
<li><p>If the Auto Scaling group has an attached load balancer target group or Classic Load Balancer, the instance and the load balancer must both be in the same VPC.</p>
</li>
</ul>
<p>When you attach instances, consider the following:</p>
<ul>
<li><p>The desired capacity of the group increases by the number of instances being attached. If the number of instances being attached plus the desired capacity <mark>exceeds the maximum size of the group, the request fails</mark>.</p>
</li>
<li><p>If you attach an instance to an Auto Scaling group that has an attached load balancer target group or Classic Load Balancer, <mark>the instance is registered with the load balancer.</mark></p>
</li>
</ul>
<h3 id="heading-detach-instance">Detach Instance</h3>
<p>You can detach EC2 Instance from ASG and the instance will no longer be managed by the ASG.</p>
<p>When you detach instances, consider the following:</p>
<ul>
<li><p>The desired capacity of the group decreases by the number of instances being detached. If the number of instances being detached makes the desired count <mark>lower than the minimum size of the group, the request fails</mark>. To skip this behavior you can select <code>replace</code> flag to maintain the same desired capacity where ASG adds another EC2 Instance in replacement.</p>
</li>
<li><p>If detach multiple instances from the same Availability Zone, Amazon EC2 Auto Scaling can rebalance the Availability Zones unless you suspend the <code>AZRebalance</code> process. For more information, see <a target="_blank" href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-suspend-resume-processes.html">Suspend and resume a process for an Auto Scaling group</a>.</p>
</li>
<li><p>If you detach an instance from an Auto Scaling group that has an attached load balancer target group or Classic Load Balancer, the instance is deregistered from the load balancer. If connection draining (deregistration delay) is enabled for your load balancer, Amazon EC2 Auto Scaling waits for in-flight requests to complete.</p>
</li>
</ul>
<h3 id="heading-enter-and-exit-standby">Enter and Exit Standby</h3>
<p>You can change the state of the EC2 Instance from temporary to standby mode to debug and put it back to the <code>Inservice</code> stage. ASG still manages the Instance even in the <code>StandBy</code> stage except the fact is that it doesn't receive live traffic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686053937969/e1b98c8e-18ea-4d53-bb60-d7f8cda3fcb6.png" alt class="image--center mx-auto" /></p>
<p>Things to know when dealing with <strong>Standby:</strong></p>
<ul>
<li><p>You put the instance into the standby state. The instance remains in this state until you exit the standby state.</p>
</li>
<li><p>By default, the value that you specified as your desired capacity is decremented when you put an instance on standby. This prevents the launch of an additional instance while you have this instance on standby. Alternatively, you can specify that your desired capacity is not decremented.</p>
</li>
<li><p>After you put an instance back in service, the desired capacity is incremented. If you did not decrement the capacity when you put the instance on standby, the Auto Scaling group detects that you have more instances than you need. It applies the termination policy in effect to reduce the size of the group.</p>
</li>
<li><p>Amazon EC2 Auto Scaling does not perform health checks on instances that are in a standby state. For example, if you put a healthy instance on standby and then terminate it, Amazon EC2 Auto Scaling continues to report the instance as healthy.</p>
</li>
</ul>
<h3 id="heading-lifecycle-hooks">Lifecycle hooks</h3>
<ul>
<li><p>If you added an <code>autoscaling:EC2_INSTANCE_LAUNCHING</code> lifecycle hook to your Auto Scaling group, the instances move from the Pending state to the Pending:Wait state.</p>
</li>
<li><p>After completion, the instances enter the <code>Pending:Proceed</code> state.</p>
</li>
<li><p>When the instances are fully configured, they are attached to the Auto Scaling group and they enter the InService state.</p>
</li>
<li><p>If you added an <code>autoscaling:EC2_INSTANCE_TERMINATING</code> lifecycle hook to your Auto Scaling group, the instances move from the <code>Terminating</code> state to the <code>Terminating:Wait</code> state.</p>
</li>
<li><p>After completion, the instances enter the <code>Terminating:Proceed</code> state.</p>
</li>
</ul>
<h2 id="heading-launch-templates">Launch Templates</h2>
<p>Not all Amazon EC2 Auto Scaling features are available when you use launch configurations. For example, you cannot create an Auto Scaling group that launches <strong>both Spot and On-Demand Instances</strong> or that specifies <strong>multiple instance types</strong>. You must use a launch template to configure these features.</p>
<p>With launch templates, you can also use newer features of Amazon EC2. This includes</p>
<ul>
<li><p>Systems Manager parameters (AMI ID),</p>
</li>
<li><p>the current generation of EBS Provisioned IOPS volumes (io2),</p>
</li>
<li><p>EBS volume tagging,</p>
</li>
<li><p>T2 Unlimited instances,</p>
</li>
<li><p>Elastic Inference, and</p>
</li>
<li><p>Dedicated Hosts</p>
</li>
</ul>
<p>When you create a launch template, all parameters are optional. However, if a launch template does not specify an AMI, you cannot add the AMI when you create your Auto Scaling group. If you specify an AMI but no instance type, you can add one or more instance types when you create your Auto Scaling group.</p>
<h2 id="heading-replacing-instances">Replacing Instances</h2>
<h3 id="heading-instance-refresh">Instance refresh</h3>
<p>An instance refresh can be helpful when you have a new Amazon Machine Image (AMI) or a new user data script. To use an instance refresh, first, create a new launch template that specifies the new AMI or user data script. Then, start an instance refresh to begin updating the instances in the group immediately.</p>
<p><strong>NOTE:</strong> Can cause downtime if there is only 1 EC2 Instance in the ASG, because it terminates the EC2 Instance first and then only adds after that</p>
<h3 id="heading-replace-based-on-maximum-instance-lifetime">Replace based on maximum instance lifetime</h3>
<p>The maximum instance lifetime specifies the maximum amount of time (in seconds) that an instance can be in service before it is terminated and replaced. A common use case might be a requirement to replace your instances on a schedule because of internal security policies or external compliance controls.</p>
<p><strong>NOTE:</strong> Can cause downtime if there is only 1 EC2 Instance in the ASG, because it terminates the EC2 Instance first and then only adds after that</p>
<h2 id="heading-scaling">Scaling</h2>
<h3 id="heading-manual-scaling">Manual Scaling</h3>
<p>You can scale in/out by using the following methods</p>
<ul>
<li><p>Update <strong>min/desired/max</strong> capacity as per the need and scale your EC2 Instances.</p>
</li>
<li><p>Attach/Detach EC2 Instance</p>
</li>
</ul>
<h3 id="heading-dynamic-scaling">Dynamic Scaling</h3>
<p><strong>Target Tracking Autoscaling</strong></p>
<p>Increase and decrease the current capacity of the group based on a Amazon CloudWatch metric and a target value.</p>
<ul>
<li><p>It will scale out the Auto Scaling group if <mark>any of the target tracking policies are ready for scale out</mark>, but will scale in <mark>only if all of the target tracking policies</mark> (with the scale-in portion enabled) are ready to scale in.</p>
</li>
<li><p>Scaling might determine that adding 1.5 instances will decrease the CPU utilization to close to 50 percent. Because it is not possible to add 1.5 instances, AWS rounds up and adds two instances. This might decrease the CPU utilization to a value below 50 percent, but it ensures that your application has enough resources to support it. Similarly, if Scaling determines that removing 1.5 instances increases your CPU utilization to above 50 percent, it removes just one instance.</p>
</li>
<li><p>Per-defined metrics to choose from: <code>ALBRequestCountPerTarget</code>, <code>ASGAverageNetworkOut</code>, <code>ASGAverageNetworkIn</code>, <code>ASGAverageCPUUtilization</code>.</p>
</li>
<li><p>AWS recommends that you only use metrics that are available at one-minute intervals to help you scale faster in response to utilization changes.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686032049304/f324732c-70a7-4967-aced-4140923b89a2.png" alt class="image--center mx-auto" /></p>
<p><strong>Simple Autoscaling</strong></p>
<p>Increase and decrease the current capacity of the group based on a single scaling adjustment, with a cooldown period between each scaling activity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686032250507/dfc5557f-5436-4a42-80fe-62c123381172.png" alt class="image--center mx-auto" /></p>
<p><strong>Step Autoscaling</strong></p>
<p>It is similar to simple autoscaling but we can work with multiple slices of the alarm values. Increase and decrease the current capacity of the group based on a set of scaling adjustments, known as step adjustments, that vary based on the size of the alarm breach.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686032218167/0ac57527-e48e-4cf8-b95b-591f9144aa45.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-scheduled-based-autoscaling">Scheduled Based Autoscaling</h3>
<h3 id="heading-predictive-autoscaling">Predictive Autoscaling</h3>
<h2 id="heading-termination-policies">Termination Policies:</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686053487708/62fcbea3-5a80-43ee-b4c8-85a31dae4545.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-default-termination-policy">Default Termination Policy</h3>
<p>Determines which instance to terminate on the scale-in events, instance refresh and AZ balance.</p>
<ul>
<li><p>The instance in the AZ with most instances</p>
</li>
<li><p>The instance with the older version of the launch template</p>
</li>
<li><p>If instances have the same launch template, terminate the instance with the closed billing hour</p>
</li>
</ul>
<h3 id="heading-allocation-strategy">Allocation Strategy</h3>
<p>Terminate instance based on the <code>lowest price</code>, <code>lowest priority for OnDemand Instance</code></p>
<h3 id="heading-oldest-launch-template">Oldest Launch Template</h3>
<p>Terminate instances that have the oldest launch template.</p>
<h3 id="heading-oldest-launch-configurations">Oldest Launch Configurations</h3>
<p>Terminate instances that have the oldest launch configuration.</p>
<h3 id="heading-closest-to-next-launch-hour">Closest to Next Launch Hour</h3>
<p>Terminate instances that are closest to the next billing hour.</p>
<h3 id="heading-newest-instance">Newest Instance</h3>
<p>Terminate the newest instance in the group. This policy is useful when you're testing a new launch configuration but don't want to keep it in production.</p>
<h3 id="heading-oldest-instances">Oldest Instances</h3>
<p>Terminate the oldest instance in the group. This option is useful when you're upgrading the instances in the Auto Scaling group to a new EC2 instance type.</p>
<h3 id="heading-custom-policy">Custom Policy</h3>
<p>This can be defined by using the lambda backed custom policy.</p>
<p><strong>NOTE:</strong> <mark>Amazon EC2 Auto Scaling always balances instances across Availability Zones first, regardless of which termination policy is used. As a result, you might encounter situations in which some newer instances are terminated before older instances.</mark></p>
<h2 id="heading-suspending-the-asg-process">Suspending the ASG Process</h2>
<p>There are certain times when we want to suspend various automatic processes that ASG conducts. For example: when we are deploying the changes to EC2 Instance we don't want to <code>Launch</code> a new instance and <code>Terminate</code> existing ones. We can achieve this by suspending the <code>Launch</code> and <code>Terminate</code> Process.</p>
<ul>
<li><p><strong>Launch</strong>—Adds instances to the Auto Scaling group when the group scales out, or when Amazon EC2 Auto Scaling chooses to launch instances for other reasons, such as when it adds instances to a warm pool.</p>
</li>
<li><p><strong>Terminate</strong>—Removes instances from the Auto Scaling group when the group scales in, or when Amazon EC2 Auto Scaling chooses to terminate instances for other reasons, such as when an instance is terminated for exceeding its maximum lifetime duration or failing a health check.</p>
</li>
<li><p><strong>AddToLoadBalancer</strong>—Adds instances to the attached load balancer target group or Classic Load Balancer when they are launched.</p>
</li>
<li><p><strong>AlarmNotification</strong>—Accepts notifications from CloudWatch alarms that are associated with dynamic scaling policies.</p>
</li>
<li><p><strong>AZRebalance</strong>—Balances the number of EC2 instances in the group evenly across all of the specified Availability Zones when the group becomes unbalanced.</p>
</li>
<li><p><strong>HealthCheck</strong>—Checks the health of the instances and marks an instance as unhealthy if Amazon EC2 or Elastic Load Balancing tells Amazon EC2 Auto Scaling that the instance is unhealthy.</p>
</li>
<li><p><strong>InstanceRefresh</strong>—Terminates and replaces instances using the instance refresh feature.</p>
</li>
<li><p><strong>ReplaceUnhealthy</strong>—Terminates instances that are marked as unhealthy and then create new instances to replace them.</p>
</li>
<li><p><strong>ScheduledActions</strong>—Performs the scheduled scaling actions that you create or that are created for you when you create an AWS Auto Scaling scaling plan and turn on predictive scaling.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[EC2 Autoscaling with Custom Metrics]]></title><description><![CDATA[Today you will learn how to define/create/publish the custom metrics and configure target tracking Autoscaling for your EC2 Autoscaling groups based on the metrics. In this blog we will:

Create ASG, SQS Queue, Target Tracking Policy

Use bash script...]]></description><link>https://blog.balmanrawat.com.np/ec2-autoscaling-with-custom-metrics</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/ec2-autoscaling-with-custom-metrics</guid><category><![CDATA[AWS]]></category><category><![CDATA[auto-scaling-aws]]></category><category><![CDATA[auto scaling]]></category><category><![CDATA[ec2]]></category><category><![CDATA[infrastructure]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Tue, 06 Jun 2023 09:36:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/JJpWirD-qDU/upload/e23aa046b45d9bdba7bb0cc5d2c9b2e7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today you will learn how to <code>define/create/publish</code> the custom metrics and configure target tracking Autoscaling for your EC2 Autoscaling groups based on the metrics. In this blog we will:</p>
<ul>
<li><p>Create ASG, SQS Queue, Target Tracking Policy</p>
</li>
<li><p>Use bash scripts to publish/consume messages to/from the SQS Queue</p>
</li>
<li><p>Use bash scripts to publish the custom metrics</p>
</li>
<li><p>Finally, autoscale based on the message count on the queue</p>
</li>
</ul>
<h2 id="heading-scenario">Scenario</h2>
<p>Let's suppose a scenario where users publish a data processing request in an AWS SQS. The SLA for the end user is that they should get the results within 2 seconds of the request being sent. Let's suppose each request takes <code>0.1 seconds</code> to be processed by an EC2 Instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686042437197/c1aa0628-a9dd-4153-9298-9eaba71fb188.png" alt class="image--center mx-auto" /></p>
<p><strong>Calculation backlog per Instance</strong>:</p>
<p>Now let's calculate the <strong>message backlog count per instance</strong> to determine when to run the autoscaling.</p>
<p>Backlog Per Instance = Time User Can Wait/Time it takes to serve a Request<br />Backlog Per Instance = 2/0.1 = 20</p>
<p>This means the number of messages in the <code>SQS queue/Instance count</code> shouldn't be greater than 20.</p>
<p>Now we have found <strong>Backlog Per Instance=20</strong> as our target for autoscaling. Enough theory now let's get started with creating the necessary setup in the AWS Account.</p>
<p><strong>NOTE:</strong> <mark>Please be careful with this tutorial as it can add extra costs to your AWS bills</mark></p>
<h2 id="heading-pre-requisite">Pre-requisite</h2>
<ul>
<li><p>EC2 Autoscaling Group</p>
</li>
<li><p>Amazon SQS</p>
</li>
<li><p>Basic Knowledge of AWS CLI and CloudWatch Metrics</p>
</li>
</ul>
<h3 id="heading-create-autoscaling-group"><strong>Create Autoscaling Group</strong></h3>
<p>I already have an Autoscaling group with the name <code>CustomMetricsASG</code> and please verify your <code>min/max/desired</code> capacity is properly set to allow the autoscaling to happen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686039139890/ec52a831-ee57-404e-8794-a00f459c1457.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-create-queue"><strong>Create Queue</strong></h3>
<p>Create a queue using the below command and verify</p>
<pre><code class="lang-bash">➜ aws sqs create-queue --queue-name myasgqueue
{
    <span class="hljs-string">"QueueUrl"</span>: <span class="hljs-string">"https://sqs.us-east-1.amazonaws.com/922726392568/myasgqueue"</span>
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686039295302/66c72acb-e519-46f7-b845-0f561573309f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-publish-messages-to-sqs"><strong>Publish Messages to SQS</strong></h3>
<p>Now you can use the below commands to publish the messages in the SQS queue which acts like the requests sent to our system in real-world scenario.</p>
<pre><code class="lang-bash"><span class="hljs-comment">##Producer</span>

<span class="hljs-comment">##Replace with your queue endpoint</span>
QUEUE_URL=https://sqs.us-east-1.amazonaws.com/922726392568/myasgqueue
<span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>
<span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Publishing messages..."</span>
  sleep 3
  aws sqs send-message --queue-url <span class="hljs-variable">${QUEUE_URL}</span> \
   --message-body <span class="hljs-string">"aws asg mock messages to increase the load..."</span> \
   --no-cli-pager
<span class="hljs-keyword">done</span>
</code></pre>
<p>If the above script ran successfully you will see outputs like below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686039916183/765280c6-39ca-46aa-9e08-80fa3d0b0424.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-consume-messages-from-sqsoptional"><strong>Consume Messages from SQS(Optional...)</strong></h3>
<p>This is optional but if you want to make it more realistic what you can do is increase the sleep time below such that publishing is done fast and the consuming is slower which increases the backlog in the Queue.</p>
<pre><code class="lang-bash"><span class="hljs-comment">## Conumer</span>
<span class="hljs-comment">##Replace with your queue endpoint</span>
QUEUE_URL=https://sqs.us-east-1.amazonaws.com/922726392568/myasgqueue
<span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>
<span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"consuming messages..."</span>
  sleep 3
  aws sqs receive-message --queue-url <span class="hljs-variable">${${QUEUE_URL}</span>} \
  --no-cli-pager
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-publishing-custom-metrics"><strong>Publishing Custom Metrics</strong></h3>
<p>The below script does the following things:</p>
<ul>
<li><p>Fetches the number of available messages in the SQS Queue</p>
</li>
<li><p>Fetches the number of the EC2 Instances in <code>InService</code> state</p>
</li>
<li><p>Divides <code>Message Count/InstanceCount</code> to get the message <code>BacklogPerInstance</code></p>
</li>
<li><p>Then publish the custom metrics every 60 seconds setting:</p>
<ul>
<li><p>MetricName: <code>MyBacklogPerInstance</code></p>
</li>
<li><p>Namespace: <code>MyCustomASGMetrics</code></p>
</li>
<li><p>Dimension as: <code>QueueName=${QUEUE_NAME}</code></p>
</li>
</ul>
</li>
</ul>
<p><strong>NOTE:</strong> Before running the script, please replace the values of the below variables.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment">##Replace these values, before running the script</span>
QUEUE_URL=https://sqs.us-east-1.amazonaws.com/922726392568/myasgqueue
ASG_NAME=CustomMetricsASG
QUEUE_NAME=myasgqueue

<span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>
<span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"....starting...."</span>
  sleep 60
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"[INFO] Querying.. Available Queue Message\n"</span>
  APPROX_AVAILABLE_MESSAGES=$(aws sqs get-queue-attributes --queue-url <span class="hljs-variable">${QUEUE_URL}</span> --attribute-names ApproximateNumberOfMessages --query Attributes.ApproximateNumberOfMessages --output text | tr -d <span class="hljs-string">'[:space:]'</span>)
  STATUS_CODE=$?
  <span class="hljs-keyword">if</span> [[ <span class="hljs-variable">${STATUS_CODE}</span> -ne 0 ]]
  <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">printf</span> <span class="hljs-string">"[WARN] APPROX_AVAILABLE_MESSAGES retrival failed with status code: %s ...\n"</span> <span class="hljs-variable">${STATUS_CODE}</span>
    <span class="hljs-built_in">continue</span>
  <span class="hljs-keyword">fi</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"[INFO] The Number of Available Message: %s\n"</span> <span class="hljs-string">"<span class="hljs-variable">${APPROX_AVAILABLE_MESSAGES}</span>"</span>

  <span class="hljs-built_in">printf</span> <span class="hljs-string">"[INFO] Querying.. Number of Instance in Service\n"</span>
  IN_SERVICE_COUNT=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names <span class="hljs-variable">${ASG_NAME}</span> --query <span class="hljs-string">"AutoScalingGroups[].Instances[?LifecycleState=='InService'].[InstanceId]"</span>\
    --output text | wc -l | tr -d <span class="hljs-string">'[:space:]'</span>)
  STATUS_CODE=$?
  <span class="hljs-keyword">if</span> [[ <span class="hljs-variable">${STATUS_CODE}</span> -ne 0 ]]
  <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">printf</span> <span class="hljs-string">"[WARN] IN_SERVICE_COUNT retrival failed with status code: %s ...\n"</span> <span class="hljs-variable">${STATUS_CODE}</span>
    <span class="hljs-built_in">continue</span>
  <span class="hljs-keyword">fi</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"[INFO] The Number of Instance in Service: %s\n"</span> <span class="hljs-string">"<span class="hljs-variable">${IN_SERVICE_COUNT}</span>"</span>

  BACKLOG_PER_INSTANCE=$((APPROX_AVAILABLE_MESSAGES / IN_SERVICE_COUNT))
  STATUS_CODE=$?
  <span class="hljs-keyword">if</span> [[ <span class="hljs-variable">${STATUS_CODE}</span> -ne 0 ]]
  <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">printf</span> <span class="hljs-string">"[WARN] BACKLOG_PER_INSTANCE calculation failed with status code: %s ...\n"</span> <span class="hljs-variable">${STATUS_CODE}</span>
    <span class="hljs-built_in">continue</span>
  <span class="hljs-keyword">fi</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"[INFO] Calculated Backlog per instance %s/%s: %s\n"</span> <span class="hljs-string">"<span class="hljs-variable">${APPROX_AVAILABLE_MESSAGES}</span>"</span> <span class="hljs-string">"<span class="hljs-variable">${IN_SERVICE_COUNT}</span>"</span> <span class="hljs-string">"<span class="hljs-variable">${BACKLOG_PER_INSTANCE}</span>"</span>

  aws cloudwatch put-metric-data --metric-name MyBacklogPerInstance --namespace MyCustomASGMetrics \
  --unit None --value <span class="hljs-variable">${BACKLOG_PER_INSTANCE}</span> --dimensions QueueName=<span class="hljs-variable">${QUEUE_NAME}</span>
  STATUS_CODE=$?
  <span class="hljs-keyword">if</span> [[ <span class="hljs-variable">${STATUS_CODE}</span> -ne 0 ]]
  <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">printf</span> <span class="hljs-string">"[WARN] put-metrics-data failed with status code: %s ...\n"</span> <span class="hljs-variable">${STATUS_CODE}</span>
    <span class="hljs-built_in">continue</span>
  <span class="hljs-keyword">fi</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"[INFO] Successfully published custom metrics with value: %s ...\n"</span> <span class="hljs-variable">${BACKLOG_PER_INSTANCE}</span>
<span class="hljs-keyword">done</span>
</code></pre>
<p>Once you run the above script, if everything goes fine you should be able to see the custom metrics in the AWS CloudWatch Metrics</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686042805428/b7e83710-a0d8-4f40-805e-1a8008953730.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-creating-targettracking-autoscaling"><strong>Creating TargetTracking Autoscaling</strong></h3>
<p>Until now you can't create target tracking autoscaling Policy with custom metrics from AWS Console so, you need to create it from AWS CLI.</p>
<p><strong>Policy Config</strong></p>
<p>Please copy the below config and update with your relevant values:</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"TargetValue"</span>:<span class="hljs-number">20</span>,
   <span class="hljs-attr">"CustomizedMetricSpecification"</span>:{
      <span class="hljs-attr">"MetricName"</span>:<span class="hljs-string">"MyBacklogPerInstance"</span>,
      <span class="hljs-attr">"Namespace"</span>:<span class="hljs-string">"MyCustomASGMetrics"</span>,
      <span class="hljs-attr">"Dimensions"</span>:[
         {
            <span class="hljs-attr">"Name"</span>:<span class="hljs-string">"QueueName"</span>,
            <span class="hljs-attr">"Value"</span>:<span class="hljs-string">"myasgqueue"</span>
         }
      ],
      <span class="hljs-attr">"Statistic"</span>:<span class="hljs-string">"Average"</span>,
      <span class="hljs-attr">"Unit"</span>:<span class="hljs-string">"None"</span>
   }
}
</code></pre>
<p><strong>Create the Policy</strong></p>
<p>Replace the values in the below variables and run the command</p>
<pre><code class="lang-bash"><span class="hljs-comment">##Replace the below ASG_NAME with your values</span>
ASG_NAME=CustomMetricsASG
aws autoscaling put-scaling-policy --policy-name sqs20-target-tracking-scaling-policy \
  --auto-scaling-group-name <span class="hljs-variable">${ASG_NAME}</span> --policy-type TargetTrackingScaling \
  --target-tracking-configuration file://config.json
</code></pre>
<p>Once created successfully you will be able to see it in the Autoscaling section of the ASG</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686042765011/8c81b527-42f6-4c5b-a830-17321572adc0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-scaling-in-action">Scaling in Action</h3>
<p>If there are enough points in the custom metrics and the value is greater than the defined threshold you will quickly see the scaling happening in the action.</p>
<p><strong>Checking Activity in ASG</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686042992797/3d1ff355-b86c-4278-b5cb-b081bf708cd1.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686043042479/5cc0a421-0684-4597-9905-18741d1c7e69.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>This is how you can setup autoscaling for any custom metrics and with any other autoscaling policies. You can even mix it with other autoscaling policies like: simple, step autoscaling.</p>
<p>Thanks for reading! Catch you next time!</p>
]]></content:encoded></item><item><title><![CDATA[AWS Secrets Manger in Kubernetes]]></title><description><![CDATA[Prerequisite

eksctl. details

AWS CLI. details

Helm. details


Create EKS Cluster
This will create an EKS cluster in us-east-1 region with 2 nodes in the node-group.
export CLUSTER_NAME=demo-cluster
export AWS_REGION=us-east-1
eksctl create cluster...]]></description><link>https://blog.balmanrawat.com.np/aws-secrets-manger-in-kubernetes</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/aws-secrets-manger-in-kubernetes</guid><category><![CDATA[EKS]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[AWS]]></category><category><![CDATA[secrets]]></category><category><![CDATA[Vault]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Sun, 28 May 2023 08:03:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/6IsCeQBIPmo/upload/fa306209bd091ca37342c87b0c7b9643.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-prerequisite">Prerequisite</h2>
<ul>
<li><p><strong>eksctl.</strong> <a target="_blank" href="https://github.com/weaveworks/eksctl">details</a></p>
</li>
<li><p><strong>AWS CLI.</strong> <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"><strong>details</strong></a></p>
</li>
<li><p><strong>Helm</strong>. <a target="_blank" href="https://helm.sh/docs/intro/install/">details</a></p>
</li>
</ul>
<h2 id="heading-create-eks-cluster">Create EKS Cluster</h2>
<p>This will create an EKS cluster in <code>us-east-1</code> region with 2 nodes in the node-group.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> CLUSTER_NAME=demo-cluster
<span class="hljs-built_in">export</span> AWS_REGION=us-east-1
eksctl create cluster --name <span class="hljs-variable">${CLUSTER_NAME}</span> --version 1.26 --node-type t3.medium --nodes 2 --managed --region <span class="hljs-variable">${AWS_REGION}</span>
.....
.....
2023-05-28 18:47:47 [✔]  EKS cluster <span class="hljs-string">"demo-cluster"</span> <span class="hljs-keyword">in</span> <span class="hljs-string">"us-east-1"</span> region is ready.
</code></pre>
<h3 id="heading-verify">verify</h3>
<p>When running the following command <code>kubectl get nodes</code> you should see the nodes in a ready state.</p>
<pre><code class="lang-bash">➜ kubectl get nodes   
NAME                             STATUS   ROLES    AGE    VERSION
ip-192-168-17-231.ec2.internal   Ready    &lt;none&gt;   114m   v1.26.4-eks-0a21954
ip-192-168-61-195.ec2.internal   Ready    &lt;none&gt;   114m   v1.26.4-eks-0a21954
</code></pre>
<h2 id="heading-deploy-external-secrets-operator">Deploy External Secrets Operator</h2>
<p>Use below helm commands to:</p>
<ul>
<li><p>add/update the repo</p>
</li>
<li><p>install the operator</p>
</li>
</ul>
<pre><code class="lang-bash">helm repo add external-secrets https://charts.external-secrets.io
helm repo update

helm install external-secrets \
  external-secrets/external-secrets \
    --namespace external-secrets \
    --create-namespace \
    --<span class="hljs-built_in">set</span> installCRDs=<span class="hljs-literal">true</span> \
    --<span class="hljs-built_in">wait</span>
.....
external-secrets has been deployed successfully!
....
</code></pre>
<h3 id="heading-verify-1">verify</h3>
<p>When you run <code>k get all -n external-secrets</code> you should be able to see all the pods <code>pod/external-secrets-*</code> , <code>pod/external-secrets-cert-controller-*</code>, <code>pod/external-secrets-webhook-</code>* running successfully.</p>
<pre><code class="lang-bash">➜ k get all -n external-secrets
NAME                                                    READY   STATUS    RESTARTS   AGE
pod/external-secrets-5997dc48b4-drncs                   1/1     Running   0          2m33s
pod/external-secrets-cert-controller-587977f796-mdk9p   1/1     Running   0          2m33s
pod/external-secrets-webhook-674f79cb47-jz7ds           1/1     Running   0          2m33s

NAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/external-secrets-webhook   ClusterIP   10.100.84.243   &lt;none&gt;        443/TCP   2m33s

NAME                                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/external-secrets                   1/1     1            1           2m34s
deployment.apps/external-secrets-cert-controller   1/1     1            1           2m34s
deployment.apps/external-secrets-webhook           1/1     1            1           2m34s

NAME                                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/external-secrets-5997dc48b4                   1         1         1       2m34s
replicaset.apps/external-secrets-cert-controller-587977f796   1         1         1       2m34s
replicaset.apps/external-secrets-webhook-674f79cb47
</code></pre>
<h2 id="heading-configuring-iam-roles-for-service-accounts-irsa-and-secrets-manager">Configuring IAM roles for service accounts (IRSA) and Secrets Manager</h2>
<h3 id="heading-add-oidc-provider"><strong>Add OIDC Provider</strong></h3>
<pre><code class="lang-bash">➜ eksctl utils associate-iam-oidc-provider --cluster=<span class="hljs-variable">${CLUSTER_NAME}</span> --approve

2023-05-28 13:25:07 [✔]  created IAM Open ID Connect provider <span class="hljs-keyword">for</span> cluster <span class="hljs-string">"demo-cluster"</span> <span class="hljs-keyword">in</span> <span class="hljs-string">"us-east-1"</span>
</code></pre>
<h3 id="heading-create-secret-in-secrets-manager"><strong>Create Secret in Secrets Manager</strong></h3>
<pre><code class="lang-bash">SECRET_ARN=$(aws secretsmanager create-secret --name user-creds \
    --secret-string <span class="hljs-string">"{\"user\":\"admin\",\"password\":\"topsecret\"}"</span> \
    --region <span class="hljs-variable">${AWS_REGION}</span> | jq -r .ARN)
</code></pre>
<h3 id="heading-create-iam-policy"><strong>Create IAM Policy</strong></h3>
<p>IAM Policy to Read/Describe the <code>user-creds</code> secret in the secrets manager</p>
<pre><code class="lang-bash">➜ IAM_POLICY_ARN=$(aws iam create-policy --policy-name eso-sm-reader-policy --policy-document <span class="hljs-string">'{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:DescribeSecret",
        "secretsmanager:GetSecretValue"
      ],
      "Resource": ["'</span><span class="hljs-variable">${SECRET_ARN}</span><span class="hljs-string">'"]
    }
  ]
}'</span> | jq -r .Policy.Arn)
</code></pre>
<h3 id="heading-create-a-service-account-and-association"><strong>Create a Service Account and Association</strong></h3>
<pre><code class="lang-bash">➜ eksctl create iamserviceaccount \
    --name eso-secret-irsa \
    --namespace default \
    --cluster <span class="hljs-variable">${CLUSTER_NAME}</span> \
    --role-name <span class="hljs-string">"eso-sm-reader-role"</span> \
    --attach-policy-arn <span class="hljs-variable">$IAM_POLICY_ARN</span> \
    --approve
</code></pre>
<h3 id="heading-verify-2"><strong>verify</strong></h3>
<pre><code class="lang-bash">k describe sa eso-secret-irsa                      
Name:                eso-secret-irsa
Namespace:           default
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::808053714438:role/eso-sm-reader-role
Image pull secrets:  &lt;none&gt;
Mountable secrets:   &lt;none&gt;
Tokens:              &lt;none&gt;
Events:              &lt;none&gt;
</code></pre>
<h2 id="heading-deploy-external-secret-storesecret">Deploy External Secret Store/Secret</h2>
<h3 id="heading-create-secretstore"><strong>Create SecretStore</strong></h3>
<pre><code class="lang-bash">cat &lt;&lt;EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secret-store
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-secret-irsa
EOF
</code></pre>
<h3 id="heading-verify-3"><strong>Verify</strong></h3>
<pre><code class="lang-bash">kubectl get SecretStore                
NAME           AGE   STATUS   CAPABILITIES   READY
secret-store   58s   Valid    ReadWrite      True
</code></pre>
<h3 id="heading-create-externalsecret"><strong>Create ExternalSecret</strong></h3>
<pre><code class="lang-bash">cat &lt;&lt;EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: user-creds
spec:
  refreshInterval: 5m
  secretStoreRef:
    name: secret-store
    kind: SecretStore
  target:
    name: user-creds
  data:
  - secretKey: user
    remoteRef:
      key: user-creds
      property: user
  - secretKey: password
    remoteRef:
      key: user-creds
      property: password
EOF
</code></pre>
<h3 id="heading-verify-4"><strong>Verify</strong></h3>
<pre><code class="lang-bash">➜ kubectl get ExternalSecret           
NAME         STORE          REFRESH INTERVAL   STATUS         READY
user-creds   secret-store   5m                 SecretSynced   True
➜ kubectl get secret
NAME         TYPE     DATA   AGE
user-creds   Opaque   2      30s
</code></pre>
<h2 id="heading-deploy-application">Deploy Application</h2>
<pre><code class="lang-bash">cat &lt;&lt;EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: creds-app
spec:
  containers:
    - name: app
      image: k8s.gcr.io/busybox
      <span class="hljs-built_in">command</span>: [ <span class="hljs-string">"/bin/sh"</span>, <span class="hljs-string">"-c"</span>, <span class="hljs-string">"env"</span> ]
      envFrom:
      - secretRef:
          name: user-creds
EOF
</code></pre>
<h3 id="heading-verify-5"><strong>Verify</strong></h3>
<pre><code class="lang-bash">➜ kubectl logs pod/creds-app     
....   
user=admin
password=topsecret
....
</code></pre>
]]></content:encoded></item><item><title><![CDATA[HELM Cheatsheet]]></title><description><![CDATA[Three Big Concepts
A Chart is a helm package.
A Repo is a place where these charts get collected and shared.
A Release is an instance of a chart running in a Kubernetes cluster.
Directory Structure
wordpress/
  Chart.yaml      # A YAML file containin...]]></description><link>https://blog.balmanrawat.com.np/helm-cheatsheet</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/helm-cheatsheet</guid><category><![CDATA[Helm]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[EKS]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Cloud]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Sun, 28 May 2023 03:31:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Sq0L3SPWLHI/upload/0333782c7ad53b98e25cd4f547e485ea.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-three-big-concepts">Three Big Concepts</h3>
<p>A <strong><em>Chart</em></strong> is a helm package.</p>
<p><em>A</em> <strong><em>Repo</em></strong> is a place where these charts get collected and shared.</p>
<p>A <strong><em>Release</em></strong> is an instance of a chart running in a Kubernetes cluster.</p>
<h3 id="heading-directory-structure">Directory Structure</h3>
<pre><code class="lang-bash">wordpress/
  Chart.yaml      <span class="hljs-comment"># A YAML file containing information about the chart</span>
  LICENSE         <span class="hljs-comment"># OPTIONAL: A plain text file containing the license for the chart</span>
  README.md       <span class="hljs-comment"># OPTIONAL: A human-readable README file</span>
  values.yaml     <span class="hljs-comment"># The default configuration values for this chart</span>
  values.schema.json  <span class="hljs-comment"># OPTIONAL: A JSON Schema for imposing a structure on the values.yaml file</span>
  charts/         <span class="hljs-comment"># A directory containing any charts upon which this chart depends.</span>
  crds/           <span class="hljs-comment"># Custom Resource Definitions</span>
  templates/      <span class="hljs-comment"># A directory of templates that, when combined with values,</span>
                  <span class="hljs-comment"># will generate valid Kubernetes manifest files.</span>
  templates/NOTES.txt <span class="hljs-comment"># OPTIONAL: A plain text file containing short usage notes</span>
</code></pre>
<h3 id="heading-manage-repo">Manage Repo</h3>
<pre><code class="lang-bash">helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo list
helm repo remove &lt;repo-name&gt;
helm repo update
helm repo remove
helm repo index
</code></pre>
<h3 id="heading-manage-chart">Manage Chart</h3>
<pre><code class="lang-bash"><span class="hljs-comment">#create helm chart</span>
helm create &lt;chart-name&gt;

<span class="hljs-comment">#Searching</span>
helm search repo &lt;chart-name&gt;
helm search repo &lt;repo-name&gt; <span class="hljs-comment">#to see all the charts in a repo</span>
helm search hub &lt;chart-name&gt; <span class="hljs-comment">#to search on artifcathub</span>

<span class="hljs-comment">#See information on charts</span>
helm show/inspect all/chart/crds/readme/values &lt;chart-name&gt;
helm show all bitnami/wordpress
helm show values bitnami/wordpress
helm show chart bitnami/wordpress
helm show readme bitnami/wordpress

<span class="hljs-comment">#generate the manifest from chart</span>
helm template &lt;chart-name&gt;
</code></pre>
<h3 id="heading-managing-releases">Managing Releases</h3>
<pre><code class="lang-bash"><span class="hljs-comment">#install</span>
helm install &lt;release-name&gt; &lt;chart-name&gt;/&lt;local-chart-path&gt;
<span class="hljs-comment">#list installed release</span>
helm list
<span class="hljs-comment">#uninstall</span>
helm uinstall &lt;release-name&gt;
<span class="hljs-comment">#history</span>
helm <span class="hljs-built_in">history</span> &lt;release-name&gt;
<span class="hljs-comment">#rollback</span>
helm rollback &lt;release-name&gt; &lt;revision&gt; 

<span class="hljs-comment">#See information of the release</span>
helm get all/hooks/manifest/notes/values &lt;release-name&gt;
helm get manifest happy-panda

<span class="hljs-comment">#Install dry run</span>
helm install --dry-run --disable-openapi-validation &lt;release-name&gt; &lt;chart-name&gt;
</code></pre>
<h3 id="heading-helm-built-in-objects">Helm Built-in Objects</h3>
<p>Objects are passed into a template from the template engine. The built-in values always begin with a capital letter. This is in keeping with Go's naming convention.</p>
<pre><code class="lang-bash">Release: This object describes the release itself. It has several objects inside of it:
    Release.Name
    Release.Namespace
    Release.IsUpgrade
    Release.IsInstall
    Release.Revision
    Release.Service

Values: Values passed into the template from the values.yaml file and from user-supplied files. By default, Values is empty.

Chart: The contents of the Chart.yaml file. Any data <span class="hljs-keyword">in</span> Chart.yaml  will be accessible here.

Files:
    Files.Get is a <span class="hljs-keyword">function</span> <span class="hljs-keyword">for</span> getting a file by name (.Files.Get config.ini)
    Files.GetBytes is a <span class="hljs-keyword">function</span> <span class="hljs-keyword">for</span> getting the contents of a file as an array of bytes instead of as a string. This is useful <span class="hljs-keyword">for</span> things like images.
    Files.Glob is a <span class="hljs-keyword">function</span> that returns a list of files whose names match the given shell glob pattern.
    Files.Lines is a <span class="hljs-keyword">function</span> that reads a file line-by-line. This is useful <span class="hljs-keyword">for</span> iterating over each line <span class="hljs-keyword">in</span> a file.
    Files.AsSecrets is a <span class="hljs-keyword">function</span> that returns the file bodies as Base 64 encoded strings.
    Files.AsConfig is a <span class="hljs-keyword">function</span> that returns file bodies as a YAML map.

Capabilities: This provides information about what capabilities the Kubernetes cluster supports.
    Capabilities.APIVersions is a <span class="hljs-built_in">set</span> of versions.
    Capabilities.APIVersions.Has <span class="hljs-variable">$version</span> indicates whether a version (e.g., batch/v1) or resource (e.g., apps/v1/Deployment) is available on the cluster.
    Capabilities.KubeVersion and Capabilities.KubeVersion.Version is the Kubernetes version.
    Capabilities.KubeVersion.Major is the Kubernetes major version.
    Capabilities.KubeVersion.Minor is the Kubernetes minor version.
    Capabilities.HelmVersion is the object containing the Helm Version details, it is the same output of helm version
    Capabilities.HelmVersion.Version is the current Helm version <span class="hljs-keyword">in</span> semver format.
    Capabilities.HelmVersion.GitCommit is the Helm git sha1.
    Capabilities.HelmVersion.GitTreeState is the state of the Helm git tree.
    Capabilities.HelmVersion.GoVersion is the version of the Go compiler used.

Template: Contains information about the current template that is being executed
    Template.Name: A namespaced file path to the current template (e.g. mychart/templates/mytemplate.yaml)
    Template.BasePath: The namespaced path to the templates directory of the current chart (e.g. mychart/templates).
</code></pre>
<h3 id="heading-helm-lookup-functions">Helm Lookup functions</h3>
<p>template functions list: <a target="_blank" href="https://helm.sh/docs/chart_template_guide/function_list/">https://helm.sh/docs/chart_template_guide/function_list/</a></p>
<pre><code class="lang-bash">&lt;function-name&gt; arg1 arg2...
default <span class="hljs-string">"default value"</span> .Values.&lt;value&gt;
lookup <span class="hljs-string">"apiVersion"</span>, <span class="hljs-string">"kind"</span>, <span class="hljs-string">"namespace"</span>, <span class="hljs-string">"name"</span> -&gt; resource or resource list
eg: lookup <span class="hljs-string">"v1"</span> <span class="hljs-string">"Pod"</span> <span class="hljs-string">"mynamespace"</span> <span class="hljs-string">"mypod"</span> =&gt; equivalent to: kubectl get pod mypod -n mynamespace

Operators Functions
eq, ne, lt, gt, and, or

Named Templates
{{- define <span class="hljs-string">"MY.NAME"</span> }}
  <span class="hljs-comment"># body of template here</span>
{{- end }}

{{- template <span class="hljs-string">"MY.NAME"</span> }}

{{- template <span class="hljs-string">"MY.NAME"</span> . }} <span class="hljs-comment">##using the context</span>

{{ include <span class="hljs-string">"MY.NAME"</span> . | indent 4 }} <span class="hljs-comment">##include function can be used to pass values so, it is recommend to use include. Because template is an action, and not a function, there is no way to pass the output of a template call to other functions; the data is simply inserted inline.</span>
</code></pre>
<h3 id="heading-helm-passing-the-values">Helm Passing the values</h3>
<p>There are two ways to pass values one is using <strong>values.yml</strong> file and the other is <strong>–set flag</strong>. If both are used, <strong>--set values are merged into --values</strong> with higher precedence. Overrides specified with -<strong>-set are persisted in a ConfigMap</strong>. Values that have been --set can be viewed for a given release with <strong>helm get values &lt;release-name&gt;.</strong> Values that have been --set can be cleared by running <strong>helm upgrade with --reset-values specified.</strong></p>
<pre><code class="lang-bash">helm install -f values.yaml local-mysql bitnami/mysql -<span class="hljs-built_in">set</span> db.auth.username=<span class="hljs-string">"sth"</span>, db.auth.password=<span class="hljs-string">"supersecret"</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Managing CloudFormation Stacks at Scale with cfn-compose]]></title><description><![CDATA[Overview
As infrastructure grows more complex, managing multiple CloudFormation Stacks becomes a challenge. Typically, actions such as creating, updating, or deleting stacks are performed on a single stack at a time. Inaddition deleting stacks in a d...]]></description><link>https://blog.balmanrawat.com.np/managing-cloudformation-stacks-at-scale-with-cfn-compose</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/managing-cloudformation-stacks-at-scale-with-cfn-compose</guid><category><![CDATA[cloudformation]]></category><category><![CDATA[#IaC]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[AWS CloudFormation]]></category><category><![CDATA[infrastructure]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Fri, 03 Mar 2023 01:43:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/3pCRW_JRKM8/upload/2c79aea8b74bff2183523105ed0f32f6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p>As infrastructure grows more complex, managing multiple CloudFormation Stacks becomes a challenge. Typically, actions such as creating, updating, or deleting stacks are performed on a single stack at a time. Inaddition deleting stacks in a development or testing environment can be cumbersome because we usually want to destroy the whole environment and to do that stacks must be deleted in the reverse order of creation.</p>
<p><code>cfn-compose</code> offers a solution to this problem by providing a way to manage multiple, related stacks using a declarative <code>yaml</code> language, making the process easier and more streamlined. For more details please go through the rest of the Readme.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677805678366/8ed8429f-67b1-4ed8-9042-dd38b7a4d14b.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-cfn-composecfnc-in-short">cfn-compose(cfnc in short)</h1>
<p>A command-line tool for managing CloudFormation Stacks at scale.<br /><strong>Github Repo:</strong> <a target="_blank" href="https://github.com/rbalman/cfn-compose">https://github.com/rbalman/cfn-compose</a></p>
<h2 id="heading-features">Features</h2>
<ul>
<li><p>Create/Update/Delete multiple CloudFormation stacks in parallel or sequentially</p>
</li>
<li><p>Customize the CloudFormation stacks dependency using yaml config</p>
</li>
<li><p>Delete multiple CloudFormation stacks respecting the creation sequence</p>
</li>
<li><p>DryRun mode to plan the change</p>
</li>
<li><p>Generate/Validate/visualize configuration with ease</p>
</li>
<li><p>Supports Go Template for dynamic value substitution</p>
</li>
</ul>
<h2 id="heading-limitations">Limitations</h2>
<ul>
<li><p>Supports limited CFN attributes</p>
</li>
<li><p>No Retry Mechanism</p>
</li>
<li><p>No Configurable concurrency. One Go routine is spun for every flow.</p>
</li>
<li><p>One compose file can have maximum <code>50</code> flows and each flow can have up to <code>50 stacks</code>. This is by design, to limit stacks in a compose file.</p>
</li>
</ul>
<h2 id="heading-installation">Installation</h2>
<p>Binary is available for Linux, Windows and Mac OS (amd64 and arm64). Download the binary for your respective platform from the <a target="_blank" href="https://github.com/rbalman/cfn-compose/releases">releases page</a>.</p>
<h4 id="heading-using-go-cli">Using go cli</h4>
<pre><code class="lang-shell">go install github.com/rbalman/cfn-compose@latest
</code></pre>
<h2 id="heading-usage">Usage</h2>
<pre><code class="lang-shell">➜ cfnc --help
Manage cloudformation stacks at scale. Design and deploy multiple cloudformation stacks either in sequence or in parallel using declarative configuration

Usage:
  cfnc [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  config      Generate, validate and visualize the compose configuration
  deploy      Deploys the stacks based on the sequence specified in the compose configuration
  destroy     Destroys all the stacks in the reverse order of creation
  help        Help about any command

Flags:
  -c, --config string     File path to compose file (default "cfn-compose.yml")
  -d, --dry-run           Run commands in dry run mode
  -h, --help              help for cfnc
  -l, --loglevel string   Specify Log Levels. Valid Levels are: DEBUG, INFO, WARN, ERROR (default "INFO")
  -v, --version           version for cfnc

Use "cfnc [command] --help" for more information about a command.
</code></pre>
<h4 id="heading-examples">Examples</h4>
<pre><code class="lang-shell">## Deploy
cfnc deploy
## Deploy in dry run mode
cfnc deploy -d

## Destroy
cfnc destroy
## Destroy in dry run mode
cfnc destroy -d

## Generate Validate and Visualize compose configuration
cfnc config generate
cfnc config validate
cfnc config visualize
</code></pre>
<h2 id="heading-man">Man</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Command</td><td>Options</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td>cfnc</td><td>-h, --help, help</td><td>Get description of cfnc</td></tr>
<tr>
<td>cfnc</td><td>-d, --dry-run</td><td>enable dry run mode</td></tr>
<tr>
<td>cfnc</td><td>-l, --loglevel</td><td>Specify Log Levels. Valid Levels are: DEBUG, INFO, WARN, ERROR (default "INFO")</td></tr>
<tr>
<td>cfnc</td><td>-c, --config</td><td>File path to compose file (default "cfn-compose.yml")</td></tr>
<tr>
<td>cfnc deploy</td><td>with no flag</td><td>deploys all the stacks</td></tr>
<tr>
<td>cfnc deploy</td><td>-f, --flow</td><td>Cherry pick specific flow to deploy</td></tr>
<tr>
<td>cfnc destroy</td><td>with no flag</td><td>destroys all the stacks</td></tr>
<tr>
<td>cfnc destroy</td><td>-f, --flow</td><td>Cherry pick specific flow to destroy</td></tr>
<tr>
<td>cfnc config generate</td><td>no flags</td><td>Generates compose template</td></tr>
<tr>
<td>cfnc config validate</td><td>no flags</td><td>Validates the compose configuration</td></tr>
<tr>
<td>cfnc config visualize</td><td>no flags</td><td>Visualize the stacks dependencies and creation order</td></tr>
<tr>
<td>cfnc</td><td>-v, --version</td><td>version for cfnc</td></tr>
</tbody>
</table>
</div><h2 id="heading-documentation">Documentation</h2>
<p><strong>Sample Config File:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Description:</span> <span class="hljs-string">Sample</span> <span class="hljs-string">CloudFormation</span> <span class="hljs-string">Compose</span> <span class="hljs-string">file</span>
<span class="hljs-attr">Vars:</span>
  <span class="hljs-attr">Key1:</span> <span class="hljs-string">Value1</span>
  <span class="hljs-attr">Key2:</span> <span class="hljs-string">Value2</span>
<span class="hljs-attr">Flows:</span>
  <span class="hljs-attr">Flow1:</span>
    <span class="hljs-attr">Order:</span> <span class="hljs-number">0</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Flow1</span> <span class="hljs-string">Description</span>
    <span class="hljs-attr">Stacks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Stack1</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Stack2</span>
  <span class="hljs-attr">Flow2:</span>
    <span class="hljs-attr">Order:</span> <span class="hljs-number">1</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Flow2</span> <span class="hljs-string">description</span>
    <span class="hljs-attr">Stacks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Stack1</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Stack2</span>
</code></pre>
<p>A typical compose configuration contains:</p>
<ul>
<li><p>Optional <code>Description</code></p>
</li>
<li><p>Optional <code>Vars</code> section to define variables in <code>Key: Value</code> mapping. Only static variables are supported at the moment. eg:</p>
</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">Vars:</span>
  <span class="hljs-attr">ENV_TYPE:</span> <span class="hljs-string">'nonproduction'</span>
  <span class="hljs-attr">ENV_NAME:</span> <span class="hljs-string">'demo'</span>
  <span class="hljs-attr">AWS_PROFILE:</span> <span class="hljs-string">'demo'</span>
</code></pre>
<ul>
<li><p>Mandatory <code>Flows:</code> section <code>Flow</code> is a collection of CloudFormation stacks that are deployed sequentially. <code>Flows</code> is the collection of flow which can be ordered using <code>Order</code> property. <code>Flows</code> can run in parallel or sequentially based on the Order property.</p>
<ul>
<li><p>Optional <code>Order</code> can be any <code>unsigned</code> integer. Default <code>Order</code> is set to <code>0</code>. Flow with the lowest orders are deployed first.</p>
</li>
<li><p>Optional <code>Description</code></p>
</li>
<li><p>Mandatory <code>Stacks</code> which is the collection of CFN stack. Below are the supported attributes of the stack object</p>
<ul>
<li><p>mandatory <code>template_file</code> or <code>template_url</code> (only s3 url)</p>
</li>
<li><p>mandatory <code>stack_name</code></p>
</li>
<li><p>optional <code>capabilities</code></p>
</li>
<li><p>optional <code>parameters</code></p>
</li>
<li><p>optional <code>tags</code></p>
</li>
<li><p>optional <code>tags</code></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>Sample:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Description:</span> <span class="hljs-string">Sample</span> <span class="hljs-string">CloudFormation</span> <span class="hljs-string">Compose</span> <span class="hljs-string">file</span>
<span class="hljs-attr">Vars:</span>
  <span class="hljs-attr">ENV_NAME:</span> <span class="hljs-string">cfnc</span>
  <span class="hljs-attr">ENV_TYPE:</span> <span class="hljs-string">nonproduction</span>
<span class="hljs-attr">Flows:</span>
  <span class="hljs-attr">SecurityGroup:</span>
    <span class="hljs-attr">Order:</span> <span class="hljs-number">0</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Creates</span> <span class="hljs-string">SecurityGroup</span>
    <span class="hljs-attr">Stacks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">template_file:</span> <span class="hljs-string">&lt;cfn-template-path&gt;</span>
        <span class="hljs-attr">stack_name:</span> <span class="hljs-string">stack-name1</span>
        <span class="hljs-attr">parameters:</span>
          <span class="hljs-attr">EnvironmentName:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_NAME }}</span>'</span>
          <span class="hljs-attr">EnvironmentType:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_TYPE }}</span>'</span>
        <span class="hljs-attr">tags:</span>
          <span class="hljs-attr">EnvironmentName:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_NAME }}</span>'</span>
          <span class="hljs-attr">EnvironmentType:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_TYPE }}</span>'</span>

  <span class="hljs-attr">EC2Instance:</span>
    <span class="hljs-attr">Order:</span> <span class="hljs-number">1</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Deploying</span> <span class="hljs-string">EC2</span> <span class="hljs-string">Instance</span>
    <span class="hljs-attr">Stacks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">template_file:</span> <span class="hljs-string">&lt;cfn-template-path&gt;</span>
        <span class="hljs-attr">stack_name:</span> <span class="hljs-string">stack-name2</span>
        <span class="hljs-attr">parameters:</span>
          <span class="hljs-attr">EnvironmentName:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_NAME }}</span>'</span>
          <span class="hljs-attr">EnvironmentType:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_TYPE }}</span>'</span>
        <span class="hljs-attr">tags:</span>
          <span class="hljs-attr">EnvironmentName:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_NAME }}</span>'</span>
          <span class="hljs-attr">EnvironmentType:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ .ENV_TYPE }}</span>'</span>
</code></pre>
<p>Please consult examples for quick start <a target="_blank" href="examples/ec2-sqs/Readme.md">ec2-sg example</a> and <a target="_blank" href="examples/demo/Readme.md">demo ec2-sqs-rds example</a></p>
<h2 id="heading-contributors">Contributors</h2>
<p><img src="https://contrib.rocks/image?repo=rbalman/cfn-compose" alt /></p>
<p>There exists ample opportunity for enhancement and you are welcome to make a valuable contribution. If you have any concerns, recommendations, ideas feel free to <a target="_blank" href="https://github.com/rbalman/cfn-compose/issues">create issues</a> or <a target="_blank" href="https://github.com/rbalman/cfn-compose/pulls">create PR</a>. <a target="_blank" href="https://github.com/rbalman/cfn-compose/tree/main/examples/demo">Details Example</a></p>
]]></content:encoded></item><item><title><![CDATA[Recursive deletion of CloudFormation Stacks]]></title><description><![CDATA[Context
There are particularly three actions that you can apply to your CloudFormation stacks.

CREATE

UPDATE

DELETE


This blog focuses more on the deletion side of the Stacks. Why would you want to have an advanced deletion mechanism in place:

W...]]></description><link>https://blog.balmanrawat.com.np/recursive-deletion-of-cloudformation-stacks</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/recursive-deletion-of-cloudformation-stacks</guid><category><![CDATA[cloudformation]]></category><category><![CDATA[#IaC]]></category><category><![CDATA[#purne]]></category><category><![CDATA[#destoryinfra]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Mon, 30 Jan 2023 05:15:56 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-context">Context</h2>
<p>There are particularly three actions that you can apply to your CloudFormation stacks.</p>
<ul>
<li><p><code>CREATE</code></p>
</li>
<li><p><code>UPDATE</code></p>
</li>
<li><p><code>DELETE</code></p>
</li>
</ul>
<p>This blog focuses more on the deletion side of the Stacks. Why would you want to have an advanced deletion mechanism in place:</p>
<ul>
<li><p>When you want to prune your dev/test environment repeatedly and start from scratch</p>
</li>
<li><p>When you want to save dollars by removing unnecessary resources</p>
</li>
<li><p>When you manage all your infrastructure from CloudFormation</p>
</li>
<li><p>When you want to experiment with a temporary environment</p>
</li>
<li><p>When you have more than 100s stacks and hard to delete the individual stacks because of hard dependencies between the stack resources.</p>
</li>
</ul>
<h2 id="heading-solution">Solution</h2>
<p>The solution to this problem is to use a certain form of automation that provides a mechanism to select the stacks to be deleted. Selection can be done using various parameters like stack name pattern, tags, and date time frame. Here I'd like to present you the simple script that lets you recursively delete based on the <strong>stack name pattern</strong>.</p>
<h2 id="heading-how-to">How to</h2>
<p><strong>NOTE:</strong> Please be extremely careful while using this in production.</p>
<p><strong>Syntax:</strong> <code>DELETE_PATTERN=&lt;pattern-string&gt; AWS_PROFILE=&lt;profile-name&gt; AWS_REGION=&lt;region|defaults to us-east-1&gt; ./&lt;path-to-script&gt;</code></p>
<p>Takes 3 inputs <code>DELETE_PATTERN</code>, <code>AWS_PROFILE</code> and <code>AWS_REGION</code><br />as an environment variable.</p>
<ul>
<li><strong>Download</strong>:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-comment">#Download</span>
wget https://gist.githubusercontent.com/rbalman/b607040ea1f41d2ebb3c318593fa5b1f/raw/6b69ccc94dbf50fe2c1de93080d99fa34b055b7f/cfn-delete.sh
chmod +x cfn-delete.sh
</code></pre>
<ul>
<li><p><strong>Execute</strong></p>
<p>  This will list all the name of all the stacks that are selected and prompt for approval. Then it will recursively delete all the stacks in sequential order, it will keep retrying until the stack count drops to zero.</p>
</li>
</ul>
<pre><code class="lang-bash">DELETE_PATTERN=dev- AWS_PROFILE=demo AWS_REGION=us-east-1 ./cfn-delete.sh
</code></pre>
<h2 id="heading-script-link">Script Link</h2>
<p><a target="_blank" href="https://gist.github.com/rbalman/b607040ea1f41d2ebb3c318593fa5b1f">https://gist.github.com/rbalman/b607040ea1f41d2ebb3c318593fa5b1f</a></p>
<h2 id="heading-caveats">Caveats</h2>
<p>Some resources may not get deleted due to the limitation of CloudFormation.</p>
<ul>
<li><p>bucket CloudFormation stack can't be deleted unless it is empty</p>
</li>
<li><p>resources with the <code>DeletionPolicy: Retain</code> will still be there.</p>
</li>
<li><p>Backups from the AWS Backup won't get deleted</p>
</li>
<li><p>Protected by the Stack Policy</p>
</li>
<li><p>Protected by the IAM policy</p>
</li>
</ul>
<p>In this case you need to remove the cause of the failure and retry the deletion.</p>
]]></content:encoded></item><item><title><![CDATA[Terraform S3 Backend]]></title><description><![CDATA[Hashicorp terraform supports multiple backends out of which S3 is one of the them. If you are an AWS customer and looking forward to stay within AWS boundary then s3 backend is the right choice for you. Find more details
S3 Backend
S3Backend supports...]]></description><link>https://blog.balmanrawat.com.np/terraform-s3-backend</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/terraform-s3-backend</guid><category><![CDATA[Terraform]]></category><category><![CDATA[hashicorp]]></category><category><![CDATA[Amazon S3]]></category><category><![CDATA[AWS]]></category><category><![CDATA[#IaC]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Fri, 09 Sep 2022 06:49:40 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662695634078/raKONz7EO.png" alt="terraform.png" /></p>
<p>Hashicorp terraform supports multiple backends out of which S3 is one of the them. If you are an AWS customer and looking forward to stay within AWS boundary then <code>s3 backend</code> is the right choice for you. Find more <a target="_blank" href="https://www.terraform.io/language/settings/backends/configuration">details</a></p>
<h2 id="heading-s3-backend">S3 Backend</h2>
<p>S3Backend supports</p>
<ul>
<li><code>State storage</code> with s3 bucket</li>
<li><code>History of state files</code> with s3 bucket versioning (recommended)</li>
<li><code>State locking</code> with Dynamodb table(recommended)</li>
</ul>
<h3 id="heading-setting-up-backend-infra">Setting Up Backend Infra</h3>
<p>We just need to create a S3 bucket and a dynamodb table with the configuration defined by the <a target="_blank" href="https://www.terraform.io/language/settings/backends/s3">s3backend</a>. You can create the resources by using the below commands or manually following the steps defined below:</p>
<pre><code class="lang-shell">    git clone git@github.com:BalmanRawat/terraform-s3backend.git
    cd terraform-s3backend
    make init
    ## update the variables.tf file if necessary
    make apply
</code></pre>
<p><strong>S3 Bucket Requirements</strong></p>
<ul>
<li>Any existing bucket or new one</li>
<li>Versioning enabled (recommended)</li>
<li>Encryption enabled (recommended)</li>
<li>IAM Policy required by terraform to make S3 bucket API calls</li>
</ul>
<pre><code>{
  <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>: [
    {
      <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"s3:ListBucket"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:s3:::backend-bucket"</span>
    },
    {
      <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Action"</span>: [<span class="hljs-string">"s3:GetObject"</span>, <span class="hljs-string">"s3:PutObject"</span>, <span class="hljs-string">"s3:DeleteObject"</span>],
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:s3:::backend-bucket/path/to/my/key"</span>
    }
  ]
}
</code></pre><p><strong>DynamoDB Table Requirements</strong></p>
<blockquote>
<p>DynamoDB table is optional but terraform will not be able to lock the state file.</p>
</blockquote>
<ul>
<li>The table must have a partition key named <code>LockID</code> with type of String</li>
<li>IAM Policy required by terraform to make DynamoDB API calls</li>
</ul>
<pre><code>{
  <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>: [
    {
      <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Action"</span>: [
        <span class="hljs-string">"dynamodb:GetItem"</span>,
        <span class="hljs-string">"dynamodb:PutItem"</span>,
        <span class="hljs-string">"dynamodb:DeleteItem"</span>
      ],
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:dynamodb:*:*:table/backend-table"</span>
    }
  ]
}
</code></pre><h3 id="heading-using-the-backend">Using the backend</h3>
<p>To make use of the backend we need to configure <code>backend</code> in the terraform settings.</p>
<p>Example configuration. Find all the possible configuration <a target="_blank" href="https://www.terraform.io/language/settings/backends/s3#s3-state-storage">here</a> </p>
<pre><code>terraform {
 backend <span class="hljs-string">"s3"</span> {
    bucket <span class="hljs-operator">=</span> <span class="hljs-string">"&lt;bucket-name&gt;"</span>
    key <span class="hljs-operator">=</span> <span class="hljs-string">"&lt;bucket-key-for-terraform-state-file"</span>
    region <span class="hljs-operator">=</span> <span class="hljs-string">"&lt;aws-region&gt;"</span>
    dynamodb_table <span class="hljs-operator">=</span> <span class="hljs-string">"&lt;dynamodb-table&gt;"</span>
  }
}
</code></pre><p><strong>OR</strong></p>
<p>make use of the examples in the repository.</p>
<pre><code class="lang-shell">    git clone git@github.com:BalmanRawat/terraform-s3backend.git
    cd terraform-s3backend/examples
    make init
    ## replace the bucket-name, key, region, dynamodb_table with your bucket
    make apply
</code></pre>
<p>Once we apply the change we should be able to see similar changes in the bucket and table as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662704686740/OTL9fZSi8.png" alt="bucket-image" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662704824331/07u2t1Fph.png" alt="dynamodb-table" /></p>
<p>All done. Remember to run <code>terraform destory</code> once you are done with the experiment.</p>
<p>-&gt; until next time.</p>
]]></content:encoded></item><item><title><![CDATA[CloudFormation in 15 minutes]]></title><description><![CDATA[Bit of a History
During the early stage of computing IT Infrastructures were completely physical and they were managed completely physically by the experts. Then slowly with the rapid growth of high performance computing hardwares, virtualization and...]]></description><link>https://blog.balmanrawat.com.np/cloudformation-in-a-nutshell</link><guid isPermaLink="true">https://blog.balmanrawat.com.np/cloudformation-in-a-nutshell</guid><category><![CDATA[Cloud Computing]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[infrastructure]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Balman Rawat]]></dc:creator><pubDate>Sun, 19 Sep 2021 07:20:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1632036620956/U0FZ-1HZT.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-bit-of-a-history">Bit of a History</h2>
<p>During the early stage of computing IT Infrastructures were completely physical and they were managed completely physically by the experts. Then slowly with the rapid growth of high performance computing hardwares, virtualization and Internet raised the adoption of virtualization. These virtulized Infra were  managed with various scripting languages.</p>
<p>Then came the era of the Cloud, driving the innovation entirely to the next level where everything possible was virtualized be it Servers, Queues, Storage, Databases, Networks, Caches, CDNs, Firewalls and more.</p>
<p>All these infrastructure management were abstracted with a simple API call, for eg. We can create a bucket using this   <a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html">create bucket API</a>.  The challenges with the APIs are that we need to deal with low level details like headers, authentication information, retries, dependencies, payloads, failure handling, logging, state management and many more.</p>
<p>Then comes the IAC where all these low level complexities are orchestrated through the human friendly abstractions.</p>
<h2 id="heading-infrastructure-as-a-code">Infrastructure as a Code</h2>
<p>Infrastructure as a code abbreviated as IAC is the management of IT Infrastructures like Servers, Networks, Storage, Databases, Queues either in the Cloud or the on-prem data centers through the use of some coding pattern or some form of templating language. IAC is basically a representation of Infrastructure in the form of a Code/template such that it can completely go through a similar SDLC life cycle. IAC can be the part of a repository, reviewed similar to the code-base and be the part of the CI/CD pipeline to build and deploy it through automation.</p>
<h2 id="heading-cloudformation">CloudFormation</h2>
<p><em>Verbatim:</em> AWS CloudFormation gives you an easy way to model a collection of related AWS and third-party resources, provision them quickly and consistently, and manage them throughout their lifecycles, by treating infrastructure as code. </p>
<p>Beauty of CloudFormation is that it is not a tool unlike other IACs like terraform rather it is a AWS Service which provides IAC solution, which means no additional tool required to be configured. All we need is the permission to use the CloudFormation Service, then use AWS CLI or AWS Console to do rest of the operations.</p>
<h2 id="heading-working">Working</h2>
<p>The working of the CloudFormation can be explained pretty well with the simple architecture diagram. First you will need to create a template where you will declare all the resources you want to create in a .json or .yaml file. Then either use the AWS SDK, AWS CLI or the AWS Console to provide your template to the CloudFormation Service and then CloudFormation takes care of provisioning and configuring the resources for you.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631847124741/lUfFWsGEi.png" alt="Screen Shot 2021-09-17 at 08.37.00.png" /></p>
<h2 id="heading-hello-world">Hello World</h2>
<p>Now let’s create a S3 Bucket using CloudFormation. We will use the template below and provide it to the CloudFormation Service to provision the S3 Bucket. The template can be provided using <code>AWS CLI</code> or through <code>AWS Console</code> as below.</p>
<p><strong>Sample Template</strong>
Please create a file name s3.yaml and add the following content.</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">Resources:</span>
 <span class="hljs-attr">S3Bucket:</span>
   <span class="hljs-attr">Type:</span> <span class="hljs-string">'AWS::S3::Bucket'</span>
   <span class="hljs-attr">Properties:</span>
     <span class="hljs-attr">BucketName:</span> <span class="hljs-string">'hello-world-demo-bucket-balman'</span>
</code></pre>
<p><strong>Deploy with AWS CLI</strong>
Fire the command below to create the stack.</p>
<pre><code class="lang-shell">cd &lt;template-directory&gt;
aws cloudformation create-stack --stack-name hello-world-stack --template-body file://s3.yaml --profile sandbox --region sandbox

{
"StackId":"arn:aws:cloudformation:us-east-1:856960422202:stack/hello-world-stack/ae626820-1504-11ec-86e1-0e5ae768be0f"
}
</code></pre>
<p><strong>OR Use AWS Console</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631847640730/XyTVe7Xv2.gif" alt="hello-world-cfn.gif" /></p>
<h2 id="heading-anatomy-of-hello-world-template">Anatomy of Hello World Template</h2>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># This section defines the resources</span>
<span class="hljs-attr">Resources:</span>
<span class="hljs-comment"># Your preferred name for this resource. This is called </span>
<span class="hljs-comment"># logical Id and should be unique within the template.</span>
 <span class="hljs-attr">MyS3Bucket:</span>
<span class="hljs-comment"># Resource type of the resource you want to create. eg. </span>
<span class="hljs-comment"># AWS::EC2::SecurityGroup is the type for creating security group.</span>
   <span class="hljs-attr">Type:</span> <span class="hljs-string">'AWS::S3::Bucket'</span>
<span class="hljs-comment"># Set of properties that is supported by this particular service</span>
   <span class="hljs-attr">Properties:</span>
     <span class="hljs-attr">BucketName:</span> <span class="hljs-string">'hello-world-demo-bucket-balman'</span>
</code></pre>
<h2 id="heading-cloudformation-template">CloudFormation Template</h2>
<p>CloudFormation template is a plain text file where all the required resources and their configuration are written in the pre-defined format. CloudFormation template supports both .yml and .json. I prefer to use yaml because it is more human readable.</p>
<h4 id="heading-structure">Structure</h4>
<p>CloudFormation template can only have sections that are mentioned below. They can be in any order but regarding the convention and readability following ordering is done usually. Please find the respective description of each section below.</p>
<p><strong>Note:</strong> Resources is the only mandatory section of template and you should define at least one resource to create.</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">AWSTemplateFormatVersion:</span> <span class="hljs-string">"AWS Template versions. If not provided the latest version will be used"</span>

<span class="hljs-attr">Description:</span> <span class="hljs-string">"Description of the template"</span>

<span class="hljs-attr">Metadata:</span> <span class="hljs-string">"Collection of metadata objects that provides additional information about the template."</span>

<span class="hljs-attr">Parameters:</span> <span class="hljs-string">"Values that can be passed at runtime to change the behavior of the template. It is similar to passing argument to a function"</span>

<span class="hljs-attr">Rules:</span> <span class="hljs-string">"Validates a parameter or a combination of parameters passed to a template during a stack creation or stack update."</span>

<span class="hljs-attr">Mappings:</span> <span class="hljs-string">"A collection of static keys and values that can be used to get values based on the conditions. eg. when you want to use different AMIs for different regions you can use region and key and AmiId as value"</span>

<span class="hljs-attr">Conditions:</span> <span class="hljs-string">"List of conditions that can be used to create certain resources based on the conditions. eg. you might not want to create all resources in your non production environment"</span>

<span class="hljs-attr">Transform:</span> <span class="hljs-string">"This is a more advanced topic of CloudFormation where CloudFormation templates can be transformed from one form to another form. eg. SAM template, AWS::Include can dynamically include the CloudFormation template snippets hosted in your s3 bucket"</span>

<span class="hljs-attr">Resources:</span> <span class="hljs-string">"This is where you define all of your resources and their configurations."</span>

<span class="hljs-attr">Outputs:</span> <span class="hljs-string">"List of certain attributes of the resource that you want to expose for eg. bucket-name for s3 bucket resource. Outputs can also be exported which can be imported in another stack for use. eg. export helps when you could have a VPC stack and another ec2 instance stack needs the VPCId"</span>
</code></pre>
<h2 id="heading-cloudformation-stack">CloudFormation Stack</h2>
<p>During the resource creation process CloudFormation creates a separate resource called CloudFormation stack which bundles all the created resources and preserve their state, so that they can be managed later.</p>
<p><em>Analogy:</em> CloudFormation template is like a Class whereas the CloudFormation Stack is like an object where you can create as many stacks as you want from a single template.</p>
<h2 id="heading-authoring-template-from-scratch">Authoring Template from Scratch</h2>
<h4 id="heading-1-know-the-resource">1. Know the Resource</h4>
<p>The very first step is to have knowledge about the service or the resource you want to create. You should know the various attributes of the resources and know what happens when these attributes are set to default or overridden by certain values. For eg. you should be able to know what does these attributes <code>instance_type</code>, <code>ami_id</code> mean in context to creating EC2 Instance.</p>
<h4 id="heading-2-find-the-documentation">2. Find the documentation</h4>
<p>All the resources supported by the CloudFormation are documented in the AWS CloudFormation docs, see the the list of <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html">supported resources</a>. Every CloudFormation has the same format as described below(<a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html">Sample docs</a>)</p>
<p><strong>Syntax:</strong></p>
<p>This section provides the syntax to define particular resource in the template. Syntax must have Type(what resource to create) and its Properties(how to create). </p>
<pre><code class="lang-yaml"><span class="hljs-attr">Type:</span> <span class="hljs-string">&lt;AWS</span> <span class="hljs-string">Resource</span> <span class="hljs-string">Type&gt;</span>
<span class="hljs-attr">Properties:</span>
   <span class="hljs-attr">Key:</span> <span class="hljs-string">Value</span>
</code></pre>
<p><strong>Properties :</strong> </p>
<p>This section contains the details of individual property.</p>
<ul>
<li>Description: Describes the essence of the property</li>
<li>Required: Shows whether property is required or not</li>
<li>Type: data type of the property. eg. string, number, list</li>
<li>Update requires: Specifies behavior when this property is updated. Behavior can be <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt">No Interruptions</a>,  <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-some-interrupt">Some interruptions</a>,  <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement">Replacement</a></li>
</ul>
<p><strong>Return Values :</strong> </p>
<p>Shows what value is return when referenced to the resource. It is usually name, Id, Arn of the resource.</p>
<p><strong>Examples :</strong></p>
<p>This section shows the sample examples of the given resource and can be helpful to get started with the template.</p>
<h4 id="heading-3-create-the-template-file">3. Create the template file</h4>
<p>Let's create a minimal template to create an EC2 Instance and save it as <code>ec2.yaml</code>. I have only specified <code>ImageId</code> and <code>SubnetId</code> which are required to launch the Instance.</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">Resources:</span>
    <span class="hljs-attr">MyEC2Instance:</span> 
      <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::Instance</span>
      <span class="hljs-attr">Properties:</span>
        <span class="hljs-attr">ImageId:</span> <span class="hljs-string">ami-09e67e426f25ce0d7</span> <span class="hljs-comment"># replace with your own values</span>
        <span class="hljs-attr">SubnetId:</span> <span class="hljs-string">subnet-066a074e239e6a0a3</span> <span class="hljs-comment"># replace with your own values</span>
</code></pre>
<h4 id="heading-4-create-stack">4. Create Stack</h4>
<p>AWS CLI</p>
<pre><code class="lang-shell">aws cloudformation create-stack --stack-name 'balman-ec2-instance' --template-body file://ec2.yaml --profile sandbox --region us-east-1
</code></pre>
<p>Once the stack has been created successfully you should able to see it in CloudFormation dashboard. Also in the EC2 Instances list you will be able to see the newly created EC2 Instance.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632020222099/LPDHicXk-.png" alt="CloudFormation Dashboard" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632020313494/_-uloppzp.png" alt="EC2 Instance Dashboard" /></p>
<h4 id="heading-5-managing-stack-aws-cli">5. Managing Stack: AWS CLI</h4>
<p><strong>create-stack</strong></p>
<pre><code class="lang-shell">aws cloudformation create-stack --stack-name &lt;stack-name&gt; --template-body file://&lt;file-path&gt;
</code></pre>
<p><strong>update-stack</strong></p>
<pre><code class="lang-shell">aws cloudformation update-stack --stack-name &lt;stack-name&gt; --template-body file://&lt;file-path&gt;
</code></pre>
<p><strong>delete-stack</strong></p>
<pre><code class="lang-shell">aws cloudformation delete-stack --stack-name &lt;stack-name&gt; --template-body file://&lt;file-path&gt;
</code></pre>
<p><strong>wait-stack</strong>
Wait commands are mostly useful when you want create your own scripts that needs to wait for certain conditions to happen.</p>
<pre><code class="lang-shell">aws cloudformation wait (stack-create-complete OR stack-delete-complete OR stack-update-complete) --stack-name &lt;stack-name&gt;
</code></pre>
<p><strong>create-change-set</strong>
Creating change-set is equivalent to creating a <code>dry-run</code> before executing the actual change to the running resources. This is very important when you are working with production resources.</p>
<pre><code class="lang-shell">aws cloudformation create-change-set --change-set-name &lt;your-change-set-name&gt; --stack-name '&lt;stack-name&gt;' --template-body file://&lt;file-path&gt;
</code></pre>
<p>To look for the newly created change set go through <code>Change sets</code> &gt; <code>Change-set</code> . 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632034777040/WA0FmjTve.png" alt="Create Change Set" /></p>
<p><strong>execute-change-set</strong>
Change set can be executed from AWS Console or AWS CLI to apply the change.</p>
<pre><code class="lang-shell">aws cloudformation create-change-set --change-set-name &lt;your-change-set-name&gt; --stack-name '&lt;stack-name&gt;' --template-body file://&lt;file-path&gt;
</code></pre>
<h4 id="heading-6-managing-stack-aws-console">6. Managing Stack: AWS Console</h4>
<p>One of the beauty of the AWS CloudFormation is that it provides super friendly UI to manage and see all the necessary information. Following diagram shows high level overview of what can be seen/done from the Console.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632022069224/RA9ChVSP-.png" alt="Managing Stacks from AWS Console" /></p>
<h2 id="heading-other-concepts">Other Concepts</h2>
<h4 id="heading-1-parameters">1. Parameters</h4>
<p>Parameters are like the variables of the template, they can be modified at runtime to change the attributes without needing to create a separate template. They are defined under the <code>Parameters:</code> section of the template. Parameter gives us various options to customize them to build reliable template like: </p>
<ul>
<li>Allowed Pattern(where only string with specific regex are allowed.</li>
<li>Max Length, Min Length to allow input string length within the range.</li>
<li>Min Value, Max Value to allow Numeric value within the range.</li>
<li>Date type to allow only specific data types.</li>
<li>Default values to use when no values are provided.</li>
<li>Noecho option for secrets values that shouldn't be shown in the dashboard.
For more details please find  <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html">the AWS Docs</a> .</li>
</ul>
<p><strong>Syntax:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Parameters:</span>
  <span class="hljs-attr">ParameterLogicalID:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">DataType</span>
    <span class="hljs-attr">ParameterProperty:</span> <span class="hljs-string">value</span>
</code></pre>
<p><strong>Example Template</strong>
Now Let's evolve our previous <code>ec2.yaml</code> template to use the parameters.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Parameters:</span>
  <span class="hljs-attr">MyInstanceType:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
    <span class="hljs-attr">Default:</span> <span class="hljs-string">t2.micro</span>
    <span class="hljs-attr">AllowedValues:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">t2.micro</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">m1.small</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">t2.small</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Enter</span> <span class="hljs-string">t2.micro,</span> <span class="hljs-string">m1.small,</span> <span class="hljs-string">or</span> <span class="hljs-string">t2.small.</span> <span class="hljs-string">Default</span> <span class="hljs-string">is</span> <span class="hljs-string">t2.micro.</span>
<span class="hljs-attr">Resources:</span>
  <span class="hljs-attr">MyEC2Instance:</span> 
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::Instance</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">InstanceType:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">MyInstanceType</span>
      <span class="hljs-attr">ImageId:</span> <span class="hljs-string">ami-09e67e426f25ce0d7</span>
      <span class="hljs-attr">SubnetId:</span> <span class="hljs-string">subnet-066a074e239e6a0a3</span>
</code></pre>
<p>If you do not specify parameters it will take the default value, but if you want other than default value you can pass the parameters while update/create operation. There are two ways you can specify parameters as show below:</p>
<p>a. Inline</p>
<pre><code class="lang-shell">aws cloudformation update-stack --stack-name 'balman-ec2-instance' --template-body file://ec2.yaml --parameters ParameterKey=MyInstanceType,ParameterValue=t2.small --profile sandbox --region us-east-1
</code></pre>
<p>b. Using parameters file</p>
<p>create a parameter file and save as .json.</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"ParameterKey"</span>: <span class="hljs-string">"MyInstanceType"</span>,
    <span class="hljs-attr">"ParameterValue"</span>: <span class="hljs-string">"t2.small"</span>
  }
]
</code></pre>
<p>update stack</p>
<pre><code class="lang-shell">aws cloudformation update-stack --stack-name 'balman-ec2-instance' --template-body file://ec2.yaml --parameters file://params.json --profile sandbox --region us-east-1
</code></pre>
<p>Any of above update will update the parameter and update the respective stack as shown below
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632024407661/7MOWjnW3k.png" alt="Update Stack With Parameters" /></p>
<h4 id="heading-2-pseudo-parameters">2. Pseudo Parameters</h4>
<p>Pseudo Parameters are built-in parameters which can be used similar to the user defined parameters. It can be accessed using <code>Ref</code> function. eg. <code>AWS::Region</code>, <code>AWS::AccountId</code>. Please find more details in  <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html">this AWS docs</a> .</p>
<h4 id="heading-3-built-in-functions">3. Built-in Functions</h4>
<p>CloudFormation provides us various kinds of built-in functions that we can use to make our template dynamic. Functions can be used for various kinds of cases like joining, splitting the strings, getting values from the parameters, Importing outputs from another stack, Enforcing certain conditions and many more. Please find more details in <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html">this AWS Docs</a>.</p>
<p>eg. FinInMap function can be used to find the specific values from the map created in the <code>Mappings</code> section.</p>
<p><strong>syntax:</strong>
!FindInMap [ MapName, TopLevelKey, SecondLevelKey ]</p>
<p><strong>sample</strong>
This template supports multi-region creation of the Ec2 Instance because the AMIId will be selected by the <code>FindInMap</code> functions dynamically based on the region.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Parameters:</span>
  <span class="hljs-attr">MyInstanceType:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
    <span class="hljs-attr">Default:</span> <span class="hljs-string">t2.micro</span>
    <span class="hljs-attr">AllowedValues:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">t2.micro</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">m1.small</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">t2.small</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Enter</span> <span class="hljs-string">t2.micro,</span> <span class="hljs-string">m1.small,</span> <span class="hljs-string">or</span> <span class="hljs-string">t2.small.</span> <span class="hljs-string">Default</span> <span class="hljs-string">is</span> <span class="hljs-string">t2.micro.</span>
  <span class="hljs-attr">AMITYpe:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
    <span class="hljs-attr">Default:</span> <span class="hljs-string">Ubuntu</span>
    <span class="hljs-attr">AllowedValues:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Ubuntu</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">AmazonLinux</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Enter</span> <span class="hljs-string">Ubuntu</span> <span class="hljs-string">or</span> <span class="hljs-string">AmazonLinux.</span> <span class="hljs-string">Default</span> <span class="hljs-string">is</span> <span class="hljs-string">Ubuntu.</span>
<span class="hljs-attr">Mappings:</span>
  <span class="hljs-attr">AMIIdMap:</span> <span class="hljs-comment">#Use the latest values</span>
    <span class="hljs-attr">"us-east-1":</span>
      <span class="hljs-attr">"Ubuntu":</span> <span class="hljs-string">"ami-09e67e426f25ce0d7"</span>
      <span class="hljs-attr">"AmazonLinux":</span> <span class="hljs-string">"ami-087c17d1fe0178315"</span>
    <span class="hljs-attr">"us-east-1":</span>
      <span class="hljs-attr">"Ubuntu":</span> <span class="hljs-string">"ami-097297e426f25ce0d7"</span>
      <span class="hljs-attr">"AmazonLinux":</span> <span class="hljs-string">"ami-a7297e426f25ce0d7"</span>
    <span class="hljs-attr">"us-east-1":</span>
      <span class="hljs-attr">"Ubuntu":</span> <span class="hljs-string">"ami-097297e426f25820d7"</span>
      <span class="hljs-attr">"AmazonLinux":</span> <span class="hljs-string">"ami-397297e426f25820d7"</span>
<span class="hljs-attr">Resources:</span>
  <span class="hljs-attr">MyEC2Instance:</span> 
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::Instance</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">InstanceType:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">MyInstanceType</span>
      <span class="hljs-attr">ImageId:</span> <span class="hljs-type">!FindInMap</span> [ <span class="hljs-string">AMIIdMap</span>, <span class="hljs-type">!Ref</span> <span class="hljs-string">'AWS::Region'</span>, <span class="hljs-type">!Ref</span> <span class="hljs-string">AMITYpe</span> ]
      <span class="hljs-attr">SubnetId:</span> <span class="hljs-string">subnet-066a074e239e6a0a3</span>
</code></pre>
<h4 id="heading-4-conditions">4. Conditions</h4>
<p>Conditions are used to specify conditions in templates to define certain properties or resource only when conditions are met. eg. you might want to use different instance type for different environments or even want to prevent certain resource from provisioning. Please find more details in  <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html">this AWS docs</a>.</p>
<p><strong>sample</strong>
Template below creates condition called <code>IsProduction</code> and use it to change instance type based on the <code>EnvType</code> value and provision <code>ProdSecurityGroup</code> resource only when the <code>EnvType</code> is production.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Parameters:</span>
  <span class="hljs-attr">EnvType:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
    <span class="hljs-attr">Default:</span> <span class="hljs-string">nonproduction</span>
    <span class="hljs-attr">AllowedValues:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">nonproduction</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">production</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Enter</span> <span class="hljs-string">production,</span> <span class="hljs-string">nonproduction.</span> <span class="hljs-string">Default</span> <span class="hljs-string">is</span> <span class="hljs-string">nonproduction.</span>
<span class="hljs-attr">Conditions:</span>
  <span class="hljs-attr">IsProduction:</span> <span class="hljs-type">!Equals</span> 
    <span class="hljs-bullet">-</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">EnvType</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">production</span>
<span class="hljs-attr">Resources:</span>
  <span class="hljs-attr">MyEC2Instance:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::Instance</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">InstanceType:</span> <span class="hljs-type">!If</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">IsProduction</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">'t2.micro'</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">'t2.small'</span>
      <span class="hljs-attr">ImageId:</span> <span class="hljs-string">ami-09e67e426f25ce0d7</span>
      <span class="hljs-attr">SubnetId:</span> <span class="hljs-string">subnet-066a074e239e6a0a3</span>
  <span class="hljs-attr">ProdSecurityGroup:</span>
    <span class="hljs-attr">Condition:</span> <span class="hljs-string">IsProduction</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::SecurityGroup</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">GroupDescription:</span> <span class="hljs-string">create</span> <span class="hljs-string">sg</span> <span class="hljs-string">only</span> <span class="hljs-string">if</span> <span class="hljs-string">the</span> <span class="hljs-string">env</span> <span class="hljs-string">type</span> <span class="hljs-string">is</span> <span class="hljs-string">production</span>
      <span class="hljs-attr">SecurityGroupEgress:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">CidrIp:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">/32</span>
        <span class="hljs-attr">IpProtocol:</span> <span class="hljs-string">"-1"</span>
      <span class="hljs-attr">VpcId:</span> <span class="hljs-string">vpc-0be6d92acf7a792d4</span>
</code></pre>
<h4 id="heading-5-outputs">5. Outputs</h4>
<p>Outputs in the CloudFormation can be used to show or exchange important resources attributes created by the template like resource Id, Name, Arn across different stacks. For eg. you might have a separate template that creates VPC and another template that creates EC2 Instance/RDS. In that case you will have to <code>export</code> the VPCId from the VPC template and <code>Import</code> VPCId into EC2Instance template. Please find more details in  <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html">this AWS docs</a> .</p>
<p><strong>Syntax:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Outputs:</span>
  <span class="hljs-attr">Logical ID:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Information</span> <span class="hljs-string">about</span> <span class="hljs-string">the</span> <span class="hljs-string">value</span>
    <span class="hljs-attr">Value:</span> <span class="hljs-string">Value</span> <span class="hljs-string">to</span> <span class="hljs-string">return</span>
    <span class="hljs-attr">Export:</span>
      <span class="hljs-attr">Name:</span> <span class="hljs-string">Value</span> <span class="hljs-string">to</span> <span class="hljs-string">export</span>
</code></pre>
<p><strong>Example</strong>
For the sake of simplicity I have two templates one creates security group and exports its Id and the other template Imports the Id and use it in the EC2 Instance.</p>
<p><code>sg-outputs.yaml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Resources:</span> 
  <span class="hljs-attr">ProdSecurityGroup:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::SecurityGroup</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">GroupDescription:</span> <span class="hljs-string">create</span> <span class="hljs-string">sg</span> <span class="hljs-string">only</span> <span class="hljs-string">if</span> <span class="hljs-string">the</span> <span class="hljs-string">env</span> <span class="hljs-string">type</span> <span class="hljs-string">is</span> <span class="hljs-string">production</span>
      <span class="hljs-attr">SecurityGroupEgress:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">CidrIp:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">/32</span>
        <span class="hljs-attr">IpProtocol:</span> <span class="hljs-string">"-1"</span>
      <span class="hljs-attr">VpcId:</span> <span class="hljs-string">vpc-0be6d92acf7a792d4</span>

<span class="hljs-attr">Outputs:</span>
  <span class="hljs-attr">SecurityGroupId:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Id</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">SecurityGroup</span>
    <span class="hljs-attr">Value:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">ProdSecurityGroup</span> <span class="hljs-comment">#This will get Id of the Security Group. Please look at Return values section to find more details.</span>
    <span class="hljs-attr">Export:</span>
      <span class="hljs-attr">Name:</span> <span class="hljs-string">demoSecurityGroupId</span>
</code></pre>
<p>outputs section
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632029937120/CC4RZmUwG.png" alt="Outputs being exported" /></p>
<p>Now let's use the template below to import the security group id
<code>ec2-outputs.yaml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Resources:</span>
  <span class="hljs-attr">MyEC2Instance:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::Instance</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">InstanceType:</span> <span class="hljs-string">'t2.micro'</span>
      <span class="hljs-attr">ImageId:</span> <span class="hljs-string">ami-09e67e426f25ce0d7</span>
      <span class="hljs-attr">SubnetId:</span> <span class="hljs-string">subnet-066a074e239e6a0a3</span>
      <span class="hljs-attr">SecurityGroupIds:</span> 
        <span class="hljs-bullet">-</span> <span class="hljs-type">!ImportValue</span> <span class="hljs-string">demoSecurityGroupId</span>
</code></pre>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>This is no way the exhaustive details of the CloudFormation but a mere high level overview of various components that are needed to get anyone started with it. There are other advanced topics that are completely left untouched in this blog: nested CloudFormation, custom CloudFormation, CloudFormation modules, Failure Handling, CloudFormation Registry, Transform, Macros which can be resources for other blogs. Please find the sample CloudFormation files in  <a target="_blank" href="https://github.com/BalmanRawat/blog-cloudformation">this Github Repo</a>.</p>
<p>Any comments, feedback are welcome. Thank You.</p>
]]></content:encoded></item></channel></rss>