<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>sysadmin on Digi Hunch</title><link>https://www.digihunch.com/tag/sysadmin/</link><description>Recent content in sysadmin on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Tue, 08 Apr 2025 14:57:09 -0400</lastBuildDate><atom:link href="https://www.digihunch.com/tag/sysadmin/index.xml" rel="self" type="application/rss+xml"/><item><title>Automated Deployment Pipeline 3 of 3</title><link>https://www.digihunch.com/2020/10/automated-deployment-pipeline-3-of-3/</link><pubDate>Wed, 14 Oct 2020 17:27:00 -0400</pubDate><guid>https://www.digihunch.com/2020/10/automated-deployment-pipeline-3-of-3/</guid><description>&lt;h3 class="wp-block-heading" id="h-background"&gt;Background&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We have previously covered a pipeline example with Jenkins calling Ansible to leverage OpenSSH configuration and Ansible inventory. We also discussed a use case with declarative pipeline.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this posting, I will provide another advanced example, built on declarative pipeline. The pipeline file will be pulled from Git repository. Also, the script is executed on a remote agent, instead of the Jenkins master server. The reason this example is important, is that:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Jenkinsfile is now version controlled (pipeline as code);&lt;/li&gt;&#10;&lt;li&gt;Service script (e.g. python) is also version controlled from a central repository;&lt;/li&gt;&#10;&lt;li&gt;Computing resource is provided by a remote agent. Since the script is pulled before running, the agent is still fungible;&lt;/li&gt;&#10;&lt;li&gt;The result from service script execution is archived (similar to the way build artifact is stored in Jenkins) in master.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The architecture of this pipeline now becomes the followings:&lt;/p&gt;&#10;&lt;p class="has-pale-cyan-blue-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="591px" viewBox="-0.5 -0.5 591 671" style="max-width:100%;max-height:671px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="220" y="0" width="130" height="190" rx="19.5" ry="19.5" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 7px; margin-left: 221px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Vendor&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="19" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Vendor&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="430" y="390" width="160" height="280" rx="24" ry="24" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 397px; margin-left: 431px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Customer B&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="510" y="409" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Customer B&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="400" width="160" height="270" rx="24" ry="24" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 407px; margin-left: 1px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Customer A&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="80" y="419" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Customer A&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="110" width="90" height="30" rx="4.5" ry="4.5" 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: 88px; height: 1px; padding-top: 125px; margin-left: 241px;"&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;SSH Proxy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="129" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;SSH Proxy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="30" width="90" height="60" rx="9" ry="9" 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: 88px; height: 1px; padding-top: 60px; margin-left: 241px;"&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;Jenkins (Master)&lt;br&gt;Ansible&lt;br&gt;OpenSSH&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="64" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Jenkins (Master&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="285" cy="190" rx="30" ry="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 190px; margin-left: 256px;"&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;VPN&lt;br&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="194" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VPN&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="150" cy="410" rx="30" ry="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 410px; margin-left: 121px;"&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;VPN&lt;br&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="150" y="414" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VPN&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="430" cy="400" rx="30" ry="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 400px; margin-left: 401px;"&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;VPN&lt;br&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="430" y="404" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VPN&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 215 285 C 163 285 150 320 191.6 327 C 150 342.4 196.8 376 230.6 362 C 254 390 332 390 358 362 C 410 362 410 334 377.5 320 C 410 292 358 264 312.5 278 C 280 257 228 257 215 285 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 258px; height: 1px; padding-top: 320px; margin-left: 151px;"&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;Internet&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="280" y="324" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Internet&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="470" width="100" height="90" 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: 98px; height: 1px; padding-top: 515px; margin-left: 31px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;agent.jar&lt;br&gt;Ansible&lt;br&gt;OpenSSH&lt;br&gt;Git&lt;br&gt;Python&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="80" y="519" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;agent.jar&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="460" y="470" width="100" height="90" 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: 98px; height: 1px; padding-top: 515px; margin-left: 461px;"&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;agent.jar&lt;br&gt;Ansible&lt;br&gt;OpenSSH&lt;br&gt;Git&lt;br&gt;Python&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="510" y="519" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;agent.jar&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 282.5 257.65 L 284.58 226.35" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 282.15 262.88 L 279.13 255.67 L 282.5 257.65 L 286.11 256.13 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 284.93 221.12 L 287.95 228.33 L 284.58 226.35 L 280.97 227.87 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 173.61 382.89 L 181.4 363.7" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 171.63 387.75 L 171.03 379.95 L 173.61 382.89 L 177.51 382.58 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 183.38 358.84 L 183.99 366.64 L 181.4 363.7 L 177.5 364 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 406.97 372.68 L 401.42 354.1" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 408.47 377.72 L 403.11 372.01 L 406.97 372.68 L 409.82 370.01 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 399.92 349.07 L 405.28 354.78 L 401.42 354.1 L 398.57 356.78 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 84.98 466.04 L 123.8 435.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 80.88 469.3 L 84.18 462.21 L 84.98 466.04 L 88.53 467.69 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 127.91 431.91 L 124.61 439 L 123.8 435.18 L 120.25 433.53 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 456.11 425.28 L 505.1 465.93" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 452.07 421.93 L 459.7 423.7 L 456.11 425.28 L 455.23 429.09 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 509.14 469.29 L 501.52 467.51 L 505.1 465.93 L 505.99 462.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 153.63 L 285 146.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 285 158.88 L 281.5 151.88 L 285 153.63 L 288.5 151.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 141.12 L 288.5 148.12 L 285 146.37 L 281.5 148.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 103.63 L 285 96.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 285 108.88 L 281.5 101.88 L 285 103.63 L 288.5 101.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 91.12 L 288.5 98.12 L 285 96.37 L 281.5 98.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="20" y="590" width="120" height="60" 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: 118px; height: 1px; padding-top: 620px; margin-left: 21px;"&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;Customer Inventory&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="80" y="624" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Customer Inventory&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="450" y="590" width="120" height="60" 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: 118px; height: 1px; padding-top: 620px; margin-left: 451px;"&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;Customer Inventory&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="510" y="624" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Customer Inventory&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 80 583.63 L 80 566.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 80 588.88 L 76.5 581.88 L 80 583.63 L 83.5 581.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 80 561.12 L 83.5 568.12 L 80 566.37 L 76.5 568.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 510 583.63 L 510 566.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 510 588.88 L 506.5 581.88 L 510 583.63 L 513.5 581.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 510 561.12 L 513.5 568.12 L 510 566.37 L 506.5 568.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;ellipse cx="290" cy="345" rx="50" ry="35" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 345px; margin-left: 241px;"&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;Git &lt;br&gt;Repository&lt;br&gt;Jenkisfile&lt;br&gt;scripts&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="290" y="349" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Git&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 133.46 509.65 L 236.54 350.35" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 130.61 514.06 L 131.47 506.28 L 133.46 509.65 L 137.35 510.09 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 239.39 345.94 L 238.53 353.72 L 236.54 350.35 L 232.65 349.91 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 343.67 350.2 L 456.33 509.8" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 340.64 345.91 L 347.54 349.61 L 343.67 350.2 L 341.82 353.65 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 459.36 514.09 L 452.46 510.39 L 456.33 509.8 L 458.18 506.35 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank" rel="noopener noreferrer"&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;h3 class="wp-block-heading" id="h-configure-agent"&gt;Configure Agent&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Jenkins has a plugin called &lt;a href="https://plugins.jenkins.io/ssh-slaves/"&gt;SSH Build Agent&lt;/a&gt;, that allows you to configure a Linux agent, communicating with Jenkins master in SSH. For Window agent, it uses JNLP to communicate with master, which is outside of our scope of discussion. As I touched on in &lt;a href="https://www.digihunch.com/2020/10/automated-deployment-pipeline-2-of-2/"&gt;previous post&lt;/a&gt;, Jenkins uses its own implementation of SSH protocol to achieve this. This means that it cannot re-use the configurations in ~/.ssh/config and thus the ability to do SSH chaining is eliminated. This is incompatible with our architecture so I have to register Jenkins agent using a different launch method &amp;#8220;Launch agent via execution of command on the master&amp;#8221;. The execution of command on master can still leverage OpenSSH config file. In order to do so, we must copy the agent.jar file to the remote agent first (URL is ${JENKINS_URL}/jnlpJars/agent.jar). Then use SSH command to call the jar file from agent (aka slave) machine. You may add some java argument for troubleshooting. Below is an example configuration for the node.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="2658" height="1650" src="https://www.digihunch.com/wp-content/uploads/2020/10/image-6.png" alt="" class="wp-image-1736" style="width:0px"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I shall also note that if the remote agent is a different operating system where the path of bash might be different, then you need to include the directory of bash executable in PATH environment variable. That can be done as in the screenshot above (PATH=${PATH:/usr/bin}). If this is incorrect, you might run into issues when running sh step in Jenkins pipeline. Here is an &lt;a href="https://qiita.com/xishan/items/881f1e02628170801f4a"&gt;article&lt;/a&gt; about this. To translate that page, the symptom of this issue includes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Error from pipeline execution that says the following, which is very generic:&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;process apparently never started in /home/dhunch/jenkins/workspace/site-remote-job@tmp/durable-b997d26c &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(running Jenkins temporarily with -Dorg.jenkinsci.plugins.durabletask.BourneShellScript.LAUNCH_DIAGNOSTICS=true might make the problem clearer)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Job status shows:&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hudson.AbortException: script returned exit code -2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at org.jenkinsci.plugins.workflow.steps.durable_task.DurableTaskStep$Execution.handleExit(DurableTaskStep.java:659)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at org.jenkinsci.plugins.workflow.steps.durable_task.DurableTaskStep$Execution.check(DurableTaskStep.java:605)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at org.jenkinsci.plugins.workflow.steps.durable_task.DurableTaskStep$Execution.run(DurableTaskStep.java:549)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;at java.base/java.lang.Thread.run(Thread.java:834)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Threaddump (only available during execution) shows:&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Thread #6&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at DSL.sh(awaiting process completion in /home/dhunch/jenkins/workspace/site-remote-job/durable-b997d26c; recurrence period: 9543ms; check task scheduled; cancelled? false done? false)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at WorkflowScript.run(WorkflowScript:9)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at DSL.script(Native Method)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This issue is tricky because none of the symptoms above make you think about the environment variable defined for the agent, and that the issue does not occur until you actually execute a Jenkins pipeline, usually well after the node agent is registered, and only impacts shell step (sh). It is recommended to test this with sh steps in pipeline.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-repository"&gt;Configure Repository&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Our example job executes service script pulled from SCM, on a remote agent. Then the master pulls the result file to itself. The git repository can be set in Jenkins job, where you specify that the script named Jenkinsfile (from the repo) is the pipeline file that needs to be executed.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1428" height="756" src="https://www.digihunch.com/wp-content/uploads/2020/10/image-5.png" alt="" class="wp-image-1734"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is fairly simple, but what makes the situation more complex is the following few requirements:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;it&amp;#8217;s the agent node that needs git (installed) and clone to repo;&lt;/li&gt;&#10;&lt;li&gt;the master does not need (and should not attempt to) clone from repo;&lt;/li&gt;&#10;&lt;li&gt;the master needs to do its job without having to pull SCM;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We need to be able to specify whether each step needs to pull from SCM. The following pipeline syntax shows how this is done:&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-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; agent_dir &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;initial_value&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pipeline &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; agent none&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options &lt;span style="color:#f92672"&gt;{&lt;/span&gt; skipDefaultCheckout&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;span style="color:#f92672"&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; stages &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; stage&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Execute Job&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&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; agent &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; label &lt;span style="color:#e6db74"&gt;&amp;#39;remote-agent-customer1&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options &lt;span style="color:#f92672"&gt;{&lt;/span&gt; skipDefaultCheckout&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&lt;span style="color:#f92672"&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; steps &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; echo &lt;span style="color:#e6db74"&gt;&amp;#39;Executing job on node&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#39;whoami &amp;amp;&amp;amp; pwd&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; script &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; agent_dir &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sh&lt;span style="color:#f92672"&gt;(&lt;/span&gt;returnStdout: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; script: &lt;span style="color:#e6db74"&gt;&amp;#39;echo -n ${WORKSPACE}&amp;#39;&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:#75715e"&gt;// the variable should not include carriage return&#10;&lt;/span&gt;&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; sh &lt;span style="color:#e6db74"&gt;&amp;#34;echo ${agent_dir}&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Pull Result&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&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; agent &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; label &lt;span style="color:#e6db74"&gt;&amp;#39;master&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options &lt;span style="color:#f92672"&gt;{&lt;/span&gt; skipDefaultCheckout&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#75715e"&gt;// no need to pull scm to this agent&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps &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; echo &lt;span style="color:#e6db74"&gt;&amp;#39;Pulling job below&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#34;echo ${agent_dir}&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#34;scp dhunch@site1:\&amp;#34;${agent_dir}\&amp;#34;/result.csv ./&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example pipeline file, we declare agent none for the pipeline, then an agent for each specific stage. we also specify the option skipDefaultCheckout as true at the step where pulling from SCM is not needed. This allows us to finish job with multiple agent, and only pull from SCM as needed. This snippet also exemplifies how to declare a variable, assign it from stdout from one agent, and persist the value across ensuing stages.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-store-result"&gt;Store Result&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The reason we run pipeline jobs on this remote agent is because it is sitting in customer network and has local direct access to data. So this is perfect for situation such as data analytical jobs, which access to database on local network and store result. We need to pull the result file back to agent and make it available on Jenkins. This is so similar to &amp;#8220;archive artifact&amp;#8221; task (commonly seen in CI process) that we can simply use its plugin to achieve what we need. Before archive artifact, we need to pull it to local (master), as shown in the example code above. After that, we need another stage to archive the result.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;Archive Result&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&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; agent &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; label &lt;span style="color:#e6db74"&gt;&amp;#39;master&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options &lt;span style="color:#f92672"&gt;{&lt;/span&gt; skipDefaultCheckout&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#75715e"&gt;// no need to pull scm to this agent&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps &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; archiveArtifacts artifacts: &lt;span style="color:#e6db74"&gt;&amp;#39;*.csv&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;onlyIfSuccessful: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;fingerprint: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;I believe there is plugins to compress artifacts as well.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At the end I&amp;#8217;d like to reiterate my perception about Jenkins. It is a very generic and adaptive automation platform that originally evolved from use cases in build automation. Due to this original root, many components in Jenkins are named around Continuous Integration use cases, such as the &amp;#8220;build&amp;#8221; button, and the &amp;#8220;archiveArtifacts&amp;#8221; step. These misnomers underplays what Jenkins can potentially do in continuous deployment or other automation scenarios. It is important for automation engineers to understand Jenkins components and plugins, through their functionalities and not by the name, and therefore make creative use of Jenkins as automation engine in all scenarios. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An alternative to this proposed pipeline would be Ansible Tower, a commercial project based on open-source Ansible, but with nice UI support. Ansible Tower is Ansible oriented, and it does not have everything that Jenkins can do. It should still be a decent alternative given the proposes pipeline uses Ansible a lot.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/10/automated-deployment-pipeline-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automated Deployment Pipeline 2 of 3&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/10/host-legacy-application-in-docker-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Host legacy application in Docker 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Automated Deployment Pipeline 2 of 3</title><link>https://www.digihunch.com/2020/10/automated-deployment-pipeline-2-of-2/</link><pubDate>Tue, 06 Oct 2020 22:05:00 -0400</pubDate><guid>https://www.digihunch.com/2020/10/automated-deployment-pipeline-2-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;In this posting, we continue to discuss Jenkins&amp;#8217; ability to automate deployment routines. Jenkins supports freestyle project out of the box, as well as Pipeline with several plugins. Freestyle project allows user to specify multiple steps on UI. This does not scale well when your entire process involves many steps. As explained on Jenkins&amp;#8217; website:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;While standard Jenkins “freestyle” jobs support simple continuous integration by allowing you to define sequential tasks in an application lifecycle, they do not create a &lt;strong&gt;persistent record of execution&lt;/strong&gt;, enable one script to address all the steps in a complex workflow, or confer the other advantages of pipelines.&lt;br&gt;In contrast to freestyle jobs, pipelines enable you to define the whole application lifecycle. Pipeline functionality helps Jenkins to support continuous delivery (CD). The Pipeline plugin was built with requirements for a flexible, extensible, and script-based CD workflow capability in mind.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So although freestyle projects are easy to set up, and can do technically everything that Jenkins pipeline can do, the major advantage of Jenkins Pipeline is the ability to manage multiple-step as code, and version control the pipeline-as-code. Here is some more &lt;a href="https://www.jenkins.io/blog/2017/01/19/converting-conditional-to-pipeline/"&gt;information&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We will start with a freestyle project to understand Jenkins&amp;#8217; ability and then advance to building pipelines.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-freestyle-projects"&gt;Freestyle projects&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As mentioned, Jenkins calls a task a &amp;#8220;build&amp;#8221;, and the build can be triggered in a variety of ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Time schedule (with or without parameter)&lt;/li&gt;&#10;&lt;li&gt;Remotely via API&lt;/li&gt;&#10;&lt;li&gt;On completion of other projects&lt;/li&gt;&#10;&lt;li&gt;Poll SCM for changes&lt;/li&gt;&#10;&lt;li&gt;Commit to SCM&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are also several ways to execute a job:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Execute Shell command, batch command or groovy script&lt;/li&gt;&#10;&lt;li&gt;Invoke Ansible adhoc command, playbook or vault&lt;/li&gt;&#10;&lt;li&gt;Conditional on specified boolean value, file existence, etc&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After the job one can specify post-build jobs, for example:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;start other build projects&lt;/li&gt;&#10;&lt;li&gt;notification of various means&lt;/li&gt;&#10;&lt;li&gt;publishing result file&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In deployment, it is common task to execute a task over SSH, using SSH command or invoke Ansible command with Ansible plugin. The former fits simple command line tasks. For example:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="2036" height="738" src="https://www.digihunch.com/wp-content/uploads/2020/10/image-1.png" alt="" class="wp-image-1658" style="width:812px;height:auto"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Ansible plugin is good for more steps and more complicated inventory hierarchies. This &lt;a href="https://www.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/"&gt;post&lt;/a&gt; includes an example of an inventory involving multiple layers. The other limitation with SSH command is lack of a straightforward configuration to escalate privilege and run remote command. On the other hand, Ansible addressed this with become method. Below is a screenshot &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="950" height="837" src="https://www.digihunch.com/wp-content/uploads/2020/10/image-2.png" alt="" class="wp-image-1659" style="width:785px;height:auto"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In order to use Ansible command, you will also need to specify where the Ansible binary in &lt;a href="https://medium.com/devops-process-and-tools/configure-jenkins-job-with-ansible-jenkins-plugin-to-setup-ci-for-ansible-playbooks-3ed23137d314"&gt;Global Tool Configuration&lt;/a&gt;. You will also need to store vault credential in Jenkins credential store so it&amp;#8217;s not being prompted.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-jenkins-pipeline"&gt;Jenkins Pipeline&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Jenkins pipeline allows one to describe actions in a pipeline in groovy &lt;a href="http://docs.groovy-lang.org/docs/latest/html/documentation/core-domain-specific-languages.html"&gt;Domain Specific Language (DSL)&lt;/a&gt;. There are two styles of pipelines:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Declarative pipeline&lt;/strong&gt; is identified by a block named &amp;#8216;pipeline&amp;#8217;, it is relatively new and supports the pipeline-as-code concept. It can be stored as Jenkinsfile in code repository or edited in Jenkins&amp;#8217; UI.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Scripted pipeline&lt;/strong&gt; is identified by a block named &amp;#8216;node&amp;#8217;, it is the conventional format and can only be edited in Jenkins&amp;#8217; UI.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To fully understand the two styles of pipelines you also need to know &lt;a href="https://en.wikipedia.org/wiki/Declarative_programming"&gt;declarative programming&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Imperative_programming"&gt;imperative programming&lt;/a&gt;. Read &lt;a href="https://www.jenkins.io/doc/book/pipeline/syntax/"&gt;this&lt;/a&gt; instruction for a better explanation. &lt;a href="https://e.printstacktrace.blog/jenkins-scripted-pipeline-vs-declarative-pipeline-the-4-practical-differences/"&gt;Here&lt;/a&gt; is another good one that focus on their differences.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Jenkins&amp;#8217; plugin provides UI components for user to input information (e.g. Invoke Ansible Command), now with Jenkins pipelines, plugins can provide step functions in order to help Jenkins user. &lt;a href="https://www.jenkins.io/doc/pipeline/steps/"&gt;This&lt;/a&gt; page from Jenkins lists the most common ones, many of which requires plugin installation. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One example of step function is &lt;a href="https://www.jenkins.io/doc/pipeline/steps/ssh-steps/"&gt;SSH Pipeline Steps&lt;/a&gt;. It allows one to issue SSH connection from Jenkinsfile. However, in our deployment scenarios, it has some limitations:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Unable to match a group of host by specific pattern&lt;/li&gt;&#10;&lt;li&gt;Cannot use host name aliases&lt;/li&gt;&#10;&lt;li&gt;SSH tunneling is not supported&lt;/li&gt;&#10;&lt;li&gt;No means of privilege escalation&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of these 1 through 3 are due to the fact that SSH step function does not use the OpenSSH configuration on the machine. The&amp;nbsp;&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/SSH+Slaves+plugin"&gt;jenkins-ssh-slaves plugin&lt;/a&gt;&amp;nbsp;uses&amp;nbsp;&lt;a href="https://github.com/jenkinsci/trilead-ssh2"&gt;trilead SSH2 implementation&lt;/a&gt;&amp;nbsp;written in Java. Only OpenSSH implementation uses ~/.ssh/config file. This creates problem whenever SSH tunneling is needed. For example, to register a remote slave node via SSH tunneling, we need to select &amp;#8220;Launch agent via &lt;a href="https://support.cloudbees.com/hc/en-us/articles/234491548-Run-SSH-Agent-using-a-command-on-Master"&gt;execution of command on the master&lt;/a&gt;&amp;#8221; instead of &amp;#8220;Launch agent via SSH&amp;#8221; as launch method. &lt;a href="https://support.cloudbees.com/hc/en-us/articles/234491548-Run-SSH-Agent-using-a-command-on-Master"&gt;Here&lt;/a&gt; is an instruction and below is what it looks like:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="727" height="405" src="https://www.digihunch.com/wp-content/uploads/2020/10/image-3.png" alt="" class="wp-image-1698" style="width:755px;height:auto"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similarly, if we use ssh-agent plugin as tool for deployment, we cannot use any configuration made by OpenSSH. Therefore Ansible in Jenkins Pipeline is a better tool for deployment because it can use OpenSSH.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-pipelines-with-ansible"&gt;Pipelines with Ansible&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this section I demonstrate the use of Ansible playbook and adhoc command in pipeline through two examples. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In example 1, the job pulls authorized key file from SCM, and then use a playbook from SCM, to push the key file to all servers in the specified inventory. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is the playbook file for example 1:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&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;# Example:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# ansible-playbook -l all -i ~/ansible/inventories/bh.yml push-key.yml --ask-vault-pass&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- name: push key to target&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts: all&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tasks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: sync key to host&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; copy:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; src: &amp;#34;{{authorized_keys_src}}&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dest: &amp;#34;/home/dhunch/.ssh/authorized_keys&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; force: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mode: 0600&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Here is the pipeline script (note ampersand is mistakenly displayed as &amp;amp;amp; in the box 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-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; java.net.URLEncoder;&#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;pipeline {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; agent any&#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; options {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; skipDefaultCheckout(&lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; environment {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; BITBUCKET_CREDS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; credentials(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;bitbucket&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;) &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//BITBUCKET_CREDS_USR and BITBUCKET_CREDS_PSW are set&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; BITBUCKET_CREDS_USR &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;${BITBUCKET_CREDS_USR}&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; BITBUCKET_CREDS_PSW &lt;span style="color:#f92672"&gt;=&lt;/span&gt; URLEncoder.&lt;span style="color:#a6e22e"&gt;encode&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;${BITBUCKET_CREDS_PSW}&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;) &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// if password contains special character we need to url encode it. e.g. @-&amp;gt;%40&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; stages {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Start&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Starting pipeline ...&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; deleteDir()&#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; stage(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Prep&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; wrap(&lt;span style="color:#f92672"&gt;[&lt;/span&gt;$class: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;MaskPasswordsBuildWrapper&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;, varPasswordPairs: &lt;span style="color:#f92672"&gt;[[&lt;/span&gt;password: &lt;span style="color:#e6db74"&gt;&amp;#34;${BITBUCKET_CREDS_PSW}&amp;#34;&lt;/span&gt;, var: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;RANDOM&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&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:#75715e"&gt;// MaskPasswordsBuildWrapper requires Mask Passwords Plugin and is to mask specific string in console output. &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Otherwise BITBUCKET_CREDS_PSW will display in the clear&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#34;git init &amp;amp;&amp;amp; git config core.sparsecheckout true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#34;git remote add origin https://$BITBUCKET_CREDS_USR:${BITBUCKET_CREDS_PSW}@bitbucket.org/vendorcompoany/configmanagerepo.git&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#34;echo &amp;#39;public_keys/*&amp;#39; &amp;gt;&amp;gt; .git/info/sparse-checkout&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Downloading key file&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sh &lt;span style="color:#e6db74"&gt;&amp;#34;git pull --depth=1 origin master&amp;#34;&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Deploy &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 1&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Deploying &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 1 ...&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansiblePlaybook (&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; installation: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Ansible on Mac&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; playbook: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;${WORKSPACE}&lt;span style="color:#f92672"&gt;/&lt;/span&gt;public_keys&lt;span style="color:#f92672"&gt;/&lt;/span&gt;push&lt;span style="color:#f92672"&gt;-&lt;/span&gt;key.&lt;span style="color:#a6e22e"&gt;yml&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; inventory: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;~/&lt;/span&gt;ansible&lt;span style="color:#f92672"&gt;/&lt;/span&gt;inventories&lt;span style="color:#f92672"&gt;/&lt;/span&gt;site1.&lt;span style="color:#a6e22e"&gt;yml&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vaultCredentialsId: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;vault&lt;span style="color:#f92672"&gt;-&lt;/span&gt;pass&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extraVars: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;authorized_keys_src: &lt;span style="color:#e6db74"&gt;&amp;#34;$WORKSPACE/public_keys/authorized_keys&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; echo &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Deployed &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 1 ...&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Deploy &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 2&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Deploying &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 2 ...&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansiblePlaybook (&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; installation: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Ansible on Mac&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; playbook: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;${WORKSPACE}&lt;span style="color:#f92672"&gt;/&lt;/span&gt;public_keys&lt;span style="color:#f92672"&gt;/&lt;/span&gt;push&lt;span style="color:#f92672"&gt;-&lt;/span&gt;key.&lt;span style="color:#a6e22e"&gt;yml&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; inventory: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;~/&lt;/span&gt;ansible&lt;span style="color:#f92672"&gt;/&lt;/span&gt;inventories&lt;span style="color:#f92672"&gt;/&lt;/span&gt;site2.&lt;span style="color:#a6e22e"&gt;yml&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vaultCredentialsId: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;vault&lt;span style="color:#f92672"&gt;-&lt;/span&gt;pass&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extraVars: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;authorized_keys_src: &lt;span style="color:#e6db74"&gt;&amp;#34;$WORKSPACE/public_keys/authorized_keys&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; echo &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Deployed &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 2 ...&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Deploy &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 3&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Deploying &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 3 ...&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansiblePlaybook (&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; installation: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;Ansible on Mac&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; playbook: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;${WORKSPACE}&lt;span style="color:#f92672"&gt;/&lt;/span&gt;public_keys&lt;span style="color:#f92672"&gt;/&lt;/span&gt;push&lt;span style="color:#f92672"&gt;-&lt;/span&gt;key.&lt;span style="color:#a6e22e"&gt;yml&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; inventory: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;~/&lt;/span&gt;ansible&lt;span style="color:#f92672"&gt;/&lt;/span&gt;inventories&lt;span style="color:#f92672"&gt;/&lt;/span&gt;site3.&lt;span style="color:#a6e22e"&gt;yml&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vaultCredentialsId: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;vault&lt;span style="color:#f92672"&gt;-&lt;/span&gt;pass&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extraVars: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;authorized_keys_src: &lt;span style="color:#e6db74"&gt;&amp;#34;$WORKSPACE/public_keys/authorized_keys&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; echo &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Deployed &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; Site 3 ...&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; always {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cleanWs()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example, we pull a sub-directory from git repo. We use two tricks to minimize amount of traffic. First, we use git pull with depth=1 so only the required recent commits are pulled, not the entire history. Second we use sparse checkout to get result from a sub-directory, not the entire repo.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When connecting to repo, password is required. We pull the credential from Jenkins&amp;#8217; credential store and they are masked by default. However, the password must be converted to URL string when used in git remote add origin. Otherwise if the password contains special character the URL will not work. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This modification to password brings about another challenge, password masking during the job execution. We will have to build our own wrapper function using class &lt;em&gt;MaskPasswordsBuildWrapper&lt;/em&gt; in order to mask any variable.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In example 2, we wrap an Ansible step function to call adhoc command to check version on all hosts. Here is the pipeline script:&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;def HunchVersionQuery(pattern, siteinventory){&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; step([&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $class: &amp;#39;AnsibleAdHocCommandBuilder&amp;#39;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansibleName: &amp;#39;Ansible on Mac&amp;#39;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; inventory: [$class: &amp;#39;InventoryPath&amp;#39;,path: siteinventory],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hostPattern: pattern,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; module: &amp;#39;shell&amp;#39;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; command: &amp;#39;cat /etc/*release&amp;#39;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; forks: 1,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vaultCredentialsId: &amp;#39;ansible-vault-pass&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pipeline {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; agent any&#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; stages {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage(&amp;#39;Start&amp;#39;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &amp;#39;Staring Pipeline&amp;#39;&#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; stage(&amp;#39;Query Site1&amp;#39;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps { HunchVersionQuery(&amp;#39;*app&amp;#39;,&amp;#39;~/ansible/inventories/site1.yml&amp;#39;)&#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; stage(&amp;#39;Query Site2&amp;#39;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps { HunchVersionQuery(&amp;#39;*app&amp;#39;,&amp;#39;~/ansible/inventories/site2.yml&amp;#39;)&#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; stage(&amp;#39;Query Site3&amp;#39;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; steps {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HunchVersionQuery(&amp;#39;*app&amp;#39;,&amp;#39;~/ansible/inventories/site3.yml&amp;#39;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Because &lt;a href="https://www.jenkins.io/doc/pipeline/steps/ansible/"&gt;Ansible plugin&lt;/a&gt; does not provide a warpper function for adhoc command step, we will have to build our own wrapper function in the above code.&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/automated-deployment-pipeline-1-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous 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;a rel="next" href="https://www.digihunch.com/2020/10/automated-deployment-pipeline-3-of-3/"&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 3 of 3&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Automated Deployment Pipeline 1 of 3</title><link>https://www.digihunch.com/2020/09/automated-deployment-pipeline-1-2/</link><pubDate>Wed, 30 Sep 2020 22:04:00 -0400</pubDate><guid>https://www.digihunch.com/2020/09/automated-deployment-pipeline-1-2/</guid><description>&lt;h3 class="wp-block-heading"&gt;The business case&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You launched a software application. You installed it on two customer sites. You support the application mostly by SSH to customer server and run Bash commands, or slightly better, Bash scripts. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The product is a hit to the market. You hired 20 support specialists in a customer service department. The dream client came through: an enterprise giving you a fleet of 100 servers to deploy your application on.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;More staff, more business, more installations, more incidents, but the same old command driven steps. Problems:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Non-standard support procedures. Every one takes notes and everyone&amp;#8217;s notes are slightly different.&lt;/li&gt;&lt;li&gt;Information sharing among team members are ad hoc, and at high level.&lt;/li&gt;&lt;li&gt;Post-mortem discussion is driven by memory and command fragments, instead of evidence end-to-end&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If that looks like your organization, chances are you also suffer from some secondary damages over the long term, such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Downtime resolutions rely on the knowledgeable few&lt;/li&gt;&lt;li&gt;Documentation helps. But it never catches up to the latest version of application&lt;/li&gt;&lt;li&gt;Lack of auditing of commands during support&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I propose an automation scheme to existing support and deployment practice. This automation scheme combines a suite of common technologies, such as Bash, Python, Ansible, OpenSSH and Jenkins. The automation allows the department to, either fully or partially, operationalize the steps in support and deployment, and eventually shift towards agile practice. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Bash, Python and Ansible&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Bash script is based on shell command, perfect for running critical system tasks such as volume management. When it turns into a script, it can be cumbersome, especially with complex data structure. Python, as a tool for system administration, is a good complement to that.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Python 2 comes with most Linux distributions, and is also a dependency of other built-in tools such as YUM. Python3 can be installed easily from default YUM repositories. Both Python2 and Python3 can exist on the same operating system, although new module development are now shifted to Python3. Python&amp;#8217;s syntax is very simple and offers object-oriented programming ability. Moreover, there is an entire open-source community behind Python, which offers modules in every aspect of IT (for example, Datastax has a &lt;a href="https://www.digihunch.com/2020/06/iterate-through-cassandra-table-with-datastax-python-driver/"&gt;driver module&lt;/a&gt; for connecting to Cassandra). Those modules are installed with PIP tool, or PIP3 for python3.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both bash and Python executes on local machine. To run them on remote servers over SSH. You want to have a list of target hosts, and specify which one to execute the script against. This is where Ansible comes in handy. Ansible is superior in the following aspects:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Free and open-source, with commercial alternative (Towers);&lt;/li&gt;&lt;li&gt;Inventory management (inventory);&lt;/li&gt;&lt;li&gt;Desire state engine (roles) &lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ansible is built on Python and is agent-less. Connectivity to remote host is done via secure shell so it can take advantage of existing SSH configurations. Job execution on the target machine is done through Python. With Python you can also develop custom module in Ansible. For some use cases in customer support with Ansible, refer to my two &lt;a href="https://www.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/"&gt;previous postings&lt;/a&gt; about Ansible at scale.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Jenkins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The tools above forms a package for automation. The issue is that all of them are command-line based. Any task that requires Ansible requires the IT professional craft up long command, such as running playbook, executing a role, or ad-hoc command. This is inconvenient when a task needs to be done during an incident. Such tasks also require trained professional with the relevant skills.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These tasks can be stored in, or initiated by &lt;a href="https://www.jenkins.io/"&gt;Jenkins&lt;/a&gt;. Although Jenkins is well known for build automation in continuous integration, it is automation engine for any command-line based IT tasks. The button to start such tasks in Jenkins UI is called &amp;#8220;Build&amp;#8221;, which is also a misnomer that underplays Jenkins&amp;#8217; versatility: building application from source code is just one of the many IT tasks that involves multiple long running commands. In this and next article we introduce Jenkins as an engine for deployment automation. &lt;/p&gt;&#10;&lt;p class="has-text-align-center has-background wp-block-paragraph" style="background-color:#d5edf7"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="587" viewBox="-0.5 -0.5 587 567" style="max-width:100%;max-height:567px;"&gt;&lt;defs&gt;&lt;filter id="dropShadow"&gt;&lt;feGaussianBlur in="SourceAlpha" stdDeviation="1.7" result="blur"&gt;&lt;/feGaussianBlur&gt;&lt;feOffset in="blur" dx="3" dy="3" result="offsetBlur"&gt;&lt;/feOffset&gt;&lt;feFlood flood-color="#3D4574" flood-opacity="0.4" result="offsetColor"&gt;&lt;/feFlood&gt;&lt;feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur"&gt;&lt;/feComposite&gt;&lt;feBlend in="SourceGraphic" in2="offsetBlur"&gt;&lt;/feBlend&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;g filter="url(#dropShadow)"&gt;&lt;rect x="220" y="0" width="130" height="190" rx="19.5" ry="19.5" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 7px; margin-left: 221px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Vendor&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="19" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Vendor&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="420" y="390" width="160" height="160" rx="24" ry="24" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 397px; margin-left: 421px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Customer B&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="500" y="409" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Customer B&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="400" width="160" height="160" rx="24" ry="24" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 407px; margin-left: 1px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Customer A&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="80" y="419" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Customer A&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="110" width="90" height="30" rx="4.5" ry="4.5" 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: 88px; height: 1px; padding-top: 125px; margin-left: 241px;"&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;SSH Proxy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="129" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;SSH Proxy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="30" width="90" height="60" rx="9" ry="9" 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: 88px; height: 1px; padding-top: 60px; margin-left: 241px;"&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;Jenkins&lt;br&gt;Ansible&lt;br&gt;OpenSSH&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="64" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Jenkins&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="285" cy="190" rx="30" ry="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 190px; margin-left: 256px;"&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;VPN&lt;br&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="194" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VPN&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="150" cy="410" rx="30" ry="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 410px; margin-left: 121px;"&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;VPN&lt;br&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="150" y="414" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VPN&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="430" cy="400" rx="30" ry="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 400px; margin-left: 401px;"&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;VPN&lt;br&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="430" y="404" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VPN&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 247.5 280 C 217.5 280 210 310 234 316 C 210 329.2 237 358 256.5 346 C 270 370 315 370 330 346 C 360 346 360 322 341.25 310 C 360 286 330 262 303.75 274 C 285 256 255 256 247.5 280 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 310px; margin-left: 211px;"&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;Internet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="314" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Internet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="20" y="470" width="120" height="60" 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: 118px; height: 1px; padding-top: 500px; margin-left: 21px;"&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;Server Fleet Managed by Vendor&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="80" y="504" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Server Fleet Managed&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="440" y="470" width="120" height="60" 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: 118px; height: 1px; padding-top: 500px; margin-left: 441px;"&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;Server Fleet Managed by Vendor&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="500" y="504" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Server Fleet Managed&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 286.02 255.63 L 285.18 226.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 286.17 260.88 L 282.47 253.99 L 286.02 255.63 L 289.47 253.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285.03 221.12 L 288.73 228.01 L 285.18 226.37 L 281.73 228.21 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 176.2 384.82 L 224.52 346.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 172.09 388.09 L 175.39 380.99 L 176.2 384.82 L 179.74 386.47 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 228.63 343.1 L 225.33 350.19 L 224.52 346.37 L 220.97 344.72 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 403.86 374.76 L 358.93 338.03" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 407.92 378.08 L 400.29 376.36 L 403.86 374.76 L 404.72 370.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 354.87 334.71 L 362.5 336.43 L 358.93 338.03 L 358.07 341.85 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 84.98 466.04 L 123.8 435.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 80.88 469.3 L 84.18 462.21 L 84.98 466.04 L 88.53 467.69 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 127.91 431.91 L 124.61 439 L 123.8 435.18 L 120.25 433.53 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 455.72 425.72 L 495.5 465.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 452 422 L 459.43 424.48 L 455.72 425.72 L 454.48 429.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 499.21 469.21 L 491.78 466.73 L 495.5 465.5 L 496.73 461.78 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 153.63 L 285 146.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 285 158.88 L 281.5 151.88 L 285 153.63 L 288.5 151.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 141.12 L 288.5 148.12 L 285 146.37 L 281.5 148.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 103.63 L 285 96.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 285 108.88 L 281.5 101.88 L 285 103.63 L 288.5 101.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 91.12 L 288.5 98.12 L 285 96.37 L 281.5 98.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank" rel="noopener noreferrer"&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;The infrastructure architecture is diagramed as above, and with the connection across Internet, the target hosts must be hardened properly in the following aspects:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Connectivity to remote host is via SSH chaining, through an SSH proxy;&lt;/li&gt;&lt;li&gt;Root login must be disabled for remote session or by password;&lt;/li&gt;&lt;li&gt;Service user may be shared, but must be authenticated by individual RSA key pair;&lt;/li&gt;&lt;li&gt;Service user connected remotely needs to escalate privilege by su if needed;&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I want to make a theoretical distinction between our topic here and continuous deployment. We simply focus on the technical side of deployment automation. Essentially automating a few bash scripts. On the other hand, a continuous deployment process is an extension to an existing continuous integration pipeline, with the vision to streamline the process end-to-end from code commit to production rollout. Implementing CI/CD pipelines should be approached as an organizational program rather than an individual technical initiative. &lt;a href="https://www.redhat.com/en/blog/integrating-ansible-jenkins-cicd-process"&gt;Here&lt;/a&gt; is a good technical overview on CI/CD pipeline with Jenkins and Ansible.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Security&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The security mechanism of this system is based on OpenSSH because the connectivity between servers are through SSH chaining. RSA key authentication must be used in order to encrypt traffic with password-less login. Connection to an SSH host can be done through a proxy server. Below is an example of SSH configuration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Include customer1.config&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Include customer2.config&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host *&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentityFile ~/.ssh/id_rsa&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ServerAliveInterval &lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ServerAliveCountMax &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Compression yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ControlPersist 3h&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ControlPath ~/.ssh/sockets/%r@%h-%p&#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;Host gateway&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Hostname support.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User jdoe&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Port &lt;span style="color:#ae81ff"&gt;2223&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;Host customer-server-0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Hostname 192.168.201.12&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User support&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ProxyCommand ssh -W %h:%p gateway&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Open SSH configuration file (~/.ssh/config) needs to be configured properly with useful host names and aliases. To prevent the config files from growing too long, include statement can be used to reference other configuration file (available with OpenSSH v7.3 sp1 and up). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The host names (as well as aliases) listed in SSH configuration can be directly referenced in Ansible inventory, allowing Ansible (and Jenkins) to reference site by alias and connect to target host through proxy.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Plugins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Jenkins has a community that develops a variety of plugins, which makes Jenkins the most powerful automation platform. Here are some examples of useful plugins:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Audit Trail: output job execution history to file or Elasticsearch;&lt;/li&gt;&lt;li&gt;Credentials: stores credentials in Jenkins;&lt;/li&gt;&lt;li&gt;Pipeline: build declarative (new) or scripted (old) pipeline for Jenkins jobs;&lt;/li&gt;&lt;li&gt;Simple Theme: just a theme but allows console output to be dark (using CSS);&lt;/li&gt;&lt;li&gt;Job Configuration History: job configuration audit;&lt;/li&gt;&lt;li&gt;Mask password: mask variables (including password) from console output &lt;/li&gt;&lt;li&gt;Ansible: invokes ad-hoc commands and playbooks&lt;/li&gt;&lt;li&gt;SSH agent, SSH pipeline steps, SSH credentials: features related in SSH in Jenkins pipelines.&lt;/li&gt;&lt;li&gt;Purge Job History: purge all of build history, or purge&lt;a href="https://support.cloudbees.com/hc/en-us/articles/215549798-Best-Strategy-for-Disk-Space-Management-Clean-Up-Old-Builds"&gt; by time &lt;/a&gt;and number of old builds.&lt;/li&gt;&lt;li&gt;Parameterized Scheduler: schedule to run a job and provide parameter&lt;/li&gt;&lt;li&gt;Workspace cleanup: clean up workspace when invoked.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the next article, we will go over some common job 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/2020/09/log-file-navigator-lnav/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Log file navigator (lnav)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/10/automated-deployment-pipeline-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automated Deployment Pipeline 2 of 3&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><item><title>Zookeeper Summary</title><link>https://www.digihunch.com/2020/08/zookeeper/</link><pubDate>Wed, 26 Aug 2020 23:10:00 -0400</pubDate><guid>https://www.digihunch.com/2020/08/zookeeper/</guid><description>&lt;h3 class="wp-block-heading" id="h-distributed-systems"&gt;Distributed systems&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Distributed system involves &lt;span style="text-decoration: underline;"&gt;independent computing entities&lt;/span&gt; linked together by network. The components &lt;span style="text-decoration: underline;"&gt;communicate and coordinate&lt;/span&gt; with each other to achieve a &lt;span style="text-decoration: underline;"&gt;common goal&lt;/span&gt;. In early days, designers and developers often had made some assumptions (aka. fallacies) of distributed computing:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The network is reliable&lt;/li&gt;&#10;&lt;li&gt;Latency is zero&lt;/li&gt;&#10;&lt;li&gt;Bandwidth is infinite&lt;/li&gt;&#10;&lt;li&gt;Network is secure&lt;/li&gt;&#10;&lt;li&gt;Topology doesn&amp;#8217;t change: in reality, components to a network get removed/added over time. the system should tolerate such changes.&lt;/li&gt;&#10;&lt;li&gt;There is one administrator: for distributed systems to function, they interact with external system beyond administrative control.&lt;/li&gt;&#10;&lt;li&gt;Transport cost is zero:&amp;nbsp; cost is involved everywhere, in the form of CPU cycles spent, to actual dollars paid to service provider.&lt;/li&gt;&#10;&lt;li&gt;Network is homogenous&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These fallacies make coordinating distributed computing entities a huge challenge and Zookeeper is introduced to address these challenges. Zookeeper implements common tasks for distributed coordination, such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Configuration Management (propagate configuration changes to all worker nodes dynamically)&lt;/li&gt;&#10;&lt;li&gt;Naming service&amp;nbsp;&lt;/li&gt;&#10;&lt;li&gt;Distributed synchronization (locks and barriers)&lt;/li&gt;&#10;&lt;li&gt;Cluster membership operations (e.g. detection of node leave/join)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper is a centralized coordination service for the distributed application. ZooKeeper itself is distributed as well. It runs on its own cluster of servers called a ZooKeeper ensemble, separate from application&amp;#8217;s cluster. Distributed consensus, group management, presence protocols, and leader election are implemented by the service so that the application developers do not need to reinvent the wheel by implementing them on their own.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.6.1/images/zkservice.jpg" alt="ZooKeeper Service"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Developers will have to use APIs through ZooKeeper&amp;#8217;s client library, which has language bindings for almost all popular programming languages. The client library is responsible for the interactions of an application with the ZooKeeper service. For testing with API access one can alternatively use its Java-based command-line shell (zkCli.sh)&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ zkCli.sh -server zknode:2181&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-how-zookeeper-works"&gt;How Zookeeper works&lt;/h3&gt;&#10;&lt;h4 class="wp-block-heading" id="h-data-model"&gt;Data Model&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper allows distributed process to coordinate with each other through a shared hierarchical namespace of data registers (znodes). The hierarchy start with root node which has child znode(s). Each znode can have their children, as well as store its own data (hence the name data register). The data in a znode is stored in byte format for a maximum of 1MB (ZooKeeper by design is just a coordinator service of host application, so its own data set size is fairly small).&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="360" height="368" src="https://www.digihunch.com/wp-content/uploads/2023/01/zkdm.jpeg" alt="" class="wp-image-7753" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/zkdm.jpeg 360w, https://www.digihunch.com/wp-content/uploads/2023/01/zkdm-293x300.jpeg 293w" sizes="auto, (max-width: 360px) 100vw, 360px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Zookeeper data model&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Znodes have two types (set at time of creation) &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;persistent znode: for storing persistent data, such as configuration. The znodes and their data will exist even if the creator client dies.&lt;/li&gt;&#10;&lt;li&gt;ephemeral znode: deleted by ZooKeeper service when the creating client&amp;#8217;s session ends (due to disconnection or explicit termination). It can also be explicitly deleted by creator client through delete API call. They cannot have children. Their visibility is controlled by ACL policy&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper can assign an incremental sequence number as part of znode name during its creation. This makes a sequential node. Both persistent znode and ephemeral znode can be either sequential or not.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In typical client-server architecture, server is passively open and do not initiate communication to client. Client pulls information from server. This is however an anti-pattern for large scale distributed system. ZooKeeper implements a Watch mechanism where clients can get notifications from ZooKeeper service, instead of having to poll for events. Clients can register with the ZooKeeper service (by setting a watch on znode) for any changes associated with a znode. A watch will only trigger notification once, and needs to be re-registered (by client) for trigger the next notification. A watch is triggered upon:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Any changes to the data of a znode;&lt;/li&gt;&#10;&lt;li&gt;any changes to the children of a znode;&lt;/li&gt;&#10;&lt;li&gt;Creation of deletion of a znode&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper guarantees that notifications are delivered in the order of event occurrence. When a client disconnects from ZooKeeper server, it doesn&amp;#8217;t receive any watches until the connection is re-established. &lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-api-operations"&gt;API Operations&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ZooKeeper operations are:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;create&lt;/td&gt;&lt;td&gt;Creates a znode in the specified path&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;delete&lt;/td&gt;&lt;td&gt;Deletes a znodes from the specified path. Not allowed if the znode has children. version number required&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;exists&lt;/td&gt;&lt;td&gt;Check if a znode at the specified path exists, and get version number; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getChildren&lt;/td&gt;&lt;td&gt;Get a list of children of a znode; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getData&lt;/td&gt;&lt;td&gt;get the data associated with a znode; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;setData&lt;/td&gt;&lt;td&gt;writes data into the data field of a znode. Version number required.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getACL&lt;/td&gt;&lt;td&gt;get the ACL of a znode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;setACL&lt;/td&gt;&lt;td&gt;set the ACL in a znode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sync&lt;/td&gt;&lt;td&gt;synchronizes a client&amp;#8217;s view of a znode &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The write operations (setData, create, delete) are atomic, durable and eventually consistent. Every znode has a stat structure including cZxid, mZxid an dpZxid that keeps track of the ID of the transactions that created, last modified this znode, or pertains to adding or removing its children.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Production znode ensemble with more than one node is running in quorum mode. Updates to ZooKeeper tree by clients must be persistently stored in this quorum of nodes for a transaction to be completed successfully. Odd number of node is recommended to avoid split-brain where network partition causes two subsets of servers in the ensemble function independently, and different clients get different results for the same requests, depending upon the server they are connected to.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All ZooKeeper nodes are listed in the configuration for client application to randomly pick from and try to connect and establish a session. The session is associated with every operation the client executes in a ZooKeeper service. The session also has a timeout period specified by the application client during session establishment. If the connection remains idle for more than the timeout period, the server expires the session. Appropriate session timeout should be set based on network condition. Sessions are kept alive by client sending heartbeat to ZooKeeper service. Application developer needs to handle connection-loss scenarios properly.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-leader-election-and-atomic-broadcast"&gt;Leader Election and Atomic Broadcast&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper ensemble contains a leader nodes, follower nodes and observer nodes.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The leader node is elected by the cluster. It handles all write requests. &lt;/li&gt;&#10;&lt;li&gt;The follower nodes are leader candidates that are not elected. They are backup to the leader nodes. They handle read request, and receive the updates proposed by the leader, and through a majority consensus mechanism, a consistent state is maintained across the ensemble. &lt;/li&gt;&#10;&lt;li&gt;The observer nodes are ineligible as leader candidates. They have otherwise the same function as followers.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The service relies on the replication mechanism to ensure that all updates are persistent in all servers that constitute the ensemble. This is the core mechanism in ZooKeeper, implemented as a special atomic messaging protocol called ZooKeeper Atomic Broadcast (ZAB). ZAB (a variant of Paxos algorithm) ensures the election of new leader in the event of old leader crash, and ensures integrity of data. It defines three states (looking, following and leading) of a node, and goes through four phases (election, discovery, sync, broadcast) in its operation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All read requests (exists, getData, getChildren) are process locally by the ZooKeeper node where the client is connected to. This makes read operation fast. All write requests (create, delete, and setData) are forwarded to the leader in the ensemble, which carries out the client request as a transaction. A transaction is identified by zxid and is idempotent. Transaction also satisfies the property of isolation (no transaction is interfered with by any other transaction). Only after a majority of the followers acknowledge that they have persisted the change does the leader commit the update.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.6.1/images/zkcomponents.jpg" alt="ZooKeeper Components"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Transaction processing involves two steps in ZooKeeper: leader election and atomic broadcast. This resembles a two-phase commit protocol (which also includes a leader election and an atomic broadcast)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper use local storage to persist transactions. The transactions are logged to transaction logs, in sync&amp;#8217;ed write, requiring a dedicated block device separated from boot device of server. The local storage also keep point-in-time copies (snapshots) of the ZooKeeper tree.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-zookeeper-recipes"&gt;ZooKeeper Recipes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ZooKeeper recipes defines high-level implementation (construct) of some common distributed coordination mechanism:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Barrier_(computer_science)"&gt;Barrier&lt;/a&gt;: any thread/process must stop at this point and cannot proceed until all other threads/processes reach this barrier.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://computersciencewiki.org/index.php/Queue"&gt;Queue&lt;/a&gt;: allow FIFO in distributed system&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Lock_(computer_science)"&gt;Lock&lt;/a&gt;: Fully distributed locks that are globally synchronous, meaning at any snapshot in time no two clients think they hold the same lock.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Leader_election"&gt;Leader Election&lt;/a&gt;: designate a single process as the organizer of some task distributed among several nodes.&lt;/li&gt;&#10;&lt;li&gt;Group membership: node may join or leave a group, which needs to be made available to clients. An alternative to ZooKeeper to manage group membership is &lt;a href="https://en.wikipedia.org/wiki/Gossip_protocol"&gt;gossip protocol&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="http://jasonwilder.com/blog/2014/02/04/service-discovery-in-the-cloud/"&gt;Service discovery&lt;/a&gt;: help client to determine IP and port for a service that are hosted by multiple servers.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol"&gt;Two-phase commit&lt;/a&gt;: a mechanism for atomic commitment in two steps: first a commit request phase involving a voting by participants; and second, either a commit action, or an abort action, based on the voting result.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-zookeeper-administration"&gt;Zookeeper Administration&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The official &lt;a href="https://zookeeper.apache.org/doc/r3.6.1/zookeeperAdmin.html"&gt;documentation&lt;/a&gt; includes all we need to know about administration. In addition, we need to configure &lt;a href="https://logging.apache.org/log4j/1.2/manual.html"&gt;log4j&lt;/a&gt; for proper logging. As best practices, we also should turn off &lt;a href="https://www.digihunch.com/2018/04/centos-remove-swap-safely/"&gt;swapping&lt;/a&gt; on ZooKeeper. We should clean up the data directory periodically if auto purge is not enabled. For optimal performance, ZooKeeper transaction log should be configured in a dedicated device.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For monitoring, ZooKeeper responds to a small sets of four-letter commands issued through telnet or nc to server&amp;#8217;s client port. This allows the admin to check health of server or diagnose any problems. This requires the following property in zoo keeper config:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4lw.commands.whitelist=stat, ruok, conf, isro, wchc&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The value can be set to asterick to allow all four-letter keyword. Once enabled, we can check server status&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo ruok | nc localhost &lt;span style="color:#ae81ff"&gt;2181&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;imok&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;More four-letter commands are listed &lt;a href="https://zookeeper.apache.org/doc/r3.1.2/zookeeperAdmin.html#sc_zkCommands"&gt;here&lt;/a&gt;. Apart from the four-letter commands, ZooKeeper can also be managed through Java Management Extensions (&lt;a href="https://www.oracle.com/java/technologies/javase/javamanagement.html"&gt;JMX&lt;/a&gt;).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apache ZooKeeper is a coordination service for distributed application. It has become the solution for high availability for many other projects. Some of Apache&amp;#8217;s well known open-source distributed services include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Apache Hadoop (an umbrella of projects including many components for BigData processing such as Hadoop Common, Hadoop Distributed File System (HDFS), Hadoop YARN (yet another resource negotiator) and Hadoop MapReduce)&lt;/li&gt;&#10;&lt;li&gt;Apache HBase: non-relational database on top of HDFS&lt;/li&gt;&#10;&lt;li&gt;Apache Hive: data warehouse with SQL-like interface&lt;/li&gt;&#10;&lt;li&gt;Apache Kafka: stream processing&lt;/li&gt;&#10;&lt;li&gt;Apache Nifi: automated data flow processing. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some of them, such as Nifi, has an embedded implementation of ZooKeeper ensemble if there isn&amp;#8217;t a separate ensemble. There is some limitation with embedded Zookeeper ensemble. First, we cannot start ZooKeeper without starting Nifi service on the same server. Second, we need to orchestrate the configuration so that the ZooKeeper ensemble does not grow too large. We need to keep in mind that the ZooKeeper ensemble is a separate cluster of its own, and the it is not recommended to have more than 7 nodes on ZooKeeper.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 4 of 4 – Networking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/09/host-legacy-application-with-docker-compose/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Host legacy application in Docker 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 3 of 4 – Containers</title><link>https://www.digihunch.com/2020/08/virtualization-3-of-3-containers/</link><pubDate>Tue, 18 Aug 2020 20:44:35 -0400</pubDate><guid>https://www.digihunch.com/2020/08/virtualization-3-of-3-containers/</guid><description>&lt;p class="wp-block-paragraph"&gt;In broad terms, virtualization of computing resource is about isolation of resources at different levels. We have covered hypervisor-based virtualization in the &lt;a href="https://www.digihunch.com/2020/07/overview-of-virtualization/"&gt;other&lt;/a&gt; post. In this article, we continue to dive into OS level virtualization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Remember again that the gist of virtualization is isolation of resource. To support OS level virtualization, the OS must have its own capability to isolate computing resource. There are many implementations of &lt;a href="https://en.wikipedia.org/wiki/OS-level_virtualization"&gt;OS level virtualization&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Linux Kernel provides low-level mechanisms some two kernel features(namespaces, cgroups and chroot) for building various lightweight tools that can virtualize the system environment. Docker is such framework that builds on chroot namespaces and cgroups.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-chroot"&gt;Chroot&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traditionally, root directory (/) is the top directory shared amongst all processes in the OS. There was a chroot() system call that allows each process to have its own idea of root directory. A chroot is an operation that changes the apparent root directory(/) for the current running process and their children. A program that is run in such a modified environment cannot access files and commands outside that environmental directory tree. This modified environment is called a &lt;strong&gt;chroot jail&lt;/strong&gt;. By separating a process using chroot() we ensure security by restricting the process from accessing outside its environment (breaking the jail). This short &lt;a href="https://www.youtube.com/watch?v=2wSJREC7RV8"&gt;video&lt;/a&gt; is a great lab.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although chroot() has a basic idea of isolation, it simply modifies pathname lookups for a process and its children (by prepending the new root path to any name starting with /). Relative paths can still refer any locations outside of the new root. So chroot() does not intend to defend against intentional tampering by privileged users.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-namespace-isolation"&gt;Namespace Isolation&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Namespaces are fundamentally the mechanisms to abstract, isolate, and limit the visibility that a group of processes has over various system entities such as process trees, network interfaces, user IDs and file system mounts. So there are several categories of namespaces:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Mount namespaces &amp;#8211; traditionally, there is one global mount namespace seen by all processes. The mount namespaces confine the set of filesystem mount points visible within a process namespace, enabling one process group in a mount namespace to have an exclusive view of the filesystem list, compared to another process.&lt;/li&gt;&lt;li&gt;UTS namespaces &amp;#8211; allows isolation of hostname per namespace. Each namespace can have its own hostname on the network&lt;/li&gt;&lt;li&gt;User namespaces &amp;#8211; allow a process to use unique user and group IDs&lt;/li&gt;&lt;li&gt;Cgroup namespaces &amp;#8211; processes inside a &lt;a href="https://man7.org/linux/man-pages/man7/cgroup_namespaces.7.html"&gt;cgroup namespace&lt;/a&gt; are only able to view paths relative to their namespace root.&lt;/li&gt;&lt;li&gt;IPC namespaces &amp;#8211; isolates the System V inter-process communication between namespaces, as well as POSIX message queues within each namespace. POSIX message queue allow process to exchange data in the form of messsages.&lt;/li&gt;&lt;li&gt;PID namespaces &amp;#8211; traditionally, *nix kernels spawn the init process with PID 1 during system boot, which in turn starts other user-mode process and is considered the root of the process tree (all the other processes start below this process in the tree). The PID namespace allows a process to spin off a new tree of processes under it with its own root process (PID=1). PID namespaces isolate process ID numbers, and allow duplication of PID numbers across different PID namespaces. The process IDs only needs to be unique within a PID namespace, and are assigned sequentially starting with PID 1. PID namespaces are used in containers.&lt;/li&gt;&lt;li&gt;Network namespaces &amp;#8211; traditionally, all processes in the entire OS share a single set of network interfaces and routing table entries. The routing table entries can be modified at operating system level. With network namespace, this assumption is no longer valid. Network namespace provides abstraction and virtualization of network protocol and interfaces. Each network namespace will have its own network device instances that can be configured with individual network addresses. Other network services, such as routing table, port number, are isolated as well.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Namespaces are created with the &amp;#8220;&lt;em&gt;unshare&lt;/em&gt;&amp;#8221; command or syscall, or as new flags in a &lt;em&gt;&lt;a href="https://man7.org/linux/man-pages/man2/clone.2.html"&gt;clone&lt;/a&gt;()&lt;/em&gt; syscall. The flags are listed here in the &lt;a href="https://man7.org/linux/man-pages/man7/namespaces.7.html"&gt;man&lt;/a&gt; page for namespace. Note that the &lt;em&gt;clone()&lt;/em&gt; syscall is a more generic implementation of &lt;em&gt;fork()&lt;/em&gt; syscall.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cgroup"&gt;Cgroup&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;cgroups is a Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, network, etc) of a collection of processes (not to be confused with process group, which has its own meaning). Cgroup has two versions. The control groups functionality (version 1) was merged into Linux kernel mainline in version 2.6.24, released in 2008, and version 2 in kernel 4.5 (March 2016), with significant changes to the interface and internal functionality.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Using cgroups, you can allocate resources such as CPU time, network and memory. Similiar to the process model in Linux, where each process is a child to a parent and relatively descends from the init process thus forming a single-tree like structure, cgroups are hierarchical, where child cgroups inherit the attributes of the parent, but what makes it different is that multiple cgroup hierarchies can exist within a single system, with each having distinct resource prerogatives.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Applying cgroups on namespaces results in isolation of processes into containers within a system, where resources are managed distinctly. Each container is a lightweight virtual machine, all of which run as individual entities and are oblivious of other entities within the same system.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-container-implementation"&gt;Container Implementation&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Above we covered some kernel features that enables container technology. There are many ways to use these technologies to implement the isolation. We call them container runtime. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/LXC"&gt;LXC&lt;/a&gt; is a user space interface for those Linux kernel containment features. It allows for running isolated containers on a control host using a single kernel. Users can launch a system init for each containers, also referred to as virtual environment (as opposed to virtual machines). The author of this &lt;a href="https://www.upguard.com/blog/docker-vs-lxc"&gt;article&lt;/a&gt; regard LXC as a suprcharged chroot on Linux. LXC has rest API tool called LXD. LXC was targeting sysadmin&amp;#8217;s use cases (not developer) to isolate users&amp;#8217; own private workloads from one another. In early days Docker was built on LXC. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker&amp;#8217;s target market is developers, and it moved beyond LXC with its own execution environment called &lt;em&gt;&lt;strong&gt;libcontainer&lt;/strong&gt;&lt;/em&gt;. With the initial success of Docker, a large community (Docker, CoreOS, Google, etc) emerged around the idea of using containers as the standard unit of software delivery. They started the Open Container Initiative (OCI) to define industry standards around container runtime (runtime spec) and image format (image spec). Docker &lt;a href="https://opencontainers.org/faq/#what-has-docker-done-to-help-create-this-foundation"&gt;donated&lt;/a&gt; the &lt;a href="https://github.com/docker-archive/libcontainer"&gt;libcontainer&lt;/a&gt; codebase to run independently under OCI, as &lt;a href="https://github.com/opencontainers/runc"&gt;runc&lt;/a&gt;. Docker implements isolation using the following technologies:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Namespace: to isolate process ID, networking, mount points, IPC, host and domain name;&lt;/li&gt;&lt;li&gt;Cgroups: to isolate the usage of CPU and memory between containers&lt;/li&gt;&lt;li&gt;UnionFS: isolate file system&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another container runtime technology is &lt;a href="https://en.wikipedia.org/wiki/OpenVZ"&gt;OpenVZ&lt;/a&gt;, which includes an extension of the Linux kernel. It uses container for entire operating systems (not just application and processes). All OpenVZ containers have to share the same Linux kernel version as host. The &lt;a href="https://wiki.aquasec.com/display/containers/Docker+Alternatives+-+Rkt%2C+LXD%2C+OpenVZ%2C+Linux+VServer%2C+Windows+Containers"&gt;adoption&lt;/a&gt; of OpenVZ is not high.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Framework&lt;/td&gt;&lt;td&gt;Runtime implementation&lt;/td&gt;&lt;td&gt;Management tool&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LXC&lt;/td&gt;&lt;td&gt;libvert&lt;br&gt;LXC&lt;/td&gt;&lt;td&gt;LXD (rest API)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OCI&lt;/td&gt;&lt;td&gt;Docker&amp;#8217;s runc&lt;br&gt;CoreOS&amp;#8217;s rtk&lt;/td&gt;&lt;td&gt;docker engine (daemon and cli)&lt;br&gt;rtk cli&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption&gt;container runtimes&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker is now widely adopted for application hosting in production environment. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-container-and-cloud"&gt;Container and Cloud&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Public cloud vendors also has &lt;a href="https://logz.io/blog/aws-eks-vs-ecs-vs-fargate-understand-differences/"&gt;managed services&lt;/a&gt; around Docker. Here are some examples:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Managed Container&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Image Registry&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Managed Orchestration&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS&lt;/td&gt;&lt;td&gt;Elastic Container Service&lt;/td&gt;&lt;td&gt;Elastic Container Registry&lt;/td&gt;&lt;td&gt;Elastic Kubernetes Services&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Azure&lt;/td&gt;&lt;td&gt;Container Instances&lt;/td&gt;&lt;td&gt;Container Registry&lt;/td&gt;&lt;td&gt;Azure Kubernetes Service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GCP&lt;/td&gt;&lt;td&gt;CloudRun&lt;/td&gt;&lt;td&gt;Container Registry&lt;/td&gt;&lt;td&gt;Google Kubernetes Engine&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Digital Ocean&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;Container Registry&lt;/td&gt;&lt;td&gt;Kubernetes&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption&gt;Container services from public cloud&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cloud service was originally developed with VM as a unit of computing resource to service. OS level virtualization allows container to be a unit of computing resource. All these new technologies breed the serverless architecture and cloud-native deployment model. This has significant impact on the creation and delivery of software services. The &lt;a href="https://landscape.cncf.io/"&gt;cloud native landscape&lt;/a&gt; page illustrates more tools around containers.&lt;br&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/08/cloud-storage-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cloud storage overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 4 of 4 – Networking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Java Garbage Collection</title><link>https://www.digihunch.com/2020/08/java-garbage-collection/</link><pubDate>Fri, 07 Aug 2020 23:19:17 -0400</pubDate><guid>https://www.digihunch.com/2020/08/java-garbage-collection/</guid><description>&lt;p class="wp-block-paragraph"&gt;Tuning the garbage collector is the most important thing that can be done to improve the performance of a Java application. GC is typically caused when the JVM decides GC is necessary, specifically when:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;a minor GC will be triggered when the new generation is full;&lt;/li&gt;&#10;&lt;li&gt;a full GC will be triggered when the old generation is full;&lt;/li&gt;&#10;&lt;li&gt;a concurrent GC (if applicable) will be triggered when the heap starts to fill up&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;OpenJDK has three collectors suitable for production, with different performance characteristics. In order to study the GC behaviours in application, it is important to turn on GC logging. The detailed step is different in JDK 8 and JDK 11 (read about java version &lt;a href="https://www.digihunch.com/2018/11/the-java-confusions/"&gt;here&lt;/a&gt;).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java developers don&amp;#8217;t need to manage life cycle of objects explicitly as the JVM automatically fress the object. In order to track objects that are still in use, it is insufficient to count references to objects. Instead, the JVM must periodically search the heap for unused objects. Once it finds unused objects, the JVM frees the memory occupied by those objects. It also needs to compact the memory to prevent memory fragmentation. The performance of GC is dominated by these basic operations (finding unused objects; freeing up their memory; compacting the heap), no matter which collector is used. Some algorithms delay compaction until absolutely necessary, some compact entire sections of the heap at a time, and some compact the heap by relocating small amounts of memory at a time. These different approaches are why different algorithms have different performance characteristics.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java programs are typically heavily multithreaded, and the garbage collector itself often runs multiple threads too. We refer to the application logic threads as mutator threads, since they are mutating objects as part of the application logic. When GC threads track object references or move objects around in memory, they must make sure application threads are not using those objects on the move. This introduces a pause when all application threads are stopped (known as stop-the-world pauses), which generally has the greatest impact on the performance of an application. Minimizing those pauses is one important consideration when tuning GC.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-garbage-collectors-are-generational"&gt;Garbage collectors are generational&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Most garbage collectors work by splitting the heap into generations. These are called the old (or tenured) generation, and the young generation, which is further divided into sections known as eden and survivor spaces, with eden taking up the vast majority of the young generation. The rationale for having separate generations is that many objects are used for a very short period of time in the real life of application programming. Garbage collector is designed to take advantage of this. Objects are first allocated in the young generation, which is a subset of the entire heap. When the young generation fills up, the garbage collector will stop all the application threads and empty out the young generation. Objects that are no longer in use are discarded, and objects that are still in use are moved elsewhere. This operation is called a minor GC or a young GC. Common GC algorithms have stop-the-world pauses during collection of the young generation.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="553" height="276" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-9.png" alt="" class="wp-image-1270"/&gt;&lt;figcaption class="wp-element-caption"&gt;Heap Generation&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This design has two performance advantages. First, Cleaning up young generation as a only a portion of the entire heap causes shorter pause than cleaning up the entire heap. Second, by moving used objects to survivor spaces or old generation, and discarding unused objects, compatction is achieved.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With used objects moved to the old generation, eventually it woo will fill up, and the JVM will needt o find any objects within the old generation that are no longer in use to discard. This is where GC algorithms have their biggest differences. The simpler alghorithms stop all application threads, find the unused objects, free their memory, and then compact the heap. This process is called a full GC, and it generally causes a relatively long pause for the application threads.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, sophisticated alghrithms are able to find unused objects while application threads are running. These algorithms are called concurrent collectors, or low-pause collectors. A concurrent collector typically allows an application to experience fewer and shorter pauses. The biggest trade-off here is the overall CPU required by the sophisticated algorithms.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-the-three-main-algorithms"&gt;The three main algorithms&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Serial GC is the simplest and the default for single core host (e.g. client-class machine, single-processor VM or Docker container). The serial collector uses a single thread to process the heap. It will stop all application threads as the heap is processed (for either a minor or full GC). During a full GC, it will fully compact the old generation. The serial collector is enabled by using the -XX:+UseSerialGC flag.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The throughput collector (aka parallel collector) is the default collector for any 64-bit machine with two or more CPUs. The throughput collector uses multiple threads to collect the young generation, which makes minor GCs much faster than when the serial collector is used. This uses multiple threads to process the old generation as well. The throughput collector stops all application threads during both minor and full GCs, and it fully compacts the old generation during a full GC. Since it is the default in most situations where it would be used, it needen&amp;#8217;t be expliticly enabled. To enable it where necessary, use the flag -XX:+UseParallelGC&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The G1 GC (or garbage first garbage collector) uses a concurrent collection strategy to collect the heap with minimal pauses. It is the default collector in JDK 11 and later for 64-bit JVMs on machines with two or more CPUs. G1 GC divides the heap into regions, but it still considers the heap to have two generations. Some of those regions make up the young generation, and the young generation is still collected by stopping all application threads and moving all objects that are alive into the old generation or the survisor spaces, using multiple threads. In G1 GC, the old generation is processed by background threads that don&amp;#8217;t need to stop the application threads to perform most of their work.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In G1 GC, the old generation is processed by background threads that don&amp;#8217;t need to stop the application threads to perform most of their work. Because the old generation is divided into regions, G1 GC can clean up objects from the old generation by copying from one region into another, which means that it compacts the heap during normal processing. This helps keep G1 GC heaps from becoming fragmented.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The trade-off for avoiding the full GC cycles is CPU time; the multiple background threads G1 GC uses to process the old generation requires CPU cycles available at the same time the application threads are running. G1 GC is enabled by specifying the flag -XX:+UseG1GC. It is the default in JDK 11, and functional in JDK 8 as well, with some performance feature missing.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In all cases, GC is caused when the JVM decices GC is necessary; a minor GC will be triggered when the new generation is full; a full GC will be triggered when the old generation is full, or a concurrent GC (if applicable) will be triggered when the heap starts to fill up. Java also provides a mechanism for applications to force a GC to occur: the System.gc() method, although it is always a bad idea to call that method explicitly because it triggers a full GC which hangs the application threads. This method can be disabled by including -XX:+DisableExplicitGC in the JVM arguments.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As to choosing GC algorithm, the rule of thumb is that G1 GC is the better choice. However, in JDK 8, the ability of G1 GC to avoid a full GC is also a key consideration. In this case we may need to choose betwen serial collectors and throughput collectors, based on the number of CPUs on the machine.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The serial collector makes sense when running CPU-bound applications on a machine with a single CPU, even if that single CPU is hyper-threaded. The throughput collector makes sens on multi-CPU machines running jobs that are CPU bound. Even for jobs that are not CPU bound, the throughput collector can be the better choice if it does relatively few full GCs or if the old generation is generally full.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-gc-tuning"&gt;Basic GC tuning&lt;/h3&gt;&#10;&lt;h4 class="wp-block-heading" id="h-sizing-the-heap"&gt;Sizing the heap&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If the heap is too small, the program will spend too much time performing GC and not enough time performing application logic. On the contrary, a very large heap will increase the time spent in GC pauses, even thought the pauses occur less frequently. It is also potentially dangerous due to interaction with memory swap. If a Java program with a 12 GB heap is running on a system where &lt;a href="https://www.digihunch.com/2018/04/centos-remove-swap-safely/"&gt;swap&lt;/a&gt; is enabled, the OS may handle it by keeping 8GB of the heap in RAM and 4GB on disk. The JVM does not know about this because swapping is handled by the OS. The JVM will happily fill up all 12GB of heap it has been told to use. This can cause a sever performance penalty when OS swaps data from disk to RAM. Worse, the one time this swapping is guaranteed to occur is during a full GC, when the JVM must access the entire heap. Swapping during full GC makes the pause an order of magnitude longer.&lt;br&gt;So heap size (total for all JVMs) should never exceed the amount of physical memory on the machine. Size of heap is controled by two values (Xms as initial value and Xmx as maximum value). Having an initial and maximum size for the heap allows the JVM to tune its behaviour depending on the workload. If the JVM sees that it is doing too much GC, with the initial heap size, it will continually increase the heap until the JVM is doing the &amp;#8220;correct&amp;#8221; amount of GC, or until the heap hits its maximum size.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A good rule of thumb is to size the heap so that it is 30% occupied after a full GC. To calculate this, start your application and push it to high load. Then connect to the application with jconsole, force a full GC, and observe how much memory is used when the full GC completes.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-sizing-the-generations"&gt;Sizing the generations&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The JVM must also decide how much of the heap to allocate respectively to they young generation and old generation. THe JVM usually does this automatically and usually does a good job in determining the optimal ratio. In some cases you might hand-tune these values.&lt;br&gt;In general, if there is a relatively larger young generation, young GC pause times will increase, but the young generation will be collected less often, and fewer objects will be promoted into the old genration. But on the other hand, older generation will be relatively smaller and fill up more frequently and do more full GCs. The command-line flag to tune the generation sizes are:&lt;br&gt;-XX:NewRatio=N&lt;br&gt;-XX:NewSize=N&lt;br&gt;-XX:MaxNewSize=N&lt;br&gt;-Xmn N&lt;br&gt;The size of initial young generation is determined by initial heap size and new ratio:&lt;br&gt;Initial Young Gen Size = Initial Heap Size / (1 + NewRatio)&lt;br&gt;The young generation will grow in tandem with the overall heap size, but it can also fluctuate as a percentage of the total heap (based on the initial and maximum size of the young generation). Adaptive sizing controls how the JVM alters the ratio of young genration to old gneeration within the heap. It should be kept enabled in general. For finely tuned heaps, adaptive sizing can be disabled for a small performance boost.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-sizing-the-metaspace"&gt;Sizing the metaspace&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When the JVM loads classes, it must keep track of certain metadata about those classes. This occupies a separate heap space called the metaspace. In older JVMs this was handled by a different implementation called permgen. To end users, the metaspace is opaque. It does not hold the actual instance of the class. The objects are held in the regular heap. Information in the metaspace is used only by the compiler and JVM runtime, and the data it holds is referred to as class metadata.&lt;br&gt;Tuning the metaspace is fairly rare these days because the default values for the size of metaspace are very generous. It is sized dynamically based on an initial size (-XX:MetaspaceSize=N) and will increase as needed to a maximum size (-XX:MaxMetaspaceSize=N).&lt;br&gt;Resizing the metaspace requires a full GC, so it is an expensive operation. If there are a lot of full GCs during the startup of a program (as it it loading classes), it is often because permgen or metaspace is being resized, so increasing the initial size is a good idea to improve startup in that case.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-controlling-parallelism"&gt;Controlling Parallelism&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All GC alghorithms except the serial collector use multiple threads. The number of these threads is controlled by the -XX:ParallelGCThreads=N flag. Bacuase these GC operations stop all application threads from executing, the JVM attempts to use as many CPU resources as it can in order to minimize the pause time. By default, that means the JVM will run one thread for each CPU on a machine, up to eight. Once that threashold has reached, the JVM adds new thread for only every 1.6 CPus. Sometimes this number is too large relative to the heap size and hand tuning is needed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Reference&lt;/strong&gt;: Java Performance by Scott Oaks&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2023/01/java-performance-780x1024.jpeg" alt="" class="wp-image-7911" width="240" height="315" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/java-performance-780x1024.jpeg 780w, https://www.digihunch.com/wp-content/uploads/2023/01/java-performance-229x300.jpeg 229w, https://www.digihunch.com/wp-content/uploads/2023/01/java-performance-768x1008.jpeg 768w, https://www.digihunch.com/wp-content/uploads/2023/01/java-performance.jpeg 1036w" sizes="auto, (max-width: 240px) 100vw, 240px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.amazon.com/gp/product/1492056111/ref=as_li_ss_il?ie=UTF8&amp;amp;linkCode=li2&amp;amp;tag=glowinghunch-20&amp;amp;linkId=a780d6a00fe93c93bf399c6c9393c806&amp;amp;language=en_US" target="_blank" rel="noopener noreferrer"&gt;&lt;/a&gt;This &lt;a href="https://engineering.linkedin.com/garbage-collection/garbage-collection-optimization-high-throughput-and-low-latency-java-applications"&gt;post&lt;/a&gt; also contains some helpful information, where the original Oracle &lt;a href="https://engineering.linkedin.com/garbage-collection/garbage-collection-optimization-high-throughput-and-low-latency-java-applications"&gt;white paper&lt;/a&gt; about GC was cited. Further than GC, this &lt;a href="https://www.oracle.com/java/technologies/javase/javase-core-technologies-apis.html"&gt;website&lt;/a&gt; from Oracle describes more about JVM.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 2 of 4 – Graphics Computing&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/cloud-storage-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cloud storage overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 2 of 4 – Graphics Computing</title><link>https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/</link><pubDate>Sat, 01 Aug 2020 18:24:00 -0400</pubDate><guid>https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/</guid><description>&lt;p class="wp-block-paragraph"&gt;We covered hypervisor in previous post. In this article we focus on the virtualization of graphics computing resource.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;GPU vs CPU&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;GPU is a specialized type of microprocessor primarily designed for quick image rendering. GPU appeared as a response to graphically intense applications that put a burden on the CPU and degrated computer performance. They became a way to offload those tasks from CPUs, but modern graphics processors are powerful enough to perform rapid mathematical calculations for many other purposes apart from rendering.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;CPU consists of a few cores (up to 23) optimized for sequential serial processing, which is designed to maximize the performance of a single task within a job. GPU uses thousands of smaller and more efficient cores for massively parallel architecture aimed at handling multiple functions at the same time. Typical uses cases for GPUs, in addition to graphics display, includes Games, 3D visualization, Image processing, big data and deep machine learning.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://www.apps4rent.com/wp-content/uploads/2018/04/cpu-vs-gpu.jpg" alt="GPU vs CPU | What's better?"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Moving to virtualization world, the most primitive mechanism for graphics acceleration is Soft 3D, which is commonly used in virtual desktops, or DaaS (desktop as a service). The Software 3D renderer (Soft 3D) uses the Soft 3D graphics driver to provide support for software-accelerated 3D graphics without any physical GPUs being installed in the ESXi host. With respect to GPU in virtualized environment, VMware developed a few technologies.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vSGA (Virtual Shared Graphics Acceleration)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The physical GPUs in the server are virtualized and shared across multiple guest VMs. This option involves installing an Nvidia driver into the hypervisor itself, and each guest VM uses a &lt;span style="text-decoration: underline;"&gt;proprietary VMware SVGA 3D driver&lt;/span&gt; that communicates with the Nvidia driver in ESX. The biggest limitation here is that these drivers only work with DirectX up to 9.0c, and OpenGL up to 2.1. This technology was introduced in early 2013 and is used in light workload for knowledge worker, such as PowerPoint, Visio and web browsing.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="572" height="664" src="https://www.digihunch.com/wp-content/uploads/2024/07/vSGA.png" alt="" class="wp-image-11412" style="width:443px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/vSGA.png 572w, https://www.digihunch.com/wp-content/uploads/2024/07/vSGA-258x300.png 258w" sizes="auto, (max-width: 572px) 100vw, 572px" /&gt;&lt;figcaption class="wp-element-caption"&gt;vSGA&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vDGA (Virtual Dedicated Graphics Acceleration)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vDGA, also known as &amp;#8220;GPU passthrough&amp;#8221;. It provides each VM with unrestricted, fully dedicated access to one of the host&amp;#8217;s GPUs. The hypervisor is drilling a direct hole in itself between the GPU and the guest. This technology allows you to present an internal PCI GPU directly to a VM guest. The device acts as if it were directly driven by the VM guest, and the guest detects the PCI device as if it were physically connected, using the &amp;#8220;real&amp;#8221; driver. There is no special drivers in the hypervisor. vDGA offers the highest level of performance for users with the most intensive graphics computing needs.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="444" height="588" src="https://www.digihunch.com/wp-content/uploads/2024/07/passthrough.png" alt="" class="wp-image-11417" style="width:304px;height:auto" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/passthrough.png 444w, https://www.digihunch.com/wp-content/uploads/2024/07/passthrough-227x300.png 227w" sizes="auto, (max-width: 444px) 100vw, 444px" /&gt;&lt;figcaption class="wp-element-caption"&gt;GPU passthrough&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main advantage to vDGA is that since the GPU is passed through to the guest and the guest uses regular Nvidia drivers, it fully supports everything the Nvidia driver can do natively. This enables all versions of DirectX, OpenGL and even CUDA. The downside is that vDGA is expensive, since you need one GPU per user. There is also a lack of vMotion support. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware added support for vDGA in late 2013. The target market is high-end users with intensive graphical applications (oil&amp;amp;gas, scientific simulations, CAD/CAM, etc&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vGPU (Virtual GPU)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vGPU is also known as Virtual Shared Pass-Through Graphics Acceleration. This technology sites somewhere in between the two previously introduced, as an option to strike a balance between cost-effectiveness and resource-sharing. It is essentially vDGA but with multiple users per GPU, instead of one-to-one mapping. Like vDGA, with vGPU you install the real Nvidia driver in guest VMs, and the hypervisor passes the graphics commands directly to the hypervisor without any translation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vGPU gives you all that plus the ability to share a GPU across up to 8 VMs. The idea of vGPU is that you get better performance than vSGA option, with a portion of cost when compared to vDGA. The use case for vGPU will be the higher-end knowledge workers who need real &amp;#8220;GPU&amp;#8221; access but don&amp;#8217;t need full-on multi-thousand dollar graphics workstations.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="489" height="337" src="https://www.digihunch.com/wp-content/uploads/2020/07/image-5.png" alt="" class="wp-image-1190" style="width:429px;height:auto"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware partners with Nvidia on vGPU development. Below is the use-case chart from previous VMware white paper:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="900" height="590" src="https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon.webp" alt="" class="wp-image-13153" srcset="https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon.webp 900w, https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon-300x197.webp 300w, https://www.digihunch.com/wp-content/uploads/2020/08/Deploying_Hardware_Accelerated_Graphics_View_Horizon-768x503.webp 768w" sizes="auto, (max-width: 900px) 100vw, 900px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram below illustrates the architecture of virtual GPU (NVIDIA Grid):&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="734" height="690" src="https://www.digihunch.com/wp-content/uploads/2024/07/GRID.png" alt="" class="wp-image-11414" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/GRID.png 734w, https://www.digihunch.com/wp-content/uploads/2024/07/GRID-300x282.png 300w" sizes="auto, (max-width: 734px) 100vw, 734px" /&gt;&lt;figcaption class="wp-element-caption"&gt;high-level architecture of GRID vGPU&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The best &lt;a href="https://techzone.vmware.com/resource/deploying-hardware-accelerated-graphics-vmware-horizon-7"&gt;white paper&lt;/a&gt; about the three technologies and their use cases is on VMware website.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Identify Graphics driver&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On Linux VM, we can simply use lspci to identify graphics driver.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@ghrender ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lspci | grep VGA&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;03:00.0 VGA compatible controller: Matrox Electronics Systems Ltd. Integrated Matrox G200eW3 Graphics Controller &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rev 04&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3b:00.0 VGA compatible controller: NVIDIA Corporation GP104GL &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Quadro P5000&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rev a1&lt;span style="color:#f92672"&gt;)&lt;/span&gt;In the result, the far left column is specified domain, e.g. 3b:00.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To display details on graphics card by specified domain (3b:00.0 for example) with memory information:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@ghrender ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lspci -v -s 3b:00.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3b:00.0 VGA compatible controller: NVIDIA Corporation GP104GL &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Quadro P5000&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;rev a1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;prog-if &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;VGA controller&lt;span style="color:#f92672"&gt;])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Subsystem: NVIDIA Corporation Device 11b2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Flags: bus master, fast devsel, latency 0, IRQ 190, NUMA node &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Memory at ab000000 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;32-bit, non-prefetchable&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;16M&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Memory at 382fe0000000 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;64-bit, prefetchable&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;256M&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Memory at 382ff0000000 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;64-bit, prefetchable&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;32M&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;I/O ports at &lt;span style="color:#ae81ff"&gt;6000&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;128&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;virtual&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Expansion ROM at ac080000 &lt;span style="color:#f92672"&gt;[&lt;/span&gt;disabled&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;512K&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;60&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Power Management version &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;68&lt;span style="color:#f92672"&gt;]&lt;/span&gt; MSI: Enable+ Count&lt;span style="color:#f92672"&gt;=&lt;/span&gt;1/1 Maskable- 64bit+&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;78&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Express Legacy Endpoint, MSI &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;100&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Virtual Channel&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;250&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Latency Tolerance Reporting&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;128&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Power Budgeting &amp;lt;?&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;420&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Advanced Error Reporting&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;600&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Vendor Specific Information: ID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0001&lt;/span&gt; Rev&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; Len&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;024&lt;/span&gt; &amp;lt;?&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Capabilities: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;900&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#75715e"&gt;#19&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Kernel driver in use: nvidia&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;Kernel modules: nouveau, nvidia_drm, nvidia&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The lshw command can also identify onboard Intel/AMD or Nvidia dedicated GPU:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@ghrender ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lshw -C display&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *-display&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: VGA compatible controller&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product: Integrated Matrox G200eW3 Graphics Controller&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vendor: Matrox Electronics Systems Ltd.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; physical id: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bus info: pci@0000:03:00.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version: &lt;span style="color:#ae81ff"&gt;04&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt; bits&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; clock: 66MHz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; capabilities: pm vga_controller bus_master cap_list rom&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configuration: driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mgag200 latency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;64&lt;/span&gt; maxlatency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt; mingnt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resources: irq:16 memory:91000000-91ffffff memory:92808000-9280bfff memory:92000000-927fffff&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *-display&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: VGA compatible controller&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product: GP104GL &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Quadro P5000&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vendor: NVIDIA Corporation&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; physical id: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bus info: pci@0000:3b:00.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version: a1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: &lt;span style="color:#ae81ff"&gt;64&lt;/span&gt; bits&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; clock: 33MHz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; capabilities: pm msi pciexpress vga_controller bus_master cap_list rom&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configuration: driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;nvidia latency&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resources: iomemory:382f0-382ef iomemory:382f0-382ef irq:190 memory:ab000000-abffffff memory:382fe0000000-382fefffffff memory:382ff0000000-382ff1ffffff ioport:6000&lt;span style="color:#f92672"&gt;(&lt;/span&gt;size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;128&lt;span style="color:#f92672"&gt;)&lt;/span&gt; memory:ac080000-ac0fffff&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/07/overview-of-virtualization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 1 of 4 – Hypervisor&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/java-garbage-collection/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Java Garbage Collection&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Virtualization 1 of 4 – Hypervisor</title><link>https://www.digihunch.com/2020/07/overview-of-virtualization/</link><pubDate>Mon, 27 Jul 2020 22:52:00 -0400</pubDate><guid>https://www.digihunch.com/2020/07/overview-of-virtualization/</guid><description>&lt;p class="wp-block-paragraph"&gt;In broad terms, virtualization of computing resource is about isolation of resources, at different levels. There are five levels of virtualization:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Application level, such as JVM, .NET CLR&lt;/li&gt;&#10;&lt;li&gt;Library (user-level API) level&lt;/li&gt;&#10;&lt;li&gt;Operating system level, such as LXC, Docker, OpenVZ&lt;/li&gt;&#10;&lt;li&gt;Hardware abstraction layer (HAL) level, such as VMware, Xen, etc&lt;/li&gt;&#10;&lt;li&gt;Instruction set architecture (ISA) level&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my context I deal mostly with OS level and HAL (hardware abstraction layer) level of virtualization. In loose terms, the word &lt;em&gt;containerization&lt;/em&gt; refers to &lt;span style="text-decoration: underline;"&gt;OS level virtualization&lt;/span&gt;, while the word &lt;em&gt;virtualization&lt;/em&gt; is exclusively reserved for &lt;span style="text-decoration: underline;"&gt;HAL level virtualization&lt;/span&gt;, also referred to as &lt;span style="text-decoration: underline;"&gt;hypervisor-based virtualization&lt;/span&gt;. This post will just focus on this family of technology and loosely refers to it as virtualization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtualization technology evolved from on-premise data centre environment and now is the backbone of cloud computing. The challenges of IT operation in the era of virtualization involves managing VM sprawling, investigating performance issues, planning capacity and addressing storage I/O block. The idea of virtualization is sharing (thus isolating) resources for better utilization, leading to better return on investment. This posting is to cover only the very basics of virtualization.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-hypervisor"&gt;Hypervisor&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hypervisor is the software layer which provides the capability to run multiple virtual machines on the same physical host. It is broken down into two categories:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Type I hypervisor (aka bare metal hypervisor)&lt;/strong&gt;: directly run on physical hardware. They control the hardware as well as manage the virtual machines. For example, Linux KVM, VMware ESXi, Xen and Microsoft Hyper-V&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Type II hypervisor&lt;/strong&gt;: runs as an application or service on top of the host operating system, which is installed on the bare metal. Guest operating system calls need to traverse via the host operating system stack to reach hardware resource. For example, Oracle Virtual Box, VMware Fusion and Linux Containers (LXC)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://img.vembu.com/wp-content/uploads/2019/12/Hypervisor-Types.png" alt="Type-1 vs Type-2 Hypervisor"/&gt;&lt;figcaption class="wp-element-caption"&gt;Hypervisor Types&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading" id="h-virtualization-techniques"&gt;Virtualization Techniques&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most primitive form of technology that can be arguably categorized under virtualization is hardware emulation, where a piece of (more accessible) hardware imitates another (less accessible). The architecture limits itself in functional testing only, and is not built for performance or production at all.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The original virtualization technology deals with CPU and memory virtualization. In this well-written &lt;a href="https://github.com/skonstantinov89/books/blob/master/Understanding%20Full%20Virtualization%2C%20Paravirtualization%2C%20and%20Hardware%20Assist.pdf"&gt;whitepaper &lt;/a&gt;fromVMware, there are three CPU virtualization techniques introduced for x86 architecture.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The x86 architecture offers four levels of privilege known as Ring 0,1,2 and 3 to operating system and applications to manage access to the computer hardware. User-level applications typically run in Ring 3, the OS must execute its privileged instructions in Ring 0 since it needs to have direct access to memory and hardware. The two main challenges with virtualizing x86 architecture are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A virtualization layer between hardware operating system who expects Ring 0 privilege;&lt;/li&gt;&#10;&lt;li&gt;Some instructions with different semantics when not executed in Ring 0 cannot be virtualized effectively. They need to be translated at runtime.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These challenges makes true virtualization of x86 architecture impossible and thus VMware developed three alternative technologies.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Full_virtualization"&gt;&lt;strong&gt;Full virtualization&lt;/strong&gt;&lt;/a&gt; (using binary translation): virtual machine presents a complete simulation of the actual hardware environment so that an unmodified guest OS can run in isolation. The Guest OS is not aware that the underlying environment it is running on is virtualized, and issues hardware calls to communicate with (what it thinks as) hardware. The virtual processors have to understand guest CPU instruction, and reproduce the equivalent CPU instructions of the host machine. VMware&amp;#8217;s technology to address this is called &lt;strong&gt;Binary Translation&lt;/strong&gt;. This overhead makes true full virtualization difficult to achieve. In real life, a virtual environment that provides &amp;#8220;enough representation of the underlying hardware&amp;#8221; can be considered to provide full virtualization as long as it allows guest OS to run without modification. Full virtualization comes with a performance penalty. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Paravirtualization"&gt;&lt;strong&gt;Paravirtualization &lt;/strong&gt;&lt;/a&gt;(aka OS assisted virtualization): refers to communication between the guest OS and the hypervisor to improve performance and efficiency. In this technology, guest OS is modified with an interface to host hardware to be able to communicate and operate seamlessly. Since the guest OS is modified, the VM does not need to be a complete simulation of the hardware. The modified guest OS knows it is running on a virtualized environment, and (vm driver) makes API calls (known as &amp;#8216;hyper calls&amp;#8217;) to the hypervisor. This allows para-virtualization technology to achieve performance closer to non-virtualized environment. However, since paravirtualization cannot support unmodified operating systems, its compatibility and portability is poor. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Hardware-assisted_virtualization"&gt;&lt;strong&gt;Hardware-Assisted Virtualization&lt;/strong&gt;&lt;/a&gt;: hardware vendors such as Intel and AMD both have developed extensions (new features) to simplify virtualization techniques, for example, the introduction of privileged instructions with new CPU execution mode feature to allow hypervisor to run in a new root mode below ring 0. This removed the need for full virtualization and paravirtualization. With VMware originally as a promoter of full virtualization and Xen for paravirtualization, most virtualization technologies today utilizes hardware-assisted virtualization feature, for example, Linux KVM, VMware workstation, VMware fusion, Xen, VirtualBox, etc. Intel&amp;#8217;s virtualization extension is VT-x. AMD&amp;#8217;s counterpart is AMD-V technology.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To virtualize memory, another level of memory virtualization is required (similar to the virtual memory support in Linux). Hypervisor is responsible for mapping guest physical memory to the actual machine memory, and it uses shadow page tables to accelerate the mappings, usually at a performance cost.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-popular-hypervisors"&gt;Popular hypervisors&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the market there are a few popular hypervisor technologies. They are all type 1 hypervisors:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://en.wikipedia.org/wiki/Xen"&gt;Xen &lt;/a&gt;is an open-source &lt;a href="https://xenproject.org/"&gt;hypervisor project&lt;/a&gt; originally developed in Cambridge University, licensed under GPLv2. . Based on that, Citrix developed its commercial product XenServer, a bare-metal virtualization platform with enterprise-grade features for x86 and AMD environments. Oracle VM is another commercial implementation of Xen. The Xen project also supports many cloud platforms such as Openstack, Cloudstac, etc. Xen project supports paravirtualization (Xen-PV) as well as hardware-assisted virtualization (Xen-HVM) for virtualization of X86, IA64, ARM and other CPU architectures. The earlier versions does not support memory overcommit (aka &amp;#8220;dynamic memory optimization&amp;#8221;, &amp;#8220;memory &lt;a href="https://www.digihunch.com/2020/05/understanding-where-the-memory-goes-on-linux-vm/"&gt;ballooning&lt;/a&gt;&amp;#8220;, or as Citrix calls it &amp;#8220;dynamic memory control, DMC&amp;#8221;). This delivers better performance but also has higher budgetary requirement on hardware since there isn&amp;#8217;t room for over-subscription. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hyper-V is a Microsoft product. It executes in high CPU privilege (Microsoft calls it ring -1 which is equivalent to root mode as Intel calls it). On the guest VM, OS kernel and drivers run in ring 0, application rin in ring 3. This eliminates the need for binary translation. Hyper-V does not support memory overcommit either. Hyper-V is well integrated with Windows platform. It supports Linux as well although with some performance penalty.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Linux KVM (Kernel-based Virtual Machine) is a full open-source virtualization solution for GNU/Linux. What makes KVM a special hypervisor is that it uses a loadable kernel module kvm.ko that turns itself into a hypervisor and provides VMs with direct access to the hardware. So it is a type 1 hypervisor despite of the presence of Linux OS. KVM also contains a processor specific module, kvm-intel.ko or kvm-amd.ko. KVM leverages qemu to access devices. Because KVM runs as a process inside of Linux OS, KVM can use many existing feature in Linux kernel. Redhat has an enterprise solution based on KVM.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="850" height="414" src="https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU.png" alt="" class="wp-image-7813" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU.png 850w, https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU-300x146.png 300w, https://www.digihunch.com/wp-content/uploads/2023/01/Comparison-of-Xen-KVM-and-QEMU-768x374.png 768w" sizes="auto, (max-width: 850px) 100vw, 850px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Xen vs KVM&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware &lt;a href="https://en.wikipedia.org/wiki/VMware_ESXi"&gt;ESXi &lt;/a&gt;is VMware&amp;#8217;s premium hypervisor product (not open-source) and is available for &lt;s&gt;free download&lt;/s&gt;, although the advanced features are not free. (Update no free download link &lt;a href="https://www.reddit.com/r/vmware/comments/1amtzvc/esxi_hypervisor_free_gone/"&gt;anymore&lt;/a&gt;.) VMware &lt;a href="https://www.digihunch.com/2018/07/overview-of-vsphere/"&gt;vSphere&lt;/a&gt; is virtualization platform built on top of ESXi, including a whole family of virtualization products.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-market-segments-and-players"&gt;Market segments and players&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtualization involves many market segments such as virtual desktop infrastructure (VDI, for desktop virtualization), server virtualization is the predominant domain in the virtualization of data centre environment. This effort led to Hyper-Converged Infrastructure (HCI) where almost all the traditional hardware resources are software-defined through the virtualization layer. The management of infrastructure is abstracted away from the physical hardware management. The three most fundamental areas in HCI are:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Server (compute) virtualization: the previous section covers the virtualization of memory and x86 CPU, which are the main focus on computing resource virtualization. Additionally, graphics computing resources can be virtualized today. Example products include: VMware vShpere (compute virtualization based on ESXi hypervisor).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Storage_virtualization"&gt;Storage Virtualization&lt;/a&gt;&lt;/strong&gt;: the technology to abstract physical data storage resource to make them appear as if they were a centralized resource. Storage virtualization takes place at three levels depending on the use case: block-level, file-level and object level. Example products include: VMWare vSAN (vSphere-native storage), HPE 3PAR (Tier-1 storage), EMC VxRail, PureStorage Flash Array (Tier 1), etc. Storage Virtualization enables &lt;a href="https://en.wikipedia.org/wiki/Software-defined_storage"&gt;&lt;strong&gt;Software-Defined Storage&lt;/strong&gt; &lt;/a&gt;&lt;strong&gt;(SDS)&lt;/strong&gt;, the provisioning and management of data storage independent of the underlying hardware.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Network_virtualization"&gt;Network Virtualization&lt;/a&gt;&lt;/strong&gt;: the technology to abstract network resources that were traditionally delivered in hardware to software. Network virtualization decouples network services from the underlying hardware management and allows virtual provisioning of an entire network. VLAN is a classic example of network virtualization. There are also various overlay technologies such as VXLAN, which provides an industry framework for overlaying virtualized layer 2 network over layer 3 network (used in Docker network) using an encapsulation mechanism and a control plane. Example products include: VMware NSX Data Center (L2-L7 network and security virtualization platform), Cisco ACI, Palo Alto Panorama. Network Virtualization enables &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Software-defined_networking"&gt;Software-Defined Network&lt;/a&gt; (SDN)&lt;/strong&gt;, an approach to network management that enables dynamic, programmatically efficient network configuration in order to improve network performance and monitoring, making it more like cloud computing than traditional network management.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-delivery-model"&gt;Delivery model&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Virtualization allows managed service providers (MSPs) to deliver IT service in the following three models:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Iaas (Infrastructure as a Service)&lt;/strong&gt;: MSP delivers VM to customers.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;PaaS (Platform as a Service)&lt;/strong&gt;: MSP delivers environments to customers (e.g. Database as a Service, managed RabbitMQ service, etc).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;SaaS (Software as a Service)&lt;/strong&gt;: MSP delivers entire application for the customer.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://www.redhat.com/cms/managed-files/iaas_focus-paas-saas-diagram-1200x1046.png" alt="What is IaaS?" style="width:608px;height:388px"/&gt;&lt;figcaption class="wp-element-caption"&gt;IT service delivery models enabled by virtualization technology&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since virtualization is the backbone of cloud computing. This model is also referred to as cloud computing delivery model.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-virtualization-and-containerization"&gt;Virtualization and Containerization&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These two concepts are similar and could be confusing to beginners. Both provide a mechanism to isolate computing resource for different applications, for the purpose of higher utilization of resource. The difference lies in how and where the isolation is made. Virtualization requires a guest operating system per VM (OS level isolation), whereas the container technology isolates application processes along with its runtime into a container (dependency level isolation), using some new Linux kernel features such as &lt;em&gt;namespaces &lt;/em&gt;and &lt;em&gt;cgroups&lt;/em&gt;. All containers make their system calls to the container engine on the host operating system. So they share a kernel on the same host. In this sense, container engine running on OS could be considered as type 2 hypervisor.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://dzone.com/storage/temp/10561741-vm-container-figure1.jpg" alt="Image title"/&gt;&lt;figcaption class="wp-element-caption"&gt;From VMs to containers&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VMware is a major player in enterprise data centre virtualization, which is facing fierce competition from public and private cloud vendors. VMware also has its own private cloud services. Docker is the most popular container technology that conforms to the specifications of Open Container Initiative (OCI), a governance structure for industry standards around container formats and runtimes.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-virtualization-and-cloud"&gt;Virtualization and Cloud&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Among public cloud vendors, AWS &lt;a href="https://cloudacademy.com/blog/aws-ami-hvm-vs-pv-paravirtual-amazon/"&gt;EC2 &lt;/a&gt;used Xen PV and Xen HVM in its earlier implementations. It has transitioned to AWS bare metal. The history is well summarized &lt;a href="http://www.brendangregg.com/blog/2017-11-29/aws-ec2-virtualization-2017.html"&gt;here&lt;/a&gt;. Microsoft Azure runs Azure Hypervisor as the native hypervisor in Azure Cloud Services platform. It is a customized version of Microsoft Hyper-V specifically for Azure platform. With GCP, Google &lt;a href="https://cloud.google.com/compute/docs/faq"&gt;Compute Engine&lt;/a&gt; (GCE) instance runs VMs on KVM as hypervisor. It can also enable nested virtualization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The scope of cloud computing is evolving overtime. It originally only refers to a business model of offering IT services (in one of the three delivery models outlined above) based on virtualization technology. Therefore I cannot make comparison between a technology and a business model. Today, with public cloud vendor extending their offerings (with various managed services and platforms) and people&amp;#8217;s misuse of the terms, the buzz-word &amp;#8220;cloud&amp;#8221; seems to suggest anything that is offered in public cloud service. The essence still remain the same where managed services and managed platforms are built on top of virtualized compute unit under the hood, which are driven by virtualization technologies.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/07/zookeeper-and-kafka-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kafka high-level Overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/08/virtualization-of-graphics-computing-resource/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 2 of 4 – Graphics Computing&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kafka high-level Overview</title><link>https://www.digihunch.com/2020/07/zookeeper-and-kafka-overview/</link><pubDate>Tue, 21 Jul 2020 23:19:00 -0400</pubDate><guid>https://www.digihunch.com/2020/07/zookeeper-and-kafka-overview/</guid><description>&lt;h3 class="wp-block-heading" id="h-zookeeper"&gt;Zookeeper&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;General definition of distributed system: a software system that is composed of &lt;strong&gt;independent &lt;/strong&gt;computing entities linked &lt;strong&gt;together &lt;/strong&gt;by a computer network whose components communicate and coordinate with each other to achieve a common computational goal. Implementing coordination among components of a distributed system is hard. For example, designated master node becomes single point of failure; cluster needs to detect availability of new nodes as it joins cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Zookeeper is designed to &lt;strong&gt;simplify cluster coordination&lt;/strong&gt;. Zookeeper implements key aspects in cluster coordination, such as distributed consensus, group management, presence protocols and leader election. In order to coordinate a cluster, zookeeper itself also runs in its own cluster, called &lt;strong&gt;ensemble&lt;/strong&gt;. Zookeeper exposes a simple but powerful interface of primitives. Applications can be designed on these primitives implemented through ZooKeeper APIs to solve the problems of distributed synchronization, cluster configuration management, group membership, etc.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.4.6/images/zkservice.jpg" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;Zookeeper Ensemble&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Clients can connect to a Zookeeper service by connecting to any member of the ensemble. The members of the ensemble are aware of each other&amp;#8217;s state. As long as a majority of the nodes are available, the service will be available. &lt;strong&gt;Zookeeper cli (zkCli.sh)&lt;/strong&gt; can be used to connect to Zookeeper server. they can be downloaded from &lt;a href="https://zookeeper.apache.org/releases.html"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Zookeeper is integrated with many other services apart from Kafka, such as Nifi and Hadoop.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-kafka"&gt;&lt;strong&gt;Kafka&lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://kafka.apache.org/"&gt;Kafka &lt;/a&gt;is a messaging system that is horizontally scalable, fault tolerant. It can also serve as queue storage system and stream processing system. It is distributed and use Zookeeper for cluster coordination. Each node is called a broker.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://kafka.apache.org/25/images/log_anatomy.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Topics &lt;/strong&gt;in Kafka (think of table in database) is a category or feed name to which messages (records) are published. Topic is broken up into ordered commit logs called partitions. Each partition has an ID. Each message in a partition is assigned an offset. Topics that are created in Kafka are distributed across brokers based on the partition, replication, and other factors. Each partition is replicated across several brokers depending on replication factor. For each partition, Kafka elect one replica as the leader of partition.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Writes to a partition is generally sequential. Reading messages can either be from the beginning, or rewind or skip to any port in partition given an offset value. Data in a topic is retained for a configurable period of time. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;message &lt;/strong&gt;is a unit of data in Kafka, in the format of key-value pair. A key is used to control the message that is to be written to partitions. Messages with the same keys are always written to the same partition (hash map)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;producer &lt;/strong&gt;publishes new message to a topic. Producers do not care which partition the message is written to and will balance messages over every partition of a topic evenly. Directing messages to a partition is done using the message key and a partitioner, this will generate a hash of the key and map it to a partition.&lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://kafka.apache.org/25/images/log_consumer.png" alt="" width="370" height="225"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;consumer &lt;/strong&gt;is subscribed to one or more topics and read messages sequentially. The consumer keeps track of messages it has consumed by keeping track on the offset of the message. The offset is a bit of metadata (an integer value that continually increases) that kafka adds to each message. Each partition has a unique offset which is stored with the offset of the last consumed message. A consumer can stop and start without losing its current state.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Kafka &lt;strong&gt;broker &lt;/strong&gt;is designed to operate as part of a cluster. One broker in the cluster also function as the cluster&amp;#8217;s controller, which is responsible for administrative operations such as: assigning partitions to brokers; monitoring for broker failures in cluster. A particular partition is owned by a broker and that broker is called the leader of the partition.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All consumers and producers operating on that partition must connect to the leader.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kafka cluster may replicate across cluster using MirrorMaker.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Reference: &lt;strong&gt;Kafka: The Definitive Guide: Real-Time Data and Stream Processing at Scale&lt;/strong&gt;&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2023/01/kafka-780x1024.jpeg" alt="" class="wp-image-7913" width="207" height="272" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/kafka-780x1024.jpeg 780w, https://www.digihunch.com/wp-content/uploads/2023/01/kafka-229x300.jpeg 229w, https://www.digihunch.com/wp-content/uploads/2023/01/kafka-768x1008.jpeg 768w, https://www.digihunch.com/wp-content/uploads/2023/01/kafka.jpeg 1036w" sizes="auto, (max-width: 207px) 100vw, 207px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt; &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/07/nfs-network-file-system-and-rpc-remote-procedure-call/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How RPC and NFS work&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/07/overview-of-virtualization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 1 of 4 – Hypervisor&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>How RPC and NFS work</title><link>https://www.digihunch.com/2020/07/nfs-network-file-system-and-rpc-remote-procedure-call/</link><pubDate>Wed, 15 Jul 2020 10:45:00 -0400</pubDate><guid>https://www.digihunch.com/2020/07/nfs-network-file-system-and-rpc-remote-procedure-call/</guid><description>&lt;p class="wp-block-paragraph"&gt;I touched on NFS in several previous postings, and here is a deeper dive of this particular protocol. NFS is built on top of Remote Procedure Call (RPC) and therefore it is important to understand RPC first. In fact NFS is one of the most prominent user of RPC and the best example for learning RPC.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;RPC overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;According to &lt;a href="https://en.wikipedia.org/wiki/Remote_procedure_call"&gt;Wikipedia&lt;/a&gt;, an RPC is when a computer program causes a procedure to execute in a different address space (commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction. That is, the programmer writes essentially the same code whether the subroutine is local to the executing program, or remote. This is a form of client–server interaction (caller is client, executor is server), typically implemented via a request–response message-passing system. In the object-oriented programming paradigm, RPCs are represented by remote method invocation (RMI), such as Java RMI API.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RPCs are a form of inter-process communication (IPC), in that other processes have a different address spaces: if on the same host machine, they have distinct virtual address spaces, even though the physical address space is the same; while if they are on different hosts, the physical address space is different. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RPC is a&amp;nbsp;&lt;a href="https://en.wikipedia.org/wiki/Request%E2%80%93response"&gt;request–response&lt;/a&gt;&amp;nbsp;protocol, and therefore synchronous. An RPC is initiated by the&amp;nbsp;&lt;em&gt;client&lt;/em&gt;, which sends a request message to a known remote&amp;nbsp;&lt;em&gt;server&lt;/em&gt;&amp;nbsp;to execute a specified procedure with supplied parameters. The remote server sends a response to the client, and the application continues its process. While the server is processing the call, the client is blocked (it waits until the server has finished processing before resuming execution), unless the client sends an asynchronous request to the server. There are many variations and subtleties in various implementations, resulting in a variety of different (incompatible) RPC protocols.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="694" height="475" src="https://www.digihunch.com/wp-content/uploads/2020/07/image-1.png" alt="" class="wp-image-1161"/&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading"&gt;NFS overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS is defined as a set of RPCs, including their arguments, results and effects. RPC makes NFS protocol transparent. RPC is also stateless so the server does not keep the state of RPCs once the request has been served. Each RPC contains the necessary information to complete the call. In the event of server failure, client will need to resubmit requests. &lt;a href="https://en.wikipedia.org/wiki/Network_File_System"&gt;NFS&lt;/a&gt; has several versions, with v3 and v4 most popular. We will focus on v3 in this posting and brief on v4.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Portmap&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RPC makes a remote call appears to client application as a local call, with the help of &lt;a href="https://en.wikipedia.org/wiki/Portmap"&gt;portmap&lt;/a&gt;. The utility for portmap is rpcbind. In RedHat/CentOS 5 or prior, it was even simply called portmap but they are essentially the same service for RPC port mapper. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The rpcbind service is required on both NFS client and NFS server. On the client, it talks to client application, as well as its counterpart on the server. Its main function is query its counterpart on the server, providing a RPC program number, and expects a port number in return. On the server, the rpcbind utility listens at port 111, waiting for request with RPC program number(service), and return the TCP or UDP port number on which the requested service is hosted. RPC program number is reserved numeric indicator of services as outlined in &lt;a href="https://www.iana.org/assignments/rpc-program-numbers/rpc-program-numbers.xhtml"&gt;RFC5531&lt;/a&gt;. For example, 100005 for mountd, 100021 for nlockmgr, and 100003 for nfs. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The port that rpcbind service itself listens on is always at port 111, which is known to both client and server. This is also referred to as portmapper daemon. Other than this fixed port, each NFS-related service (with their respect reserved program number) may be hosted on different ports on the server. The client rpcbind service must first look up for the port for the requested program number, then it directs the client to initiate connection to the specified port for the specific service. &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://docs.oracle.com/cd/E37838_01/html/E61058/figures/S9_portmap_seq.jpg" alt="image:Graphic illustrates portmap sequence for TCP/IP only"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With the following command rpcinfo command you may look up the program to port number mapping on destination server isilon.company.com&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;# rpcinfo -p isilon.company.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The following command is commonly used for displaying mountpoint and troubleshooting mount. Under the hood it is using the information from rpcinfo.&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;showmount -e isilon.dcb.digitalhunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;On the NFS server side, rpcbind service must start before nfs service start. Otherwise nfs service cannot register ports to rpcbind. If you restart rpcbind, every service that had registered to rpcbind must restart to register themselves again. By default, NFS server can dynamically assign a port for mountd, nlockmgr within a port range. &lt;a href="https://www.systutorials.com/fixing-ports-used-by-nfs-server/"&gt;This&lt;/a&gt; makes firewall setting a pain. These dynamically assigned port can be fixed via configuration files. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;File handle&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS uses &lt;a href="https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxb500/bpx1fr_NFS_file_handles.htm"&gt;file handles&lt;/a&gt; (or fhandle) to represent files. It is a better mechanism to reference a file object than pathname for three reasons: 1. file handle has fixed length (32bytes); 2. If the file is renamed, the file handle reference remain the same; 3. If a file is deleted, and then a new file is created with the same path, a new file handle will be created. A file handle has three parts:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;volume ID: to identify the mounted file system&lt;/li&gt;&lt;li&gt;inode #: to identify the file within the mounted file system&lt;/li&gt;&lt;li&gt;&lt;a href="https://utcc.utoronto.ca/~cks/space/blog/unix/NFSFilehandleInternals"&gt;generation #&lt;/a&gt;: to detect when file handle refers to an older version of inode. Traditional Unix filesystems may reuse inode and thus NFS client could mistakenly use an old file handle and access a new file.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The file handle information is only meaningful to the server. New file handles are returned to client by certain procedures, such as LOOKUP, CREATE, and MKDIR. The file handle for the root of the file system, is obtained by the client when it mounts the file system, as permission allows.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Permission and Locking&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When accessing a file on server, the client passes uid/gid info in RPCs, and the server performs permission checks as if the user was performing the operation locally. So users and groups are represented as integers. There are two security problems:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;The mapping from uid/gid to user must be the same on all clients. This is not practical in large deployment, although can be solved via Network Information Service (NIS);&lt;/li&gt;&lt;li&gt;Whether the root user on the client has root access to files on the server, is a server policy configuration. This can be addressed by enabling &amp;#8220;root squashing&amp;#8221; on server, so that client&amp;#8217;s uid 0 (root) is mapped to 65534 (nobody).&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Unix has &lt;a href="https://gavv.github.io/articles/file-locks/"&gt;two locking mechanisms&lt;/a&gt; (fcntl and flock). NFS protocol supports fcntl but not flock. The flock function is managed by a separate service (nfslock) to allow NFS to lock files. The &lt;a href="https://www.thegeekdiary.com/linux-os-service-nfslock/"&gt;nfslock&lt;/a&gt; daemon provides the ability to lock regions of NFS files. NFS service itself is still completely stateless with locking managed separately. This is changed in NFSv4.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Procedures used in NFS service&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS service defines a &lt;a href="https://docs.oracle.com/cd/E19620-01/805-4448/z4000027624/index.html"&gt;list of procedures&lt;/a&gt;. Here is a list with brief summary of activities. The bottom five RPCs are introduced in v3.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Procedure&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Activity&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GETATTR(fh)&lt;/td&gt;&lt;td&gt;Returns the attributes of a file, similar to stat syscall.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SETATTR(fh, attr)&lt;/td&gt;&lt;td&gt;Sets the attributes of a file (mode, uid, gid, size, atime, mtime); setting the size to 0 truncates the file&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;STATFS(fh)&lt;/td&gt;&lt;td&gt;Returns the status of a filesystem, such as block size, number of free blocks. e.g. df command.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LOOKUP (dirfh, name)&lt;/td&gt;&lt;td&gt;Returns fhandle and attributes for the named file in the directory specified by dirfh&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;READ (fh, offset, count)&lt;/td&gt;&lt;td&gt;Reads from a file, with offset and count specified. In v2, the length is up to 8192 bytes; v3 support more.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WRITE (fh, offset, count, data)&lt;/td&gt;&lt;td&gt;Writes to a file, with offset and count specified, as well as a separate field called data. Returns the new attributes of the file after the write.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CREATE (dirfh, name, attr)&lt;/td&gt;&lt;td&gt;Creates a file with the name, in directory, returns new fhandle and attributes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;REMOVE (dirfh, name)&lt;/td&gt;&lt;td&gt;Deletes the named file in from directory dirfh and returns status.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RENAME (dirfh, name, tofh, toname)&lt;/td&gt;&lt;td&gt;Renames name in directory dirfh, to toname in directory tofh.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LINK (dirfh, name, tofh, toname)&lt;/td&gt;&lt;td&gt;Creates a hard link toname, in directory tofh, that points to name, indirectory dirfh.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SYMLINK (dirfh, name, string)&lt;/td&gt;&lt;td&gt;Creates a symbolic link name, in the directory dirfh, with value string.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;READLINK (fh)&lt;/td&gt;&lt;td&gt;Reads a symbolic link and get file name of the target.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MKDIR (dirfh, name, attr)&lt;/td&gt;&lt;td&gt;Creates a directory name in the directory dirfh, and returns the new fh and attributes.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RMDIR(dirfh, name)&lt;/td&gt;&lt;td&gt;Removes a directory with the name, from parent directory dirfh.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;READDIR (dirfh, cookie, count)&lt;/td&gt;&lt;td&gt;Reads a directory and returns up to count bytes of directory entries from the directory dirfh. The cookie is used in subsequent readdir calls to start reading at a specific entry in the directory. Cookie of zero get the server to start with the first entry in the directory.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NULL&lt;/td&gt;&lt;td&gt;No activity. Used for testing only.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ACCESS&lt;/td&gt;&lt;td&gt;Helps with client caching.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MKNOD&lt;/td&gt;&lt;td&gt;Makes a device special file.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;FSINFO&lt;/td&gt;&lt;td&gt;Returns information about the server&amp;#8217;s capabilities. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;READDIRPLUS&lt;/td&gt;&lt;td&gt;Returns both file handle and attributes to eliminate LOOKUP calls when scanning a directory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;COMMIT&lt;/td&gt;&lt;td&gt;In NFSv3, the server can reply to WRITE RPCs immediately without syncing to disk. When client wants to ensure that the data is on stable storage, it sends a COMMIT RPC. This is used in asynchronous writes for better performance, which is an option negotiated at mount time.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The addition of COMMIT procedure in v3 offers the option to improve write performance in place of synchronous write. However, asynchronous write requires more coordination to ensure data integrity during transmission, in the event of server crash. NFSv3 uses write verifier for this purpose. A write verifier is an 8-bye value that the server must change if it crashes. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;After an asynchronous write, the reply from WRITE RPC includes a write verifier, the client must keep it for later use;&lt;/li&gt;&lt;li&gt;The client then sends a COMMIT RPC and the reply contains another write verifier;&lt;/li&gt;&lt;li&gt;The client compares the verifiers from the two returns for crash detection. If the verifiers don&amp;#8217;t match, the client must rewrite all uncommitted data.&lt;/li&gt;&lt;li&gt;The client must keep all uncommitted data in case of a server crash.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Additional daemon processes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to the three essential services, (nfs, rpcbind and nfslock), there are several auxiliary processes that facilitates NFS services. Their functions are listed &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/storage_administration_guide/ch-nfs"&gt;here&lt;/a&gt;:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Process&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rpc.mountd&lt;/td&gt;&lt;td&gt;Used by NFS server to process MOUNT requests from NFSv3 client. It checks that the requested NFS share is currently exported by the NFS server, and that the client is allowed to access it. If the mount request is allowed, the rpc.mountd server replies with a Success status and provides the File-Handle for this NFS share back to the NFS client.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rpc.nfsd&lt;/td&gt;&lt;td&gt;Allows explicit NFS versions and protocols the server advertises to be defined. It works with the Linux kernel to meet the dynamic demands of NFS clients, such as providing server threads each time an NFS client connects. This process corresponds to the nfs service.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rpc.lockd&lt;/td&gt;&lt;td&gt;A kernel thread which runs on both clients and servers. It implements the Network Lock Manager (NLM) protocol, which allows NFSv3 clients to lock files on the server, using procedures such as NLM_NULL, NLM_TEST, NLM_LOCK, NLM_GRANTED, NLM_UNLOCK, NLM_FREE. The service is started automatically whenever the NFS server is run and whenever an NFS file system is mounted.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rpc.statd&lt;/td&gt;&lt;td&gt;This process implements the Network Status Monitor (NSM) RPC protocol, which notifies NFS clients when an NFS server is restarted without being gracefully brought down. rpc.statd is started automatically by the nfslock service, and does not require user configuration. This is not used with NFSv4.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rpc.rquotad&lt;/td&gt;&lt;td&gt;This process provides user quota information for remote users. rpc.rquotad is started automatically by the nfs service and does not require user configuration.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rpc.idmapd&lt;/td&gt;&lt;td&gt;provides NFSv4 client and server upcalls, which map between on-the-wire NFSv4 names (strings in the form of user@domain) and local UIDs and GIDs. For idmapd to function with NFSv4, the /etc/idmapd.conf file must be configured. At a minimum, the &amp;#8220;Domain&amp;#8221; parameter should be specified, which defines the NFSv4 mapping domain. If the NFSv4 mapping domain is the same as the DNS domain name, this parameter can be skipped. The client and server must agree on the NFSv4 mapping domain for ID mapping to function properly.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading"&gt;NFSv4&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Even NFSv4 was introduced in 20 years ago, it improves access and performance of NFS on the Internet. It should be the default option for any new deployment.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;NFSv4 is TCP only protocol and it is stateful. &lt;/li&gt;&lt;li&gt;NFSv4 combines mount and lock protocols into NFS so only one port is being used. &lt;/li&gt;&lt;li&gt;Users and groups are identified with strings (user@domain, or group@domain where domain represents a registered DNS domain or sub-domain), instead of integers. The access control policies are compatible with both Unix and Windows.&lt;/li&gt;&lt;li&gt;NFSv4 mandates strong RPC security built on cryptography, with negotiation at the time of mount&lt;/li&gt;&lt;li&gt;NFSv4 adopted a framework for authentication, integrity and privacy at RPC level&lt;/li&gt;&lt;li&gt;Introduced new RPC COMPOUND, which allows for several operations in one go. At the server, operations are evaluated in order, and each has a return value.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFSv4.1 was release in 2010, and 4.2 in 2016. Both AWS EFS and Azure File storage supports 4.1.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/07/emc-productlines/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;EMC Isilon storage product&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/07/zookeeper-and-kafka-overview/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kafka high-level Overview&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Performance Analysis</title><link>https://www.digihunch.com/2020/06/performance-analysis-tools/</link><pubDate>Fri, 19 Jun 2020 16:47:01 -0400</pubDate><guid>https://www.digihunch.com/2020/06/performance-analysis-tools/</guid><description>&lt;h3 class="wp-block-heading" id="h-overview"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In 2015, Brendan Gregg posted two great articles&lt;a href="https://netflixtechblog.com/linux-performance-analysis-in-60-000-milliseconds-accc10403c55"&gt; &lt;/a&gt;on Netflix blog: &lt;a href="https://netflixtechblog.com/linux-performance-analysis-in-60-000-milliseconds-accc10403c55"&gt;Linux Performance Analysis in 60 seconds&lt;/a&gt;, and &lt;a href="https://netflixtechblog.com/netflix-at-velocity-2015-linux-performance-tools-51964ddb81cf"&gt;Linux Perfomrance Tools&lt;/a&gt;. They have great value when I was in a urgency to spot performance issues. The articles cover the essential tools for performance troubleshooting, including:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Check out load averages: w or uptime&lt;/li&gt;&#10;&lt;li&gt;Print kernel ring buffer: dmesg -T&lt;/li&gt;&#10;&lt;li&gt;Virtual memory status: vmstat 1&lt;/li&gt;&#10;&lt;li&gt;Multiple processor staticstics: mpstat -P ALL 1&lt;/li&gt;&#10;&lt;li&gt;Task status: pidstat 1&lt;/li&gt;&#10;&lt;li&gt;CPU and I/O status: iostat -xz 1&lt;/li&gt;&#10;&lt;li&gt;Free memory check: free -m&lt;/li&gt;&#10;&lt;li&gt;Network Activity record: sar -n DEV 1&lt;/li&gt;&#10;&lt;li&gt;TCP activity record: sar -n TCP,ETCP 1&lt;/li&gt;&#10;&lt;li&gt;Display processes: top&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We will dive into each of them in the next section.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-troubleshooting"&gt;Basic Troubleshooting&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The command w is equivalent of uptime (which shows uptime since boot) and who (which shows logged-in users). It also displays load average for the last 1 minute, 5 minutes and 15 minutes. The number of load average reflects the overall system load (CPU + disks), and it is further discussed in this &lt;a href="http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html"&gt;post&lt;/a&gt; with a simple take away:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;If the averages are 0.0, then your system is idle.&lt;/li&gt;&#10;&lt;li&gt;If the 1 minute average is higher than the 5 or 15 minute averages, then load is increasing.&lt;/li&gt;&#10;&lt;li&gt;If the 1 minute average is lower than the 5 or 15 minute averages, then load is decreasing.&lt;/li&gt;&#10;&lt;li&gt;If they are higher than your CPU count, then you might have a performance problem (it depends).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When Linux load averages increase, you know you have higher demand for resources (CPUs, disks, and some locks), but you aren&amp;#8217;t sure which. You will need to switch to other metrics. Brendan recommend don&amp;#8217;t spend more than 5 seconds on these numbers.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ w&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 12:14:10 up &lt;span style="color:#ae81ff"&gt;46&lt;/span&gt; days, 16:41, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; users, load average: 2.69, 2.44, 2.29&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dhunch pts/0 w6v-ghas01 24Jun20 3days 0.36s 0.30s ssh c7v-bastion&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dhunch pts/1 202.95.88.111 12:02 2.00s 0.00s 0.00s w&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dhunch pts/4 w6v-ghas01 17Jun20 15days 0.15s 0.07s view readme.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Before moving to more insightful metrics, it is also worth a quick look into the kernel ring buffer with dmesg command (dmesg -T | less +G). This will allow us to capture obvious issues such as oom-killer or TCP request dropping.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The vmstat tool reports the statistics of &lt;strong&gt;virtual memory&lt;/strong&gt;. Servers have a fixed amount of physical memory, but they can run a set of applications that use a much larger amount of virtual memory. Application tend to reserve more memory than they need, and they usually operate on only a subset of their memory. In both cases, the operating system can keep the unused parts of memory on disk, and page it into physical memory only if it is needed. For the most part, this kind of memory management works well. But it doesn&amp;#8217;t always with Java applications due to Java heap. Once a system start swapping &amp;#8211; moving pages of data from main memory to disk, and vice versa, the performance tend to be bad. Systems must be configured so that swapping never occurs.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ vmstat &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r b swpd free buff cache si so bi bo in cs us sy id wa st&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;385928&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734692&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;93&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;387732&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734704&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;43&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8017&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8524&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;85&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;387608&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734904&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;57&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6768&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7680&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;389008&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36734904&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;44&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6366&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7300&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;421728&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36700144&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8141&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7957&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;86&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;239360&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;421984&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36702048&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;467&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8994&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8362&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;85&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The tool prints key server statistics each line, with the first line showing the average since boot. Here lists the explanation of some columns:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;r&lt;/strong&gt;: number of processes running on CPU and waiting for a turn. This provides a better signal than load averages for determining CPU saturation, as it does not include I/O. To interpret: an “r” value greater than the CPU count is saturation.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;swpd&lt;/strong&gt;: the amount of virtual memory used. This number should align with the used column for Swap row from free command.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;buff, cache&lt;/strong&gt;: buffer and cache. They should align with the buff/cache column form Mem row from free command.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;free&lt;/strong&gt;: free memory in kilobytes. This number should align with the free column for Mem row from free command. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;si, so&lt;/strong&gt;: swap-ins and swap-outs. As mentioned, if these are non-zero, you&amp;#8217;re out of memory.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;bi, bo&lt;/strong&gt;: blocks received from and sent to a blcok device (# of block per second)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;in, cs&lt;/strong&gt;: number of &lt;a href="https://en.wikipedia.org/wiki/Interrupt"&gt;interrupt&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/Context_switch"&gt;context switches&lt;/a&gt; per second.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;us, sy, id, wa, st&lt;/strong&gt;: user, system, idle, wait I/O and stolen times. These are breakdowns of CPU time, on average across all CPUs. They should add up to 100% (or close). stolen time is amount of CPU time needed by a guest virtual machine that is not provided by the host. IO wait time is the CPU time waiting for I/O activity. Idle time could be several things: the process may be waiting for something (e.g. a response from database); the process may be blocked by a thread lock; or the process simply has nothing to do. user and system times are CPU times spent on user tasks and kernel tasks, respectively.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of these columns, watch for columns r, free, buff, cache, us, sy, id and wa at minimum. The combination of us and sy confirms if CPUs are busy. A constant degree of wa points to a disk bottleneck with too much time spent on pending disk I/O. The sy (kernel time) is necessary for I/O processing but sy stays high (e.g. constantly over 20%), it becomes interesting. Perhaps the kernel is processing I/O inefficiently.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For further per-CPU stats, use mpstat command (-P ALL), to prind CPU time breakdowns per CPU and check for imbalance. A single host CPU can be evidence of a single-threaded application. Here is an example output from a system of 16 CPU cores.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ mpstat -P ALL &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Linux 3.10.0-1062.12.1.el7.x86_64 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c7v-ghintapp01.digihunch.com&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 08/01/20 _x86_64_ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; CPU&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:23 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 all 13.77 0.00 0.19 0.00 0.00 0.00 0.00 0.00 0.00 86.05&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; 98.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 2.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; 2.94 0.00 0.98 0.00 0.00 0.00 0.00 0.00 0.00 96.08&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;9&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; 97.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 3.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; 0.99 0.00 0.99 0.00 0.00 0.00 0.00 0.00 0.00 98.02&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; 2.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 97.98&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; 1.98 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.02&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13:47:24 &lt;span style="color:#ae81ff"&gt;15&lt;/span&gt; 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For a per process summary of CPU consumption, use pidstat command. It can be thought of a periodical snapshot of top command, allowing you to watch for patterns. The %CPU column is the total across all CPUs so 5 CPUs have a maximum value of 500.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If vmstate indicates some I/O issue, iostat tool can help us understand block devices, on both the workload applied and the resulting performance. Key columns are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;r/s, w/s, rkB/s, wkB/s&lt;/strong&gt;: delivered reads, writes, read Kbytes, and write Kbytes per second to the device. Use these for workload characterization. A performance problem may simply be due to an excessive load applied.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;await&lt;/strong&gt;: the average wait time for I/O in milliseconds. This is the time that the application suffers, as it includes both time queued and time being serviced. Larger than expected average times can be an indicator of device saturation, or malfunction.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;avgqu-sz&lt;/strong&gt;: the average number of requests issued to device. Values greater than 1 can be evidence of saturation (although devices can typically operate on requests in parallel, especially virtual devices which front multiple back-end disks.)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;%util&lt;/strong&gt;: device utilization. This is really a busy percent, showing the time each second that the device was doing work. Values greater than 60% typically lead to poor performance (which should be seen in await), although it depends on the device. Values close to 100% usually indicate saturation.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I/O problem may either be inefficiencies in application that issues I/O request, or slowing disk unable to keep up with I/O requests. We review two examples here to illustrate each situation. The first output is as follows:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% iostat -xm &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;avg-cpu: %user %nice %system %iowait %steal %idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 23.45 0.00 37.89 0.10 0.00 38.56&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sda 0.00 11.60 0.60 24.20 0.02 0.14 13.35 0.15 6.06 5.33 6.08 0.42 1.04&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the first example, the disk stat loosk up at first glance. The w_await (time to service I/O write) is fairly low at 6.08ms. However, the system is spending 37.89% of its time in the kernel. If all that system time is from the application, it suggest something inefficient is happening. The fact that the system is doing 24.2 writes per second is another clue: that is alot when writing only 0.14 MB per second (MBps). I/O has become a bottleneck, and the next step would be to look into how the application is performing its writes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second example output is as follows:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% iostat -xm &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;avg-cpu: %user %nice %system %iowait %steal %idle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 35.05 0.00 7.85 47.89 0.00 9.20&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sda 0.00 0.20 1.00 163.40 0.00 81.09 1010.19 142.74 866.47 97.60 871.17 6.08 100.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example, it tells us that processes are spending 47.89% of their time in iowait, and the data to complete the I/O (w_await) is 871ms, the queue size is large, and the disk is writing at 81MB per second. This all points to disk I/O as a problem and that the amount of I/O in the application (or elsewhere in the system) must be reduced.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Bear in mind that poor performing disk I/O isn&amp;#8217;t necessarily an application issue. Many techniques are typically used to perform I/O asynchronously, so that the application doesn&amp;#8217;t block and suffer the latency directly (e.g. read-ahead for reads, and buffering for writes, also refer to &amp;#8220;&lt;a href="https://robertovitillo.com/why-you-should-measure-tail-latencies/"&gt;tail latency&lt;/a&gt;&amp;#8220;). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the acceptable utilization metric depends on the configuration of block device. If the storage is a logical disk device fronting many back-end disks (e.g. RAID 0), then 100% utilization may just mean that some I/O is being processed 100% of the time, however, the back-end disks may be far from being saturated, and may even be able to handle more work.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The free command gives the breakdown of memory usage. The right two columns are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;buffers&lt;/strong&gt;: for the buffer cache, used for block device I/O.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;cached&lt;/strong&gt;: for the page cache, used by file systems.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We just want to check that these aren&amp;#8217;t near-zero in size, which can lead to higher disk I/O (confirm using iostat), and worse performance. Linux uses free memory for the caches, but can reclaim it quickly if applications need it. So in a way the cached memory should be included in the free memory column. In this case, it&amp;#8217;s included in the available column. This &lt;a href="https://www.linuxatemyram.com/"&gt;website &lt;/a&gt;has further details.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To check interface stat, nicstat is a great tool but it isn&amp;#8217;t available by default in Linux. Instead we can run sar (-n DEV) to retrieve stats. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ sar -n DEV &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Linux 3.10.0-1062.12.1.el7.x86_64 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c7v-ghintapp01.digihunch.com&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 08/01/20 _x86_64_ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; CPU&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:25 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:26 eth0 3089.00 934.00 3815.33 834.61 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:26 lo 464.00 464.00 2289.07 2289.07 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:26 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:27 eth0 956.00 586.00 826.66 211.34 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:27 lo 213.00 213.00 196.00 196.00 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:27 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:28 eth0 349.00 181.00 52.32 147.19 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:51:28 lo 244.00 244.00 81.13 81.13 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Here rxkB/s/s and txkB/s represents receive and transmission throughput, as a measure of workload. If they reach the limit then the interface is the bottleneck.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On top of interface is the TCP layer, which can be monitored with sar again (-n ECP, ETCP). The key metrics are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;active/s&lt;/strong&gt;: number of locally-initiated (e.g. via connect()) TCP connections per second&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;passive/s&lt;/strong&gt;: number of remotely-initiated (e.g. via accept()) TCP connections per second&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;retrans/s&lt;/strong&gt;: number of TCP retransmits per second&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The active and passive counts are often useful as a rough measure of server load. It might help to think of active as outbound, and passive as inbound, but this isn&amp;#8217;t strictly true (e.g. consider a localhost to localhost connection). Retransmits are a sign of network or server issue; it may be an unreliable network (e.g. public Internet), or it may be due to a server being overloaded and dropping packets.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;dhunch@c7v-ghintapp01 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;$ sar -n TCP,ETCP &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Linux 3.10.0-1062.12.1.el7.x86_64 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c7v-ghintapp01.digihunch.com&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 08/01/20 _x86_64_ &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; CPU&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:29 active/s passive/s iseg/s oseg/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:30 0.00 1.00 28.00 35.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:29 atmptf/s estres/s retrans/s isegerr/s orsts/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:30 0.00 0.00 0.00 0.00 0.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:30 active/s passive/s iseg/s oseg/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:31 8.00 8.00 200.00 317.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:30 atmptf/s estres/s retrans/s isegerr/s orsts/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;16:52:31 0.00 1.00 1.00 0.00 3.00&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Last but not least is our favourite command top, which includes many of the metrics covered in previous tools. The downside to top is it is harder to see patterns over time, which may be more clear in tools like vmstat and pidstat, both of which produce rolling output.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Several tools introduced here involves sar, which is a great monitoring tool on its own that we need to be familiar with.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-system-activity-report-sar"&gt;System Activity Report (SAR)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Further to the basic tools, the sar command is very helpful as it stores historical stat every 10 minutes. Sar keeps 18 types of reports, all stored in /var/log/sa/. When viewing the report file, you may pipe the result to less command so it only prints header once. For example, if you would like to print CPU report for the 2nd of the month:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# sar -u -f /var/log/sa/sar02 | less&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If you check NFS client statistics for the 31st&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; # sar -n NFS -f /var/log/sa/sar31&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If you need to check network server statistics for today&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# sar -n NFS -f /var/log/sa/sar31&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Below are all types of reports:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;-u CPU utilization&lt;/li&gt;&#10;&lt;li&gt;-w task creation and system switching activity&lt;/li&gt;&#10;&lt;li&gt;-W swapping statistics&lt;/li&gt;&#10;&lt;li&gt;-B report paging&lt;/li&gt;&#10;&lt;li&gt;-b report I/O and transfer rate statistics&lt;/li&gt;&#10;&lt;li&gt;-R report memory statistics&lt;/li&gt;&#10;&lt;li&gt;-r memory utilization&lt;/li&gt;&#10;&lt;li&gt;-S swap space utilization&lt;/li&gt;&#10;&lt;li&gt;-H huge pages utilization statistics&lt;/li&gt;&#10;&lt;li&gt;-v inode&lt;/li&gt;&#10;&lt;li&gt;-q queue length and load average&lt;/li&gt;&#10;&lt;li&gt;-y TTY device activity&lt;/li&gt;&#10;&lt;li&gt;-d activity for each block device&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, DEV (per interface)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, EDEV (error per interface)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, NFS (NFS client)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, NFSD (NFS server)&lt;/li&gt;&#10;&lt;li&gt;-n network statistics, SOCK (socket usage)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-berkeley-packet-filter-bpf-compiler-collection-bcc-tools"&gt;Berkeley Packet Filter (BPF) Compiler Collection (bcc) tools&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For advanced, low-level performance troubleshooting, the BCC tools provide a suite of tools. Here we only cover the installation of it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In CentOS, install bcc-tools package with yum. When you try to run a command, such as cachestat, if you come across this error:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-bash: cachestat: command not found&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you will need to add its path to default:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export PATH=$PATH:/usr/share/bcc/tools&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now if you run into this error:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chdir(/lib/modules/3.10.0-1062.12.1.el7.x86_64/build): No such file or directory&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Traceback (most recent call last):&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The file listed is a symbolic link, and if it is missing, you just need to install kernel-headers that matches the kernel version:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum install kernel-headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you may use tools in /usr/share/bcc/tools. For example, cachestat help you display page cache hit ratio; gethostlatency shows DNS resolution latency; tcpconnect prints out active tcp connections (made via connect system call):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[root@dhunch ~]# /usr/share/bcc/tools/tcpconnect -t -P 8080 | gawk &amp;#39;{ print strftime(&amp;#34;%F %T  &amp;#34;), $0 }&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 00:16:57   TIME(s)  PID    COMM         IP SADDR            DADDR            DPORT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 02:16:57   0.000    15241  QNetworkAcce 4  10.100.22.21    10.101.84.10    8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 02:16:57   0.064    15241  QNetworkAcce 4  10.100.22.21    10.101.84.10    8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2020-06-13 02:16:57   0.438    15241  QNetworkAcce 4  10.100.22.21    10.101.84.10    8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command above outputs a&amp;nbsp;time and pid stamped log line every time&amp;nbsp;a TCP connection is made to port 8080; tcpaccept traces passive tcp connections (via accept system call). These tools are not as intrusive as tcpdump.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is beyond the purpose of this article to get into details of each tool in the BFP suite. The tools are covered in detail in books &amp;#8220;&lt;a href="https://amzn.to/3fEWNkq"&gt;BPF Performance Tools&lt;/a&gt;&amp;#8221; and &amp;#8220;Linux Observability with BPF&amp;#8221;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/06/network-analyzer-capture-filter-and-display-filter/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Capture filter and Display filter in Network Analyzer&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/06/iterate-through-cassandra-table-with-datastax-python-driver/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;DataStax Python Driver&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Capture filter and Display filter in Network Analyzer</title><link>https://www.digihunch.com/2020/06/network-analyzer-capture-filter-and-display-filter/</link><pubDate>Wed, 10 Jun 2020 21:21:18 -0400</pubDate><guid>https://www.digihunch.com/2020/06/network-analyzer-capture-filter-and-display-filter/</guid><description>&lt;p class="wp-block-paragraph"&gt;Capture filter is set before collecting packets. It is applied at the time of data acquisition and it impacts the size of the capture. It does not have as many variations as display filter and is usually not aware of protocols above TCP/UDP layer. A common form of capture filter is BPF (Berkerly Packet Filter) which is used in Linux Socket Filtering (e.g. &lt;a href="https://www.digihunch.com/2018/02/tcpdump-and-wireshark/"&gt;tcpdump&lt;/a&gt;).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Basic form is:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[tcp|udp] [src|dst] host 192.168.1.2 port 1234&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#39;tcp dst port 8080 and src host 147.206.160.9&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Display filter is set after packet collection. It is applied at the time of data manipulation. It does not impact the size of capture, but it controls how the data is presented (typically for analysis purpose). Display filter may support a variety of expressions that are interpreting data at TCP/UDP layer or above, for example HTTP. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here are some examples:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(tcp.flags.syn == 1) || (tcp.flags.reset == 1)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(tcp.flags.reset == 1) || (http.request.method==GET) ||&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(tcp.flags.reset == 1)||(http.request.uri contains &amp;#34;/box/url/string&amp;#34;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;||(http.response.code == 200)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://wiki.wireshark.org/DisplayFilters"&gt;Here&lt;/a&gt; are some further examples provided by Wireshark.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more details about the usage of capture filter and display filter, here is a page with &lt;a href="https://packetlife.net/blog/2008/oct/18/cheat-sheets-tcpdump-and-wireshark/"&gt;cheatsheet&lt;/a&gt;. Example for tcpdump on the left and wireshark in the middle and on the right.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To view http packet in shell terminal, there is also a helpful tool called httpry. You can applied BPF styled filter for capture, and organize display column. The drawback is there is no display filter so you&amp;#8217; would have to use grep. Here is an example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;httpry -i eth0 &amp;#39;tcp dst port 8080 and src host 147.206.160.9&amp;#39; -m GET -f Timestamp,x-correlation-id,x-userid,Request-URI | grep -v -P &amp;#39;\t\-\t&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/06/wsl2-environment-on-windows-10/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Setup WSL2 (and Docker) on Windows 10&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/06/performance-analysis-tools/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Performance Analysis&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Ansible at scale 2 of 2</title><link>https://www.digihunch.com/2020/05/ansible-directory-for-scalability-2-of-2/</link><pubDate>Mon, 25 May 2020 22:08:54 -0400</pubDate><guid>https://www.digihunch.com/2020/05/ansible-directory-for-scalability-2-of-2/</guid><description>&lt;h3 class="wp-block-heading" id="h-template-with-jinja2-and-files"&gt;Template (with Jinja2) and files &lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In an Ansible role, we can use files or templates to achieve similar results for configuration files. If the configuration file is the same across all targets then we can place it in files directory to push out. If the content of configuration file varies depending on the cluster size, we use Jinja2 template. For example, when you configure zookeeper configuration, a first entry may require total number of nodes in the cluster, a second entry may require the hostname of the server itself; and a third entry may require a comma separated line with hostnames of all nodes in the cluster. This is a typical use case of Jinja template.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We need to make sure Jinjas version is above 2.11.2 (as of May 2020) because older version such as 2.7.2 has known issues with namespaces. To check version and then upgrade Jinja2, we need to use &lt;a href="https://pypi.org/project/Jinja2/"&gt;pip&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip show Jinja2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install -U Jinja2&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The Ansible template module takes Jinja2 file as input and delivers result file on target host. Note that if the template references host variables from Ansible playbook, then you need to gather facts about host. This means you will have to use a basic playbook like below instead of adhoc command.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A basic playbook to test Jinja2 template is:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- hosts: &amp;#39;{{ansible_limit}}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gather_facts: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tasks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - template:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; src: cassandra_xml.j2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dest: /tmp/cassandra.xml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Although Jinja2 offers a lot of flexibility with loop and if-else statement, it is just a templating language and not a programming language. It requires some tricks to achieve what you may otherwise easily do with programming language. One example is persisting a variable outside of a loop. As per the &lt;a href="https://jinja.palletsprojects.com/en/2.11.x/templates/"&gt;document&lt;/a&gt;, it is not possible to set variables inside a block and have them show up outside of it. This also applies to loops. The only exception to that rule are if statements which do not introduce a scope. To achieve that, you would have to use namespace, for each loop where you need to access the variable afterwards from outside of the loop.&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;{% block db_cluster_config_nobackup %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% set ns=namespace(nodeid=0) %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% for host in groups[my_db_group]|sort %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;var name=&amp;#34;DBHost{{ns.nodeid+1}}&amp;#34; value=&amp;#34;{{hostvars[host].inventory_hostname}}&amp;#34; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% set ns.nodeid=ns.nodeid+1 %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endfor %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;var name=&amp;#34;DBClusterHosts&amp;#34; value=&amp;#34;{% for i in range(ns.nodeid) %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;${DBHost{{i+1}}}{% if not loop.last %},{% endif %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endfor %}&amp;#34; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endblock cass_cluster_config_nobackup %}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For the same reason, you might as well clearly define the start and end of each block in order to not run into trouble with scoping behaviours of variables. These limitations makes Jinja2 template not easy to read and may take several rounds of playbook runs to troubleshoot.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-handler-vs-conditional-task"&gt;Handler vs conditional task&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Sometimes you only want to run a task when its previous task results a change. There are two ways to achieve this: conditional task and handler.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With conditional task, we register the result of previous task to a variable, and execute the ensuing tasks conditionally based on assessment of the variable. We&amp;#8217;d have to specify the condition for each of the subsequent tasks that needs to execute conditionally. These tasks, if condition is met, can execute immediately after the first task that registers the variable.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The alternative is through an Ansible mechanism called handler. &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#handlers-running-operations-on-change"&gt;Handler&lt;/a&gt; implements a series of tasks in a separate yaml file in the handers directory under the role. In the triggering task we need to notify the handler. The tasks in the hander will fire if the triggering task returns &amp;#8220;changed&amp;#8221; in its result. Handler is a great way to shorten the length of task or Playbook. However, we need to understand several subtleties with regard to handlers: &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Although handler is notified during a task run, it is &lt;span style="text-decoration: underline;"&gt;not fired until the end of each block of tasks&lt;/span&gt; in a play. They are not immediately fired after triggering task.&lt;/li&gt;&#10;&lt;li&gt;A handler will &lt;span style="text-decoration: underline;"&gt;only execute once&lt;/span&gt; at the end of play, even if it was notified multiple times by different tasks during the play run.&lt;/li&gt;&#10;&lt;li&gt;Handler tasks are executed &lt;span style="text-decoration: underline;"&gt;in the order of declaration&lt;/span&gt;, not in the order of notification. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, Ansible&amp;#8217;s notification handling mechanism is asynchronous, once-only, and out of sequence. The points above are illustrated in the following playbook:&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;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- hosts: ghdocker&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tasks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: CopyFile3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; copy:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; src: ~/ansible/file3.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dest: /tmp/file3.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notify:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - handler3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - handlergeneral&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: CopyFile2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; copy:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; src: ~/ansible/file2.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dest: /tmp/file2.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notify:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - handler2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - handlergeneral&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: CopyFile1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; copy:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; src: ~/ansible/file1.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dest: /tmp/file1.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notify:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - handler1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - handlergeneral&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - debug: msg=&amp;#34;end of play!&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handlers:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: handler1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; debug: msg=&amp;#34;file1.txt has been copied.&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: handler2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; debug: msg=&amp;#34;file2.txt has been copied.&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: handler3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; debug: msg=&amp;#34;file3.txt has been copied.&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: handlergeneral&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; debug: msg=&amp;#34;A file has been copied&amp;#34;&#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;Here is the output of the playbook run:&#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;PLAY [ghdocker] ******************************************************************************&#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;TASK [Gathering Facts] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok: [ghdocker]&#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;TASK [CopyFile3] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;changed: [ghdocker]&#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;TASK [CopyFile2] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;changed: [ghdocker]&#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;TASK [CopyFile1] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;changed: [ghdocker]&#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;TASK [debug] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok: [ghdocker] =&amp;gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;msg&amp;#34;: &amp;#34;end of play!&amp;#34;&#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;RUNNING HANDLER [handler1] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok: [ghdocker] =&amp;gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;msg&amp;#34;: &amp;#34;file1.txt has been copied.&amp;#34;&#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;RUNNING HANDLER [handler2] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok: [ghdocker] =&amp;gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;msg&amp;#34;: &amp;#34;file2.txt has been copied.&amp;#34;&#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;RUNNING HANDLER [handler3] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok: [ghdocker] =&amp;gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;msg&amp;#34;: &amp;#34;file3.txt has been copied.&amp;#34;&#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;RUNNING HANDLER [handlergeneral] ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok: [ghdocker] =&amp;gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;msg&amp;#34;: &amp;#34;A file has been copied&amp;#34;&#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;PLAY RECAP ******************************************************************************&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ghdocker : ok=9 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Handler is a good way to keep idempotency. For example, Ansible does not have a way to import a yum .repo file to &lt;a href="https://stackoverflow.com/questions/53976165/importing-adding-a-yum-repo-file-using-ansible"&gt;create a repo&lt;/a&gt;. We have to take two steps: &lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;use get_url module to download the repo file (e.g. to /tmp), &lt;/li&gt;&#10;&lt;li&gt;use shell module to call yum-config-manager.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The problem is these two steps are not idempotent. If you repeat them, it will attempt to import the same repo file again. A little trick here is to use force=no option on get_url so it does not attempt to download if the file is already present in target directory. Then notify a handler to import repo file so the shell command is only called if there is a change.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The task 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- name: download repo file&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; get_url:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url: https://download.docker.com/linux/centos/docker-ce.repo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dest: /tmp/docker-ce.repo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mode: &amp;#39;0755&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; force: no&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notify:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - Add docker repository&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The handler 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- name: Add docker repository&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; shell: yum-config-manager --add-repo=/tmp/docker-ce.repo&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The handler is only fired when it is notified after get_url module returns changed in its result. Running the task again will not cause it to attempt to add the same repo again.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that when you use command or shell module, Ansible typically reports changed status. If this is not desired (e.g. you don&amp;#8217;t want it to notify handler all the time), this behaviour can be overridden with changed_when parameter. You can specify conditions to meet in order to consider the shell/command module to have a changed result. &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html#overriding-the-changed-result"&gt;Here&lt;/a&gt; is an example.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-ansible-commands"&gt;Ansible commands&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In operation, our engineer needs to run a command on a group of servers. I encourage the use of Ansible adhoc command whenever possible. I recommend start with the following two commands:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ansible-inventory --graph&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ansible all -m ping&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The ping module triggers an &amp;#8220;Ansible ping&amp;#8221; to targets in the specified group. Over the years, Ansible community developed many helpful modules, such as yum, yum_repository, apt_rpm, uri, synchronize, fine, copy, etc and many can be used instead of bash command. However, sometimes, the expected Ansible module is either unavailable or missing function. For example, Ansible&amp;#8217;s uri module cannot replace curl command with the following switches:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s -XGET http://&lt;span style="color:#f92672"&gt;{{&lt;/span&gt;inventory_hostname&lt;span style="color:#f92672"&gt;}}&lt;/span&gt;:8080/objects/&lt;span style="color:#f92672"&gt;{{&lt;/span&gt;object_id&lt;span style="color:#f92672"&gt;}}&lt;/span&gt;/binary/all -o /dev/null -w &lt;span style="color:#e6db74"&gt;&amp;#39;%{response_code} %{size_download} %{time_total} %{speed_download}\n&amp;#39;&lt;/span&gt; | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{if ($1==200) print &amp;#34;size=&amp;#34;$2/1048576&amp;#34;MB,time=&amp;#34;$3&amp;#34;s,speed=&amp;#34;$4/1048576&amp;#34;MB/s&amp;#34;; else if($1==404) print &amp;#34;Cannot find object {{object_id}}&amp;#34;; else print &amp;#34;Unknown error. Code &amp;#34;$1 &amp;#34; when retrieving object {{object_id}}&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;To leverage all these curl options, we still need to use the shell module in Ansible to call the command in shell. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Other helpful Ansible commands include ansible-pull for pulling playbooks from VCS repo, and ansible-console for interactive adhoc command execution.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-tags-and-extra-variables"&gt;Tags and extra variables&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both tags(-t) and extra variables (-e) are great ways to achieve flow control in playbooks. You can specify to run tasks with certain tags or skip tasks with certain tags. Extra variables can override the default variables from the host or the group. Both are great tools to improve re-usability of a Playbook.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-speed-up-execution"&gt;Speed up execution&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To speed up execution of Ansible tasks, there are several ways. For example, we can disable fact gathering by default so it only gathers fact if explicitly specified. This can be set in gathering=explicit under defaults section of ansible configuration file. If you have to gather facts, you may cache the facts using the following:&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;[defaults]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gathering = smart&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fact_caching_timeout = 86400&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fact_caching = jsonfile&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fact_caching_connection = /tmp/ansible_fact_cache&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Other than caching, Ansible allows you to select from several execution strategies for playbook. The linear strategy introduces configurable parallelization per task. The free strategy introduces parallelization per play. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;linear&lt;/strong&gt; (by default): Up to the fork limit of hosts will execute each task at the same time and then the next series of hosts until the batch is done, before going on to the next task. This mode ensures the progress is synchronized at each task. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;free&lt;/strong&gt;: as specified above, this is preferred when there is no need to coordinate the progress between each host target. It is a &amp;#8220;free run&amp;#8221; for each host all the way till the end of the playbook.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;debug&lt;/strong&gt;: essentially linear strategy except that the progress is controlled by an interactive debug session&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The fork limit, with a conservative default of 5, can be adjusted in Ansible configuration. The execution strategy can be either specified in Ansible configuration, or specified per play. For example, the following snippet sets the strategy to free for the current play:&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;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- hosts: all&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; strategy: free&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tasks:&#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;Ansible documentation also mentions some play-level keywords to control execution. The &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_delegation.html"&gt;&lt;strong&gt;serial&lt;/strong&gt;&lt;/a&gt; keyword, is one of them. It can be set along with any strategy above, and it introduces the effect of hosts batching. The value can be a single number, a percentage, or even a list of numbers (if size for each batch is different). Note that the batch size should not exceed the fork limit. This is particularly useful in rolling upgrades. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- name: test play&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts: webservers&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; serial: &amp;#34;30%&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With the parallelization capacity outlined above, a potential concern is some heavy-lifting task may consume a lot of resources, if being executed for all hosts at the same time. Luckily, Ansible has a task/block level keyword &lt;strong&gt;throttle&lt;/strong&gt;, which &amp;#8220;de-parallelize&amp;#8221; the multi-host progress at a particular task, or &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html"&gt;block&lt;/a&gt;. Here is an example provided by Ansible documentation:&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;tasks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- command: /path/to/cpu_intensive_command&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; throttle: 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If there are long running tasks, we can specify async and poll values so Ansible leaves a task running and check back later. For example, the following task allows Ansible to move on and check back every 5 seconds, if the task takes longer than 45 seconds, it is considered failed:&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;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - hosts: all&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; remote_user: root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tasks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: simulate long running task for 15 sec, wait for up to 45 sec, poll every 5 sec&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; command: /bin/sleep 15&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; async: 45&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; poll: 5&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-python-version"&gt;Python Version&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The recommendation is to use Python3 for any new development because there is no dependency. If there is no preference specified, Ansible tries to find out the appropriate interpreter and it can be seen in the response of ansible &lt;a href="https://docs.ansible.com/ansible/latest/modules/ping_module.html"&gt;ping&lt;/a&gt; module. You can also force the interpreter by providing additional parameter ansible_python_interpreter. To change default interpreter, specify interpreter_python in ansible.cfg. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[defaults]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;inventory=~/ansible/inventories/site.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;library=~/ansible/library/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vault_password_file = ~/ansible/.vault_key&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;host_key_checking = False&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;display_skipped_hosts = False&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;retry_files_enabled = False&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;interpreter_python=/usr/bin/python3&#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;[privilege_escalation]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;become_method=sudo&#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;[ssh_connection]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh_args = -C -o ControlMaster=auto -o ControlPersist=1h&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pipelining = True&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-my-open-issues"&gt;My open issues&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have some minor details that I have not been able to address, after a lot of time googling around. So I have to leave them for future reference.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If an Ansible playbook involves multiple plays (i.e. each with their own host), there is no way to persist a variable across different plays. A dumb alternative is to make all the variables to use available for every single host (under all directory).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Jinja2 template, if I need to access the group of a target host (as defined in inventory), and the target belongs to multiple groups, I cannot filter to match the group I need.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/05/docker-swarm-brief-notes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker Compose, Docker Stack and Docker Swarm&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/06/wsl2-environment-on-windows-10/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Setup WSL2 (and Docker) on Windows 10&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Docker Compose, Docker Stack and Docker Swarm</title><link>https://www.digihunch.com/2020/05/docker-swarm-brief-notes/</link><pubDate>Sun, 24 May 2020 21:58:03 -0400</pubDate><guid>https://www.digihunch.com/2020/05/docker-swarm-brief-notes/</guid><description>&lt;p class="wp-block-paragraph"&gt;This posting covers some basic docker orchestration tools.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Compose&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker Compose&amp;#8217;s predecessor is a tool called Fig developed by Orchard, which was acquired by Docker in 2014, with Fig renamed to Docker Compose. Docker Compose is the official container management tool. It is essentially a python script that parses yaml file, to make Docker API calls to manage containers dynamically. It is installed along with Docker on MacOS and Windows. On Linux, you will have to download package with curl command and install manually. Docker Compose has three versions so far and we should create new template with v3. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Docker compose yaml template consists of three parts:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;services&lt;/strong&gt;: similar to docker run&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;build: specify Dockerfile to build image&lt;/li&gt;&#10;&lt;li&gt;cap_add, cap_drop: specify kernel capabilities (e.g. NET_ADMIN, SYS_ADMIN)&lt;/li&gt;&#10;&lt;li&gt;command: override default startup command by container&lt;/li&gt;&#10;&lt;li&gt;container_name&lt;/li&gt;&#10;&lt;li&gt;depends_on&lt;/li&gt;&#10;&lt;li&gt;devices: map host device to container&lt;/li&gt;&#10;&lt;li&gt;dns&lt;/li&gt;&#10;&lt;li&gt;dns_search:&lt;/li&gt;&#10;&lt;li&gt;entryppoint: override entry point from image&lt;/li&gt;&#10;&lt;li&gt;env_file: specify file that stores environment variable&lt;/li&gt;&#10;&lt;li&gt;environment: specify environment variable&lt;/li&gt;&#10;&lt;li&gt;image: specify the location of image&lt;/li&gt;&#10;&lt;li&gt;pid: share the PID namespace with host&lt;/li&gt;&#10;&lt;li&gt;ports: expose network ports. HOST:CONTAINER&lt;/li&gt;&#10;&lt;li&gt;networks&lt;/li&gt;&#10;&lt;li&gt;volumes: mount host volume to container&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;networks&lt;/strong&gt;: similar to docker network create&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;volumes&lt;/strong&gt;: similar to docker volume create&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is a typical structure of docker compose yaml template (wordpress):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;version: &amp;#34;3.8&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;services:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mysql:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image:mysql:5.7&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; volumes:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - mysql_data:/var/lib/mysql&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; restart: always&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; environment:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MYSQL_ROOT_PASSWORD:root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MYSQL_DATABASE:mywordpress&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MYSQL_USER:digihunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MYSQL_PASSWORD:hunchdigi&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; wordpress:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; depends_on:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - mysql&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image: wordpress:php7.4&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ports:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &amp;#34;8080:80&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; restart:always&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; environment:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; WORDPRESS_DB_HOST:mysql:3306&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; WORDPRESS_DB_USER:digihunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; WORDPRESS_DB_PASSWORD: hunchdigi&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; WORDPRESS_DB_NAME: digihunch &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;networks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; frontend:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; backend:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;volumes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mysql-data: {}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In summary, Docker Compose is an orchestration tool for &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;single host&lt;/span&gt;&lt;/strong&gt;, typically seen in development and test environment with dependencies between services.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Stack&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A stack is a set of related services and infrastructure that gets deployed and managed as a unit. A docker stack file has the same format as Docker Compose file, with the only requirement that the version: key specify a value of 3.0. The other difference between Docker Stacks and Docker Compose, is that stacks do not support builds. All images have to be built prior to deploying the stack.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From the stack file, Docker first executes the network section and create networks that do not exist. Then it goes through other elements. A service is a JSON collection(dictionary) that contains a bunch of keys. The image key is the only mandatory key in the service objects, which will be pulled from Docker Hub by default. Ports key maps the port of Swarm to the port of each service replica. By default, all ports are mapped using ingress mode. This means they&amp;#8217;ll be mapped and accesible from every node in the Swarm -even nodes not running a replica. The alternative is host mode, where ports are only mapped on Swarm nodes running replicas for the service.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The environment key lets you inject environment variables into services replica.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The secrets key defines two secrets &amp;#8211; revprox_cert and revprox_key. These must be defined in the top-level secrets key, and must exist on the system. Secrets get mounted into service replicas as a regular file. The secrets defined in this service will be mounted in each service replica as /run/secrets/revprox_cert and /run/secrets/revprox_key, unless otherwise specified.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The volumes key is used to mount pre-created volumes and host directories into a service replica.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The networks key ensures that all replicas for the service will be attached to the front-tier network. The network specified here must be defined in the networks top-level key, and if it doesn’t already exist, Docker will create it as an overlay.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The service also defines a placement constraint under the deploy key. This ensures that replicas for this service will always run on Swarm worker nodes. Placement constraints are a form of topology-aware scheduling, and can be a great way of influencing scheduling decisions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When Docker stops a container, it issues a SIGTERM to the process with PID 1 inside the container. The container (its PID 1 process) then has a 10-second grace period to perform any clean-up operations. If it doesn’t handle the signal, it will be forcibly terminated after 10 seconds with a SIGKILL. The stop_grace_period property overrides this 10 second grace period.”&lt;br&gt;Although you may scale a docker service as part of a stack with scale command, it is not recommended. Instead, stack file should be used as the ultimate source of truth (declarative method vs imperative method). All changes to the stack should be made to the stack file, and the updated stack file used to redeploy the app.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Docker Swarm&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For multi-host cluster, Docker Swarm facilitates the deployment of micro-services. Docker Swarm is:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;a &lt;span style="text-decoration: underline;"&gt;cluster&lt;/span&gt; of Docker hosts: enterprise-grade, secure communication, PKI with automation, dynamic addition of nodes&lt;/li&gt;&#10;&lt;li&gt;an &lt;span style="text-decoration: underline;"&gt;orchestration engine&lt;/span&gt;, with deployment automation, deploying native swarm apps (using Docker API) and Kubernetes apps.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Docker nodes can be physical servers, VMs, cloud instances, etc. Nodes are configured as managers or workers. Managers look after the control plane of the cluster, and dispatches tasks to workers. Managers forms a distributed management cluster on its own, and they use Raft protocol to ensure consistency. Workers accept tasks from managers and execute them. Swarm mandatorily uses TLS to encrypt communications, authenticate nodes, and authorize roles, with Automatic key rotation.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="388" src="https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-1024x388.webp" alt="" class="wp-image-13095" srcset="https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-1024x388.webp 1024w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-300x114.webp 300w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-768x291.webp 768w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-1536x582.webp 1536w, https://www.digihunch.com/wp-content/uploads/2020/05/swarm-node-2048x777.webp 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The atomic unit of scheduling on a swarm is the service. When a container is wrapped in a service, we call it a task or a replica, and the service construct adding things like scaling, rolling updates and simple rollbacks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To initialize a swarm, we need to have the following ports open. Then we can initialize the first manager node, join additional manager nodes, and then join workers.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;2377/tcp: for secure client-to-swarm communication&lt;/li&gt;&#10;&lt;li&gt;7946/tcp &amp;amp; udp: for control plane gossip&lt;/li&gt;&#10;&lt;li&gt;4789/udp: for VXLAN-based overlay networks&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Docker node can exist either in single-engine mode as stand alone, or in swarm mode as part of a swarm. Service only exist in swarm mode. Running docker swarm init on a Docker host in single-engine mode will switch that node into swarm mode, create a new swarm, and make the node the first manager of the swarm. Then additional nodes can be joined as managers or workers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Swarm managers have native support for high availability, through an active-passive, multi-manager HA. Only one manager is considered active (the leader), which is the only one that will ever issue live commands against the swarm. If a passive manager receives commands for the swarm, it proxies them across to the leader.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Managers are either leaders or followers. This is Raft terminalogy because swarm uses an impelementation of the Raft consensus althorithm to power manager HA. As to HA, the following two best practices apply:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;deploy an odd number of managers&lt;/li&gt;&#10;&lt;li&gt;don&amp;#8217;t deploy too many managers (3 or 5 recommended, never more than 7)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Having an odd number of managers reduces the chances of split-brain conditions. Having less than 7 managers ensures that achieving consensus is quick.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With a service, we can specify name, port mappings, network to attach to, and images, as well as desired state for an application service. It is recommended in production environment to use docker-compose template to specify service. Services have replication mode, and the default is replicated. This will deploy a desired number of replicas and distribute them as evenly as possible across the cluster. The other mode is global, which runs a single replica on every node in the swarm.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Running &amp;#8220;docker service scale&amp;#8221; command can scale the number of service replicas from 5 to 10, which in the background updates the service&amp;#8217;s desired state to the newly specified number of replicas. Behind the scenes, Swarm also runs a scheduling algorithm that defaults to balancing replicas as evenly as possible across the node in the swarm. Docker makes it super easy to push updates to deployed applications. With rolling update, you may specify number of replicas to update at a time, and cool-off period per update.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a class="rank-math-link" href="https://upcloud.com/community/stories/docker-swarm-vs-kubernetes-comparison-of-the-two-giants-in-container-orchestration/"&gt;Here&lt;/a&gt; is a great article on the difference between Docker Swarm and Kubernetes.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Ansible at scale 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/05/ansible-directory-for-scalability-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Ansible at scale 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Ansible at scale 1 of 2</title><link>https://www.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/</link><pubDate>Sun, 17 May 2020 19:38:34 -0400</pubDate><guid>https://www.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://www.ansible.com/hubfs/pdfs/Ansible-InDepth-WhitePaper.pdf"&gt;Ansible In Depth&lt;/a&gt; white paper outlines Ansible&amp;#8217;s use cases in four categories:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Configuration management&lt;/li&gt;&lt;li&gt;Application deployment&lt;/li&gt;&lt;li&gt;Orchestration: for coordinating a multi-machine process such as interacting with load balancer and rolling cluster upgrade&lt;/li&gt;&lt;li&gt;As-needed task execution: ad-hoc tasks on large number of hosts&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At work, my original automation scheme involves several &lt;a href="https://www.digihunch.com/2019/05/automation-with-ansible-a-primer/"&gt;Ansible&lt;/a&gt; Playbooks that started off simple but have been sprawling ever since. I have to spend some time to revamp the Ansible code base, following the best practices from the &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#directory-layout"&gt;official documentation&lt;/a&gt;. The goal is to:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Reduce the number of Playbooks;&lt;/li&gt;&lt;li&gt;Improve code re-usability (by Ansible roles);&lt;/li&gt;&lt;li&gt;Increase portability across different customer environment;&lt;/li&gt;&lt;li&gt;Improve security;&lt;/li&gt;&lt;li&gt;Re-organize the directory so that more team members can contribute to different parts of it.&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Ansible code base is used by our customer service engineers, some of whom are ingrained with the established way they have been using certain Playbooks in their daily tasks. This requires me, throughout the development initiative, to ensure a consistent interaction between them and their Playbook commands.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-inventory-and-variables"&gt;Inventory and variables&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We cannot guarantee that every custom environment are identical, but what we can do is make sure that for a new environment, the only change to make is inventory and variables. This is where we can strike a balance between portability and customization. No changes should be made to tasks, roles or Playbooks when the Ansible directory is deployed at a different customer environment.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If the total number of servers to manage are under 100, all can be listed in a single inventory file, and specify the only inventory file as default so the -i switch is not required for every Ansible command run. If there are more than 100 servers, it is advisable to separate them out into several inventory files in YAML, each less than 200 lines. You will have to specify inventory file with -i each time you run Ansible command.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The inventory may contain a hierarchy of groups, in order to facilitate command calls to specific groups of servers. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;all:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; children:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; children:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod_app:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; children:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod_app_dc1:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost01:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost03:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zk_id: 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost05:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost07:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zk_id: 2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost09:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost11:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zk_id: 3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost13:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod_app_dc2:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost02:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost04:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zk_id: 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost06:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost08:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zk_id: 2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost10:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost12:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zk_id: 3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apphost14:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_app: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_nginx: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_db: no&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod_db:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; children:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod_db_dc1:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost01:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost03:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost05:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost07:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost09:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost11:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; clustered: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bk_node: dbhost11&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prod_db_dc2:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost02:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost04:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost06:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost08:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost10:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dbhost11:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; clustered: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bk_node: dbhost12&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_app: no&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_nginx: no&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_db: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansible_become_pass: &amp;#39;{{site_prod_root_pw}}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; test:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; children:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; test_dc1:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tapphost01:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; test_dc2:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tapphost02:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansible_become_pass: &amp;#39;{{site_test_root_pw}}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_app: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_nginx: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; has_db: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansible_become: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansible_become_method su&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansible_become_user: root&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;User of Ansible Playbook can use -l to specify a pattern that matches a single or multiple groups, such as prod_db_dc*. In the above example, the password is not stored in clear text. They should reference a variable from a separate file encrypted by ansible-vault.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-from-playbooks-to-roles"&gt;From Playbooks to roles&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As a refresher from the &lt;a href="https://www.ansible.com/hubfs/pdfs/Ansible-InDepth-WhitePaper.pdf"&gt;white paper&lt;/a&gt;, a single &amp;#8220;task&amp;#8221; in Ansible is essentially a module call with parameters. A &amp;#8220;play&amp;#8221; consists of a series of tasks (defined under &amp;#8220;&lt;em&gt;tasks&lt;/em&gt;&amp;#8221; section) all to execute on a specified host (defined under &amp;#8220;&lt;em&gt;hosts&lt;/em&gt;&amp;#8221; section). A Playbook consist of &lt;span style="text-decoration: underline;"&gt;one or several&lt;/span&gt; plays, as shown in &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html"&gt;this example&lt;/a&gt;. In reality though, a Playbook usually contains only one play. Even that one play can grow to an unmanageable length, as complexity increases over time. This is where we need to change our approach towards scalability and manageability.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Ansible community advocates the use of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-ansible-roles-to-abstract-your-infrastructure-environment"&gt;roles&lt;/a&gt; in place of Playbooks. The concept of Ansible &amp;#8220;role&amp;#8221; seems fairly abstract and confusing at the beginning. The word &amp;#8220;role&amp;#8221; pictures a static server state, whereas our existing Playbooks are full of actions (think of shell scripts). How would one convert an action list into static states? After some thought, I came to the understanding that roles should be thought of as desired end state. Yes, the end state is static, but that&amp;#8217;s all we care about. This is essentially the whole idea of Ansible&amp;#8217;s desired state configuration: you start from the end state and leave it to modules to complete what needs to be done to reach that state. The concept of role perfectly reflects how Ansible wants you to think about solving an infrastructure problem &amp;#8211; stop thinking about what you need to do. Instead, think about what you ultimately want, start from the desired state and work backwards.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In our own setup, the best practice turned out to be: if the Playbook involves a single play with less than 5 tasks, just stick to Playbook. We don&amp;#8217;t get rid of Playbooks just for the sake of it. Otherwise, if a Playbook has grown to more than 5 tasks, we need to think about our desired state, and either implement a new role, or incorporate it into an existing role. This is the time we have to transition from the Playbook oriented thinking to the role oriented thinking. Each role directory can include a task sub-directory with main.yml that references the rest of the tasks. Each role can define its own role-related variables. If there&amp;#8217;s a lot in common between two roles, we can even have a common role with or without its main.yml.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A simplified version of our Ansible directory structure 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── deploy-app.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── deploy-db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── inventories&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── group_vars&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── all&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   ├── all.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   └── vault_all.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── prod_dc1_db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── prod_dc2_db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── test_dc1_db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── test_dc2_db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── host_vars&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── site_inventory.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── roles&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── common&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── files&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── tasks&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── log.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── skip_self.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── validate_path.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── db_conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── defaults&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── files&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── handlers&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── meta&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── README.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── tasks&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   ├── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   ├── start_db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   ├── stop_db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   └── update_cluster_var.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── templates&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   ├── myid.j2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   ├── db_properties.j2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   │   └── zookeeper_properties.j2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── vars&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── app_conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── defaults&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── files&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── handlers&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── meta&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── README.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── tasks&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── bk_app_conf.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── empty_app_conf.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── push_app_conf.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── start_app.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── stop_app.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── tar_app_conf.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── untar_app_conf.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── update_cluster_var.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── templates&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── dbref_xml.j2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── vars&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── main.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── service-app.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── service-db.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Variables specific to a group of hosts or individual hosts can be included in different yml files. When the entire directory is moved to a different customer environment, our engineers will need to update the inventory and variable files. The task, roles and Playbooks should build their logics using those variables. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-vault"&gt;Vault&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Our previous implementation of Ansible Playbook stores sudo password base64 encoded and use no_log to avoid displaying values. Now we move those to encrypted variable yml file using ansible-vault. We reference the value to encrypt as regular variable:&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;ansible_become_pass: &amp;#39;{{passtoencrypt}}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ansible_become_method: sudo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ansible_become: yes&#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 run the following:&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;ansible-vault create vault_all.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This prompt for a key, and once you type in the key it opens a text editor where we can store the real password. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;passtoencrypt: MyP@ssw0rd4real!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Use the text editor to save file. The file is now saved encrypted and must be open with correct key (aka vault password). If we call Ansible Playbook with &amp;#8211;ask-vault-pass switch then the Playbook will prompt for key input, or use include_vars to include variable from vault file (&lt;a href="https://www.toptechskills.com/ansible-tutorials-courses/ansible-include-import-variables-tutorial-examples/"&gt;example&lt;/a&gt;). If we want to even skip this, we can store the key in a file and reference them from vault_password_file in ansible.cfg&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ansible Vault has more commands to edit or view the encrypted variables in the &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/vault.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-optimize-connection"&gt;Optimize connection&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;OpenSSH 5.6 and later supports multiplexing where multiple SSH sessions share a TCP connection. This can be turned on so that the following SSH connections save the time of TCP handshake. This can be configured in ansible configuration file under ssh_connection. Below is an example of this option with ControlPersist=1h. So the TCP connection is torn down after 1 hour. &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;[ssh_connection]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh_args = -C -o ControlMaster=auto -o ControlPersist=1h&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The other option we can leverage is pipelining. Ansible takes three steps to execute a task:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;build a python script based on module used&lt;/li&gt;&lt;li&gt;copy the python script to remote host&lt;/li&gt;&lt;li&gt;execute the python script on the remote host&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If pipelining is turned on, the python script is passed in along with the SSH session, this would save a roundtrip and increase performance. Pipelining can be configured under ssh_connection in Ansible configuration file:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ssh_connection]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pipelining = True&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the example below we can see by pipelining we cut the number of connection in half:&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;# with pipelining&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ghunch@control-host ~]$ ansible remote-host -vvvv -m ping | grep EST&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#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;# without pipelining&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ghunch@control-host ~]$ ansible remote-host -vvvv -m ping | grep EST&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;remote-host&amp;gt; ESTABLISH SSH CONNECTION FOR USER: ghunch&#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 if we use sudo command, then we need to disable requiretty in /etc/sudoers on the remote host.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-custom-module"&gt;Custom Module&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It&amp;#8217;s fairly straightforward to build a custom module in Ansible. Just place the module file (modulename.py) in inventory directory and use it as you would with regular Ansible module. The module file needs to be completed in Python with certain return value. Before creating custom module, you should look for existing modules to avoid re-inventing the wheel. You may also need to determine whether you simply need to &lt;a href="https://docs.ansible.com/ansible/latest/modules/script_module.html"&gt;run a python script&lt;/a&gt; on target host (with Ansible&amp;#8217;s script module), or you really need an Ansible module. The former is procedural, and the latter focus on desired state. Custom module is more used in proprietary development. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/05/understanding-where-the-memory-goes-on-linux-vm/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Balloon steals memory from virtual machines&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/05/docker-swarm-brief-notes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker Compose, Docker Stack and Docker Swarm&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Cassandra data model (as opposed to relational model)</title><link>https://www.digihunch.com/2020/04/cassandra-data-model-as-opposed-to-relational-database/</link><pubDate>Wed, 29 Apr 2020 18:45:00 -0400</pubDate><guid>https://www.digihunch.com/2020/04/cassandra-data-model-as-opposed-to-relational-database/</guid><description>&lt;p class="wp-block-paragraph"&gt;Bad data model design with &lt;a href="https://www.digihunch.com/2018/03/cassandra-architecture-summary/"&gt;Cassandra&lt;/a&gt; causes chronic pains as application scales. I had to re-read about data model design in &amp;#8220;&lt;a href="http://shop.oreilly.com/product/0636920299837.do"&gt;Cassandra &amp;#8211; the Definitive Guide&lt;/a&gt;&amp;#8221; and keep my notes and thoughts in this post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The data modelling in the relational world is indoctrinated to every students out of university. It embraces several things:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Entity-Relation: we typically start with tables that represents entities, and then tables that expresses relations;&lt;/li&gt;&#10;&lt;li&gt;Query design after table design: we can join multiple tables, index certain fields for better query performance;&lt;/li&gt;&#10;&lt;li&gt;Data normalization: several normal forms (NFs) are brought up to better organize data; de-normalization only occurs when 1) performance bottleneck reached; and 2) specific requirement on retaining snapshots of previous (un-updated) value in a field;&lt;/li&gt;&#10;&lt;li&gt;referential integrity: we can specify foreign keys on a table to reference the primary key of a record in another table; we can configure cascading deletes, etc;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Anybody with years of experience with relational database may have all these built in their instinct. Unfortunately, Cassandra does not follow any of these patterns. For someone with relational database background, the trip entering the Cassandra design is very counter-intuitive.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-no-joins"&gt;No joins&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Cassandra you have very limited options to achieve what you can do with joins in relational realm. One option is to duplicate the data column on different tables (a pattern against the &amp;#8220;normalization&amp;#8221; best practice). The second table is denormalized and it represents the join results. The other option rarely applied is to do the work on the client side.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-query-driven-data-modelling"&gt;Query-driven data modelling&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In relational database, you start writing queries after tables are laid out to pull together disparate data, using the relationship defined by the keys. The queries is secondary concern. It is assumed that you can always get the data you want as long as you have your tables modelled properly, even if you have to use several complex subqueries or join statements.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Cassandra, You do not start with tables to represent entity. Instead, you would start with queries, and then organize data around the queries. This means an upfront effort must be made to investigate what queries the client application may perform, and work backwards with tables that answers those queries in the most efficient manner. Table names in Cassandra often takes names such as: hotels_by_poi, avaialbe_rooms_by_hotel_date, reservations_by_guest, reservations_by_hotel_date. On the tables the selection of partition key and clustering keys should also consider best query performance and avoids wide partitions. It should also ensure with best effort that a query should not have to travel across multiple partitions in order to return results.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-denormalization"&gt;Denormalization&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Due to the query-driven modelling approach, Cassandra usually need to be designed with denormalization. The entire concept of normalization applies only to relational world and in Cassandra it&amp;#8217;s perfectly normal to organize data that are against NFs.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-designing-for-optimal-storage"&gt;Designing for optimal storage&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cassandra tables are each stored in separate files on disk. Its best practice to keep related columns defined together in the same table. We need to minimize the number of partitions that must be searched in order to satisfy a given query. Because the partition is a unit of storage that does not get divided across nodes, a query that searches a single partition will typically yield the best performance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The book &amp;#8220;Cassandra: the definitive guide&amp;#8221; contains a great example of modelling hotel reservation system. &lt;a href="https://www.datastax.com/blog/2015/02/basic-rules-cassandra-data-modeling"&gt;This article&lt;/a&gt; is also a good guideline.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="780" height="1024" src="https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-780x1024.webp" alt="" class="wp-image-13112" style="width:193px;height:254px" srcset="https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-780x1024.webp 780w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-229x300.webp 229w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-768x1008.webp 768w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-1171x1536.webp 1171w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book-1561x2048.webp 1561w, https://www.digihunch.com/wp-content/uploads/2025/04/cassandra-book.webp 1951w" sizes="auto, (max-width: 780px) 100vw, 780px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How memory usage adds up in Linux&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/05/understanding-where-the-memory-goes-on-linux-vm/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Balloon steals memory from virtual machines&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>How memory usage adds up in Linux</title><link>https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/</link><pubDate>Sun, 19 Apr 2020 21:12:56 -0400</pubDate><guid>https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/</guid><description>&lt;p class="wp-block-paragraph"&gt;There are too many metrics that describes some aspects about memory in Linux. This posting will make sense of those common metrics in Linux, CentOS as an example.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most fundamental command is free and my favourite switch is -h for human readable reads. You can use -m, -k, -b for different units. The result looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total used free shared buff/cache available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mem: 32780168 16832160 3200408 101356 12747600 15399528&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Swap: 2097148 2055148 42000&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Swap is essentially disk space and many application such as Cassandra, ElasticSearch recommend disabling swap as best practice and they do not want disk speed to drag the performance of memory. Many suggest that swap is not needed in today&amp;#8217;s era at all given the amount of memory for cheap. This is &lt;a href="https://askubuntu.com/questions/291378/do-we-still-need-swap-partitions-on-servers"&gt;debatable&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With the row for Mem, the four columns should add up to the total, as suggested in the chart below. &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="http://blog.yufeng.info/wp-content/uploads/2013/01/free1.jpg" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;total = free + used + shared + buff/cache &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The four columns from free command output are supposed to always add up to the physical memory size. This command simplifies things quite a bit and each of these values are actually taken from certain lines in /proc/meminfo:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Metric from free command&lt;/td&gt;&lt;td&gt;Metric in /proc/meminfo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;total&lt;/td&gt;&lt;td&gt;MemTotal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;used&lt;/td&gt;&lt;td&gt;??&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;free&lt;/td&gt;&lt;td&gt;MemFree&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shared&lt;/td&gt;&lt;td&gt;Shmem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;buff/cache&lt;/td&gt;&lt;td&gt;Cached + Slab&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;available&lt;/td&gt;&lt;td&gt;MemAvailable&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The buffer and cache (and even swap) can be freed by &lt;a href="https://www.tecmint.com/clear-ram-memory-cache-buffer-and-swap-space-on-linux/"&gt;command&lt;/a&gt;. The value of used doesn&amp;#8217;t seem to come from anwhere in /proc/meminfo, but it should be calculable from the memory used per process, which can be seen from top command.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the result of top command, the column RSS (resident set size) is from the VmRSS value in /proc/&amp;lt;pid&amp;gt;/status, it is the actual physical memory consumed by the process. This value is originally from the second read in /proc/&amp;lt;pid&amp;gt;/statm, which represents the number of pages. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ghunch@centos ~]$ cat /proc/6495/status | grep VmRSS ; cat /proc/6495/statm&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VmRSS:&#9;20852916 kB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;49829626 5213229 1212275 1 0 5773980 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ghunch@centos ~]$ getconf PAGE_SIZE&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4096&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Linux default page size is 4096 or 4K, so in the result from above, 5213229 x 4kB = 20852916 kB, which is the size of memory taken by process ID 6459. Therefore if we go through all processes and add up the VmRSS, we should get (close to) the used memory?&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;But wait a second, we have not account for slab info (memory used by kernel) yet, which is displayed in /proc/slabinfo. To calculate the total size taken by slab, we use &amp;lt;num_objs&amp;gt; and &amp;lt;objsize&amp;gt; columns from /proc/slabinfo.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from that there is &lt;a href="https://en.wikipedia.org/wiki/Page_table"&gt;page table&lt;/a&gt;, the table that stores the mapping between virtual address and physical address, is stored in the physical memory as well and the size is specified in the PageTables entry in /proc/meminfo. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now our equation becomes:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Used Memory = (RSS for all processes) + (all objects in slab) + (page table)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use the following script to calculate the used memory and compare it with free command output.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#/bin/bash&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; PROC in &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;ls /proc/|grep &lt;span style="color:#e6db74"&gt;&amp;#34;^[0-9]&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; -f /proc/$PROC/statm &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TEP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;cat /proc/$PROC/statm | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print ($2)}&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RSS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;expr $RSS + $TEP&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RSS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;expr $RSS &lt;span style="color:#ae81ff"&gt;\*&lt;/span&gt; 4&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PageTable&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;grep PageTables /proc/meminfo | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SlabInfo&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;cat /proc/slabinfo |awk &lt;span style="color:#e6db74"&gt;&amp;#39;BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo $RSS&lt;span style="color:#e6db74"&gt;&amp;#34;KB&amp;#34;&lt;/span&gt;, $PageTable&lt;span style="color:#e6db74"&gt;&amp;#34;KB&amp;#34;&lt;/span&gt;, $SlabInfo&lt;span style="color:#e6db74"&gt;&amp;#34;MB&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;printf &lt;span style="color:#e6db74"&gt;&amp;#34;rss+pagetable+slabinfo=%sMB\n&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;echo $RSS/1024 + $PageTable/1024 + $SlabInfo|bc&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;free -m&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Running it require root access and the bc package installed. The result is most likely greater than the used memory value. Below is the result from my server:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;89925884KB, 201788KB, 3303.92MB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rss+pagetable+slabinfo=91318.92MB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total used free shared buff/cache available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mem: 128772 87032 726 498 41013 40346&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Swap: 2047 3 2044&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;So the result is over by (91318 &amp;#8211; 87032) = 4286M. This is due to shared memory. The RSS value from above include memory from shared libraries as long as the pages from those libraries are in the memory. If multiple processes use the same library, the memory from shared library is counted multiple times. Check out the difference between &lt;a href="https://en.wikipedia.org/wiki/Resident_set_size"&gt;RSS&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Proportional_set_size"&gt;PSS&lt;/a&gt; (proportional set size) &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[Disclaimer] The chart and script are stolen from this authors &lt;a href="http://www.programmersought.com/article/6127180173/"&gt;post&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/04/common-local-git-operations/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Common local Git operations&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/04/cassandra-data-model-as-opposed-to-relational-database/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cassandra data model (as opposed to relational model)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Common local Git operations</title><link>https://www.digihunch.com/2020/04/common-local-git-operations/</link><pubDate>Wed, 08 Apr 2020 11:54:00 -0400</pubDate><guid>https://www.digihunch.com/2020/04/common-local-git-operations/</guid><description>&lt;p class="wp-block-paragraph"&gt;This is a summary of concepts in common Git operations. We will discuss brach, merge, rebase, cherrypick, stash and reset. Then we&amp;#8217;ll discuss pull, fetch, and push.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-commit-branch-and-head"&gt;Commit, Branch and HEAD&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you run &amp;#8220;git commit&amp;#8221;, the following happens:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Git checksums each subdirectory, and stores them as a tree object (file path and name) and blob object (file content) in Git repository;&lt;/li&gt;&lt;li&gt;Git creates a commit object that has the metadata and a pointer to the root project tree; or if this is not the first commit, the pointer will point to the commit immediately before it&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The operations above should form a chain of commit. It can be a long chain and may diverge into branches. In Git semantics however, a branch is simply a lightweight, movable pointer to one of the commits. The default branch name in Git is master. A Git repository may contain multiple branches and the name master itself does not suggest any privilege. There is also a special pointer called HEAD, which indicates the branch you are currently working on. So branch is essentially a pointer to a commit; HEAD is essentially a pointer to a branch. &amp;#8220;git checkout&amp;#8221; can switch branch that HEAD points to. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-merge"&gt;Basic Merge&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One type of basic merge simply moves branch pointer from one commit to another (along the same chain) without creating any commit. Here is a diagram before basic merge:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-branching-4.png" alt="Hotfix branch based on `master`."/&gt;&lt;figcaption&gt;Before basic merge, Hotfix branch is based on&amp;nbsp;&lt;code&gt;master&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The following command performs basic merge:&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;$ git checkout master&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git merge hotfix&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Updating f42c576..3a0874c&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Fast-forward&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; index.html | 2 ++&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 1 file changed, 2 insertions(+)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then Git simply moves the pointer (named master) forward. There is no divergent work to move together, hence no chance of merge conflict. This type of basic merge is also called &amp;#8220;fast-forward&amp;#8221; merge.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-branching-5.png" alt="`master` is fast-forwarded to `hotfix`."/&gt;&lt;figcaption&gt;After basic merge, &lt;code&gt;master&lt;/code&gt;&amp;nbsp;is fast-forwarded to&amp;nbsp;&lt;code&gt;hotfix&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The other type of merge involves reconciling divergent work together, which may or may not involve conflict. Suppose this is the commit tree to start with:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-merging-1.png" alt="Three snapshots used in a typical merge."/&gt;&lt;figcaption&gt;Three snapshots used in a typical merge&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The following commands perform the merge:&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;$ git checkout master&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Switched to branch &amp;#39;master&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git merge iss53&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Merge made by the &amp;#39;recursive&amp;#39; strategy.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;index.html | 1 +&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1 file changed, 1 insertion(+)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Instead of just moving the branch pointer forward, Git creates a new snapshot that results from this three-way merge and automatically creates a new commit that points to it. This is referred to as a merge commit, and is special in that it has more than one parent.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-merging-2.png" alt="A merge commit."/&gt;&lt;figcaption&gt;A merge commit&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now that your work is merged in, you have no further need for the&amp;nbsp;&lt;code&gt;iss53&lt;/code&gt;&amp;nbsp;branch. You can close the issue in your issue-tracking system, and delete the branch:&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;$ git branch -d iss53&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If commits from two respective branches changes the same file in different ways, then there is a merge conflict. In this case, Git cannot just create a merge commit. Instead it asks the user to resolve the conflict first. You have to choose either side of the change, or just merge the content yourself. At this point, if you introduce a change that does not appear in any parent, it is referred to as an evil merge. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Beyond the basic merge, there are more sophisticated merge conflict resolution tools covered in &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging"&gt;advanced merging&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-merge-and-rebase"&gt;Merge and Rebase&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are two ways to integrate changes from one branch to another. Merge and rebase. Suppose your commit chain diverge into a master branch and a feature branch. Merging (from feature to master) takes the content of feature branch and integrate it with master branch. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git checkout master&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git merge feature&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When you rebase a feature branch onto master, you move the base of the feature branch to master branch’s ending point.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git checkout feature&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git rebase master&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After merge, you are still &lt;span style="text-decoration: underline;"&gt;on the same branch&lt;/span&gt;. The commits from other branch are integrated into the branch that you are already on. There is no change in any existing commits (history). After rebase, your base will be &lt;span style="text-decoration: underline;"&gt;moved to a different branch&lt;/span&gt;, along with the commits that you have made in the previous branch (since the diverge). In other words, by re-playing those commits on a different branch, it changed history.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/855/1*pzT4KMiZDOFsMOKH-cJjfQ.png" alt=""/&gt;&lt;figcaption&gt;merge vs rebase&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The chart above is stolen from &lt;a href="https://hackernoon.com/git-merge-vs-rebase-whats-the-diff-76413c117333"&gt;this article&lt;/a&gt;, which does a better job explain in detail the difference, pros and cons of merge and rebase. Merge does create a &amp;#8220;merge commit&amp;#8221;, and a git history full of merges can be cluttered. Rebase does not create an extra commit but since it changes the history of a branch, it has impact to other collaborators. It can be done in an interactive way (with -i switch). The golden rules of rebasing is covered in &lt;a href="https://www.atlassian.com/git/tutorials/merging-vs-rebasing"&gt;this&lt;/a&gt; article. One of the principles is that never perform a rebase on a public branch.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="600" height="335" src="https://www.digihunch.com/wp-content/uploads/2020/08/0.gif" alt="" class="wp-image-1196"/&gt;&lt;figcaption&gt;git operations&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cherrypick"&gt;Cherrypick&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a cherrypick operation, the current branch does not change. You simply pick interested commits from other branches to re-apply to your current branch. You may pick a single or a series of commits from other branch. These commits are not &amp;#8220;moved&amp;#8221; to your current branch. They remain intact. They are just re-played as new commit to current branch. Unlike rebase, there is no re-writing of history, hence not as dangerous.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-reset-and-stash"&gt;Reset and Stash&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Suppose you are working on a part of a project and it starts getting messy. There has been an urgent bug that needs your immediate attention. It is time to save your changes and switch branches. If you are okay to give up your uncommitted work, you may perform a reset, in one of the three modes covered in a &lt;a href="https://www.digihunch.com/2019/06/git-explained-1-of-2/"&gt;previous article&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;But most likely, you don’t want to do a commit of half-done work. The solution is git stash. Stashing is handy if you need to quickly switch context and work on something else but you&amp;#8217;re mid-way through a code change and aren&amp;#8217;t quite ready to commit. In the most basic workflow, you need to run this command to save your uncommitted (but staged) work. As soon as you stash your change, the working directory is clean with all uncommitted local changes saved elsewhere. You can perform any other Git operations, such as change branch. When you&amp;#8217;re ready to resume, you may pop the stash. Here is an example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git add .&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git stash&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git checkout correctbranch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git stash pop&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Instead of pop, you can also use apply to keep the changes in working directory. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git stash apply&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;More details are on &lt;a href="https://www.atlassian.com/git/tutorials/saving-changes/git-stash"&gt;this page&lt;/a&gt; from Bitbutket.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-fetch-and-pull"&gt;Fetch and Pull&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A git fetch simply downloads blob data from remote so the .git directory comes in sync with the server. It does not attempt to update the local working directory. If there is staged or uncommitted local changes, fetch will not impact them. &lt;strong&gt;A git pull is essentially git fetch followed by git merge&lt;/strong&gt;. In addition to downloading blob data, it also updates local working directory. Therefore, there is a chance of merge conflict when the same file has been modified locally. Git will usually guide you through the merge conflict by flagging the conflict area in the file and let you decide the survival changes. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /usr/bin/env ruby&#10;&lt;/span&gt;&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;def hello&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&amp;lt; HEAD&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;hola world&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=======&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;hello mundo&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; mundo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end&#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;hello&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;You will be prompted in an editor session to reconcile the conflict. Once the file is saved, you will also need to do a &amp;#8220;merge commit&amp;#8221;, before you can pull again.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-push"&gt;Push&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Git push is the opposite of pull, where you merge local branch to the remote. (There is no opposite of fetch because there is no point to merge to remote without updating working directory, no collaborator works on the working directory on the server after all). If the local branch has fallen out of sync with the remote, there is a chance of merge error during git push. To minimize the chance of a merge during push, we can run a git pull before and reconcile any potential conflict locally. This is known as a pre-merge.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Visualizer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I came across a great visualizer of commit chain &lt;a href="https://git-school.github.io/visualizing-git/"&gt;here&lt;/a&gt;. In the command panel type some git command and it will print the commit graph for you&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/03/ntlm-and-kerberos/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;NTLM and Kerberos protocols&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How memory usage adds up in Linux&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Introduction to Active Directory (AD)</title><link>https://www.digihunch.com/2020/02/everything-about-the-domain/</link><pubDate>Fri, 28 Feb 2020 21:36:00 -0400</pubDate><guid>https://www.digihunch.com/2020/02/everything-about-the-domain/</guid><description>&lt;h3 class="wp-block-heading"&gt;Workgroup, homegroup and Windows Domain&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A workgroup is a group of computers on the same local network. A Windows computer not joined to a domain is part of a workgroup. In a workgroup, no computer has control over any other computer and it does not require a password. Any computer can join or leave a workgroup any time. Workgroup was previously for home file and printer sharing and Microsoft later introduced homegroup for more security. Compared to workgroup, all computers in a homegroup needs to be on the same home network (instead of local network). Homegroup is password protected. New computer needs to join homegroup by providing the password.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Windows domains (or domains for short) provide network administrators with a way to manage a large number of PCs and control them from one place and remotely.&amp;nbsp;One or more servers — known as domain controllers — have control over the domain and the computers on it. Computers on a domain has to be on the same local network, either physically or over VPN. Centralized control is essential for corporate operation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Administrators can join a Windows PC with professional or enterprise license to a domain. Once joined, the computer does not use its own local user accounts. When a user logs into a computer on that domain, the computer authenticates the user account name and password with the domain controller. Also, the computer cannot just leave the domain without administrator access. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Network administrators can change group policy settings on the domain controller. Each computer on the domain will get these settings from the domain controller and they’ll override any local settings users specify on their PCs. All the settings are controlled from a single place. This prevents from users from changing many system settings on a computer joined to a domain. The domain controller is in charge of what a user can do. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from centralized administration, the benefit the users is that they can log in with the same username and password on any computer joined to the domain, if permission allows.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Domain Controller&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In order to achieve centralized administration and log-in from any computer in the corporate world, a centralized service called domain controller is introduced. At a high level, a domain controller maintains a list for each of the followings:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Users and their passwords&lt;/li&gt;&lt;li&gt;Computers and their credentials&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is because in a domain, not only the users, but also the computers (workstations or servers) need to be authenticated. For example, when a Windows server boots up, it needs to log on to the domain with its own credential. This way we can control whether the server is allowed to query the domain for information about users. If it is allowed to query the domain, then we can determine whether the user is allowed to log on that server, and eventually, authenticate the user. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For a domain controller, it responsibility to credentials for users and computers, and respond to log in requests (authentication service) is a critical commitment in the enterprise environment. Domain controller is therefore commonly built with high availability and fault tolerance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Administrators needs to add each new user to the user directory in domain controller. They also needs to register each new computer with the domain controller by joining them to the domain. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Joining a Domain&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As explained earlier, joining a Window domain means register a computer in the domain so it has the permission to query the domain to validate users identity and permissions. Both Windows server and Linux server can join a domain. Windows servers usually provide a path through UI to join a domain and password is required. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To join a Linux (e.g. Redhat) server to a domain, we can use a tool called &lt;em&gt;adcli&lt;/em&gt;. Here is a good example of using this command to join a &lt;a href="https://www.2daygeek.com/join-integrate-rhel-centos-linux-system-to-windows-active-directory-ad-domain/"&gt;domain&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Authentication&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Windows or Linux server in the domain needs to go to the domain controller to authenticate itself and the users. Authentication involves several protocols, including kerberos, NTLM, TLS/SSL and Digest, as part of an extensible architecture. In addition, some protocols are combined into authentication packages such as Negotiate and the Credential Security Support Provider.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://web.mit.edu/kerberos/krb5-1.12/doc/index.html"&gt;MIT Kerberos Documentation&lt;/a&gt; provides some tools (e.g. kinit, klist) to configure and troubleshoot Kerberos protocol.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Active Directory &lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since Windows 2000, Active Directory is a complete redesign and re-branding of the entire Windows Domain system. The term &lt;em&gt;Active Directory&lt;/em&gt; now refer to either the entire domain system, or the actual database that comprises the Windows Domain information or both.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All of the information that makes up an Active Directory is stored in an X.500 compatible database, typically replicated between domain controllers to ensure high availability and fault tolerance. X.500 is a set of network directory standards. A Windows Domain is a kind of network directory, hence the name Active Directory for its replacement. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Active Directory introduced one important new type of object and concept, Forests. An Active Directory Forest is kind of a list of lists, meaning, it is a collection of Domains that are all related to each other for both security and management purposes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://medium.com/@yoursproductly/understanding-active-directory-4e7508372b80"&gt;Here&lt;/a&gt; is more details about Active Directory.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Lightweight Directory Access Protocol&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As mentioned above, &lt;a href="https://en.wikipedia.org/wiki/X.500"&gt;X.500&lt;/a&gt; is a series of computer networking standards covering electronic directory services. ISO incorporated it into OSI suite of protocols. The protocols defined by X.500 include DAP (Directory Access Protocol), DSP (Directory System Protocol), DISP (Directory Information Shadowing Protocol) and DOP (Directory Operational Bindings Management Protocol). DAP is a heavyweight protocol that operates over a full OSI protocol stack and requires a significant amount of computing resources. LDAP (Lightweight Directory Access Protocol), as its alternative, is designed to operate over TCP/IP and provides most of the functionality of DAP at a much lower cost.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Technically speaking, LDAP is a directory access protocol to an X.500 directory service. In early days, the typical architecture involves a proxy. Client connects to the proxy in LDAP and the proxy connects to X.500 server in DAP. Nowadays, it is common that LDAP is directly implemented in X.500 servers. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because &lt;a href="https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol"&gt;LDAP &lt;/a&gt;is based on a simpler subset of the standards contained within the X.500 standard, LDAP was sometimes called X.500-lite. While DAP and the other X.500 protocols can now use the TCP/IP networking stack, LDAP remains a popular directory access protocol.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/01/several-ways-to-ensure-high-availability/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;High Availability and Load Balancer&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/03/lightweight-directory-access-protocol-ldap/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Lightweight Directory Access Protocol (LDAP)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Nginx as a reverse proxy for Nifi web UI and Kibana</title><link>https://www.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/</link><pubDate>Thu, 16 Jan 2020 22:22:51 -0400</pubDate><guid>https://www.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/</guid><description>&lt;p class="wp-block-paragraph"&gt;Nginx can act as a application neutral proxy. One example is to front Nifi. The nifi default configuration provides an HTTP access point, specified in the following entries in nifi.properties:&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;nifi.web.http.host=192.168.133.5&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nifi.web.http.port=8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Nifi can provide secure port by commenting out the lines above and provide 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nifi.web.https.host=192.168.133.5&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nifi.web.https.port=8083&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;However, it does require configuring JKS keystore for Java, as well as authentication. Customers with existing AD servers are likely to require authentication via LDAP. While Nifi does support LDAP integration according to its &lt;a href="https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#ldap_login_identity_provider"&gt;administration guide&lt;/a&gt;. The configuration is quite involving. You need to configure the identity provider, as well as authorizes. I have personally spent a couple days on this without much progress. The information in the logging isn&amp;#8217;t to the point. Restarting nifi also is a long process, making it painful to troubleshoot. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I then moved to Nginx (open-source) as an alternative and it is quite enlightening. I already knew that the SSL termination in nginx is super easy to configure. This time I learned that the opensource community even has a support for LDAP integration. Here is a diagram of how it works:&lt;/p&gt;&#10;&lt;p class="has-text-align-center 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="542px" viewBox="-0.5 -0.5 542 272" style="max-width:100%;max-height:272px;"&gt;&lt;defs&gt;&lt;linearGradient x1="0%" y1="0%" x2="0%" y2="100%" id="mx-gradient-ffffff-1-33ebff-1-s-0"&gt;&lt;stop offset="0%" style="stop-color:#FFFFFF"&gt;&lt;/stop&gt;&lt;stop offset="100%" style="stop-color:#33EBFF"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="540" height="270" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="10" y="75" width="60" height="30" rx="4.5" ry="4.5" 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: 58px; height: 1px; padding-top: 90px; margin-left: 12px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Client&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="40" y="94" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Client&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="169.25" y="230" width="118.5" height="30" rx="4.5" ry="4.5" 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: 116px; height: 1px; padding-top: 245px; margin-left: 171px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Active Directory&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="229" y="249" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Active Directory&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="430" y="10" width="85" height="70" fill="#ffffff" stroke="#000000" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 83px; height: 1px; padding-top: 17px; margin-left: 432px;"&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;Container1&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="473" y="29" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Container1&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="445" y="40" width="55" height="30" rx="4.5" ry="4.5" 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: 53px; height: 1px; padding-top: 55px; margin-left: 447px;"&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;Nifi&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="473" y="59" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Nifi&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="430" y="120" width="85" height="70" fill="#ffffff" stroke="#000000" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 83px; height: 1px; padding-top: 127px; margin-left: 432px;"&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;Container2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="473" y="139" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Container2&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="445" y="150" width="55" height="30" rx="4.5" ry="4.5" 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: 53px; height: 1px; padding-top: 165px; margin-left: 447px;"&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;Nifi&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="473" y="169" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Nifi&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="140" y="20" width="177" height="170" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="140" y="20" width="177" height="170" fill="#ffffff" stroke="#000000" stroke-dasharray="3 3" 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 flex-start; justify-content: unsafe center; width: 175px; height: 1px; padding-top: 27px; margin-left: 142px;"&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; text-decoration: underline; white-space: normal; word-wrap: normal; "&gt;Container&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="229" y="39" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" text-decoration="underline"&gt;Container&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="155" y="50" width="144" height="60" rx="9" ry="9" 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 flex-start; justify-content: unsafe center; width: 142px; height: 1px; padding-top: 57px; margin-left: 157px;"&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; text-decoration: underline; white-space: normal; word-wrap: normal; "&gt;Nginx process&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="227" y="69" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" text-decoration="underline"&gt;Nginx process&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="165" y="76" width="120" height="28" rx="4.2" ry="4.2" fill="url(#mx-gradient-ffffff-1-33ebff-1-s-0)" 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 flex-start; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 83px; margin-left: 167px;"&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; text-decoration: underline; white-space: normal; word-wrap: normal; "&gt;http_auth_request&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="225" y="95" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle" text-decoration="underline"&gt;http_auth_request&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="155" y="150" width="145" 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: 143px; height: 1px; padding-top: 165px; margin-left: 157px;"&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;ldap-auth daemon&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="228" y="169" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;ldap-auth daemon&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 228 104.81 L 227.57 143.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 227.51 148.88 L 224.09 141.84 L 227.57 143.63 L 231.09 141.92 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 125px; margin-left: 228px;"&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; background-color: #ffffff; white-space: nowrap; "&gt;http&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="228" y="129" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;http&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 285 83 L 438.73 56.1" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 443.9 55.19 L 437.61 59.85 L 438.73 56.1 L 436.4 52.95 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 55px; margin-left: 370px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #FFFFCC; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;d&lt;font color="#000000"&gt;http&lt;/font&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="370" y="58" fill="#FFFFCC" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;dhttp&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 285 97 L 439.14 162.51" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 443.97 164.56 L 436.16 165.05 L 439.14 162.51 L 438.9 158.6 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 120px; margin-left: 370px;"&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: #FFFFCC; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;&lt;font color="#000000"&gt;http&lt;/font&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="370" y="124" fill="#FFFFCC" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;http&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 228.37 223.63 L 227.63 186.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 228.48 228.88 L 224.84 221.95 L 228.37 223.63 L 231.84 221.81 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 227.52 181.12 L 231.16 188.05 L 227.63 186.37 L 224.16 188.19 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 205px; margin-left: 228px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;LDAP&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="228" y="208" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;LDAP&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 76.37 90 L 158.63 90" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 71.12 90 L 78.12 86.5 L 76.37 90 L 78.12 93.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 163.88 90 L 156.88 93.5 L 158.63 90 L 156.88 86.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 90px; margin-left: 106px;"&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; background-color: #ffffff; white-space: nowrap; "&gt;https&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="106" y="94" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;https&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This approach is outlined in a &lt;a href="https://www.nginx.com/blog/nginx-plus-authenticate-users/"&gt;blog post&lt;/a&gt; on &lt;a href="https://www.nginx.com/"&gt;Nginx &lt;/a&gt;website. The &lt;a href="https://github.com/nginxinc/nginx-ldap-auth"&gt;ldap-auth daemon&lt;/a&gt; is implemented in Python can can be wrapped up as a systemd service. Once a client sends a request in https, security layer is terminated in nginx, and an authentication request in http is sent to the ldap-auth daemon, which proxies converts the request into LDAP searches and proxies it over to customer&amp;#8217;s Active Directory server, for authentication. Once authenticated, the http request can make to one of the backend container or server which hosts Nifi. Below is an example of how this can be configure on RedHat.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Install python3 and python-ldap&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RedHat may have both python2 and python3 pre-installed, python2 being the default. We do not want to change the default because other applications such as yum still depends on python2 as of early 2020.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum -y install python3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum -y install gcc python3-devel openldap-devel&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip3 install python-ldap&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Once python3 is installed, pip3 will be available and we use that to install python-ldap. This is a module in Python3 that will be used by the script that act as ldap daemon.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Configure ldap-auth daemon as systemd service&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the github project for &lt;a href="https://github.com/nginxinc/nginx-ldap-auth"&gt;ldap-auth&lt;/a&gt;, download nginx-ldap-auth-daemon.py to local location such as /usr/bin, then we create &lt;a href="https://github.com/nginxinc/nginx-ldap-auth/blob/master/nginx-ldap-auth-daemon.py"&gt;nginx-ldap-auth.service&lt;/a&gt; in /etc/systemd/system/ with the following content.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Unit]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;LDAP authentication helper &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; Nginx&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After&lt;span style="color:#f92672"&gt;=&lt;/span&gt;network&lt;span style="color:#f92672"&gt;.&lt;/span&gt;target network&lt;span style="color:#f92672"&gt;-&lt;/span&gt;online&lt;span style="color:#f92672"&gt;.&lt;/span&gt;target&#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;[Service]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;simple&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;User&lt;span style="color:#f92672"&gt;=&lt;/span&gt;root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Group&lt;span style="color:#f92672"&gt;=&lt;/span&gt;root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WorkingDirectory&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;var&lt;span style="color:#f92672"&gt;/&lt;/span&gt;run&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ExecStart&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;usr&lt;span style="color:#f92672"&gt;/&lt;/span&gt;bin&lt;span style="color:#f92672"&gt;/&lt;/span&gt;python3 &lt;span style="color:#f92672"&gt;/&lt;/span&gt;usr&lt;span style="color:#f92672"&gt;/&lt;/span&gt;bin&lt;span style="color:#f92672"&gt;/&lt;/span&gt;nginx&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ldap&lt;span style="color:#f92672"&gt;-&lt;/span&gt;auth&lt;span style="color:#f92672"&gt;-&lt;/span&gt;daemon&lt;span style="color:#f92672"&gt;.&lt;/span&gt;py&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;KillMode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;process&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;KillSignal&lt;span style="color:#f92672"&gt;=&lt;/span&gt;SIGINT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Restart&lt;span style="color:#f92672"&gt;=&lt;/span&gt;on&lt;span style="color:#f92672"&gt;-&lt;/span&gt;failure&#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;[Install]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WantedBy&lt;span style="color:#f92672"&gt;=&lt;/span&gt;multi&lt;span style="color:#f92672"&gt;-&lt;/span&gt;user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;target&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then, run the following command to load, start and check nginx-ldap-auth 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;systemctl reload-daemon&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl start nginx-ldap-auth&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl status nginx-ldap-auth&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This service will be up and listening to port 8888 for http traffic.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Configure Nginx&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt; Then configure nginx with the following entries in its default.conf file, typically located in /etc/nginx/conf.d.&amp;nbsp; &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-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;upstream nifibackend {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; # default: round robin&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; server container1.nifi.digihunch.com:8080;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; server container2.nifi.digihunch.com:8080;&#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;proxy_cache_path cache/ keys_zone=auth_cache:10m;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# nifi proxy&#10;&lt;/span&gt;&lt;/span&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; listen 8083ssl;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; include /etc/nginx/ssl/default.conf;&#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; location / {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth_request /auth-proxy;&#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; proxy_pass http://nifibackend;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header Host $host:$server_port;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-ProxyScheme https;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-ProxyHost $1;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-ProxyPort 8083;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-ProxyContextPath /;&#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; location /auth-proxy {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; internal;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_pass http://127.0.0.1:8888;&#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; proxy_pass_request_body off;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header Content-Length &amp;#34;&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_cache auth_cache;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_cache_valid 200 10m;&#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; proxy_cache_key &amp;#34;$http_authorization$cookie_nginxauth&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-Ldap-URL &amp;#34;ldaps://ldap.digihunch.com:636&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-Ldap-BaseDN &amp;#34;OU=Corporate User Accounts,DC=digihunch,DC=org&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-Ldap-BindDN &amp;#34;CN=Digi Hunch Service Account,OU=Digi,OU=ServiceAccounts,OU=Digi,OU=Digi Applications,DC=digihunch,DC=org&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-Ldap-BindPass &amp;#34;myownpasswordtricks&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-CookieName &amp;#34;nginxauth&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header Cookie nginxauth=$cookie_nginxauth;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-Ldap_Starttls &amp;#34;true&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxy_set_header X-Ldap-Template &amp;#34;(&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;amp;&lt;/span&gt;(sAMAccountName=%(username)s)(objectClass=organizationalPerson)(memberOf=CN=GH_SYSADMIN,OU=GHCO,OU=Groups,OU=Digi,OU=Digi Applications,DC=digihunch,DC=org))&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We need the full distinguished name of bind user to get this to work. Once configured properly, and user attempts to connect through a browser, Nginx will pop up a prompt for username and password. The username will be plugged into the X-Ldap-Template for further queries. The same HTTP header also allows you to filter by membership that the user is associated with.&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/2019/12/networking-basics-3-of-3-common-network-technologies/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking Basics 3 of 3 – common network protocols and technologies&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/01/several-ways-to-ensure-high-availability/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;High Availability and Load Balancer&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Networking Basics 3 of 3 – common network protocols and technologies</title><link>https://www.digihunch.com/2019/12/networking-basics-3-of-3-common-network-technologies/</link><pubDate>Fri, 20 Dec 2019 10:28:00 -0400</pubDate><guid>https://www.digihunch.com/2019/12/networking-basics-3-of-3-common-network-technologies/</guid><description>&lt;p class="wp-block-paragraph"&gt;The 5 layer TCP/IP model (or its more rigorously defined alternative OSI model) leads to a whole world of network protocols. Understanding these new protocols requires one to map it out agains the network layers (e.g. at Layer 4 whether it is TCP or UDP, etc) .&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VPN is a whole family of technologies with many flavours of implementation. The previous posting covered some basics of the idea, as well as the two common forms (remote access VPN and site-to-site VPN). The VPN implementation protocols vary a lot. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;PPTP(Point-to-Point Tunnelling Protocol) is outdated and less secure&amp;nbsp;&lt;/li&gt;&lt;li&gt;IPSec (Internet Protocol Security)&lt;/li&gt;&lt;li&gt;L2TP (Layer 2 Tunnelling Protocol) replacement of PPTP, more secure, more overhead and slightly slower.&lt;/li&gt;&lt;li&gt;OpenVPN &amp;#8211; very secure, and reliable and supported by communities all over the world.&lt;/li&gt;&lt;li&gt;TLS/SSL and SSH connections may be considered VPN as well.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Phone service protocols&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;VoIP (voice over IP, operating at network layer)&lt;/strong&gt; &amp;#8211; allows one to make and receive phone calls over the network. Communication on the IP network is perceived as less reliable in contrast to the circuit-switched public telephone network because it does not provide a network-based mechanism to ensure that data packets are not lost, and are delivered in sequential order. It is a best-effort network without fundamental Quality of Service (QoS) guarantees. Voice, and all other data, travels in packets over IP networks with fixed maximum capacity. This system may be more prone to data loss in the presence of congestion[a] than traditional circuit switched systems; a circuit switched system of insufficient capacity will refuse new connections while carrying the remainder without impairment, while the quality of real-time data such as telephone conversations on packet-switched networks degrades dramatically. Therefore, VoIP implementations may face problems with latency, packet loss, and jitter.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SIP (session initiation protocol, operating at application layer)&lt;/strong&gt; &amp;#8211; a VOIP signaling protocol responsible for the creation and tearing down of media connections. So it supports all types of media.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;ALG (application layer gateway, aka proxy server)&lt;/strong&gt; &amp;#8211; a software component that manages specific application protocols such as SIP and FTP. An ALG acts as an intermediary between the Internet and an application server that can understand the application protocol. ALG proxies connection to destination on behalf of client. This adds &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/12/image-5.png" alt="" class="wp-image-561" width="505" height="213"/&gt;&lt;figcaption&gt;Application Layer Gateway&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DDNS&lt;/strong&gt; (dynamic domain name service) &amp;#8211; a router service that assigns your device a fixed domain name even though you are using dynamic IP.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAT&lt;/strong&gt; &amp;#8211; another family of technologies, usually implemented in the following&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Port preservation: source port chosen by a client is the same port used by the router&amp;nbsp;&lt;/li&gt;&lt;li&gt;Port forwarding: NAT application redirects a communication request from one address and port number combination to another while the packets are traversing a network gateway, such as a router or firewall. &lt;/li&gt;&lt;li&gt;Port triggering: a dynamic form of the port forwarding model. Generally, port triggering is used when the user needs to use port forwarding to reach multiple local computers. Port are close when they aren&amp;#8217;t in use (more secure) protocol used is UPnP&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DMZ&lt;/strong&gt; &amp;#8211; a physical or logical subnet that contains external facing service to untrusted network (e.g. Internet). The purpose is to add an additional layer of security so an external network can assess what is exposed in DMZ while the rest of network remains firewalled.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;VPN passthrough&lt;/strong&gt; &amp;#8211; a feature that allows any device connected to the router to establish outbound VPN connections. Most modern router already have this built in.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;WAN optimization&lt;/strong&gt; &amp;#8211; a collection of techniques for increasing data transfer efficiencies across wide-area networks.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Deduplication&lt;/li&gt;&lt;li&gt;Compression&lt;/li&gt;&lt;li&gt;Latency optimization&lt;/li&gt;&lt;li&gt;Caching/proxy&lt;/li&gt;&lt;li&gt;Protocol spoofing&lt;/li&gt;&lt;li&gt;Traffic shaping&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Network performance tuning&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Performance tuning in network covers a variety of skills. It is important to understand in which layer the problem occurs. Anything above layer 4 is more likely to be an application issue. For ethernet performance tuning, I found this &lt;a href="https://cromwell-intl.com/open-source/performance-tuning/ethernet.html"&gt;page&lt;/a&gt; and this &lt;a href="https://www.coverfire.com/articles/queueing-in-the-linux-network-stack/"&gt;page&lt;/a&gt; to be helpful in my practices.&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/2019/12/tcp-ip-basics-2-of-3-layer-4-and-common-technologies/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking basics 2 of 3 – Layer 4 and common network configurations&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Nginx as a reverse proxy for Nifi web UI and Kibana&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Networking basics 2 of 3 – Layer 4 and common network configurations</title><link>https://www.digihunch.com/2019/12/tcp-ip-basics-2-of-3-layer-4-and-common-technologies/</link><pubDate>Sat, 07 Dec 2019 23:19:00 -0400</pubDate><guid>https://www.digihunch.com/2019/12/tcp-ip-basics-2-of-3-layer-4-and-common-technologies/</guid><description>&lt;h3 class="wp-block-heading" id="h-transport-layer"&gt;Transport Layer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Transport Layer handles multiplexing &amp;amp; de-multiplexing through ports. Port is more or less a virtual concept. Source port is usually ephemeral. Two dominant protocols are TCP and UDP.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;TCP relies on acknowledgement. TCP control flags are SYN, ACK, FIN, URG, PSH, RST, ECE, CWR. TCP connection is established by 3-way handshake and torn down by 4-way termination.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1223" height="502" src="https://www.digihunch.com/wp-content/uploads/2019/12/image.png" alt="" class="wp-image-515"/&gt;&lt;figcaption class="wp-element-caption"&gt;TCP handshake and termination&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Socket &amp;#8211; the instantiation of an end-point in a potential TCP connection. A socket can be in one of the following states:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;LISTEN: a TCP socket is ready and listening for incoming connections;&lt;/li&gt;&#10;&lt;li&gt;SYN_SENT: a SYNC request has been sent but connection hasn&amp;#8217;t been established yet;&lt;/li&gt;&#10;&lt;li&gt;SYN_RECEIVED: a socket previously in a LISTEN state has received a SYNC request and sent a SYN/ACK back;&lt;/li&gt;&#10;&lt;li&gt;ESTABLISHED: connection is up;&lt;/li&gt;&#10;&lt;li&gt;FIN_WAIT: FIN sent, ACK hasn&amp;#8217;t been received yet;&lt;/li&gt;&#10;&lt;li&gt;CLOSE_WAIT: connection has been closed at the TCP layer but the application that opened the socket hasn&amp;#8217;t release the hold on the socket yet;&lt;/li&gt;&#10;&lt;li&gt;CLOSED: connection fully terminated;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="403" height="306" src="https://www.digihunch.com/wp-content/uploads/2023/01/tcp-format.gif" alt="" class="wp-image-7811"/&gt;&lt;figcaption class="wp-element-caption"&gt;TCP packet format&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;TCP is a connection-oriented protocol&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Transport layer is responsible for re-sending data if data is lost&lt;/li&gt;&#10;&lt;li&gt;Sequence # is important because packet may arrive out of sync but receiver reassemble them in order&lt;/li&gt;&#10;&lt;li&gt;There is a lot of overhead (acknowledgement, establish connection first, tear down connection afterwards)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, UDP is connectionless. A good example is video streaming, where it is okay to lose a few packet along the way, in exchange of bandwidth saving.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Firewall may operate at different layers but it is most commonly used at transport layer, to block traffic based on port.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-application-layer"&gt;Application Layer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is no dominant protocol at this layer. IIS, Nginx and Apache are examples of applications operating at this layer.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-network-configurations"&gt;Basic network configurations&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Standard modern network configuration involves: IP address, subnet mask, gateway and DNS server. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DNS &lt;/strong&gt;&amp;#8211; global and highly distributed network service that resolves domain name into IP address. There are &lt;a href="https://www.golinuxhub.com/2014/01/how-does-dns-query-works-when-you-type.html"&gt;many steps in DNS resolution&lt;/a&gt;. DNS service listens on port 53. Two famous free public DNS servers are 8.8.8.8 and 8.4.4.4. DNS servers have five categories:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Caching name servers: store known domain name lookups in cache. TTL today can be a few hours, much shorter than what it used to be in early days;&lt;/li&gt;&#10;&lt;li&gt;Recursive name servers: perform full DNS resolution request;&lt;/li&gt;&#10;&lt;li&gt;Root name servers;&lt;/li&gt;&#10;&lt;li&gt;TLD name servers;&lt;/li&gt;&#10;&lt;li&gt;Authoritative name servers;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://1.bp.blogspot.com/-JqjgddtqOiw/U6ESNhWETCI/AAAAAAAADXM/JpwsJeSpCg8/s1600/dnsquery.png" alt="" style="width:433px;height:357px"/&gt;&lt;figcaption class="wp-element-caption"&gt;DNS resolution steps&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DNS uses UDP protocol and it can generate a lot of traffic (TCP is impractical. If implemented in TCP, it would have required 44 packet for a DNS query, which is very expensive considering DNS query is just a precursor of the real traffic)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Anycast DNS&lt;/strong&gt; &amp;#8211; any one of a number of DNS servers can respond to DNS queries, and typically the one that is geographically closest will provide the response. This reduces latency, improves uptime for the DNS resolving service and provides protection against DNS flood DDoS attacks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DNS record types&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A record: domain name to IP address. DNS service round robin across multiple A records&lt;/li&gt;&#10;&lt;li&gt;AAAA (quad A) record: domain name to IPv6 address&lt;/li&gt;&#10;&lt;li&gt;CNAME: redirect traffic from one domain to another (e.g. test.com to www.test.com so you can minimize IP references)&lt;/li&gt;&#10;&lt;li&gt;MX record&lt;/li&gt;&#10;&lt;li&gt;SRV record&lt;/li&gt;&#10;&lt;li&gt;TXT record: originally for human consumption, freeform text for configuration purpose.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A FQDN (fully qualified domain name) can have up to 127 domains, but only three in most cases. (i.e. subdomain.domain.topleveldomain)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DNS zones&lt;/strong&gt; &amp;#8211; allow for easier control over multiple levels of a domain. DNS zones are configured in zone files. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Domains vs zones&lt;/strong&gt; &amp;#8211; Domains are broken into zones for which individual DNS servers are responsible. A domain represents the entire set of names/machines that are contained under an organizational domain name. For example, all domain names ending with &amp;#8220;.com&amp;#8221; are part of the &amp;#8220;com&amp;#8221; domain. A &amp;#8220;zone&amp;#8221; is a domain less any sub-domains delegated to other DNS servers. A DNS server could be responsible (authoritative) for all records under the &amp;#8220;xyz.com&amp;#8221; domain, but by defining NS-records for &amp;#8220;abc.xyz.com&amp;#8221;, this part of the domain is delegated to other DNS servers &amp;#8211; and possibly a different company/entity. A zone contains exactly one SOA-record describing the general properties of the zone, and any number of other DNS records. Entire zones can transferred from a primary DNS server to secondary DNS servers through Zone Transfers. A domain administrator would be responsible for creating zones, and delegating responsibility for these zones to an administrator and DNS server.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="597" height="529" src="https://www.digihunch.com/wp-content/uploads/2024/07/DNS-Zones-Illustration.jpg" alt="" class="wp-image-11546" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/DNS-Zones-Illustration.jpg 597w, https://www.digihunch.com/wp-content/uploads/2024/07/DNS-Zones-Illustration-300x266.jpg 300w" sizes="auto, (max-width: 597px) 100vw, 597px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Reverse DNS lookup&lt;/strong&gt; &amp;#8211; query for FQDN by IP. This is commonly used by email servers where anti-spam mechanism on the receiver needs to validate that sender&amp;#8217;s IP is associated with a domain as claimed. This is also used in logging application to convert IP into human-readable domains in the log data. Reverse DNS lookups query DNS server for a PTR (pointer reserve record). If the server does not have a PTR record, it cannot resolve a reverse lookup.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DHCP&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DHCP operates at application layer and helps you to configure&amp;nbsp; IP automatically with a lease, through automatic allocation, or fixed allocation based on MAC, etc. DHCP process involves address allocation, renewal, and release. Address allocation takes four steps:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Client sends a broadcast to discover DHCP server;&lt;/li&gt;&#10;&lt;li&gt;DHCP server broadcast a DHCP offer;&lt;/li&gt;&#10;&lt;li&gt;Client requests IP address from the DHCP server;&lt;/li&gt;&#10;&lt;li&gt;Server acknowledged the DHCP request;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is an illustration of DHCP address allocation.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="713" height="527" src="https://www.digihunch.com/wp-content/uploads/2024/07/DHCP-address-allocation.gif" alt="" class="wp-image-11548"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DHCP can also be used to set NTP address.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAT&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Network Address Translation (NAT), can be implemented in many different ways in different OS. Essentially, it is a technology that allows a gateway, usually a router or firewall, to rewrite the source IP of an outgoing IP datagram while retaining the original IP in order to rewrite it into the response.&amp;nbsp; Two categories of NAT are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Basic NAT: provides a one-to-one translation of IP addresses, aka one-to-one NAT. Basic NATs can be used to interconnect two IP networks that have incompatible addressing.&lt;/li&gt;&#10;&lt;li&gt;One-to-many NAT: maps multiple private hosts to one publicly exposed IP address, aka IP masquerading. This can be a security measure so that no external host can establish to your computer without knowing your actual IP. Source port conflict can be managed in two ways:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;port preservation: When making outgoing connection, NAT preserves the ephemeral port number used by internal client that initiates the connection; if two clients happen to use the same ephemeral port, then NAT picks a random port to initiate outgoing TCP connection;&lt;/li&gt;&#10;&lt;li&gt;port forwarding (port mapping): Forward traffic to certain destination based on the port of incoming request that NAT receives. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Internet_port_forwarding.png/800px-Internet_port_forwarding.png" alt="File:Internet port forwarding.png" style="width:521px;height:293px"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Proxy&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Proxy refers to a concept rather than a specific implementation. It exists on almost every layer in the network model, and act on behalf of a client in order to access other service. For example, &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Web proxy used to be used to cache web traffic data in slow Internet but it is not necessary any more because 1) there is not much speed benefit; 2) website today is much more dynamic. &lt;/li&gt;&#10;&lt;li&gt;Reverse proxy is a popular architecture of web server, such as Nginx, to act as a front end of web servers, as well as point of decryption so web servers are free to just serve the content.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;VPN&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VPN &amp;#8211; allows for extension of a private or local network to host that might not be on that local private network by using encrypted tunnel. There are many flavours of implementation for many purposes. It is a general concept rather than a specific protocol (just like NAT). VPN client provisions the computer with a &lt;span style="text-decoration: underline;"&gt;virtual interface&lt;/span&gt; with an IP that matches the address space of the private network, and establish a VPN tunnel to it. Most VPNs work by using the payload section of transport layer to carry an encrypted payload that actually contains an entire second set of packets: the network, the transport and the application layers of a packet intended to traverse a network. Basically, this payload is carried to the VPN&amp;#8217;s endpoint where all the other layers are stripped away and discarded. Then, the payload is unencrypted, leaving the VPN server with the top three layers of a new packet. This gets encapsulated with the proper datalink layer information and sent across the network. This process is completed in the inverse in the opposite direction. VPN usually requires strict authentication procedures and encryption. VPN can also be used to establish site-to-site connection (aka point-to-point VPN) where individual user doesn&amp;#8217;t have to establish connections on their own. Both sites needs specialized hardware to achieve this. Site-to-site VPN is a good alternative to WAN when two site don&amp;#8217;t need to transfer large amount of data for very fast speed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;WAN&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wide Area Network &amp;#8211; act like a single network, but span across multiple physical locations, it requires that you contract the link across the internet with ISP. ISP handles data link from one site to another.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="586" height="160" src="https://www.digihunch.com/wp-content/uploads/2019/12/image-4.png" alt="" class="wp-image-533"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Wireless Network&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless protocol (802.11 family) defines how Wifi operates at physical and data link layer. Wifi networks operates on 2.4GHz and 5GHz frequency bands. Wireless frame is fairly different from Ethernet frame due to the nature of wireless transmission.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="http://static1.squarespace.com/static/55e6d56ee4b0139f372acc16/t/57597dd259827ef6e49d01cf/1465482710010/?format=1500w" alt="Image result for wireless frame&amp;quot;"/&gt;&lt;figcaption class="wp-element-caption"&gt;Wireless frame&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless access point is a device that bridges the wireless and wired portions of a network. A single wired network might have many wireless access points to cover a large area.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless network can be configured in a few main ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Ad-hoc network &amp;#8211; nodes all directly speak to each other. No supporting infrastructure is needed but not most common. It can be powerful tool during disasters.&lt;/li&gt;&#10;&lt;li&gt;Wireless LAN (WLAN) &amp;#8211; one or more access points act as abridge between wireless and wired network. This is the most common type in business world where the wired LAN provides link to the Internet.&lt;/li&gt;&#10;&lt;li&gt;Mesh networks &amp;#8211; a hybrid of the two above&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Wireless Security&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless transmission is across the air so encryption is more important. The number of bit in the encryption key corresponds to how secure the encryption is.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;WEP (encryption technology) provides low level of privacy (40-bit encryption) and it is not preferred today;&lt;/li&gt;&#10;&lt;li&gt;WPA provides 128-bit key encryption; &lt;/li&gt;&#10;&lt;li&gt;WPA2 provides 256-bit key encryption and is most common today.&lt;/li&gt;&#10;&lt;li&gt;MAC filtering also help security in wireless&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Basic networking troubleshooting&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ICMP ping to test general quality of connection;&lt;/li&gt;&#10;&lt;li&gt;traceroute discovers the path between two nodes and give you the information along the way;&lt;/li&gt;&#10;&lt;li&gt;netcat checks port and host address (telnet is retiring);&lt;/li&gt;&#10;&lt;li&gt;nslookup: very powerful in interactive mode for resolution tools;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;IPv6 &lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPv4 address running out in November 2019 is a major crisis. IPv6 becomes more critical to implement for many organizations. IPv6 address is very long with numbers. Numbers represents a natural way that computer thinks and operates, but not for human. So IPv6 comes with two rules to shorten the address:&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="289" height="159" src="https://www.digihunch.com/wp-content/uploads/2019/12/image-2.png" alt="" class="wp-image-520"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;You may remove any leading 0;&lt;/li&gt;&#10;&lt;li&gt;Any number of consecutive groups can be replaced with two colons;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPv6 header looks like below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="742" height="458" src="https://www.digihunch.com/wp-content/uploads/2019/12/image-3.png" alt="" class="wp-image-521" style="width:523px;height:323px"/&gt;&lt;figcaption class="wp-element-caption"&gt;IPv6 header&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Coexistence &amp;#8211; the optimal approach for existing networks is to focus not on transition but on coexistence. Coexistence may live a long period with these phases: 1) Turn on IPv6 routing in their existing IPv4 networks and start using it; 2) Contract IPv6 service with their upstream, peer, and downstream neighbours; 3) Use the IPv6 protocol in addition to IPv4 in their applications and services both on server equipment and on their clients; 4) turn off IPv4 at some point when it is no longer a business requirement.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/11/storage-nitty-gritty-5-of-5-replication/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 5 of 5 – Replication&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/12/networking-basics-3-of-3-common-network-technologies/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking Basics 3 of 3 – common network protocols and technologies&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Clean up Git repository</title><link>https://www.digihunch.com/2019/10/clean-up-your-git-repository/</link><pubDate>Sat, 26 Oct 2019 20:33:00 -0400</pubDate><guid>https://www.digihunch.com/2019/10/clean-up-your-git-repository/</guid><description>&lt;p class="wp-block-paragraph"&gt;A BitBucket repo has a hard limit of 2GB in size, and soft limit of 1GB. This is&amp;nbsp;&lt;a href="https://confluence.atlassian.com/bitbucket/what-kind-of-limits-do-you-have-on-repository-file-size-273877699.html"&gt;not expandable&lt;/a&gt;&amp;nbsp;as per&amp;nbsp;Bitbucket and contributors will start receiving warnings once soft limit is reached. We can tell the usage of a repo from the landing page of the repo in BitBucket.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2020/03/git.png" alt="" class="wp-image-694" width="202" height="300"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Git is a distributed version control system for source code management, which implies the followings:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;It is intended for source code, or configuration code; but not for storing build artifacts, or installers;&lt;/li&gt;&#10;&lt;li&gt;Git remembers every single commit, including the ones associated with large files;&lt;/li&gt;&#10;&lt;li&gt;Even a contributor deletes a large file (&amp;#8220;git rm filename&amp;#8221;) after commit, the large file is only removed from the HEAD. The historical commit still stores the file. After all, the whole point of version control is to survive crazy deletion.&lt;/li&gt;&#10;&lt;li&gt;distributed means that those large files will be pulled down to contributors laptop (waste everybody&amp;#8217;s space although up to 2G:);&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With all these implications, shrinking the size of a repo isn&amp;#8217;t as straightforward as just removing large files from current commit. We&amp;#8217;d have to&amp;nbsp;rewrite the commit history. Here are the steps we should take once repo size grows over the soft limit.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-clean-up-remote-orphaned-branches"&gt;Clean up remote orphaned branches&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Removing these branches (remotes/origin/branchname) per se does not free up space. It simplifies the branch structure, leaving /remote/origin/HEAD the only branch left to cleanse for the rest of the steps.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# git push origin --delete branchname&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-remove-useless-files-in-current-commit-head"&gt;Remove useless files in current commit (HEAD)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this step we remove useless files in current commit. Again we should not expect much space freed because all file committed previously, even deleted, are still stored. They are just now showing up in the working directory. For this step, we can create a separate local dir 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p /Users/digihunch/repo-cleanup&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd /Users/digihunch/repo-cleanup&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now within the new directory, we create a bare repo and then the full repo:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone --mirror https://gh@bitbucket.org/digihunch/source.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://gh@bitbucket.org/digihunch/source.git&#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 dive into the full repo and identify the large files:&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;find . -type f -size +1000k -exec ls -lh {} \; |awk &amp;#39;{print $9&amp;#34;:&amp;#34; $5}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can run &amp;#8220;git rm &amp;#8221; against the files identified as too large or deletable. Then commit and push to remote repo. This removes large files from current commit.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-remove-large-file-and-the-relevant-commits-in-the-history"&gt;Remove large file and the relevant commits in the history&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As previously mentioned, we have to re-write the history so history forget about the large files. After this step, the historical commits that large files are associated with will all be deleted. Compare the two charts below to understand what the effect is:&lt;/p&gt;&#10;&lt;figure class="wp-block-gallery aligncenter has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex"&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1206" height="1038" data-id="695" src="https://www.digihunch.com/wp-content/uploads/2020/03/git1.png" alt="" class="wp-image-695"/&gt;&lt;/figure&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1234" height="852" data-id="700" src="https://www.digihunch.com/wp-content/uploads/2020/03/git2-1.png" alt="" class="wp-image-700"/&gt;&lt;/figure&gt;&#10;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use the bare repo created in the last step, with &amp;#8220;git filter-branch&amp;#8221; tool to cleanse the branch tree. Some advocate as a faster third party tool&amp;nbsp;&lt;a href="https://rtyley.github.io/bfg-repo-cleaner/"&gt;BFG Repo-Cleaner&lt;/a&gt;&amp;nbsp;as a faster, third-party alternative but I usually lean towards native tool.&amp;nbsp;This&amp;nbsp;&lt;a href="https://www.nicoespeon.com/en/2014/04/clean-git-repo-like-a-boss/"&gt;article&lt;/a&gt;&amp;nbsp;explains the command switches.&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;git filter-branch -f --tree-filter &amp;#34;rm -rf \large_file.zip&amp;#34; --prune-empty -- --all&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After this steps the repo should be cleansed. According to this&amp;nbsp;&lt;a href="https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html"&gt;guide&lt;/a&gt;&amp;nbsp;from BitBucket, we still need to contact their support to run a garbage collection for us in order to see the size change. It even takes time for the size to be reflected after garbage collection. This&amp;nbsp;&lt;a href="https://www.saschawillems.de/blog/2017/09/10/how-to-shrink-down-a-github-repository/"&gt;reference&lt;/a&gt;&amp;nbsp;also does great job explaining what we need to do.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-other-contributors-re-sync-history"&gt;Other contributors re-sync history&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to understand that the step above modifies history. Although the commit hash did not change, they are assigned with different commit-ids and you can tell from the commit history where it displays former commit id.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This activity only affects remote repository. Each contributor&amp;#8217;s local repository still stores the old commits and should be sync&amp;#8217;ed with the remote origin by deleting the entire repo and run &amp;#8220;git clone&amp;#8221; again. Although not welcomed by every individual contributors, but it is a necessary evil and better approached with explicit instruction.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because this activity takes higher risks, changes each commit, involves vendor support and requires activities by each contributor, the support team should focus on preventing this from happening instead of fixing it.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-pre-commit-hook-as-a-preventive-measure"&gt;Configure pre-commit hook as a preventive measure&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As we have more Ansible tasks related, working directory becomes complicated and sometimes contributors accidentally committed large unwanted files (and pushed into the remote repo).&amp;nbsp;Down the road, the best practice is to prevent contributors from committing junks.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The best spot to detect this should be a pre-receive hook on the server side, which is only available with self-hosted Bitbucket Server. Unfortunately, this is not a viable option for&amp;nbsp;&lt;a href="https://community.atlassian.com/t5/Bitbucket-questions/Does-Bitbucket-Cloud-support-git-pre-receive-hooks/qaq-p/950235"&gt;Bitbucket cloud&lt;/a&gt;. Our best bet is client-side pre-commit hook, in which a script&amp;nbsp;performs size check when contributors run &amp;#8220;git commit&amp;#8221;. The purpose is to fail the commit if total file size is over the limit (20M), and the hook itself should be version controlled as well. Compared to (server side) pre-receive hook, the drawback of (client side) pre-commit hook is it requires initial client configuration. The upside is it captures large files before commit.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This hook can be a shell script as simple as 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;commitsizelimit&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stagedfilelist&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git diff --name-only --cached&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stagedfilecnt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$stagedfilelist&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;| sed &lt;span style="color:#e6db74"&gt;&amp;#39;/^\s*$/d&amp;#39;&lt;/span&gt; |wc -l&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; $stagedfilecnt -gt &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; totalcommitsize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;du -cm $stagedfilelist | tail -1 | cut -f 1&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Redirect output to stderr.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exec 1&amp;gt;&amp;amp;&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; $totalcommitsize &amp;gt; $commitsizelimit &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Warning: Total size of all files in staging area is &amp;#34;&lt;/span&gt;$totalcommitsize&lt;span style="color:#e6db74"&gt;&amp;#34;MB, exceeding the limit of &amp;#34;&lt;/span&gt;$commitsizelimit&lt;span style="color:#e6db74"&gt;&amp;#34;MB.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34; To list files by size, run &amp;#39;du -ch \$(git diff --name-only --cached)&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34; To drop large ones from staging area with &amp;#39;git rm -f filename&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34; To bypass this limit, use &amp;#39;git commit --no-verify&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the repo we will have a .githook directory to store hooks (e.g. ~/source/.githooks/pre-commit) and point to the hooks directory using the following 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config core.hooksPath .githooks&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/10/storage-nitty-gritty-4-of-5-backup-and-archive-solutions/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 4 of 5 – Backup and Archive Solutions&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/11/networking-basics-layer-1-and-layer-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking Basics 1 of 3 – Layer 1 through Layer 3&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Storage Nitty-Gritty 4 of 5 – Backup and Archive Solutions</title><link>https://www.digihunch.com/2019/10/storage-nitty-gritty-4-of-5-backup-and-archive-solutions/</link><pubDate>Mon, 14 Oct 2019 19:42:00 -0400</pubDate><guid>https://www.digihunch.com/2019/10/storage-nitty-gritty-4-of-5-backup-and-archive-solutions/</guid><description>&lt;h4 class="wp-block-heading"&gt;Business Continuity&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Information Availability IA&lt;/strong&gt; = MTBF/(MTBF+MTTR), where&lt;br&gt;* MTBF (Mean Time Between Failure) &amp;#8211; average time available for a system or component to perform its normal operations between failures.&lt;br&gt;* MTTR (Mean Time to Repair) &amp;#8211; the average time required to repair a failed component.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Disaster Recovery&lt;/strong&gt; &amp;#8211; the coordinated process of restoring systems, data, and the infrastructure required to support ongoing business operations after a disaster occurs. It is the process of restoring a previous copy of the data and applying logs or other necessary processes to that copy to bring it to a known point of consistency.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Recovery-Point Objective (RPO)&lt;/strong&gt; &amp;#8211; the point in time to which systems&lt;br&gt; and data must be recovered after an outage. It defi nes the amount&lt;br&gt; of data loss that a business can endure. A large RPO signifi es high tolerance&lt;br&gt; to information loss in a business.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Recovery-Time Ojbective (RTO)&lt;/strong&gt; &amp;#8211; The time within which systems and applications must be recovered after an outage. It defi nes the amount of downtime that a business can endure and survive. Businesses can optimize disaster recovery plans after defi ning the RTO for a given system.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="557" height="191" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-55.png" alt="" class="wp-image-401"/&gt;&lt;figcaption&gt;Strategies to meet RTO and RPO&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Data Vault&lt;/strong&gt;: a repository at a remote site where data can be periodically or continuously copied so a copy is always available in that site.&lt;br&gt;&lt;strong&gt;Hot site&lt;/strong&gt;: A backup site running all the time.&lt;br&gt;&lt;strong&gt;Cold site&lt;/strong&gt;: A backup site with minimum infrastructure, to be activated for operation in the event of disaster.&lt;br&gt;&lt;strong&gt;Server Clustering&lt;/strong&gt;: a group of servers and relevant resources coupcled to operate as a single syste. Clusters can ensure high availability and load balancing.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Single Point of Failure&lt;/strong&gt; &amp;#8211; failure of a component that can terminate the availability of the entire system or IT service. To mitigate single point of failure, systems are designed with redundancy. This includes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;redundant HBA on server &lt;/li&gt;&lt;li&gt;NIC teaming&lt;/li&gt;&lt;li&gt;redundant switch&lt;/li&gt;&lt;li&gt;multiple storage array ports&lt;/li&gt;&lt;li&gt;RAID and hot spare configuration&lt;/li&gt;&lt;li&gt;Redundant storage array&lt;/li&gt;&lt;li&gt;server clustering (e.g. clustered servers exchange heartbeat to inform each other about their health. If one of the servers fails, other server can take up the workload.&lt;/li&gt;&lt;li&gt;VM Fault Tolerance&lt;/li&gt;&lt;li&gt;Multipathing software: If one path fails, I/O does not reroute unless the system recognizes that it has an alternative path. Multipathing software provides the functionality to recognize and utilize alternative I/O paths to data. Multipathing software also managees the load balancing by distributing I/Os to all available, active paths.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Backup &lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Backup &lt;/strong&gt;is an additional copy of production data created and retained for the sole purpose of recovering lost or corrupted data. Backup are typically performed for the following purposes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Disaster recovery&lt;/strong&gt;. e.g. the backup copies are used for restoring data at an alternate site, when the primary site is incapacitated due to disaster.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Operational recovery&lt;/strong&gt;. e.g. accidental deletion, file corruption&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Archival&lt;/strong&gt;. e.g. data is not changed or accessed any more.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Common considerations for backup includes: time interval between two backups (to meet RPO), retention period, media type (to meet RTO), granularity, compression and deduplication&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Backup Granularity&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Full backup&lt;/strong&gt;: backup of the complete data on the production volumes.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Incremental backup&lt;/strong&gt;: copies the data that has changed since the last full or incremental backup, whichever occurred more recently.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Cumulative backup&lt;/strong&gt;: copies the data that has changed since the last full backup.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Backup Methods&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;hot backup/online backup&lt;/strong&gt;: backup is completed while application is up and running;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;cold backup/offline backup&lt;/strong&gt;: backup is completed while the application is shutdown for the backup window.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The hot backup of online production data is challenging because data is actively used and changed. If a file is open, it is normally not backed up during the backup process. In such situations, an open file agent is required to back up the open file. These agents interact directly with the operating system or application and enable the creation of consistent copies of open files. In database environments, To ensure a consistent database backup, all files need to be backed up in the same state. That does not necessarily mean that all files need to be backed up at the same time, but they all must be synchronized so that the database can be restored with consistency. The disadvantage associated with a hot backup is that the agents usually affect the overall application performance. If this is not acceptable, PIT (point-in-time) copy method can be utilized to create a PIT copy from the production volume and use it as the source for the backup. PIT copy method can reduce impact on production volume.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Typical Backup Architecture &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-58.png" alt="" class="wp-image-404" width="353" height="302"/&gt;&lt;figcaption&gt;Typical Backup Architecture&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="555" height="300" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-59.png" alt="" class="wp-image-405"/&gt;&lt;figcaption&gt;Typical Backup steps&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="554" height="296" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-60.png" alt="" class="wp-image-406"/&gt;&lt;figcaption&gt;Typical Restore steps&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Backup topologies&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;Direct-attached backup:&lt;/strong&gt; the storage node is configured on a backup client, and the backup device is attached directly to the client;&lt;br&gt;&lt;strong&gt;LAN-based backup&lt;/strong&gt;: the clients, backup server, storage node, and backup device are connected to the LAN;&lt;br&gt;&lt;strong&gt;SAN-based backup (LAN-free):&lt;/strong&gt; The SAN-based backup topology is the most appropriate solution when a backup device needs to be shared among clients;&lt;br&gt;&lt;strong&gt;Mixed topology:&lt;/strong&gt; mix of LAN-based and SAN-based topologies;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NDMP protocol &lt;/strong&gt;is for backup in NAS environment&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Backup media&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Tape&lt;/strong&gt;: for long-term offsite storage due to low cost. data access is sequential which implies slowness for both backup and restore. Tapes are susceptible to wear and tear.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Disk&lt;/strong&gt;: fast backup and retrieve to improve RPT and RTO. No offsite capability.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Virtual Tape&lt;/strong&gt;: virtual taps are disk drives emulated and presented as tapes to the backup software. VTL (virtual tape library) has the same components as that of a physical tape library.&lt;br&gt; Fig 10-18&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Data deduplication&lt;/strong&gt; &amp;#8211; identify and eliminate redundant data to reduce backup window and size. Common data deduplication methods:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;file-level deduplication&lt;/strong&gt; (aka. single-instance storage) detects and removes redundant copies of identical files. It enables storing only one copy of the file; the subsequent copies are replaced with a pointer that points to the original file.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;subfile deduplication&lt;/strong&gt; breaks file into smaller chunks and then uses a specialized althorithm to detect redundant data within and across the file. This eliminates duplicate data across files. This has two forms:&lt;br&gt; * &lt;strong&gt;fixed-length block deduplication&lt;/strong&gt; &amp;#8211; divides the files into fi xed length blocks and uses a hash algorithm to fi nd the duplicate data. &lt;br&gt; *&lt;strong&gt; variable-length segment deduplication&lt;/strong&gt; &amp;#8211; if there is a change in the segment, the boundary for only that segment is adjusted, leaving the remaining segments unchanged.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Data deduplication implementation&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;source-based data deduplication&lt;/strong&gt; &amp;#8211; eliminates redundant data at the source before it&lt;br&gt; transmits to the backup device. This requires less bandwidth and shortens backup window. It increases the overhead on the backup client and could impact the performance of the backup and application running on the client.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;target-based data deduplication&lt;/strong&gt; &amp;#8211; deduplication occurs at the backup device, which offloads the backup client from the deduplication process. This takes two forms:&lt;/li&gt;&lt;li&gt;&lt;strong&gt;inline deduplication&lt;/strong&gt; &amp;#8211; performs deduplication on the backup data before it is stored on the backup device. this reduces storage need, but introduces time overhead to identify and remove duplication. best for large backup window&lt;/li&gt;&lt;li&gt;&lt;strong&gt;post-process deduplication&lt;/strong&gt; &amp;#8211; enables backup data to be stored on backup device first, and then deduplicate later. This is suitable for tighter backup windows, but requires more storage.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In virtualized environments, backup agent can be installed on the hypervisor, where the VMs appear as a set of files to the agent. VM files can be backed up by performing a file system backup from a hypervisor. For example, Image-based backup operates at hypervisor level and essentially takes a snapshot of the VM. It creates a copy of the guest OS and all the data associated with it (snapshot of VM disk files), including the VM state and application configurations. The backup is saved as a single file (an image) and mounted on a separate server as proxy, which acts as backup client. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="536" height="294" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-62.png" alt="" class="wp-image-409"/&gt;&lt;figcaption&gt;Image Based Backup&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Data archive&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Archive &lt;/strong&gt;&amp;#8211; a repository where fixed content is stored. Fixed content can be data that were changed but will not be changed anymore.&lt;br&gt;&lt;strong&gt;Online archive&lt;/strong&gt;: A storage device directly connected to a host that makes&lt;br&gt; the data immediately accessible.&lt;br&gt;&lt;strong&gt;Nearline archive&lt;/strong&gt;: A storage device connected to a host, but the device where the data is stored must be mounted or loaded to access the data.&lt;br&gt;&lt;strong&gt;Offline archive&lt;/strong&gt;: A storage device not ready to use. Manual intervention is required to connect, mount or load the storage device before data can be accessed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An &lt;strong&gt;archiving agent&lt;/strong&gt; is software installed on application server. The agent is responsible for identify data that can be archvied based on policy. After the data is identified for archiving, the agent sends the data to the archiving server. Then the original data on the application server is replaced with a stub file, which contains the address of the archived data. &lt;br&gt;&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-63.png" alt="" class="wp-image-410" width="373" height="313"/&gt;&lt;figcaption&gt;Archiving Solution Architecture&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;br&gt;An &lt;strong&gt;archiving serve&lt;/strong&gt;r is software installed on a host that enables administrators to configure the policies for archiving data. An archiving storage device stores fixed content.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Related Postings&lt;/h4&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/03/storage-nitty-gritty-1-5/"&gt;Disk and RAID&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/05/storage-nitty-gritty-2-5/"&gt;SAN&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/"&gt;NAS and Object Storage&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/11/storage-nitty-gritty-5-of-5-replication/"&gt;Replication&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/10/personal-vim-cheatsheet/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Personal Vim cheatsheet&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/10/clean-up-your-git-repository/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Clean up Git repository&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Personal Vim cheatsheet</title><link>https://www.digihunch.com/2019/10/personal-vim-cheatsheet/</link><pubDate>Mon, 07 Oct 2019 17:52:00 -0400</pubDate><guid>https://www.digihunch.com/2019/10/personal-vim-cheatsheet/</guid><description>&lt;p class="wp-block-paragraph"&gt;This is my personal cheatsheet as intermediate Vim user so I skipped the ones that I consider basic. All the commands listed are used in command mode for fast editing.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Command execution&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In command mode, use colon to start ex command. Here are some examples of ex commands:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;e: edit file&lt;/li&gt;&lt;li&gt;g: global command&lt;/li&gt;&lt;li&gt;q: quit&lt;/li&gt;&lt;li&gt;w: write&lt;/li&gt;&lt;li&gt;s: substitute&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The commands (g)lobal and (s)ubstitute are heavily used in string manipulation. The rest are summarized here:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;:e!&lt;/td&gt;&lt;td&gt;reload current file discarding all unsaved changes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:e newfile.txt&lt;/td&gt;&lt;td&gt;open file newfile.txt for editing &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:e .&lt;/td&gt;&lt;td&gt;load current directory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:w!&lt;/td&gt;&lt;td&gt;force write (if permission allows)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shift + zz&lt;/td&gt;&lt;td&gt;equivalent to :wq!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shift + zq&lt;/td&gt;&lt;td&gt;equivalent to :q!&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Operator&lt;/h4&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;.&lt;/td&gt;&lt;td&gt;repeat last operation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fx&lt;/td&gt;&lt;td&gt;find next character x on the same line&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Text Editing&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;y for yank(copy), i for inside, a for around, d for delete, w for word, p for paragraph or paste. Examples:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;yyp&lt;/td&gt;&lt;td&gt;copy current line and insert after&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;diw&lt;/td&gt;&lt;td&gt;delete the entire word where the cursor sits in (dw deletes from cursor to end of word; db deletes from cursor to beginning of word)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shift + V&lt;/td&gt;&lt;td&gt;select entire line in visual mode (v selects character in visual mode) &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&amp;gt;&lt;/td&gt;&lt;td&gt;visual mode: indent twice on all selected lines&lt;br&gt;edit mode: indent once for 2 lines&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&amp;lt;&lt;/td&gt;&lt;td&gt;visual mode: outdent three times on all selected lines&lt;br&gt;edit mode: outdent once for 3 lines&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;di&amp;#8221;&lt;/td&gt;&lt;td&gt;delete everything between the double quotes surrounding the cursor (exclusive); use c instead of d to finish the same effect with insert mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;di&amp;gt;&lt;/td&gt;&lt;td&gt;delete everything between &amp;lt; and &amp;gt; surrounding the cursor (exclusive); use (c)hange instead of (d)elete to finish the same effect with insert mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;dit&lt;/td&gt;&lt;td&gt;delete everything between tags. e.g. &amp;lt;xml&amp;gt;contenttodelete&amp;lt;/xml&amp;gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;dip&lt;/td&gt;&lt;td&gt;delete the entire paragraph&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;da&amp;#8217;&lt;/td&gt;&lt;td&gt;delete everything between the single quote surrounding the cursor (inclusive); use c instead of d to finish the same effect with insert mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;da}&lt;/td&gt;&lt;td&gt;delete everything between { and } surrounding the cursor (inclusive); use c instead of d to finish the same effect with insert mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;dt.&lt;/td&gt;&lt;td&gt;delete all characters until the next .&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;i&lt;/td&gt;&lt;td&gt;insert at cursor location&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shift + I&lt;/td&gt;&lt;td&gt;move cursor to first non-blank character of line and start in insert mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;insert at the location next to cursor&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;shift + A&lt;/td&gt;&lt;td&gt;move cursor to last non-blank character of line and start in insert mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;move to beginning of line. ^ moves to first non-blank character in the line. $ moves to the end of line&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note: wherever d is used in this table, c can be used instead for the same effect but switch to editing mode at the end.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;String Manipulation&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The general patterns are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;[range]g/pattern/cmd&lt;/li&gt;&lt;li&gt;[range]s/match/replacement/option&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If range is not specified, it applies to current line only! To specify the whole file, use % range. You may also specify line range such as &amp;#8220;10,20&amp;#8221;. Here are some examples: &lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;:%s/bacon/lettuce&lt;/td&gt;&lt;td&gt;For every line of the file, replace the first occurrence of bacon in each line to lettuce&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:%s/bacon/lettuce/g&lt;/td&gt;&lt;td&gt;For every line of the file, replace all occurrences of bacon to lettuce&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:s/bacon/lettuce&lt;/td&gt;&lt;td&gt;For current line, replace the first occurrence of bacon to lettuce&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:s/bacon/lettuce/gi&lt;/td&gt;&lt;td&gt;For current line, replace all occurrences of bacon to lettuce, case insensitive&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:g/bacon/d&lt;/td&gt;&lt;td&gt;delete all lines that contain pattern &amp;#8216;bacon&amp;#8217;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:g!/lettuce/d&lt;/td&gt;&lt;td&gt;delete all lines that do not contain pattern &amp;#8216;lettice&amp;#8217;; or use :v/lettuce/d instead&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;:g/^\s*$/d&lt;/td&gt;&lt;td&gt;delete all blank lines. \s* represents zero or more white spaces&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Bookmarking&lt;/h4&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ma&lt;/td&gt;&lt;td&gt;mark cursor line as bookmark a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;`a&lt;/td&gt;&lt;td&gt;jump to cursor position at line a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;#8216;a&lt;/td&gt;&lt;td&gt;jump to beginning of line a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;`.&lt;/td&gt;&lt;td&gt;jump to last line where change occurred&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;#8220;&lt;/td&gt;&lt;td&gt;jump back &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Insert Mode&lt;/h4&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Ctrl + N&lt;/td&gt;&lt;td&gt;Auto complete&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Edit and run&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;While tmux and screen can help split screen in Bash, we sometimes need to split a bash screen to run a quick command when we&amp;#8217;re already in vim. This can be done with some simple commands.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;:term&lt;/td&gt;&lt;td&gt;Open up a terminal above vim. You can also spell :ter or :terminal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl+W; Ctrl+W&lt;/td&gt;&lt;td&gt;Press Ctrl+W twice can help you toggle between the terminal and vim buffer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl+D&lt;/td&gt;&lt;td&gt;Close the terminal&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This can be very helpful when you are debugging code and need it run repeatedly. You don&amp;#8217;t need to exit vim just to run a command and come back. Note that while you&amp;#8217;re in terminal, you can&amp;#8217;t use Ctrl+W as a shortcut key to backspace a word. Use Alt + Delete instead.&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/2019/09/cryptographic-concepts-for-busy-it-professionals-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cryptography basics 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/10/storage-nitty-gritty-4-of-5-backup-and-archive-solutions/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 4 of 5 – Backup and Archive Solutions&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>AWS Certified DevOps Engineer Exam Tips</title><link>https://www.digihunch.com/2019/08/aws-certified-devops-engineer-exam-tips/</link><pubDate>Sat, 17 Aug 2019 23:07:59 -0400</pubDate><guid>https://www.digihunch.com/2019/08/aws-certified-devops-engineer-exam-tips/</guid><description>&lt;p class="wp-block-paragraph"&gt;The last 30 days have been exhausting for me. I studied hard on the new AWS Certified DevOps Engineer exam and thank goodness I passed (750 out of 1000 is required). This was the hardest professional certification I ever worked on. The exam was re-launched recently in March 2019 so there is still a shortage of information around the community. I was hoping to share my experience to help demystify this new exam. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I had a somewhat solid background to begin with, having taken the AWS Certified Solution Architect Professional exam (before the 2019 update), and worked quite a bit on CloudFormation, automation and Git. However, I still did not anticipate the exam to be this difficult until I was halfway through and already had the exam and materials paid for.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1469" height="518" src="https://www.digihunch.com/wp-content/uploads/2019/08/image.png" alt="" class="wp-image-127"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Without clear guideline on study material other than the white papers, I first skimmed through the ACloudGuru course, which helped me form a high level sense of exam coverage. Nonetheless it does not cover any topic in-depth and therefore by no means makes an essential part of my study. I checked out &lt;strong&gt;LinuxAcademy course&lt;/strong&gt; and they are much more in-depth for the major topics indeed. I like the course material in Lucid chart. However, the LinuxAcademy course along does not cover everything you need to know.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;What I found extremely helpful is the free training videos from the &lt;a href="https://www.aws.training"&gt;official training website&lt;/a&gt;. I strongly recommend the 7-hour course &lt;strong&gt;Exam Readiness: AWS Certified DevOps Engineer – Professional&lt;/strong&gt;. The instructor did a great job outlining the services and knowledge areas in the assessment . The course also comes with quality sample questions with answers and explanations on what the thinkings are behind the correct answers or why some choices are obviously wrong. I went through these questions twice and feel much better at not only understanding the question, but also understanding the intent of the question.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="833" height="156" src="https://www.digihunch.com/wp-content/uploads/2019/08/image-1.png" alt="" class="wp-image-129"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the Exam Readiness course, other &lt;strong&gt;free introductory courses&lt;/strong&gt; from the official training website are helpful as well especially for those services that you only need to know the basics. Most of those courses are 5 ~ 10 minutes long, with brief but sufficient introduction and a demo session. The other extremely helpful resource is the &lt;strong&gt;official practice questions&lt;/strong&gt;. The practice exam is harder than the actual exam but they closely resemble the actual question style in the exam. Unfortunately, no answer is provided but they made me spend time finding answers across the documentations and blogs. It is worth-noting that the AWS blogs provides plenty of use cases that are covered in the questions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to the real exam, it covers a lot more topics than its predecessor. Many questions are long and confusing. And I wish I could run a diff command to highlight the differences between choices. During exam preparation you really need to train yourself on reading efficiently. I found myself sometimes eyeball through all four choices at the same time, which get my mind scattered.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;During my study, I divide all services into three categories based on the level of familiarity, and here is my list:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Category 1. Know these services very well, in and out:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS ElasticBeanstalk, OpsWorks Stacks, OpsWorks Chef Automate, CloudFormation, CloudWatch, CodeBuild, CodeCommit, CodeDeploy, CodePipeline, CodeStar, Lambda, API Gateway, Config, Trusted Advisor, CloudTrail, Systems Manager, Autoscaling Group in EC2, DynamoDB&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Category 2. Know these services well, but not necessarily down to every single detail:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Amazon Kinesis Firehose, Kinesis Analytics, Kinesis Streams, Step Functions, Elastic Load Balancer, Secrets Manager, Serverless Application Model (SAM), Route53, RDS, Certificate Manager, ElasticSearch, ECS, ECR&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Category 3. Know about these services at a high level, but do not skip any:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS Organization, X-Ray, GuardDuty, Macie, Inspector, Service Catalog, KMS, Batch, Athena, Single-Sign-On, Data LifeCycle Manager, CloudSearch, Health Dashboard, Glue, QuickSight, LightSail&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although I did not mention much about the white papers, I want to highlight their importance again. I would not attempt the exam without reading and understanding the required white papers. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you aspire to take the AWS certified DevOps Engineer exam I hope this helps you a little bit. Good luck.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 3 of 5 – NAS and Object Storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/09/cryptographic-concepts-for-busy-it-professionals-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cryptography basics 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Storage Nitty-Gritty 3 of 5 – NAS and Object Storage</title><link>https://www.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/</link><pubDate>Sat, 13 Jul 2019 23:31:00 -0400</pubDate><guid>https://www.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/</guid><description>&lt;h4 class="wp-block-heading" id="h-nas-network-attached-storage"&gt;&lt;strong&gt;NAS (network attached storage)&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NAS server is dedicated to file-serving. NAS device runs its own specialized operating system that is optimized for file I/O, integrated hardware and software component that meets specific file-service needs, and performs file I/O better than a general-purpose server. NAS device can serve more clients than general-purpose servers and provide the benefit of server consolidation (centralized storage).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NAS uses network and file-sharing protocols to provide access to the file data. These protocols include TCP/IP for data transfer, and Common Internet File System (CIFS) and Network File System (NFS) for network file service.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Network File Sharing&lt;/strong&gt; &amp;#8211; user who creates a file determines the type of access to be given to other user. When multiple users try to access a shared file at the same time, a locking scheme is required to maintain data integrity and, at the same time, make this sharing possible. Examples of file sharing method (FTP, DFS, NFS, CIFS, P2P)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Components of NAS&lt;/strong&gt; &amp;#8211; NAS head (CPU, memory, NIC, optimized OS, ports, applications that supports CIFS/NFS) and Storage Array&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/2019/11/image-42.png" alt="" class="wp-image-379" width="502" height="279"/&gt;&lt;figcaption class="wp-element-caption"&gt;Typical NAS components&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAS I/O operation&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Client packages an I/O request into TCP/IP and forwards it through network stack. NAS head receives this request from network;&lt;/li&gt;&#10;&lt;li&gt;NAS head converts the I/O request into an appropriate physical storage request, which is a block-level I/O, and then performs the operation on the physical storage;&lt;/li&gt;&#10;&lt;li&gt;When NAS head receives data from the storage array, it processes and repackages the data into an appropriate NFS/CIFS response;&lt;/li&gt;&#10;&lt;li&gt;NAS head packages this response into TCP/IP again and forwards it to the client through the network&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1128" height="452" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-43.png" alt="" class="wp-image-380"/&gt;&lt;figcaption class="wp-element-caption"&gt;NAS I/O operation&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAS implementation&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Unified NAS&lt;/strong&gt; &amp;#8211;&amp;nbsp; consolidate NAS-based and SAN-based data access within a unified storage platform and provides a unified management interface for managing both the environments. &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/2019/11/image-44.png" alt="" class="wp-image-381" width="464" height="456"/&gt;&lt;figcaption class="wp-element-caption"&gt;Unified NAS connectivity&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Gateway implementation&lt;/strong&gt; &amp;#8211; similar to unified NAS, the storage is shared with other applications that use block-level I/O. The gateway NAS is more scalable compared to unified NAS because NAS heads and storage arrays can be independently scaled up when required. For example, NAS heads can be added to scale up the NAS device performance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When the storage limit is reached, it can scale up, adding capacity on the SAN, independent of NAS heads. Similar to a unified NAS, a gateway NAS also enables high utilization of storage capacity by sharing it with the SAN environment.&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/2019/11/image-45.png" alt="" class="wp-image-382" width="547" height="366"/&gt;&lt;figcaption class="wp-element-caption"&gt;Gateway NAS connectivity&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Scale-out NAS&lt;/strong&gt; &amp;#8211; enables grouping multiple nodes together to construct a clustered NAS system. A scaled-out NAS provides the capability to scale its resources by simply adding nodes to a clustered NAS architecture. The cluster works as a single NAS device and is managed centrally. Scaled-out NAS creates a single file system that runs on all nodes in the cluster. All information is shared among nodes, so the entire file system is accessible by clients connecting to any node in the cluster. Scale-out NAS stripes data across all nodes in a cluster along with mirror or parity protection. As data is sent from clients to the cluster, the data is divided and allocated to different nodes in parallel. When a client sends a request to read a file, the scale-out NAS retrieves the appropriate blocks from multiple nodes, recombines the blocks into a file, and presents the file to the client. As nodes are added, the file system grows dynamically and data is evenly distributed to every node. Each node added to the cluster increases the aggregate storage, memory, CPU, and network capacity. Hence, cluster performance also increases.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Scale-out NAS use separate internal and external networks for back-end and front-end connectivity, respectively. The internal network offers high throughput and low-latency and uses high-speed networking technology, such as InfiniBand or Gigabit Ethernet.&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/2019/11/image-46.png" alt="" class="wp-image-383" width="475" height="247"/&gt;&lt;figcaption class="wp-element-caption"&gt;Scale-out NAS with dual internal and single external networks&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NFS protocol &lt;/strong&gt;&amp;#8211; originally based on UDP, uses RPC as a method of inter-process communication between two computers. NFS provides a set of RPCS to access remote file system for the following operations:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Searching files and directories&lt;/li&gt;&#10;&lt;li&gt;Opening, reading, writing to and closing a file&lt;/li&gt;&#10;&lt;li&gt;Changing file attributes&lt;/li&gt;&#10;&lt;li&gt;Modifying file links and directories&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NFSv3&lt;/strong&gt; and earlier is stateless protocol. Each call provides a full set of arguments to access files on the server. NFSv3 is most commonly used version, based on UDP or TCP.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NFSv4&lt;/strong&gt; uses TCP and is based on stateful protocol design.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;CIFS&lt;/strong&gt; &amp;#8211; a public, or open variation of SMB protocol. Filenames in CIFS are encoded using unicode characters. It is stateful protocol because the server maintain connection information regarding every connected client. If a network failure or CIFS server failure occurs, the client receives a disconnection notification. If application has embedded intelligence to restore the connection, then the storage solution is fault tolerant. If the embedded intelligence is missing, the user must take steps to reestablish the CIFS connection.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAS Performance&lt;/strong&gt; &amp;#8211; network congestion is one of the most significant sources of latency in NAS environment. Other factors&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;number of hops&lt;/li&gt;&#10;&lt;li&gt;authentication with AD&lt;/li&gt;&#10;&lt;li&gt;Retransmission &amp;#8211; speed and duplex settings on the network devices and NAS heads must match&lt;/li&gt;&#10;&lt;li&gt;Over-utilized routers and switches&lt;/li&gt;&#10;&lt;li&gt;File system lookup and metadata request &amp;#8211; deep directory structure could cause delay.&lt;/li&gt;&#10;&lt;li&gt;&lt;span style="text-decoration: underline;"&gt;Over-utilized NAS devices&lt;/span&gt; &amp;#8211; client accessing multiple files can cause high utilization levels on a NAS device&lt;/li&gt;&#10;&lt;li&gt;&lt;span style="text-decoration: underline;"&gt;Over-utilized clients&lt;/span&gt; &amp;#8211; if a client is busy itself, it requires a longer time to process the request and responses.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="988" height="664" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-47.png" alt="" class="wp-image-385"/&gt;&lt;figcaption class="wp-element-caption"&gt;NAS latency&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NFS server manages privilege and does not require username and password from the client at the time of mounting. CIFS share does require username and password.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Common network optimization practices&lt;/strong&gt; for network contestion:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;span style="text-decoration: underline;"&gt;VLAN&lt;/span&gt; is a logical segment of a switched network or logical grouping of end devices connected to different physical networks. The segmentation or grouping can be done based on business functions, project teams, or applications. VLAN is a Layer 2 (data link layer) construct and works similar to a physical LAN. A network switch can be logically divided among multiple VLANs, enabling better utilization of the switch and reducing overall cost of deploying a network infrastructure.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The broadcast traffic on one VLAN is not transmitted outside that VLAN, which substantially reduces the broadcast overhead, makes bandwidth available for applications, and reduces the network&amp;#8217;s vulnerability to broadcast storms.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;span style="text-decoration: underline;"&gt;MTU&lt;/span&gt; setting determines the size of the largest packet that can be transmitted without data fragmentation. &lt;span style="text-decoration: underline;"&gt;Path maximum transmission&lt;/span&gt; unit discovery is the process of discovering the maximum size of a packet that can be sent across a network without fragmentation. The default MTU setting for an Ethernet interface card is 1,500 bytes. A feature called &lt;span style="text-decoration: underline;"&gt;jumbo frames&lt;/span&gt; sends, receives or transports Ethernet frames with an MTU of more than 1,500 bytes. The most common deployments of jumbo frames have an MTU of 9,000 bytes. However, not all vendors use the same MTU size for jumbo frames. Servers send and receive larger frames more efficiently than smaller ones in heavy network traffic conditions. Jumbo frames ensure increased efficiency because it takes fewer, larger frames to transfer the same amount of data. Larger packets also reduce the amount of raw network bandwidth being consumed for the same amount of payload. Larger frames also help to smooth sudden I/O burst.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;span style="text-decoration: underline;"&gt;TCP window size&lt;/span&gt; is the maximum amount of data that can be sent at any time for a connection. For example, if a pair of hosts is talking over a TCP connection that has a TCP windows size of 64KB, the sender can send only 64KB of data and must then wait for an acknowledgement from the receiver. If the receiver acknowledges that all the data has been received, then the sender is free to send another 64 KB of data. If the sender receives an acknowledgment from the receiver that only the first 32 KB of data has been received, which can happen only if another 32 KB of data is in transit or was lost, the sender can send only another 32 KB of data because the transmission cannot have more than 64 KB of unacknowledged data outstanding.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In theory, the TCP window size should be set to the product of the available bandwidth of the network and the round-trip time of data sent over the network. For example, if a network has a bandwidth of 100 Mbps and the round-trip time is 5 milliseconds, the TCP window should be as follows:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;100 Mb/s x .005 seconds = 524,288 bits or 65,536 bytes&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The size of the TCP window fi eld that controls the fl ow of data is between 2 bytes and 65,535 bytes&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;span style="text-decoration: underline;"&gt;Link aggregation&lt;/span&gt; is the process of combining two or more network interfaces into a logical network interface, enabling higher throughput, load sharing or load balancing, transparent path failover, and scalability. Due to link aggregation, multiple active Ethernet connections to the same switch appear as one link. If a connection or a port in the aggregation is lost, then all the network traffic on that link is redistributed across the remaining active connections.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;File-level virtualization&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;File-level virtualization eliminates the dependencies between the data accessed at the file level and the location where the files are physically stored. Implementation of file-level virtualization is common in NAS or file-server environments. It provides non-disruptive file mobility to optimize storage utilization.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It provides user or application independence from the location where the files are stored. File-level virtualization creates a logical pool of storage, enabling users to use a logical path, rather than a physical path, to access files. While the files are being moved, clients can access their files non-disruptively. Clients can also read their files from the old location and write them back to the new location without realizing that the physical location has changed. A global namespace is used to map the logical path of a file to the physical path names.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1114" height="682" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-48.png" alt="" class="wp-image-386"/&gt;&lt;figcaption class="wp-element-caption"&gt;File-serving environment before and after file-level virtualization&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h4 class="wp-block-heading" id="h-object-based-storage"&gt;&lt;strong&gt;Object-based storage&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In NAS, metadata are stored as part of the file distributed throughout the environment, which adds to the complexity and latency in searching and retrieving files. Object-based storage, on the other hand, stores file data in the form of objects based on its content and other attributes, rather than the name and location.&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/2019/11/image-49.png" alt="" class="wp-image-387" width="402" height="240"/&gt;&lt;figcaption class="wp-element-caption"&gt;Hierarchical File System and Flat Address Space&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;OSD &amp;#8211; object-based storage devices&lt;/strong&gt;, stores data in the form of objects using flat address space. There is no hierarchy of directories and file. Object is identified by objectID, which is usually generated using hash function.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In block storage, when file system receives the IO from an application, the file system maps the incoming I/O to the disk blocks. The block interface is used for sending the I/O over the channel or network to the storage device. The I/O is then written to the block allocated on the disk drive. When an application accesses data stored in OSD, the request is sent to the file system user component. The file system user component communicates to the OSD interface, which in turn sends the request to the storage device. The storage device has the OSD storage component responsible for managing the access to the object on a storage device.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Benefit of object storage&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;security and reliability: OSD can use special algorithm for strong encryption capacity. Request authentication is performed at the storage device rather than with an external authentication mechanism&lt;/li&gt;&#10;&lt;li&gt;platform independence: standard web access via REST or SOAP&lt;/li&gt;&#10;&lt;li&gt;scalability: Both storage and OSD nodes can be scaled independently in terms of performance and capacity&lt;/li&gt;&#10;&lt;/ul&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/2019/11/image-50.png" alt="" class="wp-image-388" width="401" height="445"/&gt;&lt;figcaption class="wp-element-caption"&gt;Block-level access vs object-level access&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;OSD components&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;nodes: a server with OSD operating environment to provide services to store, retrieve and manage data. Two key services are metadata service (generating objectID and maintaining the mapping between objectID and file) and storage service (manage a set of disks where data are stored).&lt;/li&gt;&#10;&lt;li&gt;private network: provides node-to-node connectivity and node-to-storage connectivity.&lt;/li&gt;&#10;&lt;li&gt;storage device&lt;/li&gt;&#10;&lt;/ul&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/2019/11/image-51.png" alt="" class="wp-image-389" width="518" height="166"/&gt;&lt;figcaption class="wp-element-caption"&gt;OSD system components&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Storage mechanism&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The application server presents the file to be stored to the OSD node.&lt;/li&gt;&#10;&lt;li&gt;The OSD node divides the file into two parts: user data and metadata.&lt;/li&gt;&#10;&lt;li&gt;The OSD node generates the object ID using a specialized algorithm. The algorithm is executed against the contents of the user data to derive an ID unique to this data.&lt;/li&gt;&#10;&lt;li&gt;For future access, the OSD node stores the metadata and object ID using the metadata service.&lt;/li&gt;&#10;&lt;li&gt;The OSD node stores the user data (objects) in the storage device using the storage service.&lt;/li&gt;&#10;&lt;li&gt;An acknowledgment is sent to the application server stating that the object is stored.&lt;/li&gt;&#10;&lt;/ol&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/2019/11/image-52.png" alt="" class="wp-image-390" width="512" height="334"/&gt;&lt;figcaption class="wp-element-caption"&gt;OSD: object storage&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Retrieval mechanism&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The application server sends a read request to the OSD system.&lt;/li&gt;&#10;&lt;li&gt;The metadata service retrieves the object ID for the requested file.&lt;/li&gt;&#10;&lt;li&gt;The metadata service sends the object ID to the application server.&lt;/li&gt;&#10;&lt;li&gt;The application server sends the object ID to the OSD storage service for object retrieval.&lt;/li&gt;&#10;&lt;li&gt;The OSD storage service retrieves the object from the storage device.&lt;/li&gt;&#10;&lt;li&gt;The OSD storage service sends the file to the application server.&lt;/li&gt;&#10;&lt;/ol&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/2019/11/image-53.png" alt="" class="wp-image-391" width="525" height="291"/&gt;&lt;figcaption class="wp-element-caption"&gt;OSD object retrieval&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;OSD usage&lt;/strong&gt;: data archival, especially long-term; and cloud storage, storage as service&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;CAS &amp;#8211; content addressed storage&lt;/strong&gt;, a special type of OSD designed for secure online storage and retrieval of fixed content. Data access in CAS differs from other OSD devices. &lt;span style="text-decoration: underline;"&gt;In CAS, the application server access the CAS device only via the CAS API running on the application server&lt;/span&gt;. However, the way CAS stores data is similar to the other OSD systems.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;CAS&lt;/strong&gt; &lt;strong&gt;Use case &lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Healthcare: storing patient studies &amp;#8211; size of radiology study ranges from 15MB to more than 1GB. Newly acquired studies are retained for 60 days and moved to long term storage.&lt;/li&gt;&#10;&lt;li&gt;Finance: storing financial records &amp;#8211; bank stores images of cheques (~25KB each) for about 90 millions a month. Images are processed in transaction system for 5 days. For the next 60 days images are requested for verifications. After 60 days access requirements drop drastically. Retention policy manages life-cycle of the images.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Unified storage&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Components&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;storage controller: The storage controller provides block-level access to application servers through iSCSI, FC, or FCoE protocols.&lt;/li&gt;&#10;&lt;li&gt;NAS head: a dedicated file server that provides file access to NAS clients&lt;/li&gt;&#10;&lt;li&gt;OSD node: accesses the storage through the storage controller using a FC or FCoE connection.&lt;/li&gt;&#10;&lt;li&gt;Storage&lt;/li&gt;&#10;&lt;/ul&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/2019/11/image-54.png" alt="" class="wp-image-392" width="459" height="533"/&gt;&lt;figcaption class="wp-element-caption"&gt;Unified storage platform&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h4 class="wp-block-heading" id="h-related-postings"&gt;Related Postings&lt;/h4&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/03/storage-nitty-gritty-1-5/"&gt;Disk and RAID&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/05/storage-nitty-gritty-2-5/"&gt;SAN&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/"&gt;Backup and Archive Solutions&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/11/storage-nitty-gritty-5-of-5-replication/"&gt;Replication&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/07/practical-cryptography-for-it-professional/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cryptography Basics 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/08/aws-certified-devops-engineer-exam-tips/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AWS Certified DevOps Engineer Exam Tips&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Git Explained 1 of 2</title><link>https://www.digihunch.com/2019/06/git-explained-1-of-2/</link><pubDate>Sat, 08 Jun 2019 22:39:00 -0400</pubDate><guid>https://www.digihunch.com/2019/06/git-explained-1-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;In a nutshell, Git is a distributed version control system, commonly used as source control management. It places files in one of three logical areas (working dir, staging, repo) below depending where it is in its lifecycle. &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1618" height="992" src="https://www.digihunch.com/wp-content/uploads/2019/10/image.png" alt="" class="wp-image-178"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many cheetsheets out there but this article will just sort through some concepts unique to Git. To understand how Git works it is crucial to think in terms of Git data model.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Working directory&lt;/strong&gt; a single checkout of one version of the project. These files are pulled out of the object database in the Git directory (upon checkout) and placed in the project directory on disk, for you to use or modify;&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Index&lt;/strong&gt; a file contained in your Git directory (stored as binary data in file .git/index) that keeps information about what will go into your next commit.&amp;nbsp;To display what&amp;#8217;s in the index, run `git ls-files &amp;#8211;stage`. Read &lt;a href="https://hackernoon.com/https-medium-com-zspajich-understanding-git-data-model-95eb16cc99f5"&gt;this post&lt;/a&gt; for further details&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Repository&lt;/strong&gt;: where Git stores the metadata and &lt;code&gt;&lt;span style="text-decoration: underline;"&gt;object database&lt;/span&gt;&lt;/code&gt; for your project.&amp;nbsp;The local repository is in .git/ under the project directory.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Add&lt;/strong&gt; &amp;#8211; register one or more modified files to staging area. You may edit several files with only a few needed registered for future commit. Add activity ensures the file edited are recorded in the index (as a preview of next commit). You technically need to run add against each file. But the command syntax with * or . allows you to capture all edits in the same directory or under.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Commit&lt;/strong&gt; &amp;#8211; persist the staged file edits to the repository (so they are stored in Git object database). A commit represents all the file edits that were staged by add command in previous steps.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Branch&lt;/strong&gt; &amp;#8211; a branch is simply a movable pointer to a commit. Default branch name created by git init is called &amp;#8220;master&amp;#8221;. Other than the name, there is nothing special about master branch. Everytime you commit, the master branch pointer moves forward automatically. Branch pointers are kept in .git/refs directory. Read &lt;a href="https://hackernoon.com/understanding-git-branching-2662f5882f9"&gt;this post&lt;/a&gt; for further details.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;HEAD&lt;/strong&gt; &amp;#8211; the pointer to the current branch reference, which is in turn a pointer to the last commit made on that branch. Git use HEAD pointer to know what branch you&amp;#8217;re currently on. HEAD will be the parent of the upcoming commit.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Tag&lt;/strong&gt; &amp;#8211; an annotated tag contains the SHA of the commit being tagged. Alias of a commit.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Merge&lt;/strong&gt; &amp;#8211; choose current commit of other branch and apply it onto your branch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Rebase&lt;/strong&gt; &amp;#8211; copy all commits from other branch to your branch. Compared to merge, rebasing forms a cleaner commit history.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Cherrypick&lt;/strong&gt; &amp;#8211; choose a previous commit from other branch and apply it onto your branch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Stash&lt;/strong&gt; &amp;#8211; &amp;nbsp;temporarily stashes changes you&amp;#8217;ve made to working tree so you can work on something else, and then come back and re-apply them later on.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Reset&lt;/strong&gt; &amp;#8211; at a high level, reset is to revert some operations. After pulling code, developer usually follow three steps: editing-&amp;gt;add-&amp;gt;commit. reset is to reverse these steps, based on different modes. The Pro Git reference has further details on the &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified"&gt;three different modes&lt;/a&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;soft mode&lt;/strong&gt; (reverse operation of commit): based on what branch HEAD points to, move where that branch points to (e.g. from latest commit, to a different commit several steps up the link);&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;mixed mode&lt;/strong&gt; (default; reverse operation of commit and add) &amp;#8211; in addition to soft mode, also update index;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;hard mode&lt;/strong&gt; (reverse operation of commit, add and file editing)- in addition to mixed mode, also update working directory. Edit on files are discarded.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Git object model&lt;/strong&gt; &amp;#8211; In Git database, files, commits and directories are stored as objects, In Git &lt;a href="https://shafiul.github.io/gitbook/1_the_git_object_model.html"&gt;object model&lt;/a&gt;, there are three types (to tell object type, run `git cat-file -t`). Read &lt;a href="https://hackernoon.com/https-medium-com-zspajich-understanding-git-data-model-95eb16cc99f5"&gt;this post&lt;/a&gt; for further details:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;blob object&lt;/strong&gt; &amp;#8211; stores file data with metadata; use `git show` to examine blob object;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;tree object&lt;/strong&gt; &amp;#8211; represents a directory. It references other tree objects (sub-directories) or blob objects (files under the directory, of a certain version); use `git ls-tree` to examine tree object;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;commit object&lt;/strong&gt; &amp;#8211; represents a commit. It references its parent commit, as well as a tree object that represents the entire project directory. use `git cat-file -p` to inspect commit object;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects"&gt;diagram&lt;/a&gt; from from Git Pro outlines the interactions amongst these types of objects.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/data-model-3.png" alt="All the reachable objects in your Git directory."/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;For more details&lt;/strong&gt;, the&amp;nbsp;&lt;a href="https://git-scm.com/book/en/v2"&gt;official documentation&lt;/a&gt;&amp;nbsp;is actually the most helpful reference with illustrations. In addition, I find on Hakcermoon three excellent articles with thorough explanation on&amp;nbsp;&lt;a href="https://hackernoon.com/https-medium-com-zspajich-understanding-git-data-model-95eb16cc99f5"&gt;data model&lt;/a&gt;,&amp;nbsp;&lt;a href="https://hackernoon.com/understanding-git-branching-2662f5882f9"&gt;branching&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://hackernoon.com/understanding-git-index-4821a0765cf"&gt;index&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/05/automation-with-ansible-a-primer/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Set up automation with Ansible&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/06/git-explained-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Git Explained 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Set up automation with Ansible</title><link>https://www.digihunch.com/2019/05/automation-with-ansible-a-primer/</link><pubDate>Wed, 22 May 2019 22:11:00 -0400</pubDate><guid>https://www.digihunch.com/2019/05/automation-with-ansible-a-primer/</guid><description>&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.ansible.com/overview/how-ansible-works"&gt;Ansible &lt;/a&gt;is very flexible automation tools with many benefits. The free version is command-line based and here is an example to set it up.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Environment setup&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Ansible 2.8 is required or some command may not work.&lt;/li&gt;&lt;li&gt;Ansible files (including playbooks, tasks and inventory files) are all located in /home/glowing/ansible&lt;/li&gt;&lt;li&gt;Default inventory file needs to be referenced in Ansible configuration /etc/ansible/ansible.cfg. This ensures ansible or ansible-playbook command can pick up hosts or host patterns without requiring inventory file through -i on every execution. Here is what the inventory config looks like in ansible.cfg:&lt;/li&gt;&lt;/ol&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[defaults]&#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;# some basic default values...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;inventory = /etc/ansible/hosts,/home/glowing/ansible/inventories/glowing_inventory.yml&#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;host_key_checking = False&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol class="wp-block-list" start="4"&gt;&lt;li&gt;As best practice, servers involved should be able to ssh to each other on RSA key authentication. This can be achieved by adding a separate authorized keys file and reference it from /etc/ssh/sshd_config, at the line starting with AuthorizedKeysFile, and separated with the file name of existing authorized keys with a space. In this way, you may keep public keys of human user in one authorized key file and the public keys of machines in the other.&lt;/li&gt;&lt;li&gt;Build inventory file in ~/ansible/inventories/glowing_inventory.yml. The inventory file can declare some variables to use across hosts. If password is involved (e.g. synchronize module still requires password), it can be stored here with base64 encoded. Here is an example of glowing_inventory.yml&lt;/li&gt;&lt;/ol&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&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;all:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vars:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ansible_user: glowing&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gh_sudo_pass: qGS0bWVuu3Jr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gh_dir: &lt;span style="color:#f92672"&gt;/&lt;/span&gt;opt&lt;span style="color:#f92672"&gt;/&lt;/span&gt;glowing&lt;span style="color:#f92672"&gt;/&lt;/span&gt;etc&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tmp_dir: &lt;span style="color:#f92672"&gt;/&lt;/span&gt;tmp&#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; children:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dc1_front_end:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe01:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe03:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe05:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe07:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe09:&#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; dc2_front_end:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe02:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe04:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe06:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe08:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghfe10:&#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; dc1_back_end:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghbe01:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghbe03:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghbe05:&#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; dc2_back_end:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghbe02:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghbe04:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghbe06:&#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; dc1_database:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghdb01:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghdb03:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghdb05:&#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; dc2_database:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghdb02:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghdb04:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e9a&lt;span style="color:#f92672"&gt;-&lt;/span&gt;ghdb06:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;That is the basic steps to set up Ansible. Now we can run adhoc commands. The command below allows me to copy a file from executing server to all destination servers that match a pattern:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ansible &amp;#39;dc*_back_end:!&amp;#39;`hostname -s` -e &amp;#34;file_name={{gh_dir}}/test.zip&amp;#34; -m copy -a &amp;#34;src={{file_name}} dest={{file_name}}&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;In this command:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;variable {{gh_dir}} is declared in the inventory file. It must be referenced by placing variable name between double curly bracket;&lt;/li&gt;&lt;li&gt;An extra variable {{file_name}} is declared at run time because this is the dynamic part of the command;&lt;/li&gt;&lt;li&gt;This adhoc ansible command uses copy module. With copy module, the src anddest files are in the same absolute path here so we use this variable to save some typing;&lt;/li&gt;&lt;li&gt;reference to the host support wildcard such as dc*_back_end;&lt;/li&gt;&lt;li&gt;&lt;code&gt;hostname -s&lt;/code&gt; returns the host name of the server where the adhoc command is run&lt;/li&gt;&lt;li&gt;:! excluds the running host from being matched as destination server. this is in case that running machine is already in the dc*_back_end group, where copy source and destination are identical&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is another example for deleting a file from destination servers:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ansible dc2_database -e &amp;#34;fn=/tmp/file_to_delete&amp;#34; -m file -a &amp;#34;path={{fn}} state=absent&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;If we can run adhoc command, then we can start writing some playbook and roles. Below is a simple playbook test-conn.yml to ping each server:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: measure mint retrieval time from {{ ansible_limit }}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hosts: &amp;#39;{{ ansible_limit }}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; serial: 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order: sorted&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gather_facts: no&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ignore_errors: yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tasks:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: measure time&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; command: curl -s -XGET http://{{ inventory_hostname }}:8080/index.html -o /dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delegate_to: localhost&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; register: curlout&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; no_log: true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - name: display result&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; debug:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; msg: &amp;#34;Time to load webpage from {{ inventory_hostname }} is {{ curlout.delta }}.&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you can run that playbook with the following 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# ansible-playbook -l dc2_front_end test-conn.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Ansible automation is essentially editing yaml files for playbooks. Writing playbook involves a lot of module interaction and one needs to follow best practices. Understanding&lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html"&gt; Ansible roles&lt;/a&gt; can help reuse some code.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition here are some common playbook keywords you should be familiar with:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;serial&lt;/li&gt;&lt;li&gt;order&lt;/li&gt;&lt;li&gt;gather_facts&lt;/li&gt;&lt;li&gt;ignore_errors&lt;/li&gt;&lt;li&gt;when&lt;/li&gt;&lt;li&gt;run_once&lt;/li&gt;&lt;li&gt;local_action&lt;/li&gt;&lt;li&gt;register&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here are common modules to know&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;set_fact&lt;/li&gt;&lt;li&gt;state&lt;/li&gt;&lt;li&gt;touch&lt;/li&gt;&lt;li&gt;fail&lt;/li&gt;&lt;li&gt;synchoronize&lt;/li&gt;&lt;li&gt;lineinfile&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/05/storage-nitty-gritty-2-5/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 2 of 5 – SAN&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/06/git-explained-1-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Git Explained 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Lightsail – create a WordPress site in one hour</title><link>https://www.digihunch.com/2019/04/build-a-wordpress-site-in-one-hour-with-lightsail/</link><pubDate>Wed, 10 Apr 2019 01:30:44 -0400</pubDate><guid>https://www.digihunch.com/2019/04/build-a-wordpress-site-in-one-hour-with-lightsail/</guid><description>&lt;p class="wp-block-paragraph"&gt;My challenge with my just revived wordpress.com site is the plug-ins. Even paid premium users cannot install plug-ins for diagram, etc. I don&amp;#8217;t want to upgrade to business plan so I decided to build my own.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;14 years ago in university, I prototyped an intranet site using the Windows, Apache, MySQL, and PHP. It took a few weeks. Nowadays, this is referred to as WAMP stack with WordPress. AWS has a post on the &lt;a href="https://aws.amazon.com/blogs/architecture/wordpress-best-practices-on-aws/"&gt;best practices&lt;/a&gt; for hosting WordPress. However, I just need a single all-in-one server and there are some &lt;a href="https://docs.aws.amazon.com/linux/al2/ug/al2-tutorials.html"&gt;instruction&lt;/a&gt; on that. With Amazon Lightsail it was made a lot easier. The annual cost to host a WordPress site would be $55 in US, given that virtually nobody visits it except myself. The cost consists of:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Domain registration for $13 a year&lt;/li&gt;&#10;&lt;li&gt;Lightsail blueprint for $3.5 a month&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are several benefit of using this Lightsail blueprint, such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;You can actually SSH into the EC2 instance. This is important to myself in the habit of probing into how things work;&lt;/li&gt;&#10;&lt;li&gt;Resources are all packaged into a fixed price plan. Remember the pesky accidental AWS charges? &lt;/li&gt;&#10;&lt;li&gt;You have the whole LAMP stack pre-installed, with the bitnami image for WordPress;&lt;/li&gt;&#10;&lt;li&gt;Start small but with scalability.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To start with Lightsail is extremely intuitive. Just click and launch:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/06/image.png" alt="" style="width:399px;height:450px"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is a good instruction &lt;a href="https://www.youtube.com/watch?v=upZOhKhefAs"&gt;here&lt;/a&gt; on YouTube. Once launched successfully, there are some post configurations:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Request a static IP and assign it to the EC2 instance;&lt;/li&gt;&#10;&lt;li&gt;Register a domain (within Lightsail, with Route53 or any other domain registrar) and create an A record referencing the IP address;&lt;/li&gt;&#10;&lt;li&gt;Create a certificate and set up anto-renewal. &lt;a href="https://lightsail.aws.amazon.com/ls/docs/en_us/articles/amazon-lightsail-using-lets-encrypt-certificates-with-wordpress"&gt;Here &lt;/a&gt;is an instruction but the steps differ if DNS is managed outside of Lightsail;&lt;/li&gt;&#10;&lt;li&gt;Redirect http to https. Refer to the &lt;a href="https://docs.bitnami.com/aws/apps/wordpress-multisite/administration/force-https-apache/"&gt;instruction &lt;/a&gt;from bitnami;&lt;/li&gt;&#10;&lt;li&gt;Export from wordpress.com and import the xml into this little site.&lt;/li&gt;&#10;&lt;li&gt;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Notice that all services relevant to hosting are packaged into a single service called bitnami. In order to restart service, instead of restarting php, httpd and mysql individually, you can run the following:&lt;/p&gt;&#10;&lt;p&gt;# service bitnami stop&lt;br /&gt;&#10;# service bitnami start&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In fact, in /etc/rc5.d or /etc/init.d, I do not find the individual services for httpd or mysql. There is actually an instance of MySQL database in the Instance:&lt;/p&gt;&#10;&lt;p&gt;# mysql -u root -p&lt;br /&gt;&#10;mysql &amp;gt; show databases;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There you go! Now the site is live. This little &lt;a href="https://www.digihunch.com/2019/04/build-a-wordpress-site-in-one-hour-with-lightsail/"&gt;instruction&lt;/a&gt; will come in handy for rebuilding when this site is blown away 🙂&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-migrate-to-new-lightsail-instance"&gt;Migrate to new LightSail instance&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have been using the same LightSail instance for 3.5 years and it&amp;#8217;s been stable. By Dec 2022, the version of PHP (7.2.17) has been outdated and I decided to launch a new LightSail instance based on new version of image, which comes with newer version of PHP (8.1.13). Here is a note of my steps:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Create a new LightSail instance with the same SSH key, assign a new static IP to it. As soon as the new instance is created, we can access it by IP on HTTP, using the &lt;a href="https://docs.bitnami.com/aws/faq/get-started/find-credentials/"&gt;initial credential&lt;/a&gt; provided.&lt;/li&gt;&#10;&lt;li&gt;We need the latest version of All-in-One WP Migration plugin to perform the migration. Ensure its latest version is installed on both old and new instance. &lt;/li&gt;&#10;&lt;li&gt;Export site to file using All-in-One WP Migration plugin. It should produce a single file with wpress extension. For this site, the site is 450MB by Dec 2022&lt;/li&gt;&#10;&lt;li&gt;For the new site, try to upload the file and notice that the max size allowed is 80MB. I made the following changes:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://docs.bitnami.com/aws/apps/wordpress-multisite/administration/increase-upload-limit-php/"&gt;Change PHP attributes&lt;/a&gt;. Edit /opt/bitnami/php/etc/php.ini at the following attributes:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;post_max_size = 512M&lt;/li&gt;&#10;&lt;li&gt;upload_max_filesize = 512M&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://help.servmask.com/2018/10/27/how-to-increase-maximum-upload-file-size-in-wordpress/"&gt;Change WordPress attributes&lt;/a&gt;. Create /opt/bitnami/wordpress/.htaccess with the following attributes:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;php_value upload_max_filesize 512M&lt;/li&gt;&#10;&lt;li&gt;php_value post_max_size 512M&lt;/li&gt;&#10;&lt;li&gt;php_value memory_limit 512M&lt;/li&gt;&#10;&lt;li&gt;php_value max_execution_time 0&lt;/li&gt;&#10;&lt;li&gt;php_value max_input_time 300&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://docs.bitnami.com/aws/faq/administration/control-services/"&gt;Restart services &lt;/a&gt;related to wordpress &lt;/li&gt;&#10;&lt;li&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Now upload the .wpress file and it should take less than 5 minutes to upgrade. After the process, the new site is imported. Delete useless plugins.&lt;/li&gt;&#10;&lt;li&gt;Port the certificate files, including keys, certificate and certificate chains. In my case, I had to edit the following files:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;/opt/bitnami/apache/conf/vhosts/wordpress-https-vhost.conf&lt;/li&gt;&#10;&lt;li&gt;/opt/bitnami/apache/conf/vhosts/wordpress-vhost.conf&lt;/li&gt;&#10;&lt;li&gt;/opt/bitnami/apache/conf/bitnami/bitnami.conf&lt;/li&gt;&#10;&lt;li&gt;/opt/bitnami/apache/conf/bitnami/bitnami-ssl.conf&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Configure HTTP-&amp;gt;HTTPS redirect on the new server. Revisit the files above.&lt;/li&gt;&#10;&lt;li&gt;Change DNS record to point to the new server.&lt;/li&gt;&#10;&lt;li&gt;Browse the site, check page and posts and pay attention to: images, tables, code blocks.&lt;/li&gt;&#10;&lt;li&gt;One common issue is images do not display. Check the image URL. The URL might have been replaced by bad values during import. Use a plugin (e.g. Go Live Update Urls) to correct the URLs in tables.&lt;/li&gt;&#10;&lt;li&gt;Use a plugin to find out broken links such as image, etc and fix the broken links. This should be an ongoing maintenance activity but is particularly worth doing on newly migrated site.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h4 class="wp-block-heading"&gt;Other little things&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many little things to fix. For example, I need to give transparent images white background. This is because when the site&amp;#8217;s background is black and the transparent area in the diagram will be black too, making diagrams (authored in white background) illegible. To do this, I have to add the following section to the additional CSS setting of the active theme:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img decoding="async" src="https://www.digihunch.com/wp-content/uploads/2021/07/image-3.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;WordPress is dynamic site and if you don&amp;#8217;t edit the site often, and don&amp;#8217;t want to start a static site from scratch (e.g. using &lt;a href="https://gohugo.io/"&gt;Hugo &lt;/a&gt;or &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; frameworks), you can use WP2Static plugin. Make sure to configure S3 bucket and CloudFront accordingly. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now, enjoy blogging.&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/2019/03/storage-nitty-gritty-1-5/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 1 of 5 – Disk and RAID&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/04/application-i-o-characteristics/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Application I/O Characteristics&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Storage Nitty-Gritty 1 of 5 – Disk and RAID</title><link>https://www.digihunch.com/2019/03/storage-nitty-gritty-1-5/</link><pubDate>Sat, 09 Mar 2019 22:25:52 -0500</pubDate><guid>https://www.digihunch.com/2019/03/storage-nitty-gritty-1-5/</guid><description>&lt;p class="wp-block-paragraph"&gt;On my trip I have been through several pre-sales discussions on storage. Therefore I&amp;#8217;m taking this opportunities to write up a series of postings deep diving into storage technologies. In this first section, we lay out the foundation of storage technology, from physical device to RAID, focusing on the concepts. Some contents are excerpts from Information Storage and Management.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Volume Manager&lt;/strong&gt; &amp;#8211; In early days, a file system occupies the entire disk drive, and presents continuous disk blocks directly to operating systems. Logical Volume Manager (LVM) was then introduced to bring a layer of abstraction (logical volume) on top of disks. The layers are shown as follows:&lt;/p&gt;&#10;&lt;p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="604px" viewBox="-0.5 -0.5 604 186" style="max-width:100%;max-height:186px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="127" width="120" height="50" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(6.5,145.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="106" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 107px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Hard Drive&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="53" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Hard Drive&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="147" width="300" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(336.5,155.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="106" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 106px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Hard Drive&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="53" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Hard Drive&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="120" y="127" width="120" height="50" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(126.5,145.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="106" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 107px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Hard Drive&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="53" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Hard Drive&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="127" width="120" height="20" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(277.5,130.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="44" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; vertical-align: top; width: 45px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Partition&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="22" y="12" fill="#333333" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Partition&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="127" width="180" height="20" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(427.5,130.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="44" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; vertical-align: top; width: 45px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Partition&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="22" y="12" fill="#333333" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Partition&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="97" width="120" height="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(255.5,105.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="88" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 89px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="44" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="120" y="97" width="120" height="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(135.5,105.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="88" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 89px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="44" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="97" width="120" height="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(15.5,105.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="88" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 89px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="44" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="97" width="180" height="30" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(405.5,105.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="88" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 89px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Physical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="44" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Physical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="67" width="360" height="30" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(141.5,75.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="77" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 78px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Volume Group&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="39" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Volume Group&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="67" width="180" height="30" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(411.5,75.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="77" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 78px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Volume Group&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="39" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Volume Group&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="37" width="170" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(43.5,45.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="82" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 83px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Logical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="41" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Logical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="170" y="37" width="190" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(223.5,45.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="82" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 83px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Logical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="41" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Logical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="37" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(408.5,45.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="82" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 82px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;Logical Volume&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="41" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;Logical Volume&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="7" width="170" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(53.5,15.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="63" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 63px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;File System&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="32" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;File System&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="170" y="7" width="190" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(233.5,15.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="62" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 64px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;File System&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="31" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;File System&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="7" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(418.5,15.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="62" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 64px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;File System&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="31" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;File System&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="560" y="117" width="40" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(556.5,120.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="46" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 47px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;pvcreate&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="23" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;pvcreate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="560" y="87" width="40" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(556.5,90.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="46" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 47px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;vgcreate&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="23" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;vgcreate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="560" y="57" width="40" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(558.5,60.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="42" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 43px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;lvcreate&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="21" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;lvcreate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="550" y="32" width="40" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(556.5,35.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="26" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 27px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;mkfs&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="13" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;mkfs&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="550" y="137" width="40" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(557.5,140.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow:visible;" pointer-events="all" width="25" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 26px; white-space: nowrap; overflow-wrap: normal; text-align: center;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;"&gt;fdisk&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="13" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica"&gt;fdisk&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 177 L 550 13.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 550 8.12 L 553.5 15.12 L 550 13.37 L 546.5 15.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With all these layers, a byte in user file maps to disk sectors through several layers:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-3.png" alt="" class="wp-image-311" width="519" height="348"/&gt;&lt;figcaption class="wp-element-caption"&gt;Mapping from user file to physical disk&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;File System&lt;/strong&gt; &amp;#8211; a hierarchical structure of files. It organizes data in a structural hierarchical manner. It includes files, directories as well as metadata. metadata must be consistent for the file system to be considered healthy. In Linux, metadata consists of:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Superblock&lt;/strong&gt;: important information about file system, e.g. type, creation and modification dates, size, mount status flag&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Inodes&lt;/strong&gt;: a data structure that contains information associated with every file or directory&lt;/li&gt;&#10;&lt;li&gt;list of data blocks free and in use&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Host connects to storage through various Interface Protocols. Common interface protocols include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;IDE/&lt;strong&gt;ATA&lt;/strong&gt; and Serial &lt;strong&gt;ATA&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;SCSI&lt;/strong&gt; (Small Computer System Interface)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;FC&lt;/strong&gt; (Fibre Channel)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;IP&lt;/strong&gt; (Internet Protocol per se is a network protocol traditionally used for host-to-host traffic in the early days. In the virtualization era, it has become a viable option for host-to-storage communication. Examples are &lt;strong&gt;iSCSI&lt;/strong&gt; and &lt;strong&gt;FCIP&lt;/strong&gt;)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most prevalent disk drive types are &lt;strong&gt;SSD&lt;/strong&gt; (solid state drive) and &lt;strong&gt;HDD&lt;/strong&gt; (hard disk driveA). SSD (solid state drive) is newer, flash-based technology. Without seek and rotational latencies they deliver a high number of IOPS with low response times. They are especially suited for applications with small block size and random-read workloads requiring constant latency &amp;lt;1ms.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;HDD is traditional and more cost effective. Its physical components are illustrated in the following two graphs:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-4.png" alt="" class="wp-image-312" width="325" height="226"/&gt;&lt;figcaption class="wp-element-caption"&gt;Hard disk component&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-5.png" alt="" class="wp-image-313" width="367" height="213"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For HDD, disk service time (time taken by a disk to complete an I/O request) is determined by the following factors:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Seek time&lt;/strong&gt; (aka access time) describes the time taken to position the R/W heads across the platter with a radial movement (moving along the radius of the platter). In other words, it is the time taken to position and settle the arm and the head over the correct track.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Rotation latency&lt;/strong&gt; is the time taken by the platter to rotate and position the data under the R/W head. It depends on the rotation speed of the spindle and is measured in milliseconds.&lt;/li&gt;&#10;&lt;li&gt;(&lt;strong&gt;Data) transfer rate &lt;/strong&gt;is the average amount of data per unit time that the drive can deliver from disk controller to the HBA (on the host).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Zone Bit Recording&lt;/strong&gt; &amp;#8211; a mechanism to use disk efficiently by grouping tracks into zones based on their distance from the disk.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Logical Block Addressing (LBA) &lt;/strong&gt;&amp;#8211; disk controller translates LBA to a physical address (CHS, cylinder, head and sector). The host only needs to know the size of disk drive in terms of number of blocks. The logical blocks are mapped to physical sectors on a 1:1 basis&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;IO request processing &lt;/strong&gt;&amp;#8211; I/O controller is introduce to improve response time for I/O request, in this model, The I/O requests arrive at the controller at the rate generated by the application. This rate is also called the arrival rate. These requests are held in the I/O queue, and the I/O controller processes them one by one, as shown here:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-6.png" alt="" class="wp-image-314" width="541" height="57"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The relationship between controller utilization and average response time is: &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Average response time = Service time / (1 – Utilization) &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;where service time is the time taken by disk controller to service the I/O request. This results in a classic relation between response time and utilization, as plotted below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-7.png" alt="" class="wp-image-315" width="434" height="196"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The graph indicates that the response time changes are nonlinear as the utilization increases. When the average queue sizes are low, the response time remains low. The response time increases slowly with added load on the queue and increases exponentially when the utilization exceeds 70 percent. Therefore, for performance-sensitive applications, it is common to utilize disks below their 70 percent of I/O serving capability. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Command queuing&lt;/strong&gt; is a technique implemented on modern disk drives that determines the execution order of received I/Os and reduces unnecessary drive-head movements to improve disk performance. When an I/O is received for execution at the disk controller, the command queuing algorithms assign a tag that defines a sequence in which the commands should be executed. With command queuing, commands are executed based on the organization of data on the disk, regardless of the order in which the commands are received. Below is an example:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-8.png" alt="" class="wp-image-318" width="525" height="383"/&gt;&lt;figcaption class="wp-element-caption"&gt;Disk Command Queuing&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RAID is a technology that leverages multiple drives as part of a set that provides data protection against drive failures. It may also improve performance by serving I/Os from multiple disks simultaneously. It is primarily used in HDD but SSD may still benefit from it. RAID may be implemented by software but hardware RAID with a controller is widespread. RAID is built on three basic techniques:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Striping&lt;/strong&gt;: spread data across multiple drives (more than one) to use the drives in parallel.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Mirroring&lt;/strong&gt;: same data is stored on two different disk drives, yielding two copies of the data.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Parity&lt;/strong&gt;: a method to protect striped data from disk drive failure without the cost of mirroring. An additional disk drive is added to hold parity, a mathematical construct that allows re-creation of the missing data.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Basic RAID levels are summarized here:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-9.png" alt="" class="wp-image-320" width="520" height="199"/&gt;&lt;figcaption class="wp-element-caption"&gt;RAID summary&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RAID 0, 1, 5 and 6 are pretty common in data centre operations. In addition to these levels above. If you hear RAID 1+0, 5+0 and RAID 6+0, they are called nested RAID. They are simply a RAID 0 on top of RAID1, RAID 5 and RAID 6, respectively.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;RAID 0&lt;/strong&gt; &amp;#8211; data striping technique utilizes full capacity of drives. Although it is a good option for applications that need high I/O throughput. It lacks data protection so it cannot drive application requiring high availability.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-10.png" alt="" class="wp-image-321" width="256" height="364"/&gt;&lt;figcaption class="wp-element-caption"&gt;RAID 0&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;RAID 1&lt;/strong&gt; &amp;#8211; mirroring technique ensures data duplication. In the event of disk failure, it introduces minimal impact to the disk array. It is suitable for applications that require high availability and cost is no constraint.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-11.png" alt="" class="wp-image-322" width="294" height="382"/&gt;&lt;figcaption class="wp-element-caption"&gt;RAID 1&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;RAID 1+0 or RAID 10&lt;/strong&gt; &amp;#8211; performs well for workloads with small, random, write-intensive I/Os. Some applications that benefit from RAID 1+0 include the following:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;High transaction rate Online Transaction Processing (OLTP)&lt;/li&gt;&#10;&lt;li&gt;Large messaging installations&lt;/li&gt;&#10;&lt;li&gt;Database applications with write intensive random access workloads&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-12.png" alt="" class="wp-image-323" width="334" height="367"/&gt;&lt;figcaption class="wp-element-caption"&gt;RAID 1+0&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;RAID 3&lt;/strong&gt; &amp;#8211; RAID 3 stripes data for performance and uses parity for fault tolerance. the total disk space required is 1.25 times the size of the data disks. RAID 3 always reads and writes complete stripes of data across all disks because the drives operate in parallel. RAID 3 provides good performance for applications that involve large sequential data access, such as data backup or video streaming.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-13.png" alt="" class="wp-image-324" width="266" height="370"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;RAID 5&lt;/strong&gt; &amp;#8211; RAID5 is similar to RAID 4 because it uses striping. The drives (strips) are also independently accessible. The difference between RAID 4 and RAID 5 is the parity location. In RAID 4, parity is written to a dedicated drive, creating a write bottleneck for the parity disk. In RAID 5, parity is distributed across all disks to overcome the write bottleneck of a dedicated parity disk.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-14.png" alt="" class="wp-image-325" width="281" height="390"/&gt;&lt;figcaption class="wp-element-caption"&gt;RAID 5&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RAID 5 is good for random, read-intensive I/O applications and preferred for messaging, data mining, medium-performance media serving, and relational database management system (RDBMS) implementations, in which database administrators (DBAs) optimize data access.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;RAID 6&lt;/strong&gt; &amp;#8211; works the same way as RAID 5, except that RAID 6 includes a second parity element to enable survival if two disk failures occur in a RAID set. The write penalty in RAID 6 is more than that in RAID 5; therefore, RAID 5 writes perform better than RAID 6. The rebuild operation in RAID 6 may take longer than that in RAID 5 due to the presence of two parity sets.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-15.png" alt="" class="wp-image-326" width="288" height="405"/&gt;&lt;figcaption class="wp-element-caption"&gt;RAID 6&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Intelligent Storage System&lt;/strong&gt; involves cache as the core component. An intelligent storage system involves front end, cache, back end and physical disks, as shown here:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-16.png" alt="" class="wp-image-330" width="565" height="255"/&gt;&lt;figcaption class="wp-element-caption"&gt;Intelligent Storage System components&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A variety of &lt;strong&gt;physical disk&lt;/strong&gt; types and speed (e.g. mix of FC, SATA, SAS and flash) can be supported in a modern intelligent storage system.&amp;nbsp;The &lt;strong&gt;front end&lt;/strong&gt; provides the interface between the storage system and the host. It consists of ports and controllers, with redundancy. The &lt;strong&gt;back end&lt;/strong&gt; provides an interface between cache and the physical disks. It consists of ports and controllers. For high data protection and high availability, storage systems are configured with dual controllers with multiple ports.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Cache&lt;/strong&gt; improves storage system performance by isolating hosts from mechanical delays associated with hard disks. In intelligent storage system, read and write are first attempted on cache.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-17.png" alt="" class="wp-image-334" width="454" height="226"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Page is the basic unit of cache, and the size of page is configured based on application I/O size. Cache consists of:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;data store&lt;/strong&gt;: holds the actual data temporarily&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;tag RAM&lt;/strong&gt;: mainly serves three purposes:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;it tracks locations of data in cache and on disk; &lt;/li&gt;&#10;&lt;li&gt;it maintains dirty bit flag to indicate whether data in cache has been committed to disk;&lt;/li&gt;&#10;&lt;li&gt;it keeps time based information such as last access time, for cache management&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Read Operation with Cache&lt;/strong&gt;: When host issues a read request, the storage controller reads the tag RAM first to determine whether required data is available in cache:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Read cache hit&lt;/strong&gt;: data is sent to host without any disk operation;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Read cache miss&lt;/strong&gt;: back end access the disk to retrieve the requested data. Data is then placed in cache and sent to host through front end.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-18.png" alt="" class="wp-image-335" width="546" height="466"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cache miss increases I/O response time, to increase read hit ratio, read-ahead algorithm can be used when read requests are sequential. In a sequential read request, a contiguous set of associated blocks is retrieved. Several other blocks that have not yet been requested by the host can be read from the disk and placed into cache in advance. When the host subsequently requests these blocks, the read operations will be read hits. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Fixed prefetch&lt;/strong&gt; &amp;#8211; the intelligent storage system prefetches a fixed amount of data. It is most suitable when host I/O sizes are uniform. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Variable prefetch&lt;/strong&gt;, the storage system prefetches an amount of data in multiples of the size of the host request. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Maximum prefetch&lt;/strong&gt; limits the number of data blocks that can be prefetched to prevent the disks from being rendered busy with prefetch at the expense of other I/Os.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Write Operation with Cache&lt;/strong&gt;: When an I/O is written to cache and acknowledged, it is completed in far less time (from the host’s perspective) than it would take to write directly to disk. Sequential writes also offer opportunities for optimization because many smaller writes can be coalesced for larger transfers to disk drives with the use of cache. Write operation with cache can be implemented in two ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Write-back cache&lt;/strong&gt;: Data is placed in cache and an acknowledgment is sent to the host immediately. Later, data from several writes are committed (de-staged) to the disk. Write response times are much faster because the write operations are isolated from the mechanical delays of the disk. However, uncommitted data is at risk of loss if cache failures occur.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Write-through cache&lt;/strong&gt;: Data is placed in the cache and immediately written to the disk, and an acknowledgment is sent to the host. Because data is committed to disk as it arrives, the risks of data loss are low, but the write-response time is longer because of the disk operations.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If the size of an I/O request exceeds the &lt;strong&gt;write aside size&lt;/strong&gt;, writes are sent to the disk directly to reduce the impact of large writes consuming a large cache space. This is helpful where cache resources are constrained and cache is required for small random I/Os. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cache space can be assigned in two ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;dedicated cache&lt;/strong&gt;: separate sets of locations are reserved for read and write;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;global cache&lt;/strong&gt;: user may specify percentage of cache for read and write based on application workload pattern; or the system set is dynamically.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cache Management algorithm is used to determine when, and what pages of the cache need to be free up during maintenance. Most commonly used algorithms are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;LRU (least recently used): assuming data not accessed for a while will not be requested by host any more;&lt;/li&gt;&#10;&lt;li&gt;MRU (most recently used): assuming data recently accessed will not be requested by host again&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As cache fills, the storage system must take action to flush dirty pages by committing data from cache to disk. There are several triggers for cache management action:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Idle flushing &amp;#8211; occurs continuously at modest rate when cache utilization level is between high and low watermark;&lt;/li&gt;&#10;&lt;li&gt;High watermark flushing &amp;#8211; activated when utilization hits high watermark; and stops at low watermark; this has impact to I/O processing;&lt;/li&gt;&#10;&lt;li&gt;Forced flushing &amp;#8211; occurs in the event of large I/O burst when cache reaches 100% capacity; this significantly impacts I/O response time&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2019/11/image-19.png" alt="" class="wp-image-336" width="523" height="184"/&gt;&lt;figcaption class="wp-element-caption"&gt;Types of Flushing&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Cache data protection&lt;/strong&gt; is the mechanism to prevent losing uncommitted data held in cache. Common mechanisms are:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Cache mirroring&lt;/strong&gt; &amp;#8211; Each write to cache is held in two different memory locations on two independent memory cards. If a cache failure occurs, the write data will still be safe in the mirrored location and can be committed to the disk. The array operating environment needs to maintain cache coherency between the redundant memory locations. Read cache does not need mirroring.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Cache vaulting&lt;/strong&gt; &amp;#8211; In the event of server power failure, use battery power to write the cache content to the disk (vault drive). When power is restored, data from these disks is written back to write cache and then written to the intended disks.&lt;/p&gt;&#10;&lt;p&gt;&lt;!--StartFragment--&gt;&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-related-postings"&gt;Related Postings&lt;/h4&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/03/storage-nitty-gritty-1-5/"&gt;SAN&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/"&gt;NAS and Object Storage&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/10/storage-nitty-gritty-4-of-5-backup-and-archive-solutions/"&gt;Backup and Archive Solution&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.digihunch.com/2019/11/storage-nitty-gritty-5-of-5-replication/"&gt;Replication&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;!--EndFragment--&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/2019/02/package-repository-management-in-linux/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Package Repository Management for Linux&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/04/build-a-wordpress-site-in-one-hour-with-lightsail/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Lightsail – create a WordPress site in one hour&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Package Repository Management for Linux</title><link>https://www.digihunch.com/2019/02/package-repository-management-in-linux/</link><pubDate>Sat, 09 Feb 2019 22:14:56 -0400</pubDate><guid>https://www.digihunch.com/2019/02/package-repository-management-in-linux/</guid><description>&lt;p class="wp-block-paragraph"&gt;RPM is the package manager tool in Linux. YUM is a repository management tool to fetch appropriate package for the particular version of Linux. YUM performs automatic dependency resolution when updating, installing or removing packages, and thus is able to automatically determine, fetch and install all available dependent packages. This posting is about common commands used for package management. Then we walk through the step to set up a local yum repository. Lastly, we summarize the best practice for patch management.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Tools&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to the basic yum install, update and remove commands, here are some more tools for repository management:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## check for installed packages with updates available&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum check-update&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## search for packages&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum search package.name&#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:#75715e"&gt;## check for dependency&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum deplist package.name&#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:#75715e"&gt;## list installed or available packages:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum list installed package.name&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum list available package.name&#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:#75715e"&gt;## list all available packages of a repo:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum --disablerepo&lt;span style="color:#f92672"&gt;=&lt;/span&gt;* --enablerepo&lt;span style="color:#f92672"&gt;=&lt;/span&gt;repo.name list available&#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:#75715e"&gt;## display package info&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum info package.name&#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:#75715e"&gt;## list all repositories&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum repolist&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum repoinfo&#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:#75715e"&gt;## list all transactions&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum history list all&#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:#75715e"&gt;## display history of a package or transaction id&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum history package-list package.name&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum history info package.name&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum history info transaction.id&#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:#75715e"&gt;## undo or redo a transaction&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum history undo transaction.id&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum history redo transaction.id&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;RHEL provides a more complete &lt;a href="https://access.redhat.com/sites/default/files/attachments/rh_yum_cheatsheet_1214_jcs_print-1.pdf"&gt;cheatsheet&lt;/a&gt; for yum commands.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to single package, YUM also manages &lt;a href="https://access.redhat.com/solutions/15815"&gt;package groups&lt;/a&gt;, and works with &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/sec-yum_plugins"&gt;plugins&lt;/a&gt;. For example, you may enable security related packages only by using the &lt;a href="https://access.redhat.com/solutions/10021"&gt;yum-plugin-security&lt;/a&gt;. For security, YUM repositories can enable GPG check (based on public key cryptography).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Local Repo configuration&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we configure a YUM repository server and reference that from a client to update packages. To begin with, we collect all RPM packages in /home/dhunch/upgrade/downloads/, then we install createrepo package and use it to create metadata:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum install createrepo yum-utils&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;createrepo /home/dhunch/upgrade/downloads/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Examine the directory and you will find a new repodata directory created. Now we can configure nginx with the following configuration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&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; listen 8088;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; server_name rpmsource.digihunch.com;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; root /home/dhunch/upgrade/downloads/;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; location / {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; autoindex on; #enable listing of directory index&#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;Restart nginx and browse to server name at port 8088, you should see the directory in html. If you&amp;#8217;re getting 403 error, most likely nginx has issues accessing the directory. Nginx process should be able to traverse each level of directory to serve the files. The parent directories should have executable permission for others. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod o+x /home/dhunch/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Here is a reference to &lt;a href="https://www.digitalocean.com/community/questions/proper-permissions-for-web-server-s-directory"&gt;nginx permission&lt;/a&gt; requirement. Now we can continue to configure the client.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the client, we need to add a file in /etc/repos.d/yum&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;[digirepo]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;name=Local YUM Repository &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;baseurl=http://rpmsource.digihunch.com:8088/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;enabled=1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpgcheck=0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Last we can check the available packages from the client:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum --disablerepo&lt;span style="color:#f92672"&gt;=&lt;/span&gt;* --enablerepo&lt;span style="color:#f92672"&gt;=&lt;/span&gt;digirepo list available&#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 in order to keep the local repo up-to-date, there is additional maintenance work on the server. You may need to sync from official source, such as:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;reposync -g -l -d -m --repoid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;base-source --newest-only --download-metadata --download_path&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/home/dhunch/upgrade/downloads/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;You may also need to set up a daily job with &lt;a href="https://www.redhat.com/sysadmin/using-yum-cron"&gt;yum-cron&lt;/a&gt; service so this is automated.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Sometimes the server is locked down and you need HTTP proxy to allow YUM to access repo. You may configure proxy in /etc/yum.conf in the proxy, proxy_username, and proxy_password entries. Also, you may export environment variable http_proxy. &lt;a href="https://www.rosehosting.com/blog/how-to-enable-proxy-settings-for-yum-command-on-centos-7/" class="rank-math-link"&gt;Here&lt;/a&gt; are more information.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Sometimes yum cache may introduce issues. To clean cache before installing, 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum clean all &amp;amp;amp;&amp;amp;amp; yum -y install python3&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading"&gt;Python packages&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Python3 is not installed by default on CentOS 7. So it needs to be installed with YUM. Do not replace the existing python2 with python3 by changing where symbolic link /bin/python points to, because YUM is dependent on python2.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For python3 use pip3 as package management. If the server does not have a public route, then you need to install pip3 package offline. For example, on a server with Internet, 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;pip3 download -d ~/Downloads javaobj-py3&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then SCP the file to the offline server (e.g. to /home/dhunch/javaobj/), from which you can 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip3 install --no-index --find-links=/home/dhunch/javaobj/ javaobj-py3 --user&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This will install the pip3 package offline. Note it is recommended to not run pip3 installer as root user. The switch &amp;#8211;user allows you to run as non-root user.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Best Practices&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To configure a patch management environment, we assume that the servers do not have access to the Internet, not even through proxy. This should be part of the security guideline anyways. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This requires a local repository server to be setup. Administrators may use createrepo tool as outline above to create such repo, use reposync to synchronize local source from official source, or yum-cron to automate this for daily task.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the clients, administrators should manage the repo file in /etc/yum.repo.d, to ensure they are pointing to the correct source, using gpg check, etc. A local script to enable and disable different repos are also helpful. Otherwise, the enabelrepo and disablerepo switch can be used per command.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to catalogue the state of operating system, before and after each patching activity, for verification and auditing purposes. This usually requires scripting work using yum history or package-cleanup tools.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The actual patch work can be done either manually or automatically (e.g. with Ansible&amp;#8217;s yum module). RPM is also a good tool for troubleshooting. If the scope of patching is only for security, use the yum-plugin-security to limit the packages to only security related ones.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Roll-back should be prepared in case of inadvertent outcome. Roll-back scripting relies heavily on yum history commands. If the OS needs to boot with a previous kernel, use the grubby tool.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Happy Patching!&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/2019/01/interesting-terms-about-unsuccessful-software-project-management/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Interesting terms and principles&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/03/storage-nitty-gritty-1-5/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 1 of 5 – Disk and RAID&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Java version confusions</title><link>https://www.digihunch.com/2018/11/the-java-confusions/</link><pubDate>Mon, 05 Nov 2018 17:32:53 -0400</pubDate><guid>https://www.digihunch.com/2018/11/the-java-confusions/</guid><description>&lt;p class="wp-block-paragraph"&gt;Anyone working with deploying Java applications inevitably came across one of these confusions with the terms. Let&amp;#8217;s clarify them. This clarification is not for Java developer and does not go deep with underlying technologies. This is for installation/DevOps engineers to understand Java environment.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Java SE, EE and ME&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java Platform, Standard Edition &lt;strong&gt;(Java SE&lt;/strong&gt;) is a computing platform for development and deployment of portable code for desktop and server environments. Java SE was formerly known as Java 2 Platform, Standard Edition (J2SE). Java SE defines a range of general-purpose APIs, and also includes the Java Language Specification and the Java Virtual Machine Specification. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java Enterprise Edition (&lt;strong&gt;Java EE&lt;/strong&gt;), formerly Java 2 Platform, Enterprise Edition, currently rebranded as Jakarta EE but the new brand is still being adopted. It is an extension to Java SE with specifications for enterprise features such as distributed computing, web services, XML processing, JMS (messaging). It is more widespread in enterprise contexts such as e-commerce, accounting, banking information systems. The specification defines APIs and their interactions for providers to meet in order to declare compliance with Java EE. For example Apache Tomcat is an implementation of a subset of Java EE. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java Platform, Micro Edition (Java ME, formerly knowned as Java 2 Platform, Micro Edition or J2ME) is a subset of Java SE for embedded and mobile devices. The advent of Android significantly de-popularized Java ME.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;JRE and JDK&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Java SE is the foundation for developing in Java language. The aforementioned platforms (Java SE, EE and ME) are just specifications, not implementations. Java Software Development Toolkit (SDK) is called JDK (Java Development Kit) for short. Strictly speaking, the JDK can be an implementation of any one of the platforms above. In every day language, people loosely refers to the implementation of Java SE as &lt;strong&gt;JDK&lt;/strong&gt;, whereas Oracle&amp;#8217;s implementation of Java EE is referred to as&lt;strong&gt; Java EE SDK&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://miro.medium.com/max/636/1*8oNn6HxcWFmrCsgUt27k0w.jpeg" alt="" width="527" height="299"/&gt;&lt;figcaption&gt;JDK vs JRE&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;JDK consists of Java Runtime Environment (&lt;strong&gt;JRE&lt;/strong&gt;) along with tools to compile and debug Java code for developing Java applications. JRE consists of libraries, Java Virtual Machine (JVM), Java Pluging and Java Web Start to run Java applications. JRE alone does not contain compilers and debugging tools. The two most widespread JDKs are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Oracle JDK: Oracle&amp;#8217;s official implementation of Java SE.&lt;/li&gt;&lt;li&gt;OpenJDK: a free and open-source implementation of Java SE.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;They are both created and maintained by Oracle. Almost everything in Oracle JDK is from OpenJDK. The slight &lt;a href="https://www.educba.com/oracle-vs-openjdk/"&gt;difference &lt;/a&gt;between them is an entirely separate topic itself but the idea is their binaries will be converged:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2020/03/image-5.png" alt="" class="wp-image-886" width="571" height="322"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Oracle JDK was licensed under Oracle Binary Code License Agreement, whereas OpenJDK has the GNU General Public License (GNU GPL) version 2 with a linking exception. It is worth-noting that Oracle has announced that the Oracle JDK 8 builds released after &lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html"&gt;Jan 2019&lt;/a&gt; cease to be free for commercial use. This drives may application vendor to &lt;a href="https://developers.redhat.com/blog/2018/11/05/migrating-from-oracle-jdk-to-openjdk-on-red-hat-enterprise-linux-what-you-need-to-know/"&gt;migrate&lt;/a&gt; from Oracle JDK to OpenJDK in their platforms.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Version History&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If what you have read so far is not confusing enough, here&amp;#8217;s some more muds. The version scheme for Java has changed in it&amp;#8217;s 20 years history. Here is a list of main versions.&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Platform Version&lt;/td&gt;&lt;td&gt;Internal Version&lt;/td&gt;&lt;td&gt;Release Date&lt;/td&gt;&lt;td&gt;Notes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JDK 1.0&lt;/td&gt;&lt;td&gt;1.0&lt;/td&gt;&lt;td&gt;Jan 1996&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JDK 1.1&lt;/td&gt;&lt;td&gt;1.1&lt;/td&gt;&lt;td&gt;Feb 1997&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 1.2&lt;/td&gt;&lt;td&gt;1.2&lt;/td&gt;&lt;td&gt;Dec 1998&lt;/td&gt;&lt;td&gt;In 1998 JDK splits into J2SE and J2EE. Code name for J2SE 1.2 is Playground&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 1.3&lt;/td&gt;&lt;td&gt;1.3&lt;/td&gt;&lt;td&gt;May 2000&lt;/td&gt;&lt;td&gt;Code name is Kestrel&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 1.4&lt;/td&gt;&lt;td&gt;1.4&lt;/td&gt;&lt;td&gt;Feb 2002&lt;/td&gt;&lt;td&gt;Code name is Merlin&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J2SE 5.0&lt;/td&gt;&lt;td&gt;1.5&lt;/td&gt;&lt;td&gt;Sep 2004&lt;/td&gt;&lt;td&gt;In 2004, Sun introduced internal version and external version. Code name for this version is Tiger.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 6&lt;/td&gt;&lt;td&gt;1.6&lt;/td&gt;&lt;td&gt;Dec 2006&lt;/td&gt;&lt;td&gt;Code name Mustang&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 7&lt;/td&gt;&lt;td&gt;1.7&lt;/td&gt;&lt;td&gt;Jul 2011&lt;/td&gt;&lt;td&gt;Code name Dolphin&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 8&lt;/td&gt;&lt;td&gt;1.8&lt;/td&gt;&lt;td&gt;Mar 2014&lt;/td&gt;&lt;td&gt;5 year from previous version, LTS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 9&lt;/td&gt;&lt;td&gt;1.9&lt;/td&gt;&lt;td&gt;Sep 2017&lt;/td&gt;&lt;td&gt;3.5 year from previous version. Going forward new version will be released every six month&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 10&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Mar 2018&lt;/td&gt;&lt;td&gt;It was proposed that versions should simply increase incrementally&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 11&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;Sep 2018&lt;/td&gt;&lt;td&gt;LTS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 12&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;Mar 2019&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;#8230;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java SE 17&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;Sep 2021&lt;/td&gt;&lt;td&gt;LTS&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since 2018, new version will be release every six month and the there is no longer a distinction between internal and external versions.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Multi-version management&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We only cover Linux here to manage multiple versions of JDK. We use a tool named alternatives to maintain symbolic links determining default commands. How this works with Java is:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Make /usr/bin/java a symbolic link pointing to /etc/alternatives/java&lt;/li&gt;&lt;li&gt;Make /etc/alternatives/java also a symbolic link pointing to the desired version of java&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To start configuration, run:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alternatives --config java&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you will be given a list of Java versions to choose from. If the list does not have your desired version, and you confirm that the version has been installed. You will need to add this version by doing something like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alternatives --install /usr/bin/java java /usr/java/jdk1.6.0_25/bin/java 1000&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command executes and takes effect by modifying files under /var/lib/alternatives directory.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Also, one can overwrite environment variable $JAVA_HOME to force an application to use a different version of Java. This is because many application picks up JDK location from that variable.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Redhat Firewall configuration: from iptables to firewalld&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/01/interesting-terms-about-unsuccessful-software-project-management/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Interesting terms and principles&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Redhat Firewall configuration: from iptables to firewalld</title><link>https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/</link><pubDate>Fri, 12 Oct 2018 17:56:00 -0400</pubDate><guid>https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/</guid><description>&lt;h3 class="wp-block-heading"&gt;Tools to manage firewall&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Packet filter rules in Linux Kernel is managed by an user-space application named iptables in CentOS and RedHat. Since CentOS 7, &lt;a href="https://en.wikipedia.org/wiki/Firewalld"&gt;firewalld&lt;/a&gt; is introduced as an alternative to iptables. Firewalld can be installed and executed as a systemd service, and it is supposed to replace iptables. This &lt;a href="https://www.tecmint.com/firewalld-vs-iptables-and-control-network-traffic-in-firewall/"&gt;article&lt;/a&gt; describes how to configure both. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are several advantages in firewalld. One is is the support of &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-using_zones_to_manage_incoming_traffic_depending_on_source"&gt;zones&lt;/a&gt;. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-using-firewalld-on-centos-7"&gt;Here&lt;/a&gt; are some useful information. Also, iptables involves three different services for IPv4(iptables), IPv6(ip6tables), and software bridging (ebtables), whereas firewalld only involves a single service to manage all three. Firewalld allows user to add or remove rules/ports from running firewall, without restarting firewall. Unless you have specific reason to use iptables, always use firewalld service to manage firewall. Here is an instruction to firewalld service. In this posting however, we will be focusing on iptables to understand firewall managment. We also go through an example of opening a TCP port. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;How does iptables work&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When working with iptables, it is important to understand that its related concepts (&lt;strong&gt;tables-&amp;gt;chains-&amp;gt;rules-&amp;gt;criteria and targets&lt;/strong&gt;) and how the &lt;strong&gt;order of rules&lt;/strong&gt; plays a factor. There are five independent &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;tables&lt;/span&gt;&lt;/strong&gt;, each contains a number of &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;chains&lt;/span&gt;&lt;/strong&gt;, either &lt;em&gt;built-in&lt;/em&gt; or &lt;em&gt;user-defined&lt;/em&gt;. Administrators mostly deals with &lt;em&gt;&lt;span style="text-decoration: underline;"&gt;built-in chains&lt;/span&gt;&lt;/em&gt; in &lt;em&gt;&lt;span style="text-decoration: underline;"&gt;filter&lt;/span&gt;&lt;/em&gt; and &lt;em&gt;&lt;span style="text-decoration: underline;"&gt;nat&lt;/span&gt;&lt;/em&gt; tables. The five tables are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;filter&lt;/strong&gt;: If -t isn&amp;#8217;t specified, this is the default table. It contains built-in chains:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;INPUT&lt;/strong&gt;: for packet destined to local sockets&lt;/li&gt;&lt;li&gt;&lt;strong&gt;FORWARD&lt;/strong&gt;: for packets being routed through the box&lt;/li&gt;&lt;li&gt;&lt;strong&gt;OUTPUT&lt;/strong&gt;: for locally-generated packets&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;nat&lt;/strong&gt;: this table is consulted when a packet that creates a new connection is encountered. It has three built-in chains:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;PREROUTING&lt;/strong&gt;: for altering packets as soon as they come in&lt;/li&gt;&lt;li&gt;&lt;strong&gt;OUTPUT&lt;/strong&gt;: for altering locally generated packets before routing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;POSTROUTING&lt;/strong&gt;: for altering packets as they are about to go out&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;mangle&lt;/strong&gt;: this table is used for specialized packet alternation, with five built-in chains (since kernel 2.4.18): PREROUTING and OUTPUT, INPUT, FORWARD, and POSTROUTING&lt;/li&gt;&lt;li&gt;&lt;strong&gt;raw&lt;/strong&gt;: this table is mainly for configuring exceptions from connection tracking with two built-in chains: PREROUTING and OUTPUT&lt;/li&gt;&lt;li&gt;&lt;strong&gt;security&lt;/strong&gt;: for Mandatory Access Control (MAC) networking rules, with three built-in chains: INPUT, OUTPUT, and FORWARD.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Under the table (e.g. filter, nat), each chain (e.g. INPUT, OUTPUT, etc) consists of list of &lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;firewall rules&lt;/strong&gt;&lt;/span&gt;. Each rule is made up of two parts defined for the packets:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Criteria&lt;/strong&gt;: if the packet does not match the criteria, the next rule in the chain is examined; if it does match, then the next rule is specified by the value of the target.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Target&lt;/strong&gt;: what to do if criteria is met. The target can be:&lt;ul&gt;&lt;li&gt;user-defined chain, &lt;/li&gt;&lt;li&gt;one of the target described in iptables-extensions, or &lt;/li&gt;&lt;li&gt;in most cases, one of the special values ACCEPT, DROP or RETURN&lt;ul&gt;&lt;li&gt;&lt;strong&gt;ACCEPT&lt;/strong&gt; &amp;#8211; let the packet through&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DROP&lt;/strong&gt; &amp;#8211; drop the packet on the floor&lt;/li&gt;&lt;li&gt;&lt;strong&gt;RETURN&lt;/strong&gt; &amp;#8211; stop traversing this chain, and resume at &lt;span style="text-decoration: underline;"&gt;next rule in the previous (calling) chain&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The rules, defined in each chain under their tables, can be found in file /etc/sysconfig/iptables. You can find tables (prefix with asterisk *), chains (prefix with colon :), rules under their chains and a statement COMMIT after each table. The iptables process flow illustrates how a packet interact with all these rules under different chains and tables defined in this file: &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/1000/1*OIoNQkH4RTSm-eY2lUMBcQ.jpeg" alt="IPTables and Docker. In this post I will be talking about… | by Edouard Buschini | Medium"/&gt;&lt;figcaption&gt;iptables Process Flow&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although this big picture looks formidable, an administrator commonly only deals with the green and purple blocks (filter and nat), with the big picture in mind. Here is an example of /etc/sysconfig/iptables file from a newly installed system:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Generated by iptables-save v1.4.21 on Fri Sep 11 23:15:32 2017&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*filter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:INPUT ACCEPT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;0:0&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;:FORWARD ACCEPT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;0:0&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;:OUTPUT ACCEPT &lt;span style="color:#f92672"&gt;[&lt;/span&gt;132:17200&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;-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p icmp -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -i lo -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt; -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A FORWARD -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COMMIT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Completed on Fri Sep 11 23:15:32 2017&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The rule simply allows SSH traffic. This file will be loaded up on every reboot (specifically, restart of iptables service). So if you have made some changes to rules and you want the change picked up on reboot. The rules should be saved to this file:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo iptables-save &amp;gt; /etc/sysconfig/iptables&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Other than saving rule for reboot, if you simply want to edit the rules (e.g. order of rules is incorrect), you can save the rules to file, modify the file and restore the rule from file:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo iptables-save &amp;gt; ~/iptables.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo vi ~/iptables.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo iptables-restore &amp;amp;lt; ~/iptables.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture"&gt;Here&lt;/a&gt; is some further reading about iptables architecture.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Anatomy of a rule&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The man page for iptables species the following synopsis:&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;iptables [-t table] {-A|-C|-D} chain rule-specification&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rule-specification = [matches...] [target]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;match = -m matchname [per-match-options]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;target = -j targetname [per-target-options]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;So when you append (-A), delete (-D), insert (-I) or replace (-R) a rule, you need to specify rule specification. The man page further explains that the following parameters make up a rule specification:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;protocol (-p)&lt;/strong&gt;: the protocol of the rule of the packet to check. value can be tcp, udp, icmp, all or any name defined in /etc/protocols.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;match (-m)&lt;/strong&gt;: specifies the name of a match to use and is followed by match options. The match refers to an extension module that tests for a specific property. Those extension modules are documented in the man page of &lt;a href="https://ipset.netfilter.org/iptables-extensions.man.html"&gt;iptables-extensions&lt;/a&gt;. You may specify -m multiple times for different match names, which together make up the condition under which a target is invoked. Matches are evaluated first to last as specified. We often use extensions &lt;em&gt;tcp&lt;/em&gt; and &lt;em&gt;state&lt;/em&gt;. According to iptables-extensions man page, we can specify &amp;#8211;dport followed by port number for the &lt;em&gt;tcp&lt;/em&gt; extension, and &amp;#8211;state followed by value such as NEW or ESTABLISHED for the &lt;em&gt;state&lt;/em&gt; extension.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;jump (-j)&lt;/strong&gt;: specifies the target of the rule, such as ACCEPT, REJECT or DROP.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;source and destination (-s and -d)&lt;/strong&gt;: source and destination IP address or masks. Hostname will work but not recommended since resolution is needed.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;inbound and outbound interface (-i and -o)&lt;/strong&gt;: name of interface via which the packet was received and is going to be sent.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;goto (-g)&lt;/strong&gt;: processing should continue in a user specified chain&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Other&lt;/strong&gt; parameters: -4/&amp;#8211;ipv4, -6/&amp;#8211;ipv6, -c/&amp;#8211;set-counters, -f/&amp;#8211;fragment&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we run iptables command to view rules, we need to specify the table (e.g. filter, nat, etc) followed by -S or &amp;#8211;list-rules:&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;$ iptables -t nat -S&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If you do not specify -t switch, the default (-t filter) is applied. Be aware that in this case, you&amp;#8217;re only seeing rules under filter table, and not all rules under tall tables!&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the result, for example one line from command &amp;#8220;iptables -S&amp;#8221; may say:&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;-A INPUT -p tcp -m state --state NEW -m tcp --dport &lt;span style="color:#ae81ff"&gt;9200&lt;/span&gt; -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The interpretation: appending a rule to INPUT chain of filter table (implicitly specified). The protocol is tcp. The first match extension is state, and the state value shall be NEW. The second match extension is tcp, and the dport value shall be 9200. If the packet is a match, then the target (action) is ACCEPT.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Managing rules&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As mentioned earlier, rules can be dumped to any file or /etc/sysconfig/iptables, in which the rules are assessed in order. Below is a real life iptables file with a nat table as well. &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;# Generated by iptables-save v1.4.21 on Wed Jan 15 13:58:39 2017&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*filter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:INPUT DROP [0:0]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:FORWARD DROP [0:0]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:OUTPUT ACCEPT [4:208]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p icmp -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -i lo -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -s 10.100.160.56/32 -p tcp -m state --state NEW -m tcp --dport 7000:7001 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -s 10.100.160.56/32 -p tcp -m state --state NEW -m tcp --dport 7199 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -s 10.100.160.56/32 -p tcp -m state --state NEW -m tcp --dport 9042 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -s 10.100.160.56/32 -p tcp -m state --state NEW -m tcp --dport 9160 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p udp -m state --state NEW -m udp --dport 161 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p udp -m state --state NEW -m udp --dport 162 -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A FORWARD -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COMMIT&#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;*nat&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:PREROUTING ACCEPT [1:328]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:INPUT ACCEPT [0:0]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:OUTPUT ACCEPT [0:0]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:POSTROUTING ACCEPT [0:0]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A PREROUTING -p tcp -m tcp --dport 2392 -j REDIRECT --to-ports 2398&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A PREROUTING -p tcp -m tcp --dport 2393 -j REDIRECT --to-ports 2398&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A OUTPUT -o lo -p tcp -m tcp --dport 2392 -j REDIRECT --to-ports 2398&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A OUTPUT -o lo -p tcp -m tcp --dport 2393 -j REDIRECT --to-ports 2398&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COMMIT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Completed on Wed Jan 15 13:58:39 2017&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example, the nat table defines traffic forwarding: traffic arriving at TCP port 2392 and 2393 are forwarded to port 2398; outgoing traffic to port 2392 and 2393 are also redirected to port 2398. These rules do not overlap each other so the rules probably don&amp;#8217;t matter.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, the tcp filter table lists the rules to open certain TCP and UDP ports. Its block starts with a couple accepting rules and ends with a couple reject rules (regardless of protocols or ports). This is a good way to close a chain of rules with security. However, if you need to add additional rules to open more TCP ports, the new rule should not be appended after the reject rules at the bottom since the order matter here!&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Correct way to open a TCP port&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It&amp;#8217;s a common task for developers to open a TCP port simply for the purpose of bring up a web service and make it accessible to client. If we simply add a new rule to existing list, for example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 9870 -j ACCEPT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# iptables -S&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-P INPUT ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-P FORWARD ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-P OUTPUT ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p icmp -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -i lo -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt; -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport &lt;span style="color:#ae81ff"&gt;9870&lt;/span&gt; -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A FORWARD -j REJECT --reject-with icmp-host-prohibited&#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:#75715e"&gt;# systemctl reload iptables&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;You will notice that the rule is appended to the end of INPUT block, below the INPUT REJECT rule, which will never take effect.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To address this, you can use iptables-save and iptables-restore to export, edit to correct order and reload the rule, as illustrated above, instead of using iptables command to modify the rule directly. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# iptables-save &amp;gt; /tmp/rule.list&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# vi /tmp/rule.list&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# iptables-restore &amp;lt; /tmp/rule.list&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# iptables -S&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-P INPUT ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-P FORWARD ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-P OUTPUT ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p icmp -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -i lo -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt; -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport &lt;span style="color:#ae81ff"&gt;9870&lt;/span&gt; -j ACCEPT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A INPUT -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-A FORWARD -j REJECT --reject-with icmp-host-prohibited&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Alternatively, you could use some advanced iptables command switches to add the new rule to certain line number with &amp;#8211;line-number switch. &lt;a href="https://www.osetc.com/en/linux-iptables-insert-rule-at-a-specific-position-prepend-firewall-rule.html"&gt;Here&lt;/a&gt; is more information.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/09/log-shipping-through-elk/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Log shipping through ELK&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/11/the-java-confusions/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Java version confusions&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Log shipping through ELK</title><link>https://www.digihunch.com/2018/09/log-shipping-through-elk/</link><pubDate>Wed, 19 Sep 2018 17:26:51 -0400</pubDate><guid>https://www.digihunch.com/2018/09/log-shipping-through-elk/</guid><description>&lt;p class="wp-block-paragraph"&gt;A common devops task is build logging pipeline with ELK stack (Elasticsearch, Logstash, Kibana). Suppose the application is written in Java and currently use log4j&amp;#8217;s RollingFileAppender to generate log files locally. We can use log4j&amp;#8217;s socket appender to write to Logstash, which further pushes the log stream to Elasticsearch cluster. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this model, failure to push a log line results in the following in log4j output:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2018-09-06 11:05:39,778,ERROR,stderr - [AsyncAppender-Dispatcher-Thread-672] log4j:WARN Detected problem with connection: java.net.SocketException: Broken pipe (Write failed)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The logstash log displays the socket exception as well:&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;[2018-09-06T10:12:21,935][DEBUG][logstash.inputs.log4j ] Accepted connection {:client=&amp;gt;&amp;#34;192.168.111.56:58118&amp;#34;, :server=&amp;gt;&amp;#34;0.0.0.0:4560&amp;#34;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2018-09-06T10:12:21,963][DEBUG][logstash.pipeline ] filter received {&amp;#34;event&amp;#34;=&amp;gt;{&amp;#34;method&amp;#34;=&amp;gt;&amp;#34;?&amp;#34;, &amp;#34;thread&amp;#34;=&amp;gt;&amp;#34;676774870@qtp-1804103302-6&amp;#34;, &amp;#34;message&amp;#34;=&amp;gt;&amp;#34;Unable to resolve session ID from SessionKey [org.apache.shiro.web.session.mgt.WebSessionKey@71da9a4]. Returning null to indicate a session could not be found.&amp;#34;, &amp;#34;priority&amp;#34;=&amp;gt;&amp;#34;DEBUG&amp;#34;, &amp;#34;type&amp;#34;=&amp;gt;&amp;#34;log4j&amp;#34;, &amp;#34;path&amp;#34;=&amp;gt;&amp;#34;org.apache.shiro.session.mgt.DefaultSessionManager&amp;#34;, &amp;#34;@timestamp&amp;#34;=&amp;gt;2018-09-06T15:12:21.950Z, &amp;#34;file&amp;#34;=&amp;gt;&amp;#34;?:?&amp;#34;, &amp;#34;@version&amp;#34;=&amp;gt;&amp;#34;1&amp;#34;, &amp;#34;host&amp;#34;=&amp;gt;&amp;#34;192.168.111.56:58118&amp;#34;, &amp;#34;logger_name&amp;#34;=&amp;gt;&amp;#34;org.apache.shiro.session.mgt.DefaultSessionManager&amp;#34;, &amp;#34;class&amp;#34;=&amp;gt;&amp;#34;?&amp;#34;, &amp;#34;timestamp&amp;#34;=&amp;gt;1536246741950}}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2018-09-06T10:12:21,963][DEBUG][logstash.pipeline ] output received {&amp;#34;event&amp;#34;=&amp;gt;{&amp;#34;method&amp;#34;=&amp;gt;&amp;#34;?&amp;#34;, &amp;#34;thread&amp;#34;=&amp;gt;&amp;#34;676774870@qtp-1804103302-6&amp;#34;, &amp;#34;message&amp;#34;=&amp;gt;&amp;#34;Unable to resolve session ID from SessionKey [org.apache.shiro.web.session.mgt.WebSessionKey@71da9a4]. Returning null to indicate a session could not be found.&amp;#34;, &amp;#34;priority&amp;#34;=&amp;gt;&amp;#34;DEBUG&amp;#34;, &amp;#34;type&amp;#34;=&amp;gt;&amp;#34;log4j&amp;#34;, &amp;#34;path&amp;#34;=&amp;gt;&amp;#34;org.apache.shiro.session.mgt.DefaultSessionManager&amp;#34;, &amp;#34;@timestamp&amp;#34;=&amp;gt;2018-09-06T15:12:21.950Z, &amp;#34;file&amp;#34;=&amp;gt;&amp;#34;?:?&amp;#34;, &amp;#34;@version&amp;#34;=&amp;gt;&amp;#34;1&amp;#34;, &amp;#34;host&amp;#34;=&amp;gt;&amp;#34;192.168.111.56:58118&amp;#34;, &amp;#34;logger_name&amp;#34;=&amp;gt;&amp;#34;org.apache.shiro.session.mgt.DefaultSessionManager&amp;#34;, &amp;#34;class&amp;#34;=&amp;gt;&amp;#34;?&amp;#34;, &amp;#34;timestamp&amp;#34;=&amp;gt;1536246741950}}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2018-09-06T10:12:22,041][DEBUG][logstash.inputs.log4j ] Closing connection {:client=&amp;gt;&amp;#34;192.168.111.56:58118&amp;#34;, :exception=&amp;gt;java.io.InvalidObjectException: Object type java.util.Hashtable is not allowed.}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The troubleshooting isn&amp;#8217;t very straightforward. So its alternative is preferred. The alternative is to keep the existing RollingFileAppender as well as the local log files, but use a filebeat agent for each application node. Here is the diagram:&lt;/p&gt;&#10;&lt;figure class="wp-block-image is-style-default"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/960/1*AZPJdp8V82H6t7Vbu68_Ig.png" alt="Log Analysis Pipelines in Elastic Stack | by Thejan Rupasinghe | Medium"/&gt;&lt;figcaption&gt;ELK Log Shipping Pipeline&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;Filebeat is a very light agent to be installed with the application on the same server or container. &lt;/li&gt;&lt;li&gt;Streams of text information congregate to logstash nodes. These log stash service pushes the converged stream to ElasticSearch cluster&lt;/li&gt;&lt;li&gt;ElasticSearch cluster ingest the stream for indexing&lt;/li&gt;&lt;li&gt;Kibana is responsible for viewing. &lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is an example architecture with many potential variations. For example, log stash service may be deployed on the same hosts with ElasticSearch cluster. If there aren&amp;#8217;t many application nodes, filebeat may directly push its output to ElasticSearch. This architecture provides a lot of flexibility and scalability.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With log4j format, two challenges to address are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;identifying multi-line entry in the log&lt;/li&gt;&lt;li&gt;mapping sections of a log entry into fields in Elastisearch&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, the following log entry reflects both challenges above:&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;2018-09-06 11:52:18,022,DEBUG,service.dataportal.web.retrieve - [687285925@qtp-564051174-3350] creating streaming output request 9868594 and requid=fb9a2cad-e24b-4eb8-ad62-b027184e7b9b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2018-09-06 11:52:18,054,ERROR,org.glassfish.jersey.server.ServerRuntime$Responder - [687285925@qtp-564051174-3350] An I/O error has occurred while writing a re&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sponse message entity to the container output stream.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;org.glassfish.jersey.server.internal.process.MappableException: org.mortbay.jetty.EofException&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:91)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1135)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:662)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:395)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:385)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:280)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.internal.Errors.process(Errors.java:316)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.internal.Errors.process(Errors.java:298)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.internal.Errors.process(Errors.java:268)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:390)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.HandlerList.handle(HandlerList.java:49)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.Server.handle(Server.java:326)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:926)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Caused by: org.mortbay.jetty.EofException&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:789)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.AbstractGenerator$Output.flush(AbstractGenerator.java:568)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpConnection$Output.flush(HttpConnection.java:1010)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.servlet.internal.ResponseWriter$NonCloseableOutputStreamWrapper.flush(ResponseWriter.java:330)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.CommittingOutputStream.flush(CommittingOutputStream.java:287)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$UnCloseableOutputStream.flush(WriterInterceptorExecutor.java:305)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.StreamingOutputProvider.writeTo(StreamingOutputProvider.java:79)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.StreamingOutputProvider.writeTo(StreamingOutputProvider.java:61)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:266)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:251)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:109)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.spi.ContentEncoder.aroundWriteTo(ContentEncoder.java:137)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ... 40 more&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Caused by: java.net.SocketException: Broken pipe (Write failed)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at java.net.SocketOutputStream.socketWrite0(Native Method)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at java.net.SocketOutputStream.write(SocketOutputStream.java:155)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.io.ByteArrayBuffer.writeTo(ByteArrayBuffer.java:368)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.io.bio.StreamEndPoint.flush(StreamEndPoint.java:122)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:723)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ... 58 more&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To address multi-line entry we need to tell filebeat how to identify the start of a line through its multiline.pattern configuration option:&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;- type: log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; enabled: true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; paths:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - /var/log/dhunch/app.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; multiline.pattern: &amp;#39;^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; multiline.negate: true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; multiline.match: after&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fields:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: dhunchapp&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; type: app_log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content: diagnostic&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When filebeat pushes log entries to logstash, there will be an additional &amp;#8220;time&amp;#8221; column added, which reflects the time the log is ingested. The entire log4j message line is all put in a single field called &amp;#8220;message&amp;#8221;, this message field contains the timestamp from application, the thread, class, logging level and the actual diagnostic message. These are not mapped to separate sections making it difficult to search in Elasticsearch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To address section mapping, the configuration is made on the logstash side. We will take advantage of a plugin called &lt;a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html"&gt;grok filter&lt;/a&gt;. The function of grok filter here, is to parse the original &amp;#8220;message&amp;#8221; field, map different sections into separate columns, respectively called logtime, loglevel, logclass, logthread and logmsg. The grok filter expression is 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;filter {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grok {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; match =&amp;gt; { &amp;#34;message&amp;#34; =&amp;gt; &amp;#34;%{TIMESTAMP_ISO8601:logtime},%{LOGLEVEL:loglevel},%{NOTSPACE:logclass} - \[%{DATA:logthread}\] %{GREEDYDATA:logmsg}&amp;#34; }&#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; date {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; match =&amp;gt; [ &amp;#34;logtime&amp;#34; , &amp;#34;yyyy-MM-dd HH:mm:ss,SSS&amp;#34; ]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timezone =&amp;gt; &amp;#34;America/Chicago&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; target =&amp;gt; &amp;#34;@timestamp&amp;#34;&#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; mutate {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; replace =&amp;gt; [ &amp;#34;message&amp;#34; , &amp;#34;%{logmsg}&amp;#34; ]&#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; mutate {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; remove_field =&amp;gt; [ &amp;#34;logmsg&amp;#34; ]&#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;The date plugin indicates to Elasticsearch to treat this field as datetime with specified time zone. The mutate plugin below essentially renames the &amp;#8220;logmsg&amp;#8221; column to &amp;#8220;message&amp;#8221;, which allows Elasticsearch to understand this field as log message.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/07/overview-of-vsphere/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;A review of vSphere virtualization technologies&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/10/redhat-firewall-configuration-firewalld-vs-iptables/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Redhat Firewall configuration: from iptables to firewalld&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>A review of vSphere virtualization technologies</title><link>https://www.digihunch.com/2018/07/overview-of-vsphere/</link><pubDate>Sat, 21 Jul 2018 15:54:00 -0400</pubDate><guid>https://www.digihunch.com/2018/07/overview-of-vsphere/</guid><description>&lt;p class="wp-block-paragraph"&gt;This post is a summary of VMware&amp;#8217;s white paper Introduction to VMware vSphere.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;ESXi &lt;/span&gt;&lt;/strong&gt;is the hypervisor (virtualization layer) on bare metal servers that abstracts processor, memory, storage and networking resources into multiple virtual machines. It was previously known as ESX and VMware discontinued ESX at version 4.1 so only ESXi is provided at and above version 5.0. &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;vSphere &lt;/span&gt;&lt;/strong&gt;is the platform to view, configure and manage the key aspects of virtualization, including:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;computing and memory resources (hosts, clusters and resource pools)&lt;/li&gt;&lt;li&gt;storage resources (data stores)&lt;/li&gt;&lt;li&gt;networking resources (networks)&lt;/li&gt;&lt;li&gt;virtual machines&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Under vSphere product family, vCenter Server is the central point for configuring, provisioning and managing the virtual environment. vShphere client is a client application to connect remotely to vCenter Server, or ESXi from any Windows PC. There is also vSphere Web Access for users from non-Windows environment.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="550" height="511" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-8.png" alt="" class="wp-image-1264"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For each aspects of virtualization, there is some vSphere features.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Computing&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;host &lt;/span&gt;&lt;/strong&gt;is a virtual representation of of the computing and memory resources of a physical machine running ESXi. When two or more physical machines are grouped to work and be managed as a whole, the aggregate computing and memory resources form a &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;cluster&lt;/span&gt;&lt;/strong&gt;. Physical machines can be dynamically added to or removed from a cluster. A cluster acts and can be managed as a single entity. It represents the aggregate computing and memory resources of a group of physical x86 servers sharing the same network and storage arrays. Computing and memory resources from hosts and clusters can be finely partitioned into a hierarchy of &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;resource pools&lt;/span&gt;&lt;/strong&gt;. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="441" height="499" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-1.png" alt="" class="wp-image-1253"/&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can dynamically change &lt;strong&gt;resource allocation policies&lt;/strong&gt; without shutting down the associated VMs. When reserved resources are not being used by a resource pool or a VM, the resources can be shared. This helps to maximize resource use while also ensuring that reservations are met and resource policies enforced.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ESXi provides a &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;memory compression cache &lt;/span&gt;&lt;/strong&gt;to improve VM performance when you use memory overcommitment. Memory compression is enabled by default. When a hosts memory becomes overcommitted, ESXi compresses virtual pages and stores them in memory. This is because accessing compressed memory is faster than accessing memory that has been swapped out to disk. Memory compression in ESXi allows you to overcommit memory without hindering performance. When a virtual page needs to be swapped, ESXi first attempts to compress the page. Pages that can be compressed to 2KB or smaller are stored in the VM&amp;#8217;s compression cache, increasing the capacity of the host.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;High Availability&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VMware vMotion&lt;/span&gt;&lt;/strong&gt; enables the migration of running VMs from one physical server to another without service interruption. The effect is a more efficient assignment of resources across physical servers. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;Storage vMotion&lt;/span&gt;&lt;/strong&gt; enables the migration of VMs from one datastore to another datastore without service interruption. This allows administrators to off-load VMs from one storage array to another. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VMware DRS (distributed resource scheduler)&lt;/span&gt;&lt;/strong&gt; helps you manage a cluster of physical hosts as a single compute resource. You can configure DRS to execute VM placement, VM migration, and host power actions. When you create a VM on a cluster, DRS places the VM in such a way as to ensure that load across the cluster is balanced, and cluster-wide resource allocation policies (e.g. reservations, priorities, and limits) are enforced. When you add a new physical server to a cluster, DRS enables VMs to immediately take advantage of the new resources. When a VM is powered on, DRS performs an initial placement of the VM on a host. As cluster conditions (e.g. load and available resources) change over time, DRS migrates (using vMotion) VMs to other hosts as necessary.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="615" height="338" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-3.png" alt="" class="wp-image-1257"/&gt;&lt;figcaption&gt;VMware DRS&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;DPM (distributed power management)&lt;/span&gt;&lt;/strong&gt; is enabled, the system compares cluster-level and host-level capacity to the demands of VMs running in the cluster. If the resource demands of the running VMs can be met by a subset of hosts in the cluster, DPM migrates the VMs to this subset and powers down the hosts that are not needed. When resource demands increase, DPM powers these hosts back on and migrates the VMs to them. This dynamic cluster right-sizing that DPM performs reduces the power consumption of the cluster, without sacrificing VM performance or availability.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;Storage I/O control congestion management&lt;/span&gt;&lt;/strong&gt; allows cluster-wide storage I/O prioritization and enables administrator to set congestion thresholds for I/O shares.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VMware HA&lt;/span&gt;&lt;/strong&gt; enables quick automated restart of virtual machines on a different physical server within a cluster if a host fails. HA monitors all physical hosts in a cluster and detects host failures. An agent placed on each physical host maintains a heartbeat with the other hosts in the resource pool. Loss of a heartbeat initiates the process of restarting all affected VMs on that host. HA also provides a VM monitoring feature that monitors the status of VM in an HA cluster. If a VM does not generate heartbeats within a specified time, VM monitoring identifies it as having failed and restarts it. HA is configured centrally through vCenter Server and once configured, it operates continuously and in a distributed manner on every ESXi host without needing vCenter Server to stay up.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="614" height="362" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-2.png" alt="" class="wp-image-1256"/&gt;&lt;figcaption&gt;VMware HA&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VMware vLockstep&lt;/span&gt;&lt;/strong&gt; technology and VMware Fault Tolerance provides continuous availability by protecting a VM with a shadow copy that runs in virtual lockstep on a separate host. Inputs and events performed on the primary VM are recorded and replayed on the secondary VM to ensure identical state. The secondary VM in virtual lockstep can take over execution at any point without interruption or loss of data.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Storage&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;Datastores &lt;/span&gt;&lt;/strong&gt;are virtual representations of combinations of underlying physical storage resources in the data center. These physical storage resources include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Local SCSI, SAS, or SATA disks attached to the physical machines&lt;/li&gt;&lt;li&gt;Fibre Channel or iSCSI SAN disk arrays&lt;/li&gt;&lt;li&gt;Network Attached Storage (NAS) arrays&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Storage subsystem appears as a virtual SCSI controller connected to one or more virtual SCSI disks. These virtual controllers (BusLogic Parallel, LSI Logic Parallel, LSI Logic SAS and VMware Paravirtual) are the only types of SCSI controllers that a VM can see and access. The virtual SCSI disks are provisioned from datastore. This datastore abstraction is a model that assigns storage space to VMs while insulating the guest from the complexity of the underlying physical storage technology. The guest VM however, is not exposed to Fibre Channel SAN, iSCSI SAN, direct attached storage or NAS.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="649" height="687" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-5.png" alt="" class="wp-image-1260"/&gt;&lt;figcaption&gt;Storage Architecture&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each datastore is a &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VMFS volume&lt;/span&gt;&lt;/strong&gt; on a storage device. Datastore can span multiple physical storage subsystems.&lt;strong&gt; A single VMFS volume&lt;/strong&gt; can contain &lt;strong&gt;one or more LUNs&lt;/strong&gt; from a local SCSI disk array on a physical host, a Fibre Channel disk farm, or iSCSI SAN disk farm. New LUNs added to any of the physical storage subsystems are detected and made available to all existing new datastores. Storage capacity on a previously created datastore can be extended without powering down physical hosts or storage subsystems. &lt;span style="text-decoration: underline;"&gt;If any of the LUNs within a VMFS volume fails, only VMs that use that LUN are affected.&lt;/span&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each VM is stored as a set of files in a directory in the datastore. The disk storage associated with each VM is a set of files within the guest&amp;#8217;s directory. You can operate on the guest disk storage as an ordinary file, which can be copied, moved, or backed up. New virtual disks can be added to a virtual machine without powering it down. In that case, a &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;virtual disk file (.vmdk) &lt;/span&gt;&lt;/strong&gt;is created in VMFS to provide new storage for the added virtual disk&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;span style="text-decoration: underline;"&gt;VMFS is a clustered file system that leverages shared storage to allow multiple physical hosts to read and write the same storage simultaneously.&lt;/span&gt; VMFS provides on-disk locking to ensure that the same virtual machine is not powered on by multiple servers at the same time. If a physical host fails, the on-disk lock for each VM is released so that VMs can be restarted on other physical hosts. VMFS also features failure consistency and recovery mechanisms, such as distributed journaling, a failure-consisten VM I/O path, and VM state snapshots. These mechanisms can aid quick identification of the cause and recovery from VM, physical host and storage subsystem failures. VMFS also supports &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;raw device mapping (RDM)&lt;/span&gt;&lt;/strong&gt;, which is a mechanism for a VM to have direct access to a LUN on the physical storage subsystem (Fibre Channel or iSCSI only). An RDM is a symbolic link from a VMFS volume to a raw LUN. The mapping makes LUNs appear as files in a VMFS volume. The mapping file, not the raw LUN, is referenced in the VM configuration. When a LUN is opened for access, the mapping file is read to obtain the reference to the raw LUN. Thereafter, reads and writes go directly to the raw LUN rather than going through the mapping file.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="462" height="577" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-4.png" alt="" class="wp-image-1259"/&gt;&lt;figcaption&gt;Raw Device Mapping&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Networking&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Each VM has one or more &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;vNICs &lt;/span&gt;&lt;/strong&gt;(virtual network interface cards). The guest OS and application program communicate with a vNIC through either a commonly available device driver or a VMware device driver optimized for the virtual environment. In either case, communication in the guest OS occurs just as it would with a physical device. On the network, the vNIC responds to standard Ethernet protocol as would a physical NIC. An outside agent does not detect that it is communicating with a virtual machine.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="638" height="408" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-7.png" alt="" class="wp-image-1262"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;virtual switch (vSwitch) &lt;/span&gt;&lt;/strong&gt;works like a layer 2 physical switch. Each server has its own virtual switches. One one side of the virtual switch are port groups that connect to virtual machines. On the other side are uplink connections to physical Ethernet adapters on the physical server where the virtual switch resides. VMs connect to the outside world through the physical Ethernet adapters that are connected to the virtual switch uplinks. A virtual switch can connect its uplinks to more than one physical Ethernet adapter to enable NIC teaming.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;Port group&lt;/span&gt;&lt;/strong&gt; is a unique concept in the virtual environment. A port group is a mechanism for setting policies that govern the network connected to it. A vSwitch can have multiple port groups. A VM connects its vNIC to a port group instead of to a particular port on the vSwitch, for better network segmentation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;vNetwork Distributed Swtich (vDs)&lt;/span&gt;&lt;/strong&gt; function as a single virtual switch across all associated hosts. This functionality allows VMs to maintain consistent network configuration as they migrate across multiple hosts. Like vSwitch, &lt;span style="text-decoration: underline;"&gt;each VDS is a network hub that VMs can use and it can route traffic internally between VMs or link to an external network by connecting to physical Ethernet adapters&lt;/span&gt;. Each vDS can also hae one or more &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;dvPort groups &lt;/span&gt;&lt;/strong&gt;assigned to it. dvPort groups aggregate multiple ports under a common configuration and provide a stable anchor point for VMs connecting to labeled networks.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="627" height="439" src="https://www.digihunch.com/wp-content/uploads/2020/08/image-6.png" alt="" class="wp-image-1261"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When network resource management is enabled, vDS traffic is divided into six network resource pools: FT traffic, iSCSI traffic, vMotion traffic, management traffic, NFS traffic, and VM traffic. You can control the priority of each of these network resource pools.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;vCenter&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;vCenter Server provides centralized managed for data centers. It communicates with the ESXi host agent through the &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;VMware vSphere API&lt;/span&gt;&lt;/strong&gt;. When you first add a host to vCenter Server sends a vCenter Server agent to run on the host. The &lt;strong&gt;&lt;span style="text-decoration: underline;"&gt;vCenter Server agent &lt;/span&gt;&lt;/strong&gt;acts as a small vCenter Server to perform many fundamental management functions.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/06/dicom-data-encoding/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;DICOM data encoding&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/09/log-shipping-through-elk/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Log shipping through ELK&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>cron and anacron in RedHat Linux (How logrotate works)</title><link>https://www.digihunch.com/2018/05/cron-and-logrotate-in-centos/</link><pubDate>Tue, 15 May 2018 14:11:00 -0400</pubDate><guid>https://www.digihunch.com/2018/05/cron-and-logrotate-in-centos/</guid><description>&lt;h4 class="wp-block-heading"&gt;Cron and anacron&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We all know cron is a job scheduler. Many admin uses crontab to manage scheduled task. It is also important to know that crontab works at different levels as well, as well as the distinction between cron and anacron. They are similar, but different, managed by different sets of files. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is a brief description of how cron works from this &lt;a href="https://www.sitepoint.com/a-comprehensive-crash-course-into-cronjobs/"&gt;article&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After Cron starts, it searches its spool area to find and load crontab files into the memory. It additionally checks the /etc/crontab and or /etc/cron.d directories for system crontabs.&lt;br&gt;After loading the crontabs into memory, Cron checks the loaded crontabs on a minute-by-minute basis, running the events which are due.&lt;br&gt;In addition to this, Cron regularly (every minute) checks if the spool directory’s modtime (modification time) has changed. If so, it checks the modetime of all the loaded crontabs and reloads those which have changed. That’s why we don’t have to restart the daemon when installing a new cron job.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Basically, in cron, you specify a particular time at which a job will run. These jobs are managed by files in &lt;em&gt;/var/spool/cron/ &lt;/em&gt;directory. In this directory, each file is named by the username that owns the crontab file. These files shall not be edited directly by respective users. Instead, they are edited by crontab by each user. To understand the syntax, one can refer to RedHat document for &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-Automating_System_Tasks"&gt;automating system tasks&lt;/a&gt;. Note that you can specify periodical jobs here with special syntax. For example, */5 at the minute slot indicates every five minutes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Running cron jobs can be allowed or disallowed for different users. For this purpose, use the &lt;em&gt;/etc/cron.allow&lt;/em&gt; and &lt;em&gt;/etc/cron.deny&lt;/em&gt; files. If the cron.allow file exists, a user must be listed in it to be allowed to use cron If the cron.allow file does not exist but the cron.deny file does exist, then a user must not be listed in the cron.deny file in order to use cron. If neither of these files exists, only the super user is allowed to use cron.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition, there is a system-wide crontab file in /etc/crontab, in which you need to not only specify tasks, but also the user to run those tasks. By default, the schedule in this file is empty.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The limitation of cron is it assumes the servers is up all the time, if a script misses the schedule while the server is down, it will not be executed when the server comes back up. This is where anacron comes in handy. Although anacron can only be used by superuser, it doesn&amp;#8217;t expect system to be running 24&amp;#215;7. If a job is scheduled at a time system is down, it starts the job when system comes back up. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both cron and anacron are run by systemd service named crond.service. Although they require different packages installed (cronie vs cronie-anacron). They are also managed by different sets of files as explained below:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&#10;&lt;table id="tablepress-4" class="tablepress tablepress-id-4 tbody-has-connected-cells"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;th class="column-1"&gt;package&lt;/th&gt;&lt;th class="column-2"&gt;file or directory&lt;/th&gt;&lt;th class="column-3"&gt;purpose&lt;/th&gt;&lt;th class="column-4"&gt;example&lt;/th&gt;&#10;&lt;/tr&gt;&#10;&lt;/thead&gt;&#10;&lt;tbody class="row-striping row-hover"&gt;&#10;&lt;tr class="row-2"&gt;&#10;&#9;&lt;td rowspan="2" class="column-1"&gt;cronie&lt;/td&gt;&lt;td class="column-2"&gt;/var/spool/cron/&lt;/td&gt;&lt;td class="column-3"&gt;this directory accommodates files that represents cron jobs for each individual users.&lt;/td&gt;&lt;td class="column-4"&gt;if a file named digihunch contains a valid line, it means that Linux user digihunch has a scheduled task for the time specified.&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-2"&gt;/etc/crontab&lt;/td&gt;&lt;td class="column-3"&gt;This file keeps system-wide cronjob entries. Each line needs to sepcify users.&lt;/td&gt;&lt;td class="column-4"&gt;if a line specifies schedule, user and command, it means that at the scheduled time, that user will execute the command.&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td rowspan="3" class="column-1"&gt;cronie-anacron&lt;/td&gt;&lt;td class="column-2"&gt;/var/spool/anacron/&lt;/td&gt;&lt;td class="column-3"&gt;This directory accommodates files such as cron.daily, in which a timestamp is kept to indicate last execution time.&lt;/td&gt;&lt;td class="column-4"&gt;if cron.daily in this directory reads 20180418, it indicates last daily execution time stamp is 20180418&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td class="column-2"&gt;/etc/anacrontab&lt;/td&gt;&lt;td class="column-3"&gt;This file tells anacron where in the file system to go for directories for periodical jobs.&lt;/td&gt;&lt;td class="column-4"&gt;Example:&lt;br /&gt;&#10;&lt;code&gt;&lt;br /&gt;&#10;#period in days delay in minutes job-identifier command&lt;br /&gt;&#10;1 5 cron.daily nice run-parts /etc/cron.daily&lt;br /&gt;&#10;7 25 cron.weekly nice run-parts /etc/cron.weekly&lt;br /&gt;&#10;@monthly 45 cron.monthly nice run-parts /etc/cron.monthly&lt;br /&gt;&#10;&lt;/code&gt;&lt;br /&gt;&#10;The file usually also indicates RANDOM_DELAY and START_HOURS_RANGE&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td class="column-2"&gt;/etc/cron.hourly/&lt;br /&gt;&#10;/etc/cron.daily/&lt;br /&gt;&#10;/etc/cron.weekly/&lt;br /&gt;&#10;/etc/cron.monthly/&lt;/td&gt;&lt;td class="column-3"&gt;These directories stores script files that anacron needs to execute at different intervals. this is configured in /etc/anacrontab&lt;/td&gt;&lt;td class="column-4"&gt;if script logrotate is present in /etc/cron.daily/, it means the script is to be executed daily&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-4 from cache --&gt;&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;An anacron example: logrotate&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Rotating logs is a common task in Linux that can be done by logrotate. To understand how this works, first, make sure cronie-anacron package is installed and crond.service is up. Then examine the /etc/anacrontab file:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/anacrontab: configuration file for anacron&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:#75715e"&gt;# See anacron(8) and anacrontab(5) for details.&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;SHELL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/bin/sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/sbin:/bin:/usr/sbin:/usr/bin&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAILTO&lt;span style="color:#f92672"&gt;=&lt;/span&gt;root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# the maximal random delay added to the base delay of the jobs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RANDOM_DELAY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;45&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# the jobs will be started during the following hours only&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;START_HOURS_RANGE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;3-22&#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:#75715e"&gt;#period in days delay in minutes job-identifier command&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; cron.daily nice run-parts /etc/cron.daily&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt; cron.weekly nice run-parts /etc/cron.weekly&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@monthly &lt;span style="color:#ae81ff"&gt;45&lt;/span&gt; cron.monthly nice run-parts /etc/cron.monthly&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This indicates that daily, weekly and monthly jobs are active. Go into /etc/cron.daily/, and examine script logrotate:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/sh&#10;&lt;/span&gt;&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;/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;EXITVALUE&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:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; $EXITVALUE !&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /usr/bin/logger -t logrotate &lt;span style="color:#e6db74"&gt;&amp;#34;ALERT exited abnormally with [&lt;/span&gt;$EXITVALUE&lt;span style="color:#e6db74"&gt;]&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;exit &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This indicates that logrotate loads configuration from /etc/logrotate.conf, the man page of logrotate explains how this configuration works, along with an example. If you have any custom application where the log file needs rotated, it can be configured in this file.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/04/basics-of-linux-os/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Basics Terms in Linux OS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/06/text-processing-with-linux-bash/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Linux Admin Basics 3 of 3 – text processing, regex, sed &amp;amp; awk&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Basics Terms in Linux OS</title><link>https://www.digihunch.com/2018/04/basics-of-linux-os/</link><pubDate>Tue, 24 Apr 2018 19:16:00 -0400</pubDate><guid>https://www.digihunch.com/2018/04/basics-of-linux-os/</guid><description>&lt;p class="wp-block-paragraph"&gt;These are the things quite confusing or abstract while I was at school but now makes lots of sense after many years working with different flavours of OS.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;&lt;strong&gt;GPL and BSD as software license types&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main difference is that BSD (Berkeley Software Distribution) is a permissive (non-protective) license, while GPL(GNU General Public License) is a copyleft (protective) license.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Permissive licenses do not protect the code from being used in non-open source apps and apply no restrictions on the derivatives, while copyleft licenses force the creator of derivatives or re-distributor of the software to open the modified code. Under GPL you can&amp;#8217;t sub-license, meaning, you can’t change any of the original license terms or introduce any of your own. You’re also required to state all the changes you make to the original code. That is why components licensed under GPL and other copyleft licenses should be avoided in commercial products that would later be distributed under proprietary licenses.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The BSD license family (including the Modified BSD License), on the other hand, doesn’t compel you to do any of the above. They have fairly relaxed redistribution terms.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;&lt;strong&gt;GNU/Linux and BSD as operating systems&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Unix &lt;/strong&gt;&amp;#8211; the name of the original system designed at AT&amp;amp;T in the 1970s. At the time, it featured a great deal of novelties such as multi-tasking, multi-user support, time sharing, etc. It was made portable by using C language.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Linux Kernel&lt;/strong&gt; &amp;#8211; a free, open-source, monolithic, Unix-like OS kernel. It was conceived and created in 1991 by Linus Torvalds for his personal computer. The word Linux, technically, is just the kernel. By itself, Linux (Kernel) has no place for user to land (e.g. no apps, no commands)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;GNU/Linux&lt;/strong&gt; &amp;#8211; GNU project has developed a comprehensive set of free software tools for use with Unix and Linux. GNU/Linux involves the software tools along with the Linux Kernel.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;GNU/Linux distribution&lt;/strong&gt; &amp;#8211; ready-to-use full OS, including Linux Kernel, GNU library and tools, whose developers have made a commitment to follow GNU GFSD, X Window and desktop environment (e.g. KDE, GNOME) and includes many pieces of software. GNU/Linux distro is what many people refer to as &amp;#8220;Linux&amp;#8221;, and it includes Debian, Ubuntu, RedHat and CentOS. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;BSD (Berkeley Software Distribution) &amp;#8211; an OS based on Research Unix, originally developed at Bell Labs, eventually grown into a complete operating system. Today, &amp;#8220;BSD&amp;#8221; often refers to its decendants, such as FreeBSD, OpenBSD, NetBSD, or DragonFly BSD. Each of these are both a kernel and an operating system. Another famous BSD descendant is Darwin, which is what Mac OS X based on.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Comparing &amp;#8220;BSD&amp;#8221; with &amp;#8220;Linux&amp;#8221;&lt;/strong&gt; &amp;#8211; Linux is more popular and tends to support new hardware sooner. Typical users usually don&amp;#8217;t feel the difference between them. FreeBSD as desktop OS uses the same GNOME, KDE, or Xfce desktop environments that many flavours of Linux use as well. Although you need to install the desktop environment yourself. Another important difference is the licensing model as mentioned above between GPL and BSD. This article is a good reference about all the differences bewteen &amp;#8220;BSD&amp;#8221; and &amp;#8220;Linux&amp;#8221;&lt;br&gt; https://www.educba.com/linux-vs-bsd/&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;&lt;strong&gt;Swap, Cache and Buffer&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Swap &lt;/strong&gt;&amp;#8211; swap file or swap partition. The primary function is to substitute disk space for RAM memory when real RAM fills up and more space is needed. The kernel uses a memory management program that detects blocks, aka pages, of memory in which the contents have not been used recently. The memory management program swaps enough of these relatively infrequently used pages of memory out to a special partition on the hard drive specifically designated for “paging”, or swapping. This frees up RAM and makes room for more data to be entered into your spreadsheet. Those pages of memory swapped out to the hard drive are tracked by the kernel’s memory management code and can be paged back into RAM if they are needed.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;Swapping &lt;/strong&gt;moves entire process between main memory and secondary storage; this is the original Unix method and can cause severe performance loss;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Paging &lt;/strong&gt;moves small unites of memory (i.e. pages with 4Kbytes). It is more efficient and was added to BSD&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In both cases, least recently used memory is moved to secondary storage and back to main memory only when needed again. In Linux, the term swapping is used to refer to paging. Older Unix-style swapping of entire thread and process is no longer supported.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;CPU Cache&lt;/strong&gt; &amp;#8211; a hardware cache used by CPU to reduce the average time to access data from the main memory. A cache is a smaller, faster memory, located closer to a processor core, which stores copies of the data from frequently used main memory locations. Most CPUs have different independent caches, inclusing insructions and data caches, where the data cache is usually organized as a hierarchy of more cache levels (L1, L2, L3, L4, etc)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Page Cache (or disk cache)&lt;/strong&gt; &amp;#8211; kept by the OS in computer&amp;#8217;s main memory and controlled by the computer. The OS keeps a page cached in otherwise unused portions of the main memory, resulting in quicker access to the contents of cached pages and overall performance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Disk Buffer&lt;/strong&gt; &amp;#8211; (or ambiguously called disk cache or cache buffer) the embedded memory in a hard disk drive acting as a buffer between the rest of the computer and the physical hard disk platter that is used for storage. Modern hard disk drives come with 8 to 256 MiB of such memory. Disk buffer is physically distinct from and is used differently from page cache. It is controlled by the microcontroller in the hard disk drive.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;GRUB (GNU GRand Unified Bootloader)&lt;/strong&gt; &amp;#8211; a boot loader package from GNU project. It is predominantly used for Unix-like systems. Current version is GRUB2&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;System call&lt;/strong&gt; &amp;#8211; In computing, a system call is the programmatic way in which a computer program requests a service from the kernel of the operating system it is executed on. This may include hardware-related services (for example, accessing a hard disk drive), creation and execution of new processes, and communication with integral kernel services such as process scheduling. System calls provide an essential interface between a process and the operating system. Below is a list of key system calls:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;System Call&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;read()&lt;/td&gt;&lt;td&gt;read bytes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;write()&lt;/td&gt;&lt;td&gt;write bytes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;open()&lt;/td&gt;&lt;td&gt;open a file&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;close()&lt;/td&gt;&lt;td&gt;close a file&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fork()&lt;/td&gt;&lt;td&gt;create a new process&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;exec()&lt;/td&gt;&lt;td&gt;execute a new program&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;connect()&lt;/td&gt;&lt;td&gt;connect to a network host&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;accept()&lt;/td&gt;&lt;td&gt;accept a network connection&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;stat()&lt;/td&gt;&lt;td&gt;fetch file statistics&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ioctl()&lt;/td&gt;&lt;td&gt;set I/O properties, or other miscellaneous functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mmap()&lt;/td&gt;&lt;td&gt;map a file to the memory address space&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;brk()&lt;/td&gt;&lt;td&gt;extend the heap pointer&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;a href="http://man7.org/linux/man-pages/man2/syscalls.2.html"&gt;strace &lt;/a&gt;&lt;/strong&gt;is the tool to trace system calls and signals in Linux. &lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;&lt;strong&gt;sysVinit, runit and systemd&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;init &lt;/strong&gt;&amp;#8211; in Unix-based OS, init is the first process started during booting of OS. Init is a daemon process that continues running until the system is shutdown. It is the direct or indirect ancestor of all other processes and automatically adopts all orphaned processes. Init is started by the kernel during the booting process; a kernel panic will occur if the kernel is unable to start it. Init is typically assigned process identifier 1 and its job is to start other programs that are essential to the operation of your system. All other processes are descended from init.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;init systems&lt;/strong&gt;&lt;br&gt; Linux has several options as init systems. For example: sysvinit, runit, systemd and upstart. Here is a comparison of commands involved in managing each. &lt;br&gt; http://unix.cafe/wp/en/2017/07/howto-manage-a-service-in-systemd-sysvinit-upstart-runit-and-openrc/&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;SysV init&lt;/strong&gt; &amp;#8211; System initialization process is handled by the init daemon. One of the original daemon is SysVinit, which is a collection of System V-style init programs. init process starts serially. It is a run-once process during the start of the OS. One task starts only after the last task startup was successful and it was loaded in the memory. This often resulted in delayed and long booting time. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Runit &lt;/strong&gt;&amp;#8211; an init scheme for Unix-like operating systems that initializes, supervises, and ends processes throughout the operating system. It is a replacement of sysvinit and features brevity and simplicity.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Systemd &lt;/strong&gt;&amp;#8211; A init replacement daemon designed to start process in parallel, implemented in a number of standard distribution – Fedora, OpenSuSE, Arch, RHEL, CentOS, etc. Its flexibility comes with more complexity. It is an event driven init system, that not only starts stuff at boot (hence managing dependencies), but also after that. It also keeps track of many things after boot, such as mounts, availability of services, integration with resource management, etc. Because of that, systemd is also good at logging and monitoring. Systemd allows services to start when:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;the system boots&lt;/li&gt;&lt;li&gt;a hardware components attaches to the system&lt;/li&gt;&lt;li&gt;other service started&lt;/li&gt;&lt;li&gt;a timer fires&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To determine which system initialization method your current Linux distribution is using (SysVinit or systemd), simply check process id 1:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt; $ ps -p 1&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading"&gt;&lt;strong&gt;Soft link and hard link&lt;/strong&gt;&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;inode &lt;/strong&gt;&amp;#8211; stores the attributes and disk block locations of a file or directory&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Soft/Symbolic link&lt;/strong&gt; &amp;#8211; essentially a shortcut to another file. The link itself is a separate file, pointing to the destination file or directory. The inode of the file is different from that of the symbolic link. Deleting the destination file will leave the symbolic link file a &amp;#8220;dangling link&amp;#8221;. Symbolic file may also have different permissions from the destination file or directory.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Hard link&lt;/strong&gt; &amp;#8211; essentially an alias of a file. The link itself is not a separate file, and the destination can only be a file (no directory). The inode of the link the the same as the file itself. So there is actually no distinction between destination file and link. Both files are equal. If you delete the file, the link continue to work until the number of hard links to the file becomes zero.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Soft link points to a file by name whereas hard link points by inode number.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/04/centos-remove-swap-safely/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;CentOS – Remove Swap safely&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/05/cron-and-logrotate-in-centos/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;cron and anacron in RedHat Linux (How logrotate works)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>CentOS – Remove Swap safely</title><link>https://www.digihunch.com/2018/04/centos-remove-swap-safely/</link><pubDate>Sat, 14 Apr 2018 16:15:43 -0400</pubDate><guid>https://www.digihunch.com/2018/04/centos-remove-swap-safely/</guid><description>&lt;p class="wp-block-paragraph"&gt;If the default installation has swap on, you will see it from block device list:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@server /&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lsblk&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sda 8:0 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 500G &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; disk&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├─sda1 8:1 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 1G &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; part /boot&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└─sda2 8:2 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 499G &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; part&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├─centos-root 253:0 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 50G &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; lvm /&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├─centos-swap 253:1 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 7.9G &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; lvm &lt;span style="color:#f92672"&gt;[&lt;/span&gt;SWAP&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; └─centos-home 253:2 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 441.1G &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; lvm /home&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sr0 11:0 &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 1024M &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; rom&#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;root@server /&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# free -h&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total used free shared buff/cache available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mem: 7.6G 217M 7.2G 11M 206M 7.2G&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Swap: 7.9G 0B 7.9G&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Many installations require swap to be off for performance reasons (although some advocate turning off &lt;a href="https://www.howtogeek.com/449691/what-is-swapiness-on-linux-and-how-to-change-it/"&gt;swappiness&lt;/a&gt; of the application, instead of removing swap partition from operating system, which is a separate topic). This can be turned off by a simple 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;swapoff -a&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then if you run free command after rebooting, you will see the Swap is set to 0G. Great. But lsblk and df commands still shows the space being used by swap. This disk space is not being used at all.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;root@c7v-vitvcast02 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# free -m&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total used free shared buff/cache available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mem: &lt;span style="color:#ae81ff"&gt;7802&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;237&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7380&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;185&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7333&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Swap: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#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;root@c7v-vitvcast02 ~&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# lvdisplay&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --- Logical volume ---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Path /dev/centos/swap&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Name swap&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VG Name centos&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV UUID 7zHtvT-zGYn-sNBc-JRlT-nTfU-O9hN-r07y3J&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Write Access read/write&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Creation host, time localhost, 2018-04-07 10:03:21 -0500&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Status available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# open 0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Size &amp;lt;7.88 GiB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Current LE &lt;span style="color:#ae81ff"&gt;2016&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Segments &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Allocation inherit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Read ahead sectors auto&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - currently set to &lt;span style="color:#ae81ff"&gt;8192&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Block device 253:1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --- Logical volume ---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Path /dev/centos/home&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Name home&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VG Name centos&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV UUID 4XMTa7-uR44-qH0u-Oag4-PDyK-Z1BC-3oUSiz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Write Access read/write&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Creation host, time localhost, 2018-04-07 10:03:22 -0500&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Status available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# open 0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Size &amp;lt;441.12 GiB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Current LE &lt;span style="color:#ae81ff"&gt;112926&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Segments &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Allocation inherit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Read ahead sectors auto&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - currently set to &lt;span style="color:#ae81ff"&gt;8192&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Block device 253:2&#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; --- Logical volume ---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Path /dev/centos/root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Name root&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VG Name centos&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV UUID tkMCM4-cWxV-FaAC-hgJx-r1vS-DiY5-fyZvjt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Write Access read/write&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Creation host, time localhost, 2018-04-07 10:03:22 -0500&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Status available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# open 1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LV Size 50.00 GiB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Current LE &lt;span style="color:#ae81ff"&gt;12800&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Segments &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Allocation inherit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Read ahead sectors auto&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - currently set to &lt;span style="color:#ae81ff"&gt;8192&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Block device 253:0&#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 then remove the line for swap from /etc/fstab, this will keep the OS from mounting it upon reboot. However, the logical volume for swap is there, as a partition that&amp;#8217;s not exposed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you need to reclaim space from swap partition, delete the logical volume:&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;lvremove /dev/centos/swap&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Do you really want to remove active logical volume centos/swap? &lt;span style="color:#f92672"&gt;[&lt;/span&gt;y/n&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: y&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Logical volume &lt;span style="color:#e6db74"&gt;&amp;#34;swap&amp;#34;&lt;/span&gt; successfully removed&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now, using command &lt;em&gt;lvdisplay&lt;/em&gt; or &lt;em&gt;lvs&lt;/em&gt;, the swap space is not displayed anymore. So I rebooted the server in the hopes that the swap space is not being presented and everything is hunky-dory. But no&amp;#8230; the server doesn&amp;#8217;t boot up at all. It took a long time at the splash screen only to enter dracut, where /boot isn&amp;#8217;t present.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On further reading, this is because the grub file is still referencing /dev/centos/swap somewhere. I need to somehow get to the grub file and fix that before it can reboot again. Specifically, I pressed &amp;#8220;e&amp;#8221; at the menu, which gave me an opportunity to edit grub file. From there I removed section for swap. Then I saved it with &amp;#8220;Ctrl + X&amp;#8221; (as suggested on the screen) so the OS booted into normal mode. Alternatively, I entered &lt;a href="https://www.tecmint.com/boot-into-single-user-mode-in-centos-7/"&gt;single user mode&lt;/a&gt; in order to get to the point I can edit the grub file.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once booted, I have to make change to the actual grub file at /boot/grub2/grub.cfg so the change persists. In the file, locate the lines starting with linux16 and remove the section &amp;#8220;rd.lvm.lv=centos/swap&amp;#8221;. There are two appearances of this section on my machine. Then the server will boot just normally.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, the clean way to turn off swap on CentOS involves the following steps:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&lt;li&gt;turn off swap&lt;/li&gt;&lt;li&gt;Remove swap mount from /etc/fstab (and umount)&lt;/li&gt;&lt;li&gt;remove the logical volume for swap&lt;/li&gt;&lt;li&gt;remove reference to swap from grub&lt;/li&gt;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Out of those steps, 3 and 4 are needed to reclaim swap spaces. They must be done together. If you rebooted the server before step 4. The server will not be able to boot complaining about missing swap.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/03/cassandra-architecture-summary/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cassandra Architecture&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/04/basics-of-linux-os/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Basics Terms in Linux OS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>TCPdump and Wireshark configuration</title><link>https://www.digihunch.com/2018/02/tcpdump-and-wireshark/</link><pubDate>Wed, 28 Feb 2018 18:30:06 -0400</pubDate><guid>https://www.digihunch.com/2018/02/tcpdump-and-wireshark/</guid><description>&lt;p class="wp-block-paragraph"&gt;This article explains how to troubleshoot TCP packet from Linux (CentOS) and Windows with TCP dump and wireshark. Both are important tools for troubleshooting. If you are troubleshooting a Windows server and have access to it to install Wireshark then there is nothing to worry about. Even if the server to troubleshoot is a Linux one with proper desktop (KDE/GNOME), you may still install the Wireshark UI on it and work from the server. If the server is Linux without any UI, this is where this article is trying to help because you need to run tcpdump on the server and somehow download the capture to your local computer for analysis.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you work off of a MacOS, and need to capture in real time from a Linux server without a desktop (KDE/GNOME), then the best bet is to run tcpdump remotely from the server and pipe the result into Wireshark. This would require root access to the server. Tcpdump will require libpcap and tcpdump packages. Then from MacBook you can 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;&lt;span style="color:#75715e"&gt;# ssh root@remote-server &amp;#34;tcpdump -w - -s0 -pi eth0 dst port 443 or src port 443&amp;#34;|wireshark -k -i -&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 will pipe the tcpdump result into Wireshark session in Mac in real time with a delay.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you work off a Windows computer where plink.exe is available, you can run the following command if you know the root password:&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;C:&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;ools&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;link.exe -l root -pw rootpassword 192.168.117.12 -P &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tcpdump -w - -s0 -pi eth0 dst port 9042&amp;#34;&lt;/span&gt; |&lt;span style="color:#e6db74"&gt;&amp;#34;C:\Program Files\Wireshark\Wireshark.exe&amp;#34;&lt;/span&gt; -k -i -&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Both tricks above assumes that you have direct root log-in to the server, by RSA key or password. It is because running tcpdump requires root access on the server. It is not a good security practice to run tcpdump with a non-root user because it needs to scan the interface.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;-s: snap length in bytes. Setting to 0 is making it use default 65535&lt;/li&gt;&lt;li&gt;-i: specify the interface to listen on. e.g. eth0 or ens192&lt;/li&gt;&lt;li&gt;-p: no-promiscuous mode. this option asks tcpdump to not put interface in promiscuous mode&lt;/li&gt;&lt;li&gt;-w: write the raw packets to file rather than parsing and printing them out. a hyphen indicates standard output here.&lt;/li&gt;&lt;li&gt;-Z: drops the privileges of root and changes ownership to the specified user&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you do not have direct root login access, but you can log in as a different user and su to root, you may run this once you are on root user:&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;echo &amp;#34;###Capture Begin: $(date &amp;#39;+%Y %b %d %H:%M:%S&amp;#39;)&amp;#34; &amp;amp;&amp;amp; tcpdump dst port 1524 or src port 1524 -s 0 -i eth0 -w &amp;#34;/tmp/cap.$(date +%Y%m%d_%H%M%S).cap&amp;#34; -Z linuser &amp;amp;&amp;amp; echo &amp;#34;###Capture End: $(date &amp;#39;+%Y %b %d %H:%M:%S&amp;#39;)&amp;#34; &amp;amp;&amp;amp; ls -ltr /tmp/cap*.cap&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To stop capture, you can use Ctrl-C but make sure that is passed to the server terminal or you will leave a zombie tcpdump process&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2018/02/linux-tips-and-tricks-in-shell/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Linux Admin Basics 1 of 3 – Bash&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2018/03/bash-tricks-continued/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Linux Admin Basics 2 of 3 – shell scripting&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Linux Admin Basics 1 of 3 – Bash</title><link>https://www.digihunch.com/2018/02/linux-tips-and-tricks-in-shell/</link><pubDate>Fri, 02 Feb 2018 21:35:00 -0400</pubDate><guid>https://www.digihunch.com/2018/02/linux-tips-and-tricks-in-shell/</guid><description>&lt;p class="wp-block-paragraph"&gt;This article summarized my time-saving Linux tips, mostly with CentOS environment.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-bash-shortcuts"&gt;Bash Shortcuts&lt;/h3&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2020/03/bash.jpg" alt="" class="wp-image-809" width="547" height="215"/&gt;&lt;figcaption class="wp-element-caption"&gt;Bash shortcut illustration&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These shortcuts save a lot of arrow keystrokes. Some of them may require some tweaking to work on MacOS. &lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; w&lt;/td&gt;&lt;td&gt;delete word&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; a&lt;/td&gt;&lt;td&gt;cursor to end of line&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; e&lt;/td&gt;&lt;td&gt;cursor to beginning of line&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; k&lt;/td&gt;&lt;td&gt;delete to end of line&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; u&lt;/td&gt;&lt;td&gt;delete to beginning of line&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; l&lt;/td&gt;&lt;td&gt;clear screen&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;cd &amp;#8211; &lt;/td&gt;&lt;td&gt;change to previous directory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;alt &amp;#8211; b&lt;/td&gt;&lt;td&gt;move cursor back by a word&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;alt &amp;#8211; f&lt;/td&gt;&lt;td&gt;move cursor forward by a word&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;alt &amp;#8211; .&lt;/td&gt;&lt;td&gt;type last parameter of previous command&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-job-control"&gt;Job Control&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We need to first understand the following job states:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes color:black"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Actively running&lt;/td&gt;&lt;td&gt;Displayed in Active Session&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;foreground job&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;suspended job&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;background job&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;These states can be managed by the following shortcut keys:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; z&lt;/td&gt;&lt;td&gt;send active job to suspended&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ctrl &amp;#8211; c&lt;/td&gt;&lt;td&gt;send SIGINT to active job to kill it&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;jobs&lt;/td&gt;&lt;td&gt;list jobs with id&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bg&lt;/td&gt;&lt;td&gt;bring a job to background. the current shell still &amp;#8220;owns&amp;#8221; the job&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fg&lt;/td&gt;&lt;td&gt;bring a job to foreground (by id)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;disown&lt;/td&gt;&lt;td&gt;remove job from current shell&amp;#8217;s job table. the job is still running and can be found by ps command&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kill&lt;/td&gt;&lt;td&gt;kill a job by id. the job is no longer running and won&amp;#8217;t be found by ps command&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Note 1: running a command &lt;strong&gt;with ampersand(&amp;amp;) at the end starts the process and pushes it to the background&lt;/strong&gt;, so you can continue typing;&lt;/li&gt;&#10;&lt;li&gt;Note 2: an example is to start vim with a file, ctrl-z to push to background, jobs to view, fg + job id to bring it back&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-share-screen"&gt;Share Screen&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Different persons may share screen on Linux shell and interact with each other. To do so, everyone need to log on the same server as the same linux user. Then the first person runs:&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;screen -S screen_name&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then the second (and third, etc) person runs:&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;screen -x screen_name&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now everyone can collaborate by seeing what each other is doing and type at the same time. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-i-o-redirection"&gt;I/O redirection&lt;/h3&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;file descriptor&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;input or output expression&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;stdin&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;to read file: 0&amp;lt; or &amp;lt; for short&lt;br&gt;to read a single line: &amp;lt;&amp;lt;&amp;lt;&lt;br&gt;to read multiple lines: &amp;lt;&amp;lt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;stdout&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;to overwrite: 1&amp;gt; or &amp;gt; for short&lt;br&gt;to append: 1&amp;gt;&amp;gt; or &amp;gt;&amp;gt; for short&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;stderr&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;to overwrite: 2&amp;gt;&lt;br&gt;to append: 2&amp;gt;&amp;gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, here is how you display all lines between &amp;#8220;ANYWORD&amp;#8221; (aka &amp;#8220;here document&amp;#8221;):&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;cat &amp;lt;&amp;lt;ANYWORD&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;paragraph&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;after&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;paragraph&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ANDWORD&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In addition, to redirect between commands, pipe (|) is used. You may also redirect stdout into stderr or the other way round. Below are several examples:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;command1 | command2&lt;/td&gt;&lt;td&gt;redirect stdout of command1 to stdin of command2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;command3 &amp;gt; /dev/null&lt;/td&gt;&lt;td&gt;get rid of stdout from command3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;command4 2&amp;gt; file1&lt;/td&gt;&lt;td&gt;redirect stderr of command4 to file1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;command5 | tee file2&lt;/td&gt;&lt;td&gt;redirect stdout of command5 to stdin for tee command, which display stdout and write to file2 at the same time&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;command6 2&amp;gt;&amp;amp;1&lt;/td&gt;&lt;td&gt;redirect command6&amp;#8217;s stderr into stdout. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;command7 1&amp;gt;&amp;amp;2&lt;/td&gt;&lt;td&gt;redirect command7&amp;#8217;s stdout into stderr&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note: in the last two examples, the ampersand just indicates the following number is file descriptor instead of file name. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, in the first command below, stderr is redirected to stdout, and stdout goes to file. The second and third commands are just two forms of shortcut for the first:&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;command &amp;gt; file 2&amp;gt;&amp;amp;1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;command &amp;gt;&amp;amp; file&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;command &amp;amp;&amp;gt; file&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Here is an advanced example to compare two files from two different servers:&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;diff &amp;lt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;ssh user@host1 &lt;span style="color:#e6db74"&gt;&amp;#39;cat /tmp/file1.txt&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;ssh user@host2 &lt;span style="color:#e6db74"&gt;&amp;#39;cat /tmp/file2.txt&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With the knowledge of I/O redirection, let&amp;#8217;s compare the efficiency of the following two commands in dealing with a huge file:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;cat huge.file | mycmd&lt;/li&gt;&#10;&lt;li&gt;mycmd &amp;lt; huge.file&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the first command, cat first causes an I/O read of huge.file, then stdout gets written into pipe buffer. Lastly, the mycmd reads it from its stdin. This is an example of inefficiency. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the second command, huge.file is provided as stdin to my cmd. Only one read is involved as compared with two read and one write in the previous command. The second command is expected to be three times as fast as the first.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is a diagram to illustrate command and I/O redirection:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="723" height="240" src="https://www.digihunch.com/wp-content/uploads/2020/09/image.png" alt="" class="wp-image-1603"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Check out the page for more scenarios. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-operators"&gt;Operators&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, let&amp;#8217;s introduce some special shell variable and operators:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e7f5fe"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;IFS&lt;/td&gt;&lt;td&gt;A special variable indicating internal field separator&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;;&lt;/td&gt;&lt;td&gt;The ; token just separates commands to run. Use it when you&amp;#8217;d like to combine multiple lines of commands into a single line&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;amp;&amp;amp;&lt;/td&gt;&lt;td&gt;Logical AND operator. When you run command1 &amp;amp;&amp;amp; command2, command2 ONLY runs if command1 returns true (success, or exit status = 0).&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;||&lt;/td&gt;&lt;td&gt;Logical OR operator. When you run command1 &amp;amp;&amp;amp; command2, command2 ONLY runs if command1 does not return false (fail, or exit status != 0)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;{}&lt;/td&gt;&lt;td&gt;command combination operator. e.g. {command1;command2}&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;()&lt;/td&gt;&lt;td&gt;precedence operator&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note the use of &amp;amp;&amp;amp; and || are fairly common. &amp;amp;&amp;amp; is used when you want to use the first command to provide a good status for the second. || is used when you want to report error about the first command. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test -d &amp;#34;/tmp/newdir&amp;#34; || mkdir -p &amp;#34;/tmp/newdir&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test -f &amp;#34;/var/run/app/app.pid&amp;#34; || echo &amp;gt;&amp;amp;2 &amp;#34;ERROR: cannot detect pid&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading"&gt;Attributes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ve been using &lt;strong&gt;chown &lt;/strong&gt;and &lt;strong&gt;chmod &lt;/strong&gt;to manipulate owners and file permissions against owners, groups and other users. I recently realized another command &lt;strong&gt;chattr &lt;/strong&gt;(change attribute) which controls file attributes, regardless of users or groups. In other words, if you change the attribute of a file to immutable, then even the owner isn&amp;#8217;t able to change it. Its manual has full list of attributes, but common ones are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;a -&amp;gt; Append only&lt;/li&gt;&#10;&lt;li&gt;i -&amp;gt; Immutable&lt;/li&gt;&#10;&lt;li&gt;c -&amp;gt; File automatically compressed in kernel.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, the append only attribute is commonly used for log file to prevent any other users from modifying the file. Sometimes we want to keep system files from being changed as well:&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;chattr -i /etc/resolv.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &amp;#34;nameserver 8.8.8.8&amp;#34; &amp;gt; /etc/resolv.conf &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chattr +i /etc/resolv.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Whatever attribute that we set with &lt;strong&gt;chattr&lt;/strong&gt;, we can use &lt;strong&gt;lsattr &lt;/strong&gt;to list them. These two commands are in Linux not BSD.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-loop"&gt;Loop&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you can tell how many iterations, then use for loop. For example, you want to execute a command for each file in a directory:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; file in /tmp/*.hl7&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Picking File &lt;/span&gt;$file&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; output.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hl7snd -f &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$file&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -d destionatiohost:2398 &amp;gt;&amp;gt; output.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&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;or you may specify range and steps for a for loop: &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i in &lt;span style="color:#f92672"&gt;{&lt;/span&gt;1..11..2&lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;13..23..2&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:#66d9ef"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;hostmachine&amp;#34;&lt;/span&gt;$i&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scp local.file &lt;span style="color:#e6db74"&gt;&amp;#34;hostmachine&amp;#34;&lt;/span&gt;$i:/tmp/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that the way we use step here is only available on Bash 4.x (check version through variable $BASH_VERSION, reference)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If it&amp;#8217;s hard to tell how many iterations, then use while loop. Here are some examples&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Example 1. You want to execute the same command multiple times, each with a line in a file as parameter:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; IFS&lt;span style="color:#f92672"&gt;=&lt;/span&gt; read -r var&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;date +%Y%m%d:%H%M%S&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; displaying line &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$var&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; resend.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; &amp;lt; list.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Example 2. One step further, if the input file is a CSV format 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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;acc1,pid1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;acc2,pid2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;acc3,pid3&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;And you want to repeat a command multiple times, each execution with a column 1 as parameter 1, column 2 as parameter 2, etc. Then you can run something like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IFS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;,&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo -en &lt;span style="color:#e6db74"&gt;&amp;#34;\r\n\b&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; read -r f1 f2; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo processing $f1, $f2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -i -X PUT -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type:application/json&amp;#34;&lt;/span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;userId&amp;#34;:&amp;#34;dhunch&amp;#34;}&amp;#39;&lt;/span&gt; --url http://localhost:9876/monitoring/document/$f1/$f2/publish;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; &amp;lt; input.txt &amp;gt;&amp;gt;out.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example IFS assignment is done before while loop. So it will take effect outside of while loop. This is why \r, \n and \b are specified in addition to comma. If this is not something you like, you may put IFS assignment after while and only specify comma as field separator.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Example 3. If you just want to repeat the same command every 5 seconds, apart from watch command, you can leverage while loop doing something like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; true; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; df -Ph | grep &lt;span style="color:#e6db74"&gt;&amp;#34;sda1&amp;#34;&lt;/span&gt;; sleep 10; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In fact in all these examples while loop can be expressed in a single line, with semicolon to indicate where you would have typed enter.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-find-with-xargs-and-exec"&gt;find, with xargs and -exec {}&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The find command offers flexibility searching files with certain conditions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Example 1. Find files from within last 5 * 24 hours in current directory:&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;find -maxdepth &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; -type f -mtime -5&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Example 2. Find tar files older than 6 * 24 hours:&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;find . -type f -name &lt;span style="color:#e6db74"&gt;&amp;#34;*.tar&amp;#34;&lt;/span&gt; -mtime +6&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Notes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;the switch -iname is similiar to -name but case insensitive&lt;/li&gt;&#10;&lt;li&gt;the switch -mtime goes by modified datetime; -atime goes by access time&lt;/li&gt;&#10;&lt;li&gt;the switch -mmin measures in minutes&lt;/li&gt;&#10;&lt;li&gt;the swtich -daystart makes it measure time from the beginning of current day (instead of 24 hours from current time of current day)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.cyberciti.biz/faq/howto-finding-files-by-date/"&gt;Here&lt;/a&gt; are more information about find command, and &lt;a href="https://www.cyberciti.biz/faq/linux-unix-osxfind-files-by-date/"&gt;more examples&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To execute command per result from find, we have the options of xargs and -exec {}. Both build command based on parameter input, instead of I/O redirection. xargs is considered more efficient and it also works with commands other than find. -exec {} only works with find command. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here&amp;#8217;s a basic example of -exec {}&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;find . -iname &lt;span style="color:#e6db74"&gt;&amp;#39;*.dcm&amp;#39;&lt;/span&gt; -exec dcm2txt &lt;span style="color:#f92672"&gt;{}&lt;/span&gt; &lt;span style="color:#ae81ff"&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;On the other hand, xargs works with any command followed by a pipe. It takes input from previous command, split it by space or carriage return into a list, then build command with each item on the list. To help understand parameter passing with xargs, we examine two examples. The first example is a directory with three files in it: x.a, y.a, z.a&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;$ ls&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x.a x.b x.c&#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 want to prefix each file with pre, we can do the following:&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;ls | xargs -I aa echo &lt;span style="color:#e6db74"&gt;&amp;#34;mv aa prefix_aa&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;In the second example, suppose find command produces the following result:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;firstdir&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;seconddir&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Compare the following two commands:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;find . -type d | xargs ls -l&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;find . -type d | xargs -n &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; ls -l&#9;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the first command, xargs invokes &amp;#8220;ls -l firstdir seconddir&amp;#8221; whereas in the second command, xargs invokes &amp;#8220;ls -l firstdir&amp;#8221; and then &amp;#8220;ls -l seconddir&amp;#8221;. The first command requires that the utility takes multiple parameters (in this case, ls does. Other commands such as wc, grep also do). The second command is particular helpful when the &lt;span style="text-decoration: underline;"&gt;utility only takes one parameter&lt;/span&gt;. This is because the switch -n sets the maximum number of arguments taken from standard input for each invocation of utility.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Last but not least, operators are quite useful in Bash scripting. &lt;a href="https://kapeli.com/cheat_sheets/Bash_Test_Operators.docset/Contents/Resources/Documents/index"&gt;Here &lt;/a&gt;is a good reference.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;p&gt;&lt;a rel="next" href="https://www.digihunch.com/2018/02/tcpdump-and-wireshark/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;TCPdump and Wireshark configuration&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>