<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Posts on  &gt; T.W.Stewart</title>
        <link>https://blog.twstewart.me/posts/</link>
        <description>Recent content in Posts on  &gt; T.W.Stewart</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <managingEditor>twstewart42&#43;blog@gmail.com (Tom Stewart)</managingEditor>
        <webMaster>twstewart42&#43;blog@gmail.com (Tom Stewart)</webMaster>
        <copyright>&lt;a href=&#34;https://creativecommons.org/licenses/by-nc/4.0/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CC BY-NC 4.0&lt;/a&gt;</copyright>
        <lastBuildDate>Thu, 01 Jan 2026 07:26:25 -0400</lastBuildDate>
        <atom:link href="https://blog.twstewart.me/posts/index.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>Dormant not Dead</title>
            <link>https://blog.twstewart.me/posts/dormant/</link>
            <pubDate>Thu, 01 Jan 2026 07:26:25 -0400</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/dormant/</guid>
            <description>&lt;p&gt;It&amp;rsquo;s been a bit since I&amp;rsquo;ve posted&amp;hellip;like nearly 5 years. I&amp;rsquo;m such a differently person than I was 5 years ago, that it&amp;rsquo;s hard for even me to relate to some of my old postings.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to publish more of my writing in both book form and here on my old blog, but I feel at the current intersection of AI generated slop and an overabundance of content &amp;ndash; helpful tech blogs, email lists, and youtube videos &amp;ndash; there really isn&amp;rsquo;t a point any more. And there hasn&amp;rsquo;t been for a long time for me. I write documentation and advice nearly everyday for DevOps/SRE/Security teams I work with, but it&amp;rsquo;s so so niche that I don&amp;rsquo;t think anyone would find it helpful or meaningful to read.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>It&rsquo;s been a bit since I&rsquo;ve posted&hellip;like nearly 5 years. I&rsquo;m such a differently person than I was 5 years ago, that it&rsquo;s hard for even me to relate to some of my old postings.</p>
<p>I&rsquo;d like to publish more of my writing in both book form and here on my old blog, but I feel at the current intersection of AI generated slop and an overabundance of content &ndash; helpful tech blogs, email lists, and youtube videos &ndash; there really isn&rsquo;t a point any more. And there hasn&rsquo;t been for a long time for me. I write documentation and advice nearly everyday for DevOps/SRE/Security teams I work with, but it&rsquo;s so so niche that I don&rsquo;t think anyone would find it helpful or meaningful to read.</p>
<p>Ultimately, I&rsquo;m leaving the blog in place as a historical marker of my own personal journey throught the early years of my career. Otherwise the information is out of date and irrelevant.</p>
<p>Cheers, and happy new years!</p>
]]></content>
        </item>
        
        <item>
            <title>The 3 phases of great documentation</title>
            <link>https://blog.twstewart.me/posts/3-phases-of-docs/</link>
            <pubDate>Mon, 21 Dec 2020 07:12:24 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/3-phases-of-docs/</guid>
            <description>&lt;p&gt;I&amp;rsquo;ve recently come to the realization that I &lt;em&gt;&lt;strong&gt;&amp;lt;3&lt;/strong&gt;&lt;/em&gt; great documentation. Like it makes the hole in my chest where my heart should be tingle when I create or come across very well done documentation. Over the years I&amp;rsquo;ve worked with many different sized teams and management styles and they&amp;rsquo;ve all struggled with getting documentation right. For some teams, they had at most a shared OneNote notebook, or they had a wiki but it was horribly out of date, or the information reflected the organization structure and so key information was locked behind siloed doors. Great documentation can make or break a team. If done well, it can ease the challenge of onboarding new hands and it can also democratize specialization, so your team and company does not have to fear the &lt;a href=&#34;https://en.wikipedia.org/wiki/Bus_factor&#34;&gt;bus factor&lt;/a&gt;.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>I&rsquo;ve recently come to the realization that I <em><strong>&lt;3</strong></em> great documentation. Like it makes the hole in my chest where my heart should be tingle when I create or come across very well done documentation. Over the years I&rsquo;ve worked with many different sized teams and management styles and they&rsquo;ve all struggled with getting documentation right. For some teams, they had at most a shared OneNote notebook, or they had a wiki but it was horribly out of date, or the information reflected the organization structure and so key information was locked behind siloed doors. Great documentation can make or break a team. If done well, it can ease the challenge of onboarding new hands and it can also democratize specialization, so your team and company does not have to fear the <a href="https://en.wikipedia.org/wiki/Bus_factor">bus factor</a>.</p>
<div style='position:relative; padding-bottom:calc(56.00% + 44px)'><iframe src='https://gfycat.com/ifr/AppropriateBrilliantGopher' frameborder='0' scrolling='no' width='100%' height='100%' style='position:absolute;top:0;left:0;' allowfullscreen></iframe></div><p><a href="https://gfycat.com/discover/author-gifs">from Author GIFs</a> <a href="https://gfycat.com/appropriatebrilliantgopher-writing-author-typing-pretend-cute">via Gfycat</a></p>
<p>The Three phases of great documentation are <strong>Personal</strong>, <strong>Professional</strong>, and <strong>Presentable</strong></p>
<h2 id="phase-1-personal-documentation">Phase 1) Personal Documentation</h2>
<p>The main point of personal documentation should be to gather as much information, notes, logs, output steps, etc; and slam them into a plain text format of your choice. Do not worry about formatting or anyone else seeing this documentation. It is for your eyes only, and as long as you can follow it, then continue to build that repository of information.</p>
<div style='position:relative; padding-bottom:calc(74.00% + 44px)'><iframe src='https://gfycat.com/ifr/FluffyBogusFox' frameborder='0' scrolling='no' width='100%' height='100%' style='position:absolute;top:0;left:0;' allowfullscreen></iframe></div><p><a href="https://gfycat.com/discover/author-gifs">from Author GIFs</a> <a href="https://gfycat.com/fluffybogusfox-writing-author-typing">via Gfycat</a></p>
<p>Naturally, when you take time to place and organize your own notes, your brain is going to make a pseudo index of that information. Key details may be missing from what it is you are recording, but you will most always remember where you put that detail, and then when you need it again, can easily retrieve the information.</p>
<p>Some scholarly folks have even invented an entire &ldquo;Second Brain&rdquo; called a <a href="https://zettelkasten.de/">zettelkasten</a> around this concept of continually taking notes, indexing them with tags and/or interlinks, and this can be more than just notes at work. These Second Brain&rsquo;s are for everything; recipes, meetings, shopping lists, and just about anything.</p>
<h2 id="phase-2-professional-documentation">Phase 2) Professional Documentation</h2>
<p>This phase is contingent on having some previous notes from the personal documentation phase. This is where you are formally recording notes that you want to share with your organization. Many teams use a form of Wiki or Shared documents to go about this.</p>
<div style='position:relative; padding-bottom:calc(56.00% + 44px)'><iframe src='https://gfycat.com/ifr/EarlyBrokenIndiancow' frameborder='0' scrolling='no' width='100%' height='100%' style='position:absolute;top:0;left:0;' allowfullscreen></iframe></div><p><a href="https://gfycat.com/discover/author-gifs">from Author GIFs</a> <a href="https://gfycat.com/earlybrokenindiancow-writing-author-typing">via Gfycat</a></p>
<p>The documentation needs to be accurate, true, and above all readable to a wide audience - You never know who in the future may need to understand the whys and whats of your instructions.</p>
<p>Things that a professional documentation system requires:</p>
<ul>
<li><strong>Searchability</strong> - If you can&rsquo;t easily search for <strong>ANY</strong> text within the docs, then it will be impossible for someone else to find your article</li>
<li><strong>Tagging</strong> - Not all documentation fits in a purely hierarchy structure, nor should it be forced into one, so tags are a way to cut across hierarchy and create a new grouping of closely related topics.</li>
<li><strong>Viewable</strong> - Everyone in the organization should be able to view your documentation. Likewise, you should make your documentation understandable to a wide audience, and also be careful of revealing secret information.</li>
<li><strong>Mixed Media</strong> - The wiki system should make it easy to include images, videos, diagrams, whatever is needed for those making and reading to understand the concept.</li>
</ul>
<h2 id="phase-3-presentable-documentation">Phase 3) Presentable Documentation</h2>
<p>Most teams stop at step 2, with the thought that so long as it&rsquo;s in the wiki someone can find it. I&rsquo;m here today, to tell you that most other people, do not read documentation until forced to. Does that mean this is all a waste of time and effort? <strong>NO.</strong> The previous phases were for <em>your</em> understanding, phase 1 is explaining it to yourself, phase 2 is explaining it to your team, now phase 3 is you distilling everything down into few slides of a presentation and verbally explaining it to your team or larger development organization.</p>
<div style='position:relative; padding-bottom:calc(55.78% + 44px)'><iframe src='https://gfycat.com/ifr/UnlawfulFormalGermanshepherd' frameborder='0' scrolling='no' width='100%' height='100%' style='position:absolute;top:0;left:0;' allowfullscreen></iframe></div><p> <a href="https://gfycat.com/unlawfulformalgermanshepherd">via Gfycat</a></p>
<p>At my current place of employment, every friday a team from the Engineering department presents on a new or innovative topic that they want to share with the rest of the organization. These are great, people listen and ask questions, you can physically see engagement go from close to zero to a ton of side conversations spinning out as people absorb and then start to imagine using the new tool/concept/idea in their own work.</p>
<p>Likewise, anytime our team builds something new, we do a show and tell, running through both the documentation on our wiki and live demoing the new feature so that everyone understand how it works, why it was made, and what problem it solves. These presentations are recorded for future viewers and are by far the most effective way to spread the word and get team members involved and engaged with the new concept.</p>
<p>Plus, if you can accurately explain a technology or idea to someone else, that means you fundamentally have learned this new skill and you can be proud of the information you have and know that people want to learn from you.</p>
]]></content>
        </item>
        
        <item>
            <title>Beginner Linux Tools and Troubleshooting</title>
            <link>https://blog.twstewart.me/posts/beginner-linux-tools/</link>
            <pubDate>Mon, 07 Dec 2020 20:30:22 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/beginner-linux-tools/</guid>
            <description>&lt;p&gt;I wrote this wiki for my interns and junior admins as a quick overview of basic things to check and use when they encountered an issue. The intent of this guide is to provide nothing more than the fundamental tools that one uses to operate a Linux Server and act as a central jumping point (use the links to learn more). This was originally written around the year 2014/5, but still remains relevant due to the core nature of these utilities. They will most likely still be relevant to troubleshooting and navigating a linux server in 10 or 20 years.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>I wrote this wiki for my interns and junior admins as a quick overview of basic things to check and use when they encountered an issue. The intent of this guide is to provide nothing more than the fundamental tools that one uses to operate a Linux Server and act as a central jumping point (use the links to learn more). This was originally written around the year 2014/5, but still remains relevant due to the core nature of these utilities. They will most likely still be relevant to troubleshooting and navigating a linux server in 10 or 20 years.</p>
<p>This was written during the height of CentOS 6. Some concepts may be stale and out of date.</p>
<h1 id="summary">Summary</h1>
<p>There are basically an infinite amount of tools and ways to resolve an issue. 10 times out of 10, if you have a problem someone else has already encountered it and documented how to resolve the issue somewhere out there via google.com.</p>
<ul>
<li><a href="http://www.linux.org/threads/in-linux-everything-is-a-file.4245/">EVERYTHING</a> on Linux is a file</a>, understanding this fact is key to understanding linux, permissions, access, drivers, kernels, and debugging</li>
<li>Follow through! Do not just hit enter and walk away, understand the consequences (good and bad) of what you are doing before you change something.</li>
<li>Start small, the first thing every aspiring Linux Admin should build is a LAMP or LAPP Stack and then move on to more complicated services/setups</li>
<li>Take notes, so that you remember where you got stuck and what you did so it can be a repeatable process that should be able to be duplicated many times over (this is what the wiki is for)</li>
<li><a href="http://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Don.27t_Panic">Don&rsquo;t Panic!</a> when you mess up, learn how to fix it this will make you a better admin, trial by fire is the way of life for all of us.</li>
<li>Take snapshots, backups, SVN/GIT, copy files before any major changes, always start at Testing/DEV and work your way up to BTA/PROD deployments.</li>
<li>Ask questions, discuss what you are doing before/after you change something(even if it is just research), measure twice/cut once: This allows everyone to know where you are and what you are doing, that way if something bad happens we can quickly triage the situation.</li>
</ul>
<h1 id="bash---shell-scripting">Bash - Shell Scripting</h1>
<p><a href="http://linux.die.net/man/1/bash">Bash</a> scripting is the bread and butter of systems administration. Too often we have to repeat a set of commands across many machines, and the goal of everyone should be to complete that goal with as much automation as possible. Any command that you can type on the CMD line is valid in bash scripting along with basic <a href="http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-6.html">if</a>/elif/<a href="http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html">else</a> logic, <a href="http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-7.html">for loops</a>, variable substitution, and <a href="http://tldp.org/LDP/abs/html/arithexp.html">mathematical expressions</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span></code></pre></div><p><a href="http://en.wikipedia.org/wiki/Bash_%28Unix_shell%29">Wikipedia:Bash (Unix shell)</a></p>
<h1 id="man-pages">Man Pages</h1>
<p>If you are ever unsure of what a command does man pages are the way to locally read and understand how to use any command in Linux. It is literally the command <a href="http://linux.die.net/man/">manual</a> in text format.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>man ifconfig
</span></span><span style="display:flex;"><span> IFCONFIG<span style="color:#f92672">(</span>8<span style="color:#f92672">)</span>                Linux Programmer’s Manual               IFCONFIG<span style="color:#f92672">(</span>8<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> NAME
</span></span><span style="display:flex;"><span>        ifconfig - configure a network interface
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> SYNOPSIS
</span></span><span style="display:flex;"><span>        ifconfig <span style="color:#f92672">[</span>interface<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>        ifconfig interface <span style="color:#f92672">[</span>aftype<span style="color:#f92672">]</span> options | address ...
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> DESCRIPTION
</span></span><span style="display:flex;"><span>        Ifconfig  is  used to configure the kernel-resident network interfaces.
</span></span><span style="display:flex;"><span>        It is used at boot time to set up interfaces as necessary.  After that,
</span></span><span style="display:flex;"><span>        it  is  usually  only  needed  when  debugging or when system tuning is
</span></span><span style="display:flex;"><span>        needed.
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        If no arguments are given, ifconfig displays the  status  of  the  cur-
</span></span><span style="display:flex;"><span>        rently  active interfaces.  If a single interface argument is given, it
</span></span><span style="display:flex;"><span>        displays the status of the given interface only; <span style="color:#66d9ef">if</span> a single  -a  argu- 
</span></span><span style="display:flex;"><span>        ment  is  given,  it  displays the status of all interfaces, even those
</span></span><span style="display:flex;"><span>        that are down.  Otherwise, it configures an interface.
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">[</span>there is much more to this document in full<span style="color:#f92672">]</span>
</span></span></code></pre></div><h2 id="varlogmessages">/var/log/messages</h2>
<p><code>/var/log/messages</code> is the default catch-all for any system errors that may be occurring. There are sometimes logs setup for specific services. Always try to see if a specific service has its own log file or directory (httpd,cron,secure,php-fpm,mysql,postgresql) and if it does not exist or is empty, check /var/log/messages it will probably have the errors, warning, INFO, emergencies, kernel panics, and any other frightening logs that you need to resolve any issues that may occur.</p></p>
<h2 id="journalctl">journalctl</h2>
<p>The command <a href="https://manpages.debian.org/stretch/systemd/journalctl.1.en.html">journalctl</a> can be used to read system and application logs through systemd&rsquo;s journaling service. This is the newer, sexier way to quickly access logs, but by default journalctl only keep logs around for a few days or until you max out the reserved amount.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ journalctl -f <span style="color:#75715e"># follow the logs</span>
</span></span><span style="display:flex;"><span>$ journlactl -n100 <span style="color:#75715e"># look back last 100 lines of the log file</span>
</span></span></code></pre></div><p>If you have checked both the service log and /var/log/messages and still cannot find any information about the service you are trying to fix, you may have to enable debugging mode(s) or level(s) on the service itself. This is different for all services and can usually be found in the products documentation.</p>
<h1 id="machine-info">Machine Info</h1>
<p>The following is a collection of commands and their result to gather basic information about the machine.</p>
<p>Get name, kernel # and date of build with uname</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ uname -a
</span></span><span style="display:flex;"><span>Linux devsql005.example.com 3.10.0-123.8.1.el7.x86_64 <span style="color:#75715e">#1 SMP Mon Sep 22 19:06:58 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux</span>
</span></span></code></pre></div><p>OS release info can be found in the following files</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cat /etc/redhat-release; <span style="color:#75715e"># shows official CentOS/Redhat version number</span>
</span></span><span style="display:flex;"><span>  CentOS Linux release 7.0.1406 <span style="color:#f92672">(</span>Core<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>or for Ubuntu</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cat /etc/lsb-release                                             
</span></span><span style="display:flex;"><span>DISTRIB_ID<span style="color:#f92672">=</span>Ubuntu
</span></span><span style="display:flex;"><span>DISTRIB_RELEASE<span style="color:#f92672">=</span>20.04
</span></span><span style="display:flex;"><span>DISTRIB_CODENAME<span style="color:#f92672">=</span>focal
</span></span><span style="display:flex;"><span>DISTRIB_DESCRIPTION<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;Ubuntu 20.04.1 LTS&#34;</span>
</span></span></code></pre></div><p>Use <code>Uptime</code> to get the amount of time since the server has started, and also load averages</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ uptime
</span></span><span style="display:flex;"><span> 20:40:43 up <span style="color:#ae81ff">1</span> day, 22:36,  <span style="color:#ae81ff">0</span> users,  load average: 0.23, 0.16, 0.18
</span></span></code></pre></div><p>Ip Address and network information</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ip addr; 
</span></span><span style="display:flex;"><span> 1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu <span style="color:#ae81ff">65536</span> qdisc noqueue state UNKNOWN
</span></span><span style="display:flex;"><span>     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
</span></span><span style="display:flex;"><span>     inet 127.0.0.1/8 scope host lo
</span></span><span style="display:flex;"><span>        valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span>     inet6 ::1/128 scope host
</span></span><span style="display:flex;"><span>        valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> 2: ens160: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu <span style="color:#ae81ff">1500</span> qdisc mq state UP qlen <span style="color:#ae81ff">1000</span>
</span></span><span style="display:flex;"><span>     link/ether 00:50:56:84:59:79 brd ff:ff:ff:ff:ff:ff
</span></span><span style="display:flex;"><span>     inet 10.0.0.<span style="color:#f92672">[</span>?<span style="color:#f92672">]</span>/24 brd 10.0.0.255 scope global ens160
</span></span><span style="display:flex;"><span>        valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span>     inet6 fe80::250:56ff:fe84:5979/64 scope link
</span></span><span style="display:flex;"><span>        valid_lft forever preferred_lft forever
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ifconfig;
</span></span><span style="display:flex;"><span> ens160: flags<span style="color:#f92672">=</span>4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu <span style="color:#ae81ff">1500</span>
</span></span><span style="display:flex;"><span>         inet 10.0.0.<span style="color:#f92672">[</span>?<span style="color:#f92672">]</span>  netmask 255.255.255.0  broadcast 10.0.50.255
</span></span><span style="display:flex;"><span>         inet6 fe80::250:56ff:fe84:5979  prefixlen <span style="color:#ae81ff">64</span>  scopeid 0x20&amp;lt;link&amp;gt;
</span></span><span style="display:flex;"><span>         ether 00:50:56:84:59:79  txqueuelen <span style="color:#ae81ff">1000</span>  <span style="color:#f92672">(</span>Ethernet<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>         RX packets <span style="color:#ae81ff">1675151509</span>  bytes <span style="color:#ae81ff">296413707167</span> <span style="color:#f92672">(</span>276.0 GiB<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>         RX errors <span style="color:#ae81ff">0</span>  dropped <span style="color:#ae81ff">235945</span>  overruns <span style="color:#ae81ff">0</span>  frame <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>         TX packets <span style="color:#ae81ff">1428044385</span>  bytes <span style="color:#ae81ff">471991954342</span> <span style="color:#f92672">(</span>439.5 GiB<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>         TX errors <span style="color:#ae81ff">0</span>  dropped <span style="color:#ae81ff">0</span> overruns <span style="color:#ae81ff">0</span>  carrier <span style="color:#ae81ff">0</span>  collisions <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> lo: flags<span style="color:#f92672">=</span>73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu <span style="color:#ae81ff">65536</span>
</span></span><span style="display:flex;"><span>         inet 127.0.0.1  netmask 255.0.0.0
</span></span><span style="display:flex;"><span>         inet6 ::1  prefixlen <span style="color:#ae81ff">128</span>  scopeid 0x10&amp;lt;host&amp;gt;
</span></span><span style="display:flex;"><span>         loop  txqueuelen <span style="color:#ae81ff">0</span>  <span style="color:#f92672">(</span>Local Loopback<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>         RX packets <span style="color:#ae81ff">409040766</span>  bytes <span style="color:#ae81ff">330694444196</span> <span style="color:#f92672">(</span>307.9 GiB<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>         RX errors <span style="color:#ae81ff">0</span>  dropped <span style="color:#ae81ff">0</span>  overruns <span style="color:#ae81ff">0</span>  frame <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>         TX packets <span style="color:#ae81ff">409040766</span>  bytes <span style="color:#ae81ff">330694444196</span> <span style="color:#f92672">(</span>307.9 GiB<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>         TX errors <span style="color:#ae81ff">0</span>  dropped <span style="color:#ae81ff">0</span> overruns <span style="color:#ae81ff">0</span>  carrier <span style="color:#ae81ff">0</span>  collisions <span style="color:#ae81ff">0</span>
</span></span></code></pre></div><p>Find the directory you are in</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ pwd 
</span></span><span style="display:flex;"><span>/var/lib/pgsql/9.3/data
</span></span></code></pre></div><p>Show local users</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cat /etc/passwd;
</span></span><span style="display:flex;"><span> root:x:0:0:root:/root:/bin/bash
</span></span><span style="display:flex;"><span> bin:x:1:1:bin:/bin:/sbin/nologin
</span></span><span style="display:flex;"><span> daemon:x:2:2:daemon:/sbin:/sbin/nologin
</span></span><span style="display:flex;"><span> adm:x:3:4:adm:/var/adm:/sbin/nologin
</span></span><span style="display:flex;"><span> lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
</span></span><span style="display:flex;"><span> sync:x:5:0:sync:/sbin:/bin/sync
</span></span><span style="display:flex;"><span> shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
</span></span><span style="display:flex;"><span> halt:x:7:0:halt:/sbin:/sbin/halt
</span></span><span style="display:flex;"><span> mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
</span></span><span style="display:flex;"><span> ....
</span></span></code></pre></div><p>Show local groups</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cat /etc/group;
</span></span><span style="display:flex;"><span> root:x:0:
</span></span><span style="display:flex;"><span> bin:x:1:
</span></span><span style="display:flex;"><span> daemon:x:2:
</span></span><span style="display:flex;"><span> sys:x:3:
</span></span><span style="display:flex;"><span> adm:x:4:
</span></span><span style="display:flex;"><span> tty:x:5:
</span></span><span style="display:flex;"><span> disk:x:6:
</span></span><span style="display:flex;"><span> ....
</span></span></code></pre></div><p>Find which groups the user &lsquo;apache&rsquo; is in</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ groups apache;
</span></span><span style="display:flex;"><span> apache : apache svc_account1
</span></span></code></pre></div><h1 id="grepawksed">grep/awk/sed</h1>
<p>Learn these three tools, they are the swiss army knife to the system&rsquo;s administrator&rsquo;s war chest.
They can be used to do most anything and you will see many example combinations and use cases in the sections below.</p>
<p>You can be like captain planet and when you combine the power of all three of these tools you can create very powerful and flexible scripts that do all the heavy lifting for you.</p>
<h2 id="grep">grep</h2>
<p><a href="http://linux.die.net/man/1/grep">grep</a> is a tool mainly used for searching or weeding out information.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ grep -r mysql; <span style="color:#75715e"># will search the entire directory tree and every file in those directories for lines containing the word mysql</span>
</span></span><span style="display:flex;"><span>$ cat /var/log/maillog | grep user@fqdn.com
</span></span></code></pre></div><p><a href="https://en.wikipedia.org/wiki/Grep">https://en.wikipedia.org/wiki/Grep</a></p>
<h2 id="awk">awk</h2>
<p>I generally use <a href="http://linux.die.net/man/1/awk">awk</a> piped at the end of a command to filter parts of the returned information. But awk is it&rsquo;s own programming language and can be just as powerful as perl or any other scripting language.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ df -h -t nfs -P | grep /vol/ |  awk <span style="color:#e6db74">&#39;{ print $5 &#34; &#34; $6}&#39;</span>; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># df -h gives me a listing of the mounts, I search for /vol/ to get mounted drives, then I awk for result $5 and $6 of each returned line which is always used percentage and the partition that is in question.</span>
</span></span><span style="display:flex;"><span>$ grep <span style="color:#e6db74">&#34;request ID&#34;</span> history | awk <span style="color:#e6db74">&#39;match($0,&#34;is&#34;){print substr($0,RSTART+3,50)}&#39;</span><span style="color:#e6db74">`</span>; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># this searches for &#34;request ID&#34; in a file called history and awk looks for the word &#34;is&#34; then prints 50 characters, 3 characters after &#34;is&#34;, which is always the request ID in this example. </span>
</span></span></code></pre></div><p><a href="http://en.wikipedia.org/wiki/AWK">http://en.wikipedia.org/wiki/AWK</a></p>
<h2 id="sed">sed</h2>
<p><a href="http://linux.die.net/man/1/sed">sed</a> is used for inline text editing and <a href="http://sed.sourceforge.net/sed1line.txt">manipulation</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sed -n 51,61p sbr/index.html | sed -i <span style="color:#e6db74">&#39;50r /dev/stdin&#39;</span> testsed;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># this takes lines 51-61 of index.html and appends them after line 50 in testsed</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>IP<span style="color:#f92672">=</span><span style="color:#e6db74">`</span>nslookup $HOSTNAME | grep Address | grep -v <span style="color:#e6db74">&#34;#53&#34;</span>| awk <span style="color:#e6db74">&#39;{ print $2}&#39;</span><span style="color:#e6db74">`</span>;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># looks up hostname using DNS and greps for address, ignores anything with #53,prints second value on returned line</span>
</span></span><span style="display:flex;"><span>$ echo <span style="color:#e6db74">&#34;</span>$IP<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>$ sed -i <span style="color:#e6db74">&#39;s/0.0.0.0/&#39;</span>$IP<span style="color:#e6db74">&#39;/g&#39;</span> /etc/monitrc; - <span style="color:#75715e"># reads file, greps for 0.0.0.0, then replaces 0.0.0.0 with result of $IP in script.</span>
</span></span></code></pre></div><p><a href="http://www.bashoneliners.com/oneliners/oneliner/popular/">http://www.bashoneliners.com/oneliners/oneliner/popular/</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ curl -s http://cbsg.sourceforge.net/cgi-bin/live | grep -Eo <span style="color:#e6db74">&#39;^&amp;lt;li&amp;gt;.*&amp;lt;/li&amp;gt;&#39;</span> | sed s,<span style="color:#ae81ff">\&amp;</span>lt;/<span style="color:#ae81ff">\\</span>?li<span style="color:#ae81ff">\&amp;</span>gt;,,g | shuf -n <span style="color:#ae81ff">1</span>
</span></span></code></pre></div><p><a href="https://en.wikipedia.org/wiki/Sed">https://en.wikipedia.org/wiki/Sed</a></p>
<h1 id="disk-is-full">Disk is full</h1>
<p>The following are commands and explanation of the many things you can do to find and remove large log/temporary files, unusual file names, and similar scenarios.</p>
<p>Below is a list of the most likely to be filled areas and should be the first place(s) one looks for space that can be reclaimed</p>
<ol>
<li><code>/var/log</code></li>
<li><code>/var/spool/{clientmqueue,mqueue,mail}</code></li>
<li><code>/tmp</code></li>
<li><code>/var/tmp</code></li>
</ol>
<h2 id="df">df</h2>
<p><a href="http://linux.die.net/man/1/df">df</a> -h;
shows one the local and mounted filesystems and the amount of space, used vs available.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ df -h
</span></span><span style="display:flex;"><span>Filesystem                              Size  Used Avail Use% Mounted on
</span></span><span style="display:flex;"><span>/dev/sda3                                12G  4.9G  6.7G  43% /
</span></span><span style="display:flex;"><span>/dev/sda1                               497M  115M  383M  24% /boot
</span></span><span style="display:flex;"><span>10.10.0.248:/vol/remote_data            1.9T  1.2T  628G  66% /mnt/remote_data
</span></span></code></pre></div><h2 id="du">du</h2>
<p><a href="http://linux.die.net/man/1/du">du</a>-sh /var/log/; will count the size of each file within a directory and total it.</p></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ du -sh /var/log
</span></span><span style="display:flex;"><span>113M    /var/log
</span></span><span style="display:flex;"><span>$ du -shx /var/log/*
</span></span><span style="display:flex;"><span>4.0K    /var/log/alternatives.log
</span></span><span style="display:flex;"><span>100K    /var/log/apt
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">0</span>       /var/log/btmp
</span></span><span style="display:flex;"><span>4.0K    /var/log/dist-upgrade
</span></span><span style="display:flex;"><span>92K     /var/log/dpkg.log
</span></span><span style="display:flex;"><span>4.0K    /var/log/journal
</span></span><span style="display:flex;"><span>4.0K    /var/log/landscape
</span></span><span style="display:flex;"><span>4.0K    /var/log/lastlog
</span></span><span style="display:flex;"><span>4.0K    /var/log/unattended-upgrades
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">0</span>       /var/log/wtmp
</span></span></code></pre></div><h2 id="clearing-large-log-files">Clearing large Log files</h2>
<p>Do not just rm -rf logfile; if a process, say apache, is still writing to the logfile and you remove the file from existence the process will crash. Remember we wish to have an uptime of 24/7/365. Instead you can clear out the file to size 0 and allows the process to continue writing to it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ cat /dev/null &gt; /var/log/messages
</span></span></code></pre></div><h2 id="no-space-left-to-delete">No Space left to Delete</h2>
<p>Rarely, but it does happen a volume will fill up and be so full that rm will not work as it creates temporary records while it deletes files. There is another way to delete the file by finding it&rsquo;s inode number and using find to delete the file</p>
<p>let&rsquo;s assume you did all steps above and found the large offending file.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ rm /var/tmp/3a8066e5-a90c-4ae5-bdc6-47e117acf354.error
</span></span><span style="display:flex;"><span> rm: remove regular file ‘/var/tmp/3a8066e5-a90c-4ae5-bdc6-47e117acf354.error’? y
</span></span><span style="display:flex;"><span> rm: cannot remove ‘/var/tmp/3a8066e5-a90c-4ae5-bdc6-47e117acf354.error’: No space left on device
</span></span><span style="display:flex;"><span>$ ls -li /var/tmp/3a8066e5-a90c-4ae5-bdc6-47e117acf354.error
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">56436168</span> -rw-r--r-- <span style="color:#ae81ff">1</span> gerbn308 zxdev <span style="color:#ae81ff">0</span> May <span style="color:#ae81ff">13</span> 11:17 /var/tmp/3a8066e5-a90c-4ae5-bdc6-47e117acf354.error
</span></span><span style="display:flex;"><span>find . -inum <span style="color:#ae81ff">56436168</span> -delete
</span></span></code></pre></div><h2 id="find">find</h2>
<p><a href="http://man7.org/linux/man-pages/man1/find.1.html">Find</a> is really really useful and below are some of the ways find has solved strange issues for me</p>
<h3 id="find-date-range">find date range</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ll -tr;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># will list out files in time/date order newest -&amp;gt; oldest (remove the r if you would like oldest -&amp;gt; newest)</span>
</span></span><span style="display:flex;"><span>$ find . -type f -newer file_xyz.txt ! -newer recent_zzy.txt -exec ls -l <span style="color:#f92672">{}</span> <span style="color:#ae81ff">\;</span> -print &amp;&gt; output.txt;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># make sure I got the correct range of data greped for earliest date and latest date</span>
</span></span><span style="display:flex;"><span>$ find . type -newer file_xyz.txt ! -newer recent_zzy.txt delete; 
</span></span></code></pre></div><h3 id="find-a-specific-file-type">find a specific file type</h3>
<p>In this example we are finding all <a href="http://www.cyberciti.biz/tips/howto-linux-unix-find-move-all-mp3-file.html">mp3</a> files and moving them to a mounted usb drive on /mnt/mp3</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ grep *.mp3 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># lets assume there is a mix of file types with no standard naming onvention and there are thousands of them</span>
</span></span><span style="display:flex;"><span>$ find / -iname <span style="color:#e6db74">&#34;*.mp3&#34;</span> -exec mv <span style="color:#f92672">{}</span> /mnt/mp3 ;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># you can have any command in the -exec section</span>
</span></span></code></pre></div><h3 id="find-anything-older-than-12-hours">find anything older than 12 hours</h3>
<p>Sometimes you have to alleviate some pressure so the file system does not fill up and you do not want to clear out files that may be actively being worked on.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ls -R | wc -l; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># will list number of files in directory</span>
</span></span><span style="display:flex;"><span>find . -type f -mmin +720 -delete; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># find and delete anything over 12 hours old</span>
</span></span></code></pre></div><h3 id="find-like-grep">find like grep</h3>
<p>find can be used much in the same way as grep to search for the name of files in a directory.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /usr/local/lib;
</span></span><span style="display:flex;"><span>find ./ -name <span style="color:#e6db74">&#34;*gdal*&#34;</span> -print; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># prints out all files containing the word gdal in their name</span>
</span></span><span style="display:flex;"><span>find ./ -name <span style="color:#e6db74">&#34;*gdal*&#34;</span> -delete; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># deletes all the files containing the word gdal in their name</span>
</span></span></code></pre></div><h1 id="server-seems-slow">Server seems slow</h1>
<p>This is the holy grail of complaints as there are literally millions of things that could cause a server to be slow.</p>
<h2 id="top">top</h2>
<p><a href="http://linux.die.net/man/1/top">top</a> gives one a task explorer like peak into the activity of the server. When one is using top you can press <code>u</code>; key and sort by specific username. <code>c</code>; allows one to get more detail on the processes running. top by default tries to list everything in order by highest use of %CPU.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ top -u apache
</span></span><span style="display:flex;"><span>  top - 08:37:14 up <span style="color:#ae81ff">41</span> days, 20:30,  <span style="color:#ae81ff">3</span> users,  load average: 0.00, 0.04, 0.08
</span></span><span style="display:flex;"><span>  Tasks: <span style="color:#ae81ff">140</span> total,   <span style="color:#ae81ff">3</span> running, <span style="color:#ae81ff">136</span> sleeping,   <span style="color:#ae81ff">0</span> stopped,   <span style="color:#ae81ff">1</span> zombie
</span></span><span style="display:flex;"><span>  %Cpu<span style="color:#f92672">(</span>s<span style="color:#f92672">)</span>:  1.7 us,  2.0 sy,  0.0 ni, 95.9 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
</span></span><span style="display:flex;"><span>  KiB Mem:   <span style="color:#ae81ff">1885520</span> total,  <span style="color:#ae81ff">1339856</span> used,   <span style="color:#ae81ff">545664</span> free,        <span style="color:#ae81ff">0</span> buffers
</span></span><span style="display:flex;"><span>  KiB Swap:  <span style="color:#ae81ff">4095996</span> total,    <span style="color:#ae81ff">52368</span> used,  <span style="color:#ae81ff">4043628</span> free.   <span style="color:#ae81ff">462444</span> cached Mem
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">11462</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">1005776</span>  <span style="color:#ae81ff">12212</span>   <span style="color:#ae81ff">4072</span> S  0.0  0.6   1:13.79 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">11492</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">1005640</span>  <span style="color:#ae81ff">12688</span>   <span style="color:#ae81ff">4208</span> S  0.0  0.7   1:13.22 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">11493</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">1005636</span>  <span style="color:#ae81ff">12124</span>   <span style="color:#ae81ff">4104</span> S  0.0  0.6   1:13.60 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">12802</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span>  <span style="color:#ae81ff">254124</span>   <span style="color:#ae81ff">1636</span>    <span style="color:#ae81ff">836</span> S  0.0  0.1   0:00.00 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">12803</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span>  <span style="color:#ae81ff">255300</span>   <span style="color:#ae81ff">1532</span>    <span style="color:#ae81ff">672</span> S  0.0  0.1   0:14.62 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">12804</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span>  <span style="color:#ae81ff">255300</span>   <span style="color:#ae81ff">1508</span>    <span style="color:#ae81ff">652</span> S  0.0  0.1   0:00.18 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">12806</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">1005940</span>  <span style="color:#ae81ff">12892</span>   <span style="color:#ae81ff">3548</span> S  0.0  0.7   4:35.21 httpd
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">19201</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span>  <span style="color:#ae81ff">656156</span>  <span style="color:#ae81ff">13036</span>   <span style="color:#ae81ff">1832</span> S  0.0  0.7   0:00.03 php-fpm
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">19202</span> apache    <span style="color:#ae81ff">20</span>   <span style="color:#ae81ff">0</span>  <span style="color:#ae81ff">656156</span>  <span style="color:#ae81ff">13036</span>   <span style="color:#ae81ff">1832</span> S  0.0  0.7   0:00.03 php-fpm
</span></span></code></pre></div><h2 id="free">free</h2>
<p>The <a href="http://linux.die.net/man/1/free">free</a> command will give one a snapshot of the current memory and swap usage. Remember to subtract buffered and cached memory from used to get an actual representation of the amount of RAM in use.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ free
</span></span><span style="display:flex;"><span>              total       used       free     shared    buffers     cached
</span></span><span style="display:flex;"><span> Mem:       <span style="color:#ae81ff">3924876</span>    <span style="color:#ae81ff">3663056</span>     <span style="color:#ae81ff">261820</span>          <span style="color:#ae81ff">0</span>     <span style="color:#ae81ff">298528</span>    <span style="color:#ae81ff">1755512</span>
</span></span><span style="display:flex;"><span> -/+ buffers/cache:    <span style="color:#ae81ff">1609016</span>    <span style="color:#ae81ff">2315860</span>
</span></span><span style="display:flex;"><span> Swap:      <span style="color:#ae81ff">4194296</span>     <span style="color:#ae81ff">235456</span>    <span style="color:#ae81ff">3958840</span>
</span></span></code></pre></div><h2 id="ps">ps</h2>
<p>The <a href="http://linux.die.net/man/1/ps">ps</a> or process command can be used to get a very detailed account of every single process running at the exact moment you enter the command, it does not refresh like top, but can clue you into process trees and zombie/dead/defunct processes that might not show up in top. I will truncate the output below as it can be quite lengthy.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ps fax
</span></span><span style="display:flex;"><span>  PID TTY      STAT   TIME COMMAND
</span></span><span style="display:flex;"><span>    1 ?        Ss     0:18 /sbin/init
</span></span><span style="display:flex;"><span>  480 ?        S&amp;lt;s    0:00 /sbin/udevd -d
</span></span><span style="display:flex;"><span> 5799 ?        S&amp;lt;     0:00  <span style="color:#ae81ff">\_</span> /sbin/udevd -d
</span></span><span style="display:flex;"><span> 1057 ?        S      1:29 /opt/chef-server/embedded/service/bookshelf/erts-5.9.3.1/bin/epmd -daemon
</span></span><span style="display:flex;"><span> 1349 ?        Sl   174:03 /usr/sbin/vmtoolsd
</span></span><span style="display:flex;"><span> 1836 ?        S&amp;lt;sl   0:47 auditd
</span></span><span style="display:flex;"><span> 1854 ?        Ss     0:00 /sbin/portreserve
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span> 2194 ?        Ssl    1:07 hald
</span></span><span style="display:flex;"><span> 2195 ?        S      0:00  <span style="color:#ae81ff">\_</span> hald-runner
</span></span><span style="display:flex;"><span> 2224 ?        S      0:00      <span style="color:#ae81ff">\_</span> hald-addon-input: Listening on /dev/input/event2 /dev/input/event0
</span></span><span style="display:flex;"><span> 2235 ?        S      0:00      <span style="color:#ae81ff">\_</span> hald-addon-acpi: listening on acpid socket /var/run/acpid.socket
</span></span><span style="display:flex;"><span> 2255 ?        Ssl    1:44 automount --pid-file /var/run/autofs.pid
</span></span><span style="display:flex;"><span> 2307 ?        Ss     0:00 rpc.rquotad
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">3078</span> tty2     Ss+    0:00 /sbin/mingetty /dev/tty2
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">3081</span> tty3     Ss+    0:00 /sbin/mingetty /dev/tty3
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">3083</span> tty4     Ss+    0:00 /sbin/mingetty /dev/tty4
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">3092</span> tty5     Ss+    0:00 /sbin/mingetty /dev/tty5
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">3095</span> tty6     Ss+    0:00 /sbin/mingetty /dev/tty6
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">4846</span> tty1     Ss+    0:00 /sbin/mingetty /dev/tty1
</span></span><span style="display:flex;"><span>21685 ?        Ss     3:27 /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7578 ?        S      0:18  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7579 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7580 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7581 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7582 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7583 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7584 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7585 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 7586 ?        S      0:00  <span style="color:#ae81ff">\_</span> /usr/sbin/httpd
</span></span><span style="display:flex;"><span> 6584 ?        Sl    24:30 /usr/bin/monit
</span></span><span style="display:flex;"><span> 6133 ?        Ss     0:07 /usr/sbin/sssd -f -D
</span></span><span style="display:flex;"><span> 6135 ?        S      0:02  <span style="color:#ae81ff">\_</span> /usr/libexec/sssd/sssd_nss --debug-to-files
</span></span><span style="display:flex;"><span> 6136 ?        S      0:02  <span style="color:#ae81ff">\_</span> /usr/libexec/sssd/sssd_pam --debug-to-files
</span></span><span style="display:flex;"><span> 6137 ?        S      0:01  <span style="color:#ae81ff">\_</span> /usr/libexec/sssd/sssd_ssh --debug-to-files
</span></span><span style="display:flex;"><span> 6166 ?        S      0:05  <span style="color:#ae81ff">\_</span> /usr/libexec/sssd/sssd_be --domain default --debug-to-files
</span></span></code></pre></div><p>ps faux # the <b>u</b> will give one system <b>u</b>sage along with the process tree</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ps" data-lang="ps"><span style="display:flex;"><span><span style="color:#a6e22e">USER</span>       <span style="color:#a6e22e">PID</span> <span style="color:#75715e">%CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">2951</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span> <span style="color:#ae81ff">117296</span>  <span style="color:#ae81ff">1256</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>    <span style="color:#ae81ff">2014</span>   <span style="color:#a6e22e">1:35</span> <span style="color:#a6e22e">crond</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">6584</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span> <span style="color:#ae81ff">179628</span>  <span style="color:#ae81ff">2872</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Sl</span>   <span style="color:#a6e22e">Mar17</span>  <span style="color:#a6e22e">24:30</span> /usr/bin/monit
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">jenkins</span>   <span style="color:#ae81ff">5454</span>  <span style="color:#ae81ff">1.4</span> <span style="color:#ae81ff">14.7</span> <span style="color:#ae81ff">2503936</span> <span style="color:#ae81ff">577244</span> <span style="color:#a6e22e">?</span>      <span style="color:#a6e22e">Ssl</span>  <span style="color:#a6e22e">Apr09</span> <span style="color:#a6e22e">700:00</span> /etc/alternatives/java <span style="color:#a6e22e">-Djava.awt.headless=true</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>     <span style="color:#ae81ff">28121</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span> <span style="color:#ae81ff">101428</span>  <span style="color:#ae81ff">3364</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>   <span style="color:#a6e22e">May11</span>   <span style="color:#a6e22e">0:09</span> /var/cfengine/bin/cf-execd
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>     <span style="color:#ae81ff">28130</span>  <span style="color:#ae81ff">0.6</span>  <span style="color:#ae81ff">0.1</span> <span style="color:#ae81ff">366888</span>  <span style="color:#ae81ff">4236</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>   <span style="color:#a6e22e">May11</span>  <span style="color:#a6e22e">24:15</span> /var/cfengine/bin/cf-serverd
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>     <span style="color:#ae81ff">28141</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.1</span>  <span style="color:#ae81ff">35624</span>  <span style="color:#ae81ff">5284</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>   <span style="color:#a6e22e">May11</span>   <span style="color:#a6e22e">0:59</span> /var/cfengine/bin/cf-monitord
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>     <span style="color:#ae81ff">17905</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">22180</span>   <span style="color:#ae81ff">988</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>   <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:00</span> <span style="color:#a6e22e">xinetd</span> <span style="color:#a6e22e">-stayalive</span> <span style="color:#a6e22e">-pidfile</span> /var/run/xinetd.pid
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">dhcpd</span>    <span style="color:#ae81ff">18334</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.1</span>  <span style="color:#ae81ff">49000</span>  <span style="color:#ae81ff">4376</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>   <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:02</span> /usr/sbin/dhcpd <span style="color:#a6e22e">-user</span> <span style="color:#a6e22e">dhcpd</span> <span style="color:#a6e22e">-group</span> <span style="color:#a6e22e">dhcpd</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">6133</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span> <span style="color:#ae81ff">199608</span>  <span style="color:#ae81ff">2372</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">Ss</span>   <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:07</span> /usr/sbin/sssd <span style="color:#a6e22e">-f</span> <span style="color:#a6e22e">-D</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">6135</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.3</span> <span style="color:#ae81ff">201872</span> <span style="color:#ae81ff">14588</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">S</span>    <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:02</span>  <span style="color:#a6e22e">\_</span> /usr/libexec/sssd/sssd_nss <span style="color:#a6e22e">--debug-to-files</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">6136</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span> <span style="color:#ae81ff">192212</span>  <span style="color:#ae81ff">2808</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">S</span>    <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:02</span>  <span style="color:#a6e22e">\_</span> /usr/libexec/sssd/sssd_pam <span style="color:#a6e22e">--debug-to-files</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">6137</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.0</span> <span style="color:#ae81ff">189892</span>  <span style="color:#ae81ff">2688</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">S</span>    <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:01</span>  <span style="color:#a6e22e">\_</span> /usr/libexec/sssd/sssd_ssh <span style="color:#a6e22e">--debug-to-files</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root</span>      <span style="color:#ae81ff">6166</span>  <span style="color:#ae81ff">0.0</span>  <span style="color:#ae81ff">0.1</span> <span style="color:#ae81ff">229764</span>  <span style="color:#ae81ff">6916</span> <span style="color:#a6e22e">?</span>        <span style="color:#a6e22e">S</span>    <span style="color:#a6e22e">May12</span>   <span style="color:#a6e22e">0:05</span>  <span style="color:#a6e22e">\_</span> /usr/libexec/sssd/sssd_be <span style="color:#a6e22e">--domain</span> <span style="color:#a6e22e">default</span>
</span></span></code></pre></div><h2 id="kill">kill</h2>
<p>Now is the time to learn how to stop runaway processes. Always first try to do a service reset as it will exit &ldquo;correctly&rdquo;; and start again and if there are more problems you should be able to see them in a logfile, instead if you just kill it. The process will not have time to show you errors.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ kill PID PID2 PID3;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># you can kill any number of specific processes as long as you get their PID number from either top or ps fax.</span>
</span></span><span style="display:flex;"><span>$ kill -9 PID PID2 PID3; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># this &lt;b&gt;REALY&lt;/b&gt; kills it if the above does not work, sometime you have to resort to the most drastic measure to get a zombie process out of there. Use sparingly as regular kill alows the process to stop and let go of files before exiting. adding -9 kills it immediately and may leave behind file locks.</span>
</span></span><span style="display:flex;"><span>$ killall httpd; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># will kill all processes with the word httpd in the name</span>
</span></span><span style="display:flex;"><span>$ killall -u user1; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># will kill all processes running as the user1 user</span>
</span></span></code></pre></div><h2 id="kill---my-favorite-one-liner">Kill - my favorite one liner</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ps fax | grep httpd | awk <span style="color:#e6db74">&#39;{print $1}&#39;</span> | xargs kill 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># substitue any process name in the grep and kill a bunch at one time, good for when things are going really crazy</span>
</span></span></code></pre></div><h1 id="networking">Networking</h1>
<p>Often times you will have to determine if a service is working correctly by whether or not it is listening on the <a href="http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers">correct</a> port or if it is responding at all</p>
<h2 id="ping">ping</h2>
<p>The most common network debugging tool, <a href="http://linux.die.net/man/8/ping">ping</a>. It is good for a quick up/down test of the machine, but NOTE if the machine is super busy it will not respond to ping right away and you could be seeing slow ping times and it could have nothing to do with the network or the NIC.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ping -t 8.8.8.8 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -t will continuously ping, never stop until you quit</span>
</span></span></code></pre></div><h2 id="telnet">telnet</h2>
<p><a href="http://linux.die.net/man/1/telnet">Telnet</a> is a good way to query a specific remote listening port to see if a service is responding as it should</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ telnet sql_host <span style="color:#ae81ff">5432</span>
</span></span><span style="display:flex;"><span>Trying sql_host...
</span></span><span style="display:flex;"><span>Connected to sql_host.
</span></span><span style="display:flex;"><span>Escape character is <span style="color:#e6db74">&#39;^]&#39;</span>.
</span></span><span style="display:flex;"><span>^<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>exit
</span></span></code></pre></div><h2 id="netstat">netstat</h2>
<p><a href="http://linux.die.net/man/8/netstat">netstat</a> produces a list of listening interfaces, ports, and connections to and from the machine.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ netstat -an
</span></span><span style="display:flex;"><span>Active Internet connections <span style="color:#f92672">(</span>servers and established<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>Proto Recv-Q Send-Q Local Address           Foreign Address         State
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 127.0.0.1:25            0.0.0.0:*               LISTEN
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:5308            0.0.0.0:*               LISTEN
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:46341           0.0.0.0:*               LISTEN
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 127.0.0.1:9000          0.0.0.0:*               LISTEN
</span></span><span style="display:flex;"><span>tcp6       <span style="color:#ae81ff">0</span>      0 :::443                  :::*                    LISTEN
</span></span><span style="display:flex;"><span>tcp6       <span style="color:#ae81ff">0</span>      0 :::34459                :::*                    LISTEN
</span></span><span style="display:flex;"><span>tcp6       <span style="color:#ae81ff">0</span>      0 :::22                   :::*                    LISTEN
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:53348           0.0.0.0:*
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:111             0.0.0.0:*
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:123             0.0.0.0:*
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:36748           0.0.0.0:*
</span></span><span style="display:flex;"><span>udp6       <span style="color:#ae81ff">0</span>      0 :::111                  :::*
</span></span><span style="display:flex;"><span>udp6       <span style="color:#ae81ff">0</span>      0 :::55414                :::*
</span></span><span style="display:flex;"><span>udp6       <span style="color:#ae81ff">0</span>      0 :::48913                :::*
</span></span><span style="display:flex;"><span>raw6       <span style="color:#ae81ff">0</span>      0 :::58                   :::*                    <span style="color:#ae81ff">7</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ netstat -anetu | grep 514; 
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:514                 0.0.0.0:*                   LISTEN
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      0 :::514                      :::*                        LISTEN
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      0 :::5514                     :::*                        LISTEN
</span></span><span style="display:flex;"><span>tcp        <span style="color:#ae81ff">0</span>      0 ::ffff:127.0.0.1:44946      ::ffff:127.0.0.1:9300       ESTABLISHED <span style="color:#ae81ff">497</span>        <span style="color:#ae81ff">3320514</span>
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span> 0.0.0.0:514                 0.0.0.0:*                       
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      0 :::514                      :::*                            
</span></span><span style="display:flex;"><span>udp        <span style="color:#ae81ff">0</span>      0 :::5514                     :::*       
</span></span></code></pre></div><h2 id="ss">ss</h2>
<p>the ss command can do the same and more as netstat and should be used in the future. <a href="http://linux.die.net/man/8/netstat%7C">netstat</a></a> has become deprecated/obsolete to <a href="http://linux.die.net/man/8/ss%7C">ss</a> since CentOS 6.4</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ss -apnetu | grep 443;
</span></span><span style="display:flex;"><span> tcp    LISTEN     <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">128</span>                   :::443                  :::*      ino:202599741 sk:ffff88013beba100
</span></span><span style="display:flex;"><span> tcp    ESTAB      <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      ::ffff:10.0.20.63:443    ::ffff:70.198.42.193:2400   timer:<span style="color:#f92672">(</span>keepalive,119min,0<span style="color:#f92672">)</span> uid:48 ino:228626831 sk:ffff880011629880
</span></span><span style="display:flex;"><span> tcp    TIME-WAIT  <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      ::ffff:10.0.20.63:443    ::ffff:70.198.42.193:2426   timer:<span style="color:#f92672">(</span>timewait,48sec,0<span style="color:#f92672">)</span> ino:0 sk:ffff88013cb3c940
</span></span><span style="display:flex;"><span> tcp    ESTAB      <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      ::ffff:10.0.20.63:443    ::ffff:70.198.42.193:2421   timer:<span style="color:#f92672">(</span>keepalive,119min,0<span style="color:#f92672">)</span> uid:48 ino:228626830 sk:ffff8800027380c0
</span></span><span style="display:flex;"><span> tcp    TIME-WAIT  <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      ::ffff:10.0.20.63:443    ::ffff:70.198.42.193:2410   timer:<span style="color:#f92672">(</span>timewait,47sec,0<span style="color:#f92672">)</span> ino:0 sk:ffff88013cb3ca80
</span></span><span style="display:flex;"><span> tcp    ESTAB      <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      ::ffff:10.0.20.63:443    ::ffff:70.198.42.193:2414   timer:<span style="color:#f92672">(</span>keepalive,119min,0<span style="color:#f92672">)</span> uid:48 ino:228626829 sk:ffff88006031c100
</span></span><span style="display:flex;"><span> tcp    TIME-WAIT  <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      ::ffff:10.0.20.63:443    ::ffff:70.198.42.193:2401   timer:<span style="color:#f92672">(</span>timewait,48sec,0<span style="color:#f92672">)</span> ino:0 sk:ffff880017550e80
</span></span></code></pre></div><h3 id="find-all-incoming-connections-from-unique-ip-addresses">Find all incoming connections from unique IP Addresses</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ netstat -tapn | awk <span style="color:#e6db74">&#39;{print $5}&#39;</span> | sed <span style="color:#e6db74">&#39;s/::ffff://&#39;</span> | sed <span style="color:#e6db74">&#39;s/:.*//&#39;</span> | sort | uniq -c | sort;
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">1</span> 10.0.20.63
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">1</span> 10.0.50.232
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">1</span> 10.0.50.234
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">1</span> 10.0.50.244
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">1</span> 10.0.5.54
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">1</span> 10.10.1.231
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">2</span> 10.0.5.154
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">2</span> 129.82.224.115
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">22</span> 129.82.224.137
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">3</span> 10.0.20.201
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">39</span> 10.0.20.52
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">4</span> 10.0.20.30
</span></span><span style="display:flex;"><span>     <span style="color:#ae81ff">9</span> 0.0.0.0
</span></span></code></pre></div><h3 id="find-all-outgoing-connections-to-unique-ip-addresses">Find all outgoing connections to unique IP Addresses</span></h3></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ss -tapw | awk <span style="color:#e6db74">&#39;{print $5}&#39;</span> | sed <span style="color:#e6db74">&#39;s/::ffff://&#39;</span> | sed <span style="color:#e6db74">&#39;s/:.*//&#39;</span> | sort | uniq -c | sort;
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">1</span> 10.10.1.63
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">12</span> 10.0.20.63
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">1</span> Local
</span></span><span style="display:flex;"><span>  <span style="color:#ae81ff">3</span> 10.0.50.63
</span></span></code></pre></div><h2 id="lsof">lsof</h2>
<p><a href="http://linux.die.net/man/8/lsof">lsof</a> can also be used to determine which process is using a specific port. We once had an issue with a process running on a sendmail port which would not allow sendmail to start on one of our production web servers. This was key in tracking that down and has become a quicker way to check specific ports than netstat.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ lsof -i :514
</span></span><span style="display:flex;"><span>COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
</span></span><span style="display:flex;"><span>rsyslogd <span style="color:#ae81ff">4835</span> root    1u  IPv4 <span style="color:#ae81ff">3354631</span>      0t0  TCP *:shell <span style="color:#f92672">(</span>LISTEN<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>rsyslogd <span style="color:#ae81ff">4835</span> root    2u  IPv6 <span style="color:#ae81ff">3354632</span>      0t0  TCP *:shell <span style="color:#f92672">(</span>LISTEN<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>rsyslogd <span style="color:#ae81ff">4835</span> root    3u  IPv4 <span style="color:#ae81ff">3354623</span>      0t0  UDP *:syslog
</span></span><span style="display:flex;"><span>rsyslogd <span style="color:#ae81ff">4835</span> root    4u  IPv6 <span style="color:#ae81ff">3354624</span>      0t0  UDP *:syslog
</span></span></code></pre></div><h1 id="copying-large-number-of-files">Copying large number of files</h1>
<h2 id="rsync">rsync</h2>
<p><a href="http://linux.die.net/man/1/rsync">rsync</a></a> is the method to use when copying a large number of files big or small. The / in the from where to where are very important and is the diference between copying the directory or just everything within the directory to a new location.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ rsync -avr /nfs/data othermachine:/new/dataarea/ --progress;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># copies local directory and all of it&#39;s content data to othermachine:/new/dataarea/data</span>
</span></span><span style="display:flex;"><span>$ rsync -avr othermachine:/new/dataarea/ /nfs/data --progress; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># copies all data FROM the othermachine under dataarea/ to /nfs/data/</span>
</span></span></code></pre></div><h1 id="screen">screen</h1>
<p>Highly recommended to do any long running operations in a <a href="http://aperiodic.net/screen/quick_reference">screen</a> session so that if your connection to the machine timesout, the operation does not end. This is also a way to do things privetly on a server no one can see within a screen session, unless you allow it</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ screen -S copyfiles; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># start a new session, named copyfiles</span>
</span></span><span style="display:flex;"><span>$ rsync -avr /nfs/data othermachine:/new/dataarea/ --progress; 
</span></span><span style="display:flex;"><span><span style="color:#75715e">## example command</span>
</span></span><span style="display:flex;"><span>Ctrl-a d 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># While the rsync is running, Control-a d will detach but leave running the operation and you can continue doing what ever you want</span>
</span></span><span style="display:flex;"><span>$ screen -r copyfiles; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># will reattach to the the rsync screen session</span>
</span></span><span style="display:flex;"><span>$ screen -d R session_share; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># allows you to &lt;a class=&#34;external text&#34; href=&#34;http://technonstop.com/screen-commands-for-terminal-sharing&#34; rel=&#34;nofollow&#34;&gt;share your session&lt;/a&gt; with someone else, useful for training.</span>
</span></span><span style="display:flex;"><span>$ screen -x session_share; 
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># allows your friend on the same machine to connect to your session</span>
</span></span><span style="display:flex;"><span>Ctrl-d - <span style="color:#66d9ef">while</span> inside the screen will detach and terminate the session
</span></span></code></pre></div><h1 id="i-need-my-script-to-run-at-startup">I need my script to run at startup</h1>
<p>Scenario: On many of our servers we need NFS mounts to other servers/NAS devices to be present at start up so that users/services/websites can get to their data.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ vim /usr/local/bin/nfs_mounts.sh
</span></span><span style="display:flex;"><span><span style="color:#75715e"># insert nfs mount command to new file nfs_mounts.sh</span>
</span></span><span style="display:flex;"><span>$ chmod <span style="color:#ae81ff">700</span> /usr/local/bin/nfs_mounts.sh
</span></span><span style="display:flex;"><span><span style="color:#75715e"># changes permissions so only root user can execute.</span>
</span></span><span style="display:flex;"><span>$ vim /etc/rc.local
</span></span><span style="display:flex;"><span><span style="color:#75715e"># add the line at the end</span>
</span></span><span style="display:flex;"><span>$ /usr/local/bin/nfs_mounts.sh &amp;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># make sure their is an &amp;; symbol after the command</span>
</span></span><span style="display:flex;"><span>$ chmod -x /etc/rc.local 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># the rc.local file should be set to executable permissions</span>
</span></span></code></pre></div><p><strong>sidenote</strong>: Do not use fstab/mtab to auto-mount NFS volumes if they are not present at boot time then the system will hang indefinetly until it is available. our nfs_mounts shell script is the way to get around that.</p>
<h1 id="path">PATH</h1>
<p>Sometimes a problem occurs when one build a project via source and the executable are not located in /usr/bin or /usr/local/bin, the filesystem does not automatically know where to find them via name so one has to type out the full path to interact with those files.</p>
<h2 id="env">env</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ env | grep PATH
</span></span><span style="display:flex;"><span>PATH<span style="color:#f92672">=</span>/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/var/cfengine/bin:/root/bin
</span></span></code></pre></div><h2 id="add-new-location-to-path">Add new location to PATH</h2>
<p>To temporarily add a new location to path type the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ export PATH<span style="color:#f92672">=</span>/home/user/new_path:$PATH;  
</span></span><span style="display:flex;"><span><span style="color:#75715e"># adding $PATH will keep the old PATH along with the new one, order matters, IF you forget $PATH nothing will work, reboot</span>
</span></span></code></pre></div><p>To permanently add a new location to path there are 2 options:</p>
<h3 id="user-specific-paths">User Specific paths</h3>
<p>If the user always logs onto the same machine and needs certain files.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ vim /nethome/username/.bashrc
</span></span><span style="display:flex;"><span>add export command to the file, examples of some below
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># .bashrc</span>
</span></span><span style="display:flex;"><span> export SVN_EDITOR<span style="color:#f92672">=</span>vim
</span></span><span style="display:flex;"><span> export PATH<span style="color:#f92672">=</span>/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/opt/grads-  2.0.1.oga.1/Contents:/opt/grib2/wgrib2:/home/gempak/NAWIPS/os/linux64/bin:/home/ldm/bin:
</span></span><span style="display:flex;"><span> export GAVERSION<span style="color:#f92672">=</span>2.0.1.oga.1
</span></span><span style="display:flex;"><span> source /home/gempak/NAWIPS/Gemenviron.profile
</span></span><span style="display:flex;"><span> export LAPS_DATA_ROOT<span style="color:#f92672">=</span>/wxrnd/lapsdata
</span></span><span style="display:flex;"><span> export LAPS_SRC_ROOT<span style="color:#f92672">=</span>/opt/laps-0-50-19
</span></span><span style="display:flex;"><span> export LAPSINSTALLROOT<span style="color:#f92672">=</span>/opt/laps-0-50-19
</span></span></code></pre></div><h3 id="machine-specific-paths">Machine Specific paths</h3>
<p>This is probably the &ldquo;best practices&rdquo;; way and I would recommend doing this from now instead of .bashrc. As it allows anyone who logs into the machine to have the same PATH settings.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ touch /etc/profile.d/postgresl.sh
</span></span><span style="display:flex;"><span>$ vim /etc/profile.d/postgresql.sh;  <span style="color:#75715e"># make sure file ends with .sh or it will not be read by filesystem</span>
</span></span><span style="display:flex;"><span> export PATH<span style="color:#f92672">=</span>/usr/pgsql-9.3/bin:$PATH
</span></span><span style="display:flex;"><span> export MANPATH<span style="color:#f92672">=</span>$MANPATH:/usr/pgsql-9.3/share/man
</span></span><span style="display:flex;"><span><span style="color:#75715e"># save, log out and log back in</span>
</span></span><span style="display:flex;"><span>$ env | grep PATH;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># and you should now see the new PATH settings</span>
</span></span></code></pre></div><p>Repeat this for any/all source built files that have non-standard paths or settings.</p>
<h1 id="change-hostname">Change Hostname</h1>
<p>The following files are what you need to edit to change the hostname of a machine</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ vim /etc/hostname;
</span></span><span style="display:flex;"><span><span style="color:#75715e"># edit to newhostname.example.com</span>
</span></span><span style="display:flex;"><span>$ vim /etc/hosts; 
</span></span><span style="display:flex;"><span><span style="color:#75715e"># add/edit &#34;newhostname.example.com newhostname&#34; to beginning of both lines in file</span>
</span></span><span style="display:flex;"><span>$ hostname newhostname.example.com
</span></span><span style="display:flex;"><span>$ export HOSTNAME<span style="color:#f92672">=</span>newhostname.example.com;
</span></span></code></pre></div>]]></content>
        </item>
        
        <item>
            <title>Kaizen Your Reservations and Company</title>
            <link>https://blog.twstewart.me/posts/kaizen-your-reservations/</link>
            <pubDate>Sat, 05 Dec 2020 12:42:09 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/kaizen-your-reservations/</guid>
            <description>&lt;p&gt;As we near the end of the year, a majority of us start looking to the next and imagining what we would like to change or improve upon to achieve a goal. I&amp;rsquo;ll explain how mixing a technique that was pioneered by Toyota for lean manufacturing, adapts very well to DevOps transformations as well as personal goal setting and achievement.&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;
&lt;img width=&#34;300&#34; height=&#34;250&#34; src=&#34;https://blog.twstewart.me/img/kaizen_change_good.jpg&#34;&gt;&lt;/img&gt;
&lt;/p&gt;
&lt;h1 id=&#34;what-is-kaizen&#34;&gt;What is Kaizen?&lt;/h1&gt;
&lt;p&gt;The Japanese words “&lt;em&gt;kai-&lt;/em&gt;” which means “&lt;em&gt;change&lt;/em&gt;” and “&lt;em&gt;-zen&lt;/em&gt;” which means “&lt;em&gt;good.&lt;/em&gt;”&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>As we near the end of the year, a majority of us start looking to the next and imagining what we would like to change or improve upon to achieve a goal. I&rsquo;ll explain how mixing a technique that was pioneered by Toyota for lean manufacturing, adapts very well to DevOps transformations as well as personal goal setting and achievement.</p>
<p align="center">
<img width="300" height="250" src="/img/kaizen_change_good.jpg"></img>
</p>
<h1 id="what-is-kaizen">What is Kaizen?</h1>
<p>The Japanese words “<em>kai-</em>” which means “<em>change</em>” and “<em>-zen</em>” which means “<em>good.</em>”</p>
<p><a href="https://en.wikipedia.org/wiki/Kaizen">Kaizen</a> is a process created by the Toyota company to focus on continual improvement of their manufacturing processes. Possibly creating the granddaddy and literal analogue to what we in the tech industry understand as the general concept of Agile Software development. Kaizen as a strategy requires that there is involvement from all levels of a company. It doesn&rsquo;t work as effectively if only one business unit attempts to use this method, and that is because for an organization of any size, the change comes from all places. The planning comes from identifying inefficient processes and then rapidly iterating on solutions until a proper one is found. For that to be most effective, you&rsquo;ll need more than just a single team.</p>
<p>Read more on Kaizen: <a href="https://www.leanproduction.com/kaizen.html">here</a>, <a href="https://www.kaizen.com/what-is-kaizen.html">here</a>, and <a href="https://www.leansixsigmadefinition.com/glossary/kaizen/">here</a></p>
<h1 id="how-do-i-use-kaizen">How do I use Kaizen?</h1>
<p>During new years it is customary in many parts of the world, to set new years resolutions. These are often big monumental changes, and as often joked about, falter after a few weeks. The focus of Kaizen is on small incremental improvements that can be done now, get those all complete, then re-evaluate, the next small incremental improvement to get closer to the overall goal.</p>
<h2 id="kaizen-applied-to-real-life">Kaizen applied to real life</h2>
<p>I gained too much weight over quarantine and need to lose 30 pounds.</p>
<p>If you immediately run to the gym, jump on a treadmill, and begin running, good for you. Keep at it. but what happens to most of us, is life. Simply said, things get in our way, we skip a day, and the next time you have a chance to blink, that goal is three months behind and maybe you&rsquo;ve gone in the opposite direction and need to lose more weight than when you began.</p>
<p>Instead of setting one goal that is far away and hard to imagine how many days, how many miles, how many meals are between it and you now. Set a weekly goal. I&rsquo;ll reduce calories and workout enough that I lose 2 lbs a week.</p>
<p>This way at the end of each week, you have mental check-ins and can adapt much more often to changes in your schedule, time, stress, and diet.</p>
<p>At no part am I saying, don&rsquo;t set a big goal, those are great and we all have them. Just don&rsquo;t forget to map out a realistic path to achieving that, with check-ins at regular intervals along the way to ensure you remain on track.</p>
<h1 id="how-can-a-devops-team-use-kaizen">How can a DevOps Team use Kaizen?</h1>
<p>An example of this, is one I actually witnessed at a previous employer. This company had 2-dozen separate business units (tons of merges and acquisitions) each with their own SaaS product, with entirely different legacy of development and technical debt.</p>
<p>In an effort to standardize processes, tech stacks, and stream line communication, all DevOps and SysAdmin teams became a company wide SRE organization. Suddenly we were in charge of release cadence, creating Service Level Indicators, and overall worshiping the <a href="https://sre.google/workbook/table-of-contents/">Google SRE handbook</a>, in addition to everything else we did previously.</p>
<p>Each group had to define Service Level Objectives that were better than our Service Level Agreements with our customers. We also had to call out areas that were in danger of keeping us from not meeting those goals, whether those were single points of failure in our tech stack or if they were toil issues, like endless support tickets that could and should be automated.</p>
<p>Once we had this all defined and agreed upon with key stakeholders, we used this live document to re-asses at monthly intervals how well we were doing.</p>
<p>The key point is that we didn&rsquo;t just have utopian and mostly empty goals like 4 nines of uptime. We had to prove with real data that we were meeting our Service Level Objectives. If we started to dip below that, then we were within our rights to hit the breaks on all development. Circle the wagons, and swarm issues until they were fully resolved.</p>
<p>A few problems with the way we did this (telling you this, because these things are difficult to implement): 1) There wasn&rsquo;t a clear understanding of what the Kaizen process was. 2) It took too long. We spent a good 2-3 months just documenting things, most of that was due to poor buy in from different departments, so it took weeks to get questions answered. By the time we had everything together, other stakeholders were tired of waiting and their attention fizzled out. 3) It can&rsquo;t just be a DevOps/SRE thing, it has to include everyone; finance, development, project management, sales, etc</p>
<h1 id="kaizen-and-agile">Kaizen and Agile</h1>
<p>If everything I&rsquo;ve described now sounds a lot like <a href="https://en.wikipedia.org/wiki/Agile_software_development">Agile</a>, good, it should. But the thing is, Agile is mostly seen as just a way to manage software development. Kaizen is much bigger than that, focusing on the entire company.</p>
<p>The core focus of both Agile and Kaizen rest on continually having periods that one critically evaluates goals, performance, and the gap between them to make the company or product successful. In Agile this is expressed through sprint retrospectives, sprint planning, and having well known definitions of done.</p>
<p>If a lot of your time is stuck with handling issues and not developing new infrastructure or features, then maybe it is time to talk cross-departments. Get out of your silo and figure out a solution. Challenge yourself, your team, and your company. I&rsquo;m positive the company will be better off if you solve these critical but time consuming bugs. Of course, that makes Kaizen all the harder to pull off, as you need buy in from more than just your team. The flip side of this, is that it gives your department a more holistic view of the problems on the other side. Maybe the ticket management workflow is too cumbersome for that other group, and so they keep personally contacting you to help with a problem. Maybe if you had a week and 3 other dedicated hands on keyboards, you could knock out several critical known problems that keep you awake at night.</p>
<h1 id="kaizen-events">Kaizen Events</h1>
<p>A Kaizen event (also known as a burst or blitz), can be a great way to kickstart this process in your company. Here&rsquo;s a few steps to do that:</p>
<ol>
<li><strong>Plan</strong>: Gather a lot of people (virtually now), from different cross-functional teams. Create a list of top 5-10 improvement ideas. Talk this through, make sure everyone involved sees the value of these improvements and has a part in achieving it.</li>
<li><strong>Execute</strong>: During the event 80% of the action items should be achievable. The other 20% should be completable within 30 days of the event.</li>
<li><strong>Check</strong>: Have a post event meeting where the state of improvements are discussed and documented - did they work, were they effective?</li>
<li><strong>Act</strong>: Keep these newly adapted processes or fixes; then start a new cycle</li>
</ol>
<p>Back at Toyota, they made these week long events - <a href="https://www.leansixsigmadefinition.com/glossary/kaikaku/">five days and one night</a>. So they are intense focused action, all hands on deck. And because they are that way, they should only be conducted a few times a year to avoid over use and burn out.</p>
]]></content>
        </item>
        
        <item>
            <title>Be Kind - Kubernetes sandbox on Windows 10</title>
            <link>https://blog.twstewart.me/posts/be-kind-kube-on-win10/</link>
            <pubDate>Sun, 22 Nov 2020 07:30:11 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/be-kind-kube-on-win10/</guid>
            <description>&lt;p&gt;Last week, I spent a fantastic couple of days remotely visiting &lt;a href=&#34;https://events.linuxfoundation.org/kubecon-cloudnativecon-north-america/&#34;&gt;KubeCon2020 and CloudNativeCon2020&lt;/a&gt;. Tons of great keynotes, in depth demos, and more information than was humanly possible to absorb. I, as an interested explorer to all this Cloud Native &amp;ldquo;Stuff&amp;rdquo;, wanted to rush home and try it for myself. In that aim, there are a few projects aimed at small, sandbox style installations, for the individual that wishes to get their hands dirty and start learning without spending buckeroos on cloud resources or &lt;a href=&#34;https://github.com/todaywasawesome/atomic-cluster&#34;&gt;making a fleet of Atomic Pis&lt;/a&gt; (side note: I absolutely will be doing this in the near future).&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>Last week, I spent a fantastic couple of days remotely visiting <a href="https://events.linuxfoundation.org/kubecon-cloudnativecon-north-america/">KubeCon2020 and CloudNativeCon2020</a>. Tons of great keynotes, in depth demos, and more information than was humanly possible to absorb. I, as an interested explorer to all this Cloud Native &ldquo;Stuff&rdquo;, wanted to rush home and try it for myself. In that aim, there are a few projects aimed at small, sandbox style installations, for the individual that wishes to get their hands dirty and start learning without spending buckeroos on cloud resources or <a href="https://github.com/todaywasawesome/atomic-cluster">making a fleet of Atomic Pis</a> (side note: I absolutely will be doing this in the near future).</p>
<h2 id="be-kind">Be Kind</h2>
<p align="center">
<img width="300" height="250" src="/img/kind-logo.png"></img>
</p>
<p>The main solution that I found to be the most painless to setup was from a small and somewhat newer project called Kind. There are others like <a href="https://minikube.sigs.k8s.io/docs/start/">MiniKub</a> or <a href="https://microk8s.io/">Microk8s</a> . All of them provide about the same thing, but watching the Con chatter it seemed a lot more folks were excited to share Kind(ness) as the main getting started tool.</p>
<p>The big difference is that Kind does not use a Virtual machine, but instead uses docker containers, so it feels much more integrated with the cloud native super project Kubernetes, which is afterall, a container orchestrator.</p>
<p><a href="https://kind.sigs.k8s.io/">https://kind.sigs.k8s.io/</a></p>
<h2 id="getting-started">Getting Started</h2>
<p>These instructions are going to be based around Windows specifically, because that is what I use for my daily computer. Because it&rsquo;s windows, there are a few extra hoops to jump through, but have no worries, because we will step through those together.</p>
<h3 id="1-prerequisites">1. Prerequisites</h3>
<p>The biggest pre-req to make this all work seamlessly, is to have a version of windows (OS build 20211 or higher) that support WSL version 2.  If you do not have a version of windows that is compatible with WSLv2, then the other projects like MiniKub or MicroK8S may work better for your use case.</p>
<p>Verify your system is up to date with V2 by following the <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">Microsoft WSLv2 docs</a></p>
<p>WSL v2 is the Windows Subsystem for Linux, but what is special about V2, is that docker for windows can utilize WSL for managing near-natural (cgroups, shared kernel) docker containers within WSL instead of creating a headless Virtual Machine, which tends to be slow and overall a less than pleasing experience. Another improvement with WSLv2, is the integration with VSCode is much nicer. Through VSCode one can open a session in WSLv2 with whatever flavor of Linux you choose ( I went with Ubuntu 20.04) and have full Linux support/tooling, which often speeds things up as many documents and Cloud Native technologies are geared more towards the Linux/MacOS crowds.</p>
<h4 id="enable-wsl2">Enable WSL2</h4>
<p>With Admin Powershell</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform, Microsoft-Windows-Subsystem-Linux
</span></span></code></pre></div><p>Reboot</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>wsl --set-default-version <span style="color:#ae81ff">2</span>
</span></span></code></pre></div><p>Install a Linux distro from the Windows Store, I&rsquo;ve gone with Ubuntu 20.04</p>
<h3 id="docker-desktop">Docker Desktop</h3>
<p>After WSLv2, Docker for Windows needs to be installed and configured. Follow Docker&rsquo;s instructions here: <a href="https://docs.docker.com/docker-for-windows/install/">Docker Desktop</a></p>
<p>Once Docker Desktop is installed there is one setting to verify to make sure it is using the WSLv2 backend instead of a Virtual Machine.</p>
<ol>
<li>Find the whale in your system tool bar (usually on the bottom right)</li>
<li>Right click on Whale, select Settings</li>
<li>Within General, make sure Use the WSL2 Based Engine is selected</li>
<li>Apply and Restart</li>
</ol>
<h3 id="go">Go</h3>
<p>Now we are read to install the final Prerequisite, GO. Since we are using WSL for everything it must be a Linux compatible version of Golang and must be greater than version 1.11. Which of course does not come in the default (apt install golang) installation path. To install a recent version of go follow these instructions</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># All of these steps are to be ran on Ubuntu 20.04 within WSLv2</span>
</span></span><span style="display:flex;"><span>wget https://golang.org/dl/go1.15.5.linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>sudo tar -C /usr/local -xzf go1.15.5.linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>export PATH<span style="color:#f92672">=</span>$PATH:/usr/local/go/bin
</span></span><span style="display:flex;"><span><span style="color:#75715e"># to save the path for every start up do the following</span>
</span></span><span style="display:flex;"><span>sudo touch /etc/profile.d/go.sh
</span></span><span style="display:flex;"><span>sudo chmod +x /etc/profile.d/go.sh
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;export PATH=</span>$PATH<span style="color:#e6db74">:/usr/local/go/bin&#34;</span> | sudo tee -a /etc/profile.d/go.sh
</span></span></code></pre></div><p>Then every time you login the path will be added to the default and magic can begin</p>
<h2 id="setup-kind">Setup Kind</h2>
<p>Lucky for us, although the prerequisites are a bit of jumble to get up and running, the install of Kind is a single one line</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GO111MODULE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;on&#34;</span> sudo go get sigs.k8s.io/kind@v0.9.0
</span></span></code></pre></div><h2 id="install-kubectl">install kubectl</h2>
<p>The main and official tool to manage a kubernetes cluster is known as kubectl. Now is the time to install that, and double bonus the wonderful folks at Google, have distilled all of the complexities down to a single one liner.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -LO <span style="color:#e6db74">&#34;https://storage.googleapis.com/kubernetes-release/release/</span><span style="color:#66d9ef">$(</span>curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt<span style="color:#66d9ef">)</span><span style="color:#e6db74">/bin/linux/amd64/kubectl&#34;</span>
</span></span></code></pre></div><p>From here you can start a kubernetes cluster on your machine</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo kind create cluster
</span></span><span style="display:flex;"><span>sudo kubectl create deployment nginx --image<span style="color:#f92672">=</span>nginx --port<span style="color:#f92672">=</span><span style="color:#ae81ff">80</span>
</span></span></code></pre></div><p>&hellip;and you&rsquo;re done&hellip;well sort of</p>
<h3 id="windows-funkiness">Windows Funkiness</h3>
<p>Since we&rsquo;re a few layers deep in abstractions and subsystems and dockers, there&rsquo;s a special way to deploy kind so that it can forward traffic from windows to a deployment which enables you to bring up the frontend deployment in a web browser or suing a cli tool like curl.</p>
<h4 id="start-over">Start over</h4>
<p>Assuming you started the nginx deployment from the above instructions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo kubectl delete deployment nginx
</span></span><span style="display:flex;"><span>sudo kind delete cluster
</span></span></code></pre></div><p>create a file in wsl2, cluster-config.yml</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Cluster</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">kind.x-k8s.io/v1alpha4</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">nodes</span>:
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">role</span>: <span style="color:#ae81ff">control-plane</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">extraPortMappings</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">containerPort</span>: <span style="color:#ae81ff">30000</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">hostPort</span>: <span style="color:#ae81ff">30000</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">protocol</span>: <span style="color:#ae81ff">TCP</span>
</span></span></code></pre></div><p>The above YAML once applied will give our windows machine access to a service running within Kubernetes on port 30000.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo kind create cluster --config<span style="color:#f92672">=</span>cluster-config.yml
</span></span><span style="display:flex;"><span>sudo kubectl create deployment nginx --image<span style="color:#f92672">=</span>nginx --port<span style="color:#f92672">=</span><span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>sudo kubectl create service nodeport nginx --tcp<span style="color:#f92672">=</span>80:80 --node-port<span style="color:#f92672">=</span><span style="color:#ae81ff">30000</span>
</span></span><span style="display:flex;"><span>curl http://localhost:30000
</span></span></code></pre></div><p>The above and more information on all of the WSL2 considerations for using Kind can be found on the official docs here: <a href="https://kind.sigs.k8s.io/docs/user/using-wsl2/">https://kind.sigs.k8s.io/docs/user/using-wsl2/</a></p>
<h1 id="summary">Summary</h1>
<p>If you have Windows 10 OS build 20211 or higher, than this guide can take you from 0 kubernetes to up and running in about a half an hour, with most of that waiting for a few things to download.</p>
<p>Getting started with Kubernetes on Windows is getting easier and more accessible all the time, and thanks to tools like WSLv2 and Kind, now is a great opportunity to get started without a high barrier to entry.</p>
]]></content>
        </item>
        
        <item>
            <title>AWS CodeCommit CodeBuild and CodePipeline...Oh My</title>
            <link>https://blog.twstewart.me/posts/aws-code-commit-build-pipeline-oh-my/</link>
            <pubDate>Fri, 20 Mar 2020 05:26:04 -0400</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/aws-code-commit-build-pipeline-oh-my/</guid>
            <description>&lt;p&gt;At my day job we&amp;rsquo;ve leveraged Azure DevOps to handle our Continuous Integration and Continuous Deployment needs and for the most part it is a really well done tool. Nearly all tasks can be defined as code in a single YAML file that is easy to track changes and having the integrated ticket/kanban board is a huge bonus so one can immediately see when certain changes were applied, which erases ambiguity and confusion. I&amp;rsquo;ve also used jenkins and gitlab to some extent at past gigs, which all had pluses and minuses, but I feel like I have a pretty good grasp on what developers and operators require in a CI/CD pipeline.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>At my day job we&rsquo;ve leveraged Azure DevOps to handle our Continuous Integration and Continuous Deployment needs and for the most part it is a really well done tool. Nearly all tasks can be defined as code in a single YAML file that is easy to track changes and having the integrated ticket/kanban board is a huge bonus so one can immediately see when certain changes were applied, which erases ambiguity and confusion. I&rsquo;ve also used jenkins and gitlab to some extent at past gigs, which all had pluses and minuses, but I feel like I have a pretty good grasp on what developers and operators require in a CI/CD pipeline.</p>
<p>As a lazy soul and DevOps engineer, I&rsquo;ve gotten tired of manually building and syncing the changes for this website to the S3 bucket which hosts the content you are viewing. My goal was to automate the hugo build and upload to S3 for this very website. A perfect opportunity and chance to throw together some automation to handle these tasks for me. For me I needed 3 features or tasks in my pipeline:</p>
<ol>
<li>A place to push code to/A git repo</li>
<li>A way to build the website/Continous Integration</li>
<li>A way to deploy the rendered output to an S3 bucket/Continous Deployment</li>
</ol>
<p>Earlier this week, as the title suggests, I decided to take a gander at AWS&rsquo;s offerings which are comprised of 4 maybe 5 services -  AWS CodeCommit, AWS CodeBuild, AWS CodeDeploy, AWS CodePipeline, and AWS CodeStar. Each service does what it sounds, but much of the cookie cutter templates would prove to not work for me and hugo so AWS CodeStar was out immediately.</p>
<h2 id="aws-codecommit">AWS CodeCommit</h2>
<p>AWS CodeCommit proved to be the most straight forward thing to setup. If you&rsquo;ve ever started a repo on github or any hosted git solution, they all work the same and this one is no different. HTTPS and SSH authentication/keys are all managed with your IAM user account, which is nice to not have a separate place to manage access and authentication.</p>
<p>The Service claims to be free for 5 or less users so a perfect tool for very small teams or personal projects, even if all you are looking for is a code backup solution.</p>
<p>My own negative for CodeCommit is that there&rsquo;s no issue tracker, seems like they could setup a CodeTracker service and I&rsquo;d think AWS could at least compete with Azure DevOps, but as it stands it&rsquo;s just not a fully realized suite of tools yet. A great tool for personal projects, but not for teams.</p>
<h2 id="aws-codebuild">AWS CodeBuild</h2>
<p>AWS CodeBuild allows you, through a series of click through menus, to define an environment which can run a build process on a container or EC2 instance and output a file or folder to S3 or some other destination for storage.</p>
<p>This took the most time to get right, mostly because I was used to the way Azure DevOps handles Build tasks. In AZDO you can define everything; environment, server type (linux vs windows), steps, tasks, jobs, and integrate with tons of plugins for different services ALL in a single YAML file. What you can define in the buildspec.yml file for CodeBuild is only the task steps that will happen inside the build agent/runner.</p>
<p>Below is the buildspec.yml file for building a site with Hugo</p>
<pre tabindex="0"><code>version: 0.2

phases:
  install:
    commands:
      - echo Install Start
      - apt-get -qq update &amp;&amp; apt-get -qq install curl
      - apt-get -qq install asciidoctor
      - curl -s -L https://github.com/gohugoio/hugo/releases/download/v0.67.1/hugo_0.67.1_Linux-64bit.deb -o hugo.deb
      - dpkg -i hugo.deb
    finally:
      - echo Install Finished
  build:
    commands:
      - echo Build Start
      - cd $CODEBUILD_SRC_DIR
      - rm -f buildspec.yml &amp;&amp; rm -rf .git &amp;&amp; rm -f README.md
      - hugo --quiet
    finally: 
      - echo Build Finished
artifacts:
  files:
    - &#39;**/*&#39;
  base-directory: $CODEBUILD_SRC_DIR/public/
  discard-paths: no
</code></pre><p>The Publish/Artifact Section gave me a lot of trouble. The S3 bucket which hosts the website has all the content at <code>/</code> and luckily there was some miscellaneous forum posts which steered me in the right direction.</p>
<p><img src="/img/codebuild-s3.png" alt="CodeBuild Artifact"></p>
<ul>
<li>Name needs to be /</li>
<li>Disable artifact encryption needs to be checked for the files to be readable to folks like you.</li>
</ul>
<p>As I got that solved, I had everything I thought I needed, the git repo, the build process, and the file delivery to S3. A short lived victory, until I discovered that there is no way to auto-trigger CodeBuild from CodeCommit, you can run them manually, but that&rsquo;s not the DevOps way. Nay, the only way to automagically start a build process is through CodePipeline.</p>
<h2 id="aws-codepipeline">AWS CodePipeline</h2>
<p>The essential use of CodePipeline is to unify all the other CodeBlah services into one final grouping and be the missing automation part that so many of us seek.</p>
<ul>
<li>Source = Match to correct repo in AWS CodeCommit</li>
<li>Build = Match to the correct Build process in AWS CodeBuild</li>
<li>Deploy = ??? Re-define the Deploy to S3 but more confusingly</li>
</ul>
<p>The question marks are from me, in my Build process it was delivering the output to my desired bucket. Apparently when ran through Pipelines, the Pipeline ignores those settings and will upload the artifact as a zipped up package to a secondary S3 bucket. Then you have the joy of setting up a third Deploy step of the pipeline to get the Build Artifact and push the output into the intended final destination.</p>
<p>If you are confused, because it seems like we already did that step in the Build process and now you have to set it up again, you&rsquo;d be correct in that feeling.</p>
<p>CodePipeline also charges $1/per month for an active (ran at least once) pipeline.</p>
<h2 id="oh-my">Oh My</h2>
<p>Overall I think there is good value to gain from having a semi-unified suite of tools to handle CI/CD. Far better than a spattering of separate Git service, Jenkins for CI/CD, and JIRA or Trello for project tasks/issue tracking. I do wish AWS would streamline a few of these features and not make them separate services, maybe for larger projects the complexity is justified, but for my experiment it felt overkill. Also if they dug in and made a project management/issue tracking service (even if it&rsquo;s a github clone) that integrates with all of them, I suspect more teams would be willing to overcome some of the rougher edges of these services in order to have everything in AWS vs using another third party service be that Azure DevOps or the Atlassian suite of tools for the same.</p>
]]></content>
        </item>
        
        <item>
            <title>Refreshed Creative Blog</title>
            <link>https://blog.twstewart.me/posts/refreshed-creative-blog/</link>
            <pubDate>Wed, 18 Mar 2020 06:53:24 -0400</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/refreshed-creative-blog/</guid>
            <description>&lt;p&gt;A few months back I noticed some links on my &lt;a href=&#34;https://stewstunes.com&#34;&gt;creative/writing/music blog&lt;/a&gt; that were no longer working, and as things spun out of control I decided to redo both the platform for blogging and the look and feel of that website.&lt;/p&gt;
&lt;div align=&#34;center&#34;&gt;
&lt;a href=&#34;https://stewstunes.com&#34;&gt;
 &lt;img src=&#34;https://stewstunes.com/wp-content/uploads/2017/05/stew-stunes-bcard2.png&#34; style=&#34;width:500px; height:300px&#34; alt=&#34;Amazon Link&#34;&gt;&lt;/img&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;The old version of my creative website ran marvelously with &lt;a href=&#34;https://wordpress.org/&#34;&gt;wordpress&lt;/a&gt; on the smallest available instance size inside google cloud. That worked fine, but it was always just a tiny bit slow and the more I thought about it, the more I realized I didn&amp;rsquo;t need many features. The unfortunate truth is that because wordpress is so popular for blogging, is that it creates a wide target for bots and hackers to attack. In short, all the management features that made wordpress a very nice and easy to use platform, was also creating this insecure mess that I felt I needed to extract myself from. Why does a very simple, mostly static, website require a database backend and bunch of miscellanies ways to log into the &amp;ldquo;backend&amp;rdquo;? All this website should do is present content, words, and images, nothing more.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<p>A few months back I noticed some links on my <a href="https://stewstunes.com">creative/writing/music blog</a> that were no longer working, and as things spun out of control I decided to redo both the platform for blogging and the look and feel of that website.</p>
<div align="center">
<a href="https://stewstunes.com">
 <img src="https://stewstunes.com/wp-content/uploads/2017/05/stew-stunes-bcard2.png" style="width:500px; height:300px" alt="Amazon Link"></img>
</a>
</div>
<p>The old version of my creative website ran marvelously with <a href="https://wordpress.org/">wordpress</a> on the smallest available instance size inside google cloud. That worked fine, but it was always just a tiny bit slow and the more I thought about it, the more I realized I didn&rsquo;t need many features. The unfortunate truth is that because wordpress is so popular for blogging, is that it creates a wide target for bots and hackers to attack. In short, all the management features that made wordpress a very nice and easy to use platform, was also creating this insecure mess that I felt I needed to extract myself from. Why does a very simple, mostly static, website require a database backend and bunch of miscellanies ways to log into the &ldquo;backend&rdquo;? All this website should do is present content, words, and images, nothing more.</p>
<p>The creative website is now compiled by <a href="https://gohugo.io/">Hugo</a>, a static site generator, then uploaded to Amazon&rsquo;s S3 Storage solution, then using tricks with their CDN called Cloudfront, you are served a website with no backend to manage, just content, words, and images. Plus it is far&hellip;far&hellip;far&hellip;less expensive. like ~$1.50/month vs ~$12.</p>
<p>The hard part, that took many months, was finding a way to convert the old website content from html to markdown syntax, fixing ALL the links, and then working with the new system/layout to get everything perfect.</p>
<p>I also created my own hosting/backend for playing music. I was tired of services like SoundCloud or Fanburst retiring or limiting what I could do, so I rolled my own.</p>
</br>
<div align="center">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://stewstunes.com/html5-audio-player/css/AudioPlayer.css" rel="stylesheet">
<div id='player'></div>
<style>
#player{
  background-position: center;
  background-color: black;
  background-image: url("https://stewstunes.com/wp-content/uploads/2018/03/kingschallenge_wp-768x418.jpg");
  background-repeat: no-repeat;
  position: relative;
  max-width: 700px;
  height: 500px;
  border: solid 1px rgb(161, 0, 46);
}
</style>
<script type="text/javascript" src="https://stewstunes.com/html5-audio-player/js/AudioPlayer.js"></script>
<script type="text/javascript" src="https://stewstunes.com/html5-audio-player/the-kings-challenge.js"></script>
</div>
</br>
<h1 id="crossing-the-streams">Crossing the Streams</h1>
<p>In general, I try to keep my creative works separate from my professional work, but today I thought I&rsquo;d break that rule if only because I&rsquo;m so proud with how the website turned out.</p>
<p>So yes, I amateurishly write books and music as way to let off steam and because I like creating and exploring a non-technical side of my nature.</p>
<p>For one post only, below is a condensed view of what all I&rsquo;ve made and feel proud to share with all of you.</p>
<h2 id="among-the-clouds">Among The Clouds</h2>
<p><strong>A Wild Girl, A Toxic Mist, A Rebel Princess &amp;&amp; The City Among The Clouds</strong></p>
<blockquote>
<p>Jai is a huntress warrior for her tribe. When her village and mother falls sick from a toxic mist, she sets her target on the floating city that abandoned the surface centuries ago.</p>
<p>On her journey to find a cure, she encounters allies, violent arcade games, rebel gangs, kings and queens, and a weapon so dangerous that it could rewrite human history. With the help of Alice, the disgraced princess, Jai stumbles and battles through the maze of skyscrapers, love, and advanced technology aboard the floating city. Fighting to return home before time runs out on her loved ones.</p>
<p>Among The Clouds is a female-fronted, fast-paced, YA novel, set in a backdrop of cyberpunk and science fiction aesthetics. Among The Clouds is Stew Stunes third full-length novel which exists within his shared narrative “Universe of Chaos.”</p>
</blockquote>
<p>Read More Here: <a href="https://stewstunes.com/books/among-the-clouds">Among The Clouds</a></p>
<link href="https://stewstunes.com/css/grid.css" rel="stylesheet">
<div class="rowwelcome" >
<div class="columnwelcome">
<a href="https://www.amazon.com/gp/product/B07C6MZVZB/">
 <img src="https://stewstunes.com/wp-content/uploads/2017/06/amazon-logo_black.png" style="width:150px; height:75px" alt="Amazon Link"></img>
</a>
</div>
</br> 
<div class="columnwelcome">
<a href="https://www.amazon.com/gp/product/B07C6MZVZB/">
 <img src="https://stewstunes.com/wp-content/uploads/2018/02/ATC1-skull-188x300.jpg" style="width:188px; height:300px" alt="Amazon Link"></img>
</a>
</div>
</div>
</div>
<p> </p>
<hr>
<h2 id="the-kings-challenge">The King&rsquo;s Challenge</h2>
<p>My second full-length novel is available on Amazon! A female-fronted dark fantasy adventure set in the same narrative universe as The Writers of the Universe, this story takes place in the world of Aura 400 years after the events in my first book.</p>
<blockquote>
<p>The once prosperous nation of Redren fought back against the darkness. A darkness that entered their world through an impossible event and promised to deliver every living soul to its master, Death. The king and magici of an age long past found a way to defeat the darkness and wrote The Knowledge, a historical account that should never be forgotten. Within The Knowledge, they created The King’s Challenge a once in a generation event to determine the king‘s worthiness and readiness should the darkness return. As family and friends fall victim to a faceless shadow who has haunted Khymn throughout her life, she must decide on the ultimate choice: Save the world and turn her back on everything her people hold dear or be the one who allowed permanent darkness to fall over the entire world of Aura.</p>
</blockquote>
<div class="rowwelcome" >
  <div class="columnwelcome">
    <a href="https://www.amazon.com/gp/product/B07C6MZVZB/">
      <img src="https://stewstunes.com/wp-content/uploads/2018/02/kingschallenge2-188x300.jpg" style="width:188px; height:300px" alt="Amazon Link"></img>
    </a>
  </div>
  </br> 
  <div class="columnwelcome">
    <a href="https://www.amazon.com/gp/product/B07BJNXMZD/">
      <img src="https://stewstunes.com/wp-content/uploads/2017/06/amazon-logo_black.png" style="width:150px; height:75px" alt="Amazon Link"></img>
    </a>
  </div>
</div>
</br>  
<p>Read More Here: <a href="https://stewstunes.com/books/the-kings-challenge">The King’s Challenge</a></p>
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/ogSERyDKtDY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p> </p>
<hr>
<h2 id="the-writers-of-the-universe">The Writers of the Universe</h2>
<blockquote>
<p>The ending has changed! Through their words written in stories and books, The Writers of the Universe control the lives of us all. A struggling human author and quite possibly the most unlucky person in existence is about to leap from the page and journey to realms unknown in order to bring back chaos and free will to the universe. For without uncertainty, without fear of the unknown, there is no point in going through life if its only purpose is to get to the end of the book.</p>
</blockquote>
<div class="rowwelcome" >
<div class="columnwelcome">
<a href="https://www.amazon.com/gp/product/B079VTV63S/">
 <img src="https://stewstunes.com/wp-content/uploads/2017/06/amazon-logo_black.png" style="width:150px; height:75px" alt="Amazon Link"></img>
</a>
</div>
</br> 
<div class="columnwelcome">
<a href="https://www.amazon.com/gp/product/B079VTV63S/">
 <img src="https://stewstunes.com/wp-content/uploads/2018/02/Writers-of-the-Universe-The-Stew-Stunes-188x300.jpeg" style="width:188px; height:300px" alt="Amazon Link"></img>
</a>
</div>
</div>
</br>
<p>Read More Here: <a href="https://stewstunes.com/books/the-writers-of-the-universe">The Writers of the Universe</a></p>
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/XX2ugcNP11g" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
]]></content>
        </item>
        
        <item>
            <title>Migrating from SFTP to AWS Transfer Service</title>
            <link>https://blog.twstewart.me/posts/sftp-uploads/</link>
            <pubDate>Sat, 01 Feb 2020 05:17:19 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/sftp-uploads/</guid>
            <description>&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;This document was written by Myself for a former employer, with contributions from Michael G in Feb 2019. This project was never fully deployed, and it is still to this day, one of my greatest professional disappointments - not being able to get this solution to replace the old solution. The failure was not of a technical nature but a loss of development talent in our project group and a near total refusal to invest in these legacy solutions. I think many companies with a major SFTP type workflow are stuck with something similar to what we had to deal with. It is my hope that companies left with a legacy solution one which worked 20 years ago, but in modern times falls far short on redundancy, security, and even cost. That there are companies, that care enough to invest in a robust solution like the one detailed here, will be able to take this document and build from this proposal.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<h2 id="overview">Overview</h2>
<p>This document was written by Myself for a former employer, with contributions from Michael G in Feb 2019. This project was never fully deployed, and it is still to this day, one of my greatest professional disappointments - not being able to get this solution to replace the old solution. The failure was not of a technical nature but a loss of development talent in our project group and a near total refusal to invest in these legacy solutions. I think many companies with a major SFTP type workflow are stuck with something similar to what we had to deal with. It is my hope that companies left with a legacy solution one which worked 20 years ago, but in modern times falls far short on redundancy, security, and even cost. That there are companies, that care enough to invest in a robust solution like the one detailed here, will be able to take this document and build from this proposal.</p>
<p>A project to improve redundancy and security issues related to the current configuration of the upload and processing servers and ssh/sftp access allowed to clients into that server</p>
<h3 id="original-scopeidea">Original Scope/Idea</h3>
<p>Make it so that all upload and import functions are not dependent on a single server in a single AZ.</p>
<p>Move all customer logins to an LDAP/AD solution/get away from local Unix accounts:</p>
<p>Move all data to S3 backend and design an sftp → log in via AD -&gt; S3 directly.</p>
<p>Then on the back-end mount s3 to main and have no direct access to main from outside</p>
<h3 id="what-ended-up-happening">What ended up happening</h3>
<p>Our original concept for this project was to join AWS&rsquo;s Transfer for SFTP to AWS&rsquo; managed Active Directory in order to send uploads to S3 instead of the main server. What ended up happening through the discovery and Proof of Concept phase of this project is something much different but, in my opinion, an overall better solution. One that scales from dozens of logins to thousands or millions, is reliable, is secure and encrypted, is easy to manage and works for our team, development, and most of all our customers.</p>
<p>AWS Transfer for SFTP does offer its own user management but requires SSH Key authentication only. It does not support password authentication, and it was thought that it would be overly burdensome to have all our thousands of customers understand public/private keys and reimplement every customer to support this change. We want this to be as invisible to the customer experience as possible. This is why it was pertinent to stick with a custom authentication method in order to have success with this project.</p>
<p>We began with an attempt to set up AWS&rsquo; Managed Active Directory solution, this was a complete fail for us because you needed more Microsoft Tools, like a Certificate Authority, to provision it properly with SSL.</p>
<p><br>
We then set up an OpenLDAP server, that we ourselves built and would have to manage, a compromise for sure, okay, but not great.</p>
<p>We shifted gears to understand the AWS Transfer for SFTP service AWS offered and even setting up a single test proved to show that we needed far more layers than originally thought. Originally thinking all we needed was some type of directory service plus the Transfer Server. At this point I thought we were doomed, instead, we learned that AWS SFTP needed to connect to AWS API Gateway to connect to AWS Lambda to run code that could verify an account in LDAP to enable access to a single S3 bucket/Directory. What we once thought as 3 new pieces of technology quickly grew to 5 independent pieces.</p>
<p>Lucky to us, AWS had provided a sample CloudFormation template, so we could set up a sample Lambda + API Gateway to see these pieces in action.</p>
<p>From there, the project started to take shape, and we made the discovery that we didn&rsquo;t need LDAP/AD. It would be much faster and reduce a lot of upkeep cost to store a hash of the login information on DynamoDB and use that to verify accounts.</p>
<p>DynamoDB is a NoSQL solution that allows multi-region, multi-master tables, which we could leverage to make this a much more resilient and smarter application. Users could theoretically connect to the geographically closest SFTP Server and no matter which region their account/S3 bucket lives in have seemingly much faster connection to the service.</p>
<p>Since we had no experience with the API Gateway, all we could do is use the example one and recreate it manually, in order to understand all the pieces of the service. With the help of advanced logging to CloudWatch, we were set up to create our own API Gateway and pull data via the linked Lambda function.</p>
<p>We then built a new Lambda function in python to be able to query DynamoDB and return to the SFTP service the user&rsquo;s bucket and home directory, along with other data required for the service.</p>
<p>Hooking a new Transfer server to the self-built API Gateway and Lambda function, then working through all the small misunderstandings between the documentation and our own implementation, we were eventually able to get a successful login and upload of data. From there we have continued to optimize the process by modifying the sample CloudFormation template to automatically build our custom Lambda Function, API gateway, S3 buckets, and IAM Roles in each region. At this time, CloudFormation has no hooks to the transfer service so we are unable to automate it using this method.</p>
<p>With the data being available in encrypted S3 buckets, Development can begin to shift how we do imports. Instead of shuffling data around from a manager process to an agent server, the agent service could pull directly from S3 and save a lot of processing and I/O. Furthermore, we think we can hook in additional AWS tools like <a href="https://github.com/aws-samples/serverless-data-analytics">RedShift</a> and <a href="https://github.com/aws-samples/aws-glue-samples">AWS Glue</a> to make powerful <a href="https://github.com/aws-samples/aws-glue-samples/blob/master/examples/data_cleaning_and_lambda.md">Extract</a>, <a href="https://github.com/aws-samples/aws-glue-samples/blob/master/examples/join_and_relationalize.md">Transform</a>, Load Jobs/techniques and possibly make this whole import workflow completely serverless (!) and much closer to error-free. There&rsquo;s a lot of work to get there, but the future is not that hard to imagine if we can make sure this new method works with datalink integration and other technical debt items that may inhibit a leap forward like this solution.</p>
<p><img src="/img/sftp-proj/alert.png" alt="Alert"> With other teams developing ways for client organizations to have a single account for all products, I believe using DynamoDB as the backend &lsquo;Data-lake&rsquo; solution for all this account information would be the perfect use case. The way it can scale in both data housing, replication to multiple regions, and handle high volumes of transactional activity makes it a great candidate and one we would be remiss from not considering.</p>
<h4 id="new-concept">New Concept</h4>
<p><img src="/img/sftp-proj/new-concept.png" alt="New Concept"></p>
<ul>
<li>Every Link between services is done over encrypted traffic be that sftp or https, all data stored is encrypted at rest</li>
</ul>
<h4 id="old-concept">Old Concept</h4>
<p><img src="/img/sftp-proj/old-concept.png" alt="Old Concept">
 </p>
<ul>
<li>Main is a Single Point of Failure in the current design. </li>
<li>Allowing direct SSH Access to one of our most important machines is in our opinion a security and reliability threat to this product.</li>
</ul>
<p>Images Created With <a href="https://www.draw.io/">https://www.draw.io/</a></p>
<h3 id="cost">Cost</h3>
<h4 id="estimated-cost-per-month-for-all-these-new-services">Estimated Cost per month for all these new services</h4>
<table>
  <tr>
   <td><strong>Service</strong>
   </td>
   <td><strong>number</strong>
   </td>
   <td><strong>price</strong>
   </td>
   <td><strong>hour/amount</strong>
   </td>
   <td><strong>Total</strong>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>SFTP
   </td>
   <td>3
   </td>
   <td>0.3
   </td>
   <td>720 Hrs
   </td>
   <td>648
   </td>
   <td>Seems steep compared directly to 1 server, but in reality, you are getting multiple servers in different AZ's
   </td>
  </tr>
  <tr>
   <td>SFTP Transfer
   </td>
   <td>3
   </td>
   <td>0.04
   </td>
   <td>200 GB
   </td>
   <td>24
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>S3 + DynamoDB
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>3 TB
   </td>
   <td>80
   </td>
   <td><a href="https://calculator.s3.amazonaws.com/index.html?key=cloudformation/1e9c79a2-562c-4d46-9977-b6667a3f7455">https://calculator.s3.amazonaws.com/index.html?key=cloudformation/1e9c79a2-562c-4d46-9977-b6667a3f7455</a>
   </td>
  </tr>
  <tr>
   <td>API Gateway
   </td>
   <td>first 333 M
   </td>
   <td>3.5
   </td>
   <td>
   </td>
   <td>3.5
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>Lambda
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>10
   </td>
   <td><a href="https://aws.amazon.com/lambda/pricing/">https://aws.amazon.com/lambda/pricing/</a>
   </td>
  </tr>
  <tr>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>Total
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>765.5
   </td>
   <td>
   </td>
  </tr>
</table>
<p>I believe with the new method what we would save in administrative and work time (although I do not have a way to quantify) and the future opportunities this solution provides, far outweigh the slight uptick in cost for this service compared with our traditional method.</p>
<h4 id="estimated-cost-per-month-for-traditional-way">Estimated Cost per month for traditional way</h4>
<p>If all we did was set up a second main server in a failover auto-scaling group within each cluster to take over should the primary fail, here is the cost structure of that.</p>
<table>
  <tr>
   <td><strong>Server</strong>
   </td>
   <td><strong>Count</strong>
   </td>
   <td><strong>Cost Per</strong>
   </td>
   <td><strong>hour / size</strong>
   </td>
   <td><strong>Total $</strong>
   </td>
   <td><strong>RI</strong>
   </td>
   <td><strong>Total $</strong>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>main (m4.Large)
   </td>
   <td>12
   </td>
   <td>0.10
   </td>
   <td>720 Hrs
   </td>
   <td>864
   </td>
   <td>~40%
   </td>
   <td>345.6
   </td>
   <td>The hope is that without the need to support sftp traffic and zip files to the import servers, we could make the main server smaller and save money
   </td>
  </tr>
  <tr>
   <td>EBS Volumes
   </td>
   <td>6
   </td>
   <td>0.1
   </td>
   <td>500 GB
   </td>
   <td>300
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>The hope is that we could do away with these large provisioned volumes that we barely use. The volume for 02-1 main uses 150GB and that is the largest one and yet all are this large by default
   </td>
  </tr>
  <tr>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>Total
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>1164
   </td>
   <td>
   </td>
   <td>818.4
   </td>
   <td>
   </td>
  </tr>
</table>
<p>As we add more clusters this cost will continue to expand whereas the cost of the new method would stay fairly static and grow only with consumption of resources while adding new volume to the existing traffic </p>
<h3 id="to-do">To do</h3>
<ul>
<li>Have Dev and QA review this solution and understand these changes, give feedback to DevOps for any changes</li>
<li>Test this solution under &ldquo;production&rdquo; levels of use</li>
<li>Integrate with Provisioning App</li>
<li>If approved for production, the creation of a migration plan/user outreach/education on why these improvements are necessary and better</li>
</ul>
<h3 id="important-findings-with-thenew-solution">Important findings with the new solution</h3>
<p>This section is for the lessons, caveats, and findings that were made during testing of this new method that may impact our ability to deploy or things we need to consider as we update datalink or inform our clients.</p>
<ul>
<li>The new solution only supports the SFTP protocol. It does not support scp, ssh or anything else other than sftp.</li>
<li>I think this is especially important for Datalink client, as I know it supports both sftp and scp but not sure what the default is</li>
</ul>
<p><strong>SCP blocked</strong></p>
<pre tabindex="0"><code>$ scp attendance.txt my1sftp@99-uploads.xx.net:
my1sftp@99-uploads.xx.net&#39;s password:
Could not chdir to home directory /dev/null: Not a directory
/bin/false: No such file or directory
lost connection
</code></pre><ul>
<li>S3 filesystem does not appear to understand the SETSTAT command, which clients like WinSCP use to preserve timestamps</li>
<li>If you do not make these changes a warning pops up and looks kind of scary, selecting &lsquo;Skip all&rsquo; on error will allow the file to upload as normal \</li>
</ul>
<p><img src="/img/sftp-proj/scp-blocked.png" alt="SCP Blocked"></p>
<ul>
<li>To disable in WinSCP, Edit Site</li>
<li>Advanced drop-down arrow, select Transfer Setting Rule</li>
<li>Unselect &ldquo;Preserve Timestamps&rdquo;</li>
<li>Preset Description - no stat</li>
<li>Save</li>
<li>Data stored on S3 are eventually consistent due to data replication</li>
<li><a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html">https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html</a> - Amazon S3 Data Consistency Model</li>
</ul>
<h3 id="service-limitations">Service Limitations</h3>
<table>
  <tr>
   <td><strong>Description</strong>
   </td>
   <td><strong>Amount</strong>
   </td>
   <td><strong>Can be Increased</strong>
   </td>
   <td><strong>Source</strong>
   </td>
  </tr>
  <tr>
   <td colspan="4" ><strong>API Gateway</strong>
   </td>
  </tr>
  <tr>
   <td>Throttle limit per region across REST APIs, WebSocket APIs, and WebSocket callback APIs
   </td>
   <td>10,000/s
   </td>
   <td>Yes
   </td>
   <td><a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html">https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html</a>
   </td>
  </tr>
  <tr>
   <td>Regional APIs per account
   </td>
   <td>600
   </td>
   <td>No
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>Edge-Optimized APIs per account
   </td>
   <td>120
   </td>
   <td>No
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td colspan="4" ><strong>AWS Lambda</strong>
   </td>
  </tr>
  <tr>
   <td>Concurrent executions
   </td>
   <td>1000
   </td>
   <td>
   </td>
   <td><a href="https://docs.aws.amazon.com/lambda/latest/dg/limits.html">https://docs.aws.amazon.com/lambda/latest/dg/limits.html</a>
   </td>
  </tr>
  <tr>
   <td>Function memory allocation
   </td>
   <td>128 MB to 3008 MB, in 64 MB increments.
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>Function timeout
   </td>
   <td>900 Seconds
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td colspan="4" ><strong>S3</strong>
   </td>
  </tr>
  <tr>
   <td>Maximum object size - Single-part upload
   </td>
   <td>5GB
   </td>
   <td>
   </td>
   <td><a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/qfacts.html">https://docs.aws.amazon.com/AmazonS3/latest/dev/qfacts.html</a>
   </td>
  </tr>
  <tr>
   <td>Maximum object size - Multi-part upload
   </td>
   <td>5TB
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>PUT/POST/DELETE
   </td>
   <td>3500/s
   </td>
   <td>
   </td>
   <td><a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html">https://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html</a>
   </td>
  </tr>
  <tr>
   <td>GET
   </td>
   <td>5500/s
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>There are no limits to the number of prefixes in a bucket. It is simple to increase your read or write performance exponentially. For example, if you create 10 prefixes in an Amazon S3 bucket to parallelize reads, you could scale your read performance to 55,000 read requests per second.
   </td>
   <td>55,000/s
   </td>
   <td>
   </td>
   <td>key prefix
<p>
A logical grouping of the objects in a <a href="https://docs.aws.amazon.com/general/latest/gr/glos-chap.html#bucket">bucket</a>. The prefix value is similar to a directory name that enables you to store similar data under the same directory in a bucket.
   </td>
  </tr>
</table>
<h2 id="demonstration">Demonstration</h2>
<p>For testing this solution, we created a new org, called &lsquo;Test SFTP 1&rsquo;</p>
<p>We then have a script to copy the pertinent account data from the MySQL Database to DyanmoDB.</p>
<p>We have another script to run on main to &ldquo;migrate&rdquo; the /home/##### Directory to be on the mounted S3 filesystem and a sym-link to make the change invisible to the application.</p>
<h4 id="showcase-of-functionality">Showcase of Functionality</h4>
<p><strong>sftp upload</strong></p>
<pre tabindex="0"><code>$ sftp my1sftp@99-uploads.xx.net
my1sftp@99-uploads.xx.net&#39;s password:
Connected to 99-uploads.xx.net.
sftp&gt;

# Demo of Upload/PUT Speed
sftp&gt; put attendance.txt
Uploading attendance.txt to /99-5-uploads/175781/attendance.txt
attendance.txt                                                                                                  100% 5660KB   5.2MB/s   00:01
sftp&gt; put organization\ full.txt
Uploading organization full.txt to /99-5-uploads/175781/organization full.txt
organization full.txt                                                                                           100%   99KB   1.4MB/s   00:00
sftp&gt; put data_test.txt
Uploading student full.txt to /99-5-uploads/175781/data_test.txt
data_test.txt                                                                                                100%   28MB   7.7MB/s   00:03

# Demo of Download/GET Speed
sftp&gt; get student\ full.txt
Fetching /99-5-uploads/175781/data_test.txt to data_test.txt
/99-5-uploads/175781/data_test.txt                                                                      100%   28MB   5.6MB/s   00:05

# Demonstration of security enhancements - no access to other users&#39; files on the same S3 bucket
sftp&gt; ls
attendance.txt          get-pip.py              organization full.txt   99-5-import.csv       data_test.txt
sftp&gt; ls ../
Couldn&#39;t read directory: Permission denied
sftp&gt; exit
Connection reset by 18.224.78.82 port 22
</code></pre><h4 id="results">Results</h4>
<p>I was then able to go to the website for this org and configure an import using the files uploaded to S3</p>
<p>I was also able to run the import and it appears to have worked without error</p>
<p><strong>Import Success</strong></p>
<pre tabindex="0"><code>Test Sftp 1 Import Log
STUDENTS - (IMPORT 3822 AT TEST SFTP 1)
STARTED:         2019-02-08 10:18:33 am EST
COMPLETED:  2019-02-08 10:18:43 am EST
RESULTS:
      Import handed to i3.localdomain IMPORTAGENT
      2019-02-08 10:18:36 am EST Data import started on host i3
      2019-02-08 10:18:36 am EST Data import using database importDB_2000002664
      2019-02-08 10:18:36 am EST Extracting data files.
      Import Files:
      /home/175781/data_test.txt (2019-02-08 15:13:55.00)
      Creating Database for import of org 2000002664
      2019-02-08 10:18:43 am EST Data import completed.
</code></pre><p>The Initial functionality test shows that the world continues to spin and to the application the change is invisible. woohoo!</p>
<p><strong>¡MAJOR WIN</strong>!</p>
<h2 id="technical-breakdown-of-technologies">Technical Breakdown of Technologies</h2>
<p>I am going to structure each piece of this tech stack from backend to the front, so we can understand and further develop these technologies</p>
<p>The &lsquo;99&rsquo; designation in names indicates that this is Dev/Testing</p>
<h3 id="cloudformation">CloudFormation</h3>
<pre><code>AWS CloudFormation is a service that helps you model and set up your Amazon Web Services resources so that you can spend less time managing those resources and more time focusing on your applications that run in AWS. You create a template that describes all the AWS resources that you want (like Amazon EC2 instances or Amazon RDS DB instances), and AWS CloudFormation takes care of provisioning and configuring those resources for you.
</code></pre>
<p>Docs: <a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html">https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html</a></p>
<p>Amazon provided a sample CFN template that helped us tremendously with getting the API and Lambda portions setup. Of course, we have adapted thier CFN template to work with our own needs, but without that showing us the way this project would have taken much longer to put together.</p>
<p>Original CFN Template: <a href="https://da02pmi3nq7t1.cloudfront.net/aws-transfer-apig-lambda.cfn.yml">https://da02pmi3nq7t1.cloudfront.net/aws-transfer-apig-lambda.cfn.yml</a> </p>
<p>One of our Templates: [REDACTED]</p>
<p>The templates that we have written setup S3 buckets, IAM Roles, Lambda Function, and API Gateway for each region. </p>
<p>There are no CFN hooks into AWS Transfer/SFTP service at this time, so we cannot automate this the same way. We also leave DynamoDB out of CFN since the service has to be there before the rest of the stack</p>
<p>After deploying with CFN for the first time, you must do the following</p>
<ol>
<li>Get Prod API URL for API Gateway</li>
<li>Setup Transfer Server with URL from step #1</li>
<li>Grab the Server ID, ex.:  s-36d2dbb2bb8941f59</li>
<li>Go to lambda and on line 33 set the new serverId</li>
<li>Still, in Lambda, Add new Trigger with API Gateway to Prod API Gateway. <strong>DO NOT REMOVE</strong> the one already there</li>
<li>At this point, all should be configured and testing can begin</li>
</ol>
<h3 id="dynamodb">DynamoDB</h3>
<pre><code>_Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale. It's a fully managed, multiregion, multimaster database with built-in security, backup and restore, and in-memory caching for internet-scale applications. DynamoDB can handle more than 10 trillion requests per day and support peaks of more than 20 million requests per second._
</code></pre>
<table>
  <tr>
   <td><strong>Table</strong>
   </td>
   <td><strong>Environment</strong>
   </td>
   <td><strong>Info</strong>
   </td>
   <td><strong>Primary Key</strong>
   </td>
   <td><strong>Secondary Index</strong>
   </td>
   <td><strong>Backups</strong>
   </td>
   <td><strong>Encryption</strong>
   </td>
  </tr>
  <tr>
   <td>dydb-99-uploads
   </td>
   <td>Test
   </td>
   <td>Account data for all 99 clusters
   </td>
   <td>orgid
   </td>
   <td>username
   </td>
   <td>PIT us-west-2 only
   </td>
   <td>Default AES-256
   </td>
  </tr>
  <tr>
   <td>dydb-uploads
   </td>
   <td>Production
   </td>
   <td>Account data for all production 01,02,03 clusters
   </td>
   <td>orgid
   </td>
   <td>username
   </td>
   <td>PIT us-west-2 only
   </td>
   <td>Default AES-256
   </td>
  </tr>
</table>
<h4 id="set-up-a-table">Set up a Table</h4>
<ol>
<li>Create Table</li>
<li>Set Table Name - dydb-99-uploads</li>
<li>Primary key - orgid - Number</li>
<li>Uncheck Default Settings</li>
<li>Add Secondary Index
<ol>
<li>username - String value</li>
</ol>
</li>
<li>Provisioning: On Demand</li>
<li>Encryption: Default</li>
<li>Select Create</li>
</ol>
<h4 id="set-up-multi-region-replication">Set up Multi-Region, Replication</h4>
<p>There can be no data in the tables to setup Streaming Replication, so you need to do this at the beginning right after setup, before you add any items to the table.</p>
<ol>
<li>Select Global Tables Tab</li>
<li>Add region button</li>
<li>Select Region from menu, Continue</li>
<li>Repeat if necessary, for another region</li>
</ol>
<p>The items that do not get replicated are settings related to backups or Tags, we have Point-in-Time Recovery backups of tables enabled in 1 region only, and you have to region hop to apply the same Tags to each table.</p>
<p>We&rsquo;ve set our tables to replicate with 3 regions: us-west-2, us-east-1, us-east-2</p>
<h3 id="aws-lambda">AWS Lambda</h3>
<pre><code>AWS Lambda is a compute service that lets you run code without provisioning or managing servers. AWS Lambda executes your code only when needed and scales automatically, from a few requests per day to thousands per second.
</code></pre>
<p>Docs: <a href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html">https://docs.aws.amazon.com/lambda/latest/dg/welcome.html</a> </p>
<p>In each region that we have a table configured, we would like to have a lambda function that has Read Only access to the DynamoDB table in the same region.</p>
<table>
  <tr>
   <td><strong>Function</strong>
   </td>
   <td><strong>Region</strong>
   </td>
   <td><strong>Runtime</strong>
   </td>
   <td><strong>Role</strong>
   </td>
   <td><strong>API Gateway Prod URL</strong>
   </td>
   <td><strong>Environment</strong>
   </td>
  </tr>
  <tr>
   <td>lambda-99-upload-us-west-2
   </td>
   <td>us-west-2
   </td>
   <td>python3.7
   </td>
   <td>99-upload-us-west-2-LambdaExecutionRole-1FIE08KVUA8WD
   </td>
   <td>[redacted]
   </td>
   <td>Test
   </td>
  </tr>
  <tr>
   <td>lambda-99-upload-us-east-2
   </td>
   <td>us-east-2
   </td>
   <td>python3.7
   </td>
   <td>99-upload-us-east-2-LambdaExecutionRole-1N1T7LDGGZJPY
   </td>
   <td>[redacted]
   </td>
   <td>Test
   </td>
  </tr>
</table>
<h4 id="set-up-a-function">Set up a Function</h4>
<ul>
<li>Function Name: lambda-99-upload-us-west-2</li>
<li>Runtime: Python 3.7</li>
<li>Role: 99-upload-us-west-2-LambdaExecutionRole-XXXXXXX </li>
<li>lambda_function: link to git code</li>
<li>Memory: Minimum 128 MB</li>
<li>Timeout: 3 Sec</li>
</ul>
<p><strong>lambda_function.py</strong></p>
<pre tabindex="0"><code>#!/usr/bin/env python
&#34;&#34;&#34;
Author: Tom Stewart 
About: Lambda function to handle SFTP Login for API Gateway/SFTp Transfer solution
       Hooks into DynamoDB where we store SSH224 of user creds


Exec: - AWS Lambda

Notes: -
	python3.7
&#34;&#34;&#34;
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
from hashlib import sha224
from base64 import b64encode

def _dyn_lookup_user(event):
    dynclient = boto3.resource(&#39;dynamodb&#39;)
    # dydb-uploads = prod, dydb-99-uploads = test
    dyntable = dynclient.Table(&#39;dydb-99-uploads&#39;)
    dynresponse = dyntable.query(
    IndexName=&#39;username-index&#39;,
         KeyConditionExpression=Key(&#39;username&#39;).eq(event[&#39;username&#39;])
         )
    for d in dynresponse[&#39;Items&#39;]:
        return d

def _main(event):
    # Replace serverID with newly created one after Transfer setup
    if event[&#39;serverId&#39;] != &#39;s-36d2dbb2bb8941f59&#39;:
        return {&#39;statusCode&#39;:401,
            &#39;body&#39;: json.dumps({&#39;statusMessage&#39;: &#39;Unauthorixed sftpserver&#39;})
            }


    # once the test was online, we got thousands of bots trying to login with default creds, so easier to stop it asap then let it go further with processing
    bad_name_list = [&#39;admin&#39;, &#39;support&#39;, &#39;root&#39;, &#39;ubuntu&#39;, &#39;tomcat&#39;, &#39;mysql&#39;, 
                    &#39;solr&#39;,					&#39;postgres&#39;, &#39;test&#39;, &#39;user&#39;, &#39;user1&#39;, &#39;centos&#39;, 
                    &#39;ec2_user&#39;]
    if event[&#39;username&#39;] in bad_name_list:
       return {&#39;statusCode&#39;:401,
               &#39;body&#39;: json.dumps({&#39;statusMessage&#39;: &#39;Unauthorized user&#39;})
              }


    authdyn = _dyn_lookup_user(event)
    ctx = sha224(event[&#39;password&#39;].encode(&#39;utf-8&#39;))
    phash = b64encode(ctx.digest())

    if authdyn is None:
        return {&#39;statusCode&#39;:404,
                &#39;body&#39;: json.dumps({&#39;statusMessage&#39;: &#39;Username Not Found&#39;})
               }


    if event[&#39;username&#39;] == authdyn[&#39;username&#39;] AND
       phash ==  authdyn[&#39;password&#39;].encode(&#39;utf-8&#39;):
       return {
          	&#39;statusCode&#39;: 200,
          	&#39;body&#39;: json.dumps({&#39;statusMessage&#39;: &#39;Success&#39;}),
          &#39;Role&#39;: &#39;arn:aws:iam::024643489849:role/sftp-transfer&#39;,
          &#39;Policy&#39;: &#39;{&#34;Version&#34;:&#34;2012-10-17&#34;,&#34;Statement&#34;:[{&#34;Sid&#34;:&#34;VisualEditor0&#34;,&#34;Effect&#34;:&#34;Allow&#34;,&#34;Action&#34;:&#34;s3:ListBucket&#34;,&#34;Resource&#34;:&#34;arn:aws:s3:::${transfer:HomeBucket}&#34;,&#34;Condition&#34;:{&#34;StringLike&#34;:{&#34;s3:prefix&#34;:[&#34;${transfer:HomeFolder}/*&#34;,&#34;${transfer:HomeFolder}&#34;]}}},{&#34;Sid&#34;:&#34;VisualEditor1&#34;,&#34;Effect&#34;:&#34;Allow&#34;,&#34;Action&#34;:[&#34;s3:ListAllMyBuckets&#34;,&#34;s3:GetBucketLocation&#34;],&#34;Resource&#34;:&#34;*&#34;},{&#34;Sid&#34;:&#34;VisualEditor2&#34;,&#34;Effect&#34;:&#34;Allow&#34;,&#34;Action&#34;:[&#34;s3:PutObject&#34;,&#34;s3:GetObject&#34;,&#34;s3:DeleteObjectVersion&#34;,&#34;s3:DeleteObject&#34;,&#34;s3:GetObjectVersion&#34;],&#34;Resource&#34;:&#34;arn:aws:s3:::${transfer:HomeDirectory}*&#34;}]}&#39;,
         &#39;HomeDirectory&#39;: authdyn[&#39;HomeDirectory&#39;],
          }
    else:
         return {&#39;statusCode&#39;:403,
                 &#39;body&#39;: json.dumps({&#39;statusMessage&#39;: &#39;Incorrect Password&#39;})
		}

def lambda_handler(event, context):
    status = _main(event)
    return status
</code></pre><p>The execution role needs to have IAM access to DynamoDB tables and is executed by API Gateway.</p>
<p>Cloud formation configures all of this and downloads the python code from S3 buckets in the same region. S3 bucket: lambda-pl-upload-${region} Object: pl-upload.zip (prod) pl-upload-99.zip (Test)</p>
<p>Test data for lambda function:</p>
<p><strong>test</strong></p>
<pre tabindex="0"><code>{
  &#34;serverId&#34;: &#34;s-36d2dbb2bb8941f59&#34;,
  &#34;username&#34;: &#34;test99&#34;,
  &#34;password&#34;: &#34;fawrawt45245&#34;
}
</code></pre><h3 id="api-gateway">API Gateway</h3>
<pre><code>_Amazon API Gateway is an AWS service that enables you to create, publish, maintain, monitor, and secure your own [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) and [WebSocket](https://tools.ietf.org/html/rfc6455) APIs at any scale._
</code></pre>
<p>Docs: <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html">https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html</a> </p>
<p>Honestly, the only way I understand this part is to create the sample API Gateway provided by the sample CFN template and recreate it manually to find all the settings and items to configure. That being said I will try my best to outline all the things to setup.</p>
<table>
  <tr>
   <td><strong>API Gateway</strong>
   </td>
   <td><strong>Region</strong>
   </td>
   <td><strong>Lambda Function</strong>
   </td>
   <td><strong>Prof URL</strong>
   </td>
   <td><strong>Environment</strong>
   </td>
  </tr>
  <tr>
   <td>api-99-upload-us-west-2
   </td>
   <td>us-west-2
   </td>
   <td>lambda-99-upload-us-west-2
   </td>
   <td>[redacted]
   </td>
   <td>Test
   </td>
  </tr>
  <tr>
   <td>api-99-upload-us-east-2
   </td>
   <td>us-east-2
   </td>
   <td>lambda-99-upload-us-east-2
   </td>
   <td>[redacted]
   </td>
   <td>Test
   </td>
  </tr>
</table>
<h4 id="set-up-an-api">Set up an API</h4>
<ol>
<li>
<ul>
<li>Create API</li>
</ul>
</li>
<li>
<p>Protocol REST</p>
</li>
<li>
<p>Name: api-99-upload-us-west-2</p>
</li>
<li>
<p>Endpoint Type: RegionalResources</p>
</li>
<li>
<ol>
<li>Select /, Actions Create Resource- Name new resource /servers</li>
<li>Select /servers, Actions Create Resource- Name new resource /{serverId}</li>
<li>Select /servers/{serverId}, Actions Create Resource- Name new resource /users</li>
<li>Select /servers/{serverId}/users, Actions Create Resource- Name new resource /{username}</li>
<li>Select /servers/{serverId}/users/{username}, Actions Create Resource- Name new resource /{config</li>
<li>Select /servers/{serverId}/users/{username}/{config, Actions Create <strong>Method</strong>- Name GET</li>
</ol>
</li>
<li>
<p>GET Method</p>
<ol>
<li>Authorization AWS_IAM</li>
<li>Request Paths: serverId and username</li>
<li>HTTP Reguest Headers: Password</li>
<li>All other settings leave as defaults</li>
</ol>
</li>
<li>
<p>Integration Request</p>
<ol>
<li>Integration Type: Lambda Function
<ol>
<li>Unselect Use Lambda Proxy Integration <img src="/img/sftp-proj/alert.png" alt="Alert"> - we will make our own custom mapping in a later step</li>
<li>Lambda Function: lambda-99-upload-us-west-2</li>
</ol>
</li>
<li>Mapping Template
<ol>
<li>
<p>Select &ldquo;When no template matchets the reguest Content-Type header</p>
</li>
<li>
<ul>
<li>mapping template</li>
</ul>
</li>
<li>
<p>application/json</p>
</li>
<li>
<p>Insert this object</p>
<p><strong>Mapping Template</strong></p>
<pre tabindex="0"><code>{
  &#34;username&#34;: &#34;$input.params(&#39;username&#39;)&#34;,
  &#34;password&#34;: &#34;$input.params(&#39;Password&#39;)&#34;,
  &#34;serverId&#34;: &#34;$input.params(&#39;serverId&#39;)&#34;
}
</code></pre></li>
<li>
<p>Save</p>
</li>
</ol>
</li>
</ol>
</li>
<li>
<p>Integration Response</p>
<ol>
<li>No changes to be made here</li>
</ol>
</li>
<li>
<p>Method Response</p>
<ol>
<li>
<p>Before we can do this step we have to create Models for the response data</p>
</li>
<li>
<p>On left-hand bar find Models</p>
</li>
<li>
<p>Create</p>
</li>
<li>
<p>Model Name: 200ResponseModel</p>
<p><strong>200ResponseModel</strong></p>
<pre tabindex="0"><code>{&#34;$schema&#34;:&#34;http://json-schema.org/draft-04/schema#&#34;,&#34;title&#34;:&#34;200Response&#34;,&#34;type&#34;:&#34;object&#34;,&#34;properties&#34;:{&#34;Policy&#34;:{&#34;type&#34;:&#34;string&#34;},&#34;Role&#34;:{&#34;type&#34;:&#34;string&#34;},&#34;HomeDirectory&#34;:{&#34;type&#34;:&#34;string&#34;}}}
</code></pre></li>
<li>
<p>Repeat to Create a 4XX Response model:</p>
<p><strong>4XXResponseModel</strong></p>
<pre tabindex="0"><code>{&#34;$schema&#34;:&#34;http://json-schema.org/draft-04/schema#&#34;,&#34;title&#34;:&#34;4xxResponse&#34;,&#34;type&#34;:&#34;object&#34;,&#34;properties&#34;:{&#34;statusMessage&#34;:{&#34;type&#34;:&#34;string&#34;}}}
</code></pre></li>
</ol>
</li>
<li>
<p>Return to Resources - GET - Method Response</p>
<ol>
<li>Add Response</li>
<li>HTTP Status: 200</li>
<li>Response Body</li>
<li>Content type: Application/json</li>
<li>Models: 200ResponseModel</li>
<li>Repeat for 401, 403, 404 - Model should be 4XXRespnseModel</li>
</ol>
</li>
<li>
<p>Actions</p>
<ol>
<li>Deploy API</li>
<li>Deploy to Prod stage
<ol>
<li>Any changes made to the api will have to be Deployed to Prod</li>
</ol>
</li>
</ol>
</li>
<li>
<p>Use Test interface to test that the API + Lambda can communicate. </p>
</li>
<li>
<p>Go back to the Lambda Function and add a new trigger for API Gateway</p>
<ol>
<li>Link to api-99-upload-us-west-2</li>
<li>Prod Stage</li>
<li>Save Lambda Function</li>
</ol>
</li>
<li>
<p>Get Prod url for Transfer service</p>
<ol>
<li>Back on API Gateway, navigate to Stages</li>
<li>Select &lsquo;Prod&rsquo;</li>
<li>Invoke URL: right click copy URL: ← this is the custom identity provider URL that Transfer needs
<ol>
<li>Example: https://redacted.execute-api.us-west-2.amazonaws.com/prod</li>
</ol>
</li>
</ol>
</li>
</ol>
<h3 id="aws-transfer-for-sftp">AWS Transfer for SFTP</h3>
<pre><code>_AWS Transfer for SFTP (AWS SFTP) is a fully managed AWS service that enables you to transfer files over Secure File Transfer Protocol (SFTP), into and out of Amazon Simple Storage Service (Amazon S3) storage._
</code></pre>
<p>Docs: <a href="https://docs.aws.amazon.com/transfer/latest/userguide/what-is-aws-transfer-for-sftp.html">https://docs.aws.amazon.com/transfer/latest/userguide/what-is-aws-transfer-for-sftp.html</a></p>
<p>For Test, we have 2 Transfer Servers setup in us-west-2 and us-east-2 regions</p>
<table>
  <tr>
   <td><strong>R53 Domain</strong>
   </td>
   <td><strong>ServerId</strong>
   </td>
   <td><strong>Region</strong>
   </td>
   <td><strong>Custom Identity URL</strong>
   </td>
   <td><strong>Invocation Role</strong>
   </td>
   <td><strong>Environment</strong>
   </td>
  </tr>
  <tr>
   <td>99-uploads-us-west-2.xx.net
   </td>
   <td>s-36d2dbb2bb8941f59
   </td>
   <td>us-west-2
   </td>
   <td>[redacted]
   </td>
   <td>99-upload-us-west-2-TransferIdentityProviderRol-IQR1DD02N3LN
   </td>
   <td>Test
   </td>
  </tr>
  <tr>
   <td>99-uploads-us-east-2.xx.net
   </td>
   <td>s-2811a8a8ee9d497f8
   </td>
   <td>us-east-2
   </td>
   <td>[redacted]
   </td>
   <td>99-upload-us-east-2-TransferIdentityProviderRol-E8XY4G85I5CH
   </td>
   <td>Test
   </td>
  </tr>
</table>
<p>For Prod, we imagine having 1 in each region for a total of 3</p>
<h4 id="set-up-a-transfer-server">Set up a Transfer Server</h4>
<ol>
<li>Create Server</li>
<li>Custom hostname: 
<ol>
<li>None is fine, for Test we have Amazon Route53 DNS alias: 99-upload-us-west-2.pt-xx.net</li>
</ol>
</li>
<li>Identity Provider:
2. Custom
3. Paste the link from Prod URL of API Gateway</li>
<li>Invocation role: 
4. 99-upload-${region}-TransferidentityProviderRole-XXXXXXX 
5. This is created by CFN template and is unique for every Transfer Service</li>
<li>Logging Role; 
6. can be blank, but if you need cloudwatch logs, select sftp-log~~-~~role</li>
<li>Enter Tags</li>
<li>Create</li>
</ol>
<p>The Transfer server takes about 10-15 minutes to set up and become &ldquo;Online&rdquo;</p>
<p>You can immediately test with SFTP (so long as you have a set username/password in the DynamoDB table or one can use the very effective Test_transfer python script</p>
<p><strong>test_transfer.py</strong></p>
<pre tabindex="0"><code>#!/usr/bin/python
import boto3
client = boto3.client(&#39;transfer&#39;, &#39;us-west-2&#39;)
response = client.test_identity_provider(
    ServerId=&#39;s-36d2dbb2bb8941f59&#39;,
    UserName=&#39;test99&#39;,
    UserPassword=&#39;fawrawt45245&#39;
    )
print (response)
</code></pre><h3 id="route53--geodns">Route53 / GeoDNS</h3>
<pre><code>Amazon Route 53 is a highly available and scalable Domain Name System (DNS) web service.
</code></pre>
<p>Docs: <a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html">https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html</a></p>
<table>
  <tr>
   <td><strong>Name</strong>
   </td>
   <td><strong>Type</strong>
   </td>
   <td><strong>Alias Target</strong>
   </td>
   <td><strong>Routing Policy</strong>
   </td>
   <td><strong>Region</strong>
   </td>
  </tr>
  <tr>
   <td>99-uploads-us-west-2.xx.net
   </td>
   <td>CNAME
   </td>
   <td>s-36d2dbb2bb8941f59.server.transfer.us-west-2.amazonaws.com
   </td>
   <td>
   </td>
   <td>us-west-2
   </td>
  </tr>
  <tr>
   <td>99-uploads-us-east-2.xx.net
   </td>
   <td>CNAME
   </td>
   <td>s-2811a8a8ee9d497f8.server.transfer.us-east-2.amazonaws.com
   </td>
   <td>
   </td>
   <td>us-east-2
   </td>
  </tr>
  <tr>
   <td>99-uploads.xx.net.
   </td>
   <td>CNAME / ALIAS
   </td>
   <td>99-uploads-us-west-2.xx.net.
   </td>
   <td>Latency
   </td>
   <td>us-west-2
   </td>
  </tr>
  <tr>
   <td>99-uploads.xx.net.
   </td>
   <td>CNAME / ALIAS
   </td>
   <td>99-uploads-us-east-2.xx.net.
   </td>
   <td>Latency
   </td>
   <td>us-east-2
   </td>
  </tr>
</table>
<h4 id="set-up-a-geodns-record">Set Up a GeoDNS Record</h4>
<p>Steps to set up a GeoDNS record for one address so that users are provided with the fastest response time based on their location</p>
<ol>
<li>Hosted zones, xx.net</li>
<li>Create Record Set</li>
<li>Name: 99-uploads.xx.net</li>
<li>Type: CNAME</li>
<li>Alias Yes</li>
<li>Alias target: Select 99-uploads-us-west-2.xx.net.</li>
<li>Routing Policy: Latency</li>
<li>Region: us-west-2</li>
<li>Save Record Set</li>
<li>Repeat steps with same Name &amp; Settings, but different Alias Target/Region</li>
</ol>
<p>After discussing GeoDNS with the team at large we determined not to go forward with using this feature and instead just having regional endpoints, this is because we worried about the cost of intra-regional data transfers, something I hadn’t thought of when developing the solution. This is a strong case for over-engineering a project beyond the scope of what’s necessary.</p>
<h3 id="iam-policies-and-roles">IAM Policies and Roles</h3>
<p>CloudFormation generates the Roles and Policies that are generally needed for each service. However, it does not add permissions to the existing sftp-transfer Role for the S3 buckets that is attached via the Lambda function if there is a successful authentication attempt</p>
<p>sftp-transfer: arn:aws:iam::111111111111:role/sftp-transfer</p>
<p>When production is created, we will have to attach the policy arn:aws:iam::024643489849:policy/pl-pt-uploads-sftp-transfer to sftp-transfer in order to allow uploads and downloads from/to the S3 buckets</p>
<table>
  <tr>
   <td><strong>Role</strong>
   </td>
   <td><strong>Policy</strong>
   </td>
   <td><strong>Attached</strong>
   </td>
   <td><strong>Buckets</strong>
   </td>
  </tr>
  <tr>
   <td>arn:aws:iam::111111111111:role/sftp-transfer
   </td>
   <td>arn:aws:iam::111111111111:policy/99-uploads-sftp-transfer
   </td>
   <td>Yes
   </td>
   <td>99-5-uploads, 99-4-uploads, 99-3-uploads, 99-2-uploads, 99-1-uploads, 99-0-uploads
   </td>
  </tr>
  <tr>
   <td>
   </td>
   <td>arn:aws:iam::111111111111:policy/uploads-sftp-transfer
   </td>
   <td>No
   </td>
   <td>01-1-uploads, 01-2-uploads, 01-3-uploads, 02-1-uploads, 02-2-uploads, 03-1-uploads, 03-2-uploads
   </td>
  </tr>
</table>
<p>The Lambda Function passes the following Role and Policy. The Scope-down policy enables a chroot like action so that the user can only view files in their directory.</p>
<p><strong>Role and Policy</strong></p>
<pre tabindex="0"><code>&#39;Role&#39;: &#39;arn:aws:iam::111111111111:role/sftp-transfer&#39;,
&#39;Policy&#39;: &#39;{&#34;Version&#34;:&#34;2012-10-17&#34;,&#34;Statement&#34;:[{&#34;Sid&#34;:&#34;VisualEditor0&#34;,&#34;Effect&#34;:&#34;Allow&#34;,&#34;Action&#34;:&#34;s3:ListBucket&#34;,&#34;Resource&#34;:&#34;arn:aws:s3:::${transfer:HomeBucket}&#34;,&#34;Condition&#34;:{&#34;StringLike&#34;:{&#34;s3:prefix&#34;:[&#34;${transfer:HomeFolder}/*&#34;,&#34;${transfer:HomeFolder}&#34;]}}},{&#34;Sid&#34;:&#34;VisualEditor1&#34;,&#34;Effect&#34;:&#34;Allow&#34;,&#34;Action&#34;:[&#34;s3:ListAllMyBuckets&#34;,&#34;s3:GetBucketLocation&#34;],&#34;Resource&#34;:&#34;*&#34;},{&#34;Sid&#34;:&#34;VisualEditor2&#34;,&#34;Effect&#34;:&#34;Allow&#34;,&#34;Action&#34;:[&#34;s3:PutObject&#34;,&#34;s3:GetObject&#34;,&#34;s3:DeleteObjectVersion&#34;,&#34;s3:DeleteObject&#34;,&#34;s3:GetObjectVersion&#34;],&#34;Resource&#34;:&#34;arn:aws:s3:::${transfer:HomeDirectory}*&#34;}]}&#39;,
</code></pre><p>The special Policy cannot be attached with a traditional ARN and thus requires posting the entire JSON object back to the user at login time. <a href="https://docs.aws.amazon.com/transfer/latest/userguide/users-policies-scope-down.html">https://docs.aws.amazon.com/transfer/latest/userguide/users-policies-scope-down.html</a> </p>
<ul>
<li>A Funny aside, as we were starting to evaluate this service, the original Scope Down policy did not have the ${transfer:HomeFolder} variable and thus required the userName to match the Directory in the bucket. The result was that we had a lot of trouble getting the chroot like scope-down policy to apply correctly. We had a mysterious case where with one S3 bucket the chroot worked, but not with a different bucket. Never could figure out the difference. Lucky for us they updated the ability almost overnight while I was experimenting with this.</li>
</ul>
<h3 id="s3-buckets">S3 Buckets</h3>
<p>CloudFormation handles all of the bucket creation for the &ldquo;stacks&rdquo;. Each bucket is created in the same region as the main server it should be configured to handle data for.</p>
<table>
  <tr>
   <td><strong>Bucket</strong>
   </td>
   <td><strong>Environment</strong>
   </td>
   <td><strong>Region</strong>
   </td>
   <td><strong>Encryption</strong>
   </td>
   <td><strong>Versioning</strong>
   </td>
   <td><strong>Bucket Policy</strong>
   </td>
  </tr>
  <tr>
   <td>99-5-uploads
   </td>
   <td>Test
   </td>
   <td>us-west-2
   </td>
   <td>Enabled
   </td>
   <td>Enabled
   </td>
   <td>{"Version":"2012-10-17","Id":"Policy1548340984399","Statement":[{"Sid":"Stmt1548340975673","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:role/sftp-transfer"},"Action":["s3:DeleteObject","s3:DeleteObjectVersion","s3:GetObject","s3:GetObjectVersion","s3:PutObject"],"Resource":"arn:aws:s3:::pl-pt99-5-uploads/*"}]}
   </td>
  </tr>
  <tr>
   <td>99-4-uploads
   </td>
   <td>Test
   </td>
   <td>us-west-2
   </td>
   <td>Enabled
   </td>
   <td>Enabled
   </td>
   <td>{"Version":"2012-10-17","Id":"Policy1548340984399","Statement":[{"Sid":"Stmt1548340975673","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:role/sftp-transfer"},"Action":["s3:DeleteObject","s3:DeleteObjectVersion","s3:GetObject","s3:GetObjectVersion","s3:PutObject"],"Resource":"arn:aws:s3:::pl-pt99-4-uploads/*"}]}
   </td>
  </tr>
  <tr>
   <td>99-3-uploads
   </td>
   <td>Test
   </td>
   <td>us-west-2
   </td>
   <td>Enabled
   </td>
   <td>Enabled
   </td>
   <td>{"Version":"2012-10-17","Id":"Policy1548340984399","Statement":[{"Sid":"Stmt1548340975673","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:role/sftp-transfer"},"Action":["s3:DeleteObject","s3:DeleteObjectVersion","s3:GetObject","s3:GetObjectVersion","s3:PutObject"],"Resource":"arn:aws:s3:::pl-pt99-3-uploads/*"}]}
   </td>
  </tr>
  <tr>
   <td>99-2-uploads
   </td>
   <td>Test
   </td>
   <td>us-west-2
   </td>
   <td>Enabled
   </td>
   <td>Enabled
   </td>
   <td>{"Version":"2012-10-17","Id":"Policy1548340984399","Statement":[{"Sid":"Stmt1548340975673","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:role/sftp-transfer"},"Action":["s3:DeleteObject","s3:DeleteObjectVersion","s3:GetObject","s3:GetObjectVersion","s3:PutObject"],"Resource":"arn:aws:s3:::pl-pt99-2-uploads/*"}]}
   </td>
  </tr>
  <tr>
   <td>99-1-uploads
   </td>
   <td>Test
   </td>
   <td>us-west-2
   </td>
   <td>Enabled
   </td>
   <td>Enabled
   </td>
   <td>{"Version":"2012-10-17","Id":"Policy1548340984399","Statement":[{"Sid":"Stmt1548340975673","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:role/sftp-transfer"},"Action":["s3:DeleteObject","s3:DeleteObjectVersion","s3:GetObject","s3:GetObjectVersion","s3:PutObject"],"Resource":"arn:aws:s3:::pl-pt99-1-uploads/*"}]}
   </td>
  </tr>
  <tr>
   <td>99-0-uploads
   </td>
   <td>Test
   </td>
   <td>us-west-2
   </td>
   <td>Enabled
   </td>
   <td>Enabled
   </td>
   <td>{"Version":"2012-10-17","Id":"Policy1548340984399","Statement":[{"Sid":"Stmt1548340975673","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:role/sftp-transfer"},"Action":["s3:DeleteObject","s3:DeleteObjectVersion","s3:GetObject","s3:GetObjectVersion","s3:PutObject"],"Resource":"arn:aws:s3:::pl-pt99-0-uploads/*"}]}
   </td>
  </tr>
</table>
<p>Production S3 buckets are not yet provisioned as we still need to test S3→ Main Solutions</p>
<h3 id="mounting-s3-buckets-to-a-filesystem">Mounting S3 buckets to a Filesystem</h3>
<p>To Mount S3 to the main server, we leveraged an opensource project called s3fs-fuse. It is well regarded through the community and is used in many production systems.</p>
<p><br>
It is in the standard or EPEL repositories for both CentOS 7 and Ubuntu servers.</p>
<p><a href="https://github.com/s3fs-fuse/s3fs-fuse/wiki/Fuse-Over-Amazon">s3fs-fuse github</a></p>
<h4 id="set-up-of-s3-filesystem">Set up of S3 Filesystem</h4>
<p>It happened on 99-5, on <em>main</em>, <em>i1</em> and <em>h1</em>, for testing purposes and proof-of-concept. All servers are CentOS 7.x. All actions as root.</p>
<p><strong>Install and Mount S3 filesystem</strong></p>
<pre tabindex="0"><code>$ yum update (to 7.6)
$ yum install s3fs-fuse
$ reboot
$ echo [access keys in .aws/credentials in format ID:KEY]  &gt; /etc/passwd-s3fs
$ chmod 600 /etc/passwd-s3fs
$ mkdir /sftp
$ s3fs 99-5-uploads /sftp -o passwd_file=/etc/passwd-s3fs
</code></pre><p>It is important that each upload action (i.e. copy to s3) starts with first locally copying what needs to be uploaded (transparently by s3fs) and then upload it. That means that there must be enough space in the local filesystem to support any file, which will uploaded to s3. The place where the intermediate temporary file is created, can be set with the <code>use_cache</code> directive.</p>
<p>In order to permanently set mount at boot, in case we mount the bucket 99-5-uploads to /sftp as root and temp. files in /tmp/s3-cache, we will add to fstab as follows.</p>
<pre tabindex="0"><code>s3fs#99-5-uploads  /sftp   fuse   allow_other,use_cache=/tmp/s3-cache,rw,nosuid,nodev,uid=0,gid=0   0 0
</code></pre><p><em><span style="text-decoration:underline;">Note</span></em>: For the tests, we disabled caching (which works <em>only</em> for reading from S3, i.e. GET) , the setting was as : <code>use_cache=&quot;&quot;</code> .</p>
<h4 id="git-file-tracking">Git File Tracking </h4>
<p>Since s3fs uses the <a href="https://github.com/libfuse/libfuse">Fuse</a> sub-system and not a true filesystem, there is no way for <a href="http://man7.org/linux/man-pages/man7/inotify.7.html">inotify</a> to notice changes to s3 and notify the filewatcher.py process.</p>
<p>I&rsquo;m flagging this as a process that will need to be updated to harness S3 Object Version History features in order to replicate the git features we&rsquo;ve set up. </p>
<p>When testing on 99-5 server, we had to move the path because the symbolic links caused issues with another internal tool that leverages inotify and local git repo checkins for file tracking (which does work with writing files from the system, just not via sftp/s3)</p>
<h4 id="size-tests">Size tests</h4>
<pre><code>Credit to Michael G, a former colleague of mine who led the performance testing of this solution
</code></pre>
<p>The S3 buckets, at the time of writing (Feb. 2019) can support up to 5TB, with files up to 5GB. However, the upload process may happen in two ways: As a single stream for files up to 2GB and multipart, which allows up to 5GB.</p>
<p><br>
The s3fs, by default support multipart upload. However, it chooses the single stream way, if the file is up to 20MB. In case of streaming-like commands (e.g. <code>dd</code> to the s3) the s3fs sees it as less than 20MB and uses single stream, with the limit of 2GB.</p>
<p>For the size tests (and speed of single uploads) we used two types of files, text and binary, and various sizes, 1MB, 5MB, 10MB, 50MB, 100MB, 200MB, 500MB, 1GB, 2GB and 5GB. The purpose of that is <em><span style="text-decoration:underline;">not</span></em> to simulate the actual workload, instead to stress the s3fs and know its limitations. The sizes were chosen to compare up/download times and investigate any speed differentiation in relation to size. The text files are close to real text, i.e. created by arbitrary words of the american-english dictionary, not just letters. This way, any compressing capability will exhibit its merits.</p>
<h5 id="speed-of-transfer-from-outside">Speed of transfer from outside</h5>
<ol>
<li>Uploading to the sftp server was found practically the same with uploading via sftp to the master server. The result is expected since the AWS SFTP is nothing more but an instance that reads/writes files from/to S3.</li>
<li>The upload/download speed was found quite stable and consistent, with about 5MBytes/sec from Pennsylvania, USA and about 3.3MBytes/sec from Brno, Europe. <br>
This measurement, although not exact, is indicative for the expected upload speed from most, if not all, of the customers.</li>
<li>Since the traffic from/to the outside world is more or less limited by the Internet-AWS connections, it is important to have a picture of the actual abilities of the S3 mount. This can be measured with copies in both directions, i.e. uploads and downloads. The test host is <em>i1</em>. All results are saved in an worksheet.
<ol>
<li>The tests consisted of repetitions of uploads and dowloads, overwriting or writing new files, repeating same file and whole set twice, with or without cache. Sizes where from 1MB to 5GB.</li>
<li>The most prevalent conclusion is that the results are highly inconsistent, with speed varying between 5 MB/s and 30 MB/s for upload and 60 MB/s and 200 MB/s for downloads. An exemplar case is the not-cached download of 50MB, which saw speeds between 28 MB/s and 66 MB/s.</li>
<li>Binary and text files offer similar speeds. In some cases they seemed to be different, but that was widely inconsistent: In other cases the difference was reversed.</li>
<li>The results show that cache has no effect in writing (i.e. uploading).</li>
<li>Caching in downloading, though, offers an improvement of 1.8x to 7x comparing to not-cached. The highest benefit is in smaller size files, below 50MB.</li>
<li>The speed for uploads reaches highest values from the 50MB.</li>
<li>Download speed changes differently, with highest values for sizes between 5MB and 1GB, but lowering for 2GB and 5GB. That applies to both cached and not cached tests.</li>
</ol>
</li>
</ol>
<table>
  <tr>
   <td>
   </td>
   <td>
<strong>cached</strong>
   </td>
   <td><strong><em>in MB/s</em></strong>
   </td>
   <td><strong>no cache</strong>
   </td>
   <td><strong><em>in MB/s</em></strong>
   </td>
  </tr>
  <tr>
   <td><strong>Size (MB)</strong>
   </td>
   <td><strong>Uploads</strong>
   </td>
   <td><strong>Downloads</strong>
   </td>
   <td><strong>Uploads</strong>
   </td>
   <td><strong>Downloads</strong>
   </td>
  </tr>
  <tr>
   <td>1
   </td>
   <td><em>4,7</em>
   </td>
   <td><em>61,2</em>
   </td>
   <td>4,4
   </td>
   <td><em>13,2</em>
   </td>
  </tr>
  <tr>
   <td>5
   </td>
   <td><em>12,6</em>
   </td>
   <td><em>160,8</em>
   </td>
   <td>12,2
   </td>
   <td><em>22,7</em>
   </td>
  </tr>
  <tr>
   <td>10
   </td>
   <td><em>14,7</em>
   </td>
   <td><em>188,7</em>
   </td>
   <td>16,6
   </td>
   <td><em>31,0</em>
   </td>
  </tr>
  <tr>
   <td>50
   </td>
   <td><em>24,3</em>
   </td>
   <td><em>134,7</em>
   </td>
   <td>26,3
   </td>
   <td><em>45,1</em>
   </td>
  </tr>
  <tr>
   <td>100
   </td>
   <td><em>20,9</em>
   </td>
   <td><em>102,0</em>
   </td>
   <td>24,1
   </td>
   <td><em>54,3</em>
   </td>
  </tr>
  <tr>
   <td>200
   </td>
   <td><em>27,1</em>
   </td>
   <td><em>86,2</em>
   </td>
   <td>25,2
   </td>
   <td><em>47,9</em>
   </td>
  </tr>
  <tr>
   <td>500
   </td>
   <td><em>26,7</em>
   </td>
   <td><em>188,2</em>
   </td>
   <td>23,7
   </td>
   <td><em>48,4</em>
   </td>
  </tr>
  <tr>
   <td>1000
   </td>
   <td><em>25,0</em>
   </td>
   <td><em>73,6</em>
   </td>
   <td>22,7
   </td>
   <td><em>43,8</em>
   </td>
  </tr>
  <tr>
   <td>2000
   </td>
   <td><em>23,0</em>
   </td>
   <td><em>59,6</em>
   </td>
   <td>23,9
   </td>
   <td><em>33,8</em>
   </td>
  </tr>
  <tr>
   <td>5000
   </td>
   <td><em>23,5</em>
   </td>
   <td><em>59,0</em>
   </td>
   <td>22,9
   </td>
   <td><em>31,7</em>
   </td>
  </tr>
</table>
<h4 id="performance-tests-stress">Performance tests (stress)</h4>
<p>The test consist of multiple reading and writing to S3, mounted to various numbers of systems. The reason is to understand and document the abilities of the S3 as mounted filesystem (s3fs). <br>
Based on the outcome of this test, it can be decided whether agent servers can directly access files on S3.</p>
<h5 id="describing-the-setup">Describing the setup</h5>
<p>The number of agent servers can be up to 10, therefore this is the max number of test hosts. <br>
For better understanding of the scalability of S3 as a file system, the tests run various mixes of reading and writing, from one host, single thread, to ten hosts with 10 threads each.</p>
<p><br>
As target objects (i.e. files) are used the files created in the size tests, only the text version, since there is no usable difference, while those files resemble more to the actual load.</p>
<p><br>
The hosts used are T3.medium, which -according to documentation and various sites - offer a good performance.   It is important to note that the AWS instances offer very different throughput in networking. A simple T2.nano can reach 100Mbps, while the chosen t3.medium offers quite stable 250 Mbps, with bursts that reach 10 Gbps.</p>
<p><br>
There is no scalability in the various instance types, only those two numbers, so there is no reason to try different instances.</p>
<h5 id="preliminary-results">Preliminary results</h5>
<p>These tests help identify the right scaling mix of reads and writes and a basis for comparison. It also helped fine-tune the tests.</p>
<p><em>Info from: <a href="https://cloudonaut.io/ec2-network-performance-cheat-sheet/">https://cloudonaut.io/ec2-network-performance-cheat-sheet/</a> , <a href="https://aws.amazon.com/ec2/instance-types/">https://aws.amazon.com/ec2/instance-types/,</a> <a href="https://aws.amazon.com/ec2/faqs/#Which_instance_types_support_Enhanced_Networking">https://aws.amazon.com/ec2/faqs/#Which_instance_types_support_Enhanced_Networking,</a> <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html">https://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html</a></em></p>
<ol>
<li>The time spent reading from the local filesystem is hardly measurable, being consistently under 0.09 sec for 200MB and under 0.004 sec for 10MB. Compared to the actual time spent within s3fs, the local read is negligible. It does not participate in the outcome, since it is lost by rounding the measurements.</li>
<li>Writing to the local filesystem is quite higher (around 1.5 sec), still much smaller than the s3fs part. However, that part is skipped by sending the read data to /dev/null. This way, the time measured for <em>get</em> actions is cleaned from local filesystem overhead.</li>
<li>As seen from the results, the effect of threaded multiple copies is more prevalent with bigger size of files. The speed degradation is (averagely) linear (the increase in performance with 200MB files is likely statistical &ldquo;noise&rdquo;, possibly some arbitrary improved performance in S3-side or other AWS-infrastructure change).</li>
<li>The results show that it is very important to run the tests from different hosts, to identify the load: Is it S3-based or host&rsquo;s network throughput ?</li>
<li>Also, the results show that there is no need to run all the threaded tests. However, for validity and confirmation, there could be a set limited to 1 up to 8 threads.</li>
</ol>
<table>
  <tr>
   <td>
   </td>
   <td colspan="6" >
<strong>Size (in MB)</strong>
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td><strong>Th-count</strong>
   </td>
   <td>1
   </td>
   <td>5
   </td>
   <td>10
   </td>
   <td>50
   </td>
   <td>100
   </td>
   <td>200
   </td>
   <td rowspan="14" >
    <img src="/img/sftp-proj/performance.png" width="" alt="Performance">
   </td>
  </tr>
  <tr>
   <td><strong>1</strong>
   </td>
   <td>3,9
   </td>
   <td>9,1
   </td>
   <td>13,7
   </td>
   <td>22,8
   </td>
   <td>22,2
   </td>
   <td>21,5
   </td>
  </tr>
  <tr>
   <td><strong>2</strong>
   </td>
   <td>3,7
   </td>
   <td>10,5
   </td>
   <td>13,0
   </td>
   <td>20,0
   </td>
   <td>20,5
   </td>
   <td>20,4
   </td>
  </tr>
  <tr>
   <td><strong>3</strong>
   </td>
   <td>3,9
   </td>
   <td>10,1
   </td>
   <td>11,5
   </td>
   <td>17,9
   </td>
   <td>17,5
   </td>
   <td>18,8
   </td>
  </tr>
  <tr>
   <td><strong>4</strong>
   </td>
   <td>3,2
   </td>
   <td>8,4
   </td>
   <td>12,0
   </td>
   <td>16,7
   </td>
   <td>17,4
   </td>
   <td>18,7
   </td>
  </tr>
  <tr>
   <td><strong>5</strong>
   </td>
   <td>3,1
   </td>
   <td>9,0
   </td>
   <td>10,1
   </td>
   <td>18,1
   </td>
   <td>17,2
   </td>
   <td>16,5
   </td>
  </tr>
  <tr>
   <td><strong>6</strong>
   </td>
   <td>2,5
   </td>
   <td>7,4
   </td>
   <td>9,2
   </td>
   <td>17,4
   </td>
   <td>18,3
   </td>
   <td>14,8
   </td>
  </tr>
  <tr>
   <td><strong>7</strong>
   </td>
   <td>3,0
   </td>
   <td>7,4
   </td>
   <td>9,7
   </td>
   <td>15,9
   </td>
   <td>15,1
   </td>
   <td>13,1
   </td>
  </tr>
  <tr>
   <td><strong>8</strong>
   </td>
   <td>3,0
   </td>
   <td>8,4
   </td>
   <td>9,4
   </td>
   <td>15,3
   </td>
   <td>15,1
   </td>
   <td>11,5
   </td>
  </tr>
  <tr>
   <td><strong>9</strong>
   </td>
   <td>2,9
   </td>
   <td>7,6
   </td>
   <td>9,4
   </td>
   <td>13,5
   </td>
   <td>15,3
   </td>
   <td>10,5
   </td>
  </tr>
  <tr>
   <td><strong>10</strong>
   </td>
   <td>2,7
   </td>
   <td>7,2
   </td>
   <td>9,3
   </td>
   <td>11,7
   </td>
   <td>15,1
   </td>
   <td>12,7
   </td>
  </tr>
  <tr>
   <td><strong>AVG</strong>
   </td>
   <td><strong>1,74</strong>
   </td>
   <td><strong>8,51</strong>
   </td>
   <td><strong>10,73</strong>
   </td>
   <td><strong>16,92</strong>
   </td>
   <td><strong>17,37</strong>
   </td>
   <td><strong>15,84</strong>
   </td>
  </tr>
  <tr>
   <td><strong>MIN</strong>
   </td>
   <td><strong>2,46</strong>
   </td>
   <td><strong>7,20</strong>
   </td>
   <td><strong>9,21</strong>
   </td>
   <td><strong>11,66</strong>
   </td>
   <td><strong>15,06</strong>
   </td>
   <td><strong>10,52</strong>
   </td>
  </tr>
  <tr>
   <td><strong>MAX</strong>
   </td>
   <td><strong>3,92</strong>
   </td>
   <td><strong>10,53</strong>
   </td>
   <td><strong>13,66</strong>
   </td>
   <td><strong>22,77</strong>
   </td>
   <td><strong>22,18</strong>
   </td>
   <td><strong>21,46</strong>
   </td>
  </tr>
</table>
<h5 id="results-1">Results</h5>
<p>The final tests comprises of the following (code attached as <a href="/download/attachments/265758497/the_tests.sh?version=1&amp;modificationDate=1552494786132&amp;api=v2">the_tests.sh</a>):</p>
<ul>
<li>Threaded copies from the host to the S3 (i.e. PUT or upload)</li>
<li>Threaded copies to the host from the S3 (i.e. GET or download)</li>
<li>Threaded copies between the host and the S3 in both directions.</li>
</ul>
<p>The tests are repeated 30 times (i.e. 30 iterations of the same copy command), on each thread.  <br>
Threads run from 1 to 15, i.e. 15 parallel copies from a single host.</p>
<p>The hosts were finally as follows:</p>
<ul>
<li>a single t3.medium</li>
<li>a single m4.xlarge</li>
</ul>
<p>Unfortunately, this is how far the setup-tests could go. There were continues issues with running the tests. Running 15 repetitions and up to 10 threads, or running the test with only 1MB files in place of each size, it caused no errors. However, when the full-scal setup ran, it never made it to the end. Sometimes, the <code>cp</code> command stuck, while others the whole host faced problems, like an unreachable /var/TEST directory, while in tests. In most cases, the only solution was to reboot.</p>
<p>The configuration of s3fs (i.e. mount options) was the aforementioned for m4.xlarge and t3.medium first run. In t3.medium, another try was done with different configuration (described later), which indeed alleviated the issues.</p>
<pre><code>Tests need to finalise and re-run, at some point.
</code></pre>
<h4 id="caveats">Caveats</h4>
<p>There may be some issues with stability. There were cases that the connection was dropped and, in any action, appears: &ldquo;Transport endpoint is not connected&rdquo;. <br>
It may be solved with various limit, timeout and retries parameters. The mixture tried now is: <code><em>stat_cache_expire=10,retries=9,readwrite_timeout=30,multireq_max=50,connect_timeout=20</em></code> .</p>
<p>There are symptoms with using the S3 that might stem from the test or the hosts used. If such symptoms persist, they may critically impede application on production. <br>
The symptoms are explained in current (2019-03-13) results section: The m4.xlarge host did not make it, with the full scale tests.</p>
<pre tabindex="0"><code>only put 50 1_15_14 6.12 7.25 3.20 1.80 7.82 
cp: error writing &#39;/sftp/uploads/test.50m.bin.1_15_14&#39;: No space left on device 
cp: failed to extend &#39;/sftp/uploads/test.50m.bin.1_15_14&#39;: No space left on device 
         2.39 7.54 8.01 1.61 5.02 
cp: error writing &#39;/sftp/uploads/test.50m.bin.1_15_14&#39;: No space left on device 
[...]
only put 5 1_15_14 1.08 0.89 1.69 1.28 0.82 0.52 0.85 0.75 0.51 1.71 0.73 cp: cannot create regular file &#39;/sftp/uploads/test.5m.bin.1_15_14&#39;: Input/output error 1.05 cp: cannot create regular file &#39;/sftp/uploads/test.5m.bin.1_15_14&#39;: Input/output error 2.03 cp: cannot create regular file &#39;/sftp/uploads/test.5m.bin.1_15_14&#39;: Input/output error 
</code></pre><p>Source of info:</p>
<ul>
<li><a href="https://github.com/s3fs-fuse/s3fs-fuse/wiki/Fuse-Over-Amazon">https://github.com/s3fs-fuse/s3fs-fuse/wiki/Fuse-Over-Amazon</a></li>
<li><a href="https://code.google.com/archive/p/s3fs/issues/314">https://code.google.com/archive/p/s3fs/issues/314</a></li>
<li><a href="https://github.com/s3fs-fuse/s3fs-fuse/issues/254">https://github.com/s3fs-fuse/s3fs-fuse/issues/254</a></li>
<li><a href="https://github.com/s3fs-fuse/s3fs-fuse/issues/506#issuecomment-454331760">https://github.com/s3fs-fuse/s3fs-fuse/issues/506#issuecomment-454331760</a></li>
<li><a href="https://github.com/s3fs-fuse/s3fs-fuse/issues/340">https://github.com/s3fs-fuse/s3fs-fuse/issues/340</a></li>
<li><a href="https://github.com/s3fs-fuse/s3fs-fuse/issues/152#issuecomment-278894805">https://github.com/s3fs-fuse/s3fs-fuse/issues/152#issuecomment-278894805</a></li>
<li><a href="https://github.com/s3fs-fuse/s3fs-fuse/issues/748">https://github.com/s3fs-fuse/s3fs-fuse/issues/748</a></li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>The Sound of Noise</title>
            <link>https://blog.twstewart.me/posts/sound-of-noise/</link>
            <pubDate>Thu, 21 Nov 2019 20:17:30 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/sound-of-noise/</guid>
            <description>&lt;h2 id=&#34;what-does-a-file-sound-like&#34;&gt;What does a file sound like?&lt;/h2&gt;
&lt;p&gt;What does a script or program or binary executable sound like? It may seem like a silly question, but this is actually one I have an answer for. Did you know that
with some free open source tools you can transform any type of data into a digital representation of what a computer thinks it should
sound like, and thus create &amp;ldquo;music&amp;rdquo; in ways completely unintended by most anyone.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<h2 id="what-does-a-file-sound-like">What does a file sound like?</h2>
<p>What does a script or program or binary executable sound like? It may seem like a silly question, but this is actually one I have an answer for. Did you know that
with some free open source tools you can transform any type of data into a digital representation of what a computer thinks it should
sound like, and thus create &ldquo;music&rdquo; in ways completely unintended by most anyone.</p>
<h2 id="data-bending--glitch-art">Data Bending &amp; Glitch Art</h2>
<p>No, this isn&rsquo;t the fifth element of the next <a href="https://en.wikipedia.org/wiki/Avatar:_The_Last_Airbender">Avatar</a> series. This is a technique that came from the before time; when things were analog,
like cameras, recorders, tape reels, VCRs, and really any medium one can imprint on, would wear and tear over time and use. In some cases, these pieces
of analog equipment could be altered in a way that produced extra effect on the final image or recording. Like adding a physical and permanent
instagram filter to ones camera, or an unintentional echo from a lousy microphone. Full hipster points, if your gear did this by accident, but as
all cool things go, the more people notice them, the more people want to replicate it and try it themselves.</p>
<p>So here we are, far past the age of tape drives and dial up, where we don&rsquo;t have to wait for our gear to break down for us to be able to play and experiment.
The emergent term for this kind of fun is called, Glitch Art or Data Bending</p>
<div align="center">
<img width="300" height="250" alt="original image" src="/img/me-original.jpg"></img>
<img width="300" height="250" alt="databent image" src="/img/me-databent.png"></img>
</div>
<p>I was able to achive the above effect by, saving the image as a .TIFF. Using <a href="https://www.audacityteam.org/">Audacity</a>, import as RAW uncompressed &ldquo;U-Law&rdquo; encoding, Adding an invert effect
at the middle of the sound profile, then exporting it as .TIFF with RAW uncompress U-LAW encoding.</p>
<p>Of course, this post is not about visual but auditory data bending.</p>
<h2 id="bring-the-noise">Bring the Noise</h2>
<p>Using audacity, we can import ANY file and represent that as sound.
Now you can solve the question for what Git, Google Chrome, or even the exported image from above sound like.</p>
<div align="center">
<audio src="/snd/headshot.mp3" type="audio/mp3" preload="metadata" controls controlsList="nodownload"></audio>
</div>
<br></br>
Some sounds very cool like this one, others are pure harsh noise that physically hurt to listen to.    
Here's one spliced from the Go binary
<br></br>
<div align="center">
<audio src="/snd/go.mp3" type="audio/mp3" preload="metadata" controls controlsList="nodownload"></audio>
</div>
<p>As the good man, Chuck D, once said&hellip;.<a href="https://www.youtube.com/watch?v=kl1hgXfX5-U">YEEAAAAA BOY&hellip;bring the noise</a></p>
<p>The fun part ( if you are okay with getting lots of duds of harsh white noise ) is exploring this very strange
avenue to produce music never before heard by human kind.</p>
<h3 id="the-steps-to-do-this">The Steps to do this</h3>
<ol>
<li>Download/Open Audacity</li>
<li>File -&gt; Import -&gt; Raw Data</li>
<li>Select ANY file, anything above a few dozen KB will have enough &ldquo;time&rdquo; to get meaningful sounds out of.</li>
<li>Encoding: U-LAW or A-LAW - U LAW seems to have the best results</li>
<li>Byte Order: Little Endian</li>
<li>Import and Modify to your hearts desire</li>
<li>Once happy with your experiment, Export As MP3 or WAV</li>
</ol>
<h2 id="the-final-result">The Final Result</h2>
<p>Shameless cross promotion aside, here is a live example of taking sound from different binary data, slapping a beat on top of it, and calling it music.</p>
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/6g-ZTiMlAAQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>  
</div>
<p>Hurts doesn&rsquo;t it&hellip;that&rsquo;s intentional, if you read the <a href="https://www.amazon.com/Writers-Universe-Stew-Stunes/dp/1980313202/">book</a> it makes sense narratively for The Firth World. A world created to be punished by it&rsquo;s authors/creators.</p>
<p>In High School, my music teacher once told me, <i>&ldquo;Music is just organized noise.&quot;</i><br>
With this method, we&rsquo;re testing that theory. Sure this is no <a href="https://www.youtube.com/watch?v=JTEFKFiXSx4">4:33</a>, <a href="https://en.wikipedia.org/wiki/Avant-garde">avant-garde</a> probably would roll it&rsquo;s eyes at calling this music. And maybe not in it&rsquo;s raw form, but part of my love for music comes from breaking rules and boundaries. This is forcing terrible harsh noise, kicking and screeching, into some semblance of a tune that maybe with the right composer and audience could call music.</p>
<p>My aunt once told me after listening to some of my early guitar recordings, <i>&ldquo;What&rsquo;s the point if it&rsquo;s just noise? No one want&rsquo;s to hear that.&quot;</i> <br>
Yeah they were bad, but I was having fun. So I made a small album of literal machine noise using these techniques. The Punk in me rides on, I guess.</p>
<p>Final words: don&rsquo;t be afraid to bend your files and data&hellip;but like don&rsquo;t break your data. Although, audacity makes a temp copy of the file during editing, don&rsquo;t like overwrite the source file. That will break things&hellip;have fun.</p>
]]></content>
        </item>
        
        <item>
            <title>New Blog</title>
            <link>https://blog.twstewart.me/posts/my-first-post/</link>
            <pubDate>Sun, 17 Nov 2019 18:57:41 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/my-first-post/</guid>
            <description>&lt;h1 id=&#34;trying-out-a-new-thing&#34;&gt;Trying out a new Thing&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve made the decision to split my digital resume from my digital blog. The resume is basically a few pages that infrequently change, and this is in conflict
with the nature of a blog. So I&amp;rsquo;m splitting the work. The resume site will remain as is, with but a single link change to forward folks to this new blog.&lt;/p&gt;
&lt;p&gt;To make both my historical posts and new content faster to produce, I&amp;rsquo;ve since learned about these neat little technologies called static site generators. In essence, after the initial setup, make it much easier to add content and publish quicker without worrying about links and formatting, just words and content.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<h1 id="trying-out-a-new-thing">Trying out a new Thing</h1>
<p>I&rsquo;ve made the decision to split my digital resume from my digital blog. The resume is basically a few pages that infrequently change, and this is in conflict
with the nature of a blog. So I&rsquo;m splitting the work. The resume site will remain as is, with but a single link change to forward folks to this new blog.</p>
<p>To make both my historical posts and new content faster to produce, I&rsquo;ve since learned about these neat little technologies called static site generators. In essence, after the initial setup, make it much easier to add content and publish quicker without worrying about links and formatting, just words and content.</p>
<ul>
<li>This blog is created with <a href="https://gohugo.io/">Hugo</a>, a static site generator written in go</li>
<li>The Theme is slightly adapted from <a href="https://themes.gohugo.io/hugo-theme-hello-friend-ng/">hello-friend-ng</a></li>
<li>This blog site is hosted in AWS S3, and distributed via AWS Cloudfront</li>
<li>This list has gotten too long</li>
</ul>
<p align="center">
<img src="/img/animals/wink.jpg" alt="wink" style="float:center;"/>  
</p>
]]></content>
        </item>
        
        <item>
            <title>How Centralized Logging Saved Our Jobs</title>
            <link>https://blog.twstewart.me/posts/elk-stack-logging-saved-us/</link>
            <pubDate>Sat, 16 Nov 2019 19:53:29 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/elk-stack-logging-saved-us/</guid>
            <description>&lt;body&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt; Memory of a Postmortem &lt;/h1&gt;
&lt;p&gt;I am writing about a past experience, one that never ceases to make me laugh and shudder at how crazy 
  and simple the solution became once we had it figured out. I do apologize for lack of screen shots, this all occurred 
  over 3 years ago, but I think the lessons learned are still relevant.
&lt;/p&gt;
&lt;br&gt;
&lt;img align=center src=&#34;https://blog.twstewart.me/img/memes/train-crash-s.png&#34;&gt;&lt;/img&gt;
&lt;h2&gt; Background&lt;/h2&gt;
&lt;p&gt;
  One of the products I was gifted with administering was a very large content management solution, that on this single product 
  when you added up each individual site that we hosted on this platform, was in the millions of views a day. This was all co-hosted 
  in a datacenter, a Webscreen hardware solution to block malicious traffic, running a series of Cisco firewalls, some F5 Load Balancers, 20 some VCenter clusters, and five or so 
  storage back ends. The app itself was a very bloated java app, that required no less than 28GB of RAM per running process,
  we generally ran 2 processes on each VM, with northward of 60 VMs in total to handle the traffic. Totally ready for cloud scale, am I right? 
&lt;/p&gt;</description>
            <content type="html"><![CDATA[<body>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1> Memory of a Postmortem </h1>
<p>I am writing about a past experience, one that never ceases to make me laugh and shudder at how crazy 
  and simple the solution became once we had it figured out. I do apologize for lack of screen shots, this all occurred 
  over 3 years ago, but I think the lessons learned are still relevant.
</p>
<br>
<img align=center src="/img/memes/train-crash-s.png"></img>
<h2> Background</h2>
<p>
  One of the products I was gifted with administering was a very large content management solution, that on this single product 
  when you added up each individual site that we hosted on this platform, was in the millions of views a day. This was all co-hosted 
  in a datacenter, a Webscreen hardware solution to block malicious traffic, running a series of Cisco firewalls, some F5 Load Balancers, 20 some VCenter clusters, and five or so 
  storage back ends. The app itself was a very bloated java app, that required no less than 28GB of RAM per running process,
  we generally ran 2 processes on each VM, with northward of 60 VMs in total to handle the traffic. Totally ready for cloud scale, am I right? 
</p>
<br>
<p>
  All of that + not a single solution for centralized logging and because this product had changed hands so many times through
  acquisitions and mergers, we had not a single developer resource available. Just keep it alive, it's still making money,
  but let's not do anything to improve Quality of Life. Being someone whose job it is to fix things, I pushed back as hard as 
  I could on this.
</p>
<br>
<p>
  Fun side note -- when I joined, the team had migrated monitoring to New Relic, but the old Nagios instance was still online
  and was emitting over 700 critical notifications per minute. The old team had built a lot of custom monitoring to alert 
  when things were going south with the app, the new team pretty much lost all that insight when trying to migrate to New Relic.
  Two months of side work between sprints, I was able to clean out the cruft and turn Nagios back into it's former glory, 
  and it became our first place to look for issues presenting.
</p>
<h1>Where Things Went Wrong</h1>
<p>
  <b>Blamoo! DOS attack!</b> everything is on fire, and people are losing their minds...about once a month we'd get a huge surge in traffic, 
  our Webscreen solution would fail to respond, 
  all the traffic came in, slammed our very heavy java apps, and 20 severs or so would fall over, and it would take us an hour 
  or so to mitigate and get things back in order. It was very disruptive to our clients, and these events felt entirely 
  invisible to me, the webscreen interface was overly clicky, cisco logs were too fast to read and isolate patterns. 
</p>
<br>
<p>
  ...and eventually it got worse...like every week we were down for hours, the time between events getting shorter, our managers and clients
  getting redder in the face each time. Jobs were threatened, it got so bad, that our firewalls were even crashing from the overwhelming amount of traffic. 
  They would fail over, die, fail over again, and to make matters worse, our secondary was not in full sync with our primary, so things 
  like certs would fail and have to be manually added up to the secondary.
  What do we do? It's chaos.
</p>
<br>
<img align=center src="/img/memes/disaster-girl-s.jpg"></img>
<h1>Where Things Went Right</h1>
<p>
  We are blind, I decried to our manager. We've got to build something that can show us what is happening. Out of choices, and having a good 
  track record in the past, my manager let me off the reins. I abandoned my ongoing sprint, and created a new epic to do this. 
</p>
<br>
<p>
  I knew we had to be able to visualize the traffic, then narrow down to exact IPs. Which sounded more and more like a centralized logging
  solution.
</p>
<br>
<p>
  At the time, the 2 most popular open-source logging solutions were <a href="https://www.elastic.co/what-is/elk-stack">ELK(Elasticsearch, Logstash, Kibana) stack</a> 
  and <a href="https://www.graylog.org/">graylog2</a>. I had small experience with each, but wanted something that could handle billions of events
  from our firewalls. I didn't think I needed app metrics, or data from the F5s. We just needed to see the traffic, then we could build out 
  black lists and anything else. This solution would be highly targeted to this need only, so as to not waste time with extra configuration or 
  use cases.
</p>
<br>
<p>
  I ended up going with ELK stack. I had tried graylog2 for another solution and while some things appeared easier, without the ability to transform logs(Logstash),
  it gave me a lot of trouble in my testing. So I moved on to ELK, I configured Kibana and Logstash to be on one VM, with 3 nodes of 2 TB volumes, for elasticsearch. 
</p>
<br>
<p>
  We were able to very quickly build visualizations and dashboards, the next DDos hit and we were prepared within about 3 days of time from Okay to Ready.
</p>
<br>
<p>
  This is the part I wish I still had screen shots for -- It became immediately obvious that what the firewall was seeing and what the Webscreen was showing were opposites. The webscreen sits in front of 
  the firewalls, so the patterns should be the exact same, with some reduced amount of traffic on the firewalls if the webscreen was activated.
  Well, we got the opposite, The webscreen saying I don't see a huge spike of incoming traffic, our firewalls logs jumping from thousands to millions
  per second. Something was very very off about the whole situation. I talked with other experts around our team and the only thing we could agree on
  was that it looked like the webscreen was wrong about everything. 
</p>
<br>
<img align=center src="/img/memes/jurassic-p-s.png"></img>
<br>
<p>
  Digging into the logs further, we realized that a large majority of the IPs in the flood were our own! It turned out that the webscreen had a software
  button that could flip which "side" was being monitoring. Ingress or Outgress, somehow through all the months of continued Dos, we had missed 
  that someone had flipped the configuration to monitor our Outgress traffic between the webscreen and the firewalls. 
  We were defending the internet from ourselves (probably a wise choice jk). 
</p>
<br>
<p>
  After pressing the single button, we pretty much did not experience a Denial of Service event that took us down ever again. Although any proof 
  had long since gone away, the sheer amount (this was not the only incident) of misconfigurations and poor practices lead me to believe 
  that there were some intentional bombs left behind by old teams. But with the right attitude and tools, you too can root out those gremlins
  and defeat them. Turn an old legacy behemoth, into something that can float as long as the company demands.
</p>
<br>
<p>
  The ELK stack also showed that over 75% of our internal traffic was coming/going to a dead <a href="https://graphiteapp.org/">graphite</a> server. 
  It had locked up so bad, it had rebooted into read only single user mode, but the agents on every server didn't care and kept sending, failing that, and resending, on and on.
</p>
<h1>In Summary</h1>
<p>
  When big problems present, have a way to comprehend what is happening, whether this is centralized logging with visualizations, or good and deep 
  monitoring. Which technology you choose to accomplish that goal, is much less important than getting something quickly stood up and immediately 
  provide value to your team.
</p>
<br>
<p>
  After all those lessons learned, and some more company shuffling we had a new product (the one above finally End of Life'd), once again 
  without centralized logging and wouldn't you believe it management would not approve me to work on a solution like the above. 
  Sighting that since we were in the cloud it would be too expensive. I argue that the expense from downtime and disruptive work is far 
  more than the cost of storing and computing that data, but could not get them to see it that way.
</p>
</body>]]></content>
        </item>
        
        <item>
            <title>Adventure in Ml</title>
            <link>https://blog.twstewart.me/posts/adventure-in-ml/</link>
            <pubDate>Sat, 03 Aug 2019 19:53:16 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/adventure-in-ml/</guid>
            <description>&lt;h1&gt;Adventures with Machine Learning for Fiction Writing &lt;/h1&gt;
&lt;p&gt;One part of my head is firmly planted in technology, while another is constantly working on my side-projects such as 
  &lt;a href=&#34;https://stewstunes.com/welcome/&#34;&gt;fiction writing&lt;/a&gt;. Sometimes those things cross into each other - I like 
  to imagine stories and one of these sessions was about a person working a menial job and slowly 
  realizing that they were a small piece in a Neural Network, they keep trying to solve the same problem in their life, 
  each time trying something different, and as they begin to solve it and become aware to their reality, 
  the AI controlling everything begins to crumble. A bit like &lt;a href=&#34;https://www.imdb.com/title/tt0133093/&#34;&gt;The Matrix&lt;/a&gt;
   meets &lt;a href=&#34;https://www.imdb.com/title/tt0113957/&#34;&gt;The Net&lt;/a&gt;.
&lt;/p&gt;</description>
            <content type="html"><![CDATA[<h1>Adventures with Machine Learning for Fiction Writing </h1>
<p>One part of my head is firmly planted in technology, while another is constantly working on my side-projects such as 
  <a href="https://stewstunes.com/welcome/">fiction writing</a>. Sometimes those things cross into each other - I like 
  to imagine stories and one of these sessions was about a person working a menial job and slowly 
  realizing that they were a small piece in a Neural Network, they keep trying to solve the same problem in their life, 
  each time trying something different, and as they begin to solve it and become aware to their reality, 
  the AI controlling everything begins to crumble. A bit like <a href="https://www.imdb.com/title/tt0133093/">The Matrix</a>
   meets <a href="https://www.imdb.com/title/tt0113957/">The Net</a>.
</p>
<p><b>OR</b> something like that...</p>
<p>The other key ingredient in this novel idea would be to have an actual AI write parts of the book and see if people 
  could tell. The beginning would be written entirely by myself, with the AI slowly fading in like intrusive thoughts
   in between the narrative, and finally taking over the whole story. 
</p>
<p>What I didn't know was if that was technically possible or if I was seeing too much fiction in my science fiction.</p>
<p> And so the following is an account of my adventures trying to harness the current state of Machine Learning and 
  make use of for my project.</p>
<h2>GPT-2</h2>
<p>In early 2019, the company <a href="https://openai.com/blog/better-language-models/">OpenAI</a> released a new 
Machine Learning model called GPT-2. Claiming that it was so good at text 
generation that they had to reduce the size of the model offered to the public for safety and ethical reasons. Right away 
this one felt like the real deal, I had found a half dozen of random student projects on github, trying to DIY different 
style of GANs or RNNs for text generation, but none of them felt like they would be powerful enough. In addition to 
the hype around gpt-2, there were a number of helpful articles and walk-throughs on how to get started using it, and or
training it on new data. This is what I really wanted, the ability to train the model on previous and highly rated fiction
novels to see if it could compete.
</p>
<h2>Data Wrangling</h2>
<p>
  To train any ML model you need data, and lots of it + plus time and a hearty GPU. After the side quest of acquiring 
  the proper hardware and getting CUDA/tensorflow setup, I was ready to tackle the Data part. Over the years I've 
  collected a hefty amount of ebooks. For this project,  I needed more, so I began seeking out alternative sources
  until I had over 1000 different books from Fantasy and SciFI.
</p>
<p>All of these books came in either epub format, .lit (?) or a flat PDF, so the task was to convert all of those to text, 
  which then could be imported into a binary blob for training. I wrote a script, 
  ( <a href="https://github.com/twstewart42/digital-resume/blob/master/scripts/eboook.py">eboook.py</a> ) to handle this task, 
  but what I learned is that no 2 ebooks are the same, with different headings, section names, etc. It's almost infeasible
  to select just "Chapter 1" and output that, as in most cases that could be section 002, or 004, or any number really. 
  First pass was to output the full book, into individual text files.</p>
<p>I want to pause here and give a big shout out to <a href="https://www.gwern.net/GPT-2">Gwern</a>, the creator of 
  <a href="https://www.thiswaifudoesnotexist.net/">thiswaifudoesnotexist.net</a> and the gpt-2-poetry project.
  His work allowed me and many others to follow in his footsteps and use this awesome technology. User 
  <a href="https://github.com/nshepperd/gpt-2">nsheppard's Git repo</a> seems to be the best for training 
  GPT-2 on new data.
</p>
<pre><code>
$ ls -l ../data_origs/originals/txt_proc/*.txt | wc -l
1019
$ PYTHONPATH=src python3 encode.py ../data_origs/originals/txt_proc/ novels.npz
# takes like 15 minutes with the large dataset
$ PYTHONPATH=src python3 train.py --model_name 117M --dataset novels.npz --save_every 1000 --sample_every 100 --run_name novel1
Loading dataset...
100%
dataset has 38779104 tokens
Training...
[16 | 6.98] loss=3.34 avg=3.34
[17 | 8.45] loss=3.25 avg=3.30
[18 | 9.93] loss=3.29 avg=3.29
[19 | 11.42] loss=3.27 avg=3.29
[20 | 12.90] loss=3.22 avg=3.27
[21 | 14.38] loss=3.20 avg=3.26
...
99 | 131.98] loss=3.21 avg=3.22
======== SAMPLE 1 ========
ok to them, he thought.  
They turned around—and the girl turned, and she fell.  
Hitting a hard wall, he pulled a gun from her waist, and fired at her.  
She did not dare go to ground.  
The girl was unconscious.  
Her breathing was heavy, and she did not speak.  
She saw the light of a flash in the sky.  
A massive star would have exploded that day.The next day she and Alia were 
working with the man who lived in a forest,and in the morning she saw that 
it had exploded and the town had fallen.  
She recognized it as the village where her brothers lived.  
It was small and quiet here, but she could tell that men had been there 
before the town's fall.  
She had a hard time telling the difference between what might have been the 
town and the village.  
They were very far away, near the old town, but they were near what she 
called the 'gates.  
She had her head above the ground as well, and she did not want to go in 
and shoot anyone.  
She had left her weapon behind, and she went to the town gate.  
She looked out of the gate, and she saw that there were two men hiding 
beneath a stone-thick fence.  
She took off her gloves, and saw that they were carrying the two men.  
She stood before them.  
Her eyes were dark and wide, and she did not dare go in.  
She saw him running toward the fence, and she did not dare go in on him.
She made her way through a man's door, and saw that he was carrying an iron.  
With her pistol, she fired at him.  
With the gun and the iron gun that was with her, she shot him in a single 
blow, and the man who had shot him stood 
up and ran toward her.And the same thing happened to her brothers and 
sisters—where are you?She went out to the woods, knowing she had to.  
With a knife and a saw, she started into the woods, aiming toward the 
hills below.  
Some day she and Alia had got back down the road, and her brothers and 
sisters saw her go.And she stood in the open, and she thought, I am going 
to take you back down the road.  
It was going to be a long day, and she knew what it was.  

...
# and between 24 and 48 hours later it's been properly cooked.
</code></pre>
<p> For all trained models, I used the same couple of statements to see what results I would get.
<blockquote>
“What am I missing?” He asked to the machine before him. The machine was a metal contraption with a sphere in the center. If his machine could keep that sphere in the center without external energy sources, he would achieve the improbable. Infinite Energy. 
</blockquote>
<pre><code>
$ python3 src/interactive_conditional_samples.py --model_name novel1 --temperature 0.9 --top_k 30
model prompt >>> “What am I missing?” He asked to the machine before him. The machine was a metal contraption with a 
sphere in the center. If his machine could keep that sphere in the center without external energy sources, he would 
achieve the improbable. Infinite Energy.
======================================== SAMPLE 1 ========================================
<h1 id="hed-never-known-that-he-had-ever-been-born-a-human-but-he-felt-that-balance-with-a-mortal-might-be-impossible">The infinite.<br>
He had no other answer; why was there no other thing to get him to understand? All he knew now about the existence
of the infinite was — could NOT be.<br>
He looked around, saw it, saw the other world outside, and saw it, and it held him and him, and he saw, and felt it,
and yet it did not release him to the world.<br>
This was a machine.<br>
This was a machine belonging to the agency of others, of all the universes, and all the entities that comprised it.
“We are not brothers,” the machine said.<br>
“We are not like other men.<br>
We are not as we were.”The man had no words for “brother” but he had said that, and the machine responded by clicking.<br>
The machine had to know the world.<br>
Could he guess what it would make of his friends? They were holding him in a circle of three, not two.<br>
The circle of three was larger than the one at which he was holding.<br>
“Your friends are not the only entities in existence who are connected to you,” the machine told him.<br>
“They have been here before and they are still here.<br>
They are the Earths, the Great Sphere, that we know of you.<br>
They will be all the way to Earth.<br>
They will stand in the clearing before you.”“It is a prophecy of mine,” the man said, feeling the machine’s presence.<br>
“That we must be ready.” His hand flapped like the hand of a lover.“All in good time,” the machine said.<br>
“For the Balance.”He was, of course, not a great man, but his spirit was tender.<br>
He knew that the Balance was behind everything.<br>
His world was not balanced on a single step.<br>
He’d never been any great in any person, but it seemed that the great Balance had always been balanced on a single step,
before they’d cut that one off.<br>
He’d never known that he had ever been born a human, but he felt that balance with a mortal might be impossible</h1>
<p></code></pre>
As one can see, it &ldquo;works&rdquo; but there&rsquo;s definitely room for improvement. I see a lot of circular conversations, repeated
phrases and sometime, absolute copy/paste from one of the src stories instead of something new.</p>
</p>
<p>I'll state here again, this is where I wasn't sure if I was expecting too much of the technology or if I 
  should be happy with the current results and learn to love the quirks.</p>
<h2>Phase 2...let's try all the things</h2>
<p>As any real or pretend scientist will tell you, it can be hard to find the point at which enough is enough and 
  to let the results be as they are, and so I continued to break down my data into different sizes and shapes, 
  sometimes only using the first few chapters instead of the entire book, or outputting the books into one mega text file,
instead of individual files. A few attempts using only a single author but their entire catalogue. 
Over the course of a few weeks, I would setup the data in the evening, start the training, and in the morning decide if it 
was doing well enough to continue or stop and try something else. But with all that effort and fun, I never 
really got better results than the first training. I do think I need better formatted data, but at this point I'm not
sure how to achieve that. But I will keep trying, and eventually I do believe I'll have something I can work with
to make this sci-fi daydream become a real novel.
</p>  
<h3>fantasy1</h3>
<pre><code>
python3 src/interactive_conditional_samples.py --model_name fantasy1 --temperature 0.9 --top_k 30
Model prompt >>> “What am I missing?” He asked to the machine before him. The machine was a metal contraption with a 
sphere in the center. If his machine could keep that sphere in the center without external energy sources, he would 
achieve the improbable. Infinite Energy.
======================================== SAMPLE 1 ========================================
The object itself was a tiny cube; it was not large enough to reach into the center of the sphere to cause it to 
collapse. As if it were an egg.
<p>The contraption was the first I encountered. “How do they do it?” He asked as he moved over it.</p>
<p>“The best I can do is to just do it without being able to do anything else with the machine.” As he pointed toward
the object, the object began to slide outward.</p>
<p>The contraption was a large, white, circular sphere that had two sides. They were the corners of the ballista. It was
a large, circular ballista. The ballista moved with its axis, and this time it was not moving with the other side.
The two halves of the sphere were at the same time, and the machine seemed to be moving along some sort of horizontal
axis.</p>
<p>When I stood there, I wondered what the purpose of the contraption was, but it was obvious. The object was being moved,
as if through a series of concentric circles. It felt as if the machine was being moved. And then, just as the
contraption began to move, the object suddenly vanished.</p>
<p>“I see. I see.” I said to the machine as I moved over the ballista.</p>
<p>“Oh. That . . .” The object vanished.</p>
<p>“Why don’t you go to the other side?” I asked him.</p>
<p>“I know it would be a waste to bring that sphere to the other side.”</p>
<p>“But why?”</p>
<p>“Because, just because something is gone, it doesn’t mean nothing. I can do something that I can do without touching
anything.”</p>
<p>“Well, we need to go to it,” I said.</p>
<p>“Then go to it,” he said back.</p>
<p>“We need to go to that side.” I pointed over the machine.</p>
<p>“We are in a room,” he said.</p>
<p>“The right side is where the ballista was moving,” I said.</p>
<p>“The left,” he said.</p>
<p>“I’m sure.”</p>
<p>“The machine can do everything,” he said.
</code></pre></p>
<h3>fantasy2</h3>
<pre><code>
$ python3 src/interactive_conditional_samples.py --model_name fantasy2 --temperature 0.9 --top_k 30
What am I missing?” He asked to the machine before him. The machine was a metal contraption with a sphere in 
the center. If his machine could keep that sphere in the center without external energy sources, he would achieve 
the improbable. Infinite Energy.
======================================== SAMPLE 1 ========================================
 He’d built it himself.
<p>His arm grew numb, then it stopped, and he felt a chill and the feel of something cold, something as solid as the earth.
His heart stopped, and his lungs were already failing. He tried to turn his head to look at the ceiling—it was a very
tall thing, about forty feet tall. It had a dome, like the dome in the cavern outside the city itself, where air rose
above it. It would have been easy to take off a piece of it just then, and take up two or three of its sides just to
reach the dome. He felt the weight of the thing falling off in the air like a falling horse. Just then something slammed
into it, a rock like a rock thrown back a few feet. It slammed against his arm and, somehow, he fell.</p>
<p>He felt the cold air, and he felt its bite. It was a cold, heavy, strong thing. He had to pull himself up. He was
nearly at the top of the dome when the rock hit. He didn’t have to pull back, not even to the point where he didn’t
feel the whole thing hit. He was suddenly falling again. It stopped, and he let out a small cry.</p>
<p>“No! No!”</p>
<p>The air was so cold, he was almost choking.</p>
<p>“Aaaaahhhh!”</p>
<p>His hands were freezing so badly that he couldn’t get up. He was holding his head against the dome, and his lungs
were going to shut down. As he started to get up, something hit him again. It was a big rock, and it smashed out at
him, one big chunk of stone falling on top of him and hitting the floor, hitting the ceiling, hitting the window of
the cellar, and hitting all of them down, right up to the very top and leaving him on his face. He struggled up and
found his arm and began to struggle.</p>
<p>A big stone hit him in the face, hard, and he was not sure why—the thing hit his face in a terrible collision. It hit
his chest and nose, and his mouth opened. He started to breathe hard, and his chest started growing larger and bigger,
and his mouth came out of him, and he tried to open his mouth, to tell the thing what to do. It wouldn’t, but something
else hit him in the throat.
</code></pre></p>
<h3>Steven King Books</h3>
<pre><code>
$ python3 src/interactive_conditional_samples.py --model_name steve1 --temperature 0.9 --top_k 30
Model prompt >>> "What am I missing?” He asked to the machine before him. The machine was a metal contraption with a 
sphere in the center. If his machine could keep that sphere in the center without external energy sources, he would 
achieve the improbable. Infinite Energy.
======================================== SAMPLE 1 ========================================
<p>“Thank you, Mr. Potter.”</p>
<p>The machine was a little too small to put the cylinder up on its own, but it did what it was supposed to do, which was
to put the cylinder in place on the machine. Because the machine could move so fast, it could not be kept moving by
some weird mechanical device. And as far more than three other machines had ever been put into the center of the machine,
it had managed to keep it moving on its own. That was the point, and as of now it was the only one left to do so.</p>
<p>“You&rsquo;re right, and it did its job, but my machine is not a machine.”</p>
<p>“Why do you have to worry about that?”</p>
<p>“I have no need for that.”</p>
<p>The machine seemed to shake and twitching slightly as it did so. It was a little too small to make the cylinder move up
and down, but that didn&rsquo;t stop it from moving.</p>
<p>It held the cylinder in place and started moving. That was the only thing it could do, and it was not a much bigger
machine.</p>
<p>“I want to say a few words,”</p>
<p>The machine seemed to move and to start moving as soon as it was moved. It didn&rsquo;t stop moving, but it changed. It had
changed its head, got its hands on its legs and began to pull them at the same time to make it move. It was moving at
three hundred miles a second on its own.</p>
<p>“I want to say a few words,” the robot said at long last.</p>
<p>It did so with the same sudden and sudden speed of its original motion, but it could have stopped now, and the machine
would still have been moving. The machine would have moved faster than it was now, but no more.</p>
<p>“What do I do now?”</p>
<p>“I want to say a few words,” the robot asked.</p>
<p>It did so with a very small smile. The robot had a little piece of the machine in its hands and had started to pick it
up, putting it on its shoulders and making it fly up.</p>
<p>“I want to say a few words,” the robot said. It did so, and with a small smile.</p>
<p>The robot turned as fast as it could.
</code></pre></p>
<h3>117M - The original gpt-2</h3>
<p>Here are results with the stock gpt-2 model 117M</p>
<pre><code>
Model prompt >>> "What am I missing?” He asked to the machine before him. The machine was a metal contraption with a 
sphere in the center. If his machine could keep that sphere in the center without external energy sources, he would 
achieve the improbable. Infinite Energy.
======================================== SAMPLE 1 ========================================
‡
<p>When she looked at him, his face had turned dark. It was dark and cold, but she kept the smile on her smile.</p>
<p>&ldquo;I&rsquo;ve seen you before, don&rsquo;t get me wrong. I&rsquo;ve met you before. But I am not like you. I am not as tall as you.&rdquo;</p>
<p>A smile began to appear on the white-robed woman&rsquo;s face, and then he let out a soft sigh. She smiled back, she smiled
back… but then she stopped.</p>
<p>&ldquo;I&rsquo;ve become like you. I&rsquo;ve lost my sense of right and wrong.&rdquo; He said in a voice very similar to her smile that he
had been following. He said that he would go back to that situation if he did not think that she was trying to do
him harm, but that she did not understand him at all and was simply trying to kill him. The girl looked even more
pale, not like she had taken on a human form. At this point, she started crying out in frustration and crying out
in pain.</p>
<p>But you were just too strong…&quot;</p>
<p>A voice from the machine spoke, and the girl froze, her eyes widened. She was holding him by the hand, and he was
trembling, his shoulders sagging, his face trembling. However, the words were not a threat, but something to her.
&ldquo;I&rsquo;m sorry about that.&rdquo;</p>
<p>She nodded, and the machine slowly took on a voice, and the girl cried out again. However, no one spoke, and the
voice was silent for a few short seconds.</p>
<p>&ldquo;I know you want to kill me, and I know you want to kill me, and I know you want to kill me. But I&rsquo;m sorry for
that. I&rsquo;m sorry we didn&rsquo;t live in peace, for the past ten years, and for now. I want to see you grow up and find
your purpose, and I want to see you understand that I&rsquo;m not a robot, who&rsquo;s just a little girl who wanted a place
to live. I want to see you understand that you need to find happiness, and I want to see you see the joy that you
will have with humans, and that you will live with everyone else who lives.&rdquo;</p>
<p>The machine slowly began to work on his hands.</p>
<p>&ldquo;And now you want to kill me?&rdquo;</p>
<p>&ldquo;No, I want my life to be worth anything, even if it means the end of your life.&rdquo; The girl shook her head
</code></pre></p>
<p>
  And that is the story so far on my adventure with Machine Learning for fiction writing. So as one can see, at least the way
  I've got it setup, it isn't ready for prime time, but given more time and understanding on my part, I think it still has a lot
  of potential. Something I will keep experimenting with and building until I have a good platform.
</p>
</body>]]></content>
        </item>
        
        <item>
            <title>A New Website, A New Job, and A New Cup of Coffee</title>
            <link>https://blog.twstewart.me/posts/new-website-2019/</link>
            <pubDate>Thu, 01 Aug 2019 19:53:43 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/new-website-2019/</guid>
            <description>&lt;body&gt;
&lt;section&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;A New Website, A New Job, and A New Cup of Coffee&lt;/h1&gt;
&lt;h2&gt;New Website&lt;/h2&gt;
&lt;p&gt;
    Welcome internet travelers, to the new edition of my public website. 
    For the 2019 edition, I decided to go as minimal as possible, as a way to fight back against the endless noise 
    that the majority of the internet has become. Videos and advertisements attack us at every click, 
    endless scroll refuses to let us leave, but here in this small corner of the web, 
    I thought it might be nice to give readers a break from all that activity. A return to basics and normalcy.
&lt;/p&gt;</description>
            <content type="html"><![CDATA[<body>
<section> 
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1>A New Website, A New Job, and A New Cup of Coffee</h1>
<h2>New Website</h2>
<p>
    Welcome internet travelers, to the new edition of my public website. 
    For the 2019 edition, I decided to go as minimal as possible, as a way to fight back against the endless noise 
    that the majority of the internet has become. Videos and advertisements attack us at every click, 
    endless scroll refuses to let us leave, but here in this small corner of the web, 
    I thought it might be nice to give readers a break from all that activity. A return to basics and normalcy.
</p>
<p>
    The previous version of this website was running on a simple <a href="https://wordpress.org/">Wordpress</a> installation, hosted on 
    <a href="https://cloud.google.com/">GCP</a> 
    (Google Compute Platform) with <a href="https://www.cloudflare.com/">Cloudflare</a> acting as a 
    CDN (Content Distribution Network), SSL via <a href="https://letsencrypt.org/">Let's Encrypt</a>.
    Fun, and neat, but it was costing me <strike>Big Money</strike> like $5 a month for the smallest sliver of 
    a micro server GCP would provide, and as time went on it became clear that I did not need all these 
    extra Wordpress features. The site was nothing more than text and some fancy menus. 
    With AWS's free tiers and getting outside of the traditional LAMP Stack/everything-must-run-on-a-machine 
    mindset, I was able to setup this website using 
    <a href="https://registry.terraform.io/modules/chgangaraju/cloudfront-s3-website/aws/1.0.0">Terraform</a> 
    to do the following things; host files out of <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html">AWS S3</a>
    , then leveraged <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-cloudfront-walkthrough.html">CloudFront</a>
    for SSL termination and act as CDN. If I've done my math right everything should be 
    <b>FREE</b> for a year and after that pennies in total cost. For heavier use websites, this might not be
    the way to go, but for me and this website, this setup is perfect, giving me total control over code and infrastructure,
    cats and dogs, cost and speed.
</p>
<p>
    All of the code for both the website and deploying the technologies via terraform are available on this 
    <a href="https://github.com/twstewart42/digital-resume">github repo</a>. So if you want, you can build your 
    own digital resume or personal website following this as an example.
</p>
<h2>New Job</h2>
<p>
    In a few days, August 6th, I will start a new job with KCF Technology and I am really looking forward to the new gig.
    I think it's going to be a place that pushes me to work better and get my hands on a very diverse range of exiting technologies,
    plus from all accounts, this might be the first professional position with a growing company and not one that is shrinking (YaY!)
</p>
<h2>New Cup of Coffee</h2>
<p>
    As also hinted in the title, I brewed a fresh carafe full of coffee, and will begin partaking after publishing this page. 
    I've been told by many folks on the internet, that people like to know these things, and this is in no way a waste of your time.
</p>
<p> Let me know on <a href="https://twitter.com/twstewart42">twitter @twstewart42</a>, if you like the new look of the website or if you have any other comments.</p>
</section>   
</body>]]></content>
        </item>
        
        <item>
            <title>The Wheel of Misfortune</title>
            <link>https://blog.twstewart.me/posts/the-wheel-of-misfortune/</link>
            <pubDate>Fri, 21 Jun 2019 19:53:58 -0500</pubDate>
            <author>twstewart42&#43;blog@gmail.com (Tom Stewart)</author>
            <guid>https://blog.twstewart.me/posts/the-wheel-of-misfortune/</guid>
            <description>&lt;body&gt;
&lt;div id=&#39;content&#39;&gt;
&lt;h1 id=&#34;wheel-of-misfortune&#34;&gt;Wheel Of Misfortune&lt;/h1&gt;
&lt;h2 id=&#34;the-game&#34;&gt;The Game&lt;/h2&gt;
&lt;img src=&#34;https://blog.twstewart.me/img/wom/main-logo.png&#34; alt=&#34;main logo&#34;&gt;
&lt;p&gt;A role-playing game for incident management training&lt;/p&gt;
&lt;p&gt;The Wheel of Misfortune is best done with an on-call group but can be expanded to fit larger groups up to the entire enterprise depending on how thorough and complex you make your scenarios.&lt;/p&gt;
&lt;p&gt;My team has found that this works best as a monthly activity, and in general we get through 2 incidents in about an hour as we select an on-call agent using the random picker, spin the wheel (terrifying), work through the incidents – with team leads adding information and hints or extra challenges along the way, then a wrap up of the incident by going through a mock Root Cause Analysis/Blameless Postmortem.&lt;/p&gt;</description>
            <content type="html"><![CDATA[<body>
<div id='content'>
<h1 id="wheel-of-misfortune">Wheel Of Misfortune</h1>
<h2 id="the-game">The Game</h2>
<img src="/img/wom/main-logo.png" alt="main logo">
<p>A role-playing game for incident management training</p>
<p>The Wheel of Misfortune is best done with an on-call group but can be expanded to fit larger groups up to the entire enterprise depending on how thorough and complex you make your scenarios.</p>
<p>My team has found that this works best as a monthly activity, and in general we get through 2 incidents in about an hour as we select an on-call agent using the random picker, spin the wheel (terrifying), work through the incidents – with team leads adding information and hints or extra challenges along the way, then a wrap up of the incident by going through a mock Root Cause Analysis/Blameless Postmortem.</p>
<h2 id="hosted">Hosted</h2>
<p>A live demo of this project is available to view at <a href="http://wom.twstewart.me">wom.twstewart.me</a></p>
<p>It is hosted as a static site in an AWS S3 bucket, a walk through on setting up a static website using S3 is available below</p>
<h2 id="testing">Testing</h2>
<p>One can use the included Dockerfile to test and/or host a local version of the game…or just open the index.html in your favorite browser (low tech testing)</p>
<pre><code>$ docker build -t wheel-of-misfortune-test .
$ docker run --name wom-test -d -p 8080:80 wheel-of-misfortune-test
</code></pre>
<h2 id="screenshots">Screenshots</h2>
<p>The Wheel of Misfortune Welcome page  <br />
<img src="/img/wom/wom-1.png" alt="Demo 1" width="720" height="570" /></p>
<p>Select an On-call Agent  <br />
Spin the wheel and get started solving incidents  <br />
<img src="/img/wom/wom-2.png" alt="Demo 2" width="720" height="570"/></p>
<h2>Walkthrough: Hosting the Wheel of Misfortune</h2>
<p>As the app/live demo is fairly basic HTML/javascript project, I found that the simplest way (and cheapest) was to configuring an AWS S3 bucket to host a live static website.</p>
<h3>How to:</h3>
<ol>
<li>Create a new S3 bucket, name it the same as the domain name you wish to host &#8216;wom.twstewart.me&#8217;</li>
<li>Use default settings for S3 bucket</li>
<li>On the Properties tab, enable Versioning and Static Website Hosting</li>
<li>For Static Website Hosting:
<ol>
<li>index document = index.html</li>
<li>error document = index.html</li>
<li>Save</li>
</ol>
</li>
<li>On the Permission tab, turn OFF public access blocks</li>
<li>On Bucket Policy Tab, you can restrict the IP&#8217;s allowed to connect to the website, obviously, mine is open to all public, if you wish to do the same it is recommended to restrict it to specific IPs
<ol>
<li style="list-style-type: none;">
<ol>
<li>
<pre>{
   "Version": "2012-10-17",
   "Id": "S3PolicyId1",
   "Statement": [
   {
     "Sid": "IPAllow",
     "Effect": "Allow",
     "Principal": "*",
     "Action": "s3:GetObject",
     "Resource": "arn:aws:s3:::wom.twstewart.me/*",
     "Condition": {
          "IpAddress": {
          "aws:SourceIp": "0.0.0.0/0"
          }
     }
  }
  ]
}</pre>
</li>
</ol>
</li>
</ol>
<ol>
<li>On CORS tab, enable this policy to allow systems to read the incident_response.json
<ol>
<li>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"&gt;
&lt;CORSRule&gt;
&lt;AllowedOrigin&gt;*&lt;/AllowedOrigin&gt;
&lt;AllowedMethod&gt;GET&lt;/AllowedMethod&gt;
&lt;AllowedHeader&gt;*&lt;/AllowedHeader&gt;
&lt;/CORSRule&gt;
&lt;/CORSConfiguration&gt;</pre>
</li>
</ol>
</li>
</ol>
</li>
<li>Upload site files to the S3 bucket</li>
<li>See if you can hit the static website URL, and if perms are correct all assets should load</li>
<li>Then if you want to set up an easy name to go to the s3 bucket URL, on your DNS system, create a CNAME redirect like:
<ol>
<li>CNAME wom.twstewart.me wom.twstewart.me.s3-website-us-east-1.amazonaws.com</li>
</ol>
</li>
<li>Congratulations! You have an awesome website hosting in AWS on an S3 bucket.</li>
</ol>
<h1 id="credits">Credits</h1>
<p>The concept of a Wheel of Misfortune comes from the Google's <a href="https://landing.google.com/sre/sre-book/chapters/accelerating-sre-on-call/#xref_training_disaster-rpg">Site Reliability Engineer ebook</a>, and the tool was originally created by <a href="https://github.com/dastergon/wheel-of-misfortune">dastergon</a> and then I forked and expanded to fit my team’s needs, added ‘dark mode’ and a random on-call agent selector at <a href="https://github.com/twstewart42/wheel-of-misfortune">My Github</a></p>
</div>
</body>]]></content>
        </item>
        
    </channel>
</rss>
