<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Agile Web Operations</title>
	<atom:link href="https://agileweboperations.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://agileweboperations.com</link>
	<description>Technical Leadership since 2008</description>
	<lastBuildDate>Sun, 23 Nov 2025 21:03:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>

<image>
	<url>https://agileweboperations.com/wp-content/uploads/2018/10/cropped-favicon.png?w=32</url>
	<title>Agile Web Operations</title>
	<link>https://agileweboperations.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">152654741</site><cloud domain='agileweboperations.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<atom:link rel="search" type="application/opensearchdescription+xml" href="https://agileweboperations.com/osd.xml" title="Agile Web Operations" />
	<atom:link rel='hub' href='https://agileweboperations.com/?pushpress=hub'/>
	<item>
		<title>How to run OpenCode AI in a Docker container</title>
		<link>https://agileweboperations.com/2025/11/23/how-to-run-opencode-ai-in-a-docker-container/</link>
					<comments>https://agileweboperations.com/2025/11/23/how-to-run-opencode-ai-in-a-docker-container/#comments</comments>
		
		<dc:creator><![CDATA[Matthias Marschall]]></dc:creator>
		<pubDate>Sun, 23 Nov 2025 12:36:50 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[llm]]></category>
		<category><![CDATA[opencode]]></category>
		<category><![CDATA[technology]]></category>
		<guid isPermaLink="false">http://agileweboperations.com/?p=5222</guid>

					<description><![CDATA[]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-jetpack-markdown"><p>If you want to give your coding tool maximal freedom but you fear for the safety of your computer (you should), then letting it run in a Docker container is a good idea. If your coding tool goes rogue, it only damages your Docker container, which you can restore in an instant rather than destroying your work computer.</p>
<p>I will show you how to</p>
<ul>
<li>build a Docker image based on Ubuntu with Opencode and necessary tools installed and configured</li>
<li>authenticate your LLM for Opencode and use this auth in your Docker container</li>
<li>mount your local Opencode config files into your container (agents, plugins, …)</li>
<li>bundle all of that in a bash script for quick access</li>
</ul>
<p>I assume you have <a href="https://docs.docker.com/desktop/">Docker Desktop</a> installed and running. I also assume you are working on a coding project and have it checked out locally using git.</p>
<h1>Create a Dockerfile in your coding project</h1>
<p>First, you need to create a Dockerfile describing your container.</p>
<p>Let’s walk through it step-by-step.</p>
<pre><code># Start from the latest official Ubuntu release
FROM ubuntu:latest

# Set common environment variables
ENV DEBIAN_FRONTEND=noninteractive

# Install core tools
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y --no-install-recommends \
    # Core utilities needed for setup and the curl install script
    wget \
    vim \
    ca-certificates \
    curl \
    unzip \
    gnupg \
    sudo
</code></pre>
<p>We need to be able to interact with GitHub:</p>
<pre><code># Install GitHub CLI (gh)
 RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg \
    &amp;amp;&amp;amp; echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main&quot; | tee /etc/apt/sources.list.d/github-cli.list &amp;gt; /dev/null \
    &amp;amp;&amp;amp; apt-get update \
    &amp;amp;&amp;amp; apt-get install -y gh \
    # Clean up APT lists
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*
</code></pre>
<p>Time to set up the local user in our container and enable it to connect to the GitHub servers:</p>
<pre><code># Add the 'ubuntu' user to the sudo group and allow passwordless sudo
RUN usermod -aG sudo ubuntu \
    &amp;amp;&amp;amp; echo &quot;ubuntu ALL=(ALL) NOPASSWD: ALL&quot; &amp;gt; /etc/sudoers.d/ubuntu \
    &amp;amp;&amp;amp; chmod 0440 /etc/sudoers.d/ubuntu

# Set the existing non-root 'ubuntu' user as the default user
USER ubuntu

# Set the working directory to the user's home folder
WORKDIR /home/ubuntu

# 1. Create the .ssh directory
RUN mkdir -p /home/ubuntu/.ssh

# 2. Add GitHub to the known_hosts file so git commands work non-interactively
# We use ssh-keyscan to fetch GitHub's public key and save it.
RUN ssh-keyscan github.com &amp;gt;&amp;gt; /home/ubuntu/.ssh/known_hosts
</code></pre>
<p>Next, we enable OpenCode to use the local config you have on your work computer inside the container. This will give it all the agents, plugins, etc you have configured locally:</p>
<pre><code># Create the directory structure for the auth file and fix ownership
# This prevents Docker from creating it as 'root' when the volume is mounted.
# This is important to be able to mount the OpenCode auth on docker run
#    -v ~/.local/share/opencode/auth.json:/home/ubuntu/.local/share/opencode/auth.json
RUN mkdir -p /home/ubuntu/.local/share/opencode \
    &amp;amp;&amp;amp; chown -R ubuntu:ubuntu /home/ubuntu/.local/share/opencode
  
RUN mkdir -p /home/ubuntu/.config/opencode \
    &amp;amp;&amp;amp; chown -R ubuntu:ubuntu /home/ubuntu/.config/opencode
</code></pre>
<p>There are multiple ways to get your development project into your container: copy it in, git pull it or link it from your local box into the container. I show the copy method here. You can use <code>.</code> in the copy command as the <code>Dockerfile</code> lives in your project root. Replace &#8220; below.</p>
<pre><code># copy the project dir into the VM    
COPY --chown=ubuntu:ubuntu . /home/ubuntu/
</code></pre>
<p>And last but not least, we install OpenCode AI:</p>
<pre><code># Install OpenCode AI (Native Binary Method
# https://opencode.ai/docs/
RUN curl -fsSL https://opencode.ai/install | bash
</code></pre>
<h1>Build and run the Docker container</h1>
<p>Simply run <code>docker build -t ubuntu-opencode .</code> in your project root to build your container.</p>
<p>To be able to run it, you need to have a few things set up:</p>
<ol>
<li>You need a GitHub token in your environment (e.g. <code>export GH_TOKEN=</code>). You generate that in the <a href="https://github.com/settings/personal-access-tokens">Developer Settings on GitHub</a>.</li>
<li>You need your SSH key to be able to mount it into your container. Mine is <code>id_ed25519</code></li>
<li>You need to have your local OpenCode authenticated for the model you want to use by using <code>opencode auth login</code>. This will generate the <code>$HOME/.local/share/opencode/auth.json</code> file, which we mount into our container as well.</li>
</ol>
<p>Instead of <code>opencode-1</code> you can use any name for the instance you want to spin up. <code>-h</code> sets it as the hostname so that your bash prompts will show it.
Replace &#8220; below.</p>
<pre><code>docker run -dit --name opencode-1 \
    -h opencode-1 \
    -e GH_TOKEN \
    -w /home/ubuntu/ \
    -v &quot;$HOME/.ssh/id_ed25519&quot;:/home/ubuntu/.ssh/id_ed25519:ro \
    -v &quot;$HOME/.config/opencode&quot;:/home/ubuntu/.config/opencode \
    -v &quot;$HOME/.local/share/opencode/auth.json&quot;:/home/ubuntu/.local/share/opencode/auth.json \
    ubuntu-opencode /bin/bash
</code></pre>
<h1>Bundle Docker handling in a handy shell script</h1>
<p>You need to change &#8220; in the script below.</p>
<p>Create a file called <code>run-opencode-docker.sh</code> in your project dir.
<code>chmod a+x run-opencode-docker.sh</code> to make it executable and run either directly or with the parameter <code>rebuild</code> if you want to re-create the container after changing your Dockerfile.</p>
<pre><code>#!/bin/bash

# Configuration
CONTAINER_NAME=&quot;opencode-1&quot;
IMAGE_NAME=&quot;ubuntu-opencode&quot;
DOCKERFILE=&quot;Dockerfile&quot;

PROJECT_DIR=&quot;&quot;

LOCAL_AUTH_FILE=&quot;$HOME/.local/share/opencode/auth.json&quot;

# Function to handle building
build_image() {
    echo &quot;&#x1f528; Building image from $DOCKERFILE...&quot;
    
    if [ -z &quot;$GH_TOKEN&quot; ]; then
        echo &quot;&#x274c; Error: GH_TOKEN is not set. Cannot build.&quot;
        exit 1
    fi

    docker build -t $IMAGE_NAME -f $DOCKERFILE .
    
    if [ $? -ne 0 ]; then
        echo &quot;&#x274c; Docker build failed. Exiting.&quot;
        exit 1
    fi
    echo &quot;&#x2705; Build successful.&quot;
}

# ---------------------------------------------------------
# 0. Check for &quot;rebuild&quot; parameter
# ---------------------------------------------------------
if [ &quot;$1&quot; == &quot;rebuild&quot; ]; then
    echo &quot;Force rebuild requested...&quot;
    build_image
    exit 0
fi

# ---------------------------------------------------------
# 1. Check if Container Exists (Running or Stopped)
# ---------------------------------------------------------
if [ &quot;$(docker ps -q -f name=$CONTAINER_NAME)&quot; ]; then
    echo &quot;&#x1f504; Container '$CONTAINER_NAME' is already running.&quot;
    echo &quot;&#x1f517; Connecting to OpenCode...&quot;
    exec docker exec -it -w /home/ubuntu/$PROJECT_DIR $CONTAINER_NAME /bin/bash -c &quot;/home/ubuntu/.opencode/bin/opencode .&quot;
elif [ &quot;$(docker ps -aq -f name=$CONTAINER_NAME)&quot; ]; then
    echo &quot;&#x1f504; Container '$CONTAINER_NAME' exists but is stopped.&quot;
    echo &quot;&#x1f5d1;  Removing old container...&quot;
    docker rm $CONTAINER_NAME
fi

# ---------------------------------------------------------
# 4. Auto-Build Image (if missing)
# ---------------------------------------------------------
if [[ &quot;$(docker images -q $IMAGE_NAME 2&amp;gt; /dev/null)&quot; == &quot;&quot; ]]; then
    echo &quot;&#x26a0;  Image '$IMAGE_NAME' not found locally.&quot;
    build_image
fi

# ---------------------------------------------------------
# 5. Run New Container
# ---------------------------------------------------------
echo &quot;&#x1f680; Starting OpenCode...&quot;

docker run -dit --name $CONTAINER_NAME \
    -h $CONTAINER_NAME \
    -e GH_TOKEN \
    -w /home/ubuntu/$PROJECT_DIR \
    -v &quot;$HOME/.ssh/id_ed25519&quot;:/home/ubuntu/.ssh/id_ed25519:ro \
    -v &quot;$HOME/.config/opencode&quot;:/home/ubuntu/.config/opencode \
    -v &quot;$LOCAL_AUTH_FILE&quot;:/home/ubuntu/.local/share/opencode/auth.json \
    $IMAGE_NAME /bin/bash

echo &quot;&#x1f517; Connecting to OpenCode...&quot;
exec docker exec -it -w /home/ubuntu/$PROJECT_DIR $CONTAINER_NAME /bin/bash -c &quot;/home/ubuntu/.opencode/bin/opencode .&quot;
</code></pre>
<p>Now you can run OpenCode on your project inside of your Docker container without risking the AI bricking your work computer.
Make sure to always <code>git pull</code> when you start working and <code>git push</code> before you stop the container. You can instruct OpenCode to do this for you by putting this as instructions into your <code>AGENTS.md</code> file.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2025/11/23/how-to-run-opencode-ai-in-a-docker-container/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5222</post-id>
		<media:content url="https://0.gravatar.com/avatar/6c307c54b3b17168bd3a2e22d47584c5d1e76a57bb4a4a0828a2a3f1d1345661?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">webops</media:title>
		</media:content>
	</item>
		<item>
		<title>Managing Github Secrets with Vault</title>
		<link>https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/</link>
					<comments>https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/#respond</comments>
		
		<dc:creator><![CDATA[Dan Ackerson]]></dc:creator>
		<pubDate>Thu, 04 Aug 2022 08:39:32 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Tools & Technology]]></category>
		<category><![CDATA[automated infrastructure]]></category>
		<category><![CDATA[continuous integration]]></category>
		<guid isPermaLink="false">http://agileweboperations.com/?p=5207</guid>

					<description><![CDATA[I&#8217;m a huge fan of GitHub actions and organization level secrets, but you can&#8217;t see what the current secret is w/i GitHub (w/o deploying a container somewhere with it exported as an ENV var). I tried to keep a copy in my former secret management tool clipperz.is/app but drift is real. It was always too &#8230; <a href="https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/" class="more-link">Continue reading <span class="screen-reader-text">Managing Github Secrets with&#160;Vault</span></a>]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-group is-content-justification-center is-nowrap is-layout-flex wp-container-core-group-is-layout-94bc23d7 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-large is-resized"><img data-attachment-id="5209" data-permalink="https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/vault/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2022/08/vault.png" data-orig-size="678,677" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="vault" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=678" alt="" class="wp-image-5209" width="185" height="185" srcset="https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=185 185w, https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=370 370w, https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=300 300w" sizes="(max-width: 185px) 100vw, 185px" /></figure>



<figure class="wp-block-image size-large is-resized"><img data-attachment-id="5211" data-permalink="https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/github-logo/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg" data-orig-size="2000,1156" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="github-logo" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=656" src="https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=1024" alt="" class="wp-image-5211" width="163" height="93" srcset="https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=163 163w, https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=326 326w, https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=150 150w, https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=300 300w" sizes="(max-width: 163px) 100vw, 163px" /></figure>
</div>



<p class="wp-block-paragraph">I&#8217;m a huge fan of GitHub actions and organization level secrets, but you <strong>can&#8217;t</strong> <strong>see</strong> what the current secret is w/i GitHub (w/o deploying a container somewhere with it exported as an ENV var). I tried to keep a copy in my former secret management tool <a href="https://clipperz.is/app/">clipperz.is/app</a> but drift is real. It was always too scary to change a secret; I didn&#8217;t really know where it was being used and, thus, which applications I should redeploy to ensure proper operations.</p>



<p class="wp-block-paragraph">This pain of manual &#8220;secret management&#8221; (syncing between clipperz and GitHub) + the stagnant codebase of clipperz (<a href="https://github.com/clipperz/password-manager/commit/eb45334efecb66202898a216fb02800008a1d636">last commit was Oct 2019</a>),  made me realize it was time to take control of the situation. So, I started looking for &#8220;off-the-shelf&#8221; services. </p>



<p class="wp-block-paragraph"><a href="https://1password.com/products/secrets/">1Password Secrets Automation</a> ($29 / month) + <a href="https://www.keepersecurity.com/secrets-manager.html">Keeper Secrets Manager</a> ($10 / month) are probably highly architected and professionally maintained tools, but should I really give up Netflix and/or Amazon Prime to migrate to one of them for my personal projects? And even if I did this, it doesn&#8217;t mean that 5 years from now when they double their prices (or go bankrupt and disappear), I&#8217;m stuck yet again trying to figure out how best to manage my secrets.</p>



<p class="wp-block-paragraph">Instead, I spent the last 9 months learning <strong>a lot</strong> about running my own Vault instance, setting up a few API tokens and automating the whole secret management flow. It all starts from this <a href="https://github.com/ackersonde/pi-ops/blob/master/scripts/crontab.txt#L8">crontab entry</a>:</p>



<p class="wp-block-paragraph"><code>32 * * * * /home/ubuntu/my-ca/update_github_secrets.sh</code></p>



<p class="wp-block-paragraph">After ingesting the requisite environment variables, it calls <a href="https://github.com/ackersonde/pi-ops/blob/master/scripts/secrets/vault_update_secrets.py">vault_update_secrets.py</a> which initially gets the last-modified dates of all my Vault application secrets. Then it goes over to Github and grabs the same data in order to compare which secrets are out-of-sync (meaning updated in Vault where I now manage my secrets).</p>



<p class="wp-block-paragraph">Updated (or created) secrets are highlighted via Slackbot message with a super helpful pointer to which repos use those secrets (and may need to be redeployed) via e.g. <a href="https://api.github.com/search/code?q=org%3Aackersonde+ORG_CLOUD_SYNCTHING_KEY&amp;type=Code">https://api.github.com/search/code?q=org%3Aackersonde+{secret_name}&amp;type=Code</a>. Here&#8217;s a diagram to better explain what&#8217;s going on:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img width="638" height="734" data-attachment-id="5214" data-permalink="https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/screenshot-2022-08-03-at-09-52-35/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png" data-orig-size="638,734" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="screenshot-2022-08-03-at-09.52.35" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png?w=261" data-large-file="https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png?w=638" src="https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png?w=638" alt="" class="wp-image-5214" srcset="https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png 638w, https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png?w=130 130w, https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png?w=261 261w" sizes="(max-width: 638px) 100vw, 638px" /></figure>
</div>


<p class="wp-block-paragraph">Vault manages secret versions OOTB so I can rollback w/ confidence if something breaks. The automatic sync is a lifesaver and I no longer stress about rolling over &amp; updating my secrets on a regular basis. The next, logical automation step is auto-deploying the repos identified to be using updated secrets.</p>



<p class="wp-block-paragraph"> If you want to learn more about running your own Vault server, just lemme know and we can chat. If there&#8217;s enough interest, I&#8217;ll write a separate post on what I&#8217;ve learned in the past year.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2022/08/04/managing-github-secrets-with-vault/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5207</post-id>
		<media:content url="https://2.gravatar.com/avatar/bb9ac0ea0110eb7b8b81350a7032353ea55d6effe88ad7e7a20552072197579d?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ackerson</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2022/08/vault.png?w=678" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2022/08/github-logo.jpg?w=1024" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2022/08/screenshot-2022-08-03-at-09.52.35.png?w=638" medium="image" />
	</item>
		<item>
		<title>Secure GitHub Deployments to Your Home</title>
		<link>https://agileweboperations.com/2020/11/29/secure-github-deployments-to-your-home/</link>
					<comments>https://agileweboperations.com/2020/11/29/secure-github-deployments-to-your-home/#respond</comments>
		
		<dc:creator><![CDATA[Dan Ackerson]]></dc:creator>
		<pubDate>Sun, 29 Nov 2020 15:22:59 +0000</pubDate>
				<category><![CDATA[Tools & Technology]]></category>
		<category><![CDATA[automated infrastructure]]></category>
		<category><![CDATA[continuous deployment]]></category>
		<category><![CDATA[k3s]]></category>
		<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[pi]]></category>
		<category><![CDATA[vpn]]></category>
		<category><![CDATA[wireguard]]></category>
		<guid isPermaLink="false">http://agileweboperations.com/?p=5150</guid>

					<description><![CDATA[I&#8217;m a cheap bastard when it comes to online services. All my repositories have been publicly hosted on GitHub well before Microsoft swooped in and offered unlimited private repos. Since around 2016, this had been my build/deploy pipeline: You might ask how could that ssh key be comprised. Well, back in the day, CircleCI would &#8230; <a href="https://agileweboperations.com/2020/11/29/secure-github-deployments-to-your-home/" class="more-link">Continue reading <span class="screen-reader-text">Secure GitHub Deployments to Your&#160;Home</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I&#8217;m a cheap bastard when it comes to online services. All my repositories have been publicly hosted on GitHub well before Microsoft swooped in and offered unlimited private repos.  </p>



<p class="wp-block-paragraph">Since around 2016, this had been my build/deploy pipeline:</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="724" height="208" data-attachment-id="5171" data-permalink="https://agileweboperations.com/image-3/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png" data-orig-size="724,208" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-3" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png?w=724" alt="" class="wp-image-5171" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png 724w, https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png?w=300 300w" sizes="(max-width: 724px) 100vw, 724px" /></figure>



<p class="wp-block-paragraph">You might ask how could that ssh key be comprised. Well, back in the day, CircleCI would happily echo anything you (stupidly) told it right into the publicly available build log including secrets. Yeah, fun times&#8230;</p>



<p class="wp-block-paragraph">Last summer when the new Raspberry Pi 4 was released, my scrooge senses went into overdrive realizing I could recoup the $5 month cost for my Digital Ocean droplet in less than a year and a half. </p>



<p class="wp-block-paragraph">What I didn&#8217;t consider was that it would also take me a year and a half to figure out how-to securely &amp; reliably connect my well established CI/CD pipeline to my home.</p>



<figure class="wp-block-image size-large is-style-rounded"><img loading="lazy" width="1024" height="870" data-attachment-id="5157" data-permalink="https://agileweboperations.com/2020/11/29/secure-github-deployments-to-your-home/pxl_20201127_132239976/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg" data-orig-size="1432,1217" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="PXL_20201127_132239976" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=1024" alt="pixel4Cluster" class="wp-image-5157" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=1024 1024w, https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=300 300w, https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=768 768w, https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg 1432w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>&#8220;PHENOMENAL COSMIC POWER&#8230;itty bitty living space!&#8221; My pi4, k3s cluster w/ 2TB media drive</figcaption></figure>



<p class="wp-block-paragraph">I wanted to share how I architected my working setup this summer, but that led me to think even harder about security. How secure was my VPN/ssh deploy from CircleCI? Exposing your home network on the internet is one thing. Bragging about it, quite another. I needed to do an honest assessment and go the extra mile.</p>



<h2 class="wp-block-heading">Secrets</h2>



<p class="wp-block-paragraph">First things first &#8211; I am ruthless about stripping out secrets or any hardcoded sensitive data from my source code. I was always a big fan of using CircleCI secrets and greatly benefited from their shift to Organization wide ones (e.g. not having to copy/paste the same keys into <strong>every</strong> repository). GitHub employs a mirror copy of this setup. </p>



<p class="wp-block-paragraph">Since I push all my work to public repositories, I extract all secrets and re-inject at build time. It&#8217;s definitely extra effort, but I&#8217;m happy (and proud) to share my work online, to inspire others and give back to the amazing community of opensource developers.</p>



<p class="wp-block-paragraph">Using secrets is good coding practice &#8211; not only is it more secure, but it gives you flexibility for deployments in the long term.</p>



<h2 class="wp-block-heading">VPN</h2>



<p class="wp-block-paragraph">For my deploys from CircleCI, I was connecting via some kind of Cisco IPSec on my router that:</p>



<ul class="wp-block-list"><li>the manufacture provided no versioning information or update cycle for &#8211; basically, it was none of my business</li><li>the credentials were difficult to create on the router via a script so rotating these would be painful &#8211; hurdles to automation suck</li></ul>



<figure class="wp-block-image size-large"><img loading="lazy" width="884" height="324" data-attachment-id="5179" data-permalink="https://agileweboperations.com/image-7/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png" data-orig-size="884,324" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-7" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=884" alt="" class="wp-image-5179" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png 884w, https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=300 300w, https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=768 768w" sizes="(max-width: 884px) 100vw, 884px" /></figure>



<p class="wp-block-paragraph">I had heard a lot about <a href="https://www.wireguard.com/">Wireguard</a> and, within an hour of installing it, had successfully connected from my mobile network into my home. Promising! Now to hook it up to CircleCI and away we&#8217;d go.</p>



<p class="wp-block-paragraph">Or so I thought. Not only did CircleCI not have the latest Ubuntu 20.04 available (which has full support for Wireguard packages by default), even after bootstrapping this OS w/i a docker container, I couldn&#8217;t get the networking right. It seemed that CircleCI was actively blocking this type of outbound VPN access from docker containers.</p>



<p class="wp-block-paragraph">As the build was already running well over 5 minutes and wasn&#8217;t even close to working, I decided to shop around with other CI/CD providers. First place I went was <a href="https://semaphoreci.com/">Semaphore CI</a>. Although I could use an off-the-shelf Ubuntu 20.04, the wireguard connection was also blocked by Semaphore.</p>



<p class="wp-block-paragraph">Slowly giving up hope, I thought about Github Actions. Now, I don&#8217;t like having all my eggs in one basket, but within a few hours of <a href="https://github.com/ackersonde/simple-wireguard-deploy/blob/master/.github/workflows/test.yml">setting up my configuration</a> (which was very comparable to CircleCI), I had a successful ssh connection running <code>ls -lrt</code> on the k3s master node sitting in my hall. I haven&#8217;t looked back since (sorry CircleCI \o we had a lot of good years together).</p>



<p class="wp-block-paragraph">Using the <a href="https://hub.docker.com/r/linuxserver/wireguard">Wireguard container from linuxserverio.io</a>, it&#8217;s a breeze to reissue client keys, and I do this on the same monthly cadence as regenerating my deployment SSH certificates:</p>



<pre class="wp-block-code"><code>sudo kubectl exec -it $(sudo kubectl get po | grep wireguard | awk '{print $1; exit}' | tr -d \\n) -- rm -Rf /config/peer_githubActions /config/wg0.conf</code></pre>



<p class="wp-block-paragraph">As you can tell, I&#8217;m running this container as a <a href="https://github.com/ackersonde/traefik2_k3s/blob/master/config/base-deploy/wireguard.yml">kubernetes service/deployment</a>. The entire client peer config is uploaded as a secret to my Github org allowing me to access it from any of <a href="https://github.com/ackersonde/ackerson.de-go/blob/master/.github/workflows/build.yml">my repositories during deployment</a>:</p>



<pre class="wp-block-code"><code>- name: Create wireguard tunnel configuration
  run: |
    sudo apt-get install wireguard resolvconf
    echo "${{ secrets.CTX_WIREGUARD_GITHUB_ACTIONS_CLIENT_CONFIG }}" | sudo tee /etc/wireguard/tunnel.conf</code></pre>



<p class="wp-block-paragraph">To save you some hair pulling, remember to uncomment <code>net.ipv4.ip_forward=1</code> in your <code>/etc/sysctl.conf</code> on any nodes you&#8217;d like to have Wireguard running.</p>



<h2 class="wp-block-heading">SSH access</h2>



<p class="wp-block-paragraph">Ok, we&#8217;ve got rotating Wireguard client creds &#8211; time to move on to the main event: ssh access to deploy your new code. Rotating keys here would mean carefully managing each node&#8217;s <code>~/.ssh/authorized_keys</code> file, but why automate yack shaving?</p>



<p class="wp-block-paragraph">Searching around the Internets led me to <a href="https://medium.com/better-programming/how-to-use-ssh-certificates-for-scalable-secure-and-more-transparent-server-access-720a87af6617">Aakash Yadav&#8217;s article about </a><strong><a href="https://medium.com/better-programming/how-to-use-ssh-certificates-for-scalable-secure-and-more-transparent-server-access-720a87af6617">SSH certificates</a> </strong>which does a great job explaining the why&#8217;s and how&#8217;s of using them for authentication. With this, I can regenerate the ssh keys used for GitHub deployments every month, create a certificate and upload these artifacts to my organization&#8217;s secrets store.</p>



<p class="wp-block-paragraph">The authentication magic happens because I created a Certificate Authority (CA) keypair, adding the public key to the bottom of every node&#8217;s <code>/etc/ssh/sshd_config</code> file with this simple line: <code>TrustedUserCAKeys /etc/ssh/github_deploy_ca.pub</code> and using the private CA key (<code><strong>ca_key</strong></code>) to sign &amp; certify any ssh keypair:</p>



<pre class="wp-block-code"><code>ssh-keygen -t ed25519 -a 100 -f $DIR/id_ed25519_github_deploy -q -N ""
ssh-keygen -s $DIR/<strong>ca_key </strong>-I github -n ubuntu,user -P "$CACERT_KEY_PASS" -V <strong>+5w</strong> -z $(date +%s) $DIR/id_ed25519_github_deploy</code></pre>



<p class="wp-block-paragraph">If these credentials are compromised, they&#8217;ll automatically be invalidated after some time period. I chose 5 weeks to ensure my 4 week update cron (shown below) is always one step ahead of the expiration.</p>



<h2 class="wp-block-heading">Automation</h2>



<pre class="wp-block-code"><code># m h <strong>dom </strong>mon dow command
50 07 <strong>02 </strong>* * /home/ubuntu/my-ca/gen_new_deploy_keys.sh &gt; /var/log/gen_new_deploy_keys.log 2&gt;&amp;1</code></pre>



<p class="wp-block-paragraph">We&#8217;ve done our homework when it comes to security, but now it&#8217;s time to put our money where are mouth is and automate it. By setting an expiration timer on the cert validity, we&#8217;ve forced ourselves to do the right thing. The cron executes <a href="https://github.com/ackersonde/traefik2_k3s/blob/master/scripts/gen_new_deploy_keys.sh">this script</a> which rotates both the GitHub wireguard VPN client credentials and ssh deploy key &amp; certificate on the 2nd day of <strong>every </strong>month.</p>



<p class="wp-block-paragraph">Updating GitHub secrets is not very straightforward, so I ended up writing this <a href="https://github.com/ackersonde/traefik2_k3s/blob/master/scripts/github_deploy_secrets.py">python script</a> todo the heavy lifting.</p>



<p class="wp-block-paragraph">Figuring out how-to actually get a token to allow this automation was a project in and of itself! Yes, I definitely do recommend you set yourself up with a GitHub organization as this will allow you to share secrets across repository builds.</p>



<p class="wp-block-paragraph">I&#8217;ve created some dummy URLs in the steps below to help you out. You&#8217;ll need to replace &lt;your-org&gt; &amp; &lt;your-app&gt; after navigating to them.</p>



<p class="wp-block-paragraph">0. Create a <a href="https://github.com/settings/apps/new">new GitHub App</a> in your organization. I called mine &#8220;Secrets manager&#8221; (aka &lt;your-app&gt;):</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="830" height="218" data-attachment-id="5180" data-permalink="https://agileweboperations.com/image-8/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png" data-orig-size="830,218" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-8" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=830" alt="" class="wp-image-5180" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png 830w, https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=300 300w, https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=768 768w" sizes="(max-width: 830px) 100vw, 830px" /></figure>



<p class="wp-block-paragraph">1. Add the following permissions:</p>



<ul class="wp-block-list"><li><strong>R/W</strong> access to <em>Repository</em> &amp; <em>Org</em> <strong>Secrets </strong>(update the keys)</li><li><strong>R/W</strong> access to <em>Repository</em><strong> Actions</strong> (trigger repo workflow CI/CD w/ new keys)</li></ul>



<p class="wp-block-paragraph">2. To sign access token requests, generate &amp; download a private key for your <a href="https://github.com/organizations/ackersonde/settings/apps/secrets-manager">newly created app</a>:</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="808" height="149" data-attachment-id="5183" data-permalink="https://agileweboperations.com/image-9/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png" data-orig-size="808,149" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-9" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=808" alt="" class="wp-image-5183" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png 808w, https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=300 300w, https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=768 768w" sizes="(max-width: 808px) 100vw, 808px" /></figure>



<p class="wp-block-paragraph">3. Install the <a href="https://github.com/organizations/%3Cyour-org%3E/settings/apps/%3Cyour-app%3E/installations">application to your organization</a>:</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="1024" height="266" data-attachment-id="5185" data-permalink="https://agileweboperations.com/image-10/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png" data-orig-size="1093,284" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-10" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=1024" alt="" class="wp-image-5185" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=1024 1024w, https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=300 300w, https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=768 768w, https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png 1093w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">4. Finally, note the <strong>install ID</strong> by navigating to your new app and looking at the URL. You&#8217;ll need this to <a href="https://github.com/ackersonde/traefik2_k3s/blob/master/scripts/github_deploy_secrets.py#L103">generate your Access Token with JWT</a>.</p>



<h2 class="wp-block-heading">Securely automating your home</h2>



<p class="wp-block-paragraph">Now, I run my homepage, slackbot, pihole, private VPN, transmission, plex media server &amp; wireguard from my 2 node cluster. Deployment performance is snappy and my staging environment is just a kubernetes node label.</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="846" height="336" data-attachment-id="5177" data-permalink="https://agileweboperations.com/image-6/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png" data-orig-size="846,336" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-6" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=656" src="https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=846" alt="" class="wp-image-5177" srcset="https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png 846w, https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=150 150w, https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=300 300w, https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=768 768w" sizes="(max-width: 846px) 100vw, 846px" /></figure>



<p class="wp-block-paragraph">Next up: <a href="https://syncthing.net/">Syncthing </a>and taking back control of my photos.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2020/11/29/secure-github-deployments-to-your-home/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5150</post-id>
		<media:thumbnail url="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg" />
		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg" medium="image">
			<media:title type="html">PXL_20201127_132239976</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/bb9ac0ea0110eb7b8b81350a7032353ea55d6effe88ad7e7a20552072197579d?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ackerson</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/image-3.png?w=724" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/pxl_20201127_132239976.jpg?w=1024" medium="image">
			<media:title type="html">pixel4Cluster</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/image-7.png?w=884" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/image-8.png?w=830" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/image-9.png?w=808" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/image-10.png?w=1024" medium="image" />

		<media:content url="https://agileweboperations.com/wp-content/uploads/2020/11/image-6.png?w=846" medium="image" />
	</item>
		<item>
		<title>Three things you need to stop doing when becoming an engineering manager</title>
		<link>https://agileweboperations.com/2017/01/13/three-things-you-need-to-change-when-you-become-an-engineering-manager/</link>
					<comments>https://agileweboperations.com/2017/01/13/three-things-you-need-to-change-when-you-become-an-engineering-manager/#comments</comments>
		
		<dc:creator><![CDATA[Matthias Marschall]]></dc:creator>
		<pubDate>Fri, 13 Jan 2017 10:45:56 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[agile management]]></category>
		<category><![CDATA[leadership]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=4528</guid>

					<description><![CDATA[You are a great software engineer with years of experience under your belt. You&#8217;ve seen too many technologies come and go to easily fall for the next hyped framework. You choose the tools which get the job done and have a good chance to keep working for the years to come. Despite growing experience, your &#8230; <a href="https://agileweboperations.com/2017/01/13/three-things-you-need-to-change-when-you-become-an-engineering-manager/" class="more-link">Continue reading <span class="screen-reader-text">Three things you need to stop doing when becoming an engineering&#160;manager</span></a>]]></description>
										<content:encoded><![CDATA[<p><figure style="width: 640px" class="wp-caption alignleft"><img loading="lazy" title="Railway Switch by Rainer Stropek at Flickr" src="https://c1.staticflickr.com/4/3907/14239192157_2abb4868af_z.jpg" alt="engineering management" width="640" height="428" /><figcaption class="wp-caption-text"><small><a title="Attribution-ShareAlike License" href="http://creativecommons.org/licenses/by-sa/2.0/" target="_blank" rel="noopener"><img loading="lazy" src="https://agileweboperations.com/wp-content/uploads/2018/10/cc.png?w=128" alt="Creative Commons License" width="16" height="16" align="absmiddle" border="0" /></a> <a title="Railway Switch by Rainer Stropek at Flickr" href="https://www.flickr.com/photos/rainerstropek/" target="_blank" rel="noopener"> Rainer Stropek </a></small></figcaption></figure></p>
<p>You are a great software engineer with years of experience under your belt. You&#8217;ve seen too many technologies come and go to easily fall for the next hyped framework. You choose the tools which get the job done and have a good chance to keep working for the years to come.</p>
<p>Despite growing experience, your personal growth slowed to a crawl. You&#8217;re wondering what the next step should be as many of your peers have chosen to become an engineering manager.</p>
<p>Before you set off down this road, be aware of the implications of such a step: becoming an engineering manager is not a promotion, but a change of careers!</p>
<h3>Why becoming an engineering manager is a change of careers</h3>
<p>There are three things you have to completely change when switching from engineer to manager. You need to:</p>
<ul>
<li>stop negativity</li>
<li>stop writing production code alone</li>
<li>stop saying &#8220;No&#8221; without providing options</li>
</ul>
<p><span id="more-4528"></span></p>
<h3>Why you need to stop negativity and focus on positive change instead</h3>
<p>&#8220;Negativity destroys performance in the short term and precludes success and happiness in the long term&#8221;, says <a href="http://sealfit.com/about/coaching-staff/mark-divine/">Mark Divine</a>, a former Navy SEALS commander. This is why it’s so powerful for organizations to focus on making their people happy at work. Making people feel good can help free the workplace from negativity.</p>
<p>Negativity in engineers leads to unnecessary rewrites. I&#8217;ve seen it way too often: new engineers look at the existing code and gasp as WTF lights up in their eyes. The first reaction is always: &#8220;Oh, this code is so bad &#8211; it has to be thrown away and rewritten!&#8221;</p>
<p>While almost all code older than three months looks really bad, don’t forget that it’s running a (hopefully) successful business! I’ve seldom seen an engineer appreciate that and approach “legacy” code with a positive attitude to improve it.</p>
<p>As an engineering manager you need to spot negativity and stop it immediately if you experience it. A lot of engineers are proud to be sarcastic and enjoy devastatingly dark humor.</p>
<p>While funny at first, the negativity below the surface slowly erodes a positive attitude towards your team’s tasks and jeopardizes long term success. How often have you seen engineers with a negative attitude create awesome software?</p>
<h3>Why you need to stop coding and focus on leveraging others instead</h3>
<p>When you write production code alone, it has a 1:1 impact on your business. As an engineering manager you need to increase your impact to 1:n by leveraging others. Focus on how you can make others better, increasing their individual impacts on the business.</p>
<p>The sum of all those improvements is the leverage you have as an engineering manager. Only if the sum of those improvements is greater than your individual 1:1 impact, are you a worthy manager. Spending too much time writing production code on your own not only decreases your business impact, but also the time you’ll spend helping your teams!</p>
<p>Of course, this doesn’t mean stop coding altogether. You need to stay in touch with the daily challenges of your team and the advances of technology to be able to make good decisions in the long run.</p>
<p>But writing production code alone is dangerous. You&#8217;ll hardly have enough time to write clean, maintainable code between all the meetings you’re attending. If you write it over a long weekend, you’ll need to train your team mates to maintain and evolve it over time.</p>
<p>Becoming a bottleneck for critical parts of your company’s technical infrastructure means you’ve failed to manage your engineers.</p>
<h3>Why you need to stop saying &#8220;No&#8221; and provide options instead</h3>
<p>It&#8217;s easy to say, &#8220;No, this is not possible; or it’s too complex&#8221; as an engineer. While they can get away with it, as an engineering manager, this is no longer an acceptable answer.</p>
<p>Instead of just saying &#8220;No&#8221;, provide options how to reach the business goals despite the fact that the currently discussed option might not be possible or too difficult. What alternatives are there? Could you reduce scope? Is there an approach that moves in the desired direction with smaller steps? Could you buy a solution?</p>
<p>While the answer ultimately might be that the business idea is not feasible, as an engineering manager you need to make sure that all possible options have been evaluated to make an informed business decision.</p>
<p>The same is true for saying &#8220;Yes&#8221;.</p>
<p>You need to protect your teams from unreasonable requirements and feature overload. By saying &#8220;Yes&#8221; too early and too often, you&#8217;ll overload your team and steer it away from success. They&#8217;ll have to deal with seemingly easy but utterly complex tasks. A lot of engineering challenges look simple from the outside but become ugly if you look more closely.</p>
<p>As an engineering manager you must protect your teams from these unnatural expectations.</p>
<p>If you consider changing careers and become an engineering manager, be aware of these three fundamental changes. If you&#8217;re ready to take this step: Congratulations! You&#8217;re on your way to helping more and more developers be even more successful while helping your business thrive with your leadership!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2017/01/13/three-things-you-need-to-change-when-you-become-an-engineering-manager/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4528</post-id>
		<media:content url="https://0.gravatar.com/avatar/6c307c54b3b17168bd3a2e22d47584c5d1e76a57bb4a4a0828a2a3f1d1345661?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">webops</media:title>
		</media:content>

		<media:content url="https://c1.staticflickr.com/4/3907/14239192157_2abb4868af_z.jpg" medium="image">
			<media:title type="html">Railway Switch by Rainer Stropek at Flickr</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/cc.png?w=128" medium="image">
			<media:title type="html">Creative Commons License</media:title>
		</media:content>
	</item>
		<item>
		<title>Write your own Slack chatbot in Golang</title>
		<link>https://agileweboperations.com/2016/10/23/write-your-own-slack-chatbot-in-golang/</link>
					<comments>https://agileweboperations.com/2016/10/23/write-your-own-slack-chatbot-in-golang/#respond</comments>
		
		<dc:creator><![CDATA[Dan Ackerson]]></dc:creator>
		<pubDate>Sun, 23 Oct 2016 09:15:54 +0000</pubDate>
				<category><![CDATA[Tools & Technology]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=4502</guid>

					<description><![CDATA[Chatbots are all the rage nowadays so I&#8217;ll show you how to plug one into your own Slack channel. It&#8217;s easy, fun and, best of all, completely free! I. Preparation Of course, you&#8217;ll need a Slack team which I&#8217;ll refer to as yourteam.slack.com throughout this post. The free plan includes up to 10 apps or &#8230; <a href="https://agileweboperations.com/2016/10/23/write-your-own-slack-chatbot-in-golang/" class="more-link">Continue reading <span class="screen-reader-text">Write your own Slack chatbot in&#160;Golang</span></a>]]></description>
										<content:encoded><![CDATA[<p><img class="aligncenter size-full wp-image-4503" style="border:3px solid #eeeeee;padding:3px;margin:3px;" src="https://agileweboperations.com/wp-content/uploads/2018/10/screenshot-2018-10-17-at-11-00-07.png" alt="Slack Chatbot" /></p>
<p>Chatbots are all the rage nowadays so I&#8217;ll show you how to plug one into your own Slack channel. It&#8217;s easy, fun and, best of all, completely free!<br />
<span id="more-4502"></span></p>
<h2>I. Preparation</h2>
<p>Of course, you&#8217;ll need a <a href="https://slack.com/" target="_blank" rel="noopener">Slack team</a> which I&#8217;ll refer to as <strong>yourteam.slack.com</strong> throughout this post. The free plan includes up to 10 apps or service integrations &amp; 5GB total file storage for the team which is more than enough for hooking up GitHub, your bot and your CI tool of choice: semaphore, Travis &amp; Circle CI are all natively supported OOTB.</p>
<p>From there, you can create a custom integration for your Bot:<br />
<a href="https://yourteam.slack.com/apps/build/custom-integration"><img loading="lazy" data-attachment-id="4511" data-permalink="https://agileweboperations.com/screen-shot-2016-10-22-at-17-19-39-2/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2016/10/screen-shot-2016-10-22-at-17-19-39-1.png" data-orig-size="1666,1102" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="screen-shot-2016-10-22-at-17-19-39" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2016/10/screen-shot-2016-10-22-at-17-19-39-1.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2016/10/screen-shot-2016-10-22-at-17-19-39-1.png?w=656" class="aligncenter size-large wp-image-4511" style="border:3px solid #eeeeee;padding:3px;margin:3px;" src="https://agileweboperations.com/wp-content/uploads/2018/10/customintegrationbender.png" alt="screen-shot-2016-10-22-at-17-19-39" width="1024" height="677" /></a></p>
<p>Now, you&#8217;ve got an API token which allows your Bot to connect to Slack! Copy this and keep it handy for the next step.</p>
<h2>II. Code</h2>
<p>I used Norberto Lopes&#8217; <a href="https://github.com/slack-go/slack/blob/master/README.md" target="_blank" rel="noopener">Slack API</a> as the connector glue for my bot.</p>
<p>Pay particular attention how I get the Bot to <a href="https://github.com/ackersonde/bender-slackbot/blob/master/bender.go#L105" target="_blank" rel="noopener">only react when he&#8217;s called</a> so it doesn&#8217;t get into any weird infinite loops (like an argument with himself).</p>
<p>Now for the fun part: writing your own custom commands so the Bot can do some heavy lifting for you. As a simple prototype, I asked him to fetch the 10 day forecast for my town via the <a href="https://github.com/ackersonde/bender-slackbot/blob/master/commands/commands.go#L214" target="_blank" rel="noopener"><code>sw</code> cmd</a>. In the last 5 years, I&#8217;ve built out <a href="https://github.com/ackersonde/bender-slackbot/blob/master/commands/commands.go#L241">many other tasks</a> that he helps me out with.</p>
<p>During baseball season, <a href="https://github.com/ackersonde/bender-slackbot/blob/master/bender.go#L30">he even sends me the list</a> of yesterday&#8217;s free-to-air MLB games. The possibilities are only limited by your imagination so let loose and get creative!</p>
<h2>III. Continuous Deployment</h2>
<p>I use GitHub Actions (also <strong>free</strong> for building public repos) to <a href="https://github.com/ackersonde/bender-slackbot/actions" target="_blank" rel="noopener">build and deploy</a> my Bot as a Docker container to a DigitalOcean instance with every <code>git push</code>. It&#8217;s much easier than it sounds and the entire process (building a new Golang binary hosted inside a container and deployed to Ubuntu) takes <strong>about 1 minute</strong>!</p>
<p>Note where the code <a href="https://github.com/ackersonde/bender-slackbot/blob/master/bender.go#L122" target="_blank" rel="noopener">loads the Slack API token</a> from the environment &#8211; I&#8217;ve set this up in <a href="https://github.com/ackersonde/bender-slackbot/blob/master/.github/workflows/build.yml#L76">my build</a> to read from my GitHub organization secrets. Because sometimes tokens have indigestible characters which can get strangely escaped, I&#8217;ve base64 encoded some secrets and <a href="https://github.com/ackersonde/bender-slackbot/blob/master/.github/workflows/build.yml#L55" target="_blank" rel="noopener">decode it at docker runtime</a>.</p>
<p>Lemme know if you have any questions or suggestions!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2016/10/23/write-your-own-slack-chatbot-in-golang/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4502</post-id>
		<media:content url="https://2.gravatar.com/avatar/bb9ac0ea0110eb7b8b81350a7032353ea55d6effe88ad7e7a20552072197579d?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ackerson</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/screenshot-2018-10-17-at-11-00-07.png" medium="image">
			<media:title type="html">Slack Chatbot</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/customintegrationbender.png" medium="image">
			<media:title type="html">screen-shot-2016-10-22-at-17-19-39</media:title>
		</media:content>
	</item>
		<item>
		<title>Technical Debt</title>
		<link>https://agileweboperations.com/2015/09/30/technical-debt/</link>
					<comments>https://agileweboperations.com/2015/09/30/technical-debt/#comments</comments>
		
		<dc:creator><![CDATA[Matthias Marschall]]></dc:creator>
		<pubDate>Wed, 30 Sep 2015 16:35:40 +0000</pubDate>
				<category><![CDATA[Kanban & Agile]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[agile development]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[technical debt]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=4353</guid>

					<description><![CDATA[You have to make that release date. You need more time to get the structure of your modules right, but you don&#8217;t have it. Hitting the release date is more important than cleaning your code, so you defer the cleanup to make the deadline. You agree to take on technical debt which you&#8217;ll have to &#8230; <a href="https://agileweboperations.com/2015/09/30/technical-debt/" class="more-link">Continue reading <span class="screen-reader-text">Technical Debt</span></a>]]></description>
										<content:encoded><![CDATA[<p><img src="https://farm3.staticflickr.com/2005/1996032236_da76c6bd69_b.jpg" alt="scary debt monster by Frank Hebbert" border="0" /></p>
<p>You have to make that release date. You need more time to get the structure of your modules right, but you don&#8217;t have it. Hitting the release date is more important than cleaning your code, so you defer the cleanup to make the deadline. You agree to take on <b>technical debt</b> which you&#8217;ll have to pay back later. Speeding up now to hit this release date will make you slower hitting the next one. If you&#8217;re not careful, over time, development slows down to a crawl and every feature takes weeks instead of days.</p>
<p>To be able to manage technical debt in agile software development, we need to fully understand <b>tech debt</b>. Otherwise, we&#8217;ll ruin our software. Some questions to find a <b>technical debt definition</b> are:</p>
<ul>
<li>What is technical debt (and what isn&#8217;t)?</li>
<li>Where does technical debt come from?</li>
<li>When is it ok to create technical debt?</li>
<li>How does technical debt hurt development?</li>
<li>Technical debt management: how can I measure technical debt?</li>
<li>How can I reduce technical debt?</li>
<li>How can I avoid technical debt?</li>
</ul>
<p><span id="more-4353"></span></p>
<p><a name="what"></a></p>
<h3>What is Technical Debt (and what isn&#8217;t)?</h3>
<p>According to <strong><a href="http://c2.com/cgi/wiki?TechnicalDebt">Ward Cunningham, technical debt</a></strong> describes <strong>deferred, necessary work</strong>:</p>
<blockquote><p>Technical Debt includes those internal things that you choose not to do now, but which will impede future development if left undone. This includes deferred refactoring.<br />
Technical Debt doesn&#8217;t include deferred functionality, except possibly in edge cases where delivered functionality is &#8220;good enough&#8221; for the customer, but doesn&#8217;t satisfy some standard (e.g., a UI element that isn&#8217;t fully compliant with some UI standard).</p></blockquote>
<p>Further describing <a href="http://martinfowler.com/bliki/TechnicalDebt.html"><strong>technical debt, Fowler</strong> wrote</a>:</p>
<blockquote><p>You have a piece of functionality that you need to add to your system. You see two ways to do it, one is quick to do but is messy &#8211; you are sure that it will make further changes harder in the future. The other results in a cleaner design, but will take longer to put in place.</p></blockquote>
<p>And <a href="https://sites.google.com/site/unclebobconsultingllc/a-mess-is-not-a-technical-debt">Uncle Bob reminds us</a> that a mess isn&#8217;t technical debt:</p>
<blockquote><p>A mess is not a technical debt. A mess is just a mess. Technical debt decisions are made based on real project constraints. They are risky, but they can be beneficial. The decision to make a mess is never rational, is always based on laziness and unprofessionalism, and has no chance of paying off in the future. A mess is always a loss.</p></blockquote>
<p>If your code is messy, it&#8217;s always bad &#8211; if you defer necessary work, it might be a smart business decision. Unfortunately both, creating a mess and taking on technical debt, lead to similar issues over time.</p>
<h3>Let&#8217;s look at some examples and see what technical debt <em>is</em>, and what it isn&#8217;t.</h3>
<p>A typical example for incurring technical debt in code is that huge source file every developer fears to touch. Everyone on the team knows that it needs to be broken into more manageable pieces, but it would delay upcoming features by at least two weeks. Touching this module without refactoring it is adding to the technical debt of your product. Every such change will make the eventual refactoring harder and therefore needs to be paid back later &#8211; with interest. The interest you&#8217;ll have to pay is the added complexity of every change you added. The module grows with every code addition making its refactoring (and even the decision to do so) ever more difficult.</p>
<p>An example for technical debt from the operations side of things is deploying your application in only one availability zone in your datacenter even though you have demanding uptime requirements. Setting up a split environment, which covers multiple availability zones is more work. It might be necessary to defer this additional work to be able to deliver critical features to your clients. In this scenario, technical debt does not materialize in added complexity but in higher risk of downtime. To avoid necessary work right now, you introduce single points of failure which will ultimately lower your uptime.</p>
<h3>So, what isn&#8217;t technical debt?</h3>
<p>Unnecessary complexity, dirty hacks, and unreadable code are <em>not</em> technical debt. There is no good reason for introducing such a mess in the first place. If developers feel that they don&#8217;t have the time to write simple, maintainable code they&#8217;re clearly lacking experience and professionalism.</p>
<p>&#8220;Single brain know-how&#8221; is another phenomenon often described as technical debt. But being dependent on a single person to maintain or run a critical part of your application isn&#8217;t tech debt &#8211; it&#8217;s a lethal threat to your business. You could argue that not sharing know-how because that person needs to deliver critical features is technical debt, but I disagree. No single line of code should go live without a second pair of eyes scrutinizing it. Things like pair programming or code reviews are good, professional practice and should not be traded for the illusion of higher delivery speed.</p>
<p>By reading this far, you should now have a clear idea what <em>is</em> technical debt &#8211; and what <em>isn&#8217;t</em>. We&#8217;ll cover the other questions in upcoming articles. Make sure you subscribe to our blog updates below.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2015/09/30/technical-debt/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4353</post-id>
		<media:content url="https://0.gravatar.com/avatar/6c307c54b3b17168bd3a2e22d47584c5d1e76a57bb4a4a0828a2a3f1d1345661?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">webops</media:title>
		</media:content>

		<media:content url="https://farm3.staticflickr.com/2005/1996032236_da76c6bd69_b.jpg" medium="image">
			<media:title type="html">scary debt monster by Frank Hebbert</media:title>
		</media:content>
	</item>
		<item>
		<title>Knife EC2: Manage Amazon EC2 Instances With Chef</title>
		<link>https://agileweboperations.com/2015/08/03/amazon-ec2-instances-with-opscode-chef-using-knife/</link>
					<comments>https://agileweboperations.com/2015/08/03/amazon-ec2-instances-with-opscode-chef-using-knife/#comments</comments>
		
		<dc:creator><![CDATA[Matthias Marschall]]></dc:creator>
		<pubDate>Mon, 03 Aug 2015 06:48:23 +0000</pubDate>
				<category><![CDATA[Tools & Technology]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[chef]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[knife]]></category>
		<category><![CDATA[opscode]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=2907</guid>

					<description><![CDATA[Chef enables you to automate your infrastructure. It provides a command line tool called knife to help you manage your configurations. Using the knife EC2 plugin you can manage your Amazon EC2 instances with Chef. knife EC2 makes it possible to create and bootstrap Amazon EC2 instances in just one line &#8211; if you go &#8230; <a href="https://agileweboperations.com/2015/08/03/amazon-ec2-instances-with-opscode-chef-using-knife/" class="more-link">Continue reading <span class="screen-reader-text">Knife EC2: Manage Amazon EC2 Instances With&#160;Chef</span></a>]]></description>
										<content:encoded><![CDATA[<p><figure style="width: 640px" class="wp-caption alignleft"><img loading="lazy" title="knife by r. nial bradshaw at Flickr" src="https://farm4.staticflickr.com/3759/8897071246_8e1ef7d247_z.jpg" alt="knife ec2" width="640" height="426" /><figcaption class="wp-caption-text"><small><a title="Attribution-ShareAlike License" href="http://creativecommons.org/licenses/by-sa/2.0/" target="_blank" rel="noopener"><img loading="lazy" src="https://www.agileweboperations.com/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" width="16" height="16" align="absmiddle" border="0" /></a> <a title="knife by r. nial bradshaw at Flickr" href="https://www.flickr.com/photos/zionfiction/8897071246" target="_blank" rel="noopener"> r. nial bradshaw </a></small></figcaption></figure></p>
<p><strong>Chef</strong> enables you to automate your infrastructure. It provides a command line tool called <em>knife</em> to help you manage your configurations. Using the <strong>knife EC2</strong> plugin you can manage your <strong>Amazon EC2 instances</strong> with Chef. <em>knife EC2</em> makes it possible to create and bootstrap Amazon EC2 instances in just one line &#8211; <em>if</em> you go through a few setup steps. In this article, I want to show you how to setup your Chef installation and AWS configuration so that you can easily bootstrap new Amazon EC2 instances with Chef&#8217;s <em>knife</em>.<br />
<span id="more-2907"></span></p>
<h3>Prepare SSH Access To Your Amazon EC2 Instances</h3>
<p><strong>Configure Your Amazon Security Group</strong><br />
As Amazon blocks all incoming traffic to your EC2 instances by default. you&#8217;ll need to open the SSH port for <code>knife</code> to access a newly created instance. This is pretty easy. Just login to the AWS management console and navigate to EC2 under Services &gt; Compute. Go to <em>Security Groups</em> and select the <em>default</em> group.</p>
<p>Open the <em>Actions</em> drop-down and choose <em>Edit inbound rules</em>:<br />
<img loading="lazy" data-attachment-id="4335" data-permalink="https://agileweboperations.com/bildschirmfoto-2015-08-03-um-15-02-40/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-02-401.png" data-orig-size="938,364" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Bildschirmfoto 2015-08-03 um 15.02.40" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-02-401.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-02-401.png?w=656" class="aligncenter size-full wp-image-4335" src="https://www.agileweboperations.com/wp-content/uploads/2011/03/Bildschirmfoto-2015-08-03-um-15.02.40.png" alt="AWS Security Group: Choose menu - Edit inbound rules" width="938" height="364" /></p>
<p>Then add a rule for <em>Type</em> <code>SSH</code> with <em>Source</em> <code>Anywhere</code> and save the new inbound rule:<br />
<img loading="lazy" data-attachment-id="4334" data-permalink="https://agileweboperations.com/bildschirmfoto-2015-08-03-um-15-03-12/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-03-121.png" data-orig-size="1668,470" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Bildschirmfoto 2015-08-03 um 15.03.12" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-03-121.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-03-121.png?w=656" class="aligncenter size-full wp-image-4334" src="https://www.agileweboperations.com/wp-content/uploads/2011/03/Bildschirmfoto-2015-08-03-um-15.03.12.png" alt="AWS Security Group: Edit inbound rules screen" width="1668" height="470" /></p>
<p>This enables SSH connections from anywhere (<em>0.0.0.0/0</em>) in the world. If you want to limit access to just your home or work network, choose <code>Custom IP</code> instead of <code>Anywhere</code> and enter the corresponding net mask.</p>
<p>When you&#8217;re done, your <em>default</em> security group should look like this:<br />
<img loading="lazy" data-attachment-id="4333" data-permalink="https://agileweboperations.com/bildschirmfoto-2015-08-03-um-15-04-13/" data-orig-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-04-131.png" data-orig-size="1558,754" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="AWS security setting SSH" data-image-description="" data-image-caption="" data-medium-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-04-131.png?w=300" data-large-file="https://agileweboperations.com/wp-content/uploads/2011/03/bildschirmfoto-2015-08-03-um-15-04-131.png?w=656" class="aligncenter size-full wp-image-4333" src="https://www.agileweboperations.com/wp-content/uploads/2011/03/Bildschirmfoto-2015-08-03-um-15.04.13.png" alt="AWS security setting SSH" width="1558" height="754" /></p>
<p><strong>Generate Key Pair in AWS Console</strong><br />
To enable SSH access to your Amazon EC2 instances you need to create a key pair. Amazon will install the public key of that key pair on every EC2 instance. <em>knife</em> will use the private key of that key pair to connect to your Amazon EC2 instances.</p>
<p>Just select <em>Key Pairs</em> under <em>NETWORK &amp; SECURITY</em> in your AWS management console and press <em>Create Key Pair</em>. Give it a name (e.g. <em>knife</em> so you know that this key pair will be used by <em>knife</em>) and press <em>Create</em>. This will create the key pair and download the private key to your local workstation.</p>
<p><img loading="lazy" class="aligncenter size-full wp-image-4337" src="https://www.agileweboperations.com/wp-content/uploads/2011/03/AWS-management-create-key-pair.png" alt="AWS management - create key pair" width="1362" height="838" /></p>
<p>Store the downloaded private key <em>knife.pem</em> in <code>~/.ssh/knife.pem</code></p>
<p><strong>Prepare your SSH configuration to avoid host key mismatch errors</strong><br />
Open your <code>~/.ssh/config</code> and add:</p>
<pre>Host ec2*compute-1.amazonaws.com
  StrictHostKeyChecking no
  User ubuntu
  IdentityFile  /Users/mm/.ssh/knife.pem
</pre>
<p>(make sure you fix the path to your home dir)</p>
<p>Now, SSH access to your Amazon EC2 instances will work. Time to move on to the next step.</p>
<h3>Configure <em>knife</em> Enabling it to Manage EC2 Instances</h3>
<p><strong>Install the <em>knife EC2</em> plugin</strong><br />
To enable <em>knife</em> to manage Amazon EC2 instances you need to install the <em>knife EC2</em> plugin.</p>
<p>Either add it to your <code>Gemfile</code> or install it using <code>gem</code>:</p>
<pre>$ gem install knife-ec2
</pre>
<p>The <em>knife EC2</em> plugin adds the <code>ec2</code> sub-command to <em>knife</em>, which we&#8217;ll use to manage our Amazon EC2 instances.</p>
<p><strong>Tell <em>knife</em> about your AWS credentials</strong><br />
Create a new user for <em>knife</em> in your AWS management console under <em>Services &gt; Administration &amp; Security</em>. You&#8217;ll use this user&#8217;s AWS credentials (<em>access key ID</em> and <em>secret access key</em>) to manage your Amazon EC2 instances with <em>knife EC2</em>.</p>
<p>Add the AWS credentials of your <em>knife</em> user to your <em>knife</em> configuration file <code>~/.chef/knife.rb</code>:</p>
<pre>knife[:aws_access_key_id] = "..."
knife[:aws_secret_access_key] = "........."
</pre>
<p>Now, <em>knife</em> should be able to use the Amazon AWS API to manage Amazon EC2 instances.</p>
<p><strong>Choose an AMI for your Amazon EC2 instances</strong><br />
We&#8217;re going to instantiate an Amazon EC2 instance running Ubuntu 14.04 LTS.</p>
<p>Look here for <a href="http://uec-images.ubuntu.com/releases/14.04.2/release/">Ubuntu 14.04 LTS AMI IDs</a> which you can use to instantiate Amazon EC2 instances with <em>knife</em>.</p>
<p>You need to choose the right AMI for your region, architecture and root storage. Note down the AMI ID (ami-XXXXXXXX) to use it with <em>knife</em>.</p>
<p><strong>Create an EC2 instance using Chef <em>knife</em></strong><br />
Now, it&#8217;s time to use <em>knife</em> to fire up and configure a new Amazon EC2 instance.</p>
<pre>$ knife ec2 server create -r "role[ubuntu]" -I  ami-1ed88f69 -f t2.small 
  -S knife -i ~/.ssh/knife.pem --ssh-user ubuntu --region eu-west-1 -Z eu-west-1a
</pre>
<ul>
<li><code>"role[ubuntu]"</code> is the <em>run_list</em> I want to associate with the newly created node. You can put any roles and recipes you like here</li>
<li><code>-I</code> is the AMI ID you selected earlier</li>
<li><code>-f</code> is the <a href="http://aws.amazon.com/ec2/instance-types/">Amazon EC2 instance type</a> (see <em>Model</em>)</li>
<li><code>-S</code> is the <em>name</em> you gave to the SSH key pair generated in the AWS management console</li>
<li><code>-i</code> points to the <em>private key file</em> of that SSH key pair as downloaded when the key pair was created in the AWS management console</li>
<li><code>--ssh-user</code> the official Ubuntu EC2 AMIs use <em>ubuntu</em> as the default user</li>
<li><code>--region eu-west-1</code> If you want your instances to be deployed in any specific Amazon AWS region, add this parameter and the desired region</li>
<li><code>-Z eu-west-1a</code> is the availability zone within your region</li>
</ul>
<p><strong>ATTENTION</strong>: make sure to kill the instance again if not needed anymore <img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3>Managing Amazon EC2 Instances With <em>knife</em></h3>
<p>Once you&#8217;ve started up at least one Amazon EC2 instance with <em>knife</em>, you can use <em>knife</em> to find running EC2 instances like this:</p>
<pre>$ knife ec2 server list --region eu-west-1
</pre>
<p>(make sure you use the correct <code>--region</code> parameter)</p>
<p>And, if you want to get rid of an instance (terminate instance and delete the corresponding Chef node), it&#8217;s as easy as:</p>
<pre>$ knife ec2 server delete i-XXXXXXXX --region eu-west-1
$ knife node delete i-XXXXXXXX
</pre>
<p>(<code>i-XXXXXXXX</code> is the ID of the instance as found in the AWS management console or a <code>knife ec2 server list</code> call)</p>
<p>After getting the initial setup right it&#8217;s a breeze to start, list, and stop your EC2 instances at Amazon with Chef&#8217;s <em>knife ec2</em>. With just a single command you can instantiate a new server, bootstrap it as a Chef client and run all Chef recipes defined in the <em>run_list</em>. Pretty sweet. What are your experiences with <em>knife</em> and Amazon EC2 (or other cloud providers)? Let us know in the comments&#8230;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2015/08/03/amazon-ec2-instances-with-opscode-chef-using-knife/feed/</wfw:commentRss>
			<slash:comments>27</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2907</post-id>
		<media:content url="https://0.gravatar.com/avatar/6c307c54b3b17168bd3a2e22d47584c5d1e76a57bb4a4a0828a2a3f1d1345661?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">webops</media:title>
		</media:content>

		<media:content url="https://farm4.staticflickr.com/3759/8897071246_8e1ef7d247_z.jpg" medium="image">
			<media:title type="html">knife by r. nial bradshaw at Flickr</media:title>
		</media:content>

		<media:content url="https://www.agileweboperations.com/wp-content/plugins/photo-dropper/images/cc.png" medium="image">
			<media:title type="html">Creative Commons License</media:title>
		</media:content>

		<media:content url="https://www.agileweboperations.com/wp-content/uploads/2011/03/Bildschirmfoto-2015-08-03-um-15.02.40.png" medium="image">
			<media:title type="html">AWS Security Group: Choose menu - Edit inbound rules</media:title>
		</media:content>

		<media:content url="https://www.agileweboperations.com/wp-content/uploads/2011/03/Bildschirmfoto-2015-08-03-um-15.03.12.png" medium="image">
			<media:title type="html">AWS Security Group: Edit inbound rules screen</media:title>
		</media:content>

		<media:content url="https://www.agileweboperations.com/wp-content/uploads/2011/03/Bildschirmfoto-2015-08-03-um-15.04.13.png" medium="image">
			<media:title type="html">AWS security setting SSH</media:title>
		</media:content>

		<media:content url="https://www.agileweboperations.com/wp-content/uploads/2011/03/AWS-management-create-key-pair.png" medium="image">
			<media:title type="html">AWS management - create key pair</media:title>
		</media:content>
	</item>
		<item>
		<title>Kanban vs Scrum vs Agile</title>
		<link>https://agileweboperations.com/2015/07/27/scrum-vs-kanban/</link>
					<comments>https://agileweboperations.com/2015/07/27/scrum-vs-kanban/#comments</comments>
		
		<dc:creator><![CDATA[Matthias Marschall]]></dc:creator>
		<pubDate>Mon, 27 Jul 2015 19:35:13 +0000</pubDate>
				<category><![CDATA[Kanban & Agile]]></category>
		<category><![CDATA[agile development]]></category>
		<category><![CDATA[kanban]]></category>
		<category><![CDATA[scrum]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=2275</guid>

					<description><![CDATA[When inflexible and wasteful software development processes are making your organization inefficient, it&#8217;s time to introduce an agile methodology. Kanban vs Scrum then becomes an essential question: Which agile software development methodology is better suited for my own situation? And is Kanban agile? What about Scrum vs agile? Confusion is spreading&#8230; Let&#8217;s have a look &#8230; <a href="https://agileweboperations.com/2015/07/27/scrum-vs-kanban/" class="more-link">Continue reading <span class="screen-reader-text">Kanban vs Scrum vs&#160;Agile</span></a>]]></description>
										<content:encoded><![CDATA[<p><figure style="width: 640px" class="wp-caption alignleft"><img loading="lazy" title="Politiek debat op Hofstijl: de lezer versus David Rietveld versus Martin Wörsdörfer by raaphorst at Flickr" src="https://farm5.staticflickr.com/4019/5128338694_e095506789_z.jpg" alt="" width="640" height="403" /><figcaption class="wp-caption-text"><small><a title="Attribution-ShareAlike License" href="http://creativecommons.org/licenses/by-sa/2.0/" target="_blank" rel="noopener"><img loading="lazy" src="https://www.agileweboperations.com/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" width="16" height="16" align="absmiddle" border="0" /></a> <a title="Politiek debat op Hofstijl: de lezer versus David Rietveld versus Martin Wörsdörfer by raaphorst at Flickr" href="http://www.flickr.com/photos/raaphorst/5128338694/" target="_blank" rel="noopener">raaphorst</a></small></figcaption></figure></p>
<p>When inflexible and wasteful software development processes are making your organization inefficient, it&#8217;s time to introduce an agile methodology. <strong>Kanban vs Scrum</strong> then becomes an essential question: Which agile software development methodology is better suited for my own situation? And <strong>is Kanban agile</strong>? What about <strong>Scrum vs agile</strong>? Confusion is spreading&#8230; Let&#8217;s have a look how to sort out all those questions.<br />
<span id="more-2275"></span></p>
<h3>Scrum &#8211; A Fundamental Shift</h3>
<p>Scrum is a well-defined process framework for structuring your work. Introducing Scrum is quite a change for a team not used to agile software development: They have to start working in iterations, build cross-functional teams, appoint a product owner and a Scrum master, as well as introducing regular meetings for iteration planning, daily status updates and sprint reviews. The benefits of the Scrum methodology are well understood: Less superfluous specifications and fewer handovers due to cross-functional teams and more flexibility in roadmap planning due to short sprints. Switching your organization to use Scrum is a fundamental shift which will shake up old habits and transform them into more effective ones.</p>
<h3>Scrum Leverages Commitment As Change Agent</h3>
<p>The initial introduction of Scrum is not an end in itself. Working with Scrum you want to change your teams&#8217; habits: Take more responsibility, raise code quality, increase speed. As your teams commit to sprint goals, they are intrinsically motiviated to get better and faster in order to deliver what they promised. Scrum leverages team commitment as change agent. It&#8217;s amazing to see how much teams demand from themselves &#8211; often way more you as a manager ever dared ask for.</p>
<h3>Kanban &#8211; Incremental Improvements</h3>
<p>The Kanban methodology is way less structured than Scrum. It&#8217;s no process framework at all, but a model for introducing change through incremental improvements. You can apply Kanban principles to any process you are already running (even to Scrum <img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ). In Kanban, you organize your work on a Kanban board. The board has states as columns, which every work item passes through &#8211; from left to right. You pull your work items along through the <em>in progress</em>, <em>testing</em>, <em>ready for release</em>, and <em>released</em> columns. And you may have various <em>swim lanes</em> &#8211; horizontal &#8220;pipelines&#8221; for different types of work. The only management criteria introduced by Kanban is the so called &#8220;Work In Progress (WIP)&#8221;. By managing WIP you can optimize flow of work items. Besides visualizing work on a Kanban board and monitoring WIP, nothing else needs to be changed to get started with Kanban.</p>
<h3>Kanban Leverages Work In Progress (WIP) Limits as Change Agent</h3>
<p>For every column (state) on your Kanban board you should define a &#8220;Work In Progress&#8221;-Limit (WIP Limit). The WIP limit tells you how much work items are allowed to be in a certain state at any given point in time. If a state reaches its pre-defined WIP limit, no new work can enter that state. The whole team has to help clear the filled up state first. Work items trapped in a state will build highly visible clusters on the Kanban board. These clusters make bottlenecks in the progress visible &#8211; you can simply look at the Kanban Board to see where your process needs improvements. Making the need for improvement visible challenges your team to change the way they work to avoid such bottlenecks in the future. That&#8217;s how WIP limit act as change agent in Kanban.</p>
<h3>Kanban vs Scrum</h3>
<p>Looking at both agile software development methodologies it should be more clear what to introduce when: If your organization is really stuck and needs a fundamental shift towards a more efficient process, Scrum seems to be more appropiate. If you already have working processes, which you want to improve over time without shaking up the whole system, Kanban should be your tool of choice.</p>
<h3>And Scrum vs Agile?</h3>
<p>Asking for the differences between Scrum vs Agile or Agile vs Scrum is like asking for the differences between &#8220;Water&#8221; and &#8220;Ice&#8221;. Ice is water in a specific physical state. The same could be said about Scrum vs Agile. Scrum is agile in a specific shaping. It is an agile process framework. Scrum and Kanban in software development are both specific shapings of an agile software methodology. While Scrum vs Kanban or Kanban vs Scrum is comparing two agile methodologies, Scrum vs Agile is comparing a concrete example with its fundamental principles.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2015/07/27/scrum-vs-kanban/feed/</wfw:commentRss>
			<slash:comments>31</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2275</post-id>
		<media:content url="https://0.gravatar.com/avatar/6c307c54b3b17168bd3a2e22d47584c5d1e76a57bb4a4a0828a2a3f1d1345661?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">webops</media:title>
		</media:content>

		<media:content url="https://farm5.staticflickr.com/4019/5128338694_e095506789_z.jpg" medium="image">
			<media:title type="html">Politiek debat op Hofstijl: de lezer versus David Rietveld versus Martin Wörsdörfer by raaphorst at Flickr</media:title>
		</media:content>

		<media:content url="https://www.agileweboperations.com/wp-content/plugins/photo-dropper/images/cc.png" medium="image">
			<media:title type="html">Creative Commons License</media:title>
		</media:content>
	</item>
		<item>
		<title>Enable Your Teams to Rapidly Ship and Operate Quality Software</title>
		<link>https://agileweboperations.com/2015/01/30/enable-your-teams-to-rapidly-ship-and-operate-quality-software/</link>
					<comments>https://agileweboperations.com/2015/01/30/enable-your-teams-to-rapidly-ship-and-operate-quality-software/#respond</comments>
		
		<dc:creator><![CDATA[Dan Ackerson]]></dc:creator>
		<pubDate>Fri, 30 Jan 2015 14:13:37 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=4264</guid>

					<description><![CDATA[How often do your development teams release to production? Who gets the alert in the middle of the night when everything crashes and burns? Do these questions make you uncomfortable or rather their answers? Or maybe you are already discussing changes to your current deploy process? Because it sucks, right? If you&#8217;re honest, it will &#8230; <a href="https://agileweboperations.com/2015/01/30/enable-your-teams-to-rapidly-ship-and-operate-quality-software/" class="more-link">Continue reading <span class="screen-reader-text">Enable Your Teams to Rapidly Ship and Operate Quality&#160;Software</span></a>]]></description>
										<content:encoded><![CDATA[<p><figure style="width: 640px" class="wp-caption alignleft"><img src="https://farm4.staticflickr.com/3516/3460124131_735ce559b0_z.jpg?zz=1" alt="Sea Launch at Sea by Steve Jurvetson" border="0" /><br /><small><a href="http://creativecommons.org/licenses/by/2.0/" title="Attribution License" target="_blank"><img loading="lazy" src="https://agileweboperations.com/wp-content/uploads/2018/10/cc.png?w=16&amp;h=16" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a><a href="https://www.flickr.com/photos/jurvetson/3460124131" title="Sea Launch at Sea" target="_blank">Steve Jurvetson</a></small><figcaption class="wp-caption-text"> </figcaption></figure>How often do your development teams release to production? Who gets the alert in the middle of the night when everything crashes and burns? Do these questions make you uncomfortable or rather their answers? Or maybe you are already discussing changes to your current deploy process? Because it sucks, right? If you&#8217;re honest, it will <strong>always</strong> suck because it constantly needs to be adapted to the current business requirements.</p>
<p>Enter the &#8220;Platform Team&#8221;: a group of build &amp; deploy experts that jumpstart your teams down the road to operational success while providing a safety net. And, no, I&#8217;m  <strong>not</strong> referring to a System Administrator with a pager. Instead, I&#8217;m suggesting a three-ply construction of automation, containerization and monitoring.<br />
<span id="more-4264"></span></p>
<h3>But wouldn&#8217;t hiring a full stack developer be cheaper?</h3>
<p>The amount of job ads for <a href="https://www.google.com/search?q=full+stack+developer">&#8220;Full-Stack Developers&#8221;</a> is insane. Hunting for them is like searching for unicorns &#8211; the closest you&#8217;ll get is an interview with an insane, cone-ornamented horse telling you your search is at an end.</p>
<p>Take the time and effort to teach the folks you have right now. Let them know the most important job is customer satisfaction and this means much more than just writing code.</p>
<p>A platform team is composed of great teachers: test managers who understand &#8216;release-ready&#8217; test coverage and how to automate it; sysadmins that can point out application bottlenecks and suggest monitoring tools.</p>
<h3>How to infect cross-functional teams with the DevOps bug?</h3>
<p>The culture of DevOps is founded upon interdisciplinary communication. What minimally was a developer and sysadmin talking about howto deploy and operate an application, now involves product owners and UX folks in the conversation. <a href="https://www.google.com/search?q=minimum+viable+product">MVP</a> has replaced monthly releases and the pace only quickens.</p>
<p>With automation becoming ever easier to employ and virtual servers costing pennies during a workday, now&#8217;s the time to ready teams for self-service. Here are the first three Self-Service Mantras: </p>
<h4>Self-Reliance (aka &#8220;We can build it&#8221;)</h4>
<p>Work with the developer &#8211; your customer &#8211; to get their requirements right (<em>spoiler</em>: they&#8217;ll change during implementation ;). Make the build and deploy commands executable in the online chat room &#8211; we use <a href="https://hubot.github.com/">hubot</a>. And if you don&#8217;t use an online chat go checkout <a href="https://www.hipchat.com/">hipchat</a> or <a href="https://slack.com/">slack</a>. These rooms are essential for improving situational awareness + simplicity + remote deploys.</p>
<p>Attach a minimal test harness to ensure the build really did what you wanted. Is it really the right code version? Does it start correctly and fulfill it&#8217;s primary goals? <a href="https://saucelabs.com/resources/selenium-jenkins/">Jenkins + Saucelabs</a> is an inexpensive 1-2 knockout punch for this!</p>
<p>Finally, get those builds auto-deployed to your testing environment. A simple, post-build hook from Jenkins in your chat room with the corresponding URL is a honeypot for product owners keen on signing off features for release. &#8220;Houston, we are &#8216;GO&#8217; for launch!&#8221;</p>
<h4>Self-Confidence (aka &#8220;We know what&#8217;s going on&#8221;)</h4>
<ol>
<li>it takes:
<ul>
<li>20-30 builds to work out build bugs (git commit hooks, right branches, etc.)</li>
<li>20-30 <em>more</em> builds to perfect test harness (those damn outliers)</li>
<li>20-30 <em>more</em> builds to prove auto deployment (yes, this is the right version)</li>
</ul>
<li>with ~100 builds, the team is confident</li>
<li>they know how it works, why it breaks and how to fix it</li>
</ol>
<h4>Self-Responsibility (aka &#8220;I care about my users&#8221;)</h4>
<p>Monitoring used to be black magic, but today, with tools like <a href="https://www.loggly.com/">loggly</a>, <a href="http://www.datadoghq.com/">datadog</a> and <a href="http://newrelic.com/">newrelic</a>, it&#8217;s incredibly easy to get deep insight on your applications and servers. Most of these support direct alerts into your online chat room too, so the entire team can feel the pain they subject upon their hapless users. Seeing a long list of disturbing, red graphs with the first cup of coffee in the morning turns the daily standup into a Q&amp;A session.</p>
<p>To provide first-level support, cross-functional teams need to be able to dig for root causes. Gathering data from all your monitoring services (you are using more than one right?), you can quickly narrow down potential problems until you&#8217;ve found what&#8217;s not working. This takes time and experience, so be patient and always available for pair-troubleshooting!</p>
<p>These mantras close the gap between developers and sysadmins and can be unified with the overarching principle &#8220;You build it. You run it.&#8221; Empowered developers take the time to build higher quality products, because they&#8217;re the ones getting the call during the night.</p>
<h3>Scarcity lends Focus</h3>
<p>We don&#8217;t have enough Platform Engineers to embed in each product team, so we loan them out for new projects as &#8220;on-site consultants&#8221;. Once they&#8217;ve gotten the engineers through the three phases above, they move on to the next construction site always with an eye on improving and adding to the company&#8217;s architectural platform and engineering excellence. This style of teaching <em>full-stack development</em> is much more rewarding and infinitely more successful.</p>
<p>There&#8217;s something to be said for time-boxing new technical initiatives. They tend to be streamlined applications that only do what&#8217;s needed. Since there&#8217;s not as much code or moving pieces involved, they&#8217;re also easier to maintain.</p>
<p>As you widen developers&#8217; horizons to the world of automation and virtualization, a few may decide to pick up the standard and become platform engineers themselves. It&#8217;s a win-win situation for your employees and your business.</p>
<p>And what about those unsettling questions about your release process? With development teams responsible for building, shipping and operating their applications, it will suck a whole lot less!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2015/01/30/enable-your-teams-to-rapidly-ship-and-operate-quality-software/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4264</post-id>
		<media:content url="https://2.gravatar.com/avatar/bb9ac0ea0110eb7b8b81350a7032353ea55d6effe88ad7e7a20552072197579d?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ackerson</media:title>
		</media:content>

		<media:content url="https://farm4.staticflickr.com/3516/3460124131_735ce559b0_z.jpg?zz=1" medium="image">
			<media:title type="html">Sea Launch at Sea by Steve Jurvetson</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/cc.png?w=16&#038;h=16" medium="image">
			<media:title type="html">Creative Commons License</media:title>
		</media:content>
	</item>
		<item>
		<title>Supporting Millions of Pretty URL Rewrites in Nginx with Lua and Redis</title>
		<link>https://agileweboperations.com/2014/10/13/supporting-millions-of-pretty-url-rewrites-in-nginx-with-lua-and-redis/</link>
					<comments>https://agileweboperations.com/2014/10/13/supporting-millions-of-pretty-url-rewrites-in-nginx-with-lua-and-redis/#comments</comments>
		
		<dc:creator><![CDATA[Dan Ackerson]]></dc:creator>
		<pubDate>Mon, 13 Oct 2014 11:16:41 +0000</pubDate>
				<category><![CDATA[Tools & Technology]]></category>
		<category><![CDATA[lua]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[redirects]]></category>
		<category><![CDATA[redis]]></category>
		<category><![CDATA[rewrites]]></category>
		<guid isPermaLink="false">http://www.agileweboperations.com/?p=4229</guid>

					<description><![CDATA[About a year ago, I was tasked with greatly expanding our url rewrite capabilities. Our file based, nginx rewrites were becoming a performance bottleneck and we needed to make an architectural leap that would take us to the next level of SEO wizardry. In comparison to the total number of product categories in our database, &#8230; <a href="https://agileweboperations.com/2014/10/13/supporting-millions-of-pretty-url-rewrites-in-nginx-with-lua-and-redis/" class="more-link">Continue reading <span class="screen-reader-text">Supporting Millions of Pretty URL Rewrites in Nginx with Lua and&#160;Redis</span></a>]]></description>
										<content:encoded><![CDATA[<p><figure style="width: 640px" class="wp-caption alignleft"><img loading="lazy" title="Infinite Staircase? by Alexandre Duret-Lutz at Flickr" src="https://agileweboperations.com/wp-content/uploads/2018/10/288607676_4f03530eaf_z.jpg" alt="engineering management" width="640" height="428" /><figcaption class="wp-caption-text"><small><a title="Attribution-ShareAlike License" href="http://creativecommons.org/licenses/by-sa/2.0/" target="_blank" rel="noopener"><img loading="lazy" src="https://agileweboperations.com/wp-content/uploads/2018/10/cc.png?w=128" alt="Creative Commons License" width="16" height="16" align="absmiddle" border="0" /></a> <a title="Infinite Staircase? by Alexandre Duret-Lutz at Flickr" href="https://www.flickr.com/photos/gadl" target="_blank" rel="noopener"> Alexandre Duret-Lutz </a></small></figcaption></figure><br />
<img src="" alt=" " border="0" /></p>
<p>About a year ago, I was tasked with greatly expanding our url rewrite capabilities. Our file based, nginx rewrites were becoming a performance bottleneck and we needed to make an architectural leap that would take us to the next level of SEO wizardry.</p>
<p>In comparison to the total number of product categories in our database, Stylight supports a handful of &#8220;pretty URLs&#8221; &#8211; those understandable by a human being. With <a href="http://www.stylight.com/Sandals/Women/" rel="nofollow">http://www.stylight.com/Sandals/Women/</a> you have a good idea what&#8217;s going to be on that page.<br />
<span id="more-4229"></span></p>
<p>Our web application, however, only does URLs like <a href="http://www.stylight.com/search.action?gender=women&#038;tag=10580&#038;tag=10630" rel="nofollow">http://www.stylight.com/search.action?gender=women&#038;tag=10580&#038;tag=10630</a>. So, nginx needs to translate pretty URLs into something our app can find, fetch and return to your browser. And this needs to happen as fast as computationally possible. Copy/paste that link and you&#8217;ll see we redirect you to the pretty URL. This is because we&#8217;ve discovered women really love sandals so we should give them a page they&#8217;d like to bookmark.</p>
<p>We import and update millions of products a day, so the vast majority of our links start out as &#8220;?tag=10580&#8221;. Googlebot knows how dynamic our site is, so it&#8217;s constantly crawling and indexing these functional links to feed its search results. As we learn from our users and ad campaigns which products are really interesting, we dynamically assign pretty URLs and inform Google with 301 redirects.</p>
<p>This creates 2 layers of redirection and <strong>doubles</strong> the urls our webserver needs to know about:</p>
<ul>
<li><em>301 redirects</em> for the user and bots: ?gender=women&amp;tag=10580&amp;tag=10630 -&gt; /Sandals/Women/</li>
<li><em>internal rewrites</em> for our app: /Sandals/Women/ -&gt; ?gender=women&amp;tag=10580&amp;tag=10630</li>
</ul>
<p>So, how can we provide millions of pretty URLs to showcase all facets of our product search results?</p>
<h2>The problem with file based, nginx rewrites: memory &amp; reload times</h2>
<p>With 800K rewrites and redirects (or <strong>R&amp;Rs</strong> for short) in over 12 country rewrite.conf files, our &#8220;next level&#8221; initially means about ~8 million <strong>R&amp;Rs</strong> urls. But we could barely cope with our current requirements.</p>
<p>File based <strong>R&amp;Rs</strong> are loaded into memory for each of our 16 nginx workers. Besides 3GB of RAM, it took almost <strong>5 seconds</strong> just to reload or restart nginx! As a quick test, I doubled the amount of rewrites for one country. <strong>20 seconds</strong> later nginx finished reloading now running with 3.5GB of memory. Talk about &#8220;scale fail&#8221;.</p>
<h2>What are the alternatives?</h2>
<p>Google searching for nginx with millions of rewrites or redirects didn&#8217;t give a whole lot of insight, but I eventually found <a href="http://openresty.org/">OpenResty</a>. Not being a full-time sysadmin, I don&#8217;t care to build and maintain custom binaries &#8211; had someone else packaged this?</p>
<p>My next search for OpenResty on Ubuntu Trusty led me to <a href="https://launchpad.net/ubuntu/trusty/+package/lua-nginx-redis">lua-nginx-redis</a> &#8211; perhaps not the most performant solution, but I&#8217;d take the compromise for community supported patches. A <code>sudo apt-get install lua-nginx-redis</code> gave us the basis for our new architecture.</p>
<p>As an initial test, I copied our largest country&#8217;s rewrites into redis, made <a href="https://gist.github.com/danackerson/af7481a869396839b3da">a quick lua script for handling the rewrites</a> and did an initial head-to-head test:<br />
<img loading="lazy" class="aligncenter" src="https://agileweboperations.com/wp-content/uploads/2018/10/redis-vs-nginx-1024x578.png" alt="redis-vs-nginx" width="1024" height="578" /></p>
<p>I included network round trip times in my test to get an idea of the complete performance improvement we hoped to realize with this re-architecture. Interesting how quite a few URLs (those towards the bottom of the rewrite file) caused significant spikes in response times. From these initial results, we decided to make the investment and completely overhaul our <strong>R&amp;Rs</strong> infrastructure.</p>
<p>The <em>301 redirects</em> lived exclusively on the frontend load balancers while the <em>internal rewrites</em> were handled by our app servers. First order of business would be to combine these, leaving the application to concentrate on just serving requests. Next, we set up a cronjob to incrementally update <strong>R&amp;Rs</strong> every 5 minutes. I gave the <strong>R&amp;Rs</strong> a TTL of one month to keep the redis db tidy by automatically dropping unused rewrites. Weekly, we run a full insert which resets the TTL. And, yes, we monitor the TTLs of our <strong>R&amp;Rs</strong> &#8211; don&#8217;t want all of them disappearing over night!</p>
<h2>The performance of Lua and Redis</h2>
<p>We launched the new solution in mid-July. To give some historical perspective, here&#8217;s a look at our pageviews over the last 18 months:<br />
<img loading="lazy" class="aligncenter size-medium" src="https://agileweboperations.com/wp-content/uploads/2018/10/ga_pageviews.png" alt="ga_pageviews" width="600" height="196" /><br />
And our average response time during the same period:<br />
<img loading="lazy" class="aligncenter size-medium" src="https://agileweboperations.com/wp-content/uploads/2018/10/pingdom_response_time.png" alt="pingdom_response_time" width="600" height="258" /></p>
<p>As you can see, despite rapidly growing traffic, we saw significant improvements to our site&#8217;s response time by moving the <strong>R&amp;Rs</strong> out of files and into redis. Reload times for nginx are instant &#8211; there are no more rewrites for it to load and distribute per worker &#8211; and memory usage has dropped below 900MB!</p>
<p>Since the launch, we&#8217;ve double our number of <strong>R&amp;Rs</strong>. Checkout how the memory scales:<br />
<img loading="lazy" class="aligncenter size-medium" src="https://agileweboperations.com/wp-content/uploads/2018/10/redis_keys_memory_growth.png" alt="redis_keys_memory_growth" width="600" height="564" /></p>
<p>Soon, we&#8217;ll be able to serve all our URLs like <a href="http://www.stylight.com/Dark-Green/Long-Sleeve/T-Shirts/Gap/Men/" rel="nofollow">http://www.stylight.com/Dark-Green/Long-Sleeve/T-Shirts/Gap/Men/</a> by default. No, we&#8217;re not quite there yet, but if you need <a href="http://www.stylight.com/Men/?shop=4748&amp;tag=10203&amp;tag=10472&amp;tag=10474&amp;tag=415" rel="nofollow">that kinda shirt</a>&#8230;</p>
<p>We&#8217;ve got a lot of SEO work ahead of us which will require millions more rewrites. And now we have a performant architecture which supports it. If you have any questions or would like to know more details, don&#8217;t hesitate to contact me <a href="https://twitter.com/@danackerson">@danackerson</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://agileweboperations.com/2014/10/13/supporting-millions-of-pretty-url-rewrites-in-nginx-with-lua-and-redis/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4229</post-id>
		<media:content url="https://2.gravatar.com/avatar/bb9ac0ea0110eb7b8b81350a7032353ea55d6effe88ad7e7a20552072197579d?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ackerson</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/288607676_4f03530eaf_z.jpg" medium="image">
			<media:title type="html">Infinite Staircase? by Alexandre Duret-Lutz at Flickr</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/cc.png?w=128" medium="image">
			<media:title type="html">Creative Commons License</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/redis-vs-nginx-1024x578.png" medium="image">
			<media:title type="html">redis-vs-nginx</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/ga_pageviews.png" medium="image">
			<media:title type="html">ga_pageviews</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/pingdom_response_time.png" medium="image">
			<media:title type="html">pingdom_response_time</media:title>
		</media:content>

		<media:content url="https://agileweboperations.com/wp-content/uploads/2018/10/redis_keys_memory_growth.png" medium="image">
			<media:title type="html">redis_keys_memory_growth</media:title>
		</media:content>
	</item>
	</channel>
</rss>
