<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>logging on Digi Hunch</title><link>https://www.digihunch.com/tag/logging/</link><description>Recent content in logging on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 02 Apr 2025 13:59:03 -0400</lastBuildDate><atom:link href="https://www.digihunch.com/tag/logging/index.xml" rel="self" type="application/rss+xml"/><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 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 are the priority of the controls:&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 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 allows 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 is 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>Logging and Monitoring in Kubernetes with PLG stack</title><link>https://www.digihunch.com/2021/10/logging-and-monitoring-in-kubernetes-with-plg-stack/</link><pubDate>Wed, 13 Oct 2021 21:29:00 -0400</pubDate><guid>https://www.digihunch.com/2021/10/logging-and-monitoring-in-kubernetes-with-plg-stack/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-k8s-plg.webp" alt="Featured image of post Logging and Monitoring in Kubernetes with PLG stack" /&gt;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ve checked out the the actors in PLG stack (Promtail, Loki, Node Exporter, Prometheus, Grafana) and whipped up a quick pipeline on MacOS. Now I&amp;#8217;m going a little further to implement the same PLG stack (Prometheus Loki and Grafana) in a Kubernetes cluster. This setup is for demo only, therefore no persistent storage is enabled.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-test-workload"&gt;Test Workload&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I host a deployment of Flog with three pods running on Minikube. Flog is an open-source emulating log generation behaviour of an application. On the Minikube cluster we start the deployment as below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;minikube start --driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hyperkit --container-runtime&lt;span style="color:#f92672"&gt;=&lt;/span&gt;containerd --memory&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12288&lt;/span&gt; --cpus&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl create ns obsv&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv create deployment flog --image&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mingrammer/flog --replicas&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; -- flog -f rfc3164 -l -d 300ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv get po&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The Pods will come up in a heartbeat. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I will use &lt;a href="https://www.digihunch.com/2021/07/helm-configuration-management-for-kubernetes-resources/"&gt;helm&lt;/a&gt; to install the objects required for logging and metrics pipelines. There are multiple Helm charts for each components. Some high-level charts (usually with a name suffix of -stack) contain several other resource as sub-charts. They are created as one-stop-shop for multiple components but I found none of them serve my exact purpose. For example, both &lt;a href="https://artifacthub.io/packages/helm/grafana/loki-stack"&gt;loki-stack&lt;/a&gt; and &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack"&gt;kube-prometheus-stack&lt;/a&gt; include Grafana. But I only need one instance of Grafana. Therefore I stick to the low-level charts.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt; &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-log-shipping"&gt;Log Shipping&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Add a helm repo, and install loki and promtail. Note that we need to specify correct loki address when installing Promtail. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add grafana https://grafana.github.io/helm-charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo update&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --namespace obsv --install loki grafana/loki&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --namespace obsv --install promtail grafana/promtail --set &lt;span style="color:#e6db74"&gt;&amp;#34;config.lokiAddress=http://loki.obsv.svc.cluster.local:3100/loki/api/v1/push&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When installing Promtail, a DaemonSet is created on the Node. The default configuration applies appropriate configuration and tagging strategy for Kubernetes Pod and Node. So the only customization I specified is Loki address. We can then check logging with Loki. To do so, first expose port 3100 to host, and then use logcli (e.g. on MacOS) to query for logs:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv port-forward service/loki 3100:3100&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli labels&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli labels pod&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli query &lt;span style="color:#e6db74"&gt;&amp;#39;{pod=&amp;#34;flog-775d5fc5c8-p4rlx&amp;#34;}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Log lines should be pumped to Loki a minute after Loki comes up. The logcli labels command should display the tags. The logcli query command should return the log lines. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-metrics"&gt;Metrics&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I use Premetheus with node exporter. In its &lt;a href="https://prometheus.io/docs/introduction/overview/#architecture"&gt;architecture&lt;/a&gt;, Prometheus contain the server, the pushgateway, and alertmanager. The &lt;a href="https://github.com/prometheus-community/helm-charts"&gt;helm chart&lt;/a&gt; for &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/prometheus"&gt;Prometheus&lt;/a&gt; contains all of those components. It also has a dependency repo for &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/kube-state-metrics"&gt;kube-state-metrics&lt;/a&gt;. To install:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add prometheus-community https://prometheus-community.github.io/helm-charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add kube-state-metrics https://kubernetes.github.io/kube-state-metrics&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm --namespace&lt;span style="color:#f92672"&gt;=&lt;/span&gt;obsv install prometheus prometheus-community/prometheus&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n obsv get svc&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Once installed, a DaemonSet for Prometheus Node Exporter is created. The exporter is already configured by default for Kubernetes monitoring. The Prometheus server is also configured, on port 80 by default. it needs to be forwarded in order to access from Browser:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl --namespace obsv port-forward service/prometheus-server 9100:80&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To verify installation of Prometheus, browse to localhost:9100 to examine the metrics.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-visualization"&gt;Visualization&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Last but not least, I will configure Grafana. The repo has been added already so we&amp;#8217;ll just install the chart:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --namespace obsv --install grafana grafana/grafana&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get secret --namespace obsv grafana -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{.data.admin-password}&amp;#34;&lt;/span&gt; | base64 --decode ; echo&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The second command retrieves the credential. To access the web portal, we need port forwarding again:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl port-forward --namespace obsv service/grafana 3000:80&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To verify installation, browse to http://localhost:3000 and log in as user admin with the password above. Then add two data sources:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Type: Prometheus, URL: http://prometheus-server.obsv.svc.cluster.local:80&lt;/li&gt;&#10;&lt;li&gt;Type: Loki, URL: http://loki.obsv.svc.cluster.local:3100&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then we can explore data using both data sources.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://d33wubrfki0l68.cloudfront.net/0862f7967545b9ebe1041764e9427a8bf0f44a08/6b8ba/assets/img/uploads/2020/04/image2.png" alt="grafana workflow"/&gt;&lt;figcaption class="wp-element-caption"&gt;PLG stack&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the last two posts I reviewed the setups for PLG stack in Kubernetes, from a regular environment to k8s cluster. Fluentd, Prometheus are both CNCF projects. The PLG stack seems to be more adopted than EFK but both have their own advantages. Welcome to the PLG vs EFK debate.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/10/intro-to-plg-stack-prometheus-loki-and-grafana/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Intro to PLG stack -Prometheus, Loki and Grafana&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/10/notes-on-azure/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Azure Deets&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Intro to PLG stack -Prometheus, Loki and Grafana</title><link>https://www.digihunch.com/2021/10/intro-to-plg-stack-prometheus-loki-and-grafana/</link><pubDate>Sun, 03 Oct 2021 12:59:00 -0400</pubDate><guid>https://www.digihunch.com/2021/10/intro-to-plg-stack-prometheus-loki-and-grafana/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-plg-intro.webp" alt="Featured image of post Intro to PLG stack -Prometheus, Loki and Grafana" /&gt;&lt;p class="wp-block-paragraph"&gt;Last month we discussed &lt;a href="https://www.digihunch.com/2021/09/log-shipping-in-kubernetes-with-efk/"&gt;log shipping with EFK&lt;/a&gt;. This week I spent sometime checking out its alternative Loki. Having been exposed to the &lt;a href="https://www.digihunch.com/2018/09/log-shipping-through-elk/"&gt;ELK stack&lt;/a&gt; extensively, I am also interested in exploring the counterparts in this new stack, such as Prometheus, Loki and Grafana. So I need to address the issues of shipping both metrics and logs.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s start by clarifying the terms:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Grafana&lt;/strong&gt; is a visualizer. It supports many backends such as Prometheus, Loki, Elasticsearch, CloudWatch and Azure Monitor. It is the flagship product of Grafana Labs.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Prometheus&lt;/strong&gt; is a time-series database and alerting platform. To push metrics to Premethus, you can either integrate your application with client library (in their term, &lt;em&gt;instrumenting&lt;/em&gt;), or configure an existing &lt;a href="https://prometheus.io/docs/instrumenting/exporters/"&gt;exporters&lt;/a&gt; for a third party application such as PostgreSQL. Prometheus &lt;em&gt;collects&lt;/em&gt; and &lt;em&gt;stores&lt;/em&gt; its metrics as time series data ( i.e. metrics information is stored with the timestamp at which it was recorded, alongside optional key-value pairs called labels) and it comes with basic visualization capability. Premetheus is a CNCF project since 2016 and is maintained by Grafana Labs.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Loki&lt;/strong&gt; is a log aggregation system, also developed by Grafana Labs. Loki does not index the contents of the logs. Instead it groups entries into streams, and indexes a set of labels for each log stream. You can use grafana or logcli to consume the logs. Loki supports clients such as Fluentd, Fluentbit, Logstash and Promtail.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Promtail&lt;/strong&gt; is a log collection agent built for Loki.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The roles of the components such as Prometheus, Loki, Grafana and Promtail are similar to the ELK stack. Grafana resembles Kibana. Promtail resembles Filebeat. Premethus exporters resemble Metricbeat. Both Premetheus and Loki resemble Elasticsearch in some aspects. Premetheus keeps metrics and Loki persists log streams. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These tools are heavily used in Kubernetes. For a simple start, I&amp;#8217;d like to just configure two minimally working pipelines on my MacBook without any containerization. My example setup is to achieve the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Fake up some log lines and ship them to Loki&lt;/li&gt;&#10;&lt;li&gt;Ship OS metrics to Prometheus&lt;/li&gt;&#10;&lt;li&gt;Display the metrics and logs with Grafana&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here&amp;#8217;s the diagram of what we want to implement:&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="491px" viewBox="-0.5 -0.5 491 106" style="max-width:100%;max-height:106px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="490" height="105" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 206.37 30 L 220 30 L 210 30 L 223.63 30" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 201.12 30 L 208.12 26.5 L 206.37 30 L 208.12 33.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 228.88 30 L 221.88 33.5 L 223.63 30 L 221.88 26.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="110" y="15" width="90" height="30" rx="4.5" ry="4.5" 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: 88px; height: 1px; padding-top: 30px; margin-left: 111px;"&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;Promtail&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="155" y="34" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Promtail&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 206.37 70 L 220 70 L 210 70 L 223.63 70" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 201.12 70 L 208.12 66.5 L 206.37 70 L 208.12 73.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 228.88 70 L 221.88 73.5 L 223.63 70 L 221.88 66.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="110" y="55" width="90" height="30" rx="4.5" ry="4.5" 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: 88px; height: 1px; padding-top: 70px; margin-left: 111px;"&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;Node Exporter&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="155" y="74" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Node Exporter&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 326.37 30 L 340 30 L 340 50 L 353.63 50" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 321.12 30 L 328.12 26.5 L 326.37 30 L 328.12 33.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 358.88 50 L 351.88 53.5 L 353.63 50 L 351.88 46.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="230" y="15" width="90" height="30" rx="4.5" ry="4.5" 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: 88px; height: 1px; padding-top: 30px; margin-left: 231px;"&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;Loki&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="275" y="34" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Loki&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 326.37 70 L 340 70 L 340 50 L 353.63 50" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 321.12 70 L 328.12 66.5 L 326.37 70 L 328.12 73.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 358.88 50 L 351.88 53.5 L 353.63 50 L 351.88 46.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="230" y="55" width="90" height="30" rx="4.5" ry="4.5" 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: 88px; height: 1px; padding-top: 70px; margin-left: 231px;"&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;Prometheus&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="275" y="74" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Prometheus&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="35" width="90" height="30" rx="4.5" ry="4.5" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 50px; margin-left: 361px;"&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;Grafana&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="405" y="54" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Grafana&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 30 L 100 30 L 90 30 L 103.63 30" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 108.88 30 L 101.88 33.5 L 103.63 30 L 101.88 26.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="10" y="15" width="70" height="30" fill="#ffffff" stroke="#000000" 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: 68px; height: 1px; padding-top: 30px; 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: all; white-space: normal; word-wrap: normal; "&gt;Log File&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="45" y="34" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Log File&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 70 L 100 70 L 90 70 L 103.63 70" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 108.88 70 L 101.88 73.5 L 103.63 70 L 101.88 66.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="10" y="55" width="70" height="30" fill="#ffffff" stroke="#000000" 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: 68px; height: 1px; padding-top: 70px; 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: all; white-space: normal; word-wrap: normal; "&gt;Collectors&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="45" y="74" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Collectors&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.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;This is simple enough to build a quick and dirty setup&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-node-exporter"&gt;Configure Node Exporter&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use home brew to install node_exporter on MacOS. For Linux, use the equivalent package management tool:&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;brew install node_exporter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew services start node_exporter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl http://localhost:9100/metrics | grep &lt;span style="color:#e6db74"&gt;&amp;#34;node_&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 node_exporter collect metrics and make it available for scrape using port 9100 as shown above. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-prometheus"&gt;Configure Prometheus&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Install Prometheus with homebrew and start the service:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install prometheus&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew services start prometheus&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;&lt;meta charset="utf-8"&gt;If curl to port 9090 with GET returns &amp;#8220;Found&amp;#8221;, then Prometheus is successfully installed. We also want to configure it so it scrapes node exporter for metrics. Edit the configuration file, in my case, /usr/local/etc/prometheus.yml, by adding the followings:&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;job_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&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:#f92672"&gt;static_configs&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;targets&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#39;localhost:9100&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;This tells Prometheus to scrape metrics at specified interval. Restart Prometheus and browse to http://localhost:9090/ for Prometheus UI. Click on Status -&amp;gt; Targets and you can see the node as an export. Click on Graph and Execute a query for example &amp;#8220;node_memory_free_bytes&amp;#8221; and click on Graph. You should see a plot of the metric value.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1057" height="801" src="https://www.digihunch.com/wp-content/uploads/2021/10/image-1.png" alt="" class="wp-image-2759"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can later configure to display the chart in Grafana.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-generate-log-lines"&gt;Generate log lines &lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use a tool called &lt;a href="https://hub.docker.com/r/mingrammer/flog"&gt;flog&lt;/a&gt; to generate fake logs:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew tap mingrammer/flog&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install flog&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flog -f rfc3164 -l -d 300ms -t log -o /tmp/test.log -w&#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 produces log in RFC3164 format to /tmp/test.log, one line every 300ms. The log does not rotate.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-loki"&gt;Configure Loki&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Install Loki with homebrew and start the service. We want to configure Loki before Promtail so it is ready to receive logs. You might as well install LogCLI to interact with Loki.&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;brew install loki&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install logcli&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew services start loki&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If nc to port 3100 returns success, then Loki is successfully installed. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-promtail"&gt;Configure Promtail&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Install promtail using homebrew:&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;brew install promtail&#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 Promtail is not installed as a &lt;a href="https://github.com/Homebrew/homebrew-services"&gt;homebrew&lt;/a&gt; service. Although it can be manually configured as a service, I&amp;#8217;d rather stay focused and use command line just for the time being. A copy of configuration file is located in /usr/local/etc/promtail-local-config.yaml but it needs to be modified first with a job to tell it where to scrap the log lines. The configuration 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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; http_listen_port: &lt;span style="color:#ae81ff"&gt;9080&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grpc_listen_port: &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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;positions:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; filename: /tmp/positions.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;clients:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - url: http://localhost:3100/loki/api/v1/push&#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;scrape_configs:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- job_name: app&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; static_configs:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - targets:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - localhost&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; labels:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; job: applogs&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; __path__: /tmp/test.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then, use the command as in the &lt;a href="https://grafana.com/docs/loki/latest/getting-started/get-logs-into-loki/"&gt;documentation&lt;/a&gt; to start promtail:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;promtail -config.file /usr/local/etc/promtail-local-config.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Port 9080 will be open when Promtail is running. Promtail will push logs to Loki. You can use &lt;a href="https://grafana.com/docs/loki/latest/getting-started/logcli/"&gt;LogCLI&lt;/a&gt; to interact with Loki and see the latest log lines pushed to Loki. &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;logcli labels job&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logcli query &lt;span style="color:#e6db74"&gt;&amp;#39;{job=&amp;#34;applogs&amp;#34;}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;However, unlike Prometheus, Loki itself does not have any visualization capabilities. We will need Grafana to display the log nicely on the web.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-grafana"&gt;Configure Grafana&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Grafana to visualize both the logs and metrics. To install Grafana on Mac:&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;brew install grafana&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew services start grafana&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If curl to port 3000 with GET returns &amp;#8220;Found&amp;#8221;, then Grafana is successfully installed. Browse to localhost:3000, with default credential admin and admin. From the UI, add two data sources. For the first data source, specify Prometheus as type and localhost:9000 as destination. For the second, specify Loki as the destination http://localhost:3100&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The steps to see logs in Loki is pretty much the same as on &lt;a href="https://grafana.com/docs/loki/latest/getting-started/grafana/"&gt;this&lt;/a&gt; page of its documentation.&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="1394" height="1055" src="https://www.digihunch.com/wp-content/uploads/2021/10/image-3.png" alt="" class="wp-image-2766"/&gt;&lt;figcaption class="wp-element-caption"&gt;Exploring logs&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Exploring metrics data is similar. Click on Explore on the side bar, then select Prometheus from the dropdown as data source. Then execute a query such as &amp;#8220;&lt;meta charset="utf-8"&gt;node_memory_free_bytes&amp;#8221;.&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" src="https://www.digihunch.com/wp-content/uploads/2021/10/image-2.png" alt="" class="wp-image-2764" width="840" height="513"/&gt;&lt;figcaption class="wp-element-caption"&gt;Explore Metrics&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With this quick and dirty configuration, we established a good understanding of what Prometheus, Loki and Grafana do. Next, we will move all these configurations to K8s cluster and understand some specific points of configurations.&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/file-storage-vs-object-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;File storage vs object storage in the cloud&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/10/logging-and-monitoring-in-kubernetes-with-plg-stack/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Logging and Monitoring in Kubernetes with PLG stack&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Log Shipping in Kubernetes with EFK stack</title><link>https://www.digihunch.com/2021/09/log-shipping-in-kubernetes-with-efk/</link><pubDate>Sat, 04 Sep 2021 21:50:00 -0400</pubDate><guid>https://www.digihunch.com/2021/09/log-shipping-in-kubernetes-with-efk/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-elk-logshipping.webp" alt="Featured image of post Log Shipping in Kubernetes with EFK stack" /&gt;&lt;p class="wp-block-paragraph"&gt;I first worked on &lt;a href="https://www.digihunch.com/2018/09/log-shipping-through-elk/"&gt;log shipping&lt;/a&gt; with ELK stack three years ago. In the context of Kubernetes cluster, log shipping has similar challenges. In this post I will discuss the set up of log shipping with Kubernetes cluster using EFK stack&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-logging-architecture"&gt;Logging Architecture&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As &lt;a href="https://www.digihunch.com/2021/08/docker-desktop-a-single-node-kubernetes-cluster/"&gt;discussed&lt;/a&gt;, if the Kubernetes cluster has a runtime in compliant with CRI (e.g. &lt;em&gt;containerd&lt;/em&gt;), then the stdout and stderr of the Pod is stored on the node, in the location /var/log/containers/.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When creating log shipping solution, it is important to use a compliant cluster (e.g. minikube) to ensure what you develop will work across environments. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Kubernetes document has a section on &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/logging/#cluster-level-logging-architectures"&gt;logging architecture&lt;/a&gt; which is a good start point. It outlines several different patterns. Logging at the node level is turned on by default and does not require special configuration, as explained in the section above. The EFK pattern is close to the diagram under &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/logging/#cluster-level-logging-architectures"&gt;using a node logging agent&lt;/a&gt; for cluster-level logging.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://d33wubrfki0l68.cloudfront.net/2585cf9757d316b9030cf36d6a4e6b8ea7eedf5a/1509f/images/docs/user-guide/logging/logging-with-node-agent.png" alt="Using a node level logging agent"/&gt;&lt;figcaption class="wp-element-caption"&gt;Node logging agent&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram above is stolen from Kubernetes documentation. In EFK stack, the agent is a daemonset running fluentd Pod. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-efk-stack"&gt;EFK stack&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;While we can use ELK (Elasticsearch, Logstash, Kibana) stack for log shipping, EFK (Elasticsearch, Fluentd, Kibana) is generally recommended in Kubernetes cluster. We compare the two in the following table:&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;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;ELK&lt;/td&gt;&lt;td&gt;EFK&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Development language&lt;/td&gt;&lt;td&gt;All in Java. Logstash and Filebeat both require JVM. Managed as an open-source project by Elastic company&lt;/td&gt;&lt;td&gt;Fluentd in Ruby and does not require JVM to run. Fluentd is a CNCF project built to integrate with Kubernetes.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Typical pattern&lt;/td&gt;&lt;td&gt;Filebeat acts as a lightweight collector to monitor the source log. Logstash as aggregator to receive from filebeat, and push to Elasticsearch&lt;/td&gt;&lt;td&gt;The fluentd Pod can be configured to serve as forwarder and aggregator based on configuration. fluentd-forwarder is deployed as daemonset on node, and ship the result to fluentd-aggregator, which may run in a separate cluster. The fluentd-aggregator pushes processed results to Elasticsearch.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Metrics&lt;/td&gt;&lt;td&gt;use metric beat for data collection&lt;/td&gt;&lt;td&gt;scrape metrics from prometheus server&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Event Routing&lt;/td&gt;&lt;td&gt;tag-based&lt;/td&gt;&lt;td&gt;if-then statement&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;ELK and EFK stacks comparison&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main problems in log shipping are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;column mapping : identify column patterns in each log line and map them to appropriate column in Elasticsearch.&lt;/li&gt;&#10;&lt;li&gt;multi-line processing: identify when a logging entry spread across multiple lines and process accordingly.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Suppose we want to congregate the logs from stdout and stderr of PostgreSQL pods. The raw output in /var/log/container on the node, 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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:46.75370563Z stdout F server stopped&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:46.757173069Z stderr F postgresql-repmgr 15:27:46.75 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Starting PostgreSQL in background...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:46.883126928Z stderr F postgresql-repmgr 15:27:46.88 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Registering Primary...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.017164653Z stderr F postgresql-repmgr 15:27:47.01 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Loading custom scripts...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.023334611Z stderr F postgresql-repmgr 15:27:47.02 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Loading user&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;s custom files from /docker-entrypoint-initdb.d ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.026169813Z stderr F postgresql-repmgr 15:27:47.02 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Starting PostgreSQL in background...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.343607487Z stderr F postgresql-repmgr 15:27:47.34 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Stopping PostgreSQL...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.448111425Z stdout F waiting &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; server to shut down.... &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;2021-08-28T15:27:47.448172479Z stdout F server stopped&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.453722807Z stderr F postgresql-repmgr 15:27:47.45 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; ** PostgreSQL with Replication Manager setup finished! **&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.453829953Z stdout F&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.503516746Z stderr F postgresql-repmgr 15:27:47.50 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; Starting PostgreSQL in background...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.532558987Z stdout F waiting &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; server to start....2021-08-28 15:27:47.532 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: pgaudit extension initialized&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.533307459Z stdout F 2021-08-28 15:27:47.533 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: listening on IPv4 address &lt;span style="color:#e6db74"&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;, port &lt;span style="color:#ae81ff"&gt;5432&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.533466407Z stdout F 2021-08-28 15:27:47.533 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: listening on IPv6 address &lt;span style="color:#e6db74"&gt;&amp;#34;::&amp;#34;&lt;/span&gt;, port &lt;span style="color:#ae81ff"&gt;5432&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.537987947Z stdout F 2021-08-28 15:27:47.537 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: listening on Unix socket &lt;span style="color:#e6db74"&gt;&amp;#34;/tmp/.s.PGSQL.5432&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.547956465Z stdout F 2021-08-28 15:27:47.547 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: redirecting log output to logging collector process&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.548005463Z stdout F 2021-08-28 15:27:47.547 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; HINT: Future log output will appear in directory &lt;span style="color:#e6db74"&gt;&amp;#34;/opt/bitnami/postgresql/logs&amp;#34;&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.551741571Z stdout F 2021-08-28 15:27:47.551 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;275&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: database system was shut down at 2021-08-28 15:27:47 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.558012894Z stdout F 2021-08-28 15:27:47.557 GMT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;273&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: database system is ready to accept connections&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.618577092Z stdout F &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;2021-08-28T15:27:47.618708978Z stdout F server started&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.630065958Z stderr F postgresql-repmgr 15:27:47.62 INFO &lt;span style="color:#f92672"&gt;==&lt;/span&gt;&amp;gt; ** Starting repmgrd **&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.638116348Z stderr F &lt;span style="color:#f92672"&gt;[&lt;/span&gt;2021-08-28 15:27:47&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; repmgrd &lt;span style="color:#f92672"&gt;(&lt;/span&gt;repmgrd 5.2.1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; starting up&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.65317627Z stderr F INFO: set_repmgrd_pid&lt;span style="color:#f92672"&gt;()&lt;/span&gt;: provided pidfile is /opt/bitnami/repmgr/tmp/repmgr.pid&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2021-08-28T15:27:47.653232015Z stderr F &lt;span style="color:#f92672"&gt;[&lt;/span&gt;2021-08-28 15:27:47&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; starting monitoring of node &lt;span style="color:#e6db74"&gt;&amp;#34;orthweb-postgresql-ha-postgresql-0&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;ID: 1000&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;2021-08-28T15:27:47.681683703Z stderr F &lt;span style="color:#f92672"&gt;[&lt;/span&gt;2021-08-28 15:27:47&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; monitoring cluster primary &lt;span style="color:#e6db74"&gt;&amp;#34;orthweb-postgresql-ha-postgresql-0&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;ID: 1000&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;2021-08-28T15:28:11.742865958Z stderr F &lt;span style="color:#f92672"&gt;[&lt;/span&gt;2021-08-28 15:28:11&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; new standby &lt;span style="color:#e6db74"&gt;&amp;#34;orthweb-postgresql-ha-postgresql-1&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;ID: 1001&lt;span style="color:#f92672"&gt;)&lt;/span&gt; has connected&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From this snippet of log, we can see each line in stdout or stderr is appended with a timestamp. There are multi-line log entries but each is still appended with a timestamp. This is just how kubernetes keeps the log file for Pod stdout and stderr. To handle that, we need to first take out the real log line, and then process multi-line.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We will go over the installation of EFK stack and the mechanism to address the two challenges above.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-install-elasticsearch-and-kibana"&gt;Install Elasticsearch and Kibana&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To install Elasticsearch, we use the helm chart provided by the official repository:&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 elastic https://helm.elastic.co&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If we run multiple pods on the same hosts, then we need some customized values in order to get the installation to work. The values.yaml file 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:#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:#a6e22e"&gt;antiAffinity&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;soft&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:#a6e22e"&gt;esJavaOpts&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;-Xmx128m -Xms128m&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:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Allocate&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;smaller&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;chunks&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;of&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;memory&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;per&lt;/span&gt; &lt;span style="color:#a6e22e"&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:#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;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;512M&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;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;1000m&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;512M&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 can &amp;#8220;preview&amp;#8221; what values are used for installation, with helm&amp;#8217;s template 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;helm template elasticsearch elastic/elasticsearch -f 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;The effect of the antiAffinity property allows multiple Elasticsearch Pod to be scheduled on the same node. This is not required in production with multiple nodes. To install Elasticsearch and Kibana, 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;helm install elasticsearch elastic/elasticsearch -f values.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm install kibana elastic/kibana&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The Kibana service is exposed on port 5601 of the cluster. To access the port on cluster, we need to run port-forward command as below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl port-forward deployment/kibana-kibana &lt;span style="color:#ae81ff"&gt;5601&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-install-fluentd"&gt;Install Fluentd&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are different ways to configure Fluentd. For example, in the forwarder-aggregator pattern, a forwarder Pod is a Daemonset on each Kubernetes node. The forwarder pushes to the aggregator, at port 24224. &lt;a href="https://docs.fluentd.org/deployment/high-availability"&gt;This&lt;/a&gt; page has some configuration details. This pattern is similar to filebeat -&amp;gt; logstash pattern in ELK stack.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In our case, we use a simplified pattern, with a fluentd daemonset acting as collector and then forward to Elasticsearch. Similarly, in ELK stack we can use filebeat to push to Elasticsearch without Logstash. The only reason is the entire architecture isn&amp;#8217;t as complicated as requiring an aggregator. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We need to create configmap 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-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;ConfigMap&lt;/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;fluentd-cm&lt;/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;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.kubernetes.io/component&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;forwarder&lt;/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.kubernetes.io/instance&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fluentd&lt;/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.kubernetes.io/managed-by&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Helm&lt;/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.kubernetes.io/name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fluentd&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;helm.sh/chart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fluentd-1.3.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;annotations&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;meta.helm.sh/release-name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fluentd&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;meta.helm.sh/release-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;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;fluentd.conf&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; # Ignore fluentd own events&#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; &amp;lt;match fluent.**&amp;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; @type null&#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; &amp;lt;/match&amp;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;&#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; # HTTP input for the liveness and readiness probes&#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; &amp;lt;source&amp;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; @type http&#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; port 9880&#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; &amp;lt;/source&amp;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;&#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; # Throw the healthcheck to the standard output instead of forwarding it&#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; &amp;lt;match fluentd.healthcheck&amp;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; @type null&#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; &amp;lt;/match&amp;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;&#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; # Get the logs from the containers running in the node&#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; &amp;lt;source&amp;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; @type tail&#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; read_from_head 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; tag kubernetes.*&#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; path /var/log/containers/orthweb-postgresql-ha-postgresql-**.log&#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; pos_file /opt/bitnami/fluentd/logs/buffers/fluentd-docker.pos&#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; &amp;lt;parse&amp;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; @type regexp&#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; expression ^(?&amp;lt;time&amp;gt;\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.[^Z]*Z)\s(?&amp;lt;stream&amp;gt;[^\s]+)\s(?&amp;lt;character&amp;gt;[^\s])\s(?&amp;lt;message&amp;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; &amp;lt;/parse&amp;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; &amp;lt;/source&amp;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; # enrich with kubernetes metadata&#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; &amp;lt;filter kubernetes.**&amp;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; @type kubernetes_metadata&#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; @id filter_kube_metadata&#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; kubernetes_url &amp;#34;#{ENV[&amp;#39;FLUENT_FILTER_KUBERNETES_URL&amp;#39;] || &amp;#39;https://&amp;#39; + ENV.fetch(&amp;#39;KUBERNETES_SERVICE_HOST&amp;#39;) + &amp;#39;:&amp;#39; + ENV.fetch(&amp;#39;KUBERNETES_SERVICE_PORT&amp;#39;) + &amp;#39;/api&amp;#39;}&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; verify_ssl &amp;#34;#{ENV[&amp;#39;KUBERNETES_VERIFY_SSL&amp;#39;] || true}&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; ca_file &amp;#34;#{ENV[&amp;#39;KUBERNETES_CA_FILE&amp;#39;]}&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; skip_labels &amp;#34;#{ENV[&amp;#39;FLUENT_KUBERNETES_METADATA_SKIP_LABELS&amp;#39;] || &amp;#39;false&amp;#39;}&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; skip_container_metadata &amp;#34;#{ENV[&amp;#39;FLUENT_KUBERNETES_METADATA_SKIP_CONTAINER_METADATA&amp;#39;] || &amp;#39;false&amp;#39;}&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; skip_master_url &amp;#34;#{ENV[&amp;#39;FLUENT_KUBERNETES_METADATA_SKIP_MASTER_URL&amp;#39;] || &amp;#39;false&amp;#39;}&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; skip_namespace_metadata &amp;#34;#{ENV[&amp;#39;FLUENT_KUBERNETES_METADATA_SKIP_NAMESPACE_METADATA&amp;#39;] || &amp;#39;false&amp;#39;}&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; &amp;lt;/filter&amp;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;&#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; &amp;lt;match kubernetes.var.log.containers.orthweb-postgresql-ha-postgresql-**.log&amp;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; @type elasticsearch&#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; include_tag_key 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; host &amp;#34;elasticsearch-master.default.svc.cluster.local&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; port &amp;#34;9200&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; index_name &amp;#34;postgresql-logs&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; &amp;lt;buffer&amp;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; @type file&#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; path /opt/bitnami/fluentd/logs/buffers/orthanc-logs.buffer&#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; flush_thread_count 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; flush_interval 5s&#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; &amp;lt;/buffer&amp;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; &amp;lt;/match&amp;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 we can create the resource, with helm chart pointing to the config map:&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 fluentd bitnami/fluentd --set aggregator.enabled&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false --set forwarder.configMap&lt;span style="color:#f92672"&gt;=&lt;/span&gt;fluentd-cm&#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 validate the index creation on Elasticsearch:&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;kubectl port-forward service/elasticsearch-master 9200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -XGET localhost:9200/_cat/indices&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From Kibana, we can forward the port as instructed above, and browse to the UI. Once logged on to Kibana, we need to add index pattern first before being able to view the content of index.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-how-about-fluent-bit"&gt;How about Fluent Bit&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Fluentd has an even more lightweight brother Fluent Bit, also a CNCF project, designed by the same team, for environments with tighter resource restrictions. The technical differences are outlined on &lt;a href="https://docs.fluentbit.io/manual/about/fluentd-and-fluent-bit"&gt;this&lt;/a&gt; page outlines the technical differences. In terms of use case, Fluentd is a log aggregator and Fluent Bit is simply a forwarder. In terms of ecosystem, Fluentd has a stronger ecosystem whereas Fluent Bit is more seen in IoT devices. Read &lt;a href="https://logz.io/blog/fluentd-vs-fluent-bit/"&gt;this&lt;/a&gt; post for more comparison.&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;EFK stack (Elasticsearch, Fluentd and Kibana) on Kubernetes is a natural choice for ELK users. Fluentd is a CNCF project created for integration with Kubernetes. It is good alternative to enterprise solution such as &lt;a href="https://docs.fluentd.org/v/0.12/articles/free-alternative-to-splunk-by-fluentd"&gt;Splunk&lt;/a&gt;. There are lots of &lt;a href="https://docs.fluentd.org/input"&gt;plugins&lt;/a&gt; supported and &lt;a href="https://docs.fluentd.org/v/0.12/articles"&gt;articles&lt;/a&gt; on configuration. While developing a solution I had to spend time reading the &lt;a href="https://docs.fluentd.org/input"&gt;input plugin&lt;/a&gt; documentations. &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/08/creating-tls-certificate-kubernetes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Creating X.509 TLS certificate for workload on Kubernetes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/09/single-node-kubernetes-cluster-minikube/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next 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;/nav&gt;&#10;</description></item><item><title>Log file navigator (lnav)</title><link>https://www.digihunch.com/2020/09/log-file-navigator-lnav/</link><pubDate>Wed, 23 Sep 2020 21:03:00 -0400</pubDate><guid>https://www.digihunch.com/2020/09/log-file-navigator-lnav/</guid><description>&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ve used a number of log viewers in command terminal, on MacOS and Linux server. I read system logs, log4j formats, as well as json formats. Unfortunately, I have not found an ideal (free) log viewer, either on UI or in command terminal.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of those I tried lnav is one of the better ones. It&amp;#8217;s been around for more than 10 years and is configurable for a variety of formats. It is available in EPEL-repository for Linux and home brew for Mac.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, if our log (produced by log4j) 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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-10-06 20:59:25,471,DEBUG,org.dcm4che2.net.Association - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;platform-dicomServer-44104-574917&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Association&lt;span style="color:#f92672"&gt;(&lt;/span&gt;552550&lt;span style="color:#f92672"&gt;)&lt;/span&gt;: start ARTIM 5000ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-10-06 20:59:25,471,DEBUG,org.dcm4che2.net.Association - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;platform-dicomServer-44104-574917&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Association&lt;span style="color:#f92672"&gt;(&lt;/span&gt;552550&lt;span style="color:#f92672"&gt;)&lt;/span&gt;: Client closed connecti&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;on without sending data&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-10-06 20:59:25,471,DEBUG,org.dcm4che2.net.Association - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;platform-dicomServer-44104-574917&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Association&lt;span style="color:#f92672"&gt;(&lt;/span&gt;552550&lt;span style="color:#f92672"&gt;)&lt;/span&gt; enter state: Sta1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-10-06 20:59:25,471,INFO,org.dcm4che2.net.Association - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;platform-dicomServer-44104-574917&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Association&lt;span style="color:#f92672"&gt;(&lt;/span&gt;552550&lt;span style="color:#f92672"&gt;)&lt;/span&gt;: close Socket&lt;span style="color:#f92672"&gt;[&lt;/span&gt;addr&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/10.100.101.10,port&lt;span style="color:#f92672"&gt;=&lt;/span&gt;24976,localport&lt;span style="color:#f92672"&gt;=&lt;/span&gt;44104&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;2020-10-06 20:59:25,471,DEBUG,org.dcm4che2.net.AssociationReaper - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;platform-dicomServer-44104-574917&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Stop check &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; idle Association&lt;span style="color:#f92672"&gt;(&lt;/span&gt;552550&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;We can introduce custom formatting, 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-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;dapp&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;title&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;dapp log4j format&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;description&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;dapp log4j format&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;regex&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;dapp&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;pattern&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;^(?&amp;lt;timestamp&amp;gt;\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}),(?&amp;lt;level&amp;gt;\\w+),(?&amp;lt;component&amp;gt;[\\w-.]+) - \\[(?&amp;lt;thread&amp;gt;[^ ]+)\\] (?&amp;lt;body&amp;gt;.*)$&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;level-field&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;level&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;level&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;error&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;ERROR&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;warning&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;WARN&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;info&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;INFO&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;debug&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;DEBUG&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;value&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;level&amp;#34;&lt;/span&gt; : { &lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;identifier&amp;#34;&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:#e6db74"&gt;&amp;#34;component&amp;#34;&lt;/span&gt; : { &lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;identifier&amp;#34;&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:#e6db74"&gt;&amp;#34;thread&amp;#34;&lt;/span&gt; : { &lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;identifier&amp;#34;&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:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt; : { &lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;string&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;highlights&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;DIMSE&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;pattern&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;A-(ASSOCIATE-(RQ|AC)|RELEASE-(RQ|RP)|ABORT)|C-(STORE|MOVE|FIND|ECHO)-(RQ|RSP)&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;color&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;Red&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;sample&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;line&amp;#34;&lt;/span&gt; : &lt;span style="color:#e6db74"&gt;&amp;#34;2020-10-06 12:00:28,500,INFO,dicom.dicom-main - [main] Start listening on port 44104&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;]&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;Save the content above as ~/.lnav/formats/installed/dapp.json, then load the log file with lnav, lnav will display the log by presenting columns in different colours.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1622" height="237" src="https://www.digihunch.com/wp-content/uploads/2020/10/image.png" alt="" class="wp-image-1616"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One of the default behaviours is highlighting the IPv4 address, as shown above. This behaviour is however, not optional and currently cannot be turned off, which is reported &lt;a href="https://github.com/tstack/lnav/issues/783"&gt;here&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/09/spark-cassandra-and-python/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Spark, Cassandra and Python&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/09/automated-deployment-pipeline-1-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automated Deployment Pipeline 1 of 3&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>