<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>deployments on Digi Hunch</title><link>https://static.digihunch.com/tag/deployments/</link><description>Recent content in deployments on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 02 Apr 2025 13:59:03 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/deployments/index.xml" rel="self" type="application/rss+xml"/><item><title>FluxCD: Continuous Deployment with GitOps</title><link>https://static.digihunch.com/2022/01/fluxcd-continuous-deployment-with-gitops/</link><pubDate>Sat, 15 Jan 2022 18:49:00 -0400</pubDate><guid>https://static.digihunch.com/2022/01/fluxcd-continuous-deployment-with-gitops/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-flux-pipeline.webp" alt="Featured image of post FluxCD: Continuous Deployment with GitOps" /&gt;&lt;p class="wp-block-paragraph"&gt;This post explains why I land on FluxCD GitOps for my project. Let&amp;#8217;s star&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-background"&gt;Background&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the &lt;a href="https://github.com/digihunch/korthweb"&gt;Korthweb&lt;/a&gt; project, I landed on Istio for the &lt;a href="https://static.digihunch.com/2021/12/from-ingress-to-gateway-why-you-need-istio-gateways-on-kubernetes-platforms/"&gt;Ingress Gateway&lt;/a&gt; technology. I first attempted to expand the &lt;a href="https://github.com/digihunch/korthweb/tree/main/helm"&gt;orthanc&lt;/a&gt; Helm Chart to bring Istio as dependency (sub-chart). One of the external chart for Istio gateway needs to be referenced multiple times (for ingress and egress). However, it cannot even be used as dependency (sub-chart) because of &lt;a href="https://github.com/istio/istio/issues/35495#issuecomment-1007197188"&gt;this&lt;/a&gt; issue. Istio didn&amp;#8217;t re-introduce Helm Chart as a supported deployment until September 2021. So I&amp;#8217;m not too confident about it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This leads to a second thought of the &amp;#8220;Big Helm Chart&amp;#8221; approach to deploy all tiers in Korthweb. Helm is based on templating, and having two layers of charts brings complexity. I do need to bring a number of Helm charts together, but not necessarily by another Helm Chart. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Kubernetes documentation also mentions &lt;a href="https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/"&gt;Kustomize&lt;/a&gt; as an alternative to Helm. Below is a quick exploration of it.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-kustomize"&gt;Kustomize&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A challenge with managing declarative object is to organize numerous manifest files to maintain consistency and readability. &lt;a href="https://github.com/kubernetes-sigs/kustomize"&gt;Kustomize&lt;/a&gt; is a standalone tool to customize Kubernetes objects through a &lt;a href="https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kustomization"&gt;Kustomization&lt;/a&gt; file. This &lt;a href="https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/"&gt;page&lt;/a&gt; provides a good list of fields that can be used:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;vars and replacements: copy fields from one source into any number of specified targets.&lt;/li&gt;&#10;&lt;li&gt;namePrefix, namespace, nameSuffix: customize namespace and names.&lt;/li&gt;&#10;&lt;li&gt;configMapGenerator, secretGenerator, and generatorOptions: create configuration entries from literal, files, or environment variables.&lt;/li&gt;&#10;&lt;li&gt;resources: indicates another kustomization directory (e.g. as base)&lt;/li&gt;&#10;&lt;li&gt;patches (also called overlays) add or override fields on resources.&lt;/li&gt;&#10;&lt;li&gt;patchesStrategicMerge: modifies values in known (loaded) resources&lt;/li&gt;&#10;&lt;li&gt;images: modifies the name, tags and/or digest for images, without creating patches.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/openapi/"&gt;openapi&lt;/a&gt;: use Kubernetes OpenAPI data to get merge key and patch strategy information about resource types.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The base -&amp;gt; overlay pattern ensures readability as well as portability. Typical use pattern is create one base kustomization, and several overlay kustomizations each representing an environment, such as dev, qa and production. &lt;a href="https://www.youtube.com/watch?v=btqZkVQIdd8"&gt;Here&lt;/a&gt; is a good tutorial. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Helm and Kustomize are two approaches to deploy Kubernetes workload. Both tackle the challenge of managing many YAML declarations. &lt;span style="text-decoration: underline;"&gt;Helm is template driven, and is commonly used by application developers as a means of packaging application releases while orchestrating the dependencies. Kustomize follows a base-overlay pattern, and is more commonly used by cluster operators for re-using manifests across multiple environments (e.g. dev, staging and prod)&lt;/span&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my use case, my deployment needs both. I need third party Helm chart to configure components such as PostgreSQL for HA. I also need Kustomize for the orthanc workload. With the need for both, there should be a higher level deployment technology that ingrate with both mechanisms.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-gitops"&gt;GitOps&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;GitOps is originally brought up by Weaveworks in 2017. It is a methodology to deploy workload continuously, using a Git repository as source of truth. The point of GitOps is not about tooling, or specific platform, but rather to ensure the workload state matches the declaration in repository. For example, you can implement &lt;a href="https://www.redhat.com/sysadmin/ansible-webhooks-gitops"&gt;GitOps with Ansible&lt;/a&gt; for VM environment.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to managing Kubernetes workload, a GitOps tool must handle the challenge with managing many YAML declarations. Therefore most GitOps tools seek to support many well-adopted mechanisms such as Helm and Kustomize as discussed above, instead of simply taking an enormous amount of raw YAML declarations. The amount of deployment mechanisms supported, is a key indicator of how powerful a GitOps tool is. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most notable tools are ArgoCD and FluxCD. Both are currently CNCF incubating projects. ArgoCD is powerful with many &lt;a href="https://argo-cd.readthedocs.io/en/stable/user-guide/application_sources/"&gt;tools&lt;/a&gt; supported, such as Kustomize, Helm, Ksonnet, Jsonnet, etc. It also contains a user interface and aims to manage an entire deployment workflow. On the other hand, FluxCD has controllers mainly for &lt;a href="https://fluxcd.io/docs/components/kustomize/"&gt;Kustomize&lt;/a&gt; and &lt;a href="https://fluxcd.io/docs/components/helm/"&gt;Helm&lt;/a&gt;, with a jsonnet &lt;a href="https://fluxcd.io/integrations/#flux-extensions"&gt;extension&lt;/a&gt; from third party. This &lt;a href="https://blog.container-solutions.com/fluxcd-argocd-jenkins-x-gitops-tools"&gt;post&lt;/a&gt; draws a comparison of them (along with JenkinsX) based on earlier versions (from mid 2020). There is even a standard (&lt;a href="https://opengitops.dev/"&gt;OpenGitOps&lt;/a&gt;) in &lt;a href="https://www.cncf.io/projects/opengitops/"&gt;CNCF&lt;/a&gt; landscape but still at Sandbox level.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For Korthweb project, I chose FluxCD. It is simple, and provides just enough types of controller for what I do.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-fluxcd"&gt;FluxCD&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram below illustrates the components:&lt;/p&gt;&#10;&lt;p class="has-white-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="501px" viewBox="-0.5 -0.5 501 321" style="max-width:100%;max-height:321px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="240" height="320" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;image x="9.5" y="9.5" width="40" height="40" xlink:href="https://cdn4.iconfinder.com/data/icons/socialcones/508/Github-128.png" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 30px; margin-left: 52px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Git Repository&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="52" y="34" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Git Rep&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="0" width="220" height="320" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;image x="449.5" y="9.5" width="41.67" height="40" xlink:href="https://app.diagrams.net/img/lib/mscae/Kubernetes.svg"&gt;&lt;/image&gt;&lt;rect x="61" y="50" width="144" height="42" rx="6.3" ry="6.3" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;image x="69.5" y="53.5" width="34" height="34" xlink:href="https://cdn0.iconfinder.com/data/icons/octicons/1024/file-directory-128.png" preserveAspectRatio="none" pointer-events="none"&gt;&lt;/image&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 71px; margin-left: 106px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;flux-system&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="106" y="75" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;flux-s&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="60" y="160" width="140" height="40" rx="6" ry="6" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;image x="69.5" y="165.5" width="34" height="34" xlink:href="https://cdn0.iconfinder.com/data/icons/octicons/1024/file-directory-128.png" preserveAspectRatio="none" pointer-events="none"&gt;&lt;/image&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 183px; margin-left: 106px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Kustomize:&lt;br&gt;infrastructure&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="106" y="187" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Kustom&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 133 92 L 133 103.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 133 108.88 L 129.5 101.88 L 133 103.63 L 136.5 101.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 60 130 L 40 130 L 40 180 L 53.63 180" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 58.88 180 L 51.88 183.5 L 53.63 180 L 51.88 176.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 60 130 L 40 130 L 40 280 L 53.63 280" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 58.88 280 L 51.88 283.5 L 53.63 280 L 51.88 276.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="60" y="210" width="140" height="40" rx="6" ry="6" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;image x="69.5" y="215.5" width="34" height="34" xlink:href="https://cdn0.iconfinder.com/data/icons/octicons/1024/file-directory-128.png" preserveAspectRatio="none" pointer-events="none"&gt;&lt;/image&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 233px; margin-left: 106px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Kustomize:&lt;br&gt;dependency&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="106" y="237" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Kustom&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="60" y="260" width="140" height="40" rx="6" ry="6" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;image x="69.5" y="265.5" width="34" height="34" xlink:href="https://cdn0.iconfinder.com/data/icons/octicons/1024/file-directory-128.png" preserveAspectRatio="none" pointer-events="none"&gt;&lt;/image&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 283px; margin-left: 106px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Kustomize:&lt;br&gt;application&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="106" y="287" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Kustom&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="63" y="110" width="140" height="40" rx="6" ry="6" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;path d="M 63 130 L 40 130 L 40 230 L 53.63 230" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 58.88 230 L 51.88 233.5 L 53.63 230 L 51.88 226.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;image x="72.5" y="115.5" width="34" height="34" xlink:href="https://cdn0.iconfinder.com/data/icons/octicons/1024/file-directory-128.png" preserveAspectRatio="none" pointer-events="none"&gt;&lt;/image&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 133px; margin-left: 109px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Kustomize:&lt;br&gt;dev&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="109" y="137" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Kustom&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="300" y="55" width="167.5" height="120" rx="18" ry="18" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;path d="M 337.35 65.02 C 336.99 65.05 336.62 65.14 336.3 65.3 L 321.98 72.13 C 321.22 72.49 320.68 73.18 320.49 73.98 L 316.95 89.35 C 316.79 90.06 316.93 90.81 317.32 91.43 C 317.39 91.5 317.43 91.57 317.48 91.66 L 327.39 103.97 C 327.92 104.61 328.72 105 329.54 105 L 345.44 105 C 346.26 105 347.06 104.61 347.59 103.97 L 357.5 91.64 C 358 91 358.21 90.15 358.03 89.35 L 354.48 73.98 C 354.3 73.18 353.75 72.49 353 72.13 L 338.68 65.3 C 338.27 65.09 337.81 65 337.35 65.02 Z" fill="#ffffff" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 337.36 66.22 C 337.02 66.24 336.67 66.33 336.37 66.48 L 322.91 72.9 C 322.2 73.24 321.68 73.89 321.51 74.64 L 318.18 89.09 C 318.03 89.76 318.16 90.46 318.53 91.04 C 318.59 91.11 318.64 91.17 318.68 91.26 L 328 102.83 C 328.49 103.43 329.24 103.8 330.02 103.8 L 344.96 103.8 C 345.74 103.8 346.49 103.43 346.98 102.83 L 356.3 91.24 C 356.77 90.64 356.97 89.84 356.79 89.09 L 353.47 74.64 C 353.29 73.89 352.78 73.24 352.07 72.9 L 338.61 66.48 C 338.22 66.29 337.79 66.2 337.36 66.22 Z" fill="#2875e2" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 325.2 74.38 L 324 74.38 L 324 77.05 L 325.34 77.05 L 325.34 74.48 L 327.88 74.48 L 327.88 73.14 L 325.2 73.14 Z M 329.22 74.48 L 331.9 74.48 L 331.9 73.14 L 329.22 73.14 Z M 333.23 74.48 L 335.91 74.48 L 335.91 73.14 L 333.23 73.14 Z M 337.25 74.48 L 339.93 74.48 L 339.93 73.14 L 337.25 73.14 Z M 341.26 74.48 L 343.94 74.48 L 343.94 73.14 L 341.26 73.14 Z M 345.28 74.48 L 347.96 74.48 L 347.96 73.14 L 345.28 73.14 Z M 349.29 74.48 L 349.66 74.48 L 349.66 75.45 L 351 75.45 L 351 73.81 C 351 73.31 350.77 73.14 350.33 73.14 L 349.29 73.14 Z M 349.66 79.46 L 351 79.46 L 351 76.79 L 349.66 76.79 Z M 324 81.07 L 325.34 81.07 L 325.34 78.39 L 324 78.39 Z M 349.66 83.48 L 351 83.48 L 351 80.8 L 349.66 80.8 Z M 324 85.08 L 325.34 85.08 L 325.34 82.41 L 324 82.41 Z M 349.66 87.49 L 351 87.49 L 351 84.82 L 349.66 84.82 Z M 324 89.1 L 325.34 89.1 L 325.34 86.42 L 324 86.42 Z M 349.66 91.51 L 351 91.51 L 351 88.83 L 349.66 88.83 Z M 324 93.11 L 325.34 93.11 L 325.34 90.44 L 324 90.44 Z M 349.66 95.52 L 351 95.52 L 351 92.85 L 349.66 92.85 Z M 324 96.19 C 324 96.66 324.2 96.86 324.67 96.86 L 325.61 96.86 L 325.61 95.52 L 325.34 95.52 L 325.34 94.45 L 324 94.45 Z M 326.94 96.86 L 329.62 96.86 L 329.62 95.52 L 326.94 95.52 Z M 330.96 96.86 L 333.64 96.86 L 333.64 95.52 L 330.96 95.52 Z M 334.97 96.86 L 337.65 96.86 L 337.65 95.52 L 334.97 95.52 Z M 338.99 96.86 L 341.67 96.86 L 341.67 95.52 L 338.99 95.52 Z M 343 96.86 L 345.68 96.86 L 345.68 95.52 L 343 95.52 Z M 347.02 96.86 L 349.7 96.86 L 349.7 95.52 L 347.02 95.52 Z" fill="#ffffff" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 85px; margin-left: 362px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"&gt;flux-system&lt;br&gt;namespace&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="362" y="89" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;flux-sys&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="300" y="225" width="167.5" height="70" rx="10.5" ry="10.5" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"&gt;&lt;/rect&gt;&lt;path d="M 337.35 235.02 C 336.99 235.05 336.62 235.14 336.3 235.3 L 321.98 242.13 C 321.22 242.49 320.68 243.18 320.49 243.98 L 316.95 259.35 C 316.79 260.06 316.93 260.81 317.32 261.43 C 317.39 261.5 317.43 261.57 317.48 261.66 L 327.39 273.97 C 327.92 274.61 328.72 275 329.54 275 L 345.44 275 C 346.26 275 347.06 274.61 347.59 273.97 L 357.5 261.64 C 358 261 358.21 260.15 358.03 259.35 L 354.48 243.98 C 354.3 243.18 353.75 242.49 353 242.13 L 338.68 235.3 C 338.27 235.09 337.81 235 337.35 235.02 Z" fill="#ffffff" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 337.36 236.22 C 337.02 236.24 336.67 236.33 336.37 236.48 L 322.91 242.9 C 322.2 243.24 321.68 243.89 321.51 244.64 L 318.18 259.09 C 318.03 259.76 318.16 260.46 318.53 261.04 C 318.59 261.11 318.64 261.17 318.68 261.26 L 328 272.83 C 328.49 273.43 329.24 273.8 330.02 273.8 L 344.96 273.8 C 345.74 273.8 346.49 273.43 346.98 272.83 L 356.3 261.24 C 356.77 260.64 356.97 259.84 356.79 259.09 L 353.47 244.64 C 353.29 243.89 352.78 243.24 352.07 242.9 L 338.61 236.48 C 338.22 236.29 337.79 236.2 337.36 236.22 Z" fill="#2875e2" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 325.2 244.38 L 324 244.38 L 324 247.05 L 325.34 247.05 L 325.34 244.48 L 327.88 244.48 L 327.88 243.14 L 325.2 243.14 Z M 329.22 244.48 L 331.9 244.48 L 331.9 243.14 L 329.22 243.14 Z M 333.23 244.48 L 335.91 244.48 L 335.91 243.14 L 333.23 243.14 Z M 337.25 244.48 L 339.93 244.48 L 339.93 243.14 L 337.25 243.14 Z M 341.26 244.48 L 343.94 244.48 L 343.94 243.14 L 341.26 243.14 Z M 345.28 244.48 L 347.96 244.48 L 347.96 243.14 L 345.28 243.14 Z M 349.29 244.48 L 349.66 244.48 L 349.66 245.45 L 351 245.45 L 351 243.81 C 351 243.31 350.77 243.14 350.33 243.14 L 349.29 243.14 Z M 349.66 249.46 L 351 249.46 L 351 246.79 L 349.66 246.79 Z M 324 251.07 L 325.34 251.07 L 325.34 248.39 L 324 248.39 Z M 349.66 253.48 L 351 253.48 L 351 250.8 L 349.66 250.8 Z M 324 255.08 L 325.34 255.08 L 325.34 252.41 L 324 252.41 Z M 349.66 257.49 L 351 257.49 L 351 254.82 L 349.66 254.82 Z M 324 259.1 L 325.34 259.1 L 325.34 256.42 L 324 256.42 Z M 349.66 261.51 L 351 261.51 L 351 258.83 L 349.66 258.83 Z M 324 263.11 L 325.34 263.11 L 325.34 260.44 L 324 260.44 Z M 349.66 265.52 L 351 265.52 L 351 262.85 L 349.66 262.85 Z M 324 266.19 C 324 266.66 324.2 266.86 324.67 266.86 L 325.61 266.86 L 325.61 265.52 L 325.34 265.52 L 325.34 264.45 L 324 264.45 Z M 326.94 266.86 L 329.62 266.86 L 329.62 265.52 L 326.94 265.52 Z M 330.96 266.86 L 333.64 266.86 L 333.64 265.52 L 330.96 265.52 Z M 334.97 266.86 L 337.65 266.86 L 337.65 265.52 L 334.97 265.52 Z M 338.99 266.86 L 341.67 266.86 L 341.67 265.52 L 338.99 265.52 Z M 343 266.86 L 345.68 266.86 L 345.68 265.52 L 343 265.52 Z M 347.02 266.86 L 349.7 266.86 L 349.7 265.52 L 347.02 265.52 Z" fill="#ffffff" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 255px; margin-left: 362px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"&gt;application&lt;br&gt;namespace&lt;br&gt;dev&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="362" y="259" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;applicat&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 337.35 115.02 C 336.99 115.05 336.62 115.14 336.3 115.3 L 321.98 122.13 C 321.22 122.49 320.68 123.18 320.49 123.98 L 316.95 139.35 C 316.79 140.06 316.93 140.81 317.32 141.43 C 317.39 141.5 317.43 141.57 317.48 141.66 L 327.39 153.97 C 327.92 154.61 328.72 155 329.54 155 L 345.44 155 C 346.26 155 347.06 154.61 347.59 153.97 L 357.5 141.64 C 358 141 358.21 140.15 358.03 139.35 L 354.48 123.98 C 354.3 123.18 353.75 122.49 353 122.13 L 338.68 115.3 C 338.27 115.09 337.81 115 337.35 115.02 Z" fill="#ffffff" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 337.36 116.22 C 337.02 116.24 336.67 116.33 336.37 116.48 L 322.91 122.9 C 322.2 123.24 321.68 123.89 321.51 124.64 L 318.18 139.09 C 318.03 139.76 318.16 140.46 318.53 141.04 C 318.59 141.11 318.64 141.17 318.68 141.26 L 328 152.83 C 328.49 153.43 329.24 153.8 330.02 153.8 L 344.96 153.8 C 345.74 153.8 346.49 153.43 346.98 152.83 L 356.3 141.24 C 356.77 140.64 356.97 139.84 356.79 139.09 L 353.47 124.64 C 353.29 123.89 352.78 123.24 352.07 122.9 L 338.61 116.48 C 338.22 116.29 337.79 116.2 337.36 116.22 Z" fill="#2875e2" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 346.63 134.43 L 344.92 134.43 L 344.92 129.85 C 344.92 128.59 343.91 127.58 342.65 127.58 L 338.07 127.58 L 338.07 125.85 C 338.07 124.29 336.78 123 335.22 123 C 333.64 123 332.35 124.29 332.35 125.85 L 332.35 127.58 L 327.78 127.58 C 326.54 127.58 325.5 128.59 325.5 129.85 L 325.5 134.21 L 327.21 134.21 C 328.91 134.21 330.3 135.59 330.3 137.28 C 330.3 138.98 328.91 140.37 327.21 140.37 L 325.5 140.37 L 325.5 144.72 C 325.5 145.96 326.54 147 327.78 147 L 332.13 147 L 332.13 145.29 C 332.13 143.59 333.52 142.2 335.22 142.2 C 336.91 142.2 338.29 143.59 338.29 145.29 L 338.29 147 L 342.65 147 C 343.91 147 344.92 145.96 344.92 144.72 L 344.92 140.15 L 346.63 140.15 C 348.21 140.15 349.5 138.86 349.5 137.28 C 349.5 135.72 348.21 134.43 346.63 134.43 Z" fill="#ffffff" stroke="none" pointer-events="none"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 135px; margin-left: 365px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"&gt;flux-system&lt;br&gt;custom resource&lt;br&gt;controllers&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="365" y="139" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;flux-syst&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 220.59 83.73 L 216.18 93.26 L 205.45 71.21 L 229.21 65.13 L 224.8 74.66 L 284.41 102.27 L 288.82 92.74 L 299.55 114.79 L 275.79 120.87 L 280.2 111.34 Z" fill="#f8cecc" stroke="#b85450" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 378.75 194.5 L 368.25 194.5 L 383.75 175.5 L 399.25 194.5 L 388.75 194.5 L 388.75 205.5 L 399.25 205.5 L 383.75 224.5 L 368.25 205.5 L 378.75 205.5 Z" fill="#f8cecc" stroke="#b85450" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 201px; margin-left: 420px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;reconcile&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="420" y="204" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;reconcile&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 205.4 183.38 L 294.6 239.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 200.95 180.59 L 208.74 181.33 L 205.4 183.38 L 205.03 187.27 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 299.05 241.91 L 291.26 241.17 L 294.6 239.12 L 294.97 235.23 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 206.1 231.83 L 293.9 258.17" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 201.07 230.32 L 208.78 228.98 L 206.1 231.83 L 206.77 235.69 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 298.93 259.68 L 291.22 261.02 L 293.9 258.17 L 293.23 254.31 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 206.37 279.84 L 293.63 277.66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 201.12 279.97 L 208.03 276.3 L 206.37 279.84 L 208.2 283.3 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 298.88 277.53 L 291.97 281.2 L 293.63 277.66 L 291.8 274.2 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The configurations starts with a bootstrapping process, which creates directory in Git repository (if not exist), and installs flux-system components in the target Kubernetes cluster. The sync process starts as soon as bootstrapping is completed. The process in charge of syncing declarations to target cluster, confusingly, is also called Kustomization. Therefore there are two Kustomizations. According to the FAQ on FluxCD website:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are two Kustomization types. the &lt;em&gt;kustomization.kustomize.toolkit.fluxcd.io&lt;/em&gt; is a Kubernetes custom resource while &lt;em&gt;kustomization.kustomize.config.k8s.io&lt;/em&gt; is the type used to configure a Kustomize overlay. The &lt;em&gt;kustomization.kustomize.toolkit.fluxcd.io&lt;/em&gt; object refers to a kustomization.yaml file path inside a Git repository or Bucket source.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Inside of the Git repository, with a &lt;meta charset="utf-8"&gt;&lt;em&gt;kustomization.kustomize.toolkit.fluxcd.io&lt;/em&gt; obejct, the flux-system points to Kustomization file (representing &lt;meta charset="utf-8"&gt;&lt;em&gt;kustomization.kustomize.config.k8s.io&lt;/em&gt; object) at root level. The kustomization.yaml file organizes resources in the same directory. A kustomize directory may also reference other kustomize directory, forming a hierarchy. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-implementation"&gt;Implementation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;FluxCD has a command &amp;#8220;flux check &amp;#8211;pre&amp;#8221; to check the prerequisite, such as kubectl. The code is stored in the &lt;a href="https://github.com/digihunch/korthweb/tree/main/gitops"&gt;GitOps&lt;/a&gt; directory of &lt;a href="https://github.com/digihunch/korthweb"&gt;Korthweb&lt;/a&gt; repository.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To configure deployment, we need to first create a &lt;a href="https://fluxcd.io/docs/installation/#github-and-github-enterprise"&gt;personal access token&lt;/a&gt;. For GitHub, &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token"&gt;here&lt;/a&gt; is the instruction. Export the token to environment variable, and launch bootstrapping:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export GITHUB_TOKEN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;xxx_yyy55555XXXodr7ABBBB234CCccw&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flux bootstrap github &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --owner&lt;span style="color:#f92672"&gt;=&lt;/span&gt;digihunch &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --repository&lt;span style="color:#f92672"&gt;=&lt;/span&gt;korthweb &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;main &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --personal &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --path&lt;span style="color:#f92672"&gt;=&lt;/span&gt;gitops/environment/dev&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;A deploy key is configured during the bootstrapping process. As soon as bootstrapping is completed, the sync (aka kustomization, or reconciliation) has started, which can be monitored using:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flux get kustomizations --watch&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Running this command without &amp;#8211;watch switch returns the overview of all kustomizations. Once reconciliation is completed, it should display something like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME &#9;READY&#9;MESSAGE &#9;REVISION &#9;SUSPENDED&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;application &#9;True &#9;Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;False&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dependency &#9;True &#9;Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;False&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flux-system &#9;True &#9;Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;False&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;infrastructure&#9;True &#9;Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7&#9;False&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;FluxCD introduced a number of CRDS. For example, to check configured source repository, check gitrepositories CRD:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get gitrepositories -n flux-system&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To check the detail of one kustomization (e.g. infrastructure), check kusomization CRD in flux-system namespace:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n flux-system get kustomizations flux-system -o yaml | less&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Other commonly used custom resources include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;helmcharts&lt;/li&gt;&#10;&lt;li&gt;helmreleases&lt;/li&gt;&#10;&lt;li&gt;helmrepositories&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Helm release can be created imperatively, for example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flux create source helm istio &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;1h &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --url&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://istio-release.storage.googleapis.com/charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flux create helmrelease istio-base &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;1h &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --release-name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istio-base &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --target-namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istio-system &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --create-target-namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --source&lt;span style="color:#f92672"&gt;=&lt;/span&gt;HelmRepository/istio &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --chart&lt;span style="color:#f92672"&gt;=&lt;/span&gt;base &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --chart-version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1.12.0&amp;#34;&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ flux create helmrelease istiod &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;1h &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --release-name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istiod &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --target-namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istio-system &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --source&lt;span style="color:#f92672"&gt;=&lt;/span&gt;HelmRepository/istio &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --chart&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istiod &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --chart-version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1.12.0&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --values&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istiod-values.yaml &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;However, in a GitOps approach, they should be stored as code (use &amp;#8211;export to export declaration). For example, the &lt;a href="https://github.com/digihunch/korthweb/tree/main/gitops/infrastructure"&gt;infrastructure kustomization&lt;/a&gt; keeps HelmReleases for installing Istio and PostgreSQL. This kustomization is referenced by a Flux &lt;a href="https://github.com/digihunch/korthweb/blob/main/gitops/infrastructure/kustomization.yaml"&gt;Kustomization&lt;/a&gt; from higher level.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can also manually &lt;a href="https://fluxcd.io/docs/cmd/flux_reconcile/"&gt;reconcile&lt;/a&gt; one of the kustomizations (or other CRDs) with flux reconcile command, for example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flux reconcile kustomization dependency&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Since Nov 2021, FluxCD (&lt;a href="https://fluxcd.io/blog/2021/11/november-2021-update/#server-side-apply-has-landed"&gt;0.20&lt;/a&gt;) supports reconciliation based on &lt;a href="https://kubernetes.io/docs/reference/using-api/server-side-apply/"&gt;server-side&lt;/a&gt; apply. This increases performance and help address issues such as &lt;a href="https://www.reddit.com/r/kubernetes/comments/sbw7lo/the_configmap_is_invalid_metadataannotations_too/"&gt;applying large config map&lt;/a&gt;, which is the equivalent of adding &amp;#8211;server-side flag to the kubectl apply command.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-limitation"&gt;Limitation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Troubleshooting the FluxCD repo can be involving and counter-intuitive. I had to commit a lot of changes to the repo because it serves as source of truth. Even though the commits can be made to a branch, it still involves a lot of code pushes. Traditionally I commit a change after testing. In the GitOps workflow, I commit a change then to test.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;More flexible troubleshooting options are still to be desired. For example, there is no way to run one (FluxCD&amp;#8217;s) Kustomization object at a time (and disable the rest), unless you remove their YAML files from the repo.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The next limitation is the ordering of resources in Kustomization. Arguably this is a limitation from Kustomize, instead of FluxCD&amp;#8217;s. For example, I need the following manifest to be executed:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager.io/v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterIssuer&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;selfsigned-issuer&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selfSigned&lt;/span&gt;: {}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The resource is a CRD that needs to be first installed using Helm chart (cert-manager). However there&amp;#8217;s no way to control the sequence (Helm release executed first before declaration using CRD). Although there&amp;#8217;s some &lt;a href="https://github.com/kingdonb/bootstrap-repo/tree/staging/apps/cert-manager"&gt;workaround&lt;/a&gt;, it is not convenient. Alternatively, we can separate the resource creation and CRD creation into separate kustomization objects with dependency relationship, as suggested in &lt;a href="https://fluxcd.io/docs/components/kustomize/kustomization/#kustomization-dependencies"&gt;this&lt;/a&gt; example.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Deployment in Kubernetes can get complicated with a lot of manifests. Helm, Kustomize and the like are means to handle the complexity due to numerous manifests. GitOps tools such as FlexCD brings these tools under a single framework, and more importantly, implements the idea of using Git repository as source of truth for continuous deployment.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/01/kubernetes-admission-control/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Admission Control&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/01/traffic-segmentation-on-kubernetes-platform/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Traffic Segmentation on Kubernetes Platform&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>AKS Lessons Learned 1 of 2</title><link>https://static.digihunch.com/2021/12/aks-troubleshooting-lessons-learned/</link><pubDate>Sat, 04 Dec 2021 02:11:06 -0400</pubDate><guid>https://static.digihunch.com/2021/12/aks-troubleshooting-lessons-learned/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-aks-lesson-1.webp" alt="Featured image of post AKS Lessons Learned 1 of 2" /&gt;&lt;p class="wp-block-paragraph"&gt;In general, troubleshooting Kubernetes is tricky. That is because one has to get in and out of pods. I took two days to troubleshoot some networking issues with private AKS cluster. For the amount of of tricks I had to employ, I need to take some notes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="the-issue"&gt;The issue&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After writing the Terraform code, I used the following dummy service to test the private AKS cluster:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;apps/v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deployment&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aks-helloworld-one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;replicas&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aks-helloworld-one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;template&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aks-helloworld-one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;containers&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aks-helloworld-one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;neilpeterson/aks-helloworld:v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;containerPort&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;TITLE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;value&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Welcome to Azure Kubernetes Service (AKS)&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Service&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aks-helloworld-one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;LoadBalancer&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aks-helloworld-one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The expected behaviour, is that the service object will tell cloud API to provision a load balancer, with public IP listing at port 80. I should be able to curl to the IP address and connect to the site in the Pod. However, I was not able to. On the bastion host, I was able to curl to nodePort of the node address. But anything on public IP does not work, no matter where I ran curl from. This feels like a basic issue, but is is quite annoying because the native troubleshooting tool for Azure Load Balancer is horrible. In and out of a bunch of components named &amp;#8220;insights&amp;#8221;, &amp;#8220;diagnostic log&amp;#8221;, or &amp;#8220;Metrics&amp;#8221;, I can&amp;#8217;t simply find a way to trace whether it received an HTTP request. Most of the information I was able to see was irrelevant or useless.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="the-approach"&gt;The approach&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The hard way to troubleshooting infrastructure as code, is configuration comparison approach: revert to a baseline configuration, and see if the expected function works. Then from the baseline, change one configuration at a time and see where it starts to break. This approach is very time consuming, and AKS cluster as a relatively large resource, with numerous attributes, takes this effort to extreme. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The baseline configuration I started with is:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks create -g AutomationTest -n orthCluster --generate-ssh-keys --node-count &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; --tags Owner&lt;span style="color:#f92672"&gt;=&lt;/span&gt;MyOwner&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With this baseline, I simply use kubectl to apply the YAML file above. Then I can tell that the port is working. With a good start point, I started to apply one change at a time and repeat the test. I ran into a snug when I&amp;#8217;m using the following configuration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks create -g AutomationTest -n orthCluster --generate-ssh-keys --node-count &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; --tags Owner&lt;span style="color:#f92672"&gt;=&lt;/span&gt;MyOwner --enable-private-cluster --network-plugin azure --network-policy calico&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With the cluster created from the command above, the variable introduced is &amp;#8211;enable-private-cluster. This puts the cluster on a private network. I cannot connect to the cluster via a public endpoint anymore, and thus have to figure out some tricks to run the kubectl commands. I had to play with the Command Run feature of AKS cluster because I don&amp;#8217;t have a bastion host when using AZ CLI command. The Command Run feature would not allow me to use any file from bastion host. So i had to create my test objects, the Deploy and the Service objects all by imperative commands. The equivalent commands I worked out is:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl create deployment aks-helloworld-one --image&lt;span style="color:#f92672"&gt;=&lt;/span&gt;neilpeterson/aks-helloworld:v1 --replicas&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; --port&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl expose deploy aks-helloworld-one --port &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt; --target-port &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt; --type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;LoadBalancer&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then I realized a limitation with Command Run feature: it only supports basic command switches and doesn&amp;#8217;t like switches such as &amp;#8211;replicas. So I used the following commands:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks command invoke -g AutomationTest -n orthCluster -c &lt;span style="color:#e6db74"&gt;&amp;#34;kubectl get no&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks command invoke -g AutomationTest -n orthCluster -c &lt;span style="color:#e6db74"&gt;&amp;#34;kubectl create deployment aks-helloworld-one --image=neilpeterson/aks-helloworld:v1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks command invoke -g AutomationTest -n orthCluster -c &lt;span style="color:#e6db74"&gt;&amp;#34;kubectl get deploy&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks command invoke -g AutomationTest -n orthCluster -c &lt;span style="color:#e6db74"&gt;&amp;#34;kubectl expose deploy aks-helloworld-one --port 80 --target-port 80 --type=LoadBalancer&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks command invoke -g AutomationTest -n orthCluster -c &lt;span style="color:#e6db74"&gt;&amp;#34;kubectl get svc&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This trick allows me to continue with the testing eliminate Azure CNI and Calico policy as the cause. Testing after each cluster creation is painful because the cluster creation can take 10 minutes.I had to temporarily minimize the size of the cluster to speed up provisioning. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I finally came to the point that I can reproduce the issue using TF template. I realized that when I set the vnet_subnet_id attribute of azurerm_kubernetes_cluster&amp;#8217;s default_node_pool, the problem came back. That&amp;#8217;s the smoking gun that the node subnet is the issue. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="the-network-security-group-on-node-subnet"&gt;The Network Security Group on Node Subnet&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The node subnet has an associated network security group. I discovered that once I add an allow rule for port 80 to the security group, the curl test will work. I also noticed the security group rule change will take 60 sec to come to effect and load balancer will also take 60 sec to warm up.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This confuses me because port 80 is only listened by the load balancer and not by any of the nodes. It&amp;#8217;s most likely when public load balancer is used the load balancer is placed on the node subnet. According to &lt;a href="https://docs.microsoft.com/en-us/azure/aks/load-balancer-standard"&gt;this&lt;/a&gt; note: &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Inbound, external traffic flows from the load balancer to the virtual network for your AKS cluster. The virtual network has a Network Security Group (NSG) which allows all inbound traffic from the load balancer. This NSG uses a service tag of type LoadBalancer to allow traffic from the load balancer.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The packet coming from external source can travel up to the VNet, but it was blocked at the NSG of node subnet.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="lessons-learned"&gt;Lessons Learned&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We always need to have some dummy service ready to test what we need. We can use nginx dummy service like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;apps/v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deployment&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;replicas&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;template&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;containers&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;containerPort&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Service&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;LoadBalancer&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;protocol&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;TCP&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-nginx&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;As discussed above, it&amp;#8217;s also important to have a Bastion host able to access the control plane when the AKS cluster is private. Azure touts about CloudShell (and its ability to run in specified V-Net) but it&amp;#8217;s pretty useless in troubleshooting. CloudShell sessions run inside of Kubernetes cluster and lacks common network troubleshooting tool such as nc. Azure has a managed service for Bastion but it requires a subnet with the exact name of AzureBastionSubnet.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We will explore more issues in the &lt;a href="https://static.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;next&lt;/a&gt; post.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/11/from-microservice-to-service-mesh/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;From Microservice to Service Mesh&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AKS Lessons Learned 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Logging and Monitoring in Kubernetes with PLG stack</title><link>https://static.digihunch.com/2021/10/logging-and-monitoring-in-kubernetes-with-plg-stack/</link><pubDate>Wed, 13 Oct 2021 21:29:00 -0400</pubDate><guid>https://static.digihunch.com/2021/10/logging-and-monitoring-in-kubernetes-with-plg-stack/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-k8s-plg.webp" alt="Featured image of post Logging and Monitoring in Kubernetes with PLG stack" /&gt;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ve checked out the the actors in PLG stack (Promtail, Loki, Node Exporter, Prometheus, Grafana) and whipped up a quick pipeline on MacOS. Now I&amp;#8217;m going a little further to implement the same PLG stack (Prometheus Loki and Grafana) in a Kubernetes cluster. This setup is for demo only, therefore no persistent storage is enabled.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-test-workload"&gt;Test Workload&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I host a deployment of Flog with three pods running on Minikube. Flog is an open-source emulating log generation behaviour of an application. On the Minikube cluster we start the deployment as below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;minikube start --driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hyperkit --container-runtime&lt;span style="color:#f92672"&gt;=&lt;/span&gt;containerd --memory&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12288&lt;/span&gt; --cpus&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl create ns obsv&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv create deployment flog --image&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mingrammer/flog --replicas&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; -- flog -f rfc3164 -l -d 300ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv get po&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The Pods will come up in a heartbeat. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I will use &lt;a href="https://static.digihunch.com/2021/07/helm-configuration-management-for-kubernetes-resources/"&gt;helm&lt;/a&gt; to install the objects required for logging and metrics pipelines. There are multiple Helm charts for each components. Some high-level charts (usually with a name suffix of -stack) contain several other resource as sub-charts. They are created as one-stop-shop for multiple components but I found none of them serve my exact purpose. For example, both &lt;a href="https://artifacthub.io/packages/helm/grafana/loki-stack"&gt;loki-stack&lt;/a&gt; and &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack"&gt;kube-prometheus-stack&lt;/a&gt; include Grafana. But I only need one instance of Grafana. Therefore I stick to the low-level charts.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt; &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-log-shipping"&gt;Log Shipping&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Add a helm repo, and install loki and promtail. Note that we need to specify correct loki address when installing Promtail. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add grafana https://grafana.github.io/helm-charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo update&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --namespace obsv --install loki grafana/loki&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --namespace obsv --install promtail grafana/promtail --set &lt;span style="color:#e6db74"&gt;&amp;#34;config.lokiAddress=http://loki.obsv.svc.cluster.local:3100/loki/api/v1/push&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When installing Promtail, a DaemonSet is created on the Node. The default configuration applies appropriate configuration and tagging strategy for Kubernetes Pod and Node. So the only customization I specified is Loki address. We can then check logging with Loki. To do so, first expose port 3100 to host, and then use logcli (e.g. on MacOS) to query for logs:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv port-forward service/loki 3100:3100&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli labels&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli labels pod&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli query &lt;span style="color:#e6db74"&gt;&amp;#39;{pod=&amp;#34;flog-775d5fc5c8-p4rlx&amp;#34;}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Log lines should be pumped to Loki a minute after Loki comes up. The logcli labels command should display the tags. The logcli query command should return the log lines. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-metrics"&gt;Metrics&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I use Premetheus with node exporter. In its &lt;a href="https://prometheus.io/docs/introduction/overview/#architecture"&gt;architecture&lt;/a&gt;, Prometheus contain the server, the pushgateway, and alertmanager. The &lt;a href="https://github.com/prometheus-community/helm-charts"&gt;helm chart&lt;/a&gt; for &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/prometheus"&gt;Prometheus&lt;/a&gt; contains all of those components. It also has a dependency repo for &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/kube-state-metrics"&gt;kube-state-metrics&lt;/a&gt;. To install:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add prometheus-community https://prometheus-community.github.io/helm-charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add kube-state-metrics https://kubernetes.github.io/kube-state-metrics&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm --namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;obsv install prometheus prometheus-community/prometheus&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv get svc&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Once installed, a DaemonSet for Prometheus Node Exporter is created. The exporter is already configured by default for Kubernetes monitoring. The Prometheus server is also configured, on port 80 by default. it needs to be forwarded in order to access from Browser:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl --namespace obsv port-forward service/prometheus-server 9100:80&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To verify installation of Prometheus, browse to localhost:9100 to examine the metrics.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-visualization"&gt;Visualization&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Last but not least, I will configure Grafana. The repo has been added already so we&amp;#8217;ll just install the chart:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --namespace obsv --install grafana grafana/grafana&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get secret --namespace obsv grafana -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{.data.admin-password}&amp;#34;&lt;/span&gt; | base64 --decode ; echo&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The second command retrieves the credential. To access the web portal, we need port forwarding again:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl port-forward --namespace obsv service/grafana 3000:80&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To verify installation, browse to http://localhost:3000 and log in as user admin with the password above. Then add two data sources:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Type: Prometheus, URL: http://prometheus-server.obsv.svc.cluster.local:80&lt;/li&gt;&#10;&lt;li&gt;Type: Loki, URL: http://loki.obsv.svc.cluster.local:3100&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then we can explore data using both data sources.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://d33wubrfki0l68.cloudfront.net/0862f7967545b9ebe1041764e9427a8bf0f44a08/6b8ba/assets/img/uploads/2020/04/image2.png" alt="grafana workflow"/&gt;&lt;figcaption class="wp-element-caption"&gt;PLG stack&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the last two posts I reviewed the setups for PLG stack in Kubernetes, from a regular environment to k8s cluster. Fluentd, Prometheus are both CNCF projects. The PLG stack seems to be more adopted than EFK but both have their own advantages. Welcome to the PLG vs EFK debate.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/10/intro-to-plg-stack-prometheus-loki-and-grafana/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Intro to PLG stack -Prometheus, Loki and Grafana&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/10/notes-on-azure/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Azure Deets&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Certified Kubernetes Administrator (CKA) Exam</title><link>https://static.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/</link><pubDate>Fri, 30 Apr 2021 09:50:00 -0400</pubDate><guid>https://static.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/</guid><description>&lt;p class="wp-block-paragraph"&gt;The Certified Kubernetes Administrator (CKA) exam is a hands-on session where you need to follow the instructions to configure the system in a bash terminal on the web browser. In my experience, some shortcut keys (such as Alt+F) do not work, which slows me down a little bit. For each question, you need to switch kubectl context as instructed in the question. Some questions share the same context so it is very easy to omit this step. You can verify response with your own command but will not be told whether you scored in each question. During the CKA exam I tried to spin up a terminal session from within &lt;a href="https://static.digihunch.com/2019/10/personal-vim-cheatsheet/"&gt;Vim&lt;/a&gt; editor and the terminal ran out of buffer. I had to reboot the machine with the help of proctor, and my completed work are saved.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1405" height="121" src="https://static.digihunch.com/wp-content/uploads/2021/05/image.png" alt="" class="wp-image-2266"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In general this is an exam I enjoy preparing and writing because it is very hands on. Result is out a day after, and I passed at 96%. I heard about tight timelines but I managed to finish 15 minutes before the end, most likely owing to my dexterity with Linux commands. With that I&amp;#8217;m happy to share my notes in preparation for the CKA exam.&lt;/p&gt;&#10;&lt;p class="has-white-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="590px" viewBox="-0.5 -0.5 590 638" style="max-width:100%;max-height:638px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="347" width="170" height="290" rx="25.5" ry="25.5" fill="#fff2cc" stroke="#d6b656" stroke-dasharray="3 3" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 344px; margin-left: 1px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-weight: bold; background-color: #ffffff; white-space: normal; word-wrap: normal; "&gt;Worker Node&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="85" y="344" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="377" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 392px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-proxy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="85" y="396" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-proxy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="427" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 442px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kubelet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="85" y="446" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kubelet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="477" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 492px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container runtime&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="85" y="496" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container runtime&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 85 457 L 85 477 L 85 457 L 85 470.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 85 475.88 L 81.5 468.88 L 85 470.63 L 88.5 468.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="10" y="527" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 537px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="45" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="527" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 537px; margin-left: 91px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="125" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="557" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 567px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="45" y="571" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="557" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 567px; margin-left: 91px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="125" y="571" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="587" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 597px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="45" y="601" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="587" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 597px; margin-left: 91px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="125" y="601" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="190" y="347" width="170" height="290" rx="25.5" ry="25.5" fill="#fff2cc" stroke="#d6b656" stroke-dasharray="3 3" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 344px; margin-left: 191px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-weight: bold; background-color: #ffffff; white-space: normal; word-wrap: normal; "&gt;Worker Node&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="275" y="344" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="200" y="377" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 392px; margin-left: 201px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-proxy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="275" y="396" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-proxy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="200" y="427" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 442px; margin-left: 201px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kubelet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="275" y="446" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kubelet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="200" y="477" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 492px; margin-left: 201px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container runtime&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="275" y="496" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container runtime&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 275 457 L 275 477 L 275 457 L 275 470.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 275 475.88 L 271.5 468.88 L 275 470.63 L 278.5 468.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="200" y="527" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 537px; margin-left: 201px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="235" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="527" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 537px; margin-left: 281px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="315" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="200" y="557" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 567px; margin-left: 201px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="235" y="571" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="557" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 567px; margin-left: 281px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="315" y="571" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="200" y="587" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 597px; margin-left: 201px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="235" y="601" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="587" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 597px; margin-left: 281px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="315" y="601" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="380" y="347" width="170" height="290" rx="25.5" ry="25.5" fill="#fff2cc" stroke="#d6b656" stroke-dasharray="3 3" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 344px; margin-left: 381px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-weight: bold; background-color: #ffffff; white-space: normal; word-wrap: normal; "&gt;Worker Node&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="465" y="344" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="390" y="377" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 392px; margin-left: 391px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-proxy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="465" y="396" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-proxy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="390" y="427" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 442px; margin-left: 391px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kubelet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="465" y="446" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kubelet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="390" y="477" width="150" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 492px; margin-left: 391px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container runtime&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="465" y="496" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container runtime&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 465 457 L 465 477 L 465 457 L 465 470.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 465 475.88 L 461.5 468.88 L 465 470.63 L 468.5 468.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="390" y="527" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 537px; margin-left: 391px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="425" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="470" y="527" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 537px; margin-left: 471px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="390" y="557" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 567px; margin-left: 391px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="425" y="571" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="470" y="557" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 567px; margin-left: 471px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="571" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="390" y="587" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 597px; margin-left: 391px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="425" y="601" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="470" y="587" width="70" height="20" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 597px; margin-left: 471px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="601" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 546.37 392 L 570 392 Q 580 392 580 382 L 580 197 Q 580 187 570 187 L 431.37 187" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 541.12 392 L 548.12 388.5 L 546.37 392 L 548.12 395.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 426.12 187 L 433.12 183.5 L 431.37 187 L 433.12 190.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="10" y="17" width="460" height="250" rx="37.5" ry="37.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 458px; height: 1px; padding-top: 14px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-weight: bold; white-space: normal; word-wrap: normal; "&gt;Control&amp;nbsp; Plane&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="240" y="14" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold"&gt;Control&amp;nbsp; Plane&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="37" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 44px; margin-left: 31px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-controller-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="130" y="56" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-controller-manager&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="107" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 114px; margin-left: 31px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-scheduler&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="130" y="126" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-scheduler&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="40" y="47" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 54px; margin-left: 41px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-controller-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="140" y="66" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-controller-manager&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="50" y="57" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 64px; margin-left: 51px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-controller-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="150" y="76" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-controller-manager&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="40" y="117" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 124px; margin-left: 41px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-scheduler&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="140" y="136" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-scheduler&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="50" y="127" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 134px; margin-left: 51px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-scheduler&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="150" y="146" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-scheduler&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="187" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 194px; margin-left: 31px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;cloud-controller-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="130" y="206" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;cloud-controller-manager&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="40" y="197" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 204px; margin-left: 41px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;cloud-controller-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="140" y="216" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;cloud-controller-manager&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="50" y="207" width="200" height="30" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 214px; margin-left: 51px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;cloud-controller-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="150" y="226" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;cloud-controller-manager&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="305" y="167" width="120" height="40" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 174px; margin-left: 306px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;kube-api-server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="365" y="186" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;kube-api-server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 335 55 C 335 44.33 395 44.33 395 55 L 395 99 C 395 109.67 335 109.67 335 99 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 335 55 C 335 63 395 63 395 55 M 335 59 C 335 67 395 67 395 59 M 335 63 C 335 71 395 71 395 63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 74px; margin-left: 336px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;etcd&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="365" y="86" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;etcd&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 254.25 76.75 L 330.75 162.25" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 250.75 72.83 L 258.02 75.72 L 254.25 76.75 L 252.8 80.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 334.25 166.17 L 326.98 163.28 L 330.75 162.25 L 332.2 158.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 255.8 219.36 L 299.2 199.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 251.02 221.54 L 255.94 215.45 L 255.8 219.36 L 258.84 221.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 303.98 197.46 L 299.06 203.55 L 299.2 199.64 L 296.16 197.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 255.37 145.42 L 299.63 173.58" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 250.94 142.6 L 258.73 143.41 L 255.37 145.42 L 254.97 149.31 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 304.06 176.4 L 296.27 175.59 L 299.63 173.58 L 300.03 169.69 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 365 113.37 L 365 160.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 365 108.12 L 368.5 115.12 L 365 113.37 L 361.5 115.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 365 165.88 L 361.5 158.88 L 365 160.63 L 368.5 158.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 546.37 442 L 570 442 Q 580 442 580 432 L 580 197 Q 580 187 570 187 L 431.37 187" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 541.12 442 L 548.12 438.5 L 546.37 442 L 548.12 445.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 426.12 187 L 433.12 183.5 L 431.37 187 L 433.12 190.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For taking CKA exam, we should be familiar with the diagram above.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-tips-for-troubleshooting"&gt;Tips for Troubleshooting&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The CKA exam is hands-on and therefore requires quite a bit of troubleshooting. Here are my notes.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Check Node status to start with&lt;/li&gt;&#10;&lt;li&gt;Check core services on each node:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;sudo systemctl status kubelet&lt;/li&gt;&#10;&lt;li&gt;sudo systemctl status docker&lt;/li&gt;&#10;&lt;li&gt;sudo journalctl -u kubelet&lt;/li&gt;&#10;&lt;li&gt;sudo journalctl -u docker&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Check component logs (on hosting VM)&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;/var/log/kube-apiserver.log&lt;/li&gt;&#10;&lt;li&gt;/var/log/kube-scheduler.log&lt;/li&gt;&#10;&lt;li&gt;/var/log/kube-controller-manager.log&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;If cluster is built by kubeadm, then some of those services are running in Pods within kube-system namespace. Check those pods:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;run interactive shell: &amp;gt; kubectl exec podname &amp;#8211;stdin &amp;#8211;tty &amp;#8212; /bin/sh&lt;/li&gt;&#10;&lt;li&gt;there is an image for lots of useful network tool called nicolaka/netshoot&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Store pod names to variable. e.g. &amp;gt; POD_NAME=$(kubectl get pods -l run=nginx -o jsonpath=&amp;#8221;{.items[0].metadata.name}&amp;#8221;)&lt;/li&gt;&#10;&lt;li&gt;With kubectl, you may&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;alias it to k for faster typing&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;dry-run: to run imperative command without creating object&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;record: record the command that was used to make a change&lt;/li&gt;&#10;&lt;li&gt;-o: set output format, wide, yaml, or jsonpath=&amp;#8221;expression&amp;#8221;. For example, to get pod name: &amp;gt; kubectl get pods -l run=nginx -o jsonpath=&amp;#8221;{.items[0].metadata.name}&amp;#8221;&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;sort-by: use JSONPath expression&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;selector: filter results &lt;strong&gt;by label&lt;/strong&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-build-k8s-cluster-using-kubeadm"&gt;Build K8s cluster using kubeadm&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The CKA exam requires you to know how to build cluster with kubeadm. This involves installing four components (docker-ce, kubeadm, kubectl and kubelet), as outlined below:&lt;/p&gt;&#10;&lt;table id="tablepress-12" class="tablepress tablepress-id-12 tbody-has-connected-cells"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;th class="column-1"&gt;step&lt;/th&gt;&lt;th colspan="2" class="column-2"&gt;command&lt;/th&gt;&#10;&lt;/tr&gt;&#10;&lt;/thead&gt;&#10;&lt;tbody class="row-striping row-hover"&gt;&#10;&lt;tr class="row-2"&gt;&#10;&#9;&lt;td class="column-1"&gt;1. Install docker-ce&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -&lt;br /&gt;&#10;&gt; sudo add-apt-repository \&lt;br /&gt;&#10; "deb [arch=amd64] https://download.docker.com/linux/ubuntu \&lt;br /&gt;&#10; $(lsb_release -cs) \&lt;br /&gt;&#10; stable"&lt;br /&gt;&#10;&gt; sudo apt-get update&lt;br /&gt;&#10;&gt; sudo apt-get install -y docker-ce=18.06.1~ce~3-0~ubuntu&lt;br /&gt;&#10;&gt; sudo apt-mark hold docker-ce&lt;br /&gt;&#10;&gt; sudo systemctl status docker&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-1"&gt;2. Install kubeadm, kubelet and kubectl&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -&lt;br /&gt;&#10;cat &lt;&lt; EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list&lt;br /&gt;&#10;deb https://apt.kubernetes.io/ kubernetes-xenial main&lt;br /&gt;&#10;EOF&lt;br /&gt;&#10;&gt; sudo apt-get update&lt;br /&gt;&#10;&gt; sudo apt-get install -y kubelet kubeadm kubectl&lt;br /&gt;&#10;&gt; sudo apt-mark hold kubelet kubeadm kubectl&lt;br /&gt;&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td class="column-1"&gt;3. Form a K8s cluster&lt;/td&gt;&lt;td class="column-2"&gt;On master node:&lt;br /&gt;&#10;&gt; sudo kubeadm init --pod-network-cidr=10.244.0.0/16&lt;br /&gt;&#10;This command prints out a command for worker nodes to join.&lt;br /&gt;&#10;&lt;/td&gt;&lt;td class="column-3"&gt;On worker node:&lt;br /&gt;&#10;sudo the command generated on master&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td class="column-1"&gt;4. Configure kubectl&lt;/td&gt;&lt;td class="column-2"&gt;On master node:&lt;br /&gt;&#10;&gt; mkdir -p $HOME/.kube&lt;br /&gt;&#10;&gt; sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config&lt;br /&gt;&#10;&gt; sudo chown $(id -u):$(id -g) $HOME/.kube/config&lt;br /&gt;&#10;&lt;/td&gt;&lt;td class="column-3"&gt;Optionally on worker node:&lt;br /&gt;&#10;&gt; mkdir -p $HOME/.kube&lt;br /&gt;&#10;then scp $HOME/.kube/config from control plane node&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td class="column-1"&gt;5. Set up cluster networking&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; echo "net.bridge.bridge-nf-call-iptables=1" | sudo tee -a /etc/sysctl.conf&lt;br /&gt;&#10;&gt; sudo sysctl -p&lt;br /&gt;&#10;Then from any environment with kubectl, bring up the system pods for cluster networking&lt;br /&gt;&#10;&gt; kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml&lt;br /&gt;&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-12 from cache --&gt;&#10;&lt;h3 class="wp-block-heading" id="h-add-new-node-to-kubeadm-cluster"&gt;Add new node to KubeAdm cluster&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is fairly simple with the help of kubeadm. The node to join cluster must be able to communicate with master node. Create a token and print join command from master node:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; kubeadm token create --print-join-command&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then from the node to join, run this command &lt;strong&gt;as sudo&lt;/strong&gt;. You will see that it performs the TLS bootstrap for you. Once completed, the standard output will say this node has joined the cluster. You can confirm with command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; kubectl get nodes&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Sometimes one needs to migrate pods to the newly joined node. This can be done by draining the existing nodes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note you can also use &lt;a href="https://github.com/kubernetes-sigs/kubespray" class="rank-math-link"&gt;kubespray &lt;/a&gt;to build K8s cluster as &lt;a href="https://static.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/" class="rank-math-link"&gt;previously &lt;/a&gt;discussed, and &lt;a href="https://github.com/digihunch/kubelab" class="rank-math-link"&gt;here &lt;/a&gt;is a my IaC project to launch AWS instances and build a K8s cluster with kubespray on top of it. For my learning, I often create a GKE (Google Kubernetes Engine) cluster from GCP&amp;#8217;s cloudshell. There is a &lt;a href="https://cloud.google.com/kubernetes-engine/docs/quickstart" class="rank-math-link"&gt;guide&lt;/a&gt; on how to start a cluster but it comes down to three commands:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ gcloud config set compute/zone us-east1-b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ gcloud container clusters create tcluster --num-nodes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ gcloud container clusters get-credentials tcluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The third command above is to configure kubectl on the cloudshell. Follow &lt;a href="https://cloud.google.com/anthos/clusters/docs/on-prem/1.5/how-to/ssh-cluster-node" class="rank-math-link"&gt;this&lt;/a&gt; guide if you need to SSH to node.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-upgrade-kubeadm-cluster"&gt;Upgrade KubeAdm cluster&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/"&gt;This&lt;/a&gt; involves upgrade three components (kubeadm, kubectl and kubelet) on two types of node: master node and worker node. They steps vary slightly for two nodes. But drain and uncordon is needed for both types of nodes. Pick a node and follow the steps below:&lt;/p&gt;&#10;&lt;table id="tablepress-13" class="tablepress tablepress-id-13 tbody-has-connected-cells"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;th class="column-1"&gt;Step&lt;/th&gt;&lt;th colspan="2" class="column-2"&gt;Command&lt;/th&gt;&#10;&lt;/tr&gt;&#10;&lt;/thead&gt;&#10;&lt;tbody class="row-striping row-hover"&gt;&#10;&lt;tr class="row-2"&gt;&#10;&#9;&lt;td class="column-1"&gt;1. drain the node from kubectl client (e.g. master node)&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; sudo kubectl drain nodename --ignore-daemonsets&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-1"&gt;2. Determine kubeadm target version&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; apt-mark showhold&lt;br /&gt;&#10;&gt; sudo apt-mark unhold kubeadm kubectl kubelet&lt;br /&gt;&#10;&gt; apt list --installed | grep kube&lt;br /&gt;&#10;&gt; apt-cache show kubeadm | less&lt;br /&gt;&#10;&gt; sudo apt-get install -y kubeadm=1.20.2-00&lt;br /&gt;&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td class="column-1"&gt;3. update kubeadm&lt;/td&gt;&lt;td class="column-2"&gt;On master node:&lt;br /&gt;&#10;&gt; sudo kubeadm upgrade plan v1.20.2&lt;br /&gt;&#10;&gt; sudo kubeadm upgrade apply v1.20.2&lt;br /&gt;&#10;&lt;/td&gt;&lt;td class="column-3"&gt;On worker node:&lt;br /&gt;&#10;&gt; sudo kubeadm upgrade node&lt;br /&gt;&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td class="column-1"&gt;4. On the node to update, determine target version for kubectl and kubelet, then install&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; apt-cache show kubectl | less&lt;br /&gt;&#10;&gt; apt-cache show kubelet | less&lt;br /&gt;&#10;&gt; sudo apt-get install -y kubectl=1.20.2-00 kubelet=1.20.2-00&lt;br /&gt;&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td class="column-1"&gt;5. Restart kubelet&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; sudo systemctl daemon-reload&lt;br /&gt;&#10;&gt; sudo systemctl restart kubelet&lt;br /&gt;&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-7"&gt;&#10;&#9;&lt;td class="column-1"&gt;6. Uncordon&lt;/td&gt;&lt;td colspan="2" class="column-2"&gt;&gt; kubectl uncordon nodename&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-13 from cache --&gt;&#10;&lt;h3 class="wp-block-heading" id="h-backup-and-restore-etcd"&gt;Backup and restore Etcd&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://etcd.io/"&gt;Etcd&lt;/a&gt; is a distributed key-value store. It uses Raft protocol for distributed consensus. Etcd is the third distributed system I touch on. The previous two are: Cassandra (using Paxos protocol for distributed consensus) and ZooKeeper (using ZAB protocol). &lt;a href="https://www.alibabacloud.com/blog/a-brief-analysis-of-consensus-protocol-from-logical-clock-to-raft_594675"&gt;Here&lt;/a&gt; is a good article that summarizes the protocols. As for the exam we only need to use etcd with the client tool.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The etcd itself can run on a cluster of servers, each running etcd as a systemd service as etcd/etcd (user/group). It can be deployed in two ways: &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;stacked etcd: an instance of etcd lives with kube-api-server on the same control plane node&lt;/li&gt;&#10;&lt;li&gt;external etcd: in a dedicated cluster of etcd&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Alternatively, etcd can run as a pod, most likely in kube-system namespace. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The etcd service listens on port 2379 for client communication and on port 2380 for server (peer-to-peer) communication. When the systemd service was initialized there are a few key environment variables (e.g. cert locations, ETCD_DATA_DIR) privoded as configuration. To see them, run:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; cat /etc/systemd/system/etcd.service | grep Env&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;These environment variables (prefixed with ETCD_) are for the service only. They can provide current configuration information for us to use later. &amp;nbsp;When it’s running as a pod, check out the directory for static pod for the yaml declaration (e.g. /etc/Kubernetes/manifests/etcd.yaml), where these parameters are passed in as environment variable.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/"&gt;etcdctl&lt;/a&gt; utility is a command line client for etcd. The default API version is 3 so no need any more to set ETCDCTL_API=3 before each command. The utility needs three arguments three arguments (&amp;#8211;cacert, &amp;#8211;cert, and &amp;#8211;key) but we can pass the information via environment variables:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; export ETCDCTL_CACERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt; /home/cloud_user/etcd-certs/etcd-ca.pem&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; export ETCDCTL_CERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt; /home/cloud_user/etcd-certs/etcd-server.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; export ETCDCTL_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt; /home/cloud_user/etcd-certs/etcd-server.key&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; export ETCDCTL_ENDPOINTS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://etcd1:2379&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The environment variable names are uppercase of the argument name with prefix ETCDCTL_. Only global options of arguments can be supplied via environment variables. They remain effective throughout the rest of activities. Also note that the CACERT is needed only when client-cert-auth is true. Now to backup, we can simply run:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; etcdctl snapshot save /home/cloud_user/etcd_backup.db&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To restore from a file, you want to remove existing etcd data directory first. The directory can be found in ETCD_DATA_DIR variable. Suppose it is /var/lib/etcd, you need root permission to write to it, then correct ownership before starting the service:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; sudo systemctl stop etcd &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo mv /var/lib/etcd/ /tmp/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; sudo etcdctl snapshot restore /home/cloud_user/etcd_backup.db --data-dir /var/lib/etcd&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; sudo chown -R etcd:etcd /var/lib/etcd &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo systemctl start etcd&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To verify the restore result, simply run:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; etcdctl get cluster.name&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-object-management"&gt;Object Management&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the CKA exam, we need to interact with many types of built-in Kubernetes objects.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;RBAC objects:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A Role defines permissions &lt;strong&gt;within namespace&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;A ClusterRole defines &lt;strong&gt;cluster-wide&lt;/strong&gt; permissions.&lt;/li&gt;&#10;&lt;li&gt;Both Roles and ClusterRoles are K8s objects that defines a set of permissions&lt;/li&gt;&#10;&lt;li&gt;RoleBinding and ClusterRoleBinding are objects that connect Roles and ClusterRoles to users.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Service Account: an account &lt;strong&gt;used by container processes&lt;/strong&gt; within Pods to authenticate the K8s API. If your Pods need to communicate with the K8s API, you can use service accounts to control their access.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="has-white-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="271px" viewBox="-0.5 -0.5 271 261" style="max-width:100%;max-height:261px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="150" y="70" width="120" height="50" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 152px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: left; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;RoleBinding&lt;br&gt;* roleRef&lt;br&gt;* subjects&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="152" y="99" fill="#000000" font-family="Helvetica" font-size="12px"&gt;RoleBinding&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="70" width="120" height="50" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 2px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: left; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;ClusterRoleBinding&lt;br&gt;* roleRef&lt;br&gt;* subjects&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="2" y="99" fill="#000000" font-family="Helvetica" font-size="12px"&gt;ClusterRoleBinding&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="0" width="100" height="30" rx="4.5" ry="4.5" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 15px; margin-left: 91px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;ServiceAccount&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="140" y="19" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;ServiceAccount&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="170" width="120" height="90" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 215px; margin-left: 2px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: left; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;&lt;div&gt;&lt;span&gt;ClusterRole:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;* rules&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; apiGroups&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; resources&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; resourceNames&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; verbs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="2" y="219" fill="#333333" font-family="Helvetica" font-size="12px"&gt;ClusterRole:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="150" y="170" width="120" height="90" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 215px; margin-left: 152px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: left; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;&lt;div&gt;&lt;span&gt;Role:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;* rules&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; apiGroups&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; resources&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; resourceNames&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;#8211; verbs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="152" y="219" fill="#333333" font-family="Helvetica" font-size="12px"&gt;Role:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 196.37 15 L 203.18 15 Q 210 15 210 25 L 210 70" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 191.12 15 L 198.12 11.5 L 196.37 15 L 198.12 18.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 83.63 15 L 70 15 Q 60 15 60 25 L 60 70" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 88.88 15 L 81.88 18.5 L 83.63 15 L 81.88 11.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 60 120 L 60 163.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 60 168.88 L 56.5 161.88 L 60 163.63 L 63.5 161.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 210 120 L 210 163.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 210 168.88 L 206.5 161.88 L 210 163.63 L 213.5 161.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Inspect resource usage either with a K8s Metrics Server, or by command:&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; kubectl top pod --sort-by &amp;lt;JSONPATH&amp;gt; --selector &amp;lt;selector&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://andrewlock.net/running-kubernetes-and-the-dashboard-with-docker-desktop/"&gt;Here&lt;/a&gt; is a good guide to install metrics server and dashboard (e.g. on docker-desktop).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-pods-and-containers"&gt;Pods and Containers&lt;/h3&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ConfigMaps: store data in key-value map.&lt;/li&gt;&#10;&lt;li&gt;Secrets: same as ConfigMaps but for sensitive data only&lt;/li&gt;&#10;&lt;li&gt;Two ways to pass ConfigMap and Secret data to your container:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;As environment variables in container operating system&lt;/li&gt;&#10;&lt;li&gt;As files presented on mounted volumes in container file system.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Container Resource management:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Resource requests: K8s scheduler will use resource requests to avoid scheduling pods on nodes that do not have enough available resources. 1 CPU unit = 1/1000 of one core&lt;/li&gt;&#10;&lt;li&gt;Resource limits: allow you to limit the amount of resources your containers can use. The container runtime is responsible for enforcement. The enforcement behaviour is different. For example, some terminates container that attempts to use more resource than the limit.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Probes&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Liveness Probe: automatically determine whether or not a container application is in a healthy state. By default K8s does not consider a container to be down until the container process stops. Liveness Probe allow you to customize this detection mechanism and make it more sophisticated.&lt;/li&gt;&#10;&lt;li&gt;Startup Probes: similar to liveness probes. However, while liveness probes run constantly on a schedule, startup probes run at container startup and stop running once they succeed. Startup probes are used to determine when the application has successfully started up. It is especially useful for legacy applications that can have long startup times.&lt;/li&gt;&#10;&lt;li&gt;Readiness Probes: determine when a container is ready to accept requests. When you have a service backed by multiple container endpoints, user traffic will not be sent to a particular pod until its containers have all passed the readiness checks defined by their readinesse probes. Use readiness probes to prevent user traffic from being sent to pods that are still in the process of starting up.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Restart policy for self-healing pods&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;(default) Always: container will always be restarted if they stop, even if they completed successfully (returned 0).&lt;/li&gt;&#10;&lt;li&gt;OnFailure: container will be restarted if the container process exists with an error code, or the container is determined to be unhealthy by a liveness probe.&lt;/li&gt;&#10;&lt;li&gt;Never: let it be&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Multi-container pods:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;containers share the same networking namespace and can communicate with one another on any port, even if the port is not exposed to the cluster&lt;/li&gt;&#10;&lt;li&gt;Container can use volumes to share data in a Pod. Example: a legacy application is hard-coded to write log output to a file on disk. You use a sidecar container to read the log file from shared volume and prints it to the console so the log output will appear in the container log.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Init containers: containers that run once during the startup process of a pod. A pod can have any number of init containers, and they will each run once into completion, before the next init container starts. You may use init containers to perform a variety of startup tasks, they can contain and use software and setup scripts that are not needed by your main containers. They are often useful in keeping your main containers lighter and more secure by offloading startup tasks to a separate container. Use case include:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;cause a pod to wait for another K8s resource to be created before finishing startup&lt;/li&gt;&#10;&lt;li&gt;perform sensitive startup steps securely outside of app containers&lt;/li&gt;&#10;&lt;li&gt;populate data into a shared volume at startup&lt;/li&gt;&#10;&lt;li&gt;communicate with another service at startup&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Scheduling: Scheduler (a component in control plane) assigns Pods to a suitable Node so kubelets can run them. The factor taken into account:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;resource request vs available node resources&lt;/li&gt;&#10;&lt;li&gt;various configurations that affect scheduling using node labels&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Pod allocation&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;nodeSelector is an attribute of Pod to allow you to limit which Node(s) the Pod can be scheduled on. The selector is based on label.&lt;/li&gt;&#10;&lt;li&gt;nodeName is an attribute of Pod that allows you to bypass scheduling and assign Pod to a specific Node by name.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;DaemonSet: automatically runs a copy of a Pod on each node. When a new node is added to the clsuter, DaemonSet will run a new copy of the Pod on it. DaemonSets also respect normal scheduling rules around node labels, taints and tolerations. If a pod would not normally be scheduled on a node, a DaemonSet will not create a copy of the Pod on that node.&lt;/li&gt;&#10;&lt;li&gt;Static Pod: A Pod that is managed directly by the kubelet on a node, not by the K8s API server. They can run even if there is not K8s API server present. Kubelet automatically creates static Pods from YAML manifest files located in the manifest path on the node.&lt;/li&gt;&#10;&lt;li&gt;Mirror Pod: Kubelet will create a mirror Pod for each static Pod. Mirror Pods allow you to see the status of the static Pod via the K8s API, but you cannot change or manage them via the API.&lt;/li&gt;&#10;&lt;li&gt;Taints: applied to nodes to repel a set of pods. A taint specifies key-value and effect. Effect can be &lt;code&gt;NoSchedule&lt;/code&gt; or &lt;code&gt;NoEffect&lt;/code&gt;. The former prevents pods without matching tolerations to schedule to the tainted node. The latter also evicts pre-existing pods with no matching toleration. &lt;/li&gt;&#10;&lt;li&gt;Tolerations: applied to pods so they can be scheduled to nodes with matching taints. A toleration consists of key-value pair, effect and operation. The operation can be &lt;code&gt;Equal&lt;/code&gt; or &lt;code&gt;Exists&lt;/code&gt;. To determine whether a toleration matches a taint. The keys and the effects must be the same. In addition:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;the operator is Exists (and thus no value should be specified in the toleration); or&lt;/li&gt;&#10;&lt;li&gt;the operator is Equal, and all the values match those of the taints;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we have three ways to influence the scheduling behaviour. The first, is simply by specifying &lt;code&gt;nodeSelector&lt;/code&gt; on the Pod, with the required the node label. The second, as just discussed, is to use &lt;code&gt;Taints&lt;/code&gt; and &lt;code&gt;Tolerations&lt;/code&gt;. The third way, is similar to the first, using &lt;code&gt;nodeAffinity&lt;/code&gt; attributes on Pods. nodeAffinity is more powerful and flexible than nodeSelector by supporting more complex scheduling rules (e.g. matching rules).&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Use Node Affinity when your scheduling rule is based on &lt;span style="text-decoration: underline" class="underline"&gt;direct condition&lt;/span&gt;, i.e. &lt;span style="text-decoration: underline" class="underline"&gt;schedule a Pod to this Node when XXX&lt;/span&gt;. In this case, you have &lt;a href="https://kubernetes.io/docs/reference/labels-annotations-taints"&gt;well-known labels&lt;/a&gt; on nodes, and specify &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/#schedule-a-pod-using-required-node-affinity"&gt;nodeAffinity&lt;/a&gt; on Pods. &lt;/li&gt;&#10;&lt;li&gt;Use Taints and Tolerations when your scheduling rule is based on &lt;span style="text-decoration: underline" class="underline"&gt;inverse statement, i.e. do not schedule a Pod to this Node unless XXX&lt;/span&gt;. In this case, you put a taint &amp;#8220;MyCondition:NoSchedule&amp;#8221; on a Node, so that no Pod will ever get scheduled to this Node. The only exception is when a Pod has the Toleration &amp;#8220;MyCondition:NoSchedule&amp;#8221;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-deployments"&gt;Deployments&lt;/h3&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Deployment is an object that defines a desired state for a ReplicaSet (a set of replica Pods). The Deployment Controller seeks to maintain the desired state by creating, deleting, and replacing Pods with new configurations.&lt;/li&gt;&#10;&lt;li&gt;With Deployments, you can horizontally scale an application up and down by changing the number of replicas. You can perform rolling updates and rollback.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-networking"&gt;Networking&lt;/h3&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The K8s network model defines how Pods communicate with each other, regardless of which Node they are running on.&lt;/li&gt;&#10;&lt;li&gt;Each Pod has its own unique IP address within the cluster. Any Pod can reach any other Pod using that Pod&amp;#8217;s IP address. This creates a virtual network that allows Pods to easily communicate with each other.&lt;/li&gt;&#10;&lt;li&gt;One type of K8s network plugin is CNI plugin. It has many flavours such as Calico. Each plugin has its own unique installation process. Kubenetes nodes will remain &lt;strong&gt;NotReady&lt;/strong&gt; until a network plugin is installed.&lt;/li&gt;&#10;&lt;li&gt;The K8s virtual network uses a DNS (e.g. a Kubeadm cluster uses CoreDNS pod in kube-system namespace) to allow Pods to locate other Pods and Services using domain names. The Pod DNS name follows this format: pod-ip-address.namespace.pod.cluster.local&lt;/li&gt;&#10;&lt;li&gt;A K8s NetworkPolicy is an object that allows you to control the flow of network communication to and from Pods so you can isolate traffic. NetworkPolicy can apply to Ingress (using from selector), Egress (using to selector) or both.&lt;/li&gt;&#10;&lt;li&gt;NetworkPolicy has an attribute podSelector to determine to which Pods in the namespace the NetworkPolicy applies, by selecting Pods by with Pod labels.&lt;/li&gt;&#10;&lt;li&gt;By default, Pods are considered non-isolated and completed open to all communication. If any NetworkPolidy selects a Pod, the Pod is considered isolated and will only be open to traffic allowed by NetworkPolicies.&lt;/li&gt;&#10;&lt;li&gt;A variety of selector can be used: podSelector, namespaceSelector, ipBlockSelector and port.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-services"&gt;Services&lt;/h3&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Services provide a way to expose an application running as a set of pods, so clients can access applications in an abstract way without needing to be aware of the application pods. In this model, client make requests to a Service, which routes traffic to its pods in a load-balanced fashion&lt;/li&gt;&#10;&lt;li&gt;Endpoints are the backend entities to which Services route traffic. If there are multiple Pods behind a service, each Pod will have an endpoint associated with the service.&lt;/li&gt;&#10;&lt;li&gt;Each service has a type that determines how and where service will expose your application.&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ClusterIP: expose application inside the cluster network&lt;/li&gt;&#10;&lt;li&gt;NodePort: expose application outside the cluster network&lt;/li&gt;&#10;&lt;li&gt;LoadBalancer: expose application outside thecluster network, but use an extermal cloud load balancer from cloud platform.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Services are assigned with DNS names. The FQDN follows this format: service.namespace.svc.cluster-domain.example, which is used by pods across namespaces&lt;/li&gt;&#10;&lt;li&gt;Pods within the same namespace can reference service simply by service name.&lt;/li&gt;&#10;&lt;li&gt;To manage external access to service, you can also use Ingress object. Ingress object is capable of providing more functionality than a simple NodePort Service, such as SSL termination, advanced load balancing, or name-based virtual hosting. You must install one or more Ingerss controller (many different implementations) to back up the ingress objects.&lt;/li&gt;&#10;&lt;li&gt;Ingress defines a set of routing rules. Each rule has a set of paths, each with a backend. Requests matching a path will be routed to its associated backend.&lt;/li&gt;&#10;&lt;li&gt;If a Service uses a named port, an ingress can also use the port&amp;#8217;s name (instead of port number) to choose to which port of a service it will route.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-storage"&gt;Storage&lt;/h3&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Volumes allow you to store data outside the container file system, while allowing the container to access the data at runtime. When Pod is gone, volumes do not persist.&lt;/li&gt;&#10;&lt;li&gt;Persistent Volumes are a slightly more advanced form of Volume. They allow you to treat storage as an abstract resource and consume it in Pods. PV can be provisioned separately by storage administrator, and they persist regardless of pod lifecycle. PV needs to be claimed by pods. PV uses a set of attributes to describe the underlying storage resource.&lt;/li&gt;&#10;&lt;li&gt;Both volumes and PVs each have a volume type: NFS, Cloud (AWS, Azure, GCP), ConfigMaps and Secrets, Simple Directory on node&lt;/li&gt;&#10;&lt;li&gt;Two volume types to distinguish:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;hostPath: store data in a specified directory on K8s node&lt;/li&gt;&#10;&lt;li&gt;emptyDir: store data in dynamically created location on the node. The directory exists only as long as the Pod exists on the node. The directory and the data are deleted as Pod is removed. This type is useful for simply sharing data between containers in the same pod.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Both volumes and PVs are specified under Pod, and individual containers must include volumeMounts object to map volume name to local mountPath&lt;/li&gt;&#10;&lt;li&gt;Storage Class object allow K8s admins to specify the types of storage services they offer on their platform. A key property is allowVolumeExpansion. This allows PVC to resize. At storage class level, there are two reclaim policies: Retain and Delete. The default is Delete.&lt;/li&gt;&#10;&lt;li&gt;PV has an attribute named persistentVolumeReclaimPolicy. This is reclaim policy at PV level. If the attribute is not defined, it is inherited from storage class. The persistentVolumeReclaimPolicy has three options. When PVC is deleted:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Retain: keeps all data but requires admin to manually reclaim the volume (i.e. delete PV, clean up data, delete storage asset)&lt;/li&gt;&#10;&lt;li&gt;Delete (cloud storage only): deletes both PV and the underlying storage resource automatically&lt;/li&gt;&#10;&lt;li&gt;Recycle: scrub (rm -rf /vol/) all data in the underlying storage resource, and allow the volume to be reused.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;PVC represents a user&amp;#8217;s request for storage resources. It defines a set of attributes similiar to those of a PV. When a PVC is created, it will look for a PV that is able to meet the requested criteria. If it finds one, it will automatically be bound to the PV. PVC can be mounted to a Pod&amp;#8217;s containers just like any other volume&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In general the CKA exam experience is quite positive and rewarding. In future posts I will shift focus on Kubernetes not only for the CKA exam, but also for keeping track of my learning.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Good luck with your CKA exam.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/04/public-key-infrastructure-pki/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Public Key Infrastructure (PKI) – Introduction&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/05/secure-web-application-deployment/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Secure web application deployment&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Basic Resource Object in Kubernetes 2 of 2</title><link>https://static.digihunch.com/2021/02/basic-resource-object-in-kubernetes-2-of-2/</link><pubDate>Mon, 08 Feb 2021 21:02:16 -0400</pubDate><guid>https://static.digihunch.com/2021/02/basic-resource-object-in-kubernetes-2-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;We continued from previous posting about resource object, starting from storage related ones. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/vol-128.png" alt=""/&gt;&lt;figcaption&gt;Volume&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes, we use the term volume to refer to a section storage device. There are many plugins, compliant to Container Storage Interface (CSI), to allow heterogeneous storage resources to be surfaced as volumes in Kubernetes. CSI allows storage driver to operate in parallel to the main Kubernetes code tree. Any driver that complies with CSI would work with any orchestration platform that requires CSI, such as Docker Swarm, Kubernetes. Three main resources in the storage system are: PV (persistent volumes), PVC (persistent volume claims), and SC (storage classes).&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/pv-128.png" alt=""/&gt;&lt;figcaption&gt;Persistent Volume&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Persistent Volumes (PV) allows you to map external storage onto the Kubernetes cluster. It is a representation of the external storage on the cluster. A single external storage volume can only be represented by a single PV. For example, you cannot have a 50GB external volume that has two 25GB PVs each representing half of it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;PV can be mounted in three options:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;RWO (ReadWriteOnce): allows single PVC to mount. This is common for block device.&lt;/li&gt;&lt;li&gt;RWM (ReadWriteMany): allows multiple PVCs to bind as read and write. This is common for file and object level access.&lt;/li&gt;&lt;li&gt;ROM (ReadOnlyMany): allows multiple PVCs to bind as read only. Think of it along the lines of ISO media.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that a PV can only be opened in one of the modes above. All connecting PVC (if multiple are allowed) will use that mode.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/pvc-128.png" alt=""/&gt;&lt;figcaption&gt;Persistent Volume Claim&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Persistent Volume Claims (PVC) act like tickets that authorize applications (Pods) to use a PV. Once a Pod has the PVC, it can bind the respective PV as a volume. You need to specify PV name when declaring a PVC to associate them. Pods do not act directly on PVs, they always act on the PVC object that is bound to the PV. When a PVC is released, two actions can be configured in the policy: Delete and Retain. The delete policy will delete the PV as well as associated storage resource on the external storage system. The retain policy will keep the associated PV object on the cluster as well as any data stored on the associated external assets.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The spec section of PVC object declaration must match the fields in the corresponding PV it binds to. For example access modes, capacity and storage class name.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/sc-128.png" alt=""/&gt;&lt;figcaption&gt;Storage Class&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Storage classes allow you to define different classes (or tiers) of storage using an external provisioner such as aws-ebs. This works well with cloud storage provider. As long as the plugin for storage backend is available, you can configure as many StorageClass object as you need, and even specify to encrypt them. Storage classes create PV dynamically, so you will need to create PVC object that reference the newly created storage class, in order to use cloud storage.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The whole purpose of storage class is to create PVs dynamically, for various storage backend/plugin. You just create the StorageClass object and use a plugin to tie it to a particular type of storage on a particular storage back-end. When matching PVCs appear, the StorageClass dynamically creates the required volume on the back-end storage system.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If a cluster has a default storage class, you can deploy a Pod using just PVC with PodSpec, without explicitly declare storage class per Pod. However, this is not recommended in production.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/cm-128.png" alt=""/&gt;&lt;figcaption&gt;ConfigMaps&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With modern application it is a good practice to decouple configurations from application execution environment. They are stored separately but brought together at runtime. ConfigMap (CM) allows you to store configuration data outside of a Pod, and dynamically inject the configuration data into a Pod at runtime. ConfigMaps are essentially key/value pairs, and each key/value pair is called an entry.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once data is stored in a ConfigMap, it can be injected into containers at run-time via one of the three methods:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;environment variables: updates to ConfigMap is not updated &lt;/li&gt;&lt;li&gt;arguments to the container&amp;#8217;s startup command (very limited)&lt;/li&gt;&lt;li&gt;files in a volume (most flexible): requires creating a ConfigMap volume in the Pod template and mounting. Eateries in the ConfigMap will appear in the container as individual files. You can make changes to entries after a container is deployed, and the change is seen in the file.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The application is unaware that the data originally came from a ConfigMap. Also note that ConfigMap is not to store sensitive data.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/secret-128.png" alt=""/&gt;&lt;figcaption&gt;Secret&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes Secrets let you store and manage sensitive information, such as passwords, OAuth tokens, and ssh keys. Storing confidential information in a Secret is safer and more flexible than putting it verbatim in a Pod definition or in a container image.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The name of a Secret object must be a valid DNS subdomain name. A Secret can be used with a Pod in three ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;As files in a volume mounted on one or more of its containers.&lt;/li&gt;&lt;li&gt;As container environment variable.&lt;/li&gt;&lt;li&gt;By the kubelet when pulling images for the Pod.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/ing-128.png" alt=""/&gt;&lt;figcaption&gt;Ingress&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ingress manages manages external access to the services in a cluster, typically HTTP. It may provide load balancing, SSL termination and name-based virtual hosting. Also, you must have an Ingress controller to satisfy an Ingress. Only creating an Ingress resource has no effect.You can choose from a number of Ingress controllers. Nginx is a common flavour.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/limits-128.png" alt="" width="128" height="124"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;By default, containers run with unbounded compute resources on a Kubernetes cluster. With resource quotas, cluster administrators can restrict resource consumption and creation on a namespace basis. Within a namespace, a Pod or Container can consume as much CPU and memory as defined by the namespace&amp;#8217;s resource quota. There is a concern that one Pod or Container could monopolize all available resources. A LimitRange is a policy to constrain resource allocations (to Pods or Containers) in a namespace.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A LimitRange provides constraints that can:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Enforce minimum and maximum compute resources usage per Pod or Container in a namespace.&lt;/li&gt;&lt;li&gt;Enforce minimum and maximum storage request per PersistentVolumeClaim in a namespace.&lt;/li&gt;&lt;li&gt;Enforce a ratio between request and limit for a resource in a namespace.&lt;br&gt;Set default request/limit for compute resources in a namespace and automatically inject them to Containers at runtime.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/quota-128.png" alt=""/&gt;&lt;figcaption&gt;Resource Quotas&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When several users or teams share a cluster with a fixed number of nodes, there is a concern that one team could use more than its fair share of resources. Resource quotas are a tool for administrators to address this concern.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A resource quota, defined by a ResourceQuota object, provides constraints that limit aggregate resource consumption per namespace. It can limit the quantity of objects that can be created in a namespace by type, as well as the total amount of compute resources that may be consumed by resources in that namespace.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/01/a-shallow-dive-into-artificial-intelligence/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;A shallow dive into Artificial Intelligence&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/02/interpret-census-data-from-statistics-canada/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Census Data from Statistics Canada&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Basic Resource Object in Kubernetes 1 of 2</title><link>https://static.digihunch.com/2021/01/basic-kubernetes-resource-object-1-of-2/</link><pubDate>Sat, 16 Jan 2021 22:13:00 -0400</pubDate><guid>https://static.digihunch.com/2021/01/basic-kubernetes-resource-object-1-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;For someone from a system administration background, it would be amazing to discover that Kubernetes provides a solution to every pain point in the traditional software deployment landscape. On the contrary, it also brings about a lot of complexity due to the types of resource objects introduced. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/pod-128.png" alt=""/&gt;&lt;figcaption&gt;Pod&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Pod is a shared execution environment for one or more containers. The containers running in a Pod share resources such as memory, volumes, network namespace (e.g. IP address, port range, hostname, routing table), UTS namespace (e.g. hostname) and IPC namespace (Unix domain sockets). Every Pod has its own IP address that is routable on the Pod network. All Pods connect to the same flat network called the Pod network.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A pod most commonly only contains a single container, which is considered a good practice, unless there is good reasons to put two containers in a single pod (sharing resource). One such good reason is to co-schedule tightly-coupled workloads (such as logging, sharing volume, etc). Within the Pod, the containers communicate with each other via localhost interface of the Pod. In service mesh model, there is also a proxy container in each application Pod. The proxy container handles all network traffic entering and leaving the Pod. Also, within the Pod, to avoid competing for resources, individual containers can have their own cgroup limits, which actively police resource usage.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Pods are mortal (composable). They come and go (with dynamic IPs), so application should not store state in Pods. Deploying a Pod is an atomic (all or nothing) operation. When a Pod is scheduled to a node, it enters the pending state while the container runtime on the node downloads images and starts any containers. Once&amp;#8217;s everything is ready, the Pod enters the running state.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We typically deploy Pods via higher-level controllers such as Deployments (to offer scalability and rolling updates), DaemonSets (to run one instance of a service on every node in the cluster), StatefulSets (for stateful application components), and CronJobs (for short-lived tasks that need to run at set times just like a Linux &lt;a href="https://static.digihunch.com/2018/05/cron-and-logrotate-in-centos/"&gt;cronjob&lt;/a&gt;).&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/deploy-128.png" alt=""/&gt;&lt;figcaption&gt;Deployments&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Deployment manages multiple replicas of the same Pod (via ReplicaSets). To follow best practice, you interact with Deployments instead of ReplicaSets, and use YAML file (declarative model). You can perform rolling update or rollback.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/rs-128.png" alt=""/&gt;&lt;figcaption&gt;ReplicaSets&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ReplicaSets provide self-healing and scaling capabilities to Pods. If a Pod fails, it will be replaced. If load increases, then the ReplicaSets creates new Pod. This is all implemented with a background reconciliation loop that is constantly checking whether the right number of Pod replicas are present on the cluster. If not, Kubernetes declares a red-alert condition, orders the control plan to bring up more replicas. The best practice however, is that you should not manage ReplicaSets directly. Instead, you should perform all actions against the Deployment object and leave the Deployment to manage ReplicaSets.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://storage.googleapis.com/cdn.thenewstack.io/media/2017/11/07751442-deployment.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/svc-128.png" alt=""/&gt;&lt;figcaption&gt;Service&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Pods themselves are mortal (IP churn) so it&amp;#8217;s a bad idea to talk directly to individual Pods. Service object provides stable and reliable networking for a set of dynamic Pods. Service gets its own stable IP address, stable port and stable DNS name. It can also load-balance request across the Pods.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Services are loosely coupled with Pods via labels and label selectors. You specify label selector for Service and labels on Pods when creating them. All the labels in label selector are used to select target Pods. Service acts as front-end, consisting of stable IP, DNS name and port, with Pods acting as backend, consisting of constantly changing Pods. Labels are simple yet extremely powerful. During blue-green update, you may use version label as a technique to control what backend pool is used behind Service object. For example, start with version=1, deploy version 2, remove version from label selector, and eventually add version=2 back to label selector, before phasing out the old Deployment.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Services learn Pod status via Endpoint object, more details to follow.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are several types of Service, the default being &lt;strong&gt;ClusterIP&lt;/strong&gt;. A ClusterIP Service has a stable IP address and port that is only accessible from inside the cluster. The ClusterIP gets registered against the name of the Service on the cluster&amp;#8217;s internal DNS service (implemented via coreDNS with Control plane Pods). This means that the ClusterIP only works within the cluster, not outside. The other type of Service is called a &lt;strong&gt;NodePort&lt;/strong&gt;, which is built on top of ClusterIP, but also enables access from outside of the cluster. The Service object has a reliable NodePort mapped to every node in the cluster. The NodePort value is the same on every cluster. Traffic from outside of the cluster can hit any node in the cluster on the NodePort and get through the the Pods.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Other types of Services include LoadBalancer and ExternalName. LoadBalancer Services integrate with load-balancers from cloud provider. They build on top of NodePort Services and allow clients on the internet to reach your Pods via the load balancer of cloud vendor. ExternalName Services route traffic to systems outside of your K8s cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For service discovery within the cluster, Kubelet program every container with the knowledge of the internal DNS (/etc/resolv.conf). The internal DNS service watches constantly the API server for new Services and automatically register them in the DNS. The other means of service discovery is through environment variables. However, in this method the Pods have no way of learning about new Services added to the cluster after the Pod itself is created.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/ep-128.png" alt=""/&gt;&lt;figcaption&gt;Endpoints&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Endpoints object is a dynamic list of all the healthy Pods on the cluster that match the Service&amp;#8217;s label selector. Each Service gets its own Endpoints objects for an up-to-date list of matching Pods. Kubernetes is constantly evaluating the Service&amp;#8217;s label selector against the currently list of healthy Pods on the cluster. Any new Pods that match the selector get added to the Endpoints object, and any Pods that disappear get removed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When sending traffic to Pods, via a Service, an application will query the cluster&amp;#8217;s internal DNS for the IP address of a Service, then sends the traffic to this stable IP address. Service then forwards it on to a Pod. Kubernetes-native application however, has the ability to query the Endpoints API directly, bypassing the DNS lookup and use of the Service&amp;#8217;s IP.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It requires a thorough understanding of Services, Endpoints and the service discovery mechanism to perform effective troubleshooting in Kubernetes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The aforementioned internal DNS service (we usually call it the &amp;#8220;cluster DNS&amp;#8221;) is implemented in the kube-system Namespace as a set of Pods managed by a Deployment called coredns. These Pods are fronted by a Service called kube-dns. The cluster DNS is constantly looking for new Services and automatically register their details (metadata.name). We might need to check the logs for each of the coredns Pods during troubleshooting. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The kubelet process on every node is watching the API Server for new Endpoints objects, when it sees them, it creates local networking rules that redirect ClusterIP traffic to Pod IPs, using &lt;a href="https://static.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/" class="rank-math-link"&gt;IPVS technology&lt;/a&gt; on Linux to manage these rules.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/ds-128.png" alt=""/&gt;&lt;figcaption&gt;DaemonSet&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A DaemonSet ensures that all (or some) Nodes run a copy of a Pod. As nodes are added to the cluster, Pods are added to them. As nodes are removed from the cluster, those Pods are garbage collected. Deleting a DaemonSet will clean up the Pods it created.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some typical uses of a DaemonSet are: cluster storage daemon on every node, logs collection daemon on every node, a node monitoring daemon on every node.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/hpa-128.png" alt=""/&gt;&lt;figcaption&gt;Horizontal Pod Autoscaler&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Horizontal Pod Autoscaler automatically scales the number of Pods in a replication controller, deployment, replica set or stateful set based on observed CPU utilization (or, with custom metrics support, on some other application-provided metrics). Note that Horizontal Pod Autoscaling does not apply to objects that can&amp;#8217;t be scaled, for example, DaemonSets.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Horizontal Pod Autoscaler is implemented as a Kubernetes API resource and a controller. The resource determines the behaviour of the controller. The controller periodically adjusts the number of replicas in a replication controller or deployment to match the observed average CPU utilization to the target specified by user.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are more details about HPA &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/" class="rank-math-link"&gt;here&lt;/a&gt; and &lt;a href="https://cloud.google.com/kubernetes-engine/docs/concepts/horizontalpodautoscaler" class="rank-math-link"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/blob/master/icons/png/resources/labeled/sts-128.png?raw=true" alt="sts-128.png"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;StatefulSets are designed for stateful application, which creates and saves valuable data. The three properties that form the state of a Pod are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Pod names (&amp;lt;StatefulSetName&amp;gt;-&amp;lt;Integer&amp;gt;)&lt;/li&gt;&lt;li&gt;DNS hostnames&lt;/li&gt;&lt;li&gt;volume bindings&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;They are sometimes referred to as the Pods &lt;em&gt;sticky ID&lt;/em&gt;. StatefulSets ensures that these are all predictable and persistent. For example, failed Pods managed by a StatefulSet will be replaced by new Pods with the exact same Pod name, the exact same DNS hostname, and the exact same volumes, even if the replacement Pod is started on a different cluster Node.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that StatefulSets create one Pod at a time, and always wait for previous Pods to be &lt;em&gt;running and ready&lt;/em&gt; before creating the next. Scaling operations are also governed by the same ordered startup rules. This is different from Deployments that use a ReplicaSet controller to start all Pods at the same time, causing potential race conditions. The way StatefulSet controllers do their own self-healing and scaling is architecturally different to Deployments which use a separate ReplicaSet controller for these operations. The reason it is a game changer to know the order in which Pods will be scaled down, as well as that Pods will not be terminated in parallel, is because clustered apps that store data are usually at high risk of losing data if multiple replicas go down at the same time.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Deleting a StatefulSet does not terminate Pods in order. So you may want to scale a StatefulSet to 0 replicas before deleting it. You might also set 10 seconds grace period before terminating to allow applications a chance to flush local buffers and safely commit any writes still in flight.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes, Volumes are decoupled from Pods via PersistentVolumes and PersistentVolumeClaims. So volumes have separate lifecycles to Pods and can survive Pod failures and termination operations. When a StatefulSet Pod is created, any volumes it needs are created at the same time and named in a way to connect them to the right Pod. Any time a StatefulSet Pod fails or is terminated, the associated volumes are unaffected. This allows replacement Pods to attach to the same storage as the Pods they&amp;#8217;re replacing, even if the replacement Pod is scheduled to a different cluster Node. Similarly, if a StatefulSet Pod is detected as part of a scale-down operation, subsequent scale-up operations will attach new Pods to the existing volumes that match their names.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since each StatefulSet Pod needs its own unique storage, hence its own PVC, this can be done by volumeClaimTemplate, which dynamically creates a PVC each time a new Pod replica is dynamically created. This eliminates the hassle to have to pre-create a unique PVC for every potential StatefulSet Pod.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/kubernetes/community/raw/master/icons/png/resources/labeled/ns-128.png" alt=""/&gt;&lt;figcaption&gt;Namespaces&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Namespaces allows you to partition resource objects. For example, you may create a Namespace called prod and dev. Object names must be unique within Namespaces but not across Namespaces.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/12/ansible-tower-lab-environment-on-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AWS CDK example in Typescript – provision an AWX server&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/01/blockchain-and-di-fi/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Blockchain and DeFi&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>