<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Deni Bertovic</title>
        <link>https://denibertovic.com</link>
        <description><![CDATA[Blog posts by Deni Bertovic]]></description>
        <atom:link href="https://denibertovic.com/rss.xml" rel="self"
                   type="application/rss+xml" />
        <lastBuildDate>Sat, 28 Feb 2026 00:00:00 UT</lastBuildDate>
        <item>
    <title>You can just build things</title>
    <link>https://denibertovic.com/posts/you-can-just-build-things/</link>
    <description><![CDATA[<p>Somewhere around summer 2025 I started taking AI coding agents<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> more seriously. I’d been
somewhat dismissive of them for a while - the early versions produced code that looked plausible
but fell apart rather quickly. They would often get stuck and make silly mistakes.
The newer models are different.
Not because they magically write perfect code, but because they’re good enough to be
directed. You tell them what to build, how to structure it, and they <strong>do</strong> the mechanical
work while you make the decisions. The code is not the most effective and most beatiful
but I don’t really care since I can change it so easily.</p>
<p>It clicked when I rebuilt the core of <a href="https://pixbin.net">pixbin.net</a> in about two hours.
Pixbin is a super simple image sharing service I’ve been running since 2012 - drag and drop upload,
auto-expiring images, shareable links. The codebase was showing its age and I’ve been
putting off a rewrite/upgrade for years. With an agent handling the boilerplate while I focused on
architecture and review, the whole thing came together in a single sitting on a hot summers day.
Django, Celery for background image processing, S3 storage, Kubernetes deployment configs. Two hours.
And then I started adding features!</p>
<p>This changed how I looked at my project backlog and list of ideas.</p>
<!--more-->
<h2 id="templates-matter-more-than-prompts">Templates matter more than prompts</h2>
<p>Honstly, the biggest unlock wasn’t better prompting - although precise prompts do help.
It was having a solid project template. I built
<a href="https://github.com/denibertovic/hellok8s-django">hellok8s-django</a> with all my opinions
baked in: <a href="https://nixos.org/">Nix</a> and <a href="https://devenv.sh">devenv</a> for reproducible
environments, Docker builds with <a href="https://github.com/astral-sh/uv">uv</a>, Helm charts,
<a href="https://github.com/mozilla/sops">SOPS</a> for secrets, GitHub Actions CI/CD. And yes I used
Agents to make the template as well.
Now, when I start a new project, the agent understands the conventions immediately because
they’re encoded in the template. No explaining where things go or how deployments work.</p>
<p>This is important because agents are excellent at following patterns but bad at inventing
them. If you give an agent a blank canvas you get mediocre code with inconsistent structure.
If you give it a well-organized codebase with clear conventions, it produces code that fits
right in.</p>
<p>None of this is vibe coding. I’m reviewing every diff, making the architectural calls,
fixing the things it gets wrong - it does get things wrong. But the grunt work - the CRUD views,
the serializers, the test scaffolding, the Helm values - that’s largely handled.
My throughput went through the roof.</p>
<h2 id="solving-the-same-problems-once">Solving the same problems once</h2>
<p>Once I had the template and the workflow down, I started noticing a pattern: every project I
build needs the same handful of infrastructure services. Email, image hosting, form handling.
I’d been solving these ad-hoc per project, or paying for SaaS tools I didn’t really need. So I
started building them as shared services that my future projects can use.</p>
<h3 id="email">Email</h3>
<p>Every web app needs email. Registration, password resets, notifications. I was looking at
various SaaS services - but I don’t have real users yet for most of these
projects. Paying $20+/month for a service that sends maybe 5 messages a month is
absurd. Also some of them doing a rugpull and disabling their free tier without notification didn’t
help either.</p>
<p>I remember reading <a href="https://x.com/levelsio/status/1934197733989999084">a tweet by Pieter Levels</a>
about how the economics of AWS SES were just unmatched, and how most of these SaaS services were
wrappers around SES. I don’t know if it was this specific tweet but something to that effect.
This rang true to me - at least for my needs.</p>
<p>So I built <a href="https://sesy.email">sesy.email</a>.
It’s a Django service that wraps AWS SES behind a REST API. You delegate a
subdomain (like <code>notify.yourdomain.com</code>), and SESY handles everything
automatically: creates the Route53 hosted zone, provisions the SES identity,
writes the DKIM CNAMEs, SPF and DMARC TXT records, configures the MAIL FROM
domain. It also handles bounces and complaints via SNS webhooks and maintains
per-project suppression lists and limits. It’s a clicky web interface with a “next-next-finish”
feel to it. I am thinking about making it agent first but haven’t gotten to it yet.</p>
<p>I built Python, JavaScript, and Django SDKs for it. The Django SDK is a standard
<code>EmailBackend</code>, so any Django project can use it with two lines of config. SES costs
fractions of a cent at my scale. I have this problem solved forever.</p>
<p>Now … being heavy in infra all my life I’m saddend by the steady degradation of the distributed
internet and I briefly thought about building the email backend directly but I wanted
to get going faster. I may still do it. I mean, I still self host my own email server so why not.</p>
<h3 id="image-hosting">Image hosting</h3>
<p>Pixbin already existed, but I gave it a proper API so all my projects can use it for image
uploads and processing. Every web app eventually needs thumbnails, resized versions, maybe
format conversion. I don’t want to reimplement this per project. Upload an image, get back
URLs. Done.</p>
<h3 id="forms">Forms</h3>
<p>I was using one SaaS for contact forms on my static sites. I don’t have any beef with
them but I built my own replacement:
<a href="https://formsubmit.cloud">formsubmit.cloud</a>. It’s a Django app - you create a
form, get a <code>POST /f/{uuid}/</code> endpoint, and submissions get emailed to you via
SESY. I added Cloudflare Turnstile for captcha, honeypot fields, some basic
spam scoring, and webhook support for forwarding submissions to other services.</p>
<p>Then one evening I wanted to organize a dinner with friends. We usually used Doodle
for this kind of thing. Doodle now requires registration and has
gotten so bloated that it takes genuinely long to load and it’s a pain to use.
I built basic scheduling functionality into formsubmit instead. Took an hour or so.</p>
<p>That’s the theme here. Something annoys me, I build a replacement, and it takes an evening
instead of a month. The agent does the mechanical work, I make the design decisions, and the
template ensures everything deploys the same way and generally reuses the same patterns.</p>
<h2 id="what-im-working-on-now">What I’m working on now</h2>
<p><strong>A digital products store</strong> - inspired by Basecamp’s
<a href="https://once.com/">ONCE</a> model. Pay once, own forever. No subscriptions. Django backend,
Stripe for payments with multi-currency support (USD and EUR, geo-detected via Cloudflare’s
<code>CF-IPCountry</code> header), pre-signed S3 URLs for file delivery, and GitHub API integration for
granting repository access to buyers. Once it’s ready I’ll list SESY there as a purchasable
self-hosted product. Haven’t decided yet if formsubmit will be a product or remain a hosted
service.</p>
<p><strong>A social media scheduler</strong> - just for myself, just Twitter and LinkedIn. Next.js with
PostgreSQL. I want to batch-write posts on Sunday and have them go out during the week.
Nothing fancy.</p>
<p>And a few more that I’ll talk about in other posts.</p>
<h2 id="the-economics">The economics</h2>
<p>I run everything on a single bare metal instance on
<a href="https://www.scaleway.com/">Scaleway</a>. Kubernetes, all my services, all my databases,
one box. Total cost is the hosting bill plus whatever pennies SES charges me. No per-service
SaaS subscriptions, no usage tiers that suddenly spike when you cross some arbitrary
threshold. And yes it runs NixOS. :)</p>
<p>Self-hosting has real costs - I maintain the Kubernetes “cluster”, handle ops, keep things
patched. But for a single developer building tools for myself this is manageable. And the
marginal cost of deploying one more service is effectively zero. That changes the math on
what’s worth building.</p>
<p>If a project takes off I change the credentials in the CICD pipeline and point it to a
proper kubernetes cluster where I can scale. Easy peasy.</p>
<h2 id="what-changed">What changed</h2>
<p>I haven’t been this excited about computers since I first learned programming, discovered the internet
and deployed my first webapp.</p>
<p>The backlog of projects that lived in my head for years is actually shrinking.
Ideas that would have stayed in a notes app are getting deployed - even if I’m
the only user, at least I’m trying stuff out.
I’m also way less tolerant of small annoynaces in my day to day - like on my laptop - I just tell
the agent to fix them.</p>
<p>What used to take a week takes a weekend. What used to take a weekend takes an evening. Not
because the agent is doing the thinking for me, but because it’s doing the typing. I still
need to know what I want, how to structure it, and what a good solution looks like. But the
bottleneck has shifted from implementation speed to decision-making speed, and that’s a
genuinely different way to work.</p>
<p>You can just build things.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>AI coding agents are tools like <a href="https://docs.anthropic.com/en/docs/claude-code">Claude
Code</a> that run inside your
terminal or editor and can read your codebase, edit files, run commands,
and iterate on errors autonomously. It’s not copy-pasting snippets from a
chat window - the agent operates directly in your project, understands your
file structure, and executes changes in place.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></description>
    <pubDate>Sat, 28 Feb 2026 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/you-can-just-build-things/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Slopbox: A sandbox for our AI slop :)</title>
    <link>https://denibertovic.com/posts/slopbox-a-sandbox-for-your-ai-slop/</link>
    <description><![CDATA[<p>I don’t normally do this … but … occasionally I want to let an AI agent run
unsupervised on a task: to generate a throwaway POC, explore an implementation
idea, or hack on something while I’m doing other work. Even with each Agent’s
permission system, giving it free rein over my repo feels uncomfortable. One
bad <code>rm -rf</code> or overzealous refactor and I’m picking through <code>git reflog</code>
trying to recover my afternoon. Not to mention the damange it could do to my
environment or access stuff it shouldn’t.</p>
<p>So I built a small tool called
<a href="https://github.com/denibertovic/slopbox">slopbox</a> for running AI agents in
isolated Docker containers and a copy of my repo. This way I’m free to merge
changes in (or throw them away) without it affecting my main worktree. It was
initially inspired by Dagger’s
<a href="https://github.com/dagger/container-use">container-use</a>. This is just much
more in tune with my workflow and the tools I use - but also I don’t trust an
Agent to do something (consistently) just because it says so in a markdown
file. I need stronger guarantees.</p>
<!--more-->
<h2 id="how-it-works">How it works</h2>
<figure>
<img src="/media/posts/slopbox-flow.png" alt="Slopbox flow diagram" />
<figcaption aria-hidden="true">Slopbox flow diagram</figcaption>
</figure>
<p>Slopbox creates disposable sandboxes using Docker. Each sandbox gets:</p>
<ul>
<li>A git worktree clone of my repo (not the actual repo)</li>
<li>A shared Nix store mounted read-only from a daemon container (yes I don’t
want to let it touch my host’s nix store either)</li>
<li>My devenv environment, so tools “just work”</li>
</ul>
<p>When I run <code>slop my-feature</code>, it creates a branch called <code>agent/my-feature</code>,
clones my repo to <code>~/.cache/slopbox/worktrees/</code>, and drops me (or the agent)
into a container. The agent can commit, install packages, break
things…whatever. My real repo and environment stays untouched.</p>
<p>When I’m done, <code>slop diff my-feature</code> shows what’s changed. If I like it,
<code>slop apply my-feature</code> merges it back. If not, <code>slop gc</code> deletes everything.</p>
<h2 id="this-is-an-experiment">This is an experiment</h2>
<p>This is super experimental and exploratory! It may change or break in
unexpected ways - or I may abandon it completely. I rarely run unsupervised
agent loops right now. Most of my Agent usage is interactive - I’m watching what
it does, reviewing diffs, steering it when it goes off course. But I want the
<em>option</em> to spin up a sandbox, point an agent at a problem, and check back
later.</p>
<p>The use case I keep coming back to is throwaway POCs. “Hey Claude, prototype a
CLI that does X” or “sketch out how we’d add Y to this service.” Things where I
don’t care about code quality, I just want to see if an idea is feasible.
Having the agent work in an isolated clone means I can let it run wild without
worrying about the mess.</p>
<h2 id="future-directions">Future directions</h2>
<p>Docker containers provide decent isolation for my use-case. I don’t think I
really need much stronger isolation than that - nor am I thinking about
restricting it’s network access or some such. That said, I did think
about other approaches:</p>
<p><strong><a href="https://github.com/containers/bubblewrap">Bubblewrap</a></strong> - The sandboxing
tool Flatpak uses. Lighter weight than Docker, gives me fine-grained control
over namespaces and bind mounts. Only reason I’m considering it cause it
could be faster to spin up and tear down.</p>
<p><strong>Full NixOS VMs</strong> - Since I’m already running NixOS on my laptop, I could
theoretically replicate my entire workstation inside a VM. VirtualBox, QEMU,
whatever - once. The Nix configuration is already there - my system is declarative. This
would give stronger isolation than containers (separate kernel, no shared
daemon) at the cost of more overhead. Might be worth it for truly untrusted
workloads. Realistically I don’t see this happening since it will be even slower than
the current approach and that irks me. But we’ll see. Maybe if it’s a long running
single VM for all agent workflow and not one per agent.</p>
<p>Neither of these exist yet. Just ideas I’m mulling over.</p>
<p>I also may consider writing a proper CLI in a proper programming language.
Right now it’s just a bunch of bash scripts that are distributed via devenv.
Honestly, if devenv had this built in that would be kinda cool I guess.
But I don’t think the idea has fully crystallized yet and bash is
fine for experimentation. Ironically I’m totally fine with bash now that I’m
not the one writing them - the Agent is.</p>
<h2 id="drawbacks">Drawbacks</h2>
<p>This tool is tightly coupled to my setup and a very specific way I work. I use
<a href="https://devenv.sh">devenv</a> and Nix for most of my projects. If you’re not
already in that ecosystem, the on-boarding cost is high. There are probably
simpler solutions for your use case.</p>
<p>Other issues:</p>
<ul>
<li><strong>Slow</strong> - Even with caching, sandbox startup is slower than running locally.
First run for a project is especially painful as it populates the Nix store.
The good news is that subsequent runs (even for other projects) are much faster.</li>
<li><strong>Disk hungry</strong> - Each worktree is a full clone. The shared Nix store grows
over time. Run <code>slop gc</code> periodically.</li>
<li><strong>Complexity</strong> - Docker daemon, Nix daemon, volumes, git mirrors. More moving
parts means more things that can break.</li>
<li><strong>Manual agent setup</strong> - There’s an instructions file at
<code>/etc/slop/instructions.txt</code> that explains the environment to agents running inside it,
but I have to manually tell them to read it. Haven’t thought about how to automate
that yet.</li>
<li><strong>Multi agent support</strong> - Right now I mostly run claude but I’d like to have custom
support built it for each major CLI agent. And not have to remember <code>--dangerously-skip-permissions</code>
would also be nice.</li>
</ul>
<p>I’m sharing this mostly for my own documentation, because I promised myself I’d
write more this year and because I might gain more insight by writing things
down. Maybe it’s useful as a reference for others thinking about similar
problems.</p>
<p>Check it out in all it’s bash glory at:
<a href="https://github.com/denibertovic/slopbox">github.com/denibertovic/slopbox</a>.</p>]]></description>
    <pubDate>Wed, 04 Feb 2026 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/slopbox-a-sandbox-for-your-ai-slop/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Deploying Next.js to Kubernetes: A practical guide with a complete DevOps Pipeline</title>
    <link>https://denibertovic.com/posts/deploying-nextjs-to-kubernetes-a-practical-guide-with-a-complete-devops-pipeline/</link>
    <description><![CDATA[<p><em>This post continues the <code>hellok8s-*</code> series, following my previous exploration of <a href="https://denibertovic.com/posts/deploying-python-django-to-kubernetes-a-practical-guide-with-a-complete-devops-pipeline/">deploying a Django applications to Kubernetes</a>. While the technology stack differs, the core DevOps principles and deployment challenges remain remarkably similar across frameworks.</em></p>
<p>As modern web applications grow in complexity, the gap between development tutorials and production-ready deployments becomes increasingly challenging. Most Next.js guides stop at <code>npm run dev</code>, but production applications require containerization, orchestration, secrets management, CI/CD pipelines, and reproducible development environments. Today, I want to share insights from <a href="https://github.com/denibertovic/hellok8s-nextjs">hellok8s-nextjs</a>, a comprehensive project template that demonstrates how to bridge this gap with battle-tested DevOps practices.</p>
<h2 id="the-challenge-from-tutorial-to-production">The Challenge: From Tutorial to Production</h2>
<p>First, let’s address the elephant in the room: <strong>Vercel exists, and it’s excellent</strong>. For many Next.js applications, Vercel provides the easiest deployment experience with zero configuration, automatic scaling, and seamless integration with the Next.js ecosystem. If Vercel meets your needs, use it - it’s a fantastic platform that handles most of the complexity I’m about to discuss.</p>
<p>However, real-world enterprise requirements often demand self-hosted solutions for a variety of reasons:</p>
<p><strong>Compliance requirements</strong> (HIPAA, SOC 2, PCI DSS) that require data to remain within specific geographic boundaries or private networks.</p>
<p><strong>Cost optimization</strong> for high-traffic applications where predictable infrastructure costs matter.</p>
<p><strong>Integration with existing infrastructure</strong> and legacy systems that can’t be easily migrated.</p>
<p><strong>Custom security policies</strong> that require full control over the deployment environment.</p>
<p><strong>Air-gapped environments</strong> or on-premises deployments where external platforms aren’t viable.</p>
<p><strong>Multi-cloud strategies</strong> that require vendor independence.</p>
<p>When these constraints apply, scaling from a local development server to a production Kubernetes deployment involves numerous considerations that most tutorials don’t address. How do you ensure consistent environments across team members? How do you manage secrets securely? How do you achieve zero-downtime deployments with proper rollback capabilities?</p>
<p>After implementing these patterns across multiple technology stacks and organizations, I’ve distilled these proven practices into <a href="https://github.com/denibertovic/hellok8s-nextjs">hellok8s-nextjs</a>, a production-ready template that demonstrates the complete DevOps lifecycle for modern Next.js applications. These patterns have proven successful for teams ranging from small startups to enterprise organizations managing hundreds of developers.</p>
<!--more-->
<h2 id="what-makes-this-different">What Makes This Different</h2>
<h3 id="reproducible-development-with-nix-and-devenv">1. Reproducible Development with Nix and devenv</h3>
<p>The project uses <a href="https://nixos.org/">Nix</a> and <a href="https://devenv.sh">devenv</a> to create completely reproducible development environments. When developers run <code>direnv allow</code>, they get:</p>
<ul>
<li><strong>Runtime environment</strong> (Node.js 23 with Yarn 4 pre-configured)</li>
<li><strong>Database services</strong> (PostgreSQL and Redis) automatically configured and running</li>
<li><strong>Kubernetes tooling</strong> (kubectl, Helm, SOPS) pre-installed</li>
<li><strong>AI development tools</strong> (Sourcegraph AMP, Claude, Gemini CLI) ready to use</li>
<li><strong>Git hooks</strong> for automated code formatting and linting</li>
<li><strong>Hot reloading</strong> for the Next.js application</li>
<li>Everything running natively with no container overhead</li>
</ul>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># One command to rule them all</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">devenv</span> up</span></code></pre></div>
<p>This eliminates the infamous “works on my machine” syndrome and reduces onboarding time from days to minutes. I’ve implemented similar setups for clients managing teams of 100+ developers, resulting in a 90% reduction in environment-related support tickets.</p>
<h3 id="modern-next.js-stack-with-type-safety">2. Modern Next.js Stack with Type Safety</h3>
<p>The application showcases current Next.js best practices using the T3 Stack:</p>
<ul>
<li><strong>Next.js 15+</strong> with App Router</li>
<li><strong>TypeScript</strong> for end-to-end type safety</li>
<li><strong>tRPC</strong> for type-safe API routes</li>
<li><strong>Drizzle ORM</strong> for type-safe database operations</li>
<li><strong>NextAuth.js</strong> for authentication with credential-based auth</li>
<li><strong>Tailwind CSS</strong> for utility-first styling</li>
<li><strong>Vitest</strong> for fast testing with transaction rollbacks</li>
</ul>
<h3 id="optimized-container-builds-with-modern-tooling">3. Optimized Container Builds with Modern Tooling</h3>
<p>The Docker build process leverages multi-stage builds and modern Node.js tools for fast, efficient container images:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> node:23-slim <span class="kw">AS</span> base</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ENV</span> YARN_VERSION=4.9.1</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">yarn</span> set version <span class="va">$YARN_VERSION</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co"># dependency installation from lockfile</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> base <span class="kw">AS</span> deps</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> .yarnrc.yml package.json yarn.lock ./</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">yarn</span> <span class="at">--immutable</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Production build with standalone output</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> base <span class="kw">AS</span> builder</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="kw">ENV</span> SKIP_ENV_VALIDATION=1</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> <span class="op">--from=deps</span> /app/node_modules ./node_modules</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> . .</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">yarn</span> run build <span class="kw">&amp;&amp;</span> <span class="ex">yarn</span> run build:scripts</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="co"># Runtime image</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> base <span class="kw">AS</span> runner</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> <span class="op">--from=builder</span> <span class="op">--chown=nextjs:nodejs</span> /app/.next/standalone ./</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> <span class="op">--from=builder</span> <span class="op">--chown=nextjs:nodejs</span> /app/.next/static ./.next/static</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;node&quot;</span>, <span class="st">&quot;server.js&quot;</span>]</span></code></pre></div>
<p>This approach reduces Docker image build times significantly, which is crucial for CI/CD pipelines that build hundreds of images daily. The standalone output feature reduces the final image size by including only necessary files.</p>
<h3 id="enterprise-grade-secrets-management">4. Enterprise-Grade Secrets Management</h3>
<p>Security is built-in from the start using <a href="https://github.com/mozilla/sops">SOPS</a> for encrypted secrets management. Database passwords, API keys, and certificates are stored encrypted in the repository and automatically decrypted during deployment:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Secrets are encrypted at rest, decrypted at deploy time</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="fu">database</span><span class="kw">:</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">password</span><span class="kw">:</span><span class="at"> ENC[AES256_GCM,data:encrypted_value]</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="fu">nextauth</span><span class="kw">:</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">secret</span><span class="kw">:</span><span class="at"> ENC[AES256_GCM,data:encrypted_value]</span></span></code></pre></div>
<p>The project supports both age encryption and cloud provider KMS (AWS, GCP, Azure), making it suitable for compliance requirements while maintaining developer productivity.</p>
<h3 id="kubernetes-native-with-helm-charts">5. Kubernetes-Native with Helm Charts</h3>
<p>The template includes a complete Helm chart structure with environment-specific configurations:</p>
<ul>
<li><strong>Rolling updates</strong> with zero downtime</li>
<li><strong>Health checks</strong> and readiness probes</li>
<li><strong>Resource limits</strong> and requests for optimal scheduling</li>
<li><strong>Ingress configuration</strong> with automatic HTTPS (Let’s Encrypt)</li>
<li><strong>ConfigMap and Secret</strong> management with SOPS integration</li>
</ul>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Deploy to any environment with one command</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="fu">make</span> IMAGE_TAG=sha-123 ENVIRONMENT=prod NAMESPACE=hellok8s deploy</span></code></pre></div>
<h3 id="advanced-cicd-with-github-actions">6. Advanced CI/CD with GitHub Actions</h3>
<p>The GitHub Actions setup demonstrates modern CI/CD patterns:</p>
<ul>
<li><strong>Reusable workflow templates</strong> for multiple environments</li>
<li><strong>Docker layer caching</strong> for faster builds</li>
<li><strong>Automated testing</strong> and type checking</li>
<li><strong>GitOps-style deployments</strong> for different environments</li>
<li><strong>Multi-registry support</strong> (Docker Hub, AWS ECR, etc.)</li>
</ul>
<div class="sourceCode" id="cb5"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Example: Reusable deployment workflow</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">deploy</span><span class="kw">:</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> ./.github/workflows/deploy.yml</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">environment</span><span class="kw">:</span><span class="at"> prod</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">namespace</span><span class="kw">:</span><span class="at"> hellok8s</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">url</span><span class="kw">:</span><span class="at"> https://hellok8s-nextjs.deni.cloud</span></span></code></pre></div>
<h3 id="fast-testing-with-real-database">7. Fast Testing with Real Database</h3>
<p>Unlike many projects use a different database (such as in-memory sqlite) for testing - this template
uses the same database as production (postgres) while automating <strong>transaction rollbacks</strong> for lightning-fast tests:</p>
<ul>
<li><strong>Speed</strong>: Tests run against real PostgreSQL but roll back all changes</li>
<li><strong>Fast hashing</strong>: MD5 for password hashing in tests (bcrypt in production)</li>
<li><strong>Isolation</strong>: Each test runs in a clean state</li>
<li><strong>Real behavior</strong>: Catches actual database constraints and behaviors</li>
</ul>
<div class="sourceCode" id="cb6"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Example: Fast integration test</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="fu">describe</span>(<span class="st">&quot;getAll - listing posts (public)&quot;</span><span class="op">,</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="fu">it</span>(<span class="st">&quot;should return all posts for unauthenticated users&quot;</span><span class="op">,</span> <span class="kw">async</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">await</span> testDb<span class="op">.</span><span class="fu">runInTransaction</span>(<span class="kw">async</span> (db) <span class="kw">=&gt;</span> {</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> superUser <span class="op">=</span> <span class="cf">await</span> <span class="fu">createTestUser</span>(db<span class="op">,</span> {</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>      email<span class="op">:</span> <span class="st">&quot;super@example.com&quot;</span><span class="op">,</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>      firstName<span class="op">:</span> <span class="st">&quot;Super&quot;</span><span class="op">,</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>      lastName<span class="op">:</span> <span class="st">&quot;User&quot;</span><span class="op">,</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>      isSuperuser<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    })<span class="op">;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Create test posts</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">await</span> <span class="fu">createTestPost</span>(db<span class="op">,</span> superUser<span class="op">.</span><span class="at">id</span><span class="op">,</span> {</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>      title<span class="op">:</span> <span class="st">&quot;First Post&quot;</span><span class="op">,</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>      slug<span class="op">:</span> <span class="st">&quot;first-post&quot;</span><span class="op">,</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>    })<span class="op">;</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">await</span> <span class="fu">createTestPost</span>(db<span class="op">,</span> superUser<span class="op">.</span><span class="at">id</span><span class="op">,</span> {</span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a>      title<span class="op">:</span> <span class="st">&quot;Second Post&quot;</span><span class="op">,</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>      slug<span class="op">:</span> <span class="st">&quot;second-post&quot;</span><span class="op">,</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a>    })<span class="op">;</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> caller <span class="op">=</span> <span class="fu">createCaller</span>(<span class="fu">createMockContext</span>(db<span class="op">,</span> <span class="kw">null</span>))<span class="op">;</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> result <span class="op">=</span> <span class="cf">await</span> caller<span class="op">.</span><span class="fu">getAll</span>()<span class="op">;</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a>    <span class="fu">expect</span>(result)<span class="op">.</span><span class="fu">toHaveLength</span>(<span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Just check that both posts are returned, order may vary in tests</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> titles <span class="op">=</span> result<span class="op">.</span><span class="fu">map</span>((p<span class="op">:</span> <span class="dt">any</span>) <span class="kw">=&gt;</span> p<span class="op">.</span><span class="at">title</span>)<span class="op">.</span><span class="fu">sort</span>()<span class="op">;</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a>    <span class="fu">expect</span>(titles)<span class="op">.</span><span class="fu">toEqual</span>([<span class="st">&quot;First Post&quot;</span><span class="op">,</span> <span class="st">&quot;Second Post&quot;</span>])<span class="op">;</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a>  })<span class="op">;</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div>
<h3 id="real-world-impact-measurable-results">Real-World Impact: Measurable Results</h3>
<p>These aren’t just theoretical improvements. This template reflects patterns I’ve implemented for organizations ranging from early-stage startups to Fortune 500 companies, delivering measurable improvements:</p>
<ul>
<li><strong>Reduced deployment times</strong> from hours to minutes (average 15x improvement)</li>
<li><strong>Eliminated environment drift</strong> across development, staging, and production</li>
<li><strong>Improved security posture</strong> with encrypted secrets management</li>
<li><strong>Decreased onboarding time</strong> for new developers by 90%</li>
<li><strong>Enabled true DevOps practices</strong> with infrastructure as code</li>
<li><strong>Increased deployment frequency</strong> from weekly to multiple times per day</li>
<li><strong>Reduced production incidents</strong> by 60% through better testing and staging</li>
</ul>
<h2 id="how-i-can-help-your-organization">How I Can Help Your Organization</h2>
<p>This project template reflects years of hands-on experience helping teams overcome production challenges. I offer consulting services to help organizations implement these patterns:</p>
<h3 id="devops-transformation-cloud-native-architecture">DevOps Transformation &amp; Cloud-Native Architecture</h3>
<ul>
<li><strong>Kubernetes Strategy</strong>: Container optimization, security hardening, and best practices</li>
<li><strong>CI/CD Pipeline Design</strong>: From basic automation to advanced GitOps workflows</li>
<li><strong>Infrastructure as Code</strong>: Terraform, Helm, and cloud provider best practices</li>
<li><strong>Secrets Management</strong>: SOPS, HashiCorp Vault, and cloud-native solutions</li>
</ul>
<h3 id="developer-experience-team-scaling">Developer Experience &amp; Team Scaling</h3>
<ul>
<li><strong>Reproducible Environments</strong>: Nix, Docker, and devenv implementations</li>
<li><strong>Onboarding Optimization</strong>: Reducing time-to-productivity for new team members</li>
<li><strong>Quality Processes</strong>: Code review standards, automated testing, and quality gates</li>
<li><strong>Technical Leadership</strong>: Architecture reviews and technology selection</li>
</ul>
<h3 id="lets-talk">Let’s Talk</h3>
<p>Whether you’re struggling with Next.js deployment complexity, seeking to modernize your development practices, or planning a cloud migration, the patterns demonstrated in this template can transform your organization’s engineering capabilities.</p>
<h3 id="common-scenarios-where-i-can-help">Common Scenarios Where I Can Help:</h3>
<ul>
<li><strong>“Our deployments are unreliable and take forever”</strong> - Implement automated CI/CD with rollback capabilities</li>
<li><strong>“New developers take weeks to get productive”</strong> - Create reproducible development environments</li>
<li><strong>“We’re afraid to deploy on Fridays”</strong> - Build confidence through automated testing, easy rollbacks and deployable PRs (review apps)</li>
<li><strong>“Managing secrets is a security nightmare”</strong> - Implement encrypted, automated secrets management</li>
<li><strong>“Our Next.js app doesn’t scale”</strong> - Optimize performance and implement proper caching strategies</li>
</ul>
<p>I’m available for hands-on consulting engagements ranging from focused architecture reviews to full-scale DevOps transformations. Whether you need a quick assessment or comprehensive implementation, let’s discuss how these proven practices can accelerate your team’s delivery and reliability.</p>
<p><strong>Ready to get started?</strong> This project serves as a perfect conversation starter for your Next.js infrastructure needs. We can review your current setup, identify pain points, and create a roadmap for improvement that delivers measurable results. <a href="https://initeq.net/contact.html">Reach out now using this contact form</a>.</p>
<hr />
<p><em>The complete source code and documentation for hellok8s-nextjs is available on <a href="https://github.com/denibertovic/hellok8s-nextjs">GitHub</a>. Feel free to use it as a starting point for your own projects, or reach out if you’d like help implementing these patterns in your organization.</em></p>]]></description>
    <pubDate>Wed, 23 Jul 2025 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/deploying-nextjs-to-kubernetes-a-practical-guide-with-a-complete-devops-pipeline/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Containers and Signal Handling: Why You Need to Care About PID 1</title>
    <link>https://denibertovic.com/posts/containers-and-signal-handling-why-you-need-to-care-about-pid-1/</link>
    <description><![CDATA[<p>When running applications in Docker containers, many developers overlook a critical detail: what process runs as PID 1. This seemingly minor choice can lead to unresponsive containers, resource leaks, and unexpected behavior during shutdown.</p>
<h2 id="why-pid-1-is-special">Why PID 1 is Special</h2>
<p>In Linux, the kernel treats PID 1 differently from all other processes. It’s the “init” process that bootstraps the system and has two critical responsibilities:</p>
<p><strong>Signal handling</strong>: The kernel doesn’t deliver certain signals (like SIGTERM) to PID 1 unless it explicitly registers handlers for them.</p>
<p><strong>Process reaping</strong>: PID 1 must clean up zombie processes by calling <code>waitpid()</code> on dead children.</p>
<p>When you run a container with:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;./my-app&quot;</span>]</span></code></pre></div>
<p>Your application becomes PID 1, inheriting these kernel expectations whether it’s designed for them or not.</p>
<!--more-->
<h2 id="what-goes-wrong">What Goes Wrong</h2>
<p>Without proper PID 1 handling, you’ll encounter:</p>
<ul>
<li><strong>Unresponsive shutdowns</strong>: <code>docker stop</code> sends SIGTERM to PID 1, but if your app doesn’t handle it, the kernel ignores it. Docker waits 10 seconds, then forcibly kills with SIGKILL</li>
<li><strong>Zombie accumulation</strong>: Child processes that die aren’t reaped, leaving zombie entries in the process table</li>
<li><strong>Orphaned processes</strong>: Background processes lose their parent but aren’t properly managed</li>
</ul>
<h2 id="the-solution">The Solution</h2>
<p>Use a proper init system that handles PID 1 responsibilities:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Install tini</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">apk</span> add <span class="at">--no-cache</span> tini</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Use tini as entrypoint</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">ENTRYPOINT</span> [<span class="st">&quot;/sbin/tini&quot;</span>, <span class="st">&quot;--&quot;</span>]</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;./my-app&quot;</span>]</span></code></pre></div>
<p>Or enable Docker’s built-in init:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> run <span class="at">--init</span> my-image</span></code></pre></div>
<p>Another excellent option is <code>fpco/pid1</code>, a minimal init system implemented in Haskell:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">ENV</span> PID1_VERSION=0.1.3.1</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">curl</span> <span class="at">-sSL</span> <span class="st">&quot;https://github.com/fpco/pid1/releases/download/v</span><span class="va">${PID1_VERSION}</span><span class="st">/pid1&quot;</span> <span class="at">-o</span> /sbin/pid1 <span class="kw">&amp;&amp;</span> <span class="dt">\</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="fu">chown</span> root:root /sbin/pid1 <span class="kw">&amp;&amp;</span> <span class="dt">\</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">chmod</span> +x /sbin/pid1</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="kw">ENTRYPOINT</span> [<span class="st">&quot;/sbin/pid1&quot;</span>, <span class="st">&quot;-u&quot;</span>, <span class="st">&quot;appuser&quot;</span>, <span class="st">&quot;-g&quot;</span>, <span class="st">&quot;appgroup&quot;</span>]</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;./my-app&quot;</span>]</span></code></pre></div>
<p>What makes <code>fpco/pid1</code> particularly useful are its <code>-u</code> and <code>-g</code> flags, which allow you to execute the child process as a specified user and group. This eliminates the need for separate user switching logic while maintaining proper PID 1 behavior.
It is also very useful for development as it makes it possible to switch to a dynamic user that that matches your host UID so no permissons get messed up on munted host volumes.</p>
<p>There’s also the new and improved <code>pid1-rs</code> (rewritten in Rust):</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">ENV</span> PID1_VERSION=0.1.2</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">curl</span> <span class="at">-sSL</span> <span class="st">&quot;https://github.com/fpco/pid1-rs/releases/download/v</span><span class="va">${PID1_VERSION}</span><span class="st">/pid1-x86_64-unknown-linux-musl&quot;</span> <span class="at">-o</span> /sbin/pid1 <span class="kw">&amp;&amp;</span> <span class="dt">\</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="fu">chown</span> root:root /sbin/pid1 <span class="kw">&amp;&amp;</span> <span class="dt">\</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">chmod</span> +x /sbin/pid1</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">ENTRYPOINT</span> [<span class="st">&quot;/sbin/pid1&quot;</span>]</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;./my-app&quot;</span>]</span></code></pre></div>
<p>Even better, if you’re writing Rust applications, you can embed PID 1 functionality directly by using the <code>pid1</code> crate. This means your application can handle PID 1 responsibilities natively without needing an external init process:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pid1::</span>Pid1Settings<span class="op">;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> main() <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> settings <span class="op">=</span> <span class="pp">Pid1Settings::</span>new()<span class="op">;</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    settings<span class="op">.</span>enable_signal_handling()<span class="op">.</span>enable_reaping()<span class="op">;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    settings<span class="op">.</span>launch(<span class="op">||</span> <span class="op">{</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>        <span class="co">// Your application code here</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>        run_my_app()<span class="op">;</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>)<span class="op">.</span>expect(<span class="st">&quot;Failed to launch with pid1&quot;</span>)<span class="op">;</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>A proper init process:</p>
<ul>
<li>Forwards signals to your application</li>
<li>Reaps zombie processes automatically</li>
<li>Ensures graceful shutdown by sending SIGTERM to children before exiting</li>
<li>Provides the “grace period” needed for clean application shutdown</li>
</ul>
<p>Unless your application is specifically designed to handle PID 1 responsibilities, wrap it with a minimal init system. It’s a small change that prevents hard-to-debug container behavior and ensures predictable shutdown semantics.</p>
<p>Your future self (and your ops team) will thank you.</p>]]></description>
    <pubDate>Tue, 08 Jul 2025 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/containers-and-signal-handling-why-you-need-to-care-about-pid-1/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Deploying Python (Django) to Kubernetes: A practical guide with a complete DevOps Pipeline</title>
    <link>https://denibertovic.com/posts/deploying-python-django-to-kubernetes-a-practical-guide-with-a-complete-devops-pipeline/</link>
    <description><![CDATA[<p>As businesses increasingly move to cloud-native architectures, the complexity of deploying and maintaining modern web applications continues to grow. Today, I want to share insights from a comprehensive project template I’ve developed that demonstrates DevOps best practices, from local development to production Kubernetes deployments. While this example uses Django, the patterns and practices apply to any modern web framework.</p>
<h2 id="the-challenge-bridging-the-gap-from-tutorial-to-production">The Challenge: Bridging the Gap from Tutorial to Production</h2>
<p>Most web framework tutorials stop at basic development servers. But production applications require so much more: containerization, orchestration, secrets management, CI/CD pipelines, and reproducible development environments. The gap between “hello world” and production-ready is where most teams struggle and where costly delays, security vulnerabilities, and scaling bottlenecks emerge.</p>
<p>After years of building web applications across multiple technology stacks, I’ve distilled these battle-tested practices into <a href="https://github.com/denibertovic/hellok8s-django">hellok8s-django</a>, a production-ready template that demonstrates the complete DevOps lifecycle, from local development to Kubernetes at scale. These patterns have proven successful with Python, Node.js, Go, Ruby, Haskell and other modern tech stacks.
<!--more--></p>
<h2 id="what-makes-this-different">What Makes This Different</h2>
<h3 id="reproducible-development-with-nix-and-devenv">1. Reproducible Development with Nix and devenv</h3>
<p>Gone are the days of “works on my machine.” This project uses <a href="https://nixos.org/">Nix</a> and <a href="https://devenv.sh">devenv</a> to create completely reproducible development environments. When developers run <code>direnv allow</code>, they get:</p>
<ul>
<li>Runtime environment (Python 3.13 with uv in this case)</li>
<li>Database (and other supporting services) automatically configured and running</li>
<li>kubectl, Helm, and other cloud provider tools pre-installed</li>
<li>Asset compilation with file watching</li>
<li>Git hooks for code formatting and linting</li>
<li>Everything running natively on your local machine with no container overhead</li>
</ul>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># One command to rule them all</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">devenv</span> up</span></code></pre></div>
<p>This approach eliminates the infamous “works on my machine” syndrome and reduces onboarding time from days to minutes. I’ve implemented similar setups for clients managing teams of 100+ developers across multiple time zones, resulting in a 90% reduction in environment-related support tickets.</p>
<h3 id="lightning-fast-container-builds-with-modern-tooling">2. Lightning-Fast Container Builds with Modern Tooling</h3>
<p>While many projects still use legacy dependency management, this template leverages modern, high-performance tooling that greatly simplifies the entire container building process.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Install the project&#39;s dependencies using the lockfile and settings</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="op">--mount=type=cache,target=/root/.cache/uv</span> <span class="op">\</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">--mount=type=bind,source=uv.lock,target=uv.lock</span> <span class="op">\</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">--mount=type=bind,source=pyproject.toml,target=pyproject.toml</span> <span class="op">\</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="ex">uv</span> sync <span class="at">--locked</span> <span class="at">--no-install-project</span> <span class="at">--no-dev</span></span></code></pre></div>
<p>This approach reduces Docker image build times, crucial for CI/CD pipelines that build hundreds of images daily. For one client, this optimization alone saved 4 hours of build time daily across their development team. The same patterns apply whether you’re using npm, Go modules, Cargo, or other package managers.</p>
<h3 id="enterprise-grade-secrets-management-with-sops">3. Enterprise-Grade Secrets Management with SOPS</h3>
<p>Security isn’t an afterthought. The project integrates <a href="https://github.com/mozilla/sops">SOPS</a> for encrypted secrets management, supporting both age encryption and cloud native tooling such as AWS KMS. Database passwords, API keys, and certificates are stored encrypted in the repository and automatically decrypted during deployment.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Secrets are encrypted at rest, decrypted at deploy time</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="fu">database_password</span><span class="kw">:</span><span class="at"> ENC[AES256_GCM,data:encrypted_value]</span></span></code></pre></div>
<p>This approach satisfies compliance requirements while maintaining developer productivity (secrets are “diffable” in PRs), something I’ve implemented for clients with strict regulatory requirements.</p>
<h3 id="kubernetes-native-with-helm-charts">4. Kubernetes-Native with Helm Charts</h3>
<p>The template includes a complete Helm chart structure with environment-specific configurations. The deployment process demonstrates:</p>
<ul>
<li>Rolling updates with zero downtime</li>
<li>Health checks and readiness probes</li>
<li>Resource limits and requests</li>
<li>Ingress configuration with HTTPS (letsencrypt)</li>
<li>ConfigMap and Secret management</li>
</ul>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Deploy to any environment with one command</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="fu">make</span> IMAGE_TAG=sha-123 ENVIRONMENT=prod NAMESPACE=hellok8s deploy</span></code></pre></div>
<h3 id="advanced-cicd-with-reusable-workflows">5. Advanced CI/CD with Reusable Workflows</h3>
<p>The GitHub Actions setup showcases modern CI/CD patterns:</p>
<ul>
<li>Reusable workflow templates for multiple environments</li>
<li>Docker layer caching for faster builds</li>
<li>Automated testing and security scanning</li>
<li>GitOps-style deployments with approvals</li>
<li>Integration with multiple container registries</li>
</ul>
<h2 id="real-world-impact-measurable-results">Real-World Impact: Measurable Results</h2>
<p>This isn’t just a demo project. It’s a distillation of patterns I’ve implemented for companies ranging from early-stage startups to Fortune 500 enterprises. The techniques demonstrated here have delivered measurable improvements:</p>
<ul>
<li><strong>Reduced deployment times</strong> from hours to minutes (average 15x improvement)</li>
<li><strong>Eliminated environment drift</strong> across development, staging, and production</li>
<li><strong>Improved security posture</strong> no more plain text secrets</li>
<li><strong>Decreased onboarding time</strong> for new developers by 80% (from weeks to minutes)</li>
<li><strong>Enabled true DevOps practices</strong> with infrastructure as code and full CI/CD</li>
<li><strong>Increased deployment frequency</strong> from weekly to multiple times daily</li>
<li><strong>Reduced production incidents</strong> by 60% through better testing and staging practices</li>
</ul>
<h2 id="how-i-can-help-your-organization">How I Can Help Your Organization</h2>
<p>This project template reflects years of hands-on experience helping teams overcome production challenges. I offer consulting services in several key areas:</p>
<h3 id="devops-transformation-modernization">DevOps Transformation &amp; Modernization</h3>
<ul>
<li><strong>Cloud Migration</strong>: Lift-and-shift to cloud-native orchestration with zero-downtime strategies</li>
<li><strong>CI/CD Pipeline Design</strong>: From basic automation to advanced GitOps workflows</li>
<li><strong>Infrastructure as Code</strong>: Terraform, Helm, and cloud provider best practices</li>
<li><strong>Kubernetes Strategy</strong>: Container optimization, security hardening, best practices</li>
</ul>
<h3 id="application-development-excellence">Application Development Excellence</h3>
<ul>
<li><strong>Architecture Design</strong>: Scalable patterns for high-traffic applications across frameworks</li>
<li><strong>Performance Optimization</strong>: Database tuning, caching strategies, and async processing</li>
<li><strong>API Development</strong>: RESTful APIs with proper authentication and rate limiting</li>
<li><strong>Modern Tooling</strong>: Adoption of cutting-edge development tools and practices for any tech stack</li>
</ul>
<h3 id="cloud-native-architecture">Cloud-Native Architecture</h3>
<ul>
<li><strong>Microservices Strategy</strong>: When, how, and what to decompose from monoliths</li>
<li><strong>Event-Driven Systems</strong>: Message queues, event sourcing, and distributed architectures</li>
<li><strong>Observability</strong>: Comprehensive monitoring, logging, and alerting strategies</li>
<li><strong>Cost Optimization</strong>: Right-sizing resources and implementing FinOps best practices</li>
</ul>
<h3 id="team-process-excellence">Team &amp; Process Excellence</h3>
<ul>
<li><strong>Developer Experience</strong>: Reducing friction in daily workflows and improving productivity</li>
<li><strong>Quality Processes</strong>: Code review standards, testing strategies, and quality gates</li>
<li><strong>Team Scaling</strong>: Onboarding processes and knowledge management systems</li>
<li><strong>Technical Leadership</strong>: Architecture reviews, technology selection, and strategic planning</li>
</ul>
<h2 id="lets-talk">Let’s Talk</h2>
<p>Whether you’re struggling with deployment complexity, seeking to modernize your development practices, or planning a cloud migration, the patterns demonstrated in this template can transform your organization’s engineering capabilities. <a href="https://initeq.net/#contact">Reach out now using this contact form</a>.</p>
<h3 id="common-scenarios-where-i-can-help">Common Scenarios Where I Can Help:</h3>
<ul>
<li><strong>“Deployments take hours and often fails”</strong> - Implement automated CI/CD with rollback capabilities</li>
<li><strong>“New developers take weeks to get productive”</strong> - Create reproducible development environments</li>
<li><strong>“Afraid to deploy on Fridays”</strong> - Build confidence through automated testing, preview deployments, easy rollbacks and observability tooling.</li>
<li><strong>“Managing secrets is a nightmare”</strong> - Implement secure, automated secrets management (sops, Hashicorp Vault, etc)</li>
<li><strong>“Cloud costs are spiraling out of control”</strong> - Optimize resource usage and implement cost governance</li>
</ul>
<p>I’m available for hands-on consulting engagements ranging from focused architecture reviews to full-scale DevOps transformations. Whether you need a quick assessment or comprehensive implementation, let’s discuss how these proven practices can accelerate your team’s delivery and reliability.</p>
<p><strong>Ready to get started?</strong> This project serves as a perfect conversation starter, regardless of your current technology stack. We can review your current setup, identify pain points, and create a roadmap for improvement that delivers measurable results. <a href="https://initeq.net/#contact">Reach out now using this contact form</a>.</p>
<hr />
<p><em>The complete source code and documentation for hellok8s-django is available on <a href="https://github.com/denibertovic/hellok8s-django">GitHub</a>. Feel free to use it as a starting point for your own projects, or reach out if you’d like help implementing these patterns in your organization.</em></p>]]></description>
    <pubDate>Tue, 01 Jul 2025 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/deploying-python-django-to-kubernetes-a-practical-guide-with-a-complete-devops-pipeline/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>New homelab machine</title>
    <link>https://denibertovic.com/posts/new-homelab-machine/</link>
    <description><![CDATA[<p>I’ve been meaning to share the new addition to my homelab setup for a while now but just never got around to it.
It’s a beast that’s definitely overkill but I wanted to play around and future proof as much as possible.</p>
<p>It initially started as a project to replace my old 2-bay Synology NAS. I needed an upgrade, storage wise, but I also
wanted to switch to <a href="https://openzfs.org/wiki/Main_Page">ZFS</a>, and since Synology doesn’t support ZFS I knew I’d have to
build my own rig. That in turn meant I needed to figure out what I was going to install on the thing. Initially I looked at <a href="https://www.openmediavault.org/">openmediavault</a> but it didn’t seem to support ZFS at the time (I don’t know if this is still true). Then I looked at FreeNAS as they were going through their rebranding to <a href="https://www.truenas.com/">TrueNAS</a> - but I was weary about switching to FreeBSD at the time. I wanted something that I knew well (Linux) and that I can easily customize. Before you mention TrueNAS Scale (which is Linux based) it wasn’t ready at the time<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> and I was hesitant about all the cruft that comes pre-installed with it (my main issue with Synology). I guess I wanted to install and configure stuff my way when I need it.</p>
<!--more-->
<p>So…Since I was going to just install plain ol’ Linux on this thing I figured there’s no reason to restrict it to
being just a NAS. That’s when things got a bit out of hand and I went all out. :)</p>
<p>Here’s a list of components I ended up going with:</p>
<ul>
<li>Case: Silverstone CS381</li>
<li>Motherboard: Supermicro X12STH-F</li>
<li>CPU: Intel Xeon E-2388G</li>
<li>Noctua NH-L9i (initially Noctua L9x65 but it didn’t fit)</li>
<li>RAM: 32GB DDR4 ECC (x2)</li>
<li>Samsung Evo 970 Plus 1TB</li>
<li>Samsung 870 Evo 4TB (x2)</li>
<li>WD Red Plus WD60EFZX 6TB (x2)</li>
<li>Be-Quiet Silent Wings 3 (x2)</li>
<li>Noctua NF-A9x14 (x2)</li>
<li>DeLock Kabel Mini SAS HD SFF 8643 x4</li>
<li>PSU: Be quiet! SFX-L Power 600W 80+ Gold Power Supply</li>
<li>Mini SAS HD (SFF 8643) to 4X Sata (<strong>Reverse Breakout</strong> Cable)</li>
</ul>
<p>With that kind of power I knew I had to utilize this thing beyond just a NAS machine.
Since most of my machines these days (including my other homelab server - an Intel NUC) already run <a href="https://nixos.org/">NixOS</a>,
that’s what I decided to go with. At the time I was doing some heavy duty projects that seemed to prove too big to handle
for my slightly outdated X1 Carbon laptop, so I set it up as a remote nix builder among other things. But that’s something for a future blog post. It’s been running for a while now and I’m quite happy with it.</p>
<p>Here are some photos for your viewing pleasure:</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Though honestly, if I was able to easily purchase a pre-built FreeNAS machine by ixSystems <strong>in Europe</strong> … I might have gone this route…but since I wasn’t I ended up looking at a different solution.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></description>
    <pubDate>Mon, 19 Jun 2023 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/new-homelab-machine/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>How I do backups</title>
    <link>https://denibertovic.com/posts/how-i-do-backups/</link>
    <description><![CDATA[<p>Backups are important! I don’t just mean for production environments either - you should back up your laptop regularly and in an automated fashion.</p>
<p>I’ve started doing regular backups of my machines after suffering 2 consecutive drive failures with one of my laptops. After something like that (which BTW never happened since XD) you kind of realize that backups are not just a nice thing to have, but a hard requirement. In this blog post I’ll outline how I’ve set up my on-site backup solution.
<!--more--></p>
<p>When two of my hard drives failed I fortunately didn’t loose a lot of data. I used to rsync some of my stuff between 2 machines (but not very often), my work stuff was pushed to remote git repos, I used various cloud storages for some stuff etc. Still, there were some things that I lost with the drives and it was annoying and time consuming to get up and running again. This should not be the case.</p>
<p>After the second incident I decided I would do something about it and set up an on-site backup solution. Why on-site? Well, performance mostly, but I also didn’t trust any of the cloud based solutions (I use <a href="https://www.tarsnap.com/">tarsnap</a> now but that’s another blog post :)). So - I bought a NAS<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> and went looking for a backup tool.</p>
<p><code>NOTE</code>: I’ve been sitting on this blog post for 3 years. So bear in mind that some things might have changed since then which I didn’t bother to get up to date on. However, I’m still happily using the method described in this post.</p>
<h2 id="requirements">Requirements</h2>
<p>While searching the web for potential candidates I put together a couple of requirements:</p>
<ul>
<li>The tool needs to be OSS</li>
<li>Backups needs to be encrypted</li>
<li>Backups need to be incremental</li>
<li>Backups need to reside on a local NAS I have running in my homelab</li>
<li>The backups needs to happen regularly without needing my input from a human</li>
</ul>
<p><code>NOTE</code>: Since I only run Linux machines I didn’t care if the tool was cross platform.</p>
<h2 id="borg-backup">Borg backup</h2>
<p>I quickly found that <a href="https://borgbackup.readthedocs.io/en/stable/">borg-backup</a> would be a great fit for my use case. It’s a very widely known (and used) tool that seemed like it checked all of the boxes above (except the last one…but we’ll get to that).</p>
<p>Borg was fairly easy to set up and run. The documentation is excellent. I did fiddle a bit with the <code>ignorelist</code> (so that it would not backup useless files like caches etc) but after that it basically just worked.</p>
<p>Borg is designed as a client/server system. This means that you have to install the server component on your storage device (where the backups are going) so that the client (ie. your laptop) can communicate with it. You can read more about it <a href="https://borgbackup.readthedocs.io/en/stable/faq.html#what-is-the-difference-between-a-repo-on-an-external-hard-drive-vs-repo-on-a-server">here</a>.
At the time my NAS didn’t have an official/supported way of installing borg<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> so I opted for the client-only approach where I just mount the backup folder via NFS and let the client do it’s thing. NFS is pretty battle tested and I didn’t see this as a huge
downside. Although performance does suffer a bit, it has generally worked well so far.</p>
<p>To run it automatically I wrapped the entire thing in a <a href="https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups">shell script</a> and set it to run daily at a certain time - and the simplest possible way was to use cron to do that.</p>
<p>This is the lowest possible amount of effort I needed to make to get an “out of sight out of mind” backup system working without me having to babysit it.</p>
<h2 id="problems">Problems</h2>
<p>Basically borg was checking all the boxes except the last one. How do I run backups automatically without needing my input or attention.</p>
<p>There were 3 main problems with the above approach:</p>
<ul>
<li>What if my laptop was not turned on during the time of day that the backup is scheduled</li>
</ul>
<p>The simplest possible way to solve this is to try and re-run the backup scripts every so often - between the hours when the laptop is most likely to be running. It was important that the backups didn’t happen in the middle of the day because they can sometimes be a bit CPU intensive if I’m doing X other things (encryption is client side).</p>
<p>I decided to just re-run the script every hour between hours 19h and 23h. I figured this would give the backups enough chance to run (given my laptop usage patterns).</p>
<pre class="shell"><code>30 19-23 * * * /path/to/my_backups.sh</code></pre>
<p>Side note: check out <a href="https://crontab.guru/#30_19-23_*_*_*">crontab.guru</a> for a more detailed explanation of the cron syntax.</p>
<p>This solution required that I change my shell script in such a way that it first <strong>checks</strong> if a backup for the day has already been done. It not, then run the backup, otherwise just exit. Doesn’t seem that big of a deal right?</p>
<p>Well, even small changes in shell scripts can make them more complex and less maintainable.</p>
<p>In retrospect a better solution would most likely be if I had just used <a href="https://man7.org/linux/man-pages/man8/anacron.8.html">anacron</a> which is more suited for systems that don’t run 24/7.</p>
<ul>
<li>What if I turn off my laptop in the middle of a backup?</li>
</ul>
<p>I never really investigated too much what would happen here. I assume if it’s a clean shutdown borg would just cleanly abort and do a backup in the next backup window. Unclean shutdowns are another thing. Usually you end up with a lock file that didn’t get cleaned up and no future backups can run until you <a href="https://borgbackup.readthedocs.io/en/stable/usage/lock.html?highlight=lock#borg-break-lock">manually sort it out</a>.</p>
<p>I chose to ignore this and just make sure that the human (me) is always notified when a backup starts and when it finished. On Linux this is pretty easy with <code>libnotify</code>. I use <a href="https://manpages.ubuntu.com/manpages/xenial/man1/notify-send.1.html">notify-send</a> to send the start and finish notifications:</p>
<pre class="shell"><code>notify-send -u normal &quot;backup started...&quot;
notify-send -u normal &quot;backup finished!&quot;</code></pre>
<p>This would catch my attention and I would know not to power off the machine if the backups haven’t finished yet. If I somehow forgot that I dismissed the notification my <a href="https://xmonad.org/">window manager</a> makes it really easy to cycle through old notifications and verify if the backup was done. It’s not perfect but it works for me.</p>
<ul>
<li>What if I’m not connected to my LAN and therefore cannot mount my NFS drive?</li>
</ul>
<p>This was the biggest issue with the above approach. I could just let the cronjob fail as it would be picked up later anyway - but I didn’t like that.</p>
<p>I briefly thought about extending my shell script wrapper with the ability to check if I’m connected to my home WiFi and <strong>only then</strong> start the backup. This in turn made the shell script even more complex and hard to maintain. I don’t know about you but when I have a lot of conditional logic in shell scripts things start to fall apart really quickly.</p>
<p>As a friend of my <a href="https://github.com/denibertovic/pid1/pull/1#issuecomment-979443101">often</a> <a href="https://github.com/elixir-lang/elixir/issues/11114#issue-942584610">says</a> “nobody can write even shell one-liners without bugs” - and I tend to agree with him!</p>
<p>I briefly considered rewriting it in Python - and that would have been fine - but I was <a href="https://twitter.com/denibertovic/status/1004291881554071552">writing most of my tooling in Haskell</a> at that time.</p>
<p>And so <a href="https://github.com/denibertovic/borg-runner">borg-runner</a> was born!<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a></p>
<p>Now, instead of a brittle shell script, I had a proper binary I can configure with a config file (yes it’s YAML). I still use cron to run it though. I was thinking of making a proper daemon out of it but decided against it. It was too complicated for not much gain.</p>
<p>And there you have it. Backups for folks like me who don’t yet use ZFS! :D I jest but, I’ll likely be migrating my machines to ZFS starting next year so I’m not sure how much I’ll need these tools but we’ll see.</p>
<p>I mentioned at the beginning that I also use Tarsnap for off-site backups (as well as for my servers). Why have both? Redundancy is always a good thing - if my local backups get corrupted I can always turn to my cloud backups or vice versa. Also, you never really know if your backups work if you don’t test restoring from a backup often. I still don’t do that part very often so I figured it’s best to have two systems just in case.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>It’s just a 2 bay Synology DS215+ device.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>Synology runs it’s own customized linux based OS. It’s mostly great but it hides away Linux from you. As a
consequence you can’t just <code>apt-get install</code> whatever you want but need to go through their own “application store”.
A friend recently mentioned that borg is available under third party packages. That’s still not officially
supported and I didn’t want to break anything on my NAS. It’s an appliance and not a general purpose Linux
machine so I treat it as such.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>I had just watched the new Blade Runner remake so that’s where the pun comes from. :D<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></description>
    <pubDate>Thu, 09 Dec 2021 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/how-i-do-backups/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Haskell Showroom: Switching between different AWS accounts</title>
    <link>https://denibertovic.com/posts/switching-between-different-aws-accounts/</link>
    <description><![CDATA[<p>In my <a href="https://denibertovic.com/posts/haskell-showroom-how-to-switch-between-kubernetes-clusters/">previous post</a> I
talked about <a href="https://github.com/denibertovic/denv">denv</a> and how I switch between different Kubernetes clusters.</p>
<p>I also talked about the importance of being explicit about which environment you’re currently working on, easily
switching between and deactivating an environment so that we don’t run accidental commands in the wrong
context.</p>
<p>Continuing <a href="https://denibertovic.com/categories/haskell-showroom/">this series</a>, in this post I will talk about how to effectively switch between different AWS accounts.</p>
<!--more-->
<h2 id="the-problem">The problem</h2>
<p>In my “day to day” I usually interact with multiple AWS accounts tied to various clients. I usually do this from the command line
while using tools like <a href="https://www.terraform.io/">terraform</a>, <a href="https://www.packer.io/">packer</a>,
<a href="https://github.com/kubernetes/kops">kops</a>, <a href="https://aws.amazon.com/cli/">AWS CLI</a> etc.</p>
<p>These AWS accounts are usually set up with some best practices in mind:</p>
<ul>
<li>Mandatory <a href="https://aws.amazon.com/iam/details/mfa/">MFA</a></li>
</ul>
<p>This means that we require each user to sign into the AWS Console and set up a MFA device. This can be a virtual device
like Google Authenticator or it can be a physical device like a Yubikey.</p>
<ul>
<li>No direct permissions are assigned to users</li>
<li>Instead users have to <a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html">assume a role</a>
depending on which permissions they need for the task at hand</li>
</ul>
<p>At <a href="https://www.fpcomplete.com">work</a> we developed a <a href="https://registry.terraform.io/modules/fpco/foundation/aws/0.8.1/submodules/setup-meta-infrastructure">terraform module</a> that sets all of this up for us.</p>
<ul>
<li>Use of short lived <a href="https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html">STS</a> session credentials</li>
</ul>
<p>What this means is that we use “short term credentials” from AWS STS which we then use to request (and refresh) role
based credentials if we’re assuming a role. For certain endpoints AWS requires MFA when using STS so <code>denv</code> makes sure to
enforce it. This means that the users <code>aws_access_key_id</code> and <code>aws_secret_access_key</code> are only used at the beginning to fetch the
temporary STS credentials. The STS credentials are then cached and used to assume a role and fetch another set of
credentials that have permission to actually do things on AWS (like create EC2 instances, RDS databases and similar).
The user is prompted for the MFA token during the initial request and not again during the entire cache period.</p>
<p>Session credentials expire after 36 hours<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> and role credentials expire by default after 1 hour <a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>, after which they need to be
refreshed.<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a></p>
<p>When defining access credentials for AWS accounts you’re going to be dealing with 2 files:</p>
<ul>
<li><code>~/.aws/config</code></li>
</ul>
<p>and</p>
<ul>
<li><code>~/.aws/credentials</code></li>
</ul>
<p>These files can be configured using the <a href="https://aws.amazon.com/cli/">AWS CLI</a> but since it’s a straight forward
ini format I usually edit them in my editor.</p>
<h2 id="the-non-solution">The non-solution</h2>
<p>One could argue that you can just prefix each command with the <code>AWS_PROFILE=myprofile</code> environment variable and be done
with it. This is true, and most tools do support this way of specifying which AWS profile to use. However, the trouble is
it only works for the naive approach, just exporting the aws access key and secret key. It does <strong>not</strong> work if we’re also using MFA and
Role Assuming.</p>
<h2 id="the-solution">The solution</h2>
<p>First things first.</p>
<p>If you have already setup your <code>~/.aws/config</code> and <code>~/.aws/credentials</code> files, make sure to delete (or rename) the <code>[default]</code> entry.
We don’t want to have a default profile. Instead, we want each profile to have its own unique name
that forces us to specify the profile we’re using.</p>
<p><code>NOTE</code>: Make sure to set the correct permissions: <code>chmod 600 ~/.aws/config &amp;&amp; chmod 600 ~/.aws/credentials</code></p>
<p>Now let’s look at 3 use-cases that you might encounter.</p>
<h3 id="case-1-mfa-and-assuming-roles">Case 1: MFA and assuming roles</h3>
<p>In this case your configuration files will look like this:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">#### ~/.aws/config ####</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">[profile project1]</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dt">region</span><span class="ot">=</span><span class="st">us-east-1</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">[profile project1-prod-admin]</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">role_arn</span><span class="ot">=</span><span class="st">arn:aws:iam::....:role/admin</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="dt">mfa_serial</span><span class="ot">=</span><span class="st">arn:aws:iam::....:mfa/deni</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="dt">source_profile</span><span class="ot">=</span><span class="st">project2</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">##### ~/.aws/credentials #####</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="kw">[project1]</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="dt">aws_access_key_id</span><span class="ot">=</span><span class="st">.....</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="dt">aws_secret_access_key</span><span class="ot">=</span><span class="st">.....</span></span></code></pre></div>
<p>Alright, let’s break down what happens when we run the following command:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">~</span> denv aws <span class="at">-p</span> project1-prod-admin</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">Enter</span> MFA code for device <span class="st">&quot;arn:aws:iam::...:mfa/deni&quot;</span>: xxxx</span></code></pre></div>
<p>Once we enter our MFA code (using Google Authenticator or a similar app):</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">aws</span><span class="kw">|</span><span class="ex">project1-prod-admin</span>  ~  export <span class="kw">|</span> <span class="fu">grep</span> AWS</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="va">AWS_ACCESS_KEY_ID</span><span class="op">=</span>xxxx</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="va">AWS_DEFAULT_REGION</span><span class="op">=</span>us-east-1</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="va">AWS_REGION</span><span class="op">=</span>us-east-1</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="va">AWS_SECRET_ACCESS_KEY</span><span class="op">=</span>xxxx</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="va">AWS_SECURITY_TOKEN</span><span class="op">=</span><span class="st">&#39;xxxxx&#39;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="va">AWS_SESSION_TOKEN</span><span class="op">=</span><span class="st">&#39;xxxxxx&#39;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="va">_DENV_SET_VARS</span><span class="op">=</span>AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_SECURITY_TOKEN,AWS_DEFAULT_REGION,AWS_REGION,_OLD_DENV_PS1,_DENV_SET_VARS</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="ex">aws</span><span class="kw">|</span><span class="ex">project1-prod-admin</span>  ~  aws sts get-caller-identity</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="kw">{</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;UserId&quot;</span><span class="ex">:</span> <span class="st">&quot;...&quot;</span>,</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;Account&quot;</span><span class="ex">:</span> <span class="st">&quot;...&quot;</span>,</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;Arn&quot;</span><span class="ex">:</span> <span class="st">&quot;arn:aws:sts::...:assumed-role/admin/...&quot;</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div>
<p>We see that in the newly spawned shell we have all the required environment variables set that all the standard tools
will look for. As with the kubernetes example in the previous post, the prompt is annotated with the account name so that
we don’t forget which account we’re working on.</p>
<p><code>NOTE</code>: <code>aws sts get-caller-identity</code> is just an example command. You might be more interested in running something like
<code>aws ec2 describe-instances</code> or similar.</p>
<p>Once we’re done, we can run <code>denv deactivate</code> to make sure that all the environment variables are unset from the current
shell. This way we make sure that if we run any other AWS CLI command we’re not accessing an account by mistake.</p>
<p>This form of using the <code>denv aws</code> subcommand is called the <code>eval form</code>. Since these credentials expire I recommend using
the <code>exec form</code> that looks like this:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">~</span> denv aws <span class="at">-p</span> project1-prod-admin <span class="at">--</span> aws sts get-caller-identity</span></code></pre></div>
<p>The <code>exec form</code> makes sure to execute your command in the required “context” with all the above variables set and once
it’s done, none of them get leaked to your currently running shell.</p>
<p>This form is preferable because it will always make sure to fetch fresh credentials if they’re needed. This is not the case
with a long running shell prompt because the credentials exposed in that shell might
be stale.</p>
<p>Let’s break down what <code>denv aws -p project1-prod-admin</code> does:</p>
<ol type="1">
<li>Parse and fetch the configuration and credentials from the standard files mentioned above</li>
<li>Check if we need to assume a role</li>
<li>If we do, check if we have valid cached credentials for that role</li>
<li>If we don’t have valid cached credentials check if we have valid cached session credentials that we’re using to fetch
the above role credentials</li>
<li>If we don’t have session credentials request new STS session credentials</li>
<li>Request new role credentials with the above session credentials</li>
<li>Export the role credentials into the current shell or run the supplied command while making
the credentials available.</li>
</ol>
<p>These series of steps are taken with each <code>denv aws</code> run. The cached credentials are stored in <code>~/.aws-env/</code>.</p>
<h3 id="case-2-assuming-a-role-but-without-mfa">Case 2: Assuming a role but without MFA</h3>
<p>In this case your configuration would look like this:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">#### ~/.aws/config ####</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">[profile project2]</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="dt">region</span><span class="ot">=</span><span class="st">us-west-2</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">[profile project2-prod-admin]</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="dt">role_arn</span><span class="ot">=</span><span class="st">arn:aws:iam::....:role/admin</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="dt">source_profile</span><span class="ot">=</span><span class="st">project2</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co">##### ~/.aws/credentials #####</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="kw">[project2]</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="dt">aws_access_key_id</span><span class="ot">=</span><span class="st">.....</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="dt">aws_secret_access_key</span><span class="ot">=</span><span class="st">.....</span></span></code></pre></div>
<p>Activate this environment by running: <code>denv -p project2-prod-admin</code>.
The main difference between this case and <code>Case 1</code> is that we’re not going to use intermediate session credentials from
STS. Instead, we’re going to use the raw credentials from <code>~/.aws/credentials</code> to fetch our temporary role credentials. The
reason we’re skipping the session credentials is because AWS requires Multi Factor Authentication to access any AWS IAM
APIs. <a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></p>
<h3 id="case-3-using-raw-credentials">Case 3: Using raw credentials</h3>
<div class="sourceCode" id="cb6"><pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">#### ~/.aws/config ####</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="kw">[profile project3]</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="dt">region</span><span class="ot">=</span><span class="st">us-west-2</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="co">##### ~/.aws/credentials #####</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="kw">[project3]</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="dt">aws_access_key_id</span><span class="ot">=</span><span class="st">.....</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="dt">aws_secret_access_key</span><span class="ot">=</span><span class="st">.....</span></span></code></pre></div>
<p>The last case is the “naive case” where <code>denv</code> is only exporting the <code>aws_access_key_id</code> and <code>aws_secret_access_key</code>
defined in the above configuration file. It’s not talking to the AWS API and fetching any short term or role
credentials of any sort. I find <code>denv</code> is still useful in this case since it explicitly shows me which account I’m working
on. Using the <code>eval form</code> for this case is safe and saves us a bunch of keystrokes (which I’m a big fan of).</p>
<h2 id="how-it-works">How it works</h2>
<p>We’re using Haskell and some awesome libraries but the most interesting one is <a href="http://hackage.haskell.org/package/amazonka">amazonka</a>
which we use to talk to the AWS APIs and fetch temporary session and role credentials.</p>
<p>The logic I outlined above is encoded like so:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>env <span class="ot">&lt;-</span> runMaybeT <span class="op">$</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">MaybeT</span> (runRIO awsEnv <span class="op">$</span> <span class="fu">maybe</span> nop (getFromRoleCache p) roleArn) <span class="op">&lt;|&gt;</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">MaybeT</span> (runRIO awsEnv <span class="op">$</span> <span class="fu">maybe</span> nop (<span class="fu">maybe</span> nop1 (getSTSWithRole awsenv region&#39; lgr p sourceProfile) mfaSerial) roleArn) <span class="op">&lt;|&gt;</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">MaybeT</span> (runRIO awsEnv <span class="op">$</span> <span class="fu">maybe</span> nop (getFromSessionCache p sourceProfile) mfaSerial) <span class="op">&lt;|&gt;</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">MaybeT</span> (runRIO awsEnv <span class="op">$</span> <span class="fu">maybe</span> nop (getSTS awsenv region&#39; lgr p sourceProfile) mfaSerial) <span class="op">&lt;|&gt;</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>  <span class="dt">MaybeT</span> (runRIO awsEnv <span class="op">$</span> <span class="fu">maybe</span> nop (useRawWithRole awsenv region&#39; lgr p sourceProfile) roleArn) <span class="op">&lt;|&gt;</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt">MaybeT</span> (runRIO awsEnv <span class="op">$</span> useRaw key&#39; secret&#39;)</span></code></pre></div>
<p>Fetching STS credentials is fairly straightforward:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">mkStsSessionTokenRequest ::</span> <span class="dt">MfaSerial</span> <span class="ot">-&gt;</span> <span class="dt">STS.GetSessionToken</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">STS.GetSessionToken</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>mkStsSessionTokenRequest (<span class="dt">MfaSerial</span> mfaSerial) st <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  req <span class="ot">&lt;-</span> <span class="kw">do</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>        tokenCode <span class="ot">&lt;-</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>          promptLine <span class="op">$</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>          <span class="st">&quot;Enter MFA code for device &quot;</span> <span class="op">&lt;&gt;</span> <span class="fu">show</span> mfaSerial <span class="op">&lt;&gt;</span> <span class="st">&quot;: &quot;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>        <span class="fu">return</span> <span class="op">$</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>          st <span class="op">&amp;</span> STS.gstTokenCode <span class="op">.~</span> (<span class="dt">Just</span> <span class="op">$</span> T.pack tokenCode) <span class="op">&amp;</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>          STS.gstSerialNumber <span class="op">.~</span> (<span class="dt">Just</span> mfaSerial) <span class="op">&amp;</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>          STS.gstDurationSeconds <span class="op">.~</span> (<span class="dt">Just</span> <span class="op">$</span> <span class="fu">fromIntegral</span> defaultSessionDurationSeconds)</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>  <span class="fu">return</span> req</span></code></pre></div>
<p>And actually running the request:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ot">execStsRequest ::</span> <span class="dt">AWS.AWSRequest</span> a <span class="ot">=&gt;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>     <span class="dt">AWS.Env</span> <span class="ot">-&gt;</span> <span class="dt">AWS.Logger</span> <span class="ot">-&gt;</span> <span class="dt">AWS.Region</span> <span class="ot">-&gt;</span>  a <span class="ot">-&gt;</span> <span class="dt">IO</span> (<span class="dt">AWS.Rs</span> a)</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>execStsRequest awsenv lgr region req <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  ret <span class="ot">&lt;-</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    runResourceT <span class="op">$</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    AWS.runAWS (awsenv <span class="op">&amp;</span> AWS.envLogger <span class="op">.~</span> lgr) <span class="op">$</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    AWS.within region <span class="op">$</span> AWS.send req</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">return</span> ret</span></code></pre></div>
<p>Once we have the credentials it’s just a matter of going to the entire process of exporting them into the current shell
which I outlined in more detail in the previous post.</p>
<h2 id="summary">Summary</h2>
<p>We’ve seen how <code>denv aws</code> helps us switch securely between multiple AWS accounts while using short term credentials.
Using short lived credentials is encouraged as they are much easier to replace in case of a leak. They usually expire
before it becomes a problem.
It’s also good that the raw credentials that the user gets from the AWS Console “travel the wire” much less in this
scenario (even though it might be over a secure line).</p>
<p>Haskell helped me add this feature to <code>denv</code> fairly easily and while the initial version had even fewer lines than
its bash counterpart<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a> it was much easier to maintain and reason about.</p>
<p>This feature is currently still in beta but I’ve been using it for a few months <strong>every day</strong> and have not found any bugs so
far. I will likely remove the “beta” stamp once I allow for more configuration regarding cache expiry times.</p>
<p>Head over to the <a href="https://github.com/denibertovic/denv/releases">repo’s releases page</a> and try it out!</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>In the current version of <code>denv</code> this is hardcoded but future versions will make this value configurable.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>As with session credentials this will be configurable in a future release.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>Role credentials are valid between 1 and 12 hours and AWS lets us configure that on a per role basis.
This is useful since for some important roles you might wish for the credentials to expire sooner but for certain
long running tasks it might be useful to be a bit more lenient.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>This use-case (along with pruning bash from my life) was the main reason why I started investigating how to solve
this issue with Haskell, since the bash script we were using at work could not fix this issue easily.
See <a href="https://github.com/fpco/devops-helpers/issues/3">this github issues</a>.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5"><p>Not that this is a good measure or anything. Especially since the number of lines grew as I refined it more.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></description>
    <pubDate>Sun, 12 May 2019 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/switching-between-different-aws-accounts/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Haskell Showroom: How to switch between multiple kubernetes clusters and namespaces</title>
    <link>https://denibertovic.com/posts/haskell-showroom-how-to-switch-between-kubernetes-clusters/</link>
    <description><![CDATA[<p>A while ago I <a href="https://twitter.com/denibertovic/status/1004291881554071552">decided</a> that I was done writing anything in
bash. I just won’t do it anymore! Instead I’ve started writing everything, even the smallest of tools, in Haskell.</p>
<p>I just don’t think it’s possible to write good and maintainable software with bash, no matter how simple the tool might
be. In my opinion the main benefit with bash is that it’s so easy to distribute to end users. There’s no special
installation or configuration, you just download a script and run it. With haskell I’m aiming towards distributing
statically linked binaries (although in simple cases dynamically linked binaries work just fine as well).<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>This series of posts titled “Haskell Showroom” are my attempt at showcasing what Haskell can be used for.
It’s an attempt to answer the question I get asked a lot:</p>
<p><code>"What is Haskell a good fit for?"</code></p>
<p>Haskell has a very good reputation when it comes to writing compilers but it’s a general purpose programming language
and it really can be used for all sorts of things. I mainly use it for writing web apps and CLI tools.</p>
<p>In the first post in this series I will talk about my tool called <a href="https://github.com/denibertovic/denv">denv</a>, which
is a tool to help me “manage environments”. While it helps me manage (and switch between) various environments,
in this post I will focus on how it helps with switching between multiple kubernetes clusters in a sane and simple way.
<!--more--></p>
<h2 id="the-problem">The problem</h2>
<p>I work with a bunch of kubernetes clusters. Some at work for various clients, and some for personal projects.
It’s sometimes hard and confusing to switch between these clusters (and <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/">namepsaces</a> within a cluster)
while <a href="https://blog.travis-ci.com/2018-04-03-incident-post-mortem">confidently knowing</a> which cluster (and namespace) you’re currently working on.</p>
<p>The “default” way kube does this is by having all the configurations for all the clusters
written into one single yaml file: <code>~/.kube/config</code>.
Then you have to use a combination of <code>kubectl config set-context</code> and <code>kubectl config use-context</code> which will in turn mutate the global config file, changing the <code>current-context</code> field.
This, to me, feels clumsy and does not address the part where we confidently know which cluster (and namespace) we’re
working on.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<p>There are tools that try to address this, such as: <a href="https://github.com/ahmetb/kubectx">kubectx</a>, <a href="https://github.com/ahmetb/kubectx">kubens</a> and <a href="https://github.com/jonmosco/kube-ps1">kube-ps1</a>.
However from what I can tell they all fall short of a couple of things:</p>
<ul>
<li>They still rely on a single global config file that they mutate using <code>kubectl</code> behind the scenes.</li>
</ul>
<p>There’s something about “mutating global state” that just does not sit well with me. <a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>
Apart from that it’s a hard requirement for me to be able to separate different projects I’m working on.</p>
<ul>
<li>All of these appear to be written in bash</li>
</ul>
<p>I think the main reason tools like this get written in bash is because it’s easy to inject environment variables in the
currently running shell. If you write a custom tool it is going to run as a child process of your shell and so it’s not
possible for a child process to change environment variables for the parent process (your shell). Later in the post
I’ll show a way how to work around this.</p>
<h2 id="the-proposed-solution">The proposed solution</h2>
<p>First things first, let’s get rid of the global config file:</p>
<pre><code># Don&#39;t remove the old file. Back it up just in case you need to extract
# auth info for each cluster and break it up into separate files
mv ~/.kube/config ~/.kube/config.old

touch ~/.kube/config
chmod 400 ~/.kube/config`</code></pre>
<p>This should prevent any tools from the k8s ecosystem from modifying that file.
However, since we can’t use the default file we have to set the <code>KUBECONFIG</code> environment variable to something
like <code>~/.kube/customer1-prod.yaml</code>. All of the tooling from the k8s ecosystems (like <code>kubectl</code>, <code>kops</code>, <code>helm</code> etc) respects
this environment variable.
This is especially useful when creating a cluster with <code>kops</code>, given that it saves a configuration file for you once the cluster is created,
and so it will write it to a new file rather than mutate the one single file.</p>
<p>So, it’s crucial to use <code>export KUBECONFIG=~/.kube/customer1-prod.yaml</code> before running any <code>kubectl</code> (or similar)
commands. This will make sure that we isolate each cluster in it’s own config file.</p>
<p>There’s a problem though. This seems tedious and does not show us which cluster we’re currently working on in our terminal.
Also, we have to prepend <code>--namespace=&lt;NAMESPACE&gt;</code> to each <code>kubectl</code> command.</p>
<h2 id="using-denv">Using “denv”</h2>
<p>Let’s install <a href="https://github.com/denibertovic/denv/releases">denv</a>.</p>
<p>Add the following alias to your <code>.bashrc</code> or <code>.zshrc</code>:</p>
<pre><code>alias k=&#39;kubectl --namespace=${KUBECTL_NAMESPACE:-default}&#39;</code></pre>
<p>Add this hook at the end:</p>
<pre><code>eval &quot;$(denv hook ZSH)&quot;</code></pre>
<p>or <code>eval "$(denv hook BASH)"</code> for bash.</p>
<p>Run: <code>denv kube -p ~/.kube/customer1-prod.yaml -n kube-system</code> to activate the <code>customer1</code> cluster within the
<code>kube-system</code> namespace. Observe how we are told in the prompt which cluster and namespace we’re working on.</p>
<div>
<img src="/media/posts/denv-kube-example.png" alt="denv-kube-example" />
</div>
<p><br/></p>
<p>You may be thinking how the same thing can probably be done with an <code>.envrc</code> file and <a href="https://direnv.net/">direnv</a> that just auto exports the above variables.
While I have a lot of respect for direnv, and I still find inspiration for a lot of stuff from their repo, I have 2 issues with it:</p>
<ol type="1">
<li><p>I don’t like the fact that changing to a different directory will automatically inject things into
my environment and automagically change my configuration for who knows what.
I much prefer the semantics of the <code>workon</code> command from Python’s <a href="https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html">virtualenvwrapper</a> where you have to <strong>explicitly activate</strong> and <strong>deactivate</strong> an environment before anything
is changed in your running session. And it works independent of which directory you’re currently in. <a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></p></li>
<li><p>It requires that I change my current working directory to a specific directory where the <code>.envrc</code> file
is located. This does not work well with some of my workflows (it would require duplicating the .envrc file in
a couple of places).</p></li>
</ol>
<p>What <code>denv kube</code> brings to the table is a way that forces you to activate a certain cluster/namespace before being able
to do anything with it. It forces you to deactivate that specific cluster if you’re done working with it. This
will make sure to unset the above environment variables so there is no way that you have a long running terminal
somewhere that has access to a cluster you’re not aware of.</p>
<div>
<img src="/media/posts/denv-kube-example-deactivate.png" alt="denv-kube-example-deactivate" />
</div>
<h2 id="how-it-works">How it works</h2>
<p>Let’s define our cluster (I call it project) and namespace that we will parse from the <code>-p</code> and <code>-n</code> flags
respectively:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">KubeProjectName</span> <span class="ot">=</span> <span class="dt">String</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">KubeNamespace</span> <span class="ot">=</span> <span class="dt">String</span></span></code></pre></div>
<p>We also need a way to define what the list of kube specific environment variables we’re dealing with:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">KubeVariable</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">KubeConfig</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">KubeConfigShort</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">KubectlNamespace</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>)</span></code></pre></div>
<p>We will provide a <code>Show</code> typeclass instance so that we can convert these to a String later on.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Show</span> <span class="dt">KubeVariable</span> <span class="kw">where</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">show</span> <span class="dt">KubeConfig</span> <span class="ot">=</span> <span class="st">&quot;KUBECONFIG&quot;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">show</span> <span class="dt">KubeConfigShort</span> <span class="ot">=</span> <span class="st">&quot;KUBECONFIG_SHORT&quot;</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">show</span> <span class="dt">KubectlNamespace</span> <span class="ot">=</span> <span class="st">&quot;KUBECTL_NAMESPACE&quot;</span></span></code></pre></div>
<p>Apart from kube specific variables we have some special variables that we will track:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SpecialVariable</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Prompt</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">OldPrompt</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">DenvSetVars</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="kw">deriving</span> (<span class="dt">Eq</span>)</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Show</span> <span class="dt">SpecialVariable</span> <span class="kw">where</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">show</span> <span class="dt">Prompt</span> <span class="ot">=</span> <span class="st">&quot;PS1&quot;</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>  <span class="fu">show</span> <span class="dt">OldPrompt</span> <span class="ot">=</span> <span class="st">&quot;_OLD_DENV_PS1&quot;</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>  <span class="fu">show</span> <span class="dt">DenvSetVars</span> <span class="ot">=</span> <span class="st">&quot;_DENV_SET_VARS&quot;</span></span></code></pre></div>
<p>Great, now that we have all these types we need a way to tell our program if we want to <code>Set</code> or <code>Unset</code> a given
variable. This is important since we also want to be able to <code>deactivate</code> an environment, that is to say, unset all of the environment
variables that we’ve injected into our shell session.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">DenvVariable</span> <span class="kw">where</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Set</span><span class="ot"> ::</span> (<span class="dt">Eq</span> a, <span class="dt">Show</span> a, <span class="dt">Typeable</span> a) <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">T.Text</span> <span class="ot">-&gt;</span> <span class="dt">DenvVariable</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Unset</span><span class="ot"> ::</span> (<span class="dt">Eq</span> a, <span class="dt">Show</span> a, <span class="dt">Typeable</span> a) <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">DenvVariable</span></span></code></pre></div>
<p>Let’s go ahead and create our environment.
We’ll use the <code>mkKubeEnv</code> function, which will take the project name, namespace
and create the environment:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ot">mkKubeEnv ::</span> <span class="dt">KubeProjectName</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">KubeNamespace</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>mkKubeEnv p n <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>  checkEnv</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  exists <span class="ot">&lt;-</span> doesFileExist p</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  unless exists (die <span class="op">$</span> <span class="st">&quot;ERROR: Kubeconfig does not exist: &quot;</span> <span class="op">++</span> p)</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> p&#39; <span class="ot">=</span> takeFileName p</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> n&#39; <span class="ot">=</span> fromMaybe <span class="st">&quot;default&quot;</span> n</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> env <span class="ot">=</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>        withVarTracking</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>          <span class="dt">Nothing</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>          [ <span class="dt">Set</span> <span class="dt">KubeConfig</span> <span class="op">$</span> T.pack p</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>          , <span class="dt">Set</span> <span class="dt">KubeConfigShort</span> <span class="op">$</span> T.pack p&#39;</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>          , <span class="dt">Set</span> <span class="dt">KubectlNamespace</span> <span class="op">$</span> T.pack n&#39;</span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a>          , <span class="dt">Set</span> <span class="dt">OldPrompt</span> ps1</span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a>          , <span class="dt">Set</span> <span class="dt">Prompt</span> <span class="op">$</span></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a>            mkEscapedText <span class="st">&quot;k8s|$KUBECTL_NAMESPACE|$KUBECONFIG_SHORT $PS1&quot;</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a>          ]</span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a>  writeRc env</span></code></pre></div>
<p><code>writeRc</code> is a function with the type signature <code>writeRc :: [DenvVariable] -&gt; IO ()</code> and it does 2 things:</p>
<ol type="1">
<li>It converts <code>"Set KubeConfig "~/.kube/customer1-prod.yaml"</code> to <code>export KUBECONFIG=~/.kube/customer1-prod.yaml</code> or
<code>Unset KubeConfg</code> to <code>unset KUBECONFIG</code>.</li>
<li>Writes all of these environment variables to <code>~/.denv</code>.</li>
</ol>
<p>Once the <code>~/.denv</code> file is in place the <code>eval "$(denv hook BASH)"</code> that we put in our <code>.bashrc</code> will check for it and
inject the environment variables into the current shell session.
This is actually a very ingenious way of injecting variables from a child process into the parent process (denv -&gt; shell).
All credit for the idea goes to <a href="https://github.com/direnv/direnv">direnv</a> authors.</p>
<p>If you’re interested in the details please refer to the source code in the <a href="https://github.com/denibertovic/denv">github
repo</a>.</p>
<h2 id="summary">Summary</h2>
<p>In this post I’ve showed you how I go about switching between multiple kubernetes clusters
while averting potential disasters and confusions about which cluster I’m currently working on.</p>
<p>I used Haskell to solve this particular problem because Haskell allows me to easily extend this tool with
different functionality, furiously refactor without fear and keep me from shooting myself in the foot by
not treating everything as a String <a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a>.</p>
<p>This code is far from perfect and could definitely use more type safety but hopefully I was able to demonstrate
how Haskell can be <strong>not</strong> scary (no fancy type gymnastics) and very much usable in an “imperative” way if so required to get the job done
(and move on).</p>
<p>In the second post in this series I will talk about <code>denv aws</code> and how to switch between multiple AWS accounts, use
temporary credentials, multi factor authentication and key rotation.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>While static compilation can be a bit of a chore with Haskell, if you don’t have any special C dependencies
even dynamically linked binaries will most likely work on various linux distros (since most of them have the necessary <code>lib*-dev</code> packages installed already).<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>I’ve observed this to be a very big stumbling block for folks starting out with kubernetes.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>One of the main reasons why I like Haskell and Functional Programming in general; It eliminates all sorts of bugs
with regards to global mutable state.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>One of the main reasons why I like Haskell and Functional Programming in general; It eliminates all sorts of bugs
with regards to global mutable state.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5"><p>I can’t count the number of times where I debugged a typo in Bash or Python.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></description>
    <pubDate>Mon, 14 Jan 2019 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/haskell-showroom-how-to-switch-between-kubernetes-clusters/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Haskell and Docker: Down the rabbit hole and back</title>
    <link>https://denibertovic.com/posts/haskell-and-docker-down-the-rabbit-hole-and-back/</link>
    <description><![CDATA[<p>For the past couple of years I’ve been learning Haskell, and while I enjoy reading new materials in forms
of books, papers, blogs posts and even tweets, thankfully I quickly came to realize that the best way to learn is to
build things.</p>
<p>That’s not to say I just jumped right in. I spent a long time just playing around with the language, exploring various
language features, libraries and getting to know the ecosystem in general. Not doing anything serious <a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<p>At the time that was a bit more painful than now because now-a-days there’s a lot more resources and organized materials
around. The community has really stepped up! <a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<p><em>Anyhow…</em> Building things. I’ve been using docker since it was released and have grown from resident docker fan-boy to
resident expert over time. Also I’ve been involved with the <a href="https://github.com/docker/docker-py">Python API wrapper</a> since the early days so it made sense
to try and write a Haskell API wrapper. At the time that was the closest I came to web development (the docker daemon
listens on a HTTP API) as I felt comfortable.</p>
<p>The first attempt was awful. But it worked! I was able to launch containers and everything. Of course, the API of the
library was horrendous. There were very little type guarantees for anything and the whole thing was just one big giant
IO blob.</p>
<p>Having realized this, I went on a crusade to learn all the fancy type machinery and make use of every trick and
extension under the sun. That attempt went on and off for a couple of months until I realized that I created a monster
and that I didn’t need half the stuff I was using. So, having went from one extreme to the other I decided to delete
everything and start fresh. I had some guidance from a couple of friends (thank you!) and I was getting pretty close to
an API I was (<em>more</em>) happy with and that should be usable by most people. The library being usable was one criteria,
but the other one was that it’s more or less straightforward to contribute to. The reason being that the Docker Engine
API is kind of huge at this point and I’m certainly either going to miss something, or implement it wrong, and it should
be more or less easy for anyone to go in, and contribute a bugfix or a feature. Naturally, this dragged on since
obviously I was doing this for fun and in my spare time. Thankfully, I got help somewhere along the way when <a href="http://jamesparker.me/">James
Parker</a> jumped in. He was instrumental in getting the library in better shape so that we can finally release that major
refactored version.
<!--more--></p>
<p>As you might have guessed this rewrite can’t even compare to the previous release. Not only did the API change, but even
the namespace under which the library lives has changed.</p>
<p>This <a href="https://hackage.haskell.org/package/docker">version</a> is far from stable but I think it’s a step in the right direction. Even though the next major
release will bring more API changes we’ve decided to release this version anyway, so that the work gets out there, and
to have people using it. Feedback (and help) is always welcome (we’re working on a contribution guide). The other
reason for releasing, even though there’s still stuff to do, is for me to get over the
“it-must-be-perfect-right-away” mindset and do small iterative changes. I looked at the early releases of some very
popular libraries in the haskell ecosystem and they all started small, so why shouldn’t I?!</p>
<p>While this is not my first project with Haskell, it’s certainly the one that was most challenging simply because of the
fact that it’s a library. Not a project/product, not an executable that I (or others would) use, but a library. It turns
out it’s that much harder to get a library right and make it easy to use in other people’s code.</p>
<p>So let’s see how the library progressed as I learned things, how it looked like at the beginning and how it looks like
now.</p>
<h3 id="how-it-all-started">How it all started</h3>
<p>The initial version of the library, while it worked (I could create containers with it) was pretty simplistic. For
instance, this is how <code>createContainer</code> looked like:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">createContainer ::</span> <span class="dt">DockerClientOpts</span> <span class="ot">-&gt;</span> <span class="dt">CreateContainerOpts</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> (<span class="dt">Maybe</span> <span class="dt">Text</span>)</span></code></pre></div>
<p>We had to pass the client configuration (api url and the like) to each and every function.. And I figured I’ll make it
the first argument so if people get tired of passing it in all the time they can partially apply the functions that they
use in their code with the <code>DockerClientOpts</code> of their choosing. I had <code>Text</code> and <code>String</code> all around the place… For
instance I was constructing my URLs like this:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>printf <span class="st">&quot;/containers/%s/start&quot;</span> containerId</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>printf <span class="st">&quot;%s%s%s&quot;</span> url apiVersion endpoint</span></code></pre></div>
<p>I was using <code>lenses</code> and <code>TemplateHaskell</code> even though I didn’t really need them, or understand any of it for that
matter. <a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>
And of course a lot of non-idiomatic haskell was there, syntax wise and more.</p>
<p>After the initial version I thought to myself, hey, I can make this more type safe, this is haskell dammit. So I deleted
<strong>everything</strong> and went down the rabbit hole. And naturally the first thing that happened was that the number of GHC
extensions grew. These are some of the extension that I suddenly discovered that I needed:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveFunctor     #-}</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE FlexibleContexts  #-}</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DataKinds         #-}</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE TypeFamilies      #-}</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveFunctor     #-}</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveGeneric     #-}</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GADTs             #-}</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE RankNTypes        #-}</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE RecordWildCards   #-}</span></span></code></pre></div>
<p>And that’s not all of them… So what was going on?</p>
<p>Among other things, is was using Free monads to construct an interpreter for HTTP requests and I was using singleton
types to make sure that I couldn’t accidentally return a list of Image ID’s with the endpoint that’s supposed to return
Container ID’s. This was making the library much more complex, and while it provided type safety for the library author,
it didn’t provide much benefit for the library user. In fact, the library was harder to use, and so much more harder to
contribute to.</p>
<p>The beauty of haskell, to me, is that I can come back to a codebase after a few months and instantly know
what’s what, and be able to continue working on as if I didn’t have a pause at all. This, to me, is the <em>single most
beneficial feature of haskell</em>. This was very important to this project especially…since I was basically just playing
around and would often have breaks like that (couple of months) before coming back to the project again. So the turning point
was when I lost this ability…</p>
<p>Now, while this was a wonderful learning exercise, it was a complete overkill for what I was trying to do. Some of the
ideas were sound, like using <code>Reader</code> and not have to pass in the <code>DockerClientOpts</code> to <strong>every single function</strong>, but
others were not. On the one hand I was practically dabbling with dependant typing and on the other I was still
concatenating strings for URL’s (printf) and returning things like <code>Maybe Text</code>. This just wouldn’t do.</p>
<p>At that point I talked to a friend of mine who’s an experienced Haskell hacker and he said the same thing: get rid of
all the extensions and start small. So I did this for the nth time:</p>
<pre><code>commit 2d064140910b69c7b1337f4f1f508dd6c9f3109a
Author: Deni Bertovic
Date:   Wed Mar 2 15:28:59 2016 +0100

    Delete everything. Start from scratch.</code></pre>
<h3 id="current-version">Current version</h3>
<p>After many more trials and errors I came to an API that I like and now you can do this:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Docker.Client</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="ot">runNginxContainer ::</span> <span class="dt">IO</span> <span class="dt">ContainerID</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>runNginxContainer <span class="ot">=</span> runDockerT (defaultClientOpts, defaultHttpHandler) <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> pb <span class="ot">=</span> <span class="dt">PortBinding</span> <span class="dv">80</span> <span class="dt">TCP</span> [<span class="dt">HostPort</span> <span class="st">&quot;0.0.0.0&quot;</span> <span class="dv">8000</span>]</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> myCreateOpts <span class="ot">=</span> addPortBinding pb <span class="op">$</span> defaultCreateOpts <span class="st">&quot;nginx:latest&quot;</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    cid <span class="ot">&lt;-</span> createContainer myCreateOpts (<span class="dt">Just</span> <span class="st">&quot;myNginxContainer&quot;</span>)</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> cid <span class="kw">of</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Left</span> err <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="op">$</span> <span class="fu">show</span> err</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Right</span> i <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>            _ <span class="ot">&lt;-</span> startContainer defaultStartOpts i</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>            <span class="fu">return</span> i</span></code></pre></div>
<p>I’m using <code>ReaderT</code> to pass in the <code>defaultClientOpts</code> used for configuring the Client. <code>DockerT</code> is just a wrapper
around ReaderT:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">DockerT</span> m a <span class="ot">=</span> <span class="dt">DockerT</span> {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    unDockerT ::</span> <span class="dt">Monad</span> m <span class="ot">=&gt;</span> <span class="dt">ReaderT</span> (<span class="dt">DockerClientOpts</span>, <span class="dt">HttpHandler</span> m) m a</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>And I’m also passing in a default HTTP handler. For most people the <code>defaultHttpHandler</code> will be sufficient, but this
leaves room for advanced users to provide their own. The handler’s type is:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">HttpHandler</span> m <span class="ot">=</span> <span class="dt">Request</span> <span class="ot">-&gt;</span> m (<span class="dt">Either</span> <span class="dt">DockerError</span> <span class="dt">Response</span>)</span></code></pre></div>
<p>And <em>now</em> the type of <code>createContainer</code> function looks like this:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">createContainer ::</span> <span class="kw">forall</span> m<span class="op">.</span> <span class="dt">Monad</span> m <span class="ot">=&gt;</span> <span class="dt">CreateOpts</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">ContainerName</span> <span class="ot">-&gt;</span> <span class="dt">DockerT</span> m (<span class="dt">Either</span> <span class="dt">DockerError</span> <span class="dt">ContainerID</span>)</span></code></pre></div>
<p>It’s polymorphic in terms of the Monad that’s used and it’s determined by the how the HTTP handler passed in looks like.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ot">runDockerT ::</span> <span class="dt">Monad</span> m <span class="ot">=&gt;</span> (<span class="dt">DockerClientOpts</span>, <span class="dt">HttpHandler</span> m) <span class="ot">-&gt;</span> <span class="dt">DockerT</span> m a <span class="ot">-&gt;</span> m a</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>runDockerT (opts, h) r <span class="ot">=</span> runReaderT (unDockerT r) (opts, h)</span></code></pre></div>
<p>There were some other considerations like how to handle exceptions. I talked to <a href="http://www.snoyman.com/">Michael Snoyman</a> about this and while I
agree that in the end if you’re ultimately using <code>IO</code> you will have exceptions so there’s no reason to hide those and
introduce multiple layers of error handling <a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>. That being said I wanted to make it so that most people don’t even
have to know that they are talking to a HTTP API. That’s why we have the <code>Either</code> in the response and don’t just pass
the <code>HttpException</code> along to the user. <a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a> People will be trying to use Docker, and they should only have to think in
Docker semantics and not worry if the communication is over HTTP or something else. There’s still some work to be done
on that front but that’s the general idea.</p>
<p>The fact that we can pass in the http handler should be enough for most people, and should come in handy for writing some
interesting tests I think.</p>
<p>I intentionally left the entire git history, with all of these various attempts in the repo, so that I (and anyone else)
could refer to them later. It paints a good picture of what it’s like to explore haskell and try different approaches.</p>
<p>I found this design to be somewhere in between simplicity and power. As I already said above, any feedback is appreciated
and I’d be very much interested to hear about any advice, tip or critique.</p>
<h3 id="whats-next">What’s next:</h3>
<p>The biggest challenge that’s up next is described in this <a href="https://github.com/denibertovic/docker-hs/issues/13">Github issue</a>. It has to do with streaming. After we figure
that out the API should become more stable.</p>
<h3 id="fin">Fin</h3>
<p>That’s all from me for now.
I’m sharing this in the hopes that my story will help someone else that’s just starting out with haskell by showing that it’s okay
to start small, and make a lot of mistakes along the way.</p>
<p>If you’ve liked what you’ve read please share it and comment down below. If you want to learn more about
how to manipulate docker containers with Haskell please see the docs
<a href="https://hackage.haskell.org/package/docker-0.3.0.1/docs/Docker-Client.html">here</a>.</p>
<h3 id="p.s.-if-you-want-to-look-at-some-other-haskell-code-that-i-wrote-check-out-these-projects">P.S. If you want to look at some other Haskell code that I wrote check out these projects:</h3>
<ul>
<li><a href="https://github.com/denibertovic/njusko-hs">A web scraper</a></li>
</ul>
<p>I had to move, and was looking for new apartments to rent, but the good ones go so fast so I needed to know when new ads
were published as soon as possible. The result is this scraper, for the local ads website, that would notify me about new
listings (based on some criteria) via email. I enjoyed using <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> for the CLI parts.</p>
<ul>
<li><a href="https://github.com/denibertovic/s2hl">Parser for bank statement csv/html files</a></li>
</ul>
<p>I wanted to use <a href="http://hledger.org/">hledger</a> for my personal accounting but didn’t want to manually enter each statement into the journal file.</p>
<ul>
<li><a href="https://github.com/denibertovic/dummy-api">A Trello like REST API</a></li>
</ul>
<p>I wanted to learn a few frontend frameworks, and new languages like <a href="http://elm-lang.org/">Elm</a> and <a href="http://www.purescript.org/">Purescript</a>, but wanted to do so building a
real(ish) project and not just a Todo app. Something like a subset of Trello seemed to have the right amount of
complexity in it so I decided to build a dummy-api that I could use for testing. I built it using the excellent
<a href="https://haskell-servant.github.io/">Servant</a> library and learned a lot in the process.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>I didn’t attempt to build a web app right away.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>If you’re just starting out I recommend looking at <a href="http://haskellbook.com/">Haskell programming from first principles</a>.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>The next release might re-introduce lenses because at least <code>CreateOpts</code> has a lot of nested fields that need
setting when creating a container<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>Relevant article is <a href="https://www.schoolofhaskell.com/user/commercial/content/exceptions-best-practices">here</a>.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5"><p>However since advanced users can create their own <code>HttpHandler</code> they can implement it in a way that doesn’t
mask the underlying Http Exceptions.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></description>
    <pubDate>Thu, 08 Sep 2016 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/haskell-and-docker-down-the-rabbit-hole-and-back/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Handling Permissions with Docker Volumes</title>
    <link>https://denibertovic.com/posts/handling-permissions-with-docker-volumes/</link>
    <description><![CDATA[<p>In this post I’ll try to explain the method I use to avoid having permission issues when using
Docker Volumes. This is pre Docker 1.10 (which added user namespaces) and I will talk about
those in my next post.</p>
<p>Before we begin let me explain what are Docker Volumes and what they’re used for.
The official Docker docs explain this feature as follows:</p>
<pre><code>A data volume is a specially-designated directory within one or more containers
that bypasses the Union File System.</code></pre>
<p>The main use-case for volumes is for persisting data between container runs (seeing as container are ephemeral).
This is useful for data directories when running databases (such as PostgreSQL) within containers. Other than persisting
databases it’s useful for sharing code folders from your host system to the container when running in your development
environment.
<!--more--></p>
<p>However, there are 2 problems we have here:</p>
<ol type="1">
<li>If you write to the volume you won’t be able to access the files that
container has written because the process in the container usually runs as root.</li>
<li>You shouldn’t run the process inside your containers as root but even if you run as some
hard-coded user it still won’t match the user on your laptop/jenkins/staging.</li>
</ol>
<p>The permissions problem is most annoying in development and testing environments because usually at some
point you want to remove files that the process running in the container has created but you can’t because
on your laptop you’re running as UID 1000 (on most Linux machines) and the files are owned either by UID 0 (root)
or by some other UID that was perhaps hardcoded in the Dockerfile.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span>  <span class="ex">useradd</span> <span class="at">--shell</span> /bin/bash <span class="at">-u</span> 1024 <span class="at">-o</span> <span class="at">-c</span> <span class="st">&quot;&quot;</span> <span class="at">-m</span> myuser</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="fu">mkdir</span> <span class="at">-p</span> /shared/tmp <span class="kw">&amp;&amp;</span> <span class="fu">chown</span> user. /shared/ <span class="at">-R</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">USER</span> myuser</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> <span class="ex">/usr/local/bin/myprocess</span></span></code></pre></div>
<p>This solution is inadequate because you hard-code the UID of the user in the build process
and even though your process won’t be running as root it’s still running as a user that’s:</p>
<ol type="1">
<li>Not present on your local machine</li>
<li>The UID of the user is not 1000 (ie. your UID) and you still won’t be able to cleanup files in the /shared/tmp
directory</li>
</ol>
<p>Docker provides a <code>-u</code> flag with it’s <code>run</code> command to dynamically switch to a specified UID during container start.
So we can write something like this:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">deni@kanta:~$</span> docker run <span class="at">-it</span> <span class="at">-u</span> <span class="kw">`</span><span class="fu">id</span> <span class="at">-u</span> <span class="va">$USER</span><span class="kw">`</span> debian:jessie /bin/bash</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ex">I</span> have no name!@dcb415bad433:/$ id</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="va">uid</span><span class="op">=</span>1000 <span class="va">gid</span><span class="op">=</span>0<span class="kw">(</span><span class="ex">root</span><span class="kw">)</span> <span class="va">groups</span><span class="op">=</span>0<span class="kw">(</span><span class="ex">root</span><span class="kw">)</span></span></code></pre></div>
<p>This approach, while dynamic in the sense that the UID is specified at runtime, has 2 drawbacks:</p>
<ol type="1">
<li>The GID (group id) of the user is still 0 (root)</li>
<li>The UID 1000 is not present in the container’s <code>/etc/passwd</code> file.</li>
</ol>
<p>While no. 1. is definitely problematic for obvious reasons no. 2. is where we hit a wall. Now while
the Linux Filesystem doesn’t really care about user names, rather just UID’s, some applications will
refuse to start if the user is not present in <code>/etc/passwd</code>.</p>
<p>So what we need is something like <code>-u</code> but that doesn’t just use the UID of our user but actually creates
a user with that UID and then starts the process owned by it.</p>
<p>To do that we have to create a base Dockerfile from which all of our other Dockerfiles will inherit.
That Dockerfile should look something like this.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> debian:jessie</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">apt-get</span> update <span class="kw">&amp;&amp;</span> <span class="ex">apt-get</span> <span class="at">-y</span> <span class="at">--no-install-recommends</span> install <span class="dt">\</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    ca-certificates <span class="dt">\</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    curl</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">gpg</span> <span class="at">--keyserver</span> ha.pool.sks-keyservers.net <span class="at">--recv-keys</span> B42F6819007F00F88E364FD4036A9C25BF357DD4</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">curl</span> <span class="at">-o</span> /usr/local/bin/gosu <span class="at">-SL</span> <span class="st">&quot;https://github.com/tianon/gosu/releases/download/1.4/gosu-</span><span class="va">$(</span><span class="ex">dpkg</span> <span class="at">--print-architecture</span><span class="va">)</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">&amp;&amp;</span> <span class="ex">curl</span> <span class="at">-o</span> /usr/local/bin/gosu.asc <span class="at">-SL</span> <span class="st">&quot;https://github.com/tianon/gosu/releases/download/1.4/gosu-</span><span class="va">$(</span><span class="ex">dpkg</span> <span class="at">--print-architecture</span><span class="va">)</span><span class="st">.asc&quot;</span> <span class="dt">\</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">&amp;&amp;</span> <span class="ex">gpg</span> <span class="at">--verify</span> /usr/local/bin/gosu.asc <span class="dt">\</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">&amp;&amp;</span> <span class="fu">rm</span> /usr/local/bin/gosu.asc <span class="dt">\</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>    <span class="kw">&amp;&amp;</span> <span class="fu">chmod</span> +x /usr/local/bin/gosu</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> entrypoint.sh /usr/local/bin/entrypoint.sh</span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="fu">chmod</span> +x /usr/local/bin/entrypoint.sh</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="kw">ENTRYPOINT</span> [<span class="st">&quot;/usr/local/bin/entrypoint.sh&quot;</span>]</span></code></pre></div>
<p>In this base Dockerfile we’re installing a tool called <code>gosu</code> and setting an <a href="https://docs.docker.com/engine/reference/builder/#entrypoint">entrypoint</a>.
An entrypoint is basically a script that gets executed before any other command that you might pass to your container.
So unless we overwrite the entrypoint we are guaranteed to go through this script every time we launch our containers,
before we actually run our actual process.</p>
<p>In fact the CMD statement from the Dockefile or from docker CLI gets passed to the <code>entrypoint.sh</code> script as command line arguments.
The reason we’re installing gosu is because we will need it to switch to the newly created user.</p>
<p><code>NOTE</code>: The reason why we don’t use sudo is explained in gosu repo’s README.</p>
<p>Now let’s look at the <code>entrypoint.sh</code> script:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Add local user</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Either use the LOCAL_USER_ID if passed in at runtime or</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co"># fallback</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="va">USER_ID</span><span class="op">=</span><span class="va">${LOCAL_USER_ID</span><span class="op">:-</span>9001<span class="va">}</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;Starting with UID : </span><span class="va">$USER_ID</span><span class="st">&quot;</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="ex">useradd</span> <span class="at">--shell</span> /bin/bash <span class="at">-u</span> <span class="va">$USER_ID</span> <span class="at">-o</span> <span class="at">-c</span> <span class="st">&quot;&quot;</span> <span class="at">-m</span> user</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">HOME</span><span class="op">=</span>/home/user</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="bu">exec</span> /usr/local/bin/gosu user <span class="st">&quot;</span><span class="va">$@</span><span class="st">&quot;</span></span></code></pre></div>
<p>What we’re doing here is fetching a UID from an environment variable, defaulting to 9001 if it doesn’t exist,
and actually creating the user “user” with the familiar <code>useradd</code> command while setting it’s UID explicitly.</p>
<p>And lastly we use <code>gosu</code> to execute our process <code>"$@"</code> as that user. Remember CMD from a Dockerfile or
command from docker CLI gets passed to the entrypoint.sh script as command line arguments.</p>
<p>Now to build our base image:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">deni@kanta:~$</span> docker build <span class="at">-t</span> mybase .</span></code></pre></div>
<p>And create our new child image:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> mybase</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;/bin/bash&quot;</span>]</span></code></pre></div>
<p>Build it:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ex">deni@kanta:~$</span> docker build <span class="at">-t</span> myimage .</span></code></pre></div>
<p>Run it:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ex">deni@kanta:~$</span> docker run <span class="at">-it</span> myimage</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="ex">Starting</span> with UID : 9001</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="ex">user@056b9bb45214:/$</span> id</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="va">uid</span><span class="op">=</span>9001<span class="kw">(</span><span class="ex">user</span><span class="kw">)</span> <span class="va">gid</span><span class="op">=</span>9001<span class="kw">(</span><span class="ex">user</span><span class="kw">)</span> <span class="va">groups</span><span class="op">=</span>9001<span class="kw">(</span><span class="ex">user</span><span class="kw">)</span></span></code></pre></div>
<p>Run it with passing in our UID:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">deni@kanta:~$</span> docker run <span class="at">-it</span> <span class="at">-e</span> LOCAL_USER_ID=<span class="kw">`</span><span class="fu">id</span> <span class="at">-u</span> <span class="va">$USER</span><span class="kw">`</span> myimage</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="ex">Starting</span> with UID : 1000</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="ex">user@fc07b6c32b4f:/$</span> id</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="va">uid</span><span class="op">=</span>1000<span class="kw">(</span><span class="ex">user</span><span class="kw">)</span> <span class="va">gid</span><span class="op">=</span>1000<span class="kw">(</span><span class="ex">user</span><span class="kw">)</span> <span class="va">groups</span><span class="op">=</span>1000<span class="kw">(</span><span class="ex">user</span><span class="kw">)</span></span></code></pre></div>
<p>Done! Now remember, the reason this works is because the Filesystem doesn’t really care if the user is called
“user” or “deni” or “jenkins”. It only cares about the UID attached to that user, so the permissions will be
preserved and various applications will not complain that there is no user with that UID.</p>
<h2 id="conclusion">Conclusion</h2>
<p>When using docker containers it’s a bad idea to run your processes as root (some applications even refuse to run as root). While running as root
or any other hard-coded user it’s hard to work with volume mounts because the files being written from within the container
are going to be owned by a different user. That makes working with them or cleaning them up hard and needing to resort to sudo
or similar. Which is increasingly annoying in development and CI environments.</p>
<p>In this post I’ve showed you a technique that you can use to build all of your images off of a base image (which you’re probably already
doing) that will allow you to start as whatever user you specify making sure to create that user in the process.</p>
<p>If a UID is specified, the container will start as that user, and if no UID is specified it will start as a default user with a random
UID that should not collide with any existing users in docker images. (Aaand <strong>it’s over 9000!</strong>)</p>
<p>So we’re taking care of the permission issue and not allowing the containers to start as root all in one.</p>
<p>If this was helpful please consider following me on <a href="https://twitter.com/denibertovic">twitter</a>.</p>]]></description>
    <pubDate>Wed, 17 Feb 2016 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/handling-permissions-with-docker-volumes/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Celery - Best Practices</title>
    <link>https://denibertovic.com/posts/celery-best-practices/</link>
    <description><![CDATA[<p>If you’ve worked with <a href="https://www.djangoproject.com/">Django</a> at some point you probably had the need for some background
processing of long running tasks. Chances are you’ve used some sort of task queue, and <a href="http://www.celeryproject.org/">Celery</a> is currently
the most popular project for this sort of thing in the Python (and Django) world (but there are others).</p>
<p>While working on some projects that used Celery for a task queue I’ve gathered a number of best practices and decided to document them.
Nevertheless, this is more a rant about what I think should be the proper way to do things, and about some underused features that the celery
ecosystem offers.
<!--more--></p>
<h3 id="no.1-dont-use-the-database-as-your-amqp-broker">No.1: Don’t use the database as your AMQP Broker</h3>
<p>Let me explain why I think this is wrong (aside from the <a href="http://docs.celeryproject.org/en/latest/getting-started/brokers/django.html#limitations">limitations</a> pointed out in the celery docs).</p>
<p>A database is not built for doing the things a proper AMQP broker like RabbitMQ is designed for. It will break down at one point, probably in production
with not that much traffic/user base.</p>
<p>I guess the most popular reason people decide to use a database is because, well, they already have one for their web app, so why not re-use it. Setting up is a
breeze and you don’t need to worry about another component (like RabbitMQ).</p>
<p>Not so hypothetical scenario: Let’s say you have 4 background workers processing the tasks you’ve put in the database. This means that you get 4
processes polling the database for new tasks fairly often, not to mention that each of those 4 workers can have multiple concurrent threads of it’s own.
At some point you notice that you are falling behind on your task processing and more tasks are coming in than are being completed, so naturally you increase the number
of workers doing the task processing. Suddenly your database starts falling apart due to the huge number of workers polling the database for new tasks, your disk IO goes
through the roof and your webapp starts being affected by this slow down because the workers are basically DDOS-ing the database.</p>
<p>This does not happen when you have a proper AMQP like <a href="http://www.rabbitmq.com/">RabbitMQ</a> because, for one thing, the queue resides in memory so you don’t hammer your disk.
The consumers (the workers) do not need to resort to polling as the queue has a way of pushing new tasks to the consumers, and if the AMQP does get overwhelmed for some other reason,
at least it will not bring down the user facing web app with it.</p>
<p>I would go as far to say that you shouldn’t use a database for a broker even in development, what with things like Docker and a ton of pre-built images that already give you
RabbitMQ <a href="https://registry.hub.docker.com/search?q=rabbitmq">out of the box</a>.</p>
<h3 id="no.2-use-more-queues-ie.-not-just-the-default-one">No.2: Use more Queues (ie. not just the default one)</h3>
<p>Celery is fairly simple to set up, and it comes with a default queue in which it puts all the tasks unless you tell it otherwise.
The most common thing you’ll see is something like this:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="at">@app.task</span>()</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> my_taskA(a, b, c):</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span>(<span class="st">&quot;doing something here...&quot;</span>)</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="at">@app.task</span>()</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> my_taskB(x, y):</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span>(<span class="st">&quot;doing something here...&quot;</span>)</span></code></pre></div>
<p>What happens here is that <em>both</em> tasks will end up in the same Queue (if not specified otherwise in the <code>celeryconfig.py</code> file).
I can definitely see the appeal of doing something like this because with just one decorator you’ve got yourself some sweet background tasks.
My concern here is that taskA and taskB might be doing totally different things, and perhaps one of them might
even be much more important than the other, so why throw them both in the same basket? Even if you’ve got just one worker processing both tasks,
suppose that at some point the unimportant taskB gets so massive in numbers that the more important taksA just can’t get enough attention from the worker?
At this point increasing the number of workers will probably not solve your problem as all workers still need to process both tasks, and with taskB so great in numbers taskA
still can’t get the attention it deserves. Which brings us to the next point.</p>
<h3 id="no.3-use-priority-workers">No.3: Use priority workers</h3>
<p>The way to solve the issue above is to have taskA in one queue, and taskB in another and then assign <code>x</code> workers to
process Q1 and all the other workers to process the more intensive Q2 as it has more tasks coming in.
This way you can still make sure that taskB gets enough workers all the while maintaining a few priority workers that just need to process
taskA when one comes in without making it wait to long on processing.</p>
<p>So, define your queues manually:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>CELERY_QUEUES <span class="op">=</span> (</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    Queue(<span class="st">&#39;default&#39;</span>, Exchange(<span class="st">&#39;default&#39;</span>), routing_key<span class="op">=</span><span class="st">&#39;default&#39;</span>),</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    Queue(<span class="st">&#39;for_task_A&#39;</span>, Exchange(<span class="st">&#39;for_task_A&#39;</span>), routing_key<span class="op">=</span><span class="st">&#39;for_task_A&#39;</span>),</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    Queue(<span class="st">&#39;for_task_B&#39;</span>, Exchange(<span class="st">&#39;for_task_B&#39;</span>), routing_key<span class="op">=</span><span class="st">&#39;for_task_B&#39;</span>),</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>)</span></code></pre></div>
<p>And your <code>routes</code> that will decide which task goes where:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>CELERY_ROUTES <span class="op">=</span> {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="st">&#39;my_taskA&#39;</span>: {<span class="st">&#39;queue&#39;</span>: <span class="st">&#39;for_task_A&#39;</span>, <span class="st">&#39;routing_key&#39;</span>: <span class="st">&#39;for_task_A&#39;</span>},</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="st">&#39;my_taskB&#39;</span>: {<span class="st">&#39;queue&#39;</span>: <span class="st">&#39;for_task_B&#39;</span>, <span class="st">&#39;routing_key&#39;</span>: <span class="st">&#39;for_task_B&#39;</span>},</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Which will allow you to run workers for each task:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">celery</span> worker <span class="at">-E</span> <span class="at">-l</span> INFO <span class="at">-n</span> workerA <span class="at">-Q</span> for_task_A</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="ex">celery</span> worker <span class="at">-E</span> <span class="at">-l</span> INFO <span class="at">-n</span> workerB <span class="at">-Q</span> for_task_B</span></code></pre></div>
<h3 id="no.4-use-celerys-error-handling-mechanisms">No.4: Use Celery’s error handling mechanisms</h3>
<p>Most tasks I’ve seen in the wild don’t have a notion of error handling at all. If a task fails that’s it, it failed. This might be fine for some
use cases, however, most tasks I’ve seen are talking to some kind of 3rd party API and fail because of some sort of network error,
or other kind of “resource availability” error.
The most simple way we can handle these kinds of errors is to just retry the task, because maybe the 3rd party API just had some server/network issues
and it will be back up shortly, why not give it a go?</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="at">@app.task</span>(bind<span class="op">=</span><span class="va">True</span>, default_retry_delay<span class="op">=</span><span class="dv">300</span>, max_retries<span class="op">=</span><span class="dv">5</span>)</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> my_task_A():</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">try</span>:</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;doing stuff here...&quot;</span>)</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">except</span> SomeNetworkException <span class="im">as</span> e:</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;maybe do some clenup here....&quot;</span>)</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.retry(e)</span></code></pre></div>
<p>What I like to do is define per task defaults for how long should a task wait before being retried, and how many retries is enough
before finally giving up (the <code>default_retry_delay</code> and <code>max_retries</code> parameters respectively). This is the most basic form of error handling that I can think of
and yet I see it used almost never. Of course Celery offers more in terms of error handling but I’ll leave you with the celery docs for that.</p>
<h3 id="no.5-use-flower">No.5: Use Flower</h3>
<p>The <a href="http://celery.readthedocs.org/en/latest/userguide/monitoring.html#flower-real-time-celery-web-monitor">Flower</a> project is a wonderful tool for monitoring your celery tasks
and workers. It’s web based and allows you to do stuff like see task progress, details, worker status, bringing up new workers and so forth. Check out the full list of features in
the provided link.</p>
<h3 id="no.6-keep-track-of-results-only-if-you-really-need-them">No.6: Keep track of results only if you really need them</h3>
<p>A task status is the information about the task exiting with a success or failure. It can be useful for some kind of statistics later on.
The big thing to note here is that the exit status is not the result of the job that the task was performing, that information is most likely some sort
of side effect that gets written to the database (ie. update a user’s friend list).</p>
<p>Most projects I’ve seen don’t really care about keeping persistent track of a task’s status after it exited yet most of them use either the default sqlite database for saving
this information, or even better, they’ve taken the time and use their regular database (postgres or otherwise).</p>
<p>Why hammer your webapp’s database for no reason? Use <code>CELERY_IGNORE_RESULT = True</code> in your <code>celeryconfig.py</code> and discard the results.</p>
<h3 id="no.7-dont-pass-databaseorm-objects-to-tasks">No.7: Don’t pass Database/ORM objects to tasks</h3>
<p>After giving this talk at a local Python meetup a few people suggested I add this to the list. What’s it all about? You shouldn’t pass Database objects (for instance your User model) to
a background task because the serialized object might contain stale data. What you want to do is feed the task the User id and have the task ask the database for a fresh
User object.</p>]]></description>
    <pubDate>Wed, 18 Jun 2014 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/celery-best-practices/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Setting up Systemd on Debian in 10 minutes</title>
    <link>https://denibertovic.com/posts/setting-up-systemd-on-debian-in-10-minutes/</link>
    <description><![CDATA[<p>Recently I’ve been reading more and more about Systemd. Now, most distros have already moved to
<code>Systemd</code> but I’m using Debian and was stuck with <code>init</code> which was the default. Given the recent
discussion on the Debian mailing lists about migrating to <code>systemd</code> as the new default, I was even more
inclined to make the switch now and get used to it before it’s forced upon us.</p>
<p>Actually I was really looking forward to it, but as Linux goes I was expecting it to be a pain, so I
was pleasantly surprised that it only took me 10 minutes and wasn’t a hassle at all.</p>
<p>I’ve decided to document the steps I’ve taken in case that someone finds it useful.
<!--more--></p>
<p>First some prerequisites, make sure that you’ve got your system up2date.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">sudo</span> apt-get update <span class="kw">&amp;&amp;</span> <span class="fu">sudo</span> apt-get upgrade</span></code></pre></div>
<p>Make sure systemd is actually installed (it should be but check anyway):</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">sudo</span> apt-get install systemd</span></code></pre></div>
<p>Before you switch permanently it’s recommended that you try a one-time-boot with <code>systemd</code> just to see
if everything’s okay. Reboot your machine and on the grub screen select the kernel you wish to boot
and press <code>e</code>. Then at the end of the kernel line add <code>init=/bin/systemd</code> and boot your machine.
If everything is fine continue with making systemd a permanent choice.</p>
<p>The recommended way to replace init with systemd is to install the package <code>systemd-sysv</code>
which takes care of everything.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">sudo</span> apt-get install systemd-sysv</span></code></pre></div>
<p>Edit <code>/etc/default/grub</code> and add <code>init=/bin/systemd</code> at the end of the line <code>GRUB_CMDLINE_LINUX_DEFAULT</code>.
Save the file and run the following command to update grub:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">sudo</span> update-grub2</span></code></pre></div>
<p>Reboot and you should be running systemd (use <code>ps aux</code> and check for PID 1).</p>
<p>One last thing with Debian is to set up peristent logging with systemd’s logging component called <a href="https://docs.google.com/document/pub?id=1IC9yOXj7j6cdLLxWEBAGRL6wl97tFxgjLUEHIX3MSTs">journal</a>.
By default journal will log to <code>/run</code> which is ephemeral meaning the logs will disappear after reboot. The process
of making the logs persistent (if you choose to do so) is documented here: <code>/usr/share/doc/systemd/README.Debian</code>
Basically you just need to run this:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">install</span> <span class="at">-d</span> <span class="at">-g</span> systemd-journal /var/log/journal</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ex">setfacl</span> <span class="at">-R</span> <span class="at">-nm</span> g:adm:rx,d:g:adm:rx /var/log/journal</span></code></pre></div>
<p>It’s worth noting that you don’t have to use journal if you don’t want to, it’s designed to co-exist with syslog, that’s
already running on your system, so you can continue to use that. I personally find journal awesome and would
recommend that you at least check it out and see what it brings to the table.</p>
<p>And that’s it folks, now go read more stuff about systemd <a href="http://0pointer.de/blog">here</a>.</p>
<p>Thanks to <code>babilen</code> on <code>irc.oftc.net</code> for taking the time and explaining everything patiently :).</p>]]></description>
    <pubDate>Sun, 06 Apr 2014 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/setting-up-systemd-on-debian-in-10-minutes/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Docker and Logstash: Smarter Log Management For Your Containers</title>
    <link>https://denibertovic.com/posts/docker-and-logstash-smarter-log-management-for-your-containers/</link>
    <description><![CDATA[<p><a href="http://www.docker.io/">Docker</a> currently supports getting logs from a container that logs to stdout/stderr.
Everything that the process running in the container writes to <code>stdout</code> or <code>stderr</code> docker will
convert to <code>json</code> and store in a file on the host machine’s disk which you can then
retrieve with the <code>docker logs</code> command.</p>
<p>This is handy but it has its drawbacks because you don’t get any log rotation and the file
size of the collected logs can become an issue as it eats up your host’s disk space. Not to mention
the fact that every time you run <code>docker logs container_id</code> you get all the logs of that processes from
the beginning.</p>
<p>While there are some interesting things being discussed on the <a href="https://groups.google.com/forum/#!searchin/docker-dev/logging/docker-dev/3paGTWD6xyw/hvZlnFD5x5sJ">docker-dev</a> mailing list,
I wanted to see if I could get docker to play along with proven logging systems out there with the
functionality I have now.
<!--more--></p>
<p>First things first, a couple of requirements:</p>
<ul>
<li>I don’t want to run multiple processes in a container (syslog + my_process or /sbin/init)</li>
<li>I don’t want to have to configure the host to keep track of docker logs</li>
</ul>
<p>If I run the container in a so called “machine mode”, obviously I can leverage the tools a full blown
system provides (such as syslog). I didn’t want to do this because it’s not the docker way and I want
a separation of concerns meaning every process/service running in it’s own container.</p>
<p>The reason for <code>#2</code> is because I don’t want to have to do heavy duty host setup when all that’s needed is docker installed
so I can run everything in a container. Think of host machines as throw-away cloud instances that
you provision, run a few services on them (isolated in containers) and just throw them away when done.</p>
<p>The first solution that came to mind was to use <code>bind mounts</code> and mount the host’s <code>/dev/log</code> inside the
container and just have the container log to that. This way I could aggregate the logs on the host and
possibly ship them somewhere to a central location. Although a viable solution, I didn’t quite care for it
as it still meant needing to properly configure <code>syslog</code> on the host server.
To be fair, I could have used a single syslog container and mount it’s volumes to all the containers that needed
to log stuff, but I kind of wanted to try out something different than syslog.</p>
<p>I heard great things about <a href="http://logstash.net/">Logstash</a> and <a href="http://www.elasticsearch.org/">Elastisearch</a> so I wanted to try this solution out. Especially since I’ve seen the new Kibana web interface for logstash.</p>
<p>To quote their <a href="http://logstash.net/">website</a>: “Logstash is a tool for managing events and logs. You can use it to collect logs, parse them, and store them for later use (like, for searching). Speaking of searching, logstash comes with a web interface for searching and drilling into all of your logs.”</p>
<p>Getting Logstash up and running was fairly trivial and I’ve prepared a <a href="https://github.com/denibertovic/logstash-dockerfile">Dockerfile</a> so you can get started quickly. I’m using the embedded elasticsearch
server but you can run you own server on a different machine and just pass the IP and Port to the logstash container.</p>
<p>Now, the logstash service is kind of heavy on the resources so I didn’t want to run a logstash container on every
host machine but rather I wanted co collect logs from every container on every host machine and send it to a central
logstash server. That’s where <a href="https://github.com/elasticsearch/logstash-forwarder">logstash-forwarder</a> comes in.</p>
<p>Logstash-forwarder, previously know as <code>lumberjack</code> is used to collect the logs from each and every container on a host machine and send them to a central logstash server (or multiple servers). I’ve prepared a Dockerfile <a href="https://github.com/denibertovic/logstash-forwarder-dockerfile">here</a>.</p>
<p>Logstash-forwarder can be configured to watch certain directories and files but I was more interested in the ability
to listen on <code>stdin</code>.</p>
<p>So the idea is to run <code>logstash-forwarder</code> on every host machine and expose a named pipe (FIFO) as a volume that
other containers on that host can mount and write to.</p>
<p>So first thing’s first, let’s run the logstash container (see github link above for how to build it):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> run <span class="at">-name</span> logstash <span class="at">-p</span> 9292:9292 <span class="at">-d</span> <span class="at">-t</span> logstash</span></code></pre></div>
<p>This will allow access to the Kibana web interface on <code>localhost:9292</code>.</p>
<p>Now let’s run the <code>logstash-forwarder</code> container (again, see github link above for how to build it):</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># replace the IP with the actual IP of the logstash container</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> run <span class="at">-name</span> forwarder <span class="at">-d</span> <span class="at">-v</span> /tmp/feeds <span class="at">-e</span> LOGSTASH_SERVER=<span class="st">&quot;172.17.0.69:5043&quot;</span> <span class="at">-t</span> forwarder</span></code></pre></div>
<p>Now all we would need to do to run a service that would write to the <code>forwarder</code> is:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> run <span class="at">-volumes-from</span> forwarder <span class="at">-i</span> <span class="at">-t</span> ubuntu /bin/bash <span class="at">-c</span> <span class="st">&quot;echo &#39;test&#39; &gt;&gt; /tmp/feeds/fifofeed&quot;</span></span></code></pre></div>
<p>If you go to the Kibana web interface you should see that the message got through.
We could just as easily build containers using Dockerfiles where we specify a <code>CMD</code> or <code>ENTRYPOINT</code>
directives like so:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># My App</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co"># VERSION               0.0.1</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="ex">FROM</span>      ubuntu</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="ex">MAINTAINER</span> Me <span class="st">&quot;me@email.com&quot;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="ex">CMD</span> /usr/local/bin/run_my_app.sh <span class="op">&gt;&gt;</span> /tmp/feed/fifofeed</span></code></pre></div>
<p>The downside of this is that we redirect all output to the FIFO pipe and therefore we short circuit
the <code>docker logs</code> command as it will get no output anymore. But this is fine as it’s much better to have
logs on a central location and not worry about the logfile filling up the disk space on the host machine.</p>
<h3 id="update-on-april-10th-2014">[UPDATE on April 10th 2014]:</h3>
<p>As was pointed out to me in the comments (thanks Alan) a named pipe will
blow up just the same way as an anonymous pipe would if there is no reader at the reading end.
This means that even if my application handles the appropriate signal and reopens the pipe if there
is no one on the reading end it would block.</p>
<p>So I did a little change to the architecture explained above: I mount <code>/dev/log</code> from the host system into
the app container, that way the process in the container can be set up to log to syslog, which in this case
is going to end up on the host’s syslog. After that I just have another container that runs logstash-forwarder
(that also mounts /dev/log from the host) and ships the logs off to the logstash server.</p>
<p>The benefit of this approach is, again, that I don’t have to do much setting up on the host seeing as every distro comes
with some kind of syslog daemon already set up (mostly rsyslogd these days).</p>]]></description>
    <pubDate>Mon, 03 Feb 2014 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/docker-and-logstash-smarter-log-management-for-your-containers/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Classes for all the things?</title>
    <link>https://denibertovic.com/posts/classes-for-all-the-things/</link>
    <description><![CDATA[<p>Recently I’ve been thinking a lot about how to simplify my code. Now, the key thing to note
here is that <code>simple != familiar</code>.</p>
<p>Classes for example are familiar to most people. But code consisting exclusively of classes
and OOP concepts isn’t necessarily simple all of the time. The question arises: Is there something
simpler?</p>
<p>Well, the first thing that comes to mind, of course, good old functions.
Functions are universal, everybody understands them, even new programmers that don’t yet get all
the fancy concepts about OOP, get plain old functions.</p>
<p>Keep in mind, I’m not saying that classes don’t have their place in our code, but rather that maybe we don’t
need them quite as often as we might think.
<!--more--></p>
<p>For example: Let’s say we want to write a program that needs to do some sort of calculation on some data we provide.
We want to have different types of calculations for getting different information from our data.
Let’s call them <code>Calculation Type A</code> and <code>Calculation Type B</code>. We need to take into account data normalization
before starting the calculation, and let’s make room for future improvements as we might add
a <code>Calculation Type C</code> later on that may require a different kind of normalization.</p>
<p>We immediately think to ourselves: Well I’m just going to make an abstract class and just inherit from that.
So here we go:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> AbstractCalc(<span class="bu">object</span>):</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> normalize(<span class="va">self</span>, data):</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>        <span class="co"># do complex normalization</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> data</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> calc(<span class="va">self</span>, data):</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">raise</span> <span class="pp">NotImplementedError</span></span></code></pre></div>
<p>This is nice. We have an abstract class that has the <code>normalize</code> method implemented (which we can override in derived classes if need be) and we have a <code>calc</code> method that needs to be implemented by the derived classes.
So let’s implement those:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> ATypeCalc(AbstractCalc):</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> calc(<span class="va">self</span>, data):</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>        normalizes_data <span class="op">=</span> <span class="va">self</span>.normalize(data)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span> <span class="st">&quot;Doing some complex A Calculations&quot;</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>        result <span class="op">=</span> ...</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> result</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> BTypeCalc(AbstractCalc):</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> calc(<span class="va">self</span>, data):</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>        normalized_data <span class="op">=</span> <span class="va">self</span>.normalize(data)</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span> <span class="st">&quot;Doing some complex B Calculations&quot;</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>        result <span class="op">=</span> ...</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> result</span></code></pre></div>
<p>Awesome. We have our concrete calculation classes and for now we use the parents <code>normalize</code> method but we
can just as easily use our own custom one.
The usage of this implementation is something like this:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> calc_type <span class="op">==</span> <span class="st">&#39;a&#39;</span>:</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    calc <span class="op">=</span> ATypeCalc()</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> calc_type <span class="op">==</span> <span class="st">&#39;b&#39;</span>:</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    calc <span class="op">=</span> BTypeCalc()</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co"># do actual calculation</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>calc.calc(data)</span></code></pre></div>
<p>We can just wrap this in a dispatcher function called <code>get_calc</code> and then we get:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> get_calc(calc_type):</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> calc_type <span class="op">==</span> <span class="st">&#39;a&#39;</span>:</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> ATypeCalc()</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">elif</span> calc_type <span class="op">==</span> <span class="st">&#39;b&#39;</span>:</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> BTypeCalc()</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>calc <span class="op">=</span> get_calc(calc_type)</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>calc.calc(data)</span></code></pre></div>
<p>Pretty awesome, because now to use our code we just call the <code>get_calc</code> function which returns an
object instantiated from one of the calculation classes and we’re good to go.</p>
<p>This is all fine and dandy but we have just 2 methods in those classes. Do we really need a class for that?</p>
<p>I wonder if we could get away with using just plain old functions? We just need to keep the code modular enough
that adding new calculations is easy and preserve the existing API.</p>
<p>Let’s see how that would look. First we define our <code>normalize</code> function, and our a/b calculation functions:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> normalize(data):</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="co"># do complex normalization</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> data</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> a_calc(normalize, data):</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    normalized_data <span class="op">=</span> normalize(data)</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span> <span class="st">&quot;Doing some complex Calculations A&quot;</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    result <span class="op">=</span> ...</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> result</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> b_calc(normalize, data):</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>    normalized_data <span class="op">=</span> normalize(data)</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span> <span class="st">&quot;Doing some complex Calculations B&quot;</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    result <span class="op">=</span> ...</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> result</span></code></pre></div>
<p>Seems straight forward enough. We have a shared <code>normalize</code> function which we pass into the <code>a_calc</code> and <code>b_calc</code>
functions, and this way if one day we need to change the <code>normalize</code> function for <code>a_calc</code> we just
pass in a different function in there. Yes, we can do this in Python because functions are first class citizens so we can
pass functions to other functions and have functions return functions. Pretty neat.</p>
<p>What about using this new implementation. We define 2 more functions, one called <code>calc</code>, and one dispatcher function:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> calc(fn, normalize, data):</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    fn(normalize, data)</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> get_calc(calc_type):</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    <span class="im">from</span> functools <span class="im">import</span> partial</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> calc_type <span class="op">==</span> <span class="st">&#39;a&#39;</span>:</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> partial(calc, a_calc, normalize)</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">elif</span> calc_type <span class="op">==</span> <span class="st">&#39;b&#39;</span>:</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> partial(calc, b_calc, normalize)</span></code></pre></div>
<p>But wait, what’s this <code>partial</code> funny business? Remember how I said <code>simple != familiar</code>, so stay with me.</p>
<p>With partial application we can use our <code>calc</code> function that takes exactly 3 parameters and bind the first 2
parameters (namely the concrete a/b calc and normalize functions) and leave the 3rd parameter
(the data) unbound. This effectively returns a new function that accepts only one parameter.
You can, of course, partially apply any number of parameters of a function, and get back a new function that takes
that much less parameters.</p>
<p>It’s the same solution just implemented solely with functions. Using this method we left room
for adding a 3rd calculation type that can use the general <code>normalize</code> function we have implemented or use it’s
custom normalization. In the same effect we can change the normalize function in both A and B type calculations.</p>
<p>And now we can use this API in a similar way we did the one before:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>calc <span class="op">=</span> get_calc(calc_type)</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>calc(data)</span></code></pre></div>
<p>There you have it. Two different approaches for solving the same problem, with roughly the same amount of
lines of code and the same API.</p>
<p>Notice how nowhere in the post did I mention the term FP (Functional programming).
This is mostly because Python is not really a functional language despite it having many of the nice
functional-like features. Also, when people hear “Functional Programming” most of the time
they immediately run away.</p>
<p>Personally I find the functional solution cleaner, more readable, and easier to explain to new programmers.
Alas, I leave it to you to decide what the best approach is for your concrete problem.
Remember, there’s no silver bullet, always use the best tool at your disposal for the problem at hand.</p>]]></description>
    <pubDate>Sat, 18 Jan 2014 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/classes-for-all-the-things/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>The switch to Nikola</title>
    <link>https://denibertovic.com/posts/the-switch-to-nikola/</link>
    <description><![CDATA[<p>I switched Blog engines because I wasn’t very happy with the one I was using (<a href="https://github.com/rigoneri/syte">Syte</a>)
and I wanted something more simple. Something that wold let me focus on writing and not maintaining
code. Don’t get me wrong, Syte is an awesome project, it just seems like way too much.
And the deal breaker for me was the fact that it used Tumblr for hosting blog posts and Tumblr
has one of the worst online editors ever. It’s just not made for writing.
<!--more--></p>
<p>So, The main criteria was to be able to write posts using <a href="http://www.sublimetext.com/">Sublime</a>.
I spend most of my time in the day using this editor so it’s only logical.</p>
<p>After doing some research I figured that a static site generator would be the best choice.
Enter <a href="http://nikola.ralsina.com.ar/">Nikola</a>. The name itself made it a winner and it has really
nice features, but most importantly it was really easy to set up.</p>
<p>Customizing the default template (Nikola supports Mako and Jinja2) was the most time consuming
but even that was a breeze.</p>
<p>Now I can write new posts using Sublime and have them deployed just by pushing to a git
repository. The new posts are rebuilt automatically and Nginx serves the static content.</p>
<p>I’m pretty happy with the end result.</p>]]></description>
    <pubDate>Wed, 04 Sep 2013 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/the-switch-to-nikola/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>One-liner Instant Postgres for your development environment</title>
    <link>https://denibertovic.com/posts/57513642221/</link>
    <description><![CDATA[<p>When working on web applications it’s very important to have a setup as similar to the production
environment as possible.</p>
<p>The major component here is using the same database locally, for development, as you use in
production. I myself work mostly with Django and, as most of you Django devs know, it’s very
easy to get started using a Sqlite database.
<!--more-->
Now, I’m not bashing on Sqlite, it definitely has it’s purpose but it’s not on the main dev environment.
It’s nice to be able to set up the designer’s development environment without too much of a hassle
but you should be developing on a database you use in production.</p>
<p>I use Postgres on all of my production servers. I come from a strong Sysadmin background and I set up
most of this on my laptop as well. But it’s one thing to setup everything on my laptop
and a whole different story setting up Postgres on each workstation of every
colleague. It’s exhausting.</p>
<p>Of course you can have a staging server and all that but I really want that everyone has a local
Postgres instance even then (you just catch way to many bugs this way even before the code hits
the staging server).</p>
<p>Enter <a href="http://docker.io">docker</a>. Docker is an open-source engine which automates the deployment of
applications as highly portable, self-sufficient containers which are independent of hardware,
language, framework, packaging system and hosting provider. [3]</p>
<p>Under the hood Docker uses LXC for running isolated containers.
Now, I’ve used LXC by itself, and have some production environments that
use it, but Docker is a huge deal. Not only is it a higher level wrapper on top of LXC but it really
takes so much hassle out of the picture and just enables you to run virtual containers, of your app, or of
other processes. Docker is going to be a huge deal in a lot of production environments and the way we
do deployments in the coming months, but for now, in this post at least, we are just going to use
it for setting up our development environment quickly and easily.</p>
<p>Docker requires a 64 bit Linux distro (kernel 3.8 or higher). Ubuntu has a PPA
(<a href="http://docs.docker.io/en/latest/installation/ubuntulinux/">link</a>) and it’s fairly easy to install it.
For Debian there is a package in the works but you can just download a pre-compiled binary and put
it somewhere on your PATH. This goes for other distros as well.
For Mac and Windows users, you can run docker inside a Vagrant box
(<a href="http://docs.docker.io/en/latest/installation/vagrant/">link</a>).</p>
<p>Linux Containers deployed with Docker have some advantages over the full virtual machines like
VritualBox. A Linux Container looks just like a real virtual machine form the inside. It has it’s
own filesystem, it’s own network interface and so on. In reality it’s just a group of processes
totally isolated form the host operating system but running on the same kernel as the host.
This in turn means that it’s much easier to emulate I/O devices and hardware, that it has far less
CPU overhead and memory consumption and still maintains the portability of a full virtual machine.
Last but not least, the most important feature is that it’s lightning fast to boot up. Containers sometime
take milliseconds to bring up.</p>
<p>Now, before we continue any further we need to explain the concept of docker images. Docker images
are basically snapshots of a system that are used for firing up new containers. So let’s say
I have a ‘postgres’ image you could just do:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>docker run <span class="op">-</span>i <span class="op">-</span>t postgres <span class="op">/</span><span class="bu">bin</span><span class="op">/</span>bash</span></code></pre></div>
<p>Which would bring up a new container and attach you to it’s bash prompt from which you could run
your Postgres process.</p>
<p>Docker images are hosted on a public <a href="https://index.docker.io/">Docker Index</a> from where you can browse
and download all kinds of prepared images, ranging from base images from the docker team (ubuntu, base)
to specialized user images like denibertovic/postgres. There’s also an option to run your own local index
but that’s another blog post :).</p>
<p>I’ve prepared a Postgres container denibertovic/postgres (versions 9.1 and 9.2) which are uploaded
to the docker index and can be used freely by anyone.</p>
<p>Now, I’ve promised you a one-liner solution and I wish to deliver on that. Go ahead and download the
Makefile I’ve prepared <a href="https://gist.github.com/denibertovic/6130681">here</a>. Put it into your project
folder or integrate it into your existing Makefile if you already have one.</p>
<p>All it takes now is to just type:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">make</span> postgres</span></code></pre></div>
<p>This will bring up a new container and run a Postgres 9.2 instance in it. For 9.1 just change the
POSTGRES_VERSION variable in the Makefile. If the above command is run for the first time it will
first download the “denibertovic/postgres” image from the docker index and then run the container. All
subsequent runs will use the already downloaded image.</p>
<p>You can list the images you have locally using the below command:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> images</span></code></pre></div>
<p>Once you have your Postgres instance up and running you can connect to it using:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">psql</span> <span class="at">-Upostgres</span> <span class="at">-h</span> localhost</span></code></pre></div>
<p>Use this to create your database and user</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>postgres<span class="op">=</span># <span class="kw">CREATE</span> <span class="kw">DATABASE</span> my_database;</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>postgres<span class="op">=</span># <span class="kw">CREATE</span> <span class="fu">USER</span> my_user <span class="kw">WITH</span> <span class="kw">PASSWORD</span> <span class="st">&#39;myuserpassword&#39;</span>;</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>postgres<span class="op">=</span># <span class="kw">GRANT</span> <span class="kw">ALL</span> <span class="kw">ON</span> <span class="kw">DATABASE</span> my_database <span class="kw">to</span> my_user;</span></code></pre></div>
<p>You have now successfully created your database and the user for that database.</p>
<p>Now you can set up you project settings files to point to the given database.
For Django projects I like to use <a href="https://github.com/kennethreitz/dj-database-url">dj_database_url</a>
and then you end with something looking like this:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>DATABASES <span class="op">=</span> {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="st">&#39;default&#39;</span>: dj_database_url.config(</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>        default<span class="op">=</span><span class="st">&#39;postgres://my_user:myuserpassword@localhost:5432/my_database&#39;</span>)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Docker containers are ephemeral which means all of the changes done to the container are gone once
you stop the container. With our Postgres container this means that once we stop the container our changes
to the database, and even the database itself, would disappear.
For some cases this might be fine, but most of the time we want out development database to have
persistent data.</p>
<p>To accomplish this what we do is tell the containerized Postgres instance to write all the
database stuff in a folder called “__data” located in the same folder as the Makefile on the host system.
So if at one point you wish to start from scratch and delete all the database data you can do so
simply by deleting the whole “__data” folder and everything in it and repeat the procedure above.</p>
<p>Another thing to note is that the containerized Postgres instance is set up to run on the default
“5432” port which is in turn NAT-ed to localhost on the same port.
This means that if you run one Postgres instance for one project you first need to stop it
before you can run another instance on another project (don’t worry the “make postgres” command
will warn you about this).</p>
<p>So first grab the container ID from the running container with:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> ps</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="ex">ID</span>                  IMAGE                       COMMAND                PORTS</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="ex">000f0a07e49a</span>        denibertovic/postgres:9.2   /usr/local/bin/start   5432-<span class="op">&gt;</span>5432</span></code></pre></div>
<p>And then use the ID to stop the container:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> stop 000f0a07e49a</span></code></pre></div>
<p>And that’s it folks. Hope you find this useful and feel free to leave comments/tips/improvements.</p>
<p>Next up will be a post about App deployments.</p>]]></description>
    <pubDate>Tue, 06 Aug 2013 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/57513642221/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Pixbin.us just got an API</title>
    <link>https://denibertovic.com/posts/32625923174/</link>
    <description><![CDATA[<p>Took us awhile but Pixbin just got an API. Well…the beginning of one at least. You heard it here first folks. :)
You can now upload images from your code.
<!--more--></p>
<p>How to do it?! Here’s an example:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> base64, urllib, urllib2</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="cf">with</span> <span class="bu">open</span>(<span class="st">&#39;image.jpeg&#39;</span>) <span class="im">as</span> f:</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    imagedata <span class="op">=</span> base64.b64encode(f.read())</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    data <span class="op">=</span> urllib.urlencode({<span class="st">&#39;image&#39;</span>: imagedata, <span class="st">&#39;caption&#39;</span>: <span class="st">&#39;test&#39;</span>})</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    u <span class="op">=</span> urllib2.urlopen(<span class="st">&#39;http://pixbin.us/api/post/&#39;</span>, data)</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span> u.readlines()</span></code></pre></div>
<p>The example above is in Python but the same principles apply for your language of choice. Just base64 encode an image and send a POST request with the shown parameters to the given link. The response you get back will be a json like so:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span><span class="dt">&quot;link&quot;</span><span class="fu">:</span> <span class="st">&quot;http://pixbin.us/abcabc&quot;</span><span class="fu">}</span></span></code></pre></div>
<p>This can be very useful if you want to, let’s say, quickly share a screenshot with somebody.
We have a very handy little bash script that let’s you do just that. You can check it out here. Special thanks to Senko for making the script.</p>
<p>Check back next week for more, hopefully useful, stuff. :)</p>]]></description>
    <pubDate>Mon, 01 Oct 2012 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/32625923174/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>
<item>
    <title>Finally</title>
    <link>https://denibertovic.com/posts/30680397805/</link>
    <description><![CDATA[<p>I’ve been struggling for a while now with the idea of writing a blog.
The problem was that I never got around to actually making the site and months passed by.
<!--more--></p>
<p>A while ago I stumbled upon a github project that was aiming to solve exactly this kind of issue. The thing is that sometimes people just don’t have enough time on their hands, and with a blog site the content is the thing that matters, not what platform you are using. And there are many of them out there.</p>
<p>I decided to use Syte because it was built specifically with this in mind and was built for web developers/designers. Another thing that was a plus is that it was built using Django :) . Thanks Rodrigo.</p>
<p>Now I have no more excuses so I have to start writing. Mainly I’ll write about all the geeky things that caught my eye and some posts are gonna serve merely as a reference about some technical stuff so I don’t forget later on how I solved a problem. And hey if it helps someone else in the meantime even better.</p>
<p>Over and out. For now.</p>]]></description>
    <pubDate>Sat, 01 Sep 2012 00:00:00 UT</pubDate>
    <guid>https://denibertovic.com/posts/30680397805/</guid>
    <dc:creator>Deni Bertovic</dc:creator>
</item>

    </channel>
</rss>
