<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tech Reviews on Digi Hunch</title><link>https://www.digihunch.com/category/tech-review/</link><description>Recent content in Tech Reviews on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Fri, 02 May 2025 10:58:45 -0400</lastBuildDate><atom:link href="https://www.digihunch.com/category/tech-review/index.xml" rel="self" type="application/rss+xml"/><item><title>SageMaker AI and Networking</title><link>https://www.digihunch.com/2025/04/sagemaker-ai-and-networking/</link><pubDate>Tue, 01 Apr 2025 00:53:00 -0400</pubDate><guid>https://www.digihunch.com/2025/04/sagemaker-ai-and-networking/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-sagemaker-networking.webp" alt="Featured image of post SageMaker AI and Networking" /&gt;&lt;p class="wp-block-paragraph"&gt;Machine learning workflows are highly experimental. To smooth out the processes, Amazon SageMaker AI packages many features as managed services. As an infrastructure specialist, I want to remain compliant. At a basic level, compliant architecture means multi-account structure and hub-and-spoke VPC topology in a &lt;a href="https://www.digihunch.com/2022/12/landing-zone-in-aws/"&gt;landing zone&lt;/a&gt;. While the multi-account &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/sagemaker-studio-admin-best-practices/operating-model.html"&gt;operating model&lt;/a&gt; for SageMaker is well documented, these ML managed services obscure the network configuration. I&amp;#8217;m always looking for low-level insights on where the computing activity is happening and how the application traffic flows in and out of our VPCs. I don&amp;#8217;t always get straight answers and I often have to experiment them out. This post is a review of the networking aspects of SageMaker AI I recently learned.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Naming Shenanigans&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Unfortunately, I have to start with how AWS has renamed these services, so the terms remain clear throughout the post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Dec 2024, &lt;a href="https://aws.amazon.com/blogs/aws/introducing-the-next-generation-of-amazon-sagemaker-the-center-for-all-your-data-analytics-and-ai/"&gt;AWS&lt;/a&gt; renamed SageMaker to SageMaker AI. The name of SageMaker going forward represents the overarching AWS service for machine learning, data, analytics and generative AI. &lt;a href="https://www.youtube.com/watch?v=iVIcl-546qA"&gt;Here&lt;/a&gt; is a video for clarification. I&amp;#8217;m not a fan of how they repurpose the names. As of date there are still a lot of content referencing SageMaker AI as SageMaker, whose meaning has changed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If that&amp;#8217;s not enough, here&amp;#8217;s another one. The SageMaker Studio launched originally in 2019 for model development. In 2023 that became SageMaker Studio classic, in favour of the newly launched studio, taking the name of &lt;a href="https://aws.amazon.com/blogs/machine-learning/experience-the-new-and-improved-amazon-sagemaker-studio/"&gt;SageMaker studio&lt;/a&gt;. In Feb 2025, AWS deprecated SageMaker Studio classic. You can only create SageMaker Studio in SageMaker AI. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At the SageMaker level, AWS launched &lt;a href="https://aws.amazon.com/blogs/big-data/an-integrated-experience-for-all-your-data-and-ai-with-amazon-sagemaker-unified-studio/"&gt;SageMaker Unified Studio&lt;/a&gt;, the all-encompassing development environment for data analytics, generative AI, and so on. In this post though, we talk about many features under SageMaker AI and SageMaker Studio. While the service UIs are picturesque, we remain focused on two questions: how these services interact with resources on our VPCs, and how they connect to the Internet.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Workload Categories&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We divide machine learning workload into three categories, based on network connectivity pattern: notebooks, model hosting, and pipeline jobs.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Notebooks are where data scientists carry out experiments by running experimental scripts on performing hardwares (depending on the tasks), usually within IDE application as Jupyter Labs, Code Editors. Data scientist users may perform any machine learning related activities such as model evaluation, etc. It is possible that one part of a notebook only requires consumer grade CPU and another part of the notebook program requires a performant GPU. It all depends on the nature of the program code.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Model is the key artifact in Machine Learning workflows. Models themselves are files stored in S3 buckets. The machine learning engineers perform two most common activities. They train the model, and feed the model with unseen data for new output (inference). In simple workflows, data scientists may build, train a model and run inference all from within the same notebook. As the experiment concludes and the team wants to operationalize the inference, it makes senses the run inference in a client-server architecture. This calls for a inference endpoint acting as the server, backed by the trained model, operating on a single or an autoscaling group of instances. The client application feeds the endpoint with unseen data, often using REST API calls, and expects inference results.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Pipeline steps like training do not operate on a server. They are similar to Notebook workloads. The difference is that pipeline steps are headless executions. The steps are non-interactive without engaging the Studio GUI. Many other types of activities in machine learning are in similar pattern, such as model evaluation, model optimization or any general processing such as a Python script. I consider them similar to training activities. Since we orchestrate these headless activities with pipelines (e.g. SageMaker pipeline), and each step may execute on some specialized instance depending on the computing requirements. Collectively, I call these activities the pipeline jobs.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s look at these workloads through the networking lens.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Studio Notebooks &lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most common Studio app is some kind of notebooks, such as Jupyper Lab. However, this category can generally include all kinds of SageMaker Studio apps, e.g. Canvas, Code Editor. I use the term Studio app and Studio notebook interchangeably but the APIs mostly refer to these as apps, such as &lt;a href="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateDomain.html#sagemaker-CreateDomain-request-AppNetworkAccessType"&gt;AppNetworkAccessType&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the app, a user may create one or more spaces each specifying the backing instance type. The configuration that influences the instances&amp;#8217; networking setup is in SageMaker AI domain&amp;#8217;s Network Setting. There are two parts of this &lt;a href="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateDomain.html#sagemaker-CreateDomain-request-AppNetworkAccessType"&gt;AppNetworkAccessType&lt;/a&gt; setting:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Network Mode (also called AppNetworkAccessType in AWS SDK): &#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;PublicInternetOnly (default): only EFS traffic goes through the specified VPC and subnets. Other studio traffic (e.g. API calls) goes through the Internet Gateway of the VPC that the studio manages internally&lt;/li&gt;&#10;&lt;li&gt;VpcOnly: all studio traffic goes through the specified VPC and subnets. This delegates the responsibility of connectivity to endpoints to the VPC&amp;#8217;s owner. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;VPC and Subnet: to place EFS mount points on. Also route other studio traffic in VpcOnly mode.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are one diagrams on the &lt;a href="https://docs.aws.amazon.com/sagemaker/latest/dg/studio-notebooks-and-internet-access.html"&gt;documentation&lt;/a&gt; for each network mode (PublicInternetOnly on the left; VPCOnly on the right):&lt;/p&gt;&#10;&lt;figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex"&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="464" data-id="13006" src="https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-1.webp" alt="" class="wp-image-13006" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-1.webp 1024w, https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-1-300x136.webp 300w, https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-1-768x348.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="474" data-id="13005" src="https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-2.webp" alt="" class="wp-image-13005" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-2.webp 1024w, https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-2-300x139.webp 300w, https://www.digihunch.com/wp-content/uploads/2025/04/network-mode-2-768x356.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagrams (as of March 2025) are not accurate because the PublicInternetOnly mode also has a domain managed ENI per app space. The VpcOnly mode is when the ML do not like the idea that a Notebook instance can bypass centrally managed Internet path. The team must ensure the endpoints are reachable, either via Internet, or via routable VPC endpoint (e.g. Gateway Endpoint for S3 and Interface endpoint for the rest). In a hub-and-spoke setup it might be another dedicated VPC that provides the interface endpoints centrally.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_DomainSettings.html"&gt;domain settings&lt;/a&gt; include a few configurations on the underlying instance. For example, &lt;strong&gt;SecurityGroupIds&lt;/strong&gt; specifies the security groups associated with the ENIs. &lt;strong&gt;DockerSettings&lt;/strong&gt; enables Docker daemon on the instance, allowing users to test container workload in &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/12/sagemaker-studio-local-mode-docker/"&gt;local mode&lt;/a&gt;. Note that in the more constraint VPC-only mode, Docker pull and push operations outside of Amazon Elastic Container Registry aren&amp;#8217;t supported. To pull or push from ECRs users also need to white-list account IDs of the private ECRs in the &lt;strong&gt;VpcOnlyTrustedAccounts&lt;/strong&gt; setting.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In comparison with the other two types of workload, the network traffic for studio notebooks are the easiest to control because they are all configured at the SageMaker AI domain level. Once users with user profiles under a domain creates a notebook, the domain or user profile determines the network mode, and subnet values and security groups. Users themselves cannot change these settings.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Inference Endpoint&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The machine learning realm has a few established programming frameworks to host a model file behind an inference endpoint. For example, TensorFlow, PyTorch, Scikit-learn, and even Fast API. Amazon SageMaker AI supports many such frameworks and makes it straightforward. Managing the frameworks requires complex dependency management, a typical use case of containers. Apart from choosing a proper container image, user also selects instance types. These machine learning special purpose instances are pricier than their commodity counterpart. In low-traffic workflows, users may provision inference endpoint on-demand and use it in a controlled time-window, or just use serverless inference endpoint if the model supports it. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When creating inference endpoint, the CreateModel API is used. Under the VpcConfig attribute, two parameters are at play: Network Isolation and VPC-Subnet configuration. I summarize them as below based on the &lt;a href="https://docs.aws.amazon.com/sagemaker/latest/dg/interface-vpc-endpoint.html"&gt;documentation&lt;/a&gt;:&lt;/p&gt;&#10;&lt;figure class="wp-block-table"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Network Isolation&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;VPC-Subnet Configuration&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td rowspan="2"&gt;Disabled&lt;/td&gt;&lt;td&gt;Not specified&lt;/td&gt;&lt;td&gt;SageMaker AI containers are able to access external service and resources on the public Internet; but not able to access resources inside your VPC &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Specified&lt;/td&gt;&lt;td&gt;SageMaker AI containers communicate with resources inside your VPC through an ENI (Elastic Network Interface). Users are responsible for managing network access to your VPC and Internet.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan="2"&gt;Enabled&lt;/td&gt;&lt;td&gt;Not specified&lt;/td&gt;&lt;td&gt;SageMaker AI container cannot communicate with resources inside your VPC or on the public Internet&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Specified&lt;/td&gt;&lt;td&gt;The download and upload operations are routed through your VPC, but the inference (and training) containers themselves continue to be isolated from the network, and do not have access to any resource within your VPC or on the internet.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Network Isolation option governs the container connectivity option. If we do not expect the inference activity to make outgoing network calls (except for downloading artifact and packages), then we should enable network isolation. On the other hand, if the inference container needs resources on VPC or on the Internet, disable network isolation. Either way, we specify the VPC so that we manage the routing through VPC. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a compliant networking setup where Internet access must be centralized, the VPC-Subnet configuration must always be configured. The network isolation value depends on the nature of inference workload. However, what seems to be missing in the Studio UI is the activity to enforce that VPC-Subnet configuration is always specified.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As a workaround, we can use the SageMaker Domain&amp;#8217;s IAM role to contain such attempt at API level. Below is an example of deny policy:&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-js" data-lang="js"&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:#e6db74"&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&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; &lt;span style="color:#e6db74"&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sagemaker:CreateModel&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:#e6db74"&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;BoolIfExists&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sagemaker:VpcSubnets&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;false&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; &lt;span style="color:#e6db74"&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Deny&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;DenyModelcreationIfNotOnVPC&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; &lt;span style="color:#e6db74"&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sagemaker:CreateModel&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:#e6db74"&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;ForAnyValue:StringNotEquals&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sagemaker:VpcSubnets&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;subnet-999999999999999&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;subnet-111111111111111&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; },&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Deny&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;DenyModelcreationIfAnySpecifiedSubnetIsNotIntended&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;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can use this policy in conjunction with the AmazonSageMakerFullAccess managed policy. The request to create a mode gets denied, either if VpcSubnets are not specified, or they are but not from the preset list of subnet IDs. Once the user selects subnets, corresponding ENIs will get created in the subnets too and user needs to specify security groups for the ENIs.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Enforcing in IAM policy requires that the user who creates endpoint either on SageMaker Studio UI or SageMaker SDK must know the exact subnet IDs as well as appropriate security groups. This requires access to the VPC and can turn into an operation pain point if the users are not well versed with networking. Ideally subnet configuration should also be enforceable at the domain level. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Pipeline Jobs&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Most machine learning jobs do not need to function behind an endpoint (i.e. server-side), for example, training, labeling job, model optimization, hyper parameter tuning, etc. In operation, we often use a pipeline to orchestrate these short-lived, non-interactive, headless jobs. Therefore, I simply refer to them as pipeline jobs. They sometimes rely on special purpose instance types. In most cases, they need access to either the Internet or other resources available via customer VPC, a connectivity pattern similar to that of interface endpoints.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To create such resources, SageMaker AI domain user either operate on SageMaker Studio, or program with SageMaker SDK. To make it easy to specify network isolation and subnet configurations, the SDK even has a class for &lt;code&gt;NetworkConfig&lt;/code&gt; that can pass to many types of processors (steps).&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-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; sagemaker.network &lt;span style="color:#f92672"&gt;import&lt;/span&gt; NetworkConfig&#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;security_group_ids &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;sg-#&amp;#39;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;subnets &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;subnet-#&amp;#39;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;enable_network_isolation &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&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;network_config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; NetworkConfig(&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; security_group_ids&lt;span style="color:#f92672"&gt;=&lt;/span&gt;security_group_ids, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subnets&lt;span style="color:#f92672"&gt;=&lt;/span&gt;subnets, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; enable_network_isolation&lt;span style="color:#f92672"&gt;=&lt;/span&gt;enable_network_isolation&#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;script_processor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ScriptProcessor(&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image_uri&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;my-script-processor-image&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; command&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;python3&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;script.py&amp;#39;&lt;/span&gt;],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; instance_type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;ml.m5.large&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; role&lt;span style="color:#f92672"&gt;=&lt;/span&gt;role,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; network_config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;network_config&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&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 &lt;code&gt;network_config&lt;/code&gt; parameter exists as an &lt;a href="https://aws-sagemaker-mlops.github.io/sagemaker-model-building-pipeline-definition-JSON-schema"&gt;argument&lt;/a&gt; in the creation method of many other resources via SDK. However, the SageMaker UI domain does not have a mechanism to enforce it one way or another. Leaving this option open to users is not what every organization wants either. We could exercise control as much as we can with condition keys such as &lt;code&gt;sagemaker:VpcSubnets&lt;/code&gt; in the deny policy for SageMaker IAM role as the example above shows. The Service Authorization Reference &lt;a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonsagemaker.html"&gt;document&lt;/a&gt; lists out in which SageMaker SDK calls the &lt;code&gt;sagemaker:VpcSubnets&lt;/code&gt; condition key (or equivalent) exists. A proactive IAM &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html"&gt;policy&lt;/a&gt; to safeguard all the applicable SDK calls would be helpful as a workaround to the missing enforceability at domain level for SageMaker AI.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="1009" src="https://www.digihunch.com/wp-content/uploads/2025/04/pipeline-setting.webp" alt="" class="wp-image-13009" style="width:504px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/pipeline-setting.webp 1024w, https://www.digihunch.com/wp-content/uploads/2025/04/pipeline-setting-300x296.webp 300w, https://www.digihunch.com/wp-content/uploads/2025/04/pipeline-setting-768x757.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that on the SageMaker Studio&amp;#8217;s Pipeline tool there is a Network configuration seemingly for the pipeline. However, the &lt;a href="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreatePipeline.html#sagemaker-CreatePipeline-request-PipelineDefinition"&gt;CreatePipeline&lt;/a&gt; SDK call does not have an argument about network configuration. The PipelineDefinition argument requires a JSON format input to define the pipeline configuration and the network configuration is defined per step in the definition. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another perspective to look at this issue is how we can give a pipeline step (or in general any job runtime) the flexibility to connect to Internet, and in the mean time remain in control of its network connectivity. It depends on the intended security posture but we mainly look at these two questions on the requirement:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;can the job runtime access the VPC?&lt;/li&gt;&#10;&lt;li&gt;can the job runtime access the Internet on its own path?&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Depending on the answer, we can configure a Job in three ways:&lt;/p&gt;&#10;&lt;figure class="wp-block-table"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Configuration&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;Neither #1 or #2 are allowed&lt;/td&gt;&lt;td&gt;Most secure but might be overly restrictive because the job runtime may need to download artifacts. This requires enabling Network Isolation and specify VPC-Subnet configuration.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;Either #1 or #2 is allowed, but not both at the same time.&lt;/td&gt;&lt;td&gt;Exclusively allowing #1 is more secure because the VPC can manage access to Internet for the job runtime. Exclusively allowing #2 breaks the central Internet access pattern and should not be allowed if central Internet access is a compliance requirement.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;Both #1 and #2 are allowed at the same time&lt;/td&gt;&lt;td&gt;This configuration should not be made possible due to exfiltration risk. &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This classification of network configuration for ML pipeline job, is quite similar to DevOps pipeline job agent (think of Azure DevOps agent or Terraform agents). The user may use service provider&amp;#8217;s agent which come with its own Internet access, or choose to self-host the agent to allow access to VPC but the VPC&amp;#8217;s owner is then responsible for managing Internet routing through the custom VPC. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;SageMaker AI makes c impossible, which is good. SageMaker administrator needs to evaluate the requirement between a and b and determine how to enforce it with IAM policy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As an infrastructure security specialist, I investigated networking options in SageMaker AI. When any user is performing any task in SageMaker AI, I am concerned with two questions:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;How does the instance behind the endpoint connects to the internet and to our VPCs;&lt;/li&gt;&#10;&lt;li&gt;How do we enforce the connectivity pattern (enforceability);&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For the studio notebooks workload, the control is through SageMaker domain. For Inference endpoint and pipeline jobs, the control is reactive. We use IAM policies and role to deny attempts to create resources with bad network configuration. The limitation is that the SageMaker users have to know what network configuration options are available.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As a result, the infrastructure security specialist must clearly define the required network configuration, and communicate it out to SageMaker AI users.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2025/02/the-most-cost-effective-web-and-email-hosting/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;The Leanest Web and Email Hosting&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2025/04/training-and-inference-in-sagemaker-ai/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Training and Inference in SageMaker AI&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Firewall Deployment Patterns</title><link>https://www.digihunch.com/2024/11/firewall-deployment-patterns/</link><pubDate>Sat, 16 Nov 2024 16:24:15 -0400</pubDate><guid>https://www.digihunch.com/2024/11/firewall-deployment-patterns/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-fw-deploy.webp" alt="Featured image of post Firewall Deployment Patterns" /&gt;&lt;p class="wp-block-paragraph"&gt;The Hub-and-Spoke topology is the most common topic in the discussion for building cloud infrastructure design. This topology appeared in both AWS and Azure design papers and had been around as a very important option in physical networking design. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The AWS whitepaper &lt;a href="https://docs.aws.amazon.com/pdfs/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/building-scalable-secure-multi-vpc-network-infrastructure.pdf"&gt;Building a Scalable and Secure Multi-VPC AWS Network Infrastructure &lt;/a&gt;has thorough discussion on the topology. This topology often feature a Transit Gateway as the hub. In addition to workload VPCs, the network topology often includes some special-purpose VPCs, such as &lt;a href="https://github.com/aws-samples/landing-zone-accelerator-on-aws-for-cccs-medium/blob/main/architecture-doc/readme.md#632-endpoint-vpc"&gt;interface endpoints&lt;/a&gt; VPC, or &lt;a href="https://github.com/aws-samples/landing-zone-accelerator-on-aws-for-cccs-medium/tree/main/architecture-doc#636-central-vpc"&gt;shared tooling &lt;/a&gt;VPCs. One of the special-purpose VPC is the inspection VPC. It is a key design area to suit the need of inspection and traffic management for the business and the design may vary a lot depending on the available inspection tools such as a Firewall appliance. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Inspection Requirements&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most common situation with an enterprise is connecting with on-prem networking. Options including Direct Connect, site-to-site IPsec or SD-WAN overlay. The business decides whether and at what level they would like to inspect the traffic between on-prem and their VPCs. Here is an example.&lt;/p&gt;&#10;&lt;figure class="wp-block-table"&gt;&lt;table class="has-fixed-layout"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Connectivity&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Inspection Requirement&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Between Workload VPCs (East-West)&lt;/td&gt;&lt;td&gt;No inspection&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Between a workload VPC and a special-purpose VPC&lt;/td&gt;&lt;td&gt;Normal Inspection&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ingress Traffic from Internet to Workload VPC&lt;/td&gt;&lt;td&gt;Deep Packet Inspection&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Egress Traffic from Workload VPC to Internet&lt;/td&gt;&lt;td&gt;Deep Packet Inspection&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Between Workload VPC and on-prem networking over Direct Connect&lt;/td&gt;&lt;td&gt;Normal Inspection&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;#8230;&amp;#8230;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With a normal inspection, the firewall appliance only checks the information in the packet&amp;#8217;s header, such as the source and destination IP addresses, port number, etc. With deep packet inspection, the appliance examins a larger range of metadata as well as the data in each packet. DPI provides a more effective mechanism to perform network packet filtering and find otherwise hidden threats. It is however an expensive operations from a performance standpoint. Ultimately the business makes the call but it is important to identify ALL connectivity scenarios in this phase and explicitly document the decision and rationales. They can choose from an NGFW product or the Network Firewall service from AWS, depending on capability required.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Inspection Architecture&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At minimum, inspection is required for ingress and egress traffic to and from workload VPC. The design must account for both routing and inspection. Many would use the same VPC for ingress/egress traffic and for inspection. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is also possible to separate these two purposes into two different dedicated VPCs: an inspection VPC that hosts firewall services or appliances, and an ingress/egress VPC that directs traffic from and to the Internet but we must route the traffic to the inspection appliance. If all traffic to be inspected has to be routed through the Transit Gateway both ways, the cost would be high. In 2020 AWS introduced Gateway Load Balancer (GWLB) to address this use case. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="351" src="https://www.digihunch.com/wp-content/uploads/2024/11/fw-gwlb.webp" alt="" class="wp-image-12996" srcset="https://www.digihunch.com/wp-content/uploads/2024/11/fw-gwlb.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/11/fw-gwlb-300x103.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/11/fw-gwlb-768x263.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/integrate-your-custom-logic-or-appliance-with-aws-gateway-load-balancer/"&gt;recommended pattern&lt;/a&gt; using GWLB allows you to place firewall appliance and a GWLB in one VPC, and place the GWLB endpoint (GWLBE) in a different VPC. The connectivity between GWLBE and GWLB is backed by HyperPlane, a technology that also enables other endpoint service such as PrivateLink. The connectivity between GWLB and the appliance take place with &lt;a href="https://en.wikipedia.org/wiki/Generic_Network_Virtualization_Encapsulation"&gt;Geneve&lt;/a&gt; encapsulation. This pattern places any appliance behind an endpoint, so long as the appliance supports Geneve protocol. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The GWLB technology enables a number of inspection patterns based on distributed ingress paths, as summarized in &lt;a href="https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/distributed-inspection-architectures-gwlb-ra.pdf"&gt;this&lt;/a&gt; document. Distributed ingress/egress means each workload VPC can have their own Internet Gateway and NAT gateways. They must configure their route table so as to send the traffic via GWLBEs to inspection appliances. In general, I recommend this pattern over the centralized &lt;a href="https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/centralized-ingress-with-alb-and-ec2-target-ra.pdf?did=wp_card&amp;amp;trk=wp_card"&gt;ingress&lt;/a&gt;/&lt;a href="https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/NAT-gateway-centralized-egress-ra.pdf?did=wp_card&amp;amp;trk=wp_card"&gt;egress&lt;/a&gt; patterns where only the inspection VPC can take ingress traffic from Internet Gateway. The &lt;a href="https://d1.awsstatic.com/events/reinvent/2021/Network_architectures_for_inbound_traffic_inspection_REPEAT_NET311-R1.pdf"&gt;Network architectures for ingress traffic inspection&lt;/a&gt; presentation from 2021 ReInvent covered this topic as well, especially about the scaling benefit of distributed ingress.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Firewall deployment patterns&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The firewall deployment pattern available differ between vendors and the requirements. Since the GWLB pattern places appliances behind the GWLB, the appliances rely on Geneve traffic that GWLB forward over. Some vendors may argue that this pattern keeps the NGFW product from performing other tasks that do not support Geneve traffic. One example is Network Address Translation. The native NAT gateway services is very expensive (consider &lt;a href="https://fck-nat.dev/stable/"&gt;fck-nat&lt;/a&gt; as an alternative for NAT). Many clients want to use the NAT feature of the NGFW product. The architecture therefore has to be adjusted in favour of centralized egress. Review &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/best-practices-for-deploying-gateway-load-balancer/"&gt;this post &lt;/a&gt;about one-arm mode and two-arm mode.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If we have to go with central ingress/egress anyways, there are still numerous options. Take FortiGate for example, while the GWLB pattern of deployment is &lt;a href="https://github.com/fortinet/fortigate-terraform-deploy/tree/main/aws/7.6/gwlb-transit"&gt;supported&lt;/a&gt;, other available options include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/fortinet/fortigate-terraform-deploy/tree/main/aws/7.6/transitgwy"&gt;Traditional pattern&lt;/a&gt; with multiple interfaces across different subnets in the inspection VPC (L3 mode)&lt;/li&gt;&#10;&lt;li&gt;Integration with Transit Gateway using Transit Gateway &lt;a href="https://github.com/fortinet/fortigate-terraform-deploy/tree/main/aws/7.6/transitgwyconnect"&gt;Connect Attachment&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Integration with Transit Gateway using Transit Gateway &lt;a href="https://community.fortinet.com/t5/Blogs/FortiGate-in-AWS-Landing-Zone-Accelerator/ba-p/339107"&gt;VPN Attachment&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I regard the first option as traditional because it does not &lt;span style="text-decoration: underline;"&gt;directly&lt;/span&gt; integrate with Transit Gateway and it is very similar to how we deploy them in a physical networking environment. Fortigate refers to it as &lt;a href="https://docs.fortinet.com/document/fortigate/7.4.0/ips-architecture-guide/756476/l3-nat-route-mode"&gt;L3 (NAT/route) mode&lt;/a&gt;. In this mode the Firewall appliance can also influence network routing. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="680" src="https://www.digihunch.com/wp-content/uploads/2024/11/fgt-deploy.webp" alt="" class="wp-image-12997" srcset="https://www.digihunch.com/wp-content/uploads/2024/11/fgt-deploy.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/11/fgt-deploy-300x199.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/11/fgt-deploy-768x510.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second and the third options are similar except for different types of Transit Gateway attachments are used. The reason to directly integrate with Transit Gateway is so that the Transit Gateway can route the traffic for inspection therefore no need for a Gateway Load Balancer, and thus no dependency on the firewall features supporting Geneve. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second option builds a GRE (Generic Routing Encapsulation) tunnel over a Transit Gateway Connect attachment as the transport tunnel, and uses BGP to exchange routes between the Transit Gateway and the appliance. It treats the firewall instances as &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/migrating-sd-wan-appliances-to-aws-transit-gateway-connect/"&gt;SD-WAN appliance&lt;/a&gt; and has performance benefit. The third option uses VPN attachment with the main benefit of encryption if it is part of compliance requirement.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Rules&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The rule configuration for Firewall is critical to the operation of the entire multi-VPC network configuration. Unfortunately, there is no standard with the rule syntax across majore NGFW vendors, leading to challenges for customers to swap vendors. Most flavours of rules have common elements such as&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Action (Pass, drop, or alert)&lt;/li&gt;&#10;&lt;li&gt;Source and Destination&lt;/li&gt;&#10;&lt;li&gt;Protocol and Port&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A very common open-source firewall rule syntax is the &lt;a href="https://docs.suricata.io/en/latest/index.html"&gt;Suricata&lt;/a&gt;-compatible format. One important adopter is the AWS Network Firewall, which supports both stateful and stateless rule groups. With stateful rule group, there are two options for how the Suricata engine &lt;a href="https://docs.aws.amazon.com/network-firewall/latest/developerguide/suricata-rule-evaluation-order.html"&gt;evaluates rules&lt;/a&gt;. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;With &amp;#8220;Action Order&amp;#8221; option, Suricata engine evaluates the rules in the order of: pass, drop, reject and alert. You can use the priority attribute to influence evaluation; &lt;/li&gt;&#10;&lt;li&gt;With strict order, the rules are evaluated in the order of the rule definition;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to be aware of the rule evaluation order since it impacts the firewall behaviour deeply.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the networking infrastructure design, ingress and egress routing are the most critical one-way door decision. This decision must account for both routing and inspection. While there are many options, we usually start with capturing the key requirements. In this post we reviewed how to approach the requirement, a key technology Gateway Load Balancer and some firewall deployment patterns with FortiGate as an example. The approach is similar for other NGFW vendors, such as &lt;a href="https://www.paloaltonetworks.com/resources/guides/aws-transit-gateway-deployment-guide"&gt;Palo Alto&lt;/a&gt;, &lt;a href="https://checkpoint.awsworkshop.io/"&gt;Check Point&lt;/a&gt; or &lt;a href="https://catalog.us-east-1.prod.workshops.aws/workshops/38565e8c-3a5f-4e93-8412-5fdec23744ca/en-US"&gt;Cisco&lt;/a&gt; Secure Firewall. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2024/10/choosing-cloud-certifications-wisely/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cloud Certifications for Learning?&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2025/02/the-most-cost-effective-web-and-email-hosting/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;The Leanest Web and Email Hosting&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Cloud Certifications for Learning?</title><link>https://www.digihunch.com/2024/10/choosing-cloud-certifications-wisely/</link><pubDate>Wed, 09 Oct 2024 16:03:29 -0400</pubDate><guid>https://www.digihunch.com/2024/10/choosing-cloud-certifications-wisely/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-cloud-learning.webp" alt="Featured image of post Cloud Certifications for Learning?" /&gt;&lt;p class="wp-block-paragraph"&gt;Certification count is a measurement of capability of a consulting practice. At individual level it sometimes serves to navigate the learning journey, especially for engineers aspiring to advance to an adjacent technical area, for example, a network engineer moving to cloud engineer, or a data engineer transitioning to machine learning engineer. Personally I don&amp;#8217;t find certifications the most efficient way to learn. However, it is the most measurable way. The market has a plethora of choices for certifications but the quality is not very consistent. Through years of studying for different certificates, I have learned to treat certificates as necessary evil and be extremely cautious before investing time to it. After I sat another exam yesterday, I decided to write down my views of certifications and alternative ways of studying a topic in cloud technologies.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Dosage of Marketing&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A few years ago, I sat the Azure exams on &lt;a href="https://www.digihunch.com/2021/01/a-shallow-dive-into-artificial-intelligence/" target="_blank" rel="noreferrer noopener"&gt;AI fundamentals&lt;/a&gt; (AI-900) and AI engineer associate (AI-102). Out of the two I found the fundamentals one better, despite of lower level. That is because AI-900 focuses on the core concepts that are broadly useful. In contrast, the AI-102 exam, at least at that time, felt like a marketing exam all about what managed AI services Azure has for what kinds of problems. I’m not a fan of marketing exams but admittedly all these exams by cloud service providers contain some dose of marketing. You just can&amp;#8217;t completely avoid marketing from these exams. It’s just a matter of how much. My first principle: be wary of the marketing scheme behind the exam.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Why is this important? We choose an exam because we want to dedicate precious time to learn. Unfortunately, the time we, aspiring IT engineers starting to study a new topic, is the time we are the most vulnerable to marketing schemes. Not only are we eagerly open to devouring the course contents, we’re also laser-focused and &lt;a href="https://www.quora.com/What-is-the-psychology-behind-repeating-the-same-commercial-over-and-over-until-most-people-cant-stand-it" target="_blank" rel="noreferrer noopener"&gt;repeating&lt;/a&gt; a lot. This tunnel vision while cramming the exam allows biases to sneak in and reinforce in favour of the product names in the exam.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you’re about to build a container platform and your first flash of thought is Elastic Container Service, you should congratulate Amazon on the marketing win. If your decision process relies too much on these mental shortcuts that the exam study has covertly built, you are developing a tendency of not being analytical to engineering problems. This makes you a worse engineer than you could otherwise have been.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A wise learner has be cautious about the dosage of marketing in these exams. One indicator of high dose of marketing, is the portion of questions focusing managed and unique services in the particular cloud service provider. The certifications by non-profit organizations such as CompTIA and Linux Foundations tend to have low dose of marketing in the exams.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Stability of&amp;nbsp;Topics&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The time studying an exam is an investment. We want the certification to remain stable over the course of several years. New certification exams should only launch under rigorous review of the necessity, maintainability and the direction should not change drastically. Unfortunately, this is not always the case. Let me pick on AWS specialty exam this time. If you have been in the certification game for long, you might remember the following specialty certifications:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Big Data specialty&lt;/strong&gt; — launched in Oct 2016, and deprecated in Apr 2020 in favour of Data Analytics specialty and Database specialty&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Data Analytics specialty &lt;/strong&gt;— launched in Apr 2020, retired in Apr 2024&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Database specialty&lt;/strong&gt; — launched in Mar 2020, retired in Apr 2024&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;SAP on AWS specialty &lt;/strong&gt;— launched in Apr 2022, retired in Apr 2024&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Alexa Skill Builder&lt;/strong&gt; — launched in May 2019, retired in Mar 2021&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These changes are a main reason I am very hesitant to go for specialty certifications with AWS. Some even had a lifespan of less than two years. In my opinion, learning is worth it but spending time on those short-lived certificates is a completely waste of time. In &lt;a href="https://en.wikipedia.org/wiki/Lindy_effect" target="_blank" rel="noreferrer noopener"&gt;Lindy effect&lt;/a&gt;, the longer a non-perishable item has been around, the longer it&amp;#8217;s likely to persist into the future. Technologies such as TCP/IP, Unix, Object Oriented Programming, have been around for decades and yet still profoundly impact a production system today. Only lasting knowledge is worth more time. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As of spring 2024, AWS introduced two certificate level exams: Data Engineer Associate and Machine Learning Engineer Associate. So it seems that some specialty certifications are being consolidated into the associate-level. All previous associate-level exams had been around for more than a decade. Given that record of stability, I decided to venture the AWS Certified Machine Learning Engineer Associate certification.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Breadth and&amp;nbsp;Depth&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The next important factor to consider is the breadth and depth of certification exams.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some certifications are targeting different audiences. For example, I personally regard the AWS Certified Cloud Practitioner as targeting non-technical folks and would not carry much weight for an experienced cloud engineer. On the other hand, I chose AWS Certified Machine Learning Engineer Associate over the Machine Learning Specialty because the latter targets data scientists and is a lot more demanding on theories. Even if I grind it out there wouldn’t be opportunity in my day-to-day responsibility to digest and reinforce what I have jammed in my head.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I’m dubious on certifications for a single tool but there seems to be more and more of them these days. The only exception is if the tool plays such a crucial and fundamental role in the IT systems, and it is complex but there is no alternatives. For example, Red Hat as a classic distribution of Linux operating system, Kubernetes as a complex container orchestration service. However, certifications on single tools such as Prometheus, Istio, Cilium, and Terraform, are just too narrow to worth the effort. In my opinion these certifications provide very limited return on investment. We just need to learn these tools as we go with documentations, or go through the essential workshops.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Upkeep&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Renewal requirement is another consideration, especially if the organization requires your active certification status. Having a streamlined renewal process is a big plus because once certified our main duty is on the actual work. Kudos to the Azure certifications that only require a shortened free on-line exam for renewal. It is quite a hassle to have to retake the exam again. The validity of the certification is also subject to arbitrary changes. For example in 2024 Linux Foundation changed the validity of CKA exam from 3 years to 2 years.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Certification inflation is real. There might have been a time when they measure one&amp;#8217;s competency in the technical field. But today very few of them can differentiate one&amp;#8217;s technical competency, thanks to &lt;a href="https://en.wikipedia.org/wiki/Goodhart%27s_law"&gt;Goodhart&amp;#8217;s law.&lt;/a&gt; In addition to the certification inflation, another threat to the value is the question leaking. The longer the exam has been administered, the more likely the questions are leaked all over the place. This gives an unfair advantage for users of question dumps. And because the scores are scaled results, this systematically penalizes honesty, and ultimately impairs the value of the certification.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;More &lt;/strong&gt;ways of learning&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Last year I had a number of certifications all up for renewal, I gave up many of them that are time-consuming. They did help with learning but only up to certain extent. Unfortunately, so long as I work in consulting I still have to live with it. As a result, I learned to be very picky about which ones to pursue to get the most value of my time. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At the end of the post, I must reiterate that I don&amp;#8217;t personally find certifications are best way to learn. In the preparation, I always tend to focus on acing the exam itself. Over the years, I&amp;#8217;ve also used a few other ways in my learning, and can summarize them as such:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;certification exam: most measurable but least effective in my experience. &lt;/li&gt;&#10;&lt;li&gt;online courses: highly dependent on the quality of the individual course. Low-quality ones are just workshops. The best ones walk you through a classic project to fill the knowledge gap. &lt;/li&gt;&#10;&lt;li&gt;workshops: my preferred way going into a new topic. Vendor managed ones may over-emphasize on tools and under-emphasize on concepts.&lt;/li&gt;&#10;&lt;li&gt;side projects: most effective but requires a real-life problem and can be time consuming&lt;/li&gt;&#10;&lt;li&gt;quality white paper: an effective way of learning going into new topic in a systematic way&lt;/li&gt;&#10;&lt;li&gt;read a book: as long as the book content is up to date, this is the most systematic approach&lt;/li&gt;&#10;&lt;li&gt;blogging: effective but time-consuming&lt;/li&gt;&#10;&lt;li&gt;talk: to someone using the technology hands-on, free of fluff&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I hope every cloud engineers can also choose certifications wisely and maximize their leanring.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2024/08/debating-between-count-and-for_each-in-terraform/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Debating between count and for_each in Terraform&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/11/firewall-deployment-patterns/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Firewall Deployment Patterns&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Public Key Infrastructure 3 of 3 – PKI Implementation</title><link>https://www.digihunch.com/2024/03/public-key-infrastructure-3-of-3-use-cases/</link><pubDate>Sat, 30 Mar 2024 00:13:00 -0400</pubDate><guid>https://www.digihunch.com/2024/03/public-key-infrastructure-3-of-3-use-cases/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-pki-3.webp" alt="Featured image of post Public Key Infrastructure 3 of 3 – PKI Implementation" /&gt;&lt;p class="wp-block-paragraph"&gt;After the last two post, now we can focus on PKI implementation. The use case is software testing, where we need to create and recycle a lot of short-lived certificates. Typically, we don&amp;#8217;t have to create public certificates because testing workload is internal. Also, hosting a public CA is much more involving. In this post we go over some options to host private CAs.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Root CA&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;m assuming we are creating a self-signed root CA for the organization. Large organizations usually keep their root CA offline and store the key material in highly protected configuration such as HSM. That is not something we can easily emulate. Nor are we concerned with the details of protecting root CA. In order to enable configuration of intermediate CAs, all we need for root CA, is to create a self-signed certificate along with the key. We can do this with OpenSSL following the first section of &lt;a href="https://www.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;this post&lt;/a&gt;. Now we&amp;#8217;ll use open-source Step CA. In my opinion, this tool is handier. For example, we can create a root CA this with a single 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;step ca init&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This will start a prompt with a few questions. Then it will create a root CA and an intermediate CA. The keys and certificates are stored in ~/.step/ directory. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that by default, the root CA certificate has a path length of 1. To make the path length more than 1, we&amp;#8217;d have to customize the creation. We can specified the desired path length in a certificate template and create a self-signed certificate with the template:&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;cat &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; EOF &amp;gt; root.tpl&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;&amp;#34;subject&amp;#34;: {{ toJson .Subject }},&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;&amp;#34;issuer&amp;#34;: {{ toJson .Subject }},&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;&amp;#34;keyUsage&amp;#34;: [&amp;#34;certSign&amp;#34;, &amp;#34;crlSign&amp;#34;],&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;&amp;#34;basicConstraints&amp;#34;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;&#9;&amp;#34;isCA&amp;#34;: true,&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;&#9;&amp;#34;maxPathLen&amp;#34;: 2&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#9;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;EOF&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;step certificate create --kty&lt;span style="color:#f92672"&gt;=&lt;/span&gt;RSA --size &lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt; --template root.tpl &lt;span style="color:#e6db74"&gt;&amp;#34;DigiHunch Root CA&amp;#34;&lt;/span&gt; root_ca.crt root_ca.key&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command outputs the certificate and key of our test root CA. With that, next, we&amp;#8217;ll create subordinate CAs with a few different tools.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;AWS Private CA as intermediate CA&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ll start with AWS Private CA. It was a spin-off service from AWS Certificate Manager and is fairly simple. However, we need to understand its capacity limit. AWS Private CA documentation has a page on &lt;a href="https://docs.aws.amazon.com/privateca/latest/userguide/RFC-compliance.html"&gt;RFC Compliance&lt;/a&gt;, which lists what in RFC 5280 are supported and what are not. It performs very basic CA functions. It does not perform domain validation, hence no ACME support. We can create root CA as well but here we&amp;#8217;ll create an intermediate CA:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;In AWS Console, create a Private CA. Indicate you want to create a subordinate CA. You may specify CRL distribution and OCSP endpoint. However, you&amp;#8217;re responsible for the providing CRL and/or hosting OCSP service;&lt;/li&gt;&#10;&lt;li&gt;In the Private CA, we need to create CA Certificate. We need to provide CSR to our root CA to sign this CA&amp;#8217;s certificate. The root CA can be either another AWS Private CA or external CA. In this case we choose external CA and export the CSR to a file (e.g. dh.csr). &lt;/li&gt;&#10;&lt;li&gt;We can use Step CA to sign the request. Note that we have to give reasonable value for expiry date. Because ACM creates a certificate with &lt;a href="https://repost.aws/knowledge-center/private-ca-validity-period"&gt;13 month validity period&lt;/a&gt; by default, the expiry date must be at least 13 months from the current date. We also need to set path length. In this case, I put 0 so the Private CA can only issue end-entity certificate. The command looks like this:&lt;/li&gt;&#10;&lt;/ol&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;step certificate sign pca.csr &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; /Users/digihunch/.step/certs/root_ca.crt &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; /Users/digihunch/.step/secrets/root_ca_key &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; --profile intermediate-ca &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; --not-after&lt;span style="color:#f92672"&gt;=&lt;/span&gt;2027-01-24T07:20:50.52Z &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-len&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol class="wp-block-list" start="4"&gt;&#10;&lt;li&gt;In the AWS console, we paste the generate certificate content in the as certificate body, and the root CA&amp;#8217;s certificate as certificate chain. &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If the Private CA serves as parent CA of one more layer of CA, set the path length to 1. This requires the path length to be at least 2. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once the CA certificate is installed, we have completed creating a private CA. We can reference this CA from ACM (AWS certificate manager) when creating a private certificate. Since there is no validation support, ACM will allow you to claim any domain. Currently it does not support ACME-based certificate automation. However, you can use AWS CLI or any AWS based automation tool to create your private certificate.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Step CA&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As a cheaper alternative, we can host private CA using step CA on virtual machines. One of the benefits is ACME support (http-01 challenge). In the init command in this post, we already create the intermediate CA certificate and configuration. In this part of the lab, we&amp;#8217;ll have two servers: the CA server (pki.digihunch.internal) and the web server (web.digihunch.internal). Make sure the DNS resolution works for both servers. The architecture looks like this:&lt;/p&gt;&#10;&lt;p class="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="452px" viewBox="-0.5 -0.5 452 312" style="max-width:100%;max-height:312px;"&gt;&lt;defs&gt;&lt;style type="text/css"&gt;@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&amp;#xa;&lt;/style&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="450" height="310" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;path d="M 300 30 L 430 30 L 430 290 L 300 290 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 300 30 L 325 30 L 325 55 L 300 55 Z M 321.07 38.37 L 321.07 37.66 L 319.39 37.66 L 319.39 35.61 L 317.25 35.61 L 317.25 33.93 L 316.54 33.93 L 316.54 35.61 L 315 35.61 L 315 33.93 L 314.29 33.93 L 314.29 35.61 L 312.77 35.61 L 312.77 33.93 L 312.06 33.93 L 312.06 35.61 L 310.53 35.61 L 310.53 33.93 L 309.81 33.93 L 309.81 35.61 L 308.29 35.61 L 308.29 33.93 L 307.57 33.93 L 307.57 35.61 L 305.61 35.61 L 305.61 37.66 L 303.93 37.66 L 303.93 38.37 L 305.61 38.37 L 305.61 39.9 L 303.93 39.9 L 303.93 40.61 L 305.61 40.61 L 305.61 42.14 L 303.93 42.14 L 303.93 42.86 L 305.61 42.86 L 305.61 44.39 L 303.93 44.39 L 303.93 45.1 L 305.61 45.1 L 305.61 46.63 L 303.93 46.63 L 303.93 47.34 L 305.61 47.34 L 305.61 49.39 L 307.57 49.39 L 307.57 51.07 L 308.29 51.07 L 308.29 49.39 L 309.81 49.39 L 309.81 51.07 L 310.53 51.07 L 310.53 49.39 L 312.06 49.39 L 312.06 51.07 L 312.77 51.07 L 312.77 49.39 L 314.29 49.39 L 314.29 51.07 L 315 51.07 L 315 49.39 L 316.53 49.39 L 316.53 51.07 L 317.24 51.07 L 317.24 49.39 L 319.39 49.39 L 319.39 47.34 L 321.07 47.34 L 321.07 46.63 L 319.39 46.63 L 319.39 45.1 L 321.07 45.1 L 321.07 44.39 L 319.39 44.39 L 319.39 42.86 L 321.07 42.86 L 321.07 42.14 L 319.39 42.14 L 319.39 40.61 L 321.07 40.61 L 321.07 39.9 L 319.39 39.9 L 319.39 38.37 Z M 318.68 48.68 L 306.32 48.68 L 306.32 36.32 L 318.68 36.32 Z" fill="#d6b656" stroke="none" pointer-events="all"/&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 flex-start; justify-content: unsafe flex-start; width: 98px; height: 1px; padding-top: 37px; margin-left: 332px;"&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: all; white-space: normal; overflow-wrap: normal;"&gt;CA Server&lt;br /&gt;(ACME Server)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="332" y="49" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;CA Server&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="348.63" y="110" width="26.73" height="18.85" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 348.67 121.9 L 354.01 121.9 L 354.01 120.69 L 348.67 120.69 Z M 348.67 118.16 L 355.08 118.16 L 355.08 116.95 L 348.67 116.95 Z M 348.67 114.42 L 356.15 114.42 L 356.15 113.21 L 348.67 113.21 Z M 368.5 120.69 L 360.13 120.69 L 360.9 117.63 L 369.27 117.63 Z M 359.95 116.41 L 358.58 121.9 L 369.45 121.9 L 370.82 116.41 Z M 370.66 127.45 C 370.64 127.56 370.54 127.64 370.43 127.64 L 355.59 127.64 C 355.49 127.64 355.43 127.58 355.41 127.55 C 355.38 127.51 355.34 127.44 355.36 127.35 L 358.11 114.39 L 373.43 114.39 Z M 358.97 111.21 L 373.81 111.21 C 373.91 111.21 373.97 111.27 374 111.3 C 374.02 111.34 374.07 111.41 374.05 111.51 L 373.69 113.18 L 358.36 113.18 L 358.74 111.4 C 358.76 111.29 358.86 111.21 358.97 111.21 Z M 374.94 110.54 C 374.66 110.2 374.25 110 373.81 110 L 358.97 110 C 358.29 110 357.69 110.49 357.55 111.15 L 354.17 127.09 C 354.08 127.52 354.19 127.97 354.46 128.31 C 354.74 128.65 355.15 128.85 355.59 128.85 L 370.43 128.85 C 371.11 128.85 371.71 128.37 371.85 127.7 L 375.23 111.76 C 375.32 111.33 375.22 110.88 374.94 110.54 Z" fill="#e7157b" stroke="none" pointer-events="all"/&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 flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 107px; margin-left: 362px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;Step CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="362" y="107" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Step&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="300" y="240" width="130" height="30" fill="none" stroke="none" pointer-events="all"/&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: 128px; height: 1px; padding-top: 255px; margin-left: 301px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;pki.digihunch.internal&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="365" y="259" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;pki.digihunch.internal&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 360 230 L 361.82 128.85" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 18.98 30 L 148.98 30 L 148.98 290 L 18.98 290 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 18.98 30 L 43.98 30 L 43.98 55 L 18.98 55 Z M 40.05 38.37 L 40.05 37.66 L 38.37 37.66 L 38.37 35.61 L 36.23 35.61 L 36.23 33.93 L 35.52 33.93 L 35.52 35.61 L 33.98 35.61 L 33.98 33.93 L 33.27 33.93 L 33.27 35.61 L 31.75 35.61 L 31.75 33.93 L 31.04 33.93 L 31.04 35.61 L 29.51 35.61 L 29.51 33.93 L 28.79 33.93 L 28.79 35.61 L 27.27 35.61 L 27.27 33.93 L 26.55 33.93 L 26.55 35.61 L 24.59 35.61 L 24.59 37.66 L 22.91 37.66 L 22.91 38.37 L 24.59 38.37 L 24.59 39.9 L 22.91 39.9 L 22.91 40.61 L 24.59 40.61 L 24.59 42.14 L 22.91 42.14 L 22.91 42.86 L 24.59 42.86 L 24.59 44.39 L 22.91 44.39 L 22.91 45.1 L 24.59 45.1 L 24.59 46.63 L 22.91 46.63 L 22.91 47.34 L 24.59 47.34 L 24.59 49.39 L 26.55 49.39 L 26.55 51.07 L 27.27 51.07 L 27.27 49.39 L 28.79 49.39 L 28.79 51.07 L 29.51 51.07 L 29.51 49.39 L 31.04 49.39 L 31.04 51.07 L 31.75 51.07 L 31.75 49.39 L 33.27 49.39 L 33.27 51.07 L 33.98 51.07 L 33.98 49.39 L 35.51 49.39 L 35.51 51.07 L 36.22 51.07 L 36.22 49.39 L 38.37 49.39 L 38.37 47.34 L 40.05 47.34 L 40.05 46.63 L 38.37 46.63 L 38.37 45.1 L 40.05 45.1 L 40.05 44.39 L 38.37 44.39 L 38.37 42.86 L 40.05 42.86 L 40.05 42.14 L 38.37 42.14 L 38.37 40.61 L 40.05 40.61 L 40.05 39.9 L 38.37 39.9 L 38.37 38.37 Z M 37.66 48.68 L 25.3 48.68 L 25.3 36.32 L 37.66 36.32 Z" fill="#d6b656" stroke="none" pointer-events="all"/&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 flex-start; justify-content: unsafe flex-start; width: 98px; height: 1px; padding-top: 37px; margin-left: 51px;"&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: all; white-space: normal; overflow-wrap: normal;"&gt;Web Server&lt;br /&gt;(ACME Client)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="51" y="49" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Web Server&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="66" y="110" width="28.77" height="20.29" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 66.04 122.81 L 71.79 122.81 L 71.79 121.51 L 66.04 121.51 Z M 66.04 118.78 L 72.94 118.78 L 72.94 117.48 L 66.04 117.48 Z M 66.04 114.76 L 74.09 114.76 L 74.09 113.45 L 66.04 113.45 Z M 87.39 121.51 L 78.38 121.51 L 79.21 118.21 L 88.22 118.21 Z M 78.19 116.9 L 76.71 122.81 L 88.41 122.81 L 89.89 116.9 Z M 89.72 128.78 C 89.69 128.9 89.58 128.98 89.46 128.98 L 73.49 128.98 C 73.39 128.98 73.32 128.92 73.29 128.89 C 73.26 128.85 73.22 128.78 73.24 128.67 L 76.2 114.73 L 92.7 114.73 Z M 77.13 111.31 L 93.1 111.31 C 93.21 111.31 93.27 111.37 93.3 111.4 C 93.33 111.44 93.38 111.51 93.36 111.62 L 92.97 113.42 L 76.47 113.42 L 76.88 111.51 C 76.91 111.39 77.01 111.31 77.13 111.31 Z M 94.32 110.58 C 94.02 110.21 93.58 110 93.1 110 L 77.13 110 C 76.4 110 75.75 110.52 75.6 111.24 L 71.96 128.4 C 71.86 128.86 71.98 129.34 72.28 129.71 C 72.58 130.08 73.02 130.29 73.49 130.29 L 89.46 130.29 C 90.2 130.29 90.84 129.77 90.99 129.05 L 94.63 111.89 C 94.73 111.43 94.62 110.95 94.32 110.58 Z" fill="#e7157b" stroke="none" pointer-events="all"/&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 flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 107px; margin-left: 80px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;Step CLI &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="80" y="107" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Step&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="18.98" y="240" width="130" height="30" fill="none" stroke="none" pointer-events="all"/&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: 128px; height: 1px; padding-top: 255px; margin-left: 20px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;web.digihunch.internal&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="84" y="259" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;web.digihunch.internal&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 230 L 80.35 130.29" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 80 230 L 353.63 230" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 358.88 230 L 351.88 233.5 L 353.63 230 L 351.88 226.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 360 160 L 86.37 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 81.12 160 L 88.12 156.5 L 86.37 160 L 88.12 163.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;rect x="290" y="150" width="20" height="20" fill="#f5f5f5" stroke="#666666" pointer-events="all"/&gt;&lt;g fill="#333333" font-family="Helvetica" text-anchor="end" font-size="12px"&gt;&lt;text x="287.5" y="144.5"&gt;ephemeral&lt;/text&gt;&lt;/g&gt;&lt;rect x="140" y="220" width="20" height="20" fill="#f5f5f5" stroke="#666666" pointer-events="all"/&gt;&lt;g fill="#333333" font-family="Helvetica" font-size="12px"&gt;&lt;text x="163.5" y="257.5"&gt;ephemeral&lt;/text&gt;&lt;/g&gt;&lt;rect x="290" y="220" width="20" height="20" fill="#f5f5f5" stroke="#666666" pointer-events="all"/&gt;&lt;g fill="#333333" font-family="Helvetica" text-anchor="end" font-size="12px"&gt;&lt;text x="287.5" y="257.5"&gt;TCP 443&lt;/text&gt;&lt;/g&gt;&lt;rect x="140" y="150" width="20" height="20" fill="#f5f5f5" stroke="#666666" pointer-events="all"/&gt;&lt;g fill="#333333" font-family="Helvetica" font-size="12px"&gt;&lt;text x="163.5" y="144.5"&gt;TCP 80&lt;/text&gt;&lt;/g&gt;&lt;rect x="140" y="160" width="130" height="30" fill="none" stroke="none" pointer-events="all"/&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: 128px; height: 1px; padding-top: 175px; margin-left: 141px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;/challenge/response&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="205" y="179" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;/challenge/response&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="190" y="30" width="60" height="30" fill="none" stroke="none" pointer-events="all"/&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: 58px; height: 1px; padding-top: 45px; margin-left: 191px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;standalone&lt;br /&gt;mode&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="220" y="49" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;standalone&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="185" y="200" width="120" height="30" fill="none" stroke="none" pointer-events="all"/&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: 118px; height: 1px; padding-top: 215px; margin-left: 186px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;/acme/order/&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="245" y="219" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;/acme/order/&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/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 CA server runs the Step CA process acting as ACME server. We deploy the CA in &lt;a href="https://smallstep.com/docs/step-cli/reference/ca/init/"&gt;standalone mode&lt;/a&gt; (instead of linked or hosted deployment), meaning it&amp;#8217;s not connected to any cloud services. We can host the service on port 443 so make sure the process has port binding permission within the operating system, and the firewall (security group) allows traffic via port 443. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Web Server runs Step CLI acting as ACME client. On the web server, we run Step CLI in &lt;a href="https://smallstep.com/docs/step-cli/reference/ca/certificate/"&gt;standalone mode&lt;/a&gt; (instead of webroot). During the challenge-response phase, the CLI will get the required random number from ACME server, and host it the as the response on port 80. Make sure that Step CLI process has port binding permission in the OS, and the firewall (security group) allows traffic via port 80.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Make sure the DNS resolution works for both servers. On the CA server, we fetch the fingerprint (in preparation for setting up Step CLI on web server). Then we add an ACME provisioner, and host the CA server with a single 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;step certificate fingerprint &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;step path&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;/certs/root_ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;step ca provisioner add myacme --type ACME&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;step-ca &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;step path&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;/config/ca.json &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The CA server is listening on port 443 (by default). Now we can test ACME process with any ACME compatible client. Let&amp;#8217;s use step CLI on the web server:&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;step ca bootstrap --ca-url https://pki.digihunch.internal:443 &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; --fingerprint &amp;lt;fingerprintvalue&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;step ca certificate web.digihunch.internal acme.crt acme.key &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; --acme https://pki.digihunch.internal/acme/myacme/directory&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The bootstrapping step is to establish trust on the CA server. Then the provisioning process should complete automatically:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="121" src="https://www.digihunch.com/wp-content/uploads/2024/01/image-7-1024x121.webp" alt="" class="wp-image-10722" srcset="https://www.digihunch.com/wp-content/uploads/2024/01/image-7-1024x121.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/01/image-7-300x35.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/01/image-7-768x91.webp 768w, https://www.digihunch.com/wp-content/uploads/2024/01/image-7-1536x182.webp 1536w, https://www.digihunch.com/wp-content/uploads/2024/01/image-7-2048x242.webp 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here the step CLI command uses standalone mode by default so it is important to ensure reachability (port 80, DNS name) from the CA server when step CLI hosts the response. You can also go with webroot mode, where Step CLI generate the file to the web root directory so the response becomes available. This is helpful when you already run another web hosting process such as Nginx on the web server. This &lt;a href="https://smallstep.com/blog/private-acme-server/"&gt;blog post&lt;/a&gt; covers the usage of other ACME-compatible tools with Step CA as private CA server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another well-known tool is HashiCorp Vault. There is already a good tutorial &lt;a href="https://developer.hashicorp.com/vault/tutorials/secrets-management/pki-engine-external-ca"&gt;here&lt;/a&gt; and all the OpenSSL command in the tutorial can be replaced with Step commands. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cert Manager on Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Cert Manager project on Kubernetes makes PKI work simple. In my discussion about &lt;a href="https://www.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;self-signed certificate on Kubernetes&lt;/a&gt;, I covered how to use Cert Manager to create self-signed certificate. However, a corporate with multiple clusters may need to chain each cluster-wide issuing CA to an intermediate CA outside of the cluster. Let&amp;#8217;s look at this architecture as an example of a full PKI implementation:&lt;/p&gt;&#10;&lt;p class="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="1301px" viewBox="-0.5 -0.5 1301 931" style="max-width:100%;max-height:931px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="1300" height="930" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="50" y="520" width="450" height="300" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 320 20 L 570 20 L 570 270 L 320 270 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 329.02 32.33 L 329.02 33.08 L 335.99 33.08 L 335.99 32.33 Z M 329.02 29.28 L 329.02 30.03 L 335.99 30.03 L 335.99 29.28 Z M 329.02 26.24 L 329.02 26.99 L 335.99 26.99 L 335.99 26.24 Z M 327.64 23.73 C 327.44 23.73 327.27 23.9 327.27 24.11 L 327.27 41.5 C 327.27 41.71 327.44 41.87 327.64 41.87 L 337.37 41.87 C 337.58 41.87 337.75 41.71 337.75 41.5 L 337.75 24.11 C 337.75 23.9 337.58 23.73 337.37 23.73 Z M 328.02 24.48 L 337 24.48 L 337 41.12 L 328.02 41.12 Z M 320 45 L 320 20 L 345 20 L 345 45 Z" fill="#666666" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe flex-start; width: 218px; height: 1px; padding-top: 27px; margin-left: 352px;"&gt;&lt;div data-drawio-colors="color: #333333; " 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(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Data Center&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="352" y="39" fill="#333333" font-family="Helvetica" font-size="12px"&gt;Data Center&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="464.5" y="79.5" width="40" height="40" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NC4zIDY0Ij48ZyBmaWxsPSIjZTQyNDMzIj48ZWxsaXBzZSBjeD0iMzIuMSIgY3k9IjMyIiByeD0iMzIuMSIgcnk9IjMyIi8+PGVsbGlwc2UgY3g9IjMyLjEiIGN5PSIzMiIgcng9IjI2LjQiIHJ5PSIyNi4zIi8+PC9nPjxwYXRoIGQ9Ik00MS4zIDI0LjRjMC01LTQuMS05LjEtOS4xLTkuMXMtOS4xIDQuMS05LjEgOS4xdjQuMWMtMS45IDIuMi0zIDUuMS0zIDggMCA2LjcgNS40IDEyLjIgMTIuMiAxMi4yIDYuNyAwIDEyLjItNS40IDEyLjItMTIuMiAwLTIuOS0xLjEtNS44LTMtOGwtLjItNC4xem0tMTUuMyAwYTYgNiAwIDAgMSA2LjUtNS42YzMgLjIgNS40IDIuNiA1LjYgNS42VjI2YTEyLjEgMTIuMSAwIDAgMC0xMi4yIDBsLjEtMS42em0zLjggMTguOGwtNS43LTUuNiAyLjItMi4xIDMuNSAzLjUgOC04IDIuMiAyLjEtMTAuMiAxMC4xeiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg=="&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 flex-end; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 77px; margin-left: 507px;"&gt;&lt;div data-drawio-colors="color: #000000; " 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; white-space: nowrap;"&gt;Root CA&lt;br&gt;Offline&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="507" y="77" fill="#000000" font-family="Helvetica" font-size="12px"&gt;Root CA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="80" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 340 85.66 L 345.83 80.04 L 377.91 80.04 C 379.81 80 381.44 81.36 381.67 83.18 L 381.67 84.8 L 379.34 84.02 C 379.39 83.12 378.69 82.35 377.76 82.28 L 347.07 82.28 L 347.07 86.9 L 342.32 86.9 L 342.32 103.62 C 342.38 104.27 342.89 104.79 343.56 104.9 L 360.41 104.9 L 361.75 107.09 L 343.36 107.09 C 341.59 106.84 340.21 105.48 340 103.76 Z M 362.74 116.76 L 367.09 109.62 C 361.97 106.24 359.79 100.02 361.72 94.35 C 363.65 88.68 369.23 84.91 375.42 85.11 C 381.61 85.31 386.93 89.42 388.46 95.2 C 390 100.98 387.39 107.05 382.06 110.09 L 386.96 117.19 L 381.62 115.81 L 380.09 119.95 L 374.35 112.57 L 370 120 L 368.02 115.52 Z M 362.79 98.66 C 363.1 104.96 368.45 109.93 374.99 110 C 381.48 109.85 386.74 104.9 387.05 98.66 C 386.88 92.4 381.69 87.34 375.19 87.09 C 368.48 87.06 362.97 92.2 362.79 98.66 Z M 365.75 98.66 C 365.74 96.3 366.71 94.03 368.45 92.37 C 370.18 90.7 372.54 89.78 374.99 89.8 C 377.42 89.81 379.74 90.76 381.45 92.42 C 383.15 94.08 384.1 96.32 384.09 98.66 C 383.98 103.44 379.95 107.26 374.99 107.28 C 369.98 107.34 365.86 103.49 365.75 98.66 Z M 367.78 98.66 C 367.98 102.39 371.13 105.33 374.99 105.43 C 378.82 105.28 381.91 102.35 382.11 98.66 C 382.01 94.9 378.9 91.86 374.99 91.71 C 371.05 91.81 367.88 94.86 367.78 98.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 127px; margin-left: 365px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Root CA&lt;br&gt;Certificate&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" text-anchor="middle"&gt;Root CA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 465 100 L 390 100" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 485 80 L 485 60 L 365 60 L 365 73.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 365 78.88 L 361.5 71.88 L 365 73.63 L 368.5 71.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 51px; margin-left: 408px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;self-sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="408" y="54" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;self-sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="370" y="190" width="26" height="39" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 382.36 195.97 L 382.36 194.48 L 372.98 194.48 L 372.98 195.97 Z M 370 224.83 L 370 191.94 C 370 190.84 370.85 190 371.65 190 L 383.67 190 C 384.47 190 385.34 190.79 385.34 192 L 385.34 210.39 L 381.42 210.39 L 377.67 214.78 L 377.67 214.88 L 372.98 214.88 L 372.98 216.37 L 377.67 216.37 L 377.67 219.35 L 372.98 219.35 L 372.98 220.85 L 377.67 220.85 L 377.67 224.83 Z M 378.95 215.87 L 378.95 215.37 L 381.93 211.89 L 382.36 211.89 L 382.36 215.87 Z M 378.95 224.83 L 378.95 216.87 L 383.21 216.87 L 383.21 211.89 L 393.44 211.89 L 393.44 215.71 C 392.93 215.37 392.19 215.06 391.33 215.08 C 389.2 215.08 387.06 217.07 387.06 220.06 C 387.04 221.68 387.69 222.83 388.28 223.59 L 387.73 224.83 Z M 391.33 223.33 C 392.99 223.33 394.12 221.81 394.12 220.06 C 394.12 218.42 392.97 216.87 391.33 216.87 C 389.79 216.87 388.58 218.37 388.58 220.06 C 388.58 222.12 390.02 223.33 391.33 223.33 Z M 391.33 222.54 C 390.26 222.54 389.27 221.52 389.27 220.06 C 389.27 218.84 390.16 217.66 391.33 217.66 C 392.63 217.66 393.44 218.9 393.44 220.06 C 393.44 221.45 392.53 222.54 391.33 222.54 Z M 391.33 222.04 C 392.29 222.04 393.02 221.17 393.02 220.06 C 393.02 219.17 392.4 218.15 391.33 218.15 C 390.62 218.15 389.69 218.8 389.69 220.06 C 389.69 221.13 390.4 222.04 391.33 222.04 Z M 387.35 227.97 L 389.44 223.39 C 388.56 222.64 387.99 221.51 387.99 220.06 C 387.99 217.82 389.62 216.12 391.33 216.12 C 393.47 216.12 394.81 218.13 394.81 220.06 C 394.81 221.58 394.16 222.57 393.45 223.29 L 396 227.61 L 393.99 227.07 L 392.89 228.86 L 391.33 224.18 L 390.29 229 L 389.13 227.61 Z" fill="#505050" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 236px; margin-left: 383px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Intermediate CA&lt;br&gt;on-prem&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="383" y="248" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Inte&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="471" y="190" width="28" height="39" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 484.31 195.97 L 484.31 194.48 L 474.21 194.48 L 474.21 195.97 Z M 471 224.83 L 471 191.94 C 471 190.84 471.92 190 472.77 190 L 485.73 190 C 486.59 190 487.52 190.79 487.52 192 L 487.52 210.39 L 483.3 210.39 L 479.26 214.78 L 479.26 214.88 L 474.21 214.88 L 474.21 216.37 L 479.26 216.37 L 479.26 219.35 L 474.21 219.35 L 474.21 220.85 L 479.26 220.85 L 479.26 224.83 Z M 480.64 215.87 L 480.64 215.37 L 483.85 211.89 L 484.31 211.89 L 484.31 215.87 Z M 480.64 224.83 L 480.64 216.87 L 485.23 216.87 L 485.23 211.89 L 496.25 211.89 L 496.25 215.71 C 495.7 215.37 494.9 215.06 493.97 215.08 C 491.68 215.08 489.37 217.07 489.37 220.06 C 489.35 221.68 490.05 222.83 490.69 223.59 L 490.09 224.83 Z M 493.97 223.33 C 495.76 223.33 496.97 221.81 496.97 220.06 C 496.97 218.42 495.74 216.87 493.97 216.87 C 492.31 216.87 491.01 218.37 491.01 220.06 C 491.01 222.12 492.56 223.33 493.97 223.33 Z M 493.97 222.54 C 492.82 222.54 491.75 221.52 491.75 220.06 C 491.75 218.84 492.71 217.66 493.97 217.66 C 495.37 217.66 496.25 218.9 496.25 220.06 C 496.25 221.45 495.26 222.54 493.97 222.54 Z M 493.97 222.04 C 495.01 222.04 495.79 221.17 495.79 220.06 C 495.79 219.17 495.12 218.15 493.97 218.15 C 493.2 218.15 492.21 218.8 492.21 220.06 C 492.21 221.13 492.97 222.04 493.97 222.04 Z M 489.69 227.97 L 491.94 223.39 C 490.98 222.64 490.37 221.51 490.37 220.06 C 490.37 217.82 492.13 216.12 493.97 216.12 C 496.27 216.12 497.71 218.13 497.71 220.06 C 497.72 221.58 497.02 222.57 496.25 223.29 L 499 227.61 L 496.84 227.07 L 495.65 228.86 L 493.97 224.18 L 492.85 229 L 491.61 227.61 Z" fill="#505050" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 236px; margin-left: 485px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Intermediate CA&lt;br&gt;on-prem&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="485" y="248" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Inte&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="40" y="460" width="610" height="410" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 470.86 114.14 L 400.31 190.71" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 396.76 194.57 L 398.93 187.05 L 400.31 190.71 L 404.07 191.79 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 485 120 L 485 183.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 485 188.88 L 481.5 181.88 L 485 183.63 L 488.5 181.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 10 290 L 1290 290 L 1290 910 L 10 910 Z" fill="none" stroke="#858b94" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 20.73 296.89 C 18.19 296.86 16.05 299.34 16.28 301.84 C 14.63 302.3 13.58 304.09 13.93 305.76 C 14.17 307.45 15.84 308.8 17.55 308.58 C 21.3 308.56 25.05 308.61 28.79 308.55 C 30.71 308.38 32.05 306.29 31.58 304.45 C 31.34 303.13 30.21 301.98 28.87 301.81 C 28.86 300.3 27.26 298.82 25.79 299.6 C 24.97 300.39 24.82 298.44 24.02 298.13 C 23.14 297.28 21.95 296.82 20.73 296.89 Z M 20.75 297.63 C 22.68 297.51 24.32 299.05 24.91 300.79 C 25.31 301.19 25.67 300.52 25.97 300.34 C 27.25 299.57 28.21 301.17 28.23 302.29 C 28.58 302.77 29.39 302.38 29.77 302.91 C 31.21 303.93 31.3 306.3 29.88 307.37 C 28.9 308.09 27.63 307.75 26.5 307.83 C 23.28 307.82 20.06 307.84 16.84 307.82 C 15.2 307.58 14.19 305.69 14.79 304.17 C 15.07 303.16 16.05 302.67 16.94 302.36 C 17.19 301.62 16.85 300.71 17.33 299.99 C 17.92 298.65 19.25 297.61 20.75 297.63 Z M 10 315 C 10 306.67 10 298.33 10 290 C 18.33 290 26.67 290 35 290 C 35 298.33 35 306.67 35 315 C 26.67 315 18.33 315 10 315 Z" fill="#858b94" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe flex-start; width: 1248px; height: 1px; padding-top: 297px; margin-left: 42px;"&gt;&lt;div data-drawio-colors="color: #858B94; " 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(133, 139, 148); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;AWS Cloud&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="42" y="309" fill="#858B94" font-family="Helvetica" font-size="12px"&gt;AWS Cloud&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 499.14 114.14 L 645 230 L 645 323.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 645 328.88 L 641.5 321.88 L 645 323.63 L 648.5 321.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 286px; margin-left: 660px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="660" y="289" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="40" y="460" width="610" height="410" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 520 300 L 910 300 L 910 410 L 520 410 Z" fill="none" stroke="#cd2264" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 520 300 L 520 325 L 545 325 L 545 300 L 520 300 Z M 524.09 303.69 L 540.91 303.69 C 541.01 303.69 541.12 303.73 541.19 303.81 C 541.27 303.88 541.31 303.99 541.31 304.09 L 541.31 320.91 C 541.31 321.01 541.27 321.12 541.19 321.19 C 541.12 321.27 541.01 321.31 540.91 321.31 L 524.09 321.31 C 523.99 321.31 523.88 321.27 523.81 321.19 C 523.73 321.12 523.69 321.01 523.69 320.91 L 523.69 304.09 C 523.69 303.99 523.73 303.88 523.81 303.81 C 523.88 303.73 523.99 303.69 524.09 303.69 Z M 524.49 304.49 L 524.49 320.51 L 540.51 320.51 L 540.51 304.49 L 524.49 304.49 Z M 536.5 305.31 C 536.65 305.31 536.79 305.38 536.86 305.51 L 539.46 310.72 C 539.53 310.84 539.52 310.99 539.45 311.11 C 539.37 311.23 539.25 311.3 539.11 311.3 L 533.9 311.3 L 533.9 311.3 C 533.76 311.3 533.63 311.23 533.56 311.11 C 533.49 310.99 533.48 310.84 533.54 310.72 L 536.14 305.51 C 536.21 305.38 536.36 305.31 536.5 305.31 Z M 536.5 306.59 L 534.55 310.5 L 538.46 310.5 L 536.5 306.59 Z M 526.09 309.3 L 531.3 309.3 C 531.41 309.3 531.51 309.34 531.58 309.41 C 531.66 309.49 531.7 309.59 531.7 309.7 L 531.7 314.9 C 531.7 315.01 531.66 315.11 531.58 315.19 C 531.51 315.26 531.41 315.3 531.3 315.3 L 526.09 315.3 C 525.99 315.3 525.89 315.26 525.81 315.19 C 525.74 315.11 525.69 315.01 525.69 314.9 L 525.69 309.7 C 525.69 309.59 525.74 309.49 525.81 309.41 C 525.89 309.34 525.99 309.3 526.09 309.3 Z M 526.49 310.1 L 526.49 314.5 L 530.9 314.5 L 530.9 310.1 L 526.49 310.1 Z M 535.7 313.3 C 537.47 313.3 538.9 314.74 538.91 316.5 C 538.9 318.27 537.47 319.7 535.7 319.71 C 533.94 319.7 532.5 318.27 532.5 316.5 C 532.5 314.74 533.94 313.3 535.7 313.3 Z M 535.7 314.1 C 534.38 314.1 533.3 315.18 533.3 316.5 C 533.3 317.83 534.38 318.9 535.7 318.91 C 537.03 318.9 538.1 317.83 538.1 316.5 C 538.1 315.18 537.03 314.1 535.7 314.1 Z" fill="#cd2264" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe flex-start; width: 358px; height: 1px; padding-top: 307px; margin-left: 552px;"&gt;&lt;div data-drawio-colors="color: #CD2264; " 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(205, 34, 100); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Ops Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="552" y="319" fill="#CD2264" font-family="Helvetica" font-size="12px"&gt;Ops Account&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="620" y="330" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 620 335.66 L 625.83 330.04 L 657.91 330.04 C 659.81 330 661.44 331.36 661.67 333.18 L 661.67 334.8 L 659.34 334.02 C 659.39 333.12 658.69 332.35 657.76 332.28 L 627.07 332.28 L 627.07 336.9 L 622.32 336.9 L 622.32 353.62 C 622.38 354.27 622.89 354.79 623.56 354.9 L 640.41 354.9 L 641.75 357.09 L 623.36 357.09 C 621.59 356.84 620.21 355.48 620 353.76 Z M 642.74 366.76 L 647.09 359.62 C 641.97 356.24 639.79 350.02 641.72 344.35 C 643.65 338.68 649.23 334.91 655.42 335.11 C 661.61 335.31 666.93 339.42 668.46 345.2 C 670 350.98 667.39 357.05 662.06 360.09 L 666.96 367.19 L 661.62 365.81 L 660.09 369.95 L 654.35 362.57 L 650 370 L 648.02 365.52 Z M 642.79 348.66 C 643.1 354.96 648.45 359.93 654.99 360 C 661.48 359.85 666.74 354.9 667.05 348.66 C 666.88 342.4 661.69 337.34 655.19 337.09 C 648.48 337.06 642.97 342.2 642.79 348.66 Z M 645.75 348.66 C 645.74 346.3 646.71 344.03 648.45 342.37 C 650.18 340.7 652.54 339.78 654.99 339.8 C 657.42 339.81 659.74 340.76 661.45 342.42 C 663.15 344.08 664.1 346.32 664.09 348.66 C 663.98 353.44 659.95 357.26 654.99 357.28 C 649.98 357.34 645.86 353.49 645.75 348.66 Z M 647.78 348.66 C 647.98 352.39 651.13 355.33 654.99 355.43 C 658.82 355.28 661.91 352.35 662.11 348.66 C 662.01 344.9 658.9 341.86 654.99 341.71 C 651.05 341.81 647.88 344.86 647.78 348.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 350px; margin-left: 618px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Intermediate CA&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="618" y="354" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Intermedi&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 760 330 L 800 330 L 800 370 L 760 370 Z" fill="#dd344c" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 795.43 336.86 C 795.74 336.86 796 337.11 796 337.43 L 796 365.43 C 796 365.74 795.74 366 795.43 366 L 792 366 L 792 364.86 L 794.86 364.86 L 794.86 338 L 793.71 338 L 793.71 336.86 Z M 768 363.71 L 768 364.86 L 787.43 364.86 L 787.43 366 L 767.43 366 C 767.11 366 766.86 365.74 766.86 365.43 L 766.86 363.71 Z M 792 334 C 792.32 334 792.57 334.26 792.57 334.57 L 792.57 362 C 792.57 362.32 792.32 362.57 792 362.57 L 789.14 362.57 L 789.14 361.43 L 791.43 361.43 L 791.43 338 L 765.14 338 L 765.14 361.43 L 784.57 361.43 L 784.57 362.57 L 764.57 362.57 C 764.26 362.57 764 362.32 764 362 L 764 334.57 C 764 334.26 764.26 334 764.57 334 Z M 773.71 354.57 L 773.71 358.84 L 778.34 356.82 C 778.49 356.76 778.65 356.76 778.8 356.82 L 783.43 358.84 L 783.43 354.57 L 784.57 354.57 L 784.57 359.71 C 784.57 359.91 784.47 360.09 784.31 360.19 C 784.22 360.25 784.11 360.29 784 360.29 C 783.92 360.29 783.84 360.27 783.77 360.24 L 778.57 357.97 L 773.37 360.24 C 773.19 360.32 772.99 360.3 772.83 360.19 C 772.67 360.09 772.57 359.91 772.57 359.71 L 772.57 354.57 Z M 778.57 339.14 C 782.83 339.15 786.28 342.6 786.29 346.86 C 786.28 351.12 782.83 354.57 778.57 354.57 C 774.31 354.57 770.86 351.12 770.86 346.86 C 770.86 342.6 774.31 339.15 778.57 339.14 Z M 778.57 340.29 C 774.95 340.29 772 343.23 772 346.86 C 772 350.48 774.95 353.43 778.57 353.43 C 782.19 353.43 785.14 350.48 785.14 346.86 C 785.14 343.23 782.19 340.29 778.57 340.29 Z M 781.6 344.45 L 782.4 345.26 L 777.83 349.83 C 777.72 349.94 777.57 350 777.43 350 C 777.28 350 777.14 349.94 777.02 349.83 L 774.74 347.55 L 775.55 346.74 L 777.43 348.62 Z M 791.43 335.14 L 765.14 335.14 L 765.14 336.86 L 791.43 336.86 Z" fill="#ffffff" stroke="none" pointer-events="all"&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: 350px; margin-left: 802px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " 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(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;AWS Private CA&lt;br&gt;Intermediate CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="802" y="354" fill="#232F3E" font-family="Helvetica" font-size="12px"&gt;AWS Pri&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 669 350 L 760 350" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 764.5 380 L 794.5 380 L 794.5 410 L 764.5 410 Z" fill="#dd344c" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 776.5 403.57 C 776.5 403.34 776.31 403.14 776.07 403.14 C 775.83 403.14 775.64 403.34 775.64 403.57 C 775.64 403.81 775.83 404 776.07 404 C 776.31 404 776.5 403.81 776.5 403.57 Z M 777.36 403.57 C 777.36 404.28 776.78 404.86 776.07 404.86 C 775.36 404.86 774.79 404.28 774.79 403.57 C 774.79 402.86 775.36 402.29 776.07 402.29 C 776.78 402.29 777.36 402.86 777.36 403.57 Z M 785.68 403.93 L 784.89 403.14 L 778.95 403.14 C 778.76 403.14 778.59 403.02 778.54 402.84 C 778.21 401.76 777.2 401 776.07 401 C 774.65 401 773.5 402.15 773.5 403.57 C 773.5 404.99 774.65 406.14 776.07 406.14 C 777.06 406.14 777.97 405.57 778.4 404.67 C 778.47 404.52 778.62 404.43 778.78 404.43 L 779.93 404.43 C 780.04 404.43 780.15 404.47 780.23 404.55 L 780.79 405.11 L 781.34 404.55 C 781.42 404.47 781.53 404.43 781.64 404.43 L 782.5 404.43 C 782.61 404.43 782.72 404.47 782.8 404.55 L 783.36 405.11 L 783.91 404.55 C 783.99 404.47 784.1 404.43 784.21 404.43 L 784.94 404.43 Z M 786.66 403.7 C 786.75 403.79 786.8 403.91 786.78 404.04 C 786.77 404.17 786.7 404.29 786.6 404.36 L 785.31 405.21 C 785.24 405.26 785.16 405.29 785.07 405.29 L 784.39 405.29 L 783.66 406.02 C 783.49 406.18 783.22 406.18 783.05 406.02 L 782.32 405.29 L 781.82 405.29 L 781.09 406.02 C 780.92 406.18 780.65 406.18 780.48 406.02 L 779.75 405.29 L 779.04 405.29 C 778.43 406.34 777.3 407 776.07 407 C 774.18 407 772.64 405.46 772.64 403.57 C 772.64 401.68 774.18 400.14 776.07 400.14 C 777.47 400.14 778.73 401.01 779.25 402.29 L 785.07 402.29 C 785.19 402.29 785.29 402.33 785.37 402.41 Z M 782.68 388.57 L 785.07 390.97 L 787.47 388.57 L 785.07 386.18 Z M 784.77 391.87 L 781.77 388.87 C 781.6 388.71 781.6 388.44 781.77 388.27 L 784.77 385.27 C 784.94 385.1 785.21 385.1 785.37 385.27 L 788.37 388.27 C 788.54 388.44 788.54 388.71 788.37 388.87 L 785.37 391.87 C 785.29 391.96 785.18 392 785.07 392 C 784.96 392 784.85 391.96 784.77 391.87 Z M 776.97 390.29 L 774.36 386.73 L 771.77 390.29 Z M 777.81 391.14 L 770.93 391.14 C 770.77 391.14 770.62 391.05 770.55 390.91 C 770.47 390.77 770.49 390.59 770.58 390.46 L 774.01 385.75 C 774.09 385.64 774.22 385.57 774.36 385.57 C 774.5 385.56 774.62 385.64 774.7 385.75 L 778.16 390.46 C 778.25 390.59 778.27 390.76 778.19 390.91 C 778.12 391.05 777.97 391.14 777.81 391.14 Z M 771.79 396.07 C 771.79 397.37 772.84 398.43 774.14 398.43 C 775.44 398.43 776.5 397.37 776.5 396.07 C 776.5 394.77 775.44 393.71 774.14 393.71 C 772.84 393.71 771.79 394.77 771.79 396.07 Z M 770.93 396.07 C 770.93 394.3 772.37 392.86 774.14 392.86 C 775.92 392.86 777.36 394.3 777.36 396.07 C 777.36 397.84 775.92 399.29 774.14 399.29 C 772.37 399.29 770.93 397.84 770.93 396.07 Z M 782.93 398 L 786.79 398 L 786.79 394.14 L 782.93 394.14 Z M 787.64 398.43 C 787.64 398.67 787.45 398.86 787.21 398.86 L 782.5 398.86 C 782.26 398.86 782.07 398.67 782.07 398.43 L 782.07 393.71 C 782.07 393.48 782.26 393.29 782.5 393.29 L 787.21 393.29 C 787.45 393.29 787.64 393.48 787.64 393.71 Z M 791.5 383.43 L 791.5 404.86 C 791.5 405.09 791.31 405.29 791.07 405.29 L 787.64 405.29 L 787.64 404.43 L 790.64 404.43 L 790.64 383.86 L 768.36 383.86 L 768.36 404.43 L 771.36 404.43 L 771.36 405.29 L 767.93 405.29 C 767.69 405.29 767.5 405.09 767.5 404.86 L 767.5 383.43 C 767.5 383.19 767.69 383 767.93 383 L 791.07 383 C 791.31 383 791.5 383.19 791.5 383.43 Z" fill="#ffffff" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 395px; margin-left: 763px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;Resource Access Manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="763" y="399" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Resou&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 20 430 L 1280 430 L 1280 890 L 20 890 Z" fill="none" stroke="#cd2264" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 20 430 L 20 455 L 45 455 L 45 430 L 20 430 Z M 24.09 433.69 L 40.91 433.69 C 41.01 433.69 41.12 433.73 41.19 433.81 C 41.27 433.88 41.31 433.99 41.31 434.09 L 41.31 450.91 C 41.31 451.01 41.27 451.12 41.19 451.19 C 41.12 451.27 41.01 451.31 40.91 451.31 L 24.09 451.31 C 23.99 451.31 23.88 451.27 23.81 451.19 C 23.73 451.12 23.69 451.01 23.69 450.91 L 23.69 434.09 C 23.69 433.99 23.73 433.88 23.81 433.81 C 23.88 433.73 23.99 433.69 24.09 433.69 Z M 24.49 434.49 L 24.49 450.51 L 40.51 450.51 L 40.51 434.49 L 24.49 434.49 Z M 36.5 435.31 C 36.65 435.31 36.79 435.38 36.86 435.51 L 39.46 440.72 C 39.53 440.84 39.52 440.99 39.45 441.11 C 39.37 441.23 39.25 441.3 39.11 441.3 L 33.9 441.3 L 33.9 441.3 C 33.76 441.3 33.63 441.23 33.56 441.11 C 33.49 440.99 33.48 440.84 33.54 440.72 L 36.14 435.51 C 36.21 435.38 36.36 435.31 36.5 435.31 Z M 36.5 436.59 L 34.55 440.5 L 38.46 440.5 L 36.5 436.59 Z M 26.09 439.3 L 31.3 439.3 C 31.41 439.3 31.51 439.34 31.58 439.41 C 31.66 439.49 31.7 439.59 31.7 439.7 L 31.7 444.9 C 31.7 445.01 31.66 445.11 31.58 445.19 C 31.51 445.26 31.41 445.3 31.3 445.3 L 26.09 445.3 C 25.99 445.3 25.89 445.26 25.81 445.19 C 25.74 445.11 25.69 445.01 25.69 444.9 L 25.69 439.7 C 25.69 439.59 25.74 439.49 25.81 439.41 C 25.89 439.34 25.99 439.3 26.09 439.3 Z M 26.49 440.1 L 26.49 444.5 L 30.9 444.5 L 30.9 440.1 L 26.49 440.1 Z M 35.7 443.3 C 37.47 443.3 38.9 444.74 38.91 446.5 C 38.9 448.27 37.47 449.7 35.7 449.71 C 33.94 449.7 32.5 448.27 32.5 446.5 C 32.5 444.74 33.94 443.3 35.7 443.3 Z M 35.7 444.1 C 34.38 444.1 33.3 445.18 33.3 446.5 C 33.3 447.83 34.38 448.9 35.7 448.91 C 37.03 448.9 38.1 447.83 38.1 446.5 C 38.1 445.18 37.03 444.1 35.7 444.1 Z" fill="#cd2264" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe flex-start; width: 1228px; height: 1px; padding-top: 437px; margin-left: 52px;"&gt;&lt;div data-drawio-colors="color: #CD2264; " 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(205, 34, 100); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Workload Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="52" y="449" fill="#CD2264" font-family="Helvetica" font-size="12px"&gt;Workload Account&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="60" y="680" width="270" height="175" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 610 460 L 650 460 L 650 500 L 610 500 Z" fill="#ed7100" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 633.56 475.36 L 629.73 479.8 L 633.89 484.66 L 632.34 484.66 L 628.6 480.29 L 628.6 484.78 L 627.48 484.78 L 627.48 475.24 L 628.6 475.24 L 628.6 479.17 L 632.06 475.36 Z M 643.18 484.35 L 638.69 481.66 L 638.69 475.24 C 638.69 475.05 638.59 474.86 638.42 474.76 L 631.96 471 L 631.96 465.57 L 643.18 472.2 Z M 644.03 471.4 L 631.69 464.1 C 631.51 464 631.3 464 631.12 464.1 C 630.95 464.2 630.84 464.39 630.84 464.59 L 630.84 471.32 C 630.84 471.52 630.95 471.7 631.12 471.8 L 637.57 475.57 L 637.57 481.98 C 637.57 482.17 637.68 482.36 637.85 482.46 L 643.45 485.82 C 643.54 485.88 643.64 485.9 643.74 485.9 C 643.84 485.9 643.93 485.88 644.02 485.83 C 644.2 485.73 644.3 485.54 644.3 485.34 L 644.3 471.88 C 644.3 471.68 644.2 471.5 644.03 471.4 Z M 629.97 494.8 L 616.82 487.81 L 616.82 472.2 L 628.04 465.57 L 628.04 471.01 L 622.13 474.77 C 621.96 474.87 621.87 475.05 621.87 475.24 L 621.87 484.78 C 621.87 484.99 621.98 485.18 622.17 485.28 L 629.71 489.21 C 629.88 489.29 630.07 489.29 630.23 489.21 L 637.55 485.42 L 642.06 488.13 Z M 643.47 487.67 L 637.86 484.3 C 637.69 484.2 637.49 484.19 637.32 484.28 L 629.97 488.08 L 622.99 484.44 L 622.99 475.55 L 628.9 471.79 C 629.06 471.69 629.16 471.51 629.16 471.32 L 629.16 464.59 C 629.16 464.39 629.05 464.2 628.88 464.1 C 628.7 464 628.49 464 628.31 464.1 L 615.97 471.4 C 615.8 471.5 615.7 471.68 615.7 471.88 L 615.7 488.15 C 615.7 488.35 615.81 488.54 615.99 488.64 L 629.71 495.93 C 629.79 495.98 629.88 496 629.97 496 C 630.07 496 630.16 495.98 630.25 495.93 L 643.45 488.64 C 643.63 488.54 643.74 488.36 643.74 488.16 C 643.75 487.96 643.64 487.77 643.47 487.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 480px; margin-left: 608px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;"&gt;Elastic Kubernetes&lt;br&gt;Service Cluster&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="608" y="484" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="end" font-weight="bold"&gt;Elastic&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 75.51 685.02 C 75.24 685.03 74.97 685.1 74.73 685.22 L 63.98 690.35 C 63.42 690.62 63.01 691.13 62.87 691.73 L 60.21 703.26 C 60.09 703.79 60.2 704.36 60.49 704.82 C 60.54 704.87 60.57 704.93 60.61 704.99 L 68.04 714.23 C 68.44 714.71 69.04 715 69.65 715 L 81.58 715 C 82.2 715 82.8 714.71 83.19 714.23 L 90.63 704.98 C 91 704.5 91.16 703.86 91.02 703.26 L 88.36 691.73 C 88.23 691.13 87.82 690.62 87.25 690.35 L 76.51 685.22 C 76.2 685.07 75.86 685 75.51 685.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 75.52 685.92 C 75.26 685.93 75 686 74.78 686.11 L 64.68 690.92 C 64.15 691.18 63.76 691.67 63.63 692.23 L 61.14 703.07 C 61.03 703.57 61.12 704.1 61.4 704.53 C 61.44 704.58 61.48 704.63 61.51 704.69 L 68.5 713.38 C 68.87 713.83 69.43 714.1 70.01 714.1 L 81.22 714.1 C 81.8 714.1 82.36 713.83 82.74 713.38 L 89.72 704.68 C 90.08 704.23 90.22 703.63 90.1 703.07 L 87.6 692.23 C 87.47 691.67 87.08 691.18 86.55 690.92 L 76.45 686.11 C 76.16 685.96 75.84 685.9 75.52 685.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 69.07 691.16 L 69.07 691.81 L 68.42 691.81 L 68.42 693.24 L 69.14 693.24 L 69.14 691.87 L 70.49 691.87 L 70.49 691.16 Z M 71.21 691.16 L 71.21 691.87 L 72.64 691.87 L 72.64 691.16 Z M 73.35 691.16 L 73.35 691.87 L 74.77 691.87 L 74.77 691.16 Z M 75.49 691.16 L 75.49 691.87 L 76.92 691.87 L 76.92 691.16 Z M 77.63 691.16 L 77.63 691.87 L 79.06 691.87 L 79.06 691.16 Z M 79.77 691.16 L 79.77 691.87 L 81.19 691.87 L 81.19 691.16 Z M 81.91 691.16 L 81.91 691.87 L 82.11 691.87 L 82.11 692.38 L 82.82 692.38 L 82.82 691.52 C 82.82 691.32 82.67 691.16 82.47 691.16 Z M 82.11 693.1 L 82.11 694.52 L 82.82 694.52 L 82.82 693.1 Z M 68.42 693.95 L 68.42 695.38 L 69.14 695.38 L 69.14 693.95 Z M 82.11 695.24 L 82.11 696.66 L 82.83 696.66 L 82.83 695.24 Z M 68.42 696.09 L 68.42 697.52 L 69.14 697.52 L 69.14 696.09 Z M 82.11 697.38 L 82.11 698.8 L 82.83 698.8 L 82.83 697.38 Z M 68.42 698.23 L 68.42 699.66 L 69.14 699.66 L 69.14 698.23 Z M 82.11 699.52 L 82.11 700.94 L 82.83 700.94 L 82.83 699.52 Z M 68.42 700.38 L 68.42 701.8 L 69.14 701.8 L 69.14 700.38 Z M 82.11 701.66 L 82.11 703.09 L 82.83 703.09 L 82.83 701.66 Z M 68.42 702.51 L 68.42 703.45 C 68.43 703.54 68.46 703.63 68.53 703.7 C 68.6 703.76 68.69 703.8 68.78 703.8 L 69.28 703.8 L 69.28 703.09 L 69.14 703.09 L 69.14 702.51 Z M 69.99 703.09 L 69.99 703.8 L 71.42 703.8 L 71.42 703.09 Z M 72.13 703.09 L 72.13 703.8 L 73.56 703.8 L 73.56 703.09 Z M 74.27 703.09 L 74.27 703.8 L 75.7 703.8 L 75.7 703.09 Z M 76.41 703.09 L 76.41 703.8 L 77.84 703.8 L 77.84 703.09 Z M 78.55 703.09 L 78.55 703.8 L 79.98 703.8 L 79.98 703.09 Z M 80.69 703.09 L 80.69 703.8 L 82.12 703.8 L 82.12 703.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="75.13" y="709"&gt;ns&lt;/text&gt;&lt;/g&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: 700px; margin-left: 93px;"&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: all; white-space: nowrap;"&gt;namespace&lt;br&gt;workload1&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="93" y="704" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="391.25" y="510" width="198.75" height="130" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 574.26 510.02 C 573.99 510.03 573.72 510.1 573.48 510.22 L 562.73 515.35 C 562.17 515.62 561.76 516.13 561.62 516.73 L 558.96 528.26 C 558.84 528.79 558.95 529.36 559.24 529.82 C 559.29 529.87 559.32 529.93 559.36 529.99 L 566.79 539.23 C 567.19 539.71 567.79 540 568.4 540 L 580.33 540 C 580.95 540 581.55 539.71 581.94 539.23 L 589.38 529.98 C 589.75 529.5 589.91 528.86 589.77 528.26 L 587.11 516.73 C 586.98 516.13 586.57 515.62 586 515.35 L 575.26 510.22 C 574.95 510.07 574.61 510 574.26 510.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 574.27 510.92 C 574.01 510.93 573.75 511 573.53 511.11 L 563.43 515.92 C 562.9 516.18 562.51 516.67 562.38 517.23 L 559.89 528.07 C 559.78 528.57 559.87 529.1 560.15 529.53 C 560.19 529.58 560.23 529.63 560.26 529.69 L 567.25 538.38 C 567.62 538.83 568.18 539.1 568.76 539.1 L 579.97 539.1 C 580.55 539.1 581.11 538.83 581.49 538.38 L 588.48 529.68 C 588.83 529.23 588.97 528.63 588.85 528.07 L 586.35 517.23 C 586.22 516.67 585.83 516.18 585.3 515.92 L 575.2 511.11 C 574.91 510.96 574.59 510.9 574.27 510.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 567.82 516.16 L 567.82 516.81 L 567.17 516.81 L 567.17 518.24 L 567.89 518.24 L 567.89 516.87 L 569.24 516.87 L 569.24 516.16 Z M 569.96 516.16 L 569.96 516.87 L 571.39 516.87 L 571.39 516.16 Z M 572.1 516.16 L 572.1 516.87 L 573.52 516.87 L 573.52 516.16 Z M 574.24 516.16 L 574.24 516.87 L 575.67 516.87 L 575.67 516.16 Z M 576.38 516.16 L 576.38 516.87 L 577.81 516.87 L 577.81 516.16 Z M 578.52 516.16 L 578.52 516.87 L 579.94 516.87 L 579.94 516.16 Z M 580.66 516.16 L 580.66 516.87 L 580.86 516.87 L 580.86 517.38 L 581.57 517.38 L 581.57 516.52 C 581.57 516.32 581.42 516.16 581.22 516.16 Z M 580.86 518.1 L 580.86 519.52 L 581.57 519.52 L 581.57 518.1 Z M 567.17 518.95 L 567.17 520.38 L 567.89 520.38 L 567.89 518.95 Z M 580.86 520.24 L 580.86 521.66 L 581.58 521.66 L 581.58 520.24 Z M 567.17 521.09 L 567.17 522.52 L 567.89 522.52 L 567.89 521.09 Z M 580.86 522.38 L 580.86 523.8 L 581.58 523.8 L 581.58 522.38 Z M 567.17 523.23 L 567.17 524.66 L 567.89 524.66 L 567.89 523.23 Z M 580.86 524.52 L 580.86 525.94 L 581.58 525.94 L 581.58 524.52 Z M 567.17 525.38 L 567.17 526.8 L 567.89 526.8 L 567.89 525.38 Z M 580.86 526.66 L 580.86 528.09 L 581.58 528.09 L 581.58 526.66 Z M 567.17 527.51 L 567.17 528.45 C 567.18 528.54 567.21 528.63 567.28 528.7 C 567.35 528.76 567.44 528.8 567.53 528.8 L 568.03 528.8 L 568.03 528.09 L 567.89 528.09 L 567.89 527.51 Z M 568.74 528.09 L 568.74 528.8 L 570.17 528.8 L 570.17 528.09 Z M 570.88 528.09 L 570.88 528.8 L 572.31 528.8 L 572.31 528.09 Z M 573.02 528.09 L 573.02 528.8 L 574.45 528.8 L 574.45 528.09 Z M 575.16 528.09 L 575.16 528.8 L 576.59 528.8 L 576.59 528.09 Z M 577.3 528.09 L 577.3 528.8 L 578.73 528.8 L 578.73 528.09 Z M 579.44 528.09 L 579.44 528.8 L 580.87 528.8 L 580.87 528.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="573.88" y="534"&gt;ns&lt;/text&gt;&lt;/g&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-end; width: 1px; height: 1px; padding-top: 525px; margin-left: 557px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;namespace&lt;br&gt;cert-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="557" y="529" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="400.75" y="549.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="453" y="569" width="73" height="15" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="452.75" y="579.5"&gt;ClusterIssuer&lt;/text&gt;&lt;/g&gt;&lt;image x="209.5" y="714.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="262" y="726" width="59" height="30" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="261.5" y="737.5"&gt;Workload1&lt;/text&gt;&lt;text x="261.5" y="751.5"&gt;Issuer&lt;/text&gt;&lt;/g&gt;&lt;path d="M 210 740 L 189 740" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="140" y="810" width="42.11" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 142.58 810 C 141.16 810 140 811.22 140 812.71 L 140 843.84 C 140 845.33 141.16 846.56 142.58 846.56 L 169.88 846.56 L 169.88 850 L 173.08 848.07 L 176.28 850 L 176.28 846.56 L 179.53 846.56 C 180.95 846.56 182.11 845.33 182.11 843.84 L 182.11 812.71 C 182.11 811.22 180.95 810 179.53 810 Z M 142.58 811.23 L 179.53 811.23 C 180.32 811.23 180.94 811.88 180.94 812.71 L 180.94 843.84 C 180.94 844.68 180.32 845.33 179.53 845.33 L 176.28 845.33 L 176.28 843.01 C 177.62 841.95 178.4 840.29 178.4 838.53 C 178.4 837.04 177.84 835.61 176.84 834.55 C 175.83 833.5 174.47 832.9 173.05 832.9 C 170.09 832.9 167.7 835.42 167.7 838.53 C 167.7 840.31 168.51 841.99 169.88 843.05 L 169.88 845.33 L 142.58 845.33 C 141.79 845.33 141.17 844.68 141.17 843.84 L 141.17 812.71 C 141.17 811.88 141.79 811.23 142.58 811.23 Z M 148.25 818.55 L 148.25 820.58 L 174.12 820.58 L 174.12 818.55 Z M 148.25 824.05 L 148.25 826.08 L 174.12 826.08 L 174.12 824.05 Z M 148.25 829.62 L 148.25 831.65 L 162.41 831.65 L 162.41 829.62 Z" fill="#005073" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 830px; margin-left: 138px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Workload1&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="138" y="834" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Workload&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 182.11 827.6 L 210 826.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 234.82 802.03 C 234.38 802.05 233.94 802.16 233.56 802.36 L 216.37 810.55 C 215.47 810.99 214.81 811.81 214.59 812.77 L 210.34 831.22 C 210.15 832.07 210.31 832.98 210.78 833.72 C 210.86 833.8 210.92 833.88 210.97 833.99 L 222.87 848.77 C 223.5 849.53 224.46 850 225.45 850 L 244.53 850 C 245.51 850 246.47 849.53 247.1 848.77 L 259 833.96 C 259.6 833.2 259.85 832.18 259.63 831.22 L 255.38 812.77 C 255.16 811.81 254.5 810.99 253.6 810.55 L 236.41 802.36 C 235.92 802.11 235.37 802 234.82 802.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 234.83 803.47 C 234.42 803.49 234.01 803.59 233.65 803.77 L 217.49 811.48 C 216.64 811.89 216.02 812.66 215.82 813.57 L 211.82 830.91 C 211.64 831.71 211.8 832.56 212.23 833.25 C 212.31 833.33 212.36 833.41 212.41 833.51 L 223.6 847.4 C 224.19 848.12 225.09 848.56 226.02 848.56 L 243.95 848.56 C 244.88 848.56 245.78 848.12 246.38 847.4 L 257.56 833.49 C 258.13 832.76 258.36 831.81 258.15 830.91 L 254.16 813.57 C 253.95 812.66 253.33 811.89 252.48 811.48 L 236.33 803.77 C 235.86 803.54 235.35 803.44 234.83 803.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 245.47 815.8 L 245.47 826.93 L 235.72 832.33 L 235.67 818.71 Z M 224.53 815.8 L 224.53 826.93 L 234.29 832.34 L 234.34 818.71 Z M 224.53 814.63 L 235 811.6 L 245.47 814.63 L 235 817.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="234.5" y="841.3"&gt;pod&lt;/text&gt;&lt;/g&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: 826px; margin-left: 262px;"&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: all; white-space: nowrap;"&gt;Workload1&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="262" y="830" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Workload1&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="140" y="720" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 140 725.66 L 145.83 720.04 L 177.91 720.04 C 179.81 720 181.44 721.36 181.67 723.18 L 181.67 724.8 L 179.34 724.02 C 179.39 723.12 178.69 722.35 177.76 722.28 L 147.07 722.28 L 147.07 726.9 L 142.32 726.9 L 142.32 743.62 C 142.38 744.27 142.89 744.79 143.56 744.9 L 160.41 744.9 L 161.75 747.09 L 143.36 747.09 C 141.59 746.84 140.21 745.48 140 743.76 Z M 162.74 756.76 L 167.09 749.62 C 161.97 746.24 159.79 740.02 161.72 734.35 C 163.65 728.68 169.23 724.91 175.42 725.11 C 181.61 725.31 186.93 729.42 188.46 735.2 C 190 740.98 187.39 747.05 182.06 750.09 L 186.96 757.19 L 181.62 755.81 L 180.09 759.95 L 174.35 752.57 L 170 760 L 168.02 755.52 Z M 162.79 738.66 C 163.1 744.96 168.45 749.93 174.99 750 C 181.48 749.85 186.74 744.9 187.05 738.66 C 186.88 732.4 181.69 727.34 175.19 727.09 C 168.48 727.06 162.97 732.2 162.79 738.66 Z M 165.75 738.66 C 165.74 736.3 166.71 734.03 168.45 732.37 C 170.18 730.7 172.54 729.78 174.99 729.8 C 177.42 729.81 179.74 730.76 181.45 732.42 C 183.15 734.08 184.1 736.32 184.09 738.66 C 183.98 743.44 179.95 747.26 174.99 747.28 C 169.98 747.34 165.86 743.49 165.75 738.66 Z M 167.78 738.66 C 167.98 742.39 171.13 745.33 174.99 745.43 C 178.82 745.28 181.91 742.35 182.11 738.66 C 182.01 734.9 178.9 731.86 174.99 731.71 C 171.05 731.81 167.88 734.86 167.78 738.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 740px; margin-left: 138px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;CA Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="138" y="744" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA Certif&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 235 765 L 235 780 L 161 780 L 161.04 803.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 161.05 808.88 L 157.54 801.89 L 161.04 803.63 L 164.54 801.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 779px; margin-left: 190px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="190" y="782" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="680" width="270" height="175" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 375.51 685.02 C 375.24 685.03 374.97 685.1 374.73 685.22 L 363.98 690.35 C 363.42 690.62 363.01 691.13 362.87 691.73 L 360.21 703.26 C 360.09 703.79 360.2 704.36 360.49 704.82 C 360.54 704.87 360.57 704.93 360.61 704.99 L 368.04 714.23 C 368.44 714.71 369.04 715 369.65 715 L 381.58 715 C 382.2 715 382.8 714.71 383.19 714.23 L 390.63 704.98 C 391 704.5 391.16 703.86 391.02 703.26 L 388.36 691.73 C 388.23 691.13 387.82 690.62 387.25 690.35 L 376.51 685.22 C 376.2 685.07 375.86 685 375.51 685.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 375.52 685.92 C 375.26 685.93 375 686 374.78 686.11 L 364.68 690.92 C 364.15 691.18 363.76 691.67 363.63 692.23 L 361.14 703.07 C 361.03 703.57 361.12 704.1 361.4 704.53 C 361.44 704.58 361.48 704.63 361.51 704.69 L 368.5 713.38 C 368.87 713.83 369.43 714.1 370.01 714.1 L 381.22 714.1 C 381.8 714.1 382.36 713.83 382.74 713.38 L 389.73 704.68 C 390.08 704.23 390.22 703.63 390.1 703.07 L 387.6 692.23 C 387.47 691.67 387.08 691.18 386.55 690.92 L 376.45 686.11 C 376.16 685.96 375.84 685.9 375.52 685.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 369.07 691.16 L 369.07 691.81 L 368.43 691.81 L 368.43 693.24 L 369.14 693.24 L 369.14 691.87 L 370.49 691.87 L 370.49 691.16 Z M 371.21 691.16 L 371.21 691.87 L 372.64 691.87 L 372.64 691.16 Z M 373.35 691.16 L 373.35 691.87 L 374.77 691.87 L 374.77 691.16 Z M 375.49 691.16 L 375.49 691.87 L 376.92 691.87 L 376.92 691.16 Z M 377.63 691.16 L 377.63 691.87 L 379.06 691.87 L 379.06 691.16 Z M 379.77 691.16 L 379.77 691.87 L 381.19 691.87 L 381.19 691.16 Z M 381.91 691.16 L 381.91 691.87 L 382.11 691.87 L 382.11 692.38 L 382.82 692.38 L 382.82 691.52 C 382.82 691.32 382.67 691.16 382.47 691.16 Z M 382.11 693.1 L 382.11 694.52 L 382.82 694.52 L 382.82 693.1 Z M 368.43 693.95 L 368.43 695.38 L 369.14 695.38 L 369.14 693.95 Z M 382.11 695.24 L 382.11 696.66 L 382.82 696.66 L 382.82 695.24 Z M 368.43 696.09 L 368.43 697.52 L 369.14 697.52 L 369.14 696.09 Z M 382.11 697.38 L 382.11 698.8 L 382.82 698.8 L 382.82 697.38 Z M 368.43 698.23 L 368.43 699.66 L 369.14 699.66 L 369.14 698.23 Z M 382.11 699.52 L 382.11 700.94 L 382.82 700.94 L 382.82 699.52 Z M 368.43 700.38 L 368.43 701.8 L 369.14 701.8 L 369.14 700.38 Z M 382.11 701.66 L 382.11 703.09 L 382.82 703.09 L 382.82 701.66 Z M 368.43 702.51 L 368.43 703.45 C 368.43 703.54 368.46 703.63 368.53 703.7 C 368.6 703.76 368.69 703.8 368.78 703.8 L 369.28 703.8 L 369.28 703.09 L 369.14 703.09 L 369.14 702.51 Z M 369.99 703.09 L 369.99 703.8 L 371.42 703.8 L 371.42 703.09 Z M 372.13 703.09 L 372.13 703.8 L 373.56 703.8 L 373.56 703.09 Z M 374.27 703.09 L 374.27 703.8 L 375.7 703.8 L 375.7 703.09 Z M 376.41 703.09 L 376.41 703.8 L 377.84 703.8 L 377.84 703.09 Z M 378.55 703.09 L 378.55 703.8 L 379.98 703.8 L 379.98 703.09 Z M 380.69 703.09 L 380.69 703.8 L 382.12 703.8 L 382.12 703.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="375.13" y="709"&gt;ns&lt;/text&gt;&lt;/g&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: 700px; margin-left: 393px;"&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: all; white-space: nowrap;"&gt;namespace&lt;br&gt;workload2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="393" y="704" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="509.5" y="714.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="562" y="726" width="59" height="30" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="561.5" y="737.5"&gt;Workload2&lt;/text&gt;&lt;text x="561.5" y="751.5"&gt;Issuer&lt;/text&gt;&lt;/g&gt;&lt;path d="M 510 740 L 489 740" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="440" y="810" width="42.11" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 442.58 810 C 441.16 810 440 811.22 440 812.71 L 440 843.84 C 440 845.33 441.16 846.56 442.58 846.56 L 469.88 846.56 L 469.88 850 L 473.08 848.07 L 476.28 850 L 476.28 846.56 L 479.53 846.56 C 480.95 846.56 482.11 845.33 482.11 843.84 L 482.11 812.71 C 482.11 811.22 480.95 810 479.53 810 Z M 442.58 811.23 L 479.53 811.23 C 480.32 811.23 480.94 811.88 480.94 812.71 L 480.94 843.84 C 480.94 844.68 480.32 845.33 479.53 845.33 L 476.28 845.33 L 476.28 843.01 C 477.62 841.95 478.4 840.29 478.4 838.53 C 478.4 837.04 477.84 835.61 476.84 834.55 C 475.83 833.5 474.47 832.9 473.05 832.9 C 470.09 832.9 467.7 835.42 467.7 838.53 C 467.7 840.31 468.51 841.99 469.88 843.05 L 469.88 845.33 L 442.58 845.33 C 441.79 845.33 441.17 844.68 441.17 843.84 L 441.17 812.71 C 441.17 811.88 441.79 811.23 442.58 811.23 Z M 448.25 818.55 L 448.25 820.58 L 474.12 820.58 L 474.12 818.55 Z M 448.25 824.05 L 448.25 826.08 L 474.12 826.08 L 474.12 824.05 Z M 448.25 829.62 L 448.25 831.65 L 462.41 831.65 L 462.41 829.62 Z" fill="#005073" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 830px; margin-left: 438px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Workload2&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="438" y="834" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Workload&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 482.11 827.6 L 510 826.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 534.82 802.03 C 534.38 802.05 533.94 802.16 533.56 802.36 L 516.37 810.55 C 515.47 810.99 514.81 811.81 514.59 812.77 L 510.34 831.22 C 510.15 832.07 510.31 832.98 510.78 833.72 C 510.86 833.8 510.92 833.88 510.97 833.99 L 522.87 848.77 C 523.5 849.53 524.46 850 525.45 850 L 544.53 850 C 545.51 850 546.47 849.53 547.1 848.77 L 559 833.96 C 559.6 833.2 559.85 832.18 559.63 831.22 L 555.38 812.77 C 555.16 811.81 554.5 810.99 553.6 810.55 L 536.41 802.36 C 535.92 802.11 535.37 802 534.82 802.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 534.83 803.47 C 534.42 803.49 534.01 803.59 533.65 803.77 L 517.49 811.48 C 516.64 811.89 516.02 812.66 515.82 813.57 L 511.82 830.91 C 511.64 831.71 511.8 832.56 512.23 833.25 C 512.31 833.33 512.36 833.41 512.41 833.51 L 523.6 847.4 C 524.19 848.12 525.09 848.56 526.02 848.56 L 543.95 848.56 C 544.88 848.56 545.78 848.12 546.38 847.4 L 557.56 833.49 C 558.13 832.76 558.36 831.81 558.15 830.91 L 554.16 813.57 C 553.95 812.66 553.33 811.89 552.48 811.48 L 536.33 803.77 C 535.86 803.54 535.35 803.44 534.83 803.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 545.47 815.8 L 545.47 826.93 L 535.72 832.33 L 535.67 818.71 Z M 524.53 815.8 L 524.53 826.93 L 534.29 832.34 L 534.34 818.71 Z M 524.53 814.63 L 535 811.6 L 545.47 814.63 L 535 817.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="534.5" y="841.3"&gt;pod&lt;/text&gt;&lt;/g&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: 826px; margin-left: 562px;"&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: all; white-space: nowrap;"&gt;Workload2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="562" y="830" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Workload2&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="440" y="720" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 440 725.66 L 445.83 720.04 L 477.91 720.04 C 479.81 720 481.44 721.36 481.67 723.18 L 481.67 724.8 L 479.34 724.02 C 479.39 723.12 478.69 722.35 477.76 722.28 L 447.07 722.28 L 447.07 726.9 L 442.32 726.9 L 442.32 743.62 C 442.38 744.27 442.89 744.79 443.56 744.9 L 460.41 744.9 L 461.75 747.09 L 443.36 747.09 C 441.59 746.84 440.21 745.48 440 743.76 Z M 462.74 756.76 L 467.09 749.62 C 461.97 746.24 459.79 740.02 461.72 734.35 C 463.65 728.68 469.23 724.91 475.42 725.11 C 481.61 725.31 486.93 729.42 488.46 735.2 C 490 740.98 487.39 747.05 482.06 750.09 L 486.96 757.19 L 481.62 755.81 L 480.09 759.95 L 474.35 752.57 L 470 760 L 468.02 755.52 Z M 462.79 738.66 C 463.1 744.96 468.45 749.93 474.99 750 C 481.48 749.85 486.74 744.9 487.05 738.66 C 486.88 732.4 481.69 727.34 475.19 727.09 C 468.48 727.06 462.97 732.2 462.79 738.66 Z M 465.75 738.66 C 465.74 736.3 466.71 734.03 468.45 732.37 C 470.18 730.7 472.54 729.78 474.99 729.8 C 477.42 729.81 479.74 730.76 481.45 732.42 C 483.15 734.08 484.1 736.32 484.09 738.66 C 483.98 743.44 479.95 747.26 474.99 747.28 C 469.98 747.34 465.86 743.49 465.75 738.66 Z M 467.78 738.66 C 467.98 742.39 471.13 745.33 474.99 745.43 C 478.82 745.28 481.91 742.35 482.11 738.66 C 482.01 734.9 478.9 731.86 474.99 731.71 C 471.05 731.81 467.88 734.86 467.78 738.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 740px; margin-left: 438px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;CA Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="438" y="744" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA Certif&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 535 765 L 535 780 L 461 780 L 461.04 803.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 461.05 808.88 L 457.54 801.89 L 461.04 803.63 L 464.54 801.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 779px; margin-left: 490px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="490" y="782" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="60" y="480" width="270" height="175" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 75.51 485.02 C 75.24 485.03 74.97 485.1 74.73 485.22 L 63.98 490.35 C 63.42 490.62 63.01 491.13 62.87 491.73 L 60.21 503.26 C 60.09 503.79 60.2 504.36 60.49 504.82 C 60.54 504.87 60.57 504.93 60.61 504.99 L 68.04 514.23 C 68.44 514.71 69.04 515 69.65 515 L 81.58 515 C 82.2 515 82.8 514.71 83.19 514.23 L 90.63 504.98 C 91 504.5 91.16 503.86 91.02 503.26 L 88.36 491.73 C 88.23 491.13 87.82 490.62 87.25 490.35 L 76.51 485.22 C 76.2 485.07 75.86 485 75.51 485.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 75.52 485.92 C 75.26 485.93 75 486 74.78 486.11 L 64.68 490.92 C 64.15 491.18 63.76 491.67 63.63 492.23 L 61.14 503.07 C 61.03 503.57 61.12 504.1 61.4 504.53 C 61.44 504.58 61.48 504.63 61.51 504.69 L 68.5 513.38 C 68.87 513.83 69.43 514.1 70.01 514.1 L 81.22 514.1 C 81.8 514.1 82.36 513.83 82.74 513.38 L 89.72 504.68 C 90.08 504.23 90.22 503.63 90.1 503.07 L 87.6 492.23 C 87.47 491.67 87.08 491.18 86.55 490.92 L 76.45 486.11 C 76.16 485.96 75.84 485.9 75.52 485.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 69.07 491.16 L 69.07 491.81 L 68.42 491.81 L 68.42 493.24 L 69.14 493.24 L 69.14 491.87 L 70.49 491.87 L 70.49 491.16 Z M 71.21 491.16 L 71.21 491.87 L 72.64 491.87 L 72.64 491.16 Z M 73.35 491.16 L 73.35 491.87 L 74.77 491.87 L 74.77 491.16 Z M 75.49 491.16 L 75.49 491.87 L 76.92 491.87 L 76.92 491.16 Z M 77.63 491.16 L 77.63 491.87 L 79.06 491.87 L 79.06 491.16 Z M 79.77 491.16 L 79.77 491.87 L 81.19 491.87 L 81.19 491.16 Z M 81.91 491.16 L 81.91 491.87 L 82.11 491.87 L 82.11 492.38 L 82.82 492.38 L 82.82 491.52 C 82.82 491.32 82.67 491.16 82.47 491.16 Z M 82.11 493.1 L 82.11 494.52 L 82.82 494.52 L 82.82 493.1 Z M 68.42 493.95 L 68.42 495.38 L 69.14 495.38 L 69.14 493.95 Z M 82.11 495.24 L 82.11 496.66 L 82.83 496.66 L 82.83 495.24 Z M 68.42 496.09 L 68.42 497.52 L 69.14 497.52 L 69.14 496.09 Z M 82.11 497.38 L 82.11 498.8 L 82.83 498.8 L 82.83 497.38 Z M 68.42 498.23 L 68.42 499.66 L 69.14 499.66 L 69.14 498.23 Z M 82.11 499.52 L 82.11 500.94 L 82.83 500.94 L 82.83 499.52 Z M 68.42 500.38 L 68.42 501.8 L 69.14 501.8 L 69.14 500.38 Z M 82.11 501.66 L 82.11 503.09 L 82.83 503.09 L 82.83 501.66 Z M 68.42 502.51 L 68.42 503.45 C 68.43 503.54 68.46 503.63 68.53 503.7 C 68.6 503.76 68.69 503.8 68.78 503.8 L 69.28 503.8 L 69.28 503.09 L 69.14 503.09 L 69.14 502.51 Z M 69.99 503.09 L 69.99 503.8 L 71.42 503.8 L 71.42 503.09 Z M 72.13 503.09 L 72.13 503.8 L 73.56 503.8 L 73.56 503.09 Z M 74.27 503.09 L 74.27 503.8 L 75.7 503.8 L 75.7 503.09 Z M 76.41 503.09 L 76.41 503.8 L 77.84 503.8 L 77.84 503.09 Z M 78.55 503.09 L 78.55 503.8 L 79.98 503.8 L 79.98 503.09 Z M 80.69 503.09 L 80.69 503.8 L 82.12 503.8 L 82.12 503.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="75.13" y="509"&gt;ns&lt;/text&gt;&lt;/g&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: 500px; margin-left: 93px;"&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: all; white-space: nowrap;"&gt;namespace&lt;br&gt;ingress&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="93" y="504" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="209.5" y="514.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="262" y="526" width="41" height="30" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="261.5" y="537.5"&gt;Ingress&lt;/text&gt;&lt;text x="261.5" y="551.5"&gt;Issuer&lt;/text&gt;&lt;/g&gt;&lt;path d="M 210 540 L 189 540" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="140" y="610" width="42.11" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 142.58 610 C 141.16 610 140 611.22 140 612.71 L 140 643.84 C 140 645.33 141.16 646.56 142.58 646.56 L 169.88 646.56 L 169.88 650 L 173.08 648.07 L 176.28 650 L 176.28 646.56 L 179.53 646.56 C 180.95 646.56 182.11 645.33 182.11 643.84 L 182.11 612.71 C 182.11 611.22 180.95 610 179.53 610 Z M 142.58 611.23 L 179.53 611.23 C 180.32 611.23 180.94 611.88 180.94 612.71 L 180.94 643.84 C 180.94 644.68 180.32 645.33 179.53 645.33 L 176.28 645.33 L 176.28 643.01 C 177.62 641.95 178.4 640.29 178.4 638.53 C 178.4 637.04 177.84 635.61 176.84 634.55 C 175.83 633.5 174.47 632.9 173.05 632.9 C 170.09 632.9 167.7 635.42 167.7 638.53 C 167.7 640.31 168.51 641.99 169.88 643.05 L 169.88 645.33 L 142.58 645.33 C 141.79 645.33 141.17 644.68 141.17 643.84 L 141.17 612.71 C 141.17 611.88 141.79 611.23 142.58 611.23 Z M 148.25 618.55 L 148.25 620.58 L 174.12 620.58 L 174.12 618.55 Z M 148.25 624.05 L 148.25 626.08 L 174.12 626.08 L 174.12 624.05 Z M 148.25 629.62 L 148.25 631.65 L 162.41 631.65 L 162.41 629.62 Z" fill="#005073" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 630px; margin-left: 138px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Ingress&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="138" y="634" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Ingress&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 182.11 627.6 L 210 626.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 234.82 602.03 C 234.38 602.05 233.94 602.16 233.56 602.36 L 216.37 610.55 C 215.47 610.99 214.81 611.81 214.59 612.77 L 210.34 631.22 C 210.15 632.07 210.31 632.98 210.78 633.72 C 210.86 633.8 210.92 633.88 210.97 633.99 L 222.87 648.77 C 223.5 649.53 224.46 650 225.45 650 L 244.53 650 C 245.51 650 246.47 649.53 247.1 648.77 L 259 633.96 C 259.6 633.2 259.85 632.18 259.63 631.22 L 255.38 612.77 C 255.16 611.81 254.5 610.99 253.6 610.55 L 236.41 602.36 C 235.92 602.11 235.37 602 234.82 602.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 234.83 603.47 C 234.42 603.49 234.01 603.59 233.65 603.77 L 217.49 611.48 C 216.64 611.89 216.02 612.66 215.82 613.57 L 211.82 630.91 C 211.64 631.71 211.8 632.56 212.23 633.25 C 212.31 633.33 212.36 633.41 212.41 633.51 L 223.6 647.4 C 224.19 648.12 225.09 648.56 226.02 648.56 L 243.95 648.56 C 244.88 648.56 245.78 648.12 246.38 647.4 L 257.56 633.49 C 258.13 632.76 258.36 631.81 258.15 630.91 L 254.16 613.57 C 253.95 612.66 253.33 611.89 252.48 611.48 L 236.33 603.77 C 235.86 603.54 235.35 603.44 234.83 603.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 245.47 615.8 L 245.47 626.93 L 235.72 632.33 L 235.67 618.71 Z M 224.53 615.8 L 224.53 626.93 L 234.29 632.34 L 234.34 618.71 Z M 224.53 614.63 L 235 611.6 L 245.47 614.63 L 235 617.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="234.5" y="641.3"&gt;pod&lt;/text&gt;&lt;/g&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: 626px; margin-left: 262px;"&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: all; white-space: nowrap;"&gt;Ingress&lt;br&gt;Pod&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="262" y="630" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Ingress&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="140" y="520" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 140 525.66 L 145.83 520.04 L 177.91 520.04 C 179.81 520 181.44 521.36 181.67 523.18 L 181.67 524.8 L 179.34 524.02 C 179.39 523.12 178.69 522.35 177.76 522.28 L 147.07 522.28 L 147.07 526.9 L 142.32 526.9 L 142.32 543.62 C 142.38 544.27 142.89 544.79 143.56 544.9 L 160.41 544.9 L 161.75 547.09 L 143.36 547.09 C 141.59 546.84 140.21 545.48 140 543.76 Z M 162.74 556.76 L 167.09 549.62 C 161.97 546.24 159.79 540.02 161.72 534.35 C 163.65 528.68 169.23 524.91 175.42 525.11 C 181.61 525.31 186.93 529.42 188.46 535.2 C 190 540.98 187.39 547.05 182.06 550.09 L 186.96 557.19 L 181.62 555.81 L 180.09 559.95 L 174.35 552.57 L 170 560 L 168.02 555.52 Z M 162.79 538.66 C 163.1 544.96 168.45 549.93 174.99 550 C 181.48 549.85 186.74 544.9 187.05 538.66 C 186.88 532.4 181.69 527.34 175.19 527.09 C 168.48 527.06 162.97 532.2 162.79 538.66 Z M 165.75 538.66 C 165.74 536.3 166.71 534.03 168.45 532.37 C 170.18 530.7 172.54 529.78 174.99 529.8 C 177.42 529.81 179.74 530.76 181.45 532.42 C 183.15 534.08 184.1 536.32 184.09 538.66 C 183.98 543.44 179.95 547.26 174.99 547.28 C 169.98 547.34 165.86 543.49 165.75 538.66 Z M 167.78 538.66 C 167.98 542.39 171.13 545.33 174.99 545.43 C 178.82 545.28 181.91 542.35 182.11 538.66 C 182.01 534.9 178.9 531.86 174.99 531.71 C 171.05 531.81 167.88 534.86 167.78 538.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 540px; margin-left: 138px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;CA Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="138" y="544" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA Certif&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 235 565 L 235 580 L 161 580 L 161.04 603.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 161.05 608.88 L 157.54 601.89 L 161.04 603.63 L 164.54 601.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 579px; margin-left: 190px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="190" y="582" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 401.25 562.5 L 360 563 L 360 500 L 165 500 L 165 513.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 165 518.88 L 161.5 511.88 L 165 513.63 L 168.5 511.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 511px; margin-left: 346px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="346" y="514" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 401.25 587.5 L 360 588 L 360 670 L 165 670 L 165 713.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 165 718.88 L 161.5 711.88 L 165 713.63 L 168.5 711.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 660px; margin-left: 346px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="346" y="664" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 426.25 600 L 426 670 L 465 670 L 465 713.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 465 718.88 L 461.5 711.88 L 465 713.63 L 468.5 711.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 661px; margin-left: 441px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="441" y="664" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 554.82 550.03 C 554.38 550.05 553.94 550.16 553.56 550.36 L 536.37 558.55 C 535.47 558.99 534.81 559.81 534.59 560.77 L 530.34 579.22 C 530.15 580.07 530.31 580.98 530.78 581.72 C 530.86 581.8 530.92 581.88 530.97 581.99 L 542.87 596.77 C 543.5 597.53 544.46 598 545.45 598 L 564.53 598 C 565.51 598 566.47 597.53 567.1 596.77 L 579 581.96 C 579.6 581.2 579.85 580.18 579.63 579.22 L 575.38 560.77 C 575.16 559.81 574.5 558.99 573.6 558.55 L 556.41 550.36 C 555.92 550.11 555.37 550 554.82 550.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 554.83 551.47 C 554.42 551.49 554.01 551.59 553.65 551.77 L 537.49 559.48 C 536.64 559.89 536.02 560.66 535.82 561.57 L 531.82 578.91 C 531.64 579.71 531.8 580.56 532.23 581.25 C 532.31 581.33 532.36 581.41 532.41 581.51 L 543.6 595.4 C 544.19 596.12 545.09 596.56 546.02 596.56 L 563.95 596.56 C 564.88 596.56 565.78 596.12 566.38 595.4 L 577.56 581.49 C 578.13 580.76 578.36 579.81 578.15 578.91 L 574.16 561.57 C 573.95 560.66 573.33 559.89 572.48 559.48 L 556.33 551.77 C 555.86 551.54 555.35 551.44 554.83 551.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 555 559.6 L 546.52 563.37 L 546.52 569.03 C 546.52 574.26 550.14 579.15 555 580.34 C 559.87 579.15 563.48 574.26 563.48 569.03 L 563.48 563.37 Z M 555 563.59 C 556.1 563.59 557.09 564.26 557.5 565.27 C 557.92 566.29 557.7 567.45 556.92 568.23 C 556.14 569.01 554.97 569.24 553.96 568.82 C 552.94 568.4 552.28 567.41 552.28 566.31 C 552.28 564.81 553.5 563.6 555 563.6 Z M 560.09 574.82 C 560.09 574.88 560.06 574.95 560.03 575.01 L 559.84 575.28 C 558.64 576.93 556.94 578.15 555 578.76 C 553.05 578.15 551.36 576.93 550.16 575.28 L 549.97 575.01 C 549.93 574.95 549.91 574.89 549.91 574.82 L 549.91 573.31 C 549.91 571.23 553.3 570.18 555 570.18 C 556.7 570.18 560.09 571.23 560.09 573.31 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="554.5" y="589.3"&gt;sa&lt;/text&gt;&lt;/g&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 605px; margin-left: 555px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Service&lt;br&gt;Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="555" y="617" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Service&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="670" y="520" width="450" height="300" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="660" y="460" width="610" height="410" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="660" y="460" width="610" height="410" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="680" y="680" width="270" height="175" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 1230 460 L 1270 460 L 1270 500 L 1230 500 Z" fill="#ed7100" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1253.56 475.36 L 1249.73 479.8 L 1253.89 484.66 L 1252.34 484.66 L 1248.6 480.29 L 1248.6 484.78 L 1247.48 484.78 L 1247.48 475.24 L 1248.6 475.24 L 1248.6 479.17 L 1252.06 475.36 Z M 1263.18 484.35 L 1258.69 481.66 L 1258.69 475.24 C 1258.69 475.05 1258.59 474.86 1258.42 474.76 L 1251.96 471 L 1251.96 465.57 L 1263.18 472.2 Z M 1264.03 471.4 L 1251.69 464.1 C 1251.51 464 1251.3 464 1251.12 464.1 C 1250.95 464.2 1250.84 464.39 1250.84 464.59 L 1250.84 471.32 C 1250.84 471.52 1250.95 471.7 1251.12 471.8 L 1257.57 475.57 L 1257.57 481.98 C 1257.57 482.17 1257.68 482.36 1257.85 482.46 L 1263.45 485.82 C 1263.54 485.88 1263.64 485.9 1263.74 485.9 C 1263.84 485.9 1263.93 485.88 1264.02 485.83 C 1264.2 485.73 1264.3 485.54 1264.3 485.34 L 1264.3 471.88 C 1264.3 471.68 1264.2 471.5 1264.03 471.4 Z M 1249.97 494.8 L 1236.82 487.81 L 1236.82 472.2 L 1248.04 465.57 L 1248.04 471.01 L 1242.13 474.77 C 1241.96 474.87 1241.87 475.05 1241.87 475.24 L 1241.87 484.78 C 1241.87 484.99 1241.98 485.18 1242.17 485.28 L 1249.71 489.21 C 1249.88 489.29 1250.07 489.29 1250.23 489.21 L 1257.55 485.42 L 1262.06 488.13 Z M 1263.47 487.67 L 1257.86 484.3 C 1257.69 484.2 1257.49 484.19 1257.32 484.28 L 1249.97 488.08 L 1242.99 484.44 L 1242.99 475.55 L 1248.9 471.79 C 1249.06 471.69 1249.16 471.51 1249.16 471.32 L 1249.16 464.59 C 1249.16 464.39 1249.05 464.2 1248.88 464.1 C 1248.7 464 1248.49 464 1248.31 464.1 L 1235.97 471.4 C 1235.8 471.5 1235.7 471.68 1235.7 471.88 L 1235.7 488.15 C 1235.7 488.35 1235.81 488.54 1235.99 488.64 L 1249.71 495.93 C 1249.79 495.98 1249.88 496 1249.97 496 C 1250.07 496 1250.16 495.98 1250.25 495.93 L 1263.45 488.64 C 1263.63 488.54 1263.74 488.36 1263.74 488.16 C 1263.75 487.96 1263.64 487.77 1263.47 487.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 480px; margin-left: 1228px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;"&gt;Elastic Kubernetes&lt;br&gt;Service Cluster&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1228" y="484" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="end" font-weight="bold"&gt;Elastic&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 695.51 685.02 C 695.24 685.03 694.97 685.1 694.73 685.22 L 683.98 690.35 C 683.42 690.62 683.01 691.13 682.87 691.73 L 680.21 703.26 C 680.09 703.79 680.2 704.36 680.49 704.82 C 680.54 704.87 680.57 704.93 680.61 704.99 L 688.04 714.23 C 688.44 714.71 689.04 715 689.65 715 L 701.58 715 C 702.2 715 702.8 714.71 703.19 714.23 L 710.63 704.98 C 711 704.5 711.16 703.86 711.02 703.26 L 708.36 691.73 C 708.23 691.13 707.82 690.62 707.25 690.35 L 696.51 685.22 C 696.2 685.07 695.86 685 695.51 685.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 695.52 685.92 C 695.26 685.93 695 686 694.78 686.11 L 684.68 690.92 C 684.15 691.18 683.76 691.67 683.63 692.23 L 681.14 703.07 C 681.03 703.57 681.12 704.1 681.4 704.53 C 681.44 704.58 681.48 704.63 681.51 704.69 L 688.5 713.38 C 688.87 713.83 689.43 714.1 690.01 714.1 L 701.22 714.1 C 701.8 714.1 702.36 713.83 702.74 713.38 L 709.73 704.68 C 710.08 704.23 710.22 703.63 710.1 703.07 L 707.6 692.23 C 707.47 691.67 707.08 691.18 706.55 690.92 L 696.45 686.11 C 696.16 685.96 695.84 685.9 695.52 685.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 689.07 691.16 L 689.07 691.81 L 688.42 691.81 L 688.42 693.24 L 689.14 693.24 L 689.14 691.87 L 690.49 691.87 L 690.49 691.16 Z M 691.21 691.16 L 691.21 691.87 L 692.64 691.87 L 692.64 691.16 Z M 693.35 691.16 L 693.35 691.87 L 694.77 691.87 L 694.77 691.16 Z M 695.49 691.16 L 695.49 691.87 L 696.92 691.87 L 696.92 691.16 Z M 697.63 691.16 L 697.63 691.87 L 699.06 691.87 L 699.06 691.16 Z M 699.77 691.16 L 699.77 691.87 L 701.19 691.87 L 701.19 691.16 Z M 701.91 691.16 L 701.91 691.87 L 702.11 691.87 L 702.11 692.38 L 702.82 692.38 L 702.82 691.52 C 702.82 691.32 702.67 691.16 702.47 691.16 Z M 702.11 693.1 L 702.11 694.52 L 702.82 694.52 L 702.82 693.1 Z M 688.42 693.95 L 688.42 695.38 L 689.14 695.38 L 689.14 693.95 Z M 702.11 695.24 L 702.11 696.66 L 702.83 696.66 L 702.83 695.24 Z M 688.42 696.09 L 688.42 697.52 L 689.14 697.52 L 689.14 696.09 Z M 702.11 697.38 L 702.11 698.8 L 702.83 698.8 L 702.83 697.38 Z M 688.42 698.23 L 688.42 699.66 L 689.14 699.66 L 689.14 698.23 Z M 702.11 699.52 L 702.11 700.94 L 702.83 700.94 L 702.83 699.52 Z M 688.42 700.38 L 688.42 701.8 L 689.14 701.8 L 689.14 700.38 Z M 702.11 701.66 L 702.11 703.09 L 702.83 703.09 L 702.83 701.66 Z M 688.42 702.51 L 688.42 703.45 C 688.43 703.54 688.46 703.63 688.53 703.7 C 688.6 703.76 688.69 703.8 688.78 703.8 L 689.28 703.8 L 689.28 703.09 L 689.14 703.09 L 689.14 702.51 Z M 689.99 703.09 L 689.99 703.8 L 691.42 703.8 L 691.42 703.09 Z M 692.13 703.09 L 692.13 703.8 L 693.56 703.8 L 693.56 703.09 Z M 694.27 703.09 L 694.27 703.8 L 695.7 703.8 L 695.7 703.09 Z M 696.41 703.09 L 696.41 703.8 L 697.84 703.8 L 697.84 703.09 Z M 698.55 703.09 L 698.55 703.8 L 699.98 703.8 L 699.98 703.09 Z M 700.69 703.09 L 700.69 703.8 L 702.12 703.8 L 702.12 703.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="695.13" y="709"&gt;ns&lt;/text&gt;&lt;/g&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: 700px; margin-left: 713px;"&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: all; white-space: nowrap;"&gt;namespace&lt;br&gt;workload1&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="713" y="704" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="1011.25" y="510" width="198.75" height="130" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 1194.26 510.02 C 1193.99 510.03 1193.72 510.1 1193.48 510.22 L 1182.73 515.35 C 1182.17 515.62 1181.76 516.13 1181.62 516.73 L 1178.96 528.26 C 1178.84 528.79 1178.95 529.36 1179.24 529.82 C 1179.29 529.87 1179.32 529.93 1179.36 529.99 L 1186.79 539.23 C 1187.19 539.71 1187.79 540 1188.4 540 L 1200.33 540 C 1200.95 540 1201.55 539.71 1201.94 539.23 L 1209.38 529.98 C 1209.75 529.5 1209.91 528.86 1209.77 528.26 L 1207.11 516.73 C 1206.98 516.13 1206.57 515.62 1206 515.35 L 1195.26 510.22 C 1194.95 510.07 1194.61 510 1194.26 510.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1194.27 510.92 C 1194.01 510.93 1193.75 511 1193.53 511.11 L 1183.43 515.92 C 1182.9 516.18 1182.51 516.67 1182.38 517.23 L 1179.89 528.07 C 1179.78 528.57 1179.87 529.1 1180.15 529.53 C 1180.19 529.58 1180.23 529.63 1180.26 529.69 L 1187.25 538.38 C 1187.62 538.83 1188.18 539.1 1188.76 539.1 L 1199.97 539.1 C 1200.55 539.1 1201.11 538.83 1201.49 538.38 L 1208.47 529.68 C 1208.83 529.23 1208.97 528.63 1208.85 528.07 L 1206.35 517.23 C 1206.22 516.67 1205.83 516.18 1205.3 515.92 L 1195.2 511.11 C 1194.91 510.96 1194.59 510.9 1194.27 510.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1187.82 516.16 L 1187.82 516.81 L 1187.17 516.81 L 1187.17 518.24 L 1187.89 518.24 L 1187.89 516.87 L 1189.24 516.87 L 1189.24 516.16 Z M 1189.96 516.16 L 1189.96 516.87 L 1191.39 516.87 L 1191.39 516.16 Z M 1192.1 516.16 L 1192.1 516.87 L 1193.52 516.87 L 1193.52 516.16 Z M 1194.24 516.16 L 1194.24 516.87 L 1195.67 516.87 L 1195.67 516.16 Z M 1196.38 516.16 L 1196.38 516.87 L 1197.81 516.87 L 1197.81 516.16 Z M 1198.52 516.16 L 1198.52 516.87 L 1199.94 516.87 L 1199.94 516.16 Z M 1200.66 516.16 L 1200.66 516.87 L 1200.86 516.87 L 1200.86 517.38 L 1201.57 517.38 L 1201.57 516.52 C 1201.57 516.32 1201.42 516.16 1201.22 516.16 Z M 1200.86 518.1 L 1200.86 519.52 L 1201.57 519.52 L 1201.57 518.1 Z M 1187.17 518.95 L 1187.17 520.38 L 1187.89 520.38 L 1187.89 518.95 Z M 1200.86 520.24 L 1200.86 521.66 L 1201.58 521.66 L 1201.58 520.24 Z M 1187.17 521.09 L 1187.17 522.52 L 1187.89 522.52 L 1187.89 521.09 Z M 1200.86 522.38 L 1200.86 523.8 L 1201.58 523.8 L 1201.58 522.38 Z M 1187.17 523.23 L 1187.17 524.66 L 1187.89 524.66 L 1187.89 523.23 Z M 1200.86 524.52 L 1200.86 525.94 L 1201.58 525.94 L 1201.58 524.52 Z M 1187.17 525.38 L 1187.17 526.8 L 1187.89 526.8 L 1187.89 525.38 Z M 1200.86 526.66 L 1200.86 528.09 L 1201.58 528.09 L 1201.58 526.66 Z M 1187.17 527.51 L 1187.17 528.45 C 1187.18 528.54 1187.21 528.63 1187.28 528.7 C 1187.35 528.76 1187.44 528.8 1187.53 528.8 L 1188.03 528.8 L 1188.03 528.09 L 1187.89 528.09 L 1187.89 527.51 Z M 1188.74 528.09 L 1188.74 528.8 L 1190.17 528.8 L 1190.17 528.09 Z M 1190.88 528.09 L 1190.88 528.8 L 1192.31 528.8 L 1192.31 528.09 Z M 1193.02 528.09 L 1193.02 528.8 L 1194.45 528.8 L 1194.45 528.09 Z M 1195.16 528.09 L 1195.16 528.8 L 1196.59 528.8 L 1196.59 528.09 Z M 1197.3 528.09 L 1197.3 528.8 L 1198.73 528.8 L 1198.73 528.09 Z M 1199.44 528.09 L 1199.44 528.8 L 1200.87 528.8 L 1200.87 528.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="1193.88" y="534"&gt;ns&lt;/text&gt;&lt;/g&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-end; width: 1px; height: 1px; padding-top: 525px; margin-left: 1177px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;namespace&lt;br&gt;cert-manager&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1177" y="529" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="1020.75" y="549.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="1073" y="569" width="73" height="15" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="1072.75" y="579.5"&gt;ClusterIssuer&lt;/text&gt;&lt;/g&gt;&lt;image x="829.5" y="714.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="882" y="726" width="59" height="30" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="881.5" y="737.5"&gt;Workload1&lt;/text&gt;&lt;text x="881.5" y="751.5"&gt;Issuer&lt;/text&gt;&lt;/g&gt;&lt;path d="M 830 740 L 809 740" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="760" y="810" width="42.11" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 762.58 810 C 761.16 810 760 811.22 760 812.71 L 760 843.84 C 760 845.33 761.16 846.56 762.58 846.56 L 789.88 846.56 L 789.88 850 L 793.08 848.07 L 796.28 850 L 796.28 846.56 L 799.53 846.56 C 800.95 846.56 802.11 845.33 802.11 843.84 L 802.11 812.71 C 802.11 811.22 800.95 810 799.53 810 Z M 762.58 811.23 L 799.53 811.23 C 800.32 811.23 800.94 811.88 800.94 812.71 L 800.94 843.84 C 800.94 844.68 800.32 845.33 799.53 845.33 L 796.28 845.33 L 796.28 843.01 C 797.62 841.95 798.4 840.29 798.4 838.53 C 798.4 837.04 797.84 835.61 796.84 834.55 C 795.83 833.5 794.47 832.9 793.05 832.9 C 790.09 832.9 787.7 835.42 787.7 838.53 C 787.7 840.31 788.51 841.99 789.88 843.05 L 789.88 845.33 L 762.58 845.33 C 761.79 845.33 761.17 844.68 761.17 843.84 L 761.17 812.71 C 761.17 811.88 761.79 811.23 762.58 811.23 Z M 768.25 818.55 L 768.25 820.58 L 794.12 820.58 L 794.12 818.55 Z M 768.25 824.05 L 768.25 826.08 L 794.12 826.08 L 794.12 824.05 Z M 768.25 829.62 L 768.25 831.65 L 782.41 831.65 L 782.41 829.62 Z" fill="#005073" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 830px; margin-left: 758px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Workload1&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="758" y="834" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Workload&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 802.11 827.6 L 830 826.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 854.82 802.03 C 854.38 802.05 853.94 802.16 853.56 802.36 L 836.37 810.55 C 835.47 810.99 834.81 811.81 834.59 812.77 L 830.34 831.22 C 830.15 832.07 830.31 832.98 830.78 833.72 C 830.86 833.8 830.92 833.88 830.97 833.99 L 842.87 848.77 C 843.5 849.53 844.46 850 845.45 850 L 864.53 850 C 865.51 850 866.47 849.53 867.1 848.77 L 879 833.96 C 879.6 833.2 879.85 832.18 879.63 831.22 L 875.38 812.77 C 875.16 811.81 874.5 810.99 873.6 810.55 L 856.41 802.36 C 855.92 802.11 855.37 802 854.82 802.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 854.83 803.47 C 854.42 803.49 854.01 803.59 853.65 803.77 L 837.49 811.48 C 836.64 811.89 836.02 812.66 835.82 813.57 L 831.82 830.91 C 831.64 831.71 831.8 832.56 832.23 833.25 C 832.31 833.33 832.36 833.41 832.41 833.51 L 843.6 847.4 C 844.19 848.12 845.09 848.56 846.02 848.56 L 863.95 848.56 C 864.88 848.56 865.78 848.12 866.38 847.4 L 877.56 833.49 C 878.13 832.76 878.36 831.81 878.15 830.91 L 874.16 813.57 C 873.95 812.66 873.33 811.89 872.48 811.48 L 856.33 803.77 C 855.86 803.54 855.35 803.44 854.83 803.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 865.47 815.8 L 865.47 826.93 L 855.72 832.33 L 855.67 818.71 Z M 844.53 815.8 L 844.53 826.93 L 854.29 832.34 L 854.34 818.71 Z M 844.53 814.63 L 855 811.6 L 865.47 814.63 L 855 817.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="854.5" y="841.3"&gt;pod&lt;/text&gt;&lt;/g&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: 826px; margin-left: 882px;"&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: all; white-space: nowrap;"&gt;Workload1&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="882" y="830" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Workload1&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="760" y="720" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 760 725.66 L 765.83 720.04 L 797.91 720.04 C 799.81 720 801.44 721.36 801.67 723.18 L 801.67 724.8 L 799.34 724.02 C 799.39 723.12 798.69 722.35 797.76 722.28 L 767.07 722.28 L 767.07 726.9 L 762.32 726.9 L 762.32 743.62 C 762.38 744.27 762.89 744.79 763.56 744.9 L 780.41 744.9 L 781.75 747.09 L 763.36 747.09 C 761.59 746.84 760.21 745.48 760 743.76 Z M 782.74 756.76 L 787.09 749.62 C 781.97 746.24 779.79 740.02 781.72 734.35 C 783.65 728.68 789.23 724.91 795.42 725.11 C 801.61 725.31 806.93 729.42 808.46 735.2 C 810 740.98 807.39 747.05 802.06 750.09 L 806.96 757.19 L 801.62 755.81 L 800.09 759.95 L 794.35 752.57 L 790 760 L 788.02 755.52 Z M 782.79 738.66 C 783.1 744.96 788.45 749.93 794.99 750 C 801.48 749.85 806.74 744.9 807.05 738.66 C 806.88 732.4 801.69 727.34 795.19 727.09 C 788.48 727.06 782.97 732.2 782.79 738.66 Z M 785.75 738.66 C 785.74 736.3 786.71 734.03 788.45 732.37 C 790.18 730.7 792.54 729.78 794.99 729.8 C 797.42 729.81 799.74 730.76 801.45 732.42 C 803.15 734.08 804.1 736.32 804.09 738.66 C 803.98 743.44 799.95 747.26 794.99 747.28 C 789.98 747.34 785.86 743.49 785.75 738.66 Z M 787.78 738.66 C 787.98 742.39 791.13 745.33 794.99 745.43 C 798.82 745.28 801.91 742.35 802.11 738.66 C 802.01 734.9 798.9 731.86 794.99 731.71 C 791.05 731.81 787.88 734.86 787.78 738.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 740px; margin-left: 758px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;CA Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="758" y="744" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA Certif&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 855 765 L 855 780 L 781 780 L 781.04 803.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 781.05 808.88 L 777.54 801.89 L 781.04 803.63 L 784.54 801.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 779px; margin-left: 810px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="810" y="782" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="980" y="680" width="270" height="175" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 995.51 685.02 C 995.24 685.03 994.97 685.1 994.73 685.22 L 983.98 690.35 C 983.42 690.62 983.01 691.13 982.87 691.73 L 980.21 703.26 C 980.09 703.79 980.2 704.36 980.49 704.82 C 980.54 704.87 980.57 704.93 980.61 704.99 L 988.04 714.23 C 988.44 714.71 989.04 715 989.65 715 L 1001.58 715 C 1002.2 715 1002.8 714.71 1003.19 714.23 L 1010.63 704.98 C 1011 704.5 1011.16 703.86 1011.02 703.26 L 1008.36 691.73 C 1008.23 691.13 1007.82 690.62 1007.25 690.35 L 996.51 685.22 C 996.2 685.07 995.86 685 995.51 685.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 995.52 685.92 C 995.26 685.93 995 686 994.78 686.11 L 984.68 690.92 C 984.15 691.18 983.76 691.67 983.63 692.23 L 981.14 703.07 C 981.03 703.57 981.12 704.1 981.4 704.53 C 981.44 704.58 981.48 704.63 981.51 704.69 L 988.5 713.38 C 988.87 713.83 989.43 714.1 990.01 714.1 L 1001.22 714.1 C 1001.8 714.1 1002.36 713.83 1002.74 713.38 L 1009.73 704.68 C 1010.08 704.23 1010.22 703.63 1010.1 703.07 L 1007.6 692.23 C 1007.47 691.67 1007.08 691.18 1006.55 690.92 L 996.45 686.11 C 996.16 685.96 995.84 685.9 995.52 685.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 989.07 691.16 L 989.07 691.81 L 988.42 691.81 L 988.42 693.24 L 989.14 693.24 L 989.14 691.87 L 990.49 691.87 L 990.49 691.16 Z M 991.21 691.16 L 991.21 691.87 L 992.64 691.87 L 992.64 691.16 Z M 993.35 691.16 L 993.35 691.87 L 994.77 691.87 L 994.77 691.16 Z M 995.49 691.16 L 995.49 691.87 L 996.92 691.87 L 996.92 691.16 Z M 997.63 691.16 L 997.63 691.87 L 999.06 691.87 L 999.06 691.16 Z M 999.77 691.16 L 999.77 691.87 L 1001.19 691.87 L 1001.19 691.16 Z M 1001.91 691.16 L 1001.91 691.87 L 1002.11 691.87 L 1002.11 692.38 L 1002.82 692.38 L 1002.82 691.52 C 1002.82 691.32 1002.67 691.16 1002.47 691.16 Z M 1002.11 693.1 L 1002.11 694.52 L 1002.82 694.52 L 1002.82 693.1 Z M 988.42 693.95 L 988.42 695.38 L 989.14 695.38 L 989.14 693.95 Z M 1002.11 695.24 L 1002.11 696.66 L 1002.83 696.66 L 1002.83 695.24 Z M 988.42 696.09 L 988.42 697.52 L 989.14 697.52 L 989.14 696.09 Z M 1002.11 697.38 L 1002.11 698.8 L 1002.83 698.8 L 1002.83 697.38 Z M 988.42 698.23 L 988.42 699.66 L 989.14 699.66 L 989.14 698.23 Z M 1002.11 699.52 L 1002.11 700.94 L 1002.83 700.94 L 1002.83 699.52 Z M 988.42 700.38 L 988.42 701.8 L 989.14 701.8 L 989.14 700.38 Z M 1002.11 701.66 L 1002.11 703.09 L 1002.83 703.09 L 1002.83 701.66 Z M 988.42 702.51 L 988.42 703.45 C 988.43 703.54 988.46 703.63 988.53 703.7 C 988.6 703.76 988.69 703.8 988.78 703.8 L 989.28 703.8 L 989.28 703.09 L 989.14 703.09 L 989.14 702.51 Z M 989.99 703.09 L 989.99 703.8 L 991.42 703.8 L 991.42 703.09 Z M 992.13 703.09 L 992.13 703.8 L 993.56 703.8 L 993.56 703.09 Z M 994.27 703.09 L 994.27 703.8 L 995.7 703.8 L 995.7 703.09 Z M 996.41 703.09 L 996.41 703.8 L 997.84 703.8 L 997.84 703.09 Z M 998.55 703.09 L 998.55 703.8 L 999.98 703.8 L 999.98 703.09 Z M 1000.69 703.09 L 1000.69 703.8 L 1002.12 703.8 L 1002.12 703.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="995.13" y="709"&gt;ns&lt;/text&gt;&lt;/g&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: 700px; margin-left: 1013px;"&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: all; white-space: nowrap;"&gt;namespace&lt;br&gt;workload2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1013" y="704" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="1129.5" y="714.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="1182" y="726" width="59" height="30" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="1181.5" y="737.5"&gt;Workload2&lt;/text&gt;&lt;text x="1181.5" y="751.5"&gt;Issuer&lt;/text&gt;&lt;/g&gt;&lt;path d="M 1130 740 L 1109 740" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="1060" y="810" width="42.11" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 1062.58 810 C 1061.16 810 1060 811.22 1060 812.71 L 1060 843.84 C 1060 845.33 1061.16 846.56 1062.58 846.56 L 1089.88 846.56 L 1089.88 850 L 1093.08 848.07 L 1096.28 850 L 1096.28 846.56 L 1099.53 846.56 C 1100.95 846.56 1102.11 845.33 1102.11 843.84 L 1102.11 812.71 C 1102.11 811.22 1100.95 810 1099.53 810 Z M 1062.58 811.23 L 1099.53 811.23 C 1100.32 811.23 1100.94 811.88 1100.94 812.71 L 1100.94 843.84 C 1100.94 844.68 1100.32 845.33 1099.53 845.33 L 1096.28 845.33 L 1096.28 843.01 C 1097.62 841.95 1098.4 840.29 1098.4 838.53 C 1098.4 837.04 1097.84 835.61 1096.84 834.55 C 1095.83 833.5 1094.47 832.9 1093.05 832.9 C 1090.09 832.9 1087.7 835.42 1087.7 838.53 C 1087.7 840.31 1088.51 841.99 1089.88 843.05 L 1089.88 845.33 L 1062.58 845.33 C 1061.79 845.33 1061.17 844.68 1061.17 843.84 L 1061.17 812.71 C 1061.17 811.88 1061.79 811.23 1062.58 811.23 Z M 1068.25 818.55 L 1068.25 820.58 L 1094.12 820.58 L 1094.12 818.55 Z M 1068.25 824.05 L 1068.25 826.08 L 1094.12 826.08 L 1094.12 824.05 Z M 1068.25 829.62 L 1068.25 831.65 L 1082.41 831.65 L 1082.41 829.62 Z" fill="#005073" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 830px; margin-left: 1058px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Workload2&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1058" y="834" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Workload&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1102.11 827.6 L 1130 826.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 1154.82 802.03 C 1154.38 802.05 1153.94 802.16 1153.56 802.36 L 1136.37 810.55 C 1135.47 810.99 1134.81 811.81 1134.59 812.77 L 1130.34 831.22 C 1130.15 832.07 1130.31 832.98 1130.78 833.72 C 1130.86 833.8 1130.92 833.88 1130.97 833.99 L 1142.87 848.77 C 1143.5 849.53 1144.46 850 1145.45 850 L 1164.53 850 C 1165.51 850 1166.47 849.53 1167.1 848.77 L 1179 833.96 C 1179.6 833.2 1179.85 832.18 1179.63 831.22 L 1175.38 812.77 C 1175.16 811.81 1174.5 810.99 1173.6 810.55 L 1156.41 802.36 C 1155.92 802.11 1155.37 802 1154.82 802.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1154.83 803.47 C 1154.42 803.49 1154.01 803.59 1153.65 803.77 L 1137.49 811.48 C 1136.64 811.89 1136.02 812.66 1135.82 813.57 L 1131.82 830.91 C 1131.64 831.71 1131.8 832.56 1132.23 833.25 C 1132.31 833.33 1132.36 833.41 1132.41 833.51 L 1143.6 847.4 C 1144.19 848.12 1145.09 848.56 1146.02 848.56 L 1163.95 848.56 C 1164.88 848.56 1165.78 848.12 1166.38 847.4 L 1177.56 833.49 C 1178.13 832.76 1178.36 831.81 1178.15 830.91 L 1174.16 813.57 C 1173.95 812.66 1173.33 811.89 1172.48 811.48 L 1156.33 803.77 C 1155.86 803.54 1155.35 803.44 1154.83 803.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1165.47 815.8 L 1165.47 826.93 L 1155.72 832.33 L 1155.67 818.71 Z M 1144.53 815.8 L 1144.53 826.93 L 1154.29 832.34 L 1154.34 818.71 Z M 1144.53 814.63 L 1155 811.6 L 1165.47 814.63 L 1155 817.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="1154.5" y="841.3"&gt;pod&lt;/text&gt;&lt;/g&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: 826px; margin-left: 1182px;"&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: all; white-space: nowrap;"&gt;Workload2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1182" y="830" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Workload2&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="1060" y="720" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 1060 725.66 L 1065.83 720.04 L 1097.91 720.04 C 1099.81 720 1101.44 721.36 1101.67 723.18 L 1101.67 724.8 L 1099.34 724.02 C 1099.39 723.12 1098.69 722.35 1097.76 722.28 L 1067.07 722.28 L 1067.07 726.9 L 1062.32 726.9 L 1062.32 743.62 C 1062.38 744.27 1062.89 744.79 1063.56 744.9 L 1080.41 744.9 L 1081.75 747.09 L 1063.36 747.09 C 1061.59 746.84 1060.21 745.48 1060 743.76 Z M 1082.74 756.76 L 1087.09 749.62 C 1081.97 746.24 1079.79 740.02 1081.72 734.35 C 1083.65 728.68 1089.23 724.91 1095.42 725.11 C 1101.61 725.31 1106.93 729.42 1108.46 735.2 C 1110 740.98 1107.39 747.05 1102.06 750.09 L 1106.96 757.19 L 1101.62 755.81 L 1100.09 759.95 L 1094.35 752.57 L 1090 760 L 1088.02 755.52 Z M 1082.79 738.66 C 1083.1 744.96 1088.45 749.93 1094.99 750 C 1101.48 749.85 1106.74 744.9 1107.05 738.66 C 1106.88 732.4 1101.69 727.34 1095.19 727.09 C 1088.48 727.06 1082.97 732.2 1082.79 738.66 Z M 1085.75 738.66 C 1085.74 736.3 1086.71 734.03 1088.45 732.37 C 1090.18 730.7 1092.54 729.78 1094.99 729.8 C 1097.42 729.81 1099.74 730.76 1101.45 732.42 C 1103.15 734.08 1104.1 736.32 1104.09 738.66 C 1103.98 743.44 1099.95 747.26 1094.99 747.28 C 1089.98 747.34 1085.86 743.49 1085.75 738.66 Z M 1087.78 738.66 C 1087.98 742.39 1091.13 745.33 1094.99 745.43 C 1098.82 745.28 1101.91 742.35 1102.11 738.66 C 1102.01 734.9 1098.9 731.86 1094.99 731.71 C 1091.05 731.81 1087.88 734.86 1087.78 738.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 740px; margin-left: 1058px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;CA Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1058" y="744" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA Certif&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1155 765 L 1155 780 L 1081 780 L 1081.04 803.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 1081.05 808.88 L 1077.54 801.89 L 1081.04 803.63 L 1084.54 801.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 779px; margin-left: 1110px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1110" y="782" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="680" y="480" width="270" height="175" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 695.51 485.02 C 695.24 485.03 694.97 485.1 694.73 485.22 L 683.98 490.35 C 683.42 490.62 683.01 491.13 682.87 491.73 L 680.21 503.26 C 680.09 503.79 680.2 504.36 680.49 504.82 C 680.54 504.87 680.57 504.93 680.61 504.99 L 688.04 514.23 C 688.44 514.71 689.04 515 689.65 515 L 701.58 515 C 702.2 515 702.8 514.71 703.19 514.23 L 710.63 504.98 C 711 504.5 711.16 503.86 711.02 503.26 L 708.36 491.73 C 708.23 491.13 707.82 490.62 707.25 490.35 L 696.51 485.22 C 696.2 485.07 695.86 485 695.51 485.02 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 695.52 485.92 C 695.26 485.93 695 486 694.78 486.11 L 684.68 490.92 C 684.15 491.18 683.76 491.67 683.63 492.23 L 681.14 503.07 C 681.03 503.57 681.12 504.1 681.4 504.53 C 681.44 504.58 681.48 504.63 681.51 504.69 L 688.5 513.38 C 688.87 513.83 689.43 514.1 690.01 514.1 L 701.22 514.1 C 701.8 514.1 702.36 513.83 702.74 513.38 L 709.73 504.68 C 710.08 504.23 710.22 503.63 710.1 503.07 L 707.6 492.23 C 707.47 491.67 707.08 491.18 706.55 490.92 L 696.45 486.11 C 696.16 485.96 695.84 485.9 695.52 485.92 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 689.07 491.16 L 689.07 491.81 L 688.42 491.81 L 688.42 493.24 L 689.14 493.24 L 689.14 491.87 L 690.49 491.87 L 690.49 491.16 Z M 691.21 491.16 L 691.21 491.87 L 692.64 491.87 L 692.64 491.16 Z M 693.35 491.16 L 693.35 491.87 L 694.77 491.87 L 694.77 491.16 Z M 695.49 491.16 L 695.49 491.87 L 696.92 491.87 L 696.92 491.16 Z M 697.63 491.16 L 697.63 491.87 L 699.06 491.87 L 699.06 491.16 Z M 699.77 491.16 L 699.77 491.87 L 701.19 491.87 L 701.19 491.16 Z M 701.91 491.16 L 701.91 491.87 L 702.11 491.87 L 702.11 492.38 L 702.82 492.38 L 702.82 491.52 C 702.82 491.32 702.67 491.16 702.47 491.16 Z M 702.11 493.1 L 702.11 494.52 L 702.82 494.52 L 702.82 493.1 Z M 688.42 493.95 L 688.42 495.38 L 689.14 495.38 L 689.14 493.95 Z M 702.11 495.24 L 702.11 496.66 L 702.83 496.66 L 702.83 495.24 Z M 688.42 496.09 L 688.42 497.52 L 689.14 497.52 L 689.14 496.09 Z M 702.11 497.38 L 702.11 498.8 L 702.83 498.8 L 702.83 497.38 Z M 688.42 498.23 L 688.42 499.66 L 689.14 499.66 L 689.14 498.23 Z M 702.11 499.52 L 702.11 500.94 L 702.83 500.94 L 702.83 499.52 Z M 688.42 500.38 L 688.42 501.8 L 689.14 501.8 L 689.14 500.38 Z M 702.11 501.66 L 702.11 503.09 L 702.83 503.09 L 702.83 501.66 Z M 688.42 502.51 L 688.42 503.45 C 688.43 503.54 688.46 503.63 688.53 503.7 C 688.6 503.76 688.69 503.8 688.78 503.8 L 689.28 503.8 L 689.28 503.09 L 689.14 503.09 L 689.14 502.51 Z M 689.99 503.09 L 689.99 503.8 L 691.42 503.8 L 691.42 503.09 Z M 692.13 503.09 L 692.13 503.8 L 693.56 503.8 L 693.56 503.09 Z M 694.27 503.09 L 694.27 503.8 L 695.7 503.8 L 695.7 503.09 Z M 696.41 503.09 L 696.41 503.8 L 697.84 503.8 L 697.84 503.09 Z M 698.55 503.09 L 698.55 503.8 L 699.98 503.8 L 699.98 503.09 Z M 700.69 503.09 L 700.69 503.8 L 702.12 503.8 L 702.12 503.09 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="6px"&gt;&lt;text x="695.13" y="509"&gt;ns&lt;/text&gt;&lt;/g&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: 500px; margin-left: 713px;"&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: all; white-space: nowrap;"&gt;namespace&lt;br&gt;ingress&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="713" y="504" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;namesp&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;image x="829.5" y="514.5" width="50" height="50" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJzdmc4MjMiIHZlcnNpb249IjEuMSIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE0MiAxNDIiIGhlaWdodD0iMTQyIiB3aWR0aD0iMTQyIj4mI3hhOyAgPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTgyNyI+JiN4YTsgICAgJiN4YTsgICAgICAmI3hhOyAgICAgICAgaW1hZ2Uvc3ZnK3htbCYjeGE7ICAgICAgICAmI3hhOyAgICAgICAgJiN4YTsgICAgICAmI3hhOyAgICAmI3hhOyAgPC9tZXRhZGF0YT4mI3hhOyAgJiN4YTsgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDAyMTAzLDAsMCwxLjAzNzczMTgsNi45Mzk5OTk5ZS03LC0yLjUzMTcyNzZlLTQpIiBpZD0iZzgxNiI+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3ODIiIGZpbGw9IiMzMjZjZTUiIGQ9Im0gMTQxLjcwMiw2OC40MTggYyAwLDcuNDYzMiAtNC41NjcsMTQuMTEyMyAtNi43NDgsMjAuODM4NSAtMi4yNjMsNi45Nzg5IC0yLjU1MiwxNS4wMjg1IC02Ljc3NiwyMC44Mzg1IC00LjI2Nyw1Ljg2OCAtMTEuODU2LDguNjExIC0xNy43MTksMTIuODgxIC01LjgwNSw0LjIyOCAtMTAuNzM0NSwxMC42MjggLTE3LjcwNjEsMTIuODk1IC02LjcyODYsMi4xODYgLTE0LjQ0NjMsLTAuMDIxIC0yMS45MDE4LC0wLjAyMSAtNy40NTU1LDAgLTE1LjE3MzEsMi4yMDcgLTIxLjg5OTgsMC4wMjEgQyA0MS45Nzc4LDEzMy42MDQgMzcuMDQ4LDEyNy4yMDQgMzEuMjQyOCwxMjIuOTc2IDI1LjM3OTksMTE4LjcwNiAxNy43OTEzLDExNS45NjMgMTMuNTI0NywxMTAuMDk1IDkuMzAwNTUsMTA0LjI4NyA5LjAxMTM1LDk2LjIzNzQgNi43NDc5MSw4OS4yNTY1IDQuNTYzNTEsODIuNTIyNSAwLDc1Ljg3MzUgMCw2OC40MTggMCw2MC45NjI0IDQuNTY3MzcsNTQuMzA1NyA2Ljc0NzkxLDQ3LjU3OTUgOS4wMTEzNSw0MC42MDA1IDkuMzAwNTUsMzIuNTUwNyAxMy41MjQ3LDI2Ljc0MSAxNy43OTEzLDIwLjg3NTMgMjUuMzc5OSwxOC4xMjk3IDMxLjI0MjgsMTMuODYxNyAzNy4wNDgsOS42MzQxNCA0MS45Nzc4LDMuMjMyMDkgNDguOTUxMywwLjk2Njg3MiA1NS42NzgsLTEuMjE5MjQgNjMuMzk1NiwwLjk4NjE2NyA3MC44NTExLDAuOTg2MTY3IGMgNy40NTU1LDAgMTUuMTczMiwtMi4yMDU0MDcgMjEuODk5OSwtMC4wMTkyOTUgNi45NzM1LDIuMjY1MjE4IDExLjkwMyw4LjY2NzI2OCAxNy43MDgsMTIuODk0ODI4IDUuODYzLDQuMjY4IDEzLjQ1Miw3LjAxMzYgMTcuNzE5LDEyLjg3OTMgNC4yMjQsNS44MDk3IDQuNTEzLDEzLjg1OTUgNi43NzYsMjAuODM4NSAyLjE4MSw2LjcyNjIgNi43NDgsMTMuMzc3MSA2Ljc0OCwyMC44Mzg1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc4NCIgc3Ryb2tlPSIjZmZmZmZmIiBkPSJtIDEzLjU4ODMsNjAuNTMgYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg2IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw2OC4yNDggYyA4LjE4MDQsMCA4LjE4MDQsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE3ODQsMCA4LjE4MDQsLTMuODU5IDE2LjM2MDcsLTMuODU5IDguMTgwNCwwIDguMTc4NSwzLjg1OSAxNi4zNTg5LDMuODU5IDguMTgwNCwwIDguMTc4NSwtMy44NTkgMTYuMzU4OSwtMy44NTkgOC4xODA0LDAgOC4xODAzLDMuODU5IDE2LjM1ODgsMy44NTkgOC4xNzg1LDAgOC4xODA1LC0zLjg1OSAxNi4zNjA1LC0zLjg1OSA4LjE4MSwwIDguMTgxLDMuODU5IDE2LjM2MSwzLjg1OSIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoNzg4IiBzdHJva2U9IiNmZmZmZmYiIGQ9Im0gMTMuNTg4Myw3Ny41MDk1IGMgOC4xODA0LDAgOC4xODA0LDMuODU5IDE2LjM1ODksMy44NTkgOC4xNzg0LDAgOC4xODA0LC0zLjg1OSAxNi4zNjA3LC0zLjg1OSA4LjE4MDQsMCA4LjE3ODUsMy44NTkgMTYuMzU4OSwzLjg1OSA4LjE4MDQsMCA4LjE3ODUsLTMuODU5IDE2LjM1ODksLTMuODU5IDguMTgwNCwwIDguMTgwMywzLjg1OSAxNi4zNTg4LDMuODU5IDguMTc4NSwwIDguMTgwNSwtMy44NTkgMTYuMzYwNSwtMy44NTkgOC4xODEsMCA4LjE4MSwzLjg1OSAxNi4zNjEsMy44NTkiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5MCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3MC44NDczLDguMTg2ODMgYyAtMzMuMjM4MywwIC02MC4xODM3LDI2Ljk2NjU3IC02MC4xODM3LDYwLjIzMDk3IDAsMzMuMjY0MiAyNi45NDU0LDYwLjIzMTIgNjAuMTgzNyw2MC4yMzEyIDMzLjIzODcsMCA2MC4xODM3LC0yNi45NTkgNjAuMTgzNywtNjAuMjMxMiAwLC0zMy4yNzIxIC0yNi45NDUsLTYwLjIzMDk3IC02MC4xODM3LC02MC4yMzA5NyB6IE0gNzAuODMxOSwxMjMuNDA4IEMgNDAuNDc3OCwxMjMuNDA4IDE1LjkwNTgsOTguODA1MyAxNS45LDY4LjQyNzQgMTUuOSwzOC4wMTY3IDQwLjUzNTcsMTMuMzc5MSA3MC45MTA5LDEzLjQzNyBjIDMwLjM3NTEsMC4wNTc5IDU0LjkxMTEsMjQuNjU4OSA1NC44ODQxLDU1LjAzMjkgLTAuMDI3LDMwLjM3NCAtMjQuNjA5LDU0Ljk0ODEgLTU0Ljk2MzEsNTQuOTM4MSB6Ii8+JiN4YTsgICAgPHBhdGggaWQ9InBhdGg3OTIiIGZpbGw9IiNmZmZmZmYiIGQ9Im0gNDMuMDg0NCw5NS43MDg1IGMgLTAuMTkyOCwtMC42NjU2IC0xLjEwODYsLTEuMzI3NSAtMS44MiwtMS4yMzg3IC0xLjAxMDIsMC4xMjkzIC0yLjAxNjYsMC4yOTcxIC0zLjIzMTMsMC40Nzg1IDAuNDQ3MywtMS4xODY2IDAuODI1MiwtMi4xOTk2IDEuMjEwOCwtMy4yMTI2IDAuMzMzNSwtMC44Nzk5IC0wLjM4NTYsLTEuMzA2MyAtMC44MzY3LC0xLjc3NzEgLTAuNDUxMiwtMC40NzA4IC0wLjgwNCwwLjEyMzUgLTEuMTEwNiwwLjM2NjYgLTEuNjMzLDEuMjk0NyAtMy4yMTU4LDIuNjUzMSAtNC44NjQyLDMuOTI2NiAtMC43ODI4LDAuNjAzOSAtMC4xOTI4LDAuOTU1MSAwLjEyMzQsMS4zODkyIDAuMzE2MSwwLjQzNDEgMC42MzgxLDAuOTIyMyAxLjI4NCwwLjM1MzEgMC43NDQyLC0wLjY1NDEgMS41MzA4LC0xLjI2MTkgMi4zMTM2LC0xLjg5NjcgMC4wMjg5LDAuMDczMyAwLjA1NTksMC4xMDgxIDAuMDQ4MiwwLjEzMTIgLTAuMTU0MywwLjQ0NzcgLTAuMzIyLDAuODkxNCAtMC40NzI0LDEuMzQxIC0wLjMxNDMsMC45NDkzIDAuMjkzLDEuNjQyIDEuMjg5OCwxLjQ4NTcgMC40MTQ1LC0wLjA2NTYgMC44MzY4LC0wLjE5MjkgMS4zMzAzLDAuMDE5MyAtMC44MDIsMC42NTggLTEuNDk4LDEuMjc1NCAtMi4yNDIyLDEuODI1MyAtMC42MTEyLDAuNDUxNSAtMC4zMTgxLDAuODIgMC4wNTAxLDEuMTU4MSAwLjM2ODMsMC4zMzcgMC41MjI1LDEuMjE3IDEuMzExLDAuNTc4IDEuNzcyNSwtMS40Mjk5IDMuNTM5MSwtMi44NjQ4IDUuMywtNC4zMDQyIDAuMTgxMywtMC4xNDg2IDAuNDE0NSwtMC4yODc1IDAuMzE2MiwtMC42MjMzIHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3NS4zMTA1LDMwLjgwMjQgYyAxLjI2MDksMC41MjA5IDEuNzYwMywwLjI4MTcgMi4wMjA1LC0xLjAyMjYgMC4wNTc5LC0wLjI4MzcgMC4xMDk5LC0wLjU2NTQgMC4xNzkzLC0wLjg0NTIgMC4wNTYsLTAuMjI3NiAwLC0wLjYxMTYgMC4zNDksLTAuNTYzNCAwLjM0OSwwLjA0ODMgMC43OTYzLC0wLjAyMzEgMC45MjM1LDAuNDk3OCAwLjEyNzMsMC41MjEgMC4yODU0LDAuOTgyMiAwLjQyOCwxLjQ3MjIgMC40MDEsMS4zNTA3IDAuNjc2NywxLjU2MSAyLjA1MTQsMS42MzI0IDAuNTE2NywwLjAyNyAwLjYzMjQsLTAuMTQ4NiAwLjQ5NTUsLTAuNjA5NyAtMC4yMTU5LC0wLjczNTIgLTAuNDAzLC0xLjQ3OTkgLTAuNjMyNCwtMi4yMTEyIC0wLjEwNiwtMC4zMzM4IC0wLjE5MjgsLTAuNTMwNiAwLjI2NDEsLTAuNzIxNyAxLjExNDQsLTAuNDcwNyAxLjQxNTIsLTEuNDgxOCAxLjQ5NDIsLTIuNTIxOCAwLC0xLjI4NSAtMC43NzEyLC0yLjIyNDcgLTIuMjEzMywtMi42MTA2IC0xLjE3MjIsLTAuMzEyNiAtMi4zNzUzLC0wLjUwNzUgLTMuNTYyOSwtMC43NzE4IC0wLjQ0MzUsLTAuMDk2NSAtMC42NjUyLDAuMDgzIC0wLjc0ODEsMC41MDc1IC0wLjQ1NSwyLjMxNTQgLTAuOTE3Nyw0LjY0NjIgLTEuMzY2OSw2Ljk3MzEgLTAuMDU5OCwwLjMxNDUgLTAuMTA0MSwwLjYyMTMgMC4zMTgxLDAuNzk1IHogbSAzLjM4MTcsLTUuOTgxNCBjIDAuOTczNiwtMC4wOTg0IDEuOTEyNiwwLjQ2NSAxLjkwMjksMS4wNDE5IC0wLjAwOTYsMC41NzY5IC0wLjM0NywwLjg1NjcgLTEuMDAwNiwwLjg3NDEgLTAuNDAzLC0wLjMzOTYgLTEuMzM4LDAuMjEyMiAtMS40NzQ5LC0wLjY1MDMgLTAuMDU3OSwtMC4zNjQ3IC0wLjE5MjgsLTEuMTc4OSAwLjU3MjYsLTEuMjU4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5NiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2Mi42MTQ4LDEwNi4yODYgYyAtMC41NDc1LC0wLjE4IC0xLjEwNjYsLTAuNDA1IC0xLjI5OTQsMC41MDUgLTAuMjEwMiwxLjAwMiAtMC41MzAyLDEuOTc4IC0wLjg2MTgsMy4xODQgLTAuNTA3MSwtMS41MDkgLTAuOTc3NSwtMi44MDEgLTEuMzY4OSwtNC4xMTcgLTAuMjU2NCwtMC44NjEgLTAuOTkyOSwtMC44NTMgLTEuNjEzNywtMC45NjUgLTAuNjg0NCwtMC4xMTIgLTAuNTYxMSwwLjU3OSAtMC42NjksMC45NjUgLTAuNTc4NCwxLjk5OSAtMS4wNzU4LDQuMDI1IC0xLjY5NjYsNi4wMTIgLTAuMzA0NywwLjk4IDAuNDE4MywwLjg5MSAwLjkwMDMsMS4wNTkgMC41MTI5LDAuMTc2IDEuMDU0NiwwLjM4NiAxLjI0OTQsLTAuNDYzIDAuMjMzMiwtMS4wMTcgMC41NDM3LC0yLjAxNiAwLjg1NzksLTMuMTUzIDAuMTI5MiwwLjI5NyAwLjIxNzksMC40NjcgMC4yNzc2LDAuNjQ1IDAuMzg1NiwxLjE1NyAwLjc4ODYsMi4zMDIgMS4xNTY4LDMuNDYxIDAuMjY0MiwwLjg1NyAxLjAzNTQsMC44MDUgMS42NTA0LDAuODkgMC42MTUsMC4wODUgMC40ODc4LC0wLjU3OSAwLjU4OTksLTAuOTI4IDAuNTc4NCwtMi4wMDMgMS4wNjYyLC00LjAzMSAxLjY5NjcsLTYuMDE1IDAuMjg3MiwtMC45NiAtMC4zNzc5LC0wLjkxOCAtMC44Njk2LC0xLjA4IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDc5OCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSAxMDkuMDUyLDk0LjA4MiBjIC0wLjYzMiwtMC4wOTA3IC0xLjI1NSwtMC4yNzc5IC0xLjg4OSwtMC4zMzc3IC0wLjUyMywtMC4wNDgyIC0wLjY1NCwtMC4yMjk2IC0wLjY3MSwtMC43NzE4IC0wLjA0MywtMS4zNTA2IC0xLjI2MywtMi40NTgyIC0yLjU5MiwtMi40NzM2IC0wLjkxMiwtMC4wMjUxIC0xLjYwOSwwLjQzMjIgLTIuMTY5LDEuMDcyOCAtMC42OTYsMC43OTY5IC0xLjI5MywxLjY3ODYgLTEuOTkxMiwyLjQ3MzYgLTAuNDc0MywwLjU0MjIgLTAuNDE0NSwwLjg3NzkgMC4xNTA0LDEuMzA0MyAxLjY4ODgsMS4yNzczIDMuMzY0OCwyLjU3NTkgNS4wMDA4LDMuOTE2OSAwLjU3OSwwLjQ2MyAwLjg3LDAuNDE0OCAxLjI5NiwtMC4xNjYgMC43NzEsLTEuMDUxNSAwLjgwMiwtMS4wMjY0IC0wLjIzNywtMS44MzMgLTAuMjI4LC0wLjE3NzUgLTAuNDY3LC0wLjM0MzQgLTAuNjc1LC0wLjU0MjIgLTAuMTYsLTAuMTUyNCAtMC41NTcsLTAuMjE0MSAtMC4zMzQsLTAuNTYxNCAwLjE4MiwtMC4yNzk4IDAuMzM4LC0wLjY0NjQgMC44MDMsLTAuNTUxOSAwLjQ2NCwwLjA5NDYgMC45NjQsMC4xMDgxIDEuNDEzLDAuMjcyMSAxLjAzNSwwLjM4NTkgMS42NzksLTAuMDE3NCAyLjI0MiwtMC44NzYgMC4zNzIsLTAuNTU3NiAwLjM5MSwtMC44MjIgLTAuMzQ3LC0wLjkyNjEgeiBtIC01Ljc4NCwxLjA0NzcgYyAtMC4xNDUsMC4wMTE1IC0xLjE4MiwtMC44Nzk5IC0xLjE3MiwtMS4wMDkyIDAuMDI1LC0wLjMzOTUgMS4yMTEsLTEuNDc2IDEuNTQyLC0xLjQ3NiAwLjQ0NiwwLjA4ODcgMC43NzEsMC4zMTA2IDAuODA4LDAuODAwNyAwLjAyMSwwLjI5OTEgLTAuOTU4LDEuNjY1MiAtMS4xNzgsMS42ODQ1IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA2MS4xMjI2LDMwLjk5MzQgYyAwLjA2NzUsMC4zODU5IDAuMTkyOCwwLjY5NDYgMC42NzI4LDAuNjE5MyAxLjczNTIsLTAuMzAxIDMuNDgyLC0wLjU2MTQgNS4yMDU2LC0wLjkyMDMgMC45MSwtMC4xOTMgMC4yODM0LC0wLjg3OTkgMC4yOTMsLTEuMzUwNyAwLjAwOTcsLTAuNTAzNiAtMC4yNjQxLC0wLjY1NiAtMC43NTc3LC0wLjU1OTUgLTAuOTQyNywwLjE4MzMgLTEuODk5LDAuMzA4NyAtMi44NDE4LDAuNDk1OSAtMC40ODk3LDAuMDk2NCAtMC42MTg5LC0wLjEyNzQgLTAuNjY1MiwtMC41NDYxIC0wLjA0MDQsLTAuMzcyNCAtMC4wOTQ0LC0wLjY2MTggMC40NDM1LC0wLjcyMTYgMC43OTYyLC0wLjA4NDkgMS41NzUxLC0wLjMxMDcgMi4zNzE0LC0wLjM5NTYgMC44NjE4LC0wLjA5MjYgMC43MDM3LC0wLjYzNDggMC42MTExLC0xLjE4MjcgLTAuMDkyNSwtMC41NDggLTAuMjA4MiwtMC45OTc2IC0wLjk2MzksLTAuNzg5MiAtMC43MDc2LDAuMTkyOSAtMS40NTc2LDAuMjIzOCAtMi4xNzA5LDAuNDAzMyAtMC41MDEzLDAuMTI3MyAtMC43NDYyLDAuMDk2NCAtMC44NTAzLC0wLjQ5MDEgLTAuMTEzNywtMC42MzI5IDAuMjM3MiwtMC42ODUgMC42ODQ1LC0wLjc1MjUgMC43OTI0LC0wLjExNzcgMS41ODA5LC0wLjI3MDIgMi4zNzMzLC0wLjM4NTkgMC41MDcxLC0wLjA3NzIgMC44OTY1LC0wLjIxODEgMC42OTIxLC0wLjg1NjcgLTAuMTYzOCwtMC41MTMzIDAuMDY5NCwtMS4zMjM2IC0wLjk4OSwtMS4xMDM3IC0xLjUwNTgsMC4zMTQ1IC0zLjAyMzEsMC41Nzg5IC00LjU0NjIsMC43NzE4IC0wLjcyODcsMC4wOTI2IC0wLjgyOSwwLjQxODcgLTAuNzIxLDEuMDI0NiAwLjM5MTMsMi4yNTE3IDAuNzc1LDQuNTAxNSAxLjE1ODcsNi43Mzk3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwMiIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA4Ni44NDU1LDEwNy4xOTcgYyAtMC41NjIzLDAuMjI4IC0xLjEzNjMsMC40MjUgLTEuNzE5NywwLjU5MiAtMC40ODM5LDAuMTU0IC0wLjgzMjksMC4zNTEgLTAuNTQzNywwLjk2NSAwLjIyOTQsMC40NzQgMC4xNjc3LDEuMjY0IDEuMDgzNSwwLjg5NyAwLjE2NzgsLTAuMDY4IDAuMzQ3MSwtMC4yMjYgMC41MTA5LC0wLjAyMSAwLjE2MzksMC4yMDQgMC4wNTU5LDAuNDQ3IC0wLjAyNSwwLjY0IC0wLjIzNTIsMC41NTQgLTAuNzI4OCwwLjc5MSAtMS4yODIxLDAuODg4IC0wLjU1MzQsMC4wOTYgLTEuMDEwMywtMC4xNjggLTEuMjg2LC0wLjY1IC0wLjM4NjUsLTAuNjY4IC0wLjY0NzksLTEuNCAtMC43NzEyLC0yLjE2MSAtMC4xNzkzLC0xLjEyNSAwLjg5MDcsLTIuMTA5IDEuOTEwNiwtMS42MTcgMC44NDA2LDAuNDA1IDEuNDczLDAuMDgxIDIuMTg2NCwtMC4xMjggMC40ODc3LC0wLjE0MiAwLjQ4OTcsLTAuNDM0IDAuMjA4MiwtMC43ODkgLTAuNjUxNywtMC44MjIgLTEuNTQyNCwtMS4wODIgLTIuNTUyNywtMS4xMjUgLTEuMDYzMSwtMC4wMDMgLTIuMDkxMiwwLjM4MSAtMi44OTE5LDEuMDgxIC0wLjk2NCwwLjg2NCAtMS4zNDk2LDEuOTYgLTAuOTY0LDMuMjEyIDAuNDg3OCwxLjYzOSAwLjc3MTIsMy40NzQgMi44MDcxLDQuMDE0IDEuNTYxNywwLjQxMSAyLjkzODMsLTAuMDc0IDQuMDQ4OCwtMS4xODEgMS4yMDExLC0xLjIwNCAwLjkxLC0yLjYwNyAwLjM4NTYsLTQuMDM3IC0wLjIyOTUsLTAuNjA5IC0wLjQ1MzEsLTAuODY4IC0xLjEwNDgsLTAuNTggeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA0IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDk4LjgwMjksMTAzLjU1MiBjIC0wLjcwNTcsMC41NTIgLTEuNDczLDEuMDI0IC0yLjE4MDYsMS41NzYgLTAuMzg1NiwwLjMwMSAtMC41Nzg0LDAuMjA3IC0wLjgyNzEsLTAuMTU2IC0wLjI0ODcsLTAuMzYzIC0wLjMyNTgsLTAuNTc5IDAuMDg4NywtMC44NTEgMC42NDIsLTAuNDIzIDEuMjU3MSwtMC44ODYgMS44ODE3LC0xLjMzMyAwLjIwNDQsLTAuMTQ3IDAuNDUxMiwtMC4yNjcgMC40MjgxLC0wLjYyMiAtMC4xNjQyLC0wLjQzOCAtMC40NDMyLC0wLjgyNSAtMC44MDc5LC0xLjExOSAtMC4zNTg2LC0wLjI1MiAtMC42MzYyLDAuMTMzIC0wLjkxNTgsMC4zMjYgLTAuNTc4NCwwLjQwMiAtMS4xODM4LDAuNzcyIC0xLjcyMzYsMS4yMjQgLTAuNDQ5MiwwLjM3NCAtMC41Nzg0LDAuMDMzIC0wLjgwMDEsLTAuMjM0IC0wLjI2NjEsLTAuMzEgLTAuMjY5OSwtMC41MDMgMC4xMDIyLC0wLjc0NiAwLjcyMywtMC40NzMgMS4zOTIsLTEuMDI5IDIuMTIwOCwtMS40ODggMC41OTc2LC0wLjM3NDQgMC40NzA0LC0wLjczNzIgMC4xMTk1LC0xLjE2OTQgLTAuMzEwNCwtMC4zODU5IC0wLjQ5OTQsLTAuOTY0NyAtMS4xODU3LC0wLjQ0MzggLTEuMjU3MSwwLjkzNzggLTIuNTQ4OCwxLjgzMTIgLTMuODM0OCwyLjczNDIgLTAuNDA0OSwwLjI4NCAtMC40MjIyLDAuNTY1IC0wLjEzODgsMC45NjUgMS4zMzAzLDEuODQ2IDIuNjQzMywzLjcwNCAzLjk2MDEsNS41NjIgMC4yMzkxLDAuMzM2IDAuNDcyMywwLjQ2OSAwLjg2NzYsMC4xODIgMS4zMTg3LC0wLjk2NSAyLjY0ODQsLTEuOTE0IDMuOTg5LC0yLjg0NiAwLjYzMDgsLTAuNDM4IDAuMjQ4OCwtMC44MDMgLTAuMDIxMiwtMS4xOTggLTAuMjY5OSwtMC4zOTYgLTAuNTI2NCwtMC44MzIgLTEuMTIyMSwtMC4zNjMgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODA2IiBmaWxsPSIjZmZmZmZmIiBkPSJtIDQ5LjAwMzMsMTAwLjQyIGMgLTAuMjcsLTAuMTkzIC0wLjU1NTMsLTAuMTAyIC0wLjc5ODIsMC4wODcgLTEuOTUxMSwxLjUxOCAtMy45MDAzLDMuMDM4IC01Ljg0NzYsNC41NjEgLTAuMjU2NCwwLjE5MyAtMC40NDczLDAuNDc3IC0wLjA3NzEsMC42ODMgMC42ODI1LDAuMzg2IDEuMzA3MiwxLjM1MSAyLjIxNTMsMC4zMzIgMC4wNDQxLC0wLjA2NyAwLjExMjksLTAuMTE0IDAuMTkxNSwtMC4xMzEgMC4wNzg3LC0wLjAxNyAwLjE2MDgsLTAuMDAyIDAuMjI4NywwLjA0MSAwLjcyNSwwLjQ3NCAxLjQ1NTcsMC45MzkgMi4xNzg3LDEuNDE0IDAuMTkyOCwwLjEzMSAwLjEyOTEsMC4zMzYgMC4xMDYsMC41MjUgLTAuMTAyMiwwLjgyOCAwLjQ3MDQsMS43NTkgMS4yOTM3LDEuODA0IDAuODU5OCwwLjA0NCAwLjQ1NSwtMC44NjUgMC43MjQ5LC0xLjI5MSAwLjAzMTgsLTAuMDU2IDAuMDU0LC0wLjExOCAwLjA2NTUsLTAuMTgxIDAuNTAxMywtMi4wNTUgMS4wMDI2LC00LjExMiAxLjU0MjQsLTYuMzM5IC0wLjIxNTksLTAuODMzIC0xLjE3MDMsLTEuMDA3IC0xLjgyMzgsLTEuNTA1IHogbSAtMS4yMzQsNS4xNTYgYyAtMS41MDU3LC0wLjg4NiAtMS41MDU3LC0wLjg4NiAtMC4yODM0LC0xLjg2MiAwLjI0NjgsLTAuMTkzIDAuNTAxMywtMC4zODYgMC44NjM4LC0wLjY2NCAtMC4wOTIxLDAuODYyIC0wLjI4NjksMS43MSAtMC41ODA0LDIuNTI2IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgwOCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA0Ni41MTQzLDM1LjM4MyBjIDAuOTYzOSwxLjMzOTEgMi40NDQ2LDEuNzM2NSA0LjAxNCwxLjE1NzcgMS42MzExLC0wLjU5MDQgMi44OTIsLTIuMTg0MiAyLjgyNjQsLTMuNjQxIC0wLjAzNDIsLTAuMzQ5MyAtMC4xMTQ2LC0wLjY5MjUgLTAuMjM5MSwtMS4wMjA3IC0wLjAxNzQsLTAuMDk1NSAtMC4wNjg0LC0wLjE4MTYgLTAuMTQzNywtMC4yNDI4IC0wLjA3NTMsLTAuMDYxMiAtMC4xNjk5LC0wLjA5MzUgLTAuMjY2OSwtMC4wOTEgLTAuNTU5MSwwIC0xLjU4NjcsMS4wMDE0IC0xLjU5NDQsMS42MDE1IC0wLjAwOTcsMC44MDg1IC0wLjUyNjQsMS4yMTM3IC0xLjE3MjMsMS40NzIyIC0wLjYxMTEsMC4yNDMxIC0xLjE1NjcsMCAtMS41MTczLC0wLjUwNzQgLTAuNDMwMSwtMC41OTYzIC0wLjgxMywtMS4yMjUzIC0xLjE0NTIsLTEuODgxMyAtMC4yNjk5LC0wLjU1MzggLTAuMjE5OCwtMS4xNTc3IDAuMjgzNCwtMS41ODggMC41MDMyLC0wLjQzMDIgMS4xMjYsLTAuNzIxNiAxLjg0MTIsLTAuMzI5OSAwLjUwMTMsMC4yNzU5IDEuODUyOCwtMC4xOTMgMi4xNDAxLC0wLjY4MTEgMC4xOTI4LC0wLjMxMjYgLTAuMDU0LC0wLjQ4ODIgLTAuMjEwMiwtMC42NjE4IC0wLjI1OTcsLTAuMjk0IC0wLjU4MDksLTAuNTI3MyAtMC45NDA3LC0wLjY4MzQgLTAuMzU5OCwtMC4xNTYgLTAuNzQ5NSwtMC4yMzEgLTEuMTQxNSwtMC4yMTk2IC0yLjU3OTYsMC4wMDk2IC00LjU5MDUsMS45OTcgLTQuMzE4Niw0LjE2OTYgMC4xNTQyLDEuMjIzMyAwLjkxMTksMi4xODggMS41ODQ4LDMuMTQ3IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxMCIgZmlsbD0iI2ZmZmZmZiIgZD0ibSA3My45MDUsMTA4LjY2MSBjIC0wLjY1MzUsLTEuNzAyIC0xLjEwMjgsLTEuOTI5IC0yLjg1MTQsLTEuNTQzIC0wLjE0MjcsMC4wMTUgLTAuMjc3OSwwLjA3MSAtMC4zODk3LDAuMTYxIC0wLjExMTgsMC4wOSAtMC4xOTU0LDAuMjEgLTAuMjQwOCwwLjM0NiAtMC43MzI2LDIuMzQ2IC0xLjQ2MjcsNC42OTYgLTIuMTkwMSw3LjA1IC0wLjA2MzcsMC4yMDcgLTAuMTU2MiwwLjQ1IDAuMDkwNiwwLjYxMiAwLjUyNjMsMC4zNDcgMS45MDg3LC0wLjEzOSAyLjE0NTgsLTAuNzUxIDAuMDk4MywtMC4yNTQgMC4xMDAzLC0wLjU2NSAwLjUyMjUsLTAuNTU3IDAuNzcxMiwwLjAxNSAxLjU0MjQsMCAyLjMxMzUsLTAuMDM3IDAuMjczOCwtMC4wMTEgMC40NDM1LDAuMDkzIDAuNDkxNywwLjM0IDAuMTQ4NCwwLjc3MiAwLjYxMTEsMC45OTkgMS4yNDkzLDAuODMzIDEuMzA1MiwtMC4wMzIgMS4zMTEsLTAuMDM2IDAuODgzLC0xLjE1NyAtMC42NzM1LC0xLjc3IC0xLjM0ODMsLTMuNTM2IC0yLjAyNDQsLTUuMjk3IHogbSAtMi4wMzU5LDMuNDg3IGMgLTAuNTc4NCwwIC0wLjY2MzIsLTAuMTYyIC0wLjQ4OTcsLTAuNjU0IDAuMjEwMiwtMC42MDIgMC4zODU2LC0xLjIxNiAwLjYzMDQsLTEuOTk5IDAuMjU2NSwwLjY1NiAwLjQ0NTQsMS4xMjggMC42MjQ3LDEuNjAzIDAuNDAxLDEuMDc1IDAuMzkxNCwxLjAzOCAtMC43NjU0LDEuMDUgeiIvPiYjeGE7ICAgIDxwYXRoIGlkPSJwYXRoODEyIiBmaWxsPSIjZmZmZmZmIiBkPSJtIDkwLjg0OCwzNi40MTE0IGMgMC40NTEyLDAuMjQ3IDAuODgxMSwwLjkwNSAxLjQwMTcsMC4wMzQ4IDAuNjg4MiwtMS4xNTc3IDEuNDM0NCwtMi4yNzQ5IDIuMTUxNiwtMy40MTE0IDAuODYzNywtMS4zNjYxIDAuODg2OCwtMS40MDY2IDIuMjc1LC0wLjUzMjUgMC43Mzg0LDAuNDY2OSAwLjkzMzEsLTAuMDM2NyAxLjI0MzUsLTAuNDg2MiAwLjMzMTYsLTAuNDgyNCAwLjQ2MjcsLTAuODUwOSAtMC4xOTI4LC0xLjIzNjggLTEuNTQyNCwtMC45MTk4IC0zLjA1NTgsLTEuODc5NCAtNC41NDAzLC0yLjg3ODggLTAuNjMwNSwtMC40MjA3IC0wLjk0MjgsLTAuMzA4OCAtMS4zNDk2LDAuMzE2NCAtMC40NTEyLDAuNjg4OCAtMC4zNTg2LDEuMDU1NCAwLjMzOTMsMS40MjU5IDAuNDA0OSwwLjIxNDIgMC44NTc5LDAuNDExIDEuMTM3NSwwLjgxMjMgLTAuMTQyNywwLjIyOTYgLTAuMjU4NCwwLjQyMjYgLTAuMzg1NiwwLjYxMTcgLTAuODczNCwxLjM4MTUgLTEuNzIxNywyLjc3ODQgLTIuNjI1OSw0LjEzODcgLTAuNDg1OCwwLjc0ODYgMC4wNzUyLDAuOTQ3NCAwLjU0NTYsMS4yMDU5IHoiLz4mI3hhOyAgICA8cGF0aCBpZD0icGF0aDgxNCIgZmlsbD0iI2ZmZmZmZiIgZD0iTSA5Mi42NzAxLDc4LjQyNDEgQyA5Mi4xNzQ2LDc2LjM5MDQgOTEuNjg0OSw3NC4zODc2IDkxLjE4OTQsNzIuMzYxNiA5MS4xNjQsNzIuMjU1OSA5MS4xMTEyLDcyLjE1ODcgOTEuMDM2Myw3Mi4wOCA5MC45NjE0LDcyLjAwMTIgOTAuODY3LDcxLjk0MzYgOTAuNzYyNyw3MS45MTMxIDkwLjY1ODQsNzEuODgyNSA5MC41NDgsNzEuODgwMSA5MC40NDI0LDcxLjkwNiBjIC0wLjEwNTUsMC4wMjU5IC0wLjIwMjMsMC4wNzkzIC0wLjI4MDYsMC4xNTQ2IGwgLTQuNzY2LDQuNTY1MiBjIC0wLjA3NDQsMC4wNzE3IC0wLjEyODcsMC4xNjE3IC0wLjE1NzQsMC4yNjEgLTAuMDI4OCwwLjA5OTMgLTAuMDMwOSwwLjIwNDUgLTAuMDA2NCwwLjMwNDkgMC4wMjQ2LDAuMTAwNCAwLjA3NTEsMC4xOTI2IDAuMTQ2NSwwLjI2NzMgMC4wNzE0LDAuMDc0NyAwLjE2MTEsMC4xMjk0IDAuMjYwMiwwLjE1ODYgMC4xNzc0LDAuMDUwMSAwLjM1MDksMC4wOTg0IDAuNTI4MywwLjEzNSAwLjM4NTYsMC4wODMgMC4zNDcsMC4yMTIzIDAuMTY3NywwLjQ4NDMgLTAuOTk4NywxLjUyMjMgLTIuMzE0LDIuODExIC0zLjg1NTksMy43NzggbCAtMC4wMzg2LDAuMDIzMSBjIC0xLjMyOTEsMC42MzI0IC0yLjczNTgsMS4wODY2IC00LjE4MzcsMS4zNTA3IC0xLjQ5NzEsMC4yODczIC0zLjAxMzQsMC40NjI3IC00LjUzNjUsMC41MjQ4IC0wLjA0ODEsMC4wMDE2IC0wLjA5NTksLTAuMDA2NiAtMC4xNDA3LC0wLjAyNDEgLTAuMDQ0OCwtMC4wMTc1IC0wLjA4NTUsLTAuMDQ0IC0wLjExOTgsLTAuMDc3NyBDIDczLjQyNTMsODMuNzc3OSA3My4zOTgyLDgzLjczNzYgNzMuMzgsODMuNjkzMSA3My4zNjE4LDgzLjY0ODUgNzMuMzUyOSw4My42MDA4IDczLjM1MzcsODMuNTUyNyBWIDYyLjI1NSBjIDAsLTAuMDI3NCAwLjAwNTQsLTAuMDU0NSAwLjAxNTgsLTAuMDc5OCAwLjAxMDUsLTAuMDI1MyAwLjAyNTgsLTAuMDQ4MiAwLjA0NTIsLTAuMDY3NiAwLjAxOTMsLTAuMDE5MyAwLjA0MjMsLTAuMDM0NyAwLjA2NzUsLTAuMDQ1MSAwLjAyNTMsLTAuMDEwNSAwLjA1MjQsLTAuMDE1OSAwLjA3OTcsLTAuMDE1OSAwLjE5MjgsMCAwLjQyOCwwLjAzODYgMC45OTI5LDAuMDMyOCAxLjExMDUsLTAuMDExNiAyLjI2NzMsLTAuMDExNiAzLjM4MTcsLTAuMDc3MiAwLjM2MjQsLTAuMDIxMiAwLjU2NDksLTAuMDA5NiAwLjc5NjIsMC4xNjQgMC40Njk5LDAuMzcxNiAxLjA2MjQsMC41NTIzIDEuNjU5NSwwLjUwNjIgMC41OTcxLC0wLjA0NjEgMS4xNTQ5LC0wLjMxNTggMS41NjIyLC0wLjc1NTEgMC40MDIzLC0wLjQ0MTEgMC42MjcxLC0xLjAxNTYgMC42MzEsLTEuNjEyOCAwLjAwMzksLTAuNTk3MiAtMC4yMTMzLC0xLjE3NDcgLTAuNjA5OCwtMS42MjEgLTAuMzk4MiwtMC40NDcgLTAuOTUwNSwtMC43MjY4IC0xLjU0NjIsLTAuNzgzNCAtMC41OTU3LC0wLjA1NjUgLTEuMTkwOCwwLjExNDQgLTEuNjY1OCwwLjQ3ODUgLTAuMTMzNSwwLjEyNCAtMC4zMDYsMC4xOTc3IC0wLjQ4NzgsMC4yMDg0IC0xLjU1MjcsLTAuMDA3NyAtMy4xMDQsLTAuMDA3NyAtNC42NTQxLDAgLTAuMDk1MywwLjAxMjMgLTAuMTkxNSwtMC4wMTM0IC0wLjI2OCwtMC4wNzE0IHYgLTIuNDM1IGMgLTAuMDAxMSwtMC4wODMxIDAuMDI3NiwtMC4xNjM5IDAuMDgxLC0wLjIyNzcgMC4xMDk5LC0wLjExNDYgMC4yNDA4LC0wLjIwNjkgMC4zODU1LC0wLjI3MiAyLjUwNjQsLTEuMjczNSAzLjgwNTksLTMuMzY1MSAzLjgwNzgsLTYuMTQ5MyAwLC00LjQwNSAtNC4yOTM2LC03LjYwOTkgLTguNTM5LC02LjQ1NjEgLTMuMzY2MiwwLjkxNDYgLTUuNTUyNiw0LjI2MDMgLTQuOTE4Myw3LjY3MzYgMC40NjY2LDIuNTA4MyAxLjk0MTUsNC4yNjQyIDQuMzM5OSw1LjE5NDIgMC4wOSwwLjAzMjYgMC4xNzYsMC4wNzUzIDAuMjU2NCwwLjEyNzMgMC4wNDM2LDAuMDMzMiAwLjA3ODcsMC4wNzYxIDAuMTAyNCwwLjEyNTQgMC4wMjM4LDAuMDQ5MyAwLjAzNTYsMC4xMDM1IDAuMDM0NSwwLjE1ODMgdiAxLjk5MTIgYyAwLDAuMDQ1NCAtMC4wMDksMC4wOTA0IC0wLjAyNjUsMC4xMzIzIC0wLjAxNzUsMC4wNDE5IC0wLjA0MzEsMC4wOCAtMC4wNzU0LDAuMTExOSAtMC4wMzIyLDAuMDMyIC0wLjA3MDUsMC4wNTcyIC0wLjExMjYsMC4wNzQyIC0wLjA0MjEsMC4wMTcgLTAuMDg3MSwwLjAyNTUgLTAuMTMyNSwwLjAyNSAtMS42Njc3LC0wLjAxOTMgLTMuMzM3MywtMC4wMTE1IC01LjAxMjgsMCAtMC4yMzA0LC0wLjAwMTEgLTAuNDU0MywtMC4wNzY5IC0wLjYzODEsLTAuMjE2MSAtMC40NzYzLC0wLjM2MTcgLTEuMDcyLC0wLjUyOSAtMS42NjY4LC0wLjQ2ODEgLTAuNTk0OCwwLjA2MDggLTEuMTQ0MywwLjM0NTQgLTEuNTM3NSwwLjc5NjIgLTAuMzk0MywwLjQ0MyAtMC42MDk2LDEuMDE3MSAtMC42MDM5LDEuNjEwNCAwLjAwNTcsMC41OTMzIDAuMjMyLDEuMTYzMiAwLjYzNDgsMS41OTg2IDAuNDAyNywwLjQzNTMgMC45NTMxLDAuNzA1IDEuNTQzNywwLjc1NjMgMC41OTA2LDAuMDUxMyAxLjE3OTIsLTAuMTE5NCAxLjY1MDksLTAuNDc4OCAwLjE4MSwtMC4xNDIxIDAuNDA2MywtMC4yMTU4IDAuNjM2MiwtMC4yMDgzIDEuNjE3NiwwIDMuMjM3MSwwLjAyMzEgNC44NTQ3LDAgMC4xNDU1LC0wLjAyMTUgMC4yOTQsMC4wMSAwLjQxODMsMC4wODg3IDAuMDE5OCwwLjAxODggMC4wMzUzLDAuMDQxNiAwLjA0NTYsMC4wNjY5IDAuMDEwNCwwLjAyNTMgMC4wMTUyLDAuMDUyNSAwLjAxNDIsMC4wNzk4IHYgMjEuMTg3NyBjIC0xMGUtNSwwLjA0OTIgLTAuMDEwNCwwLjA5NzkgLTAuMDMwMiwwLjE0MyAtMC4wMTk4LDAuMDQ1MSAtMC4wNDg4LDAuMDg1NiAtMC4wODUsMC4xMTg5IC0wLjAzNjIsMC4wMzM0IC0wLjA3ODksMC4wNTg4IC0wLjEyNTUsMC4wNzQ5IC0wLjA0NjUsMC4wMTYgLTAuMDk1OCwwLjAyMjIgLTAuMTQ0OSwwLjAxODIgLTEuOTY0NiwtMC4xNjYxIC0zLjkxMjksLTAuNDg4NyAtNS44MjYzLC0wLjk2NDcgLTEuMTczNywtMC4yODMzIC0yLjMxNTgsLTAuNjg0OCAtMy40MDg3LC0xLjE5ODMgLTAuMDE5LC0wLjAwODQgLTAuMDM3MSwtMC4wMTg3IC0wLjA1NCwtMC4wMzA4IC0xLjU0MTgsLTEuMDgzMiAtMi44Mjc3LC0yLjQ5MTMgLTMuNzY3MiwtNC4xMjUzIC0wLjAyNjUsLTAuMDM4MSAtMC4wNDI1LC0wLjA4MjUgLTAuMDQ2NSwtMC4xMjg4IC0wLjAwNCwtMC4wNDYyIDAuMDA0MiwtMC4wOTI3IDAuMDIzNiwtMC4xMzQ4IDAuMDE5NSwtMC4wNDIxIDAuMDQ5NywtMC4wNzg0IDAuMDg3NSwtMC4xMDUzIDAuMDM3OCwtMC4wMjY4IDAuMDgyLC0wLjA0MzMgMC4xMjgyLC0wLjA0NzggbCAwLjUxMDksLTAuMTA0MiBjIDAuMTAxNCwtMC4wMjI5IDAuMTk1MSwtMC4wNzIgMC4yNzE2LC0wLjE0MjQgMC4wNzY1LC0wLjA3MDQgMC4xMzMyLC0wLjE1OTcgMC4xNjQ0LC0wLjI1ODkgMC4wMzEzLC0wLjA5OTIgMC4wMzU5LC0wLjIwNDkgMC4wMTM1LC0wLjMwNjUgLTAuMDIyNCwtMC4xMDE2IC0wLjA3MSwtMC4xOTU1IC0wLjE0MSwtMC4yNzI0IGwgLTQuNDg0NSwtNC44OTEzIGMgLTAuMDY4NCwtMC4wNzc3IC0wLjE1NjQsLTAuMTM1NiAtMC4yNTQ4LC0wLjE2NzcgLTAuMDk4NCwtMC4wMzIxIC0wLjIwMzYsLTAuMDM3MSAtMC4zMDQ3LC0wLjAxNDcgLTAuMTAxLDAuMDIyNSAtMC4xOTQxLDAuMDcxNyAtMC4yNjk3LDAuMTQyNSAtMC4wNzU2LDAuMDcwNyAtMC4xMzA4LDAuMTYwNSAtMC4xNTk5LDAuMjU5OSAtMC42NjMyLDIuMTAxMiAtMS4zMDkxLDQuMTU0MiAtMS45NjQ2LDYuMjM2MSAtMC4wMzQ0LDAuMTA4NiAtMC4wMzk2LDAuMjI0NCAtMC4wMTUsMC4zMzU3IDAuMDI0NywwLjExMTIgMC4wNzgzLDAuMjE0IDAuMTU1NCwwLjI5NzggMC4wNzcxLDAuMDgzOCAwLjE3NSwwLjE0NTggMC4yODM4LDAuMTc5NiAwLjEwODcsMC4wMzM3IDAuMjI0NCwwLjAzODEgMC4zMzU0LDAuMDEyNyBsIDAuMzQ3MSwtMC4wNzkxIGMgMC4yMTM0LC0wLjA0ODggMC40MzcsLTAuMDI1MyAwLjYzNTYsMC4wNjY5IDAuMTk4NSwwLjA5MjIgMC4zNjA5LDAuMjQ3OSAwLjQ2MTQsMC40NDI1IDIuNTc5Niw1LjAxNjcgNi40NTg3LDguNjQ2IDExLjU5MSwxMC45NDAyIDIuMDA1MSwwLjg5NzIgMi43OTk0LDEuMTU3NyA0LjUzMDcsMS40MjIgMC4wNzMyLDAuMDExMiAwLjE0MDgsMC4wNDU3IDAuMTkyOCwwLjA5ODQgbCAyLjYzNTYsMi42MTQ1IGMgMC4xNzY3LDAuMTcyMiAwLjQxMzYsMC4yNjg1IDAuNjYwMywwLjI2ODUgMC4yNDY3LDAgMC40ODM2LC0wLjA5NjMgMC42NjAzLC0wLjI2ODUgbCAyLjU2MDQsLTIuNTA4MyBjIDAuMDU4NiwtMC4wNTY5IDAuMTM0NywtMC4wOTIyIDAuMjE1OSwtMC4xMDA0IDIuNTY0MiwtMC4yNDEyIDMuNzA3NSwtMC44NzYgNi40NjY0LC0yLjEzOTggNC4yNzA1LC0xLjk1NDYgNy42NjE4LC01LjM1NjIgOS44NDQzLC05LjUxNjIgMC4xOTI4LC0wLjM3MDUgMC4zNjI0LC0wLjQxNDkgMC42OTAyLC0wLjMwNDkgMC4xMjUzLDAuMDQyNSAwLjI1MjUsMC4wODMgMC4zODU2LDAuMTIxNiAwLjEzNTQsMC4wNDIyIDAuMjc5NiwwLjA0NzMgMC40MTc2LDAuMDE0OSAwLjEzODEsLTAuMDMyNCAwLjI2NDksLTAuMTAxMiAwLjM2NzQsLTAuMTk5MyAwLjEwMjUsLTAuMDk4IDAuMTc2OSwtMC4yMjE3IDAuMjE1NSwtMC4zNTgzIDAuMDM4NiwtMC4xMzY2IDAuMDQsLTAuMjgxIDAuMDA0LC0wLjQxODIgeiBNIDcwLjY3MTksNTIuOTg3NiBjIC0wLjQ1MSwtMC4wMTU4IC0wLjg5NDMsLTAuMTIwOSAtMS4zMDQ0LC0wLjMwOTIgLTAuNDEwMSwtMC4xODgzIC0wLjc3ODksLTAuNDU2IC0xLjA4NTEsLTAuNzg3NyAtMC4zMDYyLC0wLjMzMTcgLTAuNTQzNiwtMC43MjA4IC0wLjY5ODcsLTEuMTQ0OSAtMC4xNTUxLC0wLjQyNDEgLTAuMjI0NywtMC44NzQ3IC0wLjIwNDgsLTEuMzI1OCAwLjA5MjUsLTIuMDE4MiAxLjc2OTksLTMuNTA3OCAzLjY2MzEsLTMuMzYzMSAxLjk5MzYsMC4xNTQ0IDMuMzY2MywxLjc1NzggMy4yNjYsMy42MDYyIC0wLjEwNzksMi4wMTA1IC0xLjc1NDQsMy40NjU0IC0zLjYzNjEsMy4zMjQ1IHoiLz4mI3hhOyAgPC9nPiYjeGE7ICA8ZGVmcyBpZD0iZGVmczgyMSI+JiN4YTsgICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8wXzgxMiI+JiN4YTsgICAgICA8cmVjdCB5PSIwIiB4PSIwIiBpZD0icmVjdDgxOCIgZmlsbD0iI2ZmZmZmZiIgaGVpZ2h0PSIxMzYuODM3MDEiIHdpZHRoPSIxNDEuNzAyIi8+JiN4YTsgICAgPC9jbGlwUGF0aD4mI3hhOyAgPC9kZWZzPiYjeGE7PC9zdmc+" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;&lt;rect fill="rgb(255, 255, 255)" stroke="none" x="882" y="526" width="41" height="30" stroke-width="0"&gt;&lt;/rect&gt;&lt;text x="881.5" y="537.5"&gt;Ingress&lt;/text&gt;&lt;text x="881.5" y="551.5"&gt;Issuer&lt;/text&gt;&lt;/g&gt;&lt;path d="M 830 540 L 809 540" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="760" y="610" width="42.11" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 762.58 610 C 761.16 610 760 611.22 760 612.71 L 760 643.84 C 760 645.33 761.16 646.56 762.58 646.56 L 789.88 646.56 L 789.88 650 L 793.08 648.07 L 796.28 650 L 796.28 646.56 L 799.53 646.56 C 800.95 646.56 802.11 645.33 802.11 643.84 L 802.11 612.71 C 802.11 611.22 800.95 610 799.53 610 Z M 762.58 611.23 L 799.53 611.23 C 800.32 611.23 800.94 611.88 800.94 612.71 L 800.94 643.84 C 800.94 644.68 800.32 645.33 799.53 645.33 L 796.28 645.33 L 796.28 643.01 C 797.62 641.95 798.4 640.29 798.4 638.53 C 798.4 637.04 797.84 635.61 796.84 634.55 C 795.83 633.5 794.47 632.9 793.05 632.9 C 790.09 632.9 787.7 635.42 787.7 638.53 C 787.7 640.31 788.51 641.99 789.88 643.05 L 789.88 645.33 L 762.58 645.33 C 761.79 645.33 761.17 644.68 761.17 643.84 L 761.17 612.71 C 761.17 611.88 761.79 611.23 762.58 611.23 Z M 768.25 618.55 L 768.25 620.58 L 794.12 620.58 L 794.12 618.55 Z M 768.25 624.05 L 768.25 626.08 L 794.12 626.08 L 794.12 624.05 Z M 768.25 629.62 L 768.25 631.65 L 782.41 631.65 L 782.41 629.62 Z" fill="#005073" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 630px; margin-left: 758px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Ingress&lt;br&gt;Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="758" y="634" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Ingress&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 802.11 627.6 L 830 626.76" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 854.82 602.03 C 854.38 602.05 853.94 602.16 853.56 602.36 L 836.37 610.55 C 835.47 610.99 834.81 611.81 834.59 612.77 L 830.34 631.22 C 830.15 632.07 830.31 632.98 830.78 633.72 C 830.86 633.8 830.92 633.88 830.97 633.99 L 842.87 648.77 C 843.5 649.53 844.46 650 845.45 650 L 864.53 650 C 865.51 650 866.47 649.53 867.1 648.77 L 879 633.96 C 879.6 633.2 879.85 632.18 879.63 631.22 L 875.38 612.77 C 875.16 611.81 874.5 610.99 873.6 610.55 L 856.41 602.36 C 855.92 602.11 855.37 602 854.82 602.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 854.83 603.47 C 854.42 603.49 854.01 603.59 853.65 603.77 L 837.49 611.48 C 836.64 611.89 836.02 612.66 835.82 613.57 L 831.82 630.91 C 831.64 631.71 831.8 632.56 832.23 633.25 C 832.31 633.33 832.36 633.41 832.41 633.51 L 843.6 647.4 C 844.19 648.12 845.09 648.56 846.02 648.56 L 863.95 648.56 C 864.88 648.56 865.78 648.12 866.38 647.4 L 877.56 633.49 C 878.13 632.76 878.36 631.81 878.15 630.91 L 874.16 613.57 C 873.95 612.66 873.33 611.89 872.48 611.48 L 856.33 603.77 C 855.86 603.54 855.35 603.44 854.83 603.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 865.47 615.8 L 865.47 626.93 L 855.72 632.33 L 855.67 618.71 Z M 844.53 615.8 L 844.53 626.93 L 854.29 632.34 L 854.34 618.71 Z M 844.53 614.63 L 855 611.6 L 865.47 614.63 L 855 617.67 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="854.5" y="641.3"&gt;pod&lt;/text&gt;&lt;/g&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: 626px; margin-left: 882px;"&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: all; white-space: nowrap;"&gt;Ingress&lt;br&gt;Pod&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="882" y="630" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Ingress&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="760" y="520" width="50" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 760 525.66 L 765.83 520.04 L 797.91 520.04 C 799.81 520 801.44 521.36 801.67 523.18 L 801.67 524.8 L 799.34 524.02 C 799.39 523.12 798.69 522.35 797.76 522.28 L 767.07 522.28 L 767.07 526.9 L 762.32 526.9 L 762.32 543.62 C 762.38 544.27 762.89 544.79 763.56 544.9 L 780.41 544.9 L 781.75 547.09 L 763.36 547.09 C 761.59 546.84 760.21 545.48 760 543.76 Z M 782.74 556.76 L 787.09 549.62 C 781.97 546.24 779.79 540.02 781.72 534.35 C 783.65 528.68 789.23 524.91 795.42 525.11 C 801.61 525.31 806.93 529.42 808.46 535.2 C 810 540.98 807.39 547.05 802.06 550.09 L 806.96 557.19 L 801.62 555.81 L 800.09 559.95 L 794.35 552.57 L 790 560 L 788.02 555.52 Z M 782.79 538.66 C 783.1 544.96 788.45 549.93 794.99 550 C 801.48 549.85 806.74 544.9 807.05 538.66 C 806.88 532.4 801.69 527.34 795.19 527.09 C 788.48 527.06 782.97 532.2 782.79 538.66 Z M 785.75 538.66 C 785.74 536.3 786.71 534.03 788.45 532.37 C 790.18 530.7 792.54 529.78 794.99 529.8 C 797.42 529.81 799.74 530.76 801.45 532.42 C 803.15 534.08 804.1 536.32 804.09 538.66 C 803.98 543.44 799.95 547.26 794.99 547.28 C 789.98 547.34 785.86 543.49 785.75 538.66 Z M 787.78 538.66 C 787.98 542.39 791.13 545.33 794.99 545.43 C 798.82 545.28 801.91 542.35 802.11 538.66 C 802.01 534.9 798.9 531.86 794.99 531.71 C 791.05 531.81 787.88 534.86 787.78 538.66 Z" fill="#00188d" stroke="none" pointer-events="all"&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-end; width: 1px; height: 1px; padding-top: 540px; margin-left: 758px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;CA Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="758" y="544" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA Certif&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 855 565 L 855 580 L 781 580 L 781.04 603.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 781.05 608.88 L 777.54 601.89 L 781.04 603.63 L 784.54 601.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 579px; margin-left: 810px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="810" y="582" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1021.25 562.5 L 980 563 L 980 500 L 785 500 L 785 513.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 785 518.88 L 781.5 511.88 L 785 513.63 L 788.5 511.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 511px; margin-left: 966px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="966" y="514" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1021.25 587.5 L 980 588 L 980 670 L 785 670 L 785 713.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 785 718.88 L 781.5 711.88 L 785 713.63 L 788.5 711.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 660px; margin-left: 966px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="966" y="664" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1046.25 600 L 1046 670 L 1085 670 L 1085 713.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 1085 718.88 L 1081.5 711.88 L 1085 713.63 L 1088.5 711.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 661px; margin-left: 1061px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1061" y="664" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1174.82 550.03 C 1174.38 550.05 1173.94 550.16 1173.56 550.36 L 1156.37 558.55 C 1155.47 558.99 1154.81 559.81 1154.59 560.77 L 1150.34 579.22 C 1150.15 580.07 1150.31 580.98 1150.78 581.72 C 1150.86 581.8 1150.92 581.88 1150.97 581.99 L 1162.87 596.77 C 1163.5 597.53 1164.46 598 1165.45 598 L 1184.53 598 C 1185.51 598 1186.47 597.53 1187.1 596.77 L 1199 581.96 C 1199.6 581.2 1199.85 580.18 1199.63 579.22 L 1195.38 560.77 C 1195.16 559.81 1194.5 558.99 1193.6 558.55 L 1176.41 550.36 C 1175.92 550.11 1175.37 550 1174.82 550.03 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1174.83 551.47 C 1174.42 551.49 1174.01 551.59 1173.65 551.77 L 1157.49 559.48 C 1156.64 559.89 1156.02 560.66 1155.82 561.57 L 1151.82 578.91 C 1151.64 579.71 1151.8 580.56 1152.23 581.25 C 1152.31 581.33 1152.36 581.41 1152.41 581.51 L 1163.6 595.4 C 1164.19 596.12 1165.09 596.56 1166.02 596.56 L 1183.95 596.56 C 1184.88 596.56 1185.78 596.12 1186.38 595.4 L 1197.56 581.49 C 1198.13 580.76 1198.36 579.81 1198.15 578.91 L 1194.16 561.57 C 1193.95 560.66 1193.33 559.89 1192.48 559.48 L 1176.33 551.77 C 1175.86 551.54 1175.35 551.44 1174.83 551.47 Z" fill="#2875e2" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 1175 559.6 L 1166.52 563.37 L 1166.52 569.03 C 1166.52 574.26 1170.14 579.15 1175 580.34 C 1179.87 579.15 1183.48 574.26 1183.48 569.03 L 1183.48 563.37 Z M 1175 563.59 C 1176.1 563.59 1177.09 564.26 1177.5 565.27 C 1177.92 566.29 1177.7 567.45 1176.92 568.23 C 1176.14 569.01 1174.97 569.24 1173.96 568.82 C 1172.94 568.4 1172.28 567.41 1172.28 566.31 C 1172.28 564.81 1173.5 563.6 1175 563.6 Z M 1180.09 574.82 C 1180.09 574.88 1180.06 574.95 1180.03 575.01 L 1179.84 575.28 C 1178.64 576.93 1176.94 578.15 1175 578.76 C 1173.05 578.15 1171.36 576.93 1170.16 575.28 L 1169.97 575.01 C 1169.93 574.95 1169.91 574.89 1169.91 574.82 L 1169.91 573.31 C 1169.91 571.23 1173.3 570.18 1175 570.18 C 1176.7 570.18 1180.09 571.23 1180.09 573.31 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g fill="#ffffff" font-family="Arial,Helvetica" text-anchor="middle" font-size="9.600000000000001px"&gt;&lt;text x="1174.5" y="589.3"&gt;sa&lt;/text&gt;&lt;/g&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 605px; margin-left: 1175px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Service&lt;br&gt;Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1175" y="617" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Service&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 1046.25 550 L 1050 420 L 790 420 L 790 370" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 426.25 550 L 426 420 L 770 420 L 770 370" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="898.86" y="30" width="364.38" height="80" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&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-end; width: 362px; height: 1px; padding-top: 70px; margin-left: 899px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"&gt;Extend Corporate PKI to Cloud for Kubernetes workload&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="1261" y="77" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="24px" text-anchor="end" font-weight="bold"&gt;Extend Corporate PKI to Cloud f&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&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.drawio.com/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;In this architecture, we extend corporate on-premise root CA to the cloud. The Ops account hosts the AWS Private CA, and shares it out to workload account(s) using &lt;a href="https://aws.amazon.com/ram/"&gt;Resource Access Manager&lt;/a&gt;. In each EKS cluster, the Private CA serves as the cluster-level issuer, which can issue CA certificates across Kubernetes namespaces. In each namespace, there is a Cert Manager issuer responsible for issuing certificates within the namespace. Cert Manager supports many &lt;a href="https://cert-manager.io/docs/configuration/issuers/"&gt;issuers&lt;/a&gt; and we&amp;#8217;re using the &lt;a href="https://github.com/cert-manager/aws-privateca-issuer"&gt;AWS Private CA issuer&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To demonstrate the gist of this architecture, in our lab we&amp;#8217;ll create one Kubernetes cluster in the same AWS account as the Private CA, and create a Cert Manager certificate in one namespace (e.g. ingress). To get started, create a private CA with the instruction in this post and ensure that the private CA has path length of 1. Then create an EKS cluster. You may use my &lt;a href="https://github.com/digihunch/cloudkube/tree/main"&gt;CloudKube&lt;/a&gt; project to provision this cluster in Terraform or any other ways. We use the IRSA model to grant a service account access to the Private CA. First, we create an IAM policy and reference the ARN of the private CA:&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;&lt;span style="color:#f92672"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: &lt;span style="color:#f92672"&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;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;awspcaissuer&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;acm-pca:DescribeCertificateAuthority&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;acm-pca:GetCertificate&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;acm-pca:IssueCertificate&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;arn:aws:acm-pca:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:certificate-authority/&amp;lt;resource_id&amp;gt;&amp;#34;&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;}&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;]&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;}&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;Name this policy &amp;#8216;PCA-Access&amp;#8217;. It is the minimum required permission. Now let&amp;#8217;s create a service account, along with an IAM role that uses this policy:&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;eksctl utils associate-iam-oidc-provider &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; --region $AWS_REGION &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; --cluster $CLUSTER_NAME &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; --approve&#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;eksctl create iamserviceaccount &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; --cluster&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$CLUSTER_NAME &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; --namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;cert-manager &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; --name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;aws-pca-sa &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; --role-name EKSCertManagerPrivateCARole-$CLUSTER_NAME &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; --attach-policy-arn&lt;span style="color:#f92672"&gt;=&lt;/span&gt;arn:aws:iam::123456789012:policy/PCA-Access &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; --approve&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In order to run the command successfully, you need the IAM permission to create IAM role, as well as API access to the cluster. Now we can install both Cert Manager and the Private CA Issuer.&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 awspca https://cert-manager.github.io/aws-privateca-issuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add jetstack https://charts.jetstack.io&#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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm install cert-manager jetstack/cert-manager &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; --namespace cert-manager &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; --version v1.13.3 &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; --set installCRDs&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; --create-namespace&#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;helm install aws-ca awspca/aws-privateca-issuer &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; --namespace cert-manager &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; --version v1.2.7 &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; --set serviceAccount.create&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false &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; --set serviceAccount.name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;aws-pca-sa&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that when installing Private CA issuer with helm, specify the service account that we created earlier (aws-pca-sa). We install both to the cert-manager namespace, where we&amp;#8217;ll create a ClusterIssuer. Now we can declare the following manifest:&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;apiVersion: awspca.cert-manager.io/v1beta1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kind: AWSPCAClusterIssuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;metadata:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: pca-cluster-issuer-rsa&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; namespace: cert-manager&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spec:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; arn: arn:aws:acm-pca:ca-central-1:383500642091:certificate-authority/7f2d7b38-2508-4492-81f0-b5b85427c99c&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; region: ca-central-1&#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;apiVersion: v1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kind: Namespace&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;metadata:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: ingress&#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;apiVersion: cert-manager.io/v1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kind: Certificate&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;metadata:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: ingress-ca-cert&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; namespace: ingress&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spec:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; isCA: true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commonName: ingress-ca&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; secretName: ingress-ca-secret&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; privateKey:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; algorithm: RSA&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size: &lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; issuerRef:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: pca-cluster-issuer-rsa&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kind: AWSPCAClusterIssuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; group: awspca.cert-manager.io&#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;apiVersion: cert-manager.io/v1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kind: Issuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;metadata:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: ingress-ca-issuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; namespace: ingress&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spec:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ca:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; secretName: ingress-ca-secret&#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;apiVersion: cert-manager.io/v1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kind: Certificate&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;metadata:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: ingress-cert&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; namespace: ingress&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spec:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commonName: web.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; secretName: web-digihunch-secret&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; duration: 2160h&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; renewBefore: 72h&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subject:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; organizations:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - digihunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dnsNames:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - web.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; privateKey:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; algorithm: RSA&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size: &lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; issuerRef:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: ingress-ca-issuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kind: Issuer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; group: cert-manager.io&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this manifest, we create a CA certificate, along with a CA in the ingress namespace. With that, we create an end-entity certificate. As a result, we should find the certificates ready:&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 ingress get certificate&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME READY SECRET AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ingress-ca-cert True ingress-ca-secret 24s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ingress-cert True web-digihunch-secret 24s&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The certificate can be reference by the workload in the namespace. Because an Issuer can only issuer Certificate in the same namespace, we need a issuer in the namespace where the certificate will live. The other benefit that Cert Manager brings, is the support of &lt;a href="https://cert-manager.io/docs/configuration/acme/"&gt;ACME&lt;/a&gt; challenges, which is an enhancement to AWS Private CA.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We discussed several approaches in PKI implementation. PKI is a fundamental requirement in a software testing environment today. Having an internal public key infrastructure enables many other use cases too. For example, the team may use &lt;a href="https://dev.to/gvelrajan/how-to-configure-and-setup-ssh-certificates-for-ssh-authentication-b52"&gt;SSH user certificate&lt;/a&gt;. You can also host a CA with &lt;a href="https://smallstep.com/docs/tutorials/ssh-certificate-login/"&gt;Step&lt;/a&gt;. PKI allows an enterprise to configure SSL inspection on their Next Generation Firewall. The &lt;a href="https://aws.amazon.com/blogs/security/iam-roles-anywhere-with-an-external-certificate-authority/"&gt;IAM Role Anywhere&lt;/a&gt; feature on AWS also operates on an organization&amp;#8217;s own PKI.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2024/03/public-key-infrastructure-2-of-3-certificate-automation/"&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 2 of 3 – Certificate Automation&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/05/managing-ec2-instances-across-aws-accounts-ssm/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Managing EC2 instances across accounts with Ansible&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Public Key Infrastructure 2 of 3 – Certificate Automation</title><link>https://www.digihunch.com/2024/03/public-key-infrastructure-2-of-3-certificate-automation/</link><pubDate>Fri, 08 Mar 2024 00:14:00 -0400</pubDate><guid>https://www.digihunch.com/2024/03/public-key-infrastructure-2-of-3-certificate-automation/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-pki-2-1.webp" alt="Featured image of post Public Key Infrastructure 2 of 3 – Certificate Automation" /&gt;&lt;p class="wp-block-paragraph"&gt;Following the last post on PKI, we&amp;#8217;ll discuss automation of certificate issuance. Two key activities to automate are: validation of the requestor and issuance of the certificate.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Validation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Validation isn&amp;#8217;t always required. For private CAs, the trust boundary does not go beyond the internal engineering team, there is little incentive to perform any validation. AWS Private CA is based on this idea. The requestor can claim to be any identity. The private CA, when issuing the certificate, does not perform any validation. Neither is there a need to convince any entity outside of the trust boundary of the validity of the certificate. Validation is optional. For public facing certificate however, validation is a must because we&amp;#8217;re convincing every browser in the world of the validity of the certificate requestor. Common validation levels include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Domain Validation (DV): the certificate requestor must demonstrate the right to administratively manage the affected DNS domain.&lt;/li&gt;&#10;&lt;li&gt;Organization Validation (OV): in addition to the DV criterion, the issuer verifies the actual existence of the requestor&amp;#8217;s organization as a legal entity.&lt;/li&gt;&#10;&lt;li&gt;Extended Validation (EV): the certificate requestor must persuade the certificate provider of its legal identity, including manual verification checks y a human. Unlike DV and OV certificates, only a subset of CAs can issue EV certificates.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For both OV and EV, a certificate provider publishes its vetting criteria through its certificate policy. They require human validation of any registrants. At corporate level, EV certificates are required for sensitive public-facing workloads (e.g. banking, financial, health information). For non-sensitive public-facing workloads, DV certificates may be sufficient. For non-public facing workloads, such as software testing, they may go with DV certificates or no validation at all, depending on the specific use case. Since I set up PKI for the latter, I&amp;#8217;ll focus on DV. DV is the most basic level and can be fully automated. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="657" src="https://www.digihunch.com/wp-content/uploads/2024/03/dv.webp" alt="" class="wp-image-12980" srcset="https://www.digihunch.com/wp-content/uploads/2024/03/dv.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/03/dv-300x192.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/03/dv-768x493.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DigiCert, a well-known trusted third party, has a &lt;a href="https://www.digicert.com/difference-between-dv-ov-and-ev-ssl-certificates"&gt;detailed page&lt;/a&gt; on the differences among DV, OV and EV.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Certificate Automation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As to automation, there are some common certificate automation protocols:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ACME (Automated Certificate Management Environment): commonly used in web server automation.&lt;/li&gt;&#10;&lt;li&gt;SCEP (Simple Certificate Enrollment Protocol): commonly used in enterprise environments for managing certificates in the network devices such as routers, switches and IP phones.&lt;/li&gt;&#10;&lt;li&gt;EST (Enrolment over Secure Transport): a more secure alternative to SCEP suitable for various use cases beyond network devices.&lt;/li&gt;&#10;&lt;li&gt;CMP (Certificate Management Protocol): more comprehensive protocol with a wide range of functionalities for complex certificate management scenarios.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;SCEP is common in network industry. EST and CMP target very specific scenarios. We&amp;#8217;ll examine ACME as it&amp;#8217;s most relevant to the use case of web service. The biggest advocate of ACME is Let&amp;#8217;s Encrypt, a non-profit CA run by ISRG that provisions X.509 certificates at no charge. Let&amp;#8217;s Encrypt is the world&amp;#8217;s largest CA, aiming to secure all websites with HTTPS. ACME only issues DV certificates, since they can be fully automated.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;The ACME Protocol&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ACME protocol automates validation and issuance. The certificate requestor will have to use an ACME-capable client. The certificate provider (CA) needs to act as ACME server. At a high level, the flow looks like this:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="918" height="1024" src="https://www.digihunch.com/wp-content/uploads/2024/03/acme-918x1024.webp" alt="" class="wp-image-13092" srcset="https://www.digihunch.com/wp-content/uploads/2024/03/acme-918x1024.webp 918w, https://www.digihunch.com/wp-content/uploads/2024/03/acme-269x300.webp 269w, https://www.digihunch.com/wp-content/uploads/2024/03/acme-768x857.webp 768w, https://www.digihunch.com/wp-content/uploads/2024/03/acme-1377x1536.webp 1377w, https://www.digihunch.com/wp-content/uploads/2024/03/acme.webp 1662w" sizes="auto, (max-width: 918px) 100vw, 918px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I came across this good diagram on the ACME flow from a post from &lt;a href="https://smallstep.com/blog/private-acme-server/"&gt;small step&lt;/a&gt;. It has all the transactions in detail. As it shows, the delivery (issuance) of certificate material is based on HTTP POST method. The domain validation process is based on a challenge-response model. The ACME specification makes this an extension point, with the following most comment challenge types:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;HTTP-01 (HTTP Challenge): the domain in question needs to host a random number at a random URL under /.well-known/acme-challenge on port 80. The CA will fire an HTTP GET request to that URL. This is easy to configure because we usually have full control on the web server. There must be network connectivity between the web server and the CA to allow HTTP traffic.&lt;/li&gt;&#10;&lt;li&gt;DNS-01 (DNS Challenge): the requestor provisions a TXT record with random value. The ACME server does not need to connect to the web server. It only needs to perform a DNS lookup to confirm the challenge. However, the certificate requestor needs the privilege to modify DNS record.&lt;/li&gt;&#10;&lt;li&gt;TLS-ALPN-01 (TLS ALPN Challenge): ALPN is the protocol during TLS negotiation. The client presents a self-signed TLS certificate containing the challenge response as a special X.509 certificate extension. This challenge type is useful when a security policy requires the CA to reach the client via a TLS connection.&lt;/li&gt;&#10;&lt;li&gt;DEVICE-ATTEST-01 (Device Attestation Challenge): This is for Apple Managed Device Attestation (ADA) and other secure zero-touch provisioning (SZTP) applications as part of your device management (MDM) strategy. Certificates identify specific hardware devices, via permanent device IDs. These are typically client certificates that can be used for device authentication.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At the implementation level, Let&amp;#8217;s encrypt drives its public CA with &lt;a href="https://github.com/letsencrypt/boulder"&gt;Boulder&lt;/a&gt;. It supports two &lt;a href="https://letsencrypt.org/docs/challenge-types/"&gt;challenge types&lt;/a&gt;. When hosting a private CA, you can use Boulder too. Some feel Boulder is complicated and you can consider the following alternatives:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://lab-ca.net/"&gt;LabCA&lt;/a&gt;: based on Boulder and supports hosting in docker.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://smallstep.com/docs/step-ca/"&gt;Step CA&lt;/a&gt; (open source): a simple CA solution&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://cert-manager.io/docs/configuration/acme/"&gt;Cert Manager&lt;/a&gt;: very popular choice on Kubernetes&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.hashicorp.com/products/vault"&gt;Hashicorp Vault&lt;/a&gt;: a secret management solution including certificate management capability with ACME support.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You may combine different solutions for all level of CAs. For example, use Step CA for internal root CA, and Cert Manager for intermediate CAs for Kubernetes workloads. On the client side, Let&amp;#8217;s Encrypt recommends Certbot. However, there are many choices. Step CLI (by Step CA), acme.sh, etc. Let&amp;#8217;s Encrypt compiled a &lt;a href="https://letsencrypt.org/docs/client-options/"&gt;list&lt;/a&gt; here. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If we&amp;#8217;re not seeking automation with ACME in our process, and just want to manually sign certificates, we can use generic tools (e.g. openSSL, cfssl, easyRSA, etc). They act both as client (gingnerate CSR) and server (signing CSR) using different command switches.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Renewal and Revocation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lifecycle management involves renewal and revocations. Renewal is essentially re-issue certificates closer to expiration date. In software testing, we often use short-lived certificates, to ensure that our test scenario covers automated certificate renewal as well. It is the responsibility of requestor to initiate the renewal, and distribute the renewed certificates. With Let&amp;#8217;s Encrypt, the renewal process will challenge the requestor again for validation purpose. However, in some cases, the certificate provider may choose not to perform validation on every renewal. For example, short-lived certificate gets renewed every week, while validation is performed every year. During the renewal process, the private key of the website does not change. Note the difference between renewal and rekey. If the website&amp;#8217;s private key is compromised, then instead of renewal, we should re-issue a private key and request a new certificate.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Revocation is a challenging process. To declare that a certificate should no longer be trusted, there are currently two ways: CRL and OCSP but both have drawbacks. CRLs are lists of all the certificates that a CA has issued but revoked. This list can grow very large. It is not feasible for the application (e.g. Browser) to download the giant list for each CA regularly and check for every website that matches the CA. OCSP provides a query-based method. The application can query the revocation status against the OCSP endpoint. It however brings its own challenges. The OCSP server is subject to downtime. The network connectivity between application and OCSP server causes latency. Many applications simply treats query timeout as not revoked. To reduce the load, application may cache OCSP responses, leading to potentially out-dated status. Worse, a malicious CA can track website of the application user.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s encrypt has a page on these challenges, and it proposes a new &lt;a href="https://letsencrypt.org/2022/09/07/new-life-for-crls.html"&gt;browser-summarized CRLs&lt;/a&gt;. It was still a recent effort so we&amp;#8217;ll see how that plays out.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Following the first post on the PKI concepts, we discussed the automation of certificate issuance in this post. In the next one, let&amp;#8217;s go over some labs. &lt;br&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2024/02/public-key-infrastructure-1-of-3-basics/"&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 1 of 3 – Basics&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/03/public-key-infrastructure-3-of-3-use-cases/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Public Key Infrastructure 3 of 3 – PKI Implementation&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Public Key Infrastructure 1 of 3 – Basics</title><link>https://www.digihunch.com/2024/02/public-key-infrastructure-1-of-3-basics/</link><pubDate>Tue, 20 Feb 2024 00:05:00 -0400</pubDate><guid>https://www.digihunch.com/2024/02/public-key-infrastructure-1-of-3-basics/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-pki-2.webp" alt="Featured image of post Public Key Infrastructure 1 of 3 – Basics" /&gt;&lt;p class="wp-block-paragraph"&gt;In 2021, I wrote an &lt;a href="https://www.digihunch.com/2021/04/public-key-infrastructure-pki/"&gt;intro to Public Key Infrastructure (PKI)&lt;/a&gt;. Now that I have to host my own certificate authority, I decide to dive a little deeper into PKI in this series of posts. In software testing scenario, we need to issue (and recycle) a lot of certificates, and manage their lifecycle events such as (renewal, revocation). As a result, the corporate should establish its own private key infrastructure (PKI). This effort includes hosing their own Certificate Authority.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Concepts&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IT professions often use the acronyms PKI and CA interchangeably in the context of implementation. However, strictly speaking, PKI is the entire framework (strategy, policy, etc) around managing certificate at corporate level, and CA is the specific entity that issues certificates. When it comes to architecting PKI, a key design decision is the hierarchy of CAs. Microsoft has a &lt;a href="https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn786436(v=ws.11)#ca-hierarchy-options"&gt;good page&lt;/a&gt; explaining the common options. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a single-tier CA hierarchy, the root CA is also issuing CA. The root CA as the anchor of trust of the PKI also issues certificates to the end entities. Obviously, this does not scale. It is only for sandbox testing, and not suitable for any environment that needs to scale. It also carries significant risk because the private key at the anchor of trust has to remain online.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With a two-tier CA hierarchy, the root CA only issues certificates to its subordinate CAs. Therefore the root CA can go offline. As a result, the subordinate CAs issue certificates to end entities. Since the root CA can remain offline (except for issuing certificates for new CAs), the chance compromise is reduced significantly. This option also scales better with multiple issuing CAs. If one of them gets compromised, then only the end entities of the compromised CA are impacted. The root CA still needs to be online from time to time. Apart from granting new subordinate CA, the team may also need root CA online to sign CRLs, which is a management overhead.&lt;/p&gt;&#10;&lt;p class="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="511px" viewBox="-0.5 -0.5 511 701" style="max-width:100%;max-height:701px;"&gt;&lt;defs&gt;&lt;style type="text/css"&gt;@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&amp;#xa;&lt;/style&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="510" height="700" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;rect x="90" y="20" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 111.87 29.03 L 111.87 26.77 L 95.28 26.77 L 95.28 29.03 Z M 90 72.69 L 90 22.94 C 90 21.27 91.51 20 92.92 20 L 114.19 20 C 115.61 20 117.15 21.19 117.15 23.03 L 117.15 50.85 L 110.21 50.85 L 103.57 57.48 L 103.57 57.63 L 95.28 57.63 L 95.28 59.89 L 103.57 59.89 L 103.57 64.41 L 95.28 64.41 L 95.28 66.67 L 103.57 66.67 L 103.57 72.69 Z M 105.84 59.14 L 105.84 58.39 L 111.11 53.12 L 111.87 53.12 L 111.87 59.14 Z M 105.84 72.69 L 105.84 60.64 L 113.38 60.64 L 113.38 53.12 L 131.48 53.12 L 131.48 58.9 C 130.57 58.39 129.26 57.91 127.74 57.94 C 123.97 57.94 120.18 60.95 120.18 65.48 C 120.15 67.92 121.29 69.67 122.35 70.81 L 121.36 72.69 Z M 127.74 70.42 C 130.67 70.42 132.67 68.12 132.67 65.48 C 132.67 62.99 130.64 60.64 127.74 60.64 C 125.01 60.64 122.87 62.92 122.87 65.48 C 122.87 68.59 125.42 70.42 127.74 70.42 Z M 127.74 69.23 C 125.84 69.23 124.09 67.68 124.09 65.48 C 124.09 63.64 125.66 61.85 127.74 61.85 C 130.04 61.85 131.48 63.73 131.48 65.48 C 131.48 67.58 129.86 69.23 127.74 69.23 Z M 127.74 68.47 C 129.44 68.47 130.72 67.16 130.72 65.48 C 130.72 64.13 129.63 62.59 127.74 62.59 C 126.48 62.59 124.84 63.58 124.84 65.48 C 124.84 67.09 126.1 68.47 127.74 68.47 Z M 120.7 77.43 L 124.4 70.51 C 122.83 69.39 121.82 67.67 121.82 65.48 C 121.82 62.09 124.72 59.52 127.74 59.52 C 131.52 59.52 133.89 62.55 133.89 65.48 C 133.9 67.77 132.75 69.27 131.49 70.36 L 136 76.9 L 132.45 76.08 L 130.5 78.79 L 127.74 71.71 L 125.9 79 L 123.85 76.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 86px; margin-left: 113px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Root CA &amp;amp;&lt;br /&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="113" y="98" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Root CA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="170" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 111.87 179.03 L 111.87 176.77 L 95.28 176.77 L 95.28 179.03 Z M 90 222.69 L 90 172.94 C 90 171.27 91.51 170 92.92 170 L 114.19 170 C 115.61 170 117.15 171.19 117.15 173.03 L 117.15 200.85 L 110.21 200.85 L 103.57 207.48 L 103.57 207.63 L 95.28 207.63 L 95.28 209.89 L 103.57 209.89 L 103.57 214.41 L 95.28 214.41 L 95.28 216.67 L 103.57 216.67 L 103.57 222.69 Z M 105.84 209.14 L 105.84 208.39 L 111.11 203.12 L 111.87 203.12 L 111.87 209.14 Z M 105.84 222.69 L 105.84 210.64 L 113.38 210.64 L 113.38 203.12 L 131.48 203.12 L 131.48 208.9 C 130.57 208.39 129.26 207.91 127.74 207.94 C 123.97 207.94 120.18 210.95 120.18 215.48 C 120.15 217.92 121.29 219.67 122.35 220.81 L 121.36 222.69 Z M 127.74 220.42 C 130.67 220.42 132.67 218.12 132.67 215.48 C 132.67 212.99 130.64 210.64 127.74 210.64 C 125.01 210.64 122.87 212.92 122.87 215.48 C 122.87 218.59 125.42 220.42 127.74 220.42 Z M 127.74 219.23 C 125.84 219.23 124.09 217.68 124.09 215.48 C 124.09 213.64 125.66 211.85 127.74 211.85 C 130.04 211.85 131.48 213.73 131.48 215.48 C 131.48 217.58 129.86 219.23 127.74 219.23 Z M 127.74 218.47 C 129.44 218.47 130.72 217.16 130.72 215.48 C 130.72 214.13 129.63 212.59 127.74 212.59 C 126.48 212.59 124.84 213.58 124.84 215.48 C 124.84 217.09 126.1 218.47 127.74 218.47 Z M 120.7 227.43 L 124.4 220.51 C 122.83 219.39 121.82 217.67 121.82 215.48 C 121.82 212.09 124.72 209.52 127.74 209.52 C 131.52 209.52 133.89 212.55 133.89 215.48 C 133.9 217.77 132.75 219.27 131.49 220.36 L 136 226.9 L 132.45 226.08 L 130.5 228.79 L 127.74 221.71 L 125.9 229 L 123.85 226.89 Z" fill="#505050" stroke="none" pointer-events="all"/&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-end; width: 1px; height: 1px; padding-top: 200px; margin-left: 88px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Root CA&lt;br /&gt;(Offline)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="88" y="203" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Root CA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="270" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 111.87 279.03 L 111.87 276.77 L 95.28 276.77 L 95.28 279.03 Z M 90 322.69 L 90 272.94 C 90 271.27 91.51 270 92.92 270 L 114.19 270 C 115.61 270 117.15 271.19 117.15 273.03 L 117.15 300.85 L 110.21 300.85 L 103.57 307.48 L 103.57 307.63 L 95.28 307.63 L 95.28 309.89 L 103.57 309.89 L 103.57 314.41 L 95.28 314.41 L 95.28 316.67 L 103.57 316.67 L 103.57 322.69 Z M 105.84 309.14 L 105.84 308.39 L 111.11 303.12 L 111.87 303.12 L 111.87 309.14 Z M 105.84 322.69 L 105.84 310.64 L 113.38 310.64 L 113.38 303.12 L 131.48 303.12 L 131.48 308.9 C 130.57 308.39 129.26 307.91 127.74 307.94 C 123.97 307.94 120.18 310.95 120.18 315.48 C 120.15 317.92 121.29 319.67 122.35 320.81 L 121.36 322.69 Z M 127.74 320.42 C 130.67 320.42 132.67 318.12 132.67 315.48 C 132.67 312.99 130.64 310.64 127.74 310.64 C 125.01 310.64 122.87 312.92 122.87 315.48 C 122.87 318.59 125.42 320.42 127.74 320.42 Z M 127.74 319.23 C 125.84 319.23 124.09 317.68 124.09 315.48 C 124.09 313.64 125.66 311.85 127.74 311.85 C 130.04 311.85 131.48 313.73 131.48 315.48 C 131.48 317.58 129.86 319.23 127.74 319.23 Z M 127.74 318.47 C 129.44 318.47 130.72 317.16 130.72 315.48 C 130.72 314.13 129.63 312.59 127.74 312.59 C 126.48 312.59 124.84 313.58 124.84 315.48 C 124.84 317.09 126.1 318.47 127.74 318.47 Z M 120.7 327.43 L 124.4 320.51 C 122.83 319.39 121.82 317.67 121.82 315.48 C 121.82 312.09 124.72 309.52 127.74 309.52 C 131.52 309.52 133.89 312.55 133.89 315.48 C 133.9 317.77 132.75 319.27 131.49 320.36 L 136 326.9 L 132.45 326.08 L 130.5 328.79 L 127.74 321.71 L 125.9 329 L 123.85 326.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 336px; margin-left: 113px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="113" y="348" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="170" y="270" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 191.87 279.03 L 191.87 276.77 L 175.28 276.77 L 175.28 279.03 Z M 170 322.69 L 170 272.94 C 170 271.27 171.51 270 172.92 270 L 194.19 270 C 195.61 270 197.15 271.19 197.15 273.03 L 197.15 300.85 L 190.21 300.85 L 183.57 307.48 L 183.57 307.63 L 175.28 307.63 L 175.28 309.89 L 183.57 309.89 L 183.57 314.41 L 175.28 314.41 L 175.28 316.67 L 183.57 316.67 L 183.57 322.69 Z M 185.84 309.14 L 185.84 308.39 L 191.11 303.12 L 191.87 303.12 L 191.87 309.14 Z M 185.84 322.69 L 185.84 310.64 L 193.38 310.64 L 193.38 303.12 L 211.48 303.12 L 211.48 308.9 C 210.57 308.39 209.26 307.91 207.74 307.94 C 203.97 307.94 200.18 310.95 200.18 315.48 C 200.15 317.92 201.29 319.67 202.35 320.81 L 201.36 322.69 Z M 207.74 320.42 C 210.67 320.42 212.67 318.12 212.67 315.48 C 212.67 312.99 210.64 310.64 207.74 310.64 C 205.01 310.64 202.87 312.92 202.87 315.48 C 202.87 318.59 205.42 320.42 207.74 320.42 Z M 207.74 319.23 C 205.84 319.23 204.09 317.68 204.09 315.48 C 204.09 313.64 205.66 311.85 207.74 311.85 C 210.04 311.85 211.48 313.73 211.48 315.48 C 211.48 317.58 209.86 319.23 207.74 319.23 Z M 207.74 318.47 C 209.44 318.47 210.72 317.16 210.72 315.48 C 210.72 314.13 209.63 312.59 207.74 312.59 C 206.48 312.59 204.84 313.58 204.84 315.48 C 204.84 317.09 206.1 318.47 207.74 318.47 Z M 200.7 327.43 L 204.4 320.51 C 202.83 319.39 201.82 317.67 201.82 315.48 C 201.82 312.09 204.72 309.52 207.74 309.52 C 211.52 309.52 213.89 312.55 213.89 315.48 C 213.9 317.77 212.75 319.27 211.49 320.36 L 216 326.9 L 212.45 326.08 L 210.5 328.79 L 207.74 321.71 L 205.9 329 L 203.85 326.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 336px; margin-left: 193px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="193" y="348" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="250" y="270" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 271.87 279.03 L 271.87 276.77 L 255.28 276.77 L 255.28 279.03 Z M 250 322.69 L 250 272.94 C 250 271.27 251.51 270 252.92 270 L 274.19 270 C 275.61 270 277.15 271.19 277.15 273.03 L 277.15 300.85 L 270.21 300.85 L 263.57 307.48 L 263.57 307.63 L 255.28 307.63 L 255.28 309.89 L 263.57 309.89 L 263.57 314.41 L 255.28 314.41 L 255.28 316.67 L 263.57 316.67 L 263.57 322.69 Z M 265.84 309.14 L 265.84 308.39 L 271.11 303.12 L 271.87 303.12 L 271.87 309.14 Z M 265.84 322.69 L 265.84 310.64 L 273.38 310.64 L 273.38 303.12 L 291.48 303.12 L 291.48 308.9 C 290.57 308.39 289.26 307.91 287.74 307.94 C 283.97 307.94 280.18 310.95 280.18 315.48 C 280.15 317.92 281.29 319.67 282.35 320.81 L 281.36 322.69 Z M 287.74 320.42 C 290.67 320.42 292.67 318.12 292.67 315.48 C 292.67 312.99 290.64 310.64 287.74 310.64 C 285.01 310.64 282.87 312.92 282.87 315.48 C 282.87 318.59 285.42 320.42 287.74 320.42 Z M 287.74 319.23 C 285.84 319.23 284.09 317.68 284.09 315.48 C 284.09 313.64 285.66 311.85 287.74 311.85 C 290.04 311.85 291.48 313.73 291.48 315.48 C 291.48 317.58 289.86 319.23 287.74 319.23 Z M 287.74 318.47 C 289.44 318.47 290.72 317.16 290.72 315.48 C 290.72 314.13 289.63 312.59 287.74 312.59 C 286.48 312.59 284.84 313.58 284.84 315.48 C 284.84 317.09 286.1 318.47 287.74 318.47 Z M 280.7 327.43 L 284.4 320.51 C 282.83 319.39 281.82 317.67 281.82 315.48 C 281.82 312.09 284.72 309.52 287.74 309.52 C 291.52 309.52 293.89 312.55 293.89 315.48 C 293.9 317.77 292.75 319.27 291.49 320.36 L 296 326.9 L 292.45 326.08 L 290.5 328.79 L 287.74 321.71 L 285.9 329 L 283.85 326.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 336px; margin-left: 273px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="273" y="348" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="410" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 111.87 419.03 L 111.87 416.77 L 95.28 416.77 L 95.28 419.03 Z M 90 462.69 L 90 412.94 C 90 411.27 91.51 410 92.92 410 L 114.19 410 C 115.61 410 117.15 411.19 117.15 413.03 L 117.15 440.85 L 110.21 440.85 L 103.57 447.48 L 103.57 447.63 L 95.28 447.63 L 95.28 449.89 L 103.57 449.89 L 103.57 454.41 L 95.28 454.41 L 95.28 456.67 L 103.57 456.67 L 103.57 462.69 Z M 105.84 449.14 L 105.84 448.39 L 111.11 443.12 L 111.87 443.12 L 111.87 449.14 Z M 105.84 462.69 L 105.84 450.64 L 113.38 450.64 L 113.38 443.12 L 131.48 443.12 L 131.48 448.9 C 130.57 448.39 129.26 447.91 127.74 447.94 C 123.97 447.94 120.18 450.95 120.18 455.48 C 120.15 457.92 121.29 459.67 122.35 460.81 L 121.36 462.69 Z M 127.74 460.42 C 130.67 460.42 132.67 458.12 132.67 455.48 C 132.67 452.99 130.64 450.64 127.74 450.64 C 125.01 450.64 122.87 452.92 122.87 455.48 C 122.87 458.59 125.42 460.42 127.74 460.42 Z M 127.74 459.23 C 125.84 459.23 124.09 457.68 124.09 455.48 C 124.09 453.64 125.66 451.85 127.74 451.85 C 130.04 451.85 131.48 453.73 131.48 455.48 C 131.48 457.58 129.86 459.23 127.74 459.23 Z M 127.74 458.47 C 129.44 458.47 130.72 457.16 130.72 455.48 C 130.72 454.13 129.63 452.59 127.74 452.59 C 126.48 452.59 124.84 453.58 124.84 455.48 C 124.84 457.09 126.1 458.47 127.74 458.47 Z M 120.7 467.43 L 124.4 460.51 C 122.83 459.39 121.82 457.67 121.82 455.48 C 121.82 452.09 124.72 449.52 127.74 449.52 C 131.52 449.52 133.89 452.55 133.89 455.48 C 133.9 457.77 132.75 459.27 131.49 460.36 L 136 466.9 L 132.45 466.08 L 130.5 468.79 L 127.74 461.71 L 125.9 469 L 123.85 466.89 Z" fill="#505050" stroke="none" pointer-events="all"/&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-end; width: 1px; height: 1px; padding-top: 440px; margin-left: 88px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Root CA&lt;br /&gt;(Offline)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="88" y="443" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Root CA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="610" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 111.87 619.03 L 111.87 616.77 L 95.28 616.77 L 95.28 619.03 Z M 90 662.69 L 90 612.94 C 90 611.27 91.51 610 92.92 610 L 114.19 610 C 115.61 610 117.15 611.19 117.15 613.03 L 117.15 640.85 L 110.21 640.85 L 103.57 647.48 L 103.57 647.63 L 95.28 647.63 L 95.28 649.89 L 103.57 649.89 L 103.57 654.41 L 95.28 654.41 L 95.28 656.67 L 103.57 656.67 L 103.57 662.69 Z M 105.84 649.14 L 105.84 648.39 L 111.11 643.12 L 111.87 643.12 L 111.87 649.14 Z M 105.84 662.69 L 105.84 650.64 L 113.38 650.64 L 113.38 643.12 L 131.48 643.12 L 131.48 648.9 C 130.57 648.39 129.26 647.91 127.74 647.94 C 123.97 647.94 120.18 650.95 120.18 655.48 C 120.15 657.92 121.29 659.67 122.35 660.81 L 121.36 662.69 Z M 127.74 660.42 C 130.67 660.42 132.67 658.12 132.67 655.48 C 132.67 652.99 130.64 650.64 127.74 650.64 C 125.01 650.64 122.87 652.92 122.87 655.48 C 122.87 658.59 125.42 660.42 127.74 660.42 Z M 127.74 659.23 C 125.84 659.23 124.09 657.68 124.09 655.48 C 124.09 653.64 125.66 651.85 127.74 651.85 C 130.04 651.85 131.48 653.73 131.48 655.48 C 131.48 657.58 129.86 659.23 127.74 659.23 Z M 127.74 658.47 C 129.44 658.47 130.72 657.16 130.72 655.48 C 130.72 654.13 129.63 652.59 127.74 652.59 C 126.48 652.59 124.84 653.58 124.84 655.48 C 124.84 657.09 126.1 658.47 127.74 658.47 Z M 120.7 667.43 L 124.4 660.51 C 122.83 659.39 121.82 657.67 121.82 655.48 C 121.82 652.09 124.72 649.52 127.74 649.52 C 131.52 649.52 133.89 652.55 133.89 655.48 C 133.9 657.77 132.75 659.27 131.49 660.36 L 136 666.9 L 132.45 666.08 L 130.5 668.79 L 127.74 661.71 L 125.9 669 L 123.85 666.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 676px; margin-left: 113px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="113" y="688" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="170" y="610" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 191.87 619.03 L 191.87 616.77 L 175.28 616.77 L 175.28 619.03 Z M 170 662.69 L 170 612.94 C 170 611.27 171.51 610 172.92 610 L 194.19 610 C 195.61 610 197.15 611.19 197.15 613.03 L 197.15 640.85 L 190.21 640.85 L 183.57 647.48 L 183.57 647.63 L 175.28 647.63 L 175.28 649.89 L 183.57 649.89 L 183.57 654.41 L 175.28 654.41 L 175.28 656.67 L 183.57 656.67 L 183.57 662.69 Z M 185.84 649.14 L 185.84 648.39 L 191.11 643.12 L 191.87 643.12 L 191.87 649.14 Z M 185.84 662.69 L 185.84 650.64 L 193.38 650.64 L 193.38 643.12 L 211.48 643.12 L 211.48 648.9 C 210.57 648.39 209.26 647.91 207.74 647.94 C 203.97 647.94 200.18 650.95 200.18 655.48 C 200.15 657.92 201.29 659.67 202.35 660.81 L 201.36 662.69 Z M 207.74 660.42 C 210.67 660.42 212.67 658.12 212.67 655.48 C 212.67 652.99 210.64 650.64 207.74 650.64 C 205.01 650.64 202.87 652.92 202.87 655.48 C 202.87 658.59 205.42 660.42 207.74 660.42 Z M 207.74 659.23 C 205.84 659.23 204.09 657.68 204.09 655.48 C 204.09 653.64 205.66 651.85 207.74 651.85 C 210.04 651.85 211.48 653.73 211.48 655.48 C 211.48 657.58 209.86 659.23 207.74 659.23 Z M 207.74 658.47 C 209.44 658.47 210.72 657.16 210.72 655.48 C 210.72 654.13 209.63 652.59 207.74 652.59 C 206.48 652.59 204.84 653.58 204.84 655.48 C 204.84 657.09 206.1 658.47 207.74 658.47 Z M 200.7 667.43 L 204.4 660.51 C 202.83 659.39 201.82 657.67 201.82 655.48 C 201.82 652.09 204.72 649.52 207.74 649.52 C 211.52 649.52 213.89 652.55 213.89 655.48 C 213.9 657.77 212.75 659.27 211.49 660.36 L 216 666.9 L 212.45 666.08 L 210.5 668.79 L 207.74 661.71 L 205.9 669 L 203.85 666.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 676px; margin-left: 193px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="193" y="688" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="296" y="610" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 317.87 619.03 L 317.87 616.77 L 301.28 616.77 L 301.28 619.03 Z M 296 662.69 L 296 612.94 C 296 611.27 297.51 610 298.92 610 L 320.19 610 C 321.61 610 323.15 611.19 323.15 613.03 L 323.15 640.85 L 316.21 640.85 L 309.57 647.48 L 309.57 647.63 L 301.28 647.63 L 301.28 649.89 L 309.57 649.89 L 309.57 654.41 L 301.28 654.41 L 301.28 656.67 L 309.57 656.67 L 309.57 662.69 Z M 311.84 649.14 L 311.84 648.39 L 317.11 643.12 L 317.87 643.12 L 317.87 649.14 Z M 311.84 662.69 L 311.84 650.64 L 319.38 650.64 L 319.38 643.12 L 337.48 643.12 L 337.48 648.9 C 336.57 648.39 335.26 647.91 333.74 647.94 C 329.97 647.94 326.18 650.95 326.18 655.48 C 326.15 657.92 327.29 659.67 328.35 660.81 L 327.36 662.69 Z M 333.74 660.42 C 336.67 660.42 338.67 658.12 338.67 655.48 C 338.67 652.99 336.64 650.64 333.74 650.64 C 331.01 650.64 328.87 652.92 328.87 655.48 C 328.87 658.59 331.42 660.42 333.74 660.42 Z M 333.74 659.23 C 331.84 659.23 330.09 657.68 330.09 655.48 C 330.09 653.64 331.66 651.85 333.74 651.85 C 336.04 651.85 337.48 653.73 337.48 655.48 C 337.48 657.58 335.86 659.23 333.74 659.23 Z M 333.74 658.47 C 335.44 658.47 336.72 657.16 336.72 655.48 C 336.72 654.13 335.63 652.59 333.74 652.59 C 332.48 652.59 330.84 653.58 330.84 655.48 C 330.84 657.09 332.1 658.47 333.74 658.47 Z M 326.7 667.43 L 330.4 660.51 C 328.83 659.39 327.82 657.67 327.82 655.48 C 327.82 652.09 330.72 649.52 333.74 649.52 C 337.52 649.52 339.89 652.55 339.89 655.48 C 339.9 657.77 338.75 659.27 337.49 660.36 L 342 666.9 L 338.45 666.08 L 336.5 668.79 L 333.74 661.71 L 331.9 669 L 329.85 666.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 676px; margin-left: 319px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="319" y="688" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="510" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 111.87 519.03 L 111.87 516.77 L 95.28 516.77 L 95.28 519.03 Z M 90 562.69 L 90 512.94 C 90 511.27 91.51 510 92.92 510 L 114.19 510 C 115.61 510 117.15 511.19 117.15 513.03 L 117.15 540.85 L 110.21 540.85 L 103.57 547.48 L 103.57 547.63 L 95.28 547.63 L 95.28 549.89 L 103.57 549.89 L 103.57 554.41 L 95.28 554.41 L 95.28 556.67 L 103.57 556.67 L 103.57 562.69 Z M 105.84 549.14 L 105.84 548.39 L 111.11 543.12 L 111.87 543.12 L 111.87 549.14 Z M 105.84 562.69 L 105.84 550.64 L 113.38 550.64 L 113.38 543.12 L 131.48 543.12 L 131.48 548.9 C 130.57 548.39 129.26 547.91 127.74 547.94 C 123.97 547.94 120.18 550.95 120.18 555.48 C 120.15 557.92 121.29 559.67 122.35 560.81 L 121.36 562.69 Z M 127.74 560.42 C 130.67 560.42 132.67 558.12 132.67 555.48 C 132.67 552.99 130.64 550.64 127.74 550.64 C 125.01 550.64 122.87 552.92 122.87 555.48 C 122.87 558.59 125.42 560.42 127.74 560.42 Z M 127.74 559.23 C 125.84 559.23 124.09 557.68 124.09 555.48 C 124.09 553.64 125.66 551.85 127.74 551.85 C 130.04 551.85 131.48 553.73 131.48 555.48 C 131.48 557.58 129.86 559.23 127.74 559.23 Z M 127.74 558.47 C 129.44 558.47 130.72 557.16 130.72 555.48 C 130.72 554.13 129.63 552.59 127.74 552.59 C 126.48 552.59 124.84 553.58 124.84 555.48 C 124.84 557.09 126.1 558.47 127.74 558.47 Z M 120.7 567.43 L 124.4 560.51 C 122.83 559.39 121.82 557.67 121.82 555.48 C 121.82 552.09 124.72 549.52 127.74 549.52 C 131.52 549.52 133.89 552.55 133.89 555.48 C 133.9 557.77 132.75 559.27 131.49 560.36 L 136 566.9 L 132.45 566.08 L 130.5 568.79 L 127.74 561.71 L 125.9 569 L 123.85 566.89 Z" fill="#505050" stroke="none" pointer-events="all"/&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-end; width: 1px; height: 1px; padding-top: 540px; margin-left: 88px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Intermediate CA&lt;br /&gt;(Offline)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="88" y="543" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Intermed&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="296" y="510" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 317.87 519.03 L 317.87 516.77 L 301.28 516.77 L 301.28 519.03 Z M 296 562.69 L 296 512.94 C 296 511.27 297.51 510 298.92 510 L 320.19 510 C 321.61 510 323.15 511.19 323.15 513.03 L 323.15 540.85 L 316.21 540.85 L 309.57 547.48 L 309.57 547.63 L 301.28 547.63 L 301.28 549.89 L 309.57 549.89 L 309.57 554.41 L 301.28 554.41 L 301.28 556.67 L 309.57 556.67 L 309.57 562.69 Z M 311.84 549.14 L 311.84 548.39 L 317.11 543.12 L 317.87 543.12 L 317.87 549.14 Z M 311.84 562.69 L 311.84 550.64 L 319.38 550.64 L 319.38 543.12 L 337.48 543.12 L 337.48 548.9 C 336.57 548.39 335.26 547.91 333.74 547.94 C 329.97 547.94 326.18 550.95 326.18 555.48 C 326.15 557.92 327.29 559.67 328.35 560.81 L 327.36 562.69 Z M 333.74 560.42 C 336.67 560.42 338.67 558.12 338.67 555.48 C 338.67 552.99 336.64 550.64 333.74 550.64 C 331.01 550.64 328.87 552.92 328.87 555.48 C 328.87 558.59 331.42 560.42 333.74 560.42 Z M 333.74 559.23 C 331.84 559.23 330.09 557.68 330.09 555.48 C 330.09 553.64 331.66 551.85 333.74 551.85 C 336.04 551.85 337.48 553.73 337.48 555.48 C 337.48 557.58 335.86 559.23 333.74 559.23 Z M 333.74 558.47 C 335.44 558.47 336.72 557.16 336.72 555.48 C 336.72 554.13 335.63 552.59 333.74 552.59 C 332.48 552.59 330.84 553.58 330.84 555.48 C 330.84 557.09 332.1 558.47 333.74 558.47 Z M 326.7 567.43 L 330.4 560.51 C 328.83 559.39 327.82 557.67 327.82 555.48 C 327.82 552.09 330.72 549.52 333.74 549.52 C 337.52 549.52 339.89 552.55 339.89 555.48 C 339.9 557.77 338.75 559.27 337.49 560.36 L 342 566.9 L 338.45 566.08 L 336.5 568.79 L 333.74 561.71 L 331.9 569 L 329.85 566.89 Z" fill="#505050" stroke="none" pointer-events="all"/&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-end; width: 1px; height: 1px; padding-top: 540px; margin-left: 294px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: nowrap;"&gt;Intermediate CA&lt;br /&gt;(Offline)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="294" y="543" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Intermed&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="366" y="610" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 387.87 619.03 L 387.87 616.77 L 371.28 616.77 L 371.28 619.03 Z M 366 662.69 L 366 612.94 C 366 611.27 367.51 610 368.92 610 L 390.19 610 C 391.61 610 393.15 611.19 393.15 613.03 L 393.15 640.85 L 386.21 640.85 L 379.57 647.48 L 379.57 647.63 L 371.28 647.63 L 371.28 649.89 L 379.57 649.89 L 379.57 654.41 L 371.28 654.41 L 371.28 656.67 L 379.57 656.67 L 379.57 662.69 Z M 381.84 649.14 L 381.84 648.39 L 387.11 643.12 L 387.87 643.12 L 387.87 649.14 Z M 381.84 662.69 L 381.84 650.64 L 389.38 650.64 L 389.38 643.12 L 407.48 643.12 L 407.48 648.9 C 406.57 648.39 405.26 647.91 403.74 647.94 C 399.97 647.94 396.18 650.95 396.18 655.48 C 396.15 657.92 397.29 659.67 398.35 660.81 L 397.36 662.69 Z M 403.74 660.42 C 406.67 660.42 408.67 658.12 408.67 655.48 C 408.67 652.99 406.64 650.64 403.74 650.64 C 401.01 650.64 398.87 652.92 398.87 655.48 C 398.87 658.59 401.42 660.42 403.74 660.42 Z M 403.74 659.23 C 401.84 659.23 400.09 657.68 400.09 655.48 C 400.09 653.64 401.66 651.85 403.74 651.85 C 406.04 651.85 407.48 653.73 407.48 655.48 C 407.48 657.58 405.86 659.23 403.74 659.23 Z M 403.74 658.47 C 405.44 658.47 406.72 657.16 406.72 655.48 C 406.72 654.13 405.63 652.59 403.74 652.59 C 402.48 652.59 400.84 653.58 400.84 655.48 C 400.84 657.09 402.1 658.47 403.74 658.47 Z M 396.7 667.43 L 400.4 660.51 C 398.83 659.39 397.82 657.67 397.82 655.48 C 397.82 652.09 400.72 649.52 403.74 649.52 C 407.52 649.52 409.89 652.55 409.89 655.48 C 409.9 657.77 408.75 659.27 407.49 660.36 L 412 666.9 L 408.45 666.08 L 406.5 668.79 L 403.74 661.71 L 401.9 669 L 399.85 666.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 676px; margin-left: 389px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="389" y="688" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="446" y="610" width="46" height="59" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 467.87 619.03 L 467.87 616.77 L 451.28 616.77 L 451.28 619.03 Z M 446 662.69 L 446 612.94 C 446 611.27 447.51 610 448.92 610 L 470.19 610 C 471.61 610 473.15 611.19 473.15 613.03 L 473.15 640.85 L 466.21 640.85 L 459.57 647.48 L 459.57 647.63 L 451.28 647.63 L 451.28 649.89 L 459.57 649.89 L 459.57 654.41 L 451.28 654.41 L 451.28 656.67 L 459.57 656.67 L 459.57 662.69 Z M 461.84 649.14 L 461.84 648.39 L 467.11 643.12 L 467.87 643.12 L 467.87 649.14 Z M 461.84 662.69 L 461.84 650.64 L 469.38 650.64 L 469.38 643.12 L 487.48 643.12 L 487.48 648.9 C 486.57 648.39 485.26 647.91 483.74 647.94 C 479.97 647.94 476.18 650.95 476.18 655.48 C 476.15 657.92 477.29 659.67 478.35 660.81 L 477.36 662.69 Z M 483.74 660.42 C 486.67 660.42 488.67 658.12 488.67 655.48 C 488.67 652.99 486.64 650.64 483.74 650.64 C 481.01 650.64 478.87 652.92 478.87 655.48 C 478.87 658.59 481.42 660.42 483.74 660.42 Z M 483.74 659.23 C 481.84 659.23 480.09 657.68 480.09 655.48 C 480.09 653.64 481.66 651.85 483.74 651.85 C 486.04 651.85 487.48 653.73 487.48 655.48 C 487.48 657.58 485.86 659.23 483.74 659.23 Z M 483.74 658.47 C 485.44 658.47 486.72 657.16 486.72 655.48 C 486.72 654.13 485.63 652.59 483.74 652.59 C 482.48 652.59 480.84 653.58 480.84 655.48 C 480.84 657.09 482.1 658.47 483.74 658.47 Z M 476.7 667.43 L 480.4 660.51 C 478.83 659.39 477.82 657.67 477.82 655.48 C 477.82 652.09 480.72 649.52 483.74 649.52 C 487.52 649.52 489.89 652.55 489.89 655.48 C 489.9 657.77 488.75 659.27 487.49 660.36 L 492 666.9 L 488.45 666.08 L 486.5 668.79 L 483.74 661.71 L 481.9 669 L 479.85 666.89 Z" fill="#0050ef" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 676px; margin-left: 469px;"&gt;&lt;div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Issuing CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="469" y="688" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Issuing&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 113 469 L 113 503.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 113 508.88 L 109.5 501.88 L 113 503.63 L 116.5 501.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 136 439.56 L 319 440 L 319 503.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 319 508.88 L 315.5 501.88 L 319 503.63 L 322.5 501.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 113 569 L 113 603.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 113 608.88 L 109.5 601.88 L 113 603.63 L 116.5 601.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 319 569 L 319 603.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 319 608.88 L 315.5 601.88 L 319 603.63 L 322.5 601.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 339.65 569 L 364.7 604.78" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 367.71 609.08 L 360.83 605.36 L 364.7 604.78 L 366.56 601.34 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 342 554.83 L 440.7 620.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 445.07 623.55 L 437.3 622.58 L 440.7 620.63 L 441.19 616.75 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 136 568.25 L 166.02 605.78" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 169.3 609.88 L 162.2 606.6 L 166.02 605.78 L 167.66 602.22 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 113 229 L 113 263.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 113 268.88 L 109.5 261.88 L 113 263.63 L 116.5 261.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 136 228.25 L 166.02 265.78" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 169.3 269.88 L 162.2 266.6 L 166.02 265.78 L 167.66 262.22 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 136 213.88 L 244.6 281.75" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 249.05 284.53 L 241.26 283.79 L 244.6 281.75 L 244.97 277.85 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;rect x="362" y="49" width="130" height="30" fill="none" stroke="none" pointer-events="all"/&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: 128px; height: 1px; padding-top: 64px; margin-left: 363px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"&gt;Single-Tier&lt;br /&gt;CA Model&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="427" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle" font-weight="bold"&gt;Single-Tier&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="350" y="229" width="130" height="30" fill="none" stroke="none" pointer-events="all"/&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: 128px; height: 1px; padding-top: 244px; margin-left: 351px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"&gt;Two-Tier &lt;br /&gt;CA Model&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="415" y="249" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle" font-weight="bold"&gt;Two-Tier&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="510" width="130" height="30" fill="none" stroke="none" pointer-events="all"/&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: 128px; height: 1px; padding-top: 525px; margin-left: 361px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"&gt;Three-Tier &lt;br /&gt;CA Model&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="425" y="530" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle" font-weight="bold"&gt;Three-Tier&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/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 two-tier hierarchy introduces another problem. The root CA also needs to restrict the certificates that its subordinate CAs can issue. For example, one issuing CA can only issue certificates in the *.dev.digihunch.com domain, and another CA can only issue *.ops.digihunch.com. No subordinate CA shall issue &amp;#8220;rogue&amp;#8221; certificate beyond their authorized scope. In order to enforce issue boundaries on issuing CAs, RFC 5280 defines multiple ways to express constraints, such as basic constraints (including path length), name constraints, policy constraints, and EKU.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a three-tier CA hierarchy, the top-level is still a root CA that stays offline. One level below, is a layer of CAs that also stay offline and we refer to them as intermediate CAs. Going down one more level down, there are the issuing CAs for end-entity certificates. Oftentimes, we use the intermediate CAs as policy CAs where we introduce restrictions to the subordinates. In this model, the root CA can remain offline nearly all the time because we can issue CRLs at the intermediate level. This is more flexible, but also more management overhead.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Implementation Options&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The two-tier hierarchy is good in most scenarios but the three-tier hierarchy is also common for large organizations. The next consideration is implementation strategy. Take two-tier model for example. We can think about these options:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Implement a self-managed PKI, with an internal root CA. The internal subordinate CAs are the issuing CAs. They are chained to the internal root CA.&lt;/li&gt;&#10;&lt;li&gt;Implement a self-managed PKI, with its certificate purchased from a commercial CA. The internal subordinate CAs are the issuing CAs. They are chained to the external root CA, as the TPP (trusted third party). The external CA may or may not be a public root CA.&lt;/li&gt;&#10;&lt;li&gt;Purchase certificates from a commercial CA that are chained to a public root CA&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Option 3 essentially delegates the PKI to a commercial provider. This is usually not a favourable option due to the hefty charge and minimal control. In option 2, when the third party is a public root CA, you can have all your certificates with public trust, although this is a pricey option too. Even if the third-party isn&amp;#8217;t a public root CA, there is still a benefit of delegating the management of Root CA to commercial provider.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Option 1 is for use cases where we need a lot of certificates quick, for example, in agile development iterations. The certificates do not need trust beyond the organization, and the risk of root CA being compromised is manageable. In this option all CAs are private CAs. Option 2 on the other hand, is flexible in terms of trust boundary. You can just rely on the commercial provider as a TTP. If the TPP supports public root CA, you can issue certificates for public facing workload. In other words, you can choose either a private CA or a public CA. When we work with a private CA, we have to import the certificate to the trust stores of the organization.&lt;/p&gt;&#10;&lt;p class="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="571px" viewBox="-0.5 -0.5 571 231" style="max-width:100%;max-height:231px;"&gt;&lt;defs&gt;&lt;style type="text/css"&gt;@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&amp;#xa;&lt;/style&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="570" height="230" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;rect x="30" y="30" width="290" height="140" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&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 flex-start; justify-content: unsafe flex-end; width: 288px; height: 1px; padding-top: 37px; margin-left: 30px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&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; white-space: normal; overflow-wrap: normal;"&gt;CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="318" y="49" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="130" y="40" width="80" height="30" rx="4.5" ry="4.5" fill="#f9f7ed" stroke="#36393d" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 47px; margin-left: 131px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;CAS&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="170" y="59" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;CAS&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="135" y="120" width="70" height="40" rx="6" ry="6" fill="#eeeeee" stroke="#36393d" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 127px; margin-left: 136px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;RA&lt;br /&gt;region-2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="170" y="139" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="40" y="120" width="70" height="40" rx="6" ry="6" fill="#eeeeee" stroke="#36393d" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 127px; margin-left: 41px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;RA&lt;br /&gt;region-1&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="75" y="139" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="230" y="120" width="75" height="40" rx="6" ry="6" fill="#eeeeee" stroke="#36393d" pointer-events="all"/&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 flex-start; justify-content: unsafe center; width: 73px; height: 1px; padding-top: 127px; margin-left: 231px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;RA&lt;br /&gt;region-3&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="268" y="139" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RA&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 150 70 L 75 120" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 190 70 L 267.5 120" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 170 120 L 170 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;rect x="370" y="30" width="180" height="80" fill="#cce5ff" stroke="#36393d" pointer-events="all"/&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: 178px; height: 1px; padding-top: 70px; margin-left: 372px;"&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: all; white-space: normal; overflow-wrap: normal;"&gt;Certificate Authority Service:&lt;br /&gt;&amp;#8211; Holds the CA keys and certs&lt;br style="border-color: var(--border-color);" /&gt;&amp;#8211; signs CSRs from RAs&lt;br /&gt;&amp;#8211; trusts the RAs implicitly&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="372" y="74" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Certificate Authority Service:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="370" y="130" width="180" height="80" fill="#cce5ff" stroke="#36393d" pointer-events="all"/&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: 178px; height: 1px; padding-top: 170px; margin-left: 372px;"&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: all; white-space: normal; overflow-wrap: normal;"&gt;Registration Authority:&lt;br /&gt;&amp;#8211; Authenticates requests&lt;br style="border-color: var(--border-color);" /&gt;&amp;#8211; Relays CSRs and Certs&lt;br style="border-color: var(--border-color);" /&gt;&amp;#8211; Connect to CAS&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="372" y="174" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Registration Authority:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="50" y="180" width="50" height="30" fill="#ffff88" stroke="#36393d" pointer-events="all"/&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: 48px; height: 1px; padding-top: 195px; margin-left: 51px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;local&lt;br /&gt;clients&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="75" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;local&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="145" y="180" width="50" height="30" fill="#ffff88" stroke="#36393d" pointer-events="all"/&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: 48px; height: 1px; padding-top: 195px; margin-left: 146px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;local&lt;br /&gt;clients&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="170" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;local&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="242.5" y="180" width="50" height="30" fill="#ffff88" stroke="#36393d" pointer-events="all"/&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: 48px; height: 1px; padding-top: 195px; margin-left: 244px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: normal; overflow-wrap: normal;"&gt;local&lt;br /&gt;clients&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="268" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;local&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 170 180 L 170 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 75 180 L 75 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 267.5 180 L 267.5 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/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;Some PKI topologies splits a CA into two sub-components: the Certificate Authority Service (CAS, or simply CA) and the Registration Authority (RA), as the diagram above shows. With many locations, each location has an RA that communicates with a with a central CAS. The RAs receive requests from local clients and are responsible for authenticating these requests and pass the validated ones along to the CAS. The validation can take place in ACME protocol. The CAS implicitly trusts RAs and will sign the validated requests from RAs before sending them via API calls. The RA then relays the certificates back to the local requestors. This topology is more scalable with the CAS focusing only on signing and the RAs on validation and passing the requests along. Large organizations may have their central CAS hosted on-prem, and remote RAs in the CSP regions.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Components for X.509 Certificate&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;X.509 is the standard for digital certificate. The X.509 standard has the following most important fields:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Subject: The name of the subject (e.g. a user, service, device), commonly represented as X.500 formate distinguished name (DN). For website, the value can be CN=digihunch.com&lt;/li&gt;&#10;&lt;li&gt;Serial Number: A unique identifier for each certificate that a CA issues.&lt;/li&gt;&#10;&lt;li&gt;Issuer: DN of the CA. For a self-signed root CA, the issuer is the subject. They are different otherwise, such as in subordinate CA certificates and end entity certificates.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These fields were introduced in version 1. In addition, X.509 version 3 certificates introduces extensions that provide additional functionality and features to the certificate. Each extension comes in two flavours: critical and non-critical. A certificate-using system MUST reject the certificate if it encounters a critical extension it does not recognize or a critical extension that contains information that it cannot process. A non-critical extension MAY be ignored if it is not recognized, but MUST be processed if it is recognized.&lt;/p&gt;&#10;&lt;p class="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="851px" viewBox="-0.5 -0.5 851 411" style="max-width:100%;max-height:411px;"&gt;&lt;defs&gt;&lt;style type="text/css"&gt;@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&amp;#xa;&lt;/style&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="850" height="410" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;path d="M 90 40 L 90 10 L 550 10 L 550 40" fill="#cce5ff" stroke="#36393d" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 90 40 L 90 400 L 550 400 L 550 40" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 40 L 550 40" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 70 L 550 70" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 100 L 550 100" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 130 L 550 130" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 160 L 550 160" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 190 L 550 190" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 220 L 550 220" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 250 L 550 250" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 280 L 550 280" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 310 L 550 310" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 340 L 550 340" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 90 370 L 550 370" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;g fill="rgb(0, 0, 0)" font-family="Helvetica" text-anchor="middle" font-size="16px"&gt;&lt;text x="319.5" y="31.5"&gt;X.509 Certificate&lt;/text&gt;&lt;/g&gt;&lt;rect x="90" y="40" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 40 M 550 40 M 550 70 M 90 70" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 55px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Version&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="60" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Version&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="70" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 70 M 550 70 M 550 100 M 90 100" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 85px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Certificate Serial Number&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="90" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Certificate Serial Number&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="100" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 100 M 550 100 M 550 130 M 90 130" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 115px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Certificate Algorithm Identifier for Certificate Issuer&amp;#8217;s Signature&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="120" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Certificate Algorithm Identifier for Certificate Issuer&amp;#8217;s&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="130" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 130 M 550 130 M 550 160 M 90 160" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 145px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Issuer&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="150" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Issuer&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="160" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 160 M 550 160 M 550 190 M 90 190" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 175px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Validity Period&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="180" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Validity Period&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="190" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 190 M 550 190 M 550 220 M 90 220" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 205px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subject&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="210" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Subject&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="220" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 220 M 550 220 M 550 250 M 90 250" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 235px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subject Public-Key Information (Algorithm Identifier and Value)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="240" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Subject Public-Key Information (Algorithm Identifier and&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="250" width="460" height="30" fill="#eeeeee" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 250 M 550 250 M 550 280 M 90 280" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 265px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Issuer Unique Identifier&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="270" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Issuer Unique Identifier&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="280" width="460" height="30" fill="#eeeeee" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 280 M 550 280 M 550 310 M 90 310" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 295px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subject Unique Identifier&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="300" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Subject Unique Identifier&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="310" width="460" height="30" fill="#eeeeee" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 310 M 550 310 M 550 340 M 90 340" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 325px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Extensions&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="330" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Extensions&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="340" width="460" height="30" fill="#f9f7ed" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 340 M 550 340 M 550 370 M 90 370" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 355px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Certificate Authority&amp;#8217;s Digital Signature&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="360" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Certificate Authority&amp;#8217;s Digital Signature&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="90" y="370" width="460" height="30" fill="#eeeeee" stroke="none" pointer-events="all"/&gt;&lt;path d="M 90 370 M 550 370 M 550 400 M 90 400" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 458px; height: 1px; padding-top: 385px; margin-left: 92px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Extension Fields &amp;#8230;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="92" y="390" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Extension Fields &amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 250 L 75 250 Q 70 250 70 260 L 70 285 Q 70 295 65 295 L 62.5 295 Q 60 295 65 295 L 67.5 295 Q 70 295 70 305 L 70 330 Q 70 340 75 340 L 80 340" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;rect x="0" y="280" width="60" height="30" fill="none" stroke="none" pointer-events="all"/&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: 58px; height: 1px; padding-top: 295px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 15px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Optional&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="30" y="300" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="15px" text-anchor="middle"&gt;Optional&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 70 385 L 90 385" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;rect x="0" y="370" width="60" height="30" fill="none" stroke="none" pointer-events="all"/&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: 58px; height: 1px; padding-top: 385px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 15px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Optional&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="30" y="390" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="15px" text-anchor="middle"&gt;Optional&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="100" width="250" height="300" fill="#eeeeee" stroke="none" pointer-events="none"/&gt;&lt;path d="M 590 100 L 840 100 L 840 400 L 590 400 L 590 100" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 130 L 840 130" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 160 L 840 160" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 190 L 840 190" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 220 L 840 220" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 250 L 840 250" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 280 L 840 280" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 310 L 840 310" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 340 L 840 340" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;path d="M 590 370 L 840 370" fill="none" stroke="#36393d" stroke-miterlimit="10" pointer-events="none"/&gt;&lt;rect x="590" y="100" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 100 M 840 100 M 840 130 M 590 130" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 115px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subject Alternative Name (SAN)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="120" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Subject Alternative Name (SAN)&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="130" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 130 M 840 130 M 840 160 M 590 160" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 145px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Key Usage&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="150" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Key Usage&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="160" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 160 M 840 160 M 840 190 M 590 190" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 175px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Basic Constraints&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="180" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Basic Constraints&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="190" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 190 M 840 190 M 840 220 M 590 220" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 205px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Name Constraints&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="210" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Name Constraints&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="220" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 220 M 840 220 M 840 250 M 590 250" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 235px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;CRL distribution Points (CDP)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="240" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;CRL distribution Points (CDP)&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="250" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 250 M 840 250 M 840 280 M 590 280" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 265px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Authority Information Access (AIA)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="270" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Authority Information Access (A&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="280" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 280 M 840 280 M 840 310 M 590 310" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 295px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subject Key Identification (SKI)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="300" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Subject Key Identification (SKI)&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="310" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 310 M 840 310 M 840 340 M 590 340" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 325px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Authority Key Identification (AKI)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="330" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Authority Key Identification (A&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="340" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 340 M 840 340 M 840 370 M 590 370" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 355px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Extended Key Usage (EKU)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="360" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Extended Key Usage (EKU)&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="370" width="250" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 590 370 M 840 370 M 840 400 M 590 400" fill="none" stroke="#36393d" stroke-linecap="square" stroke-miterlimit="10" pointer-events="all"/&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: 248px; height: 1px; padding-top: 385px; margin-left: 592px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 26px; overflow: hidden;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Certificate Policies&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="592" y="390" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px"&gt;Certificate Policies&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="590" y="70" width="160" height="30" fill="none" stroke="none" pointer-events="all"/&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: 158px; height: 1px; padding-top: 85px; margin-left: 591px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Common Extensions&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="670" y="90" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle"&gt;Common Extensions&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 385 L 590 100.99" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 550 385 L 589 397.99" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/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;Here are some common extensions that a lot of implementations use:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Subject Alternative Name (SAN): only used in end entity certificates, not in CA certificates. The format of SAN is flexible and it does not have to be X.500 DN. For website certificate, we often place alternative DNS names here.&lt;/li&gt;&#10;&lt;li&gt;Key Usage: The intended scope of usage for a private key is specified through the Key Usage and Extended Key Usage (EKU) extensions in the associated certificate. Example: &amp;#8220;Certificate Sign, CRL Sign&amp;#8221;&lt;/li&gt;&#10;&lt;li&gt;Basic Constraints: Used to distinguish between end-entity cert and CA cert. You should also specify path length. The value can be: &amp;#8220;CA:TRUE, pathlen:1&amp;#8221;. If the path length constraint is 0, the CA may have one more level of subordinate CA. But these subordinate CAs must have path length of 0 on their own certs, and cannot extend one more level as their own subordinates. Those subordinate CAs can only issue end-entity certificates. If the path length value is none, then there is no restriction on the levels of subordinate CAs.&lt;/li&gt;&#10;&lt;li&gt;Name Constraints: for CA certs only, defined in RFC5280, to limit the scope to certain names on the certificate that the CA issues. Client must verify that a certificate is allowed to be signed by CA.&lt;/li&gt;&#10;&lt;li&gt;CDP (CRL distribution points): URL(s) where the application or service can retrieve the certificate revocation list (CRL).&lt;/li&gt;&#10;&lt;li&gt;AIA (Authority Information Access): URL(s) where the application or service can retrieve the revocation list for CA&amp;#8217;s certificate&lt;/li&gt;&#10;&lt;li&gt;SKI (Subject Key Identifier): the SHA-1 hash of the subject&amp;#8217;s public key&lt;/li&gt;&#10;&lt;li&gt;AKI (Authority Key Identifier): the SHA-1 hash of the issuer&amp;#8217;s public key&lt;/li&gt;&#10;&lt;li&gt;EKU (Extended Key Usage, also Enhanced Key Usage): an object identifier (OID) for each application or service a certificate can be used for. It needs to align with Key Usage&lt;/li&gt;&#10;&lt;li&gt;Certificate Policy: reference to the certification practice statement (CPS) of the issuer. During exchange, any relying party can access the assurance level associated with the certificate, and decide on the level of trust to put in the certificate.&lt;/li&gt;&#10;&lt;li&gt;Policy Constraints: for path validation, it can be used to prohibit policy mapping or to require that each certificate in a path contain an acceptable policy identifier.&lt;/li&gt;&#10;&lt;li&gt;Policy Mappings: in CA certificates to restrict the certificates that the CA can issue.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This isn&amp;#8217;t a complete list. Plus, X.509 V3 also supports custom extensions. &lt;a href="https://en.wikipedia.org/wiki/X.509#Sample_X.509_certificates"&gt;Here&lt;/a&gt; is a sample certificate. In real life, the way each implementation uses these extensions may vary slightly, so interoperability issues between PKIs exist. When designing your own PKI, it is important to examine the usage of extensions to comply with the standard. For example, Microsoft has the following &lt;a href="https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn786428(v=ws.11)#constraining-ca-certificates"&gt;recommendations&lt;/a&gt; to restrict certificates: &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;For subordinate CA certificates, the Basic Constraints extension should be present and marked as critical&lt;/li&gt;&#10;&lt;li&gt;The cA field should be set to TRUE&lt;/li&gt;&#10;&lt;li&gt;The pathLenConstraint field should be set to the minimum value required to enable the business scenario (i.e. 0 if that CA will issue certificates only to End Entities)&lt;/li&gt;&#10;&lt;li&gt;The EKU extension should be present and contain the minimum set of EKU object identifiers (OIDs) to enable the business scenario. Furthermore, the anyExtendedKeyUsage OID (2.5.29.37.0) should not be specified.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This post discussed the basic concepts in public key infrastructure. In the next post, I&amp;#8217;ll cover the automation of certificate issuance.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2024/01/workload-identity-on-kubernetes-2-of-2-eks-and-rosa-on-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Workload Identity on Kubernetes 2 of 2 – EKS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/03/public-key-infrastructure-2-of-3-certificate-automation/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Public Key Infrastructure 2 of 3 – Certificate Automation&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Workload Identity on Kubernetes 2 of 2 – EKS</title><link>https://www.digihunch.com/2024/01/workload-identity-on-kubernetes-2-of-2-eks-and-rosa-on-aws/</link><pubDate>Mon, 08 Jan 2024 00:05:00 -0400</pubDate><guid>https://www.digihunch.com/2024/01/workload-identity-on-kubernetes-2-of-2-eks-and-rosa-on-aws/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-workload-identity-eks.webp" alt="Featured image of post Workload Identity on Kubernetes 2 of 2 – EKS" /&gt;&lt;p class="wp-block-paragraph"&gt;I discussed in my &lt;a href="https://www.digihunch.com/2023/12/workload-identity-on-kubernetes-1-of-2-aks/"&gt;previous post&lt;/a&gt; on workload identity and dived into how it works in AKS (Azure Kubernetes Service). In this post I will continue the topic with AWS as the example. From the perspective of CSP, we consider any running process on the cloud resource as workload. Therefore, I&amp;#8217;ll start with control plan and node identities. From the perspective of a Kubernetes platform, the term workload mostly refers to applications running in Pods. So later in this article I&amp;#8217;ll distinguish two mechanisms for Pod Identity: IRSA and EKS Pod Identity. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;EKS Control Plane and Node Identity&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS directly associate an IAM role with EKS control plane and an IAM role with each node group. We don&amp;#8217;t need an extra step of assigning a &amp;#8220;managed identity&amp;#8221; (as in Azure) to a cluster or to a node group ( and then bind a role to the identity). You can find this pattern from Terraform &lt;a href="https://github.com/digihunch/cloudkube/tree/main/eks"&gt;code&lt;/a&gt;. Each &lt;code&gt;aws_eks_node_group&lt;/code&gt; resource has a &lt;code&gt;node_role_arn&lt;/code&gt; attribute to link to its IAM role, and a &lt;code&gt;cluster_name&lt;/code&gt; attribute to link to the cluster. Each &lt;code&gt;aws_eks_cluster&lt;/code&gt; resource has a &lt;code&gt;role_arn&lt;/code&gt; attribute for cluster&amp;#8217;s permission. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The cluster&amp;#8217;s IAM role is usually bound to managed policies like &lt;code&gt;AmazonEKSVPCResourceController&lt;/code&gt; and &lt;code&gt;AmazonEKSClusterPolicy&lt;/code&gt;. The IAM role that is assigned to the node group is the exact IAM role of the instance profile of each node. The kubelet process on the nodes is the main user of this role and the permission should not be broader than what it needs to do. This role usually has a few managed policies such as &lt;code&gt;AmazonEKSWorkerNodePolicy&lt;/code&gt;, &lt;code&gt;AmazonEKS_CNI_Policy&lt;/code&gt;, &lt;code&gt;AmazonSSMManagedInstanceCore&lt;/code&gt; and &lt;code&gt;AmazonEC2ContainerRegistryReadOnly&lt;/code&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The node role applies to self-managed node and managed node. When using Fargate to provide computing capacity, each Fargate profile will use its own IAM role, to connect to the cluster and pull container images. This IAM role is known as Pod Execution Role. For a private cluster, the place to run the command would be a bastion host with connectivity to the cluster&amp;#8217;s API endpoint. Refer to &lt;a href="https://www.digihunch.com/2023/06/connect-kubectl-to-private-kubernetes-cluster-in-eks-and-aks/"&gt;this&lt;/a&gt; post about the connectivity to private cluster.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;IAM Role for Service Account (IRSA)&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When AWS launched EKS in 2018, &lt;a href="https://github.com/uswitch/kiam"&gt;Kiam&lt;/a&gt; was a popular open-source project to grant Pods access to AWS resources. In 2019, AWS &lt;a href="https://aws.amazon.com/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/"&gt;introduced the official mechanism&lt;/a&gt;, IRSA (IAM Role for Service Account). IRSA ties a Kubernetes identity (in the form of Service Account) to an IAM role in AWS. IAM allows creation of web identity based on OIDC. EKS can act as an OIDC issuer. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="545" src="https://www.digihunch.com/wp-content/uploads/2024/01/eks-oidc.webp" alt="" class="wp-image-12976" style="width:458px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/01/eks-oidc.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/01/eks-oidc-300x160.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/01/eks-oidc-768x409.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This requires a few points of configurations, via the cluster API and via cloud the endpoint. The &lt;code&gt;eksctl&lt;/code&gt; utility makes it simple with two 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;$ eksctl utils associate-iam-oidc-provider &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; --cluster $CLUSTER_NAME &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; --approve&#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;$ eksctl create iamserviceaccount &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; --cluster&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$CLUSTER_NAME &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; --namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;kube-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; --name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;aws-load-balancer-controller &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; --role-name AmazonEKSLoadBalancerControllerRole &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; --attach-policy-arn&lt;span style="color:#f92672"&gt;=&lt;/span&gt;arn:aws:iam::112233445566:policy/AWSLoadBalancerControllerIAMPolicy &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; --approve&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt; The first command creates an OIDC web identity integrated with the EKS cluster, and the second creates a Service Account in Kubernetes and links it to the identity, and assign an IAM policy. These two commands must run under certain conditions. The AWS CLI identity for first command requires the permission to add OIDC provider. The second needs the permission to create an IAM role. In addition, it requires kube API access to the cluster. So the command needs to run from an environment that can access both the cluster&amp;#8217;s API and AWS API. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The IAM identity provider is somewhat similar to a managed identity with OIDC federated credential in Azure. However, unlike managed identity, here in AWS we cannot create the OIDC identity until after the cluster creation. In other words, the OIDC web identity&amp;#8217;s lifecycle does not decouple with the cluster lifecycle. We have to create a new web identity every time we create a new EKS cluster. In large organizations, the permission to create a new web identity is highly restricted.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;EKS Pod Identity&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are a few other limitations with IRSA. As &lt;a href="https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/"&gt;this&lt;/a&gt; blog post suggests:&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Further, cluster administrators have to update the IAM role trust policy each time the role is used in a new cluster during scenarios like blue-green upgrades or failover testing. Additionally, as customers grow their EKS cluster footprint, due to the per cluster OIDC provider requirement in IRSA, customers run into the per account OIDC provider limit. Similarly, as they scale the number of clusters or Kubernetes namespaces in which an IAM role is used, they run into IAM trust policy size limit, which makes them duplicate the IAM roles to overcome the trust policy size limit.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS brings the new mechanism &amp;#8220;&lt;em&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html"&gt;EKS Pod Identity&lt;/a&gt;&lt;/em&gt;&amp;#8221; at reInvent 2023. In this mechanism, user can hook up an IAM role directly to a Kubernetes service account, without having to resort to a web identity and OIDC integration. Users just need to create a Pod Identity Association, using the CreatePodIdentityAssociation API, with the following parameters:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Cluster name&lt;/li&gt;&#10;&lt;li&gt;Namespace&lt;/li&gt;&#10;&lt;li&gt;ARN of the IAM role&lt;/li&gt;&#10;&lt;li&gt;serviceAccount &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/eks/create-pod-identity-association.html"&gt;AWS CLI&lt;/a&gt; and &lt;a href="https://eksctl.io/usage/pod-identity-associations/"&gt;ekscli&lt;/a&gt; already support the CreatePodIdentityAssociation API. Before creating a Pod Identity Association, we need to install the add-on &amp;#8220;Amazon EKS Pod Identity Agent&amp;#8221;, and ensure that the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/pod-id-agent-setup.html"&gt;node roles&lt;/a&gt; have the permission. That is because the agent needs to use AssumeRoleForPodIdentity API. We also need an IAM role, with the trust policy principal being &amp;#8220;&lt;em&gt;&lt;strong&gt;pods.eks.amazonaws.com&lt;/strong&gt;&lt;/em&gt;&amp;#8221; and our own choice of &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_tags.html"&gt;resource tags&lt;/a&gt; as condition. Note that another implicit prerequisite is that the programming running in the Pod use a newer version of AWS SDK to access cloud resource. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/"&gt;This&lt;/a&gt; blog post has good details, including a diagram and a walk-through. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Comparison&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both EKS Pod Identity and IRSA are here to stay. I&amp;#8217;m afraid this is going to create confusions. I put the following table for their comparision:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-white-background-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;IRSA&lt;/th&gt;&lt;th&gt;EKS Pod Identity&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Pros&lt;/td&gt;&lt;td&gt;&amp;#8211; in use since 2019&lt;br&gt;&amp;#8211; support EKS, EKS-A, ROSA&lt;br&gt;&amp;#8211; support all EKS versions&lt;/td&gt;&lt;td&gt;&amp;#8211; support role session tags&lt;br&gt;&amp;#8211; no dependency on OIDC identity provider&lt;br&gt;&amp;#8211; create an IAM role once for all clusters. the role can be created before cluster&lt;br&gt;&amp;#8211; cross account access through resource policies and chained AssumeRole operation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cons&lt;/td&gt;&lt;td&gt;&amp;#8211; Cannot create OIDC identity provider, until the cluster is ready&lt;br&gt;&amp;#8211; One OIDC provider per cluster, with the risk of hitting quota&lt;br&gt;&amp;#8211; Trust policy sprawl as more clusters are created&lt;/td&gt;&lt;td&gt;&amp;#8211; the program has to use newer version of SDK. &lt;br&gt;&amp;#8211; ony support EKS&lt;br&gt;&amp;#8211; Pod Identity Agent (DaemonSet) can&amp;#8217;t run on Fargate&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Compairing IRSA with EKS Pod Identity&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The blog &lt;a href="https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/"&gt;post&lt;/a&gt; also contains a long table for their comparison. In the near future, I will have to check the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/pod-id-minimum-sdk.html"&gt;SDK version&lt;/a&gt; of a workload in order to assess whether EKS Pod Identity will function. This is a restriction because it depends upon software builder disclosing the SDK version used. The EKS cluster also needs to host daemonSet on a node agent. On the other hand, go with IRSA if portability between EKS and EKS-A and &lt;a href="https://www.digihunch.com/2023/06/kubernetes-paas-and-red-hat-openshift/"&gt;ROSA&lt;/a&gt; is of concern, because the IAM service principal &lt;em&gt;pods.eks.amazonaws.com&lt;/em&gt; is dedicated to EKS. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The blog post also gives the migration step as follows:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Ensure EKS cluster is above 1.24, and install the add-on for EKS pod identity agent. &lt;/li&gt;&#10;&lt;li&gt;Ensure the SDK running in pod meets the version requirement. &lt;/li&gt;&#10;&lt;li&gt;Update the IAM role&amp;#8217;s trust policy with the new principal &amp;#8220;pods.eks.amazonaws.com&amp;#8221;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So the EKS Pod Identity mechanism still requires an IAM role. It does not required an OIDC identity. The service account connects to IAM role via an agent on the node. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A good design concerns not only functionality, but also streamlined configuration experience. EKS Pod Identity is a great improvement over IRSA heading the right direction. It just came out two months ago so still too early to adopt, especially without knowing the workload details. For now I tend to use pod identity as a backup mechanism when IRSA isn&amp;#8217;t available for some reason. However, I recommend starting to introduce the Pod Identity mechanism for all new EKS clusters and new workloads.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2023/12/workload-identity-on-kubernetes-1-of-2-aks/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Workload Identity on Kubernetes 1 of 2 – AKS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/02/public-key-infrastructure-1-of-3-basics/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Public Key Infrastructure 1 of 3 – Basics&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Workload Identity on Kubernetes 1 of 2 – AKS</title><link>https://www.digihunch.com/2023/12/workload-identity-on-kubernetes-1-of-2-aks/</link><pubDate>Sat, 23 Dec 2023 16:43:00 -0400</pubDate><guid>https://www.digihunch.com/2023/12/workload-identity-on-kubernetes-1-of-2-aks/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-workload-identity-aks.webp" alt="Featured image of post Workload Identity on Kubernetes 1 of 2 – AKS" /&gt;&lt;p class="wp-block-paragraph"&gt;As applications are moved to the cloud, the application workload hosted on virtual machines need to interact with cloud resources. For this, we need an IAM solution with two mechanisms:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;a (non-human) identity in the cloud service platform (CSP), to represent the application;&lt;/li&gt;&#10;&lt;li&gt;a way to grant permission to this identity, so it can manage resources&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;CSPs such as Azure and AWS have their own implementations of the two mechanism. In Azure, we have Entra workload identity (including service principal and managed identity) for the first mechanism, and Azure roles for the second. On AWS, they are the identity pool capability of Amazon Cognito and IAM role. Next, what about the workload on managed Kubernetes service? Essentially, we will need to more mechanisms:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;a native Kubernetes identity to represent the workload (Pod);&lt;/li&gt;&#10;&lt;li&gt;a way to map the Kubernetes identity to the identity in CSP&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes Service Account is designed for the first item. The second mechanism is for the CSP to address. In this post, let&amp;#8217;s examine this in Azure. Specifically, how does Azure manage workload identity with Azure Kubernetes Service (AKS). &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Node Identity and Cluster Identity in AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s define what exactly is a workload identity. In Azure we think of it as one type of non-human identity. In our context, workload identity in the broader sense contains:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;the identity that represents the control plane (or the whole cluster)&lt;/li&gt;&#10;&lt;li&gt;the identity that represents the node (or kubelet process)&lt;/li&gt;&#10;&lt;li&gt;the identity that represents the application in a Pod (workload identity in the narrow sense);&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So it is important not to confuse these identities. In this section, I&amp;#8217;ll focus on 1 and 2 since they are part of workload identity in the broad sense. In the rest of the sections, I&amp;#8217;ll discuss 3, and use the narrow sense of workload identity. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="934" height="322" src="https://www.digihunch.com/wp-content/uploads/2023/12/aks-identity.webp" alt="" class="wp-image-12970" srcset="https://www.digihunch.com/wp-content/uploads/2023/12/aks-identity.webp 934w, https://www.digihunch.com/wp-content/uploads/2023/12/aks-identity-300x103.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/12/aks-identity-768x265.webp 768w" sizes="auto, (max-width: 934px) 100vw, 934px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we create an AKS cluster, we create both a cluster control plane and a node pool. Both the control plane and the nodes need to provision cloud resources using Cloud API from Azure. For example, if we use Terraform&amp;#8217;s AzureRM provider to create an &lt;code&gt;azurerm_kubernetes_cluster&lt;/code&gt; resource, then we specify the cluster&amp;#8217;s identity using &lt;code&gt;service_principal&lt;/code&gt; or &lt;code&gt;identity&lt;/code&gt; block. We specify the nodes&amp;#8217; identity using the &lt;code&gt;kubelet_identity&lt;/code&gt; block, because &lt;code&gt;kubelet&lt;/code&gt; is the process that runs on each node. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Even though a cluster builder might be tempted to assign the same identity to both Control plane and kubelet, the security best practice is to keep them separated. It is also the responsibility of the cluster builder to distinguish activities by the control plane and by kubelet process on each node, and attache an Azure Role with minimum privilege to each of the identities.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These two types of identities (control plane and kubelet) are relatively straightforward. In order to use them, we don&amp;#8217;t have to play with Kubernetes objects. In the next section, we&amp;#8217;ll continue to discuss the identities that represents each Pod in Azure. We now refer to them as workload identities, but the first available technology was pod managed identity.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Pod Managed Identity in AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When I first worked on &lt;a href="https://www.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;Azure Kubernetes&lt;/a&gt;, Pod managed identity was in preview and was the recommendation. However, Microsoft renamed it (to &lt;a href="https://learn.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity"&gt;Microsoft Entra pod-managed identities&lt;/a&gt;) and then deprecated it after a couple years of preview. As of Oct 2022, the recommended mechanism becomes Microsoft Entra Workload ID. For simplicity, we refer to the deprecated mechanism as &amp;#8220;Pod Identity&amp;#8221;. We discuss pod identity only for the purpose of understanding why it is no longer recommended and what is missing in it. For new workload deployment, we should always use workload identity. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For Pod Identity to work, a&lt;a href="https://learn.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity#register-the-enablepodidentitypreview-feature-flag"&gt; feature flag&lt;/a&gt; &lt;code&gt;EnablePodIdentityPreview&lt;/code&gt; must turn on. Pod Managed Identity &lt;a href="https://learn.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity#operation-mode-options"&gt;operates&lt;/a&gt; on a Kubernetes &lt;a href="https://www.digihunch.com/2022/04/kubernetes-operator/"&gt;controller&lt;/a&gt; called MIC (Managed Identity Controller) and a DaemonSet called NMI (Node Managed identity). You start with an Azure managed identity with appropriate roles. Once you installed Pod Identity, there will be two CRDs &lt;code&gt;AzureIdentity&lt;/code&gt; and &lt;code&gt;AzureIdentityBinding&lt;/code&gt;. To grant Azure permissions to a Pod, you create a CR for each CRD. The &lt;code&gt;AzureIdentity&lt;/code&gt; CR connects to your Azure managed identity. You also create an &lt;code&gt;AzureIdentityBindign&lt;/code&gt; CR. When declaring a Pod, you link to AzureIdentityBinding by using the label aadpodidbinding. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="508" src="https://www.digihunch.com/wp-content/uploads/2023/12/aks-pod-mgd-identity.webp" alt="" class="wp-image-12971" style="width:663px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2023/12/aks-pod-mgd-identity.webp 1024w, https://www.digihunch.com/wp-content/uploads/2023/12/aks-pod-mgd-identity-300x149.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/12/aks-pod-mgd-identity-768x381.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are two problems with pod managed identity. First, there is a &lt;a href="https://learn.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity#using-kubenet-network-plugin-with-microsoft-entra-pod-managed-identities"&gt;vulnerability&lt;/a&gt; when it works with kubenet as network plugin. This vulnerability requires an additional &lt;a href="https://learn.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity#mitigation"&gt;mitigation step&lt;/a&gt;. Second, it does not make use of Kubernete&amp;#8217;s Service Account. Let&amp;#8217;s discuss in the next section why it&amp;#8217;s favourable to use Kubernetes&amp;#8217; ServiceAccount. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Kubernetes Service Account&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes RBAC model, &lt;a href="https://kubernetes.io/docs/concepts/security/service-accounts/"&gt;Service Account&lt;/a&gt; can bind to Roles to gain access to other Kubernetes resources. The most common use case is allowing the running application in a Pod to access other Kubernetes resources. When it comes to letting an application in Pod access cloud resources in the CSP, it makes sense to use Service Account, for a consistent pattern.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A service account must carry a token to function. Each namespace has a default service account with the token mounted automatically. Each Pod created in a namespace uses the default service account of the namespace, unless otherwise specified. However, many security organizations do not considered this default behaviour as the best practice. For example, CIS Kubernetes &lt;a href="https://www.cisecurity.org/benchmark/kubernetes"&gt;benchmark&lt;/a&gt; 1.8 has these two recommendations:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Ensure that the default service accounts are not actively used (5.1.5)&lt;/li&gt;&#10;&lt;li&gt;Ensure that Service Account Tokens are only mounted where necessary (5.1.6)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In other words, we should create non-default service account with &lt;strong&gt;automountServiceAccountToken&lt;/strong&gt; set to false. Then when declaring a Pod, we explicitly specify the service account and where to grab the token for the service account. One way to pass ServiceAccount token is through &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#serviceaccount-token-volume-projection"&gt;volume projection&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To allow a Pod to access Azure resources, we use the combination of Kubernetes Service Account and Microsoft Entra workload identity. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Workload Identity for AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Microsoft introduced Entra Workload Identities in late 2022 to address IAM issues around machine identities. It comes with some modern features such as conditional access (e.g. location-based access, anomaly sign-in detection, etc). A &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identities-overview"&gt;workload identity&lt;/a&gt; can be:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;application: an abstract entity as the global representation of your application for use across all tenants;&lt;/li&gt;&#10;&lt;li&gt;service principal: the local representation of a global application object in a specific tenants;&lt;/li&gt;&#10;&lt;li&gt;managed identity: a special type of service principal that eliminates the need for developers to manage credentials&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In our use case for AKS workload, we also make use of Azure&amp;#8217;s Managed Identity. This part is the same as the pod identity mechanism. However, here we create a federated identity credential for managed identity. The OIDC federated identity credential is issued by the AKS cluster. Within the AKS, the service account references the identity by client_id. &lt;a href="https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster"&gt;Here&lt;/a&gt; is the documentation for the whole process.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="588" src="https://www.digihunch.com/wp-content/uploads/2023/12/aks-workload-identity.webp" alt="" class="wp-image-12972" srcset="https://www.digihunch.com/wp-content/uploads/2023/12/aks-workload-identity.webp 1024w, https://www.digihunch.com/wp-content/uploads/2023/12/aks-workload-identity-300x172.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/12/aks-workload-identity-768x441.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One of the improvements in Entra workload identity for AKS, is the use of service account, which obviates the use of CRDs. Another improvement is the use of federated identity, whose lifecycle is tied to the cluster. This pattern is not only neater, but also standard. We map a service account to a managed identity with federated credential. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On managed Kubernetes services, we need an integration mechanism to grant Kubernetes workload access to cloud resources. We discussed what&amp;#8217;s needed in this integration mechanism and looked at Azure Kubernetes as an example. In the next post, we&amp;#8217;ll discuss how this issue is addressed in Elastic Kubernetes Service on the AWS side.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2023/11/wordpress-security/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;WordPress Security Basics&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/01/workload-identity-on-kubernetes-2-of-2-eks-and-rosa-on-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Workload Identity on Kubernetes 2 of 2 – EKS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Orchestrate Landing Zone with Landing Zone Accelerator on AWS</title><link>https://www.digihunch.com/2023/09/orchestrate-landing-zone-with-landing-zone-accelerator-on-aws/</link><pubDate>Fri, 22 Sep 2023 23:05:04 -0400</pubDate><guid>https://www.digihunch.com/2023/09/orchestrate-landing-zone-with-landing-zone-accelerator-on-aws/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-aws-lza.webp" alt="Featured image of post Orchestrate Landing Zone with Landing Zone Accelerator on AWS" /&gt;&lt;p class="wp-block-paragraph"&gt;As a continuation to the &lt;a href="https://www.digihunch.com/2023/08/control-tower-aws-landing-zone/"&gt;last post&lt;/a&gt;, we explore the &lt;a href="https://aws.amazon.com/solutions/implementations/landing-zone-accelerator-on-aws/"&gt;Landing Zone Accelerator on AWS&lt;/a&gt; (LZA) as an orchestration tool in this post. LZA borrows a lot from the &lt;a href="https://aws-samples.github.io/aws-secure-environment-accelerator/"&gt;ASEA&lt;/a&gt;, an accelerator project to deploy the security reference architecture (&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/welcome.html"&gt;SRA&lt;/a&gt;). LZA is a multi-purpose project that consists of both the orchestration engine (the accelerator itself) and a few reference architectures (as configuration files).&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Comparison with Control Tower&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, let&amp;#8217;s sort out how LZA is related to Control Tower. Control Tower&amp;#8217;s main functionalities are available as an AWS service, with some customization capabilities available as a standalone solution on top of the service, as I discussed in the &lt;a href="https://www.digihunch.com/2023/08/control-tower-aws-landing-zone/"&gt;last post&lt;/a&gt;. Unlike Control Tower, LZA as a whole is a standalone solution. Luckily, the installation of the solution itself is highly automated.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I see LZA both as an extension of Control Tower, and as a complement to Control Tower. It is an extension of Control Tower because LZA can co-exist with Control Tower. We can configure LZA to enable Control Tower and use its Account Factory to provision new accounts (alternatively but not recommended, we can opt out of Control Tower and manage account creation on our own). I also see LZA as a complement to Control Tower because it comes with full end to end automation scheme for networking infrastructure and most of the services involved. This is missing in Control Tower, which leaves it with users to provision networking infrastructure in the customization. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Thanks to the infrastructure automation capability, even if you do not have a strong regulatory requirement, there are still good reason to go with LZA for its low-code automation capability. Below is a table that summarizes the differences:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-light-green-cyan-background-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Control Tower&lt;/th&gt;&lt;th&gt;Landing Zone Accelerator&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&amp;#8211; Multi-account management tool&lt;br&gt;&amp;#8211; Governance layer&lt;br&gt;&amp;#8211; Customization Framework to bring your own infrastructure automation&lt;/td&gt;&lt;td&gt;&amp;#8211; can manage Control Tower &lt;br&gt;&amp;#8211; low-code automation engine for infrastructure automation and service deployment based on CDK&lt;br&gt;&amp;#8211; reference configurations based on common industry profiles and regulatory requirements&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Comparison between Control Tower and Landing Zone Accelerator&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As the name suggests, LZA is an accelerator so there is no expectation of its user knowing how to program infrastructure as code. However, it still expects its users to know YAML very well. Knowing how CloudFormation and CDK works can greatly help the users troubleshoot deployment. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Reference architectures in LZA&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The input of LZA is configuration as code in YAML format. The LZA repository comes with a number of sample configurations to implement some industry-based best practices. The reference architectures currently include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;General best practices reference configuration: for clients other than the categories below;&lt;/li&gt;&#10;&lt;li&gt;Government customers: US Gov Cloud (FedRAMP compliant, on aws-us-gov partition), US State and Local Government, China (on aws-cn partition), Canada Federal (CCCS compliant) TSE-SE (&lt;a href="https://d1.awsstatic.com/events/Summits/awscanberrasummit/NEW202_Transform%20national%20security%20and%20defence%20missions%20with%20AWS_PDF.pdf"&gt;Highly Trusted Secure Enclave Sensitive Edition&lt;/a&gt;) on commercial partition for governments, national security, defence, and law enforcement customers reference architecture;&lt;/li&gt;&#10;&lt;li&gt;Election: for election customers including elections agencies, committees and campaigns;&lt;/li&gt;&#10;&lt;li&gt;Healthcare: for healthcare customers. However, the document does not mention HIPAA compliance or anything related to the &lt;a href="https://aws-quickstart.github.io/quickstart-compliance-hipaa/"&gt;HIPAA Reference Architecture&lt;/a&gt;;&lt;/li&gt;&#10;&lt;li&gt;Finance and Taxation: for tax workload to secure Federal Tax Information (FTI) data;&lt;/li&gt;&#10;&lt;li&gt;Education: for education industry customers.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Many of these reference architecture shares a few common traits in the networking design. Take the &lt;a href="https://github.com/awslabs/landing-zone-accelerator-on-aws/tree/main/reference/sample-configurations/lza-sample-config-cccs-medium"&gt;CCCS reference &lt;/a&gt;as an example, the networking involves the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Workload VPCs: consisting of a number VPCs for production and test environments;&lt;/li&gt;&#10;&lt;li&gt;Shared services VPC: hosting common services such as pipelines, Active Directories, etc&lt;/li&gt;&#10;&lt;li&gt;Endpoint VPCs: centrally hosting interface endpoints&lt;/li&gt;&#10;&lt;li&gt;Perimeter VPCs: acting as ingress, egress and inspection VPCs. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Perimeter VPC hosts firewalls (either AWS Network Firewall or NGFW appliances behind Gateway Load Balancers). All the VPCs are centrally managed in an AWS network account, and are shared to other accounts using Resource Access Manager. The &lt;a href="https://github.com/aws-samples/landing-zone-accelerator-on-aws-for-cccs-medium/blob/main/architecture-doc/readme.md"&gt;reference architecture &lt;/a&gt;document keeps the details of this architecture, which was derived from the security reference architecture (SRA).&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Special Purpose VPCs&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I consider the non-workload VPCs as special purpose VPCs. The shared services VPC is the most straight-forward. The Endpoint VPC is the most standardized. It is used to centrally host VPC interface endpoints for security and cost reasons. Unlike Gateway endpoint which is only available for S3 and DynamoDB, interface endpoint carries a standing charge and therefore should be consolidated. In addition, since interface endpoints are based on interfaces, we can centrally control the security group and interface policy. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To integrate the endpoint VPC, not only do we need to create those interface endpoint. We also need to account for routing (using Transit Gateway route tables) and name resolution. For name resolution, we need to create a Route53 private hosted zone for each DNS name, such as &lt;code&gt;ec2.us-east-1.amazonaws.com&lt;/code&gt; and associate them with each workload VPC. Note that the interface endpoints DNS name may not always follow the same format. See the exceptions in my &lt;a href="https://www.digihunch.com/2022/12/landing-zone-in-aws/"&gt;old post&lt;/a&gt;. Also note that this would create a lot of associations (between Private Hosted Zone for each Interface endpoint and each workload VPC). For example, 20 workload VPC with 30 private hosted zones will create 600 associations. To overcome this, use &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/profiles.html"&gt;Route53 profile&lt;/a&gt; (introduced in April 2024).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another special purpose VPC is the perimeter VPC. This VPC vary greatly between customers because of different requirement and historical preferences. One of the key design areas is the placement of NGFW, which is discussed in &lt;a href="https://www.digihunch.com/2024/11/firewall-deployment-patterns/"&gt;this &lt;/a&gt;post.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;LZA Orchestration Engine&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The installation process may feel complex at the beginning because we have to first install the pipeline to that installs the pipeline. The initial setup consists the following steps:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;CloudFormation installs the installer. &lt;/strong&gt;We start with a CloudFormation template to deploy the LZA installer itself. It deploys resources such as CodePipeline (AWSAccelerator-Installer) and CodeBuild project (AWSAccelerator-InstallerProject). These resources are in the INSTALLER circle in the diagram below; &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;The installer installs the accelerator core.&lt;/strong&gt; In the LZA installer, the CodePipeline (AWSAccelerator-Installer) and CodeBuild project (AWSAccelerator-InstallerProject) drive the installation of the LZA. The input is the official LZA GitHub and we need a GitHub token for this step. The output is the actual LZA orchestration engine, including CodePipeline (AWSAccelerator-Pipeline) and CodeBuild (AWSAccelerator-BuildProject and AWSAccelerator-ToolkitProject). The user may specify their own GitHub repo as the configuration repo. Otherwise, a CodeCommit repo will be created. The LZA resources are shown in the CORE circle in the diagram below;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;The acceleration core configures the landing zone. &lt;/strong&gt;The LZA orchestration engine deploys actual resources in the landing zone, with the CodeCommit repo (aws-accelerator-config) or the specified GitHub repo as input.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="549" src="https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-1024x549.webp" alt="" class="wp-image-12877" srcset="https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-1024x549.webp 1024w, https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-300x161.webp 300w, https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-768x412.webp 768w, https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza.webp 1288w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If we enable Control Tower with LZA, we should first log in to management account and configure Landing Zone with Control Tower. we can also create (and register) the required OUs and accounts from Control Tower. Then we can deploy&amp;nbsp;&lt;a href="https://docs.aws.amazon.com/solutions/latest/landing-zone-accelerator-on-aws/step-1.-launch-the-stack.html"&gt;Landing Zone Accelerator&lt;/a&gt;&amp;nbsp;with default configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After the initial setup, we will need to iterate over the &lt;code&gt;aws-accelerator-config&lt;/code&gt; repo. We implement our landing zone design in YAML configuration following the &lt;a href="https://awslabs.github.io/landing-zone-accelerator-on-aws/index.html"&gt;schema documentation&lt;/a&gt;. Changes in the configuration repo will trigger the pipeline (aka LZA&amp;#8217;s orchestration engine) to redo step 3, whereas step 1 and step 2 are performed only once. The duration of step 3 is significantly longer than the first two steps. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Pitfalls&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If LZA manages Control Tower, it expects existing OUs registered in Control Tower or it will report error. For account, LZA can create accounts listed in the manifest but not yet created. However, with the lengthy account vendor process for multiple account we run the risk of task time out in the pipeline.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;During the installation, some account may run into quota limit. For example, the Networking Account usually have more than five VPCs whereas the quota is 5 VPCs per region per account. We need to increase the quota on those accounts.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The full deployment usually creates some SCPs. However, if we ever need to re-deploy a configuration, some steps might be blocked by certain SCPs. Attempts to temporarily detach SCPs from OUs, or modify SCPs often get reverted. The cause is an EventBridgeRule in&amp;nbsp;us-east-1&amp;nbsp;region named&amp;nbsp;&lt;code&gt;RevertScpChangesModifySc&lt;/code&gt;. The rule should be disabled temporarily to perform the troubleshooting activity. We can do this with the following steps:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Disable the EventBridgeRule &amp;nbsp;RevertScpChangesModifySc , which is only present in us-east-1 region;&lt;/li&gt;&#10;&lt;li&gt;Detach SCPs and note down what are detached, one OU at a time;&lt;/li&gt;&#10;&lt;li&gt;Go to the failed CF stack in the region, delete the failed stacks (after turning off termination protection);&lt;/li&gt;&#10;&lt;li&gt;Rerun the pipeline step from where it failed. This time it should go past the failure to the end, if SCP is the cause as per our assumption;&lt;/li&gt;&#10;&lt;li&gt;Re-attach SCPs. Suppose Security and Infrastructure OUs share one group of SCPs, and Dev, Test, and Prod OUs share a different group of SCPs;&lt;/li&gt;&#10;&lt;li&gt;Re-enable the EventBridgeRule;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Even with the EventBridgeRule&amp;nbsp;&lt;code&gt;RevertScpChangesModifySc&lt;/code&gt; disabled, when you re-run LZA deployment pipeline, the Accounts step will re-attach the SCPs using the &lt;code&gt;AWSAccelerator-AccountsStack&lt;/code&gt; in the management account in &lt;code&gt;us-east-1&lt;/code&gt; region.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In general, how SCP works with organization structure is something to be very careful about, especially when the hierarchy consists of multiple layers of OUs. It is important to keep in mind, that deny statements in SCP take effect down the hierarchy, where as allow statements only affects the immediate child account of the OU where the SCP is attached to, as per the &lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_evaluation.html"&gt;evaluation logic&lt;/a&gt;. As a result, an SCP with allow * statement (in the &lt;code&gt;LZA-AWSFullAWSAccess&lt;/code&gt; managed policy) must be applied to Root, every OU at each level, and every account, for LZA to function. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition, there are some hard limits for SCP. Each SCP has a size limit of 5120 characters, and each OU can attach a limit of 5 SCPs. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Challenges&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Powered by CDK, LZA automates the creation of a lot of resources. The configuration files uses the &lt;code&gt;deploymentTargets&lt;/code&gt; attribute to allow users to specify to which accounts or OUs the declared resources will be deployed to.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Supporting many resources is a double-edge sword. Because the accelerator needs to go through every aspect of a landing zone, it is very slow to run. The accelerator pipeline may take as long as 40 minutes without any change to the configuration code. This is extremely slow if you just want to make some small changes in the configuration (e.g. update route table, add IAM role). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Even though LZA supports many resources, it&amp;#8217;s not flexible with every resource. For example, today we can deploy IAM roles using &lt;code&gt;&lt;a href="https://awslabs.github.io/landing-zone-accelerator-on-aws/latest/typedocs/latest/classes/_aws_accelerator_config.RoleSetConfig.html"&gt;RoleSet&lt;/a&gt;&lt;/code&gt;. However, in the trust policy of the IAM role you can only specify a two types of principals under the &lt;code&gt;&lt;a href="https://awslabs.github.io/landing-zone-accelerator-on-aws/latest/typedocs/latest/classes/_aws_accelerator_config.RoleConfig.html#assumedBy"&gt;assumedBy&lt;/a&gt;&lt;/code&gt; attribute: &lt;code&gt;account&lt;/code&gt; and &lt;code&gt;service&lt;/code&gt; types. On the other hand a trust policy can support many other &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html"&gt;types of principals&lt;/a&gt; such as another IAM role.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="611" height="321" src="https://www.digihunch.com/wp-content/uploads/2023/09/lza-az-mapping.webp" alt="" class="wp-image-12957" srcset="https://www.digihunch.com/wp-content/uploads/2023/09/lza-az-mapping.webp 611w, https://www.digihunch.com/wp-content/uploads/2023/09/lza-az-mapping-300x158.webp 300w" sizes="auto, (max-width: 611px) 100vw, 611px" /&gt;&lt;figcaption class="wp-element-caption"&gt;AZ Mapping&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another important ability that LZA does not support is consistent AZ mapping across accounts. (&lt;strong&gt;Correction&lt;/strong&gt;: this is now supported in &lt;a href="https://github.com/awslabs/landing-zone-accelerator-on-aws/releases/tag/v1.5.0"&gt;LZA v1.5&lt;/a&gt; as of Oct 2023). In some example LZA configurations, we deploy VPCs across multiple accounts using two or three availability zones referenced by their logical ID, such as &lt;code&gt;us-east-1a&lt;/code&gt; and &lt;code&gt;us-east-1b&lt;/code&gt;. However, AWS &lt;a href="https://docs.aws.amazon.com/ram/latest/userguide/working-with-az-ids.html"&gt;maps logical ID to physical ID&lt;/a&gt; and the mapping may be different in each AWS account. Using the same logical ID cannot guarantee the physical AZ are the same across account. As of LZA 1.5, the ability to reference physical ID in &lt;a href="https://awslabs.github.io/landing-zone-accelerator-on-aws/latest/typedocs/interfaces/___packages__aws_accelerator_config_lib_models_network_config.ISubnetConfig.html#availabilityZone"&gt;availabilityZone&lt;/a&gt; is supported in LZA configuration file.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ve spent a lot of time on LZA recently. It is extremely powerful. LZA streamlined the landing zone deployment process with configuration as code. It also allows users to customize their landing zone towards their own architectural needs and compliance requirement. For example, you can declare arbitrary SSM parameters in each account. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the down side, the LZA deployment is time consuming through the pipelines. It tries to automate too many aspects of the infrastructure, which makes itself quite a complex project. Expect lots of changes in each new version. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The idea of being a low-code solution is to make it simple for end users but it often sacrifices flexibility. For example, if you want to create an IAM role in each new account that references the Management account ID, it is not possible until such feature is implemented in the accelerator. When the accelerator pipeline fails, it still requires deep CloudFormation knowledge to troubleshoot. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2023/08/control-tower-aws-landing-zone/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Orchestrate Landing Zone with AWS Control Tower&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2023/10/the-systems-manager-hodgepodge/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AWS Systems Manager is an Omnipotent Hodgepodge&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Orchestrate Landing Zone with AWS Control Tower</title><link>https://www.digihunch.com/2023/08/control-tower-aws-landing-zone/</link><pubDate>Sat, 19 Aug 2023 17:25:00 -0400</pubDate><guid>https://www.digihunch.com/2023/08/control-tower-aws-landing-zone/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-control-tower.webp" alt="Featured image of post Orchestrate Landing Zone with AWS Control Tower" /&gt;&lt;p class="wp-block-paragraph"&gt;Following an &lt;a href="https://www.digihunch.com/2022/12/landing-zone-in-aws/"&gt;introduction to AWS Landing Zone&lt;/a&gt;, I&amp;#8217;ll dive deeper into Control Tower as an orchestration tool in this post.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;More on Landing Zone&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In data center operation, there are numerous tasks that other teams have to complete before the deployment of an application. For example, the 42U server cabinet must be in place with dual powers. The cabinet comes with a network switch in the middle and each ethernet port must be provisioned. Once the server is connected to the central network, the NOC team assigns IP address, configure dynamic VLAN on the switches, and configures firewalls etc according to the connectivity requirement. The new server also needs to report to centralized monitoring solutions such as SolarWinds. In cloud operations, the scope and target of a landing zone is similar to those data center operations work, with networking being the core. The idea is that the landing zone ensures security, compliance and governance, so that applications (analogous to paratroopers) can focus on their primary responsibility. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although Landing Zone is a general concept for any cloud service provider (CSP), each CSP has some prescriptive guidances on setting up landing zone in their particular environment. For example, an AWS prescriptive landing zone typically covers the following apsects:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Multi-account structure&lt;/li&gt;&#10;&lt;li&gt;Identity and Access Management&lt;/li&gt;&#10;&lt;li&gt;Governance (controls and guardrails)&lt;/li&gt;&#10;&lt;li&gt;Networking&lt;/li&gt;&#10;&lt;li&gt;Additional Security Services&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The multi-account best practice is an important aspect in AWS as the OU structure dictates how effective Service Control Policies (SCPs) can govern the entire footprint. A landing zone orchestration solution should also apply guardrails and controls based on the organization&amp;#8217;s security and compliance requirement. In addition, it is common expectation that landing zone orchestration solutions create required networking resources such that applications are ready to deploy securely. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This post is about Control Tower.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Intro to Control Tower&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Control Tower is a landing zone orchestration solution available as an AWS service. I have three impressions over Control Tower. First, it is good with managing multi-account structure. It integrates closely with AWS Organizations and requires client to have a dedicated log archive account and a dedicated security tooling account, which also serves as audit account. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second impression is Control Tower makes governance more straightforward. AWS seems to use the term control and guardrail interchangeably. Below is an illustration:&lt;/p&gt;&#10;&lt;p class="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="541px" viewBox="-0.5 -0.5 541 251" style="max-width:100%;max-height:251px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="540" height="250" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 40 90 L 80 90 L 80 130 L 40 130 Z" fill="#e7157b" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 58.08 120.68 L 57.25 121.5 L 55.6 119.85 L 56.43 119.03 Z M 49.82 112.42 L 51.48 114.07 L 50.65 114.9 L 49 113.25 Z M 53.13 115.72 L 54.78 117.38 L 53.95 118.2 L 52.3 116.55 Z M 67.42 104.79 L 65.76 103.14 L 66.58 102.31 L 68.24 103.96 Z M 70.73 108.08 L 69.07 106.43 L 69.89 105.61 L 71.55 107.25 Z M 62.45 99.85 L 63.27 99.02 L 64.93 100.67 L 64.1 101.49 Z M 70.2 112.44 L 71.02 113.27 L 69.37 114.92 L 68.55 114.09 Z M 66.89 115.74 L 67.71 116.56 L 66.06 118.21 L 65.23 117.38 Z M 63.58 119.03 L 64.4 119.86 L 62.75 121.5 L 61.92 120.68 Z M 49.32 108.08 L 48.5 107.25 L 50.15 105.6 L 50.97 106.43 Z M 55.93 101.48 L 55.1 100.65 L 56.75 99 L 57.58 99.82 Z M 52.62 104.78 L 51.8 103.95 L 53.45 102.3 L 54.28 103.13 Z M 57.84 107.84 C 58.29 107.4 58.84 107.12 59.42 107.01 L 59.42 109.42 L 57.01 109.42 C 57.12 108.84 57.4 108.29 57.84 107.84 Z M 62.16 107.84 C 62.6 108.29 62.88 108.84 62.99 109.42 L 60.58 109.42 L 60.58 107.01 C 61.16 107.12 61.71 107.4 62.16 107.84 Z M 62.16 112.16 C 61.71 112.6 61.16 112.88 60.58 112.99 L 60.58 110.58 L 62.99 110.58 C 62.88 111.16 62.6 111.71 62.16 112.16 Z M 57.84 112.16 C 57.4 111.71 57.12 111.16 57.01 110.58 L 59.42 110.58 L 59.42 112.99 C 58.84 112.88 58.29 112.6 57.84 112.16 Z M 54.58 111.92 L 53.24 110.58 L 55.83 110.58 C 55.95 111.46 56.34 112.31 57.02 112.98 C 57.69 113.66 58.54 114.05 59.42 114.17 L 59.42 116.76 L 58.08 115.42 L 57.25 116.25 L 60 119 L 62.75 116.25 L 61.92 115.42 L 60.58 116.76 L 60.58 114.17 C 61.46 114.05 62.31 113.66 62.98 112.98 C 63.66 112.31 64.05 111.46 64.17 110.58 L 66.76 110.58 L 65.42 111.92 L 66.25 112.75 L 69 110 L 66.25 107.25 L 65.42 108.08 L 66.76 109.42 L 64.17 109.42 C 64.05 108.54 63.66 107.69 62.98 107.02 C 62.31 106.34 61.46 105.95 60.58 105.83 L 60.58 103.24 L 61.92 104.58 L 62.75 103.75 L 60 101 L 57.25 103.75 L 58.08 104.58 L 59.42 103.24 L 59.42 105.83 C 58.54 105.95 57.69 106.34 57.02 107.02 C 56.34 107.69 55.95 108.54 55.83 109.42 L 53.24 109.42 L 54.58 108.08 L 53.75 107.25 L 51 110 L 53.75 112.75 Z M 60 124.35 L 59.07 123.42 L 60 122.5 L 60.93 123.42 Z M 57.42 123.42 L 60 126 L 62.58 123.42 L 60 120.85 Z M 45.65 110 L 46.58 109.07 L 47.5 110 L 46.58 110.93 Z M 46.58 107.42 L 44 110 L 46.58 112.58 L 49.15 110 Z M 72.5 110 L 73.42 109.07 L 74.35 110 L 73.42 110.93 Z M 73.42 107.42 L 70.85 110 L 73.42 112.58 L 76 110 Z M 60 95.65 L 60.93 96.58 L 60 97.5 L 59.07 96.58 Z M 62.58 96.58 L 60 94 L 57.42 96.58 L 60 99.15 Z" fill="#ffffff" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 137px; margin-left: 60px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;AWS Control Tower&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="149" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;AWS Co&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 280 90 L 320 90 L 320 130 L 280 130 Z" fill="#e7157b" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 310.99 114.18 L 312.1 114.56 L 311.4 116.6 C 311.32 116.83 311.09 116.99 310.84 116.99 C 310.82 116.99 310.79 116.99 310.77 116.99 L 308.5 116.7 L 308.64 115.53 L 309.93 115.69 C 309.42 114.9 308.47 114.4 307.33 114.4 C 305.84 114.4 304.71 115.22 304.46 116.48 L 303.3 116.24 C 303.68 114.41 305.26 113.22 307.33 113.22 C 308.79 113.22 310.02 113.84 310.76 114.84 Z M 310.21 118.17 L 311.36 118.41 C 310.99 120.23 309.37 121.45 307.33 121.45 C 306.01 121.45 304.84 120.88 304.08 119.96 L 303.96 120.52 L 302.81 120.27 L 303.28 118.07 C 303.34 117.75 303.65 117.55 303.97 117.61 L 306.11 118.03 L 305.89 119.19 L 304.81 118.98 C 305.32 119.77 306.26 120.27 307.33 120.27 C 308.82 120.27 309.95 119.45 310.21 118.17 Z M 314.82 118.36 L 314.11 118.32 C 313.83 118.3 313.57 118.49 313.5 118.77 C 313.35 119.47 313.08 120.13 312.69 120.73 C 312.54 120.97 312.58 121.29 312.8 121.48 L 313.33 121.96 L 311.96 123.33 L 311.49 122.8 C 311.29 122.59 310.97 122.54 310.73 122.7 C 310.13 123.08 309.47 123.35 308.77 123.51 C 308.49 123.57 308.3 123.83 308.32 124.12 L 308.36 124.82 L 306.41 124.82 L 306.45 124.12 C 306.47 123.83 306.28 123.57 305.99 123.51 C 305.3 123.35 304.64 123.08 304.04 122.7 C 303.79 122.54 303.47 122.58 303.28 122.8 L 302.81 123.33 L 301.44 121.96 L 301.97 121.49 C 302.18 121.29 302.23 120.97 302.07 120.73 C 301.69 120.13 301.42 119.47 301.26 118.77 C 301.2 118.49 300.93 118.3 300.66 118.32 L 299.95 118.36 L 299.95 116.41 L 300.66 116.45 C 300.92 116.47 301.2 116.28 301.27 116 C 301.42 115.3 301.7 114.65 302.08 114.04 C 302.23 113.8 302.19 113.48 301.97 113.29 L 301.44 112.81 L 302.81 111.44 L 303.29 111.97 C 303.48 112.19 303.8 112.23 304.04 112.08 C 304.64 111.7 305.3 111.43 305.99 111.27 C 306.28 111.2 306.47 110.95 306.45 110.66 L 306.41 109.95 L 308.36 109.95 L 308.32 110.66 C 308.3 110.95 308.49 111.21 308.77 111.27 C 309.47 111.43 310.12 111.7 310.73 112.08 C 310.97 112.23 311.29 112.19 311.48 111.97 L 311.96 111.44 L 313.33 112.81 L 312.8 113.29 C 312.58 113.48 312.54 113.8 312.69 114.04 C 313.07 114.64 313.35 115.3 313.5 116 C 313.57 116.28 313.84 116.47 314.11 116.45 L 314.82 116.41 Z M 315.82 115.36 C 315.7 115.25 315.53 115.19 315.38 115.2 L 314.53 115.25 C 314.38 114.76 314.19 114.29 313.94 113.84 L 314.58 113.28 C 314.7 113.17 314.77 113.02 314.78 112.85 C 314.78 112.69 314.72 112.53 314.6 112.42 L 312.35 110.16 C 312.23 110.05 312.08 109.98 311.91 109.99 C 311.75 110 311.6 110.07 311.49 110.19 L 310.92 110.83 C 310.47 110.59 310.01 110.39 309.52 110.25 L 309.57 109.39 C 309.58 109.23 309.52 109.07 309.41 108.95 C 309.3 108.84 309.14 108.77 308.98 108.77 L 305.79 108.77 C 305.63 108.77 305.47 108.84 305.36 108.95 C 305.25 109.07 305.19 109.23 305.2 109.39 L 305.25 110.25 C 304.76 110.39 304.29 110.58 303.85 110.83 L 303.28 110.19 C 303.17 110.07 303.02 110 302.85 109.99 C 302.69 109.98 302.54 110.05 302.42 110.16 L 300.16 112.42 C 300.05 112.53 299.99 112.69 299.99 112.85 C 300 113.02 300.07 113.17 300.19 113.28 L 300.83 113.85 C 300.58 114.29 300.39 114.76 300.24 115.25 L 299.39 115.2 C 299.23 115.19 299.07 115.25 298.95 115.36 C 298.84 115.47 298.77 115.63 298.77 115.79 L 298.77 118.98 C 298.77 119.14 298.84 119.3 298.95 119.41 C 299.07 119.52 299.23 119.58 299.39 119.57 L 300.24 119.52 C 300.39 120.01 300.58 120.48 300.82 120.93 L 300.19 121.49 C 300.07 121.6 300 121.75 299.99 121.91 C 299.99 122.08 300.05 122.23 300.16 122.35 L 302.42 124.6 C 302.54 124.72 302.7 124.78 302.85 124.78 C 303.02 124.77 303.17 124.7 303.28 124.58 L 303.84 123.95 C 304.29 124.19 304.76 124.38 305.25 124.53 L 305.2 125.38 C 305.19 125.54 305.25 125.7 305.36 125.82 C 305.47 125.93 305.63 126 305.79 126 L 308.98 126 C 309.14 126 309.3 125.93 309.41 125.82 C 309.52 125.7 309.58 125.54 309.57 125.38 L 309.52 124.53 C 310.01 124.39 310.48 124.19 310.93 123.95 L 311.49 124.58 C 311.6 124.7 311.75 124.77 311.91 124.78 C 312.09 124.78 312.23 124.72 312.35 124.6 L 314.6 122.35 C 314.72 122.23 314.78 122.08 314.78 121.91 C 314.77 121.75 314.7 121.6 314.58 121.49 L 313.94 120.93 C 314.19 120.48 314.38 120.01 314.53 119.52 L 315.38 119.57 C 315.53 119.58 315.7 119.52 315.82 119.41 C 315.93 119.3 316 119.14 316 118.98 L 316 115.79 C 316 115.63 315.93 115.47 315.82 115.36 Z M 285.18 118.75 L 296.97 118.75 L 296.97 119.93 L 284.59 119.93 C 284.26 119.93 284 119.67 284 119.34 L 284 94.59 C 284 94.26 284.26 94 284.59 94 L 311.7 94 C 312.03 94 312.29 94.26 312.29 94.59 L 312.29 107.56 L 311.11 107.56 L 311.11 95.18 L 285.18 95.18 Z M 304.63 102.25 L 304.04 102.25 L 304.04 101.07 L 304.63 101.07 L 306.99 101.07 L 307.58 101.07 L 307.58 102.25 L 306.99 102.25 Z M 305.22 99.89 L 305.22 97.54 L 306.4 97.54 L 306.4 99.89 Z M 307.58 107.56 L 307.58 103.43 L 308.17 103.43 C 308.49 103.43 308.75 103.17 308.75 102.84 L 308.75 100.48 C 308.75 100.16 308.49 99.89 308.17 99.89 L 307.58 99.89 L 307.58 96.95 C 307.58 96.62 307.31 96.36 306.99 96.36 L 304.63 96.36 C 304.3 96.36 304.04 96.62 304.04 96.95 L 304.04 99.89 L 303.45 99.89 C 303.12 99.89 302.86 100.16 302.86 100.48 L 302.86 102.84 C 302.86 103.17 303.12 103.43 303.45 103.43 L 304.04 103.43 L 304.04 107.56 L 305.22 107.56 L 305.22 103.43 L 306.4 103.43 L 306.4 107.56 Z M 296.38 108.74 L 295.79 108.74 L 295.79 107.56 L 296.38 107.56 L 298.74 107.56 L 299.32 107.56 L 299.32 108.74 L 298.74 108.74 Z M 296.97 106.38 L 296.97 97.54 L 298.15 97.54 L 298.15 106.38 Z M 299.32 111.09 L 299.32 109.91 L 299.91 109.91 C 300.24 109.91 300.5 109.65 300.5 109.32 L 300.5 106.97 C 300.5 106.64 300.24 106.38 299.91 106.38 L 299.32 106.38 L 299.32 96.95 C 299.32 96.62 299.06 96.36 298.74 96.36 L 296.38 96.36 C 296.05 96.36 295.79 96.62 295.79 96.95 L 295.79 106.38 L 295.2 106.38 C 294.87 106.38 294.61 106.64 294.61 106.97 L 294.61 109.32 C 294.61 109.65 294.87 109.91 295.2 109.91 L 295.79 109.91 L 295.79 116.4 L 296.97 116.4 L 296.97 109.91 L 298.15 109.91 L 298.15 111.09 Z M 287.54 102.84 L 288.13 102.84 L 290.48 102.84 L 291.07 102.84 L 291.07 104.02 L 290.48 104.02 L 288.13 104.02 L 287.54 104.02 Z M 288.72 101.66 L 288.72 97.54 L 289.89 97.54 L 289.89 101.66 Z M 288.72 115.22 L 288.72 105.2 L 289.89 105.2 L 289.89 115.22 Z M 288.13 116.4 L 290.48 116.4 C 290.81 116.4 291.07 116.13 291.07 115.81 L 291.07 105.2 L 291.66 105.2 C 291.99 105.2 292.25 104.93 292.25 104.61 L 292.25 102.25 C 292.25 101.93 291.99 101.66 291.66 101.66 L 291.07 101.66 L 291.07 96.95 C 291.07 96.62 290.81 96.36 290.48 96.36 L 288.13 96.36 C 287.8 96.36 287.54 96.62 287.54 96.95 L 287.54 101.66 L 286.95 101.66 C 286.62 101.66 286.36 101.93 286.36 102.25 L 286.36 104.61 C 286.36 104.93 286.62 105.2 286.95 105.2 L 287.54 105.2 L 287.54 115.81 C 287.54 116.13 287.8 116.4 288.13 116.4 Z" fill="#ffffff" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 137px; margin-left: 300px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;AWS Config&lt;br&gt;Rules&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="300" y="149" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;AWS Co&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 280 170 L 320 170 L 320 210 L 280 210 Z" fill="#e7157b" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 307.43 190.26 L 310.29 190.26 L 310.29 189.12 L 307.43 189.12 Z M 293.14 197.13 L 296 197.13 L 296 195.99 L 293.14 195.99 Z M 287.43 197.13 L 291.43 197.13 L 291.43 195.99 L 287.43 195.99 Z M 287.43 193.7 L 294.29 193.7 L 294.29 192.55 L 287.43 192.55 Z M 287.43 186.83 L 292.57 186.83 L 292.57 185.68 L 287.43 185.68 Z M 287.43 190.26 L 306.29 190.26 L 306.29 189.12 L 287.43 189.12 Z M 298.29 202.85 L 285.14 202.85 L 285.14 183.4 L 298.29 183.4 L 298.29 187.97 L 299.43 187.97 L 299.43 182.82 C 299.43 182.51 299.17 182.25 298.86 182.25 L 284.57 182.25 C 284.26 182.25 284 182.51 284 182.82 L 284 203.43 C 284 203.74 284.26 204 284.57 204 L 298.86 204 C 299.17 204 299.43 203.74 299.43 203.43 L 299.43 191.98 L 298.29 191.98 Z M 316 188.55 C 316 192.24 312.67 193.53 310.91 193.69 L 301.71 193.7 L 301.71 192.55 L 310.86 192.55 C 310.97 192.54 314.86 192.12 314.86 188.55 C 314.86 185.29 311.92 184.63 311.33 184.53 C 311.04 184.48 310.83 184.22 310.86 183.92 C 310.86 183.91 310.86 183.9 310.86 183.89 C 310.83 182.05 309.7 181.48 309.21 181.31 C 308.3 181.01 307.29 181.3 306.75 182.02 C 306.62 182.19 306.42 182.28 306.21 182.24 C 306 182.21 305.82 182.07 305.75 181.87 C 305.4 180.88 304.88 180.23 304.17 179.51 C 302.38 177.74 299.95 177.24 297.67 178.2 C 296.48 178.7 295.44 179.84 294.81 181.33 L 293.76 180.88 C 294.5 179.13 295.76 177.76 297.23 177.15 C 299.95 176 302.84 176.58 304.97 178.7 C 305.59 179.32 306.09 179.91 306.49 180.69 C 307.34 180.05 308.49 179.87 309.58 180.23 C 310.97 180.69 311.84 181.9 311.99 183.5 C 313.93 183.99 316 185.55 316 188.55 Z" fill="#ffffff" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 217px; margin-left: 300px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;AWS CloudFormation&lt;br&gt;hooks&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="300" y="229" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;AWS Cl&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="480" y="10" width="40" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 509.09 35.45 C 511.6 35.45 513.64 37.49 513.64 40 C 513.64 42.51 511.6 44.55 509.09 44.55 C 506.58 44.55 504.55 42.51 504.55 40 C 504.55 37.49 506.58 35.45 509.09 35.45 Z M 509.09 46.36 C 512.6 46.36 515.45 43.51 515.45 40 C 515.45 36.49 512.6 33.64 509.09 33.64 C 505.58 33.64 502.73 36.49 502.73 40 C 502.73 43.51 505.58 46.36 509.09 46.36 Z M 510.76 22.28 L 514.16 29.09 L 507.35 29.09 Z M 505.88 30.91 L 515.63 30.91 C 515.95 30.91 516.24 30.75 516.41 30.48 C 516.57 30.21 516.59 29.88 516.45 29.59 L 511.57 19.84 C 511.26 19.22 510.25 19.22 509.94 19.84 L 505.06 29.59 C 504.92 29.88 504.94 30.21 505.1 30.48 C 505.27 30.75 505.56 30.91 505.88 30.91 Z M 490.91 37.27 L 490.91 29.09 L 499.09 29.09 L 499.09 37.27 Z M 490 39.09 L 500 39.09 C 500.5 39.09 500.91 38.68 500.91 38.18 L 500.91 28.18 C 500.91 27.68 500.5 27.27 500 27.27 L 490 27.27 C 489.5 27.27 489.09 27.68 489.09 28.18 L 489.09 38.18 C 489.09 38.68 489.5 39.09 490 39.09 Z M 481.82 42.73 L 483.64 42.73 L 483.64 44.55 L 480.91 44.55 C 480.41 44.55 480 44.14 480 43.64 L 480 10.91 C 480 10.41 480.41 10 480.91 10 L 513.64 10 C 514.14 10 514.55 10.41 514.55 10.91 L 514.55 13.64 L 512.73 13.64 L 512.73 11.82 L 481.82 11.82 Z M 487.27 48.18 L 487.27 17.27 L 518.18 17.27 L 518.18 48.18 Z M 519.09 15.45 L 486.36 15.45 C 485.86 15.45 485.45 15.86 485.45 16.36 L 485.45 49.09 C 485.45 49.59 485.86 50 486.36 50 L 519.09 50 C 519.59 50 520 49.59 520 49.09 L 520 16.36 C 520 15.86 519.59 15.45 519.09 15.45 Z" fill="#e7157b" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 57px; margin-left: 500px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="500" y="69" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Account&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="400" y="90" width="40" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 427.27 113.64 C 430.28 113.64 432.73 116.08 432.73 119.09 C 432.73 122.1 430.28 124.55 427.27 124.55 C 424.26 124.55 421.82 122.1 421.82 119.09 C 421.82 116.08 424.26 113.64 427.27 113.64 Z M 427.27 126.36 C 431.28 126.36 434.55 123.1 434.55 119.09 C 434.55 115.08 431.28 111.82 427.27 111.82 C 423.26 111.82 420 115.08 420 119.09 C 420 123.1 423.26 126.36 427.27 126.36 Z M 429.09 96.58 L 433.53 105.45 L 424.65 105.45 Z M 423.18 107.27 L 435 107.27 C 435.32 107.27 435.61 107.11 435.77 106.84 C 435.94 106.57 435.95 106.24 435.81 105.96 L 429.9 94.14 C 429.6 93.52 428.59 93.52 428.28 94.14 L 422.37 105.96 C 422.23 106.24 422.24 106.57 422.41 106.84 C 422.57 107.11 422.87 107.27 423.18 107.27 Z M 406.36 114.55 L 406.36 104.55 L 416.36 104.55 L 416.36 114.55 Z M 405.45 116.36 L 417.27 116.36 C 417.78 116.36 418.18 115.96 418.18 115.45 L 418.18 103.64 C 418.18 103.13 417.78 102.73 417.27 102.73 L 405.45 102.73 C 404.95 102.73 404.55 103.13 404.55 103.64 L 404.55 115.45 C 404.55 115.96 404.95 116.36 405.45 116.36 Z M 401.82 128.18 L 401.82 91.82 L 438.18 91.82 L 438.18 128.18 Z M 439.09 90 L 400.91 90 C 400.41 90 400 90.41 400 90.91 L 400 129.09 C 400 129.59 400.41 130 400.91 130 L 439.09 130 C 439.59 130 440 129.59 440 129.09 L 440 90.91 C 440 90.41 439.59 90 439.09 90 Z" fill="#e7157b" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 137px; margin-left: 420px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;OU&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="420" y="149" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;OU&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="480" y="90" width="40" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 509.09 115.45 C 511.6 115.45 513.64 117.49 513.64 120 C 513.64 122.51 511.6 124.55 509.09 124.55 C 506.58 124.55 504.55 122.51 504.55 120 C 504.55 117.49 506.58 115.45 509.09 115.45 Z M 509.09 126.36 C 512.6 126.36 515.45 123.51 515.45 120 C 515.45 116.49 512.6 113.64 509.09 113.64 C 505.58 113.64 502.73 116.49 502.73 120 C 502.73 123.51 505.58 126.36 509.09 126.36 Z M 510.76 102.28 L 514.16 109.09 L 507.35 109.09 Z M 505.88 110.91 L 515.63 110.91 C 515.95 110.91 516.24 110.75 516.41 110.48 C 516.57 110.21 516.59 109.88 516.45 109.59 L 511.57 99.84 C 511.26 99.22 510.25 99.22 509.94 99.84 L 505.06 109.59 C 504.92 109.88 504.94 110.21 505.1 110.48 C 505.27 110.75 505.56 110.91 505.88 110.91 Z M 490.91 117.27 L 490.91 109.09 L 499.09 109.09 L 499.09 117.27 Z M 490 119.09 L 500 119.09 C 500.5 119.09 500.91 118.68 500.91 118.18 L 500.91 108.18 C 500.91 107.68 500.5 107.27 500 107.27 L 490 107.27 C 489.5 107.27 489.09 107.68 489.09 108.18 L 489.09 118.18 C 489.09 118.68 489.5 119.09 490 119.09 Z M 481.82 122.73 L 483.64 122.73 L 483.64 124.55 L 480.91 124.55 C 480.41 124.55 480 124.14 480 123.64 L 480 90.91 C 480 90.41 480.41 90 480.91 90 L 513.64 90 C 514.14 90 514.55 90.41 514.55 90.91 L 514.55 93.64 L 512.73 93.64 L 512.73 91.82 L 481.82 91.82 Z M 487.27 128.18 L 487.27 97.27 L 518.18 97.27 L 518.18 128.18 Z M 519.09 95.45 L 486.36 95.45 C 485.86 95.45 485.45 95.86 485.45 96.36 L 485.45 129.09 C 485.45 129.59 485.86 130 486.36 130 L 519.09 130 C 519.59 130 520 129.59 520 129.09 L 520 96.36 C 520 95.86 519.59 95.45 519.09 95.45 Z" fill="#e7157b" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 137px; margin-left: 500px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="500" y="149" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Account&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="480" y="170" width="40" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 509.09 195.45 C 511.6 195.45 513.64 197.49 513.64 200 C 513.64 202.51 511.6 204.55 509.09 204.55 C 506.58 204.55 504.55 202.51 504.55 200 C 504.55 197.49 506.58 195.45 509.09 195.45 Z M 509.09 206.36 C 512.6 206.36 515.45 203.51 515.45 200 C 515.45 196.49 512.6 193.64 509.09 193.64 C 505.58 193.64 502.73 196.49 502.73 200 C 502.73 203.51 505.58 206.36 509.09 206.36 Z M 510.76 182.28 L 514.16 189.09 L 507.35 189.09 Z M 505.88 190.91 L 515.63 190.91 C 515.95 190.91 516.24 190.75 516.41 190.48 C 516.57 190.21 516.59 189.88 516.45 189.59 L 511.57 179.84 C 511.26 179.22 510.25 179.22 509.94 179.84 L 505.06 189.59 C 504.92 189.88 504.94 190.21 505.1 190.48 C 505.27 190.75 505.56 190.91 505.88 190.91 Z M 490.91 197.27 L 490.91 189.09 L 499.09 189.09 L 499.09 197.27 Z M 490 199.09 L 500 199.09 C 500.5 199.09 500.91 198.68 500.91 198.18 L 500.91 188.18 C 500.91 187.68 500.5 187.27 500 187.27 L 490 187.27 C 489.5 187.27 489.09 187.68 489.09 188.18 L 489.09 198.18 C 489.09 198.68 489.5 199.09 490 199.09 Z M 481.82 202.73 L 483.64 202.73 L 483.64 204.55 L 480.91 204.55 C 480.41 204.55 480 204.14 480 203.64 L 480 170.91 C 480 170.41 480.41 170 480.91 170 L 513.64 170 C 514.14 170 514.55 170.41 514.55 170.91 L 514.55 173.64 L 512.73 173.64 L 512.73 171.82 L 481.82 171.82 Z M 487.27 208.18 L 487.27 177.27 L 518.18 177.27 L 518.18 208.18 Z M 519.09 175.45 L 486.36 175.45 C 485.86 175.45 485.45 175.86 485.45 176.36 L 485.45 209.09 C 485.45 209.59 485.86 210 486.36 210 L 519.09 210 C 519.59 210 520 209.59 520 209.09 L 520 176.36 C 520 175.86 519.59 175.45 519.09 175.45 Z" fill="#e7157b" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 217px; margin-left: 500px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;Account&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="500" y="229" fill="#232F3E" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Account&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="10" width="40" height="40" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 283.26 49.36 C 283.26 49.36 283.26 49.36 283.26 49.36 Z M 283.46 49.48 C 281.5 49.3 280 47.66 280 45.71 C 280 43.75 281.5 42.12 283.46 41.94 L 304.02 41.94 C 302.97 42.91 302.38 44.28 302.38 45.71 C 302.38 47.14 302.97 48.5 304.02 49.48 Z M 310.57 43.88 C 311.58 45.7 310.91 47.99 309.09 48.99 C 307.26 50 304.96 49.35 303.94 47.54 C 302.92 45.72 303.56 43.44 305.38 42.41 L 305.78 40.99 L 284.66 40.99 L 284.66 15.95 C 284.66 12.66 287.34 10 290.65 10 L 316.6 10 C 314.92 10.07 313.33 10.77 312.13 11.94 C 311.12 12.99 310.56 14.38 310.57 15.83 Z M 301.99 37.02 L 301.99 35.83 L 289.57 35.83 L 289.57 37.1 Z M 305.78 33.41 L 305.78 32.18 L 289.73 32.18 L 289.73 33.41 Z M 305.78 29.68 L 305.78 28.41 L 289.73 28.41 L 289.73 29.68 Z M 305.78 26.03 L 305.78 24.72 L 289.73 24.72 L 289.73 25.95 Z M 305.78 22.3 L 305.78 21.03 L 289.65 21.03 L 289.65 22.3 Z M 305.78 18.61 L 305.78 16.15 L 289.73 16.15 L 289.73 18.61 Z M 312.13 17.86 C 312.07 16.51 312.38 15.18 313.05 14.01 C 313.63 13.05 314.48 12.28 315.48 11.79 C 316.8 11.53 318.15 11.98 319.04 12.98 C 320 14.47 319.96 16.41 318.92 17.86 Z" fill="#505050" stroke="none" pointer-events="all"&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 flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 57px; margin-left: 300px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&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; white-space: nowrap;"&gt;Service Control Policy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="300" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Servic&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 110 L 140 110 L 200 30 L 278.43 30" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 283.68 30 L 276.68 33.5 L 278.43 30 L 276.68 26.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 21px; margin-left: 201px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;preventive control&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="201" y="24" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;preventive control&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 110 L 273.63 110" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 278.88 110 L 271.88 113.5 L 273.63 110 L 271.88 106.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 101px; margin-left: 201px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;detective control&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="201" y="104" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;detective control&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 110 L 140 110 L 200 190 L 273.63 190" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 278.88 190 L 271.88 193.5 L 273.63 190 L 271.88 186.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&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: 200px; margin-left: 200px;"&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: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;proactive control&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="200" y="204" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;proactive control&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 320 190 L 340 190 L 395.5 134.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 399.21 130.79 L 396.73 138.22 L 395.5 134.5 L 391.78 133.27 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 320 110 L 393.63 110" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 398.88 110 L 391.88 113.5 L 393.63 110 L 391.88 106.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 310.6 30 L 340 30 L 395.5 85.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 399.21 89.21 L 391.78 86.73 L 395.5 85.5 L 396.73 81.78 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 440 90 L 480 50" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 440 110 L 480 110" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 440 130 L 480 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&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.drawio.com/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 detective controls only captures and reports violations. The preventive controls stops the violating API request. The proactive controls remediates the detected violations. Another way to look at the controls is by considering their priority:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Mandatory controls: there are about 23 &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/mandatory-controls.html"&gt;mandatory controls&lt;/a&gt; that are enforced on each account. As soon as a new workload account becomes part of Control Tower, these controls will come enforced. &lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/optional-controls.html"&gt;Optional controls&lt;/a&gt; including &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/strongly-recommended-controls.html"&gt;strongly recommended&lt;/a&gt; controls and &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/elective-controls.html"&gt;Elective controls&lt;/a&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many existing controls and AWS is still releasing new one (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/10/aws-control-tower-proactive-controls-aws-security-hub-detective-controls/"&gt;example&lt;/a&gt;). AWS Config service uses conformance pack to organize relevant controls. For example, there are sample conformance packs such as &amp;#8220;Operational Best Practices for &amp;#8220;NIST 800 53 rev4&amp;#8221; and &amp;#8220;Security Best Practices for EKS&amp;#8221;. In addition, you can even build your own conformance pack. Once you deploy a conformance pack, AWS config deploys additional rules against the current environment. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The third impression is that Control Tower falls short with infrastructure automation. It provides a number of customization mechanism to leave infrastructure (mostly networking) automation with users. I will explain later.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Mandatory Accounts&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The function of Control Tower does not depend on a specific account structure that aligns with &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/organizing-your-aws-environment/organizing-your-aws-environment.html"&gt;the multi-account best practice&lt;/a&gt;. However it does require three &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/accounts.html"&gt;mandatory accounts&lt;/a&gt; to set up automatically when creating control tower. The account where Control Tower is configured is the management account. The log archive account is the owner of S3 buckets that hosts buckets for loggings. The audit account is a restricted account that&amp;#8217;s designed to give your security and compliance teams read and write access to all accounts in your landing zone. We also use this account as delegated administrator account for several security services at organization level. I&amp;#8217;ll elaborate in the next section.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The management account is directly under root OU and there is no SCPs applied to the management account. Also Control Tower does not turn on AWS Config recorder and configure delivery channel on the management account itself. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The log archive account and audit account are under the Security OU (or otherwise named during the setup). At the end of Control Tower setup a set of &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/mandatory-controls.html"&gt;mandatory controls&lt;/a&gt; will be applied at the OU level and thus effective to each account. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the mandatory accounts, oftentimes there is an Infrastructure OU. Typically we place shared services account under this OU. The account is for common services such as central networking, managed Active Directory, DevOps pipeline etc. Another optional OU is workload OU, where we can create new workload accounts or enrol existing workload accounts.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each enrolled workload account have its CloudTrail configured with an organization-level trail, which sends log to the central bucket that log archive account owns. Each enrolled account also has AWS Config &lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/stop-start-recorder.html"&gt;recorder&lt;/a&gt; configured with a &lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/manage-delivery-channel.html"&gt;delivery channel&lt;/a&gt; pointing to the configuration log bucket also owned by log archive account. Note that Control Tower does not turn on recorder on the management account itself.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Account Enrolment&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Most likely a customer is already in use of AWS Organization. They can delegate one account as management account, and use Control Tower console to create a landing zone for the intended &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/region-how.html"&gt;regions&lt;/a&gt;. During the creation, they will have to create two additional account, one as log archive account, and the other as security tooling or audit account. This is in alignment with multi-account best practices. After the creation of landing zone, the two new account are automatically enrolled in Control Tower.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Going forward, the client should create new accounts using Control Tower&amp;#8217;s Account Factory to save a separate enrolment step. For existing accounts and OUs we&amp;#8217;ll need to enroll them into Control Tower. At the beginning, the Control Tower Landing Zone (CTLZ) only has the mandatory controls, which are the bare minimum governance. When we enroll existing OUs and accounts, these controls (mostly detective and preventive) will extend to the newly enrolled OUs or accounts. Therefore, it is important to not introduce numerous controls prior to having all accounts and OUs enrolled. It is important to have a proper OU hierarchy upfront because both detective and preventive controls (Config Rules and SCPs) are typically applied at OU level and effective to all the children of the OU.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To enroll an existing account, the account must meet some &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/enroll-account.html"&gt;prerequisites&lt;/a&gt;. An IAM role with cross-account trust must be manually created. Since each account can have only one AWS Config configuration recorder and delivery channel, if an existing account already has one of them, they must be manually deleted using CLI.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Account Factory defines what happens when we create a new account and it is where we can bake in the customizations. When it comes to infrastructure automation, Control Tower leaves pretty much everything up to the account factory customization.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Delegated Administrator&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Many AWS services are available as &lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_integrate_services_list.html"&gt;organization wide service&lt;/a&gt;. For example, CloudTrail, GuardDuty and even AWS Organization. Most of these organization level services allow you to specify one account as delegated administrator for the entire organization. For example, for System Manager and Service Catalog, the best practice is to designate an operation account or shared services account as the delegated administrator. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are several security-related, organization-wide services, such as GuardDuty, Detective, Macie, Security Hub, Inspector, Audit Manager, and Firewall Manager. The best practices for these services are to designate the audit account as the delegated administrator. We think of the audit account as an aggregation point (or points for organizations that split the functionality across multiple accounts) for these AWS services. You can find this recommendation from the documentation of each service. However, currently Control Tower does not enforce this best practice. Even though Control Tower dashboard gives you visibility to security services such as GuardDuty and SecurityHub, you are still on your own to configure these services outside of Control Tower.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Whether you had never used these services, or you had previously designated another account as administrator for these services, it is good time to align with the best practice for delegated administrator right after Control Tower setup. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you had previously designated other accounts, you need to take additional steps to revoke the old delegation and designate new delegated administrator account. These general steps can be performed from console or CLI and apply to all these services:&lt;/p&gt;&#10;&lt;ol style="list-style-type:1" class="wp-block-list"&gt;&#10;&lt;li&gt;From the old administrator account, remove all the members&lt;/li&gt;&#10;&lt;li&gt;From the management account, revoke delegation&lt;/li&gt;&#10;&lt;li&gt;From the management account, delegate new administrator account (audit account)&lt;/li&gt;&#10;&lt;li&gt;From the new administrator account, invite all the members, and make sure to tell the service to automatically include new accounts going forward&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that when you invite an account, that account must not have created, invited or associated with other accounts (as a member). In other words, we should never let a non-delegated administrator account create, invite or associate other account as member.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Control Tower Customization&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Control Tower by itself does not play a big role in networking automation. You can create a VPC when provisioning a new account in Control Tower. That&amp;#8217;s about what you can do. It does not address how the new VPC connects to existing networking space. It does not distinguish the existing VPC topology. To address this, Control Tower has a few customization options:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-very-light-gray-to-cyan-bluish-gray-gradient-background has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;AFC&lt;/strong&gt;&lt;br&gt;Account Factory Customization&lt;/th&gt;&lt;th&gt;&lt;strong&gt;AFT&lt;/strong&gt;&lt;br&gt;Account Factory Terraform&lt;/th&gt;&lt;th&gt;&lt;strong&gt;CfCT&lt;/strong&gt;&lt;br&gt;Customizations for Control Tower&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Supported IaC language&lt;/td&gt;&lt;td&gt;Anything that Service Catalog Support (e.g. CloudFormation and Terraform HCL)&lt;/td&gt;&lt;td&gt;Terraform HCL&lt;/td&gt;&lt;td&gt;CloudFormation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Readiness&lt;/td&gt;&lt;td&gt;AFC is a native mechanism. You can specify the Service Catalog product during creation of an Account in Control Tower console.&lt;/td&gt;&lt;td&gt;Users need to first bootstrap the solution, which creates Terraform pipelines and other components.&lt;/td&gt;&lt;td&gt;Users need to first bootstrap the solution which creates a pipeline and other components.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;How it works&lt;/td&gt;&lt;td&gt;Control Tower launches the specified Service Catalog product (aka blueprint) during account creation.&lt;/td&gt;&lt;td&gt;Quite complex. Explained in the section below&lt;/td&gt;&lt;td&gt;Control Tower posts a lifecycle events to Amazon EventBridge. A lambda function will process the event using pre-baked CloudFormation templates and Step Functions.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Prebuilt customizations&lt;/td&gt;&lt;td&gt;No. However, for certain products, there are some partner-maintained blueprints&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AFC is fairly straightforward to use based on &lt;a href="https://aws.amazon.com/blogs/mt/automate-account-customization-using-account-factory-customization-in-aws-control-tower/"&gt;this&lt;/a&gt; post, as long as you know Service Catalog. It is a native capability of Control Tower. The other two ways for CT landing zone customization are not native capability and each require a separate bootstrapping process to deploy the pipeline-based solution.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Customizations for Control Tower&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In CfCT, we first bootstrap the solution from management account, using &lt;a href="https://github.com/aws-solutions/aws-control-tower-customizations/blob/main/customizations-for-aws-control-tower.template"&gt;this&lt;/a&gt; CloudFormation template on GitHub. We usually need to version control the custom configuration so we should select &amp;#8220;AWS CodeCommit&amp;#8221; as the value for the CodePipelineSource parameter. The bootstrapping (execution of this CloudFormation template) takes about 5 minutes.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="534" src="https://www.digihunch.com/wp-content/uploads/2023/08/cfct.webp" alt="" class="wp-image-12952" srcset="https://www.digihunch.com/wp-content/uploads/2023/08/cfct.webp 1024w, https://www.digihunch.com/wp-content/uploads/2023/08/cfct-300x156.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/08/cfct-768x401.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The solution is based on Control Tower&amp;#8217;s lifecycle event to trigger the pipeline with a source code repository (or S3 bucket). The source code repository (AWS CodeCommit) stores the &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/cfct-custom-configuration.html"&gt;custom configuration&lt;/a&gt;. A custom configuration consists of a manifest file in YAML format, made up with a number of resource sections. Each resource section references an artifact, either a CloudFormation template or a policy file to apply. The artifact can either be stored in the same repository, or in a remote S3 bucket. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Account Factory Terraform&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://aws.amazon.com/blogs/aws/new-aws-control-tower-account-factory-for-terraform/"&gt;Introduced&lt;/a&gt; in late 2021, the AFT solution is based on the &lt;a href="https://github.com/aws-ia/terraform-aws-control_tower_account_factory"&gt;terraform-aws-control_tower_account_factory&lt;/a&gt; repository. It is too complex as a solution in my opinion. At the beginning you need to have a designated OU and account for AFT and install the solution itself with Terraform. Note that, the solution does not address the state storage of the installation of the solution itself (chicken-or-egg). The installation also installed a few IAM roles required on the AFT management account. Having a landing zone management account and a AFT management account is not a neat setup.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once installed, the solution consists of four repositories. Their purposes are as follows (copied from workshop instruction):&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Account requests &amp;#8211; handles placing or updating account requests. See&amp;nbsp;&lt;a href="https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-account-request"&gt;example here&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;AFT account provisioning customizations &amp;#8211; manages non-Terraform customizations that are applied to all accounts. This stage runs before the global customizations stage. Examples available&amp;nbsp;&lt;a href="https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-account-provisioning-customizations"&gt;here&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Global customizations &amp;#8211; Global customizations – manages Terraform-based customizations that are applied to all accounts created by and managed with AFT.&amp;nbsp;&lt;a href="https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-global-customizations"&gt;Examples available&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Account customizations &amp;#8211; Account customizations – manages Terraform-based customizations that are applied only to specific accounts created by and managed with AFT.&amp;nbsp;&lt;a href="https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-account-customizations"&gt;Examples available&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With this solution in place, when we create a new account via account request repo, quite a number of steps will happen after that, as the workshop instruction illustrated:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="341" src="https://www.digihunch.com/wp-content/uploads/2023/08/aft.webp" alt="" class="wp-image-12953" srcset="https://www.digihunch.com/wp-content/uploads/2023/08/aft.webp 1024w, https://www.digihunch.com/wp-content/uploads/2023/08/aft-300x100.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/08/aft-768x256.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When I first looked into how this work I&amp;#8217;m very concerned about the maintainability of this &amp;#8220;solution&amp;#8221;. The amount of services and pipelines involved in this solution makes it difficult to troubleshoot end to end. To improve that AWS even added an enhancement for &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/02/tracing-customizations-aws-control-tower-account-factory-terraform/"&gt;request tracing&lt;/a&gt;. Nonetheless, I would not go with AFT just with how complex it looks like. For more details, check out the Control Tower workshop.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Control Tower helps you set up a Landing Zone without network infrastructure automation. It provides a couple of customization mechanisms, allowing you to bake in your own infrastructure automation. To build a landing zone, you can go with Control Tower in the following situations:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;you have general regulatory requirement and need to enable governance capabilities&lt;/li&gt;&#10;&lt;li&gt;your networking stack isn&amp;#8217;t centrally managed or isn&amp;#8217;t large enough to warrant infrastructure automation&lt;/li&gt;&#10;&lt;li&gt;If you do need networking automation, then you will need the customization capability of Control Tower. In this case, your team should have good handle on infrastructure as code (CloudFormation or Terraform)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of the customization options, AFC seems the least complex to me. The other two, especially AFT, is too complex. We use pipelines to trail and error with other stacks, and the pipeline solution itself must be simple.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, if you environment has strong regulatory requirement, or you are seeking a prescriptive network architecture, or your team does not have the capacity with CloudFormation or Terraform Templates, consider Landing Zone Accelerator on AWS.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2023/07/authenticate-kube-apiserver-via-oidc/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Authentication to kube-apiserver via OIDC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2023/09/orchestrate-landing-zone-with-landing-zone-accelerator-on-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Orchestrate Landing Zone with Landing Zone Accelerator on AWS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Landing Zone in Azure – Introduction</title><link>https://www.digihunch.com/2023/03/landing-zone-in-azure/</link><pubDate>Sat, 25 Mar 2023 01:30:00 -0400</pubDate><guid>https://www.digihunch.com/2023/03/landing-zone-in-azure/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-az-lz.webp" alt="Featured image of post Landing Zone in Azure – Introduction" /&gt;&lt;p class="wp-block-paragraph"&gt;I recently renewed my associate administrator certification, and feel it&amp;#8217;s a good opportunity to brush up on Azure landing zone. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The lame part of this is the semantics. I found many similar terms across cloud service provider (CSPs). In the context of Azure, it makes sense to clarify the terms again for Cloud Adoption Framework (CAF) and Cloud Operating Models.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-cloud-adoption-framework-caf"&gt;Cloud Adoption Framework (CAF)&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similar to AWS Cloud Adoption Framework (CAF), Azure also has the concept of CAF and it means the same thing. This part may feel lofty, but it&amp;#8217;s in fact foundational. To get started on the cloud there are thousand ways to configure the foundation (right or wrong). The adopter needs CAF to navigate through the offerings and define what they can achieve. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/"&gt;CAF&lt;/a&gt; documentation is good although length. The most &amp;#8220;beefy&amp;#8221; part is &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/"&gt;Ready&lt;/a&gt; section. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cloud Operating Models&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Every cloud company has some narrative about cloud operating model. For example, Here&amp;#8217;s &lt;a href="https://www.hashicorp.com/en/on-demand/enabling-a-cloud-operating-model"&gt;Hashicorp&amp;#8217;s&lt;/a&gt; definition, and here&amp;#8217;s &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/building-cloud-operating-model/building-cloud-operating-model.html"&gt;AWS&lt;/a&gt;&amp;#8216; white paper on it. In the context of Azure, the CAF document gives some guidance on developing your own operating model in alignment with the CAF. In addition, it also gives a few example cloud operating models:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Decentralized operations&lt;/li&gt;&#10;&lt;li&gt;Centralized operations&lt;/li&gt;&#10;&lt;li&gt;Enterprise operations&lt;/li&gt;&#10;&lt;li&gt;Distributed operations&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is a &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/operating-model/compare#priorities-or-scope"&gt;comparison table&lt;/a&gt; that highlights their differences as well as an &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/operating-model/compare#accountability-alignment"&gt;accountability chart&lt;/a&gt; proposing team divisions. Another &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/operating-model/compare#accelerate-operating-model-implementation-in-azure"&gt;insightful table&lt;/a&gt; is the one that list out implementation starting point and typical path of iterations for each operating model. The table also suggests that Azure Landing Zone includes two implementation options: starting small and CAF enterprise-scale.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Landing Zone at High Level&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Followed by Cloud Operating Model is the design and implementation of Azure Landing Zone. There are currently eight design areas:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Billing and Active Directory tenant: including Azure AD tenant&lt;/li&gt;&#10;&lt;li&gt;Identity and Access Management: including hybrid identity&lt;/li&gt;&#10;&lt;li&gt;Network Topology and Connectivity&lt;/li&gt;&#10;&lt;li&gt;Resource Organization: different levels of resource containers&lt;/li&gt;&#10;&lt;li&gt;Security&lt;/li&gt;&#10;&lt;li&gt;Management&lt;/li&gt;&#10;&lt;li&gt;Governance&lt;/li&gt;&#10;&lt;li&gt;Platform automation and DevOps&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of the many design areas, I fell short off IAM and Network so I&amp;#8217;ll try to discuss them in more details below in the next section. As for resource organization, apart from Resource Group and Subscription, it is also important to understand management group.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Most cloud engineers work with subscription and resource group. That is where a lot are going on. For enterprises however, Azure has to address the requirement for the capability of top-down enforcement. Management Group provides a governance scope above subscriptions, provided that all subscriptions trust a single Azure AD account. Management groups may form a hierarchy of up to six levels to help you configure policies and access, so that the all the subscriptions under each management group have unified policy and access configuration. At the very top is root management group. Any assignment of user access or policy on the root management group applies to all resources within the directory. Because of this, all customers should evaluate the need to have items defined on this scope.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can apply policy guardrails (e.g Azure Policy) at management group level so that the policies are effect across subscriptions. Azure Policy can also address operational compliance considerations by &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/management-operational-compliance#monitor-for-configuration-drift"&gt;monitoring configuration drift&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Identity and Access Management&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, we really need to distinguish &lt;strong&gt;AD DS on Windows Server&lt;/strong&gt;, &lt;strong&gt;Azure AD&lt;/strong&gt; and &lt;strong&gt;Azure AD DS&lt;/strong&gt;. In an &lt;a href="https://www.digihunch.com/2020/02/everything-about-the-domain/"&gt;old post&lt;/a&gt;, I discussed what is a Windows domain, the key role of a domain controller (to manage user identity, as well computer identity), and the fact that Active Directory is a complete redesign of Windows Domain system since Windows 2000. So we can start with AD DS on Windows Server:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;AD DS on Windows Server&lt;/strong&gt;: In the good old days, some common network administrative activities were to configure Active Directory (including the X.500 compatible database, the OUs, domains and forests) on Windows Servers, joining computers to the company&amp;#8217;s domain, configure group policy, configure LDAP and Kerberos, upgrading Domain controllers, etc. Over the years, Microsoft moved these activities to the cloud and offer them as a managed service, known as Azure AD DS.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Azure Active Directory Domain Service (Azure AD DS)&lt;/strong&gt;: allows you to use managed domain services (e.g. Windows Domain Join, group policy, LDAP, Kerberos authentication) without having to deploy, manage or patch domain controllers. It is a SaaS offering to manage your domain controllers in the cloud, with a pay-as-you-go model. The counterpart in AWS is &amp;#8220;AWS Directory Service&amp;#8221; which lets you run Microsoft Active Directory (AD) as an AWS managed service.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, both AD DS on Windows Server (self-hosted) and Azure AD DS (managed service) are identity stores that operates on Windows domains. Even though the latter is a managed service, it supports LDAP or Kerberos as integration protocol for third party applications (usually on-premis) to use. Both LDAP and Kerberos came around prior to the cloud era and they are not optimized for cloud connectivity. For example, insecure bind (on port 389) in &lt;a href="https://www.digihunch.com/2020/03/lightweight-directory-access-protocol-ldap/"&gt;LDAP&lt;/a&gt; is still prevalent. &lt;a href="https://www.digihunch.com/2020/03/ntlm-and-kerberos/"&gt;Kerberos&lt;/a&gt; is fairly complex to configure. However, they are not phased out right away because of their established presence as well as the domain&amp;#8217;s awareness to authenticate devices. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Many organizations have to keep their domain service and when they move to cloud so they still have to use Active Directory as identity store. For this, Azure has Azure AD connect. On the AWS side, there is also an &lt;a href="https://docs.aws.amazon.com/directoryservice/latest/admin-guide/directory_ad_connector.html"&gt;AD connector&lt;/a&gt; tool to allow on-prem users to log into AWS applications and services. With AD connector you can also join EC2 instance to existing AD domain. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now let&amp;#8217;s examine Azure AD.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Azure AD: is an IAM solution. It contains an identity store (with users and groups in a flat directory structure) but more importantly it integrates with external identity stores (including Domain Service, self-hosted or SaaS managed), which gives it hybrid-identity capability. A company can even sync their own on-prem identity store to Azure AD using Azure AD Connect. As an IAM solution, Azure AD also allows a company to tie their identity store to applications using modern protocols such as SAML and OAuth. Azure AD treats applications as objects, and they can represents either Microsoft Applications (Office 365, Dynamics 365, Azure) or third-party ones (Slack, Salesforce) as long as they use the supported protocol for SSO. The closest AWS counterpart of Azure AD is Amazon Cognito (arguably), even though their capabilities are not identical in every aspect.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Compared to Domain Service, Azure AD alone doesn&amp;#8217;t have the concept of domain. Therefore you cannot join a server or PC to a domain and configure group policy. Azure AD&amp;#8217;s native identity store is a flat directory structure without OUs or forests. Azure AD is NOT a replacement of domain service, either self-hosted or managed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now coming back to the Azure landing zone literature, the &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/identity-access-active-directory-hybrid-identity"&gt;document&lt;/a&gt; lays out the key decision to make about identity:&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A critical design decision for enterprise organizations adopting Azure is whether to extend current on-premises identity domains into Azure or to create new identity domains.&lt;/p&gt;&#10;&lt;cite&gt;Azure Active Directory (Azure AD) and hybrid identity&lt;/cite&gt;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The document even includes a comprehensive &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/decision-guides/identity/"&gt;identity decision guide&lt;/a&gt;. After this decision, we&amp;#8217;ll know what identity store to use. Then we can address the problem of platform access vs workload access. In other words, IAM of management traffic vs business traffic, which opens up topics such as RBAC, service principle and managed identities.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Networking&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Back in 2017, Azure published a &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/networking/architecture/hub-spoke"&gt;white paper&lt;/a&gt; about V-Net and it focuses on mesh network and hub-and-spoke. Back then Azure customers run multiple lines of business (LOB) on different V-Nets. The V-Net peering feature allows early cloud adopters to organize all their V-Nets in a mesh topology, ensuring all peers have access to all other peers, or a hub-and-spoke topology to aggregate shared resources in hubs so they can be shared by the spokes in the network.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When setting up a landing zone, network topology is a big decision. In the landing zone document today, clients need to consider the followings: &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Traditional Azure networking topologies, including:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;large flat V-Net&lt;/li&gt;&#10;&lt;li&gt;multiple V-Nets connected with multiple Azure ExpressRoute circuits/connections&lt;/li&gt;&#10;&lt;li&gt;hub-and-spoke &lt;/li&gt;&#10;&lt;li&gt;full mesh&lt;/li&gt;&#10;&lt;li&gt;hybrid&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Microsoft managed networking topology (on top of Virtual WAN)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From the 2017 white paper, most organizations at that time solve their need for network isolation and connectivity by creating a mesh architecture among various V-Nets. All nodes in the network are interconnected so network traffic is fast and can be easily redirected. However, mesh topology has significant disadvantages because it requires too many connections as the footprint expands, making it very costly to operate and quick to reach limit of number of peering links. It is not scalable. The white paper is to advocate the use of hub-and-spoke topology, which I will discuss in the next section.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is worth-noting that, today (Jan 2023) one can create both topologies with &lt;a href="https://learn.microsoft.com/en-us/azure/virtual-network-manager/"&gt;Azure Virtual Network Manager&lt;/a&gt;. It is currently a preview service but I can foresee it will eventually get integrated with landing zone.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;No matter which topology, another issue to address is &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/hybrid-networking/#hub-spoke-network-topology"&gt;connectivity to on-prem network&lt;/a&gt;, and to Azure &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/connectivity-to-azure-paas-services"&gt;PaaS services&lt;/a&gt;. If the traffic is light, we can use VPN gateway to configure IPSec tunnel that goes over public internet encrypted. It is simple to configure with a good aggregate bandwidth. This connection requires a VPN device on premise as well. A faster alternative is Azure &lt;a href="https://learn.microsoft.com/en-us/azure/expressroute/"&gt;ExpressRoute&lt;/a&gt;, which runs a private connection with a third-party connectivity provider. &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/hybrid-networking/#azure-expressroute-connection"&gt;ExpressRoute&lt;/a&gt; is more complex and expensive to set up, but it supports much higher bandwidth with direct access and better SLA. In reality, many clients configures ExpressRoute with VPN failover for connectivity to on-prem network. For &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/connectivity-to-azure-paas-services"&gt;connectivity to PaaS services&lt;/a&gt;, options are service endpoint and private link endpoint.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Hub-and-spoke topology&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/traditional-azure-networking-topology"&gt;traditional topologies&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/hub-spoke-network-topology"&gt;hub-and-spoke&lt;/a&gt; network topology is popular as the hub network provides a central point of management. Also it overcomes &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#networking-limits"&gt;subscription limits&lt;/a&gt; and institutes a separation of concerns. The Azure documentation recommends &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/hybrid-networking/hub-spoke?tabs=cli"&gt;hub-and-spoke architecture &lt;/a&gt;for larger cloud adoption efforts. If the footprint is even massive, we can even extend the model to a cluster of hubs and spokes. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="677" height="452" src="https://www.digihunch.com/wp-content/uploads/2023/03/azure-hub-spoke.webp" alt="" class="wp-image-12907" srcset="https://www.digihunch.com/wp-content/uploads/2023/03/azure-hub-spoke.webp 677w, https://www.digihunch.com/wp-content/uploads/2023/03/azure-hub-spoke-300x200.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/03/azure-hub-spoke-410x275.webp 410w" sizes="auto, (max-width: 677px) 100vw, 677px" /&gt;&lt;figcaption class="wp-element-caption"&gt;A cluster of multiple hub-and-spoke&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can connect multiple hubs using:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;V-Net peering&lt;/li&gt;&#10;&lt;li&gt;Azure ExpressRoute&lt;/li&gt;&#10;&lt;li&gt;Azure Virtual WAN&lt;/li&gt;&#10;&lt;li&gt;Site-to-site VPN&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Within a single hub-and-spoke model, the Hub V-Net hosts shared services and acts as central point of connectivity (to many spoke V-Nets). Often in the Hub V-Net are Azure Bastion, Azure Firewall and VPN Gateway or ExpressRoute gateway. The spoke V-Nets (in same or different subscriptions) isolates and manage workloads in prod, non-prod, etc. Since a single V-Net cannot traverse subscription boundaries, you have to use V-Net peering (preferred), ExpressRoute circuit, or VPN Gateways. V-Net peering works across regions, and across Azure AD tenants. It is low-latency but isn&amp;#8217;t transitive.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In some cases we also configure &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/perimeter-networks"&gt;perimeter networks&lt;/a&gt; (aka DMZs) in the hub-and-spoke architecture, to handle external traffic. Perimeter networks host services such as External Load balancer, &lt;a href="https://learn.microsoft.com/en-us/azure/firewall/overview"&gt;Azure Firewall&lt;/a&gt;, Azure Application Firewall &lt;a href="https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/ag-overview"&gt;on Azure Application Gateway&lt;/a&gt; or &lt;a href="https://learn.microsoft.com/en-us/azure/web-application-firewall/afds/afds-overview"&gt;on Azure FrontDoor&lt;/a&gt;) , &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/dmz/nva-ha"&gt;network virtual appliances&lt;/a&gt; (&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/perimeter-networks#network-virtual-appliances"&gt;NVAs&lt;/a&gt;), IDS, IPS, and other security appliances. Incoming packets flow through the security appliances before reaching back-end servers. Internet-bound packets from workloads must also flow through security appliances in the perimeter network before they can leave the network. The document gives an &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/perimeter-networks#perimeter-network-topology"&gt;example&lt;/a&gt; of a DMZ hub V-Net with two perimeter networks.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Virtual WANs&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.techtarget.com/searchnetworking/definition/WAN-wide-area-network"&gt;This&lt;/a&gt; page discusses what is WAN and SD-WAN. WAN connects multiple LANs in different geographic areas and is common with companies with multiple offices in different regions. WAN infrastructure may be privately owned or leased as a service from a third-party service provider (hybrid WAN). Companies may use IPSec VPN, SSL VPN or direct connection to build their WANs. Software-defined WAN (SD-WAN) leverages virtualization technologies, network overlays, on-site SD-WAN devices and software platforms to build hybrid WANs.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-about"&gt;Azure Virtual WAN&lt;/a&gt; (similar to &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/introducing-aws-cloud-wan-preview/"&gt;AWS cloud WAN&lt;/a&gt;) is a managed service to build a virtual WAN with a single operational interface that brings many networking, security and routing functionalities together. It simplifies end-to-end network connectivity (within Azure, between Azure and on-prem) by creating a hub-and-spoke architecture. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="608" src="https://www.digihunch.com/wp-content/uploads/2023/03/azure-virtual-wan.webp" alt="" class="wp-image-12908" srcset="https://www.digihunch.com/wp-content/uploads/2023/03/azure-virtual-wan.webp 1024w, https://www.digihunch.com/wp-content/uploads/2023/03/azure-virtual-wan-300x178.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/03/azure-virtual-wan-768x456.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Virtual WAN&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtual WAN is essentially an integrated connectivity solutions (in hub and spoke), with a &lt;a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-global-transit-network-architecture"&gt;global transit network architecture&lt;/a&gt;. The configurations, including spoke setup) is automated and troubleshooting is more intuitive. Global transit network configures multiple virtual WAN hubs with hub-to-hub connectivity, which ultimately enables any-to-any connectivity, with different paths discussed &lt;a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-global-transit-network-architecture#anytoany"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The landing zone document &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/virtual-wan-network-topology#virtual-wan-network-design-recommendations"&gt;recommends&lt;/a&gt; Virtual WAN for new large or global network deployments in Azure where you need global transit connectivity across Azure regions and on-premises locations.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Landing Zone configuration involves many components and there is no way to discuss everything thoroughly. In this post I put down my notes reading Azure landing zone documentation. Overall, working on landing zones requires learning a variety of services by the CSP.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2023/03/a-taste-of-iot-device-tracking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;A taste of IoT device tracking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2023/04/kubernetes-multiple-cpu-architecture-container-image/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes with Multiple CPU Architectures 1 of 2 – Container Image&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Landing Zone in AWS – An Introduction</title><link>https://www.digihunch.com/2022/12/landing-zone-in-aws/</link><pubDate>Thu, 08 Dec 2022 22:37:00 -0400</pubDate><guid>https://www.digihunch.com/2022/12/landing-zone-in-aws/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/landing-zone-intro.webp" alt="Featured image of post Landing Zone in AWS – An Introduction" /&gt;&lt;p class="wp-block-paragraph"&gt;Cloud adoption has gone through phases. Hashicorp&amp;#8217;s CTO Armon Dadgar has a great stream on Hashcorp&amp;#8217;s narrative of the &lt;a href="https://www.youtube.com/watch?v=IBTpNptxYF4"&gt;three Phases of Cloud Adoption&lt;/a&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Phase 1, with main focus on agility, app teams in wild west, account sprawl, inconsistent configuration, security &amp;amp; compliance challenge&lt;/li&gt;&#10;&lt;li&gt;Phase 2. consistent platform layer providing an opinionated way of configurations, security and compliance control, introduction of platform team, whose customers are application team, scaling challenges with platform team capacity not keeping up with application team&amp;#8217;s demand&lt;/li&gt;&#10;&lt;li&gt;Phase 3. self-service platform at scale for many application teams. CICD, infra-as-code&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;New greenfield clients today should aim at phase 2 or phase 3 depending on their target operation size. Either way, they need a landing zone for consistency. This post focuses on the landing zone options on AWS and the key constructs.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Overview&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/strategy-migration/aws-landing-zone.html"&gt;Prescriptive Guidance&lt;/a&gt; defines landing zone as a well-architected (secure, scalable, compliant, etc), multi-account AWS environment that is a secure baseline from which you can deploy workloads and applications. A landing zone may consists of:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Multi-account architecture&lt;/li&gt;&#10;&lt;li&gt;Identity and Access management solution&lt;/li&gt;&#10;&lt;li&gt;Governance, compliance, logging and auditing solutions&lt;/li&gt;&#10;&lt;li&gt;Security and networking design&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Landing zone reflects an enterprise opinion on how to configure networking and IAM. Establishing a landing zone can be a very involving process. In early days of cloud operation people used build landing zone by “clickops” on AWS console, which cannot keep up with the growth of landing zone and associated security services. Compared to SMB clients, some sectors such healthcare and national security have much more regulations and compliance frameworks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In fact, landing zone is such a buzzword that I have learned to be very sensitive to the context. If a solution has words “landing zone” in its name, given the complexity and loose use of the words, there is a good chance that the solution only delivers some of the aspects above. Cloud consultants are still to address the gaps. Therefore I decided to write this post about what I learned about landing zone in AWS.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-multi-account-and-organization"&gt;Multi-account and Organization&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since 2017, AWS has been officially advocating the use of &lt;a href="https://d1.awsstatic.com/aws-answers/AWS_Multi_Account_Security_Strategy.pdf"&gt;multiple accounts&lt;/a&gt; as a best practice and security strategy. They encourage clients to view an account as a resource container, just like &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal"&gt;Resource Group&lt;/a&gt; in &lt;a href="https://medium.com/@richard_64931/account-structure-comparison-between-aws-and-azure-450cb4544fff"&gt;Azure&lt;/a&gt; (&lt;a href="https://www.linkedin.com/pulse/account-structure-comparison-between-aws-azure-richard-lenan-zhao/"&gt;here&lt;/a&gt; is an article on the account structure between AWS and Azure) Client builds an account for security boundary and financial container. Many blog post came along (such as &lt;a href="https://aws.amazon.com/blogs/mt/supercharge-multi-account-management-with-aws-cloudformation/"&gt;this&lt;/a&gt; one) on the implementation details. From tooling perspective, AWS launched &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/02/aws-organizations-now-generally-available/"&gt;AWS Organization&lt;/a&gt; in 2017 to facilitate multi-account management. &lt;a href="https://www.youtube.com/watch?v=fxo67UeeN1A"&gt;This&lt;/a&gt; presentation from re:Inforce 2019 is a good material to understand multi-account environment with AWS Organizations. In 2021, AWS published a new &lt;a href="https://aws.amazon.com/blogs/architecture/new-whitepaper-provides-best-practices-for-optimizing-aws-accounts/"&gt;white paper&lt;/a&gt; on best practices with multiple accounts. My previous coworkers authored two blog posts &lt;a href="https://medium.com/slalom-build/crafting-secure-aws-environments-using-an-aws-multi-account-environment-1163910b8919"&gt;here&lt;/a&gt; and &lt;a href="https://medium.com/slalom-build/crafting-aws-accounts-applications-accounts-7d9140ab0da6"&gt;here&lt;/a&gt; to reflect multi-account setup as of late 2020.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS Organization is an account management service that provides a vehicle to centrally manage AWS accounts by groups, which brings many benefits, such as centralized logging, compliance management, consolidated billing, etc. As an administrator, you can create accounts in your organization and invite existing accounts to join the organization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Typical account structure involves:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A root organization named Root&lt;/li&gt;&#10;&lt;li&gt;A organization hierarchy with one or more OUs under root, with each OU having one or more child OUs.&lt;/li&gt;&#10;&lt;li&gt;Each OU can have multiple accounts, with each account having one email address.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can also configure &lt;strong&gt;service control policies (SCPs)&lt;/strong&gt;, a type of organization-level policy that you can use to manage permissions in your organization. SCPs offer central control over the maximum available permissions for all accounts in your organization. SCPs are a means of implementing guardrails in your AWS organization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To vend multiple account automatically, AWS introduced the Account Factory (which became part of Control Tower later in 2019) and let users create new account from AWS console and specify which OU it belongs to. It also allows users to implement customization after account creation with Service Catalog products. Some legacy orchestration solution (e.g. Augmented Account Factory) were based on this mechanism. Another orchestration solution prior to the launch of Control Tower was the AWS &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/06/introducing-aws-landing-zone/"&gt;Landing Zone solution&lt;/a&gt; (ALZ, introduced in 2018), which uses AWS CodePipeline to provision accounts and deploy resources.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-from-landing-zone-solution-alz-to-control-tower"&gt;From Landing Zone Solution (ALZ) to Control Tower&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The launch of &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/06/aws-control-tower-is-now-generally-available/"&gt;Control Tower&lt;/a&gt; was a game changer in 2019 to bring users to &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/organizing-your-aws-environment/organizing-your-aws-environment.html"&gt;multi-account&lt;/a&gt; best practices. Control Tower, as an AWS product (instead of a solution by some service teams at AWS) matured over several years. It gradually deprecated the previous generation orchestration solutions. Newer orchestration solutions all have to support Control Tower. According to &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/customize-landing-zone.html"&gt;this&lt;/a&gt; page, we can customize &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/designing-control-tower-landing-zone/welcome.html"&gt;Control Tower based Landing Zone&lt;/a&gt; in these ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;AWS Control Tower console:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Instead of creating OU in AWS organization, do it under Control Tower in the console, under “create required OUs”.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Outside of AWS Control Tower console&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;using &lt;a href="https://developer.hashicorp.com/terraform/tutorials/aws/aws-control-tower-aft"&gt;Account Factory for Terraform (AFT)&lt;/a&gt;: Terraform-based account provisioning pipeline, for heavy Terraform shops.&lt;/li&gt;&#10;&lt;li&gt;using &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/cfct-overview.html"&gt;Customizations for AWS Control Tower&lt;/a&gt; (CfCT) solution. The LZ created remains in sync with Control Tower.&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;You first launch a standardized CF stack to set up the mechanisms for customization.&lt;/li&gt;&#10;&lt;li&gt;Then you create a custom package to define the customization. This includes a manifest file.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, the original ALZ solution is currently in long-term support and will not receive any additional features. It is deprecated. AWS advises its customers &lt;a href="https://aws.amazon.com/blogs/mt/migrate-aws-landing-zone-solution-to-aws-control-tower/"&gt;migrate to AWS Control Tower&lt;/a&gt; based landing zone. The &lt;a href="https://aws.amazon.com/solutions/implementations/customizations-for-aws-control-tower/"&gt;ALZ page&lt;/a&gt; is redirected to a page about customizing Control Tower landing zone.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/designing-control-tower-landing-zone/welcome.html"&gt;Control Tower based landing zone&lt;/a&gt; configures OUs, accounts, SSO and guardrails. It however does not entail networking design except for the guardrails.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-security-reference-architecture-sra"&gt;Security Reference Architecture (SRA)&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/welcome.html"&gt;SRA&lt;/a&gt; is just a reference architecture and accompanying recommendations on AWS security services, and how they work together in a multi-account environment to host a single-page application. It comes with a &lt;a href="https://github.com/aws-samples/aws-security-reference-architecture-examples"&gt;repository&lt;/a&gt; to demonstrate how to configure a secure multi-account environment with Control Tower, CfCT, as well as security services. Alternatively, you can deploy it with CloudFormation StackSets. For fully automated deployment of this architecture, check out Landing Zone Accelerator down below.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One important feature that reflects the notion of centralized security in a multi-VPC topology, is the use of a dedicated VPC for centralized interface endpoints. This pattern is seen in all generations of security reference architectures.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-aws-security-environment-accelerator-asea"&gt;AWS Security Environment Accelerator (ASEA)&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Regulated customers often find they need to add additional controls and capabilities to be defined and setup outside of Control Tower. &lt;a href="https://aws-samples.github.io/aws-secure-environment-accelerator/"&gt;ASEA&lt;/a&gt;, as an orchestration solution, aims to remove the complexity of having to develop and maintain a separate codebase to manage the additional customizations, by providing a tool to help deploy and operate secure multi-account, multi-region AWS environments on an ongoing basis.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Overtime, as Control Tower introduces new capabilities to support the customizations required in heavily regulated environments, the capabilities will be removed from ASEA and enabled directly within the Control Tower managed service, further reducing operational burden. Read this for its &lt;a href="https://github.com/aws-samples/aws-secure-environment-accelerator#relationship-with-aws-landing-zone-solution-alz"&gt;relationship&lt;/a&gt; with ALZ and Control Tower.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ASEA was first released in late 2020. It covers more on networking design and has a fairly comprehensive &lt;a href="https://github.com/aws-samples/aws-secure-environment-accelerator#accelerator-installation-process-summary"&gt;installation process&lt;/a&gt;. ASEA primarily cater to &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/public-sector-cloud-transformation/secure-environment-accelerator-canada.html"&gt;government of Canada&lt;/a&gt;&amp;#8216;s &lt;a href="https://www.canada.ca/en/government/system/digital-government/digital-government-innovations/cloud-services/government-canada-security-control-profile-cloud-based-it-services.html#toc4"&gt;PBMM Security Configuration Profile&lt;/a&gt; with an opinionated configuration. The first few revisions were referred to as PMBB architecture. While it provides a great &lt;a href="https://aws-samples.github.io/aws-secure-environment-accelerator/v1.5.6-a/architectures/sensitive/network/"&gt;reference architecture&lt;/a&gt; for highly regulated landing zone, it has not gotten much traction elsewhere. The recommendation going forward, is to use Landing Zone Accelerator, which incorporates the features and lessons learned from ASEA.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-landing-zone-accelerator-lza"&gt;Landing Zone Accelerator (LZA)&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First released in May 2022, the &lt;a href="https://aws.amazon.com/solutions/implementations/landing-zone-accelerator-on-aws/"&gt;Landing Zone Accelerator&lt;/a&gt; on AWS solution deploys a cloud foundation that is architected to align with AWS best practices and multiple global compliance frameworks. LZA operates on top of Control Tower managed landing zone. This &lt;a href="https://docs.aws.amazon.com/solutions/latest/landing-zone-accelerator-on-aws/architecture-overview.html"&gt;page&lt;/a&gt; in the documentation brings a good explanation of how it works. Basically it employs CodeBuild as an orchestration engine, and leverages &lt;a href="https://www.digihunch.com/2020/12/ansible-tower-lab-environment-on-aws/"&gt;CDK&lt;/a&gt; to drive resource deployment. There are two repositories: the GitHub repository for Landing Zone Accelerator itself, and a CodeCommit configuration repository provisioned during preparation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As &lt;a href="https://docs.aws.amazon.com/solutions/latest/landing-zone-accelerator-on-aws/reference.html"&gt;document&lt;/a&gt; states, LZA is a fully automated implementation of the architecture guidelines documented in the &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/welcome.html"&gt;SRA&lt;/a&gt;. LZA also incorporates features and lessons learned from ASEA and &lt;a href="https://docs.aws.amazon.com/solutions/latest/compliant-framework-for-federal-and-dod-workloads-in-aws-govcloud-us/welcome.html"&gt;Compliant Framework for Federal and DoD Workloads in GovCloud (US)&lt;/a&gt;, neither of which are recommended for new deployment. In addition, LZA aims to enable iterations and extensions of a secure environment over time. The vision is to eventually replace AFT, CfCT, and ASEA. As AWS releases newer versions of LZA, client should be able to upgrade it in a pipeline run.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="549" src="https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-1024x549.webp" alt="" class="wp-image-12877" srcset="https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-1024x549.webp 1024w, https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-300x161.webp 300w, https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza-768x412.webp 768w, https://www.digihunch.com/wp-content/uploads/2022/12/base-arch-lza.webp 1288w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Baseline architecture for LZA&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Landing Zone Accelerator project also provides a &lt;a href="https://github.com/awslabs/landing-zone-accelerator-on-aws/tree/main/reference/sample-configurations"&gt;samples&lt;/a&gt; configurations in each regulated frameworks. For example, the healthcare best practice come up in Oct 2022 for &lt;a href="https://www.healthcareitnews.com/news/aws-launches-new-healthcare-specific-accelerator"&gt;healthcare industry&lt;/a&gt;. The &lt;a href="https://aws.amazon.com/blogs/industries/introducing-landing-zone-accelerator-for-healthcare/"&gt;healthcare best practice&lt;/a&gt; sample incorporates healthcare specific configurations, such as the detective guardrails defined in the&amp;nbsp;&lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/operational-best-practices-for-hipaa_security.html"&gt;Operational Best Practices for HIPAA Security&lt;/a&gt; &amp;nbsp;conformance pack. To deploy the best practices, modify the configuration in config repo, and run the pipeline again.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Landing Zone Orchestration Options&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To summarize, we have the following landing zone options in AWS:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-black-color has-pale-cyan-blue-background-color has-text-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Orchestration Layer&lt;/th&gt;&lt;th&gt;Lifecycle&lt;/th&gt;&lt;th&gt;Summary&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Solutions based on account factory with Service Catalog (e.g. ALZ, Augmented Account Factory)&lt;/td&gt;&lt;td&gt;All solutions pre-dates the launch of control tower have been or will soon be deprecated.&lt;/td&gt;&lt;td&gt;Pre-Control Tower solutions should migrate to current alternatives.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS Control Tower&lt;/td&gt;&lt;td&gt;GA in June 2019&lt;/td&gt;&lt;td&gt;To customize the landing zone, use AWS console, or alternatively, one of the following three options:&lt;br&gt;&amp;#8211; CfCT: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/cfct-overview.html"&gt;Customization for Control Tower&lt;/a&gt;&lt;br&gt;&amp;#8211; AFT: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/taf-account-provisioning.html"&gt;Account Factory Terraform&lt;/a&gt;&lt;br&gt;&amp;#8211; AFC: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/af-customization-page.html"&gt;Account Factory Customizations&lt;/a&gt;&lt;br&gt;For example, CfCT can be used to deploy Security Reference Architecture (SRA)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS Secure Environment Accelerator (ASEA)&lt;/td&gt;&lt;td&gt;Released 2020&lt;/td&gt;&lt;td&gt;Canadian Centre for Cyber Security (CCCS) Medium Cloud Control Profile, formerly known as PBMM. This approach will be replaced by LZA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS Landing Zone Accelerator (LZA)&lt;/td&gt;&lt;td&gt;Released in 2022&lt;/td&gt;&lt;td&gt;A low-code deployment option. Samples provided in support of reference architectures that align with industry best practices or compliance frameworks. Examples for industry best practices:&lt;br&gt;&amp;#8211; general best practice&lt;br&gt;&amp;#8211; healthcare&lt;br&gt;&amp;#8211; finance and tax&lt;br&gt;&amp;#8211; education&lt;br&gt;Example for compliance frameworks:&lt;br&gt;&amp;#8211; US state local government&lt;br&gt;&amp;#8211; FedRAMP for US Federal and Department of Defence (DoD)&lt;br&gt;&amp;#8211; CCCS Medium for Canadian government&lt;br&gt;&amp;#8211; AWS Trusted Secure Enclave (TSE) Sensitive Edition (SE), which also aligns with other medium level security profiles such as NIST 800-53, ITSG-33, FedRAMP moderate, CCCS-Medium, IRAP, etc&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If your organization has a compliance framework that LZA supports, it makes sense to start with LZA. Otherwise, it is sufficient to use Control Tower for multi-account setup.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Networking constructs&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Landing zone involves multiple VPCs so it&amp;#8217;s important to understand VPC peering and Transit Gateway. VPC peering can only be setup between two VPCs and transitive peering relationship is not supported. For 6 VPCs to talk to all each other, we&amp;#8217;d need 15 peering setups, which is not sustainable. We have two options:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;We can connect many VPCs to a single Transit Gateway and the VPCs will be able to talk to each other. We can also connect Transit Gateway to site-to-site VPN or Direct Connect. &lt;a href="https://ngoyal16.medium.com/vpc-peering-or-transit-gateway-b0f1176874f"&gt;This&lt;/a&gt; is a good explanation.&lt;/li&gt;&#10;&lt;li&gt;We can still use peering, but dedicate one VPC as “Transit VPC” in a hub and spoke model.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you attach a VPC to a transit gateway, you must specify one subnet from each AZ to be used by the transit gateway to route traffic. Specifying one subnet from an AZ enables traffic to reach resources in every subnet in that AZ.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/transit-vpc-solution.html"&gt;Here&lt;/a&gt; is a comparison table. Read the white-paper &amp;#8220;Building a Scalable and Secure Multi-VPC AWS Network Infrastructure&amp;#8221; for more about network design. For example, Some clients need to &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/inline-traffic-inspection-third-party-appliances/introduction.html"&gt;inspect traffic&lt;/a&gt;. When deploying multiple VPCs, we also need to ensure &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver.html"&gt;DNS resolution&lt;/a&gt; works across VPCs, and between on-prem networks, by configuring &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#AmazonDNS"&gt;Amazon DNS server&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Our VPCs also need to connect to managed AWS services. For services like S3 or DynamoDB (of the same or different account), workload in VPC can access them via public DNS. The traffic goes through Internet Gateway of VPC and then public Internet. It is neither secure nor economical. We often want all network traffic to stay on the global AWS backbone. There are three types of Endpoint under VPC to help us.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-black-color has-pale-cyan-blue-background-color has-text-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Gateway VPC Endpoint or &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/gateway-endpoints.html" target="_blank" rel="noreferrer noopener"&gt;Gateway Endpoint&lt;/a&gt;&lt;/th&gt;&lt;th&gt;Interface VPC Endpoint or &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html" target="_blank" rel="noreferrer noopener"&gt;Interface Endpoint&lt;/a&gt;&lt;/th&gt;&lt;th&gt;&lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/create-gateway-load-balancer-endpoint-service.html" target="_blank" rel="noreferrer noopener"&gt;Gateway Load Balancer Endpoint&lt;/a&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;VPC access native AWS services&lt;/td&gt;&lt;td&gt;VPC access native AWS services&lt;/td&gt;&lt;td&gt;VPC access your own service (aka Endpoint Service)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enabled by &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html" target="_blank" rel="noreferrer noopener"&gt;Private Link&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Traffic remain on AWS network&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Mechanism&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Use the public IP address of the service along with configuration in routing table to access target resource. The routing table acts as gateway. You can use the public DNS name of the service.&lt;/td&gt;&lt;td&gt;Use private IP address from the VPC to access the target service. Require endpoint-specific DNS name for the target service. Incur extra charge. &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/privatelink-interface-endpoints.html" target="_blank" rel="noreferrer noopener"&gt;S3&lt;/a&gt; as example. More secure because there is ENI in the VPC controlled by security group.&lt;/td&gt;&lt;td&gt;Your Endpoint Services is hosted in front of a fleet of network &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/vpce-gateway-load-balancer.html" target="_blank" rel="noreferrer noopener"&gt;virtual appliances&lt;/a&gt;. You can select endpoint type as you create it in console under VPC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Access from clients on premise or from other region&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Does NOT allow access from clients on premise or in VPC from other region&lt;/td&gt;&lt;td&gt;Allows access from clients on premise or clients in VPC from other region&lt;/td&gt;&lt;td&gt;Allowed&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Private Link integrates with a subset of &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html" target="_blank" rel="noreferrer noopener"&gt;AWS services&lt;/a&gt;. To check the list of interface endpoint, use:&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;aws ec2 describe-vpc-endpoint-services --query &lt;span style="color:#e6db74"&gt;&amp;#34;ServiceDetails[?ServiceType[0].ServiceType==&amp;#39;Interface&amp;#39;].ServiceName&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;Given the inter-VPC connectivity, most deployment centralizes interface endpoint into a dedicated VPC.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Centralized Interface Endpoints&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are several benefits to use a single VPC as dedicated provider of interface endpoint in a multi-VPC topology. First, the interface endpoints incurs a standing charge and it makes financial sense to consolidate them in one VPC. Second, this setup centralizes the configuration and security aspects as well. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In most cases, interface endpoint services follow the format of &lt;code&gt;com.amazonaws.&amp;lt;region&amp;gt;.&amp;lt;endpoint&amp;gt;&lt;/code&gt;, with the dns name looking like: &lt;code&gt;&amp;lt;endpoint&amp;gt;.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt;. For example:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Endpoint Service Name: &lt;code&gt;com.amazonaws.us-east-1.ssm&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Private Hosted Zone: &lt;code&gt;ssm.us-east-1.amazonaws.com&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Zone Record (A alias): &lt;code&gt;ssm.us-east-1.amazonaws.com&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;However, there are a few exceptions to that which can make it tricky to implement interface endpoint with infrastructure as code.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Exception 1&lt;/strong&gt;. Private DNS name suffix is &lt;code&gt;api.aws&lt;/code&gt; instead of &lt;code&gt;amazonaws.com&lt;/code&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Endpoint Service Name: &lt;code&gt;com.amazonaws.us-east-1.eks-auth&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Private Hosted Zone: &lt;code&gt;eks-auth.us-east-1.api.aws&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Zone Record (A alias): &lt;code&gt;eks-auth.us-east-1.api.aws&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Exception 2&lt;/strong&gt;. Endpoint Service Name doesn&amp;#8217;t start with com.amazonaws&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Endpoint Service Name: &lt;code&gt;aws.sagemaker.us-east-1.notebook&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Private Hosted Zone: &lt;code&gt;notebook.us-east-1.sagemaker.aws&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Zone Record (A alias): &lt;code&gt;notebook.us-east-1.sagemaker.aws&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Exception 3&lt;/strong&gt;. Two A-records are required under the same PHZ&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Endpoint Service Name: &lt;code&gt;com.amazonaws.us-east-1.ecr.dkr&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Private Hosted Zone: &lt;code&gt;dkr.ecr.us-east-1.amazonaws.com&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Zone Record (A alias): &lt;code&gt;dkr.ecr.us-east-1.amazonaws.com&lt;/code&gt; and &lt;code&gt;*.dkr.ecr.us-east-1.amazonaws.com&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Exception 4&lt;/strong&gt;. Two PHZs are required:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Endpoint Service Name: &lt;code&gt;com.amazonaws.us-east-1.ec2&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Private Hosted Zone: &lt;code&gt;ec2.us-east-1.amazonaws.com&lt;/code&gt; and &lt;code&gt;ec2.us-east-1.api.aws&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Zone Record (A alias): &lt;code&gt;ec2.us-east-1.amazonaws.com&lt;/code&gt; and &lt;code&gt;ec2.us-east-1.api.aws&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The infrastructure code that handles interface endpoints should be able to take care of the exceptions.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Security Constructs&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;AWS Network firewall &lt;/strong&gt;is a configuration under VPC. You associate a Network Firewall with one or more subnets in the VPC. You also associate the Network Firewall with Firewall Policies. Each policy consists of rule groups. Each rule blocks or filters traffic. The log can be published to CloudWatch or S3 via Kinesis. Usecases of AWS Network firewall include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;inspect VPC-to-VPC traffic;&lt;/li&gt;&#10;&lt;li&gt;filter outbound traffic;&lt;/li&gt;&#10;&lt;li&gt;prevent inbound internet traffic;&lt;/li&gt;&#10;&lt;li&gt;secure AWS Direct Connect and VPN traffic&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you &lt;a href="https://docs.aws.amazon.com/network-firewall/latest/developerguide/vpc-config.html"&gt;associate a firewall to your VPC&lt;/a&gt;, you must provide a subnet for each Availability Zone where you want to place a firewall endpoint to filter traffic&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many points of configuration for firewall rules in AWS. &lt;strong&gt;AWS Firewall Manager&lt;/strong&gt; is a place for central management. It connects with other services such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;AWS WAF (L7)&lt;/li&gt;&#10;&lt;li&gt;AWS Network Firewall (L4)&lt;/li&gt;&#10;&lt;li&gt;AWS Shield (DDos)&lt;/li&gt;&#10;&lt;li&gt;Amazon Route 53 Resolver DNS Firewall&lt;/li&gt;&#10;&lt;li&gt;Security Groups&lt;/li&gt;&#10;&lt;li&gt;Third party firewall support&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;IAM Identity Center&lt;/strong&gt; (previously AWS SSO) is for logging into AWS portal, giving each identity from an external identity store (such as AD) an identity on an AWS account. It addresses several challenges as a result of having multiple AWS accounts. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, a user from AD needs to access multiple accounts. Second, in each account with access, s/he needs to have an IAM role. These IAM roles are based on attributes of his/her external identity, and can be different per AWS account. With &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsetsconcept.html"&gt;Permission Set&lt;/a&gt; IAM Identity Center:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Allow you to login on different AWS account using the same credential&lt;/li&gt;&#10;&lt;li&gt;Allow you to federate with external identity store( e.g. using SAML)&lt;/li&gt;&#10;&lt;li&gt;Manage permission set for each account.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://www.youtube.com/watch?v=x7TCs9HxRFg"&gt;video&lt;/a&gt; has a good walk-through of how to configure Azure AD as identity store, and tie it to specific AWS accounts and specify permission set. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that IAM Identity Center has a different purpose than Cognito. &lt;strong&gt;Cognito&lt;/strong&gt; is to connect your app with an IdP. Your app can be an EC2-hosted application, serverless application on API gateway, or container-based application on Kubernetes, as long as the support open identity standards (e.g. OAuth 2.0, SAML 2.0 and OIDC). Cognito has two pools:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;User pool for authentication. With a user pool, your app&lt;/li&gt;&#10;&lt;li&gt;Identity pool for authorization&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;span class="underline" style="text-decoration: underline"&gt;So Cognito is for AuthN &amp;amp; AuthZ to your own app’s endpoint (business traffic) and it supports a number of standards. IAM Identity Center is for AuthN &amp;amp; AuthZ to your AWS account (management traffic).&lt;/span&gt; If you use it for your own application, your application user will have direct access your AWS resources. It mainly supports SAML (and OIDC but less used) and is commonly connected with Active Directory (Microsoft or AWS).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One can use AWS Directory Service to host a compatible and managed directory service. &lt;strong&gt;AWS Directory Service&lt;/strong&gt; supports four modes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;AWS managed Microsoft AD: actual Microsoft Active Directory in AWS Cloud&lt;/li&gt;&#10;&lt;li&gt;Simple AD, powered by Linux-Samba Active Directory-compatible server&lt;/li&gt;&#10;&lt;li&gt;AD Connector: a proxy for redirecting directory request to your existing Microsoft AD without caching any information in the cloud&lt;/li&gt;&#10;&lt;li&gt;Cognito user pools&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you combine two AD domains, you will need &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory-domain-services/concepts-forest-trust#trust-relationship-flows"&gt;domain trust&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Other security services&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to the core services above, LZA involves the following four services as well.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;AWS Macie&lt;/strong&gt; is a fully managed data security and privacy service based on ML and pattern matching. It continually evaluates your S3 environments to discovery PII and act on them. It also reports alerts on unencrypted buckets, publicly accessible buckets, etc.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;AWS GuardDuty&lt;/strong&gt; continuously analyze S3, container &amp;amp; instance workloads, user and accounts for potential threads, across account, based on ML for intelligent threat detection. In addition, GuardDuty also acts on findings.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;AWS Config&lt;/strong&gt; is an essential service that traces resource inventory, their changes and monitors for compliance:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;When you turn on AWS Config, it first discovers the supported AWS resources in your account and generates a &lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/config-concepts.html#config-items"&gt;configuration item&lt;/a&gt; (poin-in-time view of attributes) for each resource. AWS Config also generates configuration items when the configuration of a resource changes, and it maintains historical records of the configuration items of your resources from the time you start the configuration recorder.&lt;/li&gt;&#10;&lt;li&gt;On an ongoing basis, AWS Config keeps track of all changes to your resources, whether or not it is initiated by the API&lt;/li&gt;&#10;&lt;li&gt;If you are using AWS Config rules, AWS Config continuously evaluates your AWS resource configurations for desired settings. You can deploy several related rules in a pre-built &lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/conformance-packs.html"&gt;compliance pack&lt;/a&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As you may have noticed, there are many AWS services related to firewall, security and compliance. &lt;strong&gt;AWS Security Hub&lt;/strong&gt; aims to be a consolidated view of your security status in AWS. You can automate security checks, manage security findings, and identify the highest priority security issues across accounts in client environment. It can also:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;consolidate security findings from GuardDuty, Inspector (vulnerability scanner and management), Macie, Config, Systems Manager, Firewall Manager, IAM Access Analyzer and other Integrated APN solutions&lt;/li&gt;&#10;&lt;li&gt;Check findings against best practices&lt;/li&gt;&#10;&lt;li&gt;Client can take action: i.e. investigate findings or take response and remediation actions&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Landing zone deployment can take numerous iterations to finalize the requirement. It is important to have a vision of the client&amp;#8217;s &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/building-cloud-operating-model/building-cloud-operating-model.html"&gt;cloud operating model&lt;/a&gt;, which is usually discussed prior to implementation. At the implementation phase, I discuss the topic on two more separate posts on &lt;a href="https://www.digihunch.com/2023/08/control-tower-aws-landing-zone/"&gt;control tower&lt;/a&gt; and &lt;a href="https://www.digihunch.com/2023/09/orchestrate-landing-zone-with-landing-zone-accelerator-on-aws/"&gt;landing zone accelerator&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/11/key-mapping-on-external-pc-keyboard-on-macbook/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Key mapping for external PC keyboard on Mac&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/12/eks-impression/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;EKS impression&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Build and Manage Kubernetes Clusters</title><link>https://www.digihunch.com/2022/09/build-a-kubernetes-cluster/</link><pubDate>Fri, 23 Sep 2022 11:50:00 -0400</pubDate><guid>https://www.digihunch.com/2022/09/build-a-kubernetes-cluster/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-cluster.webp" alt="Featured image of post Build and Manage Kubernetes Clusters" /&gt;&lt;p class="wp-block-paragraph"&gt;There are numerous options to build a Kubernetes cluster. If your company has a multi-cloud strategy, most likely you will have to deal with cluster creation on multiple cloud platform or on virtual machines on premise. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Most likely, the chosen cloud platform already make it simple for us. However, it is still important to understand what it really takes to build a Kubernetes cluster. In general, we need to figure out these tasks:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Decide where to host the computing infrastructure (i.e. Node) : on premise or public cloud;&lt;/li&gt;&#10;&lt;li&gt;Choose a Kubernetes release: either the vanilla release or one of the third-party distributions;&lt;/li&gt;&#10;&lt;li&gt;Install Kubernetes to the computing environment, and integrate it with the cloud platform;&lt;/li&gt;&#10;&lt;li&gt;Determine required add-ons (e.g. Istio or Linkerd for Service Mesh, dashboard utility, etc);&lt;/li&gt;&#10;&lt;li&gt;Deploy application workload to Kubernetes platform;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A public cloud platform provider usually can assist you with task 1 through 3, and partially 4, depending on the provider. If your Kubernetes resides on private cloud or on-prem environment, you can use a Platform solution such as VMware Tanzu or Openshift, which usually covers task 1, 3 and 4. There is no standard about what task these platform solution must address. Therefore it is important to have this list of tasks in mind in order to make a good comparison. I will discuss each of the tasks in this post.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-hosting-environment"&gt;Hosting environment&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Nodes are the building blocks of a Kubernetes cluster. We need master nodes as well as worker nodes. In addition, a working cluster also requires storage, and networking infrastructure. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Public cloud platforms typically provides control plane as a service, obviating administrator&amp;#8217;s effort to provision master nodes. For example, the control plane of Azure AKS has two levels of uptime commitment: a free tier of 99.5% SLO and a paid tier with an SLA of 99.95% (using AZs) and 99.9% (without using AZs). This uptime commitment applies to control plane only and do not apply to worker nodes. The management of etcd store is also a responsibility of the cloud provider, which frees up the cluster administrator from managing etcd store. However, they cannot access etcd store either. This is not very convenient because as the size of the cluster grows it is a common requirement to connect to etcd store for troubleshooting purpose.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The deployment APIs for public cloud allow the cluster administrator to define the instance size, count and availability zone for the worker nodes. They also automatically register the worker nodes to control plane so that the cluster administrators do not have to do so by themselves. As to &lt;a href="https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;storage&lt;/a&gt;, the public cloud usually provide some default storage classes based on their storage as service. For networking device, the cluster provision process automatically configures the cloud API so the cluster can manage cloud resources such as network load balancer. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With private cloud or data centre, we usually use virtual machines, or bare-metal servers. Cluster administrators will need to make their own control plane with master nodes. and install worker nodes and register them to the master nodes. The Kubernetes Installation section below will discuss this.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Kubernetes release&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you have to install Kubernetes, you have to think about the Kubernetes release being used. You can use the binary from official Github &lt;a href="https://github.com/kubernetes/kubernetes"&gt;repository&lt;/a&gt;. For example, the &lt;a href="https://github.com/kubernetes/kubernetes/releases/tag/v1.24.3"&gt;release note&lt;/a&gt; of version 1.24.3 points to the &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md"&gt;change log&lt;/a&gt; file for &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md#downloads-for-v1243"&gt;download&lt;/a&gt; links to &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md#server-binaries"&gt;server binaries&lt;/a&gt;, &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md#node-binaries"&gt;node binaries&lt;/a&gt;. This is the vanilla Kubernetes release.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the vanilla release, many developers build their own distributions, based off forks of the Kubernetes project. CNCF has a page to keep track of certified Kubernetes distributions. Some of the distributions are open source and can be used for on-prem infrastructure. Here is a list of top players:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-black-color has-cyan-bluish-gray-background-color has-text-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Distribution Name&lt;/th&gt;&lt;th&gt;Repo&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://distro.eks.amazonaws.com/"&gt;EKS Distro&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/aws/eks-distro"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Used in EKS managed service or EKS Anywhere for on-prem infrastructure&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.microsoft.com/en-us/azure-stack/user/azure-stack-kubernetes-aks-engine-overview?view=azs-2108#overview-of-the-aks-engine"&gt;AKS Engine&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/Azure/aks-engine"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Used in Azure Stack for on-prem infrastructure. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://cloud.google.com/kubernetes-engine/"&gt;Google Kubernetes Engine&lt;/a&gt;&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;Used in GKE managed service only. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.openshift.com/container-platform/4.8/welcome/oke_about.html"&gt;OpenShift Kubernetes Engine&lt;/a&gt;&lt;br&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/openshift/kubernetes"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Community distribution (OKD, or &lt;a href="https://www.okd.io/"&gt;OpenShift Kubernetes Distribution&lt;/a&gt;) is the open-source upstream.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://rancher.com/docs/rke/latest/en/"&gt;Rancher Kubernetes Engine&lt;/a&gt; (RKE)&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/rancher/rke"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;still using Docker as container runtime. Supported CNI include: Canal, Flannel, Calico and Weave&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://k3s.io/"&gt;K3s&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/k3s-io/k3s"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Lightweight distro without small resource requirement. Great for Edge, IoT, ARM etc&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.rke2.io/"&gt;RKE2&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/rancher/rke2"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Originally named RKE government. Supports deployment via Cluster API. Supports containerd as container runtime. Supported CNI include: Cillium, Calico, Canal and Multus. Lightweight&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;VMware Tanzu&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/vmware-tanzu/community-edition"&gt;Link&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://tanzu.vmware.com/kubernetes-grid"&gt;VMWare Tanzu Grid&lt;/a&gt; and &lt;a href="https://tanzucommunityedition.io/"&gt;VMWare Tanzu Community&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Above is just a very incomplete list of Kubernetes distributions. There are many more distributions that are not on this list, such as CoreOS Tectonic, Docker Kubernetes, Heptio, Mesosphere, Mirantis, Platform9, Stackube, Telekube. For full details of how each distribution is different, you will need to go over their documents. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With the selected distribution, we still need to deploy the binaries to the nodes. We can do this with a cluster management platform, or standalone installers. Cluster management platform can also help us with baseline configuration (e.g. IAM integration, CNI plugin), in addition to the binary installation. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cluster Management Platform&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These platforms are also sometimes referred to as container management platform.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, OpenShift container platform is a self-managed platform based on OpenShift Kubernetes Engine and can run on a variety of hosting environment, public cloud, or private cloud. The &lt;a href="https://docs.openshift.com/container-platform/4.7/installing/index.html"&gt;installation steps &lt;/a&gt;varies depending on the hosting environment. When running on public cloud such as &lt;a href="https://aws.amazon.com/rosa/"&gt;AWS&lt;/a&gt; (aka &lt;a href="https://docs.openshift.com/rosa/welcome/index.html"&gt;ROSA&lt;/a&gt;), the public cloud only provides computing nodes and associated infrastructure. Many corporate with multi-cluster strategy use this option on public cloud to keep their Kubernetes cluster fleet consistent across cloud vendors. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Openshift container platform also packages some useful open-source add-ons with corporate support, for example:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://www.redhat.com/en/technologies/cloud-computing/openshift/what-is-openshift-service-mesh"&gt;OpenShift Service Mesh&lt;/a&gt;: Istio&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.redhat.com/en/technologies/storage/ceph"&gt;Ceph Storage&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.redhat.com/en/technologies/storage/gluster"&gt;Gluster Storage&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://docs.openshift.com/container-platform/4.10/cicd/gitops/understanding-openshift-gitops.html"&gt;OpenShift GitOps&lt;/a&gt; (ArgoCD)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://docs.openshift.com/container-platform/4.10/cicd/pipelines/op-release-notes.html"&gt;OpenShift Pipelines&lt;/a&gt;&amp;nbsp;(Tekton)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.redhat.com/en/technologies/cloud-computing/quay"&gt;Quay&lt;/a&gt; (Quay Image Registry)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.redhat.com/en/technologies/cloud-computing/openshift/openshift-streams-for-apache-kafka"&gt;OpenShift Streams for Apache Kafka&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.redhat.com/en/technologies/cloud-computing/openshift/serverless"&gt;OpenShift Serverless&lt;/a&gt; (Knative Serving)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Red Hat&amp;#8217;s strategy is to pick the most renowned open-source project in each domain and add enterprise support to it. However, for management portal, Red Hat developed its own &lt;a href="https://www.redhat.com/en/technologies/management/advanced-cluster-management"&gt;Advanced Cluster Management&lt;/a&gt; tool for Kubernetes, and &lt;a href="https://www.redhat.com/en/blog/open-sourcing-red-hat-advanced-cluster-management-kubernetes"&gt;open-sourced&lt;/a&gt; it in 2020 in the upstream &lt;a href="https://open-cluster-management.io/"&gt;project&lt;/a&gt; &lt;a href="https://github.com/open-cluster-management-io/OCM"&gt;Open Cluster Management&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similar to OpenShift, VMware Tanzu also attempts to cover the domains, with a smaller product portfolio:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://tanzu.vmware.com/service-mesh"&gt;Service Mesh&lt;/a&gt;: compatible with &lt;a href="https://tanzu.vmware.com/content/blog/istio-mode-tanzu-service-mesh"&gt;Istio&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://tanzu.vmware.com/mission-control"&gt;Mission Control&lt;/a&gt;: management portal&lt;/li&gt;&#10;&lt;li&gt;Observability&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Google &lt;a href="https://cloud.google.com/anthos/docs/concepts/overview"&gt;Anthos&lt;/a&gt; is also a container platform. Their product line include, but not limited to:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://cloud.google.com/anthos/config-management"&gt;Anthos Config Management&lt;/a&gt; (ACM)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://cloud.google.com/anthos/service-mesh"&gt;Anthos Service Mesh&lt;/a&gt; (ASM, an Istio distribution)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;SUSE, the developer of RKE, RKE2, and K3s) offers Rancher as multi-cluster management platform. Apart from the engines, SUSE also offers Lonhorn as a storage solution. However, they do not have offerings for service mesh or GitOps. So there is no doubt that Red Hat OpenShift has the most complete portfolio for Kubernetes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are also companies that only offers management platforms without their own Kubernetes distribution. For example:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://platform9.com/resource/platform9-managed-kubernetes-pmk/"&gt;Platform9&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rafay.co/"&gt;Rafay&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Product capabilities in this category vary a lot and you should refer to their specific documentation to understand. You will probably see a stack chart from each of the platform provider (e.g. SUSE Enterprise Container, &lt;a href="https://cloud.redhat.com/blog/introducing-red-hat-openshift-container-platform"&gt;OpenShift&lt;/a&gt;, &lt;a href="https://docs.vmware.com/en/VMware-Tanzu/services/tanzu-adv-deploy-config/GUID-components.html"&gt;Tanzu&lt;/a&gt;, &lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/what-are-my-hybrid-and-multicloud-deployment-options-anthos"&gt;Anthos&lt;/a&gt;, &lt;a href="https://rafay.co/why-rafay/#what-rafay-does"&gt;Rafay&lt;/a&gt;) with all technology integrations.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cluster Installation Tools&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As we saw in the installation steps for OpenShift, they are highly dependent on platform. With public cloud, the provisioning process also applies only to a specific platform. Since Kubernetes Installation process is tedious, some tools emerged to help, for example: &lt;a href="https://github.com/kubernetes-sigs/kubespray"&gt;kubespray&lt;/a&gt;, &lt;a href="https://github.com/kubernetes/kubeadm"&gt;kubeadm&lt;/a&gt;, &lt;a href="https://github.com/kubernetes/kops"&gt;kops&lt;/a&gt; and Cluster API. These are governed by &lt;a href="https://github.com/kubernetes/community/tree/master/sig-cluster-lifecycle"&gt;SIG cluster lifecycle&lt;/a&gt; special interest group. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here are some traditional options to install a Kubernetes clusters:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;kube-up&lt;/strong&gt;: the first tool to build cluster from 2015. It has been deprecated.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Kubeadm&lt;/strong&gt;: a tool built to provide best-practice &amp;#8220;fast paths&amp;#8221; for creating Kubernetes clusters that are minimum viable, and secure. Kubeadm&amp;#8217;s scope is limited to the local node filesystem and the Kubernetes API, and it is intended to be a composable building block of higher level tools. It is first released in Sep 2016. The high-level configuration steps go through initialization (kubeadm init), control plane (kubeadm join control plane), and node (kubeadm join node). Kubeadm does not integrate with cloud providers and it does not install addons (auth, monitoring, CNI, storage class)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Kubespray&lt;/strong&gt;: runs on bare metal or VMs using Ansible for provisioning and orchestration. The first release was in Oct 2015. Since v2.3 (Oct 2017) kubespray started to use kubeadm internally. In addition to kubeadm, kubespray configures CNI, storage class, other CRI. It supports cloud providers and air-gap environment. However it does not support infrastructure management.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The options above are official options. You may use kubeadm and kubespray to quickly (i.e. in an hour) spin up clusters for education purposes. However, with their limitations, it typically requires a lot of efforts to build a production-grade cluster with the needed addons and integrations. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the official options, there are also unofficial tools such as &lt;a href="http://kubicorn.io/"&gt;kubicorn&lt;/a&gt;, which was first introduced in 2018 as a cluster management framework with modular support for cloud providers. However it appears to be short-lived.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the next two sections, we introduce kops and cluster API, two most recent projects to install cluster.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Kops&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The kops utility directly performs the provisioning and orchestration via API to the cloud deployment engine. Kops, with first release in Oct 2016, is tightly integrated with the unique features of the cloud providers (e.g. AWS: ASG, ELB, EBS, KMS, S3, IAM). However, kops is a CLI tool without controller-style reconciliation. It does not support baremetal or vsphere. It also bundles addons with fixed version.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When picking a tool to install cluster, we need to strike a balance between how much simplification the tool brings, and how many different platform the installer can work with. &lt;a href="https://kops.sigs.k8s.io/"&gt;Kops&lt;/a&gt; appears to be such a good compromise. It works with a number of cloud platforms using different set of APIs, although most are in alpha and beta stages today. &lt;a href="https://kops.sigs.k8s.io/getting_started/aws/"&gt;Here&lt;/a&gt; is how to install cluster on AWS. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both kops and Cluster API have &lt;a href="https://thenewstack.io/cluster-api-kops-or-both-for-kubernetes-multicluster-deployments/"&gt;good momentum&lt;/a&gt; but they work differently. &lt;a href="https://cluster-api.sigs.k8s.io/"&gt;Cluster API&lt;/a&gt; was first released in Mar 2019, and is currently less mature than kops. However, it is declarative and may reflect the direction of where cluster lifecycle management is heading.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cluster API&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://cluster-api.sigs.k8s.io/"&gt;Cluster API&lt;/a&gt; focuses on following areas:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Manage cluster &lt;span style="text-decoration: underline" class="underline"&gt;lifecycle &lt;/span&gt;declaratively&lt;/li&gt;&#10;&lt;li&gt;Infrastructure abstraction (e.g. computing, storage, networking, security, etc)&lt;/li&gt;&#10;&lt;li&gt;Utilizing existing tools (e.g. kubeadm, cloud-init)&lt;/li&gt;&#10;&lt;li&gt;Modular and pluggable: to be adaptable to different infrastructure providers.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It involves a number of CRs as illustrated in its &lt;a href="https://cluster-api.sigs.k8s.io/user/concepts.html#concepts"&gt;diagram&lt;/a&gt;. We should be clear on the providers for Bootstrap, Infrastructure and Control Plane.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The biggest benefit is the controller pattern to manage the entire lifecycle of a cluster. This allows managing clusters with GitOps, and rolling upgrade of the cluster. It also allows for declarative node scaling, self healing and multi-cluster management.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The client utility for is &lt;a href="https://cluster-api.sigs.k8s.io/clusterctl/overview.html"&gt;clusterctl&lt;/a&gt;, and with that along with the manifest, we can create a cluster in a few commands. A lot of workflows are still in development but we can take a look at its &lt;a href="https://cluster-api.sigs.k8s.io/user/quick-start.html#quick-start"&gt;quick start&lt;/a&gt; guide to get a taste of how it works. The installation steps vary a lot based on the environment and the cluster. Also it introduces the separation of management cluster and workload cluster.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Workload cluster is the target cluster being created, as per the manifests.&lt;/li&gt;&#10;&lt;li&gt;Management cluster is where you keep track of the workload cluster being managed. You can manage multiple workload clusters from a single management cluster. Note that this management cluster will store credentials about workload clusters, and may become a single point of failure.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although Cluster API reflects a great initiative to standardize the provisioning of Kubernetes cluster, whether it will succeed has to do with the level of complexity. In the next section, we will get a taste of how it looks to deploy a Kubernetes cluster in a lab.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="516" height="181" src="https://www.digihunch.com/wp-content/uploads/2022/08/diagram.webp" alt="" class="wp-image-6757"/&gt;&lt;figcaption class="wp-element-caption"&gt;Management cluster vs workload cluster&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the lab, I use my MacBook to create a management cluster with &lt;a href="https://kind.sigs.k8s.io/"&gt;KinD&lt;/a&gt;. Then we configure a workload cluster in AWS from the management cluster. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cluster API Lab&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the steps here are based on the &lt;a href="https://cluster-api.sigs.k8s.io/user/quick-start.html#quick-start"&gt;quick start guide&lt;/a&gt; on Cluster API document. Also, there is a bug with the AWS provider so the end of the lab will report a warning. The main purpose of this lab is to demonstrate how Cluster API is supposed to work, even though it still has yet to mature.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To start, I install clusterctl (the cluster API client utility), clusterawsadm (the utility specific for AWS) on MacBook, then start a simple KinD 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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.0/clusterctl-darwin-amd64 -o clusterctl&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod +x ./clusterctl&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo mv ./clusterctl /usr/local/bin/clusterctl&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusterctl version&#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;curl -L https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/download/v1.4.1/clusterawsadm-darwin-amd64 -o clusterawsadm&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod +x clusterawsadm&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo mv clusterawsadm /usr/local/bin&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusterawsadm version&#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;kind create cluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;So far, I installed the required utility and a KinD cluster on MacBook. Then I use clusterawsadm to create InstanceProfile, ManagedPolicy and IAM Roles required for cluster creation. The AWS region and access are configured as 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;export AWS_REGION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;us-east-1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export AWS_ACCESS_KEY_ID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;AKIAXXXXXXXXXXX&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export AWS_SECRET_ACCESS_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;J8ByduiofpwuisDjDoijOISDs&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusterawsadm bootstrap iam create-cloudformation-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This runs a CloudFormation stack to create the permission related resources:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1556" height="464" src="https://www.digihunch.com/wp-content/uploads/2022/08/image-1.webp" alt="" class="wp-image-6795"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then I initialize the management cluster with the clusterctl utility, specifying AWS as a provider. I also need to assign the environment variable AWS_B64ENCODED_CREDENTIALS with proper value: &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 AWS_B64ENCODED_CREDENTIALS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;clusterawsadm bootstrap credentials encode-as-profile&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusterctl init --infrastructure aws&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now I use clusterctl to generate the manifest for the workload cluster. In environment variables, I specify cluster and node sizes, SSH key name, control plane machine type and node machine type:&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 AWS_SSH_KEY_NAME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;cskey&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export AWS_CONTROL_PLANE_MACHINE_TYPE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;t3.large&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export AWS_NODE_MACHINE_TYPE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;t3.large&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusterctl generate cluster myekscluster --kubernetes-version 1.24.3 --control-plane-machine-count&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; --worker-machine-count&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &amp;gt; capi-quickstart.yaml&#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;kubectl apply -f capi-quickstart.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;At the end I tell the management cluster to create a workload cluster as per the manifest, by simply declaring the CRs. It will take some time for the cluster to create, and there are a number of ways to monitor the progress. You can monitor the log on the controller pods in their respect namespaces. You can also check the cluster status with:&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 kubeadmcontrolplane&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusterctl describe cluster myekscluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Currently there is a &lt;a href="https://github.com/kubernetes-sigs/cluster-api/issues/6417"&gt;bug&lt;/a&gt; and the commands at the end will report as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="2423" height="206" src="https://www.digihunch.com/wp-content/uploads/2022/08/image.webp" alt="" class="wp-image-6785"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hopefully the bug will be fixed shortly. To delete the cluster, simply delete the resources in the manifest with kubectl delete -f capi-quickstart.yaml&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are numerous ways to build a Kubernetes cluster. Before deciding on the approach, I recommend having a full understanding of the hosting environment. This is because installation approach and hosting environment are still tightly coupled. This is the status quo and is not going to change in the near future. Both kops and cluster API reflects initiative to decouple the two but both are still in early stage and already facing growing complexity. Cluster API manages complexity with CRDs to abstract system resources and infrastructure, as illustrated here:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="501" height="669" src="https://www.digihunch.com/wp-content/uploads/2022/08/image-7.webp" alt="" class="wp-image-7086"/&gt;&lt;figcaption class="wp-element-caption"&gt;CRDs and providers to abstract system resources and infrastructure&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram is from the &amp;#8220;&lt;a href="https://www.oreilly.com/library/view/cluster-api-and/9781098126865/"&gt;Cluster API and declarative Kubernetes Management&lt;/a&gt;&amp;#8221; white paper. &lt;a href="https://www.cncf.io/online-programs/cluster-api-yesterday-today-tomorrow/"&gt;Here &lt;/a&gt;is a stream with more about the same topic.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/09/minio-object-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;MinIO for S3-compatible Object Storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/10/graphql-and-grpc/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;GraphQL and gRPC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kubernetes Storage on Azure 3 of 3 – Ceph by Rook</title><link>https://www.digihunch.com/2022/08/storage-solution-on-aks-2-of-3-ceph-by-rook/</link><pubDate>Fri, 26 Aug 2022 19:43:00 -0400</pubDate><guid>https://www.digihunch.com/2022/08/storage-solution-on-aks-2-of-3-ceph-by-rook/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-storage-3.webp" alt="Featured image of post Kubernetes Storage on Azure 3 of 3 – Ceph by Rook" /&gt;&lt;p class="wp-block-paragraph"&gt;In the last two posts, I covered the native storage options on Azure Kubernetes Service, as well as Portworx as an example of a proprietary Software Defined Storage (SDS) solution. There are also a number of open-source alternative SDS solutions. Ceph has nearly a decade of history from prior to containerization, and is the most widely adopted storage platform. In this post, we continue to explore Ceph as an open-source storage solution on Azure Kubernetes. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-ceph-by-rook"&gt;Ceph by Rook&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ceph is an open-source SDS platform for distributed storage on a cluster and provides object, block and file storage. Installation of Ceph SDS can be complex, especially on Kubernetes platform. &lt;a href="https://rook.io/"&gt;Rook&lt;/a&gt; is a graduated CNCF project to orchestrate storage platform. Rook by itself is not SDS and it supports:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/rook/v1.9/Getting-Started/intro/"&gt;Ceph&lt;/a&gt;: configure a Ceph cluster. Think of this as the equivalent of &lt;a href="https://docs.ceph.com/en/quincy/cephadm/"&gt;cephadm&lt;/a&gt; on Kubernetes platform.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/nfs/v1.7/"&gt;NFS&lt;/a&gt;: configure an NFS server. Think of this as the equivalent of nfsd daemon on Kubernetes platform.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/cassandra/v1.7/"&gt;Cassandra&lt;/a&gt;: an operator to configure a Cassandra database cluster. It is now &lt;strong&gt;deprecated&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We play with Rook Ceph. I also refer to it as Ceph by Rook. The contribution of Rook project is it simplifies the installation as a matter of declaring custom resources using CRDs. Here are some high-level CRDs to know:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/rook/v1.9/CRDs/ceph-cluster-crd/"&gt;CephCluster&lt;/a&gt;: creates a Ceph storage cluster&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/rook/v1.9/CRDs/Block-Storage/ceph-block-pool-crd/"&gt;CephBlockPool&lt;/a&gt;: represents a block pool&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/rook/v1.9/CRDs/Shared-Filesystem/ceph-filesystem-crd/"&gt;CephFilesystem&lt;/a&gt;: represents a file system&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/rook/v1.9/CRDs/Object-Storage/ceph-object-store-crd/#example"&gt;CephObjectStore&lt;/a&gt;: represents an object store&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://rook.io/docs/rook/v1.9/CRDs/ceph-nfs-crd/"&gt;CephNFS&lt;/a&gt;: spins up a NFS Ganesha server to export NFS shares of a CephFilesystem or CephObjectStore.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As with typical Kubernetes resources in controller pattern, Ceph by Rook needs an operator along with custom resources. We can use YAML manifest for both of them, and the manifests are usually very tediously long. We can also use Helm to install both of them, by providing a value file. Now we will install Ceph on AKS.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Install Ceph Operator on AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The steps are influenced by two relevant posts (&lt;a href="https://carlos.mendible.com/2021/10/23/aks-high-available-storage-with-rook-and-ceph/"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/evillgenius75/rook-aks"&gt;here&lt;/a&gt;). However, I&amp;#8217;ve incorporated the cluster configuration in the &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;Azure directory of the cloudkube project&lt;/a&gt;, a modular Terraform template to configure AKS cluster and facilitate storage configuration. The node group and instance sizes are selected to be just enough to run a ceph POC cluster with minimum cost. One of the node groups is tainted with storage-node, as if the following command were 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;kubectl taint nodes my-node-pool-node-name storage-node&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true:NoSchedule&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;You will only need to taint the nodes with the command above if you choose not to use the cloudkube template. The taint ensures that only Pods with corresponding toleration and effect can be scheduled to those nodes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Helm to install Rook Operator. We need a value file (e.g. rook-ceph-operator-values.yaml) with content 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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;https&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//github.com/rook/rook/blob/master/Documentation/Helm-Charts/operator-chart.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;crds&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;csi&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;provisionerTolerations&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;effect&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NoSchedule&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exists&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pluginTolerations&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;effect&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NoSchedule&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exists&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;agent&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AKS&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;https&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//rook.github.io/docs/rook/v1.7/flexvolume.html#azure-aks&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;flexVolumeDirPath&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/etc/kubernetes/volumeplugins&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;Then we install the operator with Helm:&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 install rook-ceph-operator rook-ceph --namespace rook-ceph --create-namespace --version v1.9.6 --repo https://charts.rook.io/release/ --values rook-ceph-operator-values.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n rook-ceph get po -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;rook-ceph-operator&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After installing the operator, we check the Pod status to make sure it is running. Then we can install the actual Ceph Cluster in one of the two ways. We can declare a CephClusterCRD ourself, or we can use Helm again to declare the CRD. Helm Chart gives us a lot of useful default values and saves us from editing a large body of YAML manifest.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Install Ceph CR on AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Helm to install CephCluster CRD. We create a value file (e.g. rook-ceph-cluster-values.yaml) with content 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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;https&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//github.com/rook/rook/blob/master/Documentation/Helm-Charts/ceph-cluster-chart.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;operatorNamespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rook&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ceph&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;toolbox&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;cephObjectStores&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [] &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;by&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cephObjectStore&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;will&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;be&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;created&lt;/span&gt;. &lt;span style="color:#a6e22e"&gt;Setting&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;disables&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;it&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;cephBlockPools&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;by&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cephBlockPool&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;will&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;also&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;be&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;created&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;values&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;cephFileSystems&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;by&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cephFileSystem&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;will&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;also&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;be&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;created&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;values&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:#a6e22e"&gt;cephClusterSpec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;mon&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;count&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;volumeClaimTemplate&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storageClassName&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;managed&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;premium&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requests&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Gi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;limits&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cpu&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;500m&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memory&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;1Gi&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requests&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cpu&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;100m&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memory&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;500Mi&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dashboard&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storageClassDeviceSets&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;The&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;of&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OSDs&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;device&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;count&lt;/span&gt;&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; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IMPORTANT&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;If&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;volumes&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;specified&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;by&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storageClassName&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;are&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;portable&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;across&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nodes&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;needs&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;be&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;. &lt;span style="color:#a6e22e"&gt;For&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;example&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;local&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;provisioner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;should&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;be&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portable&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Since&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OSDs&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;could&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;end&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;up&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;on&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;any&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;an&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;effort&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;needs&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;be&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;made&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;spread&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OSDs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;across&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nodes&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;much&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;possible&lt;/span&gt;. &lt;span style="color:#a6e22e"&gt;Unfortunately&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pod&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;anti&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;affinity&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;breaks&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;down&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;soon&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;you&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;have&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;more&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;than&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;one&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OSD&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;per&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;. &lt;span style="color:#a6e22e"&gt;The&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;topology&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;spread&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;constraints&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;will&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;give&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;us&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;an&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;even&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;spread&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;on&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;K8s&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1.18&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;or&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;newer&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;placement&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;topologySpreadConstraints&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;maxSkew&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&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:#a6e22e"&gt;topologyKey&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hostname&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;whenUnsatisfiable&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ScheduleAnyway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;labelSelector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;matchExpressions&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;In&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;values&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rook&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ceph&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;osd&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;tolerations&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exists&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;preparePlacement&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;tolerations&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exists&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;nodeAffinity&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;nodeSelectorTerms&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;matchExpressions&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;agentpool&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;In&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;values&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storagenp&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;topologySpreadConstraints&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;maxSkew&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&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:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IMPORTANT&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;If&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;you&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;don&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;have&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;zone&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;labels&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;change&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;another&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;such&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hostname&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;topologyKey&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;topology&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;zone&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;whenUnsatisfiable&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DoNotSchedule&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;labelSelector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;matchExpressions&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;In&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;values&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rook&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ceph&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;osd&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;prepare&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;limits&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cpu&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;500m&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memory&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;4Gi&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requests&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cpu&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;500m&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memory&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2Gi&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;volumeClaimTemplates&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requests&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Gi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storageClassName&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;managed&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;premium&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;volumeMode&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Block&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;accessModes&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ReadWriteOnce&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;During the cluster provisioning, there will be a number of preparing Pods. We want those Pods to run on nodes with label agentpool=storagenp. In real life, we need to orchestrate where to run each workload, by restricting the nodes to schedule certain types of workload.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then we can install the cluster using Helm:&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 install rook-ceph-cluster rook-ceph-cluster --namespace rook-ceph --create-namespace --version v1.9.6 --repo https://charts.rook.io/release/ --values rook-ceph-cluster-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;After running the Helm install, it may take as long as 15 minutes for all resources to settle. Watch the Pod status in rook-ceph namespace. At the end, make sure that the cluster is created successfully:&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;kubeadmin@pro-sturgeon-bastion-host:~$ kubectl -n rook-ceph get CephCluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME DATADIRHOSTPATH MONCOUNT AGE PHASE MESSAGE HEALTH EXTERNAL&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rook-ceph /var/lib/rook &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; 15m Ready Cluster created successfully HEALTH_OK&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubeadmin@pro-sturgeon-bastion-host:~$ kubectl -n rook-ceph get cephBlockPools&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME PHASE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ceph-blockpool Ready&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubeadmin@pro-sturgeon-bastion-host:~$ kubectl -n rook-ceph get cephFileSystems&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME ACTIVEMDS AGE PHASE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ceph-filesystem &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 20m Ready&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In my case it took 15 minutes before the cluster comes up as created successfully. You should notice that two storage classes were also created as a part of the install. It however did not create a storage class or CRD for object storage, because we explicitly disabled it in the Helm value file by setting cephObjectStores value to null.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Dashboard&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We enabled dashboard. To configure the dashboard view properly, we would need an ingress. For a quick view here, we can play port forwarding tricks. First we fetch the admin password for use in the next step. Then expose the dashboard to the bastion host:&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 rook-ceph get secret rook-ceph-dashboard-password -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.password}&amp;#39;&lt;/span&gt; | base64 -d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n rook-ceph port-forward svc/rook-ceph-mgr-dashboard 8443:8443&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Since I don&amp;#8217;t have UI on the bastion host, I use the port forwarding trick again from my own MacBook. Start a new terminal and SSH to the bastion host with port-forwarding switch:&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;$ ssh -L 8443:localhost:8443 kubeadmin@20.116.132.8&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command above suppose the public IP of the bastion host is 20.116.132.8. Then from my MacBook I can browse to localhost:8443 (with Safari browser which gives me the option to bypass certificate error). At the web portal, provide username (admin) and password (as retrieved above):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="1795" height="1026" src="https://www.digihunch.com/wp-content/uploads/2022/06/image-16.webp" alt="" class="wp-image-6037"/&gt;&lt;figcaption class="wp-element-caption"&gt;Ceph console for Kubernetes&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the dashboard, we can also use &lt;a href="https://docs.ceph.com/en/quincy/man/8/ceph/"&gt;ceph admin tool&lt;/a&gt; from a &lt;a href="https://github.com/rook/rook/blob/master/deploy/examples/toolbox.yaml"&gt;toolbox&lt;/a&gt; pod, following &lt;a href="https://rook.io/docs/rook/v1.9/ceph-toolbox.html"&gt;this&lt;/a&gt; instruction. For monitoring, Ceph by Rook can expose metrics for &lt;a href="https://www.rook.io/docs/rook/v1.9/Storage-Configuration/Monitoring/ceph-monitoring/"&gt;Prometheus&lt;/a&gt; to scrape.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Performance&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With default ceph configuration on AKS, I ran quick performance test using kube-str . The result is as follows:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-very-light-gray-to-cyan-bluish-gray-gradient-background has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;read_iops&lt;/td&gt;&lt;td&gt;write_iops&lt;/td&gt;&lt;td&gt;read_bw&lt;/td&gt;&lt;td&gt;write_bw&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ceph-block&lt;/td&gt;&lt;td&gt;IOPS=464.507294 BW(KiB/s)=1874&lt;/td&gt;&lt;td&gt;IOPS=243.296143 BW(KiB/s)=989&lt;/td&gt;&lt;td&gt;IOPS=509.928162 BW(KiB/s)=65797&lt;/td&gt;&lt;td&gt;IOPS=248.530762 BW(KiB/s)=32338&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ceph-filesystem&lt;/td&gt;&lt;td&gt;IOPS=438.701324 BW(KiB/s)=1770&lt;/td&gt;&lt;td&gt;IOPS=226.270660 BW(KiB/s)=920&lt;/td&gt;&lt;td&gt;IOPS=405.936340 BW(KiB/s)=52456&lt;/td&gt;&lt;td&gt;IOPS=208.869293 BW(KiB/s)=27229&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The metrics reflects performance under default configuration. It should not be considered as the best performance that Ceph can deliver on Azure Kubernetes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I discussed three storage options for Azure Kubernetes but the idea applies to other Kubernetes platform hosted on a CSP. The &lt;a href="https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;native storage&lt;/a&gt; has significant limitation. NFS has latency. Block storage does not address high availability at the storage layer. Portworx and LINSTOR fill that gap as a commercial solution. Ceph is based on Object storage.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 2 of 3 – Portworx&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/09/minio-object-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;MinIO for S3-compatible Object Storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kubernetes Storage on Azure 2 of 3 – Portworx</title><link>https://www.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/</link><pubDate>Fri, 12 Aug 2022 15:23:00 -0400</pubDate><guid>https://www.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-azstorage.webp" alt="Featured image of post Kubernetes Storage on Azure 2 of 3 – Portworx" /&gt;&lt;p class="wp-block-paragraph"&gt;In the previous &lt;a href="https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;post&lt;/a&gt;, we have discussed built-in storage classes on Azure Kubernetes. Further to that, we will examine some third-party software defined storage (SDS) options that are compatible with Azure Kubernetes Service in this post. Then we take Portworx on Azure as an example. Although, these options are specific to Azure, most of the players also have solutions for other managed Kubernetes platforms. Also, the methodology to study storage options remain the same regardless of cloud service provider.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In fact, I touched on software defined storage (SDS) in the context of general non-containerized workload in a separate &lt;a href="https://www.digihunch.com/2022/07/intro-to-ceph-storage/"&gt;post&lt;/a&gt;. In short, storage providers decouple the SDS appliance from the full storage solution in order to lower the cost and increase flexibility. To the storage consumer (e.g. a process running on Linux OS), SDS can present a block disk or file system. There are also SDS solution that can host your own object storage and we will discuss that later. In fact, SDS has gained significant popularity in recent years. For example, the report &amp;#8220;&lt;a href="https://www.architecting.it/product/brksw0140-ebook/"&gt;Validating Software-Defined Storage Operating Models for the Enterprise&lt;/a&gt;&amp;#8221; by &lt;em&gt;&lt;a href="https://www.architecting.it/"&gt;archiectingit&lt;/a&gt;&lt;/em&gt; divided the evolution into four phases and cited that Gartner predicts the SDS revolution to reach 50% of the storage market by 2024, from 15% in 2020.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This post discusses SDS in the context of container storage. Then we will install Portworx on Azure Kubernetes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-sds-for-kubernetes"&gt;SDS for Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Many SDS appliances also developed the capability to present storage volumes to containerized workload. I put together a list of SDS products that works on Azure with their supported access modes: &lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Solution&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Licence&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Development and Support&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Access Mode&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://portworx.com/products/portworx-enterprise/features/"&gt;Portworx&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Closed source. Free Essential tier. Enterprise features on License. &lt;/td&gt;&lt;td&gt;Commercially supported by PureStorage&lt;/td&gt;&lt;td&gt;RWO, RWX&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.ceph.com/en/quincy/"&gt;Ceph&lt;/a&gt; by &lt;a href="https://rook.io/"&gt;Rook&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/rook/rook"&gt;Open source&lt;/a&gt;. Rook is a graduated &lt;a href="https://www.cncf.io/projects/rook/"&gt;CNCF project&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Developed and commercially supported by &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_openshift_container_storage/4.8/html-single/red_hat_openshift_container_storage_architecture/index"&gt;Red Hat&lt;/a&gt;, &lt;a href="https://ubuntu.com/ceph"&gt;Canonical&lt;/a&gt; and &lt;a href="https://softiron.com/"&gt;SoftIron&lt;/a&gt;&lt;/td&gt;&lt;td&gt;RWO, ROX, RWX&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://openebs.io/"&gt;OpenEBS&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/openebs/openebs"&gt;Open source&lt;/a&gt;. Sandbox &lt;a href="https://www.cncf.io/projects/openebs/"&gt;CNCF project&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Developed and &lt;a href="https://openebs.io/commercial-support"&gt;commercially supported&lt;/a&gt; by &lt;a href="https://openebs.io/"&gt;MayaData&lt;/a&gt; et al.&lt;/td&gt;&lt;td&gt;RWO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://longhorn.io/"&gt;Longhorn&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/longhorn/longhorn"&gt;Open source&lt;/a&gt;. Incubating &lt;a href="https://www.cncf.io/projects/longhorn/"&gt;CNCF project&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Originally developed by &lt;a href="https://rancher.com/products/longhorn"&gt;Rancher&lt;/a&gt;, and commercially supported by &lt;a href="https://www.suse.com/products/longhorn/"&gt;SUSE&lt;/a&gt;&lt;/td&gt;&lt;td&gt;RWO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.ondat.io/docs/install/microsoft-azure-aks/"&gt;StorageOS&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Closed source. License required.&lt;/td&gt;&lt;td&gt;Commercial support by &lt;a href="https://www.ondat.io/"&gt;Ondat&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;RWO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://linbit.com/kubernetes/"&gt;LINBIT&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Open-source with enterprise plans&lt;/td&gt;&lt;td&gt;Enterprise support&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RWO is the most commonly supported mode. The report &amp;#8220;&lt;a href="https://www.architecting.it/product/performance-benchmarking-cloud-native-storage-solutions-for-kubernetes-ebook/"&gt;Performance Benchmarking Cloud Native Storage Solutions for Kubernetes&lt;/a&gt;&amp;#8221; makes a comparison of performance among some of the options in early 2021. Another potentially opinionated &lt;a href="https://linbit.com/cloud-native-sds-platform-comparsion/"&gt;comparison list&lt;/a&gt; is by LINBIT.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Portworx is a leading player with commercial SDS solution and I will test its free &lt;a href="https://portworx.com/blog/portworx-essentials-vs-portworx-enterprise/"&gt;Essential&lt;/a&gt; tier in the rest of this post. Ceph is one of the most mature leading open-source offering and I will test it in the next post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For Portworx, we can use the terraform template &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;cloudkube for Azure&lt;/a&gt;. The template assigns the kubelet&amp;#8217;s managed identity as contributor of the node resource group. The template also creates a bastion host with direct SSH access to the nodes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-portworx-operator-on-azure"&gt;Portworx Operator on Azure&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Portworx Operator to configure storage cluster. Portworx has an &lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install"&gt;instruction&lt;/a&gt; for AKS but it is not tailored to specific identity model. For simplicity, use my cloudkube &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;Terraform template&lt;/a&gt; to create the AKS cluster, and skip the &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/install-using-operator"&gt;Prepare Your AKS Platform&lt;/a&gt;&amp;#8221; page. Instead, follow the &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/azure-managed-identity-on-aks"&gt;Deploy Portworx using Azure managed identity on new AKS cluster&lt;/a&gt;&amp;#8221; page starting at step 7. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At that step, we need to create a secret with the client ID of the managed identity for node agent. The terraform template outputs the BYO identity&amp;#8217;s client ID. After cluster creation, we simply SSH to the bastion host and create the secret using the output.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1953" height="755" src="https://www.digihunch.com/wp-content/uploads/2022/06/image-15.webp" alt="" class="wp-image-5810"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To install Porworx using operator, we can follow a wizard in &lt;a href="https://central.portworx.com/"&gt;PX-central&lt;/a&gt;. If this is the first time, we need to create an account and log in to the portal. If this is not the first time and you have previously created a cluster, you need to detach that cluster by going to Profile from bottom left corner on the portal page. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Follow the guide in &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/install-using-operator"&gt;Install Portworx on AKS using the Operator&lt;/a&gt;&amp;#8220;. In the wizard, click on &amp;#8220;Portworx Essentials&amp;#8221; for free tier, or &amp;#8220;Portworx Enterprise&amp;#8221; for the 30-day trial. Then select operator with latest version. In the rest of the wizard steps, select options applicable to Azure environment. The last step will present two kubectl commands to install operator and install the CR. Run the command to install operator and verify result:&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 apply -f &lt;span style="color:#e6db74"&gt;&amp;#39;https://install.portworx.com/2.9?comp=pxoperator&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system get deployment portworx-operator&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 class="wp-block-heading"&gt;Portworx Custom Resource&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To install the CR, we need to customize the given manifest in order to use our managed identity. We can download the YAML manifest (portworx_essentials.yml) and modify it in text editor. As the page &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/azure-managed-identity-on-aks"&gt;Deploy Portworx using Azure managed identity on new AKS cluster&lt;/a&gt;&amp;#8221; suggest at step 9: in the&amp;nbsp;env&amp;nbsp;section, remove the AZURE_CLIENT_SECRET and AZURE_TENANT_ID sections but keep the AZURE_CLIENT_ID section. My CRD declaration looks like this:&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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SOURCE&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;https&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//install.portworx.com/?operator=true&amp;amp;mc=false&amp;amp;kbver=&amp;amp;oem=esse&amp;amp;user=myuserid&amp;amp;b=true&amp;amp;kd=type%3DPremium_LRS%2Csize%3D150&amp;amp;s=%22type%3DPremium_LRS%2Csize%3D150%22&amp;amp;c=my-very-long-px-cluster-id&amp;amp;aks=true&amp;amp;stork=true&amp;amp;csi=true&amp;amp;mon=true&amp;amp;tel=false&amp;amp;st=k8s&amp;amp;promop=true&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StorageCluster&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;core&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;libopenstorage&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;org&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;my&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;very&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;long&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;cluster&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kube&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;annotations&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;install&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;source&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://install.portworx.com/?operator=true&amp;amp;mc=false&amp;amp;kbver=&amp;amp;oem=esse&amp;amp;user=myuserid&amp;amp;b=true&amp;amp;kd=type%3DPremium_LRS%2Csize%3D150&amp;amp;s=%22type%3DPremium_LRS%2Csize%3D150%22&amp;amp;c=my-very-long-px-cluster-id&amp;amp;aks=true&amp;amp;stork=true&amp;amp;csi=true&amp;amp;mon=true&amp;amp;tel=false&amp;amp;st=k8s&amp;amp;promop=true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;is&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;misc&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;args&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;--oem esse&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;oci&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;monitor&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2.10&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:#a6e22e"&gt;imagePullPolicy&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Always&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kvdb&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;internal&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cloudStorage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deviceSpecs&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Premium_LRS&lt;/span&gt;,&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;150&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kvdbDeviceSpec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Premium_LRS&lt;/span&gt;,&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;150&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;secretsProvider&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stork&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;args&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;webhook&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;autopilot&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;monitoring&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;prometheus&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exportMetrics&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;featureGates&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;CSI&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AZURE_CLIENT_ID&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;azure&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AZURE_CLIENT_ID&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;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Secret&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;essential&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kube&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;essen&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aaaabbbbccccddddmyverylongpxessenuserid&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;osb&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;endpoint&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ssssssyyyyyyyzzzzzzmyverylongpxosbendpoint&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;In this custom resource manifest, I specify to get the cluster to create disk from Azure, even though it has the capability of using an existing disk. Then apply the CRD manifest:&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 apply -f portworx_essentials.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This will take up to 10 minutes to create several related resources. There are several check points to ensure the cluster is created successfully. Here are some useful 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;kubectl -n kube-system get po &lt;span style="color:#75715e"&gt;# all Pods related to portworx should be ready and running, especially the portworx-api ones&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system get storagecluster &lt;span style="color:#75715e"&gt;# the status should report online&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PX_POD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl get pods -l name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;portworx -n kube-system -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.items[0].metadata.name}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# get the name of one of the portworx Pod for storage cluster&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system exec -c portworx -it $PX_POD --tty --stdin -- /opt/pwx/bin/pxctl status &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# should report &amp;#34;Status: PX is operational&amp;#34; with valid license loaded&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get sc &lt;span style="color:#75715e"&gt;# portworx related storage classes are available&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 pre-built storage classes are not CSI based. However, CSI should be automatically enabled in recent operator versions. We should create our own &lt;a href="https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/volumelifecycle/"&gt;CSI storage classes&lt;/a&gt; and &lt;a href="https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/create-pvcs/dynamic-provisioning/"&gt;PVCs&lt;/a&gt; using our own storage classes. Here are two examples:&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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StorageClass&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;csi&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;provisioner&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pxd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;parameters&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;priority_io&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;high&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;io_profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;db&amp;#34;&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;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StorageClass&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;csi&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;artifact&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;provisioner&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pxd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;parameters&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;priority_io&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;medium&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;io_profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sequential&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;That is a bare minimum Portworx installation. Check out their &lt;a href="https://docs.portworx.com/portworx-install-with-kubernetes/"&gt;documentation&lt;/a&gt; for the full features. With this minimum install we can go to the section &amp;#8220;Validate Persistent Storage&amp;#8221; from the previous post to validate the persistent volume.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For troubleshooting purpose, &lt;a href="https://docs.portworx.com/reference/cli/basics/"&gt;pxctl&lt;/a&gt; is the utility and it is available on Portworx Pods.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Performance&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We care not only the functionality, but also the performance. So I ran a quick performance test using kube-str, using all default configuration. The result is as follows:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;read_iops&lt;/td&gt;&lt;td&gt;write_iops&lt;/td&gt;&lt;td&gt;read_bw&lt;/td&gt;&lt;td&gt;write_bw&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;px-csi-database&lt;/td&gt;&lt;td&gt;IOPS=969.614136 BW(KiB/s)=3894&lt;/td&gt;&lt;td&gt;IOPS=729.698059 BW(KiB/s)=2935&lt;/td&gt;&lt;td&gt;IOPS=1172.772827 BW(KiB/s)=150639&lt;/td&gt;&lt;td&gt;IOPS=691.626526 BW(KiB/s)=89053&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;px-csi-artifact&lt;/td&gt;&lt;td&gt;IOPS=780.681946 BW(KiB/s)=3139&lt;/td&gt;&lt;td&gt;IOPS=682.522766 BW(KiB/s)=2746&lt;/td&gt;&lt;td&gt;IOPS=773.548584 BW(KiB/s)=99549&lt;/td&gt;&lt;td&gt;IOPS=659.015320 BW(KiB/s)=84890&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that those numbers reflect performance under default configuration, and they should not be considered as the best performance that Portworx can deliver on Azure Kubernetes. Before moving to production, it is important to establish your own test parameters that best represents the container workload, and then iterate through different parameters for the storage class based on the requirement and performance output.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/08/storage-solution-on-aks-2-of-3-ceph-by-rook/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 3 of 3 – Ceph by Rook&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS</title><link>https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/</link><pubDate>Sun, 31 Jul 2022 15:22:00 -0400</pubDate><guid>https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-aks-storage-1.webp" alt="Featured image of post Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS" /&gt;&lt;p class="wp-block-paragraph"&gt;In the previous &lt;a href="https://www.digihunch.com/2022/05/hosting-database-on-kubernetes/"&gt;post&lt;/a&gt;, we understand that to host stateful workload, we need to manage persistent storage to the Kubernetes platform. In this post, I will explore the different storage options. These options are specific to Azure Kubernetes service. However, the principals apply to any Kubernetes platform regardless of cloud vendor.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In another old &lt;a href="https://www.digihunch.com/2021/06/kubernetes-storage-explained/"&gt;post&lt;/a&gt;, I discussed in-tree and CSI storage classes, and from a developer&amp;#8217;s perspective, how to mount volumes statically and dynamically once the storage class is available. Here in this post we are concerned with how to make storage classes available, from a platform specialist perspective.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Storage in Azure Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As soon as the Azure Kubernetes cluster is launched, a number of built-in &lt;a href="https://docs.microsoft.com/en-us/azure/aks/concepts-storage#storage-classes"&gt;storage classes&lt;/a&gt; are available. Unlike third-party storage classes, they do not require kubelet identity to be contributor for node resource group. As &lt;a href="https://www.digihunch.com/2022/05/hosting-database-on-kubernetes/"&gt;discussed&lt;/a&gt; we shall use CSI based storage classes. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;managed-csi&lt;/li&gt;&#10;&lt;li&gt;managed-csi-premium&lt;/li&gt;&#10;&lt;li&gt;azurefile-csi&lt;/li&gt;&#10;&lt;li&gt;azurefile-csi-premium&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main difference between them is the backing technology. However, form Kubernetes workload&amp;#8217;s perspective, the Pods as storage consumers are concerned with the &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes"&gt;access mode&lt;/a&gt; instead of backing technology. Here are supported access modes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ReadWriteOnce: read-write by a single node&lt;/li&gt;&#10;&lt;li&gt;ReadOnlyMany: read only by many nodes&lt;/li&gt;&#10;&lt;li&gt;ReadWriteMany: read-write by many nodes&lt;/li&gt;&#10;&lt;li&gt;ReadWriteOncePod: new in Kubernetes 1.22 to restrict volume access to a single Pod&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The storage classes managed-csi and managed-csi-premium support ReadWriteOnce. The storage classes azurefile-csi and azurefile-csi-premium support ReadWriteMany.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from these built-in options, Azure also suggests a few more options based on other types of Azure resources. For example, AKS can integrate with &lt;a href="https://azure.microsoft.com/en-us/services/hpc-cache/#overview"&gt;HPC cache&lt;/a&gt; and it for HPC. We can also self-manage a virtual machine &lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-nfs-volume"&gt;configured as NFS server&lt;/a&gt;, and use the &lt;a href="https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner"&gt;NFS subdir external provisioner&lt;/a&gt; to configure storage class. Despite of the overhead with managing a VM, you have more configurability. My previous client reports that they gain better performance than the built-in options. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another alternative is &lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-netapp-files"&gt;Azure NetApp Files&lt;/a&gt;. However, being a full enterprise grade solution (similar to &lt;a href="https://www.digihunch.com/2022/05/fsx-ontap-enterprise-storage-on-aws/"&gt;FSx ONTAP&lt;/a&gt;), &lt;a href="https://azure.microsoft.com/en-ca/services/netapp/"&gt;Azure NetApp Files&lt;/a&gt; costs an arm and a leg. In &lt;a href="https://www.kasten.io/kubernetes/resources/blog/aks-and-storage-performance-differences-among-kubernetes-storage-services"&gt;this comparison&lt;/a&gt;, it cost 60 to 100 times as the cost by built-in options.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another option is &lt;a href="https://docs.microsoft.com/en-us/azure/aks/use-ultra-disks"&gt;Azure Ultra Disk&lt;/a&gt;, which needs to be enabled at cluster level. You can provision performance target (DiskIOPSReadWrite and DiskMBpsReadWrite) in the storage class. Ultra Disk is a good middle ground between the pricey NetApp files and the less performant built-in options.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-terraform-template-for-aks"&gt;Terraform Template for AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To explore the storage options, I use my own terraform template to create an AKS cluster. The template is in the &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;azure&lt;/a&gt; directory of the &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; repo. The template consists a few configurations with Azure Kubernetes to facilitate storage configuration. First, it configures an SSH key pair to use between the bastion host and the node. Users can SSH to Kubernetes nodes from bastion host as soon as terraform apply is completed. Second, the third party storage options installed after the cluster creation need their Pod to instruct Azure to create Azure disks. This requires that a Kubernetes node agent have the permission to provision resources in the node resource group. This is important to understand because there are a couple of managed identities at play (refer to &lt;a href="https://www.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;this&lt;/a&gt; post) when building an AKS cluster. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Azure Kubernetes, it is the managed identity of kubelet, that needs to have contributor permission over the resource group for the nodes (not the one for the AKS cluster itself). A managed identity is expressed by a client ID, an object ID (aka principal ID), and the identity ID. We can find them out with an AZ CLI command as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1899" height="207" src="https://www.digihunch.com/wp-content/uploads/2022/06/image-14.webp" alt="" class="wp-image-5653"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can also tell that the kubelet managed identity represents node agent, by connecting to a node and looking at the argument (kubernetes.azure.com/kubelet-identity-client-id) of kubelet process:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1794" height="784" src="https://www.digihunch.com/wp-content/uploads/2022/06/image-13.webp" alt="" class="wp-image-5650"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the template, I also chose to designate the same BYO identity for both the cluster and for kubelet (node agent), in order to minimize my requirement on permission. If I had left it with a system assigned identity for node agent, I would have to assign that identity as a contributor for the node resource group, either as a user, or via Terraform&amp;#8217;s identity. Either way, it is beyond what a &lt;a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-steps"&gt;Contributor&lt;/a&gt; is allowed to do.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Benchmarking with kubestr&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I used &lt;a href="https://fio.readthedocs.io/en/latest/index.html"&gt;fio&lt;/a&gt; utility for storage benchmarking from virtual machines. However, fio utility is not for container. For &lt;a href="https://docs.portworx.com/install-with-other/operate-and-maintain/performance-and-tuning/fio/"&gt;fio testing&lt;/a&gt; on Kubernetes, I&amp;#8217;d have to use a Docker image, and test with target volume attached. Fortunately, the &lt;a href="https://www.kasten.io/"&gt;Kasten&lt;/a&gt; team shared their initiative in the open source project &lt;a href="https://kubestr.io/"&gt;Kubestr&lt;/a&gt;. The kubestr &lt;a href="https://github.com/kastenhq/kubestr/releases/tag/v0.4.31"&gt;release&lt;/a&gt; is available as an executable on common platforms. It connects to the cluster the same way as kubectl and &lt;a href="https://www.youtube.com/watch?v=j9UkQM-oa1k"&gt;here&lt;/a&gt; is a demo. To begin with, download the utility to bastion host, and run it without any argument, which prints the storage classes and &lt;a href="https://kubernetes.io/docs/concepts/storage/volume-snapshot-classes/"&gt;volume snapshot classes&lt;/a&gt;:&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;curl -L -o kubestr.tar.gz https://github.com/kastenhq/kubestr/releases/download/v0.4.31/kubestr_0.4.31_Linux_amd64.tar.gz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tar -xvf kubestr.tar.gz &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm kubestr.tar.gz &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; chmod +x kubestr&#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;./kubestr &lt;span style="color:#75715e"&gt;# if kubectl is configured, this command will print out the details of storage classes and volume snapshot classes&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;In addition to outputting details, it is also very simple to perform storage benchmarking with kubestr. All we need to do is giving it the storage class name and it will run four tests by default with common global options (ioengine=libaio verify=0 direct=1 gtod_reduce=1). The four tests are:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;JobName&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;block_s&lt;/strong&gt;ize&lt;/td&gt;&lt;td&gt;&lt;strong&gt;filesize&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;iodepth&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;rw&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;read_iops&lt;/td&gt;&lt;td&gt;4k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randread&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;write_iops&lt;/td&gt;&lt;td&gt;4k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randwrite&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;read_bw&lt;/td&gt;&lt;td&gt;128k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randread&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;write_bw&lt;/td&gt;&lt;td&gt;128k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randwrite&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;During each test, it measures and reports IOPS and bandwidth (throughput). If your I/O profile falls out of the four jobs, you can even customize your test by supplying a fio config file. For example, you need a longer test duration, or you need a larger total size for the test. Before the test, kubestr automatically mount their test volumes using the storage class being tested.&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;./kubestr fio -s my-storage-class &lt;span style="color:#75715e"&gt;# benchmarking a storage class&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;For read_iops and write_iops, we mainly look at the IOPS. For read_bw and write_bw, we mainly look at the bandwidth. The iops and bw &lt;a href="https://fio.readthedocs.io/en/latest/fio_doc.html#interpreting-the-output"&gt;based on samples&lt;/a&gt; are reported as first line of result, followed by min, max and average.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Metrics&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With kubestr I ran a performance test amongst the native storage classes with Azure Kubernetes Service, with results as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;read_iops&lt;/td&gt;&lt;td&gt;write_iops&lt;/td&gt;&lt;td&gt;read_bw&lt;/td&gt;&lt;td&gt;write_bw&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;managed-csi&lt;/td&gt;&lt;td&gt;IOPS=314.729797 BW(KiB/s)=1275&lt;/td&gt;&lt;td&gt;IOPS=297.071136 BW(KiB/s)=1204&lt;/td&gt;&lt;td&gt;IOPS=315.311188 BW(KiB/s)=40887&lt;/td&gt;&lt;td&gt;IOPS=261.048645 BW(KiB/s)=33941&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;managed-csi-premium&lt;/td&gt;&lt;td&gt;IOPS=493.395844 BW(KiB/s)=1990&lt;/td&gt;&lt;td&gt;IOPS=426.731812 BW(KiB/s)=1723&lt;/td&gt;&lt;td&gt;IOPS=455.950348 BW(KiB/s)=58894&lt;/td&gt;&lt;td&gt;IOPS=422.888855 BW(KiB/s)=54662&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;azurefile-csi&lt;/td&gt;&lt;td&gt;IOPS=259.333282 BW(KiB/s)=1053&lt;/td&gt;&lt;td&gt;IOPS=283.985779 BW(KiB/s)=1152&lt;/td&gt;&lt;td&gt;IOPS=240.447403 BW(KiB/s)=31298&lt;/td&gt;&lt;td&gt;IOPS=230.689804 BW(KiB/s)=30048&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;azurefile-csi-premium&lt;/td&gt;&lt;td&gt;IOPS=394.044739 BW(KiB/s)=1586&lt;/td&gt;&lt;td&gt;IOPS=371.181793 BW(KiB/s)=1494&lt;/td&gt;&lt;td&gt;IOPS=380.360535 BW(KiB/s)=49018&lt;/td&gt;&lt;td&gt;IOPS=491.313446 BW(KiB/s)=63310&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These numbers are based on default test parameters (e.g. 100Gi PVC size). The numbers indicate that block storage generally performs better in default setting. So we should use managed disk instead of azure file unless multiple pods needs to access the same volume.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Validate Persistent Storage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use mysql as a quick and dirty test of storage classes. We can deploy the following manifest:&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;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;PersistentVolumeClaim&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;mysql-pvc&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;storageClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;managed-csi-premium&lt;/span&gt; &lt;span style="color:#75715e"&gt;# the storage class being tested&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5Gi&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;Secret&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;mysql&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;data&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;password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;eHl6 &lt;/span&gt; &lt;span style="color:#75715e"&gt;# base64 code of xyz&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;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;mysql&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;mysql&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;mysql&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;mysql&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;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql:5.6&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;mysql&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;MYSQL_ROOT_PASSWORD&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;valueFrom&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;secretKeyRef&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;mysql&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;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;password&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;3306&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;mysql&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;volumeMounts&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;mysql-persistent-storage&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;mountPath&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/var/lib/mysql&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;volumes&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;mysql-persistent-storage&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;persistentVolumeClaim&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;claimName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql-pvc&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;mysql-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;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;service&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;tcp-mysql&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;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3306&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;targetPort&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3306&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;Once the Pod has been created, then we use a throw-away Pod to connect to mysql service and build some data:&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 run mysql-cli --rm -i --tty --image imega/mysql-client -- /bin/sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;If you don&lt;span style="color:#e6db74"&gt;&amp;#39;t see a command prompt, try pressing enter.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;/ # mysql --host=mysql-service.default.svc.cluster.local --user=root --password=xyz&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 9 Server version: 5.6.51 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type &amp;#39;&lt;/span&gt;help;&lt;span style="color:#e6db74"&gt;&amp;#39; or &amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\h&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; for help. Type &amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\c&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt; to clear the current input statement. MySQL &lt;span style="color:#f92672"&gt;[(&lt;/span&gt;none&lt;span style="color:#f92672"&gt;)]&lt;/span&gt;&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From the mysql shell, we create a test database with dummy data populated:&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-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DATABASE&lt;/span&gt; &lt;span style="color:#f92672"&gt;`&lt;/span&gt;testdb&lt;span style="color:#f92672"&gt;`&lt;/span&gt;; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;USE testdb; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; tasks ( &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task_id INT AUTO_INCREMENT &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title VARCHAR(&lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description TEXT, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; created_at &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CURRENT_TIMESTAMP&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;) ENGINE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;INNODB; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job A&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;Morning Standup&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job B&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;Latte with two shots of espresso&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job C&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;Coding coding and coding&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job D&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;git commit&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;Each SQL command should return with number of rows affected and then we can exit the MySQL shell and Pod shell. Once we exit out of the Pod shell, the Pod is deleted. We can re-connect to validate the data are still present:&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 run mysql-tester --rm -i --tty --image imega/mysql-client -- mysql --host&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mysql-service.default.svc.cluster.local --user&lt;span style="color:#f92672"&gt;=&lt;/span&gt;root --password&lt;span style="color:#f92672"&gt;=&lt;/span&gt;xyz --database testdb --execute&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM tasks;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;If you don&lt;span style="color:#e6db74"&gt;&amp;#39;t see a command prompt, try pressing enter.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Got error: Access denied for user &amp;#39;&lt;/span&gt;root&lt;span style="color:#e6db74"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;147.206.3.15&lt;span style="color:#e6db74"&gt;&amp;#39; (using password: NO)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;+---------+-------+----------------------------------+---------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| task_id | title | description | created_at |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;+---------+-------+----------------------------------+---------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 1 | Job A | Morning Standup | 2022-06-22 20:29:12 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 2 | Job B | Latte with two shots of espresso | 2022-06-22 20:29:12 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 3 | Job C | Coding coding and coding | 2022-06-22 20:29:12 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 4 | Job D | git commit | 2022-06-22 20:29:13 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;+---------+-------+----------------------------------+---------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Session ended, resume using &amp;#39;&lt;/span&gt;kubectl attach mysql-tester -c mysql-tester -i -t&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt; command when the pod is running&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pod &lt;span style="color:#e6db74"&gt;&amp;#34;mysql-tester&amp;#34;&lt;/span&gt; deleted&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The output validates the persistent storage of the data. When testing a different storage class, simply start over with a different storage class specified in the PVC part of the manifest.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This post focuses on storage options for persistent volumes on Azure Kubernetes service. I use my own Terraform template with some custom configuration. I also covered kubestr as benchmarking tool and ran it against the built-in storage classes. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In some situations, the built-in options do not suit your needs. For example, you might want your application to use persistent volume in a consistent way across multiple cloud vendors. We will have to resort to third-party software defined storage layer. In the next post, I will explore a couple of SDS-based options, namely Portworx and Ceph. The custom configurations in the Terraform template will be helpful when we configure Portworx.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/07/intro-to-ceph-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Intro to Ceph storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 2 of 3 – Portworx&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Intro to Ceph storage</title><link>https://www.digihunch.com/2022/07/intro-to-ceph-storage/</link><pubDate>Thu, 21 Jul 2022 18:55:00 -0400</pubDate><guid>https://www.digihunch.com/2022/07/intro-to-ceph-storage/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-ceph.webp" alt="Featured image of post Intro to Ceph storage" /&gt;&lt;p class="wp-block-paragraph"&gt;Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability. In this post, I will introduce Ceph and explain how it stands out from traditional enterprise storage technology. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-software-defined-storage"&gt;Software defined storage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the realm of enterprise storage, I discussed &lt;a href="https://www.digihunch.com/2020/07/emc-productlines/"&gt;PowerScale (Isilon)&lt;/a&gt; from Dell EMC, and touched on &lt;a href="https://www.digihunch.com/2021/09/file-storage-vs-object-storage/"&gt;ONTAP&lt;/a&gt; by NetApp as an alternative. These solutions usually include both enterprise grade hardware, and the software layer that manages those expensive hardware. As the competition with cloud storage arises, those vendors start to decouple the software layer from the hardware to sell them separately. As a result, clients have the options to use commodity hardware. On the other hand, the software layer is built to be more accommodative to different hardware options. Eventually, the software layer evolves into Software Defined Storage (SDS) with the purpose of supporting cheaper storage hardware.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This table shows the full solution offering and SDS offering from NetApp and Dell EMC:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-black-color has-cyan-bluish-gray-background-color has-text-color has-background has-fixed-layout"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Full solution offering&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;SDS offering&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NetApp&lt;/td&gt;&lt;td&gt;&lt;a href="https://docs.netapp.com/ontap-9/index.jsp"&gt;ONTAP&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://docs.netapp.com/us-en/ontap-select/index.html"&gt;ONTAP Select&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;EMC&lt;/td&gt;&lt;td&gt;PowerScale&lt;/td&gt;&lt;td&gt;PowerFlex&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is not easy to make a proprietary SDS appliance support commodity hardware. For example, PowerFlex currently supports (and bundles with) DELL&amp;#8217;s commodity hardware only. It is most likely an involuntary move. Then, why would these commercial providers even be motivated to support a broader range of hardware by moving to SDS? It is because they face fierce competition from open-source SDS technologies, which were born to support commodity hardware. In this family of technologies, Ceph is a rising star. This family also includes other technologies such as Gluster and HDFS.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the performance of a storage based on SDS still has to do with the underlying hardware. Therefore, comparing Ceph storage with PowerScale is apple to orange, without identical storage hardware. Now that we decoupled SDS and hardware, let&amp;#8217;s take a look at two important aspects of SDS: the distributed technology to manage hardware, and the interface it provides to storage clients.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Distributed storage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The reason to use an SDS layer to manage hardware in a distributed architecture is for better scalability and high availability. The soul of this SDS layer is the ability to manage distributed system. However, a distributed storage introduces problems of its own, such as coordinating consistency. Different storage technologies have their own way to tackle these problems. For example, with PowerScale, OneFS has its own Group Management Protocol. Ceph uses CRUSH for data distribution. GlusterFS uses DHT(Distributed Hash Table) Translator. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Storage architects usually do not need to know these technologies in detail. It is not the intention of this post to cover the details of any distributed technology in any of the storage options above. However, storage architects needs to know supported API very well.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Access API&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The supported access API of a storage system determines its compatibility with client systems. One good example is NFS for file storage, which defines the protocol for file share without defining the underlying implementation. Most GNU/Linux distributions come with nfsd (NFS server) which exports directories on XFS or ext4 FS as a file share with NFS protocol. In order to transfer data over network, NFS uses RPC, a request-response protocol. With object storage, S3 is a widespread protocol. Below is a list of storage implementations and their supported access API:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Ceph supports librados, S3, Swift and FUSE&lt;/li&gt;&#10;&lt;li&gt;GlusterFS supports SMB, NFS, FUSE,&lt;/li&gt;&#10;&lt;li&gt;PowerScale supports NFS, SMB/CIFS, HDFS, Object, POSIX&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;CephFS is distributed file system built on top of Ceph RADOS. It is also a client-server architecture. A Ceph Client, via &lt;a href="https://docs.ceph.com/en/latest/rados/api/librados/"&gt;librados&lt;/a&gt;, interacts directly with OSDs to store and retrieve data. In order to interact with OSDs, the client app must invoke librados and connect to a Ceph Monitor. For compatibility, CephFS namespaces can be export over NFS protocol using &lt;a href="https://docs.ceph.com/en/latest/rados/api/librados-intro/"&gt;NFS-Ganesha&lt;/a&gt; NFS server.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Ceph Architecture&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ceph is a high-performance, distributed storage platform. It provides object storage, block storage and distributed file system, all backed by a single, reliable storage cluster running on commodity server hardware. A Ceph Storage Cluster consists of Ceph Nodes on a network. A Ceph Storage cluster requires at least one Ceph monitor (ceph-mon), Ceph Manager (ceph-mgr) and Ceph OSDs (ceph-osd). For file system clients, it also requires Ceph Metadata Server (MDS, ceph-mds) to allow user to execute basic commands on POSIX file system (e.g. ls, find)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Under the hood, Ceph stores data as objects within logical storage pools. Using the&amp;nbsp;&lt;a href="https://docs.ceph.com/en/latest/glossary/#term-CRUSH"&gt;CRUSH&lt;/a&gt;&amp;nbsp;algorithm, Ceph calculates which placement group (PG) should contain the object, and which OSD should store the placement group. The CRUSH algorithm enables the Ceph Storage Cluster to scale, rebalance, and recover dynamically.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="689" height="483" src="https://www.digihunch.com/wp-content/uploads/2022/06/image-11.webp" alt="" class="wp-image-5585"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ceph is based on RADOS (reliable autonomic distributed object store), a self-healing system that distributes and replicates data across nodes. It then layers CephFS (a distributed file system), block storage service (RADOS Block Device or RBD), and s3-compatible object storage (RADOS Gateway or RGW) on top of RADOS. For a better description, refer to &lt;a href="https://ubuntu.com/ceph/what-is-ceph"&gt;this&lt;/a&gt; page. The chart above shows how Ceph interacts with different kinds of client. For CephFS, the client can interact with the file system via metadata daemon, as illustrated below. This diagram looks similar to the diagram for &lt;a href="https://www.digihunch.com/2020/07/nfs-network-file-system-and-rpc-remote-procedure-call/"&gt;NFS&lt;/a&gt;.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="883" height="706" src="https://www.digihunch.com/wp-content/uploads/2022/06/image-12.webp" alt="" class="wp-image-5586"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a RADOS cluster, each server runs some daemons (i.e. OSD, MON or MDS). &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="736" height="317" src="https://www.digihunch.com/wp-content/uploads/2022/09/ceph.webp" alt="" class="wp-image-7275"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When an I/O request occurs, it needs to be mapped to the specific OSD that keeps the storage units. Here is an illustration of the mapping:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="603" height="338" src="https://www.digihunch.com/wp-content/uploads/2022/09/io-path.webp" alt="" class="wp-image-7277"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As typically observed in distributed system, there is quite some communication overhead to serve a file.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Ceph Cluster Installation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Installing a VM-based Ceph cluster is no trivial effort and there are several methods. The recommended method is &lt;a href="https://docs.ceph.com/en/latest/cephadm/#cephadm"&gt;Cephadm&lt;/a&gt;. &lt;a href="https://kifarunix.com/install-and-setup-ceph-storage-cluster-on-ubuntu/"&gt;Here&lt;/a&gt; is a good instruction, where you will notice a lot of steps on each nodes, such as configuring NTP, installing docker, configuring hostname, Linux user and SSH, etc. You may also check &lt;a href="https://www.youtube.com/watch?v=LxDQyFWDNHI"&gt;this&lt;/a&gt; video for how involving it is. Red Hat adopts &lt;a href="https://www.redhat.com/en/technologies/storage/ceph"&gt;Ceph&lt;/a&gt; project as a product and has an &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_ceph_storage/5/html/installation_guide/red-hat-ceph-storage_install"&gt;installation guide&lt;/a&gt; on its documentation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Previously, there was a legacy tool &lt;a href="https://docs.ceph.com/projects/ceph-ansible/en/latest/"&gt;ceph-ansible&lt;/a&gt; to help administrators with server configuration. It is similar to the way kubespray helps administrators configure Kubernetes cluster. However, the &lt;a href="https://docs.ceph.com/en/quincy/install/#other-methods"&gt;document&lt;/a&gt; suggests that ceph-ansible is not integrated with new orchestrator APIs and therefore is not a viable option anymore. Also I did not find a way to install a single-node ceph cluster just for a quick demo. It involves tweaking the &lt;a href="https://docs.ceph.com/en/quincy/rados/operations/crush-map/"&gt;CRUSH&lt;/a&gt; map configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If we deploy Ceph on Kubernetes for Kubernetes workload, we use &lt;a href="https://rook.io/docs/rook/v1.9/Getting-Started/intro/"&gt;Rook&lt;/a&gt;, an orchestrator running on Kubernetes, to integrate storage to a cluster.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Cloud Native Storage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Moving to cloud native storage, instead of presenting storage to operating system, we need to configure storage classes for Pods to use persistent volumes dynamically, using storage provisioners. Ceph also shows good presence in cloud native storage ecosystem. In a self-managed Kubernetes cluster, Ceph gives us the capability to configure storage classes to access connected storage. In public cloud, Ceph allows us to configure storage classes connecting to disks attached to the Nodes, an alternative to the cloud vendor provided native storage classes with high availability across availability zones. This layer enables the organization to normalize how their application connects to persistent volumes, a capability particularly helpful in the multi-cloud strategy of the cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Rook is a CNCF project to orchestrate storage system on Kubernetes. It automates storage administrative tasks such as deployment, bootstrapping, configuring, provisioning and monitoring, using declarative templates. It supports Ceph and a number of other storage backends such as Cassandra, NFS, MinIO. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/07/kick-the-tires-on-argocd/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kick the tires on ArgoCD&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Etcd – the key-value store for Kubernetes</title><link>https://www.digihunch.com/2022/06/etcd-the-key-value-store-for-kubernetes/</link><pubDate>Tue, 14 Jun 2022 00:10:00 -0400</pubDate><guid>https://www.digihunch.com/2022/06/etcd-the-key-value-store-for-kubernetes/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-etcd.webp" alt="Featured image of post Etcd – the key-value store for Kubernetes" /&gt;&lt;h2 class="wp-block-heading"&gt;Etcd in Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes &lt;a href="https://www.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/"&gt;architecture&lt;/a&gt;, &lt;a href="https://etcd.io/"&gt;etcd&lt;/a&gt; is the data store. It stores the desired state of Kubernetes object. API server is the only client that connects to etcd (via &lt;a href="https://grpc.io/"&gt;gRPC&lt;/a&gt; protocol). Cluster builder specifies the endpoint of etcd as a parameter to the kube-api-server process. Other Kubernetes components, whether in the control plane or from the nodes, connect to API server. API server translates their request into etcd query, and then translates etcd query result into what its clients ask for. For this reason, communication with etcd accounts for a lot of network traffic in a Kubernetes cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The etcd store is a CNCF project for &amp;#8220;a distributed, reliable key-value store for critical data in a distributed system&amp;#8221;, developed by CoreOS team. So it is essentially a distributed key-value store for any distributed application. If an application runs on Kubernetes, it can leverage etcd store, by keeping their configurations in ConfigMap and Secret objects. One key feature is to watch for specific keys or directories for changes, and react to the changes. Voila! This is the underlying mechanism for &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controller&lt;/a&gt;!&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Kubernetes cluster may have stacked etcd deployment or connect to an external etcd store.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="848" height="560" src="https://www.digihunch.com/wp-content/uploads/2022/05/stacked.webp" alt="" class="wp-image-5250"/&gt;&lt;figcaption class="wp-element-caption"&gt;stacked etcd architecture&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="856" height="601" src="https://www.digihunch.com/wp-content/uploads/2022/05/external.webp" alt="" class="wp-image-5251"/&gt;&lt;figcaption class="wp-element-caption"&gt;external etcd architecture&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In managed Kubernetes services such as EKS in AWS and AKS in Azure, users usually do not directly access etcd store. However, it is still a very important component to understand. Its use case includes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Configuration sharing&lt;/li&gt;&#10;&lt;li&gt;Service discovery&lt;/li&gt;&#10;&lt;li&gt;Consistency&lt;/li&gt;&#10;&lt;li&gt;Watching mechanism&lt;/li&gt;&#10;&lt;li&gt;Expiry and extension of key &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The consistency use case is based on Raft protocol for distributed consensus.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Raft protocol&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I am not an expert in distributed consensus protocols and nor do I intent to cover it in depth. At a high level, I have heard of three of them so far:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Etcd uses Raft protocol&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2020/08/zookeeper/"&gt;Zookeeper&lt;/a&gt; uses ZAB protocol&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2018/03/cassandra-architecture-summary/"&gt;Cassandra&lt;/a&gt; uses paxos protocol&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&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 intro to the three protocols. Instead of getting into the fine details, I would like to discuss why we need such a consensus protocol (or consensus mechanism) in distributed systems, which are also decentralized systems.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="686" src="https://www.digihunch.com/wp-content/uploads/2025/04/etcd-topology-1024x686.webp" alt="" class="wp-image-13111" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/etcd-topology-1024x686.webp 1024w, https://www.digihunch.com/wp-content/uploads/2025/04/etcd-topology-300x201.webp 300w, https://www.digihunch.com/wp-content/uploads/2025/04/etcd-topology-768x514.webp 768w, https://www.digihunch.com/wp-content/uploads/2025/04/etcd-topology-410x275.webp 410w, https://www.digihunch.com/wp-content/uploads/2025/04/etcd-topology.webp 1138w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Centralized, Decentralized, Distributed systems&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The reason a distributed system needs consensus protocol, is that a distributed system lacks a single source of truth as centralized systems do. Different parts of the distributed system may receive different signals but they must come to agreement of a single plan to act. Lamport studies this with an analogy of &lt;a href="https://en.wikipedia.org/wiki/Byzantine_fault"&gt;Byzantine Generals&lt;/a&gt; problem, and first proposed Paxos protocol. &lt;a href="https://en.wikipedia.org/wiki/Paxos_(computer_science)"&gt;Paxos&lt;/a&gt; has been an important foundation to modern distributed systems. In Paxos, consensus is achieved in &lt;a href="https://martinfowler.com/articles/patterns-of-distributed-systems/paxos.html"&gt;two phases&lt;/a&gt;, which creates the problem of livelocks. Raft is an alternative to Paxos, and is widely adopted today. &lt;a href="http://thesecretlivesofdata.com/raft/"&gt;Here&lt;/a&gt; is a link to an animated illustration for Raft protocol. The Raft protocol is also used in Redis. It has three roles: Leader, Candidate, and follower. ZAB protocol is similar to Raft, where it needs to select a leader.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Etcd Lab&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In troubleshooting, if we suspect that the response from API server is inconsistent with etcd store, we want to directly connect to it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Managed Kubernetes services do not expose their etcd store. We can use KinD or Minikube. There are two types of jump box to access etcd store: using etcd Pod, or SSH to a Node. To connect to etcd, we also need the X509 key, certificate and CA&amp;#8217;s certificate, in addition to the endpoint, usually an IP with port 2389. When I connect to Pod shell, I find the command shell not easy to use. They might miss basic command such as ls, or do not support auto completion.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Take KinD for example, we first create a secret, then we can connect to the node with docker CLI 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;kubectl create ns myns&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n myns create secret generic mysecret --from-literal key1&lt;span style="color:#f92672"&gt;=&lt;/span&gt;value1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n myns get secret mysecret -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.key1}&amp;#39;&lt;/span&gt; | base64 -d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec -it control /bin/bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From the 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;apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt install etcd-client&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl version&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nc -vz localhost &lt;span style="color:#ae81ff"&gt;2379&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd&#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;export ETCDCTL_API&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;export ETCDCTL_CERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/apiserver-etcd-client.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/apiserver-etcd-client.key&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_CACERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_ENDPOINTS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;https://127.0.0.1:2379&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl member list write out&lt;span style="color:#f92672"&gt;=&lt;/span&gt;table&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we can see the secret object directly with etcd store:&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;etcdctl get /registry/secrets/myns/mysecret&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With get query, when using &amp;#8211;prefix, we can use &amp;#8211;keys-only switch to list keys without values:&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;etcdctl get --prefix /registry/api --keys-only&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get --prefix /registry/namespace -wjson&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can write key-value with put 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;etcdctl put myloc &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get myloc -wjson&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In Kubernetes, all the key names start with / which makes the key looks like a POSIX path. Every Kubernetes object is stored in etcd with a unique key following a self-explanatory naming pattern. To display the path, we can also use debug log that records the call to API server:&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 ns myns -v9&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Look for curl command such as:&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;I0523 22:51:43.517728 32347 round_trippers.go:466] curl -v -XGET -H &amp;#34;Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json&amp;#34; -H &amp;#34;User-Agent: kubectl/v1.23.6 (darwin/amd64) kubernetes/ad33385&amp;#34; &amp;#39;https://127.0.0.1:64081/api/v1/namespaces/myns&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From there we can see the etcd query as the URI is namespaces/myns, which we use in etcdctl query path:&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;etcdctl get /registry/namespaces/myns&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Every type of Kubernetes object has a storage.go file in their implementation that defines how api server should write object. &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/pkg/registry/core/pod/storage/storage.go"&gt;Here&lt;/a&gt; is an example for Pod object.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Etcd also supports watch command to watch for changes. 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;etcdctl watch --prefix /registry/namespace &lt;span style="color:#75715e"&gt;# watch output k create ns newns&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;Now we create a namespace with kubectl:&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 ns myns&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The output from etcdctl will reflect the change. The communication between etcdctl and etcd is gRPC protocol. The output is based on stream, as we can see from the watch result.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-etcd-maintenance"&gt;Etcd Maintenance&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Like any distributed store, etcd needs &lt;a href="https://etcd.io/docs/v3.5/op-guide/maintenance/"&gt;maintenance&lt;/a&gt; and operation work. For example, we can check endpoint status with endpoint 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;etcdctl endpoint status&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can also backup and restore etcd store with etcdctl 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;etcdctl snapshot save /tmp/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;This was an question in &lt;a href="https://www.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/"&gt;CKA exam&lt;/a&gt;. In real life, when the workload scales up, the etcd store may come across many pitfalls, such as degraded performance, unresponsiveness, some etcd member going down, network partition on etcd store causing split brain. It is important to ensure efficient communication between API server and etcd store. The etcdctl provides defrag and compact commands for common maintenance activities.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/05/hosting-database-on-kubernetes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Hosting database on Kubernetes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/06/chaos-mesh-cloud-native-chaos-engineering/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Chaos Mesh – Cloud Native Chaos Engineering&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Hosting database on Kubernetes</title><link>https://www.digihunch.com/2022/05/hosting-database-on-kubernetes/</link><pubDate>Sun, 29 May 2022 11:01:00 -0400</pubDate><guid>https://www.digihunch.com/2022/05/hosting-database-on-kubernetes/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-database.webp" alt="Featured image of post Hosting database on Kubernetes" /&gt;&lt;h2 class="wp-block-heading"&gt;Background&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&amp;#8220;We want to host Postgres database on Kubernetes. Can you help us?&amp;#8221;. The client appears assertive and reluctant to resort to managed services. So I did some homework and went through &lt;a href="https://www.youtube.com/watch?v=3TFXztwat_s"&gt;this&lt;/a&gt; tutorial. My thought: it&amp;#8217;s doable, but don&amp;#8217;t do it unless operating database as a service is your main business.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I believed that was the client&amp;#8217;s best interest, until I came across this the blog post &lt;a href="https://thenewstack.io/a-case-for-databases-on-kubernetes-from-a-former-skeptic/"&gt;A Case for Databases on Kubernetes from a Former Skeptic&lt;/a&gt;. The author explained his journey from being a skeptic, to grudging acceptance, and eventually to an evangelist on running database on Kubernetes. The same voice came from the author of the upcoming book &lt;a href="https://www.oreilly.com/library/view/managing-cloud-native/9781098111380/"&gt;Managing Cloud Native Data on Kubernetes&lt;/a&gt;, who also advocates hosting database on Kubernetes. While the points in the chapters are valid, the book also includes a good amount of technical details which might lead reader to believe the opposite view.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Just a few years back, Kubernetes was not mature to host database. This is changing in 2022. Nowadays, for clients with their own Kubernetes platform, technological maturity is no longer the main reason that keeps them from hosting database on Kubernetes, it is the operational cost. The operational cost has to do with whether the client has in-house expertise in database and Kubernetes. If they do, the hard path makes economical sense.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this post, we discuss what we need to be aware of in order to host database on Kubernetes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-benefit-with-kubernetes"&gt;Benefit with Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first few versions of Kubernetes only supported stateless workload (reference &lt;a href="https://www.youtube.com/watch?v=BE77h7dmoQU"&gt;documentary&lt;/a&gt;). That is what Kubernetes was born to solve. Built-in objects such as replicaSet, deployment, horizontalPodAutoscaler are abstractions of operations particular to stateless workload. Pods for stateless workload are ephemeral: they crash and get replaced at any time. Because they don&amp;#8217;t carry persistent data themselves, they are expendable. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes&amp;#8217; orchestration capability are driven by controllers. As &lt;a href="https://www.digihunch.com/2022/04/kubernetes-operator/"&gt;discussed&lt;/a&gt;, the &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/#controller-pattern"&gt;controller pattern&lt;/a&gt; is adopted in all controller implementations. They are the engines of the platform that works tirelessly in a control loop to ensure desired states matches their declared states. This is a key feature of Kubernetes as container platform. Let&amp;#8217;s examine a web service that requires 5 instances behind load balancer. With traditional hosting model on Linux servers, you&amp;#8217;d have it installed on all five VMs. If the process on one of the VMs dies, the VM has to be removed from the load balancer&amp;#8217;s target pool. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One may wrap the process with process monitor and control utility such as &lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt;, and re-install the application using automation utility (e.g. &lt;a href="https://www.ansible.com/products/controller"&gt;Ansible&lt;/a&gt;). However, each server is unaware of the status of its peer. Without a central &amp;#8220;Control Plane&amp;#8221;, there is no coordination between the activities of each VMs. Kubernetes controller solved all these operational problems. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://k21academy.com/wp-content/uploads/2021/05/Actual_DesiredState.png" alt="Kubernetes Control Loop"/&gt;&lt;figcaption class="wp-element-caption"&gt;Control Loop&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes comes with a set of build-in &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controllers&lt;/a&gt; that run inside the kube-controller-manager. Here is a good page about how &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controllers&lt;/a&gt; work. Controller is what is missing in many automation tools other that Kubernetes. Even though Red Hat now brands Ansible as Automation &lt;em&gt;Controller&lt;/em&gt;, it does not involve a control loop or controller pattern. If there&amp;#8217;s one thing that sets Kubernetes apart from other hosting platforms and automation platforms, it is the implementation of controller pattern. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Stateful workload&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Does the controller pattern also benefit stateful workload? Yes. How to orchestrate Pods for stateful workload is usually more tricky. CRD can define a custom object type for controller to consume. In this case, an operator is an implementation of the controller pattern. This pattern is also known as the &lt;a href="https://www.digihunch.com/2022/04/kubernetes-operator/"&gt;operator pattern&lt;/a&gt;. In a replicaSet, Pod names have extensions of randomly generated numbers. A statefulSet names its the Pods by sequential numbers. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For Postgres database, Bitnami built a good &lt;a href="https://artifacthub.io/packages/helm/bitnami/postgresql-ha"&gt;Helm Chart&lt;/a&gt; to install the database automatically. However, it does not have a control loop. If someone changes the workload after initial installation, the change is not monitored or controlled by any controller. This is a disadvantage of Helm chart as compared with operators. For PostgreSQL, there are a &lt;a href="https://blog.flant.com/comparing-kubernetes-operators-for-postgresql/"&gt;number of operators&lt;/a&gt;, the most notable being PGO (&lt;a href="https://access.crunchydata.com/documentation/postgres-operator/v5/"&gt;Postgres Operator&lt;/a&gt;) from &lt;a href="https://www.crunchydata.com/"&gt;Crunchy Data&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To install an instance of PostgreSQL database, we need to install the operator, and then declare a Custom Resource using the &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/v5/references/crd/"&gt;PostgresCluster&lt;/a&gt; CRD. The operator will set up the cluster according to the declaration made in the &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/v5/tutorial/create-cluster/"&gt;PostgresCluster&lt;/a&gt; CR. I used the &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/v5/quickstart/"&gt;quick start guide&lt;/a&gt; to bring Postgres up real quick on an Azure Kubernetes cluster. The &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/v5/"&gt;operator&lt;/a&gt; (v5) supports common cloud Kubernetes platforms (GKE, EKS, AKS), VMware Tanzu, Openshift, Rancher, Kubernetes. It does not explicitly indicate whether PGO supports Minikube or kind.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So far, I&amp;#8217;ve discussed the pros of running PostgreSQL on Kubernetes using Postgres Operator. We can describe the database deployment in a CR and the controller (operator) will monitor the resource incessantly to ensure the actual state matches the state defined in the CR. Not only is it doable to host database in Kubernetes, it makes our lives even easier. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Persistent storage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Database is not only a stateful workload, it also has special requirement on storage. It needs to persist data, support ACID transaction, and make optimal use of disks. When we operate everything on premise, we use fibre cable with a &lt;a href="https://www.digihunch.com/2019/05/storage-nitty-gritty-2-5/"&gt;SAN&lt;/a&gt; as the storage media for database file. The operating system allows the database process to interact with blocks on the storage volume via device mapper.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes, we need to give Pods persistent volumes. There are a few APIs: Storage Class, Volume Storage Class, Persistent Volume and Persistent Volume Claims. Storage Class represents how Pod can connect to a storage. Pods will need PVCs in order to read and write on PVs. However, since Pods are ephemeral &amp;#8211; a Pod may crash any time, even if it is in the middle of writing to a PV, during an ACID transaction. The scheduler may reschedule the crashed Pod to a different node. Then it will need to pick up the PV from where it left off, on the new Node. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Take Azure Kubernetes Service for example, a few storage classes are available by default, backed by Azure managed disk (managed-csi) or Azure file storage (azurefile-csi):&lt;/p&gt;&#10;&lt;table id="tablepress-19" class="tablepress tablepress-id-19 tbody-has-connected-cells"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;td class="column-1"&gt;&lt;/td&gt;&lt;th class="column-2"&gt;StorageClass&lt;/th&gt;&lt;th class="column-3"&gt;Azure storage service&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 rowspan="4" class="column-1"&gt;in-tree&lt;/td&gt;&lt;td class="column-2"&gt;default&lt;/td&gt;&lt;td class="column-3"&gt;Managed Disk using Azure StandardSSD&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-2"&gt;managed-premium&lt;/td&gt;&lt;td class="column-3"&gt;Managed Disk using Azure Premium Storage&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td class="column-2"&gt;azurefile&lt;/td&gt;&lt;td class="column-3"&gt;Azure File Share using Azure Standard Storage&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td class="column-2"&gt;azurefile-premium&lt;/td&gt;&lt;td class="column-3"&gt;Azure File Share using Azure Premium Storage&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td rowspan="4" class="column-1"&gt;csi&lt;/td&gt;&lt;td class="column-2"&gt;managed-csi&lt;/td&gt;&lt;td class="column-3"&gt;Managed Disk using Azure StandardSSD&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-7"&gt;&#10;&#9;&lt;td class="column-2"&gt;managed-csi-premium&lt;/td&gt;&lt;td class="column-3"&gt;Managed Disk using Azure Premium Storage&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-8"&gt;&#10;&#9;&lt;td class="column-2"&gt;azurefile-csi&lt;/td&gt;&lt;td class="column-3"&gt;Azure File Share using Azure Standard Storage&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-9"&gt;&#10;&#9;&lt;td class="column-2"&gt;azurefile-csi-premium&lt;/td&gt;&lt;td class="column-3"&gt;Azure File Share using Azure Premium Storage&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-19 from cache --&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If we use storage class based on Azure disks to create a PV, only one Pod can use the PV. If we use storage class based on Azure files to create a PV, then the storage is mounted as NFS (Linux) or SMB (Windows) share. File storage is not a valid &lt;a href="https://www.digihunch.com/2020/08/cloud-storage-overview/"&gt;use case&lt;/a&gt; for database workload and it can significantly degrade database performance. When I tried to use a file-storage based CSI with PGO, the Pod reports an &lt;a href="https://github.com/CrunchyData/postgres-operator/issues/2870"&gt;error&lt;/a&gt; and will not start properly. We should use Azure disk based CSI storage classes. That leaves us with two options: managed-csi and managed-csi-premium. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;High Availability&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Even with these to options left, we still have to investigate how database Pods interact with persistent volume for high availability, in order to determine whether any of the options are suitable. The two storage classes differ by disk performance but both have its own &lt;a href="https://docs.microsoft.com/en-us/azure/aks/availability-zones#azure-disk-availability-zone-support"&gt;limitation&lt;/a&gt; with multi-AZ support on Azure managed disks. When the cluster operates across zones, the Kubernetes scheduler may reschedule a Pod crashed in one zone to a Node in a different availability zone (a different data centre). Even though the managed disks, when attached to VMs, can be configured as zone-redundant, when they are used as Kubernetes volume, they are NOT zone-redundant. So the node in a different zone will not be able to attach PV to the new Pod. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are SDS (software-defined storage) solution such as &lt;a href="https://portworx.com/wp-content/uploads/2020/06/portworx-microsoft-aks-reference-architecture.pdf"&gt;Portworx&lt;/a&gt; that solves the limitation of Azure disk for cross-region storage volume. The SDS layer brings managed disks from multiple availability zones into a pool. This storage pool acts as a highly available, cross-zone storage tier presented to AKS as persistent volumes. We can install Portworx as the SDS layer using Portworx operator. To do so, we first have to configure &lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/azure-managed-identity-on-aks"&gt;grant the cluster the permission &lt;/a&gt;to provision resources in Azure, because the Portworx operator will use node&amp;#8217;s identity (kubelet identity) to provision Azure resources on behalf of the nodes. Portworx will provision Azure disks and acts as the intermediary layer.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="726" src="https://www.digihunch.com/wp-content/uploads/2025/04/aks-data-1024x726.webp" alt="" class="wp-image-13110" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/aks-data-1024x726.webp 1024w, https://www.digihunch.com/wp-content/uploads/2025/04/aks-data-300x213.webp 300w, https://www.digihunch.com/wp-content/uploads/2025/04/aks-data-768x545.webp 768w, https://www.digihunch.com/wp-content/uploads/2025/04/aks-data.webp 1430w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from cross-zone high availability enabled by PX-Store, Portworx can also help with cross-region replication of persistent volumes. The PX-DR component can perform asynchronous replication across Azure regions. The destination region needs to have its own cluster because a single AKS cluster cannot span across regions.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Storage Class&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once we have portworx installed, the following storage classes are available by default:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;px-db&lt;/li&gt;&#10;&lt;li&gt;px-db-cloud-snapshot&lt;/li&gt;&#10;&lt;li&gt;px-db-cloud-snapshot-encrypted&lt;/li&gt;&#10;&lt;li&gt;px-db-encrypted&lt;/li&gt;&#10;&lt;li&gt;px-db-local-snapshot&lt;/li&gt;&#10;&lt;li&gt;px-db-local-snapshot-encrypted&lt;/li&gt;&#10;&lt;li&gt;px-replicated&lt;/li&gt;&#10;&lt;li&gt;px-replicated-encrypted&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The steps for installing porworx on AKS are documented &lt;a href="https://web.archive.org/web/20230204230139/https://docs.portworx.com/install-portworx/cloud/azure/"&gt;here&lt;/a&gt;. This blog post has more details in the &lt;a href="https://portworx.com/blog/portworx-enterprise-2-8-installation-on-oracle-kubernetes-engine-oke/"&gt;installation&lt;/a&gt; process on a different platform. We can also built CSI based storage classes with different IO priority and replication factors.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, Kubernetes operator pattern makes it easier to manage stateful workload. However, database performance depends largely on storage. To host database on Kubernetes, one will have to also manage the storage volumes on their own. There has not been a study on the impact to performance by moving database to Kubernetes platform. However, I only expect a degraded performance due to the layers introduced.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Example&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this section we configure a (minimally viable) PostgreSQL cluster using Crunchy Data pgo to demonstrate the idea. The steps are based on its &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/5.1.1/tutorial/"&gt;tutorial&lt;/a&gt; but it works on a local KinD cluster. As discussed in a &lt;a href="https://www.digihunch.com/2021/09/single-node-kubernetes-cluster-minikube/"&gt;previous post&lt;/a&gt;, I use KinD for testing workload requiring persistent storage because Minikube has this open &lt;a href="https://github.com/kubernetes/minikube/issues/12360"&gt;issue&lt;/a&gt; with permissions on PVs with multiple nodes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To prepare the cluster, we can use &lt;a href="https://github.com/digihunch/real-quicK-cluster/blob/main/kind/kind-config.yaml"&gt;kind-config.yaml&lt;/a&gt; file from my &lt;a href="https://github.com/digihunch/real-quicK-cluster"&gt;real-quicK-cluster&lt;/a&gt; repo:&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;kind create cluster --config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;kind-config.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# to delete cluster after testing: kind delete cluster --name kind&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;We use Helm to install the operator. Since the Helm chart is not hosted in a public repo, we&amp;#8217;d have to download the directory of the Helm 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;git clone https://github.com/CrunchyData/postgres-operator-examples&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd postgres-operator-examples&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm install -n postgres-operator --create-namespace crunchy-pgo helm/install&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n postgres-operator get po --watch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl explain postgresclusters&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we can create a YAML file for the Custom Resource and let&amp;#8217;s call it test-cluster.yaml with the following content:&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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;postgres&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;crunchydata&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PostgresCluster&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;postgres&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;backups&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pgbackrest&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;registry&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;developers&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;crunchydata&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;crunchydata&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;crunchy&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pgbackrest&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ubi8&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2.38&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&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:#a6e22e"&gt;repos&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;repo1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;volume&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;volumeClaimSpec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;accessModes&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ReadWriteOnce&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requests&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Gi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storageClassName&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;registry&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;developers&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;crunchydata&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;crunchydata&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;crunchy&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;postgres&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ubi8&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;14.3&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;instances&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dataVolumeClaimSpec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;accessModes&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ReadWriteOnce&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;requests&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Gi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;storageClassName&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;instance1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;replicas&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;minAvailable&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;postgresVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&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;In the manifest, we specified a cluster, using storageclass named &amp;#8220;standard&amp;#8221;, with 3 replicas and requiring 2 available. We assume a storage class named &amp;#8220;standard&amp;#8221; already exists and optimized for database workload. In the manifest, we also configured a backup job. We can apply the manifest and watch for the Pods to come up in a few minutes.&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 apply -f test-cluster.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n postgres-operator get po --watch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n postgres-operator describe postgresclusters hippo&#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 in the postgres-operator namespace should report something like this:&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 READY STATUS RESTARTS AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hippo-backup-mwpm-ps8wk 0/1 Completed &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 21s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hippo-instance1-6mls-0 4/4 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 3m35s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hippo-instance1-hjp6-0 4/4 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 3m35s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hippo-instance1-k4qf-0 4/4 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 3m35s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hippo-repo-host-0 2/2 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 3m35s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pgo-548d5f48bc-9w4z4 1/1 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 8m41s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pgo-upgrade-566b9cc98f-d7gkr 1/1 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 8m41s&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Three Pods for PostgreSQL are all up. The first backup run has completed already. We can connect to the cluster using psql following the &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/5.1.1/quickstart/"&gt;quick start guide&lt;/a&gt;. We can also configure an application. A good example application that uses PostgreSQL database is &lt;a href="https://www.keycloak.org/"&gt;KeyCloak&lt;/a&gt;. We briefly mentioned it in OIDC &lt;a href="https://www.digihunch.com/2022/02/istio-external-authorization/"&gt;discussion&lt;/a&gt;. Currently the keycloak example on Crunchy pgo&amp;#8217;s &lt;a href="https://access.crunchydata.com/documentation/postgres-operator/5.1.1/quickstart/"&gt;quick start guide&lt;/a&gt; is outdated. Instead, use the following content as keycloak.yaml:&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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;apps&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;postgres&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;labels&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;selector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;matchLabels&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;labels&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;containers&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;quay&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;keycloak&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;args&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;start-dev&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DB_VENDOR&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;postgres&amp;#34;&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DB_ADDR&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pguser&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;host&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DB_PORT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pguser&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;port&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DB_DATABASE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pguser&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dbname&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DB_USER&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pguser&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DB_PASSWORD&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pguser&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;hippo&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;password&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KEYCLOAK_USER&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KEYCLOAK_PASSWORD&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PROXY_ADDRESS_FORWARDING&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ports&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;containerPort&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8080&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;containerPort&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8443&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;readinessProbe&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;httpGet&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;/realms/master # https://stackoverflow.com/questions/70577004/keycloak-could-not-find-resource-for-full-path&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;initialDelaySeconds&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;restartPolicy&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Always&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;Once we apply keycloak.yaml, in a minute we should see and be able to port-forward web traffic:&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 apply -f keycloak.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n postgres-operator get po -l app.kubernetes.io/name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;keycloak&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME READY STATUS RESTARTS AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;keycloak-7995d78d7c-zjp4d 1/1 Running &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 4m29s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl port-forward deploy/keycloak -n postgres-operator 8080:8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After using the port-forward command, we can browse to web portal on my MacBook by http://localhost:8080 and configure an initial password, as shown here:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1086" height="937" src="https://www.digihunch.com/wp-content/uploads/2022/07/image.webp" alt="" class="wp-image-6112"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In real life system we would need a proper Ingress. After testing, delete the cluster with kind command and specify the cluster name (kind).&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Operation Cost&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Operation cost is an important consideration. Troubleshooting on Kubernetes platform is in general more complicated than just on a Unix system. Hosting database on Kubernetes requires skills not only on the Kubernetes platform, but also on database. There used to be database administrator positions where someone has to maintain the upgrade, the storage, the replication, the multi-tenancy and the performance optimization of database. With a database hosted on Kubernetes, the database administrator will have to perform all these activities on a containerized platform. This is not an easy undertaking, and in many occasions warrants a full-time position on its own. Therefore, don&amp;#8217;t host your database on Kubernetes, unless that is your main business. It is not the technology that shots down this option. It is the operation cost, such as complexity of configuration, and staff skillset, that makes this option not worth it.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/05/fsx-ontap-enterprise-storage-on-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;FSx ONTAP – Enterprise storage on AWS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/06/etcd-the-key-value-store-for-kubernetes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Etcd – the key-value store for Kubernetes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kubernetes Operator</title><link>https://www.digihunch.com/2022/04/kubernetes-operator/</link><pubDate>Thu, 07 Apr 2022 09:39:00 -0400</pubDate><guid>https://www.digihunch.com/2022/04/kubernetes-operator/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-operator.webp" alt="Featured image of post Kubernetes Operator" /&gt;&lt;p class="wp-block-paragraph"&gt;Kubernetes has a number of tools to automate the deployment of a single workload. In previous posts, we had covered &lt;a href="https://www.digihunch.com/2021/07/helm-configuration-management-for-kubernetes-resources/"&gt;Helm&lt;/a&gt; and &lt;a href="https://www.digihunch.com/2022/01/fluxcd-continuous-deployment-with-gitops/"&gt;Kustomize&lt;/a&gt;. What are left unresolved is how to maintain the status of workload after deployment is completed. In this post, I will give an introduction to Kubernetes Operator. Compared with Helm (templating approach) and Kustomize (patching approach), Kubernetes Operator follows the &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/"&gt;operator pattern&lt;/a&gt;. Operators are usually provided by the developer of the application.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-operator-pattern"&gt;Operator Pattern&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes, we know that a controller takes care of routine tasks to ensure that desired state expressed by Kubernetes resource types matches the current state. One example is that the Deployment controller ensures the number of pods running matches the amount specified in the replica field. Controller is the key to ensure that resources can be managed by declarative manifests for Kubernetes resources. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes makes use of controller pattern throughout its own design. One of its key component, Controller Manager, is a collection of many controllers. Each controller is in charge of a control loop, responsible for listening the object it manages. Another component, Kube-scheduler, is also a special type of Controller. The kube-scheduler monitors unscheduled Pod and health of nodes and determines the best Node to schedule the new Pod to. Then it writes the decision to etcd store for kubelet to execute.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This controller pattern is fairly successful in what it does and we can extend the use of it. Beyond the built-in resource types, we can create our own custom resource definitions (CRDs), and create controllers that watches for the manifest that declares custom resources (CRs). The controller ensures that the resource status matches their specifications. This is also known as reconciliation, which is implemented as a control loop. Operator pattern can be illustrated in the diagram below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://github.com/cncf/tag-app-delivery/raw/eece8f7307f2970f46f100f51932db106db46968/operator-wg/whitepaper/img/02_1_operator_pattern.png" alt="Operator Design Pattern"/&gt;&lt;figcaption class="wp-element-caption"&gt;Operator Pattern&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Technically, there is no difference between a controller and an operator. What makes an Operator (used to install workload) different than a native Kubernetes controller, are two things. First, an Operator usually needs CRDs because the built-in resource types are insufficient. Second, the operator reflects the domain knowledge to keep the target workload running. For example, stateful workloads such as database needs their operational steps executed in certain orders.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On &lt;a href="https://github.com/cncf/tag-app-delivery/blob/eece8f7307f2970f46f100f51932db106db46968/operator-wg/whitepaper/Operator-WhitePaper_v1-0.md"&gt;Operator Pattern&lt;/a&gt;, CNCF published a &lt;a href="https://www.cncf.io/wp-content/uploads/2021/07/CNCF_Operator_WhitePaper.pdf"&gt;whitepaper&lt;/a&gt; with a deeper review. This white paper is the best reference for a good understanding of the Operator Pattern.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Custom Resource Definition&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The built-in controllers work with built-in objects (pre-defined APIs). Custom operators usually need their own APIs to function. To extend Kubernetes API, we define the schema of these APIs in the form of CRDs (&lt;a href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules"&gt;custom resource definitions&lt;/a&gt;) using &lt;a href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation"&gt;OpenAPIv3&lt;/a&gt; standard. Then, we can declare Custom Resources (CRs) in compliance with the schema. The OpenAPIv3 schema in the CRD resource tells validating web hook (&lt;a href="https://www.digihunch.com/2022/01/kubernetes-admission-control/"&gt;admission control&lt;/a&gt;) how to validate the schema when we send an CR in to API server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we work with third-party operators, they usually provide CRDs along with the operator implementation. For example, in my &lt;a href="https://github.com/digihunch/wordpress-operator"&gt;operator example&lt;/a&gt; project, we have a minimalist CRD &lt;a href="https://github.com/digihunch/wordpress-operator/blob/main/config/crd/bases/wordpress.digihunch.com_wordpresses.yaml"&gt;WordPress&lt;/a&gt; with one property: sqlRootPassword and we can declare a CR as in &lt;a href="https://github.com/digihunch/wordpress-operator/blob/main/config/samples/wordpress_v1_wordpress.yaml"&gt;this&lt;/a&gt; example. For a more realistic use case, we can take a look at &lt;a href="https://github.com/kiali/kiali-operator/blob/master/crd-docs/crd/kiali.io_kialis.yaml"&gt;Kiali CRD&lt;/a&gt;. In the next section, we&amp;#8217;ll use it along with Kiali operator to install Kiali. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Operator Usage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Like &lt;a href="https://artifacthub.io/"&gt;Artifact Hub&lt;/a&gt; to Helm, &lt;a href="https://operatorhub.io/"&gt;OperatorHub&lt;/a&gt; is a public registry of most used Kubernetes Operators. In this section, we will take an example of using Operators. We will install Kiali as an add-on to Istio using Kiali CR and operator, which also depends on Prometheus to be installed using Prometheus Operator first. Note that the Kiali installation outlined in this section is not the &lt;a href="https://istio.io/latest/docs/ops/integrations/kiali/#option-1-quick-start"&gt;quick-start&lt;/a&gt; install manifests from Istio&amp;#8217;s &lt;a href="https://github.com/istio/istio/tree/master/samples/addons"&gt;sample&lt;/a&gt; directory. For Kiali on production system we have to customize the &lt;a href="https://kiali.io/docs/installation/installation-guide/"&gt;installation&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Suppose we have installed Istio, we can then install Prometheus operator using Helm. The Prometheus operator will install Prometheus. Then we use Helm again to install Kiali operator. The Kiali operator will watch for creation of Kiali CRD, to deploy services:&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 install -f prometheus-values.yaml --namespace istio-system --repo https://prometheus-community.github.io/helm-charts --version 13.6.0 istio-prometheus prometheus --insecure-skip-tls-verify&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm install -f kiali-operator-values.yaml --namespace kiali-operator --repo https://kiali.org/helm-charts --version 1.45.0 kiali-op kiali-operator --create-namespace&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl apply -f kiali-cr.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;I include example content for each file in the commands above on Github gist (&lt;a href="https://gist.github.com/digihunch/448180c019310a5dadb700c1bcdb0772"&gt;prometheus-values.yalm&lt;/a&gt;, &lt;a href="https://gist.github.com/digihunch/5574aba4aa9fc1aa15257bd6e811bf5b"&gt;kiali-operator-values.yaml&lt;/a&gt; and &lt;a href="https://gist.github.com/digihunch/2fd0884f5999416c8baf4197ee5790f3"&gt;kiali-cr.yaml&lt;/a&gt;). For more options for installing Kiali, refer to &lt;a href="https://kiali.io/docs/installation/installation-guide/install-with-helm/"&gt;their&lt;/a&gt; documentation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I use this example to install Kiali and it includes two Operators, the Prometheus Operator and the Kiali Operator. The Prometheus Operator is one of the first ever written Kubernetes Operator. As soon as the operator is deployed, it starts to deploy the operator service. For the Kiali operator, we need to deploy Kiali CR after the Kiali Operator has been deployed. Both are valid patterns.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Operator Development&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Operator is powerful. However, authoring an Operator is not a trivial effort. One usually start with a framework. A framework creates a body of boiler plate code that has the pattern implemented and allows developers to enrich the functions following the pattern. The white paper introduced three frameworks:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;CNCF &lt;a href="https://operatorframework.io/"&gt;Operator Framework&lt;/a&gt; &amp;#8211; aims at Operator Developers with an SDK, a scaffolding tool and a test harness. It currently supports three project types: Golang, Helm and Ansible. CNCF Operator framework consists of SDK and OLM. &lt;/li&gt;&#10;&lt;li&gt;Kopf (Kubernetes Operator Pythonic Framework) &amp;#8211; an easy-to-use framework in Python that abstracts away most of the low-level Kubernetes API communications hassle.&lt;/li&gt;&#10;&lt;li&gt;kubebuilder &amp;#8211; helps build a Manager similar to the native kube-controller-manager. For difference with OperatorSDK, read &lt;a href="https://sdk.operatorframework.io/docs/faqs/#what-are-the-the-differences-between-kubebuilder-and-operator-sdk"&gt;here&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;Metacontroller: lightweight Kubernetes Controller as a Service&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In &lt;a href="https://www.cncf.io/projects/operator-framework/"&gt;CNCF&lt;/a&gt; Operator Framework, the Operator SDK supports development using &lt;a href="https://sdk.operatorframework.io/docs/building-operators/ansible/"&gt;Ansible&lt;/a&gt;, &lt;a href="https://sdk.operatorframework.io/docs/building-operators/helm/"&gt;Helm&lt;/a&gt; and &lt;a href="https://sdk.operatorframework.io/docs/building-operators/"&gt;Golang&lt;/a&gt;. The author of &lt;a href="https://www.velotio.com/engineering-blog/getting-started-with-kubernetes-operators-helm-based-part-1"&gt;this&lt;/a&gt; post makes a general comparison as follows:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-white-background-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type &lt;/th&gt;&lt;th&gt;Best use case&lt;/th&gt;&lt;th&gt;Underlying technology&lt;/th&gt;&lt;th&gt;Amt of Effort&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Helm&lt;/td&gt;&lt;td&gt;Stateless workload&lt;/td&gt;&lt;td&gt;Helm Charts&lt;/td&gt;&lt;td&gt;Med&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ansible&lt;/td&gt;&lt;td&gt;Stateless workload&lt;/td&gt;&lt;td&gt;Ansible Roles and Playbooks&lt;/td&gt;&lt;td&gt;Med&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Golang&lt;/td&gt;&lt;td&gt;Stateful workload&lt;/td&gt;&lt;td&gt;Code developed in Golang&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The aforementioned &lt;a href="https://github.com/kiali/kiali-operator"&gt;Kiali operator&lt;/a&gt; is an example of Operator developed in Ansible. The &lt;a href="https://github.com/prometheus-operator/prometheus-operator"&gt;prometheus operator&lt;/a&gt;, is developed in Golang as the workload can be stateful depending on configuration. One needs to know how to develop operator in Golang in order to tackle the most complicated situations. This is requires some serious development effort. The documentation with a quick start section is available &lt;a href="https://sdk.operatorframework.io/docs/building-operators/golang/quickstart/"&gt;here&lt;/a&gt;. Even that is not very straightforward. RedHat, the maintainer of the CNCF &lt;a href="https://cloud.redhat.com/learn/topics/operators"&gt;Operator&lt;/a&gt; framework has a good blog &lt;a href="https://developers.redhat.com/articles/2021/08/04/managing-stateful-applications-kubernetes-operators-golang#"&gt;post&lt;/a&gt; on how to develop an Operator in Golang. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The example requires some development knowledge to go through. On my MacOS (Intel) I have to configure the following prerequisites:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Install gcc, using command: xcode-select &amp;#8211;install&lt;/li&gt;&#10;&lt;li&gt;Install the right version of golang. You can find the version &lt;a href="https://sdk.operatorframework.io/docs/contribution-guidelines/developer-guide/#prerequisites"&gt;here&lt;/a&gt;. The MacOS has a version of golang installed already so I had to install version 1.17 and link to it: brew install go@1.17 &amp;amp;&amp;amp; brew link &amp;#8211;force go@1.17&lt;/li&gt;&#10;&lt;li&gt;Install operator-sdk with home brew: brew install operator-sdk&lt;/li&gt;&#10;&lt;li&gt;When you run &amp;#8220;operator-sdk version&amp;#8221;, ensure the result shows a golang version that matches your installation.&lt;/li&gt;&#10;&lt;li&gt;If you need to push docker image, also connect to docker registry by running: docker login&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then we can create our working directory, initialize the repository and create boilerplate code (scaffolding) with these 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;$ mkdir wordpress-operator &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd wordpress-operator&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ operator-sdk init --domain digihunch.com --repo github.com/digihunch/wordpress-operator&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ operator-sdk create api --group wordpress --version v1 --kind WordPress --resource --controller&#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 repo initialized, we can go to the section &amp;#8220;Defining the API&amp;#8221; and &amp;#8220;Implementing the Controller&amp;#8221;. The blog post does not cover every code editing needed to bring up wordpress. You are supposed to go to the author&amp;#8217;s &lt;a href="https://github.com/priyanka19-98/wordpress-operator-latest"&gt;repository&lt;/a&gt; to fit the changes into your own repo. The author&amp;#8217;s repo has a few more &lt;a href="https://github.com/priyanka19-98/wordpress-operator-latest/tree/master/controllers"&gt;controllers&lt;/a&gt; such as &lt;a href="https://github.com/priyanka19-98/wordpress-operator-latest/blob/master/controllers/common.go"&gt;common.go&lt;/a&gt; and &lt;a href="https://github.com/priyanka19-98/wordpress-operator-latest/blob/master/controllers/mysql.go"&gt;mysql.go&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At the end of the lab, you should be able to run the controller and bring up wordpress. I used my own &lt;a href="https://github.com/digihunch/wordpress-operator"&gt;repository&lt;/a&gt; for this lab and have made the code changes for this lap in a couple &lt;a href="https://github.com/digihunch/wordpress-operator/commit/5540d7e045bf4da1ea1d140f1b9fd189fd9f2cc9"&gt;commits&lt;/a&gt;. To test locally with the code:&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;$ git clone git@github.com:digihunch/wordpress-operator.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd wordpress-operator&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ make install run&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we can validate wordpress install from a new terminal as the instruction shows:&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 -f config/samples/wordpress_v1_wordpress.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ minikube service wordpress --url&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For Developers that requires more details, RedHat has an &lt;a href="https://www.redhat.com/cms/managed-files/cl-oreilly-kubernetes-operators-ebook-f21452-202001-en_2.pdf?extIdCarryOver=true&amp;amp;sc_cid=701f2000001Css5AAC"&gt;eBook&lt;/a&gt; for Kubernetes Operators, in supplement to the &lt;a href="https://cloud.redhat.com/learn/topics/operators"&gt;documentation&lt;/a&gt;. As DevOps professional, I&amp;#8217;m mainly concerned with understanding how Operator works and using Operators correctly.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Too many Tools?&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we seem to have too many choice of tools when it comes to deploying workload on Kubernetes. Kustomize and Helm can deploy simple workloads. Operator can deploy stateful workloads, as well as keep the workload status in check. Further, we have FluxCD and ArgoCD based on GitOps workflow.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When assessing a tool, we should think about the complexity of the workload deployed. If it is a single stateless workload, Kustomize or Helm should be sufficient. If it is not very simple but still stateless, we can consider using Helm charts developed by the community. For multiple workloads, we can build our own top-level chart to combine existing sub-charts created by the community.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Helm is essentially a package manager. It does not follow controller pattern and therefore will not monitor the current status of deployment. Helm has other limitations compared to Operator. For example, as a templating scheme, it reaches limitation when dealing with complex logic, even with the help of its helper functions. It is also hard to reason through the template code when we have to troubleshoot a deployment. Refer to &lt;a href="https://thenewstack.io/we-pushed-helm-to-the-limit-then-built-a-kubernetes-operator/"&gt;this&lt;/a&gt; blog post for the author&amp;#8217;s experience with Helm.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If we want our deployment to be fully declarative and continuous, then we will follow the Operator pattern by using a Kubernetes Operator. When we have many workloads of different levels of complexity, we can combine them with GitOps tool. Operator is one of the underlying technologies behind GitOps.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-white-background-color has-background has-fixed-layout"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Workload profile&lt;/th&gt;&lt;th&gt;Just Installation&lt;/th&gt;&lt;th&gt;Installation and Maintain Status&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Single stateless workload&lt;/td&gt;&lt;td&gt;Helm or Kustomize&lt;/td&gt;&lt;td&gt;Operator (using Ansible or Helm)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Single stateful workload&lt;/td&gt;&lt;td&gt;Helm or Kustomize&lt;/td&gt;&lt;td&gt;Operator (using Golang)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Multiple workloads&lt;/td&gt;&lt;td&gt;Helm (e.g. build parent chart)&lt;/td&gt;&lt;td&gt;GitOps in combination with Operator, Helm and Kustomize&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The table above helps refine deployment requirement. It&amp;#8217;s not a recommendation, but rather a model of analyzing deployment requirement.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Autoscaling on Kubernetes Platform&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/04/knative-introduction-serving/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Knative Serving Introduction&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Autoscaling on Kubernetes Platform</title><link>https://www.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/</link><pubDate>Mon, 28 Mar 2022 13:14:00 -0400</pubDate><guid>https://www.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-autoscaling.webp" alt="Featured image of post Autoscaling on Kubernetes Platform" /&gt;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-introduction"&gt;Introduction&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The concept of autoscaling on Kubernetes platform dates from the era where virtualization first became widespread and the overhead of provisioning a new server became lightweight through the use of &lt;a href="https://help.ubuntu.com/community/CloudInit"&gt;cloud-init&lt;/a&gt;. With public cloud, customers operate on usage-based billing. Autoscaling allows workload to scale down during idle times to reduce cost, and scale up during peak time to meet the demand of business traffic. Vertical autoscaling replaces a VM with one of higher capacity, which is usually interruptive. Horizontal autoscaling adds or removes VMs to adjust capacity, and works in conjunction with load balancing mechanism to assign load to a specific target in the group. Unless otherwise specified, we simply refer to horizontal autoscaling as autoscaling. Depending on what triggers autoscaling, it can be metrics based or event driven.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Metric-based autoscaling is based on VM (or Pod) metrics in the autoscaling group. The metrics are mostly about CPU usage, memory, IOPS, number of connections, etc. For example, when the average CPU utilization across all VMs in the last five minutes hits 70% threshold, then the scaler introduces a new VM into the autoscaling group. The trigger can factor in a variety of metrics. Advanced autoscaling APIs can also support lifecycle hooks, i.e. custom activities upon creation of new VMs during scale-up, or upon deletion of existing VMs during scale-down. Other aspects of custom behaviours include a cool-off period, i.e a no-activity window after the previous scaling activity. Since scaling activities are re-active. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Using real-time metrics as a trigger of scaling is not always a good idea. For example, a buggy order processing program may consume 100% of CPU due to an infinite loop, or 99% of memory due to memory leak. Metric-based scaling may fire off even though there is currently no order pending in the queue. Event-driven approach is more flexible. Event can fire from any type of source. For example, in Kubernetes, when scheduler fails to schedule a Pod due to constraints, it is an event This event can trigger scaling. In some case, a metric hitting a threshold fires an event. For example, scale up when size of order queue reaches 20. In this sense, metric-based autoscaling is a special case of event-driven autoscaling. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With Kubernetes, let&amp;#8217;s examine node group autoscaling (aka cluster autoscaling) and workload autoscaling (Pod autoscaling). &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-node-group-autoscaling"&gt;Node Group Autoscaling&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cluster autoscaler is the mechanism to auto-scale node groups for Kubernetes. As per its &lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#is-cluster-autoscaler-compatible-with-cpu-usage-based-node-autoscalers"&gt;documentation&lt;/a&gt;, any metric-based cluster/node group autoscalers are NOT compatible with CA. They are also not particularly suitable for Kubernetes in general. Take AKS for example, the events to trigger scale-up and scale-down are as below:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The cluster autoscaler component can watch for pods in your cluster that can&amp;#8217;t be scheduled because of resource constraints. The cluster then automatically increases the number of nodes.&lt;/li&gt;&#10;&lt;li&gt;The cluster autoscaler decreases the number of nodes when there has been unused capacity for a period of time. Pods on a node to be removed by the cluster autoscaler are safely scheduled elsewhere in the cluster.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both are in essence event driven. The behaviours can be fine-tuned with a number of &lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#what-are-the-parameters-to-ca"&gt;parameters&lt;/a&gt; as below:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;scan-interval&lt;/li&gt;&#10;&lt;li&gt;scale-down-delay-after-add&lt;/li&gt;&#10;&lt;li&gt;scale-down-delay-after-delete&lt;/li&gt;&#10;&lt;li&gt;scale-down-delay-after-failure&lt;/li&gt;&#10;&lt;li&gt;scale-down-unneeded-time&lt;/li&gt;&#10;&lt;li&gt;scale-down-unready-time&lt;/li&gt;&#10;&lt;li&gt;scale-down-utilization-threshold&lt;/li&gt;&#10;&lt;li&gt;max-graceful-termination-sec&lt;/li&gt;&#10;&lt;li&gt;balance-similar-node-groups&lt;/li&gt;&#10;&lt;li&gt;expander: random, most-pods, least-waste, priority &lt;/li&gt;&#10;&lt;li&gt;skip-nodes-with-local-storage&lt;/li&gt;&#10;&lt;li&gt;skip-nodes-with-system-pods&lt;/li&gt;&#10;&lt;li&gt;max-empty-bulk-delete&lt;/li&gt;&#10;&lt;li&gt;new-pod-scale-up-delay&lt;/li&gt;&#10;&lt;li&gt;max-total-unready-percentage&lt;/li&gt;&#10;&lt;li&gt;max-node-provision-time&lt;/li&gt;&#10;&lt;li&gt;ok-total-unready-count&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The parameters above constitute the autoscaler profiler, and are effective if cluster autoscaler is enabled. For many implementations, cluster autoscaler can be enabled and disabled even after the cluster has been created, and the parameters can be changed. The overhead of provisioning a new node should not be overlooked, because that is usually the window that a Pod needs to wait to get scheduled. As stated in CA&amp;#8217;s &lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#how-is-cluster-autoscaler-different-from-cpu-usage-based-node-autoscalers"&gt;FAQ&lt;/a&gt;, the main purpose of CA is to get pending pods a place to run, instead of pre-emptively accommodating to increasing workload.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The delay in pod scheduling while adding a new node can be controlled to a certain degree with one of the two workarounds below:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;With HPA or KEDA, set lower threshold so the workload level scaling acts more aggressive than the increase of demand. This buys some buffer time&lt;/li&gt;&#10;&lt;li&gt;Use a tool to puff up utilization, such as &lt;a href="https://artifacthub.io/packages/helm/deliveryhero/cluster-overprovisioner"&gt;cluster overprovisioner&lt;/a&gt;, which deploys pods that&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;request enough resources to reserve virtually all resources for a node&lt;/li&gt;&#10;&lt;li&gt;consume no actual resources&lt;/li&gt;&#10;&lt;li&gt;use a priority class that causes them to be evicted as soon as any other Pod needs it.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In practice the cluster autoscaler setup should be conservative and keep node size as stable as it can. For example, a 20 minutes idle-window (low utilization) on a node is not worth the overhead to remove a node and add it back in 20 minute later.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When the cluster do need to scale down by removing a node, one common symptom is failing to scale down because some Pods have nowhere else to schedule to. H&lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#what-types-of-pods-can-prevent-ca-from-removing-a-node"&gt;ere&lt;/a&gt; is a list of possible causes as &lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#troubleshooting"&gt;troubleshooting&lt;/a&gt; tips.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If node scaling should be triggered sparsely, then pod scaling is by design very dynamic. Cloud native applications should assume that pod scaling occurs very frequently.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In late 2021, AWS released the open-source project &lt;a href="https://aws.amazon.com/blogs/aws/introducing-karpenter-an-open-source-high-performance-kubernetes-cluster-autoscaler/"&gt;Karpenter&lt;/a&gt; for cluster autoscaler. Karpenter addresses some challenges with native Cluster Autoscaler on &lt;a href="https://www.digihunch.com/2022/12/eks-impression/"&gt;EKS&lt;/a&gt;. Karpenter is gaining momentum and now adding support for other cloud service providers including Azure. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-workload-autoscaling"&gt;Workload Autoscaling&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Stateless workload are controlled by a Deployment object, which is associated with a replicaSet object. For stateless workload we can use &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/"&gt;HorizontalPodAutoscaler&lt;/a&gt;, or HPA. There is a VerticalPodAutoscaler (VPA) which is much less common. HPA is metrics based with flexible options such as specifying an object, depending on what metrics are available via &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis"&gt;metrics API&lt;/a&gt;. There are two versions of HorizontalPodAutoscaler: autoscaling/v1 and autoscaling/v2. The latter supports scaling policies, such as adjusting downscale stabilization window, and limiting scale down rate. No matter which API version, the metric-based triggers in HPA are fairly limited.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We already know that metrics are not always the best indicator to trigger scaling. We need an option to trigger scaling based on the status of other components such as queue size. KEDA (Kubernetes Event Driven Autoscaling) is a great option to consider for horizontal workload scaling. KEDA works with HPA, and significantly enriches trigger options. Apart from metrics, KEDA can use a number of external mechanisms as triggers, for example:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;RabbitMQ/Kafka/SQS: scale based on queue size&lt;/li&gt;&#10;&lt;li&gt;Azure Log Analytics: scale based on a kusto query result against Azure Log Analytics&lt;/li&gt;&#10;&lt;li&gt;AWS CloudWatch, Azure Monitor: scale based on metrics from Azure Monitor/AWS CloudWatch&lt;/li&gt;&#10;&lt;li&gt;Azure Pipelines: scale based on agent pool queues of Azure Pipeline&lt;/li&gt;&#10;&lt;li&gt;Elasticsearch: scale based on elasticsearch query result&lt;/li&gt;&#10;&lt;li&gt;Kubernetes Workload: scale based on the count of running pods of a specified workload&lt;/li&gt;&#10;&lt;li&gt;MSSQL, MySQL, Postgres, Cassandra: scale based on a query result&lt;/li&gt;&#10;&lt;li&gt;Prometheus: scale based on prometheus query result&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://keda.sh/docs/2.6/concepts/"&gt;KEDA &lt;/a&gt;is a single-purpose and lightweight component. With KEDA, we don&amp;#8217;t need to explicitly define HPA. It allows us to select from a longer list of triggering mechanisms for our auto scaler. We shall not underestimate the work needed to select the most suitable trigger because having an incorrect trigger (e.g. bad metrics) is costly. Let&amp;#8217;s take Java applications as an example. Java workload operates in a JVM inside of the container. JVM request the entire heap size from operating system. The &lt;a href="https://www.digihunch.com/2020/08/java-garbage-collection/"&gt;garbage collection&lt;/a&gt; activities also consumes a good portion of CPU cycles. This pattern makes CPU and memory metrics inaccurate as an indicator for scaling activity. Because of this we need to find out what is the best scaler for Java application, based on understanding of how the entire solution stack works as a whole.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The other aspect that KEDA beats HPA is its ability to scale to 0. This can be helpful when a service is idle most of the time but cannot shut down.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-keda-lab"&gt;KEDA lab&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;let&amp;#8217;s use &lt;a href="https://www.digihunch.com/2020/07/zookeeper-and-kafka-overview/"&gt;Kafka&lt;/a&gt; as an example to configure KEDA for a dummy workload. We create a &lt;a href="https://github.com/digihunch/real-quicK-cluster"&gt;mock cluster&lt;/a&gt; using Kind with a simple &lt;a href="https://github.com/digihunch/real-quicK-cluster/blob/main/kind/kind-config.yaml"&gt;configuration file&lt;/a&gt;. Then, let&amp;#8217;s start with the following dummy workload with replica count set to 1:&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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Namespace&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;workload&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;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;apps&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;workload&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;replicas&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&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:#a6e22e"&gt;selector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;matchLabels&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;labels&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;containers&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;neilpeterson&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;ports&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;containerPort&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&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;&lt;span style="color:#f92672"&gt;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;workload&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;ports&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;selector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;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;We need to install KEDA and Kafka using Helm:&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 kedacore https://kedacore.github.io/charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm install keda kedacore/keda -n keda --create-namespace&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add bitnami https://charts.bitnami.com/bitnami&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm install kafka bitnami/kafka -n kafka --create-namespace --set volumePermissions.enabled&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true --set replicaCount&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Watch for all Pods to come up. Also read the notes from Kafa installation and confirm the Kafka service address. Now, we will apply KEDA scaled object, defined 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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;keda&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;sh&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ScaledObject&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kafka&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;scaledobject&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;workload&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scaleTargetRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;apps&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;helloworld&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;one&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pollingInterval&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cooldownPeriod&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;idleReplicaCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;minReplicaCount&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;maxReplicaCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fallback&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;failureThreshold&lt;/span&gt;&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; &lt;span style="color:#a6e22e"&gt;replicas&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&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:#a6e22e"&gt;advanced&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;restoreToOriginalReplicaCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;horizontalPodAutoscalerConfig&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;behavior&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scaleDown&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stabilizationWindowSeconds&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;policies&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Percent&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;periodSeconds&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;15&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;triggers&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kafka&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;bootstrapServers&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kafka&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;kafka&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;headless&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kafka&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;svc&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cluster&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;local&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;9092&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;consumerGroup&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;my&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;group&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Make&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sure&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;that&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;consumer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;group&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;is&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;same&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;one&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;one&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;that&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;is&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;consuming&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;topics&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;topic&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Optional&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;lagThreshold&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;5&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;offsetResetPolicy&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;latest&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 field definitions are on KEDA deploy &lt;a href="https://keda.sh/docs/1.4/concepts/scaling-deployments/"&gt;documentation&lt;/a&gt;. Items under Kafka trigger are on the trigger &lt;a href="https://keda.sh/docs/2.6/scalers/apache-kafka/"&gt;documentation&lt;/a&gt;. In this lab we set the idelReplicaCount to 0. It will scale up with average lag of all partitions reaching 5. In the next few steps, we&amp;#8217;ll mock up some messages posted to the Kafka topic named &amp;#8220;test&amp;#8221; for consumer group my-group. We can watch deployment size grow along with the growth of lags.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To emulate Kafka client activity, we can spin up a Kafka test Pod:&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 run kafka-client --restart&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Never&amp;#39;&lt;/span&gt; --image docker.io/bitnami/kafka:2.8.1-debian-10-r73 --namespace kafka --command -- sleep infinity&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl exec --tty -i kafka-client --namespace kafka -- bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From within the Pod, we can leverage the client-side scripts located in /opt/bitnami/kafka/bin/. For example, to post message to a topic (e.g. named test):&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;kafka-console-producer.sh --topic test --broker-list kafka-0.kafka-headless.kafka.svc.cluster.local:9092,kafka-1.kafka-headless.kafka.svc.cluster.local:9092,kafka-2.kafka-headless.kafka.svc.cluster.local:9092 &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The Helm installer also gives the command with broker list. To consume messages from a topic (e.g. test) to a given consumer group:&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;kafka-console-consumer.sh --topic test --bootstrap-server kafka.kafka.svc.cluster.local:9092 --group my-group&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can have two command terminals, post test messages on one terminal and watch it consumed nearly immediately on the other terminal.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Kafka trigger &lt;a href="https://keda.sh/docs/2.6/scalers/apache-kafka/"&gt;documentation &lt;/a&gt;suggests that the number of replicas will not exceed the number of partitions on a topic when a topic is specified. To make this lab work, we need to have set 5 partitions:&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;kafka-topics.sh --alter --bootstrap-server kafka.kafka.svc.cluster.local:9092 --topic test --partitions &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kafka-topics.sh --describe --bootstrap-server kafka.kafka.svc.cluster.local:9092 --topic test&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Once we confirm five partitions, we can spin up two command terminals, one to produce message and the other to consume messages. If working, we can stop the consumer and use the command below to watch for the lag for each partition. &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;kafka-consumer-groups.sh --bootstrap-server kafka.kafka.svc.cluster.local:9092 --describe --group my-group&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we can artificially trigger scaling by increasing average lag. We keep posting messages on the producer (each carriage return posts a message), and we can check the lag after posting:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1396" height="413" src="https://www.digihunch.com/wp-content/uploads/2022/03/image.webp" alt="" class="wp-image-3884"/&gt;&lt;figcaption class="wp-element-caption"&gt;Growth of average lags&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The size of deployment starts with 0 as defined in the scaled object. As the average exceeds 5, we can see deployment size growing.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="576" height="405" src="https://www.digihunch.com/wp-content/uploads/2022/03/image-1.webp" alt="" class="wp-image-3885"/&gt;&lt;figcaption class="wp-element-caption"&gt;Growth of deployment size&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This lab is an oversimplified scenario to illustrate the idea of scaling. Kafka is a typical queue construct and other queue configuration such as RabbitMQ or AWS SQS works in very similar ways. Real life use case involves more aspects to consider, such as multiple topics, and &lt;a href="https://keda.sh/docs/2.6/concepts/authentication/"&gt;authentication&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-connection-triggered-wake-up"&gt;Connection triggered wake-up&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;KEDA uses Events to scale workload from zero to one (wake up). There is no way to scale (wake up) based on an incoming web request. In many cases, such as serverless configuration, we need to scale the deployment size from zero to N once the service receives incoming web request. This is not supported by KEDA. By definition KEDA uses Events to wake up. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is an &lt;a href="https://github.com/kedacore/http-add-on"&gt;HTTP-add-on&lt;/a&gt; for KEDA still at beta but it is trying to address this problem. &lt;a href="https://github.com/kedacore/http-add-on/blob/main/docs/design.md"&gt;This&lt;/a&gt; page shows the design. Suppose a service has scaled down to zero, the followings will happen to wake it up:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The incoming request is routed to an interceptor behind the service&lt;/li&gt;&#10;&lt;li&gt;interceptor keeps track of number of pending HTTP request&lt;/li&gt;&#10;&lt;li&gt;The scaler periodically watches for the size of the pending queue on the interceptor&lt;/li&gt;&#10;&lt;li&gt;Based on the queue size, the scaler reports scaling metrics as appropriate to KEDA&lt;/li&gt;&#10;&lt;li&gt;As the queue size increases, the scaler instructs KEDA to scale up as appropriate&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The periodical check activity is the key to make it work and also what makes it a pseudo-trigger. &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;Fine-tuning autoscaling is important to the performance of workload on Kubernetes. At node level, we briefed on cluster autoscaler and suggest that we only use it sparsely. At pod level, we introduced native HPA as well as KEDA, with an example. We also discussed KEDA has limitations and the HTTP-add-on. In the &lt;a href="https://www.digihunch.com/2022/04/knative-introduction-serving/"&gt;next &lt;/a&gt;post, we&amp;#8217;ll explore Knative&amp;#8217;s autoscaling capability.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/03/istio-operation-gotchas/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Operation Gotchas&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/04/kubernetes-operator/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Operator&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Traffic Segmentation on Kubernetes Platform</title><link>https://www.digihunch.com/2022/01/traffic-segmentation-on-kubernetes-platform/</link><pubDate>Thu, 27 Jan 2022 13:54:00 -0400</pubDate><guid>https://www.digihunch.com/2022/01/traffic-segmentation-on-kubernetes-platform/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-traffic-segmentation.webp" alt="Featured image of post Traffic Segmentation on Kubernetes Platform" /&gt;&lt;p class="wp-block-paragraph"&gt;When operating Kubernetes as a platform for multiple tenants, one of the concerns is controlling the &lt;a href="https://www.digihunch.com/2021/06/kubernetes-networking-solutions-overview/"&gt;network&lt;/a&gt; traffic. This is sometimes referred to as traffic segmentation. This initiative involves a broad range of technical topics from networking to containerization. By no means I am an expert on each of those topics. I have however developed some best practices in how to break down this challenge and hence bringing the thought into this post.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="tenant-isolation"&gt;Tenant Isolation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes has the concept of namespace to logically separate resources allocated for each tenant. Each tenant only operates within their given namespaces. The isolation of computing resources such as CPU and memory can be managed via ResourceQuota objects, and they are enforced at the kernel level, leaving networking isolation the main discussion in the topic of tenant isolation. If the platform hosts a lot of stateful workload then we also needs to address tenant isolation at the storage layer. In this post we focus on the network aspect of resource isolation, aka traffic segmentation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Controlling network traffic can require a significant amount of efforts depending on the goal. That is why we need to first assess the multi-tenancy models:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Soft multi-tenancy: usually a platform is shared by multiple teams within the same organization. Tenants are incentivized to be good neighbours.&lt;/li&gt;&#10;&lt;li&gt;Hard multi-tenancy: usually a platform shared by multiple customers from different organizations. There is no trust between different tenants, or between tenant and platform operator.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Reality may sits somewhere in between, but we often have to come back to this model when making a technical decision, because it determines the degree of tenant isolation, or the amount of effort we are willing to put in on tenant isolation. At the tough end, is zero-trust network, which usually have the following requirement:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Requirement 1:&lt;/strong&gt;&amp;nbsp;All network connections are subject to enforcement (not just those that cross zone boundaries).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Requirement 2&lt;/strong&gt;: Establishing the identity of a remote endpoint is always based on multiple criteria including strong cryptographic proofs of identity. In particular, network-level identifiers like IP address and port are not sufficient on their own as they can be spoofed by a hostile network.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Requirement 3&lt;/strong&gt;: All expected and allowed network flows are explicitly allowed. Any connection not explicitly allowed is denied.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Requirement 4&lt;/strong&gt;: Compromised workloads must not be able to circumvent policy enforcement.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Requirement 5&lt;/strong&gt;: Many Zero Trust Networks also rely on encryption of network traffic to prevent disclosure of sensitive data to hostile entities snooping network traffic. This is not an absolute requirement if private data are not exchanged over the network, but to fit the criteria of a Zero Trust Network, encryption must be used on every network connection if it is required at all. A Zero Trust Network does not distinguish between trusted and untrusted network links or paths. Also note that even when not using encryption for data privacy, cryptographic proofs of authenticity are still used to establish identity.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As you can see there&amp;#8217;s a lot of efforts involved in building a zero-trust network. The cost of building a zero-trust network is worth it only when we determines that the overall business requirement demands it.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="pod-networking"&gt;Pod Networking&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to understand Pod networking before developing a traffic segmentation strategy. Pod networking has to do with the CNI driver used for the cluster. There are in general two categories:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Overlay network: Pods are placed on a VXLAN configuration. This is mostly seen in basic Kubenet mode or CNI drives such as Flannel. NAT is required for Pods to communicate across nodes, which might introduce performance issues when deployed at scale. Pods do not use IP address from the host network.&lt;/li&gt;&#10;&lt;li&gt;Regular network: In this mode Pods are on the same network as the nodes are. For example, Azure CNI assigns Pods with IP address from a given V-Net. The AWS-VPC CNI integrates VPC networking with Pods. Since Pods are on a corporate network, the traffic control must also consider measures at the whole network level.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main benefit of the first approach, is that IP exhaustion is less likely due to the introduction of a VxLAN. The other benefit from a networking perspective is that the Pod networking is born separated from the corporate network. In the second approach, by assigning Pods with a corporate IP address (which brings the risk of IP exhaustion), Pods are also potentially exposed to all corporate traffic at layer 3. To tackle this additional risk, network security group should be used in the V-Net for Azure AKS, or se&lt;a href="https://aws.amazon.com/blogs/containers/introducing-security-groups-for-pods/"&gt;curity groups for Pods&lt;/a&gt; should be considered with AWS EKS. Although we will discuss Network Policy in the rest of this essay, Network Policy mostly addresses the traffic segmentation issue within a Kubernetes cluster. A Pod placed on the corporate network needs traffic segmentation strategies from the perspective of the whole network.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another network-level traffic segmentation strategy is on the corporate firewall. For example, with AKS you can specify outbound type as user-defined routes (&lt;a href="https://docs.microsoft.com/en-us/azure/aks/egress-outboundtype#outbound-type-of-userdefinedrouting"&gt;UDR&lt;/a&gt;) to direct all outbound traffic through a corporate firewall where traffic will be inspected. There are firewall &lt;a href="https://docs.paloaltonetworks.com/pan-os/10-0/pan-os-new-features/virtualization-features/cn-series-firewalls-for-securing-kubernetes-deployments.html"&gt;products&lt;/a&gt; dedicated for managing highly dynamic pod traffic from Kubernetes. This strategy can be used in conjunction with network security groups.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="network-policy"&gt;Network Policy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes’s default behaviour is to allow traffic between any two pods in the cluster network. This is undesirable. NetworkPolicy is the native Kubernetes construct for platform operators and application developer to control network traffic at layer 3/4. It uses namespace and pod selectors, and is defined based on allow rules, which is good for general use. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Further to the native Network Policy, you can adopt third party policies for advanced features. For example, Azure has Azure Network policy (works for Azure CNI only) and Calico Network policy (works for Calico CNI, Azure CNI or Kubenet). The third party network policies usually provides advanced features such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Deny rules&lt;/li&gt;&#10;&lt;li&gt;multiple types of endpoints in addition to Pods, for example, VMs, network interfaces which can be useful in network-level traffic control&lt;/li&gt;&#10;&lt;li&gt;ordering and priority of rules&lt;/li&gt;&#10;&lt;li&gt;Flexible matching rules&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Calico network has a &lt;a href="https://projectcalico.docs.tigera.io/security/calico-network-policy"&gt;page&lt;/a&gt; that summarizes its features and how it extends the Kubernetes NetworkPolicy. Below is an example of a Calico&amp;#8217;s network policy:&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;projectcalico.org/v3 &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;NetworkPolicy &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;allow-tcp-6379 &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;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;production &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;: &lt;span style="color:#ae81ff"&gt;color == &amp;#39;red&amp;#39;&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;ingress&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;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Allow &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;source&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;: &lt;span style="color:#ae81ff"&gt;color == &amp;#39;blue&amp;#39; &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;namespaceSelector&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;shape == &amp;#39;circle&amp;#39;&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;destination&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:#ae81ff"&gt;6379&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;It is as self-explanatory as Kubernetes Network Policy. No matter which kind of network policy, this approach takes effect at layer 3/4. The rules are eventually implemented in the kernel on the node (Iptables). The management of this layer is usually by the platform team and they need to have some application knowledge.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="authorization-at-application-layer"&gt;Authorization at Application Layer&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traffic above layer 4 is considered application layer traffic. At application layer, the decision to allow or deny a request is by definition an authorization decision. Another layer of protection can be placed at layer 4 is mTLS which ensures that each request to have an identity. The authorization can be built in the application, but it is also very common to offload these functions to the service mesh layer. For example, Istio has constructs such as PeerAuthentication, Request Authentication and Authorization Policy. We will those in more details in a few coming blog posts. Below is a simple example of Istio&amp;#8217;s Authorization Policy:&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;security.istio.io/v1beta1 &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;AuthorizationPolicy &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:#e6db74"&gt;&amp;#34;details-viewer&amp;#34;&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;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;default &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;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;details &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;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ALLOW &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;rules&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;from&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;source&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;principals&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;cluster.local/ns/default/sa/bookinfo-productpage&amp;#34;&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;to&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;operation&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;methods&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;GET&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;The rule is also fairly self-explanatory. Compared to Network Policy, the point of enforcement of these Authorization policies are at the envoy proxy. The management of policies at this layer can be debatable if department boundaries are not clear, but it should in general be owned by personnels with good application knowledge.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="consistency-between-policies"&gt;Consistency between Policies&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In-cluster traffic can be controlled with both Network Policy (Calico or Kubernetes) operating at layer 3-4, and Authorization Policy (Istio) at layer 4-7. This brings another challenge of maintaining consistency between the two types of policies. This is especially challenging when they are managed by different teams in a corporate and therefore many operators for soft multi-tenant platform choose not to implement Network Policy or only implements a baseline.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some network solution providers builds a solution for this. For example, Calico has the capability to &lt;a href="https://projectcalico.docs.tigera.io/security/app-layer-policy"&gt;enforce network policy for Istio&lt;/a&gt;. This integration requires some configuration, but the enhanced &lt;a href="https://projectcalico.docs.tigera.io/security/http-methods"&gt;GlobalNetworkPolicy&lt;/a&gt; supports HTTP methods, eliminating the need to define a separate Authorization Policy in Istio and worry about its consistency with NetworkPolicy. The platform build however, still needs to determine who owns this policy construct. Below is an example from Calico &lt;a href="https://docs.tigera.io/calico/latest/reference/resources/networkpolicy"&gt;documentation&lt;/a&gt;:&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;projectcalico.org/v3&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;GlobalNetworkPolicy&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;customer&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;: &lt;span style="color:#ae81ff"&gt;app == &amp;#39;customer&amp;#39;&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;ingress&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;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Allow&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;http&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;methods&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;GET&amp;#34;&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;egress&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;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Allow&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;One of the benefits of using &lt;a href="https://www.tigera.io/blog/network-policy-and-istio-deep-dive/"&gt;this integration&lt;/a&gt; is a unified policy language based on GlobalNetworkPolicy CRD. In the mean time, organization should also develop strategy to ensure that, once Calico is integrated with Istio, then there is no need to separately build authorization policies, which may come in conflict with Global network policy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="summary"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Controlling network traffic is difficult on Kubernetes platform. In this article I proposed a few angles to approach this issue for enterprise clients.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/01/fluxcd-continuous-deployment-with-gitops/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;FluxCD: Continuous Deployment with GitOps&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Authentication and Authorization&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>File storage vs object storage in the cloud</title><link>https://www.digihunch.com/2021/09/file-storage-vs-object-storage/</link><pubDate>Thu, 23 Sep 2021 22:54:00 -0400</pubDate><guid>https://www.digihunch.com/2021/09/file-storage-vs-object-storage/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-file-obj-storage.webp" alt="Featured image of post File storage vs object storage in the cloud" /&gt;&lt;p class="wp-block-paragraph"&gt;File storage (e.g. NFS) used to be prevalent until object storage comes in for competition.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-the-competition"&gt;The competition&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traditionally, enterprise storage product lines are built around three capabilities, as listed in this table below:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-black-color has-white-background-color has-text-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Capability&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Typical Implementation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Data served&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;T1 &amp;#8211; &lt;br&gt;Block stroage&lt;/td&gt;&lt;td&gt;DAS (e.g. SAS cable) or SAN (Fibre Cable for FCP protocol, or Ethernet for iSCSI protocol)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Mission critical data that are extremely sensitive to latency (e.g. database). Client has block-level access.&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;T2 &amp;#8211; &lt;br&gt;File storage&lt;/td&gt;&lt;td&gt;NAS (connect via CIFS or NFS protocols). Storage arrays are typically a mix of HDD and SSD. Storage servers are usually deployed in the same location over low latency network. DR location is usually in the same region.&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Hot data. Multiple client access at file level. The size of each data request varies from small to medium (e.g. text document)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;T3 &amp;#8211; &lt;br&gt;Object storage&lt;/td&gt;&lt;td&gt;Hardware agnostic, connect via layer-7 protocol (e.g. S3). Storage backend can be either on premise, or in the cloud, over WAN connection.&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Warm and code data. Multiple client access at object level. Traditionally for backup but use cases are expanding. The size of each data request varies significantly, from small to very large (e.g. media content).&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the last couple decades, leading players for T2 have been enterprise storage vendors. They each have developed their secret sauces to tackle the challenges. For example, EMC has OneFS, a parallel distributed file system as the foundation of &lt;a href="https://www.digihunch.com/2020/07/emc-productlines/"&gt;PowerScale&lt;/a&gt; (formerly Isilon) product line. NetApp develops ONTAP, featuring proprietary techniques for storage efficiency (deduplication, compaction and compression).&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The leading players in T3 are mostly &lt;a href="https://www.digihunch.com/2020/08/cloud-storage-overview/"&gt;public cloud&lt;/a&gt; provider, such as Amazon&amp;#8217;s S3. They might work with enterprise storage vendor behind the scene. But the T3 services appear to the end users as provided by the public cloud. Originally, the use case for T3 was archive only for its virtually unlimited capacity. This is not entirely true today. With the drastic improvement in modern network infrastructure, T3 can also brings satisfactory performance to serve hot data. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A competition between T2 and T3 arises. After all, both offer storage service over Ethernet, and both support multiple clients. Today when developers architect the storage layer of their applications, they need to weigh between supporting T2 and T3. Since &lt;a href="https://www.digihunch.com/2020/07/nfs-network-file-system-and-rpc-remote-procedure-call/"&gt;NFS&lt;/a&gt; is the typical protocol for T2 storage (sorry Windows guys) and S3 is typical T3 storage. This competition essentially boils down to NFS versus S3.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For many, the fancy S3 is a no-brainer. While I have suffered from many NFS drawbacks, and there&amp;#8217;s even a whole &lt;a href="https://www.kernel.org/doc/ols/2006/ols2006v2-pages-59-72.pdf"&gt;article&lt;/a&gt; by Linux folks about why NFS sucks, is it sentenced to death today? Does it beat S3 in some cases? Do so many organizations still stick to NFS just out of inertia?&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To answer these questions, I examine four aspects to explore the differences between file storage via NFS protocol, and object storage in S3. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-data-request-size"&gt;Data request size&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Storage client can make request by byte range of a file. Therefore, data request size, instead of file size, is what ultimately matters. I pick a few data request sizes (1K, 4K, 16K, 64K, 246K, 1024K and 4096K) in my experiment, and want to see how much network traffic a write operation produces using NFS and using S3.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To emulate request size, I created files at each size (using dd command), and copy the entire file to each backend. In the mean time, I use tcpdump to write out traffic across the wire into capture files. The size of capture file gives me an idea of how much network traffic went through the network interface, which is closely related to latency. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For NFS, I mounted the target with sync option. This requires NFS client to write out to server synchronously on file copy. I&amp;#8217;ve also set the wsize to be 1M. For S3, I simply use the following CLI command to copy file:&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;aws s3 cp 1kb.img s3://digihunch5ffafe32ab0fd40f&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;On the network interface, I use tcpdump to filter traffic through specific TCP port (443 for S3, or 2049 for NFS) and record the size of the capture file:&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;sudo tcpdump -s0 -pi eth0 dst port &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt; or src port &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt; -w /tmp/4096kb.cap&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The key indicator is the payload size (file size) as a percentage of the capture size. I call it payload ratio. The closer it is to 1, the better. I have the following result from my experiment:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-black-color has-white-background-color has-text-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Request&lt;/td&gt;&lt;td&gt;Payload&lt;/td&gt;&lt;td&gt;S3 capture size (byte)&lt;/td&gt;&lt;td&gt;NFS capture size (byte)&lt;/td&gt;&lt;td&gt;S3 payload ratio&lt;/td&gt;&lt;td&gt;NFS payload ratio&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1K&lt;/td&gt;&lt;td&gt;1024&lt;/td&gt;&lt;td&gt;9352&lt;/td&gt;&lt;td&gt;4332&lt;/td&gt;&lt;td&gt;0.11&lt;/td&gt;&lt;td&gt;0.24&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4K&lt;/td&gt;&lt;td&gt;4096&lt;/td&gt;&lt;td&gt;12640&lt;/td&gt;&lt;td&gt;7404&lt;/td&gt;&lt;td&gt;0.32&lt;/td&gt;&lt;td&gt;0.55&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;16K&lt;/td&gt;&lt;td&gt;16384&lt;/td&gt;&lt;td&gt;25969&lt;/td&gt;&lt;td&gt;20472&lt;/td&gt;&lt;td&gt;0.63&lt;/td&gt;&lt;td&gt;0.80&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;64K&lt;/td&gt;&lt;td&gt;65536&lt;/td&gt;&lt;td&gt;79183&lt;/td&gt;&lt;td&gt;69746&lt;/td&gt;&lt;td&gt;0.83&lt;/td&gt;&lt;td&gt;0.94&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;256K&lt;/td&gt;&lt;td&gt;262144&lt;/td&gt;&lt;td&gt;290035&lt;/td&gt;&lt;td&gt;271408&lt;/td&gt;&lt;td&gt;0.90&lt;/td&gt;&lt;td&gt;0.97&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1024K&lt;/td&gt;&lt;td&gt;1048576&lt;/td&gt;&lt;td&gt;1085366&lt;/td&gt;&lt;td&gt;1074076&lt;/td&gt;&lt;td&gt;0.97&lt;/td&gt;&lt;td&gt;0.98&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4096K&lt;/td&gt;&lt;td&gt;4194304&lt;/td&gt;&lt;td&gt;4381547&lt;/td&gt;&lt;td&gt;4286910&lt;/td&gt;&lt;td&gt;0.96&lt;/td&gt;&lt;td&gt;0.98&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This result indicates that NFS has a higher ratio in all groups. However, its advantage diminishes as the data request size grows. What it tells us is that if your applications workload issues most request in small chunks of data, such as 1K, 4K, then NFS will require much less traffic over the network, and thus less latency. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This essentially explains the use case of NFS against S3: workload with small data requests.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-client-support"&gt;Client Support&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS is natively supported by Linux operating system kernel. NFS client sits below the virtual file system (VFS) layer, which sits below the system call layer. The NFS client translate system calls into RPC (remote procedure calls). Communication between client and server is completed with RPC, on top of TCP. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="554" height="314" src="https://www.digihunch.com/wp-content/uploads/2021/10/image.webp" alt="" class="wp-image-2732"/&gt;&lt;figcaption class="wp-element-caption"&gt;NFS architecture&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because of the native support, in most cases, developer can treat NFS mounts as if they were local. For performance to be sustainable as file system grows, the directory structure on NFS should follow a certain naming conventions so that files are evenly distributed across directories. The client should also use list operation as sparse as it can because that operation is expensive across the network.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From developer&amp;#8217;s perspective, NFS support is brought in by operating system and does not require much effort. On the other hand, S3 client support is not included by default in the operating system. S3 support requires special library, code changes, and integration effort to manage dependency and library version. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS has an advantage on client supportability. However, as we move applications to containers, and as container storage options mature, we will need an intermediary layer (storage class, storage provisioner, CSI driver, etc), NFS, or in general file storage, does not have this advantage any more.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-client-side-cache"&gt;Client-side Cache&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The NFS support behind VFS layer also means it can leverage the I/O caching mechanism on the client side, that comes with operating system. Client operating system with sufficient memory can take advantage of this mechanism to give it a performance boost. Check out &lt;a href="https://www.ibm.com/docs/en/aix/7.2?topic=performance-nfs-tuning-client"&gt;this&lt;/a&gt; guide for NFS cache tuning.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In comparison, S3 does not have a cache mechanism by itself. Either the application needs to implement its own cache mechanism, or a cache architecture needs to be introduced, such as CloudFront. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-consistency-and-concurrency"&gt;Consistency and concurrency&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A common consistency problem is whether client can read the changes immediately after it writes the file. S3 and NFS make a tie in this round.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;S3 &lt;a href="https://aws.amazon.com/blogs/aws/amazon-s3-update-strong-read-after-write-consistency/"&gt;originally&lt;/a&gt; came with eventual consistency model for read after write since 2006. As of &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-s3-now-delivers-strong-read-after-write-consistency-automatically-for-all-applications/"&gt;Dec 2020&lt;/a&gt; it introduced strong read-after-write consistency. For more information, refer to the guide &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html#ConsistencyModel"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS has a similar consistency guarantee called close-to-open cache coherency. Any changes made by client are flushed to the server on closing the file, and a cache revalidation occurs when you re-open it. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are more to consider in terms of consistency. For example, multiple clients tries to write the same file/object at the same time. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the S3 side, there is a locking mechanism called &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html"&gt;S3 object lock&lt;/a&gt; at object level (no byte-range lock). Without an object lock, when two PUT requests are simultaneously made to an object, the request with the latest timestamp wins. Refer to the section &lt;em&gt;Concurrent application&lt;/em&gt; on &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html#ConsistencyModel"&gt;this&lt;/a&gt; page.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As far as NFS goes, managing this kind of consistency problem is not in the scope of the standard. Although there are some tinkers. For example, NFS v4 includes a file locking mechanism. Client can choose to lock the entire file, or a byte range within the file. Locking can be mandatory or advisory.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-the-convergence"&gt;The convergence&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS and S3 each has their respective advantage. Enterprise NAS customers have been looking for ways to expand into the cloud for lower storage cost. To combine the advantages of the two, solution providers started to converge file storage and object storage. There are two types of solutions that reflects this trend of convergence. In the first trend, enterprise NAS deployed on premise now have the ability to scale out into the cloud. In the second trend, public cloud just brought enterprise NAS into their product offerings.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-scale-out-nas"&gt;Scale-out NAS&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NAS is traditionally expensive to scale because it requires physical storage media. The idea of scale-out NAS allows NAS to connect to object storage in the public cloud, making it a hybrid architecture. This essentially makes T3 storage as a backend of T2 and it can be implemented with a virtual storage appliance (VSA). The VSA translate file system activities into API calls for object storage operations. One example is AWS &lt;a href="https://aws.amazon.com/storagegateway/?whats-new-cards.sort-by=item.additionalFields.postDateTime&amp;amp;whats-new-cards.sort-order=desc"&gt;storage gateway&lt;/a&gt;. EMC has a similar appliance called ECS and this white paper explains how it proxies file system calls and interact with object backends. NetApp, a vested enterprise NAS provider, also has a counterpart called Cloud Volumes ONTAP (CVO). It works well with NetApp on-premise deployment, but the architecture is similar. &lt;a href="https://cloud.netapp.com/blog/aws-cvo-blg-aws-storage-gateway-vs.-cloud-volumes-ontap"&gt;Here&lt;/a&gt;&amp;#8216;s NetApp&amp;#8217;s take on how CVO is different than AWS Storage Gateway.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the scale-out NAS architecture, the public cloud acts merely as extension to on-premise storage solution, to provide capacity. The NAS on premise serves the storage workload primarily.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cloud-hosted-nas"&gt;Cloud hosted NAS&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For applications hosted in public cloud, it makes sense for public cloud provider to operate enterprise NAS storage as a service. The underlying storage technology is provided by storage vendor. It is just installed in the data centre managed by the public cloud vendor, instead of customer&amp;#8217;s own data centre. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One example is &lt;a href="https://azure.microsoft.com/en-us/services/netapp/"&gt;Azure NetApp Files&lt;/a&gt; (ANF). ANF is fully managed services, presented to users as storage volumes. The underlying storage technology is NetApp ONTAP. Because it is offered as a fully managed service, the customers are not able to manage the fine details of the storage, as they could with an ONTAP cluster on premise. This takes a lot of flexibility away from the user.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://aws.amazon.com/fsx/netapp-ontap/"&gt;FSx ONTAP&lt;/a&gt; is a managed NetApp storage service by AWS, launched in September 2021. The NetApp arrays are installed in AWS data centre, ready for users to provision from AWS console, or using CLI. The Terraform provider support is not available as of yet. Unlike ANF, FSx ONTAP exposes the ONTAP CLI to users, allowing for advanced storage managed by storage gurus. They can use ONTAP CLI commands to configure custom policy for Snapshot, setup SnapMirror replication, and so forth.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Likewise, PowerScale landed on GCP as public cloud partner to launch &lt;a href="https://cloud.google.com/vmware-engine/docs/vmware-ecosystem/howto-cloud-dell-powerscale#:~:text=Dell%20PowerScale%20on%20Google%20Cloud,workload%20virtual%20machines%20(VMs)."&gt;Dell Cloud PowerScale for Google Cloud&lt;/a&gt; in 2020. However, it seems to require a purchase agreement before APIs are enabled.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Object storage has a great momentum and some sees that as a replacement of file storage in the long run. However file storage has its advantages for small data requests, OS-level cache support, and built-in POSIX compatibility. It will continue to be an option for customers with specific workload. Customer stickiness to file storage is so firm, that public cloud providers now install them in their data centres. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From competition to collaboration, it will be interesting to watch what happens next for enterprise storage.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Follow-up Reading&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Tom Lyon&amp;#8217;s presentation on &lt;a href="https://blocksandfiles.com/2024/06/17/eminent-sun-alumnus-says-nfs-must-die/"&gt;why NFS must die&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/09/single-node-kubernetes-cluster-minikube/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Local multi-node cluster – Minikube, MicroK8s and KinD&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/10/intro-to-plg-stack-prometheus-loki-and-grafana/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next 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;/nav&gt;&#10;</description></item><item><title>Service and Ingress -Traffic Management in Kubernetes</title><link>https://www.digihunch.com/2021/07/traffic-management-in-kubernetes-service-and-ingress/</link><pubDate>Sun, 04 Jul 2021 01:30:00 -0400</pubDate><guid>https://www.digihunch.com/2021/07/traffic-management-in-kubernetes-service-and-ingress/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-ingress-service.webp" alt="Featured image of post Service and Ingress -Traffic Management in Kubernetes" /&gt;&lt;p class="wp-block-paragraph"&gt;Update 2022-08 &amp;#8211; Read my latest article on &lt;a href="https://medium.com/slalom-build/managing-ingress-traffic-on-kubernetes-platforms-ebd537cdfb46"&gt;ingress traffic management&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this post we discuss the traffic management in Kubernetes, specifically on Service and Ingress objects. Let&amp;#8217;s start with a traditional architecture:&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="463px" viewBox="-0.5 -0.5 463 251" style="max-width:100%;max-height:251px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="70" width="460" height="30" 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 center; width: 458px; height: 1px; padding-top: 85px; 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: all; white-space: normal; word-wrap: normal; "&gt;Network Load Balancer&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="230" y="89" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Network Load Balancer&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="230" cy="20" rx="60" ry="20" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/ellipse&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: 118px; height: 1px; padding-top: 20px; margin-left: 171px;"&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;Client&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="230" y="24" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Client&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 230 63.63 L 230 46.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 230 68.88 L 226.5 61.88 L 230 63.63 L 233.5 61.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 230 41.12 L 233.5 48.12 L 230 46.37 L 226.5 48.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="321.43" y="110" width="140" height="140" fill="#f5f5f5" stroke="#666666" 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: 138px; height: 1px; padding-top: 247px; margin-left: 322px;"&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: #333333; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;VM&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="391" y="247" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VM&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="330" y="130" width="122.86" height="30" rx="4.5" ry="4.5" fill="#d5e8d4" stroke="#82b366" 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: 121px; height: 1px; padding-top: 145px; margin-left: 331px;"&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;nginX&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="391" y="149" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;nginX&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="355" cy="205" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/ellipse&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: 48px; height: 1px; padding-top: 205px; margin-left: 331px;"&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;App1&lt;br&gt;Service&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="355" y="209" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App1&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="427.86" cy="205" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/ellipse&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: 48px; height: 1px; padding-top: 205px; margin-left: 404px;"&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;App2&lt;br&gt;Service&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="428" y="209" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App2&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 360.71 160 L 356.75 173.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 355.31 178.92 L 353.87 171.23 L 356.75 173.88 L 360.6 173.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 422.14 160 L 426.11 173.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 427.56 178.93 L 422.27 173.16 L 426.11 173.88 L 429 171.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="160" y="110" width="140" height="140" fill="#f5f5f5" stroke="#666666" 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: 138px; height: 1px; padding-top: 247px; margin-left: 161px;"&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: #333333; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;VM&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="230" y="247" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VM&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 231 100 L 231.34 123.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 231.41 128.88 L 227.81 121.93 L 231.34 123.63 L 234.81 121.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="170" y="130" width="122.86" height="30" rx="4.5" ry="4.5" fill="#d5e8d4" stroke="#82b366" 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: 121px; height: 1px; padding-top: 145px; margin-left: 171px;"&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;nginX&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="231" y="149" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;nginX&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="195" cy="205" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/ellipse&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: 48px; height: 1px; padding-top: 205px; margin-left: 171px;"&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;App1&lt;br&gt;Service&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="195" y="209" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App1&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="267.86" cy="205" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/ellipse&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: 48px; height: 1px; padding-top: 205px; margin-left: 244px;"&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;App2&lt;br&gt;Service&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="268" y="209" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App2&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 200.71 160 L 196.75 173.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 195.31 178.92 L 193.87 171.23 L 196.75 173.88 L 200.6 173.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 262.14 160 L 266.11 173.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 267.56 178.93 L 262.27 173.16 L 266.11 173.88 L 269 171.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;rect x="0" y="110" width="140" height="140" fill="#f5f5f5" stroke="#666666" 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: 138px; height: 1px; padding-top: 247px; 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: #333333; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "&gt;VM&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="70" y="247" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VM&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="130" width="122.86" height="30" rx="4.5" ry="4.5" fill="#d5e8d4" stroke="#82b366" 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: 121px; height: 1px; padding-top: 145px; 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;nginX&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="71" y="149" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;nginX&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="35" cy="205" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/ellipse&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: 48px; height: 1px; padding-top: 205px; 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;App1&lt;br&gt;Service&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="35" y="209" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App1&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="107.86" cy="205" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"&gt;&lt;/ellipse&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: 48px; height: 1px; padding-top: 205px; margin-left: 84px;"&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;App2&lt;br&gt;Service&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="108" y="209" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App2&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 40.72 160 L 36.75 173.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 35.31 178.92 L 33.87 171.23 L 36.75 173.88 L 40.6 173.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 102.14 160 L 106.11 173.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 107.56 178.93 L 102.27 173.16 L 106.11 173.88 L 109 171.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 71 100 L 71.34 123.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 71.41 128.88 L 67.81 121.93 L 71.34 123.63 L 74.81 121.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 391 100 L 391.34 123.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"&gt;&lt;/path&gt;&lt;path d="M 391.41 128.88 L 387.81 121.93 L 391.34 123.63 L 394.81 121.83 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;In this traditional architecture, we run application as processes on the operating system on each virtual machine. The application process is bound to a certain ports on the operating system, and is wrapped into services (e.g. systemd). On the same virtual machine, there is also a reverse proxy service (e.g. Nginx). There are several main functional areas as listed below, and how they are fulfilled in traditional architecture:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Requirement&lt;/td&gt;&lt;td&gt;Detail&lt;/td&gt;&lt;td&gt;Typically fulfilled by&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;L4 Load balancing&lt;/td&gt;&lt;td&gt;TCP/UDP traffic routing, operating at L3 and L4&lt;/td&gt;&lt;td&gt;Network Load Balancer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TLS termination&lt;/td&gt;&lt;td&gt;Terminate TLS traffic, operating at L4&lt;/td&gt;&lt;td&gt;TLS termination is available in many products such as Load Balancer (L4/L7), Nginx, or the application itself.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Path-based routing&lt;/td&gt;&lt;td&gt;Route request based on URI path, operating at L7&lt;/td&gt;&lt;td&gt;Nginx, modern L7 Load Balancer.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Authentication&lt;/td&gt;&lt;td&gt;Integrate with external identity store, operating at L7&lt;/td&gt;&lt;td&gt;Nginx, modern L7 Load Balancer.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These requirements are the problems that Kubernetes needs to solve in its own architecture. They are solved by different abstraction objects in Kubernetes. Before getting to traffic management, we first need to expose an application.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-service"&gt;Service&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;During traditional application deployment, we often need to organize a group of homogenous application instances as a single target for batch operation. The Pod object is an abstraction of a single application instance. The Deployment object is an abstraction of a group of homogenous Pods. The purpose of Deployment object is for Pod orchestration only. It is not designed to expose the application. To define how we want to expose an application, we use &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Service&lt;/a&gt; object.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The service object does not carry exactly the same functionalities as an operating system service. It connects to the frontend (client), as well as to the backend (server). There are two ways to connect to a backend:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;To connect to Pods as backend, use &lt;strong&gt;selector&lt;/strong&gt; and &lt;strong&gt;label&lt;/strong&gt;; the target port is Pod&amp;#8217;s port. This is the most common use case.&lt;/li&gt;&#10;&lt;li&gt;To connect to a custom backend (e.g. external database, services in different namespaces, during workload migration), define an &lt;strong&gt;Endpoints object&lt;/strong&gt; (including address and port), and target the port;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the frontend, there are several ways to expose service to client, as defined in ServiceType property. Each represents a level of exposure:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;ClusterIP (default)&lt;/strong&gt;: the service gets an internal IP address in the cluster. This is the lowest level of exposure. The service is only reachable from within the cluster. This is a good choice when the service is for internal assumption, such as database.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;NodePort&lt;/strong&gt;: the service is exposed at a static port on each node. The port must be in a range pre-specified during cluster provisioning (default 30000-32767). Each node proxies traffic to that port to the service. Without a load balancer, each node is a point of entry on its own. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt;: this option works with external load balancer in cloud deployments. The actual creation of the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer"&gt;load balancer&lt;/a&gt; happens asynchronously, and information about the provisioned balancer is published in the Service&amp;#8217;s&amp;nbsp;&lt;code&gt;.status.loadBalancer&lt;/code&gt;&amp;nbsp;field. Some cloud providers allow you to specify the&amp;nbsp;&lt;code&gt;loadBalancerIP&lt;/code&gt;. The benefit Load Balancer over NodePort, is it provides a single point of entry (for each service).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;ExternalName&lt;/strong&gt;: rare use case with custom endpoint object.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 class="wp-block-heading" id="h-headless-service"&gt;Headless service&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With service type ClusterIP, if you explicitly specify&amp;nbsp;&lt;code&gt;"None"&lt;/code&gt;&amp;nbsp;for the cluster IP (&lt;code&gt;.spec.clusterIP&lt;/code&gt;), the service is considered a headless service. With a headless service, a cluster IP is not allocated, kube-proxy does not handle these services, and there is no load balancing or proxying done by the platform for them. Each connection to the service is forwarded to one randomly selected backing pod. Hence the document points out that you can use a headless Service to interface with other service discovery mechanisms, without being tied to Kubernetes implementation. The behaviour differs slightly based on whether selectors are present, but both resembles DNS routing with multiple A record.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-virtual-ip"&gt;Virtual IP&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes manages service traffic with virtual IP. When clients connect to virtual IP (VIP), the traffic is automatically transported to an appropriate endpoint. Virtual IP is implemented with kube-proxy. Kube-proxy can work in three modes: userspace, iptables and IPVS. I discussed these terms in &lt;a href="https://www.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/"&gt;this&lt;/a&gt; post last year. The takeaway is that IPVS is the recommended mode.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-ingress"&gt;Ingress&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ingress in Kubernetes cannot match up with a counterpart in traditional architecture. It is mainly for path-based request routing. Also, do not confuse Ingress object with Ingress rule as a policy type in Network Policy object. Ingress is a high level abstraction and should be considered over Service object when the followings are involved in the routing.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Content-based or path-based L7 routing&lt;/li&gt;&#10;&lt;li&gt;Multiple protocols (e.g. gRPC, WebSockets)&lt;/li&gt;&#10;&lt;li&gt;Authentication&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ingress usually work with service object (ClusterIP), as illustrated in Kubernetes documentation:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="661" height="321" src="https://www.digihunch.com/wp-content/uploads/2021/06/image-4.webp" alt="" class="wp-image-2452"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Also note that if you have a service other than HTTP or HTTPS, that you need to expose to the Internet, it is recommended to use a service object of NodePort or LoadBalancer type.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We call Ingress a high-level abstraction. Ingress object (aka ingress resource) itself does not expose application. It simply defines a set of routing rules. The implementation is provided by another object (Ingress Controller), who enforces the routing rules by monitoring and manage traffic using its own Service and Pods. You must have an Ingress controller to satisfy an Ingress. Only creating an Ingress resource has no effect. There are a number of &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/"&gt;Ingress Controllers&lt;/a&gt; to choose from. &lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-ingress-resource"&gt;Ingress Resource&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In an Ingress resource, annotations are used to configure some options, depending on the corresponding Ingress Controller. What annotation can be used depends on the specific Ingress Controller. The backend can be either a service, or a resource. A common usage for a Resource backend is to ingress data to an object storage backend with static assets. You can define DefaultBackend for an Ingress.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each Ingress should specify a class, a reference to an IngressClass resource that contains additional configuration including the name of the controller that should implement the class. Before the IngressClass resource and ingressClassname field were added in Kubernetes 1.8, Ingress classes were specified with a &lt;code&gt;kubernetes.io/ingress.class&lt;/code&gt;&amp;nbsp;annotation on the Ingress. This annotation was never formally defined, but was widely supported by Ingress controllers. For example, &lt;a href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/"&gt;here&lt;/a&gt; is the annotations supported by Nginx Controllers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is the yaml output of the ingress from Kubernetes &lt;a href="https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/service/networking/minimal-ingress.yaml"&gt;documentation&lt;/a&gt;:&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-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&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:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Ingress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;minimal&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ingress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;annotations&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;nginx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ingress&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;rewrite&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rules&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;paths&lt;/span&gt;&lt;span style="color:#f92672"&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;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;/testpath&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pathType&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Prefix&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;backend&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;service&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 class="wp-block-heading" id="h-ingress-controller"&gt;Ingress Controller&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ingress Controller exists in the form of Pods, usually as daemonSet, sometimes as a deployment. The Pods listens for requests to create or modify Ingress within the cluster, and converts the rules in the manifest into configuration directives for a load balancing components. Below is all the components related to Ingress Controller:&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 -n ingress-nginx get all&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME READY STATUS RESTARTS AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pod/ingress-nginx-admission-create-s7486 0/1 Completed &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 11d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pod/ingress-nginx-admission-patch-sjt2q 0/1 Completed &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; 11d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pod/ingress-nginx-controller-5b74bc9868-6vmjc 1/1 Running &lt;span style="color:#ae81ff"&gt;18&lt;/span&gt; 11d&#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;NAME TYPE CLUSTER-IP EXTERNAL-IP PORT&lt;span style="color:#f92672"&gt;(&lt;/span&gt;S&lt;span style="color:#f92672"&gt;)&lt;/span&gt; AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;service/ingress-nginx-controller LoadBalancer 10.106.25.194 localhost 80:31774/TCP,443:31576/TCP 11d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;service/ingress-nginx-controller-admission ClusterIP 10.102.38.191 &amp;lt;none&amp;gt; 443/TCP 11d&#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;NAME READY UP-TO-DATE AVAILABLE AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;deployment.apps/ingress-nginx-controller 1/1 &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 11d&#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;NAME DESIRED CURRENT READY AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;replicaset.apps/ingress-nginx-controller-5b74bc9868 &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 11d&#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;NAME COMPLETIONS DURATION AGE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;job.batch/ingress-nginx-admission-create 1/1 9s 11d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;job.batch/ingress-nginx-admission-patch 1/1 25s 11d&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Ingress Controller can be implemented by load balancer resource from cloud platform, or Nginx. When you have one ingress resource and one controller, the matching is assumed. When you have multiple controllers, you need to use the &lt;a href="https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/"&gt;mechanism&lt;/a&gt; from the ingress controller to ensure correct matching.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Nginx is a popular controller and there are a couple of implementations as illustrated &lt;a href="https://www.nginx.com/blog/guide-to-choosing-ingress-controller-part-4-nginx-ingress-controller-options/#NGINX-vs.-Kubernetes-Community-Ingress-Controller"&gt;here&lt;/a&gt;. Let&amp;#8217;s take a look at Nginx Controller as an example. The troubleshooting &lt;a href="https://docs.nginx.com/nginx-ingress-controller/troubleshooting/"&gt;guide&lt;/a&gt; states that, For each Ingress/VirtualServer resource, the Ingress Controller generates a corresponding NGINX configuration file in the&amp;nbsp;&lt;code&gt;/etc/nginx/conf.d&lt;/code&gt;&amp;nbsp;folder. Additionally, the Ingress Controller generates the main configuration file&amp;nbsp;&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;, which includes all the configurations files from&amp;nbsp;&lt;code&gt;/etc/nginx/conf.d&lt;/code&gt;.&amp;nbsp;In the Rancher ingress example above, we can check the nginx &lt;a href="https://docs.nginx.com/nginx-ingress-controller/troubleshooting/#checking-the-generated-config"&gt;configuration&lt;/a&gt; with the commands 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;kubectl exec ingress-nginx-controller-5b74bc9868-6vmjc -n ingress-nginx -- cat /etc/nginx/nginx.conf | less&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;It is important to understand the difference between a load-balancer type service and an ingress. The &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#what-is-ingress"&gt;documentation&lt;/a&gt; for ingress states that: An Ingress does &lt;strong&gt;not&lt;/strong&gt; expose &lt;strong&gt;arbitrary ports or protocols&lt;/strong&gt;. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type&amp;nbsp;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#nodeport"&gt;Service.Type=NodePort&lt;/a&gt;&amp;nbsp;or&amp;nbsp;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer"&gt;Service.Type=LoadBalancer&lt;/a&gt;. This is because ingress operates at layer 7, so routes connections based on http host header or url path. Load balanced services operate at layer 4 so can load balance arbitrary tcp/udp/sctp services. Ingress should be backed by L7 load balancer, whereas load-balancer service should be backed by L4 load balancer.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-nginx-ingress-controller"&gt;Nginx Ingress Controller&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are several flavours of Nginx ingress controllers that cause much confusion. It is clarified on a blog &lt;a href="https://www.nginx.com/blog/guide-to-choosing-ingress-controller-part-4-nginx-ingress-controller-options/#NGINX-vs.-Kubernetes-Community-Ingress-Controller"&gt;post&lt;/a&gt; on Nginx website. To recap:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Community version: Found in the &lt;a href="https://github.com/kubernetes/ingress-nginx"&gt;kubernetes/ingress-nginx&lt;/a&gt; repo, the community Ingress controller is based on Nginx Open Source, with docs on &lt;a href="https://kubernetes.github.io/ingress-nginx/"&gt;Kuberentes.io&lt;/a&gt;. It is maintained by the Kubernetes community with &lt;a href="https://www.nginx.com/blog/nginx-sprint-2-0-clear-vision-fresh-code-new-commitments-to-open-source/#resources-for-kubernetes"&gt;assistance&lt;/a&gt; from the F5 Nginx team.&lt;/li&gt;&#10;&lt;li&gt;Nginx version: Found in the &lt;a href="https://github.com/nginxinc/kubernetes-ingress"&gt;nginxinc/kubernetes-ingress&lt;/a&gt; repo, the NGINX Ingress Controller is developed and maintained directly by F5 NGINX team, with docs on &lt;a href="https://docs.nginx.com/nginx-ingress-controller/"&gt;docs.nginx.com&lt;/a&gt;. It is available in two editions:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;NGINX Open Source-based&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.nginx.com/products/nginx-ingress-controller/"&gt;NGINX Plus&lt;/a&gt;-based&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are also a number of other Ingress controller based on NGINX, such as Kong, but their names are easily distinguished. If you&amp;#8217;re not sure which version you&amp;#8217;re using, check the container image, then compare the image name with the repos listed above.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-load-balancer"&gt;Load Balancer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes by itself does not have an object for Load Balancer. The function of traditional Load Balancer is implemented through Service and Ingress objects in Kubernetes, both of which can be satisfied by a load balancer object from the cloud platform (service-managed load balancer and ingress-managed load balancer). Alternatively, you may stand up a standalone load balancer independent of the Kubernetes cluster, which is not recommended.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If your architecture is complex and you have a lot of services (e.g. using microservice), then the overhead of managing everything with Service and Ingress in Kubernetes can be significant. In that case, consider delegating these tasks to a &lt;a href="https://en.wikipedia.org/wiki/Service_mesh"&gt;service mesh&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-troubleshooting"&gt;Troubleshooting&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There isn&amp;#8217;t a single recipe for troubleshooting service and ingress on Kubernetes. There are some good general guide lines &lt;a href="https://itnext.io/kubernetes-troubleshooting-saga-part-1-pods-deployments-and-cluster-52df5017df93"&gt;here&lt;/a&gt; and &lt;a href="https://itnext.io/kubernetes-troubleshooting-saga-part-2-networking-and-dns-connectivity-7f11013f6148"&gt;here&lt;/a&gt;, in addition to the guides (&lt;a href="https://kubernetes.io/docs/tasks/debug-application-cluster/debug-cluster/"&gt;here&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/tasks/debug-application-cluster/troubleshooting/"&gt;here&lt;/a&gt;) from official documentation. To run networking command from within the Pod network, you can launch a Pod using nicolaka &lt;a href="https://github.com/nicolaka/netshoot"&gt;netshoot&lt;/a&gt; image.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Bottom line&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We compared service and ingress in Kubernetes. In real life, we use both, and oftentimes along with CRDs of service mesh.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/06/kubernetes-networking-solutions-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Networking Solutions Overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/07/helm-configuration-management-for-kubernetes-resources/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Helm – Configuration Management for Kubernetes Resources&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kubernetes Networking Solutions Overview</title><link>https://www.digihunch.com/2021/06/kubernetes-networking-solutions-overview/</link><pubDate>Tue, 22 Jun 2021 12:14:26 -0400</pubDate><guid>https://www.digihunch.com/2021/06/kubernetes-networking-solutions-overview/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-networking.webp" alt="Featured image of post Kubernetes Networking Solutions Overview" /&gt;&lt;p class="wp-block-paragraph"&gt;Kubernetes networking involves a lot of details. We discuss some CNI plugins in this post. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most basic mode is &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/#kubenet"&gt;kubenet&lt;/a&gt;. We use &amp;#8211;network-plugin=kubenet with kubelet process to use it. Kubenet is not a CNI plugin, but it works with bridge, lo and host-local (CNI-compliant implementations). We can directly specify MTU with &amp;#8211;network-plugin-mtu. Kubenet is a basic network plugin, based on bridge plugin, with the addition of port mapping and traffic shaping. It does not offer cross-node networking itself. Today it is typically used with managed clusters by cloud providers, where the cloud provider set up routing rules themselves for inter-node communication.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When a cluster goes multi-node, the main challenge is communication between Pods across different nodes. Pods come and go. The size of cluster could increase or decrease as well. The network solutions come in two network types: overlay network based on encapsulation, or non-overlay networks, most likely using routing techniques. Common backends for multi-host container networking solutions include VXLAN encapsulation, IPIP encapsulation, host-gw, IPSec. In addition, there are some backends that only used by certain plugins.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-common-backends"&gt;Common Backends&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;VXLAN&lt;/strong&gt;: use in-kernel VXLAN to encapsulate the packets. VXLAN is a &lt;a href="https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking#"&gt;virtual networking capability&lt;/a&gt; in Linux which is also used in &lt;a href="https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;virtualization&lt;/a&gt; technology. VXLAN is an overlay technology requiring encapsulation of overlay network&amp;#8217;s layer-2 frame into UDP packet at layer 4 of underlay network. When configured, the VxLAN backend creates a Flannel interface on every host. When a container on one node wishes to send traffic to a different node, the packet goes from the container to the bridge interface in the host&amp;#8217;s network namespace. From there the bridge forwards it to the Flannel inteface because the kernel route table designates that this interface is the target for the non-local portion of the overlay network. The Flannel network drive wraps the packet in a UDP packet and sends it to the target host. Once it arrives at its destination, the process flows in reverse, with the Flannel driver on the destination host unwrapping the packet, sending it to the bridge interface, and from there the packet find its way into the overlay network and to the destination Pod.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;host-gw&lt;/strong&gt;: the host-gw is a non-overlay solution that maintains route tables on Linux Host to allow Pods to communicate across Nodes. It is only used in Flannel plugin. Suppose we have two hosts, each with two containers as connected below. Initially, container0 is not able to reach container2 because eth0 on node0 does not have an entry that matches container2&amp;#8217;s IP address. The packet is there sent to default route, which isn&amp;#8217;t destined to container2. &lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://gblobscdn.gitbook.com/assets%2F-LOuzrzA9qdsjWfu2rC9%2F-LUPoGQ1ihiJFofTaO7A%2F-LUPoH4I_mQkNKdfkxU1%2Fhost-gw.png?alt=media" alt="" style="width:840px;height:366px" width="840" height="366"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;However, if we build rules to match container IP address, on the route table of each node. The issue would be solved. This is essentially how host-gw works. Specifically, on node 0, we add &amp;#8220;ip route add 192.168.1.0/24 via 10.20.0.2 dev eth0&amp;#8221;, on node 1, we add &amp;#8220;ip route add 192.168.0.0/24 via 10.20.0.1 dev eth0). The host-gw in Flannel will manage rule addition to us. Note that the two hosts must have direct layer 2 connectivity. In other words, there must not be a router between the two nodes. Otherwise, the routing table on the router is out of reach. In fact, all nodes in a Flannel network must have layer 2 connectivity with each other. In other words, all nodes must be in a single LAN. Host-gw provides better performance than VxLAN.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;IPSec&lt;/strong&gt; uses in-kernel IPSec to encapsulate and encrypt the packets. IPsec is a group of protocols to ensure authentication and encryption per packet between devices. Since it secures traffic at layer 3 and now it has become a major backend technology for VPN. IPsec adds several headers and trailers to datagram containing authentication and encryption information. The two major protocols working in IPSec are AH (Authentication Header) and ESP (Encapsulating Security Payload). AH serves up authentication services only; ESP provides both authentication and encryption abilities. It also uses IKE protocol for key exchange.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPSec works in two modes: transport and tunnelling mode. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Transport mode creates a secure tunnel between two devices end to end. The payload of each datagram is encrypted, but the original IP header is not. Intermediary routers are thus able to view the final destination of each datagram, unless a separate tunnelling protocol (e.g. GRE) is used.&lt;/li&gt;&#10;&lt;li&gt;Tunnel mode works between two endpoints, such as two routers, protecting all traffic that goes through the tunnel. The original IP header containing the final destination of the datagram is encrypted, in addition to the payload. To tell intermediary routers where to forward the datagrams, IPsec adds a new IP header. At each end of the tunnel, the routers decrypt the IP headers to deliver the datagram to their destinations. The intermediary routers does not know the final destination, or what transport protocol is used.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;IPIP&lt;/strong&gt; (IP over IP) tunnel is typically used to connect two internal IPv4 subnets through public IPv4 internete. It has the lowest overhead but can only transmit IPv4 unicast traffic.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cni-plugins"&gt;CNI Plugins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Originally, the network functions were developed in-tree. Then the CNI specification came up to allow plugin development out-of-tree to implement cluster networking functions. The Container Network Interface seeks to completely decoupled network management from container runtime. Kubernetes picked CNI over CNM in 2016, as discussed in my &lt;a href="https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;virtualization&lt;/a&gt; discussion. CNI clearly defines the specification for following activities:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;When a Pod comes up, give it a network interface&lt;/li&gt;&#10;&lt;li&gt;Assign IP to the network interface&lt;/li&gt;&#10;&lt;li&gt;When a Pod is deleted, delete the associated network interface&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we configure a Kubernetes cluster, we must specify &amp;#8211;network-plugin switch, so that the cluster is operational. If we use CNI as network-plugin, we also need to install the plugin, optionally with the help of Rancher.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the worker node, we use &amp;#8211;network-plugin=cni with kubelet process to use CNI plugins. A plugin may consist one or more binaries. The binaries are located in /opt/cni/bin (or otherwise specified by &amp;#8211;cni-bin-dir). The configurations are located in /etc/cni/net.d (or otherwise specified in &amp;#8211;cni-conf-dir). Note that the configuration file may reference different plugin implementations for different network management purpose (e.g. interface creating, address allocation, etc). The &lt;a href="https://github.com/containernetworking/plugins"&gt;container networking repo&lt;/a&gt; provided some reference implementations and some of them are used by other plugins. These reference implementations include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Main (interface creating): bridge, ipvlan, loopback, ptp, macvlan, etc&lt;/li&gt;&#10;&lt;li&gt;IPAM (IP address management): host-local, dhcp, static&lt;/li&gt;&#10;&lt;li&gt;Meta (other plugins): portmap, bandwidth&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So, a CNI plugin consists of a networking solution for backend, and binaries to cover the aspects outlined above. I discussed some common backends above. Below I will introduce some common plugins and backends only available to each plugin&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-flannel"&gt;Flannel&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Flannel by CoreOS: supports a range of backends. The advantage of Flannel is it reduces the complexity of doing port mapping. &lt;a href="https://blog.laputa.io/kubernetes-flannel-networking-6a1cb1f8ec7c"&gt;This&lt;/a&gt; is a great post that covers the mechanism.&lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://miro.medium.com/max/983/1*EFr8ohzABfStS7o9gGMYKw.png" alt="" style="width:737px;height:217px" width="737" height="217"/&gt;&lt;figcaption class="wp-element-caption"&gt;Flannel with overlay (e.g. VxLAN on UDP encapsulation)&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It supports VXLAN, host-gw, IPSec, IPIP as well as the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Amazon VPC&lt;/strong&gt;: recommended with Amazon VPC. AWS VPC creates IP routes in an AWS route table. The number of records in this table is limited by 50 so you can&amp;#8217;t have more than 50 machines in a cluster.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;GCE&lt;/strong&gt;: recommended with Google Compute Engine Network. Instead of using encapsulation, GCE also manipulates IP route to achieve maximum performance. Because of this, a separate flannel interface is not created.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;UDP&lt;/strong&gt;: debugging only for old kernels that don&amp;#8217;t support VXLAN&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-calico"&gt;Calico&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Border_Gateway_Protocol"&gt;Border Gateway Protocol &lt;/a&gt;(BGP) is a standardized exterior gateway protocol designed to exchange routing and reachability information among autonomous systems (AS) on the Internet.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Calico operates at layer 3. It prefers BGP without an overlay network for the highest speed and efficiency, but in scenarios where hosts cannot directly communicate with one another, it can utilize an overlay solution (e.g. VxLAN or IP-in-IP). Calico also supports network policies for protecting workloads and nodes from malicious activity or aberrant applications.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Calico networking Pod contains a CNI container to keep track of Pod deployment, and register addresses and routes. It also contains a daemon that announces the IP and route information to the network via the Border Gateway Protocol (BGP). The BGP daemon build a map of the network that enables cross-host communication.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Calico requires a distributed and fault-tolerant key/value store, and deployments often choose etcd to deliver this component. Calico uses it to store metadata about routes, virtual interfaces, and entwork policy objects. Calico can either use a separate HA deployment of etcd, or the same etcd datastore with the Kubernetes cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we are unable to use BGP (e.g. with cloud provider, or in an environment where we have no permission to configure router peers. Calico&amp;#8217;s IP-in-IP mode encapsulates packets before sending them to other nodes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once IP-in-IP is configured, Calico wraps inter-Pod packets in a new packet with headers that indicate the source of the packet is the host with the originating Pod, and the target of the packet is the host with the destination Pod. The Linux kernel performs this encapsulation, and then forwards the packet to the destination host where it is unwrapped and delivered to the destination Pod.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-canal"&gt;Canal&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The followings is quoted from Rancher &lt;a href="https://rancher.com/blog/2019/2019-03-21-comparing-kubernetes-cni-providers-flannel-calico-canal-and-weave/"&gt;website&lt;/a&gt;:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Canal seeks to integrate the networking layer provided by Flannel with the networking policy capabilities of Calico. As the contributors worked through the details however, it became apparent that a full integration was not necessarily needed if work was done on both projects to ensure standardization and flexibility. As a result, the official project became somewhat defunct, but the intended ability to deploy the two technology together was achieved. For this reason, it&amp;#8217;s still sometimes easiest to refer to the combination as &amp;#8220;Canal&amp;#8221; even if the project no longer exists. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because Canal is a combination of Flannel and Calico, its benefits are also at the intersection of these two technologies. The networking layer is the simple overlay provided by Flannel that works across many different deployment environments without much additional configuration. The network policy capabilities layered on top supplement the base network with Calico’s powerful networking rule evaluation to provide additional security and control.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-weave-net"&gt;Weave Net&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Weave Net by Weaveworks offers a different paradigm. Weave creates a mesh overlay network between each of the nodes in the cluster, allowing for flexible routing between participants. Applications use the network just as if the containers were all plugged into the same network switch, with no need to configure port mappings and links.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more good references to determine networking options, check out these posts:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Calico &lt;a href="https://projectcalico.docs.tigera.io/networking/determine-best-networking"&gt;blog&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Rancher &lt;a href="https://www.suse.com/c/rancher_blog/comparing-kubernetes-cni-providers-flannel-calico-canal-and-weave/"&gt;blog&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Kubevious &lt;a href="https://kubevious.io/blog/post/comparing-kubernetes-container-network-interface-cni-providers"&gt;blog&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/06/kubernetes-storage-explained/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage Explained – from in-tree plugin to CSI&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/07/traffic-management-in-kubernetes-service-and-ingress/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Service and Ingress -Traffic Management in Kubernetes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kubernetes Storage Explained – from in-tree plugin to CSI</title><link>https://www.digihunch.com/2021/06/kubernetes-storage-explained/</link><pubDate>Sat, 12 Jun 2021 21:55:46 -0400</pubDate><guid>https://www.digihunch.com/2021/06/kubernetes-storage-explained/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-csi.webp" alt="Featured image of post Kubernetes Storage Explained – from in-tree plugin to CSI" /&gt;&lt;p class="wp-block-paragraph"&gt;To support a variety of storage backend, Kubernetes abstract storage issues with several objects (&lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/"&gt;volume&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;persistent volume&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims"&gt;persistent volume claim&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/"&gt;storage class&lt;/a&gt;) and adopts &lt;a href="https://github.com/container-storage-interface/spec/blob/master/spec.md"&gt;container storage interface&lt;/a&gt;. Unfortunately, the documents are not very well organized to deliver the idea of these concepts, most likely because features are introduced at very different times. Hence this article. At the bottom of this article, I also go through five examples of using volumes in different ways, taking azure disk (SSD as an example).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first to think about is whether we need just ephemeral storage or persistent storage. Generic volume with ephemeral storage lives and dies with the Pod and we don&amp;#8217;t really care where it is from. With persistent storage, we need to consider where it is from and how to create (provision) the storage. The storage can be created statically or dynamically.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-persistentvolume-pv-and-persistentvolumeclaim-pvc"&gt;PersistentVolume (PV) and PersistentVolumeClaim (PVC)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Regardlessly of static or dynamic storage provision, we first need to understand two objects before getting to that: Persistent Volume (PV) and Persistent Volume Claim (PVC). &lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;We use PV object to represent external storage volume. A single external storage volume can be represented by a single PV. So PV goes with external volumes in 1 to 1 relationship. A 100G volume cannot be represented by two PVs each with 50G, unless the storage administrator divides it into two separate volumes, each with 50G.&lt;/li&gt;&#10;&lt;li&gt;PVC goes with Pod in 1 to 1 relationship. The Pods needs a PVC in order to claim ownership of a PV. A valid PVC allows a Pod to mount a PV as its volume.&lt;/li&gt;&#10;&lt;li&gt;Here we call storage volume external in relative to the pods. If the storage volume is mapped to a directory on the host file system, it is still considered an external storage.&lt;/li&gt;&#10;&lt;li&gt;A single PV can link to multiple PVCs, so long as the total request in PVCs does not exceed PV&amp;#8217;s capacity. So PV and PVC are in 1 to many relationship.&lt;/li&gt;&#10;&lt;li&gt;How PVC binds to PV is defined by Access Mode, with three options. Note that the options are effective for the entire PV. You cannot have different options for each PVC linked to a PV:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;RWO (ReadWriteOnce): allowing the PV to be bound to a single PVC (for read write). This mode is typically used in block storage;&lt;/li&gt;&#10;&lt;li&gt;RWM (ReadWriteMany): allowing the PV to be bound to multiple PVCs (for read write). This mode is only supported by file (e.g. NFS) and object storage;&lt;/li&gt;&#10;&lt;li&gt;ROM (ReadOnlyMany): allowing the PV bound to multiple PVCs for read only.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;When a PVC is released, what to do with the PV is defined as persistentVolumeReclaimPolicy, and the two options (effective at PV level) are:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Delete&lt;/li&gt;&#10;&lt;li&gt;Retain&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 class="wp-block-heading" id="h-static-provisioning-and-dynamic-provisioning"&gt;Static Provisioning and Dynamic Provisioning&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With static provisioning, the external storage volume must be pre-created. In this context, a PV object represents a pre-created external storage volume. So PVs must be explicit declared. The K8s literature also refers to such PVs as pre-created PV.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With dynamic provisioning, the external storage volume is provisioned dynamically. Therefore, you do not need to explicitly create PVs. By the same token, access mode does not apply. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Instead of PV, now we need to explicitly declare storage class, which specifies how to dynamically provision PVs, with the following properties:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;volumeBindingMode defines when the binding and provisioning of a PersistentVolume occurs, with two options:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Immediate (default)&lt;/li&gt;&#10;&lt;li&gt;WaitForFirstConsumer (recommended): delays until a Pod using the PVC is created&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;reclaimPolicy (the equivalent of persistentVolumeReclaimPolicy for pre-created PV) with two options:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Delete (default)&lt;/li&gt;&#10;&lt;li&gt;Retain&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;provisioners: determines what volume plugin is used for provisioning PVs. There are two categories:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Internal provisioner &lt;/strong&gt;(prefixed with kubernetes.io): common ones are listed &lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner"&gt;here&lt;/a&gt;. Note that there isn&amp;#8217;t an internal &lt;a href="https://github.com/kubernetes-retired/external-storage"&gt;provisioner for NFS &lt;/a&gt;any more. External NFS provisioner is needed.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;External provisioner&lt;/strong&gt;: third-party out-of-tree plugins compliant to CSI. For example: Dell &lt;a href="https://github.com/dell/csi-xtremio-deploy"&gt;XtremIO&lt;/a&gt; CSI plugin, Dell &lt;a href="https://github.com/dell/csi-powerscale"&gt;Isilon&lt;/a&gt; plugin, &lt;a href="https://github.com/purestorage/pso-csi"&gt;PureStorage&lt;/a&gt; CSI driver, Scality &lt;a href="https://go.scality.com/l/893901/2021-05-11/3zjqn/893901/1621255966fkouGUtc/artesca_wp_v4.pdf"&gt;Artesca&lt;/a&gt; (launched in Apr 2021), and NetApp &lt;a href="https://netapp-trident.readthedocs.io/en/stable-v19.01/index.html"&gt;Trident&lt;/a&gt; CSI drivers, and &lt;a href="https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner"&gt;NFS subdir provisioner&lt;/a&gt; in Kubernetes-sigs repo.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/#parameters"&gt;parameters&lt;/a&gt;: each provisioner has its own set of mandatory and optional parameters;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/#allow-volume-expansion"&gt;allowVolumeExpansion&lt;/a&gt;: can be set to true if the underlying storage class supports volume expansion;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/#mount-options"&gt;mountOptions&lt;/a&gt;: specify only if the storage class supports it;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With the information above, we can simplify the rules as follows:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;In static provisioning, PV needs to be declared explicitly and SC is not needed&lt;/li&gt;&#10;&lt;li&gt;In dynamic provisioning, SC is required so we can specify provisioner and the parameters needed by the provisioner. PV doesn&amp;#8217;t need to be explicitly declared, even though it exists in the interaction.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In real life however, you might come across the following edge cases which seems to contradict with the two generic rules above:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/#local"&gt;Local volume&lt;/a&gt;, currently does not support dynamic provisioning. However a StorageClass should still be created to delay volume binding until Pod scheduling. The volume binding mode &lt;em&gt;WaitForFirstConsumer&lt;/em&gt;&amp;nbsp;should be specified.&lt;/li&gt;&#10;&lt;li&gt;In dynamic provisioning, if a PVC does not explicitly define PVC, the administrator should have specified a &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#dynamic"&gt;default StorageClass&lt;/a&gt; in place for the cluster. You might also come across PVC with empty string (&amp;#8220;&amp;#8221;) as storageClassName, which indicates that &lt;span style="text-decoration: underline;"&gt;no storage class will be used&lt;/span&gt; (i.e. dynamic provisioning is disabled for the PVC). According to &lt;a href="https://kubernetes.io/blog/2017/03/dynamic-provisioning-and-storage-classes-kubernetes/"&gt;this&lt;/a&gt; post, in a PVC:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;If storageClassName=&amp;#8221;&amp;#8221;, then it is static provisioning&lt;/li&gt;&#10;&lt;li&gt;If storageClassName is not specified, then the default storage class will be used. &lt;/li&gt;&#10;&lt;li&gt;If storageClassName is set to a specific value, then the matching storageClassName will be considered. If no corresponding storage class exists, the PVC will fail.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-the-confusing-volumes"&gt;The confusing &amp;#8220;Volumes&amp;#8221;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ve discussed PersistentVolume, which is a K8s object that represents an external storage volume. When the word Volume stands by itself, it generally refers to the part of storage exposed to the Kubernetes cluster, no matter what type of storage it is or where it comes from. We can distinguish them in the following table:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Generic &lt;strong&gt;Volumes&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Persistent Volumes&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pod assignment&lt;/td&gt;&lt;td&gt;Bound to a single pod, declared as part of a Pod.&lt;/td&gt;&lt;td&gt;A standalone resource type decoupled from Pod and can be bound to single, or multiple Pods via PVC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lifecycle&lt;/td&gt;&lt;td&gt;Volume is deleted as the owner Pod dies. Data on the volume may or may not persist.&lt;/td&gt;&lt;td&gt;Assuming PVC is gone with Pod, the PV persists. Data on PV may or may not persist depending on ReclaimPolicy.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Configuration&lt;/td&gt;&lt;td&gt;Pod creator (e.g. app developer) needs to know the details of storage resource in the cloud environment. (e.g volume ID)&lt;/td&gt;&lt;td&gt;Pod creator does not need the details of storage resource in the cloud environment. K8s Cluster administrator can provision PV, either statically or dynamically for Pod creator.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you want to use PeristentVolume to back a Volume in Pod, you&amp;#8217;d have to use PersistentVolumeClaim. This means, some types of volumes (including hostPath) can be both mounted as a persistent volume as well as a regular volume. To compare the two ways of mount volume (direct vs via PVC), we take a look at the Kubernetes configuration &lt;a href="https://github.com/kubernetes/examples/tree/master/staging/volumes/azure_disk"&gt;examples&lt;/a&gt; for Azure Disk. The examples are provided at the bottom of this post. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that, no matter which method of using the volumes, some types of volumes just work natively, and some requires plugin to operate. The table below summarizes the mechanism behind common volume types.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Volume Types&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Mechanism&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Mountable as &lt;strong&gt;non-persistent volume&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;mountable as &lt;strong&gt;persistent volume&lt;/strong&gt; (through PVC or SC)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;emptyDir &lt;/td&gt;&lt;td&gt;A native volume type, for temporary data only. Data is wiped along with volume. The storage media is determined by the medium of the filsystem holding the kubelet root dir (typically /var/lib/kubelet). You can even set emptyDir.medium to &amp;#8220;Memory&amp;#8221;&lt;/td&gt;&lt;td&gt;YES&lt;/td&gt;&lt;td&gt;NO. By definition, emptyDir is not persistent.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ConfigMap, Secret&lt;/td&gt;&lt;td&gt;Native volume type to store non-sensitive or sensitive configuration data. ConfigMap and Secrets are stored in etcd.&lt;/td&gt;&lt;td&gt;YES&lt;/td&gt;&lt;td&gt;NO. However, by nature, ConfigMap and Secret are stored persistently. There is no need to mount them as PV.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HostPath&lt;/td&gt;&lt;td&gt;A native volume type to mount a file or directory from the host node&amp;#8217;s filesystem into the Pod. In addition to path property, you may optionally specify a type for a hostPath volume (e.g. DirectoryOrCreate, Directory, FileOrCreate, etc). Note that there is also a type named empty string (&amp;#8220;&amp;#8221;) which is the default value. It means means that no checks will be performed before mounting the hostPath volume. &lt;br&gt;In addition to the &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/#hostpath"&gt;caveat&lt;/a&gt; with using hostPath from the documentation, we also need to understand that: &lt;br&gt;1. HostPath gives Pod the ability to maliciously modify files on the host system, or simply fill up the host file system;&lt;br&gt;2. As the document suggests, you may end up with multiple Pods trying to write simultaneously to a host path.&lt;/td&gt;&lt;td&gt;YES. Read &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/#hostpath"&gt;this&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;YES. Check out &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumes-typed-hostpath"&gt;PersistentVolumes typed hostPath&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Local&lt;/td&gt;&lt;td&gt;It represents a mounted local storage device such as a disk, partition, or directory. Compared to hostPath volumes, local volumes are used in a durable and portable manner, without manually scheduling pods to nodes. The system is aware of the volume&amp;#8217;s node constraints by looking at the node affinity on the PV. You must set nodeAffinity on the PV when using local volumes. This also means local volumes are subject to the availability of the underlying node. Refer to &lt;a href="https://kubernetes.io/blog/2019/04/04/kubernetes-1.14-local-persistent-volumes-ga/#how-is-it-different-from-a-hostpath-volume"&gt;this&lt;/a&gt; post.&lt;br&gt;This is also referred to as &lt;a href="https://kubernetes.io/blog/2019/04/04/kubernetes-1.14-local-persistent-volumes-ga/#what-is-a-local-persistent-volume"&gt;Local persistent Volume&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;NO&lt;/td&gt;&lt;td&gt;YES. Static provisioning only. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CephFS, NFS, GlusterFS, Ginder, RBD, FC, iSCSI&amp;#8230;&amp;#8230;&lt;/td&gt;&lt;td&gt;These volume types are backed by legacy in-tree plugins. They are used to connect to external storage in self-hosted clusters.&lt;/td&gt;&lt;td&gt;YES&lt;/td&gt;&lt;td&gt;YES&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;awsElasticBlockStore, AzureDisk, AzureFile, GCEPersistentDisk&lt;/td&gt;&lt;td&gt;These volume types are backed by legacy in-tree plugins. They are used to connect to external storage in public cloud&lt;/td&gt;&lt;td&gt;YES&lt;/td&gt;&lt;td&gt;YES&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Note&lt;/strong&gt; that the table above does not list &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/#persistentvolumeclaim"&gt;PersistenVolumeClaim&lt;/a&gt; as a volume type, because it obviously only support being mounted as persistent volume.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-from-in-tree-plugins-to-out-of-tree-csi-plugins"&gt;From in-tree plugins to out-of-tree CSI plugins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the table above, the bottom two rows involves in-tree plugins (aka built-in plugins). In-tree means the volume plugins are built in the Kubernetes code repository. They were built, linked, compiled, and shipped with the core Kubernetes binaries. There has been 20+ in-tree plugins. The problems of this plugin development model are:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;These in-tree plugins introduces risk to the stability of Kubernetes itself;&lt;/li&gt;&#10;&lt;li&gt;The maintenance and upgrade of plugin is tightly coupled with Kubernetes release&lt;/li&gt;&#10;&lt;li&gt;The Kubernetes community carries the burden of maintaining plugins for all storage backends.&lt;/li&gt;&#10;&lt;li&gt;Plugin developers have to open-source all their volume plugin code.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Kubernetes community seeks better alternatives, and has stopped accepting any more in-tree plugins since GA 1.8. The first alternative paradigm for shipping storage plugin, is &lt;a href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-storage/flexvolume.md"&gt;flexVolume&lt;/a&gt;, which existed since version 1.2. However, &lt;a href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-storage/flexvolume.md"&gt;flexVolume&lt;/a&gt; is still not good enough. For example, some packages like Ceph requires dependency package (ceph-common), and the deployment of plugin requires elevated access to the worker node. For that reason, the community later shifted to the Container Storage Interface (CSI) paradigm. A CSI-compliant plugin allows the storage resource to be surfaced as volumes (be it persistent or not) in Kubernetes cluster. More details in &lt;a href="https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/"&gt;this&lt;/a&gt; post and &lt;a href="https://kubernetes-csi.github.io/docs/drivers.html"&gt;here&lt;/a&gt; is a list of supported CSI-compliant drivers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Back to our azure disk example, &lt;a href="https://github.com/kubernetes-sigs/azuredisk-csi-driver/blob/master/deploy/example/e2e_usage.md"&gt;this&lt;/a&gt; page provides examples for both dynamic and static provisioning.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;CSI-compliant plugin development is more complicate but it offloads it the driver developer. The community hopes users to shift to CSI so the 20+ grandfathered in-tree plugins can eventually be phased out. With that as the goal, there are several types of volumes with the name &amp;#8220;CSI migration&amp;#8221;, allowing users to migrate from in-tree volume plugins to CSI-based plugins.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All the &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/#csi"&gt;CSI&lt;/a&gt;-based plugins are fairly recent. As of today, the document outlines three ways to use CSI volume in a Pod:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;through a reference to a PersistentVolumeClaim (examples 4 and 5 below)&lt;/li&gt;&#10;&lt;li&gt;with a &lt;a href="https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#generic-ephemeral-volume"&gt;generic ephemeral volume&lt;/a&gt; (alpha feature)&lt;/li&gt;&#10;&lt;li&gt;with a &lt;a href="https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#csi-ephemeral-volume"&gt;CSI ephemeral volume&lt;/a&gt; if the driver supports that (beta feature)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-examples"&gt;Examples&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ll go over five examples, as listed in the able below. Note that out of all the combinations, you cannot mount a csi-based plugin as a volume. No such volume type supported by CSI exist.&lt;/p&gt;&#10;&lt;table id="tablepress-15" class="tablepress tablepress-id-15 tbody-has-connected-cells"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;th class="column-1"&gt;Plug-in mechanism&lt;/th&gt;&lt;th class="column-2"&gt;Mount method&lt;/th&gt;&lt;th class="column-3"&gt;Example&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 rowspan="3" class="column-1"&gt;In-tree legacy volume plug-in&lt;/td&gt;&lt;td class="column-2"&gt;as volume&lt;/td&gt;&lt;td class="column-3"&gt;#1. using azureDisk property of Volume&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-2"&gt;as PV (static)&lt;/td&gt;&lt;td class="column-3"&gt;#2. using azureDisk property of PersistentVolume&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td class="column-2"&gt;as PV (dynamic)&lt;/td&gt;&lt;td class="column-3"&gt;#3. using kubernetes.io/azure-disk as provisioner for SC&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td rowspan="3" class="column-1"&gt;Out-of-tree CSI volume plugin&lt;/td&gt;&lt;td class="column-2"&gt;as volume&lt;/td&gt;&lt;td class="column-3"&gt;This mode does not exist. Example is not available&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td class="column-2"&gt;as PV (static)&lt;/td&gt;&lt;td class="column-3"&gt;#4. using disk.csi.azure.com as csi driver of PV&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-7"&gt;&#10;&#9;&lt;td class="column-2"&gt;as PV (dynamic)&lt;/td&gt;&lt;td class="column-3"&gt;#5 using disk.csi.azure.com as provisioner for SC&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-15 from cache --&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now, let&amp;#8217;s take a look at the example code snippet. Some examples are from Azure &lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-disks-dynamic-pv"&gt;documentation&lt;/a&gt;. Some are from the &lt;a href="https://github.com/kubernetes-sigs/azuredisk-csi-driver/tree/master/deploy/example"&gt;azure-disk-csi-driver&lt;/a&gt; repository. I&amp;#8217;ve made minor modifications for conciseness.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Example 1 uses legacy in-tree plugin, and directly mount the volume. The example &lt;a href="https://github.com/kubernetes/examples/blob/master/staging/volumes/azure_disk/azure.yaml"&gt;code&lt;/a&gt; is in Kubernetes repo.&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;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;Pod&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;mypod&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;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kubernetes/pause&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;mypod&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;volumeMounts&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;azure&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;mountPath&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/mnt/azure&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;volumes&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;azure&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;azureDisk&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;Managed&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;diskName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;myAKSDisk&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;diskURI&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/subscriptions/&amp;amp;lt;subscriptionID&amp;gt;/resourceGroups/MC_myAKSCluster_myAKSCluster_eastus/providers/Microsoft.Compute/disks/myAKSDisk&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;Example 2 uses legacy in-tree plugin, and mount the PV statically via PVC. No storage class is used (as indicated by empty string in storage class property)&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;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;PersistentVolume&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;azure-disk-pv&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;capacity&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2Gi&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;storageClassName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&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;volumeMode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Filesystem&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;azureDisk&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;Managed&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;diskName&lt;/span&gt;: &lt;span style="color:#75715e"&gt;&amp;amp;lt;enter-disk-name&amp;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;diskURI&lt;/span&gt;: &lt;span style="color:#75715e"&gt;&amp;amp;lt;enter-disk-resource-id&amp;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;&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;PersistentVolumeClaim&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;azure-disk-pvc&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;storageClassName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2Gi&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;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;Pod&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;logz-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;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;pause&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;kubernetes/pause&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;volumeMounts&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;azure-disk-vol&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;mountPath&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/mnt/logs&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;volumes&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;azure-disk-vol&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;persistentVolumeClaim&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;claimName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;azure-disk-pvc&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;Example 3 uses legacy in-tree plugin, and mount the PV dynamically and implicitly via SC. Note that Azure AKS will create several SCs for you by default so use existing ones whenever available.&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;allowVolumeExpansion&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&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;storage.k8s.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;StorageClass&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;managed-premium&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;parameters&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;cachingmode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ReadOnly&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;Managed&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;storageaccounttype&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Premium_LRS&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;provisioner&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kubernetes.io/azure-disk&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;volumeBindingMode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;WaitForFirstConsumer&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;PersistentVolumeClaim&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;azure-managed-disk&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;storageClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;managed-premium&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;resources&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;requests&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5Gi&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;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Pod&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;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;mypod&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;mypod&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;kubernetes/pause&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;volumeMounts&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;mountPath&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/mnt/azure&amp;#34;&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;volume&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;volumes&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;volume&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;persistentVolumeClaim&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;claimName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;azure-managed-disk&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;Example 4 uses CSI-based plugin, and mount the PV statically via PVC&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;---&#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;PersistentVolume&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;pv-azuredisk&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;capacity&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10Gi&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;persistentVolumeReclaimPolicy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Retain&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;csi&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;driver&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;disk.csi.azure.com&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;readOnly&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&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;volumeHandle&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/subscriptions/{sub-id}/resourcegroups/{group-name}/providers/microsoft.compute/disks/{disk-id}&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;volumeAttributes&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;fsType&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ext4&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;partition&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# optional, remove this if there is no partition&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;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;PersistentVolumeClaim&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;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;pvc-azuredisk&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10Gi&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;volumeName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;pv-azuredisk&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;storageClassName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&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;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Pod&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;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;nginx-azuredisk&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;nodeSelector&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;kubernetes.io/os&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;linux&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;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kubernetes/pause&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;mypod&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;volumeMounts&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;azuredisk01&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;mountPath&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/mnt/azuredisk&amp;#34;&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;volumes&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;azuredisk01&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;persistentVolumeClaim&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;claimName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;pvc-azuredisk&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;Example 5 uses CSI-based plugin, and mount the PV dynamically and implicitly via SC&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;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;StorageClass&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;storage.k8s.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;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;azuredisk-csi-waitforfirstconsumer&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;provisioner&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;disk.csi.azure.com&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;parameters&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;skuname&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;StandardSSD_LRS &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;allowVolumeExpansion&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&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;reclaimPolicy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Delete&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;volumeBindingMode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;WaitForFirstConsumer&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;PersistentVolumeClaim&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;pvc-azuredisk&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10Gi&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;storageClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;managed-csi&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;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Pod&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;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;nginx-azuredisk&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;nodeSelector&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;kubernetes.io/os&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;linux&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;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kubernetes/pause&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;mypod&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;volumeMounts&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;azuredisk01&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;mountPath&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/mnt/azuredisk&amp;#34;&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;volumes&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;azuredisk01&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;persistentVolumeClaim&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;claimName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;pvc-azuredisk&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-bottomline"&gt; Bottomline&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As of June 2021, the CSI support is still new. Generally, if a CSI-based plugin is available and in GA, you should consider using it. If you have existing legacy volume types using in-tree plugin, you should consider migration, and create a migration plan. Also, try to avoid the use case of mounting as generic volume (without PVC) because it is rare and not supported with CSI drivers. Without PVC, it also cannot take advantage of the &lt;strong&gt;volumeClaimTemplates&lt;/strong&gt; property in StatefulSet object.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/05/getting-started-with-github-actions/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Getting started with GitHub Actions&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/06/kubernetes-networking-solutions-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Networking Solutions Overview&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://www.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://www.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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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 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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://www.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://www.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>A shallow dive into Artificial Intelligence</title><link>https://www.digihunch.com/2021/01/a-shallow-dive-into-artificial-intelligence/</link><pubDate>Sat, 30 Jan 2021 19:10:49 -0400</pubDate><guid>https://www.digihunch.com/2021/01/a-shallow-dive-into-artificial-intelligence/</guid><description>&lt;p class="wp-block-paragraph"&gt;This is what I have learned after writing the Azure AI fundamentals exam. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Artificial intelligence is the software that imitates human behaviours and capabilities. AI encompasses a very broad range of areas. In Azure&amp;#8217;s product offering, it breaks it down into four application areas: Machine Learning, Computer Vision, Natural language processing and conversational AI. Note that the media, sometimes including tech companies, tend to use the terms AI and ML interchangeably, which is incorrect. ML did not really surface as a key&amp;nbsp; driver of AI commercially, until the last 10 &amp;#8211; 15 years. However, other areas of AI, such as computer vision and natural language processing had been around for a quite a while.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/602/1*BsywfzDEONVG2vANwIKriQ.png" alt="Image for post"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we know the distinction between AI and ML: ML is just one of the many areas of AI but it has recently become the most attention-grabbing and cutting-edge area. We will introduce ML the last.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Computer Vision&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Computer Vision is the ability of software to interpret the world visually through cameras, video and images. It has the following application scenarios:&lt;/p&gt;&#10;&lt;ol class="wp-block-list" type="1"&gt;&lt;li&gt;Image Classification: training ML model to classify images based on contents.&lt;/li&gt;&lt;li&gt;Object Detection: training ML model to classify individual objects within an image, and identify their location with bounding box.&lt;/li&gt;&lt;li&gt;Semantic Segmentation: An advanced ML technique in which individual pixels in the image are classified according to the object to which they belong. This forms mask layer&lt;/li&gt;&lt;li&gt;Image Analysis: extract information from images&lt;/li&gt;&lt;li&gt;Face detection, analysis, and recognition: specialized form of object detection that locates human face in image. This can be combined with classification and facial geometry analysis techniques to infer details such as gender, age, and emotional state. Face detection is impaired by extreme angles.&lt;/li&gt;&lt;li&gt;OCR: detect and read text in images.&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The model training process is an iterative process in which the Custom Vision service repeatedly trains the model using some of the data, but holds some back to evaluate the model. The evaluation metrics include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;precision&lt;/strong&gt;: what percentage of the class predictions made by the model were correct. E.g. model predicts 10 images are oranges. 8 actually are. Precision = 0.8&lt;/li&gt;&lt;li&gt;&lt;strong&gt;recall&lt;/strong&gt;: what percentage of class predictions did the model correctly identify. E.g. 10 images of apples, the model find 7. recall = 0.7&lt;/li&gt;&lt;li&gt;&lt;strong&gt;AP (average precision)&lt;/strong&gt;: an overall metric that takes into account both precision and recall.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Azure-specific&lt;/strong&gt;: in Azure, computer vision services include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Computer Vision: analyze images and video, and extract descriptions, tags, objects and text;&lt;/li&gt;&lt;li&gt;Custom Vision: train custom image classification (two special form: celebrity and landscape) and object detection models using your own image;&lt;/li&gt;&lt;li&gt;Face: build face detection and facial recognition solutions&lt;/li&gt;&lt;li&gt;Form recognizer: extract information from scanned forms and invoices&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Azure-specific&lt;/strong&gt;: difference between Computer Vision and Cognitive Service&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Computer Vision: A specific resource for the computer vision services. Use this type of resource if you don&amp;#8217;t intend to use any other cognitive services. Or if you want to track utilization and costs for your computer vision resource separately&lt;/li&gt;&lt;li&gt;Cognitive Service: A general cognitive service resource that include Computer Vision along with many other cognitive services, such as Text Analytics, Translator Text, and others. Use this resource type if you plan to use multiple cognitive services and want to simplify administration and development.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Natural Language Processing&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NLP is the ability of computer to interpret written or spoken language, and respond in kind.&lt;/p&gt;&#10;&lt;ol class="wp-block-list" type="1"&gt;&lt;li&gt;Analyze text&lt;/li&gt;&lt;li&gt;Recognize (speech-to-text api) and synthesize speech (text-to-speech api to generate spoken output)&lt;/li&gt;&lt;li&gt;Translate text and speech&lt;/li&gt;&lt;li&gt;Language understanding&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Models that you use to accomplish speech recognition:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;acoustic model &lt;/strong&gt;&amp;#8211; converts audio signal into phonemes (representations of specific sounds)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;language model&lt;/strong&gt; &amp;#8211; maps phonemes to words, usually using a statistical algorithm that predicts the most probable sequence of words based on the phonemes&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Core concepts in language understanding&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;utterance &lt;/strong&gt;&amp;#8211; an example of something a user might say, and your application must interpret. Eg. Switch the fan on. Turn on the light.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;entities &lt;/strong&gt;&amp;#8211; an item to which an utterance refers. e.g. fan, light. four types of entities: machine-learned, list, regex, pattern.any&lt;/li&gt;&lt;li&gt;&lt;strong&gt;intents &lt;/strong&gt;&amp;#8211; represents the purpose, or goal, expressed in user&amp;#8217;s utterance.&amp;nbsp; E.g. Turn on&lt;/li&gt;&lt;li&gt;None intent&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Azure-specific&lt;/strong&gt;: To create a language understanding application:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;First you must define entities, intents, and utterances with which to train the language model &amp;#8212; referred to as authoring the model&lt;/li&gt;&lt;li&gt;Then you must publish the model so that client applications can use it for intent and entity prediction based on user input&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Azure-specific&lt;/strong&gt;: in Azure, NLP services include&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Text Analytics: analyze text documents and extract key phrases, detect entities (places, people, dates), and evaluate sentiment (positive, negative). mixed language or ambiguous content will produce &amp;#8220;NaN&amp;#8221; in the result.&lt;/li&gt;&lt;li&gt;Translator Text: translate text between languages&lt;/li&gt;&lt;li&gt;Speech: recognize and synthesize speech, and translate spoken language&lt;/li&gt;&lt;li&gt;Language Understanding Intelligent Service (LUIS): train a language model that can understand spoken or text-based commands.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Conversational AI&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is the capability of a software agent to participate in a conversation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Azure-specific&lt;/strong&gt;: Azure Bot service is a platform for creating, publishing and managing bots. Developers can use the Bot Framework to create a bot and manage it with Azure Bot service &amp;#8211; integrating back-end services like QnA maker and LUIS, and connecting to channels for web chat. QnA Maker enables you to quickly build a knowledge base of questions and answers that can form the basis of a dialog between a human and an AI agent.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Machine Learning&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The reason I put machine learning the last, is because it&amp;#8217;s most important, and it involves some brain-burning mathematical details. Machine learning is a technique that uses mathematics and statistics to create a model that can predict unknown values. Machine learning is based on huge volumes of data. Data scientist can use all of that data to train machine learning models that can make predictions and inferences based on the relationships they find in the data.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/602/1*AKj4EVilSh4X2tnE_EmCiw.png" alt="Image for post"/&gt;&lt;figcaption&gt;Machine learning algorithms&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are so many sub-areas of machine learning and Google has an entire &lt;a href="https://developers.google.com/machine-learning/crash-course"&gt;crash course&lt;/a&gt; for it. As far as application is concerned, we need to first match a new problem with an existing problem, and from there pick an appropriate algorithm. There is a &lt;a href="https://docs.microsoft.com/en-us/azure/machine-learning/algorithm-cheat-sheet"&gt;cheat sheet&lt;/a&gt; for such purpose for Azure services. For learning purpose, we should focus on three problems. Before getting to that, we need to first distinguish supervised learning and unsupervised learning:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Supervised learning&lt;/strong&gt;: you train the machine using data which is well &amp;#8220;labelled&amp;#8221;. So some data is already tagged with correct answer. A supervised learning algorithm learns from labelled training data, and helps you predict outcomes for unforeseen data. Two typical types of supervised learning techniques are classification, and regression.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Unsupervised learning&lt;/strong&gt;: you do not need to supervise the model. Instead, you need to allow the model to work on its own to discover information by dealing with unlabeled data. Typical unsupervised learning technique is clustering. It mainly deals with finding a structure or pattern in a collection of uncategorized data.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So the three problem we are going to focus on are:&lt;/p&gt;&#10;&lt;ol class="wp-block-list" type="1"&gt;&lt;li&gt;&lt;strong&gt;Regression Model&lt;/strong&gt; (supervised): Use historic data to train the model to predict the numerical value&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Classification Model&lt;/strong&gt; (supervised): fit the features into the model and predict the classification of the label&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Clustering Model&lt;/strong&gt; (unsupervised):&amp;nbsp; you don&amp;#8217;t have a label to predict. you only have features. You have to group similar items into clusters based on features.&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we will review how to assess the learning model in each technique:&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Performance Metrics for Regression Model&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In all of the equations below, &lt;em&gt;p&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt; denotes predicted value, &lt;em&gt;a&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt; denotes actual value, and ā denotes the mean of actual values.&lt;/p&gt;&#10;&lt;ol class="wp-block-list" type="1"&gt;&lt;li&gt;Mean Absolute Error (MAE):&lt;/li&gt;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="235" height="90" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-2.webp" alt="" class="wp-image-2028"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;It has the same unit with original data so it only can be used to compare models whose errors are measured in the same unit.&lt;/li&gt;&lt;li&gt;It has similar magnitude as RMSE (as will discuss below), but smaller in value&lt;/li&gt;&lt;li&gt;The lower this value is, the better the model is predicting.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;2. Mean Square Error (MSE):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="260" height="100" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-3.webp" alt="" class="wp-image-2029"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;3. Root Mean Square Error (RMSE):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="302" height="109" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-4.webp" alt="" class="wp-image-2030"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;It measures the error rate of a regression model&lt;/li&gt;&lt;li&gt;It can only be compared between models whos errors are measured in the same unit.&lt;/li&gt;&lt;li&gt;RMSE and SD (standard deviation) have similar (not same) formula yet different purposes. SD measures the spread of data around the mean. RMSE measures the error of prediction (predicted vs true). The two formula produce the same result only if you use the mean as prediction.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;4. Relative Square Error (RSE): &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="246" height="144" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-5.webp" alt="" class="wp-image-2031"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;A relative metric between 0 and 1. It has no units so can be used to compare models whose errors are measured in different units.&lt;/li&gt;&lt;li&gt;The closer to 0 this metric is, the better the model is performing&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;5. Relative Absolute Error (RAE):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="238" height="140" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-6.webp" alt="" class="wp-image-2033"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;A relative metric between 0 and 1.&amp;nbsp; It has no units so can be used to compare models whose errors are measured in different units.&lt;/li&gt;&lt;li&gt;The closer to 0 this metric is, the better the model is performing&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;6. Coefficient of determination (R&lt;sup&gt;2&lt;/sup&gt;):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="283" height="335" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-7.webp" alt="" class="wp-image-2038"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Also known as r-squared. It summarizes the explanatory power of the regression model. In other words, &lt;span style="text-decoration: underline;"&gt;how much of the variance between predicted and actual values is explained by the model&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;It is computed from the sums-of-squares terms, including &lt;em&gt;&lt;strong&gt;Sum of Squares Total (SST)&lt;/strong&gt;&lt;/em&gt;, &lt;strong&gt;&lt;em&gt;Sum of Squares Regression (SSR)&lt;/em&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;em&gt;Sum of Squares Error (SSE)&lt;/em&gt;&lt;/strong&gt;, as illustrated above.&lt;/li&gt;&lt;li&gt;R2 describes the proportion of variance of the dependent variable explained by the regression model&lt;/li&gt;&lt;li&gt;The closer to 1 this value is, the better the model is performing. If the regression model is perfect, SSE = 0, R&lt;sup&gt;2&lt;/sup&gt; = 1&lt;/li&gt;&lt;li&gt;If the regression is a total failure, SSE=SST, no variance is explained by regression, and R&lt;sup&gt;2&lt;/sup&gt; = 0&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Performance Metrics for Classification Model&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we review the metrics for classification model. Credit to &lt;a href="https://medium.com/swlh/recall-precision-f1-roc-auc-and-everything-542aedf322b9" class="rank-math-link"&gt;this &lt;/a&gt;positing. Let&amp;#8217;s go start with some classification result, more famously known as confusion matrix:&lt;/p&gt;&#10;&lt;figure class="wp-block-table aligncenter"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;classified as negative&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;classified as positive&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;actually negative&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;TN=9000&lt;/td&gt;&lt;td&gt;FP=700&lt;/td&gt;&lt;td&gt;9700 are actually negative (TN+FP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;actually positive&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;FN=200&lt;/td&gt;&lt;td&gt;TP=100&lt;/td&gt;&lt;td&gt;300 are actually positive (FN+TP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;9100 classified correctly (TN+TP)&lt;/td&gt;&lt;td&gt;900 classified incorrectly (FN+FP)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption&gt;Suppose threshold=0.5&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that what a classification model predicts is the probability for each possible class. In the case of binary classification model, we can set a &lt;strong&gt;&lt;em&gt;threshold &lt;/em&gt;&lt;/strong&gt;(e.g. 0.5), such that predictions greater than 0.5 indicates positive, otherwise negative. So for each classification result, a changing threshold would change each value in the quadrant. &lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Accuracy: &lt;/li&gt;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="290" height="58" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-8.webp" alt="" class="wp-image-2047"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;The ratio of correct predictions (true) to the total number of predictions.&lt;/li&gt;&lt;li&gt;Indicates out of all the predictions, how much are identified correctly by the model&lt;/li&gt;&lt;li&gt;This metric is intuitive but not very useful (e.g. 3% of population is diabetic, then a model that always predicts false would be 97% accurate&amp;#8230;) so data scientists use other metrics like precision and recall to assess classification model performance&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;2. Precision: &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="174" height="58" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-9.webp" alt="" class="wp-image-2048"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;The fraction of positive cases correctly identified.&lt;/li&gt;&lt;li&gt;Indicates out of all the positive predictions, how much are actually true case.&lt;/li&gt;&lt;li&gt;In other words, in your catch, what percent are actually a problem.&lt;/li&gt;&lt;li&gt;This is much more useful than accuracy. Example: out of all the cases identified as diabetics, the rate of correct identifications. &lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;3. Recall: &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="185" height="62" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-10.webp" alt="" class="wp-image-2049"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;The fraction of the cases classified as positive that are actually positive&lt;/li&gt;&lt;li&gt;Indicates out of all the positive cases, how much are identified by the model&lt;/li&gt;&lt;li&gt;Also known as &lt;strong&gt;true positive rate (TPR)&lt;/strong&gt;; and is also much more useful than accuracy. Example: out of all the real diabetics cases, the rate of the ones correctly identified by model.&lt;/li&gt;&lt;li&gt;In other words, what percent of the problem did the model catch.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Going over the example data in the confusion matrix, Accuracy=0.91 , Precision=0.125, Recall=0.333 and now you see how useless accuracy is. The more uneven the class distribution is, the less useful accuracy is.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;4. F1 score:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="319" height="93" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-11.webp" alt="" class="wp-image-2061"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;F1 score combines Recall and Precision to one performance metrics with weighted average. &lt;/li&gt;&lt;li&gt;So it takes both false positives (the problems the model caught wrong) and false negatives (the problems the model failed to catch) into account.&lt;/li&gt;&lt;li&gt;F1 is useful because you always have to use both Recall and Precision. &lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;5. In addition, there is a metric called FPR (false positive rate) in compliment to TPR (recall):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="200" height="59" src="https://www.digihunch.com/wp-content/uploads/2021/01/image-12.webp" alt="" class="wp-image-2064"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;It indicates what percent in the catch did the model get wrong.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We learned that both recall and precision needs to be looked at when assessing a classification model. Unfortunately, Precision and Recall are often in tension: improving one typically reduces the other. We&amp;#8217;ve also learned that, those performance metrics are different as threshold changes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;6. ROC curve: to summarize performance over all possible thresholds, we introduce the ROC curve. The name ROC (Receiver Operating Characteristics) stems historically from communications theory. The ROC curve is created by plotting the TRP against the FPR, at different thresholds. It indicates how well your classification model can separate positive and negative examples and to identify the best threshold for separating them.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/700/0*1obrcm_IUzav-hnr.gif" alt="Image for post"/&gt;&lt;figcaption&gt;ROC curve plotting for each T (threshold) value&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is the result of the ROC curve&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/640/0*ROnZr_WvzJ7zpoXH.png" alt="Image for post"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;7. AUC (Area Under the Curve)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The model performance is determined by looking at the area under the ROC curve (aka AUC), which can range from 0 to 1. The larger the AUC, the better the model is performing. An excellent model has AUC near 1.0, indicating a great ability to separate positive from negative, as opposed to random guessing (coin flipping):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/640/0*Hy9S987XTAgTz5NU.png" alt="Image for post"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;That&amp;#8217;s it for classification&amp;#8230;&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Performance Metrics for Clustering Model&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Evaluating a clustering model is difficult by the fact that there are no previously known true values for the cluster assignments. A successful clustering model is defined as one that achieves a good level of separation between the items in each cluster, so we need metrics to help us measure that separation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A common clustering algorithm is &lt;a href="https://towardsdatascience.com/understanding-k-means-clustering-in-machine-learning-6a6e67336aa1" class="rank-math-link"&gt;K-Means&lt;/a&gt; Clustering. Below are some measurements:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Average Distance to Other Centre: indicates how close, on average, each point in the cluster is to the centroids of all other clusters.&lt;/li&gt;&lt;li&gt;Average Distance to Cluster Centre: indicates how close, on average, each point in the cluster is to the centroid of the cluster.&lt;/li&gt;&lt;li&gt;Number of Points: the number of points assigned to the cluster.&lt;/li&gt;&lt;li&gt;Maximal Distance to Cluster Centre: the maximum of the distances between each point and the centroid of that point’s cluster. If this number is high, the cluster may be widely dispersed. This statistic in combination with the Average Distance to Cluster Center helps you determine the cluster’s spread.&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Azure-specific: Azure Machine Learning service provides cloud-based platform for creating, managing and publishing machine learning models. including:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;automated machine learning&lt;/li&gt;&lt;li&gt;Azure machine learning designer (no-code development environment)&lt;/li&gt;&lt;li&gt;Data and computer management&lt;/li&gt;&lt;li&gt;Pipelines: to orchestrate model training, deployment and manage tasks.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here&amp;#8217;s what pipelines typically look like:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="651" height="666" src="https://www.digihunch.com/wp-content/uploads/2021/01/regression.webp" alt="" class="wp-image-2075"/&gt;&lt;figcaption&gt;Regression pipeline&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="644" height="546" src="https://www.digihunch.com/wp-content/uploads/2021/01/classification.webp" alt="" class="wp-image-2073"/&gt;&lt;figcaption&gt;classification pipeline&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="640" height="755" src="https://www.digihunch.com/wp-content/uploads/2021/01/clustering.webp" alt="" class="wp-image-2074"/&gt;&lt;figcaption&gt;Clustering model&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Machine Learning studio (ml.azure.com) provides a more focused UI for managing workspace resources. The following kinds of compute resources can be used to train models:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;compute instances: development workstation that data scientist can use to work with data and models&lt;/li&gt;&lt;li&gt;compute clusters: scalable cluster of VMs for on-demand processing of experiment code&lt;/li&gt;&lt;li&gt;inference clusters: deployment targets for predictive services that use your trained models&lt;/li&gt;&lt;li&gt;attached computer: links to existing azure compute resources, such as VMs or data-bricks clusters&lt;/li&gt;&lt;/ul&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/01/blockchain-and-di-fi/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Blockchain and DeFi&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/02/basic-resource-object-in-kubernetes-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;Basic Resource Object in Kubernetes 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Blockchain and DeFi</title><link>https://www.digihunch.com/2021/01/blockchain-and-di-fi/</link><pubDate>Sat, 23 Jan 2021 11:30:00 -0400</pubDate><guid>https://www.digihunch.com/2021/01/blockchain-and-di-fi/</guid><description>&lt;h3 class="wp-block-heading"&gt;Background&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is a reading note for book &amp;#8220;&lt;a href="https://landing.coingecko.com/how-to-defi/" class="rank-math-link"&gt;How to DeFi&lt;/a&gt;&amp;#8220;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When Tom hand in $20 to Jack in exchange of goods. Both Tom and Jack has to agree that the greenback with $20 sign actually is worth the value of the goods. Since the paper money is signed by some big shot from the central banker, which they both trust, they can therefore reach consensus on the value of that paper. The central bank acts as a centralized body of trust. Now you&amp;#8217;d ask what if the central banker cheats on us? As long as we centralize our trust to a single body, we have to worry about the centralized trust deteriorate. This is what decentralized finance aspires to address.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The other background problem is the centralized payment and clearance system. When you need to send money from one country to another, there are not only high fees involved, but also days holding for clearance. This is another opportunity for decentralized finance.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Decentralized Finance (DeFi)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The technologies in DeFi falls under three categories based on the level of decentralization:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Centralized: custodial, uses centralized price feeds, centrally determined interest rates&lt;/li&gt;&lt;li&gt;Semi-Decentralized: non-custodial, decentralized price feeds&lt;/li&gt;&lt;li&gt;Completely Decentralized: every component is decentralized. &lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Most DeFi dapps are sitting in the semi-decentralized category. There is no DeFi protocol that is completely decentralized yet. DeFi involves protocols that covers financial sectors such as Lending &amp;amp; Borrowing, Exchanges, Derivatives, Lottery, Payments, Insurance, etc. This all sounds futurism, but there are a few protocols already at play.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Ethereum&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The majority of the DeFi Dapps are built on the Ethereum blockchain, a global, open-source platform for decentralized applications. You can think of it as a world computer that cannot be shutdown. Developers can also deploy smart contracts to the Ethereum network, where it will run 24/7. Smart Contract is a programmable contract that allows two counterparties to set conditions of a transaction without needing to trust another third party for the execution. Whenever a certain condition is fulfilled, the smart contract will carry out the operation as programmed, and the process is transparent to all involved parties, bypassing the need for a trusted third party intermediary.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ether is the native currency of the Ethereum blockchain so Ether is similar to Bitcoin. Ether is also used to pay for the fee that allows smart contracts and Dapps to run on the Ethereum network. Ether is also evolving to become its own unique reserve currency and store of value.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Dapps (decentralized applications) are interfaces that interact with the blockchain through the use of smart contracts. On Ethereum, all transactions and smart contract executions require a small fee to be paid. The fee is called Gas. In technical terms, Gas refers to the unit of measure on the amount of computational effort required to execute an operation or a smart contract. Gas fees are paied entirely in ETH. The price of gas can fluctuate from time to time depending on the network demand.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ethereum can also be used for two other functions: creating DAO (Decentralized Autonomous Organization), or issuing other cryptocurrencies. A DAO is a fully autonomous organization which is not governed by a single person but is instead governed through code. This code is based on smart contracts and enables DAOs to replace how traditional organizations are typically run. As it runs on code, it would be protected from human intervention and will operate transparently. Governance decisions or rulings would be decided via DAO token voting. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are currently two popular protocols for tokens on the Ethereum Network: ERC-20 and ERC-721&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A wallet is a user-friendly interface to the blockchain network. It manages your private keys, which are basically keys to the lock on your cryptocurrencies&amp;#8217; vault. Wallets allow you to receive, store and send cryptocurrencies.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Custodial wallets are wallets where third-parties keep and maintain control over your cryptocurrencies on your behalf. By using a custodial wallet, you trust an external party to store your coins safely. However, by trusting a third party with your cryptocurrencies, you open yourself up to the risk of the custodian losing your cryptocurrencies through mismanagement or hacks (Mt. Gox)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Non-custodial wallets are wallets where you take full control and ownership of your cryptocurrencies. By using a non-custodial wallet, you trust no external party and only yourself to ensure safe storage. However, you pass the burden of security to yourself and you have to be fully equipped to store your private keys safely. If you lose your private keys, you will lose access to your cryptocurrencies too. Example of non-custodial wallet: Argent&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Stablecoins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Stablecoins are pegged to other stable assets such as the USD. The top 5 cryptocurrency stablecoins as of Feb 2020 are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Tether (USDT)&lt;/li&gt;&lt;li&gt;USD Coin (USDC)&lt;/li&gt;&lt;li&gt;Paxos Standard (PAX)&lt;/li&gt;&lt;li&gt;True USD(TUSD)&lt;/li&gt;&lt;li&gt;Dai (DAI)&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Not all stablecoins are the same as they employ different mechanisms to keep their peg against USD. There are two types of pegs: fiat-collateralized (e.g. USDT)and crypto-collateralized (e.g. DAI). Most stablecoins are the former.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;USDT pegs itself to $1 by maintaining reserves of $1 per Tether token minted. While Tether is the largest and most widely used USD stablecoin, Thether reserves are kept in financial institutions and users will have to trust Tether as an entity to actually have the reserved amounts that they claim. Tether is therefore a centralized, fiat-collateralized stablecoin.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Dai (DAI) on the other hand, is collateralized using cryptocurrencies such as Ethereum (ETH). Its value is pegged to $1 through protocols voted on by a decentralized autonomous organization and smart contracts. At any given time, the collateral to generate DAI can be easily validated by users. DAI is a decentralized, crypto-collateralized stablecoin.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DAI has a smaller market capitalization but is increasing tremendously. DAI is the native stablecoin used most widely in the DeFi ecosystem. It is the preferred USD stablecoin used in DeFi trading, lending and more. DAI operates on Maker, a smart-contract platform that runs on the Ethereum blockchain and has three tokens: &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Sai &amp;#8211; aka Single Collateral Dai, backed only by Ether(ETH) as collateral. It is legacy Dai, and will be phased out.&lt;/li&gt;&lt;li&gt;Dai &amp;#8211; aka multi-collateral Dai. Currently backed by Ether (ETH) and Basic Attention Token (BAT) as collaterals with plans to add other assets&lt;/li&gt;&lt;li&gt;Maker (MKR): is Maker&amp;#8217;s governance token and users can use it to vote for improvements on the Maker platform via the Maker Improvement Proposals. Maker is a type of DAO.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;MKR holders have voting rights proportional to the amount of MKR tokens they own in the DAO and can vote on parameters governing the Maker Protocol. The parameters that MKR holder vote on are vital in keeping the ecosystem healthy, which in turn helps ensure that Dai remains pegged to $1.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The amount of Dai that can be minted is dependent on the collateral ratio (150% worth of ETH or BAT to mint Dai). There is a stability fee and Dai Saving Rate (DSR).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Lending and Borrowing&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Compound Fiannce is an Ethereum-based open-source money market protocol where anyone can supply or borrow cryptocurrencies frictionlessly. Many tokens (BAT, ETH, USDC, DAI, and more) can be supplied or used as collateral on the Compound platform.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Compound operates as a liquidity pool that is built on the Ethereum blockchain. Suppliers supply asset to the pool and earn interest, while borrowers take a loan from the pool and pay interest on their debt. In essense, Compound bridges the gaps between the lenders who wish to accrue interest from idle funds and borrowers who wish to borrow funds for productive or investment use. Suppliers and Borrowers interact directly with the protocol for interest rate.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Anyone with a supported cryptocurrency wallet such as Argent can start using Compound immediately. To earn interest, you have to supply assets to the protocol. Once you have deposited your asset into Compound, you will immediately begin to earn interest on the assets you have put in. Upon deposit, you will receive corresponding amounts of cTokens. If you supply DAI, you will receive cDAI. If you supply Ether, you will receive cETH. Interest is not immediately distributed to you, but rather accrues on the cTokens which you now hold and are redeemable for the underlying asset and interest it represents. cTokens represent your balance in the protocol. cTokens become convertible into an increasing amount of the underlying asset it represents over time.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you want to borrow, you have to first supply assets into the system as collateral for your loan. Borrowed assets are sent directly to your Ethereum wallet and from there you can use them. Do not that borrowing incurs a small fee of 0.025% to avoid spams and misuse of the Compound protocol&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;DEX (Decentralized Exchanges)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Uniswap Exchange is a decentralized token exchange protocol built on Ethereum that allows direct swapping of tokens without the need to use a centralized exchange. On Uniswap, you can simply swap your tokens directly from your wallet without having to go through centralized exchange.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;dYdX is a decentralized exchange protocol for lending, borrowing and margein/leveraged trading. It supports ETH, USDC, and DAI. You can enter either short or long positions with leverages up to 5x.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Other Use Cases &lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Derivatives&lt;/strong&gt;: A derivative is a contract whose value is derived from another underlying asset such as stocks, commodities, currencies, indexes, bonds, or interest rates. There are several types of derivatives such as futures, options and swaps, each serving a different purpose. In DeFi, the biggest derivative protocol is Synthetix&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Fund Management&lt;/strong&gt;: In DeFi, fund management is conducted in a manner where it removes the investment manager and lets you choose the asset management strategy that best suits your financial need. TokenSets is a platform that allows crypto users to buy Strategy Enabled Tokens&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Payments&lt;/strong&gt;: Lighting Network, Request Network, xDai and Sablier&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Insurance&lt;/strong&gt;: Nexus Mutual is a decentralized insurance protocol built on Ethereum that currently offers cover on any smart contract on the Ethereum blockchain.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Dashboard&lt;/strong&gt;: a dashboard is a simple platform that aggregates all your DeFi activities in one place.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/01/basic-kubernetes-resource-object-1-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Basic Resource Object in Kubernetes 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/01/a-shallow-dive-into-artificial-intelligence/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next 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;/nav&gt;&#10;</description></item><item><title>Basic Resource Object in Kubernetes 1 of 2</title><link>https://www.digihunch.com/2021/01/basic-kubernetes-resource-object-1-of-2/</link><pubDate>Sat, 16 Jan 2021 22:13:00 -0400</pubDate><guid>https://www.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://raw.githubusercontent.com/kubernetes/community/main/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://www.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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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 create new Pods. 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://raw.githubusercontent.com/kubernetes/community/main/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 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://raw.githubusercontent.com/kubernetes/community/main/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://www.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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/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://raw.githubusercontent.com/kubernetes/community/main/icons/png/resources/labeled/sts-128.png" 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://raw.githubusercontent.com/kubernetes/community/main/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 allow 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://www.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://www.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><item><title>High Performance Computing</title><link>https://www.digihunch.com/2020/12/high-performance-computing-cluster/</link><pubDate>Fri, 11 Dec 2020 23:42:00 -0400</pubDate><guid>https://www.digihunch.com/2020/12/high-performance-computing-cluster/</guid><description>&lt;h3 class="wp-block-heading" id="h-overview"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;High Performance Computing (HPC) has recently been commoditized with the advent of commodity server hardware (x86 server), virtualization technology and cloud delivery model. It is common in specialized industries where intensive computing tasks are required, for example:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;HCL (healthcare and life science): drug discovery, computer aided diagnosis (CAD), genome engineering; &lt;/li&gt;&#10;&lt;li&gt;CAD, CAE, CAM (computer aided design, engineering, and manufacturing): 3D modeling, computational fluid dynamics (CFD), finite element analysis (FEA), structural mechanical design, etc &lt;/li&gt;&#10;&lt;li&gt;Finance: portfolio management, automated trading, risk analysis&lt;/li&gt;&#10;&lt;li&gt;Geoscience and geo-engineering: oil and gas exploration, geographic data, weather forecasting;&lt;/li&gt;&#10;&lt;li&gt;Scientific computation&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Computing performance is measured in FLOPS (floating point operations per second) and is usually delivered in a cluster to aggregate the computing power from a number of networked nodes. This is referred to as an HPC cluster. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-hardware-stack"&gt;Hardware stack&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An HPC cluster features the following components:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Head node (aka master node or login node): a gateway and coordinator; head node may be broken into several nodes&lt;/li&gt;&#10;&lt;li&gt;Compute node (worker node): the executor of jobs; the compute node can either be homogenous or heterogeneous, for different purposes. the number of compute nodes can be quite large&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are four common form factors for server: tower, rack-mount, blade, mainframe. Traditionally, the nodes are rack-mount 1U &amp;#8220;pizza box&amp;#8221; servers. Bladed systems started to replace due to the increased node density, thanks to the shared/redundant power and cooling management. In the past, the HPC cluster is operated in data centres, which is an expensive operation item. In the last decades, many organizations extends their compute workload to the cloud, forming a hybrid model.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;HPC typically has specialized storage system because HPC applications notoriously create large amounts of data. NFS traditionally does not scale well as number of node increases. Some proprietary storage system such as Isilon provides good performance via NFS protocol. There are also open-source parallel file system such as Lustre and HDFS. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;HPC networking handles three types of traffic:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;computation traffic between compute nodes (if the compute nodes interact with each other)&lt;/li&gt;&#10;&lt;li&gt;file system traffic: for compute nodes to read and write on file system (e.g. NFS)&lt;/li&gt;&#10;&lt;li&gt;administrative traffic: fairly light compared to the two above&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For that, many HPC runs two networks, a private (backend) network and a public (frontend) network. Backend network must be high speed and low latency, typically in the form of 10Gig Ethernet, or InfiniBand.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-software-stack"&gt;Software stack&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the software layer, the core functionality is &amp;nbsp;&lt;strong&gt;Message Passing Interface (MPI)&lt;/strong&gt;, a specification for the developers and users of message passing libraries. MPI constitutes a standardized and portable message-passing system which consists of a library and a protocol to support parallel computing. MPI enables passing information between various nodes of a HPC cluster or between particular clusters, and has different implementations that provide the libraries to run HPC applications in a distributed manner across different physical nodes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the operation, user submits a job through head node in order to request the resource. User needs to specify the resources for the job (e.g. how many CPU cores, how much memory, etc). The head node runs a scheduler to allocate computing resource based on pre-defined policies, based on priority of jobs, availability of resources, distribution of load, etc. Depending on the nature of the computing jobs, the nodes participating in the task may or may not communicate with one another. If they do need to talk to each other, the program must support it. Such program can be called a cluster program, and the MPI (message passing interface) library greatly facilitates the development of such program. The sub-jobs communicating with each other also creates a considerable amount of network traffic within the cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cluster software ties all nodes in the cluster together. It turns raw hardware into a functioning cluster by provisioning (installing and configuring) the head nodes. Compute nodes can usually be added or removed dynamically therefore the head nodes should be able to provision compute nodes, and administer cluster, leaving the programming as the job for the user to complete. As mentioned, in parallel programming, the most important HPC tool is MPI (Message Passing Interface), which allows programs to talk to one another over cluster networks. There are both open (e.g. &lt;a href="https://www.open-mpi.org/" class="rank-math-link"&gt;Open MPI&lt;/a&gt;) and commercial MPI (e.g. &lt;a href="https://docs.microsoft.com/en-us/message-passing-interface/microsoft-mpi" class="rank-math-link"&gt;Microsoft MPI&lt;/a&gt;) versions. Cluster software should also provide compilers, debuggers, and profilers in addition to MPI.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are cluster software in both Linux and Windows operating systems: &lt;a href="http://www.rocksclusters.org/" class="rank-math-link"&gt;Rocks Clusters&lt;/a&gt;, &lt;a href="https://oscar-cluster.github.io/oscar/" class="rank-math-link"&gt;Oscar &lt;/a&gt;(Open Source Clusters Application Resources), Red Hat HPC solution, &lt;a href="https://docs.microsoft.com/en-us/powershell/high-performance-computing/overview?view=hpc19-ps" class="rank-math-link"&gt;Microsoft HPC pack&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/parallelcluster/latest/ug/what-is-aws-parallelcluster.html" class="rank-math-link"&gt;AWS Parallel Cluster&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-implementation"&gt;Implementation&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a class="rank-math-link" href="https://www.webmo.net/support/pdf/byoc.pdf"&gt;Here&lt;/a&gt; is an example of setting up HPC cluster with CentOS. Despite of the well documented steps, note that the author of the document refers to HPC cluster simply as cluster, which is ambiguous. There are&lt;strong&gt; three basic motivators for creating a cluster&lt;/strong&gt;: high performance computing (HPC), network traffic load balancing, and service resilience in the form of high availability (HA). The author should be specific in the &lt;a class="rank-math-link" href="https://www.webmo.net/support/pdf/byoc-centos7.pdf"&gt;document&lt;/a&gt; about the HPC cluster. If RDMA (Infiniband) network is involved, a configuration guide is provided in RedHat literature.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://aws.amazon.com/getting-started/hands-on/deploy-elastic-hpc-cluster/" class="rank-math-link"&gt;Here&lt;/a&gt; is an example of deploying HPC cluster in AWS. &lt;a href="https://docs.microsoft.com/en-us/powershell/high-performance-computing/overview?view=hpc19-ps" class="rank-math-link"&gt;Here&lt;/a&gt; is the guide to deploy HPC pack in Microsoft technologies.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-hpc-and-big-data"&gt;HPC and Big Data&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;HPC and Big Data are two distinctive computing paradigmes. Although there is some signs of convergence and blurred boundaries, it is still a long way before one can treat HPC and Big Data interchangeably. This &lt;a href="https://ieeexplore.ieee.org/document/7776538" class="rank-math-link"&gt;paper&lt;/a&gt; does a phenomenal job in comparing the two paradigms. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The fundamental difference lies in the respective problems they intend to address. HPC focuses on the large computational loads, whereas Big Data targets applications that need to handle very large and complex data sets (usually in the order of multi-terabytes or exabytes). Many scientific data analytics applications are becoming I/O bound in modern systems, such as seismic algorithms, Big Data applications are thus very demanding in terms of storage, to accommodate such a masive amount of data, while HPC is usualy thought more in inters of sheer computational needs. The open-source projects in Big Data also aims to run on conventional hardware to make it easier and less expensive to scale. This is not the main focus of HPC.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So, you can run Big Data (e.g. Hadoop) analytics jobs on HPC gear. On the other hand, you can&amp;#8217;t run HPC jobs on commodity hardware as commonly seen in the Big Data stack. Both HPC and Hadoop analytics use parallel processing of data. In a Hadoop/analytics environment, data is stored on commodity hardware and distributed across multiple nodes of hardware. In HPC, where the size of data file is much greater, data storage in centralized. Also, because of the sheer volume of its files, HPC also requires more expensive networking communications such as Infiniband, because the size of the file it processes require high throughput and low latency.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In BigData job, each query in Hadoop reads data from disk and runs as a separate MapReduce job. Spark enables in-memory iterative processing (through the RDD abstraction), allowing the user to query repeatedly on a dataset without having to perform intermediate disk operations. RDDs are exposed in the Spark API where each dataset is represented as a read-only object, and transformations are invoked using methods on these objects. For an example project, check out &lt;a href="https://www.digihunch.com/2020/09/spark-cassandra-and-python/" class="rank-math-link"&gt;this&lt;/a&gt; post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The underlying software stacks for HPC and Big Data are fundamentally different, mainly due to the differences represent in their target class of applications, as outlined in the diagram below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1598" height="862" src="https://www.digihunch.com/wp-content/uploads/2020/12/image.webp" alt="" class="wp-image-1942"/&gt;&lt;figcaption class="wp-element-caption"&gt;software stack difference between HPC and Big Data&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As to which one is for me, the &lt;a href="https://www.techrepublic.com/article/4-steps-to-implementing-high-performance-computing-for-big-data-processing/" class="rank-math-link"&gt;over-simplified advice&lt;/a&gt; is: if you can avoid HPC and just use Hadoop for your analytics, do it. It is cheaper, easier, and more cloud friendly. However, bear in mind that an all-Hadoop shop is not possible for many industries such as life sciences, weather, pharmaceutical, and academic applications.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/"&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 Python – provision Kubernetes Nodes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/12/ansible-tower-lab-environment-on-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next 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;/nav&gt;&#10;</description></item><item><title>IPVS, iptables and kube-proxy</title><link>https://www.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/</link><pubDate>Tue, 24 Nov 2020 13:17:00 -0400</pubDate><guid>https://www.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/</guid><description>&lt;p class="wp-block-paragraph"&gt;This is an overview of the underlying technologies that drives load balancing. It covers LVS, Netfilter, iptables, IPVS and eventually kube-proxy.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-lvs-linux-virtual-server"&gt;LVS (Linux Virtual Server)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One of the ways to implement software load balancing is via LVS (Linux Virtual Server), as &lt;a href="https://www.digihunch.com/2020/01/several-ways-to-ensure-high-availability/" class="rank-math-link"&gt;previously discussed&lt;/a&gt;. The diagram below shows the LVS &lt;a href="http://www.linuxvirtualserver.org/about.html" class="rank-math-link"&gt;framework&lt;/a&gt;, with IPVS as the fundamental technology:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="304" height="340" src="https://www.digihunch.com/wp-content/uploads/2021/05/lvs.jpeg" alt="" class="wp-image-2262"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The major work of the LVS project is to develop advanced IP load balancing software (IPVS), application-level load balancing software (KTCPVS), cluster management components. &lt;a href="http://www.linuxvirtualserver.org/software/ktcpvs/ktcpvs.html" class="rank-math-link"&gt;KTCPVS &lt;/a&gt;implements application-level load balancing inside the Linux kernel (still under development). &lt;a href="http://www.linuxvirtualserver.org/software/ipvs.html" class="rank-math-link"&gt;IPVS &lt;/a&gt;is an advanced IP load balancing software implemented inside the Linux kernel. The IPVS code was already included into the standard Linux kernel 2.4 and 2.6.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-netfilter"&gt;Netfilter&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both IPVS and iptables (the technology behind Linux firewall, discussed &lt;a href="https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/" class="rank-math-link"&gt;here&lt;/a&gt;) are based on &lt;strong&gt;netfilter&lt;/strong&gt;, a &lt;span style="text-decoration: underline;"&gt;packet-filtering framework&lt;/span&gt; provided by the Linux kernel. In this section, we will discuss them all together, starting with Netfilter and then discuss how iptables and IPVS uses netfilter. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Netfilter allows various networking-related operations to be implemented in the form of customized handlers, by offers various functions and operations for &lt;span style="text-decoration: underline;"&gt;packet filtering&lt;/span&gt;, &lt;span style="text-decoration: underline;"&gt;network address translation&lt;/span&gt;, and &lt;span style="text-decoration: underline;"&gt;port translation&lt;/span&gt;, which provide the functionality required for directing packets through a network and prohibiting packets from reaching sensitive locations within a network. Netfilter represents a set of &lt;strong&gt;hooks&lt;/strong&gt; inside the Linux kernel, allowing specific kernel modules to register &lt;strong&gt;callback&lt;/strong&gt; functions with the kernel&amp;#8217;s networking stack. Those functions, usually applied to the traffic in the form of filtering and modification rules, are called for every packet that traverses the respective hook within the networking stack.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-iptables"&gt;Iptables&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The kernel modules named &lt;strong&gt;ip_tables&lt;/strong&gt;, &lt;strong&gt;ip6_tables&lt;/strong&gt;, &lt;strong&gt;arp_tables &lt;/strong&gt;(the underscore is part of the name), and &lt;strong&gt;ebtables &lt;/strong&gt;comprise the &lt;span style="text-decoration: underline;"&gt;legacy packet filtering portion of the Netfilter hook system&lt;/span&gt;. They provide a table-based system for defining firewall rules that can filter or transform packets. The tables can be administered through the &lt;span style="text-decoration: underline;"&gt;user-space tools&lt;/span&gt; &lt;strong&gt;iptables&lt;/strong&gt;, &lt;strong&gt;ip6tables&lt;/strong&gt;, &lt;strong&gt;arptables&lt;/strong&gt;, and &lt;strong&gt;ebtables&lt;/strong&gt;. &lt;strong&gt;Notice&lt;/strong&gt; that although both the &lt;span style="text-decoration: underline;"&gt;kernel modules&lt;/span&gt; and &lt;span style="text-decoration: underline;"&gt;userspace utilities&lt;/span&gt; have similar names, each of them is a different entity with different functionality.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="306" src="https://www.digihunch.com/wp-content/uploads/2023/01/iptables.jpeg" alt="" class="wp-image-7749" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/iptables.jpeg 1024w, https://www.digihunch.com/wp-content/uploads/2023/01/iptables-300x90.jpeg 300w, https://www.digihunch.com/wp-content/uploads/2023/01/iptables-768x230.jpeg 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When a network packet is received on a network device, it first passes through the &lt;strong&gt;&lt;em&gt;Prerouting &lt;/em&gt;&lt;/strong&gt;hook. This is where the routing decision takes place. The kernel decides whether the packet is destined for a local process (e.g., a listening socket on a server in this system) or whether to forward it (system operates as a router). In the first case, the packet passes the &lt;strong&gt;&lt;em&gt;Input &lt;/em&gt;&lt;/strong&gt;hook and is then handed over to the local process.  If the packet is destined to be forwarded, it traverses the &lt;strong&gt;&lt;em&gt;Forward &lt;/em&gt;&lt;/strong&gt;hook and then a final &lt;strong&gt;&lt;em&gt;Postrouting &lt;/em&gt;&lt;/strong&gt;hook before being sent out on a network device. For packets that are generated locally (e.g., by a client or server process that likes sending things out), they must first pass the &lt;strong&gt;&lt;em&gt;Output &lt;/em&gt;&lt;/strong&gt;hook and then the  &lt;strong&gt;&lt;em&gt;Postrouting &lt;/em&gt;&lt;/strong&gt;hook before being sent out on a network device.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The aforementioned hooks &amp;nbsp;exist independently for the IPv4 and IPv6 protocols. Thus, IPv4 and IPv6 packets each traverse their own hooks. There are also other hooks for ARP packets and for Bridging. And all the &amp;nbsp;hooks exist independently within each network namespace. Additionally, there is an&amp;nbsp;&lt;strong&gt;&lt;em&gt;ingress&amp;nbsp;&lt;/em&gt;&lt;/strong&gt;hook for each network device. The list goes on… More explanations are from &lt;a href="https://www.teldat.com/blog/en/nftables-and-netfilter-hooks-via-linux-kernel/" class="rank-math-link"&gt;here&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture#iptables-rules" class="rank-math-link"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-ipvs"&gt;IPVS&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In LVS, IPVS is also based on netfilter framework, but works only on INPUT chain, by registering ip_vs_in hook function, to process request. IPVS (aka layer-4 switching) runs on a host at the front of a cluster of real servers. It directs requests for TCP/UDP based servers to the real server, while ensuring the resonse from (one or several) real server appears to the client as if they were all from a virtual service on a sigle IP address. It is based on in-kernel hash tables. The userspace utility is ipvsadm.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://i.imgur.com/i60QKw4.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When the client request reaches the kernel space of load balancer, it arrives at PREROUTING chain. Route will determine whether the request packet is for the local host or not, based on the destination address of the packet. The packet is sent to INPUT chain if it is. The ip_vs_in function is hooked to LOCAL_IN and will examine the packet. If it finds a matching IPVS rule, it will (bypass INPUT chain) directly trigger POSTROUTING chain, &lt;strong&gt;skipping &lt;/strong&gt;iptables rules.vThis is discussed in detail &lt;a href="http://www.austintek.com/LVS/LVS-HOWTO/HOWTO/LVS-HOWTO.filter_rules.html" class="rank-math-link"&gt;here&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPVS supports 8 load balancing algorithms (round robin, weighted round robin, least-connection, weighted least connection, locality-based least-connection, locality-based least-connection with replication, destination-hashing, and source-hashing) and 3 packet-forwarding methods (NAT, tunneling and direct routing).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main difference between iptables and IPVS, is &lt;a href="https://www.thegeekstuff.com/2011/01/iptables-fundamentals/"&gt;iptables&lt;/a&gt; includes a number of tables, each with a number of chains, each further involves a number of rules. The total number of rules is large. The packet is assessed against many of such rules. For the same reason, the order of the rule matters. IPVS on the other hand, leverages hash table, with a complexity of O(1), or O(n) in the worst case scenarios. They vary significantly in the efficiency of packet filtering and forwarding, especially when the rules gets complicated. Iptable also presents more latency when adding or removing rules as more rules are involved. This &lt;a href="https://www.slideshare.net/LCChina/scale-kubernetes-to-support-50000-services" class="rank-math-link"&gt;presentation &lt;/a&gt;includes some quantitative comparison.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-kubeproxy"&gt;KubeProxy&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes architecture, &lt;a class="rank-math-link" href="https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/"&gt;KubeProxy &lt;/a&gt;takes care of load balancing. Kube-proxy can run in three modes: userspace, iptables and IPVS. &lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://d33wubrfki0l68.cloudfront.net/e351b830334b8622a700a8da6568cb081c464a9b/13020/images/docs/services-userspace-overview.svg" alt="Services overview diagram for userspace proxy" width="826" height="464"/&gt;&lt;figcaption class="wp-element-caption"&gt;userspace proxy mode&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The userspace mode is old and inefficient. The packet is compared against iptables rule and then forwarded to a pod named kube-Proxy, which operates as an application to forward packet to backend pods.&lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://d33wubrfki0l68.cloudfront.net/27b2978647a8d7bdc2a96b213f0c0d3242ef9ce0/e8c9b/images/docs/services-iptables-overview.svg" alt="Services overview diagram for iptables proxy" width="810" height="601"/&gt;&lt;figcaption class="wp-element-caption"&gt;iptables proxy mode&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The iptables mode is better since it uses the kernel feature of iptables, which is fairly mature. kube-proxy manages iptables rule based on the service yaml of Kubernetes.&lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://d33wubrfki0l68.cloudfront.net/2d3d2b521cf7f9ff83238218dac1c019c270b1ed/9ac5c/images/docs/services-ipvs-overview.svg" alt="Services overview diagram for IPVS proxy" width="810" height="601"/&gt;&lt;figcaption class="wp-element-caption"&gt;IPVS proxy mode&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With the comparison between iptables and IPVS earlier, we can expect that iptables operations slow down dramatically in large scale cluster. Therefore IPVS based kubeproxy was &lt;a class="rank-math-link" href="https://github.com/kubernetes/kubernetes/issues/17470"&gt;brought up&lt;/a&gt;. This &lt;a class="rank-math-link" href="https://speakerdeck.com/sufuf3/ipvs-based-kube-proxy-for-scaled-kubernetes-load-balancing"&gt;presentation &lt;/a&gt;illustrated the differences.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this post we discussed load balancing technologies from ipvs to iptables and then to kube-proxy, which is used in Kubernetes nodes.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How imaging devices talk to each other (in DICOM)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AWS CDK example in Python – provision Kubernetes Nodes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Docker storage</title><link>https://www.digihunch.com/2020/11/docker-storage/</link><pubDate>Tue, 03 Nov 2020 20:22:00 -0400</pubDate><guid>https://www.digihunch.com/2020/11/docker-storage/</guid><description>&lt;p class="wp-block-paragraph"&gt;Microservices are all about stateless and ephemeral workloads, and containers are great microservices. This may suggest that Docker is all about ephemeral storage. In fact, Docker supports both non-persistent and persistent storage, such as database, kafka, etc. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Non-persistent storage is automatically created, alongside the container and is tied to the lifecycle of the container. On Linux system, it is /var/lib/docker/ as part of container. This is referred to as local storage.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker has a concept of volume, which is essentially a file or a directory. Volumes are for persistent data. they are de-coupled from containers and are not tied to the lifecycle of any container. Volume allows process in docker container to bypass the default uionFS, and stores file or directory on host machine. It also allows different containers to share data. You may mount a volume to a container. even if container is deleted, volume persists.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;By default, Docker creates new volumes with the built-in local driver. Local volumes are only available to containers on the node they&amp;#8217;re created on. There are also third-party drivers as plugins that provides advanced options to integrate external storage system with Docker. (NAS, SAN, etc)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are more than 25 volume plugins that you can specify with -d switch, to cover all three categories of storage&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Block storage tends to be high performance and good for small-block random access workloads.&lt;/li&gt;&#10;&lt;li&gt;File storage is high performance, shared amongs multiple containers with NFS or SMB protocols.&lt;/li&gt;&#10;&lt;li&gt;Object storage is good for long term storage of large data blobs that do not change frequently. It is often content addressable and relatively low performance.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that if you share volume with multiple containers, the application needs to worry about data collision.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You may use docker volume create command to create volume. Note that there is no quota management within docker so the partition needs to be managed at operating system level.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Implementation of Volume&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Remember that Docker image is built on multi-layer file system. When we run a container, Docker places a read-write layer on top of the image, such that the active files in running container are all placed in this read-write layer. When container is deleted, so are the files. The file system in Docker is a pseudo file system implemented in unionFS. Volumes bypasses the uionFS and directly accesses the host file system. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we create a Docker volume, Docker places the volume data to /var/lib/docker/volumes and under each directory named after volume, creates a directory _data, which is attached to the corresponding container.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can even mount an NFS volume to container. Reference &lt;a href="https://forums.docker.com/t/nfs-mount-inside-docker-container-bypassing-the-host/77890" class="rank-math-link"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We mentioned UnionFS a couple times so far. UnionFS is a light-weight, layered file system. It can mount the contents of multiple directories to the same directory, to form a single file system. User can use unionFS like a directory. It is the foundation of Docker image and container and enables saving of spaces.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="444" src="https://www.digihunch.com/wp-content/uploads/2024/07/unionfs-1024x444.webp" alt="" class="wp-image-11424" style="width:526px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/unionfs-1024x444.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/07/unionfs-300x130.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/07/unionfs-768x333.webp 768w, https://www.digihunch.com/wp-content/uploads/2024/07/unionfs.webp 1380w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are three common types of union FS: AUFS, DeviceMapper, and OverlayFS.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;AUFS file system&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AUFS is the earliest driver that Docker uses for file system, most common in Ubuntu and Debian. To check if the system support AUFS, check out the documentation &lt;a href="https://docs.docker.com/storage/storagedriver/aufs-driver/" class="rank-math-link"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AUFS is recommended in Ubuntu or Debian. For CentOS and Redhat, it needs to be installed and make sure the command above returns aufs. To configure AUFS, create file /etc/docker/daemon.json and add:&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-java" data-lang="java"&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:#e6db74"&gt;&amp;#34;storage-driver&amp;#34;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#34;aufs&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then restart docker service. Run &amp;#8220;docker info&amp;#8221; and examine the Storage Driver section, as documented &lt;a href="https://docs.docker.com/storage/storagedriver/aufs-driver/" class="rank-math-link"&gt;here&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AUFS layers multiple directories on a single Linux host and presents them as a single directory. These directories are called branches in AUFS terminology, and layers in Docker terminology. The unification process is referred to as a union mount.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="884" height="724" src="https://www.digihunch.com/wp-content/uploads/2024/07/ubuntu-layers.webp" alt="" class="wp-image-11425" style="width:538px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/ubuntu-layers.webp 884w, https://www.digihunch.com/wp-content/uploads/2024/07/ubuntu-layers-300x246.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/07/ubuntu-layers-768x629.webp 768w" sizes="auto, (max-width: 884px) 100vw, 884px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Layers of a Ubuntu container&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt; &lt;a href="https://docs.docker.com/storage/storagedriver/aufs-driver/#example-image-and-container-on-disk-constructs" class="rank-math-link"&gt;This section&lt;/a&gt; describes how the layers work and &lt;a href="https://docs.docker.com/storage/storagedriver/aufs-driver/#how-container-reads-and-writes-work-with-aufs" class="rank-math-link"&gt;this section&lt;/a&gt; describes how it reads and writes files (Copy-on-Write (CoW) strategy to maximize storage efficiency and minimize overhead). CoW characterized AUFS.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AUFS has not been adopted in the Linux kernel mainline for lack of maintainability. So for CentOS, the recommended file system driver is devicemapper.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Devicemapper file system&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Devicemapper is a technical framework to map physical block device to virtual block device, introduced since kernel 2.6.9. So it&amp;#8217;s essentially different from AUFS. The Logical Volume Manager (LVM) in Linux is also implemented based on devicemapper.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The three critical components in devicemapper are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;mapped device: a virtual device that devicemapper provides to client&lt;/li&gt;&#10;&lt;li&gt;target device: the underlying physical device or a section of it.&lt;/li&gt;&#10;&lt;li&gt;map table: keeps track of the offset, range, etc between mapped and target devices.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Devicemapper uses target driver to block, filter, and forward I/O requests (e.g. Raid, encryption, think provisioning, etc). In thin provisioning, storage driver only assigns spaces that are needed. Docker uses snapshot technology in thin provisioning. This &lt;a class="rank-math-link" href="https://docs.docker.com/storage/storagedriver/device-mapper-driver/#how-the-devicemapper-storage-driver-works"&gt;part of the documentation&lt;/a&gt; provides further details as to how device mapper works.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="854" height="1024" src="https://www.digihunch.com/wp-content/uploads/2025/04/ubuntu-busybox-layer-854x1024.webp" alt="" class="wp-image-13114" style="width:539px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/ubuntu-busybox-layer-854x1024.webp 854w, https://www.digihunch.com/wp-content/uploads/2025/04/ubuntu-busybox-layer-250x300.webp 250w, https://www.digihunch.com/wp-content/uploads/2025/04/ubuntu-busybox-layer-768x921.webp 768w, https://www.digihunch.com/wp-content/uploads/2025/04/ubuntu-busybox-layer.webp 1046w" sizes="auto, (max-width: 854px) 100vw, 854px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Ubuntu and busybox image layers&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Devicemapper has to modes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;loop-lvm: in dev and test environment&lt;/li&gt;&#10;&lt;li&gt;direct-lvm: recommended in production&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is the performance &lt;a href="https://docs.docker.com/storage/storagedriver/device-mapper-driver/#device-mapper-and-docker-performance" class="rank-math-link"&gt;best practice&lt;/a&gt;. To configure devicemapper, create /etc/docker/daemon.json file and add:&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-json" data-lang="json"&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;&amp;#34;storage-driver&amp;#34;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#34;devicemapper&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;storage-opts&amp;#34;&lt;/span&gt;:[&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;dm.directlvm_device=/dev/xdf&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;dm.thinp_percent=95&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;dm.thinp_metapercent=1&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;dm.thinp_autoextend_threshold=80&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;dm.thinp_autoextend_percent=20&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;dm.directlvm_device_force=false&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then restart docker service. Run &amp;#8220;docker info&amp;#8221; and examine the Storage Driver section to ensure direct-lvm mode is on. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since devicemapper uses block device to store files, it is faster than directly operate on file system. It is adopted as default driver as unionFS for a long time, ensuring stable performance under Red Hat and CentOS.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;OverlayFS file system&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Earlier versions of OverlayFS (known as overlay driver) is not stable. Later version is known as overlay2, which is very stable and recommended in overlay2. It requires:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Docker version higher than 17.06.02;&lt;/li&gt;&#10;&lt;li&gt;Kernel version higher than 3.10.0-514 for CentOS and RHEL; or higher than 4.0 for other distributions of Linux;&lt;/li&gt;&#10;&lt;li&gt;Using with xfs file system with d_type turned on&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In production environment, it is recommended to moutn /var/lib/docker to separate disk or partition, to prevent the directory getting full from impacting the host OS. The option pquota is recommended for mounting options in /etc/fstab.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To configure storage driver, create file /etc/docker/daemon.json, with the following content:&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-java" data-lang="java"&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:#e6db74"&gt;&amp;#34;storage-driver&amp;#34;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#34;overlay2&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;storage-opts&amp;#34;&lt;/span&gt;:&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;overlay2.size=20G&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;overlay2.override_kernel_check=true&amp;#34;&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;]&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then restart docker service. Run &amp;#8220;docker info&amp;#8221; and examine the Storage Driver section to ensure storage driver is overlay2 and d_type is true.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The way overlay2 works is similar to AUFS, involving union mount process, with lowerdir, upperdir and merged. More details are &lt;a href="https://docs.docker.com/storage/storagedriver/overlayfs-driver/#how-the-overlay2-driver-works" class="rank-math-link"&gt;here&lt;/a&gt;, including &lt;a href="https://docs.docker.com/storage/storagedriver/overlayfs-driver/#how-the-overlay-driver-works" class="rank-math-link"&gt;how overlay2 works&lt;/a&gt; with file read and file write (e.g. CopyOnWrite).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Today, overlay2 driver is officially recommended by Docker for its stability and performance, it should be used if all the conditions are met.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/10/docker-under-the-hood/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker components&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automatic deployment of Orthanc on AWS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Docker components</title><link>https://www.digihunch.com/2020/10/docker-under-the-hood/</link><pubDate>Wed, 28 Oct 2020 20:23:00 -0400</pubDate><guid>https://www.digihunch.com/2020/10/docker-under-the-hood/</guid><description>&lt;p class="wp-block-paragraph"&gt;The previous &lt;a href="https://www.digihunch.com/2020/08/virtualization-3-of-3-containers/" class="rank-math-link"&gt;post&lt;/a&gt; about virtualization and containerization brought up some underlying technologies which Docker build containers on, including:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;namespaces &amp;#8211; a Linux kernel mechanism to isolate resources. It allows a process to run within an isolated environment (mnt, pid, net, ipt, uts, user, cgroup)&lt;/li&gt;&#10;&lt;li&gt;cgroups &amp;#8211; a Linux kernel mechanism to limit resource usage of a process or process group&lt;/li&gt;&#10;&lt;li&gt;unionFS (this will be further discussed under Docker storage)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this post we further discuss the components in Docker, the dominant and popular player in container technology, as shown in the diagram below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="629" src="https://www.digihunch.com/wp-content/uploads/2024/07/docker-component-1024x629.webp" alt="" class="wp-image-11422" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/docker-component-1024x629.webp 1024w, https://www.digihunch.com/wp-content/uploads/2024/07/docker-component-300x184.webp 300w, https://www.digihunch.com/wp-content/uploads/2024/07/docker-component-768x472.webp 768w, https://www.digihunch.com/wp-content/uploads/2024/07/docker-component-1536x943.webp 1536w, https://www.digihunch.com/wp-content/uploads/2024/07/docker-component.webp 1938w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The component names can be seen under docker install directory. It consists of three groups:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Docker related: docker, dockerd, docker-init and docker-proxy&lt;/li&gt;&#10;&lt;li&gt;Containerd related: containerd, containerd-shim and ctr&lt;/li&gt;&#10;&lt;li&gt;Container runtime: runc&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we discuss each group:&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker-related components&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;docker is just an implementation of docker client, it supports commands to achieve all functions between client and server. Alternatively, user may use REST API, or Docker SDK to communicate with Docker server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;dockerd is the server process, to receive requests from docker (client), SDK library or REST API caller. It executes the request and returns status to client. There are three ways for docker (client) to communicate with dockerd.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;By Unix Socket (unix://socket_path). The default socket path used by dockerd is /var/run/docker.sock, which is why only root can use docker after installation.&lt;/li&gt;&#10;&lt;li&gt;TCP request (tcp://host:port). It is recommended to configure TLS communication in production environment.&lt;/li&gt;&#10;&lt;li&gt;By file descriptor (fd://) used in systemd service.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Unix socket is the default communication method. To allow remote access to dockerd, use -H to specify HOST and PORT when starting dockerd.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;docker-init is used by Docker as PID 1 process for containers, in case it needs to recycle zombie containers. To use this, specify &amp;#8211;init when running container.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;docker-proxy is used for port mapping. When you use -p switch with docker run, this docker-proxy is the service that maps the container port to host port. It does so by modifying the iptables nat rule.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Containerd related components&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;containerd component was separated from dockerd since Docker 1.11, in compliance with OCI standard. It is responsible for life cycle management of containers, it also manages images (e.g. pulling from repo), request from dockerd to call runc, storage and network resources.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;dockerd uses UNIX socket to send request to containerd. The default socket path for containerd is /run/containerd/containerd.sock. containerd execute the task and return status to dockerd. You may also directly use containerd to manage containers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ctr (containderd-ctr) is the client of containerd, mostly used only in development and testing. If the environment does not have dockerd, then you can use ctr as client, to send request directly to containerd.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;containerd-shim is used to decouple containerd from the containers. containerd-shim is the parent process of containers. This is so that restarting containerd does not impact the running containers.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Container runtime&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;runc is a standard implementation of OCI container runtime. It is a command-line tool to create and run containers.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/10/host-legacy-application-in-docker-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Host legacy application in Docker 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/11/docker-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Intro to Big Data Projects</title><link>https://www.digihunch.com/2020/09/intro-to-big-data-projects/</link><pubDate>Thu, 10 Sep 2020 21:33:00 -0400</pubDate><guid>https://www.digihunch.com/2020/09/intro-to-big-data-projects/</guid><description>&lt;p class="wp-block-paragraph"&gt;Modern applications produce super large datasets beyond what traditional data-processing application can handle. Big data is a discipline that specialize in processing such data. For example, analysis, information extraction etc. The scale of large dataset grows well beyond the capacity of a single computer, which calls for computing power delivered by multi-node clustered systems. Intensive computing tasks are completed in a distributed system consisting multiple nodes each performing some tasks, known as High-Performance Computing Cluster (HPCC).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cluster computing inherit the challenges of distributed system. Moreover, two main challenges to solve are: distributed storage, and distributed computation. In Apache Hadoop projects, HDFS and MapReduce address these two challenges respectively. Now the Hadoop ecosystem has evolved to include several core projects:&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;HDFS&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A distributed file system for reliably storing huge amount of unstructured, semi-structured or structured data in the form of files. Parts of a single large file can be stored on different nodes across the cluster. HDFS works in master-slave mode:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;NameNode (master): holds file system namespace, controls access, keep track of DataNodes and replication factor &lt;/li&gt;&lt;li&gt;DataNode (slave): stores user data&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;HDFS is Java-based so is portable across all platforms. User interact with HDFS using a command-line interface called &amp;#8220;FS shell&amp;#8221;. There is also an interface called FUSE (filesystem in userspace) to mount HDFS to Linux OS. Since HDFS supports commodity hardware it is great for storing data for further processing. However, HDFS is not suitable for storing data related to applications requiring low latency access, nor is it good for simultaneous writes to the same file. Also HDFS is not suitable for large number of small files because the metadata for each file needs to be stored on the NameNode and is held in memory. &lt;a href="https://hadoop.apache.org/docs/stable1/hdfs_design.html"&gt;Here&lt;/a&gt; is the architecture guide for HDFS, and this &lt;a href="https://data-flair.training/blogs/hadoop-hdfs-data-read-and-write-operations"&gt;page&lt;/a&gt; expands further on the read and write operations in HDFS.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Compared to NAS(e.g. NFS), HDFS is distributed by design. The data blocks are distributed across different nodes. NFS storage may or may not be distributed depending on the implementation. HDFS is designed to work with MapReduce paradigm, where computation is moved to the data. In NAS, data is stored separately from the computations. Lastly, NAS is usually made up of enterprise grade hard drive but HDFS works with commodity hardware.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;MapReduce&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hadoop MapRecude is a distributed algorithm framework that allows parallel processing of huge amounts of data. It breaks a large chunk into smaller ones to be processed separately on different data nodes and automatically gather the results across the multiple nodes to return a single result. If the duration of linear data processing can be done during night hours, it makes sense to choose Hadoop MapReduce. MapReduce runs on Hadoop cluster but also supports other database formats like Cassandra and HBase. MapReduce includes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Job: a unit of work to be performed as requested by the client.&lt;/li&gt;&lt;li&gt;Task: Jobs are divided into sub-jobs known as tasks. The tasks can be run independent of each other on different nodes. There are two types of tasks: &lt;ul&gt;&lt;li&gt;Map task is performed by map() function to process one or more chunks of data and produce the output results&lt;/li&gt;&lt;li&gt;Reduce task is performed by reduce() function to consolidate the results produced by each of the map task&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;JobTracker: like the storage (HDFS), the computation (MapReduce) also works in master-slave fashion. A JobTracker node acts as the master to schedule task on appropriate nodes, coordinate execution of tasks, get the result back after execution of each task, re-execute failed tasks, and monitor overall progress. There is only one JobTracker node per Hadoop Cluster.&lt;/li&gt;&lt;li&gt;TaskTracker: a TaskTracker node acts as teh slave and is responsible for executing a task assigned to it by the JobTracker. There are usually a number of JobTracker nodes in a Hadoop Cluster. They execute the heavy lifting tasks.&lt;/li&gt;&lt;li&gt;Data Locality: if MapReduce cannot place the data and the compute on the same node, data locality put the compute on the node nearest to the respective data node(s) which contains the data to be processed.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The MapReduce programming model includes these steps: input-&amp;gt;split-&amp;gt;map-&amp;gt;combine-&amp;gt;shuffle&amp;amp;sort-&amp;gt;reduce-&amp;gt;output.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://ars.els-cdn.com/content/image/3-s2.0-B9780128093931000064-f06-04-9780128093931.jpg?_" alt=""/&gt;&lt;figcaption&gt;MapReduce programming model&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading"&gt;YARN&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;YARN (yet another resource negotiator) is a system to schedule applications and services on an HDFS cluster and manage the cluster resources like memory and CPU. The two components are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;ResourceManager: receives the processing requests, and then passes the parts of requests to corresponding NodeManager accordingly based on the needs. ResourceManager is a central authority.&lt;/li&gt;&lt;li&gt;NodeManager: installed on every DataNode, is responsible for execution of the task on every single DataNode, monitoring the resource usage and reporting to the ResourceManager.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading"&gt;HBase&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A key-value pair NoSQL database based on HDFS storage, with column family data representation, and mater-slave replication. HBase is based on Google&amp;#8217;s BigTable concept (similar to Cassandra). It runs on a cluster of commodity hardware and scales linearly. Compared with Cassandra, HBase doesn&amp;#8217;t have a query language of its own. You will have to work with JRuby-based shell, or Apache Hive. HBase is also a master-slave architecture and it uses Zookeeper as a status manager. In that sense, Cassandra is a &amp;#8220;self-sufficient&amp;#8221; database technology whereas HBase relies on other components in Hadoop. This &lt;a href="https://www.scnsoft.com/blog/cassandra-vs-hbase"&gt;article&lt;/a&gt; also compares the data model difference between the two.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Hive&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hive is a SQL interface over MapReduce for developers and analysts who prefer SQL interface over native Java MapReduce programming to query and manage large datasets residing in HDFS. With Hive you can map a tabular structure on to data stored in distributed storage. The Hive queries are written in SQL-like language known as HiveQL, executed via MapReduce. When a HiveQL query is issued, it triggers a Map and/or Reduce job(s) to perform the operation defined in the query.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Pig&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A scripting interface over MapReduce for developers who prefer scripting interface over the native Java MapReduce programming. It is a runtime environment with a shell (named &lt;strong&gt;Grunt Shell&lt;/strong&gt;) for execution of MapReduce jobs via a high-level scripting language called Pig Latin. Pig is an abstraction (high-level programming language) on top of a Hadoop cluster. Pig Latin queries/commands are compiled into one or more MapReduce jobs and then executed on Hadoop cluster. The most common commands in Pig are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;DUMP: displays the results to screen&lt;/li&gt;&lt;li&gt;STORE: stores the results to HDFS&lt;/li&gt;&lt;/ul&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://2.bp.blogspot.com/-w7KeAnwWnBQ/WfYBJzgtvQI/AAAAAAAAAMk/D58SpZfK7lkJ8QnKnQZW268mKzRvuOOnACLcBGAs/s640/HadoopStack.png" alt="Apache Hadoop Ecosystem"/&gt;&lt;figcaption&gt;Hadoop Ecosystem&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are some other Apache projects, which are sometimes considered as in the Hadoop ecosystem as well:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Oozie&lt;/strong&gt;: worflow scheduling system to manage Hadoop jobs. In Oozie, a workflow is defined as a collection of control flow nodes and action nodes in a directed acyclic graph. Control flow nodes define the beginning and the end of a workflow, as well as a mechanism to control the workflow execution path. Action nodes are the mechanism by which a workkflow triggers the execution of a computation/processing task, such as MapReduce, Pig, etc.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Sqoop&lt;/strong&gt; (SQL-to-Hadoop): a command-line interpreter tool for importing data from database (e.g. MySQL, data warehouse, etc) into the Hadoop environment (e.g. HDFS, Hive). It can also export the data back.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Flume&lt;/strong&gt;: data ingestion for streaming logs into Hadoop environment. Flume is a distributed and reliable service for collecting and aggregating huge amounts of log data.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ZooKeeper&lt;/strong&gt;: distributed service coordinator, as previously &lt;a href="https://www.digihunch.com/2020/08/zookeeper/"&gt;discussed&lt;/a&gt;. It is based on a Paxos algorithm variant called ZAB protocol.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Ambari&lt;/strong&gt;: a framework for provisioning, managing and monitoring Hadoop clusters.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hortonworks &lt;a href="https://www.cloudera.com/downloads/hortonworks-sandbox.html"&gt;sandbox&lt;/a&gt; provide a VM image that have some Hadoop services pre-installed for beginners to get a taste of how it works all together.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Spark&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hadoop is used in the industry owing to a simple programming model (MapReduce) but the speed and waiting time (between queries and running the program). Spark is introduced to speed up the computing process. Spark uses Hadoop for storage (HDFS) and processing. It extends the MapReduce model to efficiently use more types of computations which includes interactive queries and stream processing. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Spark started as a sub-project of Hadoop in 2009 but since 2014 Apache has run it as a top-level project. It is a lightning-fast in-memory cluster computing technology. The features are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Speed: in-memory computing makes super fast processing;&lt;/li&gt;&lt;li&gt;Built-in APIs support multiple languages: Scala, Python and Java;&lt;/li&gt;&lt;li&gt;Advanced analytics &amp;#8211; apart from map and reduce, Spark also has libraries that support SQL query, near real-time stream processing, Graph algorithms and machine learning.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Spark can run in &lt;a href="https://spark.apache.org/docs/latest/spark-standalone.html"&gt;standalone mode&lt;/a&gt;, on &lt;a href="https://spark.apache.org/docs/3.5.6/running-on-mesos.html"&gt;Mesos&lt;/a&gt;, or with &lt;a href="https://spark.apache.org/docs/latest/running-on-yarn.html"&gt;YARN cluster manager&lt;/a&gt;. The document also provides guide on deployment on EC2 and &lt;a href="https://spark.apache.org/docs/latest/running-on-kubernetes.html"&gt;Kubernetes&lt;/a&gt;. Spark contains these components:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Spark Core: the underlying general execution engine for spakr platform that all other functionality is built upon. It provides in-memory computing and referencing datasets in external storage systems.&lt;/li&gt;&lt;li&gt;SparkSQL: a components on top of Spark Core that introduces a new data abstraction called SchemaRDD, which supports both structured and semi-structured data.&lt;/li&gt;&lt;li&gt;Spark Streaming: perform streaming analytics on top of Spark Core. It ingests data in mini-batches and performs RDD (Resilient Distributed Datasets) transformation on the fly.&lt;/li&gt;&lt;li&gt;MLib: a distributed machine learning framework &lt;/li&gt;&lt;li&gt;GraphX: a distributed graph-processing framework&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The speed of Spark is owing to its fundamental data structure &amp;#8211; Resilient Distributed Datasets (RDD), an immutable distributed collection of objects. Each dataset in RDD (object collection) is divided into logical partitions, which can be computed on different nodes of the cluster. The object can be any type of Python, Java or Scala object, including user-defined classes. There are two ways to create RDDS:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Parallelizing an existing collection in your driver program&lt;/li&gt;&lt;li&gt;Referencing a dataset from external storage system (e.g. HDFS, HBase) or data source offering a Hadoop Input Format&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can also create RDD based on other existing RDDs. This &lt;a href="https://www.tutorialspoint.com/apache_spark/apache_spark_rdd.htm"&gt;page&lt;/a&gt; explains further how RDD speeds up computing compared to MapReduce.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/09/host-legacy-application-with-docker-compose/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Host legacy application in Docker 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/09/spark-cassandra-and-python/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Spark, Cassandra and Python&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Zookeeper Summary</title><link>https://www.digihunch.com/2020/08/zookeeper/</link><pubDate>Wed, 26 Aug 2020 23:10:00 -0400</pubDate><guid>https://www.digihunch.com/2020/08/zookeeper/</guid><description>&lt;h3 class="wp-block-heading" id="h-distributed-systems"&gt;Distributed systems&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Distributed system involves &lt;span style="text-decoration: underline;"&gt;independent computing entities&lt;/span&gt; linked together by network. The components &lt;span style="text-decoration: underline;"&gt;communicate and coordinate&lt;/span&gt; with each other to achieve a &lt;span style="text-decoration: underline;"&gt;common goal&lt;/span&gt;. In early days, designers and developers often had made some assumptions (aka. fallacies) of distributed computing:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The network is reliable&lt;/li&gt;&#10;&lt;li&gt;Latency is zero&lt;/li&gt;&#10;&lt;li&gt;Bandwidth is infinite&lt;/li&gt;&#10;&lt;li&gt;Network is secure&lt;/li&gt;&#10;&lt;li&gt;Topology doesn&amp;#8217;t change: in reality, components to a network get removed/added over time. the system should tolerate such changes.&lt;/li&gt;&#10;&lt;li&gt;There is one administrator: for distributed systems to function, they interact with external system beyond administrative control.&lt;/li&gt;&#10;&lt;li&gt;Transport cost is zero:&amp;nbsp; cost is involved everywhere, in the form of CPU cycles spent, to actual dollars paid to service provider.&lt;/li&gt;&#10;&lt;li&gt;Network is homogenous&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These fallacies make coordinating distributed computing entities a huge challenge and Zookeeper is introduced to address these challenges. Zookeeper implements common tasks for distributed coordination, such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Configuration Management (propagate configuration changes to all worker nodes dynamically)&lt;/li&gt;&#10;&lt;li&gt;Naming service&amp;nbsp;&lt;/li&gt;&#10;&lt;li&gt;Distributed synchronization (locks and barriers)&lt;/li&gt;&#10;&lt;li&gt;Cluster membership operations (e.g. detection of node leave/join)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper is a centralized coordination service for the distributed application. ZooKeeper itself is distributed as well. It runs on its own cluster of servers called a ZooKeeper ensemble, separate from application&amp;#8217;s cluster. Distributed consensus, group management, presence protocols, and leader election are implemented by the service so that the application developers do not need to reinvent the wheel by implementing them on their own.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.6.1/images/zkservice.jpg" alt="ZooKeeper Service"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Developers will have to use APIs through ZooKeeper&amp;#8217;s client library, which has language bindings for almost all popular programming languages. The client library is responsible for the interactions of an application with the ZooKeeper service. For testing with API access one can alternatively use its Java-based command-line shell (zkCli.sh)&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;$ zkCli.sh -server zknode:2181&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-how-zookeeper-works"&gt;How Zookeeper works&lt;/h3&gt;&#10;&lt;h4 class="wp-block-heading" id="h-data-model"&gt;Data Model&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper allows distributed process to coordinate with each other through a shared hierarchical namespace of data registers (znodes). The hierarchy start with root node which has child znode(s). Each znode can have their children, as well as store its own data (hence the name data register). The data in a znode is stored in byte format for a maximum of 1MB (ZooKeeper by design is just a coordinator service of host application, so its own data set size is fairly small).&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="360" height="368" src="https://www.digihunch.com/wp-content/uploads/2023/01/zkdm.jpeg" alt="" class="wp-image-7753" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/zkdm.jpeg 360w, https://www.digihunch.com/wp-content/uploads/2023/01/zkdm-293x300.jpeg 293w" sizes="auto, (max-width: 360px) 100vw, 360px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Zookeeper data model&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Znodes have two types (set at time of creation) &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;persistent znode: for storing persistent data, such as configuration. The znodes and their data will exist even if the creator client dies.&lt;/li&gt;&#10;&lt;li&gt;ephemeral znode: deleted by ZooKeeper service when the creating client&amp;#8217;s session ends (due to disconnection or explicit termination). It can also be explicitly deleted by creator client through delete API call. They cannot have children. Their visibility is controlled by ACL policy&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper can assign an incremental sequence number as part of znode name during its creation. This makes a sequential node. Both persistent znode and ephemeral znode can be either sequential or not.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In typical client-server architecture, server is passively open and do not initiate communication to client. Client pulls information from server. This is however an anti-pattern for large scale distributed system. ZooKeeper implements a Watch mechanism where clients can get notifications from ZooKeeper service, instead of having to poll for events. Clients can register with the ZooKeeper service (by setting a watch on znode) for any changes associated with a znode. A watch will only trigger notification once, and needs to be re-registered (by client) for trigger the next notification. A watch is triggered upon:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Any changes to the data of a znode;&lt;/li&gt;&#10;&lt;li&gt;any changes to the children of a znode;&lt;/li&gt;&#10;&lt;li&gt;Creation of deletion of a znode&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper guarantees that notifications are delivered in the order of event occurrence. When a client disconnects from ZooKeeper server, it doesn&amp;#8217;t receive any watches until the connection is re-established. &lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-api-operations"&gt;API Operations&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ZooKeeper operations are:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;create&lt;/td&gt;&lt;td&gt;Creates a znode in the specified path&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;delete&lt;/td&gt;&lt;td&gt;Deletes a znodes from the specified path. Not allowed if the znode has children. version number required&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;exists&lt;/td&gt;&lt;td&gt;Check if a znode at the specified path exists, and get version number; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getChildren&lt;/td&gt;&lt;td&gt;Get a list of children of a znode; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getData&lt;/td&gt;&lt;td&gt;get the data associated with a znode; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;setData&lt;/td&gt;&lt;td&gt;writes data into the data field of a znode. Version number required.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getACL&lt;/td&gt;&lt;td&gt;get the ACL of a znode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;setACL&lt;/td&gt;&lt;td&gt;set the ACL in a znode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sync&lt;/td&gt;&lt;td&gt;synchronizes a client&amp;#8217;s view of a znode &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The write operations (setData, create, delete) are atomic, durable and eventually consistent. Every znode has a stat structure including cZxid, mZxid an dpZxid that keeps track of the ID of the transactions that created, last modified this znode, or pertains to adding or removing its children.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Production znode ensemble with more than one node is running in quorum mode. Updates to ZooKeeper tree by clients must be persistently stored in this quorum of nodes for a transaction to be completed successfully. Odd number of node is recommended to avoid split-brain where network partition causes two subsets of servers in the ensemble function independently, and different clients get different results for the same requests, depending upon the server they are connected to.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All ZooKeeper nodes are listed in the configuration for client application to randomly pick from and try to connect and establish a session. The session is associated with every operation the client executes in a ZooKeeper service. The session also has a timeout period specified by the application client during session establishment. If the connection remains idle for more than the timeout period, the server expires the session. Appropriate session timeout should be set based on network condition. Sessions are kept alive by client sending heartbeat to ZooKeeper service. Application developer needs to handle connection-loss scenarios properly.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-leader-election-and-atomic-broadcast"&gt;Leader Election and Atomic Broadcast&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper ensemble contains a leader nodes, follower nodes and observer nodes.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The leader node is elected by the cluster. It handles all write requests. &lt;/li&gt;&#10;&lt;li&gt;The follower nodes are leader candidates that are not elected. They are backup to the leader nodes. They handle read request, and receive the updates proposed by the leader, and through a majority consensus mechanism, a consistent state is maintained across the ensemble. &lt;/li&gt;&#10;&lt;li&gt;The observer nodes are ineligible as leader candidates. They have otherwise the same function as followers.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The service relies on the replication mechanism to ensure that all updates are persistent in all servers that constitute the ensemble. This is the core mechanism in ZooKeeper, implemented as a special atomic messaging protocol called ZooKeeper Atomic Broadcast (ZAB). ZAB (a variant of Paxos algorithm) ensures the election of new leader in the event of old leader crash, and ensures integrity of data. It defines three states (looking, following and leading) of a node, and goes through four phases (election, discovery, sync, broadcast) in its operation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All read requests (exists, getData, getChildren) are process locally by the ZooKeeper node where the client is connected to. This makes read operation fast. All write requests (create, delete, and setData) are forwarded to the leader in the ensemble, which carries out the client request as a transaction. A transaction is identified by zxid and is idempotent. Transaction also satisfies the property of isolation (no transaction is interfered with by any other transaction). Only after a majority of the followers acknowledge that they have persisted the change does the leader commit the update.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.6.1/images/zkcomponents.jpg" alt="ZooKeeper Components"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Transaction processing involves two steps in ZooKeeper: leader election and atomic broadcast. This resembles a two-phase commit protocol (which also includes a leader election and an atomic broadcast)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper use local storage to persist transactions. The transactions are logged to transaction logs, in sync&amp;#8217;ed write, requiring a dedicated block device separated from boot device of server. The local storage also keep point-in-time copies (snapshots) of the ZooKeeper tree.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-zookeeper-recipes"&gt;ZooKeeper Recipes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ZooKeeper recipes defines high-level implementation (construct) of some common distributed coordination mechanism:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Barrier_(computer_science)"&gt;Barrier&lt;/a&gt;: any thread/process must stop at this point and cannot proceed until all other threads/processes reach this barrier.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://computersciencewiki.org/index.php/Queue"&gt;Queue&lt;/a&gt;: allow FIFO in distributed system&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Lock_(computer_science)"&gt;Lock&lt;/a&gt;: Fully distributed locks that are globally synchronous, meaning at any snapshot in time no two clients think they hold the same lock.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Leader_election"&gt;Leader Election&lt;/a&gt;: designate a single process as the organizer of some task distributed among several nodes.&lt;/li&gt;&#10;&lt;li&gt;Group membership: node may join or leave a group, which needs to be made available to clients. An alternative to ZooKeeper to manage group membership is &lt;a href="https://en.wikipedia.org/wiki/Gossip_protocol"&gt;gossip protocol&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="http://jasonwilder.com/blog/2014/02/04/service-discovery-in-the-cloud/"&gt;Service discovery&lt;/a&gt;: help client to determine IP and port for a service that are hosted by multiple servers.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol"&gt;Two-phase commit&lt;/a&gt;: a mechanism for atomic commitment in two steps: first a commit request phase involving a voting by participants; and second, either a commit action, or an abort action, based on the voting result.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-zookeeper-administration"&gt;Zookeeper Administration&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The official &lt;a href="https://zookeeper.apache.org/doc/r3.6.1/zookeeperAdmin.html"&gt;documentation&lt;/a&gt; includes all we need to know about administration. In addition, we need to configure &lt;a href="https://logging.apache.org/log4j/1.2/manual.html"&gt;log4j&lt;/a&gt; for proper logging. As best practices, we also should turn off &lt;a href="https://www.digihunch.com/2018/04/centos-remove-swap-safely/"&gt;swapping&lt;/a&gt; on ZooKeeper. We should clean up the data directory periodically if auto purge is not enabled. For optimal performance, ZooKeeper transaction log should be configured in a dedicated device.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For monitoring, ZooKeeper responds to a small sets of four-letter commands issued through telnet or nc to server&amp;#8217;s client port. This allows the admin to check health of server or diagnose any problems. This requires the following property in zoo keeper config:&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-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;4lw.commands.whitelist&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;stat, ruok, conf, isro, wchc&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 value can be set to asterick to allow all four-letter keyword. Once enabled, we can check server status&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;$ echo ruok | nc localhost &lt;span style="color:#ae81ff"&gt;2181&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;imok&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;More four-letter commands are listed &lt;a href="https://zookeeper.apache.org/doc/r3.1.2/zookeeperAdmin.html#sc_zkCommands"&gt;here&lt;/a&gt;. Apart from the four-letter commands, ZooKeeper can also be managed through Java Management Extensions (&lt;a href="https://www.oracle.com/java/technologies/javase/javamanagement.html"&gt;JMX&lt;/a&gt;).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apache ZooKeeper is a coordination service for distributed application. It has become the solution for high availability for many other projects. Some of Apache&amp;#8217;s well known open-source distributed services include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Apache Hadoop (an umbrella of projects including many components for BigData processing such as Hadoop Common, Hadoop Distributed File System (HDFS), Hadoop YARN (yet another resource negotiator) and Hadoop MapReduce)&lt;/li&gt;&#10;&lt;li&gt;Apache HBase: non-relational database on top of HDFS&lt;/li&gt;&#10;&lt;li&gt;Apache Hive: data warehouse with SQL-like interface&lt;/li&gt;&#10;&lt;li&gt;Apache Kafka: stream processing&lt;/li&gt;&#10;&lt;li&gt;Apache Nifi: automated data flow processing. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some of them, such as Nifi, has an embedded implementation of ZooKeeper ensemble if there isn&amp;#8217;t a separate ensemble. There is some limitation with embedded Zookeeper ensemble. First, we cannot start ZooKeeper without starting Nifi service on the same server. Second, we need to orchestrate the configuration so that the ZooKeeper ensemble does not grow too large. We need to keep in mind that the ZooKeeper ensemble is a separate cluster of its own, and the it is not recommended to have more than 7 nodes on ZooKeeper.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 4 of 4 – Networking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/09/host-legacy-application-with-docker-compose/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Host legacy application in Docker 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 4 of 4 – Networking</title><link>https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/</link><pubDate>Fri, 21 Aug 2020 21:53:32 -0400</pubDate><guid>https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/</guid><description>&lt;h3 class="wp-block-heading"&gt;Virtual LAN (VLAN)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although VLAN emerged before virtualization and is technically not part of virtualization topic. I&amp;#8217;d just like to start from here as a refresher. Suppose we have computers from finance department and computers from sales department all connected to a single layer-2 switch. There are at least three problems: 1) too many devices on the same broadcast domain causes traffic congestion; 2) security can be compromised and 3) each department might have several physical locations. We introduce multi-layer switch to address these with two main features: 1) the VLAN feature can map ports to logical networks, so that all hosts are physically connected to a single switch, but logically to their own network (VLAN) 2) the SVI (switch virtual interface) feature allows inter-VLAN routing at layer 3. Such multi-layer switch is sometimes referred to as layer-3 switch. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VLAN is local to a switch and a tag is required in ethernet frame in order to pass VLAN info across switches. This link between switches is called a &lt;strong&gt;trunk&lt;/strong&gt;. &lt;a href="https://en.wikipedia.org/wiki/IEEE_802.1Q"&gt;IEEE 802.1q&lt;/a&gt; (aka dot1q) is the networking standard for VLAN, which standardizes the tagging traffic between switches to tell which traffic belongs to which VLAN. The dot1q trunk (aka dot1q link) provides VLAN IDs fro frames traversing between switches. A trunk can be configured between two switches, or between a switch and a router. &lt;strong&gt;Trunking&lt;/strong&gt; is the process of traversing different VLAN traffic over the trunk. The ports on each switch need to be configured to enable trunking. While Cisco calls such ports &lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;trunk port&lt;/strong&gt;&lt;/span&gt;, others call them &lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;tagged port&lt;/strong&gt;&lt;/span&gt;. Their function is to add the VLAN tag to ethernet frame. In contrast, regular ports that send and receive frames without VLAN tag are called &lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;access port&lt;/strong&gt;&lt;/span&gt; or &lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;untagged port&lt;/strong&gt;&lt;/span&gt;. Trunk port carries traffic for multiple VLANS whereas access port carries traffic for a single VLAN. A network device connected to access port has no idea about its VLAN belonging. VLAN creation and management are the responsibility of the switch. Common trunking protocols include &lt;span style="text-decoration: underline;"&gt;VTP (VLAN trunking protocol) &lt;/span&gt;and &lt;span style="text-decoration: underline;"&gt;DTP (dynamic trunking protocol)&lt;/span&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://www.youtube.com/watch?v=NmkFzDrZsXM"&gt;video&lt;/a&gt; and &lt;a href="https://www.professormesser.com/network-plus/n10-008/n10-008-video/vlans-and-trunking-n10-008/"&gt;this&lt;/a&gt; video have good explanations on VLAN.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Virtual Extensible LAN (VXLAN)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VXLAN is an overlay protocol. Remember that in the standard TCP/IP stack, you normally encapsulate layer-3 IP datagram into a layer-2 ethernet frame. With the VXLAN encapsulation technique however, layer-2 frames can be encapsulated within layer-4 UDP packet.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VXLAN allows you to stretch layer 2 connection over an intervening layer 3 network. VXLAN tunnel endpoints (VTEPs) are endpoint devices that terminate VXLAN tunnels, and they can be either virtual or physical switch ports. It encapsulates VXLAN traffic and de-encapsulates the traffic when it leaves the VXLAN tunnel.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The VXLAN encapsulation includes the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Outer Ethernet Header (source and dest MAC for underlay VTEPs)&lt;/li&gt;&#10;&lt;li&gt;Outer IP header (source and dest IP on underlay network)&lt;/li&gt;&#10;&lt;li&gt;Outer UDP header (including source and dest ports, 4789 default)&lt;/li&gt;&#10;&lt;li&gt;VXLAN Header (including VNI)&lt;/li&gt;&#10;&lt;li&gt;Inner Ethernet Frame (with source and dest MAC for overlay interfaces)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The VNI (VXLAN network identifier, aka VNID) included in the VXLAN header is 24-bit long. It is conceptually similiar to VLAN ID in VLAN but only with 12-bit length.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="http://chansblog.com/wp-content/uploads/2015/02/0.1-VXLAN-frame.jpg" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;VXLAN Enapsulation&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The VXLAN protocol is documented in &lt;a href="https://tools.ietf.org/html/rfc7348"&gt;RFC7348&lt;/a&gt;. Its specification was originally created by VMware, Cisco and Arista. As it became more common in network virtualization (with data centre virtualization, and application containerization) several other players joined the list of contributors and they manufacture switches that support VXLAN. &lt;a href="https://support.huawei.com/enterprise/en/doc/EDOC1100086966#:~:text=VXLAN%20encapsulates%20a%20Layer%202,over%20a%20Layer%203%20network.&amp;amp;text=It%20establishes%20a%20logical%20tunnel,forward%20them%20through%20the%20tunnel."&gt;This&lt;/a&gt; is a section on VXLAN from the document of Huawei Cloud Engine 5800 switch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.openvswitch.org/"&gt;Open vSwitch&lt;/a&gt; is an example of a software-based virtual network switch that supports VXLAN overlay networks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, the main benefits of VXLAN over VLAN are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;VXLAN scales up to 16 million logical networks, thanks to the 24-bit length of VNI&lt;/li&gt;&#10;&lt;li&gt;VXLAN supports layer 2 adjacency across IP networks. A VM belonging to existing layer 2 domain can be created in different data centre (where more computing resources are available), without being constrained by layer 2 boundaries, or being forced to create geographically stretched layer 2 domains (stretched VLAN).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Virtual Machine Networking&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For VM to connect to each other, within or across hosts, we need not only vNIC on VM, but also vSwitch to connect vNICs. A vSwitch (aka bridge) is a logically defined layer-2 device that passes frames between vNICs. On the same host, vNICs are directly connected to vSwitch, which is then connected to the physical NIC. Each vSwtich connects a broadcast domain. When we setup vNIC there are three modes:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Bridged networking:&lt;/strong&gt; VM connect to outside network using host&amp;#8217;s physical NIC, which acts as a bridge between vNIC and outside network. The VM is a full participant in the network as if it were a physical computer on the network. i.e. it obtain IP addressing information from a DHCP server on the outside (physical) network. The VM&amp;#8217;s IP address is also visible and directly accessible by other computers on the network. bridge networking is common for servers as VMs.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAT networking:&lt;/strong&gt; The VM relies on the host to act as NAT device to make outgoing network connection. The IP address of VM is assigned by virtual DHCP server on host. The guest VMs form a private network and computers on the outside network are external. The host translates private IP address into the host&amp;#8217;s IP address on the way out, and listens for returning traffic. Outside network sees traffic from VM guest as if it were from the host. This network mode is common when the VMs are mainly used as a client workstation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Host-only networking: &lt;/strong&gt;creates a network that is completely contained within the host computer. The vSwtich is the hub of the private network and the physical NIC on the host is not involved. The VM will not have access to the outside network. This mode is useful when VMs need to be isolated from outside network, and only need to communicate with peers on the same host.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The difference between NAT networking and host-only networking is the exposure of VM guest to external network. All of these networking modes are &lt;a href="https://knowledge.broadcom.com/external/article/303393/understanding-networking-types-in-vmware.html"&gt;available&lt;/a&gt; on VMWare fusion, for example. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Advanced virtualization platform such as vSphere usually support multi-hosting. Multiple host can also be configured to form a distributed vSwitch, such as &lt;a href="https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.networking.doc/GUID-6E51D76A-DC9F-44E2-B673-7D92384AFDE4.html"&gt;vSphere Distributed Switch&lt;/a&gt;, in addition to standard switches.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Networking&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I had a &lt;a href="https://www.digihunch.com/2020/07/dockersnetwork/"&gt;brief&lt;/a&gt; on Docker network covering three modes. Out of the three modes, single-host bridge network is the equivalent of host-only networking. MacVLAN driver is similar to bridged networking, in the sense that container may connect to external network, using host NIC as a bridge. However, the external network is still bound by physical location. This is when overlay network comes in handy.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;CNM and CNI&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the Docker networking, container needs to map its own port to host, of which the port resource is implemented by IP tables, which limits the scale and performance of the solution. Also, those networking modes do not address the problem of multi-host networking. As multi-host networking became a real need for containers, the industry started looking into different solutions. Container project favour a model where networking is decoupled from the container runtime. This also greatly improves application mobility. In this model, networking is handled by a &amp;#8216;plugin&amp;#8217; or &amp;#8216;driver&amp;#8217; that manages the network interface, and how the containers are connected to the network. The plugin also assigns the IP address to the container&amp;#8217;s network interfaces. In order for this model to succeed, there needs to be a well-defined interface or API between the container runtime and the network plugins.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker, the company behind the Docker container runtime, came up with the Container Network Model (CNM). Around the same time, CoreOS, the company responsible for creating the rkt container runtime, came up with the Container Network Interface (CNI). Kubernetes originally seeks to use CNM for its plugins, but they eventually decided to go with CNI. The primary reason was that CNM was still seen as something designed with Docker container runtime in mind and was hard to decouple from it. After this decision, several other open source project also turned to CNI for their container runtimes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://thenewstack.io/container-networking-landscape-cni-coreos-cnm-docker/"&gt;article&lt;/a&gt; expands further into the difference between CNM and CNI.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/08/virtualization-3-of-3-containers/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 3 of 4 – Containers&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/zookeeper/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Zookeeper Summary&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 3 of 4 – Containers</title><link>https://www.digihunch.com/2020/08/virtualization-3-of-3-containers/</link><pubDate>Tue, 18 Aug 2020 20:44:35 -0400</pubDate><guid>https://www.digihunch.com/2020/08/virtualization-3-of-3-containers/</guid><description>&lt;p class="wp-block-paragraph"&gt;In broad terms, virtualization of computing resource is about isolation of resources at different levels. We have covered hypervisor-based virtualization in the &lt;a href="https://www.digihunch.com/2020/07/overview-of-virtualization/"&gt;other&lt;/a&gt; post. In this article, we continue to dive into OS level virtualization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Remember again that the gist of virtualization is isolation of resource. To support OS level virtualization, the OS must have its own capability to isolate computing resource. There are many implementations of &lt;a href="https://en.wikipedia.org/wiki/OS-level_virtualization"&gt;OS level virtualization&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Linux Kernel provides low-level mechanisms some two kernel features(namespaces, cgroups and chroot) for building various lightweight tools that can virtualize the system environment. Docker is such framework that builds on chroot namespaces and cgroups.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-chroot"&gt;Chroot&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traditionally, root directory (/) is the top directory shared amongst all processes in the OS. There was a chroot() system call that allows each process to have its own idea of root directory. A chroot is an operation that changes the apparent root directory(/) for the current running process and their children. A program that is run in such a modified environment cannot access files and commands outside that environmental directory tree. This modified environment is called a &lt;strong&gt;chroot jail&lt;/strong&gt;. By separating a process using chroot() we ensure security by restricting the process from accessing outside its environment (breaking the jail). This short &lt;a href="https://www.youtube.com/watch?v=2wSJREC7RV8"&gt;video&lt;/a&gt; is a great lab.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although chroot() has a basic idea of isolation, it simply modifies pathname lookups for a process and its children (by prepending the new root path to any name starting with /). Relative paths can still refer any locations outside of the new root. So chroot() does not intend to defend against intentional tampering by privileged users.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-namespace-isolation"&gt;Namespace Isolation&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Namespaces are fundamentally the mechanisms to abstract, isolate, and limit the visibility that a group of processes has over various system entities such as process trees, network interfaces, user IDs and file system mounts. So there are several categories of namespaces:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Mount namespaces &amp;#8211; traditionally, there is one global mount namespace seen by all processes. The mount namespaces confine the set of filesystem mount points visible within a process namespace, enabling one process group in a mount namespace to have an exclusive view of the filesystem list, compared to another process.&lt;/li&gt;&lt;li&gt;UTS namespaces &amp;#8211; allows isolation of hostname per namespace. Each namespace can have its own hostname on the network&lt;/li&gt;&lt;li&gt;User namespaces &amp;#8211; allow a process to use unique user and group IDs&lt;/li&gt;&lt;li&gt;Cgroup namespaces &amp;#8211; processes inside a &lt;a href="https://man7.org/linux/man-pages/man7/cgroup_namespaces.7.html"&gt;cgroup namespace&lt;/a&gt; are only able to view paths relative to their namespace root.&lt;/li&gt;&lt;li&gt;IPC namespaces &amp;#8211; isolates the System V inter-process communication between namespaces, as well as POSIX message queues within each namespace. POSIX message queue allow process to exchange data in the form of messsages.&lt;/li&gt;&lt;li&gt;PID namespaces &amp;#8211; traditionally, *nix kernels spawn the init process with PID 1 during system boot, which in turn starts other user-mode process and is considered the root of the process tree (all the other processes start below this process in the tree). The PID namespace allows a process to spin off a new tree of processes under it with its own root process (PID=1). PID namespaces isolate process ID numbers, and allow duplication of PID numbers across different PID namespaces. The process IDs only needs to be unique within a PID namespace, and are assigned sequentially starting with PID 1. PID namespaces are used in containers.&lt;/li&gt;&lt;li&gt;Network namespaces &amp;#8211; traditionally, all processes in the entire OS share a single set of network interfaces and routing table entries. The routing table entries can be modified at operating system level. With network namespace, this assumption is no longer valid. Network namespace provides abstraction and virtualization of network protocol and interfaces. Each network namespace will have its own network device instances that can be configured with individual network addresses. Other network services, such as routing table, port number, are isolated as well.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Namespaces are created with the &amp;#8220;&lt;em&gt;unshare&lt;/em&gt;&amp;#8221; command or syscall, or as new flags in a &lt;em&gt;&lt;a href="https://man7.org/linux/man-pages/man2/clone.2.html"&gt;clone&lt;/a&gt;()&lt;/em&gt; syscall. The flags are listed here in the &lt;a href="https://man7.org/linux/man-pages/man7/namespaces.7.html"&gt;man&lt;/a&gt; page for namespace. Note that the &lt;em&gt;clone()&lt;/em&gt; syscall is a more generic implementation of &lt;em&gt;fork()&lt;/em&gt; syscall.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cgroup"&gt;Cgroup&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;cgroups is a Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, network, etc) of a collection of processes (not to be confused with process group, which has its own meaning). Cgroup has two versions. The control groups functionality (version 1) was merged into Linux kernel mainline in version 2.6.24, released in 2008, and version 2 in kernel 4.5 (March 2016), with significant changes to the interface and internal functionality.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Using cgroups, you can allocate resources such as CPU time, network and memory. Similiar to the process model in Linux, where each process is a child to a parent and relatively descends from the init process thus forming a single-tree like structure, cgroups are hierarchical, where child cgroups inherit the attributes of the parent, but what makes it different is that multiple cgroup hierarchies can exist within a single system, with each having distinct resource prerogatives.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Applying cgroups on namespaces results in isolation of processes into containers within a system, where resources are managed distinctly. Each container is a lightweight virtual machine, all of which run as individual entities and are oblivious of other entities within the same system.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-container-implementation"&gt;Container Implementation&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Above we covered some kernel features that enables container technology. There are many ways to use these technologies to implement the isolation. We call them container runtime. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/LXC"&gt;LXC&lt;/a&gt; is a user space interface for those Linux kernel containment features. It allows for running isolated containers on a control host using a single kernel. Users can launch a system init for each container, also referred to as virtual environment (as opposed to virtual machines). The author of this &lt;a href="https://www.upguard.com/blog/docker-vs-lxc"&gt;article&lt;/a&gt; regards LXC as a suprcharged chroot on Linux. LXC has rest API tool called LXD. LXC was targeting sysadmin&amp;#8217;s use cases (not developer) to isolate users&amp;#8217; own private workloads from one another. In early days Docker was built on LXC. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker&amp;#8217;s target market is developers, and it moved beyond LXC with its own execution environment called &lt;em&gt;&lt;strong&gt;libcontainer&lt;/strong&gt;&lt;/em&gt;. With the initial success of Docker, a large community (Docker, CoreOS, Google, etc) emerged around the idea of using containers as the standard unit of software delivery. They started the Open Container Initiative (OCI) to define industry standards around container runtime (runtime spec) and image format (image spec). Docker &lt;a href="https://opencontainers.org/faq/#what-has-docker-done-to-help-create-this-foundation"&gt;donated&lt;/a&gt; the &lt;a href="https://github.com/docker-archive/libcontainer"&gt;libcontainer&lt;/a&gt; codebase to run independently under OCI, as &lt;a href="https://github.com/opencontainers/runc"&gt;runc&lt;/a&gt;. Docker implements isolation using the following technologies:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Namespace: to isolate process ID, networking, mount points, IPC, host and domain name;&lt;/li&gt;&lt;li&gt;Cgroups: to isolate the usage of CPU and memory between containers&lt;/li&gt;&lt;li&gt;UnionFS: isolate file system&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another container runtime technology is &lt;a href="https://en.wikipedia.org/wiki/OpenVZ"&gt;OpenVZ&lt;/a&gt;, which includes an extension of the Linux kernel. It uses container for entire operating systems (not just application and processes). All OpenVZ containers have to share the same Linux kernel version as host. The &lt;a href="https://wiki.aquasec.com/display/containers/Docker+Alternatives+-+Rkt%2C+LXD%2C+OpenVZ%2C+Linux+VServer%2C+Windows+Containers"&gt;adoption&lt;/a&gt; of OpenVZ is not high.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Framework&lt;/td&gt;&lt;td&gt;Runtime implementation&lt;/td&gt;&lt;td&gt;Management tool&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LXC&lt;/td&gt;&lt;td&gt;libvert&lt;br&gt;LXC&lt;/td&gt;&lt;td&gt;LXD (rest API)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OCI&lt;/td&gt;&lt;td&gt;Docker&amp;#8217;s runc&lt;br&gt;CoreOS&amp;#8217;s rtk&lt;/td&gt;&lt;td&gt;docker engine (daemon and cli)&lt;br&gt;rtk cli&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption&gt;container runtimes&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker is now widely adopted for application hosting in production environment. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-container-and-cloud"&gt;Container and Cloud&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Public cloud vendors also has &lt;a href="https://logz.io/blog/aws-eks-vs-ecs-vs-fargate-understand-differences/"&gt;managed services&lt;/a&gt; around Docker. Here are some examples:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Managed Container&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Image Registry&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Managed Orchestration&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS&lt;/td&gt;&lt;td&gt;Elastic Container Service&lt;/td&gt;&lt;td&gt;Elastic Container Registry&lt;/td&gt;&lt;td&gt;Elastic Kubernetes Services&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Azure&lt;/td&gt;&lt;td&gt;Container Instances&lt;/td&gt;&lt;td&gt;Container Registry&lt;/td&gt;&lt;td&gt;Azure Kubernetes Service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GCP&lt;/td&gt;&lt;td&gt;CloudRun&lt;/td&gt;&lt;td&gt;Container Registry&lt;/td&gt;&lt;td&gt;Google Kubernetes Engine&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Digital Ocean&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;Container Registry&lt;/td&gt;&lt;td&gt;Kubernetes&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption&gt;Container services from public cloud&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cloud service was originally developed with VM as a unit of computing resource to service. OS level virtualization allows container to be a unit of computing resource. All these new technologies breed the serverless architecture and cloud-native deployment model. This has significant impact on the creation and delivery of software services. The &lt;a href="https://landscape.cncf.io/"&gt;cloud native landscape&lt;/a&gt; page illustrates more tools around containers.&lt;br&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/08/cloud-storage-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cloud storage overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 4 of 4 – Networking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 2 of 4 – Graphics Computing</title><link>https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/</link><pubDate>Sat, 01 Aug 2020 18:24:00 -0400</pubDate><guid>https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/</guid><description>&lt;p class="wp-block-paragraph"&gt;We covered hypervisor in previous post. In this article we focus on the virtualization of graphics computing resource.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;GPU vs CPU&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;GPU is a specialized type of microprocessor primarily designed for quick image rendering. GPU appeared as a response to graphically intense applications that put a burden on the CPU and degrated computer performance. They became a way to offload those tasks from CPUs, but modern graphics processors are powerful enough to perform rapid mathematical calculations for many other purposes apart from rendering.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;CPU consists of a few cores (up to 23) optimized for sequential serial processing, which is designed to maximize the performance of a single task within a job. GPU uses thousands of smaller and more efficient cores for massively parallel architecture aimed at handling multiple functions at the same time. Typical uses cases for GPUs, in addition to graphics display, includes Games, 3D visualization, Image processing, big data and deep machine learning.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://www.apps4rent.com/wp-content/uploads/2018/04/cpu-vs-gpu.jpg" alt="GPU vs CPU | What's better?"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Moving to virtualization world, the most primitive mechanism for graphics acceleration is Soft 3D, which is commonly used in virtual desktops, or DaaS (desktop as a service). The Software 3D renderer (Soft 3D) uses the Soft 3D graphics driver to provide support for software-accelerated 3D graphics without any physical GPUs being installed in the ESXi host. With respect to GPU in virtualized environment, VMware developed a few technologies.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vSGA (Virtual Shared Graphics Acceleration)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The physical GPUs in the server are virtualized and shared across multiple guest VMs. This option involves installing an Nvidia driver into the hypervisor itself, and each guest VM uses a &lt;span style="text-decoration: underline;"&gt;proprietary VMware SVGA 3D driver&lt;/span&gt; that communicates with the Nvidia driver in ESX. The biggest limitation here is that these drivers only work with DirectX up to 9.0c, and OpenGL up to 2.1. This technology was introduced in early 2013 and is used in light workload for knowledge worker, such as PowerPoint, Visio and web browsing.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="572" height="664" src="https://www.digihunch.com/wp-content/uploads/2024/07/vSGA.webp" alt="" class="wp-image-11412" style="width:443px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/vSGA.webp 572w, https://www.digihunch.com/wp-content/uploads/2024/07/vSGA-258x300.webp 258w" sizes="auto, (max-width: 572px) 100vw, 572px" /&gt;&lt;figcaption class="wp-element-caption"&gt;vSGA&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vDGA (Virtual Dedicated Graphics Acceleration)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vDGA, also known as &amp;#8220;GPU passthrough&amp;#8221;. It provides each VM with unrestricted, fully dedicated access to one of the host&amp;#8217;s GPUs. The hypervisor is drilling a direct hole in itself between the GPU and the guest. This technology allows you to present an internal PCI GPU directly to a VM guest. The device acts as if it were directly driven by the VM guest, and the guest detects the PCI device as if it were physically connected, using the &amp;#8220;real&amp;#8221; driver. There is no special drivers in the hypervisor. vDGA offers the highest level of performance for users with the most intensive graphics computing needs.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="444" height="588" src="https://www.digihunch.com/wp-content/uploads/2024/07/passthrough.webp" alt="" class="wp-image-11417" style="width:304px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/passthrough.webp 444w, https://www.digihunch.com/wp-content/uploads/2024/07/passthrough-227x300.webp 227w" sizes="auto, (max-width: 444px) 100vw, 444px" /&gt;&lt;figcaption class="wp-element-caption"&gt;GPU passthrough&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main advantage to vDGA is that since the GPU is passed through to the guest and the guest uses regular Nvidia drivers, it fully supports everything the Nvidia driver can do natively. This enables all versions of DirectX, OpenGL and even CUDA. The downside is that vDGA is expensive, since you need one GPU per user. There is also a lack of vMotion support. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware added support for vDGA in late 2013. The target market is high-end users with intensive graphical applications (oil&amp;amp;gas, scientific simulations, CAD/CAM, etc&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vGPU (Virtual GPU)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vGPU is also known as Virtual Shared Pass-Through Graphics Acceleration. This technology sites somewhere in between the two previously introduced, as an option to strike a balance between cost-effectiveness and resource-sharing. It is essentially vDGA but with multiple users per GPU, instead of one-to-one mapping. Like vDGA, with vGPU you install the real Nvidia driver in guest VMs, and the hypervisor passes the graphics commands directly to the hypervisor without any translation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vGPU gives you all that plus the ability to share a GPU across up to 8 VMs. The idea of vGPU is that you get better performance than vSGA option, with a portion of cost when compared to vDGA. The use case for vGPU will be the higher-end knowledge workers who need real &amp;#8220;GPU&amp;#8221; access but don&amp;#8217;t need full-on multi-thousand dollar graphics workstations.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="489" height="337" src="https://www.digihunch.com/wp-content/uploads/2020/07/image-5.webp" alt="" class="wp-image-1190" style="width:429px;height:auto"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware partners with Nvidia on vGPU development. Below is the use-case chart from previous VMware white paper:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="900" height="590" src="https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon.webp" alt="" class="wp-image-13153" srcset="https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon.webp 900w, https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon-300x197.webp 300w, https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon-768x503.webp 768w" sizes="auto, (max-width: 900px) 100vw, 900px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram below illustrates the architecture of virtual GPU (NVIDIA Grid):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="734" height="690" src="https://www.digihunch.com/wp-content/uploads/2024/07/GRID.webp" alt="" class="wp-image-11414" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/GRID.webp 734w, https://www.digihunch.com/wp-content/uploads/2024/07/GRID-300x282.webp 300w" sizes="auto, (max-width: 734px) 100vw, 734px" /&gt;&lt;figcaption class="wp-element-caption"&gt;high-level architecture of GRID vGPU&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The best &lt;a href="https://techzone.vmware.com/resource/deploying-hardware-accelerated-graphics-vmware-horizon-7"&gt;white paper&lt;/a&gt; about the three technologies and their use cases is on VMware website.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Identify Graphics driver&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On Linux VM, we can simply use lspci to identify graphics driver.&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@ghrender ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lspci | grep VGA&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;03:00.0 VGA compatible controller: Matrox Electronics Systems Ltd. Integrated Matrox G200eW3 Graphics Controller &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rev 04&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3b:00.0 VGA compatible controller: NVIDIA Corporation GP104GL &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Quadro P5000&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rev a1&lt;span style="color:#f92672"&gt;)&lt;/span&gt;In the result, the far left column is specified domain, e.g. 3b:00.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To display details on graphics card by specified domain (3b:00.0 for example) with memory information:&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@ghrender ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lspci -v -s 3b:00.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3b:00.0 VGA compatible controller: NVIDIA Corporation GP104GL &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Quadro P5000&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rev a1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;prog-if &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;VGA controller&lt;span style="color:#f92672"&gt;])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Subsystem: NVIDIA Corporation Device 11b2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Flags: bus master, fast devsel, latency 0, IRQ 190, NUMA node &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Memory at ab000000 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;32-bit, non-prefetchable&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;16M&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Memory at 382fe0000000 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;64-bit, prefetchable&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;256M&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Memory at 382ff0000000 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;64-bit, prefetchable&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;32M&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;I/O ports at &lt;span style="color:#ae81ff"&gt;6000&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;128&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;virtual&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Expansion ROM at ac080000 &lt;span style="color:#f92672"&gt;[&lt;/span&gt;disabled&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;512K&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;60&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Power Management version &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;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;68&lt;span style="color:#f92672"&gt;]&lt;/span&gt; MSI: Enable+ Count&lt;span style="color:#f92672"&gt;=&lt;/span&gt;1/1 Maskable- 64bit+&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;78&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Express Legacy Endpoint, MSI &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Virtual Channel&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;250&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Latency Tolerance Reporting&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;128&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Power Budgeting &amp;lt;?&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;420&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Advanced Error Reporting&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;600&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Vendor Specific Information: ID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0001&lt;/span&gt; Rev&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; Len&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;024&lt;/span&gt; &amp;lt;?&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;900&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#75715e"&gt;#19&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Kernel driver in use: nvidia&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Kernel modules: nouveau, nvidia_drm, nvidia&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The lshw command can also identify onboard Intel/AMD or Nvidia dedicated GPU:&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@ghrender ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lshw -C display&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *-display&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: VGA compatible controller&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product: Integrated Matrox G200eW3 Graphics Controller&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vendor: Matrox Electronics Systems Ltd.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; physical id: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bus info: pci@0000:03:00.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version: &lt;span style="color:#ae81ff"&gt;04&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt; bits&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; clock: 66MHz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; capabilities: pm vga_controller bus_master cap_list rom&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configuration: driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mgag200 latency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;64&lt;/span&gt; maxlatency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt; mingnt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resources: irq:16 memory:91000000-91ffffff memory:92808000-9280bfff memory:92000000-927fffff&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *-display&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: VGA compatible controller&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product: GP104GL &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Quadro P5000&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vendor: NVIDIA Corporation&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; physical id: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bus info: pci@0000:3b:00.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version: a1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: &lt;span style="color:#ae81ff"&gt;64&lt;/span&gt; bits&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; clock: 33MHz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; capabilities: pm msi pciexpress vga_controller bus_master cap_list rom&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configuration: driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;nvidia latency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resources: iomemory:382f0-382ef iomemory:382f0-382ef irq:190 memory:ab000000-abffffff memory:382fe0000000-382fefffffff memory:382ff0000000-382ff1ffffff ioport:6000&lt;span style="color:#f92672"&gt;(&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;128&lt;span style="color:#f92672"&gt;)&lt;/span&gt; memory:ac080000-ac0fffff&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/07/overview-of-virtualization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 1 of 4 – Hypervisor&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/java-garbage-collection/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Java Garbage Collection&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 1 of 4 – Hypervisor</title><link>https://www.digihunch.com/2020/07/overview-of-virtualization/</link><pubDate>Mon, 27 Jul 2020 22:52:00 -0400</pubDate><guid>https://www.digihunch.com/2020/07/overview-of-virtualization/</guid><description>&lt;p class="wp-block-paragraph"&gt;In broad terms, virtualization of computing resource is about isolation of resources, at different levels. There are five levels of virtualization:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Application level, such as JVM, .NET CLR&lt;/li&gt;&#10;&lt;li&gt;Library (user-level API) level&lt;/li&gt;&#10;&lt;li&gt;Operating system level, such as LXC, Docker, OpenVZ&lt;/li&gt;&#10;&lt;li&gt;Hardware abstraction layer (HAL) level, such as VMware, Xen, etc&lt;/li&gt;&#10;&lt;li&gt;Instruction set architecture (ISA) level&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my context I deal mostly with OS level and HAL (hardware abstraction layer) level of virtualization. In loose terms, the word &lt;em&gt;containerization&lt;/em&gt; refers to &lt;span style="text-decoration: underline;"&gt;OS level virtualization&lt;/span&gt;, while the word &lt;em&gt;virtualization&lt;/em&gt; is exclusively reserved for &lt;span style="text-decoration: underline;"&gt;HAL level virtualization&lt;/span&gt;, also referred to as &lt;span style="text-decoration: underline;"&gt;hypervisor-based virtualization&lt;/span&gt;. This post will just focus on this family of technology and loosely refers to it as virtualization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtualization technology evolved from on-premise data centre environment and now is the backbone of cloud computing. The challenges of IT operation in the era of virtualization involves managing VM sprawling, investigating performance issues, planning capacity and addressing storage I/O block. The idea of virtualization is sharing (thus isolating) resources for better utilization, leading to better return on investment. This posting is to cover only the very basics of virtualization.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-hypervisor"&gt;Hypervisor&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hypervisor is the software layer which provides the capability to run multiple virtual machines on the same physical host. It is broken down into two categories:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Type I hypervisor (aka bare metal hypervisor)&lt;/strong&gt;: directly run on physical hardware. They control the hardware as well as manage the virtual machines. For example, Linux KVM, VMware ESXi, Xen and Microsoft Hyper-V&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Type II hypervisor&lt;/strong&gt;: runs as an application or service on top of the host operating system, which is installed on the bare metal. Guest operating system calls need to traverse via the host operating system stack to reach hardware resource. For example, Oracle Virtual Box, VMware Fusion and Linux Containers (LXC)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://img.vembu.com/wp-content/uploads/2019/12/Hypervisor-Types.png" alt="Type-1 vs Type-2 Hypervisor"/&gt;&lt;figcaption class="wp-element-caption"&gt;Hypervisor Types&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading" id="h-virtualization-techniques"&gt;Virtualization Techniques&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most primitive form of technology that can be arguably categorized under virtualization is hardware emulation, where a piece of (more accessible) hardware imitates another (less accessible). The architecture limits itself in functional testing only, and is not built for performance or production at all.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The original virtualization technology deals with CPU and memory virtualization. In this well-written &lt;a href="https://github.com/skonstantinov89/books/blob/master/Understanding%20Full%20Virtualization%2C%20Paravirtualization%2C%20and%20Hardware%20Assist.pdf"&gt;whitepaper &lt;/a&gt;fromVMware, there are three CPU virtualization techniques introduced for x86 architecture.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The x86 architecture offers four levels of privilege known as Ring 0,1,2 and 3 to operating system and applications to manage access to the computer hardware. User-level applications typically run in Ring 3, the OS must execute its privileged instructions in Ring 0 since it needs to have direct access to memory and hardware. The two main challenges with virtualizing x86 architecture are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A virtualization layer between hardware operating system who expects Ring 0 privilege;&lt;/li&gt;&#10;&lt;li&gt;Some instructions with different semantics when not executed in Ring 0 cannot be virtualized effectively. They need to be translated at runtime.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These challenges make true virtualization of x86 architecture impossible and thus VMware developed three alternative technologies.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Full_virtualization"&gt;&lt;strong&gt;Full virtualization&lt;/strong&gt;&lt;/a&gt; (using binary translation): virtual machine presents a complete simulation of the actual hardware environment so that an unmodified guest OS can run in isolation. The Guest OS is not aware that the underlying environment it is running on is virtualized, and issues hardware calls to communicate with (what it thinks as) hardware. The virtual processors have to understand guest CPU instruction, and reproduce the equivalent CPU instructions of the host machine. VMware&amp;#8217;s technology to address this is called &lt;strong&gt;Binary Translation&lt;/strong&gt;. This overhead makes true full virtualization difficult to achieve. In real life, a virtual environment that provides &amp;#8220;enough representation of the underlying hardware&amp;#8221; can be considered to provide full virtualization as long as it allows guest OS to run without modification. Full virtualization comes with a performance penalty. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Paravirtualization"&gt;&lt;strong&gt;Paravirtualization &lt;/strong&gt;&lt;/a&gt;(aka OS assisted virtualization): refers to communication between the guest OS and the hypervisor to improve performance and efficiency. In this technology, guest OS is modified with an interface to host hardware to be able to communicate and operate seamlessly. Since the guest OS is modified, the VM does not need to be a complete simulation of the hardware. The modified guest OS knows it is running on a virtualized environment, and (vm driver) makes API calls (known as &amp;#8216;hyper calls&amp;#8217;) to the hypervisor. This allows para-virtualization technology to achieve performance closer to non-virtualized environment. However, since paravirtualization cannot support unmodified operating systems, its compatibility and portability is poor. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Hardware-assisted_virtualization"&gt;&lt;strong&gt;Hardware-Assisted Virtualization&lt;/strong&gt;&lt;/a&gt;: hardware vendors such as Intel and AMD both have developed extensions (new features) to simplify virtualization techniques, for example, the introduction of privileged instructions with new CPU execution mode feature to allow hypervisor to run in a new root mode below ring 0. This removed the need for full virtualization and paravirtualization. With VMware originally as a promoter of full virtualization and Xen for paravirtualization, most virtualization technologies today utilizes hardware-assisted virtualization feature, for example, Linux KVM, VMware workstation, VMware fusion, Xen, VirtualBox, etc. Intel&amp;#8217;s virtualization extension is VT-x. AMD&amp;#8217;s counterpart is AMD-V technology.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To virtualize memory, another level of memory virtualization is required (similar to the virtual memory support in Linux). Hypervisor is responsible for mapping guest physical memory to the actual machine memory, and it uses shadow page tables to accelerate the mappings, usually at a performance cost.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-popular-hypervisors"&gt;Popular hypervisors&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the market there are a few popular hypervisor technologies. They are all type 1 hypervisors:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Xen"&gt;Xen &lt;/a&gt;is an open-source &lt;a href="https://xenproject.org/"&gt;hypervisor project&lt;/a&gt; originally developed in Cambridge University, licensed under GPLv2. . Based on that, Citrix developed its commercial product XenServer, a bare-metal virtualization platform with enterprise-grade features for x86 and AMD environments. Oracle VM is another commercial implementation of Xen. The Xen project also supports many cloud platforms such as Openstack, Cloudstac, etc. Xen project supports paravirtualization (Xen-PV) as well as hardware-assisted virtualization (Xen-HVM) for virtualization of X86, IA64, ARM and other CPU architectures. The earlier versions does not support memory overcommit (aka &amp;#8220;dynamic memory optimization&amp;#8221;, &amp;#8220;memory &lt;a href="https://www.digihunch.com/2020/05/understanding-where-the-memory-goes-on-linux-vm/"&gt;ballooning&lt;/a&gt;&amp;#8220;, or as Citrix calls it &amp;#8220;dynamic memory control, DMC&amp;#8221;). This delivers better performance but also has higher budgetary requirement on hardware since there isn&amp;#8217;t room for over-subscription. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hyper-V is a Microsoft product. It executes in high CPU privilege (Microsoft calls it ring -1 which is equivalent to root mode as Intel calls it). On the guest VM, OS kernel and drivers run in ring 0, application rin in ring 3. This eliminates the need for binary translation. Hyper-V does not support memory overcommit either. Hyper-V is well integrated with Windows platform. It supports Linux as well although with some performance penalty.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Linux KVM (Kernel-based Virtual Machine) is a full open-source virtualization solution for GNU/Linux. What makes KVM a special hypervisor is that it uses a loadable kernel module kvm.ko that turns itself into a hypervisor and provides VMs with direct access to the hardware. So it is a type 1 hypervisor despite of the presence of Linux OS. KVM also contains a processor specific module, kvm-intel.ko or kvm-amd.ko. KVM leverages qemu to access devices. Because KVM runs as a process inside of Linux OS, KVM can use many existing feature in Linux kernel. Redhat has an enterprise solution based on KVM.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="850" height="414" src="https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU.webp" alt="" class="wp-image-7813" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU.webp 850w, https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU-300x146.webp 300w, https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU-768x374.webp 768w" sizes="auto, (max-width: 850px) 100vw, 850px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Xen vs KVM&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware &lt;a href="https://en.wikipedia.org/wiki/VMware_ESXi"&gt;ESXi &lt;/a&gt;is VMware&amp;#8217;s premium hypervisor product (not open-source) and is available for &lt;s&gt;free download&lt;/s&gt;, although the advanced features are not free. (Update no free download link &lt;a href="https://www.reddit.com/r/vmware/comments/1amtzvc/esxi_hypervisor_free_gone/"&gt;anymore&lt;/a&gt;.) VMware &lt;a href="https://www.digihunch.com/2018/07/overview-of-vsphere/"&gt;vSphere&lt;/a&gt; is virtualization platform built on top of ESXi, including a whole family of virtualization products.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-market-segments-and-players"&gt;Market segments and players&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtualization involves many market segments such as virtual desktop infrastructure (VDI, for desktop virtualization), server virtualization is the predominant domain in the virtualization of data centre environment. This effort led to Hyper-Converged Infrastructure (HCI) where almost all the traditional hardware resources are software-defined through the virtualization layer. The management of infrastructure is abstracted away from the physical hardware management. The three most fundamental areas in HCI are:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Server (compute) virtualization: the previous section covers the virtualization of memory and x86 CPU, which are the main focus on computing resource virtualization. Additionally, graphics computing resources can be virtualized today. Example products include: VMware vShpere (compute virtualization based on ESXi hypervisor).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Storage_virtualization"&gt;Storage Virtualization&lt;/a&gt;&lt;/strong&gt;: the technology to abstract physical data storage resource to make them appear as if they were a centralized resource. Storage virtualization takes place at three levels depending on the use case: block-level, file-level and object level. Example products include: VMWare vSAN (vSphere-native storage), HPE 3PAR (Tier-1 storage), EMC VxRail, PureStorage Flash Array (Tier 1), etc. Storage Virtualization enables &lt;a href="https://en.wikipedia.org/wiki/Software-defined_storage"&gt;&lt;strong&gt;Software-Defined Storage&lt;/strong&gt; &lt;/a&gt;&lt;strong&gt;(SDS)&lt;/strong&gt;, the provisioning and management of data storage independent of the underlying hardware.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Network_virtualization"&gt;Network Virtualization&lt;/a&gt;&lt;/strong&gt;: the technology to abstract network resources that were traditionally delivered in hardware to software. Network virtualization decouples network services from the underlying hardware management and allows virtual provisioning of an entire network. VLAN is a classic example of network virtualization. There are also various overlay technologies such as VXLAN, which provides an industry framework for overlaying virtualized layer 2 network over layer 3 network (used in Docker network) using an encapsulation mechanism and a control plane. Example products include: VMware NSX Data Center (L2-L7 network and security virtualization platform), Cisco ACI, Palo Alto Panorama. Network Virtualization enables &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Software-defined_networking"&gt;Software-Defined Network&lt;/a&gt; (SDN)&lt;/strong&gt;, an approach to network management that enables dynamic, programmatically efficient network configuration in order to improve network performance and monitoring, making it more like cloud computing than traditional network management.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-delivery-model"&gt;Delivery model&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtualization allows managed service providers (MSPs) to deliver IT service in the following three models:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Iaas (Infrastructure as a Service)&lt;/strong&gt;: MSP delivers VM to customers.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;PaaS (Platform as a Service)&lt;/strong&gt;: MSP delivers environments to customers (e.g. Database as a Service, managed RabbitMQ service, etc).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;SaaS (Software as a Service)&lt;/strong&gt;: MSP delivers entire application for the customer.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://www.redhat.com/cms/managed-files/iaas_focus-paas-saas-diagram-1200x1046.png" alt="What is IaaS?" style="width:608px;height:388px"/&gt;&lt;figcaption class="wp-element-caption"&gt;IT service delivery models enabled by virtualization technology&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since virtualization is the backbone of cloud computing. This model is also referred to as cloud computing delivery model.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-virtualization-and-containerization"&gt;Virtualization and Containerization&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These two concepts are similar and could be confusing to beginners. Both provide a mechanism to isolate computing resource for different applications, for the purpose of higher utilization of resource. The difference lies in how and where the isolation is made. Virtualization requires a guest operating system per VM (OS level isolation), whereas the container technology isolates application processes along with its runtime into a container (dependency level isolation), using some new Linux kernel features such as &lt;em&gt;namespaces &lt;/em&gt;and &lt;em&gt;cgroups&lt;/em&gt;. All containers make their system calls to the container engine on the host operating system. So they share a kernel on the same host. In this sense, container engine running on OS could be considered as type 2 hypervisor.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://dzone.com/storage/temp/10561741-vm-container-figure1.jpg" alt="Image title"/&gt;&lt;figcaption class="wp-element-caption"&gt;From VMs to containers&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware is a major player in enterprise data centre virtualization, which is facing fierce competition from public and private cloud vendors. VMware also has its own private cloud services. Docker is the most popular container technology that conforms to the specifications of Open Container Initiative (OCI), a governance structure for industry standards around container formats and runtimes.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-virtualization-and-cloud"&gt;Virtualization and Cloud&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Among public cloud vendors, AWS &lt;a href="https://cloudacademy.com/blog/aws-ami-hvm-vs-pv-paravirtual-amazon/"&gt;EC2 &lt;/a&gt;used Xen PV and Xen HVM in its earlier implementations. It has transitioned to AWS bare metal. The history is well summarized &lt;a href="http://www.brendangregg.com/blog/2017-11-29/aws-ec2-virtualization-2017.html"&gt;here&lt;/a&gt;. Microsoft Azure runs Azure Hypervisor as the native hypervisor in Azure Cloud Services platform. It is a customized version of Microsoft Hyper-V specifically for Azure platform. With GCP, Google &lt;a href="https://cloud.google.com/compute/docs/faq"&gt;Compute Engine&lt;/a&gt; (GCE) instance runs VMs on KVM as hypervisor. It can also enable nested virtualization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The scope of cloud computing is evolving overtime. It originally only refers to a business model of offering IT services (in one of the three delivery models outlined above) based on virtualization technology. Therefore I cannot make comparison between a technology and a business model. Today, with public cloud vendor extending their offerings (with various managed services and platforms) and people&amp;#8217;s misuse of the terms, the buzz-word &amp;#8220;cloud&amp;#8221; seems to suggest anything that is offered in public cloud service. The essence still remain the same where managed services and managed platforms are built on top of virtualized compute unit under the hood, which are driven by virtualization technologies.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/07/zookeeper-and-kafka-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kafka high-level Overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 2 of 4 – Graphics Computing&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Docker network in different modes</title><link>https://www.digihunch.com/2020/07/dockersnetwork/</link><pubDate>Wed, 01 Jul 2020 20:19:00 -0400</pubDate><guid>https://www.digihunch.com/2020/07/dockersnetwork/</guid><description>&lt;p class="wp-block-paragraph"&gt;Reading notes of &amp;#8220;Docker DeepDive&amp;#8221;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker networking is backed by libnetwork, which is an implementation of &lt;a href="https://github.com/moby/libnetwork/blob/master/docs/design.md"&gt;Container Network Model&lt;/a&gt; (CNM), an open-source pluggable architecture designed to provide networking to containers. Libnetwork also provides native service discovery and basic container load balancing solution. Docker networking also involves some drivers that extend the CNM model with specific network topology implementation.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Sandbox&lt;/strong&gt; &amp;#8211; an isolated network stack, including Ethernet interfaces, ports, routing tables, and DNS config, usually implemented through Linux namespace.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Endpoints&lt;/strong&gt; &amp;#8211; behave like regular network adapters, and can only be connected to a single network at a time. It connects sandbox to network. Endpoint is implemented in veth pair in Linux.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Networks&lt;/strong&gt; &amp;#8211; software implementation of an 802.1 bridge (aka switch). They group together, and isolate, a collection of endpoints that need to communicate.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://www.dclessons.com/uploads/2019/09/Docker-7.4.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker company separates network project out from its container project, as a plugin called libnetwork, which is developed in Golang and compliant to CNM. Libnetwork is the official implementation of CNM.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Libnetwork supports the following network modes:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;network mode&lt;/td&gt;&lt;td&gt;mechanism&lt;/td&gt;&lt;td&gt;use case&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;null&lt;/td&gt;&lt;td&gt;no network is provided to containers&lt;/td&gt;&lt;td&gt;quarantined environment for security&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bridge&lt;/td&gt;&lt;td&gt;containers communicate with each other through bridge&lt;/td&gt;&lt;td&gt;containers needs to communicate with each other or with host service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;host&lt;/td&gt;&lt;td&gt;process in container has access to host network stack and use host port&lt;/td&gt;&lt;td&gt;container needs to use host network stack (e.g. licence by mac address)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;container&lt;/td&gt;&lt;td&gt;place containers in a single net namespace so they can communicate with each other as localhost&lt;/td&gt;&lt;td&gt;proxy, kubernetes&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Linux veth comes in pairs to connect virtual network devices. For example, connect two net namespaces to allow intercommunication. Linux bridge is a virtual device, to connect two net namespaces.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://developers.redhat.com/blog/wp-content/uploads/2018/10/veth.png" alt="Introduction to Linux interfaces for virtual networking - Red Hat Developer"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Dockers ships with several built-in drivers, known as native drivers or local drivers, such as bridge, overlay and macvlan on Linux. There are also 3rd-party network drivers for docker (aka remote drivers).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-host-network"&gt;Host network&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this mode libnetwork will not create network and net namespace for container. Container process shares the network configuration of the host, and therefore uses the ports on host. Other than network sharing, other aspects (e.g. process, file system, hostname, etc) are separated from host.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-bridge-networks"&gt;Bridge networks&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This type of network only exist on a single Docker host and can only connect containers that are on the same host. The word bridge refers to 802.1d bridge (layer 2 switch), which is used to connect multiple network interfaces.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Every Docker host gets a default single-host network, called &lt;span style="text-decoration: underline;"&gt;bridge&lt;/span&gt; on Linux. This is the network that all new containers will attach to by default.&lt;br&gt;Docker networks built with the bridge driver on Linux hosts are based on the linux bridge technology that has existed in the Linux kernel for a while. They&amp;#8217;re high performance and extremely stable. Linux &lt;em&gt;&lt;strong&gt;brctl&lt;/strong&gt;&lt;/em&gt; tool can inspect the linux bridge.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Bridge networks allows container on the same host to communicate with each other. Port mapping allows network connectivity between container and host. Traffic hitting host port will be redirected to container port.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-multi-host-overlays"&gt;Multi-host overlays&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cross-host networking usually uses an overlay network, which builds a mesh between host and employs a large block of IP addresses within that mesh. A mesh network is a local network topology in which the infrastructure nodes connect directly, dynamically and non-hierarchically to as many other nodes as possible and cooperate with one another to efficiently route data from/to clients.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can attach a service to overlay network, which spans across multiple Docker hosts so that containers on different hosts can communicate &lt;span style="text-decoration: underline;"&gt;at layer 2&lt;/span&gt;. They are much better alternatives than bridge network for container-to-container communication. Overlay networking is very common due to its scalability. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The trick is basically the layer 2 frame of the overlay network is encapsulated into layer 3 datagram transmitted across underlay network, at layer 3. This is achieved through VXLAN tunnels, which allows you to create a virtual Layer 2 network on top of an existing Layer 3 infrastructure. VXLAN is an encapsulation technology that existing routers and network infrastructure just see as regular IP/UDP packets without issue.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To create the virtual Layer 2 overlay network, a VXLAN tunnel is created through the underlying Layer 3 IP infrastructure (aka underlay network). Each end of the VXLAN tunnel is terminated by a &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VXLAN Tunnel Endpoint (VTEP)&lt;/span&gt;&lt;/strong&gt;. It&amp;#8217;s this VTEP that performs the encapsulation/de-encapsulation.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-vxlan-networking"&gt;VXLAN networking&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To accomplish overlay network across multiple hosts, a new network sandbox was created on each host. A sandbox is like a container, but instead of running an application, it runs an isolated network stack &amp;#8211; one that&amp;#8217;s sandboxed from the network stack of the host itself.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;&lt;em&gt;virtual switch&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt; (aka virtual bridge) called Br0 is created inside the sandbox. A &lt;strong&gt;&lt;em&gt;VTEP&lt;/em&gt;&lt;/strong&gt; is also created with one end plumbed into the Br0 virtual switch, and the other end plumbed into the host network stack (VTEP). The end in the host network gets an IP address on the underlay network the host is connected to and is bound to a UDP socket on port 4789. The two VTEPs on each host create the overlay via a VXLAN tunnel.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each container then gets its own virtual Ethernet (veth) adapter that is also plumbed into the local Br0 virtual switch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s go over an example in the following diagram, where container C1 with an overlay IP needs to communicate to another container C2, with a different overlay IP, sitting on a different node (Docker host). Each node has its own underlay IP.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://img1.wsimg.com/isteam/ip/ada6c322-5e3c-4a32-af67-7ac2e8fbc7ba/8.jpg/:/cr=t:0%25,l:0%25,w:100%25,h:100%25/rs=w:1280" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IP communication details:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;C1 creates the IP datagram with destination IP (C2) and sends it over its veth interface, which is connected to the Br0 virtual switch on the host node. &lt;/li&gt;&#10;&lt;li&gt;The virtual switch doesn&amp;#8217;t know where to send the datagram, as it doesn&amp;#8217;t have an entry in its ARP table that corresponds to the destination IP address. As a result, it floods the packet to all ports. The VTEP interface connected to Br0 knows how to forward the frame, so responds with its own MAC address. &lt;/li&gt;&#10;&lt;li&gt;This is a proxy APR reply and results in the Br0 switch learning how to forward the packet. So it updates its ARP mapping the destination IP address to the MAC address of the local VTEP.&lt;/li&gt;&#10;&lt;li&gt;The VTEP knows about C2 because all newly started containers have their network details propagated to the other nodes in the Swarm using the network&amp;#8217;s built-in gossip protocol. When the packet arrives at node2&lt;/li&gt;&#10;&lt;li&gt;The VTEP encapsulates the frame so it can be sent over the underlay transport infrastructure, by adding a VXLAN header to the Ethernet frame. The VXLAN header contains the VXLAN network ID (VNID) which is used to map frames from VLANs to VXLANs and vice versa.&lt;/li&gt;&#10;&lt;li&gt;Each VLAN gets mapped to VNID, so that the packet can be de-encapsulated on the receiving end and forwarded to the correct VLAN. This is how network isolation is maintained. The encapsulation also wraps the frame in a UDP packet with the IP address of the remote VTEP on node2 in the destination IP field, and the UDP port 4789 socket information. The underlying network does not know that it is transporting data frames for the overlay network.&lt;/li&gt;&#10;&lt;li&gt;When the packet arrives at node2, the kernel sees that it&amp;#8217;s addressed to UDP port 4789. The kernel also knows that it has a VTEP interface bound to this socket. As a result, it sends the packet to the VTEP, which reads the VNID, de-encapsulates the packet, and sends it on to its own local Br0 switch on the VLAN that corresponds the VNID. From there it is delivered to container C2&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker also supports Layer 3 routing within the same overlay network. For example, you can create an overlay network with two subnets, and Docker will take care of routing between them. Two subnets will require two virtual switches, Br0 and Br1, being created inside the sandbox, and routing happens by default.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-plugging-into-existing-vlans"&gt;Plugging into existing vLANs&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The built-in MACVLAN driver was created for onnect containerized apps to external physical network. A good example is partially containerized app, in which the containerized parts will need a way to communicate with the non-containerized parts still running on existing physical networks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To connect the container interface through the host interface to an external network, the host NIC needs to be in promiscuous mode. For public cloud, this is most likely prohibited. For data centers, this depends on the network policy.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker MACVLAN driver is built on top of Linux kernel driver with the same name. As such, it supports VLAN trunking. This means we can create multiple MACVLAN networks and connect containers on the same Docker host to them.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="392" height="230" src="https://www.digihunch.com/wp-content/uploads/2020/07/image-2.webp" alt="" class="wp-image-1169" style="width:540px;height:317px"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For connectivity issues between containers, it&amp;#8217;s worth checking both the daemon logs (on host) and container logs.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-service-discovery"&gt;Service discovery&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;allows all containers and Swarm services to locate each other by name, as long as they are on the same network. This leverages Docker&amp;#8217;s embedded DNS server as well as a DNS resolver in each container.&lt;br&gt;Each Swarm Service and standalone container started with the &amp;#8211;name flag will register its name and IP address with the Docker DNS service.&lt;br&gt;This name resolution, however, only works within the same network.&lt;br&gt;It is also possible to configure Swarm services and standalone containers with customized DNS options in case embedded Docker DNS server cannot resolve a query (/etc/resolv.conf)&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="381" height="103" src="https://www.digihunch.com/wp-content/uploads/2020/07/image-3.webp" alt="" class="wp-image-1170" style="width:547px;height:148px"/&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-ingress-load-balancing"&gt;Ingress load balancing&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Services published via ingress mode (by default, as opposed to host mode) can be accessed from any node in the Swarm, even nodes not running a service replica. Ingress mode uses a layer 4 routing mesh called the Service Mesh or the Swarm Mode Service Mesh.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="396" height="206" src="https://www.digihunch.com/wp-content/uploads/2020/07/image-4.webp" alt="" class="wp-image-1171" style="width:557px;height:290px"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Updates:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most common network modes that I use are host and bridge. With host network mode, container exposes ports on the interface of the host machine. Containers talk to each other via that interface. With bridge network, containers have their own namespace of networking separate from the one from the interface of the hosts, with a bridge getting the two networks connected.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-reference"&gt;Reference&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker Deep dive&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="830" height="1024" src="https://www.digihunch.com/wp-content/uploads/2023/01/docker-deep-dive-830x1024.jpeg" alt="" class="wp-image-7915" style="width:209px;height:258px" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/docker-deep-dive-830x1024.jpeg 830w, https://www.digihunch.com/wp-content/uploads/2023/01/docker-deep-dive-243x300.jpeg 243w, https://www.digihunch.com/wp-content/uploads/2023/01/docker-deep-dive-768x947.jpeg 768w, https://www.digihunch.com/wp-content/uploads/2023/01/docker-deep-dive.jpeg 1000w" sizes="auto, (max-width: 830px) 100vw, 830px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/06/iterate-through-cassandra-table-with-datastax-python-driver/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;DataStax Python Driver&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/07/emc-productlines/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;EMC Isilon storage product&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Performance Analysis</title><link>https://www.digihunch.com/2020/06/performance-analysis-tools/</link><pubDate>Fri, 19 Jun 2020 16:47:01 -0400</pubDate><guid>https://www.digihunch.com/2020/06/performance-analysis-tools/</guid><description>&lt;h3 class="wp-block-heading" id="h-overview"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In 2015, Brendan Gregg posted two great articles&lt;a href="https://netflixtechblog.com/linux-performance-analysis-in-60-000-milliseconds-accc10403c55"&gt; &lt;/a&gt;on Netflix blog: &lt;a href="https://netflixtechblog.com/linux-performance-analysis-in-60-000-milliseconds-accc10403c55"&gt;Linux Performance Analysis in 60 seconds&lt;/a&gt;, and &lt;a href="https://netflixtechblog.com/netflix-at-velocity-2015-linux-performance-tools-51964ddb81cf"&gt;Linux Perfomrance Tools&lt;/a&gt;. They have great value when I was in a urgency to spot performance issues. The articles cover the essential tools for performance troubleshooting, including:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Check out load averages: w or uptime&lt;/li&gt;&#10;&lt;li&gt;Print kernel ring buffer: dmesg -T&lt;/li&gt;&#10;&lt;li&gt;Virtual memory status: vmstat 1&lt;/li&gt;&#10;&lt;li&gt;Multiple processor staticstics: mpstat -P ALL 1&lt;/li&gt;&#10;&lt;li&gt;Task status: pidstat 1&lt;/li&gt;&#10;&lt;li&gt;CPU and I/O status: iostat -xz 1&lt;/li&gt;&#10;&lt;li&gt;Free memory check: free -m&lt;/li&gt;&#10;&lt;li&gt;Network Activity record: sar -n DEV 1&lt;/li&gt;&#10;&lt;li&gt;TCP activity record: sar -n TCP,ETCP 1&lt;/li&gt;&#10;&lt;li&gt;Display processes: top&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We will dive into each of them in the next section.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-troubleshooting"&gt;Basic Troubleshooting&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The command w is equivalent of uptime (which shows uptime since boot) and who (which shows logged-in users). It also displays load average for the last 1 minute, 5 minutes and 15 minutes. The number of load average reflects the overall system load (CPU + disks), and it is further discussed in this &lt;a href="http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html"&gt;post&lt;/a&gt; with a simple take away:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;If the averages are 0.0, then your system is idle.&lt;/li&gt;&#10;&lt;li&gt;If the 1 minute average is higher than the 5 or 15 minute averages, then load is increasing.&lt;/li&gt;&#10;&lt;li&gt;If the 1 minute average is lower than the 5 or 15 minute averages, then load is decreasing.&lt;/li&gt;&#10;&lt;li&gt;If they are higher than your CPU count, then you might have a performance problem (it depends).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When Linux load averages increase, you know you have higher demand for resources (CPUs, disks, and some locks), but you aren&amp;#8217;t sure which. You will need to switch to other metrics. Brendan recommends that you don&amp;#8217;t spend more than 5 seconds on these numbers.&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ w&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 12:14:10 up &lt;span style="color:#ae81ff"&gt;46&lt;/span&gt; days, 16:41, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; users, load average: 2.69, 2.44, 2.29&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dhunch pts/0 w6v-ghas01 24Jun20 3days 0.36s 0.30s ssh c7v-bastion&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dhunch pts/1 202.95.88.111 12:02 2.00s 0.00s 0.00s w&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dhunch pts/4 w6v-ghas01 17Jun20 15days 0.15s 0.07s view readme.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Before moving to more insightful metrics, it is also worth a quick look into the kernel ring buffer with dmesg command (dmesg -T | less +G). This will allow us to capture obvious issues such as oom-killer or TCP request dropping.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The vmstat tool reports the statistics of &lt;strong&gt;virtual memory&lt;/strong&gt;. Servers have a fixed amount of physical memory, but they can run a set of applications that use a much larger amount of virtual memory. Application tend to reserve more memory than they need, and they usually operate on only a subset of their memory. In both cases, the operating system can keep the unused parts of memory on disk, and page it into physical memory only if it is needed. For the most part, this kind of memory management works well. But it doesn&amp;#8217;t always with Java applications due to Java heap. Once a system start swapping &amp;#8211; moving pages of data from main memory to disk, and vice versa, the performance tend to be bad. Systems must be configured so that swapping never occurs.&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ vmstat &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;procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r b swpd free buff cache si so bi bo in cs us sy id wa st&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;385928&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734692&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;93&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;387732&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734704&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;43&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8017&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8524&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;85&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;387608&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734904&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;57&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6768&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7680&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;389008&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734904&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;44&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6366&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7300&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;421728&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36700144&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8141&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7957&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;421984&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36702048&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;467&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8994&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8362&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;85&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&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 tool prints key server statistics each line, with the first line showing the average since boot. Here lists the explanation of some columns:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;r&lt;/strong&gt;: number of processes running on CPU and waiting for a turn. This provides a better signal than load averages for determining CPU saturation, as it does not include I/O. To interpret: an “r” value greater than the CPU count is saturation.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;swpd&lt;/strong&gt;: the amount of virtual memory used. This number should align with the used column for Swap row from free command.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;buff, cache&lt;/strong&gt;: buffer and cache. They should align with the buff/cache column form Mem row from free command.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;free&lt;/strong&gt;: free memory in kilobytes. This number should align with the free column for Mem row from free command. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;si, so&lt;/strong&gt;: swap-ins and swap-outs. As mentioned, if these are non-zero, you&amp;#8217;re out of memory.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;bi, bo&lt;/strong&gt;: blocks received from and sent to a blcok device (# of block per second)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;in, cs&lt;/strong&gt;: number of &lt;a href="https://en.wikipedia.org/wiki/Interrupt"&gt;interrupt&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/Context_switch"&gt;context switches&lt;/a&gt; per second.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;us, sy, id, wa, st&lt;/strong&gt;: user, system, idle, wait I/O and stolen times. These are breakdowns of CPU time, on average across all CPUs. They should add up to 100% (or close). stolen time is amount of CPU time needed by a guest virtual machine that is not provided by the host. IO wait time is the CPU time waiting for I/O activity. Idle time could be several things: the process may be waiting for something (e.g. a response from database); the process may be blocked by a thread lock; or the process simply has nothing to do. user and system times are CPU times spent on user tasks and kernel tasks, respectively.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of these columns, watch for columns r, free, buff, cache, us, sy, id and wa at minimum. The combination of us and sy confirms if CPUs are busy. A constant degree of wa points to a disk bottleneck with too much time spent on pending disk I/O. The sy (kernel time) is necessary for I/O processing but sy stays high (e.g. constantly over 20%), it becomes interesting. Perhaps the kernel is processing I/O inefficiently.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For further per-CPU stats, use mpstat command (-P ALL), to prind CPU time breakdowns per CPU and check for imbalance. A single host CPU can be evidence of a single-threaded application. Here is an example output from a system of 16 CPU cores.&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ mpstat -P ALL &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;Linux 3.10.0-1062.12.1.el7.x86_64 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c7v-ghintapp01.digihunch.com&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 08/01/20 _x86_64_ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; CPU&lt;span style="color:#f92672"&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;13:47:23 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 all 13.77 0.00 0.19 0.00 0.00 0.00 0.00 0.00 0.00 86.05&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; 98.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 2.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; 2.94 0.00 0.98 0.00 0.00 0.00 0.00 0.00 0.00 96.08&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;9&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; 97.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 3.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; 0.99 0.00 0.99 0.00 0.00 0.00 0.00 0.00 0.00 98.02&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; 1.98 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.02&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;15&lt;/span&gt; 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For a per process summary of CPU consumption, use pidstat command. It can be thought of a periodical snapshot of top command, allowing you to watch for patterns. The %CPU column is the total across all CPUs so 5 CPUs have a maximum value of 500.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If vmstate indicates some I/O issue, iostat tool can help us understand block devices, on both the workload applied and the resulting performance. Key columns are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;r/s, w/s, rkB/s, wkB/s&lt;/strong&gt;: delivered reads, writes, read Kbytes, and write Kbytes per second to the device. Use these for workload characterization. A performance problem may simply be due to an excessive load applied.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;await&lt;/strong&gt;: the average wait time for I/O in milliseconds. This is the time that the application suffers, as it includes both time queued and time being serviced. Larger than expected average times can be an indicator of device saturation, or malfunction.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;avgqu-sz&lt;/strong&gt;: the average number of requests issued to device. Values greater than 1 can be evidence of saturation (although devices can typically operate on requests in parallel, especially virtual devices which front multiple back-end disks.)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;%util&lt;/strong&gt;: device utilization. This is really a busy percent, showing the time each second that the device was doing work. Values greater than 60% typically lead to poor performance (which should be seen in await), although it depends on the device. Values close to 100% usually indicate saturation.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I/O problem may either be inefficiencies in application that issues I/O request, or slowing disk unable to keep up with I/O requests. We review two examples here to illustrate each situation. The first output is as follows:&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;% iostat -xm &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;avg-cpu: %user %nice %system %iowait %steal %idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 23.45 0.00 37.89 0.10 0.00 38.56&#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; Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sda 0.00 11.60 0.60 24.20 0.02 0.14 13.35 0.15 6.06 5.33 6.08 0.42 1.04&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the first example, the disk stat loosk up at first glance. The w_await (time to service I/O write) is fairly low at 6.08ms. However, the system is spending 37.89% of its time in the kernel. If all that system time is from the application, it suggest something inefficient is happening. The fact that the system is doing 24.2 writes per second is another clue: that is alot when writing only 0.14 MB per second (MBps). I/O has become a bottleneck, and the next step would be to look into how the application is performing its writes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second example output is as follows:&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;% iostat -xm &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;avg-cpu: %user %nice %system %iowait %steal %idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 35.05 0.00 7.85 47.89 0.00 9.20&#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; Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sda 0.00 0.20 1.00 163.40 0.00 81.09 1010.19 142.74 866.47 97.60 871.17 6.08 100.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example, it tells us that processes are spending 47.89% of their time in iowait, and the data to complete the I/O (w_await) is 871ms, the queue size is large, and the disk is writing at 81MB per second. This all points to disk I/O as a problem and that the amount of I/O in the application (or elsewhere in the system) must be reduced.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Bear in mind that poor performing disk I/O isn&amp;#8217;t necessarily an application issue. Many techniques are typically used to perform I/O asynchronously, so that the application doesn&amp;#8217;t block and suffer the latency directly (e.g. read-ahead for reads, and buffering for writes, also refer to &amp;#8220;&lt;a href="https://robertovitillo.com/why-you-should-measure-tail-latencies/"&gt;tail latency&lt;/a&gt;&amp;#8220;). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the acceptable utilization metric depends on the configuration of block device. If the storage is a logical disk device fronting many back-end disks (e.g. RAID 0), then 100% utilization may just mean that some I/O is being processed 100% of the time, however, the back-end disks may be far from being saturated, and may even be able to handle more work.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The free command gives the breakdown of memory usage. The right two columns are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;buffers&lt;/strong&gt;: for the buffer cache, used for block device I/O.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;cached&lt;/strong&gt;: for the page cache, used by file systems.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We just want to check that these aren&amp;#8217;t near-zero in size, which can lead to higher disk I/O (confirm using iostat), and worse performance. Linux uses free memory for the caches, but can reclaim it quickly if applications need it. So in a way the cached memory should be included in the free memory column. In this case, it&amp;#8217;s included in the available column. This &lt;a href="https://www.linuxatemyram.com/"&gt;website &lt;/a&gt;has further details.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To check interface stat, nicstat is a great tool but it isn&amp;#8217;t available by default in Linux. Instead we can run sar (-n DEV) to retrieve stats. &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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ sar -n DEV &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;Linux 3.10.0-1062.12.1.el7.x86_64 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c7v-ghintapp01.digihunch.com&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 08/01/20 _x86_64_ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; CPU&lt;span style="color:#f92672"&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;16:51:25 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:26 eth0 3089.00 934.00 3815.33 834.61 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:26 lo 464.00 464.00 2289.07 2289.07 0.00 0.00 0.00&#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;16:51:26 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:27 eth0 956.00 586.00 826.66 211.34 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:27 lo 213.00 213.00 196.00 196.00 0.00 0.00 0.00&#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;16:51:27 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:28 eth0 349.00 181.00 52.32 147.19 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:28 lo 244.00 244.00 81.13 81.13 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Here rxkB/s/s and txkB/s represents receive and transmission throughput, as a measure of workload. If they reach the limit then the interface is the bottleneck.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On top of interface is the TCP layer, which can be monitored with sar again (-n ECP, ETCP). The key metrics are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;active/s&lt;/strong&gt;: number of locally-initiated (e.g. via connect()) TCP connections per second&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;passive/s&lt;/strong&gt;: number of remotely-initiated (e.g. via accept()) TCP connections per second&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;retrans/s&lt;/strong&gt;: number of TCP retransmits per second&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The active and passive counts are often useful as a rough measure of server load. It might help to think of active as outbound, and passive as inbound, but this isn&amp;#8217;t strictly true (e.g. consider a localhost to localhost connection). Retransmits are a sign of network or server issue; it may be an unreliable network (e.g. public Internet), or it may be due to a server being overloaded and dropping packets.&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;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ sar -n TCP,ETCP &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;Linux 3.10.0-1062.12.1.el7.x86_64 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c7v-ghintapp01.digihunch.com&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 08/01/20 _x86_64_ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; CPU&lt;span style="color:#f92672"&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;16:52:29 active/s passive/s iseg/s oseg/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:30 0.00 1.00 28.00 35.00&#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;16:52:29 atmptf/s estres/s retrans/s isegerr/s orsts/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:30 0.00 0.00 0.00 0.00 0.00&#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;16:52:30 active/s passive/s iseg/s oseg/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:31 8.00 8.00 200.00 317.00&#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;16:52:30 atmptf/s estres/s retrans/s isegerr/s orsts/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:31 0.00 1.00 1.00 0.00 3.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Last but not least is our favourite command top, which includes many of the metrics covered in previous tools. The downside to top is it is harder to see patterns over time, which may be more clear in tools like vmstat and pidstat, both of which produce rolling output.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Several tools introduced here involves sar, which is a great monitoring tool on its own that we need to be familiar with.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-system-activity-report-sar"&gt;System Activity Report (SAR)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Further to the basic tools, the sar command is very helpful as it stores historical stat every 10 minutes. Sar keeps 18 types of reports, all stored in /var/log/sa/. When viewing the report file, you may pipe the result to less command so it only prints header once. For example, if you would like to print CPU report for the 2nd of the month:&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;&lt;span style="color:#75715e"&gt;# sar -u -f /var/log/sa/sar02 | less&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;If you check NFS client statistics for the 31st&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; &lt;span style="color:#75715e"&gt;# sar -n NFS -f /var/log/sa/sar31&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;If you need to check network server statistics for today&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;&lt;span style="color:#75715e"&gt;# sar -n NFS -f /var/log/sa/sar31&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;Below are all types of reports:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;-u CPU utilization&lt;/li&gt;&#10;&lt;li&gt;-w task creation and system switching activity&lt;/li&gt;&#10;&lt;li&gt;-W swapping statistics&lt;/li&gt;&#10;&lt;li&gt;-B report paging&lt;/li&gt;&#10;&lt;li&gt;-b report I/O and transfer rate statistics&lt;/li&gt;&#10;&lt;li&gt;-R report memory statistics&lt;/li&gt;&#10;&lt;li&gt;-r memory utilization&lt;/li&gt;&#10;&lt;li&gt;-S swap space utilization&lt;/li&gt;&#10;&lt;li&gt;-H huge pages utilization statistics&lt;/li&gt;&#10;&lt;li&gt;-v inode&lt;/li&gt;&#10;&lt;li&gt;-q queue length and load average&lt;/li&gt;&#10;&lt;li&gt;-y TTY device activity&lt;/li&gt;&#10;&lt;li&gt;-d activity for each block device&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, DEV (per interface)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, EDEV (error per interface)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, NFS (NFS client)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, NFSD (NFS server)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, SOCK (socket usage)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-berkeley-packet-filter-bpf-compiler-collection-bcc-tools"&gt;Berkeley Packet Filter (BPF) Compiler Collection (bcc) tools&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For advanced, low-level performance troubleshooting, the BCC tools provide a suite of tools. Here we only cover the installation of it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In CentOS, install bcc-tools package with yum. When you try to run a command, such as cachestat, if you come across this error:&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-bash: cachestat: command not found&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you will need to add its path to default:&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 PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$PATH:/usr/share/bcc/tools&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now if you run into this error:&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chdir(/lib/modules/3.10.0-1062.12.1.el7.x86_64/build): No such file or directory&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Traceback (most recent call last):&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The file listed is a symbolic link, and if it is missing, you just need to install kernel-headers that matches the kernel version:&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;yum install kernel-headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you may use tools in /usr/share/bcc/tools. For example, cachestat help you display page cache hit ratio; gethostlatency shows DNS resolution latency; tcpconnect prints out active tcp connections (made via connect system call):&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[root@dhunch ~]# /usr/share/bcc/tools/tcpconnect -t -P &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt; | gawk &lt;span style="color:#e6db74"&gt;&amp;#39;{ print strftime(&amp;#34;%F %T  &amp;#34;), $0 }&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 00:16:57   TIME(s)  PID    COMM         IP SADDR            DADDR            DPORT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 02:16:57   0.000    15241  QNetworkAcce 4  10.100.22.21    10.101.84.10    8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 02:16:57   0.064    15241  QNetworkAcce 4  10.100.22.21    10.101.84.10    8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 02:16:57   0.438    15241  QNetworkAcce 4  10.100.22.21    10.101.84.10    8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command above outputs a&amp;nbsp;time and pid stamped log line every time&amp;nbsp;a TCP connection is made to port 8080; tcpaccept traces passive tcp connections (via accept system call). These tools are not as intrusive as tcpdump.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is beyond the purpose of this article to get into details of each tool in the BFP suite. The tools are covered in detail in books &amp;#8220;&lt;a href="https://amzn.to/3fEWNkq"&gt;BPF Performance Tools&lt;/a&gt;&amp;#8221; and &amp;#8220;Linux Observability with BPF&amp;#8221;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/06/network-analyzer-capture-filter-and-display-filter/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Capture filter and Display filter in Network Analyzer&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/06/iterate-through-cassandra-table-with-datastax-python-driver/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;DataStax Python Driver&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Capture filter and Display filter in Network Analyzer</title><link>https://www.digihunch.com/2020/06/network-analyzer-capture-filter-and-display-filter/</link><pubDate>Wed, 10 Jun 2020 21:21:18 -0400</pubDate><guid>https://www.digihunch.com/2020/06/network-analyzer-capture-filter-and-display-filter/</guid><description>&lt;p class="wp-block-paragraph"&gt;Capture filter is set before collecting packets. It is applied at the time of data acquisition and it impacts the size of the capture. It does not have as many variations as display filter and is usually not aware of protocols above TCP/UDP layer. A common form of capture filter is BPF (Berkerly Packet Filter) which is used in Linux Socket Filtering (e.g. &lt;a href="https://www.digihunch.com/2018/02/tcpdump-and-wireshark/"&gt;tcpdump&lt;/a&gt;).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Basic form is:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[tcp|udp] [src|dst] host 192.168.1.2 port 1234&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#39;tcp dst port 8080 and src host 147.206.160.9&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Display filter is set after packet collection. It is applied at the time of data manipulation. It does not impact the size of capture, but it controls how the data is presented (typically for analysis purpose). Display filter may support a variety of expressions that are interpreting data at TCP/UDP layer or above, for example HTTP. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here are some examples:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(tcp.flags.syn == 1) || (tcp.flags.reset == 1)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(tcp.flags.reset == 1) || (http.request.method==GET) ||&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(tcp.flags.reset == 1)||(http.request.uri contains &amp;#34;/box/url/string&amp;#34;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;||(http.response.code == 200)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://wiki.wireshark.org/DisplayFilters"&gt;Here&lt;/a&gt; are some further examples provided by Wireshark.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more details about the usage of capture filter and display filter, here is a page with &lt;a href="https://packetlife.net/blog/2008/oct/18/cheat-sheets-tcpdump-and-wireshark/"&gt;cheatsheet&lt;/a&gt;. Example for tcpdump on the left and wireshark in the middle and on the right.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To view http packet in shell terminal, there is also a helpful tool called httpry. You can applied BPF styled filter for capture, and organize display column. The drawback is there is no display filter so you&amp;#8217; would have to use grep. Here is an 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;httpry -i eth0 &lt;span style="color:#e6db74"&gt;&amp;#39;tcp dst port 8080 and src host 147.206.160.9&amp;#39;&lt;/span&gt; -m GET -f Timestamp,x-correlation-id,x-userid,Request-URI | grep -v -P &lt;span style="color:#e6db74"&gt;&amp;#39;\t\-\t&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/06/wsl2-environment-on-windows-10/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Setup WSL2 (and Docker) on Windows 10&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/06/performance-analysis-tools/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Performance Analysis&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Docker Compose, Docker Stack and Docker Swarm</title><link>https://www.digihunch.com/2020/05/docker-swarm-brief-notes/</link><pubDate>Sun, 24 May 2020 21:58:03 -0400</pubDate><guid>https://www.digihunch.com/2020/05/docker-swarm-brief-notes/</guid><description>&lt;p class="wp-block-paragraph"&gt;This posting covers some basic docker orchestration tools.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Compose&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker Compose&amp;#8217;s predecessor is a tool called Fig developed by Orchard, which was acquired by Docker in 2014, with Fig renamed to Docker Compose. Docker Compose is the official container management tool. It is essentially a python script that parses yaml file, to make Docker API calls to manage containers dynamically. It is installed along with Docker on MacOS and Windows. On Linux, you will have to download package with curl command and install manually. Docker Compose has three versions so far and we should create new template with v3. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Docker compose yaml template consists of three parts:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;services&lt;/strong&gt;: similar to docker run&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;build: specify Dockerfile to build image&lt;/li&gt;&#10;&lt;li&gt;cap_add, cap_drop: specify kernel capabilities (e.g. NET_ADMIN, SYS_ADMIN)&lt;/li&gt;&#10;&lt;li&gt;command: override default startup command by container&lt;/li&gt;&#10;&lt;li&gt;container_name&lt;/li&gt;&#10;&lt;li&gt;depends_on&lt;/li&gt;&#10;&lt;li&gt;devices: map host device to container&lt;/li&gt;&#10;&lt;li&gt;dns&lt;/li&gt;&#10;&lt;li&gt;dns_search:&lt;/li&gt;&#10;&lt;li&gt;entryppoint: override entry point from image&lt;/li&gt;&#10;&lt;li&gt;env_file: specify file that stores environment variable&lt;/li&gt;&#10;&lt;li&gt;environment: specify environment variable&lt;/li&gt;&#10;&lt;li&gt;image: specify the location of image&lt;/li&gt;&#10;&lt;li&gt;pid: share the PID namespace with host&lt;/li&gt;&#10;&lt;li&gt;ports: expose network ports. HOST:CONTAINER&lt;/li&gt;&#10;&lt;li&gt;networks&lt;/li&gt;&#10;&lt;li&gt;volumes: mount host volume to container&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;networks&lt;/strong&gt;: similar to docker network create&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;volumes&lt;/strong&gt;: similar to docker volume create&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is a typical structure of docker compose yaml template (wordpress):&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;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3.8&amp;#34;&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;services&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;mysql&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;image:mysql:5.7&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;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;mysql_data:/var/lib/mysql&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;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;always&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;environment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;MYSQL_ROOT_PASSWORD:root&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;MYSQL_DATABASE:mywordpress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;MYSQL_USER:digihunch&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;MYSQL_PASSWORD:hunchdigi&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;wordpress&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;depends_on&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;mysql&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;wordpress:php7.4&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:#e6db74"&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;restart:always&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;environment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;WORDPRESS_DB_HOST:mysql:3306&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;WORDPRESS_DB_USER:digihunch&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;WORDPRESS_DB_PASSWORD&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hunchdigi&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;WORDPRESS_DB_NAME&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;digihunch &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;networks&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;frontend&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;backend&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;volumes&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;mysql-data&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;In summary, Docker Compose is an orchestration tool for &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;single host&lt;/span&gt;&lt;/strong&gt;, typically seen in development and test environment with dependencies between services.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Stack&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A stack is a set of related services and infrastructure that gets deployed and managed as a unit. A docker stack file has the same format as Docker Compose file, with the only requirement that the version: key specify a value of 3.0. The other difference between Docker Stacks and Docker Compose, is that stacks do not support builds. All images have to be built prior to deploying the stack.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From the stack file, Docker first executes the network section and create networks that do not exist. Then it goes through other elements. A service is a JSON collection(dictionary) that contains a bunch of keys. The image key is the only mandatory key in the service objects, which will be pulled from Docker Hub by default. Ports key maps the port of Swarm to the port of each service replica. By default, all ports are mapped using ingress mode. This means they&amp;#8217;ll be mapped and accesible from every node in the Swarm -even nodes not running a replica. The alternative is host mode, where ports are only mapped on Swarm nodes running replicas for the service.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The environment key lets you inject environment variables into services replica.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The secrets key defines two secrets &amp;#8211; revprox_cert and revprox_key. These must be defined in the top-level secrets key, and must exist on the system. Secrets get mounted into service replicas as a regular file. The secrets defined in this service will be mounted in each service replica as /run/secrets/revprox_cert and /run/secrets/revprox_key, unless otherwise specified.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The volumes key is used to mount pre-created volumes and host directories into a service replica.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The networks key ensures that all replicas for the service will be attached to the front-tier network. The network specified here must be defined in the networks top-level key, and if it doesn’t already exist, Docker will create it as an overlay.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The service also defines a placement constraint under the deploy key. This ensures that replicas for this service will always run on Swarm worker nodes. Placement constraints are a form of topology-aware scheduling, and can be a great way of influencing scheduling decisions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When Docker stops a container, it issues a SIGTERM to the process with PID 1 inside the container. The container (its PID 1 process) then has a 10-second grace period to perform any clean-up operations. If it doesn’t handle the signal, it will be forcibly terminated after 10 seconds with a SIGKILL. The stop_grace_period property overrides this 10 second grace period.”&lt;br&gt;Although you may scale a docker service as part of a stack with scale command, it is not recommended. Instead, stack file should be used as the ultimate source of truth (declarative method vs imperative method). All changes to the stack should be made to the stack file, and the updated stack file used to redeploy the app.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Swarm&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For multi-host cluster, Docker Swarm facilitates the deployment of micro-services. Docker Swarm is:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;a &lt;span style="text-decoration: underline;"&gt;cluster&lt;/span&gt; of Docker hosts: enterprise-grade, secure communication, PKI with automation, dynamic addition of nodes&lt;/li&gt;&#10;&lt;li&gt;an &lt;span style="text-decoration: underline;"&gt;orchestration engine&lt;/span&gt;, with deployment automation, deploying native swarm apps (using Docker API) and Kubernetes apps.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Docker nodes can be physical servers, VMs, cloud instances, etc. Nodes are configured as managers or workers. Managers look after the control plane of the cluster, and dispatches tasks to workers. Managers forms a distributed management cluster on its own, and they use Raft protocol to ensure consistency. Workers accept tasks from managers and execute them. Swarm mandatorily uses TLS to encrypt communications, authenticate nodes, and authorize roles, with Automatic key rotation.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="388" src="https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-1024x388.webp" alt="" class="wp-image-13095" srcset="https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-1024x388.webp 1024w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-300x114.webp 300w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-768x291.webp 768w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-1536x582.webp 1536w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-2048x777.webp 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The atomic unit of scheduling on a swarm is the service. When a container is wrapped in a service, we call it a task or a replica, and the service construct adding things like scaling, rolling updates and simple rollbacks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To initialize a swarm, we need to have the following ports open. Then we can initialize the first manager node, join additional manager nodes, and then join workers.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;2377/tcp: for secure client-to-swarm communication&lt;/li&gt;&#10;&lt;li&gt;7946/tcp &amp;amp; udp: for control plane gossip&lt;/li&gt;&#10;&lt;li&gt;4789/udp: for VXLAN-based overlay networks&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Docker node can exist either in single-engine mode as stand alone, or in swarm mode as part of a swarm. Service only exist in swarm mode. Running docker swarm init on a Docker host in single-engine mode will switch that node into swarm mode, create a new swarm, and make the node the first manager of the swarm. Then additional nodes can be joined as managers or workers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Swarm managers have native support for high availability, through an active-passive, multi-manager HA. Only one manager is considered active (the leader), which is the only one that will ever issue live commands against the swarm. If a passive manager receives commands for the swarm, it proxies them across to the leader.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Managers are either leaders or followers. This is Raft terminalogy because swarm uses an impelementation of the Raft consensus althorithm to power manager HA. As to HA, the following two best practices apply:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;deploy an odd number of managers&lt;/li&gt;&#10;&lt;li&gt;don&amp;#8217;t deploy too many managers (3 or 5 recommended, never more than 7)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Having an odd number of managers reduces the chances of split-brain conditions. Having less than 7 managers ensures that achieving consensus is quick.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With a service, we can specify name, port mappings, network to attach to, and images, as well as desired state for an application service. It is recommended in production environment to use docker-compose template to specify service. Services have replication mode, and the default is replicated. This will deploy a desired number of replicas and distribute them as evenly as possible across the cluster. The other mode is global, which runs a single replica on every node in the swarm.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Running &amp;#8220;docker service scale&amp;#8221; command can scale the number of service replicas from 5 to 10, which in the background updates the service&amp;#8217;s desired state to the newly specified number of replicas. Behind the scenes, Swarm also runs a scheduling algorithm that defaults to balancing replicas as evenly as possible across the node in the swarm. Docker makes it super easy to push updates to deployed applications. With rolling update, you may specify number of replicas to update at a time, and cool-off period per update.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a class="rank-math-link" href="https://upcloud.com/community/stories/docker-swarm-vs-kubernetes-comparison-of-the-two-giants-in-container-orchestration/"&gt;Here&lt;/a&gt; is a great article on the difference between Docker Swarm and Kubernetes.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Ansible at scale 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/05/ansible-directory-for-scalability-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;Ansible at scale 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Cassandra data model (as opposed to relational model)</title><link>https://www.digihunch.com/2020/04/cassandra-data-model-as-opposed-to-relational-database/</link><pubDate>Wed, 29 Apr 2020 18:45:00 -0400</pubDate><guid>https://www.digihunch.com/2020/04/cassandra-data-model-as-opposed-to-relational-database/</guid><description>&lt;p class="wp-block-paragraph"&gt;Bad data model design with &lt;a href="https://www.digihunch.com/2018/03/cassandra-architecture-summary/"&gt;Cassandra&lt;/a&gt; causes chronic pains as application scales. I had to re-read about data model design in &amp;#8220;&lt;a href="http://shop.oreilly.com/product/0636920299837.do"&gt;Cassandra &amp;#8211; the Definitive Guide&lt;/a&gt;&amp;#8221; and keep my notes and thoughts in this post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The data modelling in the relational world is indoctrinated to every students out of university. It embraces several things:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Entity-Relation: we typically start with tables that represents entities, and then tables that expresses relations;&lt;/li&gt;&#10;&lt;li&gt;Query design after table design: we can join multiple tables, index certain fields for better query performance;&lt;/li&gt;&#10;&lt;li&gt;Data normalization: several normal forms (NFs) are brought up to better organize data; de-normalization only occurs when 1) performance bottleneck reached; and 2) specific requirement on retaining snapshots of previous (un-updated) value in a field;&lt;/li&gt;&#10;&lt;li&gt;referential integrity: we can specify foreign keys on a table to reference the primary key of a record in another table; we can configure cascading deletes, etc;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Anybody with years of experience with relational database may have all these built in their instinct. Unfortunately, Cassandra does not follow any of these patterns. For someone with relational database background, the trip entering the Cassandra design is very counter-intuitive.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-no-joins"&gt;No joins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Cassandra you have very limited options to achieve what you can do with joins in relational realm. One option is to duplicate the data column on different tables (a pattern against the &amp;#8220;normalization&amp;#8221; best practice). The second table is denormalized and it represents the join results. The other option rarely applied is to do the work on the client side.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-query-driven-data-modelling"&gt;Query-driven data modelling&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In relational database, you start writing queries after tables are laid out to pull together disparate data, using the relationship defined by the keys. The queries are a secondary concern. It is assumed that you can always get the data you want as long as you have your tables modelled properly, even if you have to use several complex subqueries or join statements.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Cassandra, You do not start with tables to represent entity. Instead, you would start with queries, and then organize data around the queries. This means an upfront effort must be made to investigate what queries the client application may perform, and work backwards with tables that answers those queries in the most efficient manner. Table names in Cassandra often takes names such as: hotels_by_poi, avaialbe_rooms_by_hotel_date, reservations_by_guest, reservations_by_hotel_date. On the tables the selection of partition key and clustering keys should also consider best query performance and avoids wide partitions. It should also ensure with best effort that a query should not have to travel across multiple partitions in order to return results.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-denormalization"&gt;Denormalization&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Due to the query-driven modelling approach, Cassandra usually need to be designed with denormalization. The entire concept of normalization applies only to relational world and in Cassandra it&amp;#8217;s perfectly normal to organize data that are against NFs.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-designing-for-optimal-storage"&gt;Designing for optimal storage&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra tables are each stored in separate files on disk. Its best practice to keep related columns defined together in the same table. We need to minimize the number of partitions that must be searched in order to satisfy a given query. Because the partition is a unit of storage that does not get divided across nodes, a query that searches a single partition will typically yield the best performance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The book &amp;#8220;Cassandra: the definitive guide&amp;#8221; contains a great example of modelling hotel reservation system. &lt;a href="https://www.datastax.com/blog/2015/02/basic-rules-cassandra-data-modeling"&gt;This article&lt;/a&gt; is also a good guideline.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="780" height="1024" src="https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-780x1024.webp" alt="" class="wp-image-13112" style="width:193px;height:254px" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-780x1024.webp 780w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-229x300.webp 229w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-768x1008.webp 768w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-1171x1536.webp 1171w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-1561x2048.webp 1561w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book.webp 1951w" sizes="auto, (max-width: 780px) 100vw, 780px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How memory usage adds up in Linux&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/05/understanding-where-the-memory-goes-on-linux-vm/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Balloon steals memory from virtual machines&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>How memory usage adds up in Linux</title><link>https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/</link><pubDate>Sun, 19 Apr 2020 21:12:56 -0400</pubDate><guid>https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/</guid><description>&lt;p class="wp-block-paragraph"&gt;There are too many metrics that describes some aspects about memory in Linux. This posting will make sense of those common metrics in Linux, CentOS as an example.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most fundamental command is free and my favourite switch is -h for human readable reads. You can use -m, -k, -b for different units. The result looks like this:&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total used free shared buff/cache available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mem: 32780168 16832160 3200408 101356 12747600 15399528&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Swap: 2097148 2055148 42000&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Swap is essentially disk space and many application such as Cassandra, ElasticSearch recommend disabling swap as best practice and they do not want disk speed to drag the performance of memory. Many suggest that swap is not needed in today&amp;#8217;s era at all given the amount of memory for cheap. This is &lt;a href="https://askubuntu.com/questions/291378/do-we-still-need-swap-partitions-on-servers"&gt;debatable&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With the row for Mem, the four columns should add up to the total, as suggested in the chart below. &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="http://blog.yufeng.info/wp-content/uploads/2013/01/free1.jpg" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;total = free + used + shared + buff/cache &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The four columns from free command output are supposed to always add up to the physical memory size. This command simplifies things quite a bit and each of these values are actually taken from certain lines in /proc/meminfo:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Metric from free command&lt;/td&gt;&lt;td&gt;Metric in /proc/meminfo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;total&lt;/td&gt;&lt;td&gt;MemTotal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;used&lt;/td&gt;&lt;td&gt;??&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;free&lt;/td&gt;&lt;td&gt;MemFree&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shared&lt;/td&gt;&lt;td&gt;Shmem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;buff/cache&lt;/td&gt;&lt;td&gt;Cached + Slab&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;available&lt;/td&gt;&lt;td&gt;MemAvailable&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The buffer and cache (and even swap) can be freed by &lt;a href="https://www.tecmint.com/clear-ram-memory-cache-buffer-and-swap-space-on-linux/"&gt;command&lt;/a&gt;. The value of used doesn&amp;#8217;t seem to come from anwhere in /proc/meminfo, but it should be calculable from the memory used per process, which can be seen from top command.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the result of top command, the column RSS (resident set size) is from the VmRSS value in /proc/&amp;lt;pid&amp;gt;/status, it is the actual physical memory consumed by the process. This value is originally from the second read in /proc/&amp;lt;pid&amp;gt;/statm, which represents the number of pages. 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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ghunch@centos ~]$ cat /proc/6495/status | grep VmRSS ; cat /proc/6495/statm&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VmRSS:&#9;20852916 kB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;49829626 5213229 1212275 1 0 5773980 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ghunch@centos ~]$ getconf PAGE_SIZE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4096&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Linux default page size is 4096 or 4K, so in the result from above, 5213229 x 4kB = 20852916 kB, which is the size of memory taken by process ID 6459. Therefore if we go through all processes and add up the VmRSS, we should get (close to) the used memory?&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;But wait a second, we have not account for slab info (memory used by kernel) yet, which is displayed in /proc/slabinfo. To calculate the total size taken by slab, we use &amp;lt;num_objs&amp;gt; and &amp;lt;objsize&amp;gt; columns from /proc/slabinfo.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from that there is &lt;a href="https://en.wikipedia.org/wiki/Page_table"&gt;page table&lt;/a&gt;, the table that stores the mapping between virtual address and physical address, is stored in the physical memory as well and the size is specified in the PageTables entry in /proc/meminfo. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now our equation becomes:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Used Memory = (RSS for all processes) + (all objects in slab) + (page table)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use the following script to calculate the used memory and compare it with free command output.&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;&lt;span style="color:#75715e"&gt;#/bin/bash&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; PROC in &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;ls /proc/|grep &lt;span style="color:#e6db74"&gt;&amp;#34;^[0-9]&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; -f /proc/$PROC/statm &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TEP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;cat /proc/$PROC/statm | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print ($2)}&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RSS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;expr $RSS + $TEP&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RSS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;expr $RSS &lt;span style="color:#ae81ff"&gt;\*&lt;/span&gt; 4&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PageTable&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;grep PageTables /proc/meminfo | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SlabInfo&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;cat /proc/slabinfo |awk &lt;span style="color:#e6db74"&gt;&amp;#39;BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&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;echo $RSS&lt;span style="color:#e6db74"&gt;&amp;#34;KB&amp;#34;&lt;/span&gt;, $PageTable&lt;span style="color:#e6db74"&gt;&amp;#34;KB&amp;#34;&lt;/span&gt;, $SlabInfo&lt;span style="color:#e6db74"&gt;&amp;#34;MB&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;printf &lt;span style="color:#e6db74"&gt;&amp;#34;rss+pagetable+slabinfo=%sMB\n&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;echo $RSS/1024 + $PageTable/1024 + $SlabInfo|bc&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;free -m&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Running it require root access and the bc package installed. The result is most likely greater than the used memory value. Below is the result from my server:&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;89925884KB, 201788KB, 3303.92MB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rss+pagetable+slabinfo=91318.92MB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total used free shared buff/cache available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mem: 128772 87032 726 498 41013 40346&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Swap: 2047 3 2044&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;So the result is over by (91318 &amp;#8211; 87032) = 4286M. This is due to shared memory. The RSS value from above includes memory from shared libraries as long as the pages from those libraries are in the memory. If multiple processes use the same library, the memory from shared library is counted multiple times. Check out the difference between &lt;a href="https://en.wikipedia.org/wiki/Resident_set_size"&gt;RSS&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Proportional_set_size"&gt;PSS&lt;/a&gt; (proportional set size) &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[Disclaimer] The chart and script are stolen from this authors &lt;a href="http://www.programmersought.com/article/6127180173/"&gt;post&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/04/common-local-git-operations/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Common local Git operations&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/04/cassandra-data-model-as-opposed-to-relational-database/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cassandra data model (as opposed to relational model)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>High Availability and Load Balancer</title><link>https://www.digihunch.com/2020/01/several-ways-to-ensure-high-availability/</link><pubDate>Wed, 22 Jan 2020 20:49:00 -0400</pubDate><guid>https://www.digihunch.com/2020/01/several-ways-to-ensure-high-availability/</guid><description>&lt;h3 class="wp-block-heading" id="h-overview"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Fault tolerance and high availability are two architectural characteristics that people often confuse with each other. High availability focuses on minimizing downtime. It guarantees uptime, but not performance in the event of component failures. Fault tolerance, on the other hand, focuses on stable capacity even in the event of component failures. Fault tolerance has higher bar, and therefore is more expensive. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Suppose an application requires four servers to meet performance goal. Placing two servers in each of the two AZs will meet HA criteria but not FT requirement. In the event of an AZ failure, application can operate at degraded performance yet still be highly available. However, FT requires stable capacity and to meet FT requirement, we&amp;#8217;d have to place four servers in each AZ. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;High availability can be achieved either by clustering, or load balancing. A cluster involves several nodes, all able to perform the same function, but may take different roles at different times (e.g. primary, standby) in order for the cluster to perform its function as a single system. In Linux, clustering is implemented by pacemaker or corosync. With a high load system, it is common to set up load balancing system to achieve high availability (and fault tolerance).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-load-balancing"&gt;Load balancing&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The idea of load balancing is simple: load goes high and we want to scale horizontally instead of simply upgrading server hardware. At a high level, there has been three approaches to load balancing:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;DNS rotating:&lt;/strong&gt; (aka. DNS round robin) DNS record resolves to multiple IPs, very simple and cheap to implement. Since DNS is cached, the load distribution will come imbalanced and it&amp;#8217;s hard to re-balance, making this a very limited approach;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Hardware Load Balancer&lt;/strong&gt;: using dedicated hardware device to configure load balancing. This option is expensive and only enterprises can afford it (&lt;a class="rank-math-link" href="https://kemptechnologies.com/compare-kemp-to-f5-big-ip-ltm-citrix-netscaler-mpx-load-balancers/"&gt;here&lt;/a&gt;&amp;#8216;s some pricing information). A classic load balancer operates at layer 3 and 4, which is also known as POLB (plain old load balancer). It is the core functionality of hardware load balancer. The hardware load balancer on the market today usually come with a variety of add-on features, such as advanced load balancing (L4, L7 path-based, script driven), compression, caching, SSL offloading, and even DDoS mitigation, etc. The whole suite of features makes it an Application Delivery Controller (ADC). Therefore many refer to hardware load balancer as &lt;a href="https://www.f5.com/company/blog/go-beyond-polb-plain-old-load-balancing"&gt;hardware-based ADC&lt;/a&gt; to highlight the features in addition to POLB. Hardware-based ADCs ship with manufactures hardware, with specialized processors, advanced network hardware, and often &lt;a href="https://www.f5.com/services/resources/white-papers/software-defined-hardware-enabling-performance-and-agility-with-the-big-ip-iseries-architecture"&gt;ASIC&lt;/a&gt; (application specific integrated circuit). At a higher expense, they have better reliability and capacity. Some major market players are:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;F5 &amp;#8211; &lt;a class="rank-math-link" href="https://www.f5.com/services/resources/white-papers"&gt;Big IP&lt;/a&gt;, F5 also has a &lt;a href="https://www.f5.com/glossary/load-balancing"&gt;good article &lt;/a&gt;about history of load balancer.&lt;/li&gt;&#10;&lt;li&gt;Cisco &amp;#8211; Citrix A&lt;a href="https://www.citrix.com/products/citrix-adc/"&gt;https://www.citrix.com/products/citrix-adc/&lt;/a&gt;DC (formerly NetScaler ADC)&lt;/li&gt;&#10;&lt;li&gt;A10 Networks &amp;#8211; &lt;a class="rank-math-link" href="https://www.a10networks.com/products/thunder-adc/"&gt;Thunder&lt;/a&gt; (general) and &lt;a class="rank-math-link" href="http://docs.hc.a10networks.com/2.2.4/ads-intro.html"&gt;Lightning&lt;/a&gt; (cloud)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Software Load Balancer:&lt;/strong&gt; using software to achieve load balancing. These solutions are affordable, and usually open-source. They can be loaded on commodity hardware (including NIC). Some (e.g. &lt;a class="rank-math-link" href="https://www.nginx.com/resources/glossary/application-delivery-controller/"&gt;Nginx&lt;/a&gt;) refers to themselves as software-based ADC. Major players are:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;HA Proxy&lt;/li&gt;&#10;&lt;li&gt;Nginx&lt;/li&gt;&#10;&lt;li&gt;Linux Virtual Server (LVS, L4 only)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The hardware ADCs are usually supported commercially and there are plenty of resources from their white papers. There is an ongoing debate about whether one is better than the other. However, there is no doubt that a software-based load balancer is more approachable as open-source tools. The line between software and hardware load balancers becomes blurred today as hardware vendors try to adapt their software appliance to commodity hardware. Check out &lt;a href="https://www.nginx.com/blog/not-all-software-load-balancers-are-created-equal/"&gt;this&lt;/a&gt; article. The rest of this post, will focus on software-based load balancer. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-software-based-load-balancer"&gt;Software-based load balancer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We explained that ADC (application delivery controller) is an expanded set of features from load balancer, and will only cover the load balancer part of the feature set in this article.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.haproxy.org/" class="rank-math-link"&gt;HAProxy&lt;/a&gt; supports both layer 4 and layer 7 load balancing. It supports load balancing based on cookie and session, as well as health check. Since it is layer 4 load balancing, it supports any TCP protocol such as read traffic for MySQL. &amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.nginx.com/" class="rank-math-link"&gt;Nginx&lt;/a&gt; is a high-performance, event-driven, cross-platform layer 7 load balancing application. It works as a reverse proxy where it receives request for the Internet and forwards it to (upstream) internal servers. It consumes less memory than many of its alternatives for layer 7 load balancing. There are many strategies for load balancing such as round robin, by weight, by hash of requesting IP, by upstream response time, or by URL hash. It supports 20-30 k concurrent connections, and support compression and health check. It is known to be very stable and common for small and medium volume. Nginx has a commercial counterpart Nginx Plus with advanced features.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Nginx and HA proxy are commonly used in front end load balancing. For backend traffic such as database (e.g. separating read write traffic), LVS can be used.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-linux-virtual-server"&gt;Linux Virtual Server&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/4/html/virtual_server_administration/ch-lvs-overview-vsa" class="rank-math-link"&gt;LVS&lt;/a&gt; (Linux Virtual Server) is part of standard Linux kernel. It performs layer 4 load balancing based on TCP or UDP and therefore consumes less memory and CPU. Compared to layer 7 load balancing, the performance is generally higher, and the configuration is less complex (with simpler routing rules). &lt;a href="http://www.linuxvirtualserver.org/" class="rank-math-link"&gt;LVS&lt;/a&gt; is usually configured in a &lt;a href="http://www.linuxvirtualserver.org/architecture.html" class="rank-math-link"&gt;common cluster architecture&lt;/a&gt; involving these components:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Load balancer: the front-end machine of the whole cluster systems, and balances requests from clients among a set of servers, so that the clients consider that all the services are from a single IP address.&lt;/li&gt;&#10;&lt;li&gt;Server cluster: set of servers running actual business workload&lt;/li&gt;&#10;&lt;li&gt;Shared storage: a shared storage space for the servers, such as NFS&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://i.imgur.com/EU0gAUv.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Load balancer is the single entry-point of server cluster systems, it can run&amp;nbsp;IPVS&amp;nbsp;that implements IP load balancing techniques inside the Linux kernel, or&amp;nbsp;KTCPVS&amp;nbsp;that implements application-level load balancing inside the Linux kernel. When IPVS is used, all the servers are required to provide the same services and contents, the load balancer forwards a new client request to a server according to the specified scheduling algorithms and the load of each server. No matter which server is selected, the client should get the same result. When KTCPVS is used, servers can have different contents, the load balancer can forward a request to a different server according to the content of request. Since KTCPVS is implemented inside the Linux kernel, the overhead of relaying data is minimal, so that it can still have high throughput.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPVS is also called layer-4 switching, it directs TCP/UDP requests to the real servers behind load balancer. It works in three modes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Network Address Translation (NAT)&lt;/li&gt;&#10;&lt;li&gt;Direct Routing (DR)&lt;/li&gt;&#10;&lt;li&gt;Tunnel mode (TUN)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These are three packet-forwarding methods in IPVS. The IPVS is implemented as a module over the netfilter framework, similar to &lt;a href="https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/" class="rank-math-link"&gt;iptables&lt;/a&gt;, which is also built on top of netfilter, based on chain and rules.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-summary"&gt;Summary &lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We had an overview of high availability, and then expanded on load balancing, an important mechanism to implement high availability. We touched on both hardware-based and software-based load balancing technologies, and dived a little more into Linux Virtual Server. It is worth-noting that LVS is also the foundation of kube-proxy, the load balancing mechanism used in Kubernetes.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Nginx as a reverse proxy for Nifi web UI and Kibana&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/02/everything-about-the-domain/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Introduction to Active Directory (AD)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Java version confusions</title><link>https://www.digihunch.com/2018/11/the-java-confusions/</link><pubDate>Mon, 05 Nov 2018 17:32:53 -0400</pubDate><guid>https://www.digihunch.com/2018/11/the-java-confusions/</guid><description>&lt;p class="wp-block-paragraph"&gt;Anyone working with deploying Java applications inevitably came across one of these confusions with the terms. Let&amp;#8217;s clarify them. This clarification is not for Java developer and does not go deep with underlying technologies. This is for installation/DevOps engineers to understand Java environment.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Java SE, EE and ME&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java Platform, Standard Edition &lt;strong&gt;(Java SE&lt;/strong&gt;) is a computing platform for development and deployment of portable code for desktop and server environments. Java SE was formerly known as Java 2 Platform, Standard Edition (J2SE). Java SE defines a range of general-purpose APIs, and also includes the Java Language Specification and the Java Virtual Machine Specification. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java Enterprise Edition (&lt;strong&gt;Java EE&lt;/strong&gt;), formerly Java 2 Platform, Enterprise Edition, currently rebranded as Jakarta EE but the new brand is still being adopted. It is an extension to Java SE with specifications for enterprise features such as distributed computing, web services, XML processing, JMS (messaging). It is more widespread in enterprise contexts such as e-commerce, accounting, banking information systems. The specification defines APIs and their interactions for providers to meet in order to declare compliance with Java EE. For example Apache Tomcat is an implementation of a subset of Java EE. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java Platform, Micro Edition (Java ME, formerly knowned as Java 2 Platform, Micro Edition or J2ME) is a subset of Java SE for embedded and mobile devices. The advent of Android significantly de-popularized Java ME.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;JRE and JDK&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java SE is the foundation for developing in Java language. The aforementioned platforms (Java SE, EE and ME) are just specifications, not implementations. Java Software Development Toolkit (SDK) is called JDK (Java Development Kit) for short. Strictly speaking, the JDK can be an implementation of any one of the platforms above. In every day language, people loosely refers to the implementation of Java SE as &lt;strong&gt;JDK&lt;/strong&gt;, whereas Oracle&amp;#8217;s implementation of Java EE is referred to as&lt;strong&gt; Java EE SDK&lt;/strong&gt;.&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://miro.medium.com/max/636/1*8oNn6HxcWFmrCsgUt27k0w.jpeg" alt="" width="527" height="299"/&gt;&lt;figcaption&gt;JDK vs JRE&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;JDK consists of Java Runtime Environment (&lt;strong&gt;JRE&lt;/strong&gt;) along with tools to compile and debug Java code for developing Java applications. JRE consists of libraries, Java Virtual Machine (JVM), Java Pluging and Java Web Start to run Java applications. JRE alone does not contain compilers and debugging tools. The two most widespread JDKs are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Oracle JDK: Oracle&amp;#8217;s official implementation of Java SE.&lt;/li&gt;&lt;li&gt;OpenJDK: a free and open-source implementation of Java SE.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;They are both created and maintained by Oracle. Almost everything in Oracle JDK is from OpenJDK. The slight &lt;a href="https://www.educba.com/oracle-vs-openjdk/"&gt;difference &lt;/a&gt;between them is an entirely separate topic itself but the idea is their binaries will be converged:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2020/03/image-5.webp" alt="" class="wp-image-886" width="571" height="322"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Oracle JDK was licensed under Oracle Binary Code License Agreement, whereas OpenJDK has the GNU General Public License (GNU GPL) version 2 with a linking exception. It is worth-noting that Oracle has announced that the Oracle JDK 8 builds released after &lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html"&gt;Jan 2019&lt;/a&gt; cease to be free for commercial use. This drives may application vendor to &lt;a href="https://developers.redhat.com/blog/2018/11/05/migrating-from-oracle-jdk-to-openjdk-on-red-hat-enterprise-linux-what-you-need-to-know/"&gt;migrate&lt;/a&gt; from Oracle JDK to OpenJDK in their platforms.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Version History&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If what you have read so far is not confusing enough, here&amp;#8217;s some more muds. The version scheme for Java has changed in it&amp;#8217;s 20 years history. Here is a list of main versions.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Platform Version&lt;/td&gt;&lt;td&gt;Internal Version&lt;/td&gt;&lt;td&gt;Release Date&lt;/td&gt;&lt;td&gt;Notes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JDK 1.0&lt;/td&gt;&lt;td&gt;1.0&lt;/td&gt;&lt;td&gt;Jan 1996&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JDK 1.1&lt;/td&gt;&lt;td&gt;1.1&lt;/td&gt;&lt;td&gt;Feb 1997&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 1.2&lt;/td&gt;&lt;td&gt;1.2&lt;/td&gt;&lt;td&gt;Dec 1998&lt;/td&gt;&lt;td&gt;In 1998 JDK splits into J2SE and J2EE. Code name for J2SE 1.2 is Playground&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 1.3&lt;/td&gt;&lt;td&gt;1.3&lt;/td&gt;&lt;td&gt;May 2000&lt;/td&gt;&lt;td&gt;Code name is Kestrel&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 1.4&lt;/td&gt;&lt;td&gt;1.4&lt;/td&gt;&lt;td&gt;Feb 2002&lt;/td&gt;&lt;td&gt;Code name is Merlin&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 5.0&lt;/td&gt;&lt;td&gt;1.5&lt;/td&gt;&lt;td&gt;Sep 2004&lt;/td&gt;&lt;td&gt;In 2004, Sun introduced internal version and external version. Code name for this version is Tiger.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 6&lt;/td&gt;&lt;td&gt;1.6&lt;/td&gt;&lt;td&gt;Dec 2006&lt;/td&gt;&lt;td&gt;Code name Mustang&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 7&lt;/td&gt;&lt;td&gt;1.7&lt;/td&gt;&lt;td&gt;Jul 2011&lt;/td&gt;&lt;td&gt;Code name Dolphin&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 8&lt;/td&gt;&lt;td&gt;1.8&lt;/td&gt;&lt;td&gt;Mar 2014&lt;/td&gt;&lt;td&gt;5 year from previous version, LTS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 9&lt;/td&gt;&lt;td&gt;1.9&lt;/td&gt;&lt;td&gt;Sep 2017&lt;/td&gt;&lt;td&gt;3.5 year from previous version. Going forward new version will be released every six month&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 10&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Mar 2018&lt;/td&gt;&lt;td&gt;It was proposed that versions should simply increase incrementally&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 11&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;Sep 2018&lt;/td&gt;&lt;td&gt;LTS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 12&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;Mar 2019&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;#8230;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 17&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;Sep 2021&lt;/td&gt;&lt;td&gt;LTS&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since 2018, new version will be release every six month and the there is no longer a distinction between internal and external versions.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Multi-version management&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We only cover Linux here to manage multiple versions of JDK. We use a tool named alternatives to maintain symbolic links determining default commands. How this works with Java is:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Make /usr/bin/java a symbolic link pointing to /etc/alternatives/java&lt;/li&gt;&lt;li&gt;Make /etc/alternatives/java also a symbolic link pointing to the desired version of java&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To start configuration, 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;alternatives --config java&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you will be given a list of Java versions to choose from. If the list does not have your desired version, and you confirm that the version has been installed. You will need to add this version by doing 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;alternatives --install /usr/bin/java java /usr/java/jdk1.6.0_25/bin/java &lt;span style="color:#ae81ff"&gt;1000&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 command executes and takes effect by modifying files under /var/lib/alternatives directory.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Also, one can overwrite environment variable $JAVA_HOME to force an application to use a different version of Java. This is because many application picks up JDK location from that variable.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Redhat Firewall configuration: from iptables to firewalld&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/01/interesting-terms-about-unsuccessful-software-project-management/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Interesting terms and principles&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Cassandra Architecture</title><link>https://www.digihunch.com/2018/03/cassandra-architecture-summary/</link><pubDate>Tue, 20 Mar 2018 18:18:00 -0400</pubDate><guid>https://www.digihunch.com/2018/03/cassandra-architecture-summary/</guid><description>&lt;p class="wp-block-paragraph"&gt;Excerpts from Cassandra The Definitive Guide&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Gossip and Failure Detection&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra uses a gossip protocol that allows each node to keep track of state information about the other nodes in the cluster. The gossiper runs every second on a timer.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Gossip protocols assume a faulty network and are commonly employed in very large, decentralized network systems, and are often used as an automatic mechanism for replication in distributed databases. When a server node is started, it registers itself with the gossiper to receive endpoint state information. Because Cassandra gossip is used for failure detection, the Gossiper class maintains a list of nodes that are alive and dead.&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Once per second, the gossiper will choose a random node in the cluster and initialize a gossip session with it. Each round of gossip requires three messages.&lt;/li&gt;&#10;&lt;li&gt;The gossip initiator sends its chosen friend a GossipDigestSynMessage.&lt;/li&gt;&#10;&lt;li&gt;When the friend receives this message, it returns a GossipDigestAckMessage.&lt;/li&gt;&#10;&lt;li&gt;When the initiator receives the ack message from the friend, it sends the friend a GossipDigestAck2Message to complete the round of gossip.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When the gossiper determines that another endpoint is dead, it “convicts” that endpoint by marking it as dead in its local list and logging that fact.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra has robust support for failure detection, as specified by a popular algorithm for distributed computing called Phi Accrual Failure Detection. The traditional failure detection (based on whether heartbeat is received or not) is deemed naive.&amp;nbsp; Accrual failure detection determines suspicion level. Suspicion offers a more fluid and proactive indication of the weaker or stronger possibility of failure based on interpretation (sampling of heartbeats), as opposed to a simple binary assessment.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Accrual Failure Detectors output a value associated with each process (or node). This value is called Phi. The value is output in a manner that is designed from the ground up to be adaptive in the face of volatile network conditions, so it’s not a binary condition that simply checks whether a server is up or down.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Phi convict threshold in the configuration adjusts the sensitivity of the failure detector. Lower values increase the sensitivity and higher values decrease it, but not in a linear fashion.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Phi value refers to a level of suspicion that a server might be down. Applications such as Cassandra that employ an AFD can specify variable conditions for the Phi value they emit. Cassandra can generally detect a failed node in about 10 seconds using this mechanism.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Snitches&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A snitch determines relative host proximity for each node in a cluster, which is used to determine which nodes to read and write from. Snitches gather information about your network topology so that Cassandra can efficiently route requests. The snitch will figure out where nodes are in relation to other nodes. Snitch property can be adjusted (endpoint_snitch in cassandra.yaml)&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Rings and Token&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A cassandra cluster presents itself as a ring. Each node in the ring is assigned one or more ranges of data described by a token, which determines its position in the ring. A token is a 64-bit integer ID used to identify each partition.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A node claims ownership of the range of values less than or equal to each token and greater than the token of previous node. The node with lowest token owns the range less than or equal to its token and the range greater than the highest token, which is also known as the &amp;#8220;wrapping range&amp;#8221; In this way the token specifies a complete ring.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Data is assigned to nodes by using a hash function to calculate a token for the partition key. This partition key token is compared to the token values for the various nodes to identify the range, and therefore the node that owns the data.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="932" height="624" src="https://www.digihunch.com/wp-content/uploads/2020/03/image-1.webp" alt="" class="wp-image-679"/&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Virtual Nodes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Instead of assigning a single token to a cassandra node, the token range is broken up into multiple smaller ranges, each represented by a vNode. By default a cassandra node will be assigned 256 vnodes (small range of tokens). Vnodes make it easier to maintain a cluster containing heterogeneous machines. Nodes in a cluster with more computing resources available can manage an increased number of vnode (num_tokens property in cassandra.yaml)&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Replication Strategies&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A node serves as a replica for different ranges of data. If one node goes down, other replicas can respond to queries for that range of data. Cassandra replicates data across nodes in a manner transparent to the user, and the replication factor is the number of nodes in your cluster that will receive copies (replicas) of the same data.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first replica will always be the node that claims the range in which the token falls, but the remainder of the replicas are placed according to the replication strategy (sometimes also referred to as the replica placement strategy). Out of the box, Cassandra provides two primary implementations of this interface (extensions of the abstract class): SimpleStrategy and NetworkTopologyStrategy. They are specified at the time of keyspace creation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The SimpleStrategy places replicas at consecutive nodes around the ring, starting with the node indicated by the partitioner. The NetworkTopologyStrategy allows you to specify a different replication factor for each data center. Within a data center, it allocates replicas to different racks in order to maximize availability.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Consistency Levels&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra provides tuneable consistency levels that allow you to make trade-offs with CAP at a fine-grained level. You specify a consistency level on each read or write query that indicates how much consistency you require.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For read queries, the consistency level specifies how many replica nodes must respond to a read request before returning the data. For write operations, the consistency level specifies how many replica nodes must respond for the write to be reported as successful to the client. Because Cassandra is eventually consistent, updates to other replica nodes may continue in the background.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Consistency levels include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;response from an absolute number of nodes: ONE, TWO or THREE&lt;/li&gt;&#10;&lt;li&gt;response from the majority of the replica nodes (e.g. replication factor/2+1): QUORUM&lt;/li&gt;&#10;&lt;li&gt;response from all nodes: ALL&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ALL and QUORUM are considered strong consistency level. But in general we can consider a cluster of strong consistency if it meets this condition:&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;R + W &amp;gt; N&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;where&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;R is read consistency level&lt;/li&gt;&#10;&lt;li&gt;W is write consistency level&lt;/li&gt;&#10;&lt;li&gt;N is replication factor&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Queries and Coordinator Nodes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A client may connect to any node in the cluster to initiate a read or write query. This node is known as the coordinator node. The coordinator identifies which nodes are replicas for the data that is being written or read and forwards the queries to them.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For a write, the coordinator node contacts all replicas, as determined by the consistency level and replication factor, and considers the write successful when a number of replicas commensurate with the consistency level acknowledge the write.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For a read, the coordinator contacts enough replicas to ensure the required consistency level is met, and returns the data to the client.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="944" height="584" src="https://www.digihunch.com/wp-content/uploads/2020/03/image-2.webp" alt="" class="wp-image-680"/&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Memtables, SSTables and Commit Logs&lt;/h3&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="932" height="464" src="https://www.digihunch.com/wp-content/uploads/2020/03/image.webp" alt="" class="wp-image-678"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you perform a write operation, it’s immediately written to a commit log so the write operation is considered successful. If you shut down the database or it crashes unexpectedly, the commit log can ensure that data is not lost. That’s because the next time you start the node, the commit log gets replayed. In fact, that’s the only time the commit log is read; clients never read from it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After it’s written to the commit log, the value is written to a memory-resident data structure called the memtable. Each memtable contains data for a specific table. In early implementations of Cassandra, memtables were stored on the JVM heap, but&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;improvements starting with the 2.1 release have moved the majority of memtable data to native memory. (check out the memtable_allocation_type property: heap_buffers/offheap_buffers/offheap_objects). This makes Cassandra less susceptible to fluctuations in performance due to Java garbage collection.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When the number of objects stored in the memtable reaches a threshold, the contents of the memtable are flushed to disk in a file called an SSTable. A new memtable is then created. This flushing is a non-blocking operation; multiple memtables may exist for a single table, one current and the rest waiting to be flushed. They typically should not have to wait very long, as the node should flush them very quickly unless it is overloaded.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each commit log maintains an internal bit flag to indicate whether it needs flushing. When a write operation is first received, it is written to the commit log and its bit flag is set to 1. There is only one bit flag per table, because only one commit log is ever being written to across the entire server. All writes to all tables will go into the same commit log, so the bit flag indicates whether a particular commit log contains anything that hasn’t been flushed for a particular table. Once the memtable has been properly flushed to disk, the corresponding commit log’s bit flag is set to 0, indicating that the commit log no longer has to maintain that data for durability purposes. Like regular logfiles, commit logs have a configurable rollover threshold, and once this file size threshold is reached, the log will roll over, carrying with it any extant dirty bit flags.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The SSTable is a concept borrowed from Google’s Bigtable. Once a memtable is flushed to disk as an SSTable, it is immutable and cannot be changed by the application. Despite the fact that SSTables are compacted, this compaction changes only their on-disk representation; it essentially performs the “merge” step of a mergesort into new files and removes the old files on success.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra supports the compression of SSTables in order to maximize use of the available storage. This compression is configurable per table. Each SSTable also has an associated Bloom filter, which is used as an additional performance enhancer.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All writes are sequential, which is the primary reason that writes perform so well in Cassandra. No reads or seeks of any kind are required for writing a value to Cassandra because all writes are append operations. This makes one key limitation on performance&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;the speed of your disk. Compaction is intended to amortize the reorganization of data, but it uses sequential I/O to do so. So the performance benefit is gained by splitting; the write operation is just an immediate append, and then compaction helps to organize for better future read performance. If Cassandra naively inserted values where they ultimately belonged, writing clients would pay for seeks up front.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On reads, Cassandra will read both SSTables and memtables to find data values, as the memtable may contain values that have not yet been flushed to disk.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Caching&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra provides three forms of caching:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Key cache: stores a map of partition keys to row index entries, facilicating faster read access into SSTables stored on disk. The key cache is stored on the JVM heap, configurable through key_cache_size_in_mb and key_cache_save_period in cassandra.yaml;&lt;/li&gt;&#10;&lt;li&gt;Row cache: caches entire rows and can greatly speed up read access for frequently accessed rows, at the cost of more memory usage. The row cache is stored in off-heap memory, configurable through row_cache_size_in_mb and row_cache_save_period in cassandra.yaml;&lt;/li&gt;&#10;&lt;li&gt;counter cache: improve counter performance by reducing lock contention for the most frequently accessed counters.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;By default, key and counter caching are enabled, while row caching is disabled, as it requires more memory. Cassandra saves its caches to disk periodically in order to warm them up more quickly on a node restart.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Hinted Handoff&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hinted handoff mechanism is introduced to cope with the situation where a write request is sent to Cassandra but the replica node where the write belongs is not available. In this situation, the coordinator will create a hint to hang onto this write. Once the coordinator detects via gossip that the intended node is back online, the coordinator node will &amp;#8220;hand off&amp;#8221; to the intended node the &amp;#8220;hint&amp;#8221; regarding the write. Cassandra holds a separate hint for each partition that is to be written.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This allows Cassandra to be always available for writes, and generally enables a cluster to sustain the same write load even when some of the nodes are down. It also reduces the time that a failed node will be inconsistent after it does come back online.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hints do not count as writes for the purposes of consistency level, except for consistency level ANY. Hinted handoff can be configured through properties hinted_handoff_enabled, max_hint_window_in_ms and hinted_handoff_throttle_in_kb, max_hints_delivery_threads and batchlog_replay_throttle_in_kb in cassandra.yaml.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is a practical problem with hinted handoffs (and guaranteed delivery approaches, for that matter): if a node is offline for some time, the hints can build up considerably on other nodes. Then, when the other nodes notice that the failed node has come back online, they tend to flood that node with requests, just at the moment it is most vulnerable (when it is struggling to come back into play after a failure). To address this problem, Cassandra limits the storage of hints to a configurable time window. It is also possible to disable hinted handoff entirely.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although hinted handoff helps increase Cassandra’s availability, it does not fully replace the need for manual repair to ensure consistency.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lightweight Transactions&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If a client is going to read (check existence) and then write a record (only if not existed already). We&amp;#8217;d like to guarantee linearizable consistency. In other words, no other client can come in between our read and write queries with their own modification. Lightweight transaction is a mechanism to support linearizable consistency based on Paxos. Paxos is a consensus algorithm that allows distributed peer nodes to agree on a proposal, without requiring a master to coordinate a transaction. It emerged as alternative to traditional two-phase commit.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra&amp;#8217;s lightweight transactions are limited to a single partition.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Tombstones&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you execute a delete operation, the data is not immediately deleted. Instead, it&amp;#8217;s treated as an update operation that places a tombstone on the record. A tombstone is a deletion marker that is required to suppress older data in SSTables until compaction can run. The per-table setting gc_grace_period is the amount of time that the server will wait to garbage-collect tombstones. Once a tombstones ages over the grace period, they will be garbage-collected.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Bloom Filters&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Introduced to boost the performance of reads, Bloom filters are very fast, non-deterministic algorithms for testing whether an element is a member of a set. Being deterministic means false-positive is possible but not false-negative. In other words, if the filter indicates the given element exists in the set, cassandra needs to make sure by checking the set (disk); if the filter indicates the given element does not exist in the set, it certainly doesn&amp;#8217;t. Bloom filter is a special kind of cache, stored in memory to improve performance by reducing the need for disk access on key lookups. The accuracy can be increased (to reduce the chance of false positives) by increasing the filter size, at the cost of more memory. This is tunable per table using bloom_filter_fp_chance. Bloom filters are used in other distributed database and caching technologies as well such as Hadoop.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Compaction&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A compaction operation in Cassandra is performed in order to merge SSTables. During compaction, the data in SSTables is merged: the keys are merged, columns are combined, tombstones are discarded, and a new index is created. Compaction is the process of freeing up space by merging large accumulated data files.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is roughly analogous to rebuilding a table in the relational world. But the primary difference in Cassandra is that it is intended as a transparent operation that is amortized across the life of the server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another important function of compaction is to improve performance by reducing the number of required seeks. There are a bounded number of SSTables to inspect to find the column data for a given key. If a key is frequently mutated, it’s very likely that the mutations will all end up in flushed SSTables. Compacting them prevents the database from having to perform a seek to pull the data from each SSTable in order to locate the current value of each column requested in a read request.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When compaction is performed, there is a temporary spike in disk I/O and the size of data on disk while old SSTables are read and new SSTables are being written. Cassandra supports multiple algorithms for compaction via the strategy pattern. The compaction strategy is an option that is set for each table. Strategies include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;SizeTieredCompactionStrategy (STCS) is the default compaction strategy and is recommended for write-intensive tables;&lt;/li&gt;&#10;&lt;li&gt;LeveledCompactionStrategy (LCS) is recommended for read-intensive tables;&lt;/li&gt;&#10;&lt;li&gt;DateTieredCompactionStrategy (DTCS), which is intended for time series or otherwise date-based data.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When compaction is performed, there is a temporary spike in disk I/O and the size of data on disk while old SSTables are read and new SSTables are being written.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Repairs&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Replica synchronization is supported via two different modes known as read repair and antri-entropy repair.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Read repair: the synchronization of replicas as data is read. Cassandra reads data from multiple replicas in order to achieve the requested consistency leve, and detects if any replicas have out of date values. If an insufficient number of nodes have the latest value, a read repair is performed to update the out of date replicas, either immediately or in the background.&lt;/li&gt;&#10;&lt;li&gt;Anti-entropy repair (aka manual repair) is manually initiated operation performed on nodes as part of a regular maintenance process. This is initiated with nodetool repair command, which executes a major compaction. During a major compaction, the server initiates a TreeRequest/TreeResponse conversation to exchange Merkle trees with neighbouring nodes.  The Merkel tree is a hash representing the data in that table. If the trees from different nodes don&amp;#8217;t match, they have to be reconciled (repaired) to determine the latest data values they should all be set to. DynamoDB also use Merkle tress for anti-entropy, with a slightly different implementation.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Reference:&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/03/bash-tricks-continued/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Linux Admin Basics 2 of 3 – shell scripting&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/04/centos-remove-swap-safely/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;CentOS – Remove Swap safely&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>