<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://gscho.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://gscho.github.io/" rel="alternate" type="text/html" /><updated>2024-06-19T18:29:15+00:00</updated><id>https://gscho.github.io/feed.xml</id><title type="html">DevOps, Cloud and Automation</title><subtitle>Hi, I'm Greg Schofield. This is my blog where I cover DevOps, Cloud and Automation.  I'm a Software Developer and DevOps Engineer with a passion for automation and cloud technologies.  I'm always learning and sharing my knowledge with others.  I hope you find something useful.
</subtitle><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><entry><title type="html">Configuring Harbor With Dex OIDC</title><link href="https://gscho.github.io/devops/docker/2023/08/30/harbor_with_dex_oidc.html" rel="alternate" type="text/html" title="Configuring Harbor With Dex OIDC" /><published>2023-08-30T00:00:00+00:00</published><updated>2023-08-30T00:00:00+00:00</updated><id>https://gscho.github.io/devops/docker/2023/08/30/harbor_with_dex_oidc</id><content type="html" xml:base="https://gscho.github.io/devops/docker/2023/08/30/harbor_with_dex_oidc.html"><![CDATA[<h1 id="configuring-harbor-with-dex-oidc">Configuring Harbor With Dex OIDC</h1>

<p>Harbor supports OIDC authentication with a few providers out of the box. In this example, we’re configuring GitHub as the auth provider which means we need Dex.</p>

<p>Dex is an OAuth provider that you’re app can integrate with but in this case we just want to deploy it as a server on kubernetes.</p>

<h2 id="configuring-dex-for-github-auth">Configuring Dex for GitHub Auth</h2>

<p>Treat the dex configuration file as a secret because it has GitHub OAuth settings.</p>

<p>You must replace:</p>

<ul>
  <li>
    <issuerUrl> - Example: https://dex.example.com
</issuerUrl>
  </li>
  <li>
    <ghClientID> - The GitHub client ID you get when creating an OAuth application in GitHub
</ghClientID>
  </li>
  <li>
    <ghClientSecret> - The GitHub client secret you get when creating an OAuth application in GitHub
</ghClientSecret>
  </li>
  <li>
    <ghOrg> - The GitHub organization users need to belong to in order to log in
</ghOrg>
  </li>
  <li>
    <harborClientID> - Your made-up clientID for harbor
</harborClientID>
  </li>
  <li>
    <harborClientSecret> - Your made-up clientSecret for harbor
</harborClientSecret>
  </li>
  <li>
    <harborUrl> - Example: https://core.harbor.example.com

</harborUrl>
  </li>
</ul>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">issuer</span><span class="pi">:</span> <span class="s2">"</span><span class="s">&lt;issuerUrl&gt;"</span>
<span class="na">storage</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">memory</span>
<span class="na">connectors</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">github</span>
  <span class="na">id</span><span class="pi">:</span> <span class="s">github</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">GitHub</span>
  <span class="na">config</span><span class="pi">:</span>
    <span class="na">clientID</span><span class="pi">:</span> <span class="s">&lt;ghClientID&gt;</span>
    <span class="na">clientSecret</span><span class="pi">:</span> <span class="s">&lt;ghClientSecret&gt;</span>
    <span class="na">redirectURI</span><span class="pi">:</span> <span class="s2">"</span><span class="s">&lt;issuerUrl&gt;/callback"</span>
    <span class="na">orgs</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">&lt;ghOrg&gt;</span>
<span class="na">staticClients</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">harbor</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">&lt;harborClientID&gt;</span>
  <span class="na">secret</span><span class="pi">:</span> <span class="s">&lt;harborClientSecret&gt;</span>
  <span class="na">redirectURIs</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s2">"</span><span class="s">&lt;harborUrl&gt;/c/oidc/callback"</span>
</code></pre></div></div>

<h2 id="deploying-dex-on-k8s">Deploying Dex on K8s</h2>

<p>This <code class="language-plaintext highlighter-rouge">yaml</code> example assumes that you created a kubernetes secret named <code class="language-plaintext highlighter-rouge">dex-config</code> with the key <code class="language-plaintext highlighter-rouge">config.yaml</code> containing the config file from above.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">dex</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">dex</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">dex</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">dex</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">dex</span>
          <span class="na">image</span><span class="pi">:</span> <span class="s">ghcr.io/dexidp/dex:v2.37.0</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">IfNotPresent</span>
          <span class="na">args</span><span class="pi">:</span> 
            <span class="pi">-</span> <span class="s">dex</span>
            <span class="pi">-</span> <span class="s">serve</span>
            <span class="pi">-</span> <span class="s">--web-http-addr</span>
            <span class="pi">-</span> <span class="s">0.0.0.0:5556</span>
            <span class="pi">-</span> <span class="s">--telemetry-addr</span>
            <span class="pi">-</span> <span class="s">0.0.0.0:5558</span>
            <span class="pi">-</span> <span class="s">/etc/dex/config.yaml</span>
          <span class="na">ports</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">http</span>
              <span class="na">containerPort</span><span class="pi">:</span> <span class="m">5556</span>
              <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">telemetry</span>
              <span class="na">containerPort</span><span class="pi">:</span> <span class="m">5558</span>
              <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
          <span class="na">livenessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/healthz/live</span>
              <span class="na">port</span><span class="pi">:</span> <span class="s">telemetry</span>
          <span class="na">readinessProbe</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/healthz/ready</span>
              <span class="na">port</span><span class="pi">:</span> <span class="s">telemetry</span>
          <span class="na">volumeMounts</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">dex-config</span>
              <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/etc/dex</span>
      <span class="na">volumes</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">dex-config</span>
          <span class="na">secret</span><span class="pi">:</span>
            <span class="na">secretName</span><span class="pi">:</span> <span class="s">dex-config</span>
</code></pre></div></div>

<h2 id="configuring-harbor-to-use-dex-oidc">Configuring Harbor to use Dex OIDC</h2>

<p>Finally, we need to tell harbor to use OIDC auth and point it to our Dex server endpoint.</p>

<p>Log in as an admin user and then click on “Configuration”.</p>

<p>Set the following:</p>

<ul>
  <li>OIDC Provider Name: <code class="language-plaintext highlighter-rouge">github</code></li>
  <li>OIDC Endpoint: <code class="language-plaintext highlighter-rouge">&lt;issuerUrl&gt;</code> from above</li>
  <li>OIDC Client ID: <code class="language-plaintext highlighter-rouge">&lt;harborClientID&gt;</code> from above</li>
  <li>OIDC Client Secret: <code class="language-plaintext highlighter-rouge">&lt;harborClientSecret&gt;</code> from above</li>
  <li>OIDC Scope: <code class="language-plaintext highlighter-rouge">openid,profile,email</code></li>
</ul>]]></content><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><category term="devops" /><category term="docker" /><summary type="html"><![CDATA[Configuring Harbor With Dex OIDC]]></summary></entry><entry><title type="html">Scheduled Scaling with Autoscaling Groups</title><link href="https://gscho.github.io/devops/aws/2023/08/14/scheduled_autoscaling.html" rel="alternate" type="text/html" title="Scheduled Scaling with Autoscaling Groups" /><published>2023-08-14T00:00:00+00:00</published><updated>2023-08-14T00:00:00+00:00</updated><id>https://gscho.github.io/devops/aws/2023/08/14/scheduled_autoscaling</id><content type="html" xml:base="https://gscho.github.io/devops/aws/2023/08/14/scheduled_autoscaling.html"><![CDATA[<h1 id="scheduled-scaling-with-autoscaling-groups">Scheduled Scaling with Autoscaling Groups</h1>

<p>Sometimes we want to scale up and scale down the desired capacity of an autoscaling group based on a schedule.</p>

<p>For example:</p>

<ul>
  <li>Work hours - evenings and weekends are not busy</li>
  <li>Business reasons - tax season, gift-giving season, etc.</li>
</ul>

<p>To do this, Amazon offers <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-scheduled-scaling.html">scheduled scaling</a></p>

<p>Terraform supports creating scaling schedules using the <code class="language-plaintext highlighter-rouge">aws_autoscaling_schedule</code> resource.</p>

<h2 id="example">Example</h2>

<p>Here are some things learned from using <code class="language-plaintext highlighter-rouge">aws_autoscaling_schedule</code>:</p>

<ul>
  <li>If you don’t set a <code class="language-plaintext highlighter-rouge">start_time</code>, the schedule will run immediately and then follow the schedule after that. Its a good idea to set the start time to the recurrence.</li>
  <li>You’ll want to ignore the changes to <code class="language-plaintext highlighter-rouge">start_time</code> otherwise terraform will want to update the resource with each <code class="language-plaintext highlighter-rouge">terraform apply</code>.</li>
  <li>Setting <code class="language-plaintext highlighter-rouge">min_size</code>, <code class="language-plaintext highlighter-rouge">max_size</code> or <code class="language-plaintext highlighter-rouge">desired_capacity</code> to -1 leaves the setting unchanged.</li>
</ul>

<p>Scheduled Scale Down</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">resource</span> <span class="s2">"aws_autoscaling_schedule"</span> <span class="s2">"scheduled_scale_down"</span> <span class="p">{</span>
  <span class="nx">scheduled_action_name</span>  <span class="p">=</span> <span class="s2">"my-cluster-scale-down"</span>
  <span class="nx">min_size</span>               <span class="p">=</span> <span class="mi">1</span>
  <span class="nx">max_size</span>               <span class="p">=</span> <span class="err">-</span><span class="mi">1</span>
  <span class="nx">desired_capacity</span>       <span class="p">=</span> <span class="mi">1</span>
  <span class="nx">start_time</span>             <span class="p">=</span> <span class="s2">"</span><span class="k">${</span><span class="kd">local</span><span class="p">.</span><span class="nx">upcoming_friday</span><span class="k">}</span><span class="s2">T23:59:00Z"</span>
  <span class="nx">recurrence</span>             <span class="p">=</span> <span class="s2">"59 23 * * FRI"</span>
  <span class="nx">time_zone</span>              <span class="p">=</span> <span class="s2">"US/Pacific"</span>
  <span class="nx">autoscaling_group_name</span> <span class="p">=</span> <span class="nx">aws_autoscaling_group</span><span class="p">.</span><span class="nx">my_cluster</span><span class="p">.</span><span class="nx">name</span>
  <span class="nx">lifecycle</span> <span class="p">{</span>
    <span class="nx">ignore_changes</span> <span class="p">=</span> <span class="p">[</span><span class="nx">start_time</span><span class="p">]</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Scheduled Scale Up</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">resource</span> <span class="s2">"aws_autoscaling_schedule"</span> <span class="s2">"scheduled_scale_up"</span> <span class="p">{</span>
  <span class="nx">scheduled_action_name</span>  <span class="p">=</span> <span class="s2">"my-cluster-scale-down"</span>
  <span class="nx">min_size</span>               <span class="p">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">my_cluster_member</span><span class="p">.</span><span class="nx">min_size</span>
  <span class="nx">max_size</span>               <span class="p">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">my_cluster_member</span><span class="p">.</span><span class="nx">max_size</span>
  <span class="nx">desired_capacity</span>       <span class="p">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">my_cluster_member</span><span class="p">.</span><span class="nx">desired_capacity</span>
  <span class="nx">start_time</span>             <span class="p">=</span> <span class="s2">"</span><span class="k">${</span><span class="kd">local</span><span class="p">.</span><span class="nx">upcoming_monday</span><span class="k">}</span><span class="s2">T03:00:00Z"</span>
  <span class="nx">recurrence</span>             <span class="p">=</span> <span class="s2">"00 03 * * MON"</span>
  <span class="nx">time_zone</span>              <span class="p">=</span> <span class="s2">"US/Pacific"</span>
  <span class="nx">autoscaling_group_name</span> <span class="p">=</span> <span class="nx">aws_autoscaling_group</span><span class="p">.</span><span class="nx">my_cluster</span><span class="p">.</span><span class="nx">name</span>
  <span class="nx">lifecycle</span> <span class="p">{</span>
    <span class="nx">ignore_changes</span> <span class="p">=</span> <span class="p">[</span><span class="nx">start_time</span><span class="p">]</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><category term="devops" /><category term="aws" /><summary type="html"><![CDATA[Scheduled Scaling with Autoscaling Groups]]></summary></entry><entry><title type="html">Enabling Actions for GitHub Enterprise Server</title><link href="https://gscho.github.io/devops/2021/09/21/enable-actions-via-ghes-settings.html" rel="alternate" type="text/html" title="Enabling Actions for GitHub Enterprise Server" /><published>2021-09-21T00:00:00+00:00</published><updated>2021-09-21T00:00:00+00:00</updated><id>https://gscho.github.io/devops/2021/09/21/enable-actions-via-ghes-settings</id><content type="html" xml:base="https://gscho.github.io/devops/2021/09/21/enable-actions-via-ghes-settings.html"><![CDATA[<h1 id="enabling-actions-for-github-enterprise-server">Enabling Actions for GitHub Enterprise Server</h1>

<p>When automating a GitHub Enterprise Server deployment, the <a href="https://docs.github.com/en/enterprise-server@3.1/rest/reference/enterprise-admin#set-settings">set-settings</a> API can be used to pass a <code class="language-plaintext highlighter-rouge">settings.json</code> object to configure the server. The <code class="language-plaintext highlighter-rouge">settings.json</code> object can be retreived from the <a href="https://docs.github.com/en/enterprise-server@3.1/rest/reference/enterprise-admin#get-settings">get-settings</a> API of your enterprise server.</p>

<p>If however you want to enable actions, the administrator usually needs to do it from the administration console of the GitHub Enterprise server.</p>

<p>It turns out you can automate this too by passing some “hidden” settings. First you will need to make sure the <code class="language-plaintext highlighter-rouge">enterprise.feature_toggles.actions.enabled</code> setting is <code class="language-plaintext highlighter-rouge">true</code> and then add the <code class="language-plaintext highlighter-rouge">enterprise.actions_storage</code> settings.</p>

<p>S3 example with only the settings for enabling Actions shown:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "enterprise": {
    "feature_toggles": {
      "actions": {
        "enabled": true
      }
    },
    "actions_storage": {
      "blob_provider": "s3",
      "s3": {
        "bucket_name": "&lt;my-bucket-name&gt;",
        "service_url": "https://s3.&lt;my-aws-region&gt;.amazonaws.com",
        "access_key_id": "&lt;redacted&gt;",
        "access_secret": "&lt;redacted&gt;"
      }
    }
  },
  "run_list": [
    "recipe[enterprise-configure]"
  ]
}
</code></pre></div></div>

<p>Azure example with only the settings for enabling Actions shown:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "enterprise": {
    "feature_toggles": {
      "actions": {
        "enabled": true
      }
    },
    "actions_storage": {
      "blob_provider": "azure",
      "azure": {
        "connection_string": "&lt;my-connection-string&gt;"
      }
    }
  },
  "run_list": [
    "recipe[enterprise-configure]"
  ]
}
</code></pre></div></div>

<p><strong>Note:</strong> If you access the <a href="https://docs.github.com/en/enterprise-server@3.1/rest/reference/enterprise-admin#get-settings">get-settings</a> API after enabling actions, the storage secrets will not be returned.</p>]]></content><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><category term="devops" /><summary type="html"><![CDATA[Enabling Actions for GitHub Enterprise Server]]></summary></entry><entry><title type="html">Managing Habitat Supervisor With Runit</title><link href="https://gscho.github.io/devops/chef/2019/10/18/managing-hab-sup-with-runit.html" rel="alternate" type="text/html" title="Managing Habitat Supervisor With Runit" /><published>2019-10-18T00:00:00+00:00</published><updated>2019-10-18T00:00:00+00:00</updated><id>https://gscho.github.io/devops/chef/2019/10/18/managing-hab-sup-with-runit</id><content type="html" xml:base="https://gscho.github.io/devops/chef/2019/10/18/managing-hab-sup-with-runit.html"><![CDATA[<h1 id="managing-habitat-supervisor-with-runit">Managing Habitat Supervisor With Runit</h1>

<p>The habitat supervisor is a component of <a href="../2019-10-12-what-is-habitat">Chef Habitat</a> that will manage the lifecycle of habitat services but what manages the habitat supervisor?</p>

<p>There are a few good tutorials out there on how to do this via systemd but let’s look at doing it using <a href="http://smarden.org/runit/">runit</a>.</p>

<h2 id="what-is-runit">What is runit?</h2>

<p>Runit is a Unix init scheme that will start services on boot and keep them running. It will also constantly monitor the state of the service and restart (with a 1s cooldown) it if it fails. It’s included in many popular linux distros and is considered a light-weight alternative to systemd. If you’ve had any experience with chef-server or other chef enterprise software in the past, you might recognize it.</p>

<h2 id="getting-started">Getting started</h2>

<p>For this tutorial we’re going to be using <a href="https://www.vagrantup.com/downloads.html">vagrant</a> so make sure that’s installed first.</p>

<p>Create a directory to work out of and a <code class="language-plaintext highlighter-rouge">Vagrantfile</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mkdir ~/hab-runit
$ cd ~/hab-runit
$ touch Vagrantfile
</code></pre></div></div>

<p>The contents of your <code class="language-plaintext highlighter-rouge">Vagrantfile</code> should be the following.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Vagrant</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span><span class="s2">"2"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">box</span> <span class="o">=</span> <span class="s2">"gscho/centos-7-4-habitat"</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">box_version</span> <span class="o">=</span> <span class="s2">"0.88.0"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This vagrant box is based on centos 7.4 and has habitat 0.88.0 pre-installed. After the file has been updated you can bring up a vagrant guest and login. And switch to root.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ vagrant up
$ vagrant ssh
$ sudo su
</code></pre></div></div>

<p>The last step is installing runit since centos 7.4 does not come with it by default. <strong>You can skip this step on distros that come with runit pre-installed or included in their package repo</strong>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl -s https://packagecloud.io/install/repositories/imeyer/runit/script.rpm.sh | sudo bash
$ yum install -y runit
</code></pre></div></div>

<h2 id="creating-the-hab-sup-runit-template">Creating the hab-sup runit template</h2>

<p>To set up a runit template for the hab-sup service we create the required directory structure inside of <code class="language-plaintext highlighter-rouge">/etc/</code>. To do this we first create the <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup</code> directory and the <code class="language-plaintext highlighter-rouge">run</code> files that will instruct runit how to run the service and collect its logs.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mkdir /etc/sv
$ mkdir /etc/sv/hab-sup
$ mkdir /etc/sv/hab-sup/log
$ mkdir /etc/sv/hab-sup/env
$ touch /etc/sv/hab-sup/run
$ touch /etc/sv/hab-sup/log/run
$ chmod +x /etc/sv/hab-sup/run
$ chmod +x /etc/sv/hab-sup/log/run
</code></pre></div></div>

<p>We also need to create a directory that we can log our services output to. Let’s use <code class="language-plaintext highlighter-rouge">/var/log/hab-sup</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mkdir -p /var/log/hab-sup
</code></pre></div></div>

<h3 id="the-run-file-in-etcsvhab-suplog">The run file in <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup/log</code></h3>

<p>The run file inside of the service’s log directory will tell runit how and where to log the output of your service. For this runit comes with a configurable loggin tool called <code class="language-plaintext highlighter-rouge">svlogd</code> which handles logging output and rotating the log file once it gets too large or reaches a certain age. It is expected that the monitored service logs to stdout.</p>

<p>Our <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup/log/run</code> file will simply be this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">exec </span>svlogd <span class="nt">-tt</span> /var/log/hab-sup
</code></pre></div></div>

<p>We execute the <code class="language-plaintext highlighter-rouge">svlogd</code> command and pass it the directory we want it to log to. The <code class="language-plaintext highlighter-rouge">-tt</code> flag means it will append each log entry with a human readable timestamp.</p>

<h3 id="the-env-directory-in-etcsvhab-sup">The env directory in <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup</code></h3>

<p>The <code class="language-plaintext highlighter-rouge">env</code> directory is an optional service directory which will contain all of the environment variables you wish to set for your service. It can be anywhere on the filesystem but having it inside the <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup</code> directory might make the most sense.</p>

<p>Runit expects that the name of each file in the env directory to be the name (key) of the environment variable and the contents of the files to be the value.</p>

<p>Let’s set the <code class="language-plaintext highlighter-rouge">HAB_BLDR_URL</code> environment variable for our service.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo "https://bldr.habitat.sh" &gt;&gt; /etc/sv/hab-sup/env/HAB_BLDR_URL
</code></pre></div></div>

<h3 id="the-run-file-in-etcsvhab-sup">The run file in <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup</code></h3>

<p>Next we need to tell runit how to run the habitat supervisor process. There is another tool called <code class="language-plaintext highlighter-rouge">chpst</code> (change process state) that can be used for modifying thing like which user the service should run as or setting environment variables.</p>

<p>Our <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup/run</code> file should look like the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">exec </span>chpst <span class="nt">-e</span> /etc/sv/hab-sup/env /bin/hab sup run 2&gt;&amp;1
</code></pre></div></div>

<p>Here we execute <code class="language-plaintext highlighter-rouge">chpst</code> and pass the <code class="language-plaintext highlighter-rouge">-e</code> flag to tell it the path to our environment directory. We also redirect stderr to stdout since syslogd collects stdout only.</p>

<h2 id="running-the-service">Running the service</h2>

<p>The final step now that our service template has been created is to add a symbolic link for <code class="language-plaintext highlighter-rouge">/etc/sv/hab-sup</code> to <code class="language-plaintext highlighter-rouge">/etc/service</code>. Most distros use this directory as the service directory but you can double check using <code class="language-plaintext highlighter-rouge">ps -ef | grep runsvdir</code> to see what runit is watching. This step makes runit aware that the hab-sup service should be started and kept alive.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ln -s /etc/sv/hab-sup /etc/service/hab-sup
</code></pre></div></div>

<p>Within 5 seconds runit will recognize the change and start the service!</p>

<h3 id="controlling-the-hab-sup-process">Controlling the hab-sup process</h3>

<p>Once the service is up and running, you can use the <code class="language-plaintext highlighter-rouge">sv</code> command to control the state of the process.</p>

<p>Here are some basic commands to get your started:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">sv u hab-sup</code> - starts the service</li>
  <li><code class="language-plaintext highlighter-rouge">sv d hab-sup</code> - stops the service</li>
  <li><code class="language-plaintext highlighter-rouge">sv t hab-sup</code> - kills the service (which causes it to start up again, in essence a restart)</li>
</ul>

<h3 id="logging">Logging</h3>

<p>Logs will be output to the file <code class="language-plaintext highlighter-rouge">/var/log/hab-sup/current</code>. This file will be automatically rotated when it reaches the default size (1000000 bytes).</p>

<h2 id="credits">Credits</h2>

<p>Thanks to <a href="https://www.mikeperham.com/2014/07/07/use-runit/">Mike Perham</a> and the <a href="https://rubyists.github.io/2011/05/02/runit-for-ruby-and-everything-else.html">Rubyists</a>.</p>]]></content><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><category term="devops" /><category term="chef" /><summary type="html"><![CDATA[Managing Habitat Supervisor With Runit]]></summary></entry><entry><title type="html">What is Chef Habitat</title><link href="https://gscho.github.io/devops/chef/2019/10/12/what-is-habitat.html" rel="alternate" type="text/html" title="What is Chef Habitat" /><published>2019-10-12T00:00:00+00:00</published><updated>2019-10-12T00:00:00+00:00</updated><id>https://gscho.github.io/devops/chef/2019/10/12/what-is-habitat</id><content type="html" xml:base="https://gscho.github.io/devops/chef/2019/10/12/what-is-habitat.html"><![CDATA[<h1 id="what-is-chef-habitat">What is Chef Habitat?</h1>

<p>This is a question I get a lot from clients, meetups and conferences (even ChefConf). If you visit <a href="https://www.habitat.sh/">the habitat website</a> you’ll probably see some marketing mumbo jumbo like “Application Automation Framework” or “Automation that travels with your app”. The real answer is habitat consists of a few different components so it’s hard to categorize in a single sentence.</p>

<h2 id="habitat-is-a-cross-platform-package-manager">Habitat is a cross-platform package manager</h2>

<p>The first component of habitat is a cross-platform package manager that supports linux and windows (not darwin). This part of habitat is most closely related to the <a href="https://nixos.org/nix/">nix package manager</a> or <a href="https://chocolatey.org/">chocolatey</a> and allows you to create a package using bash or powershell. All packages are built and uploaded to an artifact repository called Builder where you can tag versions with a channel like unstable, stable or dev/qa/prod.</p>

<p><img src="/assets/images/bldr.png" alt="Builder" /></p>

<p>This alone is a pretty cool feature since it’s the only package manager that works accross both windows and linux that I have come across. It also allows you to create packages, manage them and install them <em>the same way</em> in all of your VMs and containers.</p>

<p>For example, installing and adding jq to your <code class="language-plaintext highlighter-rouge">$PATH</code> on linux, windows or inside a docker container is all the same command.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ hab pkg install core/jq-static --binlink
</code></pre></div></div>

<h2 id="habitat-is-a-service-manager">Habitat is a service manager</h2>

<p>The next component of habitat is the habitat supervisor. This component is a service manager, not unlike systemd, runit, init.d, etc. that can manage habitat services. Habitat services are also packages but they include an application that you’d like to run as a service. This could be a web server like nginx, a ruby on rails application, or just about anything else you could imagine.</p>

<p>The habitat supervisor is responsible for running these services and making sure they stay up. It also exposes an API on <code class="language-plaintext highlighter-rouge">0.0.0.0:9631</code> that lets you query for services and their health.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl localhost:9631/services/redis/prod/health
&gt; {"status":"OK","stdout":"","stderr":""}
</code></pre></div></div>

<h2 id="habitat-is-a-service-discovery-and-configuration-tool">Habitat is a service discovery and configuration tool</h2>

<p>The habitat supervisor has the ability to <code class="language-plaintext highlighter-rouge">peer</code> with other habitat supervisors to create a <a href="https://en.wikipedia.org/wiki/Gossip_protocol">GOSSIP</a> ring. Through peering supervisors, you’re able to create service groups which can share configuration with each other. For example if you have peered N supervisors each running a redis service, you can apply a configuration to all of the redis servers in your ring at once with a single command.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ hab config apply redis.prod $(date +%s) redis.toml
</code></pre></div></div>

<p>Habitat rings also open up the ability to <code class="language-plaintext highlighter-rouge">bind</code> with other service groups so you can do service discovery. For example, if you had defined a rails application that required redis, you can use the habitat bind information to discover its IP and port and automatically configure the rails app with that information.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ hab svc load gscho/my-rails-pkg --bind redis.prod
</code></pre></div></div>

<h2 id="summary">Summary</h2>

<p>The nice thing about chef habitat is that you can pick and choose which components you’d like to use. For example you could just use it as a package manager or you could use it as a services manager but use another tool like consul for service discovery.</p>]]></content><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><category term="devops" /><category term="chef" /><summary type="html"><![CDATA[What is Chef Habitat?]]></summary></entry><entry><title type="html">Creating Cloud Resources Using Fog</title><link href="https://gscho.github.io/devops/ruby/2019/07/09/creating-cloud-resources-using-fog.html" rel="alternate" type="text/html" title="Creating Cloud Resources Using Fog" /><published>2019-07-09T00:00:00+00:00</published><updated>2019-07-09T00:00:00+00:00</updated><id>https://gscho.github.io/devops/ruby/2019/07/09/creating-cloud-resources-using-fog</id><content type="html" xml:base="https://gscho.github.io/devops/ruby/2019/07/09/creating-cloud-resources-using-fog.html"><![CDATA[<h1 id="creating-cloud-resources-using-fog">Creating Cloud Resources Using Fog</h1>

<p><img src="/assets/images/fog-logo.png" alt="fog-logo" /></p>

<h2 id="what-is-fog">What is fog?</h2>

<p>Fog is a ruby cloud service library that provides a common abstraction over cloud provider SDKs. It supports a large number of cloud providers like AWS, Azure, GCP, DigialOcean, etc. albeit with varying degrees of maturity. You can find the <a href="https://fog.io/about/provider_documentation.html">whole list here</a>. The documentation is lack-luster but most fog cloud provider libraries have extensive test suites that serve as a how-to for that particular provider.</p>

<h2 id="why-fog">Why fog?</h2>

<p>So you might be asking yourself, “Doesn’t terraform do this for me?”.</p>

<p>Terraform at its heart a large diffing algorithm that maintains a snapshot of your infrastructure’s state and tries to make changes based on the current vs. desired state. It uses a configuration language developed by HashiCorp called <code class="language-plaintext highlighter-rouge">hcl</code> to create, update and manage that state.</p>

<p>Fog on the other hand is a ruby library that can be used like any other rubygem. It’s just ruby so you get all of the goodness of a dynamic, object-oriented programming language when creating cloud resources. Where fog really shines is as a cross-platform SDK that gives a consitent interface to doing common tasks like creating servers, volumes, etc. Another great feature is <code class="language-plaintext highlighter-rouge">Fog.mock</code> that lets you unit test your logic without creating any cloud resources and incurring costs.</p>

<h2 id="use-cases">Use Cases</h2>

<p>I recently choose fog for a project where I needed to create a rails app that would allow users to CRUD compute resources and security groups for AWS, Azure and GCP.</p>

<p>Fog is a good choice if:</p>

<ul>
  <li>
    <p>your team is already using ruby and you’re tasked with creating and managing your own compute and storage infrastructure.</p>
  </li>
  <li>
    <p>you want loops, branching logic (if-else), OOP design patterns, mock testing using rspec or minitest</p>
  </li>
  <li>
    <p>you need to use more than one cloud-provider sdk and want a common interface across compute and storage resources.</p>
  </li>
</ul>

<h2 id="example">Example</h2>

<p>In the example script below I’m using fog to create one ec2 instance. Once the instance is created, the instance’s ID is saved to a yaml file to make things idempotent.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># frozen_string_literal: true</span>

<span class="nb">require</span> <span class="s1">'fog/aws'</span>
<span class="nb">require</span> <span class="s1">'yaml/store'</span>

<span class="no">STORE</span> <span class="o">=</span> <span class="no">YAML</span><span class="o">::</span><span class="no">Store</span><span class="p">.</span><span class="nf">new</span> <span class="s1">'fog-aws.yml'</span>
<span class="n">instance_id</span> <span class="o">=</span> <span class="no">STORE</span><span class="p">.</span><span class="nf">transaction</span> <span class="p">{</span> <span class="no">STORE</span><span class="p">[</span><span class="s1">'instance_id'</span><span class="p">]</span> <span class="p">}</span>
<span class="n">ec2</span> <span class="o">=</span> <span class="no">Fog</span><span class="o">::</span><span class="no">Compute</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">provider: </span><span class="s1">'AWS'</span><span class="p">,</span> <span class="ss">region: </span><span class="s1">'us-east-1'</span><span class="p">)</span>

<span class="k">if</span> <span class="n">instance_id</span><span class="p">.</span><span class="nf">nil?</span>
  <span class="n">key_name</span> <span class="o">=</span> <span class="s1">'fog-test'</span>
  <span class="n">ec2</span><span class="p">.</span><span class="nf">key_pairs</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="n">key_name</span><span class="p">)</span> <span class="k">if</span> <span class="n">ec2</span><span class="p">.</span><span class="nf">key_pairs</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">key_name</span><span class="p">).</span><span class="nf">nil?</span>
  
  <span class="n">instance</span> <span class="o">=</span> <span class="n">ec2</span><span class="p">.</span><span class="nf">servers</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span>
    <span class="ss">tags: </span><span class="p">{</span> <span class="no">Name</span><span class="p">:</span> <span class="s1">'fog-ec2'</span> <span class="p">},</span>
    <span class="ss">key_name: </span><span class="n">key_name</span><span class="p">,</span>
    <span class="ss">flavor_id: </span><span class="s1">'t3.small'</span><span class="p">,</span>
    <span class="ss">image_id: </span><span class="s1">'ami-087c17d1fe0178315'</span>
  <span class="p">)</span>
  <span class="nb">puts</span> <span class="s1">'Instance is creating'</span>
  <span class="no">STORE</span><span class="p">.</span><span class="nf">transaction</span> <span class="p">{</span> <span class="no">STORE</span><span class="p">[</span><span class="s1">'instance_id'</span><span class="p">]</span> <span class="o">=</span> <span class="n">instance</span><span class="p">.</span><span class="nf">id</span> <span class="p">}</span>
  <span class="n">instance</span><span class="p">.</span><span class="nf">wait_for</span> <span class="p">{</span> <span class="n">ready?</span> <span class="p">}</span>
  <span class="nb">puts</span> <span class="s1">'Instance is ready!'</span>
<span class="k">else</span>
  <span class="nb">puts</span> <span class="s1">'Instance already exists'</span>
  <span class="n">instance</span> <span class="o">=</span> <span class="n">ec2</span><span class="p">.</span><span class="nf">servers</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">instance_id</span><span class="p">)</span>
<span class="k">end</span>

<span class="nb">puts</span> <span class="o">&lt;&lt;-</span><span class="no">EOF</span><span class="sh">
public_dns_name = http://</span><span class="si">#{</span><span class="n">instance</span><span class="p">.</span><span class="nf">dns_name</span><span class="si">}</span><span class="sh">
public_ip_address = http://</span><span class="si">#{</span><span class="n">instance</span><span class="p">.</span><span class="nf">public_ip_address</span><span class="si">}</span><span class="sh">
private_dns_name = http://</span><span class="si">#{</span><span class="n">instance</span><span class="p">.</span><span class="nf">private_dns_name</span><span class="si">}</span><span class="sh">
private_ip_address = http://</span><span class="si">#{</span><span class="n">instance</span><span class="p">.</span><span class="nf">private_ip_address</span><span class="si">}</span><span class="sh">
</span><span class="no">EOF</span>

</code></pre></div></div>]]></content><author><name>Greg Schofield</name><email>greg.c.schofield@gmail.com</email></author><category term="devops" /><category term="ruby" /><summary type="html"><![CDATA[Creating Cloud Resources Using Fog]]></summary></entry></feed>