<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Log Analysis on Digi Hunch</title><link>https://static.digihunch.com/tag/log-analysis/</link><description>Recent content in Log Analysis on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Tue, 08 Apr 2025 14:34:33 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/log-analysis/index.xml" rel="self" type="application/rss+xml"/><item><title>Optimize CPU and Memory for Kubernetes Pod</title><link>https://static.digihunch.com/2023/01/optimize-cpu-and-memory-for-kubernetes-pods/</link><pubDate>Fri, 13 Jan 2023 11:47:00 -0400</pubDate><guid>https://static.digihunch.com/2023/01/optimize-cpu-and-memory-for-kubernetes-pods/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/cpu-feature.webp" alt="Featured image of post Optimize CPU and Memory for Kubernetes Pod" /&gt;&lt;p class="wp-block-paragraph"&gt;When optimizing workload performance, it is important to understand how on earth operating system allocates CPU and memory to processes. This helps understand how to set resource limit Kubernetes Pod in an optimal way.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cpu-resource-assignment"&gt;CPU resource assignment&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The OS distributes CPU resource to processes by the unit of time share of CPU time. Most of the time, many processes with CPU instructions (machine code) are waiting in the Job queue, for their share of CPU time in order to execute their instructions. As soon as CPU becomes idle, the CPU scheduler selects a process from the ready queue to run next:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="488" src="https://static.digihunch.com/wp-content/uploads/2023/01/cpu-assignment.webp" alt="" class="wp-image-12886" style="width:552px;height:auto" srcset="https://static.digihunch.com/wp-content/uploads/2023/01/cpu-assignment.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/01/cpu-assignment-300x143.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/01/cpu-assignment-768x366.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Ideally, OS should schedule CPU in a way that it should not waste any CPU cycle. It should also minimizes waiting time and response time of processes. At a high level, there are two types of CPU scheduling:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Preemptive: OS allocate CPU resources to a process for only a limited period of time and then takes those resources back. It could interrupt a running process to execute a higher priority process.&lt;/li&gt;&#10;&lt;li&gt;Non-preemptive: New processes are executed only after the current executing process has completed its execution.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.geeksforgeeks.org/preemptive-and-non-preemptive-scheduling/"&gt;Here&lt;/a&gt; is more information about preemptive and non-preemptive scheduling. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;CPU is compressible resource in Linux&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the &lt;a href="https://www.usenix.org/legacy/publications/library/proceedings/usenix01/freenix01/full_papers/alicherry/alicherry_html/node5.html#:~:text=All%20scheduling%20is%20preemptive%3A%20If,is%20a%20single%20run%2Dqueue."&gt;Linux&lt;/a&gt; world, all scheduling is preemptive. We also call it &lt;a href="https://en.wikipedia.org/wiki/Kernel_preemption"&gt;kernel preemption&lt;/a&gt;. As the wikipedia entry states: the&amp;nbsp;&lt;a href="https://en.wikipedia.org/wiki/Scheduling_(computing)"&gt;scheduler&lt;/a&gt;&amp;nbsp;is permitted to forcibly perform a&amp;nbsp;&lt;a href="https://en.wikipedia.org/wiki/Context_switch"&gt;context switch&lt;/a&gt;&amp;nbsp;(on behalf of a runnable and&amp;nbsp;&lt;a href="https://en.wikibooks.org/wiki/Operating_System_Design/Scheduling_Processes/Priority_Scheduling"&gt;higher-priority&lt;/a&gt;&amp;nbsp;process) on a driver or other part of the kernel during its execution, rather than&amp;nbsp;&lt;a href="https://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing"&gt;co-operatively&lt;/a&gt;&amp;nbsp;waiting for the driver or kernel function (such as a&amp;nbsp;&lt;a href="https://en.wikipedia.org/wiki/System_call"&gt;system call&lt;/a&gt;) to complete its execution and return control of the processor to the scheduler when done.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Linux scheduler implements a number of&amp;nbsp;&lt;em&gt;&lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/performance_tuning_guide/s-cpu-scheduler"&gt;scheduling policies&lt;/a&gt;&lt;/em&gt;, which determine when and for how long a thread runs on a particular CPU core. The scheduling policies in RHEL include real time policies such as SCHED_FIFO and SCHED_RR where processes have a sched_priority value in the range of 1 (low) to 99 (high); and normal policies such as SCHED_OTHER, SCHED_BATCH and SCHED_IDLE, where sched_priority (specified as 0) is not used in scheduling decisions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to understand preemptive CPU scheduling on Linux. When OS allocate CPU resource to a process for one time slot, it is not committed to the same process for the next time slot. The OS reserves the ability to revoke the next CPU use and re-assign it for processes of higher priority.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because of this, we regard CPU as a compressible resource. The compressible characteristic impacts how we optimize CPU utilization for a process, including setting CPU request and limit for Kubernetes workload. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Memory is non-compressible resource&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A few years ago, I discussed how to&lt;a href="https://static.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/"&gt; calculate memory usage&lt;/a&gt;. A process requests memory from OS using memory allocation functions (the &lt;a href="https://man7.org/linux/man-pages/man3/malloc.3.html"&gt;malloc&lt;/a&gt; family), and return memory to OS using &lt;a href="https://man7.org/linux/man-pages/man1/free.1.html"&gt;free&lt;/a&gt; functions. The design of Linux OS knows that processes have a tendency to request more memory than they use, which causes under-utilization. In combat against under-utilization, the Linux OS supports &lt;a href="https://en.wikipedia.org/wiki/Memory_overcommitment"&gt;memory overcommitment&lt;/a&gt; (on by default), allowing processes to request more memory than what is available. The processes have access to virtual memory space and the OS may swap some pages out to disks. The overcommitment mechanism also prevents processes from crashing due to insufficient memory assignment. The kernel can also OOM kill a process when the entire system is in a crisis.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Memory is non-compressible resource. When OS assigns memory pages to a process, the process has to right to keep those pages, until the OS takes them away. Unlike assigning CPU cycles, the assignment of memory pages to processes does not have an expiry time. This is the non-compressible characteristic of memory assignment. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;CPU limit and requests for Kubernetes workload&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It was considered best practice to set request and limit for memory and CPU. However, knowing CPU is compressible resource and memory isn&amp;#8217;t, we should re-consider this practice. In short, for CPU, we should set request only, &lt;a href="https://home.robusta.dev/blog/stop-using-cpu-limits"&gt;without setting limit&lt;/a&gt;. For memory, we should set &lt;a href="https://home.robusta.dev/blog/kubernetes-memory-limit"&gt;limit to exactly the same as request&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A process has different level of demands for CPU at different times. Depending on the activity in the process, the level of demand can even be spiky. If there is a lot of iowait, it may not need a lot of CPU. But when there are lots of computing-bound activities, the program is CPU-thirsty as it is programmed to to more. The last thing we want is to throttle the CPU use for a process in such legit situations. When &lt;a href="https://medium.com/indeed-engineering/unthrottled-fixing-cpu-limits-in-the-cloud-a0995ede8e89"&gt;throttling&lt;/a&gt; happens, the process does not get sufficient time share of CPU time. At the platform level, we can&amp;#8217;t control when the Pod (process) gets busy. The best thing it can do, is trying to fit more CPU time shares to this process when it becomes CPU thirsty. When we apply a limit of CPU in workload setting, we are potentially throttling the CPU use for a process at the times it needs more CPU time shares, which is counter-productive. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We should still configure CPU request, so that kube-scheduler factors it in when scheduling multiple Pods to a Node. The CPU request alone ensures the number of Pods are not excessive. This is the only thing we can do about controlling CPU assignment for Pods. We should also monitor &lt;a href="https://wbhegedus.me/understanding-kubernetes-cpu-limits/"&gt;CPU throttling&lt;/a&gt;. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Memory limit and request for Kubernetes workload&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Memory is not compressible, therefore we should set both limit and request to the same value. We set memory request so that kube-scheduler has an idea assigning Pods. We set the limit so that no single Pod takes more memory than its fair share. Unlike CPU, once a Pod takes more memory than its fair share, the platform will have to be aggressive to reclaim it back, which may impacts the running of the Pod (process). In contrast, CPU scheduler never guarantees the assignment of CPU time share to a Pod beyond the end of the current CPU cycle.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we&amp;#8217;re setting memory limit and request with different values, we&amp;#8217;re sending a confusing signal. We&amp;#8217;re inviting Pods to use more memory than they requested. This increases the chance of memory shortage at the node level, and hence the need to OOM kill a Pod.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Horizontal autoscaling and Cluster Autoscaling&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The native HPA is metrics-based. As I &lt;a href="https://static.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/"&gt;previously discussed&lt;/a&gt;, neither CPU nor memory metrics are good indicators of time to scale. A process or a Pod may have a temporary high demand of CPU purely due to how programmers write the code. Even if we followed the best practices as above, I would still not regard CPU and memory metrics as a reliable indicator to drive auto scaling. If a service is a potential point of congestion, we should use a queue in front and the queue size is almost always a much better indicator of the timing to scale. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As to cluster autoscaler, on it FAQ, it says flat out that you should NOT use a &lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#should-i-use-a-cpu-usage-based-node-autoscaler-with-kubernetes"&gt;CPU usage based scaling mechanism&lt;/a&gt;. I guess this is for a similar reason (compressibility). As discussed, when a Pod is pending for schedule for too long, it emits and event that drives the cluster autoscaler.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When I first worked on Kubernetes workload I did not give this much thought and proposed the use of CPU limit. As of January 2023 I still find static code analysis tools that requires CPU limit for Pods in the check (e.g. CKV_K8S_11 on &lt;a href="https://www.checkov.io/5.Policy%20Index/kubernetes.html"&gt;Checkov&lt;/a&gt;), which leads me to investigate the issue further, and noticed more voices advocating the correct use of resource limit (such as &lt;a href="https://sysdig.com/blog/kubernetes-limits-requests/"&gt;this&lt;/a&gt; post) in 2022. For existing deployments, it is worth a review the resource limit configuration.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/12/eks-impression/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;EKS impression&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/01/github-action-gotchas/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;GitHub Action Gotchas&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Automated Deployment Pipeline 1 of 3</title><link>https://static.digihunch.com/2020/09/automated-deployment-pipeline-1-2/</link><pubDate>Wed, 30 Sep 2020 22:04:00 -0400</pubDate><guid>https://static.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://static.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://static.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://static.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://static.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://static.digihunch.com/2020/09/log-file-navigator-lnav/</link><pubDate>Wed, 23 Sep 2020 21:03:00 -0400</pubDate><guid>https://static.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://static.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://static.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://static.digihunch.com/2020/09/automated-deployment-pipeline-1-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automated Deployment Pipeline 1 of 3&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>