<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Joshua Zenn</title>
    <description>Technology, science, music, gaming and sometimes crudely drawn art.</description>
    <link>https://armmaster17.github.io/</link>
    <atom:link href="https://armmaster17.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 07 Nov 2021 20:08:07 -0500</pubDate>
    <lastBuildDate>Sun, 07 Nov 2021 20:08:07 -0500</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>Introduction to GitOps with Flux v2</title>
        <description>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;GitOps is a word that gets thrown around a lot these days. People are talking about it a lot becuase it solves a lot of problems that have plagued the industry for a long time. Deploying to production can be an insanely complicated process for some people, requiring multiple build steps and established procedures requiring human intervention or hacky scripts somebody wrote at 11 PM who just wanted to go home for the day. GitOps allows you to syncronize your collection of kubernetes manifest files between a repository and your cluster. That way if things go south, you can destroy your cluster and rebuild in just a few minutes. Additionally you can bring over your code review, change control, and audit procedures that you use with your application development to your cluster configuration, since now changes are no different than a PR to your application’s codebase.&lt;/p&gt;

&lt;p&gt;When I first learned about GitOps, I really wanted to try it out on my homelab. I initially went with &lt;a href=&quot;https://argoproj.github.io/cd/&quot;&gt;Argo CD&lt;/a&gt;, simply because that’s what everyone else was using. Although Argo CD is cool and all, there was one major deal-breaker for me. Argo CD requires nearly 8GB of RAM for normal operation and a lot of CPU power, which was more than double what I had in my test k3s cluster. What’s the point of a GitOps tool if there’s no room left on the cluster to deploy applications? As a broke college student I figured there had to be a better (and cheaper) way. The solution I found was &lt;a href=&quot;https://github.com/fluxcd/flux&quot;&gt;Flux CD&lt;/a&gt;. It’s very lightweight, and all of it’s configuration is stored in your source control, so no need to mess with any fancy PV setups in k8s.&lt;/p&gt;

&lt;p&gt;Flux works on top of any existing k3s or k8s cluster. I’ll be using k3s for this tutorial. You’ll need about 512MB of RAM and a few milli-CPU available in your cluster. One important thing that I found is that the Flux documentation is a little confusing at times, and I often had to delete the Flux deployment and re-deploy to implement the change. This guide will attempt to get it right the first time (assuming your needs are the same as mine).&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;This tutorial assumes that you already have a Kubernetes cluster of some kind already up and running. If you do not have a cluster, there are several options available to you. &lt;a href=&quot;https://minikube.sigs.k8s.io/docs/&quot;&gt;Minikube&lt;/a&gt; is an excellent choice if you only have one (somewhat-powerful) machine. &lt;a href=&quot;https://github.com/alexellis/k3sup&quot;&gt;k3sup&lt;/a&gt; is a good tool if you have a few spare computers and want to quickly turn them into Kubernetes nodes. And finally there are always hosted options such as &lt;a href=&quot;https://aws.amazon.com/eks/&quot;&gt;EKS&lt;/a&gt; and &lt;a href=&quot;https://cloud.google.com/kubernetes-engine/&quot;&gt;GKE&lt;/a&gt;, but be aware these can get pricy very quickly. (If you enjoy a challenge and have a couple free weekends, there’s also &lt;a href=&quot;https://github.com/kelseyhightower/kubernetes-the-hard-way&quot;&gt;Kubernetes The Hard Way&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;All the commands below should be run using a computer with a Kubeconfig file that can connect to your cluster. In my case I have a dedicated LXC container that is inside an isolated network that I have created for my k3s cluster. Start by installing the Flux CLI tool:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Linux&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; https://fluxcd.io/install.sh | &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;bash
&lt;span class=&quot;c&quot;&gt;# MacOS&lt;/span&gt;
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;fluxcd/tap/flux
&lt;span class=&quot;c&quot;&gt;# Windows (requires Chocolatey)&lt;/span&gt;
choco &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;flux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next go to GitHub and create a &lt;a href=&quot;https://github.com/settings/tokens&quot;&gt;personal access token&lt;/a&gt;. Make sure that you check all the boxes under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repo&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read:packages&lt;/code&gt;. Fill in the appropriate values in the script below to bootstrap Flux into your cluster.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# On Windows, replace this with 'set'.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;gh_token&amp;gt; &lt;span class=&quot;c&quot;&gt;# Your GitHub personal access token&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;gh_username&amp;gt; &lt;span class=&quot;c&quot;&gt;# Your GitHub username&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;gh_repository&amp;gt; &lt;span class=&quot;c&quot;&gt;# Name of an empty or non-existant repository on your GitHub account (e.g. cluster-iac)&lt;/span&gt;
flux bootstrap github &lt;span class=&quot;nt&quot;&gt;--components-extra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;image-reflector-controller,image-automation-controller &lt;span class=&quot;nt&quot;&gt;--owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$GITHUB_USER&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$GITHUB_REPO&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--branch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;main &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;./clusters/prod &lt;span class=&quot;nt&quot;&gt;--personal&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--read-write-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now what does this command do? First we tell Flux that we want to bootstrap a new cluster, and we want to store our Infrastucture-as-Code (IaC) on GitHub. We then list some extra components we want to install. These will be important later when we set up automatic image update scanning. We then pass some information about GitHub to Flux. Flux will automatically create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GITHUB_REPO&lt;/code&gt; and watch the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; branch for updates. At the very end we note that the key we attached is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--read-write-key&lt;/code&gt;. This extra argument is critical for Flux to be able to write back to GitHub when image updates are found. Once you run this command give Flux a few minutes to deploy everything and fully initialize. In the meantime, you should see a new private repository on GitHub that has some Kubernetes manifest files in it. Congratulations, you now have a functioning GitOps setup!&lt;/p&gt;

&lt;h2 id=&quot;files-walkthrough&quot;&gt;Files Walkthrough&lt;/h2&gt;
&lt;p&gt;If you log in to GitHub.com, you should see your new repository. In this repository there should be some new files. What we’re interested in is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clusters/prod&lt;/code&gt; directory, as this is where configuration specific to your cluster will live. It’s important to know that you can have multiple Flux clusters use the same repository. This way you can re-use your manifests and configuration across all your clusters.&lt;/p&gt;

&lt;p&gt;In this directory, you can add several kinds of files. If you add a regular Kubernetes manifest file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.yaml&lt;/code&gt;), Flux will automatically apply it when you commit, and will ensure that it stays in sync (one important caveat to this is that if you manually edit a resource using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;, it will get overwritten the next time Flux tries to reconcile your manifest files).&lt;/p&gt;

&lt;p&gt;Another kind of file you can add is a Kustomization file. I have a kustomization file that points to another directory in the repository.&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kustomize.toolkit.fluxcd.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kustomization&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flux-system&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10m0s&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;dependsOn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;infrastructure&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;sourceRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GitRepository&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flux-system&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./apps/prod&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;prune&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This Kustomization file will apply all the manifest/Kustomization files located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps/prod&lt;/code&gt;. In turn I have a Kustomization file there that loads whatever apps I want from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps/base&lt;/code&gt; like so:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kustomization&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;../base/app1&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;../base/app2&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;../base/app3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each of these app directories has a Kustomization files that lists all the regular Kubernetes manifest files that are needed for that app, located in the same directory:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kustomization&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;namespace.yaml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pgcluster.yaml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deployment.yaml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;secrets.yaml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.yaml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ingress.yaml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This may seem a little redundant, but this gives me the flexability to keep all my app definitions in one place. Then I can enable and disable apps by commenting out one line in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps/prod/kustomization.yaml&lt;/code&gt;. There is no “right” way to organize your files as long as it works for you.&lt;/p&gt;

&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;You are now ready to jump into the world of GitOps. Coming soon is another tutorial on how to deploy your own app, completely from scratch, using only GitHub, GitHub Actions, and Flux.&lt;/p&gt;
</description>
        <pubDate>Sun, 07 Nov 2021 01:00:00 -0500</pubDate>
        <link>https://armmaster17.github.io/2021/11/07/gitops1/</link>
        <guid isPermaLink="true">https://armmaster17.github.io/2021/11/07/gitops1/</guid>
        
        
        <category>gitops</category>
        
        <category>devops</category>
        
        <category>cd</category>
        
        <category>ci</category>
        
        <category>flux</category>
        
        <category>github</category>
        
        <category>kubernetes</category>
        
        <category>docker</category>
        
      </item>
    
      <item>
        <title>Quick Bare-Bones Pastebin for Home Labs</title>
        <description>&lt;p&gt;As I have spun up and used hundreds of containers in my home cluster, I have run into a very annoying problem. Frequently I need to quickly copy initialization URLs, temporary authentication keys, or configuration files from machine to machine. My machines are configured for key-based authentication through SSH, so SCP is usually a no-go since I don’t want random containers with access to other containers. Uploading to Dropbox requires extra tools as well as introducing security risks. My Samba shares also have the same problem.&lt;/p&gt;

&lt;p&gt;Eventually, I found &lt;a href=&quot;https://github.com/solusipse/fiche&quot;&gt;Fiche&lt;/a&gt;. It’s a completely bare-bones pastebin with ZERO pre-requisites on the client machines. You only need netcat, which is installed by default on all Linux distributions, and equivilents are found in Windows and Mac OS X.&lt;/p&gt;

&lt;p&gt;If you just want a quick Ansible or Bash script, scroll to the end.&lt;/p&gt;

&lt;h1 id=&quot;pre-requisites&quot;&gt;Pre-Requisites&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;A Linux server to host Fiche (I will be using Debian 9 on top of LXC).&lt;/li&gt;
  &lt;li&gt;A static IP address for your Fiche server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;download-packages&quot;&gt;Download Packages&lt;/h1&gt;
&lt;p&gt;First, run this command to download all the needed packages. LXC users will need to remove sudo at the beginning of any command in this tutorial.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;git nginx build-essential automake autoconf autotools-dev curl
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, we’ll move to a temporary folder to download and compile Fiche.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /usr/src
git clone https://github.com/solusipse/fiche.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;fiche
make
make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;security-stuff&quot;&gt;Security Stuff&lt;/h1&gt;
&lt;p&gt;Now move to the folder that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; just created. We need to create a folder for the uploaded data. Additionally, we’ll create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fiche&lt;/code&gt; user so that we don’t need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; for a public-facing service.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /usr/local/bin/fiche
&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;fiche-data
adduser fiche
&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; fiche /usr/local/bin/fiche
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;o+r /usr/local/bin/fiche/fiche-data
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;configure-services&quot;&gt;Configure Services&lt;/h1&gt;
&lt;p&gt;First we will create a service for Fiche so that it will start automatically after a reboot. Next, we will configure Nginx to point to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fiche-data&lt;/code&gt; directory. In both of these files, replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fiche.firecore.lan&lt;/code&gt; with the DNS-resolvable hostname of your server.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; /etc/systemd/system/fiche.service
[Unit]
Description=FICHE-SERVER

[Service]
ExecStart=/usr/local/bin/fiche -d fiche.firecore.lan -o /usr/local/bin/fiche-data/ -u fiche

[Install]
WantedBy=multi-user.target
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; /etc/nginx/sites-enabled/default
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; /etc/nginx/sites-enabled/fiche
server {
	listen 80;
	server_name fiche.firecore.lan;
	charset utf-8;

	location / {
		root /usr/local/bin/fiche-data;
		index index.txt index.html;
	}
}
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;fiche
systemctl start fiche
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;testing&quot;&gt;Testing&lt;/h1&gt;
&lt;p&gt;That’s it! Now to test it. From any computer connected to the same network as your Fiche server, execute the following command (replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fiche.firecore.lan&lt;/code&gt; with your server’s DNS name or IP address).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; | nc fiche.firecore.lan 9999
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You should see the above code spit out a URL. Now to pull it up on another computer, execute the following from another computer (or the same terminal if you don’t have another one). Replace the URL with the one you recieved in the above command.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;curl &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt; http://niche.firecore.lan/xxxxxxx
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You should see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;. That’s it. A bare-bones pastebin for moving those long URLs and tokens around your network. One word of advice, keep your Fiche server behind a firewall. The trade-off for this simplicity is no security. Anyone with HTTP access to your system can read or write to the pastebins on your system.&lt;/p&gt;

&lt;p&gt;For the record, I am not the creator of Fiche. The &lt;a href=&quot;https://github.com/solusipse/fiche&quot;&gt;original GitHub repo&lt;/a&gt; can be found on &lt;a href=&quot;https://github.com/solusipse&quot;&gt;solusipse’s profile&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;ansible--bash-scripts&quot;&gt;Ansible + Bash Scripts&lt;/h1&gt;
&lt;p&gt;For those of you that are looking for it, here’s the command to execute an all-in-one Bash script and also an Ansible playbook.&lt;/p&gt;
&lt;h3 id=&quot;bash&quot;&gt;Bash&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;wget &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; https://gist.githubusercontent.com/ARMmaster17/94dcbaddd06de8df11fd294d5014d18e/raw/52f1289efccf0427048f6e72f5257c7eca9ce6c0/install-fiche.sh | bash
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;ansible-playbook&quot;&gt;Ansible Playbook&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ansible&quot; data-lang=&quot;ansible&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;# Execute with ansible fiche.yml --limit hostname.example.org
---
- hosts: all
  tasks:
  - name: install pre-req software
    package:
      name:
        - git
        - nginx
        - build-essential
        - automake
        - autoconf
        - autotools-dev
        - curl
      state: present
  - name: checkout fiche
    git:
      repo: 'https://github.com/solusipse/fiche.git'
      dest: /usr/src/fiche
  - name: compile fiche
    make:
      chdir: /usr/src/fiche
  - name: install fiche
    make:
      chdir: /usr/src/fiche
      target: install
  - name: create fiche user
    user:
      name: fiche
  - name: set directory permissions
    file:
      path: /usr/local/bin/fiche
      owner: fiche
      state: directory
      mode: o=rwx
  - name: create fiche-data directory
    file:
      path: /usr/local/bin/fiche/fiche-data
      state: directory
      owner: fiche
      mode: o=rw
  - name: create fiche service
    blockinfile:
      path: /etc/systemd/system/fiche.service
      block: |
        [Unit]
        Description=FICHE-SERVER
        [Service]
        ExecStart=/usr/local/bin/fiche -d fiche.firecore.lan -o /usr/local/bin/fiche-data/ -u fiche
        [Install]
        WantedBy=multi-user.target
  - name: create nginx site for fiche
    blockinfile:
      path: /etc/nginx/sites-enabled/fiche
      block: |
        server {
        listen 80;
        charset utf-8;
        location / {
          root /usr/local/bin/fiche-data;
        index index.txt index.html;
        }
        }
  - name: remove default nginx site
    file:
      path: /etc/nginx/sites-enabled/default
      state: absent
  - name: start fiche service
    service:
      name: fiche
      enabled: yes
      state: started
  - name: restart nginx
    service:
      name: nginx
      state: restarted
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
</description>
        <pubDate>Wed, 13 May 2020 13:00:00 -0400</pubDate>
        <link>https://armmaster17.github.io/2020/05/13/pastebin/</link>
        <guid isPermaLink="true">https://armmaster17.github.io/2020/05/13/pastebin/</guid>
        
        
        <category>container</category>
        
        <category>vm</category>
        
        <category>pastebin</category>
        
      </item>
    
      <item>
        <title>Writing a Markov-Chain Chatbot</title>
        <description>&lt;p&gt;A while back, I wrote &lt;a href=&quot;https://github.com/ARMmaster17/JeffBot&quot;&gt;Jeffbot&lt;/a&gt; as a fun side project because I was bored between classes in college. JeffBot is simply a chatbot with the intelligence of a two-year-old. It learns words and what words can/can’t be used sequentially in a sentence. At first, it spews out pure garbage or just repeats back what you just said. However, over time, JeffBot learns how to make what appears to be intelligent english.&lt;/p&gt;

&lt;p&gt;JeffBot itself is a complex app split over three different Heroku dynos and a particularly large database running on top of Rails/Sinatra/ASP.NET/I’m not even sure what anymore. In this article, I’m going to show you how to rebuild an N2 version of JeffBot in pure Ruby that can run locally with no external service/library dependencies.&lt;/p&gt;

&lt;p&gt;First, you will need to set up a directory for JeffBot. Create a directory called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JeffBot&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; into it. From now on, this directory will be referred to as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/&lt;/code&gt;. Inside this directory, create a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.rb&lt;/code&gt; and paste this code in.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# ~/main.rb&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;learn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;grams&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_cons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;grams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Struct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Ngram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;respond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sentence&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sentence&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;new_word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w2&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sentence&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_word&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_word&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentence&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Ngram&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:w1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:w2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;learning_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chomp&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;exit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;learn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;learning_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;respond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;learning_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s go through this code block by block to see what it does. Let’s start in the same place that the interpreter starts, line 32. This defines a struct called an N-gram. This represents a relationship between how likely one word is to appear after another. Take a look at the following input string:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This is a sentence.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;learn()&lt;/code&gt; function that we are going to cover in a minute is going to split that sentence into 2n N-grams like so.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[This, is]
[is, a]
[a, sentence]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Assuming this is the only training data provided, if we start with “a”, our chatbot would choose “sentence” as the next word. If multiple relations exist for one word, a random selection is made based on the choices (utilizing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count&lt;/code&gt; attribute is left as an exercise for the reader).&lt;/p&gt;

&lt;p&gt;The remainder of the code creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;learning_data[]&lt;/code&gt; array that we use to store training data. It also provides a loop to take input from the user, and exit the application if they have requested to do so by typing “exit” at the prompt. Towards the bottom of the code block, we see calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;learn()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;respond()&lt;/code&gt;. These two methods form the backbone logic of our chatbot.&lt;/p&gt;

&lt;p&gt;Now let’s take a look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;learn()&lt;/code&gt; method. We call it with two parameters, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;training_data[]&lt;/code&gt;. The first thing we need to do is convert &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt; into a series of 2N-grams like so.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;grams&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_cons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we iterate over the list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grams&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gram&lt;/code&gt; representing the relation we are currently looking at.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This line pulls out an N-gram relation if it already exists in our training data where both the first word and the second word match in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gram&lt;/code&gt; we are currently looking at.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Struct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Ngram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; block runs some logic on the results from the previous query of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;training_data[]&lt;/code&gt;. If a relation already exists, we increment the count. Otherwise we create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ngram&lt;/code&gt; Struct and shove it into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;training_data[]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s move on to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;respond()&lt;/code&gt; method. We call this with the same parameters as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;learn()&lt;/code&gt;. Our first line picks the first word based on the words that the user wrote at the prompt. We use this word to start our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sentence&lt;/code&gt; and to initialize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;word&lt;/code&gt; that we will utilize in a loop.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;    &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sentence&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we enter a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop. I set an arbitrary limit of 12 words in a sentence, you can increment this by increasing the range that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop iterates over.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;training_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;w1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eql?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Within this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop, we use similar logic from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;learn()&lt;/code&gt; method. The difference is that we only have the first part of the N-gram (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w1&lt;/code&gt;). If the number of matchine entries is zero, we add a period to the sentence and return it to the prompt loop.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sentence&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If matches are found, we assign it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new_word&lt;/code&gt;. Then we add it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sentence&lt;/code&gt; and move it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;word&lt;/code&gt; so we can use it as our new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w1&lt;/code&gt; on the next loop iteration.&lt;/p&gt;

&lt;p&gt;That’s all there is to it. Run it by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt;-ing into your project directory and running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby ./main.rb&lt;/code&gt; from the terminal. When you recieve the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; prompt, enter a string of words. Your new chatbot should reply back with some garbage. Talk with it some more, the more input you provide, the faster it can learn and respond with some more intelligent-sounding english.&lt;/p&gt;

&lt;p&gt;To quit, just type “exit” at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; prompt. Note that learning data is stored in memory. This means that when you quit, all training data is lost. When you run the program again, you are starting with a fresh slate. For more permanent storage, take a look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg&lt;/code&gt; ruby gem paired with the PostgreSQL database service.&lt;/p&gt;
</description>
        <pubDate>Sat, 23 Sep 2017 13:00:00 -0400</pubDate>
        <link>https://armmaster17.github.io/2017/09/23/markov-chain-tutorial/</link>
        <guid isPermaLink="true">https://armmaster17.github.io/2017/09/23/markov-chain-tutorial/</guid>
        
        
        <category>markov</category>
        
        <category>chain</category>
        
        <category>ai</category>
        
        <category>chatbot</category>
        
        <category>jeffbot</category>
        
        <category>tutorial</category>
        
      </item>
    
      <item>
        <title>Getting Started With Disco MapReduce</title>
        <description>&lt;p&gt;A while back, I wanted to learn how to use Hadoop. I prefer to self-host my stuff as it’s free (as in pizza), and free (as in speech).&lt;/p&gt;

&lt;p&gt;I almost immediately ran into issues getting it set up. First off, it turns out Hadoop uses Java, which I hate. It also has insane memory requirements, which my homelab couldn’t handle. To top it off, the installation docs were too confusing for something that I would use for 10 minutes and then never touch again.&lt;/p&gt;

&lt;p&gt;Then I found &lt;a href=&quot;http://discoproject.org&quot;&gt;Disco&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unlike Hadoop, Disco has libraries for use in Python, which I find slightly more acceptable than Java. It also has an extremely small footprint. I was able to run a testing worker with 256mb of RAM, 1 CPU, and 1 GB of hard drive space (including all of its dependencies).&lt;/p&gt;

&lt;p&gt;The one remaining problem, is that following the installation instructions for Disco has probably been one of the most frustrating pieces of software I’ve ever had to install. You know you’ve spent too much time trying when you’re reading a 5 year old GitHub issue, tracking down an Erlang issue related to a cross-language pipe library, all just by following the “Getting Started” guide. Of course, &lt;a href=&quot;https://xkcd.com/1742/&quot;&gt;there’s an XKCD for that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This guide is written as a fool-proof guide for anyone else who wants to get up and running with Disco with minimal hassle.&lt;/p&gt;

&lt;h2 id=&quot;1-prerequisites&quot;&gt;1. Prerequisites&lt;/h2&gt;
&lt;p&gt;There are several ways of accomplishing this. I personally used my Proxmox cluster split out over 6 different machines, all behind a private subnet with a firewall. You can do whatever as long as you meet the following requirements.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;At least one machine (preferably more, dedicated boxes like docker containers or VMs)&lt;/li&gt;
  &lt;li&gt;CentOS 7 (anything else and you are on your own)&lt;/li&gt;
  &lt;li&gt;A configurable DNS server in your network (if you think you can cheat with the HOSTS file, you are in for a world of hurt)&lt;/li&gt;
  &lt;li&gt;A free afternoon (Seriously, this could take a while)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-hello-bash-my-old-friend&quot;&gt;2. Hello bash my old friend&lt;/h2&gt;
&lt;p&gt;Go ahead and turn your machines on or provision your VMs. You can do one, or two, (or twenty, as I did). Make sure all of them are running CentOS 7. The minimum specs I got away with was 1 CPU, 512mb of RAM, and 1GB of hard drive space. If you are creating multiple “workers”, each one only needs 256mb of RAM. 512mb is only needed for the “master” node. Of course, if you intend on running bigger datasets than me, you will need to increase this number.&lt;/p&gt;

&lt;p&gt;For each box you create, you will need to create a corresponding entry on your DNS server. How to do this is beyond the scope of this article and depends on what software you choose to use for this. I was able to just add the entries to my pfSense router using the configuration page. If your router can’t do this, take a look at a BIND server.&lt;/p&gt;

&lt;p&gt;Now boot all of the boxes and run the following command.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;openssh-server openssh-client openssl-devel &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh-keygen &lt;span class=&quot;nt&quot;&gt;-N&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/.ssh/id_dsa
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;service sshd restart
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This sets everything up for SSH connections between the master node and the workers.&lt;/p&gt;

&lt;h2 id=&quot;3-installing-erlang-on-the-first-try-sometimes&quot;&gt;3. Installing Erlang on the first try (sometimes)&lt;/h2&gt;

&lt;p&gt;Now we have to install Erlang. This is a pain to set up on CentOS for some reason. All the tutorials I found were either 5 years old or not for my distro. The solution I found was Kerl. Type the following commands into the terminals on all the machines.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/kerl/kerl/master/kerl
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x kerl
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This gets Kerl all set up so we can install Erlang. Before we clone and build Erlang though, we need to install some additional dependencies.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install tar &lt;/span&gt;gcc make perl ncurses-devel git &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we can install Kerl. Run the following commands.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./kerl build 17.5 17.5
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./kerl &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;17.5 erlang/17_5/
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /root/erlang/17_5/activate
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;erl &lt;span class=&quot;nt&quot;&gt;-version&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If that works without any errors, you are now good to go. Now we can install Disco.&lt;/p&gt;

&lt;h2 id=&quot;4-time-for-the-actual-installation&quot;&gt;4. Time for the (actual) installation&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git clone git://github.com/discoproject/disco.git
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;disco
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At this point, what you need to do next depends on the type of installation you are performing. If you are installing the master node (or doing a single-machine install), run this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Otherwise, if you are creating a worker node that doesn’t need the web GUI, run this instead:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make install-node
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, on both the master and worker nodes, set up the python libraries.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;lib
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;python setup.py &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;5-developing-trust-in-your-cluster-using-math-and-crypto-stuff&quot;&gt;5. Developing trust in your cluster (using math and crypto-stuff)&lt;/h2&gt;

&lt;p&gt;The last part is to set up the Erlang cookie and SSH keys. On the master server, run the disco service and stop it by running the following.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;bin/disco start
// Wait a few seconds...
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;bin/disco stop
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now run the following on your master node. Replace NODE with the DNS-resolvable name of each of your worker boxes.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh-copy-id localhost
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp ~/.erlang.cookie localhost:
// Now run these two commands &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;every worker box you have,
// replacing NODE with the DNS resolvable name.
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh-copy-id NODE
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp ~/.erlang.cookie NODE:
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, run the following on all the nodes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;bin/disco nodaemon
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At this point, you should be able to follow the instructions in the documentation on &lt;a href=&quot;http://disco.readthedocs.io/en/develop/start/install.html#add-nodes-to-disco&quot;&gt;adding all of your nodes to the web GUI&lt;/a&gt; and running the &lt;a href=&quot;http://disco.readthedocs.io/en/develop/start/install.html#test-the-system&quot;&gt;test wordcount program&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Apr 2017 13:00:00 -0400</pubDate>
        <link>https://armmaster17.github.io/2017/04/27/getting-started-with-disco-mapreduce/</link>
        <guid isPermaLink="true">https://armmaster17.github.io/2017/04/27/getting-started-with-disco-mapreduce/</guid>
        
        
        <category>disco</category>
        
        <category>tutorial</category>
        
      </item>
    
  </channel>
</rss>
