<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Brave New Geek</title><link>https://bravenewgeek.com/</link><description>Introspections of a software engineer</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 08 May 2025 09:12:33 -0600</lastBuildDate><atom:link href="https://bravenewgeek.com/index.xml" rel="self" type="application/rss+xml"/><item><title>What is Koreo?</title><link>https://bravenewgeek.com/what-is-koreo/</link><pubDate>Thu, 08 May 2025 09:12:33 -0600</pubDate><guid>https://bravenewgeek.com/what-is-koreo/</guid><description>&lt;h2 id="the-platform-engineering-toolkit-for-kubernetes"&gt;The platform engineering toolkit for Kubernetes&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2025/05/og-image-1024x538.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Last month we open sourced &lt;a href="https://koreo.dev"&gt;Koreo&lt;/a&gt;, our “platform engineering toolkit for Kubernetes.” Since then, we’ve seen a lot of interest from folks in the platform engineering and DevOps space. We’ve also gotten a lot of questions from people trying to better understand how Koreo fits into an already crowded landscape of Kubernetes tools. Koreo is a fairly complex tool, so it can be difficult to quickly grasp just what exactly it is_,_ what problems it’s designed to solve, and how it compares to other, similar tools. In this post, I want to dive into these topics and also discuss the original motivation behind Koreo.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-platform-engineering-toolkit-for-kubernetes">The platform engineering toolkit for Kubernetes</h2>
<p><img loading="lazy" src="/wp-content/uploads/2025/05/og-image-1024x538.jpg"></p>
<p>Last month we open sourced <a href="https://koreo.dev">Koreo</a>, our “platform engineering toolkit for Kubernetes.” Since then, we’ve seen a lot of interest from folks in the platform engineering and DevOps space. We’ve also gotten a lot of questions from people trying to better understand how Koreo fits into an already crowded landscape of Kubernetes tools. Koreo is a fairly complex tool, so it can be difficult to quickly grasp just what exactly it is_,_ what problems it’s designed to solve, and how it compares to other, similar tools. In this post, I want to dive into these topics and also discuss the original motivation behind Koreo.</p>
<p>If you’ve read the Koreo website, you’ll know that we refer to it as “a new approach to Kubernetes configuration management and resource orchestration,” but what does this really mean? There are two parts to unpack here: configuration management and resource orchestration.</p>
<p>By far, the most common approach to configuration management in Kubernetes is Helm. Helm charts provide a convenient way to templatize and package up Kubernetes resources and make them configurable via values files. There are some challenges and limitations with Helm though. One is around how it works as a templating system. We talked about this in depth <a href="https://realkinetic.substack.com/p/stop-treating-yaml-like-a-string">here</a> so I won’t go into a ton of detail, but a challenge with Helm is when charts begin to grow beyond basic templating. Once you start to introduce logic and nested conditionals into your templates, they quickly become difficult to understand, maintain, and evolve. Helm is actually a pretty rudimentary approach because it really just treats configuration management as YAML string templating.</p>
<p><a href="https://kustomize.io">Kustomize</a> can handle some of these cases better by letting you specify base resource manifests and then customize them through overlays and patches that are more structurally aware. However, this largely assumes that the configuration variations are static and don’t rely on computed values or business logic. In other words, as deployments grow in complexity, Kustomize’s reliance on static overlays can become cumbersome and limiting.</p>
<p>Helm and Kustomize are not resource orchestrators—they’re <em>templating</em> tools. If your resources have non-deterministic dependencies on each other, you’ll need to handle this sequencing yourself. For example, to create a VPC and then a Kubernetes cluster in AWS, you must first apply the VPC and then use something like Helm’s <a href="https://helm.sh/docs/chart_template_guide/functions_and_pipelines/#using-the-lookup-function">lookup</a> function to retrieve its outputs (e.g. subnets) for use in templating the cluster. There are a lot of situations where the inputs of a resource require the outputs of a different resource. Some of these might be deterministic, perhaps like a CIDR block, but many are not. This dependency between resources is what I mean by <em>resource orchestration</em>. Most tools treat configuration management and resource orchestration as separate concerns: tools like Helm, Kustomize, Jsonnet, and Cue handle configuration management, while tools like Crossplane, Argo, and Kro address resource orchestration. But in practice, these responsibilities are often deeply intertwined. Especially in platform engineering, configuring resources almost always requires orchestrating them.</p>
<h3 id="the-motivation-behind-koreo">The Motivation Behind Koreo</h3>
<p>Koreo was built to make platform engineers’ lives easier. We originally developed it out of a need to both configure and orchestrate cloud resources across GCP and AWS—using <a href="https://cloud.google.com/config-connector/docs/overview">Config Connector</a> and <a href="https://aws-controllers-k8s.github.io/community/docs/community/overview/">ACK</a>—and integrate them cleanly into a broader internal developer platform. Helm wasn’t viable due to its lack of orchestration capabilities. Crossplane, while powerful, proved overly complex but also had several key limitations, namely it’s tightly coupled to Provider APIs and it offers limited support for multitenancy. Kro didn’t exist at the time, but even today it remains too rigid and inflexible for our needs. We also required a way to define standard, base configurations for resources, expose only certain configuration knobs to application teams, and give developers the ability to safely customize what they needed.</p>
<p>Additionally, we felt that much of the innovation in platform engineering and DevOps has thrown away a lot of the learnings and developments that have occurred in software engineering over the last several decades. In particular, many modern infrastructure and platform tools lack robust mechanisms for testing configuration and orchestration logic. Unlike application code, which benefits from well-established practices around unit testing, integration testing, and CI workflows, platform code is often fragile, opaque, and difficult to validate. There’s no easy way to test a change to a Helm chart or a Crossplane composition without applying it to the cluster and hoping for the best. This leads to brittle platforms, longer feedback cycles, and a higher risk of outages. With Koreo, we wanted to bring the rigor of software development—testable components, reproducible workflows, clear interfaces—into the platform space.</p>
<p>We initially built platforms using custom controllers implemented in Go and Python, which allowed us to move quickly. However, extending and iterating on the platform became increasingly difficult and time-consuming. In order to make it easier to implement new capabilities quickly, one of our developers built a lightweight framework to orchestrate workflows by mapping values between steps. Another developer sought to make our existing system more flexible with a simple approach to configuration templating. When we saw these two ideas, which were in two different code bases in two different parts of the system, we realized that they could work well together, and the idea for Koreo was born.</p>
<h3 id="koreos-approach">Koreo’s Approach</h3>
<p>At the heart of Koreo are functions and workflows. Functions are small, reusable logic units that can either be pure and computational (we call these ValueFunctions) or side-effectful and capable of interacting with the Kubernetes API to create, modify, or read resources (ResourceFunctions). These are then composed into <em>workflows</em>, which define a graph of steps, conditions, and dependencies—kind of like a programmable controller but defined entirely in YAML.</p>
<p>We chose YAML deliberately because it doesn’t abstract what is actually being configured: Kubernetes resource manifests. We’ve found keeping the tool “true” to the underlying thing it manages reduces mental overhead for developers and makes it easier to reason about what is happening. This choice also brings some practical benefits. It’s easy to take an existing Kubernetes resource definition and use it as the basis for a ResourceFunction or ResourceTemplate. It also allows us to very cleanly <em>test</em> materialized resources. With FunctionTests, we can ensure resources are configured as expected based on inputs to the function. And by using ResourceTemplates, we can define standard base configurations and then overlay dynamic values on top. This approach to resource materialization allows us to decompose configuration into small, reusable components that can be tested both in isolation and as part of a larger workflow. </p>
<p>Koreo provides a unified approach to configuration management and resource orchestration. Workflows allow us to connect resources together by mapping the outputs of a resource to the inputs of another resource. For instance, let’s say you want to provision a GKE cluster, configure network policies, and deploy an application—all based on some higher-level spec like a tenant definition. With Koreo, you can define a workflow that reads the spec, provisions the GKE cluster, waits for it to be ready, extracts its outputs (e.g. endpoint, credentials), and passes them downstream to subsequent steps. Each step is explicit, composable, and testable.</p>
<p>In effect, Koreo lets you seamlessly integrate Kubernetes controllers and off-the-shelf operators like Config Connector and ACK into a cohesive platform. With this, you can build your own platform controllers without actually needing to implement a custom operator. Any resource in Kubernetes can be connected, and we can use Koreo’s control-flow primitives to implement workflows that are as simple as provisioning a few resources or as complex as automating an entire internal developer platform.</p>
<h3 id="why-koreo-matters">Why Koreo Matters</h3>
<p>What makes Koreo different is that it treats platform engineering not as an afterthought to infrastructure management or application deployment, but as a discipline in its own right—one that deserves the same principles of clarity, testability, and modularity that we apply to software engineering. It’s not just about making YAML more bearable or chaining together a few CLI commands. It’s about enabling platform teams to build robust, composable, and predictable automation that supports developers without hiding important details or creating layers of abstraction that become liabilities over time.</p>
<p>Koreo lets you define your platform architecture as code, test it like code, and evolve it like code. It’s declarative, but programmable. It’s simple to start with, but powerful enough to scale across teams, environments, and cloud providers. It gives you a model for thinking about how platform components fit together and the control to manage that complexity as your needs grow.</p>
<h3 id="whats-next">What’s Next</h3>
<p>Since open sourcing Koreo, we’ve been actively working on improving documentation, adding more examples, and expanding support for new use cases. We’re especially excited about adding support for managing resources in other Kubernetes clusters. This enables running Koreo as a centralized control plane in addition to a federated model. We’re also investing in better tooling around developer experience, like improved type checking, workflow debugging, and enhanced UI capabilities to visualize workflow executions.</p>
<p>If you’re a platform engineer, SRE, or DevOps practitioner struggling to manage the growing complexity of Kubernetes-based platforms, we’d love for you to give <a href="https://koreo.dev">Koreo</a> a shot. We’re just getting started, and we’d love your feedback.</p>
]]></content:encoded></item><item><title>Controller-Driven Infrastructure as Code</title><link>https://bravenewgeek.com/controller-driven-infrastructure-as-code/</link><pubDate>Wed, 19 Mar 2025 14:36:25 -0600</pubDate><guid>https://bravenewgeek.com/controller-driven-infrastructure-as-code/</guid><description>&lt;h2 id="harnessing-the-kubernetes-resource-model-for-modern-infrastructure-management"&gt;Harnessing the Kubernetes Resource Model for modern infrastructure management&lt;/h2&gt;
&lt;p&gt;Infrastructure as Code (IaC) revolutionized how we manage infrastructure, enabling developers to define resources declaratively and automate their deployment. However, tools like Terraform and CloudFormation, despite their declarative configuration, rely on an &lt;em&gt;operation-centric&lt;/em&gt; model, where resources are created or updated through one-shot commands.&lt;/p&gt;
&lt;h3 id="the-evolution-of-iac-from-operations-to-controllers"&gt;The evolution of IaC: From operations to controllers&lt;/h3&gt;
&lt;p&gt;In contrast, Kubernetes introduced a new paradigm with its &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controller pattern&lt;/a&gt; and the &lt;a href="https://github.com/kubernetes/design-proposals-archive/blob/main/architecture/resource-management.md"&gt;Kubernetes Resource Model&lt;/a&gt; (KRM). This &lt;em&gt;resource-centric&lt;/em&gt; approach to APIs redefines infrastructure management by focusing on desired state rather than discrete operations. Kubernetes controllers continuously monitor resources, ensuring they conform to their declarative configurations by performing actions to move the actual state closer to the desired state, much like a human operator would. This is known as a &lt;em&gt;control loop&lt;/em&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="harnessing-the-kubernetes-resource-model-for-modern-infrastructure-management">Harnessing the Kubernetes Resource Model for modern infrastructure management</h2>
<p>Infrastructure as Code (IaC) revolutionized how we manage infrastructure, enabling developers to define resources declaratively and automate their deployment. However, tools like Terraform and CloudFormation, despite their declarative configuration, rely on an <em>operation-centric</em> model, where resources are created or updated through one-shot commands.</p>
<h3 id="the-evolution-of-iac-from-operations-to-controllers">The evolution of IaC: From operations to controllers</h3>
<p>In contrast, Kubernetes introduced a new paradigm with its <a href="https://kubernetes.io/docs/concepts/architecture/controller/">controller pattern</a> and the <a href="https://github.com/kubernetes/design-proposals-archive/blob/main/architecture/resource-management.md">Kubernetes Resource Model</a> (KRM). This <em>resource-centric</em> approach to APIs redefines infrastructure management by focusing on desired state rather than discrete operations. Kubernetes controllers continuously monitor resources, ensuring they conform to their declarative configurations by performing actions to move the actual state closer to the desired state, much like a human operator would. This is known as a <em>control loop</em>.</p>
<p>Kubernetes also demonstrated the value of providing architectural building blocks that encapsulate standard patterns, such as a <a href="https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/">Deployment</a>. These can then be composed and combined to provide impressive capabilities with little effort—<a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">HorizontalPodAutoscaler</a> is an example of this. Through extensibility, Kubernetes allows developers to define new resource types and controllers, making it a natural fit for managing not just application workloads but infrastructure of <em>any</em> kind. This enables you to actually provide a clean API for common architectural needs that encapsulates a lot of routine business logic. Extending this model to IaC is something we call <em>Controller-Driven IaC</em>.</p>
<h3 id="building-on-the-kubernetes-controller-model">Building on the Kubernetes controller model</h3>
<p>Controller-Driven IaC builds upon the Kubernetes foundation, leveraging its controllers to reconcile cloud resources and maintain continuous alignment between desired and actual states. By extending Kubernetes’ principles of declarative configuration and control loops to IaC, this approach offers a resilient and scalable way to manage modern infrastructure. Integrating cloud and external system APIs into Kubernetes controllers enables continuous state reconciliation beyond Kubernetes itself, ensuring consistency, eliminating configuration drift, and reducing operational complexity. It results in an IaC solution that is capable of working correctly with modern, dynamic infrastructure. Additionally, it brings many of the other benefits of Kubernetes—such as RBAC, policy enforcement, and observability—to infrastructure and systems <em>outside</em> the cluster, creating a unified and flexible management framework. In essence, Kubernetes becomes the control plane for your entire developer platform. That means you can offer developers a self-service experience within defined bounds, and this can further be scoped to specific application domains.</p>
<p>This concept isn’t entirely new. Kubernetes introduced <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/">Custom Resource Definitions</a> (CRDs) in 2017, enabling the creation of <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/">Operators</a>, or custom controllers, to extend its functionality. Today, <a href="https://operatorhub.io">countless Operators exist</a> to manage diverse applications and infrastructure, both <em>within</em> and <em>outside of</em> Kubernetes, including those from major cloud providers. For instance, GCP’s <a href="https://cloud.google.com/config-connector/docs/overview">Config Connector</a>, AWS’s <a href="https://github.com/aws-controllers-k8s/community">ACK</a>, and Azure’s <a href="https://azure.github.io/azure-service-operator/">ASO</a> offer controllers for managing their respective platform’s infrastructure. However, just as operationalizing Kubernetes requires tooling and investment to build an effective platform, so too does implementing Controller-Driven IaC. Integrating these various controllers into a cohesive platform requires its own kind of orchestration. We need a way to <em>program</em> control loops—whether built-in Kubernetes controllers (like Deployments or Jobs), off-the-shelf controllers (like ACK or Config Connector), or custom controllers we’ve built ourselves.</p>
<h3 id="introducing-koreo-programming-control-loops-for-modern-platforms">Introducing Koreo: Programming control loops for modern platforms</h3>
<p>There are tools such as <a href="https://www.crossplane.io">Crossplane</a> that take a controller-oriented approach to infrastructure, but they have their own challenges and limitations. In particular, we really need the ability to compose arbitrary Kubernetes resources and controllers, not just specific provider APIs. What if we could treat <em>anything</em> in Kubernetes as a referenceable object capable of acting as the input or output to an automated workflow, and without the need for building tons of CRDs or custom Operators? Additionally, it’s critical that resources can be namespaced rather than cluster-scoped to support multi-tenant environments and that the corresponding infrastructure can live in cloud projects or accounts separate from where the control plane itself lives.</p>
<p>To address these needs and deliver the full potential of Controller-Driven IaC, we’ve developed and open-sourced <a href="http://koreo.dev"><em>Koreo</em></a>, a platform engineering toolkit for Kubernetes. Koreo is a new approach to Kubernetes configuration management and resource orchestration empowering developers through programmable workflows and structured data. It enables seamless integration and automation around the Kubernetes Resource Model, supporting a wide range of use cases centered on Controller-Driven IaC. Koreo serves as a <em>meta-controller programming language</em> and runtime that allows you to compose control loops into powerful abstractions.</p>
<p><a href="/wp-content/uploads/2025/03/image1.png"><img loading="lazy" src="/wp-content/uploads/2025/03/image1-1024x790.png"></a></p>
<p><em>The Koreo UI showing a workflow for a custom AWS workload abstraction</em></p>
<p>Koreo is specifically built to empower platform engineering teams and DevOps engineers by allowing them to provide Architecture-as-Code building blocks to the teams they support. With Koreo, you can easily leverage existing Kubernetes Operators or create your own specialized Operators, then expose them through powerful, high-level abstractions aligned with your organization’s needs. For example, you can develop a “StatelessCrudApp” that allows development teams to enable company-standard databases and caches with minimal effort. Similarly, you can build flexible automations that combine and orchestrate various Kubernetes primitives.</p>
<p><a href="/wp-content/uploads/2025/03/image2.png"><img loading="lazy" src="/wp-content/uploads/2025/03/image2.png"></a></p>
<p><em>An instance of the custom AWS workload abstraction</em></p>
<p>Where Koreo really shines, however, is making it fast and safe to add new capabilities to your internal developer platform. Existing configuration management tools like Helm and Kustomize, while useful for simpler configurations, become unwieldy when dealing with the intricacies of modern Kubernetes deployments. They ultimately treat configuration as static data, and this becomes problematic as configuration evolves in complexity.</p>
<p>Koreo instead embraces configuration as code by providing a programming language and runtime with robust developer tooling. This allows platform engineers to define and manage Kubernetes configurations and resource orchestration in a way that is better suited to modern infrastructure challenges. It offers a solution that scales with complexity. A built-in testing framework makes it easy to quickly validate configuration and iterate on infrastructure, and IDE integration gives developers a familiar programming-like experience.</p>
<h3 id="the-future-of-infrastructure-management-is-controller-driven">The future of infrastructure management is controller-driven</h3>
<p>By harnessing the power of Kubernetes controllers for Infrastructure as Code, Koreo bridges the gap between declarative configuration and dynamic infrastructure management. It moves beyond the limitations of traditional IaC, offering a truly Kubernetes-native approach that brings the benefits of control loops, composability, and continuous reconciliation to your entire platform. With Koreo, you’re not just managing resources; you’re composing Kubernetes controllers to do powerful things like building internal developer platforms, managing multi-cloud infrastructure, or orchestrating application deployments and other complex workflows.</p>
<p>See what you can build with <a href="http://koreo.dev">Koreo</a>.</p>
]]></content:encoded></item><item><title>Platform Engineering as a Service</title><link>https://bravenewgeek.com/platform-engineering-as-a-service/</link><pubDate>Thu, 14 Nov 2024 13:47:29 -0700</pubDate><guid>https://bravenewgeek.com/platform-engineering-as-a-service/</guid><description>&lt;p&gt;Like most industry jargon, “DevOps” means a lot of things to a lot of different people. While many folks view it as specific to certain tooling or practices, such as CI/CD or Infrastructure as Code (IaC), I’ve always viewed it as an organizational model for how software is built and delivered. In particular, my interpretation is that DevOps is about shifting more responsibilities “left” onto developers, moving away from the more traditional “throw it over the wall” approach to IT operations. No doubt this encompasses tooling or practices like CI/CD and IaC, which are responsibilities that developers now shoulder, perhaps with the support of dev tools, productivity, or enablement teams—some companies just call this the “DevOps” team.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Like most industry jargon, “DevOps” means a lot of things to a lot of different people. While many folks view it as specific to certain tooling or practices, such as CI/CD or Infrastructure as Code (IaC), I’ve always viewed it as an organizational model for how software is built and delivered. In particular, my interpretation is that DevOps is about shifting more responsibilities “left” onto developers, moving away from the more traditional “throw it over the wall” approach to IT operations. No doubt this encompasses tooling or practices like CI/CD and IaC, which are responsibilities that developers now shoulder, perhaps with the support of dev tools, productivity, or enablement teams—some companies just call this the “DevOps” team.</p>
<p>While many organizations still operate with the traditional silos, DevOps has established itself as an industry norm. But as organizations push the boundaries of software development, the limitations of DevOps are becoming increasingly apparent. <strong>The problem is that DevOps, in its pursuit of speed and autonomy, often results in chaos and inefficiency.</strong> Teams end up reinventing the wheel, creating bespoke solutions for the same problems, and struggling with inconsistent tooling and practices across the organization. The outcome? Technical debt, fragmented processes, and wasted effort. Many of the teams we work with at <a href="https://realkinetic.com">Real Kinetic</a> spend significantly more time on the “DevOps work” than they do on actual product work.</p>
<p><a href="/wp-content/uploads/2024/11/devops_trend.png"><img loading="lazy" src="/wp-content/uploads/2024/11/devops_trend-1024x605.png"></a></p>
<p><em>Google Trends for “DevOps”</em></p>
<h2 id="the-rise-of-platform-engineering">The Rise of Platform Engineering</h2>
<p>This is where <strong>Platform Engineering</strong> comes in. Rather than having each development team own their entire infrastructure stack, platform engineering provides a centralized, productized approach to infrastructure and developer tools. It’s about creating reusable, self-service platforms that development teams can leverage to build, deploy, and scale their applications efficiently. These platforms abstract away the complexities of cloud infrastructure, CI/CD pipelines, and security, enabling developers to focus on writing code rather than managing infrastructure or “glue”.</p>
<p>Platform engineering brings structure to the chaos of DevOps by creating a standardized, cohesive platform that empowers development teams while maintaining best practices and governance. It’s a solution to the growing complexity and sprawl that comes with scaling software delivery and scaling DevOps. Platform engineering is very much in its infancy as DevOps was circa 2012, but there’s growing interest in it as organizations hit the ceiling of DevOps.</p>
<p><a href="/wp-content/uploads/2024/11/platform_engineering_trend.png"><img loading="lazy" src="/wp-content/uploads/2024/11/platform_engineering_trend-1024x602.png"></a></p>
<p><em>Google Trends for “Platform Engineering”</em></p>
<h2 id="but-theres-a-catch-the-investment-barrier">But There’s a Catch: The Investment Barrier</h2>
<p>Implementing platform engineering isn’t without its challenges. Building a robust, scalable platform requires significant time, resources, and expertise. It demands a deep understanding of your organization’s technology stack, development workflows, and business objectives. And importantly, it diverts valuable resources away from core product development efforts.</p>
<p><strong>Many organizations are hesitant to make this level of investment</strong>, especially if it’s not their core competency. They either end up doing it poorly—leading to a half-baked platform that doesn’t deliver the promised efficiencies—or they avoid it altogether, sticking to the DevOps status quo. This often leaves them with the worst of both worlds: the overhead of DevOps without the benefits of a streamlined, developer-friendly platform.</p>
<p>What we most often see are <strong>dev tools teams masquerading as platform engineering.</strong> <a href="https://skamille.medium.com/platform-engineering-beyond-cfengine-daa9268c9c5b">As Camille Fournier puts it</a>, they build <em>scripts or tools</em> around configuration management and infrastructure provisioning, not <em>products</em>. Usually it’s because they either don’t want to have skin in the game or they don’t have a mandate from leadership. “Not having skin in the game” means some combination of these things: a) they don’t want to build their own software, b) they don’t want to be on the hook for operations, or c) they don’t want to be in the critical path for production or become a bottleneck. Instead, they provide “blueprints” for these things and the burden and responsibility ultimately falls on the product teams—this is just DevOps.</p>
<p>Another issue is that organizations don’t want to allocate the headcount to do real platform engineering. They’re not wrong to be hesitant because it takes <em>real</em> investment to actually do it. As a result, however, they take half measures. We frequently see companies take an <a href="https://about.gitlab.com/topics/version-control/what-is-innersource/">InnerSource</a> approach as an attempt to basically socialize platform engineering. I have never seen this approach work well in practice unless there’s clear ownership and the team has a clear mandate. And just as before, this approach pushes scripts, not products. Without ownership and directive, it just reverts back to DevOps which leads to inefficiency and sprawl.</p>
<h2 id="the-solution-platform-engineering-as-a-service">The Solution: Platform Engineering as a Service</h2>
<p>This is where <strong>Platform Engineering as a Service (PEaaS)</strong> comes in. Unlike traditional Platform as a Service (PaaS) offerings, which provide a rigid, one-size-fits-all platform that abstracts away the underlying infrastructure, PEaaS is designed to be flexible and tailored to your unique requirements. It doesn’t hide the infrastructure but rather empowers your teams by providing the tools, automation, and best practices needed to build and operate cloud-native applications efficiently <em>for your organization</em>.</p>
<p>Instead of building and maintaining a custom platform internally, organizations can partner with experts who specialize in platform engineering and bring deep, hands-on experience to the table. With PEaaS, you get all the benefits of a mature, scalable platform without the heavy upfront investment or the distraction from your core product development. This means that a robust, enterprise-grade platform can be implemented in a fraction of the time, and managed for a fraction of the cost. What typically takes companies 6 months or more to build can be accomplished in days or weeks. And, what typically takes a team of 5 – 10 engineers working full-time to manage can be handled by 1 engineer, often on a part-time basis.</p>
<p>At Real Kinetic, we’ve been helping organizations accelerate their software delivery for years. In fact, <a href="https://www.youtube.com/watch?v=JUy3GYkPfto">we’ve been doing platform engineering long before it was called <em>platform engineering</em>.</a> We bring our extensive expertise in cloud infrastructure, CI/CD, and developer enablement to build platforms that align with your organization’s unique needs and technology stack. By leveraging our Platform Engineering as a Service, you can stay focused on what you do best—building great products—while we take care of the complexities of infrastructure, automation, and developer tooling.</p>
<h2 id="why-real-kinetic">Why Real Kinetic?</h2>
<p><strong>Why should you trust us with your platform engineering needs?</strong> Because we’ve done it before, time and time again. <a href="https://realkinetic.com">Real Kinetic</a> has helped numerous organizations—from startups to large enterprises—modernize their software delivery practices, improve developer productivity, and accelerate time to market. Our approach is rooted in real-world experience, not theory. We understand the challenges of scaling platforms because we’ve been there ourselves.</p>
<p>When you partner with Real Kinetic, you’re not just getting a service provider—you’re getting a team of experts who are invested in your success <em>and have skin in the game</em>. We’re here to build a platform that scales with your business, optimizes your development workflows, and ultimately drives more value for your customers.</p>
<h2 id="ready-to-level-up-your-software-delivery">Ready to Level Up Your Software Delivery?</h2>
<p>If you’re tired of the inefficiencies of DevOps and ready to embrace the power of platform engineering, <a href="https://realkinetic.com/#contact">let’s talk</a>. Real Kinetic’s Platform Engineering as a Service is your fast track to a scalable, efficient platform that empowers your developers and accelerates your time to market. And if you’re using AWS or GCP, we’re also looking for a few companies to pilot our batteries-included platform engineering product <a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=peaas">Konfigurate</a>.</p>
]]></content:encoded></item><item><title>Deployment-Driven Development</title><link>https://bravenewgeek.com/deployment-driven-development/</link><pubDate>Mon, 11 Nov 2024 15:57:13 -0700</pubDate><guid>https://bravenewgeek.com/deployment-driven-development/</guid><description>&lt;p&gt;&lt;a href="https://bravenewgeek.com/wp-content/uploads/2024/11/pipeline.png"&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2024/11/pipeline.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Most people use “DDD” to refer to &lt;em&gt;Domain-Driven Design&lt;/em&gt;, which is a useful tool for thinking about API boundaries and system architecture. It provides a way to map a business problem into software. At &lt;a href="https://realkinetic.com"&gt;Real Kinetic&lt;/a&gt;, we regularly help our clients utilize Domain-Driven Design as well as other strategies to architect their systems, avoid some of the pitfalls of DDD, and build an effective foundation for designing software. But &lt;em&gt;this&lt;/em&gt; DDD only speaks to one small aspect of building and shipping software.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="/wp-content/uploads/2024/11/pipeline.png"><img loading="lazy" src="/wp-content/uploads/2024/11/pipeline.png"></a></p>
<p>Most people use “DDD” to refer to <em>Domain-Driven Design</em>, which is a useful tool for thinking about API boundaries and system architecture. It provides a way to map a business problem into software. At <a href="https://realkinetic.com">Real Kinetic</a>, we regularly help our clients utilize Domain-Driven Design as well as other strategies to architect their systems, avoid some of the pitfalls of DDD, and build an effective foundation for designing software. But <em>this</em> DDD only speaks to one small aspect of building and shipping software.</p>
<p>Software architecture is critical to a number of concerns like scalability, adaptability, and speed-to-market for new products and features, but its effects are usually not felt for some time—weeks, months, even <em>years</em> later. These delayed effects are <em>lagging indicators</em> that reveal how “well” a system was architected (I use quotes here because this is really quite subjective and relative to both the short- and long-term needs of the business). Other lagging indicators also highlight problems in software development. For instance, a high number of reported bugs may point to deficiencies in QA and testing processes, while the volume or severity of findings in an audit may signal issues in security, compliance, or SDLC practices. Similarly, accumulated tech debt may reflect deeper systemic issues.</p>
<p>These lagging indicators often result from a delayed and reactive approach to managing concerns like security, compliance, quality, and even architecture. These concerns are frequently deferred to later stages in the development process or left to evolve on their own organically. It’s not uncommon for us to see teams complete the development of a product or feature, only to spend <em>months</em> navigating the hurdles to get it into production. It may be testing or production-readiness processes, integration challenges, infrastructure issues, change-review boards, or a combination of all of these. One way or another, it takes many teams inordinately long to go from idea to in-customer’s-hands.</p>
<p>Through our work consulting with startups, scaleups, and Fortune 500 companies to improve their product delivery, we’ve been building a solution to this problem called <a href="https://konfigurate.com?utm_source=bravenewgeek.com&amp;utm_campaign=ddd">Konfigurate</a>. But before diving into that, I want to introduce you to the <em>other</em> DDD—<em>Deployment-Driven Development</em>—and why it’s critical to improving delivery, how it relates to platform engineering, and how it can be implemented.</p>
<h2 id="shift-left">Shift Left</h2>
<p>The act of <em>scaling</em> a product—that is to say, going from prototype to production and beyond—takes focus off the product itself. This is because there is a whole host of undifferentiated work that is needed at various stages of a product’s lifecycle:</p>
<ul>
<li>Infrastructure configuration and management</li>
<li>CI/CD tooling</li>
<li>Workforce and workload IAM</li>
<li>System security</li>
<li>Compliance</li>
<li>Sprawl and tech debt management</li>
</ul>
<p>“Shifting left” has become a mantra for high-performing teams, particularly as it relates to software testing. The reality, though, is that much of this undifferentiated work—security, compliance, infrastructure, deployment—is still often treated as a separate concern to be tackled just before the system goes live or, in some cases, <em>after</em> it’s already been deployed to production. Security is a good example of this, where tools like Wiz scan for security issues in the runtime environment or during CI/CD—<em>after</em> the code has been written. Nothing against continuous security, but wouldn’t it be nice if systems could be built the “right” way up front to reduce rework or delays?</p>
<p><em>Deployment-Driven Development</em>—a different kind of DDD—challenges this approach by flipping the paradigm. Instead of treating deployment as a final milestone, it prioritizes deployment from the start. The idea is simple but powerful: start with a deployment to a real, production-like environment on day one then work your way backwards. Doing this shifts more of these concerns left into the development process.</p>
<h2 id="what-is-deployment-driven-development">What is Deployment-Driven Development?</h2>
<p>Deployment-Driven Development begins with a live, deployable environment and treats it as the foundation for all development activities that follow. The very first step when a new workload is created, before anything else happens, is deploying it to a real environment. From that point on, every line of code, every change, and every new feature is created and tested in an environment that mirrors production. This approach ensures that from day one, teams are building, testing, and iterating in conditions that match the realities of their live system, giving them the confidence that their application is production-ready at any given moment. As a result, teams avoid the common bottleneck of scrambling to get the application ready for production after development is complete—what I call “running the production gauntlet.”</p>
<p>While early-stage deployment to production-like environments is often considered best practice in modern software development, DDD formalizes this approach by reversing the typical order: <strong>start with deployment, then integrate code and configuration into that live setup</strong>. Setting up a real environment can be a significant lift for many teams, as provisioning and configuring production-like environments with the right infrastructure and permissions remains a complex task. By making deployment as simple and foundational as possible, Deployment-Driven Development makes it easier for teams to deliver faster with fewer roadblocks.</p>
<p>Shifting left traditionally applies to moving testing earlier in the development lifecycle, but DDD takes this idea further by shifting the deployment process itself to the beginning. Instead of validating code in isolation, the code is deployed in a full, production-ready environment, using automated provisioning to manage resources and integrate infrastructure. By proactively addressing deployment hurdles early, DDD helps reduce surprises and delays later on.</p>
<h2 id="why-legacy-infrastructure-as-code-falls-short">Why Legacy Infrastructure as Code Falls Short</h2>
<p><a href="https://blog.realkinetic.com/its-time-to-retire-terraform-30545fd5f186">Legacy Infrastructure as Code</a> (IaC) like Terraform or CloudFormation doesn’t enable Deployment-Driven Development because these tools lack opinionation—clear, enforced standards for how infrastructure should be built and configured. They are general-purpose tools designed to solve all problems, much like a general-purpose programming language. For example, “least-privileged access” is widely accepted as a best practice, yet IaC tools don’t inherently enforce this principle. Developers must implement least-privileged access and other standards themselves. These IaC primitives just wrap the cloud provider’s API. The result is that legacy IaC tools don’t facilitate Deployment-Driven Development without a sizable investment into platform engineering.</p>
<p>There are abstractions that can help with this, whether it’s writing Terraform modules or using CDK to abstract CloudFormation and implement reusable constructs, but this goes back to what I said earlier about undifferentiated work: the act of “scaling” a product takes focus off the product itself. Consequently, we often see teams—especially those following a DevOps model—spending a disproportionate amount of time writing IaC versus writing product code.</p>
<p>With Deployment-Driven Development, however, opinionated infrastructure must be baked in from the beginning, automating setup in a way that enforces best practices, such as least-privileged access, as <em>default</em> behavior rather than optional guidance. To make this work with traditional IaC tools, it requires <a href="https://blog.realkinetic.com/productize-your-engineering-organizations-internal-tools-25fd2cbe3fb0">investing in a true platform engineering team to solve these problems</a> for the rest of the organization. I rarely see teams approaching this from the DevOps angle doing this well at scale—it usually results in a great deal of inefficiency and sprawl. People copy/paste and bad patterns quickly proliferate.</p>
<h2 id="platform-engineering-and-golden-paths">Platform Engineering and Golden Paths</h2>
<p>Shipping software <em>the right way</em> should be the easy way. At Real Kinetic, our Platform Engineering as a Service empowers organizations to adopt Deployment-Driven Development by creating <a href="https://engineering.atspotify.com/2020/08/how-we-use-golden-paths-to-solve-fragmentation-in-our-software-ecosystem/"><em>golden paths</em></a> for streamlined development. A golden path is an opinionated and supported way of building something within your organization. What it allows us to do is shift more things <em>left</em> into the development process. Rather than relying on policy and security scanners like Checkov or Wiz to detect issues reactively, we make it possible to <em>only</em> ship software that conforms to your organization’s internal controls or standards. While security scanners still play a role, this model significantly reduces the undifferentiated work and removes the guesswork from figuring out your organization’s standards. It lets product teams focus on the stuff that actually matters. <a href="https://konfigurate.com?utm_source=bravenewgeek.com&amp;utm_campaign=ddd">Konfigurate</a>, our modern IaC solution, allows organizations to enforce their standards easily—without requiring a substantial platform engineering investment.</p>
<p>Konfigurate was designed and built around the notion of Deployment-Driven Development. The platform’s opinionated IaC approach represents a modern solution to deployment and infrastructure management. By shifting infrastructure, compliance, and security concerns left, Konfigurate ensures that applications are production-ready from day one, enabling faster deployments and reducing time spent on “overhead” work so you can focus more on your actual product. It minimizes this work that otherwise gets deferred or left to evolve organically until it becomes a much bigger problem. This shift-left approach to IaC not only accelerates time-to-production but also provides peace of mind, knowing that infrastructure is secure, compliant, and standardized by design.</p>
<p>Platform engineering offers a scalable approach to DevOps by enabling organizations to codify best practices while providing the tooling and services that empower product teams to work more efficiently. However, this approach requires a dedicated investment in a platform engineering team. For startups and scaleups, this can be particularly challenging as their focus is often on rapid product development rather than internal infrastructure. Even large enterprises, especially those outside the tech industry, face hurdles in adopting platform engineering. IT departments in these organizations are frequently seen as cost centers, making it difficult to justify strategic investments like building a dedicated platform engineering function.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Deployment-Driven Development represents a subtle, yet fundamental, shift in how teams approach software delivery, prioritizing deployment from day one rather than treating it as an afterthought. By starting with a real, production-like environment, teams can build, test, and iterate more effectively, reducing the friction often caused by traditional infrastructure and deployment practices. This shift-left approach ensures that security, compliance, and operational concerns are addressed early, leading to faster, more reliable releases.</p>
<p>At Real Kinetic, we’ve embraced this methodology to help our clients streamline their delivery processes. Tools like Konfigurate embody this philosophy by providing opinionated, ready-to-use infrastructure that automates best practices, eliminating much of the undifferentiated work that slows teams down. By adopting Deployment-Driven Development, organizations can not only accelerate their time-to-market but also reduce tech debt, improve security posture, and focus more on delivering value to their customers.</p>
<p>Ultimately, Deployment-Driven Development is about making deployment the easy and natural part of the development lifecycle, allowing teams to deliver high-quality software with greater agility and confidence. Whether you’re a startup looking to scale quickly or an enterprise aiming to optimize your delivery pipeline, embracing this approach can be a game changer for your organization.</p>
]]></content:encoded></item><item><title>Automating Infrastructure as Code with Vertex AI</title><link>https://bravenewgeek.com/automating-infrastructure-as-code-with-vertex-ai/</link><pubDate>Tue, 05 Nov 2024 15:23:09 -0700</pubDate><guid>https://bravenewgeek.com/automating-infrastructure-as-code-with-vertex-ai/</guid><description>&lt;p&gt;&lt;a href="https://bravenewgeek.com/wp-content/uploads/2024/11/konfigurate_ai.gif"&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2024/11/konfigurate_ai.gif"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A lot of companies are trying to figure out how AI can be used to improve their business. Most of them are struggling to not just implement AI, but to even find use cases that aren’t contrived and actually add value to their customers. We recently discovered a compelling use case for AI integration in our &lt;a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;amp;utm_campaign=vertex-ai"&gt;Konfigurate platform&lt;/a&gt;, and we found that implementing generative AI doesn’t require a great deal of complexity. I’m going to walk you through what we learned about integrating an AI assistant into our production system. There’s a ton of noise out there about what you “need” to integrate AI into your product. The good news? You don’t need much. The bad news? It took too much time sifting through nonsense to find what actually helps deliver value with AI.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="/wp-content/uploads/2024/11/konfigurate_ai.gif"><img loading="lazy" src="/wp-content/uploads/2024/11/konfigurate_ai.gif"></a></p>
<p>A lot of companies are trying to figure out how AI can be used to improve their business. Most of them are struggling to not just implement AI, but to even find use cases that aren’t contrived and actually add value to their customers. We recently discovered a compelling use case for AI integration in our <a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=vertex-ai">Konfigurate platform</a>, and we found that implementing generative AI doesn’t require a great deal of complexity. I’m going to walk you through what we learned about integrating an AI assistant into our production system. There’s a ton of noise out there about what you “need” to integrate AI into your product. The good news? You don’t need much. The bad news? It took too much time sifting through nonsense to find what actually helps deliver value with AI.</p>
<p>We’ll show you how to leverage Google’s <a href="https://cloud.google.com/vertex-ai?hl=en">Vertex AI</a> with Gemini 1.5 to implement multimodal input for automating the creation of infrastructure as code. We’ll see how to make our AI assistant context-aware, how to configure output to be well-structured, how to tune the output without needing actual model tuning, and how to test the model.</p>
<h2 id="our-use-case">Our Use Case</h2>
<h3 id="the-context">The Context</h3>
<p>Konfigurate takes a modern approach to infrastructure as code (IaC) that shifts more concerns left into the development process such as security, compliance, and architecture standardization. In addition to managing your cloud infrastructure, it also integrates with GitHub or GitLab to manage your organization’s repository structure and CI/CD.</p>
<p><a href="https://konfigurate.com/docs/workloads/">Workloads</a> are organized into Platforms and Domains, creating a structured environment that connects GitHub/GitLab with your cloud platform for seamless application and infrastructure management. Everything in Konfigurate—Platforms, Domains, Workloads, Resources—is GitOps-driven and implemented through YAML configuration. Below is an example showing the configuration for an “Ecommerce” Platform:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1alpha8
</span></span><span class="line"><span class="cl">kind: Platform
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: ecommerce
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/control-plane: konfig-control-plane
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  platformName: Ecommerce
</span></span><span class="line"><span class="cl">  gitlab:
</span></span><span class="line"><span class="cl">    parentGroupId: 88474985
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    billingAccountId: &#34;XXXXXX-XXXXXX-XXXXXX&#34;
</span></span><span class="line"><span class="cl">    parentFolderId: &#34;38822600023&#34;
</span></span><span class="line"><span class="cl">    defaultEnvs:
</span></span><span class="line"><span class="cl">      - label: dev
</span></span><span class="line"><span class="cl">    services:
</span></span><span class="line"><span class="cl">      defaults:
</span></span><span class="line"><span class="cl">        - cloud-run
</span></span><span class="line"><span class="cl">        - cloud-sql
</span></span><span class="line"><span class="cl">        - pubsub
</span></span><span class="line"><span class="cl">        - firestore
</span></span><span class="line"><span class="cl">        - redis
</span></span><span class="line"><span class="cl">  groups:
</span></span><span class="line"><span class="cl">    dev:
</span></span><span class="line"><span class="cl">      - ecomm-devs@realkinetic.com
</span></span><span class="line"><span class="cl">    maintainer:
</span></span><span class="line"><span class="cl">      - ecomm-maintainers@realkinetic.com
</span></span><span class="line"><span class="cl">    owner:
</span></span><span class="line"><span class="cl">      - sre@realkinetic.com
</span></span></code></pre></div><p><em>Example Konfigurate Platform YAML</em></p>
<h3 id="the-problem">The Problem</h3>
<p>The Konfigurate objects like Platforms, Domains, and Workloads are a well-structured problem. We have technical specifications for them defined in a way that’s easily interpretable by programs. In fact, as you can probably tell from the example above, they are simply Kubernetes CRDs, meaning they <em>are</em>—quite literally—well-defined APIs. And as you can tell from the example, these YAML configurations are fairly straightforward, but they can still be tedious to write by hand. Instead, usually what happens, which also happens with every other IaC tool, is definitions get copy/pasted and proliferated. We saw an opportunity for AI due to the structured nature of the system and definition of the problem space.</p>
<h3 id="the-solution">The Solution</h3>
<p>Our idea was to create an AI assistant that could generate Konfigurate IaC definitions based on flexible user input. Users could interact with the system in a couple different ways:</p>
<ol>
<li>
<p><strong>Text Description:</strong> users could describe their desired system architecture using natural language, e.g. “Add a new analytics domain to the ecommerce platform and within it I need a new ETL pipeline that will pull data from the orders database, process it in Cloud Run, and write the transformed data to BigQuery.”</p>
</li>
<li>
<p><strong>Architecture Diagram:</strong> users could provide an image of their architecture diagram.</p>
</li>
</ol>
<p>While we only introduced support for natural language and image-based inputs, we also validated that it worked with <em>audio</em>-based descriptions of the architecture as well with no additional effort. We tested this by recording ourselves describing the infrastructure and then providing an M4A file to the model. We decided not to include this mode of input since, while cool, it seemed not particularly practical.</p>
<h3 id="the-value">The Value</h3>
<p>This multimodal approach not only saves developers hours of time spent on boilerplate code but also accommodates different working styles and preferences. Whether a team uses visual tools for architecture design or prefers text-based planning, our system can adapt, getting them up and running with minimal mental effort. Developers would still be responsible for verifying system behavior and testing, but the initial setup time could be drastically reduced across various input methods.</p>
<p>Critically, we found this feature makes IaC more accessible and productive for a much broader set of roles and skill sets. For instance, we’ve worked with mainframe COBOL engineers, data analysts, and developers with no cloud experience who are now able to more effectively implement cloud infrastructure and systems. It doesn’t <em>hide</em> the IaC from them, it just gives them a reliable starting point to work from that is actually grounded to their environment and problem space. What we have found with our AI-assisted infrastructure and our more general approach to Visual IaC is that developers spend more time focusing on their actual product and less time on undifferentiated work.</p>
<h3 id="the-technology">The Technology</h3>
<p>Our team has a lot of GCP experience, so we decided to use the Vertex AI platform and the Gemini-1.5-Flash-002 model for this project. It was a no-brainer for us. We know the ins and outs of GCP, and Vertex AI offers an all-in-one managed solution that makes it easy to get going. This particular model is fast and most importantly it’s cost-effective. As I am sure this will ring true for many of you, we didn’t want to mess around with setting up our own infrastructure or dealing with the headaches of managing our own AI models. The Vertex AI Studio made it really easy to start developing and iterating prompts as well as trying different models.</p>
<p><a href="/wp-content/uploads/2024/11/vertex_ai_studio.png"><img loading="lazy" src="/wp-content/uploads/2024/11/vertex_ai_studio-1024x762.png"></a></p>
<p>Vertex AI Studio</p>
<h2 id="no-you-dont-need-rag-at-least-we-didnt">No, You Don’t Need RAG (At Least, We Didn’t)</h2>
<p>Great, you’ve got your fancy AI setup, but don’t you need some complex retrieval system to make it context-aware? Sure, RAG (Retrieval Augmented Generation) is often touted as essential for creating context-aware AI agents. Our experience took us down a different path.</p>
<p>When researching how to create a context-aware GPT agent, you’ll inevitably encounter <a href="https://cloud.google.com/use-cases/retrieval-augmented-generation?hl=en">RAG</a>. This typically involves:</p>
<ul>
<li><a href="https://medium.com/@mutahar789/optimizing-rag-a-guide-to-choosing-the-right-vector-database-480f71a33139">Vector databases</a> for efficient similarity search</li>
<li>Complex indexing and retrieval systems</li>
<li>Additional infrastructure for training and fine-tuning models</li>
</ul>
<h3 id="our-initial-approach">Our Initial Approach</h3>
<p>We started by preparing <a href="https://jsonlines.org/">JSONL</a>-formatted data thinking we’d feed it into a RAG system. The plan was to have our AI model learn from this structured data to understand our Konfigurate specifications like Platforms and Domains. As we experimented, we found that going the RAG route wasn’t giving us the consistent, high-quality outputs we needed, so we pivoted.</p>
<h3 id="the-big-prompt-solution">The Big Prompt Solution</h3>
<p>Instead of relying on RAG, we leaned heavily into prompt engineering. Here’s what we did:</p>
<ol>
<li>Long-Context Prompts: we crafted detailed prompts that provided the necessary context about our Konfigurate system, its components, and how they interact.</li>
<li>Example IaC: as part of the prompt, we included numerous example definitions for Konfigurate objects such as Platforms and Domains.</li>
<li>Example Prompts: we also included example prompts and their corresponding correct outputs, essentially “showing” the AI what we expected.</li>
<li>Error Handling Prompts: we even included prompting that guided the AI on how to handle errors or edge cases.</li>
</ol>
<h3 id="why-this-worked-better">Why This Worked Better</h3>
<ol>
<li>Consistency: by explicitly stating our requirements in the prompts, we got more consistent outputs.</li>
<li>Flexibility: it was easier to tweak and refine our prompts than to restructure a RAG system.</li>
<li>Control: we had more direct control over how the AI interpreted and used our domain-specific knowledge.</li>
<li>Simplicity: no need for additional infrastructure or complex retrieval systems—instead, it’s just a single API call.</li>
</ol>
<h3 id="the-takeaway">The Takeaway</h3>
<p>While RAG has its place, don’t assume it’s always necessary. For our use case, well-crafted prompts proved more effective than a sophisticated retrieval system. I believe this was a better fit because of the well-structured nature of our problem space. We can trivially validate the results output by the model because they are data structures with specifications. As a result, we got our context-aware AI assistant up and running faster, with better results, and without the overhead or complexity of RAG. Remember, in the world of technology, most times the simplest solution is the most elegant.</p>
<h2 id="prompt-engineering-the-secret-sauce">Prompt Engineering: The Secret Sauce</h2>
<p>While prompt engineering has become a bit of a meme, it turned out to be the most crucial part of this whole process. When you’re working with these AI models, everything boils down to how you craft your prompts. It’s where the magic happens—or doesn’t.</p>
<p>Let’s break down what this looks like in practice. We’re using the Vertex AI API with Node.js , so we started with their boilerplate code. The key player is the <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/reference/nodejs/latest#initialize-the-vertexai-class">getGenerativeModel()</a> function. Here’s a stripped-down version of what we’re feeding it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="k">const</span> <span class="n">generativeModel</span> <span class="o">=</span> <span class="n">vertexAi</span><span class="o">.</span><span class="n">preview</span><span class="o">.</span><span class="n">getGenerativeModel</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="n">model</span><span class="p">:</span> <span class="s2">&#34;gemini-1.5-flash-002&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">generationConfig</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">maxOutputTokens</span><span class="p">:</span> <span class="mi">4096</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">temperature</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">topP</span><span class="p">:</span> <span class="mf">0.95</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="n">safetySettings</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">category</span><span class="p">:</span> <span class="n">HarmCategory</span><span class="o">.</span><span class="n">HARM_CATEGORY_HATE_SPEECH</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">threshold</span><span class="p">:</span> <span class="n">HarmBlockThreshold</span><span class="o">.</span><span class="n">BLOCK_MEDIUM_AND_ABOVE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">category</span><span class="p">:</span> <span class="n">HarmCategory</span><span class="o">.</span><span class="n">HARM_CATEGORY_DANGEROUS_CONTENT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">threshold</span><span class="p">:</span> <span class="n">HarmBlockThreshold</span><span class="o">.</span><span class="n">BLOCK_MEDIUM_AND_ABOVE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">category</span><span class="p">:</span> <span class="n">HarmCategory</span><span class="o">.</span><span class="n">HARM_CATEGORY_SEXUALLY_EXPLICIT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">threshold</span><span class="p">:</span> <span class="n">HarmBlockThreshold</span><span class="o">.</span><span class="n">BLOCK_MEDIUM_AND_ABOVE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">category</span><span class="p">:</span> <span class="n">HarmCategory</span><span class="o">.</span><span class="n">HARM_CATEGORY_HARASSMENT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">threshold</span><span class="p">:</span> <span class="n">HarmBlockThreshold</span><span class="o">.</span><span class="n">BLOCK_MEDIUM_AND_ABOVE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="n">systemInstruction</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">role</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">parts</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="o">//</span> <span class="n">Removed</span> <span class="k">for</span> <span class="n">brevity</span> <span class="p">(</span><span class="n">detailed</span> <span class="n">below</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p><em>Gemini 1.5 model initialization</em></p>
<ul>
<li>Model: We’re using the latest version of Gemini 1.5 Flash, which is a lightweight and cost-effective model that excels at multimodal tasks and processing large amounts of text.</li>
<li>Generation Config: This is where we control things like the max output length as well as the “temperature” of the model. <em>Temperature</em> controls the randomness in token selection for the output. Gemini 1.5 Flash has a temperature range of 0 to 2 with 1 being the default. A lower temperature is good when you’re looking for a “true or correct” response, while a higher temperature can result in more diverse or unexpected results. This can be good for use cases that require a more “creative” model, but since our use case requires quite a bit of precision, we opted for a low temperature value.</li>
<li>Safety Settings: These are Google’s defaults. Refer to their <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-filters#configurable-filters">documentation</a> for customization.</li>
<li><a href="https://cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/system-instructions">System Instruction</a>: This is the real meat of prompt engineering. It’s where you prime the model, giving it context and setting its role. I’ve omitted this from the example above to go into more depth on this below since it’s a critical part of the solution.</li>
</ul>
<h3 id="the-art-and-science-of-prompting">The Art and Science of Prompting</h3>
<p>Here’s the thing: prompt engineering is a fine line between science and art. We spent a non-trivial amount of time crafting our prompts to get consistent, useful outputs. It’s not just about dumping information, it’s about structuring it in a way that guides the AI to give you what you actually need. Remember, these models will do exactly what you tell them to do, not necessarily what you <em>want</em> them to do. Sound familiar? It’s like debugging code, but instead of fixing logic errors, you’re fine-tuning language.</p>
<p>Fair warning, this is probably where you’ll spend most of your engineering time. It’s tempting to think the AI will just “get it,” but that’s not how this works. You need to be painfully clear and specific in your instructions. We went through many iterations, tweaking words here and there, restructuring our prompts, and sometimes completely overhauling our approach. But each iteration got us closer to that sweet spot where the model consistently churned out exactly what we needed. In the end, nailing your prompt engineering is what separates a frustrating, inconsistent AI experience from one that feels like you’ve just added a new team member to your crew.</p>
<p>The System Instructions mentioned above provide a way to inform the model how it should behave, provide it context, tell it how to structure output, and so forth. Though this information is separate from the actual user-provided prompt, they are still technically part of the overall prompt sent into the model. Effectively, System Instructions provide a way to factor out common prompt components from the user-provided prompt. I won’t show all of our System Instructions because there are quite a few, but I’ll show several examples below to give you an idea. Again, this is about being painstakingly explicit and clear about what you want the model to do.</p>
<ul>
<li>“Konfigurate is a system that manages cloud infrastructure in AWS or Google Cloud Platform. It uses Kubernetes YAML files in order to specify the configuration. Konfigurate makes it easy for developers to quickly and safely configure and deploy cloud resources within a company’s standards. You are a Platform Engineer who’s job is to help Application Software Engineers author their Konfigurate YAML specifications.”</li>
<li>“I am going to provide some example Konfigurate YAML files for your reference. Never output this example YAML directly. Rather, when providing examples in your output, generate new examples with different names and so forth.”</li>
<li>“Please provide the complete YAML output without any explanations or markdown formatting.”</li>
<li>“If the user asks about something other than Konfigurate or if you are unable to produce Konfigurate YAML for their prompt, tell them you cannot help with that (this is the one case to return something other than YAML). Specifically, respond with the following: ‘Sorry, I’m unable to help with that.’”</li>
</ul>
<h3 id="controlling-output-and-context-awareness">Controlling Output and Context-Awareness</h3>
<p>The example System Instructions above hint at this but it’s something worth going into more detail. First, our AI assistant has a very specific task: generate Konfigurate IaC YAML for users. For this reason, we never want it to output anything other than Konfigurate YAML to users, nor do we want it to respond to any prompts that are not directly related to Konfigurate. We handle this purely through prompting. To help the model understand Konfigurate IaC, we provide it with an extensive set of examples and tell it to only ever output complete YAML without any explanations or markdown formatting.</p>
<p>However, the output is actually more involved than this for our situation. That’s because we don’t just want to support generating new IaC, but also modify existing resources as well. This means the model doesn’t just need to be context-aware, it also needs to understand the distinction between “this is a new resource” and “this is an existing resource being modified.” This is important because Konfigurate is GitOps-driven, meaning the IaC resources are created in a branch and then a pull request is created for the changes. We need to know which resources are being created or modified, and if the latter, where those resources live.</p>
<p><a href="/wp-content/uploads/2024/11/konfigurate_ai_modify_resource.gif"><img loading="lazy" src="/wp-content/uploads/2024/11/konfigurate_ai_modify_resource.gif"></a></p>
<p>Modifying an existing resource</p>
<p>To make the model context-aware, we feed it the definitions for the existing resources in the user’s environment. This needs to happen at “prompt time”, so this information is not included as part of the System Instructions. Instead, we fetch this information on demand when a user prompt is submitted and augment their prompt with it. Additionally, we provide the UI context in which the user is submitting the prompt from. For example, if they submit a prompt to create a new Domain while within the Ecommerce Platform, we can infer that they wish to create a new Domain within this specific Platform. It may seem obvious to us, but the model is completely unaware of this and so we need to provide it with this context. Below is the full code showing how this works and how the prompt is constructed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="k">export</span> <span class="k">const</span> <span class="n">generateYaml</span> <span class="o">=</span> <span class="n">async</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">context</span><span class="p">:</span> <span class="n">AIContext</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">prompt</span><span class="p">:</span> <span class="n">string</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">fileData</span><span class="err">?</span><span class="p">:</span> <span class="n">FileData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="n">k8sApi</span> <span class="o">=</span> <span class="n">kc</span><span class="o">.</span><span class="n">makeApiClient</span><span class="p">(</span><span class="n">k8s</span><span class="o">.</span><span class="n">CustomObjectsApi</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="p">{</span> <span class="n">controlPlaneProjectId</span><span class="p">,</span> <span class="n">defaultBranch</span> <span class="p">}</span> <span class="o">=</span> <span class="n">await</span> <span class="n">getOrSetGitlabContext</span><span class="p">(</span><span class="n">k8sApi</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">//</span> <span class="n">Get</span> <span class="n">user</span><span class="s1">&#39;s environment information from the control plane</span>
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="p">[</span><span class="n">placeholders</span><span class="p">,</span> <span class="n">konfigObjects</span><span class="p">]</span> <span class="o">=</span> <span class="n">await</span> <span class="n">Promise</span><span class="o">.</span><span class="n">all</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="n">getPlaceHolders</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">getKonfigObjectsYAML</span><span class="p">(</span><span class="n">controlPlaneProjectId</span><span class="p">,</span> <span class="n">defaultBranch</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="n">parts</span><span class="p">:</span> <span class="n">Part</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">fileData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">parts</span><span class="o">.</span><span class="n">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="n">fileData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">prompt</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">parts</span><span class="o">.</span><span class="n">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="n">text</span><span class="p">:</span> <span class="n">prompt</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">//</span> <span class="n">Add</span> <span class="n">user</span><span class="s1">&#39;s environment context to the prompt</span>
</span></span><span class="line"><span class="cl">  <span class="n">parts</span><span class="o">.</span><span class="n">push</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Replace the placeholders with the following values if they should be present &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;in the output YAML unless the prompt is referring to actual YAMLs from the &#34;</span> <span class="o">+</span> 
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;user&#39;s environment, in which case use the YAML as is without replacing &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;values: &#34;</span> <span class="o">+</span> <span class="n">JSON</span><span class="o">.</span><span class="n">stringify</span><span class="p">(</span><span class="n">placeholders</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34;.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Following the &#34;---&#34; below are all existing Konfigurate YAMLs for the &#39;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;user</span><span class="se">\&#39;</span><span class="s2">s environment should they be needed either to reference or modify &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;and provide as output based on the prompt. Don&#39;t forget to never output &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;the example YAML exactly as is without modifications. Only output &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Konfigurate object YAML and no other YAML structures. Infer appropriate &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;emails for dev, maintainer, and owner groups based on those in the &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;provided YAML below if possible.</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;---</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="n">konfigObjects</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">---</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">//</span> <span class="n">Add</span> <span class="n">user</span><span class="s1">&#39;s UI context to the prompt</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">contextPrompt</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">contextSet</span> <span class="o">=</span> <span class="bp">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">platform</span> <span class="o">&amp;&amp;</span> <span class="n">context</span><span class="o">.</span><span class="n">domain</span> <span class="o">&amp;&amp;</span> <span class="n">context</span><span class="o">.</span><span class="n">workload</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextPrompt</span> <span class="o">=</span> <span class="err">`</span><span class="n">The</span> <span class="n">user</span> <span class="n">is</span> <span class="n">operating</span> <span class="n">within</span> <span class="n">the</span> <span class="n">context</span> <span class="n">of</span> <span class="n">the</span> <span class="o">$</span><span class="p">{</span><span class="n">context</span><span class="o">.</span><span class="n">workload</span><span class="p">}</span> <span class="n">Workload</span> <span class="n">which</span> <span class="n">is</span> <span class="ow">in</span> <span class="n">the</span> <span class="o">$</span><span class="p">{</span><span class="n">context</span><span class="o">.</span><span class="n">domain</span><span class="p">}</span> <span class="n">Domain</span> <span class="n">of</span> <span class="n">the</span> <span class="o">$</span><span class="p">{</span><span class="n">context</span><span class="o">.</span><span class="n">platform</span><span class="p">}</span> <span class="n">Platform</span><span class="o">.</span><span class="err">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextSet</span> <span class="o">=</span> <span class="bp">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">platform</span> <span class="o">&amp;&amp;</span> <span class="n">context</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextPrompt</span> <span class="o">=</span> <span class="err">`</span><span class="n">The</span> <span class="n">user</span> <span class="n">is</span> <span class="n">operating</span> <span class="n">within</span> <span class="n">the</span> <span class="n">context</span> <span class="n">of</span> <span class="n">the</span> <span class="o">$</span><span class="p">{</span><span class="n">context</span><span class="o">.</span><span class="n">domain</span><span class="p">}</span> <span class="n">Domain</span> <span class="n">of</span> <span class="n">the</span> <span class="o">$</span><span class="p">{</span><span class="n">context</span><span class="o">.</span><span class="n">platform</span><span class="p">}</span> <span class="n">Platform</span><span class="o">.</span><span class="err">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextSet</span> <span class="o">=</span> <span class="bp">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">platform</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextPrompt</span> <span class="o">=</span> <span class="err">`</span><span class="n">The</span> <span class="n">user</span> <span class="n">is</span> <span class="n">operating</span> <span class="n">within</span> <span class="n">the</span> <span class="n">context</span> <span class="n">of</span> <span class="n">the</span> <span class="o">$</span><span class="p">{</span><span class="n">context</span><span class="o">.</span><span class="n">platform</span><span class="p">}</span> <span class="n">Platform</span><span class="o">.</span><span class="err">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextSet</span> <span class="o">=</span> <span class="bp">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">contextSet</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">contextPrompt</span> <span class="o">+=</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34; Use this context to infer where output objects should go should &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;the user not provide explicit instructions in the prompt.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">parts</span><span class="o">.</span><span class="n">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="n">text</span><span class="p">:</span> <span class="n">contextPrompt</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="n">contents</span><span class="p">:</span> <span class="n">Content</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">role</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">parts</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="n">req</span><span class="p">:</span> <span class="n">GenerateContentRequest</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">contents</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">await</span> <span class="n">makeVertexRequest</span><span class="p">(</span><span class="n">req</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span> <span class="n">error</span><span class="p">:</span> <span class="n">resp</span> <span class="o">===</span> <span class="n">errorResponseMessage</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="n">resp</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>This prompt manipulation makes the model smart enough to understand the user’s environment and the context in which they are operating within. Feeding it all of this information is possible due to Gemini 1.5’s context window. The context window acts like a short-term memory, allowing the model to recall information as part of its output generation. While a person’s short-term memory is generally quite limited both in terms of the amount of information and recall accuracy, generative models like Gemini 1.5 can have <em>massive</em> context windows and near-perfect recall. Gemini 1.5 Flash in particular has a <em>1-million-token</em> context window, and Gemini 1.5 Pro has a 2-million-token context window. For reference, 1 million tokens is the equivalent of 50,000 lines of code (with standard 80 characters per line) or 8 average-length English novels. This is called “<a href="https://cloud.google.com/vertex-ai/generative-ai/docs/long-context">long context</a>”, and it allows us to provide the model with <em>massive</em> prompts while it is still able to find a “needle in a haystack.”</p>
<p>Long context has allowed us to make the model context-aware with minimal effort, but there’s still a question we have not yet addressed: how can the model also output metadata along with the generated IaC YAML? Specifically, we need to know the file path for each respective Konfigurate object so that we create new resources in the right place or we modify the correct existing resources. The answer, of course, is more prompt engineering. To solve this problem, we instructed the model to include metadata YAML with each Konfigurate object. This metadata contains the file path for the object and whether or not it’s an existing resource. Here’s an example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1alpha8
</span></span><span class="line"><span class="cl">kind: Domain
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: dashboard
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/platform: internal-services
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  domainName: Dashboards
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">filePath: konfig/internal-services/dashboard-domain.yaml
</span></span><span class="line"><span class="cl">isExisting: false
</span></span></code></pre></div><p>We did this by providing the model with several examples. Here is the System Instruction prompt we used:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  text:
</span></span><span class="line"><span class="cl">    &#34;For each Konfigurate YAML you output, include the following metadata, &#34; +
</span></span><span class="line"><span class="cl">    &#34;also in YAML format, following the Konfigurate object itself: &#34; +
</span></span><span class="line"><span class="cl">    &#34;filePath, isExisting. Here are some examples:\n&#34; + metadataExample,
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>It seems simple, but it was surprisingly effective and reliable.</p>
<h3 id="model-stability-and-testing">Model Stability and Testing</h3>
<p>Working with LLMs is a bit like describing a problem to someone else who writes the code to solve it—but without seeing the code, making it impossible to debug when issues arise. Worse yet, subtle changes in the description of the problem is akin to the other person starting over fully from scratch each time, so you might get consistent results <em>or</em> it could be completely different. There are also cases where no matter how explicit you are in your prompting, the model just <em>doesn’t do the right thing</em>. For example, with Gemini-1.5-Flash-001, I had problems preventing the AI from outputting the examples verbatim. I told it, in a variety of ways, to generate <em>new</em> examples using the provided ones as reference for the overall structure of resources, but it simply wouldn’t do it—<em>until I upgraded to Gemini-1.5-Flash-002</em>.</p>
<p>What we saw is that something as simple as just changing the model version could result in wildly different output. This is a nascent area but it’s a major challenge for companies attempting to leverage generative AI within their products or, <em>worse</em>, as a core component of their product. The only solution I can think of is to have a battery of test prompts you feed your AI and compare the results. But even this is problematic as the output content might be the same but the <em>structure</em> may have slight variations. In our case because we are generating YAML, it’s easy for us to validate output, but for use cases that are less structured, this seems like a major concern. Another solution is to feed results into a <em>different</em> model, but this feels equally precarious.</p>
<p>In addition to model stability, we had some challenges with “jailbreaking” the model. While we were never able to jailbreak the model to operate outside the context of Konfigurate, we were on occasion able to get it to provide Konfigurate output that was outside the bounds of our prompting. We did not invest a ton of time into this area as it felt like there wasn’t great ROI and it wasn’t really a concern within our product, but it’s certainly a concern when building with LLMs.</p>
<h3 id="patterns-that-worked-for-us-prompt-engineering-pro-tips">Patterns That Worked For Us: Prompt Engineering Pro Tips</h3>
<p>You have stuck with us this far and now it’s time for some concrete strategies that consistently improved our AI’s performance. Here’s what we learned:</p>
<ul>
<li><strong>Be Specific About Output</strong>: tell the model exactly what you want and how you want it. For us, that meant specifying YAML as the output format. Don’t leave room for interpretation—the clearer you are, the better the results.</li>
<li><strong>Show, Don’t Just Tell</strong>: give the model examples of what good output looks like. We explicitly prompted our model to reference our example resource specifications. It’s like training a new team member—<em>show</em> them what success looks like.</li>
<li><strong>Use Placeholders</strong>: providing examples to the model worked great, except when it would use specific field values from the examples in the user’s output. To address this we used sentinel placeholder values in the examples and then had a step that told the model to replace the placeholders with values from the user’s environment at prompt time.</li>
<li><strong>Error Handling is Key:</strong> just like you’d build error handling into your code, build it into your prompts. Give the model clear instructions on how to respond when it encounters ambiguous or out-of-scope requests. This keeps the user experience smooth, even when things go sideways.</li>
<li><strong>The Anti-Hallucination Trick:</strong> it sounds silly but it helps to explicitly tell the model <em>not</em> to hallucinate and to only respond within the context you’ve provided. It’s not foolproof, but we’ve seen a significant reduction in made-up information, especially when you’ve fine-tuned the temperature.</li>
</ul>
<p>Remember, prompt engineering is an iterative process. What works for one use case might not work for another. Keep experimenting, keep refining, and don’t be afraid to start from scratch if something’s not clicking. The goal is to find that sweet spot where your AI becomes a reliable, consistent part of your workflow.</p>
<h2 id="wrapping-it-up">Wrapping It Up</h2>
<p>There you have it, our journey into integrating AI into the Konfigurate platform. We started thinking we needed all sorts of fancy tech only to find that sometimes, simpler is better. The big takeaways?</p>
<ul>
<li>You don’t always need complex systems like RAG. A well-crafted prompt can often do the job just as well, if not better. Gemini 1.5’s long context and near-perfect recall makes it quite adept at the “needle-in-a-haystack” problem, and it enables pretty sophisticated use cases through complex prompting.</li>
<li>Prompt engineering isn’t just a buzzword or meme. It’s where the real work happens, and it’s worth investing your time to get it right.</li>
<li>LLMs are well-suited to structured problems because they are good at pattern matching. They’re also good at creative problems, but it’s less clear to us how to integrate something like this into a product versus a structured problem.</li>
<li>The AI landscape is constantly evolving. What works today might not be the best approach tomorrow. Stay flexible and keep experimenting.</li>
</ul>
<p>We hope sharing our experience saves you some time and headaches. Remember, there’s no one-size-fits-all solution in AI integration. What worked for us might need tweaking for your specific use case. The key is to start simple, iterate often, and don’t be afraid to challenge conventional wisdom. You might just find that the “must-have” tools aren’t so must-have after all.</p>
<p>Now, go forth and build something cool!</p>
]]></content:encoded></item><item><title>Understanding Konfig’s Opinionation</title><link>https://bravenewgeek.com/understanding-konfigs-opinionation/</link><pubDate>Tue, 11 Jun 2024 13:59:57 -0600</pubDate><guid>https://bravenewgeek.com/understanding-konfigs-opinionation/</guid><description>&lt;p&gt;In my &lt;a href="https://bravenewgeek.com/no-assembly-required-the-benefits-of-an-opinionated-platform/"&gt;last post&lt;/a&gt;, I talked about the benefits of an opinionated platform. An opinionated platform allows your engineers to focus on things that matter to your business, such as shipping and improving customer-facing products and services. This is in contrast to engineers spending substantial time on non-differentiating work like platform infrastructure. Rather than infrastructure architecture, developers can focus more on the product architecture. &lt;a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;amp;utm_campaign=understanding-opinionation"&gt;Konfig&lt;/a&gt; is an opinionated platform which provides two key value drivers: 1) reducing the investment and total cost of ownership needed to have an enterprise cloud platform and 2) minimizing the time to deliver new software products.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In my <a href="https://bravenewgeek.com/no-assembly-required-the-benefits-of-an-opinionated-platform/">last post</a>, I talked about the benefits of an opinionated platform. An opinionated platform allows your engineers to focus on things that matter to your business, such as shipping and improving customer-facing products and services. This is in contrast to engineers spending substantial time on non-differentiating work like platform infrastructure. Rather than infrastructure architecture, developers can focus more on the product architecture. <a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=understanding-opinionation">Konfig</a> is an opinionated platform which provides two key value drivers: 1) reducing the investment and total cost of ownership needed to have an enterprise cloud platform and 2) minimizing the time to deliver new software products.</p>
<p>Konfig provides an out-of-the-box, enterprise-grade platform which is built with security and governance at its heart. Building this type of platform normally requires a sizable team of platform engineers which takes constant care, maintenance, and ongoing investment. With Konfig, we can now reallocate these resources to higher-value work, and we only need a small team to manage resource templates used by developers and implement business-specific components. It enables this small team to provide a robust platform within their company with organizational standards and opinions built in. This, in turn, allows an organization’s developers to self-service with a high degree of autonomy while ensuring they work within the bounds of our organization’s standards.</p>
<p>For many organizations, bringing a new software product to market can be a monumental undertaking. Even when the code is written, it can take some companies six months to a year just to get the system to production. Konfig reduces this sunk cost by accelerating the time-to-production. This is possible because it provides an opinionated platform that solves many of the common problems involved with building software in a way that codifies industry best practices. Konfig’s approach encourages deploying to  production-like environments from day-1, something we call Deployment-Driven Development.</p>
<p>So what are Konfig’s opinions? What is the motivation behind each of them? And does an opinionated platform mean an organization is constrained or locked in as is often the case with a PaaS? Let’s explore each of these questions.</p>
<h2 id="opinions-and-their-benefits">Opinions and their benefits</h2>
<h3 id="gitlab-and-gcp">GitLab and GCP</h3>
<p>Perhaps the most obvious of Konfig’s opinions is that it is centered around Google Cloud Platform and GitLab. While we are actively exploring support for GitHub and AWS, we chose to start with building a white-glove experience around GCP and GitLab for a few reasons.</p>
<p>First, GCP has best-in-class serverless offerings and managed services which lend themselves well to Konfig’s model. This is something we’ve <a href="https://blog.realkinetic.com/gcp-and-aws-whats-the-difference-3b1329f0ffb3">written about</a> extensively before. Services like Cloud Run, Google Kubernetes Engine (GKE), BigQuery, Firestore, and Dataflow are truly differentiators for Google Cloud.</p>
<p>Second, GCP’s Config Connector operator provides, <a href="https://blog.realkinetic.com/its-time-to-retire-terraform-30545fd5f186">we argue</a>, a better alternative to Terraform for managing infrastructure. We’ll discuss this in more detail later.</p>
<p>Third, we believe GitLab’s CI/CD system is better designed than GitHub Actions. This is something worthy of its own blog post, but it’s a key factor in providing a platform that is both secure and has a great developer experience.</p>
<p>Lastly, GitLab uses a hierarchical structure with groups, subgroups, and projects which maps perfectly to Konfig’s own control plane, platform, domain hierarchy as well as GCP’s resource hierarchy with organizations, folders, and projects. This is a critical component in how Konfig manages governance.</p>
<p>The Konfig model works with any combination of cloud platform and DevOps tooling. We just chose to start with GCP and GitLab because they work so well together. With Konfig, they almost feel as if they are natively integrated.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/06/image4.png"></p>
<h3 id="service-oriented-architecture-and-domain-driven-design">Service-oriented architecture and domain-driven design</h3>
<p>Konfig has a notion of platforms and domains. A platform is intended to map to a coarse-grained organizational boundary such as a product line or business unit. Platforms are further subdivided into domains, which are groupings of related services. This is loosely borrowed from the concept of domain-driven design. While Konfig does not take a particularly strong stance on DDD or how you structure workloads, it does encourage the use of APIs to connect services versus sharing databases between them.</p>
<p>This is a best practice that Konfig embraces because it reduces coupling which makes it easier to evolve services independently. A key aspect of this grouping will make certain tasks harder (though not necessarily impossible), on purpose, such as sharing a database between domains. It also promotes more durable teams who own different parts of a system, which is an organizational best practice we routinely encourage with our clients.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/06/image5.png"></p>
<h3 id="structuring-gitlab-and-gcp">Structuring GitLab and GCP</h3>
<p>Konfig maintains a consistent structure between GitLab and GCP based around the control plane, platform, domain hierarchy. Control planes, platforms, and domains are all declaratively defined in YAML. This hierarchy is central to Konfig because it allows it to enforce best practices for access management and cloud governance. It also provides powerful cost visibility because we can easily see the cloud spend and forecasted spend for platforms and domains. This means there is a rigid opinionation to the tiered structuring of folders and projects in GCP and subgroups and projects in GitLab.</p>
<p>In GCP, a control plane maps to a folder within your GCP organization (either specified by the user during setup or created by the Konfig CLI). Within this folder, there is a control plane project which houses the control plane Kubernetes cluster and a folder for each platform. Within each platform folder, there are folders for each domain which contain a project for each environment (e.g. dev, stage, and prod).</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/06/image1.png"></p>
<p><em>Konfig hierarchy in GCP for a retail business</em></p>
<p>In GitLab, a control plane maps to a subgroup within your organization’s top-level group (again, either specified by the user during setup or created by the CLI). Within this subgroup, there is a control plane project which houses the definitions for the control plane itself as well as the platforms and domains it manages. In addition to the control plane project, the control plane subgroup contains a child subgroup for each platform. Like the GCP structure, these platform subgroups in turn contain subgroups for each of the platform’s domains. It’s in these domain subgroups that our actual workload projects go. Konfig provides a GitLab template for creating new workload projects that includes a fully functional CI/CD pipeline and workload definition for configuring service settings and infrastructure resources.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/06/image2.png"></p>
<p><em>Corresponding Konfig hierarchy in GitLab</em></p>
<p>This structure is critical because it allows Konfig to manage access and permissioning for both users as well as service accounts. This enables the platform to enforce strong isolation and security boundaries. The hierarchy also allows us to cascade permissions and governance cleanly.</p>
<h3 id="group-based-access-management">Group-based access management</h3>
<p>Konfig leverages groups to manage permissioning. Groups are managed using a customer’s identity provider, such as Google Cloud Identity or Microsoft Entra ID (formerly Azure AD). These groups are then synced into GitLab using SAML group links and into GCP with Cloud Identity (if using an external IdP). In the control plane, platform, and domain YAML definitions, we can specify what GitLab and GCP permissions a group should have from a single configuration source.</p>
<p>This opinionated model provides a single source of truth for both identity (by relying on a customer’s existing IdP) and access management (via Konfig’s YAML definitions). We’ve often seen organizations assign roles to individual users which is an anti-pattern, so Konfig relies on groups as a best practice. This model also lets us apply SDLC practices to access management.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">apiVersion</span><span class="p">:</span> <span class="n">konfig</span><span class="o">.</span><span class="n">realkinetic</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">v1beta1</span>
</span></span><span class="line"><span class="cl"><span class="n">kind</span><span class="p">:</span> <span class="n">Domain</span>
</span></span><span class="line"><span class="cl"><span class="n">metadata</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">name</span><span class="p">:</span> <span class="n">order</span><span class="o">-</span><span class="n">management</span>
</span></span><span class="line"><span class="cl">  <span class="n">namespace</span><span class="p">:</span> <span class="n">konfig</span><span class="o">-</span><span class="n">control</span><span class="o">-</span><span class="n">plane</span>
</span></span><span class="line"><span class="cl">  <span class="n">labels</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">konfig</span><span class="o">.</span><span class="n">realkinetic</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">platform</span><span class="p">:</span> <span class="n">ecommerce</span>
</span></span><span class="line"><span class="cl"><span class="n">spec</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">domainName</span><span class="p">:</span> <span class="n">Order</span> <span class="n">Management</span>
</span></span><span class="line"><span class="cl">  <span class="n">gcp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">createProjects</span><span class="p">:</span> <span class="bp">true</span>
</span></span><span class="line"><span class="cl">    <span class="n">enableWorkloadIdentity</span><span class="p">:</span> <span class="bp">true</span>
</span></span><span class="line"><span class="cl">    <span class="n">envs</span><span class="p">:</span> <span class="p">[</span><span class="n">dev</span><span class="p">,</span> <span class="n">stage</span><span class="p">,</span> <span class="n">prod</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">manageFolder</span><span class="p">:</span> <span class="bp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">gitlab</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">manageCIVars</span><span class="p">:</span> <span class="bp">true</span>
</span></span><span class="line"><span class="cl">    <span class="n">manageGroup</span><span class="p">:</span> <span class="bp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">groups</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">dev</span><span class="p">:</span> <span class="p">[</span><span class="n">order</span><span class="o">-</span><span class="n">management</span><span class="o">-</span><span class="n">devs</span><span class="err">@</span><span class="n">widgets</span><span class="o">-</span><span class="mi">4</span><span class="o">-</span><span class="n">all</span><span class="o">.</span><span class="n">biz</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">maintainer</span><span class="p">:</span> <span class="p">[</span><span class="n">order</span><span class="o">-</span><span class="n">management</span><span class="o">-</span><span class="n">maintainers</span><span class="err">@</span><span class="n">widgets</span><span class="o">-</span><span class="mi">4</span><span class="o">-</span><span class="n">all</span><span class="o">.</span><span class="n">biz</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">owner</span><span class="p">:</span> <span class="p">[</span><span class="n">sre</span><span class="o">-</span><span class="n">admins</span><span class="err">@</span><span class="n">widgets</span><span class="o">-</span><span class="mi">4</span><span class="o">-</span><span class="n">all</span><span class="o">.</span><span class="n">biz</span><span class="p">]</span>
</span></span></code></pre></div><p><em>Example domain.yaml showing group permissions</em></p>
<h3 id="workload-identity-and-least-privilege-access">Workload identity and least-privilege access</h3>
<p>One of the benefits of Konfig is that it automatically manages IAM for workloads. This means you just specify what resources your application needs, such as databases, storage buckets, or caches, and it not only provisions those resources but also configures the application’s service account to have the minimal set of permissions needed to access them for the role specified.</p>
<p>In Konfig, every workload gets a dedicated service account. This is a best practice that ensures we don’t have overly broad access defined for services. Often what happens otherwise is service accounts get reused across applications, resulting in workload identities that accrue more and more roles. Another common anti-pattern is using the Compute Engine default service account which many GCP services use if a service account is not specified. This service account usually has the Editor role, which is a privileged role that grants broad access. Konfig disables this default service account altogether, preventing this from happening.</p>
<h3 id="decoupling-iam-for-developers-cicd-control-planes-and-workloads">Decoupling IAM for developers, CI/CD, control planes, and workloads</h3>
<p>There are four main groups of identities in Konfig: users, CI/CD pipelines, control planes, and workloads. Konfig takes the position that human users should generally not require elevated permissions. Similarly, all modifications to environments should occur via CI/CD pipelines rather than manually or through “ClickOps.” For this reason, Konfig enforces strong separation of developer user accounts, CI/CD service accounts, control plane service accounts, and workload service accounts.</p>
<p>Earlier we saw how Konfig relies on group-based access management rather than assigning roles to individual users. This provides a more uniform approach to access management. These roles provide a limited set of permissions. Instead, developers interact with Konfig through GitLab pipelines. Note, however, that the “owner” group permission, which is illustrated in the example domain.yaml above, provides break-glass access that can be set at the domain, platform, and control plane level to support situations that require emergency remediation.</p>
<p>Konfig maps GCP service accounts to CI/CD pipelines in GitLab using <a href="https://cloud.google.com/iam/docs/workload-identity-federation">Workload Identity Federation</a>. The service accounts used by the CI/CD system have limited permissions that are scoped by GCP IAM and Kubernetes RBAC. This means that a pipeline for a specific workload in a domain can only apply modifications to its own control plane namespace. This greatly reduces the blast radius of a compromised GitLab credential and also prevents teams from modifying environments that they don’t own.</p>
<p>Additionally, because Konfig relies on Workload Identity Federation, there are no long-lived credentials to begin with. Workload Identity Federation uses OpenID Connect to allow a GitLab pipeline to authenticate with GCP and use a short-lived token to act as a GCP service account. This is in contrast to using service account keys for authenticating between GitLab and GCP, which is a security anti-pattern because it involves long-lived credentials that often do not get rotated. These keys are a common source of security breaches. And because the Konfig control plane is responsible for orchestrating resources, this CI/CD service account needs a very minimal set of permissions. Basically, it just needs permissions to apply Konfig definitions to its control plane namespace. The control plane handles the actual heavy lifting from that point on.</p>
<p>Each domain-environment pair gets its own namespace in the Konfig control plane. This namespace has its own service account that is scoped only to the GCP project associated with this domain environment. This allows the control plane to provision workloads and resources within a domain while having strong isolation between different domains and different environments.</p>
<p>We already discussed how Konfig manages IAM for workloads and implements least-privilege access. It’s important to note that, like the CI/CD service accounts, these workload service accounts have no keys associated with them and thus are never exposed to humans or to the CI/CD system. This means they are fully decoupled and easier to audit and monitor.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/06/image6.png"></p>
<h3 id="gitops-branching-and-release-strategies">GitOps, branching, and release strategies</h3>
<p>Konfig uses a GitOps model for managing platforms, domains, workloads, and infrastructure resource templates. These constructs are defined in YAML and deployed or promoted using Git-based workflows like merging a branch into main or tagging a release. This model is a best practice that provides a declarative single source of truth for our infrastructure (which is normally referred to as Infrastructure as Code or IaC), our GitLab and GCP implementations, and our organizational standards (by way of resource templates). For instance, we saw earlier how these declarative configurations are used to manage permissions in GitLab and GCP. This allows us to apply the same SDLC we use for application source code to our enterprise platform. This more comprehensive approach to managing infrastructure, source control, CI/CD, and cloud environment is something we call Platform as Code.</p>
<p>Konfig promotes a trunk-based development workflow with merge requests and code reviews. Releases are done by creating a tag. This GitOps model provides a clear audit trail and approval flow that most developers are already familiar with. This not only lends itself to providing a better developer experience but also a strong governance story. Infrastructure configuration is treated as data stored in source control. This makes it easy to backup and restore, but we’ve also chosen a format that is widely supported and makes writing custom tooling or integrating with existing tools easy.</p>
<h3 id="image-promotion-and-single-container-artifact">Image promotion and single container artifact</h3>
<p>Workload repositories can only contain a single deployable artifact. This means monorepos are not supported in Konfig, and repositories may only have a single Dockerfile that gets built and deployed. It also means workloads need to be containerized. This allows Konfig to make certain assumptions about CI/CD and SDLC that further improve the developer experience, security, and governance.</p>
<p>A problem we see regularly at companies is images getting rebuilt for different environments. Konfig’s image promotion model ensures the images used for testing are what is deployed to production without rebuilding containers or copying artifacts from development environments. The use of releases and environments in GitLab ensures there is a clear auditing and tracking of artifacts so that you know exactly what is deployed, by whom, and when.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/06/image3-1024x604.png"></p>
<p><em>GitLab’s environment view shows the history of all deployments to an environment</em></p>
<h3 id="managed-services-and-serverless-over-other-options-when-possible">Managed services and serverless over other options when possible</h3>
<p>We’ve spoken at length about the <a href="https://blog.realkinetic.com/cloud-without-kubernetes-d0487a4ab345">benefits of serverless</a> and how, for many businesses, it may be a better fit than Kubernetes. Previously, I mentioned that one of the reasons we chose GCP initially to build an enterprise-ready platform was because of its emphasis on serverless and managed services. In particular, services like Cloud Run, GKE, and Dataflow are truly industry-leading container platforms. For this reason, Konfig supports these runtimes natively. We recommend Cloud Run as the default workload runtime but provide GKE as a supported engine for cases where Cloud Run is just not a good fit. Dataflow provides a fully managed execution environment for unified batch and stream data processing.</p>
<p>Leveraging managed services and serverless greatly reduces operational burden, improves security posture, allows developers to focus on product and feature development, and reduces production lead times. By supporting a smaller set of services, we can provide a great developer experience and security posture by automatically configuring service account permissions, autowiring environment variables in application containers, managing secrets, and a number of other benefits. Reducing options also simplifies architecture, improves maintainability and supportability, and reduces infrastructure sprawl. We’ve said before, we believe organizations should invest their engineers’ creativity and time into differentiating their customer-facing products and services, not infrastructure and other non-differentiating work.</p>
<p>This is similar in nature to PaaS, but where Konfig differs is it provides an “escape hatch.” That is to say, it provides well-supported paths for both working around the platform’s opinions and constraints and for moving <em>off</em> of the platform if needed. I’ll touch on these a bit later.</p>
<h3 id="resource-templates-over-bespoke-configurations">Resource templates over bespoke configurations</h3>
<p>Infrastructure as code is often quite complicated because <em>infrastructure</em> is complicated. If you’ve ever worked with a large Terraform configuration you’ve probably experienced the challenges and pain points. It can be tedious to maintain and every implementation of it is different from company to company or even team to team. Konfig takes a different approach that provides an improved developer experience and stronger governance model. Workload definitions specify important metadata about a service such as the runtime engine, CPU and memory settings, infrastructure resources, and dependencies on other services. These definitions provide a declarative and holistic view of a workload that sits alongside the source code and follows the same SDLC.</p>
<p>Below is a simple workload.yaml for a service with three infrastructure resources, a Cloud Storage bucket, a Cloud SQL database, and a Pub/Sub topic. If you recall, the Konfig control plane will handle provisioning these resources and configuring the service account with properly scoped roles. It will also inject environment variables into the container so that the service can “discover” these resources at runtime.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">apiVersion</span><span class="p">:</span> <span class="n">konfig</span><span class="o">.</span><span class="n">realkinetic</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">v1beta1</span>
</span></span><span class="line"><span class="cl"><span class="n">kind</span><span class="p">:</span> <span class="n">Workload</span>
</span></span><span class="line"><span class="cl"><span class="n">metadata</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">name</span><span class="p">:</span> <span class="n">payment</span><span class="o">-</span><span class="n">api</span>
</span></span><span class="line"><span class="cl"><span class="n">spec</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">region</span><span class="p">:</span> <span class="n">us</span><span class="o">-</span><span class="n">central1</span>
</span></span><span class="line"><span class="cl">  <span class="n">runtime</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">kind</span><span class="p">:</span> <span class="n">RunService</span>
</span></span><span class="line"><span class="cl">    <span class="n">parameters</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">template</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">containers</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="o">-</span> <span class="n">image</span><span class="p">:</span> <span class="n">payment</span><span class="o">-</span><span class="n">api</span>
</span></span><span class="line"><span class="cl">    <span class="n">resources</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="o">-</span> <span class="n">kind</span><span class="p">:</span> <span class="n">StorageBucket</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span><span class="p">:</span> <span class="n">receipts</span>
</span></span><span class="line"><span class="cl">      <span class="o">-</span> <span class="n">kind</span><span class="p">:</span> <span class="n">SQLInstance</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span><span class="p">:</span> <span class="n">payment</span><span class="o">-</span><span class="n">db</span>
</span></span><span class="line"><span class="cl">      <span class="o">-</span> <span class="n">kind</span><span class="p">:</span> <span class="n">PubSubTopic</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span><span class="p">:</span> <span class="n">payment</span><span class="o">-</span><span class="n">authorized</span><span class="o">-</span><span class="n">events</span>
</span></span></code></pre></div><p><em>Example workload.yaml showing resource dependencies</em></p>
<p>As you can imagine, there’s a lot more to configuring these resources than simply specifying their name. This is where Konfig’s notion of resource templates comes into play. Konfig relies on resource templates to abstract the complexity of configuring cloud resources and provide a means to implement and enforce organizational standards. For instance, we might enforce a specific version of PostgreSQL, high availability mode, and customer-managed encryption keys. For non-production environments, we may use a non-HA configuration to reduce costs.</p>
<p>This model allows a platform engineering or SRE team to centrally manage default or required configurations for resources. Now organizations can enforce a “<a href="https://engineering.atspotify.com/2020/08/how-we-use-golden-paths-to-solve-fragmentation-in-our-software-ecosystem/">golden path</a>” or a standardized way of building something within their organization. Rather than relying on external policy scanners like Checkov that work reactively, we can build our policies directly into the platform and hide most of the complexity from developers, allowing them to focus on what matters: product and feature work. An organization can choose the right balance between autonomy and standardization for their unique situation, and we can eliminate infrastructure and architecture fragmentation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: sql.cnrm.cloud.google.com/v1beta1
</span></span><span class="line"><span class="cl">kind: SQLInstance
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: high-availability
</span></span><span class="line"><span class="cl">  namespace: konfig-templates
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    annotations:
</span></span><span class="line"><span class="cl">      konfig.realkinetic.com/extra-fields: &#34;settings.tier,settings.diskSize&#34;
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  databaseVersion: &#34;POSTGRES_15&#34;
</span></span><span class="line"><span class="cl">  settings:
</span></span><span class="line"><span class="cl">    tier: &#34;db-custom-1-3840&#34;
</span></span><span class="line"><span class="cl">    diskSize: 25
</span></span><span class="line"><span class="cl">    availabilityType: REGIONAL
</span></span><span class="line"><span class="cl">    diskType: PD_SSD
</span></span><span class="line"><span class="cl">    backupConfiguration:
</span></span><span class="line"><span class="cl">      binaryLogEnabled: true
</span></span><span class="line"><span class="cl">      enabled: true
</span></span></code></pre></div><p><em>Example resource template for Cloud SQL</em></p>
<h3 id="api-ingress-and-path-based-routing">API ingress and path-based routing</h3>
<p>Standardizing API ingress and routing is another key part of reducing architecture fragmentation and improving developer productivity. Konfig takes an opinionated stance on how workloads interact with each other and how external traffic interacts with a workload. By default, workloads are only accessible to other workloads in the same domain. However, we can also expose workloads to other workloads in the same platform or even across platforms if they are within the same control plane. Lastly, we can expose a workload to external traffic from the internet or from other control planes.</p>
<p>Konfig manages load balancers to make this ingress seamless and straightforward. It also utilizes path-based routing that maps to the platform, domain, workload hierarchy to provide a clean way of exposing APIs. Path-based routing is a best practice we promote because, compared to host-based routing, there’s less infrastructure to maintain, it removes cross-origin resource sharing (CORS) as a concern, and there are significantly fewer DNS records involved. A common challenge for SaaS companies is getting customers to whitelist hostnames. This is often a major headache for enterprise customers where whitelisting hostnames can be difficult. Path-based routing eliminates this problem by exposing services under the same domain.</p>
<h3 id="config-connector-for-iac-rather-than-terraform">Config Connector for IaC rather than Terraform</h3>
<p>As mentioned earlier, one of the reasons we chose GCP as the initial cloud platform supported in Konfig is <a href="https://cloud.google.com/config-connector/docs/overview">Config Connector</a>. Config Connector is a Kubernetes operator that lets you manage GCP resources the same way you manage Kubernetes applications, and it’s a model that many other cloud platforms and infrastructure providers are adopting as well. Config Connector offers a compelling alternative to Terraform for managing IaC with a number of advantages.</p>
<p>First, because Config Connector is specific to GCP, it offers a more native integration with the platform. This includes more fine-grained status reporting that tells us the state of individual resources. It also lets us benefit from Kubernetes events for improved visibility. This allows us to provide greater visibility into what is happening with your infrastructure which results in a better overall developer experience.</p>
<p>Second, it enables us to use a combination of Kubernetes RBAC and GCP IAM for managing access control. With this model, we can have strong security boundaries and reduced blast radius as it relates to infrastructure.</p>
<p>Third, Config Connector provides an improved model for state management and reconciliation. With Terraform, managing state drift and environment promotions can be challenging. Additionally, the Terraform state file often contains secrets stored in clear text which is a security risk and requires the state file itself to be treated as highly sensitive data. Yet, Terraform does not support client-side state encryption but rather relies on at-rest encryption of the state backends (this is <a href="https://terrateam.io/blog/opentofu-feature-preview-state-encryption/">one of the areas Terraform and OpenTofu have diverged</a>). It’s also common to hit race conditions in Terraform, where certain resources need to spin up before others can successfully apply. Sometimes this doesn’t happen and the deploy fails, requiring the apply to be re-run.</p>
<p>Config Connector takes a very different approach which solves these issues. Instead, it relies on a control loop which periodically reconciles resources to automatically correct drift. Think of it almost like “terraform apply” running regularly to ensure your desired state and actual state are in constant lockstep. The other benefit is it decouples dependent resources so we avoid race conditions and sequencing problems. This is possible because Config Connector models infrastructure as an eventually consistent state. This means resources can provision independently, even if their dependencies are not ready—no more re-running jobs to get a failed apply to work. Lastly, it can rely on Kubernetes secrets or GCP Secret Manager secrets to store sensitive information such as passwords or credentials. Sensitive data is properly isolated from our infrastructure configuration.</p>
<p>Finally, it’s possible to both bulk import existing resources <em>into</em> Config Connector and export resources to Terraform. This makes it possible to migrate existing infrastructure into Config Connector or migrate from Config Connector to Terraform. Config Connector can also reference resources that it does not manage using external references. This provides a means to gradually migrate to or from Config Connector.</p>
<h2 id="dealing-with-constraints-and-vendor-lock-in">Dealing with constraints and vendor lock-in</h2>
<p>Konfig is different from typical PaaS offerings in that it really is an opinionated bundling of components rather than a proprietary, monolithic platform or “walled garden.” Normally, with PaaS systems like Google App Engine or Heroku, you relied on proprietary APIs to build applications which made it very difficult to migrate off when the time came. It also meant when you hit the limits of the platform, that’s it. There’s nothing you can do to work around them. At the other end of the spectrum are products like Crossplane, which are geared towards helping you build your own internal cloud platform. This puts you squarely back into the realm of staffing a team of highly paid platform engineers to build and maintain such a platform. For some companies, this might be a strategic place to invest. For most, it’s not.</p>
<p>And while there are proprietary value-add components to Konfig we have built, the core of it is products you’re already using—namely GitLab and GCP—and the open source Config Connector. The value of Konfig is that it is an opinionated implementation of these pieces that reduces an organization’s total cost of ownership for an enterprise cloud platform and shortens the delivery time for new software products. What this means, though, is that there really isn’t any vendor lock-in beyond whatever lock-in GitLab and GCP already have. Because it’s built around Config Connector, you can just as well use Config Connector to manage your resources directly, you’ll just lose the benefits of Konfig like GitLab and GCP integration and governance, automatic resource provisioning and IAM, ingress management, and the Konfig UI.</p>
<p>Config Connector provides us a powerful escape hatch, either in the case of needing to remove Konfig or needing to step outside Konfig’s opinions. If we want to remove Konfig, we have a couple options. We can export the Config Connector resource definitions that Konfig manages and import them into a new Config Connector instance. This Config Connector instance can be run either as a <a href="https://cloud.google.com/config-connector/docs/how-to/install-upgrade-uninstall">GKE add-on</a> which is fully managed by Google, or it can be installed into a GKE cluster <a href="https://cloud.google.com/config-connector/docs/how-to/install-manually">manually</a>. Alternatively, we can export the resources managed by Konfig <a href="https://cloud.google.com/docs/terraform/resource-management/export">to Terraform</a>.</p>
<p>If we need to step outside Konfig’s opinions, for example to provision a resource not currently supported by Konfig such as a VM, we can use Config Connector directly which <a href="https://cloud.google.com/config-connector/docs/reference/overview">supports a broad set of resources</a>. We can manage these resources using the same GitOps model we use for Konfig workloads. With this, we have an opinionated platform that can support an organization’s most common needs but, unlike a PaaS, can grow and evolve with your organization.</p>
<hr>
<p>Konfig reduces your cloud platform engineering costs and the time to deliver new software products. <a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=understanding-opinionation">Reach out</a> to learn more about Konfig or schedule a demo.</p>
]]></content:encoded></item><item><title>No assembly required: the benefits of an opinionated platform</title><link>https://bravenewgeek.com/no-assembly-required-the-benefits-of-an-opinionated-platform/</link><pubDate>Thu, 23 May 2024 15:28:49 -0600</pubDate><guid>https://bravenewgeek.com/no-assembly-required-the-benefits-of-an-opinionated-platform/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2024/05/assembly-1024x697.png"&gt;&lt;/p&gt;
&lt;p&gt;When you talk to a doctor about a medical issue they will often present you with all of the options but shy away from providing an unambiguous recommendation. When you talk to a lawyer about a legal matter they frequently do the same. While it’s important to understand your options and their trade-offs or associated risks, when you go to these specialists you are likely seeking the counsel of an experienced and knowledgeable expert in their field who can help you make an informed decision. What most people are probably looking for is the answer to “what would you, someone who knows a lot about this stuff, do if you were in this situation?” After all, many of us are probably capable of finding the options ourselves, but the difficult part is determining what the right course of action is for a particular situation.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/wp-content/uploads/2024/05/assembly-1024x697.png"></p>
<p>When you talk to a doctor about a medical issue they will often present you with all of the options but shy away from providing an unambiguous recommendation. When you talk to a lawyer about a legal matter they frequently do the same. While it’s important to understand your options and their trade-offs or associated risks, when you go to these specialists you are likely seeking the counsel of an experienced and knowledgeable expert in their field who can help you make an informed decision. What most people are probably looking for is the answer to “what would you, someone who knows a lot about this stuff, do if you were in this situation?” After all, many of us are probably capable of finding the options ourselves, but the difficult part is determining what the right course of action is for a particular situation.</p>
<p>One can guess that the reason these professions shy away from making clear recommendations is because they don’t want to be accused of malpractice. The result is that those of us who are less litigiously inclined can have a hard time getting an expert opinion. The truth is we often do this ourselves with our consulting at Real Kinetic. A client will ask us a question and we will present them with the options and their various trade-offs. In my opinion, this isn’t even because we’re afraid of being sued. It’s more because when you don’t truly have skin in the game, it’s easy to go into “engineer mode” where you provide the analysis and decision tree without actually offering a decision. We remove ourselves from the situation because we feel it’s not really our place to be involved, nor do we want to be liable. I think this happens with other professions too.</p>
<p>Almost always what our clients are looking for is a concrete recommendation, the answer to “what would you do in this situation?” Sometimes we have clear, actionable recommendations. Other times we don’t have a strong opinion, but we help them make a decision by putting ourselves in their shoes. A seasoned engineer knows the answer is often “it depends”, but when they have skin in the game, they also have to make a choice at the end of the day.</p>
<p>This is very much the case when it comes to advising clients on operationalizing cloud platforms for their organization. When you look at a platform like AWS or GCP, it’s just a pile of Legos. No picture of what you’re building, no assembly instructions, just a collection of infinite possibilities—and millions of decisions. Some people see a pile of Legos and can start building. Others, myself included, suddenly become incapable of making decisions. This is where most of our clients find themselves. They look to the vendor, but the vendor is just like the lawyer offering all of the possible legal maneuvers one could perform. It’s not entirely helpful unless they’re willing to go out on a limb. Most will stick to generic guidance from my experience.</p>
<p>These cloud platforms are inherently unopinionated because they must meet customers—<em>all</em> customers—where they are. Just like the attorney providing all of your possible legal options, these platforms provide all of the possible building blocks you might need to construct something. How do you assemble them? Well, that’s up to you. The vendor doesn’t want to be in the business of having skin in the game.</p>
<p>Our team has a lot of experience putting the Legos together. Over the years, we’ve identified the common patterns, the things that work well, the things that don’t, and the pitfalls to avoid. Clients hire us to help them do the same, but they don’t usually want us to enumerate all of the possible configurations they could assemble. They want the “what would you do in this situation?” And while every company and situation is unique, the reality is most companies would be perfectly fine with a common, opinionated platform that implements best practices and provides just the right amount of flexibility. Those best practices <em>are</em> the opinions and recommendations we offer our clients through our consulting. This is what led us to create <a href="https://blog.realkinetic.com/how-konfig-provides-an-enterprise-platform-with-gitlab-and-google-cloud-849e31f85980">Konfig</a>, an opinionated workload delivery platform built specifically around GCP and GitLab. It’s something that lets us codify those opinions into a product customers can install. No assembly required.</p>
<p>In <a href="https://bravenewgeek.com/cloud-without-kubernetes/">previous blog posts</a>, I’ve referenced our team’s experience at Workiva, a company that went from startup to IPO on GCP. When Workiva went public, it had just two ops people who were primarily responsible for managing a small set of VMs on AWS. This was possible because Workiva leveraged Google App Engine, which provided an integrated platform that allowed its developers to focus on product and feature development. At the time, App Engine was about as opinionated as a platform could be. It felt like a grievous constraint which ultimately precipitated moving to AWS, but in fact it was a major boon to the company. It meant Workiva’s engineers allocated almost all of their time towards things that made the company money. Moving to AWS resulted in a multi-year effort to effectively recreate what we had with App Engine, just with much more headcount to support and evolve it.</p>
<p>We’ve seen firsthand the power of an integrated, opinionated platform. Through our consulting, we’ve also seen companies struggle tremendously with things like DevOps, Platform Engineering, and just generally operationalizing the cloud. GCP has evolved a lot since App Engine. Both GitLab and GCP are highly flexible platforms, but they lack any opinionation because they are designed to address a broad set of customer needs. This leaves a void where customers are left having to assemble the Legos to provide their own opinionation, which is where we see customers struggle the most. This is what prompted us to build Konfig as a means to provide that missing layer of opinionation.</p>
<p>PaaS has become a bit of a taboo now. Instead, organizations are investing significantly in developing their own Internal Developer Platform (IDP), which is basically just a PaaS you have to care for and maintain with your headcount instead of another company’s headcount. It’s not entirely obvious if this is strategically beneficial for companies that build software. In my opinion, many of these companies are better off shifting that investment towards things that differentiate their business. Companies should not be expressing their creativity on software architecture and platform infrastructure but rather on their customer-facing products and services (sometimes this necessitates innovating with infrastructure, but this tends to be more with internet-scale companies versus ordinary businesses). What an opinionated platform does is delete this discussion altogether. Now, with Konfig, we can get the same types of benefits we saw with App Engine over 10 years ago without the same constraints. And unlike App Engine, we have a means to customize the platform when needed without losing the benefits. You can have reasonable defaults and opinions but can evolve and grow as your needs or understanding change.</p>
<p>There’s a reason IKEA furniture comes with detailed instructions: while some people relish the challenge of figuring things out themselves, most just want to get on with using the finished product. The same is true for cloud platforms. While the flexibility of GCP, GitLab, and similar platforms is undeniable, it can lead to decision paralysis and wasted resources spent on building infrastructure that already exists.</p>
<p>This is where the benefits of an opinionated platform come in. By offering a pre-configured solution built around best practices, it eliminates the need for endless customization in order to get companies up and running faster. This frees up valuable engineering resources to focus on what truly matters, differentiation and innovation.</p>
<p>In my <a href="https://bravenewgeek.com/understanding-konfigs-opinionation/">next post</a>, I want to dive into exactly <em>what</em> opinions Konfig has and the reasoning behind each. We’ll also look at the escape hatch available to us so that when we do hit a constraint, we can easily move it out of the way.</p>
<hr>
<p>Konfig reduces your cloud platform engineering costs and the time to deliver new software products. <a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=benefits-of-opinionation">Reach out</a> to learn more about Konfig or schedule a demo.</p>
]]></content:encoded></item><item><title>How Konfig provides an enterprise platform with GitLab and Google Cloud</title><link>https://bravenewgeek.com/how-konfig-provides-an-enterprise-platform-with-gitlab-and-google-cloud/</link><pubDate>Mon, 29 Apr 2024 14:24:31 -0600</pubDate><guid>https://bravenewgeek.com/how-konfig-provides-an-enterprise-platform-with-gitlab-and-google-cloud/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2024/04/konfig.png"&gt;&lt;/p&gt;
&lt;p&gt;In a &lt;a href="https://blog.realkinetic.com/security-maintainability-velocity-choose-one-cf9eb9533d71"&gt;previous post&lt;/a&gt;, I explained the fundamental competing priorities that companies have when building software: security and governance, maintainability, and speed to production. These three concerns are all in constant tension with each other. For companies either migrating to the cloud or beginning a modernization effort, addressing them can be a major challenge. When you’re unfamiliar with the cloud, building systems that are both secure and maintainable is difficult because you’re not in a position to make decisions that have long-lasting and significant impact—you just don’t know what you don’t know. One small misstep can result in a major security incident. A bad decision can take years to manifest a problem. As a result, these migration and modernization efforts often stall out as analysis paralysis takes hold.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/wp-content/uploads/2024/04/konfig.png"></p>
<p>In a <a href="https://blog.realkinetic.com/security-maintainability-velocity-choose-one-cf9eb9533d71">previous post</a>, I explained the fundamental competing priorities that companies have when building software: security and governance, maintainability, and speed to production. These three concerns are all in constant tension with each other. For companies either migrating to the cloud or beginning a modernization effort, addressing them can be a major challenge. When you’re unfamiliar with the cloud, building systems that are both secure and maintainable is difficult because you’re not in a position to make decisions that have long-lasting and significant impact—you just don’t know what you don’t know. One small misstep can result in a major security incident. A bad decision can take years to manifest a problem. As a result, these migration and modernization efforts often stall out as analysis paralysis takes hold.</p>
<p>This is where Real Kinetic usually steps in: to get a stuck project moving again, to provide guard rails, and to help companies avoid the hidden landmines by offering our expertise and experience. We’ve been there before, so we help navigate our clients through the foundational decision making, design, and execution of large-scale cloud migrations. We’ve helped migrate systems generating billions of dollars in revenue and hundreds of millions in cloud spend. We’ve also helped customers <em>save</em> tens of millions in cloud spend by guiding them through more cost-effective solution architectures. And while we’ve had a lot of success helping our clients operationalize the cloud, they still routinely ask us: <em>why is it so damn difficult?</em> The truth is it doesn’t have to be if you’re willing to take just a slightly more opinionated stance.</p>
<p>Recently, we introduced <a href="https://blog.realkinetic.com/introducing-konfig-gitlab-and-google-cloud-preconfigured-for-startups-and-enterprises-6024816c6ab9">Konfig</a>, our solution for this exact problem. Konfig packages up our expertise and years of experience operationalizing and building software in the cloud. More concretely, it’s an enterprise integration of GitLab and Google Cloud that addresses those three competing priorities I mentioned earlier. The reason it’s so difficult for organizations to operationalize GitLab and GCP is because they are robust and flexible platforms that address a broad set of customer needs. As a result, they do not take an opinionated stance on pretty much anything. This leaves a gap unaddressed, and customers are left having to put together their own opinionation that meets their needs—except, they usually aren’t in a position to do this. Thus, they stall.</p>
<p><a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=enterprise-platform">Konfig</a> gives you a functioning, enterprise-ready GitLab and GCP environment that is secure by default, has strong governance and best practices built-in, and scales with your organization. The best part? You can start deploying production workloads in a matter of minutes. It does this by taking an opinionated stance on some things. It bridges the gap that is unaddressed by Google and GitLab. Those opinions are the recommendations, guidance, and best practices we share with clients when they are operationalizing the cloud.</p>
<p>Perhaps the most obvious opinion is that Konfig is specific to GCP and GitLab. We could extend this model to other platforms like AWS and GitHub, but we chose to focus on building a white-glove experience with GCP and GitLab first because they work together so well. GCP has <a href="https://blog.realkinetic.com/gcp-and-aws-whats-the-difference-3b1329f0ffb3">first-class managed services</a> and serverless offerings which lend themselves to providing a platform that is secure, maintainable, and has a great developer experience. GitLab’s CI/CD is better designed than GitHub Actions and its hierarchical structure maps well to GCP’s resource hierarchy.</p>
<p>Moreover, Konfig embraces service-oriented architecture and domain-driven design which drives how we structure folders and projects in GCP and groups in GitLab. This structure gives us a powerful way to map access management and governance, which we’ll explore later. It’s a best practice that makes systems more maintainable and evolvable. We’ll discuss Konfig’s opinions and their rationale in more depth in a future post. For now, I want to explain how Konfig provides an enterprise platform by addressing each of the three concerns in the software development triangle: security and governance, maintainable infrastructure, and speed to production.</p>
<h2 id="security-and-governance">Security and Governance</h2>
<h3 id="access-management">Access Management</h3>
<p>Konfig relies on a hierarchy consisting of control plane &gt; platforms &gt; domains &gt; workloads. The control plane is the top-level container which is responsible for managing all of the resources contained within it. Platforms are used to group different lines of business, product lines, or other organizational units. Domains are a way to group related workloads or services.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image2-1.png"></p>
<p>The Konfig hierarchy</p>
<p>This structure provides several benefits. First, we can map it to hierarchies in both GitLab and GCP, shown in the image below. A platform maps to a group in GitLab and a folder in GCP. A domain maps to a subgroup in GitLab and a nested folder along with a project per environment in GCP.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image4-1-1024x718.png"></p>
<p>Konfig synchronizes structure and permissions between GitLab and GCP</p>
<p>This hierarchy lets us manage permissions cleanly because we can assign access at the control plane, platform, and domain levels. These permissions will be synced to GitLab in the form of SAML group links and to GCP in the form of IAM roles. When a user has “dev” access, they get the Developer role for the respective group in GitLab. In GCP, they get the Editor role for dev environment projects and Viewer for higher environments. “Maintainer” has slightly more elevated access, and “owner” effectively provides root access to allow for a “break-glass” scenario. The hierarchy means these permissions can be inherited by setting them at different levels. This access management is shown in the platform.yaml and domain.yaml examples below highlighted in bold.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Platform
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: ecommerce-platform
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/control-plane: konfig-control-plane
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  platformName: Ecommerce Platform
</span></span><span class="line"><span class="cl">  groups:
</span></span><span class="line"><span class="cl">    dev: [ecommerce-devs@example.com]
</span></span><span class="line"><span class="cl">    maintainer: [ecommerce-maintainers@example.com]
</span></span><span class="line"><span class="cl">    owner: [ecommerce-owners@example.com]
</span></span></code></pre></div><p><em>platform.yaml</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Domain
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: payment-processing
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/platform: ecommerce-platform
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  domainName: Payment Processing
</span></span><span class="line"><span class="cl">  groups:
</span></span><span class="line"><span class="cl">    dev: [payment-devs@example.com]
</span></span><span class="line"><span class="cl">    maintainer: [payment-maintainers@example.com]
</span></span><span class="line"><span class="cl">    owner: [payment-owners@example.com]
</span></span></code></pre></div><p><em>domain.yaml</em></p>
<h3 id="authentication-and-authorization">Authentication and Authorization</h3>
<p>There are three different authentication and authorization concerns in Konfig. First, GitLab needs to authenticate with GCP such that pipelines can deploy to the Konfig control plane. Second, the control plane, which runs in a privileged customer GCP project, needs to authenticate with GCP such that it can create and manage cloud resources in the respective customer projects. Third, customer workloads need to be able to authenticate with GCP such that they can correctly access their resource dependencies, such as a database or Pub/Sub topic. The configuration for all of this authentication as well as the proper authorization settings is managed by Konfig. Not only that, but none of these authentication patterns involve any kind of long-lived credentials or keys.</p>
<p>GitLab to GCP authentication is implemented using Workload Identity Federation, which uses OpenID Connect to map a GitLab identity to a GCP service account. We scope this identity mapping so that the GitLab pipeline can only deploy to its respective control plane namespace. For instance, the Payment Processing team can’t deploy to the Fulfillment team’s namespace and vice versa.</p>
<p>Control plane to GCP authentication relies on domain-level service accounts that map a control plane namespace for a domain (let’s say Payment Processing) to a set of GCP projects for the domain (e.g. Payment Processing Dev, Payment Processing Stage, and Payment Processing Prod).</p>
<p>Finally, workloads also rely on service accounts to authenticate and access their resource dependencies. Konfig creates a service account for each workload and sets the proper roles on it needed to access resources. We’ll look at this in more detail next.</p>
<p>This approach to authentication and authorization means there is very little attack surface area. There are no keys to compromise and even if an attacker were to somehow compromise GitLab, such as by hijacking a developer’s account, the blast radius is minimal.</p>
<h3 id="least-privilege-access">Least-Privilege Access</h3>
<p>Konfig is centered around declaratively modeling workloads and their infrastructure dependencies. This is done with the workload.yaml. This lets us spec out all of the resources our service needs like databases, storage buckets, caches, etc. Konfig then handles provisioning and managing these resources. It also handles creating a service account for each workload that has roles that are scoped to only the resources specified by the workload. Let’s take a look at an example.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">apiVersion</span><span class="p">:</span> <span class="n">konfig</span><span class="o">.</span><span class="n">realkinetic</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">v1beta1</span>
</span></span><span class="line"><span class="cl"><span class="n">kind</span><span class="p">:</span> <span class="n">Workload</span>
</span></span><span class="line"><span class="cl"><span class="n">metadata</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">name</span><span class="p">:</span> <span class="n">order</span><span class="o">-</span><span class="n">api</span>
</span></span><span class="line"><span class="cl"><span class="n">spec</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">region</span><span class="p">:</span> <span class="n">us</span><span class="o">-</span><span class="n">central1</span>
</span></span><span class="line"><span class="cl">  <span class="n">runtime</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">kind</span><span class="p">:</span> <span class="n">RunService</span>
</span></span><span class="line"><span class="cl">    <span class="n">parameters</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">template</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">containers</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="o">-</span> <span class="n">image</span><span class="p">:</span> <span class="n">order</span><span class="o">-</span><span class="n">api</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">kind</span><span class="p">:</span> <span class="n">StorageBucket</span>
</span></span><span class="line"><span class="cl">      <span class="n">name</span><span class="p">:</span> <span class="n">receipts</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">kind</span><span class="p">:</span> <span class="n">SQLInstance</span>
</span></span><span class="line"><span class="cl">      <span class="n">name</span><span class="p">:</span> <span class="n">order</span><span class="o">-</span><span class="n">store</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">kind</span><span class="p">:</span> <span class="n">PubSubTopic</span>
</span></span><span class="line"><span class="cl">      <span class="n">name</span><span class="p">:</span> <span class="n">order</span><span class="o">-</span><span class="n">events</span>
</span></span></code></pre></div><p><em>workload.yaml</em></p>
<p>Here we have a simple workload definition for a service called “order-api”. This workload is a Cloud Run service that has three resource dependencies: a Cloud Storage bucket called “receipts”, a Cloud SQL instance called “order-store”, and a Pub/Sub topic called “order-events”. When this YAML definition gets applied by the GitLab pipeline, Konfig will handle spinning up these resources as well as the Cloud Run service itself and a service account for order-api. This service account will have the Pub/Sub Publisher role scoped only to the order-events topic and the Storage Object User role scoped to the receipts bucket. Konfig will also create a SQL user on the Cloud SQL instance whose credentials will be securely stored in Secret Manager and accessible only to the order-api service account. The Konfig UI shows this workload, all of its dependencies, and each resource’s status.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image1-1024x432.png"></p>
<p>Konfig workload UI</p>
<h3 id="enforcing-enterprise-standards">Enforcing Enterprise Standards</h3>
<p>After looking at the example workload definition above, you may be wondering: there’s a lot more to creating a storage bucket, Cloud SQL database, or Pub/Sub topic than just specifying its name. Where’s the rest? It’s a good segue into how Konfig offers a means for providing sane defaults and enforcing organizational standards around how resources are configured.</p>
<p>Konfig uses templates to allow an organization to manage either default or required settings on resources. This lets a platform team centrally manage how things like databases, storage buckets, or caches are configured. For instance, our organization might enforce a particular version of PostgreSQL, high availability mode, private IP only, and customer-managed encryption key. For non-production environments, we may use a non-HA configuration to reduce costs. Just like our platform, domain, and workload definitions, these templates are also defined in YAML and managed via GitOps.</p>
<p>We can also take this further and even manage what cloud APIs or services are available for developers to use. Like access management, this is also configured at the control plane, platform, and domain levels. We can specify what services are enabled by default at the platform level which will inherit across domains. We can also <em>disable</em> certain services, for example, at the domain level. The example platform and domain definitions below illustrate this. We enable several services on the Ecommerce platform and restrict Pub/Sub, Memorystore (Redis), and Firestore on the Payment Processing domain.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Platform
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: ecommerce-platform
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/control-plane: konfig-control-plane
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  platformName: Ecommerce Platform
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    services:
</span></span><span class="line"><span class="cl">      defaults:
</span></span><span class="line"><span class="cl">        - cloud-run
</span></span><span class="line"><span class="cl">        - cloud-sql
</span></span><span class="line"><span class="cl">        - cloud-storage
</span></span><span class="line"><span class="cl">        - secret-manager
</span></span><span class="line"><span class="cl">        - cloud-kms
</span></span><span class="line"><span class="cl">        - pubsub
</span></span><span class="line"><span class="cl">        - redis
</span></span><span class="line"><span class="cl">        - firestore
</span></span></code></pre></div><p><em>platform.yaml</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Domain
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: payment-processing
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/platform: ecommerce-platform
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  domainName: Payment Processing
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    services:
</span></span><span class="line"><span class="cl">      disabled:
</span></span><span class="line"><span class="cl">        - pubsub
</span></span><span class="line"><span class="cl">        - redis
</span></span><span class="line"><span class="cl">        - firestore
</span></span></code></pre></div><p><em>domain.yaml</em></p>
<p>This model provides a means for companies to enforce a “<a href="https://engineering.atspotify.com/2020/08/how-we-use-golden-paths-to-solve-fragmentation-in-our-software-ecosystem/">golden path</a>” or an opinionated and supported way of building something within your organization. It’s also a critical component for organizations dealing with regulatory or compliance requirements such as PCI DSS. Even for organizations which prefer to favor developer autonomy, it allows them to improve productivity by setting good defaults so that developers can focus less on infrastructure configuration and more on product or feature development.</p>
<h3 id="sdlc-integration">SDLC Integration</h3>
<p>It’s important to have an SDLC that enables developer efficiency while also providing a sound governance story. Konfig fits into existing SDLCs by following a GitOps model. It allows your infrastructure to follow the same SDLC as your application code. Both rely on a trunk-based development model. Since everything from platforms and domains to workloads is managed declaratively, in code, we can apply typical SDLC practices like protected branches, short-lived feature branches, merge requests, and code reviews.</p>
<p>Even when we create resources from the Konfig UI, they are backed by this declarative configuration. This is something we call “Visual IaC.” Teams who are more comfortable working with a UI can still define and manage their infrastructure using IaC without even having to directly <em>write</em> any IaC. We often encounter organizations who have teams like data analytics, data science, or ETL which are not equipped to deal with managing cloud infrastructure. This approach allows these teams to be just as productive—and empowered—as teams with seasoned infrastructure engineers while still meeting an organization’s SDLC requirements.</p>
<p><a href="/wp-content/uploads/2024/04/image3-1.png"><img loading="lazy" src="/wp-content/uploads/2024/04/image3-1-1024x499.png"></a></p>
<p>Creating a resource in the Konfig workload UI</p>
<h3 id="cost-management">Cost Management</h3>
<p>Another key part of governance is having good cost visibility. This can be challenging for organizations because it heavily depends on how workloads and resources are structured in a customer’s cloud environment. If things are structured incorrectly, it can be difficult to impossible to correctly allocate costs across different business units or product areas.</p>
<p>The Konfig hierarchy of platforms &gt; domains &gt; workloads solves this problem altogether because related workloads are grouped into domains and related domains are grouped into platforms. A domain maps to a set of projects, one per environment, which makes it trivial to see what a particular domain costs. Similarly, we can easily see an aggregate cost for an entire platform because of this grouping. The GCP billing account ID is set at the platform level and all projects within a platform are automatically linked to this account. Konfig makes it easy to implement an IT chargeback or showback policy for cloud resource consumption within a large organization.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Platform
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: ecommerce-platform
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/control-plane: konfig-control-plane
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  platformName: Ecommerce Platform
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    billingAccountId: &#34;123ABC-456DEF-789GHI&#34;
</span></span></code></pre></div><p><em>platform.yaml</em></p>
<h2 id="maintainable-infrastructure">Maintainable Infrastructure</h2>
<h3 id="opinionated-model">Opinionated Model</h3>
<p>We’ve talked about opinionation quite a bit already, but I want to speak to this directly. The reason companies so often struggle to operationalize their cloud environment is because the platforms themselves are unwilling to take an opinionated stance on how customers should solve problems. Instead, they aim to be as flexible and accommodating as possible so they can meet as many customers where they are as possible. But we frequently hear from clients: “just tell me how to do it” or even “can you do it for me?” Many of them don’t <em>want</em> the flexibility, they just want a preassembled solution that has the best practices already implemented. It’s the difference between a pile of Legos with no instructions and an already-assembled Lego factory. Sure, it’s fun to build something yourself and express your creativity, but this is <em>not</em> where most businesses want creativity. They want creativity in the things that generate revenue.</p>
<p>Konfig <em>is</em> that preassembled Lego factory. Does that mean you get to customize and change all the little details of the platform? No, but it means your organization can focus its energy and creativity on the things that actually matter to your customers. With Konfig, we’ve codified the best practices and patterns into a turnkey solution. This more opinionated approach allows us to provide a good developer experience that results in <em>maintainable</em> infrastructure. The absence of creative constraints tends to lead to highly bespoke architectures and solutions that are difficult to maintain, especially at scale. It leads to a great deal of inefficiency and complexity.</p>
<h3 id="architectural-standards">Architectural Standards</h3>
<p>Earlier we saw how Konfig provides a powerful means for enforcing enterprise standards and sane defaults for infrastructure as well as how we can restrict the use of certain services. While we looked at this in the context of governance, it’s also a key ingredient for maintainable infrastructure. Organizational standards around infrastructure and architecture improve efficiency and maintainability for the same reason the opinionation we discussed above does. Konfig’s templating model and approach to platforms and domains effectively allows organizations to codify their own internal opinions.</p>
<h3 id="automatic-reconciliation">Automatic Reconciliation</h3>
<p>There are a number of <a href="https://blog.realkinetic.com/its-time-to-retire-terraform-30545fd5f186">challenges with traditional IaC tools like Terraform</a>. One such challenge is the problem of state management and drift. A resource managed by Terraform might be modified outside of Terraform which introduces a state inconsistency. This can range from something simple like a single field on a resource to something very complex, such as an entire application stack. Resolving drift can sometimes be quite problematic. Terraform works by storing its configuration in a state file. Aside from the problem that the state file often contains sensitive information like passwords and credentials, the Terraform state is applied in a “one-off” fashion. That is to say, when the Terraform apply command is run, the current state configuration is applied to the environment. At this point, Terraform is no longer involved until the next time the state is applied. It could be hours, days, weeks, or longer between applies.</p>
<p>Konfig uses a very different model. In particular, it regularly reconciles the infrastructure state automatically. This solves the issue of state drift altogether since infrastructure is no longer applied as “one-off” events. Instead, it treats infrastructure the way it actually <em>is</em>—a living, breathing thing—rather than a single, point-in-time snapshot.</p>
<h2 id="speed-to-production">Speed to Production</h2>
<h3 id="turnkey-setup">Turnkey Setup</h3>
<p>Our goal with Konfig is to provide a fully turnkey experience, meaning customers have a complete and enterprise-grade platform with little-to-no setup. This includes setup of the platform itself, but also setup of new workloads within Konfig. We want to make it as easy and frictionless as possible for organizations to start shipping workloads to production. It’s common for a team to build a service that is code complete but getting it deployed to various environments takes weeks or even months due to the different organizational machinations that need to occur first. With Konfig, we <em>start</em> with a workload deploying to an environment. You can use our workload template in GitLab to create a new workload project and deploy it to a real environment in a matter of minutes. The CI/CD pipeline is already configured for you. Then you can work backwards and start adding your code and infrastructure resources. We call this “Deployment-Driven Development.” </p>
<p>Konfig works by using a control plane which lives in a customer GCP project. The setup of this control plane is fully automated using the Konfig CLI. When you run the CLI bootstrap command, it will run through a guided wizard which sets up the necessary resources in both GitLab and GCP. After this runs, you’ll have a fully functioning enterprise platform.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/konfig_cli_looped.gif"></p>
<p>Konfig CLI</p>
<h3 id="workload-autowiring">Workload Autowiring</h3>
<p>We saw earlier how workloads declaratively specify their infrastructure resources (something we call <em>resource claims</em>) and how Konfig manages a service account with the correctly scoped, minimal set of permissions to access those resources. For resources that use credentials, such as Cloud SQL database users, Konfig will manage these secrets by storing them in GCP’s Secret Manager. Only the workload’s service account will be able to access this. This secret gets automatically mounted onto the workload. Resource references, such as storage bucket names, Pub/Sub topics, or Cloud SQL connections, will also be injected into the workload to make it simple for developers to start consuming these resources.</p>
<h3 id="api-ingress-and-path-based-routing">API Ingress and Path-Based Routing</h3>
<p>Konfig makes it easy to control the ingress of services. We can set a service such that it is only accessible within a domain, within a platform, or within a control plane. We can even control <em>which</em> domains can access an API. Alternatively, we can expose a service to the internet. Konfig uses a path-based routing scheme which maps to the platform &gt; domain &gt; workload hierarchy. Let’s take a look at an example platform, domain, and workload configuration.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Platform
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: ecommerce-platform
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/control-plane: konfig-control-plane
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  platformName: Ecommerce Platform
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    api:
</span></span><span class="line"><span class="cl">      path: /ecommerce
</span></span></code></pre></div><p><em>platform.yaml</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Domain
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: payment-processing
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/platform: ecommerce-platform
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  domainName: Payment Processing
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    api:
</span></span><span class="line"><span class="cl">      path: /payment
</span></span></code></pre></div><p><em>domain.yaml</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">apiVersion</span><span class="p">:</span> <span class="n">konfig</span><span class="o">.</span><span class="n">realkinetic</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">v1beta1</span>
</span></span><span class="line"><span class="cl"><span class="n">kind</span><span class="p">:</span> <span class="n">Workload</span>
</span></span><span class="line"><span class="cl"><span class="n">metadata</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">name</span><span class="p">:</span> <span class="n">authorization</span><span class="o">-</span><span class="n">service</span>
</span></span><span class="line"><span class="cl"><span class="n">spec</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">region</span><span class="p">:</span> <span class="n">us</span><span class="o">-</span><span class="n">central1</span>
</span></span><span class="line"><span class="cl">  <span class="n">runtime</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">kind</span><span class="p">:</span> <span class="n">RunService</span>
</span></span><span class="line"><span class="cl">    <span class="n">parameters</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">template</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">containers</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="o">-</span> <span class="n">image</span><span class="p">:</span> <span class="n">authorization</span><span class="o">-</span><span class="n">service</span>
</span></span><span class="line"><span class="cl">  <span class="n">api</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">path</span><span class="p">:</span> <span class="o">/</span><span class="n">auth</span>
</span></span></code></pre></div><p><em>workload.yaml</em></p>
<p>Note the API path component in the above configurations. Our ecommerce platform specifies /ecommerce as its path, the payment-processing domain specifies /payment, and the authorization-service workload specifies /auth. The full route to hit the authorization-service would then be /ecommerce/payment/auth. We’ll explore API ingress and routing in more detail in a later post.</p>
<h2 id="an-enterprise-ready-workload-delivery-platform">An Enterprise-Ready Workload Delivery Platform</h2>
<p>We’ve looked at a few of the ways Konfig provides a compelling enterprise integration of GitLab and Google Cloud. It addresses a gap these products leave by not offering strong opinions to customers. Konfig allows us to package up the best practices and patterns for implementing a production-ready workload delivery platform and provide that missing opinionation. It tackles three competing priorities that arise when building software: security and governance, maintainable infrastructure, and speed to production. Konfig plays a strategic role in reducing the cost and improving the efficiency of cloud migration, modernization, and greenfield efforts. <a href="https://konfigurate.com/contact?utm_source=bravenewgeek.com&amp;utm_campaign=enterprise-platform">Reach out</a> to learn more about Konfig or schedule a demo.</p>
]]></content:encoded></item><item><title>Security, Maintainability, Velocity: Choose One</title><link>https://bravenewgeek.com/security-maintainability-velocity-choose-one/</link><pubDate>Wed, 17 Apr 2024 11:41:24 -0600</pubDate><guid>https://bravenewgeek.com/security-maintainability-velocity-choose-one/</guid><description>&lt;p&gt;There are three competing priorities that companies have as it relates to software development: security, maintainability, and velocity. I’ll elaborate on what I mean by each of these in just a bit. When I originally started thinking about this, I thought of it in the context of the “good, fast, cheap: choose two” &lt;a href="https://en.wikipedia.org/wiki/Project_management_triangle"&gt;project management triangle&lt;/a&gt;. But after thinking about it for more than a couple minutes, and as I related it to my own experience and observations at other companies, I realized that in practice it’s much worse. For most organizations building software, it’s more like security, maintainability, velocity: choose &lt;em&gt;one&lt;/em&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>There are three competing priorities that companies have as it relates to software development: security, maintainability, and velocity. I’ll elaborate on what I mean by each of these in just a bit. When I originally started thinking about this, I thought of it in the context of the “good, fast, cheap: choose two” <a href="https://en.wikipedia.org/wiki/Project_management_triangle">project management triangle</a>. But after thinking about it for more than a couple minutes, and as I related it to my own experience and observations at other companies, I realized that in practice it’s much worse. For most organizations building software, it’s more like security, maintainability, velocity: choose <em>one</em>.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/software_development_triangle.png"></p>
<p><em>The Software Development Triangle</em></p>
<p>Of course, most organizations are not <em>explicitly</em> making these trade-offs. Instead, the internal preferences and culture of the company reveal them. I believe many organizations, consciously or not, accept this trade-off as an immovable constraint. More risk-averse groups might even <em>welcome</em> it. Though the triangle most often results in a “choose one” sort of compromise, it’s not some innate law. You can, in fact, have all three with a little bit of careful thought and consideration. And while reality is always more nuanced than what this simple triangle suggests, I find looking at the extremes helps to ground the conversation. It emphasizes the natural tension between these different concerns. Bringing that tension to the forefront allows us to be more intentional about how we manage it.</p>
<p>It wasn’t until recently that I distilled down these trade-offs and mapped them into the triangle shown above, but we’ve been helping clients navigate this exact set of competing priorities for over six years at Real Kinetic. We built <a href="https://konfigurate.com?utm_source=bravenewgeek.com&amp;utm_campaign=triangle">Konfig</a> as a direct response to this since it was such a common challenge for organizations. We’re excited to offer a solution which is the culmination of years of consulting and which allows organizations to no longer compromise, but first let’s explore the trade-offs I’m talking about.</p>
<h2 id="security">Security</h2>
<p>Companies, especially mid- to large- sized organizations, care a great deal about security (and rightfully so!). That’s not to say startups don’t care about it, but the stakes are just much higher for enterprises. They are terrified of being the next big name in the headlines after a major data breach or ransomware attack. I call this priority <em>security</em> for brevity, but it actually consists of two things which I think are closely aligned: security and governance.</p>
<p>Governance directly supports security in addition to a number of other concerns like reliability, risk management, and compliance. This is sometimes referred to as Governance, Risk, and Compliance or GRC. Enterprises need control over, and visibility <em>into</em>, all of the pieces that go into building and delivering software. This is where things like SDLC, separation of duties, and access management come into play. Startups may play it more fast and loose, but more mature organizations frequently have compliance or regulatory obligations like SOC 2 Type II, PCI DSS, FINRA, FedRAMP, and so forth. Even if they don’t have regulatory constraints, they usually have a reputation that needs to be protected, which typically means more rigid processes and internal controls. This is where things can go sideways for larger organizations as it usually leads to practices like change review boards, enterprise (ivory tower) architecture programs, and SAFe. Enterprises tend to be pretty good at governance, but it comes at a cost.</p>
<p>It should come as no surprise that security and governance are in conflict with speed, but they are often in contention with well-architected and maintainable systems as well. When organizations enforce strong governance and security practices, it can often lead to developers following bad practices. Let me give an example I have seen firsthand at an organization.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image2.jpg"></p>
<p>A company has been experiencing stability and reliability issues with its software systems. This has caused several high-profile, revenue-impacting outages which have gotten executives’ attention. The response is to implement a series of process improvements to effectively slow down the release of changes to production. This includes a change review board to sign-off on changes going to production and a production gating process which new workloads going to production must go through before they can be released. The hope is that these process changes will reduce defects and improve reliability of systems in production. At this point, we are wittingly trading off velocity.</p>
<p>What <em>actually</em> happened is that developers began batching up more and more changes to get through the change review board which resulted in “big bang” releases. This caused even <em>more</em> stability issues because now large sets of changes were being released which were increasingly complex, difficult to QA, and harder to troubleshoot. Rollbacks became difficult to impossible due to the size and complexity of releases, increasing the impact of defects. Release backlogs quickly grew, prompting developers to move on to more work rather than sit idle, which further compounded the issue and led to context switching. Decreasing the frequency of deployments only exacerbated these problems. Counterintuitively, slowing down actually <em>increased</em> risk.</p>
<p>To avoid the production gating process, developers began adding functionality to existing services which, architecturally speaking, <em>should</em> have gone into new services. Services became bloated grab bags of miscellaneous functionality since it was easier to piggyback features onto workloads already in production than it was to run the gauntlet of getting a new service to production. These processes were directly and unwittingly impacting system architecture and maintainability. In economics, this is called a “negative externality.” We may have security and governance, but we’ve traded off velocity and maintainability. Adding insult to injury, the processes were not even accomplishing the original goal of improving reliability, they were making it worse!</p>
<h2 id="maintainability">Maintainability</h2>
<p>It’s critical that software systems are not just built to purpose, but also built to <em>last</em>. This means they need to be reliable, scalable, and evolvable. They need to be conducive to finding and correcting bugs. They need to support changing requirements such that new features and functionality can be delivered rapidly. They need to be efficient and cost effective. More generally, software needs to be built in a way that maximizes its useful life.</p>
<p>We simply call this priority <em>maintainability</em>. While it covers a lot, it can basically be summarized as: is the system architected and implemented well? Is it following best practices? Is there a lot of tech debt? How much thought and care has been put into design and implementation? Much of this comes down to gut feel, but an experienced engineer can usually intuit whether or not a system is maintainable pretty quickly. A good proxy can often be the change fail rate, mean time to recovery, and the lead time for implementing new features.</p>
<p>Maintainability’s benefits are more of a long tail. A maintainable system is easier to extend and add new features later, easier to identify and fix bugs, and generally experiences fewer defects. However, the cost for that speed is basically frontloaded. It usually means moving slower towards the beginning while reaping the rewards later. Conversely, it’s easy to go fast if you’re just hacking something together without much concern for maintainability, but you will likely pay the cost later. Companies can become crippled by tech debt and unmaintained legacy systems to the point of “bankruptcy” in which they are completely stuck. This usually leads to major refactors or rewrites which have their own set of problems.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/held_up_by_sticks.jpg"></p>
<p>Additionally, building systems that are both maintainable <em>and</em> secure can be surprisingly difficult, especially in more dynamic cloud environments. If you’ve ever dealt with IAM, for example, you know exactly what I mean. Scoping identities with the right roles or permissions, securely managing credentials and secrets, configuring resources correctly, ensuring proper data protections are in place, etc. Misconfigurations are frequently the cause of the major security breaches you see in the headlines. The unfortunate reality is security practices and tooling lag in the industry, and security is routinely treated as an afterthought. Often it’s a matter of “we’ll get it working and then we’ll come back later and fix up the security stuff,” but later never happens. Instead, an IAM principal is left with overly broad access or a resource is configured improperly. This becomes 10x worse when you are unfamiliar with the cloud, which is where many of our clients tend to find themselves.</p>
<h2 id="velocity">Velocity</h2>
<p>The last competing priority is simply speed to production or <em>velocity</em>. This one probably requires the least explanation, but it’s consistently the priority that is sacrificed the most. In fact, many organizations may even view it as the <em>enemy</em> of the first two priorities. They might equate moving fast with being reckless. Nonetheless, companies are feeling the pressure to deliver faster now more than ever, but it’s much more than just shipping quickly. It’s about developing the ability to adapt and respond to changing market conditions fast and fluidly. Big companies are constantly on the lookout for smaller, more nimble players who might disrupt their business. This is in part why more and more of these companies are prioritizing the move to cloud. The data center has long been their moat and castle as it relates to security and governance, however, and the cloud presents a new and serious risk for them in this space. As a result, velocity typically pays the price.</p>
<p>As I mentioned earlier, velocity is commonly in tension with maintainability as well, it’s usually just a matter of whether that premium is frontloaded or backloaded. More often than not, we can choose to move quickly up front but pay a penalty later on or vice versa. Truthfully though, if you’ve followed the <a href="https://dora.dev/">DORA State of DevOps Reports</a>, you know that a lot of companies neither frontload nor backload their velocity premium—they are just slow all around. These are usually more legacy-minded IT shops and organizations that treat software development as an IT cost center. These are also usually the groups that bias more towards security and governance, but they’re probably the most susceptible to disruption. “Move fast and break things” is not a phrase you will hear permeating these organizations, yet they all desire to modernize and accelerate. We regularly watch these companies’ teams spend <em>months</em> configuring infrastructure, and what they construct is often complex, fragile, and insecure.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/move-fast-and-break-things.png"></p>
<h2 id="choose-three">Choose Three</h2>
<p>Businesses today are demanding strong security and governance, well-structured and maintainable infrastructure, and faster speed to production. The reality, however, is that these three priorities are competing with each other, and companies often end up with one of the priorities dominating the others. If we can acknowledge these trade-offs, we can work to better understand and address them.</p>
<p>We built <a href="https://konfigurate.com?utm_source=bravenewgeek.com&amp;utm_campaign=triangle">Konfig</a> as a solution that tackles this head-on by providing an opinionated configuration of Google Cloud Platform and GitLab. Most organizations start from a position where they must assemble the building blocks in a way that allows them to deliver software effectively, but their own biases result in a solution that skews one way or the other. Konfig instead provides a turnkey experience that minimizes time-to-production, is secure by default, and has governance and best practices built in from the start. Rather than having to choose one of security, maintainability, and velocity, don’t compromise—have all three. In a follow-up post I’ll explain how Konfig addresses concerns like security and governance, infrastructure maintainability, and speed to production in a “by default” way. We’ll see how IAM can be securely managed for us, how we can enforce architecture standards and patterns, and how we can enable developers to ship production workloads quickly by providing autonomy with guardrails and stable infrastructure.</p>
]]></content:encoded></item><item><title>Introducing Konfig: GitLab and Google Cloud preconfigured for startups and enterprises</title><link>https://bravenewgeek.com/introducing-konfig-gitlab-and-google-cloud-preconfigured-for-startups-and-enterprises/</link><pubDate>Thu, 04 Apr 2024 14:51:23 -0600</pubDate><guid>https://bravenewgeek.com/introducing-konfig-gitlab-and-google-cloud-preconfigured-for-startups-and-enterprises/</guid><description>&lt;p&gt;&lt;a href="https://realkinetic.com/"&gt;Real Kinetic&lt;/a&gt; helps businesses transform how they build and deliver software in the cloud. This encompasses legacy migrations, app modernization, and greenfield development. We work with companies ranging from startups to Fortune 500s and everything in between. Most recently, we finished helping Panera Bread migrate their e-commerce platform to Google Cloud from on-prem and led their transition to GitLab. In doing this type of work over the years, we’ve noticed a problem organizations consistently hit that causes them to stumble with these cloud transformations. Products like GCP, GitLab, and Terraform are quite flexible and capable, but they are sort of like the piles of Legos below.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://realkinetic.com/">Real Kinetic</a> helps businesses transform how they build and deliver software in the cloud. This encompasses legacy migrations, app modernization, and greenfield development. We work with companies ranging from startups to Fortune 500s and everything in between. Most recently, we finished helping Panera Bread migrate their e-commerce platform to Google Cloud from on-prem and led their transition to GitLab. In doing this type of work over the years, we’ve noticed a problem organizations consistently hit that causes them to stumble with these cloud transformations. Products like GCP, GitLab, and Terraform are quite flexible and capable, but they are sort of like the piles of Legos below.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image3-1024x286.png"></p>
<p>These products by nature are mostly unopinionated, which means customers need to put the pieces together in a way that works for their unique situation. This makes it difficult to get started, but it’s also difficult to assemble them in a way that works well for 1 team or <em>100</em> teams. Startups require a solution that allows them to focus on product development and accelerate delivery, but ideally adhere to best practices that scale with their growth. Larger organizations require something that enables them to transform how they deliver software and innovate, but they need it to address enterprise concerns like security and governance. Yet, when you’re just getting started, you know the least and are in the worst position to make decisions that will have a potentially long-lasting impact. The outcome is companies attempting a cloud migration or app modernization effort fail to even get off the starting blocks.</p>
<p>It’s easy enough to cobble together something that works, but doing it in a way that is actually enterprise-ready, scalable, and secure is not an insignificant undertaking. In fact, it’s quite literally what we have made a business of helping customers do. What’s worse is that this is undifferentiated work. Companies are spending countless engineering hours building and maintaining their own bespoke “cloud assembly line”—or <a href="https://internaldeveloperplatform.org/">Internal Developer Platform</a> (IDP)—which are all attempting to address the same types of problems. That engineering time would be better spent on things that actually matter to customers and the business.</p>
<p>This is what prompted us to start thinking about solutions. GitLab and GCP don’t offer strong opinions because they address a broad set of customer needs. This creates a need for an opinionated configuration or <em>distribution</em> of these tools. The solution we arrived at is <a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=introducing-konfig">Konfig</a>. The idea is to provide this distribution through what we call “Platform as Code.” Where Infrastructure as Code (IAC) is about configuring the individual resource-level building blocks, Platform as Code is one level higher. It’s something that can assemble these discrete products in a coherent way—almost as if they were natively integrated. The result is a turnkey experience that minimizes time-to-production in a way that will scale, is secure by default, and has best practices built in from the start. A Linux distro delivers a ready-to-use operating system by providing a preconfigured kernel, system library, and application assembly. In the same way, Konfig delivers a ready-to-use platform for shipping software by providing a preconfigured source control, CI/CD, and cloud provider assembly. Whether it’s legacy migration, modernization, or greenfield, Konfig provides your packaged onramp to GCP and GitLab.</p>
<h2 id="platform-as-code">Platform as Code</h2>
<p>Central to Konfig is the notion of a <em>Platform</em>. In this context, a Platform is a way to segment or group parts of a business. This might be different product lines, business units, or verticals. How these Platforms are scoped and how many there are is different for every organization and depends on how the business is structured. A small company or startup might consist of a single Platform. A large organization might have dozens or more.</p>
<p>A Platform is then further subdivided into <em>Domains</em>, a concept we borrow from Domain-Driven Design. A Domain is a bounded context which encompasses the business logic, rules, and processes for a particular area or problem space. Simply put, it’s a way to logically group related services and workloads that make up a larger system. For example, a business providing online retail might have an E-commerce Platform with the following Domains: Product Catalog, Customer Management, Order Management, Payment Processing, and Fulfillment. Each of these domains might contain on the order of 5 to 10 services.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image5.png"></p>
<p>This structure provides a convenient and natural way for us to map access management and governance onto our infrastructure and workloads because it is modeled after the organization structure itself. Teams can have ownership or elevated access within their respective Domains. We can also specify which cloud services and APIs are available at the Platform level and further restrict them at the Domain level where necessary. This hierarchy facilitates a powerful way to enforce enterprise standards for a large organization while allowing for a high degree of flexibility and autonomy for a small organization. Basically, it allows for governance when you need it (and autonomy when you don’t). This is particularly valuable for organizations with regulatory or compliance requirements, but it’s equally valuable for companies wanting to enforce a “<a href="https://engineering.atspotify.com/2020/08/how-we-use-golden-paths-to-solve-fragmentation-in-our-software-ecosystem/">golden path</a>”—that is, an opinionated and supported way of building something within your organization. Finally, Domains provide clear cost visibility because cloud resources are grouped into Domain projects. This makes it easy to see what “Fulfillment” costs versus “Payment Processing” in our E-commerce Platform, for example.</p>
<p>“Platform as Code” means these abstractions are modeled declaratively in YAML configuration and managed via <a href="https://about.gitlab.com/topics/gitops/">GitOps</a>. The definitions of Platforms and Domains consist of a small amount of metadata, shown below, but that small amount of metadata ends up doing <em>a lot</em> of heavy lifting in the background.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Platform
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: ecommerce-platform
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/control-plane: konfig-control-plane
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  platformName: Ecommerce Platform
</span></span><span class="line"><span class="cl">  gitlab:
</span></span><span class="line"><span class="cl">    parentGroupId: 82224252
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    billingAccountId: &#34;123ABC-456DEF-789GHI&#34;
</span></span><span class="line"><span class="cl">    parentFolderId: &#34;1080778227704&#34;
</span></span><span class="line"><span class="cl">    defaultEnvs:
</span></span><span class="line"><span class="cl">      - dev
</span></span><span class="line"><span class="cl">      - stage
</span></span><span class="line"><span class="cl">      - prod
</span></span><span class="line"><span class="cl">    services:
</span></span><span class="line"><span class="cl">      defaults:
</span></span><span class="line"><span class="cl">        - cloud-run
</span></span><span class="line"><span class="cl">        - cloud-sql
</span></span><span class="line"><span class="cl">        - cloud-storage
</span></span><span class="line"><span class="cl">        - secret-manager
</span></span><span class="line"><span class="cl">        - cloud-kms
</span></span><span class="line"><span class="cl">        - pubsub
</span></span><span class="line"><span class="cl">        - redis
</span></span><span class="line"><span class="cl">        - firestore
</span></span><span class="line"><span class="cl">    api:
</span></span><span class="line"><span class="cl">      path: /ecommerce
</span></span></code></pre></div><p><em>platform.yaml</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: konfig.realkinetic.com/v1beta1
</span></span><span class="line"><span class="cl">kind: Domain
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: payment-processing
</span></span><span class="line"><span class="cl">  namespace: konfig-control-plane
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    konfig.realkinetic.com/platform: ecommerce-platform
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  domainName: Payment Processing
</span></span><span class="line"><span class="cl">  gcp:
</span></span><span class="line"><span class="cl">    services:
</span></span><span class="line"><span class="cl">      disabled:
</span></span><span class="line"><span class="cl">        - pubsub
</span></span><span class="line"><span class="cl">        - redis
</span></span><span class="line"><span class="cl">        - firestore
</span></span><span class="line"><span class="cl">    api:
</span></span><span class="line"><span class="cl">      path: /payment
</span></span><span class="line"><span class="cl">  groups:
</span></span><span class="line"><span class="cl">    dev: [payment-devs@example.com]
</span></span><span class="line"><span class="cl">    maintainer: [payment-maintainers@example.com]
</span></span><span class="line"><span class="cl">    owner: [gitlab-owners@example.com]
</span></span></code></pre></div><p><em>domain.yaml</em></p>
<h2 id="the-control-plane">The Control Plane</h2>
<p>Platforms, Domains, and all of the resources contained within them are managed by the Konfig control plane. The control plane consumes these YAML definitions and does whatever is needed in GitLab and GCP to make the “real world” reflect the desired state specified in the configuration.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image2-1024x686.png"></p>
<p>The control plane manages the structure of groups and projects in GitLab and synchronizes this structure with GCP. This includes a number of other resources behind the scenes as well: configuring OpenID Connect to allow GitLab pipelines to authenticate with GCP, IAM resources like service accounts and role bindings, managing SAML group links to sync user permissions between GCP and GitLab, and enabling service APIs on the cloud projects. The Platform/Domain model allows the control plane to specify fine-grained permissions and scope access to only the things that need it. In fact, there are no credentials exposed to developers at all. It also allows us to manage what cloud services are available to developers and what level of access they have across the different environments. This governance is managed centrally but federated across both GitLab and GCP.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/04/image4-1024x718.png"></p>
<p>The net result is a configuration- and standards-driven foundation for your cloud development platform that spans your source control, CI/CD, and cloud provider environments. This foundation provides a golden path that makes it easy for developers to build and deliver software while meeting an organization’s internal controls, standards, or regulatory requirements. Now we’re ready to start delivering workloads to our enterprise cloud environment.</p>
<h2 id="managing-workloads-and-infrastructure">Managing Workloads and Infrastructure</h2>
<p>The Konfig control plane establishes an enterprise cloud environment in which we could use traditional IAC tools such as Terraform to manage our application infrastructure. However, the control plane is capable of much more than just managing the foundation. It can also manage the workloads that get deployed <em>to</em> this cloud environment. This is because Konfig actually consists of two components: <em>Konfig Platform</em>, which configures and manages our cloud platform comprising GitLab and GCP, and <em>Konfig Workloads</em>, which configures and manages application workloads and their respective infrastructure resources.</p>
<p>Using the Lego analogy, think of Konfig Platform as providing a pre-built factory and Konfig Workloads as providing pre-built assembly lines within the factory. You can use both in combination to get a complete, turnkey experience or just use Konfig Platform and “bring your own assembly line” such as Terraform.</p>
<p>Konfig Workloads provides an IAC alternative to Terraform where resources are managed by the control plane. Similar to how the platform-level components like GitLab and GCP are managed, this works by using an <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/">operator</a> that runs in the control plane cluster. This operator runs on a <a href="https://kubernetes.io/docs/concepts/architecture/controller/">control loop</a> which is constantly comparing the desired state of the system with the current state and performs whatever actions are necessary to reconcile the two. A simple example of this is the thermostat in your house. You set the temperature—the desired state—and the thermostat works to bring the actual room temperature—the current state—closer to the desired state by turning your furnace or air conditioner on and off. This model removes potential for state drift, where the actual state diverges from the configured state, which can be a major headache with tools like Terraform where state is managed with backends.</p>
<p>The Konfig UI provides a visual representation of the state of your system. This is useful for getting a quick understanding of a particular Platform, Domain, or workload versus reading through YAML that could be scattered across multiple files or repos (and which may not even be representative of what’s actually running in your environment). With this UI, we can easily see what resources a workload has configured and can access, the state of these resources (whether they are ready, still provisioning, or in an error state), and how the workload is configured across different environments. We can even use the UI itself to provision new resources like a database or storage bucket that are scoped automatically to the workload. This works by generating a merge request in GitLab with the desired changes, so while we can use the UI to configure resources, everything is still managed declaratively through IAC and GitOps. This is something we call “Visual IAC.”</p>
<p><a href="/wp-content/uploads/2024/04/konfig_ui.png"><img loading="lazy" src="/wp-content/uploads/2024/04/konfig_ui-1024x597.png"></a></p>
<h2 id="your-packaged-onramp-to-gcp-and-gitlab">Your Packaged Onramp to GCP and GitLab</h2>
<p>The current cloud landscape offers powerful tools, but assembling them efficiently, securely, and at scale remains a challenge. This “undifferentiated work” consumes valuable engineering resources that could be better spent on core business needs, and it often prevents organizations from even getting off the starting line when beginning their cloud journey. Konfig, built around the principles of Platform as Code and standards-driven development, addresses this very gap. We built it to help our clients move quicker through operationalizing the cloud so that they can focus on delivering business value to their customers. Whether you’re migrating to the cloud, modernizing, or starting from scratch, Konfig provides a preconfigured and opinionated integration of GitLab, GCP, and Infrastructure as Code which gives you:</p>
<ul>
<li>
<p><strong>Faster time-to-production:</strong> Streamlined setup minimizes infrastructure headaches and allows developers to focus on building and delivering software.</p>
</li>
<li>
<p><strong>Enterprise-grade security:</strong> Built-in security best practices and fine-grained access controls ensure your cloud environment remains secure.</p>
</li>
<li>
<p><strong>Governance:</strong> Platforms and Domains provide a flexible model that balances enterprise standards with team autonomy.</p>
</li>
<li>
<p><strong>Scalability:</strong> Designed to scale with your business, easily accommodating growth without compromising performance or efficiency.</p>
</li>
<li>
<p><strong>Great developer UX</strong>: Designed to provide a great user experience for developers shipping applications and services.</p>
</li>
</ul>
<p><a href="https://konfigurate.com/?utm_source=bravenewgeek.com&amp;utm_campaign=introducing-konfig">Konfig</a> functions like an operating system for your development organization to deliver software to the cloud. It’s an opinionated IDP specializing in cloud migrations and app modernization. This allows you to focus on what truly matters—building innovative software products and delivering exceptional customer experiences.</p>
<p>We’ve been leveraging these patterns and tools for years to help clients ship with confidence, and we’re excited to finally offer a solution that packages them up. <a href="https://konfigurate.com/contact?utm_source=bravenewgeek.com&amp;utm_campaign=introducing-konfig">Please reach out</a> if you’d like to learn more and see a demo. If you’re undertaking a modernization or cloud migration effort, we want to help make it a success. We’re looking for a few organizations to partner with to develop Konfig into a robust solution.</p>
]]></content:encoded></item><item><title>Choosing Good SLIs</title><link>https://bravenewgeek.com/choosing-good-slis/</link><pubDate>Mon, 19 Feb 2024 14:11:17 -0700</pubDate><guid>https://bravenewgeek.com/choosing-good-slis/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2024/02/dashboard-1024x671.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Transitioning from an on-prem environment to a cloud environment involves a lot of major shifts for organizations. One of those shifts is often around how we monitor the overall health of systems. The typical way to measure things like the availability, reliability, and performance of systems is with SLIs or &lt;a href="https://sre.google/sre-book/service-level-objectives/"&gt;Service Level Indicators&lt;/a&gt;. SLIs are a valuable tool both on-prem and in the cloud, but when it comes to the latter, I often see organizations carrying over some operational anti-patterns from their data center environment.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/wp-content/uploads/2024/02/dashboard-1024x671.jpg"></p>
<p>Transitioning from an on-prem environment to a cloud environment involves a lot of major shifts for organizations. One of those shifts is often around how we monitor the overall health of systems. The typical way to measure things like the availability, reliability, and performance of systems is with SLIs or <a href="https://sre.google/sre-book/service-level-objectives/">Service Level Indicators</a>. SLIs are a valuable tool both on-prem and in the cloud, but when it comes to the latter, I often see organizations carrying over some operational anti-patterns from their data center environment.</p>
<p>Unlike public clouds, data centers are often resource-constrained. Services run on dedicated sets of VMs and it can take days or weeks for new physical servers to be provisioned. Consequently, it’s common for organizations to closely monitor metrics such as CPU utilization, memory consumption, disk space, and so forth since these are all precious resources within a data center.</p>
<p>Often what happens is that ops teams get really good at identifying and pattern-matching the common issues that arise in their on-prem environment. For instance, certain applications may be prone to latency issues. Each time we dig into a latency issue we find that the problem is due to excessive garbage collection pauses. As a result, we define a metric around garbage collection because it is often an indicator of performance problems in the application. In practice, this becomes an SLI, whether it’s explicitly defined as such or not, because there is some sort of threshold beyond which garbage collection is considered “excessive.” We begin watching this metric closely to gauge whether the service is healthy or not and alerting on it.</p>
<p>The cloud is a very different environment than on-prem. Whether we’re using an orchestrator such as Kubernetes or a serverless platform, containers are usually ephemeral and instances autoscale up and down. If an instance runs out of memory, it will just get recycled. This is why we sometimes say you can “pay your way out” of a problem in these environments because autoscaling and autohealing can hide a lot of application issues such as a slow memory leak. In an on-prem environment, these can be significantly more impactful. The performance profile of applications often looks quite differently in the cloud than on-prem as well. Underlying hardware, tenancy, and networking characteristics differ considerably. All this is to say, things look and behave quite differently between the two environments, so it’s important to reevaluate operational practices as well. With SLIs and monitoring, it’s easy to bias toward specific indicators from on-prem, but they might not translate to more cloud-native environments.</p>
<h2 id="user-centric-monitoring">User-centric monitoring</h2>
<p>So how do we choose good SLIs? The key question to ask is: <em>what is the customer’s experience like?</em> Everything should be driven from this. Is the application responding slowly? Is it returning errors to the user? Is it returning bad or incorrect results? These are all things that directly impact the customer’s experience. Conversely, things that do <em>not</em> directly impact the customer’s experience are questions such as what is the CPU utilization of the service? The memory consumption? The rate of garbage collection cycles? These are all things that <em>could</em> impact the customer’s experience, but without actually looking from the user’s perspective, we simply don’t know whether they are or not. Rather, they are diagnostic tools that—once an issue is identified—can help us to better understand the underlying cause.</p>
<p>Take, for example, the CPU and memory utilization of processes on your computer. Most people probably are not constantly watching the Activity Monitor on their MacBook. Instead, they might open it up when they notice their machine is responding slowly to see what might be causing the slowness.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/02/activity_monitor-1024x704.png"></p>
<h2 id="three-key-metrics">Three key metrics</h2>
<p>When it comes to monitoring services, there are really three key metrics that matter: traffic rate, error rate, and latency. These three things all directly impact the user’s experience.</p>
<h3 id="traffic-rate">Traffic Rate</h3>
<p>Traffic rate, which is usually measured in requests or queries per second (qps), is important because it tells us if something is wrong upstream of us. For instance, our service might not be throwing any errors, but if it’s suddenly handling 0 qps when it ordinarily is handling 80-100 qps, then something happened upstream that we should know about. Perhaps there is a misconfiguration that is preventing traffic from reaching our service, which almost certainly impacts the user experience.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/02/traffic.png"></p>
<p>Traffic rate or qps for a service</p>
<h3 id="error-rate">Error Rate</h3>
<p>Error rate simply tells us the rate in which the service is returning errors to the client. If our service normally returns 200 responses but suddenly starts returning 500 errors, we know something is wrong. This requires good status code hygiene to be effective. I’ve encountered codebases where various types of error codes are used to indicate non-error conditions which can add a lot of noise to this type of SLI. Additionally, this metric might be more fine-grained than just “error” or “not error”, since—depending on the application—we might care about the rate of specific 2xx, 4xx, or 5xx responses, for example.</p>
<p>It’s common for teams to rely on certain error logs rather than response status codes for monitoring. This can provide even more granularity around types of error conditions, but in my experience, it usually works better to rely on fairly coarse-grained signals such as HTTP status codes for the purposes of aggregate monitoring and SLIs. Instead, use this logging for diagnostics and troubleshooting once you have identified there is a problem (I am, however, a fan of structured logging and log-based metrics for instrumentation but this is for another blog post).</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/02/status_codes.png"></p>
<p>Response codes for a service</p>
<h3 id="latency">Latency</h3>
<p>Combined with error rate, latency tells us what the customer’s experience is really like. This is an important metric for synchronous, user-facing APIs but might be less critical for asynchronous processes such as services that consume events from a message queue. It’s important to point out that when looking at latency, <a href="https://bravenewgeek.com/everything-you-know-about-latency-is-wrong/"><em>you cannot use averages</em></a>. This is a common trap I see ops teams and engineers fall into. Latency rarely follows a normal distribution, so relying on averages or medians to provide a summarized view of how a system is performing is folly.</p>
<p>Instead, we have to look at percentiles to get a better understanding of what the latency distribution looks like. Similarly, <a href="https://latencytipoftheday.blogspot.com/2014/06/latencytipoftheday-you-cant-average.html">you cannot average percentiles</a> either. It mathematically makes no sense, meaning you can’t, for instance, look at the average 90th percentile over some period of time. To summarize latency, we can plot multiple percentiles on a graph. Alternatively, <a href="https://www.brendangregg.com/HeatMaps/latency.html">heatmaps</a> can be an effective way to visualize latency because they can reveal useful details like distribution modes and outliers. For example, the heatmap below shows that the latency for this service is actually bimodal. Requests usually either respond in approximately 10 milliseconds or 1 second. This modality is not apparent in the line chart above the heatmap where we are only plotting the 50th, 95th, and 99th percentiles. The line chart does, however, show that latency ticked up a tiny bit around 10:10 AM following a severe spike in tail latency where the 99th percentile momentarily jumped over 4 seconds…curious.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/02/latency.png"></p>
<p>Latency distribution for a service as percentiles</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/02/latency_heatmap.png"></p>
<p>Latency distribution for a service as a heatmap</p>
<h2 id="identifying-other-slis">Identifying other SLIs</h2>
<p>While these three metrics are what I consider the critical baseline metrics, there may be other SLIs that are important to a service. For example, if our service is a cache, we might care about the freshness of data we’re serving as something that impacts the customer experience. If our service is queue-based, we might care about the time messages spend sitting in the queue.</p>
<p><img loading="lazy" src="/wp-content/uploads/2024/02/cache_age.png"></p>
<p>Heatmap showing the age distribution of data retrieved from a cache</p>
<p>Whatever the SLIs are, they should be things that directly matter to the user’s experience. If they aren’t, then at best they are a useful diagnostic or debugging tool and at worst they are just dashboard window dressing. Usually, though, they’re no use for proactive monitoring because it’s too much noise, and they’re no use for reactive debugging because it’s typically pre-aggregated data.</p>
<p>What’s worse is that when we focus on the wrong SLIs, it can lead us to take steps that actively harm the customer’s experience or simply waste our own time. A real-world example of this is when I saw a team that was actively monitoring garbage collection time for a service. They noticed one instance in particular appeared to be running more garbage collections than the others. While it appeared there were no obvious indicators of latency issues, timeouts, or out-of-memory errors that would actually impact the client, the team decided to redeploy the service in order to force instances to be recycled. This redeploy ended up having a much greater impact on the user experience than any of the garbage collection behavior ever did. The team also spent a considerable amount of time tuning various JVM parameters and other runtime settings, which ultimately had minimal impact.</p>
<p>Where lower-level metrics <em>can</em> provide value is with optimizing resource utilization and cloud spend. While the elastic nature of cloud may allow us to pay our way out of certain types of problems such as a memory leak, this can lead to inefficiency and waste long term. If we see that our service only utilizes 20% of its allocated CPU, we are likely overprovisioned and could save money. If we notice memory consumption consistently creeping up and up before hitting an out-of-memory error, we likely have a memory leak. However, it’s important to understand this distinction in use cases: SLIs are about gauging customer experience while these system metrics are for identifying optimizations and understanding long-term resource characteristics of your system. At any rate, I think it’s preferable to get a system to production with good monitoring in place, put <em>real</em> traffic on it, and <em>then</em> start to fine-tune its performance and resource utilization versus trying to optimize it beforehand through synthetic means.</p>
<p>Transitioning from an on-prem environment to the cloud necessitates a shift in how we monitor the health of systems. It’s essential to recognize and discard operational anti-patterns from traditional data center environments, where resource constraints often lead to a focus on specific metrics and behaviors. This can frequently lead to a sort of “overfitting” when monitoring cloud-based systems. The key to choosing good SLIs is by aligning them with the customer’s experience. Metrics such as traffic rate, error rate, and latency directly impact the user and provide meaningful insights into the health of services. By emphasizing these critical baseline metrics and avoiding distractions with irrelevant indicators, organizations can proactively monitor and improve the customer experience. Focusing on the right SLIs ensures that efforts are directed toward resolving actual issues that matter to users, avoiding pitfalls that can inadvertently harm user experience or waste valuable time. As organizations navigate the complexities of migrating to a cloud-native environment, a user-centric approach to monitoring remains fundamental to successful and efficient operations.</p>
<h2 id="need-help-making-the-transition">Need help making the transition?</h2>
<p>Real Kinetic helps organizations with their cloud migrations and implementing effective operations. If you have questions or need help getting started, <a href="https://realkinetic.com/#contact">we’d love to hear from you</a>. These emails come directly to us, and we respond to every one.</p>
]]></content:encoded></item><item><title>Cloud without Kubernetes</title><link>https://bravenewgeek.com/cloud-without-kubernetes/</link><pubDate>Mon, 12 Feb 2024 11:58:13 -0700</pubDate><guid>https://bravenewgeek.com/cloud-without-kubernetes/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2024/02/Kubernetes-or-Cloud-Run-1024x683.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;I think it’s safe to say Kubernetes has “won” the cloud mindshare game. If you look at the CNCF &lt;a href="https://landscape.cncf.io/"&gt;Cloud Native landscape&lt;/a&gt; (and manage to not go cross eyed), it seems like most of the projects are somehow related to Kubernetes. KubeCon is one of the fastest-growing industry events. Companies we talk to at Real Kinetic who are either preparing for or currently executing migrations to the cloud are centering their strategies around Kubernetes. Those already in the cloud are investing heavily in platform-izing their Kubernetes environment. Kubernetes competitors like Nomad, Pivotal Cloud Foundry, OpenShift, and Rancher have sort of just faded to the background (or simply pivoted to Kubernetes). In many ways, “cloud native” seems to be equated with “Kubernetes”.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/wp-content/uploads/2024/02/Kubernetes-or-Cloud-Run-1024x683.jpeg"></p>
<p>I think it’s safe to say Kubernetes has “won” the cloud mindshare game. If you look at the CNCF <a href="https://landscape.cncf.io/">Cloud Native landscape</a> (and manage to not go cross eyed), it seems like most of the projects are somehow related to Kubernetes. KubeCon is one of the fastest-growing industry events. Companies we talk to at Real Kinetic who are either preparing for or currently executing migrations to the cloud are centering their strategies around Kubernetes. Those already in the cloud are investing heavily in platform-izing their Kubernetes environment. Kubernetes competitors like Nomad, Pivotal Cloud Foundry, OpenShift, and Rancher have sort of just faded to the background (or simply pivoted to Kubernetes). In many ways, “cloud native” seems to be equated with “Kubernetes”.</p>
<p>All this is to say, the industry has coalesced around Kubernetes as <em>the way</em> to do cloud. But after working with enough companies doing cloud, watching their experiences, and understanding their business problems, I can’t help but wonder: <em>should it be?</em> Or rather, is Kubernetes actually the right level of abstraction?</p>
<h2 id="going-k8sless">Going k8sless</h2>
<p>While we’ve worked with a lot of companies doing Kubernetes, we’ve also worked with some that are deliberately <em>not</em>. Instead, they leaned into serverless—heavily—or as I like to call it, they’ve gone <em>k8sless</em>. These are not small companies or startups, they are name brands you would recognize.</p>
<p>At first, we were skeptical. Our team came from a <a href="https://www.workiva.com">company</a> that made it all the way to IPO using Google App Engine, one of the earliest serverless platforms available. We have regularly <a href="https://blog.realkinetic.com/getting-big-wins-with-small-teams-on-tight-deadlines-7602d3b878fa">espoused the benefits of serverless</a>. We’ve talked to clients about how they should consider it for their own workloads (often to great skepticism). But using <em>only</em> serverless? For once, <em>we</em> were the serverless skeptics. One client in particular was beginning a migration of their e-commerce platform to Google Cloud. They wanted to do it completely serverless. We gave our feedback and recommendations based on similar migrations we’ve performed:</p>
<p>“There are workloads that aren’t a good fit.”</p>
<p>“It would require major re-architecting.”</p>
<p>“It will be expensive once fully migrated.”</p>
<p>“You’ll have better cost efficiency bin packing lots of services into VMs with Kubernetes.”</p>
<p>We articulated all the usual arguments <a href="https://world.hey.com/dhh/don-t-be-fooled-by-serverless-776cd730">made by the serverless doubters</a>. Even Google was skeptical, echoing our sentiments to the customer. “Serious companies doing online retail like The Home Depot or Target are using Google Kubernetes Engine,” was more or less the message. We have a team of serverless experts at Real Kinetic though, so we forged ahead and helped execute the migration.</p>
<p>Fast forward nearly three years later and we will happily admit it: we were wrong. You <em>can</em> run a multibillion-dollar e-commerce platform without a single VM. You <em>don’t</em> have to do a full rewrite or major re-architecting. It <em>can</em> be cost-effective. It <em>doesn’t</em> require proprietary APIs or constraints that result in vendor lock-in. It might sound like an exaggeration, but it’s not.</p>
<h2 id="container-as-the-interface">Container as the interface</h2>
<p>Over the last several years, Google’s serverless offerings have evolved far beyond App Engine. It has reached the point where it’s now viable to run a wide variety of workloads without much issue. In particular, <a href="https://cloud.google.com/run">Cloud Run</a> offers many of the same benefits of a PaaS like App Engine without the constraints. If your code can run in a container, there’s a very good chance it will run on Cloud Run with little to no modification.</p>
<p>In fact, other than using the <a href="https://cloud.google.com/sdk/gcloud">gcloud CLI</a> to deploy a service, there’s nothing really Google- or Cloud Run-specific needed to get a functioning application. This is because Cloud Run uses <a href="https://knative.dev">Knative</a>, an open-source Kubernetes-based platform, as its deployment interface. And while Cloud Run is a Google-managed backend for the Knative interface, we could just as well switch the backend to GKE or our own Kubernetes cluster. When we implement our Cloud Run services, we actually implement them using a Kubernetes Deployment manifest, shown below, and right before deploying, we swap Deployment for Knative’s Service manifest.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apiVersion: apps/v1
</span></span><span class="line"><span class="cl">kind: Deployment
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    cloud.googleapis.com/location: us-central1
</span></span><span class="line"><span class="cl">    service: my-service
</span></span><span class="line"><span class="cl">  name: my-service
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  template:
</span></span><span class="line"><span class="cl">    spec:
</span></span><span class="line"><span class="cl">      containers:
</span></span><span class="line"><span class="cl">        - image: us.gcr.io/my-project/my-service:v1
</span></span><span class="line"><span class="cl">          name: my-service
</span></span><span class="line"><span class="cl">          ports:
</span></span><span class="line"><span class="cl">            - containerPort: 8080
</span></span><span class="line"><span class="cl">          resources:
</span></span><span class="line"><span class="cl">            limits:
</span></span><span class="line"><span class="cl">              cpu: 2
</span></span><span class="line"><span class="cl">              memory: 1024Mi
</span></span></code></pre></div><p>This means we can deploy to Kubernetes without Knative at all, which we often do during development using the combination of <a href="https://skaffold.dev">Skaffold</a> and <a href="https://k3s.io">K3s</a> to perform local testing. It also allows us to use Kubernetes native tooling such as <a href="https://kustomize.io">Kustomize</a> to manage configuration. Think of Cloud Run as a Kubernetes Deployment as a service (though really more like Deployment <em>and</em> Service…as a service).</p>
<h2 id="normal-businesses-versus-internet-scale-businesses">“Normal” businesses versus internet-scale businesses</h2>
<p>What about cost? Yes, the unit cost in terms of compute is higher with serverless. If you execute enough CPU cycles to fill the capacity of a VM, you are better off renting the whole VM as opposed to effectively renting timeshares of it. But here’s the thing: most “normal” businesses tend to have highly cyclical traffic patterns throughout the day and their scale is generally modest.</p>
<p>What do I mean by “normal” businesses? These are primarily non-internet-scale companies such as insurance, fast food, car rental, construction, or financial services, not Google, Netflix, or Amazon. As a result, these companies can benefit greatly from pay-per-use, and those in the retail space also benefit greatly from the elasticity of this model during periods like Black Friday or promotional campaigns. Businesses with brick-and-mortar have traffic that generally follows their operating hours. During off-hours, they can often scale quite literally to zero.</p>
<p>Many of these businesses, for better or worse, treat software development as an IT cost center to be managed. They don’t need—or for that matter, <em>want</em>—the costs and overheads associated with platform-izing Kubernetes. A lot of the companies we interact with fall into this category of “normal” businesses, and I suspect most companies outside of tech do as well.</p>
<h2 id="byopbring-your-own-platform">BYOP—Bring Your Own Platform</h2>
<p><a href="https://bravenewgeek.com/there-and-back-again-why-paas-is-passe-and-why-its-not/">I’ve asked it before</a>: is Kubernetes really the end-game abstraction? In my opinion, it’s an implementation detail. <a href="https://twitter.com/QuinnyPig/status/1093261169614356490">I don’t think I’m alone in that opinion</a>. Some companies put a tremendous amount of investment into abstracting Kubernetes from their developers. This is what I mean by “platform-izing” Kubernetes. It typically involves significant and ongoing OpEx investment. The industry has started to coalesce around two concepts that encapsulate this: <a href="https://blog.realkinetic.com/productize-your-engineering-organizations-internal-tools-25fd2cbe3fb0">Platform Engineering</a> and <a href="https://internaldeveloperplatform.org/">Internal Developer Platform</a>. So while Kubernetes may have become the default container orchestrator, the higher-level pieces—the pieces constituting the Internal Developer Platform—are still very much bespoke. Kelsey Hightower <a href="https://twitter.com/kelseyhightower/status/851935087532945409">said it best</a>: the majority of people managing infrastructure just want a PaaS. The only requirement: <em>it has to be built by them</em>. That’s a problem.</p>
<p>Imagine having a Kubernetes cluster per Deployment. Full blast radius isolation, complete cost traceability, granular yet simple permissioning. It sounds like a maintenance nightmare though, right? Now imagine those clusters just being hidden from you completely and the Deployment itself is the only thing you interact with and maintain. You just provide your container (or group of containers), configure your CPU and memory requirements, specify the network and resource access, and deploy it. The Deployment manages your load balancing and ingress, automatically scales the pods up and down or canaries traffic, and gives you aggregated logs and metrics out of the box. You only pay for the resources consumed while processing a request. Just a few years ago, this was a <a href="https://twitter.com/kelseyhightower/status/960600001213751296">futuristic-sounding fantasy</a>.</p>
<p><a href="https://twitter.com/kelseyhightower/status/960600001213751296"><img loading="lazy" src="/wp-content/uploads/2024/02/kelsey_hightower_tweet.png"></a></p>
<p>The platform Kelsey describes above <em>does</em> now exist. From my experience, it’s a nearly ideal solution for those “normal” businesses who are looking to minimize complexity and operational costs and avoid having to bring (more like <em>build</em>) their own platform. I realize GCP is a distant third when it comes to public cloud market share so this will largely fall on deaf ears, but for those who are still listening: <em>stop wasting time on Kubernetes and just use Cloud Run</em>. Let me expand on the reasons why.</p>
<ol>
<li>
<p><strong>Easily and quickly get started with the cloud.</strong> Many of the companies we work with who are still in the midst of migrating to the cloud get hung up with analysis paralysis. Cloud Run isn’t a perfect solution for everything, but it’s good enough for the majority of cases. The rest can be handled as exceptions.</p>
</li>
<li>
<p><strong>Minimize complexity of cloud environments.</strong> Cloud Run does not eliminate the need for infrastructure (there are still caches, queues, databases, and so forth), but it greatly simplifies it. Using managed services for the remaining infrastructure pieces simplifies it further.</p>
</li>
<li>
<p><strong>Increase the efficiency of your developers and reduce operational costs.</strong> Rather than spending most of their time dealing with infrastructure concerns, allow your developers to focus on delivering business value. For most businesses, infrastructure is undifferentiated commodity work. By “outsourcing” large parts of your undifferentiated Internal Developer Platform, you can reallocate developers to product or feature development and reduce operational costs. This allows you to get the benefits of Platform Engineering with a fraction of the maintenance and overhead. Lastly, if you are a “normal” business that doesn’t operate at internet scale and has fairly cyclical traffic, it’s entirely likely Cloud Run will be <em>cheaper</em> than VM-based platforms.</p>
</li>
<li>
<p><strong>Maintain the flexibility to evolve to a more complex solution over time if needed.</strong> This is where traditional serverless platforms and PaaS solutions fall short. Again, with Cloud Run there is no actual vendor lock-in, it’s just a Kubernetes Deployment as a Service. Even without Knative, we can take that Deployment and run it in any Kubernetes cluster. This is a very different paradigm from, say, App Engine where you wrote your application using App Engine APIs and deployed your service to the App Engine runtime. In this new paradigm, the artifact is a Plain Old Container. There are cases where Cloud Run is <em>not</em> a good fit, such as certain kinds of stateful legacy applications or services with sustained, non-cyclical traffic. We don’t want to be painted into a corner with these types of situations so having flexibility is important.</p>
</li>
</ol>
<p>There are similar analogs to Cloud Run on other cloud platforms. For example, AWS has <a href="https://aws.amazon.com/apprunner/">AppRunner</a>. However, in my experience these fall short in terms of developer experience because of either lack of investment from the cloud provider or environment complexity (as I would argue is the case for AWS). Managed services like Cloud Run are one of the areas that GCP truly excels and <a href="https://blog.realkinetic.com/gcp-and-aws-whats-the-difference-3b1329f0ffb3">differentiates itself</a>.</p>
<h2 id="just-use-cloud-run-seriously">Just use Cloud Run, seriously</h2>
<p>I realize not everyone will be convinced. The gravitational pull of Kubernetes is strong and as a platform, it’s a safe bet. However, operationalizing Kubernetes properly—whether it’s a managed offering like GKE or not—requires some kind of platform team and ongoing investment. We’ve seen it approached <em>without</em> this where developers are given clusters or allowed to spin them up and fend for themselves. This quickly becomes untenable because standards are non-existent, security and compliance is unmanageable, and developer time is split between managing infrastructure and actual feature development.</p>
<p>If your organization is unable or unwilling to make this investment, I urge you to consider Cloud Run. There’s still work needed on the periphery to properly operationalize it, such as implementing CI/CD pipelines and managing accessory infrastructure, but it’s a much lower investment. Additionally, it provides an escape hatch—unlike App Engine or traditional PaaS solutions, there is no real switching cost in moving to Kubernetes if you need to in the future. With Cloud Run, serverless has finally reached a tipping point where it’s now viable for a majority of workloads rather than a niche subset. Unlike Kubernetes, it provides the right level of abstraction for most businesses building software. In my opinion, serverless is still not taken seriously due to preconceived notions, but it’s time to start reevaluating those notions.</p>
<p>Agree? Disagree? I’d love to hear your thoughts. If you’re an organization that would like to do cloud differently or are looking for the playbook to operationalize Google Cloud Platform, please <a href="https://realkinetic.com/#contact">get in touch</a>.</p>
]]></content:encoded></item><item><title>Meeting notes lose value the moment you finish writing them—and it’s time to fix that</title><link>https://bravenewgeek.com/meeting-notes-lose-value-the-moment-you-finish-writing-them-and-its-time-to-fix-that/</link><pubDate>Fri, 10 Jun 2022 10:25:44 -0600</pubDate><guid>https://bravenewgeek.com/meeting-notes-lose-value-the-moment-you-finish-writing-them-and-its-time-to-fix-that/</guid><description>&lt;p&gt;I like to be prepared in meetings. In some ways it’s probably an innate part of my personality, but it also became more important to me as my role has changed throughout my career. In particular, the first time I became an engineering manager is when I started to become a more diligent notetaker and meeting preparer. I think this is largely because my job shifted from being output-centric to more people- and meeting-centric. I still took notes and prepared when I was a software engineer, but it was for a very different context and purpose. As an engineer, my work centered around code output. As a manager, my work instead centered around coordinating, following up, and supporting my team. If you’ve never worked as a manager before, this probably just sounds like paper-pushing, but it’s actually a lot of work—and important! The work product is just &lt;em&gt;different&lt;/em&gt; from that of an individual contributor.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I like to be prepared in meetings. In some ways it’s probably an innate part of my personality, but it also became more important to me as my role has changed throughout my career. In particular, the first time I became an engineering manager is when I started to become a more diligent notetaker and meeting preparer. I think this is largely because my job shifted from being output-centric to more people- and meeting-centric. I still took notes and prepared when I was a software engineer, but it was for a very different context and purpose. As an engineer, my work centered around code output. As a manager, my work instead centered around coordinating, following up, and supporting my team. If you’ve never worked as a manager before, this probably just sounds like paper-pushing, but it’s actually a lot of work—and important! The work product is just <em>different</em> from that of an individual contributor.</p>
<p>When I became a manager, I began taking meeting notes in a small Moleskine notebook. For every meeting, I’d write down the meeting name and the date. I would try to jot down salient points or context, questions, things I wanted to follow up on, or action items I needed to do or delegate. As you can see below, it’s messy. <em>Really</em> messy. It never felt like a particularly good solution. It was hard to find things, hard to pluck out the important action items or follow-ups, hard to even remember who was in a meeting without cross-referencing my calendar. Not to mention my terrible handwriting meant even just <em>reading</em> my own notes was difficult.</p>
<p><img loading="lazy" src="/wp-content/uploads/2022/06/notebook.jpeg"></p>
<p>An interesting thing about the human brain is that it’s inherently selfish—that is, it’s really good at remembering things that are important to <em>us</em>. The things that are top of mind are probably not things I need to actually write down to remember. But most managers are likely getting pulled in a lot of different directions with a lot of different asks that are all competing for those limited brain cycles. Really good managers seem to have a special knack for juggling all of these things. It’s also why you often hear managers talk about how tiring their job is even though it seems like all they do is go to meetings!</p>
<p>The hard truth about my note-taking system is that I would take a lot of notes, write a lot of action items, and feel really productive in my meetings. <em>Then I would proceed to never look at those notes again.</em> Partly because of the chaos of meeting-packed days week after week, but also because it’s just hard to derive value from notes. Countless times a topic or question would come up in a discussion where I <em>knew</em> I had notes from a previous meeting about it, but it was just impossible to actually find anything in a notebook full of hastily scribbled notes. And by the time you find it, the conversation has moved on. You know how people say your new car loses its value the moment you drive off the lot? Your meeting notes lose value the moment you finish writing them.</p>
<p>This leads to another interesting thing about the human brain—it’s pretty good at organizing memories around time and people. “I remember talking to Joe about managing our cloud costs last week in our weekly cloud strategy meeting”—that sort of thing. And while my notebook provided a chronological ordering of my meeting notes, it wasn’t really conducive to recalling important information quickly or managing my to-do list.</p>
<p>A software engineer’s job often involves coordinating across different software systems, but their to-do list likely consists of things along the lines of “do X.” This is why tools like Jira or Asana exist, to manage the backlog of X’s that need to be done and provide visibility for the people coordinating those X’s.</p>
<p>A manager’s job involves coordinating across a different kind of system—<em>people</em>. A manager’s to-do list is going to consist mostly of things like “talk to Y about Z.” Again, the work product is different. It’s about making sure there is alignment and lines of communication between various people or teams. Your work shifts from being a do-er to a delegate-er and communicator. This kind of work is not managed in Jira or displayed in a Gantt chart. It’s often not managed <em>anywhere</em> except perhaps scribbled in the depths of a Moleskine notebook or tucked away in the corner of a meeting-fatigued brain.</p>
<p>Nevertheless, I carried on with my note-taking system of questionable value, even after transitioning back to an individual contributor role. It wasn’t until I started consulting that I had a realization. With consulting, I work with a lot of different people across a lot of different projects across a lot of different clients. The type of consulting we do at <a href="https://realkinetic.com">Real Kinetic</a> is very discussional in nature. While we have deliverables, most of our work product is in the form of discussion, guidance, recommendations, coaching, and helping organizations with their own communication challenges. It’s not work that can be managed in a traditional task-management system. Instead, it’s much like the manager’s work of connecting threads of conversation across meetings and people and juggling lots of asks from clients.</p>
<p><img loading="lazy" src="/wp-content/uploads/2022/06/threads.png"></p>
<p>For example, in a meeting with John I might realize we need to connect with Rachel to talk about strategies for improving development velocity. Sure, you could maybe put “Talk to Rachel about dev velocity” into a Trello card or a to-do list app but in doing so it <em>loses the surrounding context</em>. And for a role that is more discussion-oriented than task-oriented, the context is important. Not only that, but tools like Trello or Todoist are just not really designed for this purpose. They are meant more for the do-ers, not the delegate-ers or communicators. They are clunky to use for someone whose job consists mostly of being in meetings and talking to people day in and day out. This is the challenge with productivity apps—most of them are centered around task management and task collaboration. And actual note-taking apps like Evernote are definitely not designed to solve this because they are intended to replace my Moleskine notebook filled with notes I will never look at again.</p>
<p>Now, coming back to my realization: I realized that my meeting notes were not valuable in and of themselves. Rather, they were the <em>medium</em> for my meeting-centric work management. Unfortunately, my notebook was not a great solution, nor was Evernote, nor Google Docs.</p>
<p>What I was really looking for was a sort of to-do list oriented around people and meetings and driven from my meeting notes. Not something centered around task management or collaboration or notes as being anything other than incidental to the process. Instead, I was looking for a tool that could synthesize my notes into something valuable and actionable <em>for me</em>. And I never found it, which is why we ended up creating <a href="https://witful.com/">Witful</a>.</p>
<p>The idea behind Witful is a productivity app for the people whose jobs revolve around, well, <em>people</em>. It turns your meeting notes into something much more valuable. Now, I can take my meeting notes similar to how I used to, but rather than important items falling by the wayside, those items are surfaced to me. Witful tells me if I need to prepare for an upcoming meeting, if there are takeaways from a meeting I need to follow up on, or action items I need to address. And much like the way our brain organizes information, Witful indexes all of my meeting-related content around my meetings, the people in those meetings, and time, making it easy to quickly recall information.</p>
<p><img loading="lazy" src="/wp-content/uploads/2022/06/witful_screenshot-1024x847.png"></p>
<p>Witful has not radically altered the way I approach meetings. Instead, what it’s done is <em>augmented</em> my previous workflow. It gives me a central place for all my meeting notes, much like the Moleskine notebook did, except it lets me extract much more value from those notes. This has helped me with my consulting work because it has given me the same uncanny knack for juggling lots of things that those really good managers I’ve worked with seem to have. If you’re not a meeting note-taker, Witful might not be for you. If you are and your current system has never felt quite right, you’d like to get more value out of your notes, or you’re looking for a meeting-centric work management system, you should give it a shot.</p>
]]></content:encoded></item><item><title>SRE Doesn’t Scale</title><link>https://bravenewgeek.com/sre-doesnt-scale/</link><pubDate>Wed, 06 Oct 2021 09:44:55 -0600</pubDate><guid>https://bravenewgeek.com/sre-doesnt-scale/</guid><description>&lt;p&gt;&lt;a href="https://bravenewgeek.com/wp-content/uploads/2021/10/sre-book.jpeg"&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2021/10/sre-book.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We encounter &lt;em&gt;a lot&lt;/em&gt; of organizations talking about or attempting to implement SRE as part of our consulting at Real Kinetic. We’ve even discussed and debated ourselves, ad nauseam, how we can apply it at our own product company, &lt;a href="https://witful.com/"&gt;Witful&lt;/a&gt;. There’s a brief, unassuming section in the &lt;a href="https://sre.google/sre-book/table-of-contents/"&gt;SRE book&lt;/a&gt; tucked away towards the tail end of &lt;a href="https://sre.google/sre-book/evolving-sre-engagement-model/"&gt;chapter 32&lt;/a&gt;, “The Evolving SRE Engagement Model.” Between the SLIs and SLOs, the error budgets, alerting, and strategies for handling change management, it’s probably one of the most overlooked parts of the book. It’s also, in my opinion, one of the most important.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="/wp-content/uploads/2021/10/sre-book.jpeg"><img loading="lazy" src="/wp-content/uploads/2021/10/sre-book.jpeg"></a></p>
<p>We encounter <em>a lot</em> of organizations talking about or attempting to implement SRE as part of our consulting at Real Kinetic. We’ve even discussed and debated ourselves, ad nauseam, how we can apply it at our own product company, <a href="https://witful.com/">Witful</a>. There’s a brief, unassuming section in the <a href="https://sre.google/sre-book/table-of-contents/">SRE book</a> tucked away towards the tail end of <a href="https://sre.google/sre-book/evolving-sre-engagement-model/">chapter 32</a>, “The Evolving SRE Engagement Model.” Between the SLIs and SLOs, the error budgets, alerting, and strategies for handling change management, it’s probably one of the most overlooked parts of the book. It’s also, in my opinion, one of the most important.</p>
<p>Chapter 32 starts by discussing the “classic” SRE model and then, towards the end, how Google has been evolving beyond this model. “External Factors Affecting SRE”, under the “Evolving Services Development: Frameworks and SRE Platform” heading, is the section I’m referring to specifically. This part of the book details challenges and approaches for scaling the SRE model described in the preceding chapters. This section describes Google’s own shift towards the industry trend of microservices, the difficulties that have resulted, and what it means for SRE. Google implements a robust site reliability program which employs a small army of SREs who support some of the company’s most critical systems and engage with engineering teams to improve the reliability of their products and services. The model described in the book has proven to be highly effective for Google but is also quite resource-intensive. Microservices only serve to multiply this problem. The organizations we see attempting to adopt microservices along with SRE, particularly those who are doing it as a part of a move to cloud, frequently underestimate just how much it’s about to ruin their day in terms of thinking about software development and operations.</p>
<p>It is not going from a monolith to a handful of microservices. It ends up being <em>hundreds</em> of services or more, even for the smaller companies. This happens every single time. And that move to microservices—<em>in combination with cloud</em>—unleashes a whole new level of autonomy and empowerment for developers who, often coming from a more restrictive ops-controlled environment on prem, introduce all sorts of new programming languages, compute platforms, databases, and other technologies. The move to microservices and cloud is nothing short of a <em>Cambrian Explosion</em> for just about every organization that attempts it. I have never seen this <em>not</em> play out to some degree, and it tends to be highly disruptive. Some groups handle it well—others do not. Usually, however, this brings an organization’s delivery to a grinding halt as they try to get a handle on the situation. In some cases, I’ve seen it take a year or more for a company to actually start delivering products in the cloud after declaring they are “all in” on it. And that’s just the process of <em>starting</em> to deliver, not actually delivering them.</p>
<p>How does this relate to SRE? In the book, Google says a result of moving towards microservices is that both the number of requests for SRE support and the cardinality of services to support have increased dramatically. Because each service has a base fixed operational cost, even simple services demand more staffing. Additionally, microservices <em>almost always</em> imply an expectation of lower lead time for deployment. This is invariably one of the reasons we see organizations adopting them in the first place. This reduced lead time was not possible with the Production Readiness Review model they describe earlier in chapter 32 because it had a lead time of <em>months</em>. For many of the organizations we work with, a lead time of months to deliver new products and capabilities to their customers is simply not viable. It would be like rewinding the clock to when they were still operating on prem and completely defeat the purpose of microservices and cloud.</p>
<p>But here’s the key excerpt from the book: “Hiring experienced, qualified SREs is difficult and costly. Despite enormous effort from the recruiting organization, there are never enough SREs to support all the services that need their expertise.” The authors conclude, “the SRE organization is responsible for serving the needs of the large and growing number of development teams that do not already enjoy direct SRE support. This mandate calls for extending the SRE support model far beyond the original concept and engagement model.”</p>
<p>Even Google, who has infinite money and an endless recruiting pipeline, says the SRE model—as it is often described by the people we encounter referencing the book—does not scale with microservices. Instead, they go on to describe a more tractable, framework-oriented model to address this through things like codified best practices, reusable solutions, standardization of tools and patterns, and, more generally, what I describe as the <a href="https://speakerdeck.com/tylertreat/the-future-of-ops">“productization” of infrastructure and operations</a>.</p>
<p>Google enforces standards and opinions around things like programming languages, instrumentation and metrics, logging, and control systems surrounding traffic and load management. The alternative to this is the Cambrian Explosion I described earlier. The authors enumerate the benefits of this approach such as significantly lower operational overhead, universal support by design, faster and lower overhead SRE engagements, and a new engagement model based on shared responsibility rather than either full SRE support or no SRE support. As the authors put it, “This model represents a significant departure from the way service management was originally conceived in two major ways: it entails a new relationship model for the interaction between SRE and development teams, and a new staffing model for SRE-supported service management.”</p>
<p>For some reason, this little detail gets lost and, consequently, we see groups attempting to throw people at the problem, such as embedding an SRE on each team. In practice, this usually means two things: 1) hiring a whole bunch of SREs—which even <em>Google</em> admits to being difficult and costly—and 2) this person typically just becomes the “whipping boy” for the team. More often than not, this individual is some poor ops person who gets labeled “SRE.”</p>
<p>With microservices, which again almost always hit you with a near-exponential growth rate once you adopt them, you simply cannot expect to have a handful of individuals who are tasked with understanding the entirety of a microservice-based platform <em>and be responsible for it</em>. SRE does not mean developers get to just go back to thinking about code and features. Microservices <em>necessitate</em> developers having skin in the game, and even Google has talked about the challenges of scaling a traditional SRE model and why a different tack is needed.</p>
<p>“The constant growth in the number of services at Google means that most of these services can neither warrant SRE engagement nor be maintained by SREs. Regardless, services that don’t receive full SRE support can be built to use production features that are developed and maintained by SREs. This practice effectively breaks the SRE staffing barrier. Enabling SRE-supported production standards and tools for all teams improves the overall service quality across Google.”</p>
<p>My advice is to stop thinking about SRE as an implementation specifically and instead think about the problems it’s solving a bit more abstractly. It’s unlikely your organization has Google-level resources, so you need to consider the constraints. You need to think about the roles and responsibilities of developers as well as your ops folks. They will change significantly with microservices and cloud out of necessity. You’ll need to think about how to <a href="https://bravenewgeek.com/scaling-devops-and-the-revival-of-operations/">scale DevOps</a> within your organization and, as part of that, what “DevOps” actually <em>means</em> to your organization. In fact, many groups are probably better off simply <em>removing</em> “SRE” and “DevOps” from their vocabulary altogether because they often end up being distracting buzzwords. For most mid-to-large-sized companies, some sort of framework- and platform- oriented model is usually needed, similar to what Google describes.</p>
<p>I’ve seen it over and over. This hits companies like a ton of bricks. It requires looking at some hard org problems. A lot of self-reflection that many companies find uncomfortable or just difficult to do. But it has to be done. It’s also an important piece of context when applying the SRE book. Don’t skip over chapter 32. It might just be the most important part of the book.</p>
<hr>
<p><em>Real Kinetic helps clients build great engineering organizations.</em> <a href="https://realkinetic.com/"><em>Learn more</em></a> <em>about working with us.</em></p>
]]></content:encoded></item><item><title>Structuring a Cloud Infrastructure Organization</title><link>https://bravenewgeek.com/structuring-a-cloud-infrastructure-organization/</link><pubDate>Mon, 07 Dec 2020 11:21:46 -0600</pubDate><guid>https://bravenewgeek.com/structuring-a-cloud-infrastructure-organization/</guid><description>&lt;p&gt;Real Kinetic often works with companies just beginning their cloud journey. Many come from a conventional on-prem IT organization, which typically looks like separate development and IT operations groups. One of the main challenges we help these clients with is how to structure their engineering organizations effectively as they make this transition. While we approach this problem holistically, it can generally be looked at as two components: product development and infrastructure. One might wonder if this is still the case with the shift to DevOps and cloud, but as we’ll see, these two groups still play important and distinct roles.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Real Kinetic often works with companies just beginning their cloud journey. Many come from a conventional on-prem IT organization, which typically looks like separate development and IT operations groups. One of the main challenges we help these clients with is how to structure their engineering organizations effectively as they make this transition. While we approach this problem holistically, it can generally be looked at as two components: product development and infrastructure. One might wonder if this is still the case with the shift to DevOps and cloud, but as we’ll see, these two groups still play important and distinct roles.</p>
<p>We help clients understand and embrace the notion of a <em>product mindset</em> as it relates to software development. This is a fundamental shift from how many of these companies have traditionally developed software, in which development was viewed as an IT partner beholden to the business. This transformation is <a href="https://blog.realkinetic.com/digitally-transformed-becoming-a-technology-product-company-2e7978fbcc81">something I’ve discussed at length</a> and will not be the subject of this conversation. Rather, I want to spend some time talking about the other side of the coin: operations.</p>
<h2 id="operations-in-the-cloud">Operations in the Cloud</h2>
<p>While I’ve <a href="https://blog.realkinetic.com/scaling-devops-and-the-revival-of-operations-d647ba6e2374">talked</a> about operations in the context of cloud before, it’s only been in broad strokes and not from a concrete, organizational perspective. Those discussions don’t really get to the heart of the matter and the question that so many IT leaders ask: what does an operations organization look like in the cloud?</p>
<p>This, of course, is a highly subjective question to which there is no “right” answer. This is doubly so considering that every company and culture is different. I can only humbly offer my opinion and answer with what I’ve seen work in the context of particular companies with particular cultures. Bear this in mind as you think about your own company. More often than not, the cultural transformation is more arduous than the technology transformation.</p>
<p>I should also caveat that—outside of being a strategic instrument—Real Kinetic is not in the business of simply helping companies lift-and-shift to the cloud. When we do, it’s always with the intention of modernizing and adapting to more cloud-native architectures. Consequently, our clients are not usually looking to merely replicate their current org structure in the cloud. Instead, they’re looking to tailor it appropriately.</p>
<h2 id="defining-lines-of-responsibility">Defining Lines of Responsibility</h2>
<p>What should developers need to understand and be responsible for? There tend to be two schools of thought at two different extremes when it comes to this depending on peoples’ backgrounds and experiences. Oftentimes, developers will want more control over infrastructure and operations, having come from the constraints of a more siloed organization. On the flip side, operations folks and managers will likely be more in favor of having a separate group retain control over production environments and infrastructure for various reasons—efficiency, stability, and security to name a few. Not to mention, there are a lot of operational concerns that many developers are likely not even aware of—the sort of unsung, unglamorous bits of running software.</p>
<p>Ironically, both models can be used as an argument for “DevOps.” There are also cases to be made for either. The developer argument is better delivery velocity and innovation at a <em>team</em> level. The operations argument is better stability, risk management, and cost control. There’s also likely more potential for better consistency and throughput at an <em>organization</em> level.</p>
<p>The answer, unsurprisingly, is a combination of both.</p>
<p>There is an inherent tension between empowering developers and running an efficient organization. We want to give developers the flexibility and autonomy they need to develop good solutions and innovate. At the same time, we also need to realize the operational efficiencies that common solutions and standardization provide in order to benefit from economies of scale. Should every developer be a generalist or should there be specialists?</p>
<p>Real Kinetic helps clients adopt a model we refer to as “<a href="https://blog.realkinetic.com/operations-in-the-world-of-developer-enablement-6a54f5b8d4f5">Developer Enablement</a>.” The idea of Developer Enablement is shifting the focus of ops teams from being “masters” of production to “enablers” of production by applying a product lens to operations. In practical terms, this means less running production workloads on behalf of developers and more providing tools and products that allow developers to run workloads themselves. It also means thinking of operations less as a task-driven service model and more as a strategic enabler. However, Developer Enablement is <em>not</em> about giving full autonomy to developers to do as they please, it’s about providing the abstractions they need to be successful on the platform while realizing the operational efficiencies possible in a larger organization. This means providing common tooling, products, and patterns. These are developed in partnership with product teams so that they meet the needs of the organization. Some companies might refer to this as a “platform” team, though I think this has a slightly different meaning. So how does this map to an actual organization?</p>
<h2 id="mapping-out-an-engineering-organization">Mapping Out an Engineering Organization</h2>
<p>First, let’s mentally model our engineering organization as two groups: Product Development and Infrastructure and Reliability. The first is charged with developing products for end users and customers. This is the stuff that makes the business money. The second is responsible for supporting the first. This is where the notion of “developer enablement” comes into play. And while this group isn’t necessarily doing work that is directly strategic to the business, it is work that is critical to providing efficiencies and keeping the lights on just the same. This would traditionally be referred to as Operations.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/12/engineering_organization.jpg"></p>
<p>As mentioned above, the focus of this discussion is the green box. And as you might infer from the name, this group is itself composed of two subgroups. Infrastructure is about enabling product teams, and Reliability is about providing a first line of defense when it comes to triaging production incidents. This latter subgroup is, in and of itself, its own post and worthy of a separate discussion, so we’ll set that aside for another day. We are really focused on what a cloud <em>infrastructure</em> organization might look like. Let’s drill down on that piece of the green box.</p>
<h2 id="an-infrastructure-organization-model">An Infrastructure Organization Model</h2>
<p>When thinking about organization structure, I find that it helps to consider <em>layers of operational concern</em> while mapping the ownership of those concerns. The below diagram is an example of this. Note that these do not necessarily map to specific team boundaries. Some areas may have overlap, and responsibilities may also shift over time. This is mostly an exercise to identify key organizational needs and concerns.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/12/layers_of_operational_concern.jpg"></p>
<p>We like to model the infrastructure organization as three teams: Developer Productivity, Infrastructure Engineering, and Cloud Engineering. Each team has its own charter and mission, but they are all in support of the overarching objective of enabling product development efficiently and at scale. In some cases, these teams consist of just a handful of engineers, and in other cases, they consist of dozens or hundreds of engineers depending on the size of the organization and its needs. These team sizes also <em>change</em> as the priorities and needs of the company evolve over time.</p>
<h3 id="developer-productivity">Developer Productivity</h3>
<p>Developer Productivity is tasked with getting ideas from an engineer’s brain to a deployable artifact as efficiently as possible. This involves building or providing solutions for things like CI/CD, artifact repositories, documentation portals, developer onboarding, and general developer tooling. This team is primarily an <em>engineering spend multiplier</em>. Often a small Developer Productivity team can create a great deal of leverage by providing these different tools and products to the organization. Their core mandate is reducing friction in the delivery process.</p>
<h3 id="infrastructure-engineering">Infrastructure Engineering</h3>
<p>The Infrastructure Engineering team is responsible for making the process of getting a deployable artifact to production and managing it as painless as possible for product teams. Often this looks like providing an “opinionated platform” on top of the cloud provider. Completely opening up a platform such as AWS for developers to freely use can be problematic for larger organizations because of cost and time inefficiencies. It also makes security and compliance teams’ jobs much more difficult. Therefore, this group must walk the fine line between providing developers with enough flexibility to be productive and move fast while ensuring aggregate efficiencies to maintain organization-wide throughput as well as manage costs and risk. This can look like providing a Kubernetes cluster as a service with opinions around components like load balancing, logging, monitoring, deployments, and intra-service communication patterns. Infrastructure Engineering should also provide tooling for teams to manage production services in a way that meets the organization’s regulatory requirements.</p>
<p>The question of ownership is important. In some organizations, the Infrastructure Engineering team may own and operate infrastructure services, such as common compute clusters, databases, or message queues. In others, they might simply provide opinionated guard rails around these things. Most commonly, it is a combination of both. Without this, it’s easy to end up with every team running their own unique messaging system, database, cache, or other piece of infrastructure. You’ll have lots of <a href="https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-scare-you/">architecture astronauts</a> on your hands, and they will need to be able to answer questions around things like high availability and disaster recovery. This leads to significant inefficiencies and operational issues. Even if there isn’t shared infrastructure, it’s valuable to have an opinionated set of technologies to consolidate institutional knowledge, tooling, patterns, and practices. This doesn’t have to act as a hard-and-fast rule, but it means teams should be able to make a good case for operating outside of the guard rails provided.</p>
<p>This model is different from traditional operations in that it takes a product-mindset approach to providing solutions to internal customers. This means it’s important that the group is able to understand and empathize with the product teams they serve in order to identify areas for improvement. It also means productizing and automating traditional operations tasks while encouraging good patterns and practices. This is a radical departure from the way in which most operations teams normally operate. It’s closer to how a product development team should work.</p>
<p>This group should also own standards around things like logging and instrumentation. These standards allow the team to develop tools and services that deal with this data across the entire organization. I’ve talked about this notion with the <a href="https://blog.realkinetic.com/the-observability-pipeline-3010484eb931">Observability Pipeline</a>.</p>
<h3 id="cloud-engineering">Cloud Engineering</h3>
<p>Cloud Engineering might be closest to what most would consider a conventional operations team. In fact, we used to refer to this group as Cloud Operations but have since moved away from that vernacular due to the connotation the word “operations” carries. This group is responsible for handling common low-level concerns, underlying subsystems management, and realizing efficiencies at an aggregate level. Let’s break down what that means in practice by looking at some examples. We’ll continue using AWS to demonstrate, but the same applies across any cloud provider.</p>
<p>One of the low-level concerns this group is responsible for is AMI and base container image maintenance. This might be the AMIs used for Kubernetes nodes and the base images used by application pods running in the cluster. These are critical components as they directly relate to the organization’s security and compliance posture. They are also pieces most developers in a large organization are not well-equipped to—or interested in—dealing with. Patch management is a fundamental concern that often takes a back seat to feature development. Other examples of this include network configuration, certificate management, logging agents, intrusion detection, and SIEM. These are all important aspects of keeping the lights on and the company’s name out of the news headlines. Having a group that specializes in these shared operational concerns is vital.</p>
<p>In terms of realizing efficiencies, this mostly consists of managing AWS accounts, organization policies (another important security facet), and billing. This group owns cloud spend across the organization and, as a result, is able to monitor cumulative usage and identify areas for optimization. This might look like implementing resource-tagging policies, managing Reserved Instances, or negotiating with AWS on committed spend agreements. Spend is one of the reasons large companies standardize on a single cloud platform, so it’s essential to have good visibility and ownership over this. Note that this team is not responsible for the spend itself, rather they are responsible for visibility into the spend and cost allocations to hold teams accountable.</p>
<p>The unfortunate reality is that if the Cloud Engineering team does their job well, no one really thinks about them. That’s just the nature of this kind of work, but it has a <em>massive</em> impact on the company’s bottom line.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/12/infrastructure_organization-1024x665.jpg"></p>
<h2 id="summary">Summary</h2>
<p>Depending on the company culture, words like “standards” and “opinionated” might be considered taboo. These can be especially unsettling for developers who have worked in rigid or siloed environments. However, it doesn’t have to be all or nothing. These opinions are more meant to serve as a beaten path which makes it easier and faster for teams to deliver products and focus on business value. In fact, opinionation will accelerate cloud adoption for many organizations, enable creativity on the <em>value</em> rather than solution architecture, and improve efficiency and consistency at a number of levels like skills, knowledge, operations, and security. The key is in understanding how to balance this with flexibility so as to not overly constrain developers.</p>
<p>We like taking a product approach to operations because it moves away from the “ticket-driven” and gatekeeper model that plagues so many organizations. By thinking like a product team, infrastructure and operations groups are better able to serve developers. They are also better able to <em>scale</em>—something that is consistently difficult for more interrupt-driven ops teams who so often find themselves becoming the bottleneck.</p>
<p>Notice that I’ve entirely sidestepped terms like “DevOps” and “SRE” in this discussion. That is intentional as these concepts frequently serve as a distraction for companies who are just beginning their journey to the cloud. There are ideas encapsulated by these philosophies which provide important direction and practices, but it’s imperative to not get too caught up in the dogma. Otherwise, it’s easy to spin your wheels and chase things that, at least early on, are not particularly meaningful. It’s more impactful to focus on fundamentals and finding some success early on versus trying to approach things as <a href="https://blog.gardeviance.org/2015/03/on-pioneers-settlers-town-planners-and.html">town planners</a>.</p>
<p>Moreover, for many companies, the organization model I walked through above was the result of evolving and adapting as needs changed and less of a wholesale reorg. In the spirit of product mindset, we encourage starting small and iterating as opposed to boiling the ocean. The model above can hopefully act as a framework to help you identify needs and areas of ownership within your own organization. Keep in mind that these areas of responsibility might shift over time as capabilities are implemented and added.</p>
<p>Lastly, do not mistake this framework as something that might preclude exploration, learning, and innovation on the part of development teams. Again, opinionation and standards are not binding but rather act as a path of least resistance to facilitate efficiency. It’s important teams have a safe playground for exploratory work. Ideally, new ideas and discoveries that are shown to add value can be standardized over time and become part of that beaten path. This way we can make them more repeatable and scale their benefits rather than keeping them as one-off solutions.</p>
<p>How has your organization approached cloud development? What’s worked? What hasn’t? I’d love to hear from you.</p>
]]></content:encoded></item><item><title>We suck at meetings</title><link>https://bravenewgeek.com/we-suck-at-meetings/</link><pubDate>Tue, 10 Nov 2020 13:16:18 -0600</pubDate><guid>https://bravenewgeek.com/we-suck-at-meetings/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://bravenewgeek.com/wp-content/uploads/2020/11/dilbert.gif"&gt;&lt;/p&gt;
&lt;p&gt;I’ve worked as a software engineer, manager, consultant, and business owner. All of these jobs have involved meetings. What those meetings look like has varied greatly.&lt;/p&gt;
&lt;p&gt;As an engineer, meetings typically entailed technical conversations with peers, one-on-ones with managers, and planning meetings or demos with stakeholders.&lt;/p&gt;
&lt;p&gt;As a manager, these looked more like quarterly goal-setting with engineering leadership, one-on-ones with direct reports, and decision-making discussions with the team.&lt;/p&gt;
&lt;p&gt;As a consultant, my day often consists of talking to clients to provide input and guidance, communicating with partners to develop leads and strategize on accounts, and meeting with sales prospects to land new deals.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/wp-content/uploads/2020/11/dilbert.gif"></p>
<p>I’ve worked as a software engineer, manager, consultant, and business owner. All of these jobs have involved meetings. What those meetings look like has varied greatly.</p>
<p>As an engineer, meetings typically entailed technical conversations with peers, one-on-ones with managers, and planning meetings or demos with stakeholders.</p>
<p>As a manager, these looked more like quarterly goal-setting with engineering leadership, one-on-ones with direct reports, and decision-making discussions with the team.</p>
<p>As a consultant, my day often consists of talking to clients to provide input and guidance, communicating with partners to develop leads and strategize on accounts, and meeting with sales prospects to land new deals.</p>
<p>As a business owner, I am in conversations with attorneys and accountants regarding legal and financial matters, with advisors and brokers for things like employee benefits and health insurance, and with my co-owner Robert to discuss items relating to business operations.</p>
<p>What I’ve come to realize is this: <em>we suck at meetings</em>. We’re really bad at them. After starting my first job out of college, I quickly discovered that everyone’s just winging it when it comes to meetings. We’re winging it in a way the likes of which Dilbert himself would envy. We’re so bad at it that it’s become a meme in the corporate world. Whether it’s joking about your lack of productivity due to the number of meetings you have or <em>that one meeting that could have been an email</em>, we’ve basically come to terms with the fact that most meetings are just not very good.</p>
<p>And who’s to blame? There’s no science to meetings. It’s not something they teach you in school. Everyone just shows up and sort of finds a system that works—or <em>doesn’t</em> work—for them. What’s most shocking to me, however, is that meetings are one of the most <em>expensive</em> things a business can do—like <a href="https://meeting-report.com/financial-impact-of-meetings/0">billions-of-dollars expensive</a>. If you’re going to pay a bunch of people a lot of money to talk to other people who you’re similarly paying a lot of money, you probably want that talking to be worthwhile, right? And yet here we are, jumping from one meeting to the next, unable to even process what was said in the last one. It’s become an inside joke that every company is in on.</p>
<p>But meetings are also <em>important</em>. They’re where collaboration happens, where ideas are born, where decisions are made. Is being “good at meetings” a legitimate hiring criteria? <em>Should it be?</em></p>
<p>From all of the meetings I’ve had across these different jobs, I’ve learned that the biggest difference throughout is that of the <em>role</em> played in the meeting. In some cases, it’s The Spectator—there mostly to listen and maybe ask questions. In other cases, it’s playing the role of The Advisor—actively participating in the meeting but mostly in the form of offering advice and guidance. Sometimes it’s The Facilitator, who helps move the agenda along, captures notes, and keeps track of action items or decisions. It might be the Decision Maker, who’s there to decide which way to go and be the tie breaker.</p>
<p>Whatever the role, I’ve consistently struggled with how to insert the most value <em>into</em> meetings and extract the most value <em>out of</em> them. This is doubly so when your job revolves around people, which I didn’t recognize until I became a manager and, later, consultant. In these roles, your calendar is usually stacked with meetings, often with different groups of people across many different contexts. A software engineer’s work happens outside of meetings, but for a manager or consultant, it revolves around what gets done <em>during</em> and <em>after</em> meetings. This is true of a lot of other roles as well.</p>
<p>I’ve always had a vague sense for how to do meetings effectively—have a clear purpose or desired outcome, gather necessary context and background information, include an agenda, invite only the people you need, be present and engaged in the discussion, document the action items and decisions, follow up. The problem is I’ve never had a system for doing it that wasn’t just ad hoc and scattered. Also, most of these things happen <em>outside</em> of the conference room or Zoom call, and who has the time to do all of that when your schedule looks like a Dilbert calendar? All of it culminates in a feeling of severe meeting fatigue.</p>
<p>That’s when it occurred to us: <a href="https://www.linkedin.com/pulse/what-meetings-could-good-mike-taylor/"><em>what if meetings could be good?</em></a> Shortly after starting <a href="https://realkinetic.com/">Real Kinetic</a>, we began to explore this question, but the idea had been rattling around our heads long before that. And so <a href="https://witful.com/our-story/">we started to develop a solution</a>, first by building a prototype on nights and weekends, then later by investing in it as a full-fledged product. We call it <a href="https://witful.com">Witful</a>—a note-taking app that connects to your calendar. It’s deceptively simple, but its mission is not: <em>make meetings suck less.</em></p>
<p>Most calendar and note-taking apps focus on time. After all, what’s the first thing we do when we create a meeting? We <em>schedule</em> it. When it comes to meetings, time is important for logistical purposes—it’s how we know when we need to be somewhere. But the real value of meetings is not time, it’s the people and discussion, decisions, and action items that result. This is what Witful emphasizes by creating a network of all these relationships. It’s less an extension of your notebook and calendar and—forgive the cliche—more like an extension of your <em>brain</em>. It’s a more natural way to organize the information around your work.</p>
<p>We’re still early on this journey, but the product is evolving quickly. We’ve also been clear from the start: Witful isn’t for everyone. If your day is not run by your calendar, it might not be for you. If your role doesn’t center around managing people or maintaining relationships, it might not be for you. Our focus right now is to make you better at meetings. We want to give you the tools and resources you need to conquer your calendar and look good doing it. We use Witful every day to make our consulting work more manageable at Real Kinetic. And while we’re focused on empowering the individual today, our eyes are set towards making <em>teams</em> better at meetings too.</p>
<p>We don’t want to change the way people work, we want to help them do their <em>best</em> work. We want to make meetings suck less. Come join us.</p>
]]></content:encoded></item><item><title>Getting big wins with small teams on tight deadlines</title><link>https://bravenewgeek.com/getting-big-wins-with-small-teams-on-tight-deadlines/</link><pubDate>Mon, 02 Nov 2020 15:52:40 -0600</pubDate><guid>https://bravenewgeek.com/getting-big-wins-with-small-teams-on-tight-deadlines/</guid><description>&lt;p&gt;Part of what we do at Real Kinetic is give companies confidence to ship software in the cloud. Many of our clients are large organizations that have been around for a long time but who don’t always have much experience when it comes to cloud. Others are startups and mid-sized companies who may have some experience, but might just want another set of eyes or are looking to mature some of their practices. Whatever the case, one of the things we frequently talk to our clients about is the value of both serverless and managed services. We have found that these are critical to getting big wins with small teams on tight deadlines in the cloud. Serverless in particular has been key to helping clients get some big wins in ways others didn’t think possible.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Part of what we do at Real Kinetic is give companies confidence to ship software in the cloud. Many of our clients are large organizations that have been around for a long time but who don’t always have much experience when it comes to cloud. Others are startups and mid-sized companies who may have some experience, but might just want another set of eyes or are looking to mature some of their practices. Whatever the case, one of the things we frequently talk to our clients about is the value of both serverless and managed services. We have found that these are critical to getting big wins with small teams on tight deadlines in the cloud. Serverless in particular has been key to helping clients get some big wins in ways others didn’t think possible.</p>
<p>We often get pulled into a company to help them develop and launch new products in the cloud. These are typically high-profile projects with tight deadlines. These deadlines are almost always in terms of <em>months</em>, usually less than six. As a result, many of the executives and managers we talk to in these situations are skeptical of their team’s ability to execute on these types of timeframes. Whether it’s lack of cloud experience, operations and security concerns, compliance issues, staffing constraints, or some combination thereof, there’s always a reason as to why it can’t be done.</p>
<p>And then, some months later, it gets done.</p>
<h1 id="mental-model-of-the-cloud">Mental Model of the Cloud</h1>
<p>The skepticism is valid. Often people’s mental model of the cloud is something like this:</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/11/cloud_infrastructure-1024x590.png"></p>
<p>A subset of typical cloud infrastructure concerns</p>
<p>More often than not, this is what cloud infrastructure looks like. In addition to what’s shown, there are other concerns. These include things like managing backups and disaster recovery, multi-zone or regional deployments, VM images, and reserved instances. It can be deceiving because simply getting an app <em>running</em> in this environment isn’t terribly difficult, and most engineers will tell you that—these are the “day-one” costs. But engineers don’t tend to be the best at giving estimates while still <a href="https://blog.realkinetic.com/stop-wasting-your-beer-money-12c3fe5e4d54">undervaluing their own time</a>. The minds of most seasoned managers, however, will usually go to the “day-two” costs—what are the ongoing maintenance and operations costs, the security and compliance considerations, and the staffing requirements? This is why we consistently see so much skepticism. If this is also your initial foray into the cloud, that’s a lot of uncertainty! A manager’s job, after all, is to <a href="https://blog.realkinetic.com/trust-uncertainty-and-being-a-great-manager-28bc10f5d25a">reduce uncertainty</a>.</p>
<p>We’ve been there. We’ve also had to <em>manage</em> those day-two costs. I’ve personally gone through the phases of building a complex piece of software in the cloud, having to maintain one, having to manage a team responsible for one, and having to help a team go through the same process as an outside consultant. Getting that perspective has helped me develop an appreciation for what it really means to ship software. It’s why we like to take a different tack at Real Kinetic when it comes to cloud.</p>
<p>We are big on picking a cloud platform and going all-in on it. Whether it’s AWS, GCP, or Azure—pick your platform, embrace its capabilities, and move on. That doesn’t mean there isn’t room to use multiple clouds. Some platforms are better than others in different areas, such as data analytics or machine learning, so it’s wise to leverage the strengths of each platform where it makes sense. This is especially true for larger organizations who will inevitably span multiple clouds. What we mean by going “all-in” on a platform, particularly as it relates to application development, is sidestepping <a href="https://bravenewgeek.com/multi-cloud-is-a-trap/">the trap that so many organizations fall into</a>—<em>hedging their bets</em>. For a variety of reasons, many companies will take a half measure when adopting a cloud platform by avoiding things like managed services and serverless. Vendor lock-in is usually at the top of their list of concerns. Instead, they end up with something akin to the diagram above, and in doing so, lose out on the differentiated benefits of the platform. They also incur significantly more day-two costs.</p>
<h1 id="the-value-and-cost-of-serverless">The Value and Cost of Serverless</h1>
<p>We spend a lot of time talking to our clients about this trade-off. With managers, it usually resonates when we ask if they want their people focusing on shipping business value or doing commodity work. With engineers, architects, or operations folks, it can be more contentious. On more than a few occasions, we’ve talked clients <em>out</em> of using Kubernetes for things that were well-suited to serverless platforms. Serverless is not the right fit for everything, but the reality is many of the workloads we encounter are primarily CRUD-based microservices. These can be a good fit for platforms like AWS Lambda, Google App Engine, or Google Cloud Run. The organizations we’ve seen that have adopted these services for the correct use cases have found reduced operations investment, increased focus on shipping things that matter to the business, accelerated delivery of new products, and better cost efficiency in terms of infrastructure utilization.</p>
<p>If vendor lock-in is your concern, it’s important to understand both the constraints and the trade-offs. Not all serverless platforms are created equal. Some are highly opinionated, others are not. In the early days, Google App Engine was highly opinionated, requiring you to use its own APIs to build your application. This meant moving an application built on App Engine was no small feat. Today, that is no longer the case; the new App Engine runtimes allow you to run just about any application. Cloud Run, a serverless container platform, allows you to deploy a container that can run <em>anywhere</em>. The costs are even less. On the other hand, using a serverless database like Cloud Firestore or DynamoDB requires using a proprietary API, but APIs can be abstracted.</p>
<p>In order to decide if the trade-off makes sense, you need to determine three things:</p>
<ol>
<li>What is the honest likelihood you’ll need to move in the future?</li>
<li>What are the switching costs—the amount of time and effort needed to move?</li>
<li>What is the value you get using the solution?</li>
</ol>
<p>These are not always easy things to determine, but the general rule is this: if the value you’re getting offsets the switching costs times the probability of switching—and it often does—then it’s not worth trying to hedge your bet. There can be a lot of hidden considerations, namely operations and development overhead and opportunity costs. It can be easy to forget about these when making a decision. In practice, vendor lock-in tends to be less about code portability and more about <em>capability lock-in</em>—think things like user management, Identity and Access Management, data management, cloud-specific features and services, and so forth. These are what make switching hard, not code.</p>
<p>Another concern we commonly hear with serverless is cost. In our experience, however, this is rarely an issue for appropriate use cases. While serverless can be more expensive in terms of cloud spend for some situations, this cost is normally offset by the reduced engineering and ongoing operations costs. Using serverless and managed services for the right things can be quite cost-effective. This may not always hold true, such as for large organizations who can negotiate with providers for committed cloud spend, but for many cases it makes sense.</p>
<p>Serverless isn’t just about compute. While people typically associate serverless with things like Lambda or Cloud Functions, it actually extends far beyond this. For example, in addition to its serverless compute offerings (Cloud Run, Cloud Functions, and App Engine), GCP has serverless storage (Cloud Storage, Firestore, and Datastore), serverless integration components (Cloud Tasks, Pub/Sub, and Scheduler), and serverless data and machine learning services (BigQuery, AutoML, and Dataflow). While each of these services individually offers a lot of value, it’s not until we start to <em>compose</em> them together in different ways where we really see the value of serverless appear.</p>
<h1 id="serverless-vs-managed-services">Serverless vs. Managed Services</h1>
<p>Some might consider the services I mentioned above “managed services”, so let me clarify that. We generally talk about “serverless” being the idea that the cloud provider fully manages and maintains the server infrastructure. This means the notion of “managed services” and “serverless” are closely related, but they are also distinct.</p>
<p>A serverless product is also <em>managed</em>, but not all managed services are <em>serverless</em>. That is to say, serverless is a <em>subset</em> of managed services.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/11/serverless_vs_managed_service.png"></p>
<p>Serverless means you stop thinking about the concept of servers in your architecture. This broadly encompasses words like “servers”, “instances”, “nodes”, and “clusters.” Continuing with our GCP example, these words would be associated with products like GKE, Dataproc, Bigtable, Cloud SQL, and Spanner. These services are decidedly <em>not</em> serverless because they entail some degree of managing and configuring servers or clusters, even though they are managed services.</p>
<p>Instead, you start thinking in terms of <em>APIs and services</em>. This would be things like Cloud Functions, Dataflow, BigQuery, Cloud Run, and Firestore. These have no servers or clusters. They are simply APIs that you interact with to build your applications. They are more specialized managed services.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/11/serverless_vs_managed_service_gcp-1024x567.png"></p>
<p>Why does this distinction matter? It matters because of the ramifications it has for where we invest our time. Managing servers and clusters is going to involve a lot more operations effort, even if the base infrastructure is managed by the cloud provider. Much of this work can be considered “commodity.” It is not work that differentiates the business. This is the trade-off of getting more control—we take on more responsibility. In rough terms, the managed services that live outside of the serverless circle are going to be more in the direction of “DevOps”, meaning they will involve more operations overhead. The managed services inside the serverless circle are going to be more in the direction of “NoOps”. There is still work involved in <em>using</em> them, but the line of responsibility has moved upwards with the cloud provider responsible for more. We get less control over the infrastructure, but that means we can focus more on the business outcomes we develop on top of that infrastructure.</p>
<p>In fairness, it’s not always a black-and-white determination. Things can get a little blurry since serverless might still provide some degree of control over runtime parameters like memory or CPU, but this tends to be limited in comparison to managing a full server. There might also be some notion of “instances”, as in the case of App Engine, but that notion is much more abstract. Finally, some services appear to straddle the line between managed service and serverless. App Engine Flex, for instance, allows you to SSH into its VMs, but you have no real control over them. It’s a heavily sandboxed environment.</p>
<h1 id="why-serverless">Why Serverless?</h1>
<p>Serverless enables focusing on business outcomes. By leveraging serverless offerings across cloud platforms, we’ve seen product launches go from years to months (and often single-digit months). We’ve seen release cycles go from weeks to hours. We’ve seen development team sizes go from double digits to a few people. We’ve seen ops teams go from dozens of people to just one or two. It’s allowed these people to focus on more differentiated work. It’s given small teams of people a significant amount of leverage.</p>
<p>It’s no secret. Serverless is how we’ve helped many of our clients at Real Kinetic get big wins with small teams on tight deadlines. It’s not always the right fit and there are always trade-offs to consider. But if you’re not at least considering serverless—and more broadly, managed services—then you’re not getting the value you should be getting out of your cloud platform. Keep in mind that it doesn’t have to be all or nothing. Find the places where you can leverage serverless in combination with managed services or more traditional infrastructure. You too will be surprising and impressing your managers and leadership.</p>
]]></content:encoded></item><item><title>Continuous Deployment for AWS Glue</title><link>https://bravenewgeek.com/continuous-deployment-for-aws-glue/</link><pubDate>Thu, 15 Oct 2020 10:51:25 -0500</pubDate><guid>https://bravenewgeek.com/continuous-deployment-for-aws-glue/</guid><description>&lt;p&gt;&lt;a href="https://aws.amazon.com/glue"&gt;AWS Glue&lt;/a&gt; is a managed service for building ETL (Extract-Transform-Load) jobs. It’s a useful tool for implementing analytics pipelines in AWS without having to manage server infrastructure. Jobs are implemented using Apache Spark and, with the help of &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/dev-endpoints.html"&gt;Development Endpoints&lt;/a&gt;, can be built using Jupyter notebooks. This makes it reasonably easy to write ETL processes in an interactive, iterative fashion. Once finished, the Jupyter notebook is converted into a Python script, uploaded to S3, and then run as a Glue job.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://aws.amazon.com/glue">AWS Glue</a> is a managed service for building ETL (Extract-Transform-Load) jobs. It’s a useful tool for implementing analytics pipelines in AWS without having to manage server infrastructure. Jobs are implemented using Apache Spark and, with the help of <a href="https://docs.aws.amazon.com/glue/latest/dg/dev-endpoints.html">Development Endpoints</a>, can be built using Jupyter notebooks. This makes it reasonably easy to write ETL processes in an interactive, iterative fashion. Once finished, the Jupyter notebook is converted into a Python script, uploaded to S3, and then run as a Glue job.</p>
<p>There are a number of steps involved in doing this, so it can be worthwhile to automate the process into a CI/CD pipeline. In this post, I’ll show you how you can build an automated pipeline using GitHub Actions to do continuous deployment of Glue jobs built on PySpark and Jupyter notebooks. The <a href="https://github.com/RealKinetic/aws-glue-pipeline-example">full code</a> for this demo is available on GitHub.</p>
<h2 id="the-abstract-workflow">The Abstract Workflow</h2>
<p>First, I’m going to assume you already have a notebook for which you’d like to set up continuous deployment. If you don’t, you can take a look at my <a href="https://github.com/RealKinetic/aws-glue-pipeline-example/blob/master/traffic.ipynb">example</a>, but keep in mind you’ll need to have the appropriate data sources and connections set up in Glue for it to work. This post won’t be focusing on the ETL script itself but rather the build and deployment pipeline for it.</p>
<p>I recommend treating your Jupyter notebooks as the “source code” for your ETL jobs and treating the resulting Python script as the “build artifact.” Though this can present challenges for diffing, I find providing the notebook from which the code was derived makes the development process easier, particularly when collaborating with other developers. Additionally, GitHub has good support for rendering Jupyter notebooks, and there is tooling available for diffing notebooks, such as <a href="https://github.com/jupyter/nbdime">nbdime</a>.</p>
<p>With that in mind, the general flow of our deployment pipeline looks something like this:</p>
<ol>
<li>Upon new commits to master, generate a Python script from the Jupyter notebook.</li>
<li>Copy the generated Python script to an S3 bucket.</li>
<li>Update a Glue job to use the new script.</li>
</ol>
<p>You might choose to run some unit or integration tests for your script as well, but I’ve omitted this for brevity.</p>
<h2 id="the-implementation">The Implementation</h2>
<p>As I mentioned earlier, I’m going to use <a href="https://github.com/features/actions">GitHub Actions</a> to implement my CI/CD pipeline, but you could just as well use another tool or service to implement it. Actions makes it easy to automate workflows and it’s built right into GitHub. If you’re already familiar with it, some of this will be review.</p>
<p>In our notebook repository, we’ll create a .github/workflows directory. This is where GitHub Actions looks for workflows to run. Inside that directory, we’ll create a main.yml file for defining our CI/CD workflow.</p>
<p>First, we need to give our workflow a name. Our pipeline will simply consist of two jobs, one for producing the Python script and another for deploying it, so I’ll name the workflow “build-and-deploy.”</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">name: build-and-deploy
</span></span></code></pre></div><p>Next, we’ll configure when the workflow runs. This could be on push to a branch, when a pull request is created, on release, or a number of other events. In our case, we’ll just run it on pushes to the master branch.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">on:
</span></span><span class="line"><span class="cl">  push:
</span></span><span class="line"><span class="cl">    branches: [ master ]
</span></span></code></pre></div><p>Now we’re ready to define our “build” job. We will use a tool called <a href="https://github.com/jupyter/nbconvert">nbconvert</a> to convert our .ipynb notebook file into an executable Python script. This means our build job will have some setup. Specifically, we’ll need to install Python and then install nbconvert using Python’s pip. Before we define our job, we need to add the “jobs” section to our workflow file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># A workflow run is made up of one or more jobs that can run
</span></span><span class="line"><span class="cl"># sequentially or in parallel.
</span></span><span class="line"><span class="cl">jobs:
</span></span></code></pre></div><p>Here we define the jobs that we want our workflow to run as well as their order. Our build job looks like the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">build</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">runs</span><span class="o">-</span><span class="n">on</span><span class="p">:</span> <span class="n">ubuntu</span><span class="o">-</span><span class="n">latest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">steps</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Checks-out your repository under $GITHUB_WORKSPACE, so your</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># job can access it</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">uses</span><span class="p">:</span> <span class="n">actions</span><span class="o">/</span><span class="n">checkout</span><span class="err">@</span><span class="n">v2</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Set</span> <span class="n">up</span> <span class="n">Python</span> <span class="mf">3.8</span>
</span></span><span class="line"><span class="cl">      <span class="n">uses</span><span class="p">:</span> <span class="n">actions</span><span class="o">/</span><span class="n">setup</span><span class="o">-</span><span class="n">python</span><span class="err">@</span><span class="n">v2</span>
</span></span><span class="line"><span class="cl">      <span class="n">with</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">python</span><span class="o">-</span><span class="n">version</span><span class="p">:</span> <span class="s1">&#39;3.8&#39;</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Install</span> <span class="n">nbconvert</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">upgrade</span> <span class="n">pip</span>
</span></span><span class="line"><span class="cl">        <span class="n">pip</span> <span class="n">install</span> <span class="n">nbconvert</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Convert</span> <span class="n">notebook</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="n">jupyter</span> <span class="n">nbconvert</span> <span class="o">--</span><span class="n">to</span> <span class="n">python</span> <span class="n">traffic</span><span class="o">.</span><span class="n">ipynb</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Upload</span> <span class="n">python</span> <span class="n">script</span>
</span></span><span class="line"><span class="cl">      <span class="n">uses</span><span class="p">:</span> <span class="n">actions</span><span class="o">/</span><span class="n">upload</span><span class="o">-</span><span class="n">artifact</span><span class="err">@</span><span class="n">v2</span>
</span></span><span class="line"><span class="cl">      <span class="n">with</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span><span class="p">:</span> <span class="n">traffic</span><span class="o">.</span><span class="n">py</span>
</span></span><span class="line"><span class="cl">        <span class="n">path</span><span class="p">:</span> <span class="n">traffic</span><span class="o">.</span><span class="n">py</span>
</span></span></code></pre></div><p>The “runs-on” directive determines the base container image used to run our job. In this case, we’re using “ubuntu-latest.” The available base images to use are listed <a href="https://github.com/actions/virtual-environments#available-environments">here</a>, or you can create your own <a href="https://docs.github.com/en/free-pro-team@latest/actions/hosting-your-own-runners">self-hosted runners</a> with Docker. After that, we define the steps to run in our job. This consists of first checking out the code in our repository and setting up Python using built-in actions.</p>
<p>Once Python is set up, we pip install nbconvert. We then use nbconvert, which works as a subcommand of Jupyter, to convert our notebook file to a Python file. Note that you’ll need to specify the correct .ipynb file in your repository—mine is called traffic.ipynb. The file produced by nbconvert will have the same name as the notebook file but with the .py extension.</p>
<p>Finally, we upload the generated Python file so that it can be shared between jobs and stored once the workflow completes. This is necessary because we’ll need to access the script from our “deploy” job. It’s also useful because the artifact is now available to view and download from the workflow run, including historical runs.</p>
<p>Now that we have our Python script generated, we need to implement a job to deploy it to AWS. This happens in two steps: upload the script to an S3 bucket and update a Glue job to use the new script. To do this, we’ll need to install the AWS CLI tool and configure credentials in our job. Here is the full deploy job definition, which I’ll talk through below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">deploy</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">needs</span><span class="p">:</span> <span class="n">build</span>
</span></span><span class="line"><span class="cl">  <span class="n">runs</span><span class="o">-</span><span class="n">on</span><span class="p">:</span> <span class="n">ubuntu</span><span class="o">-</span><span class="n">latest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">steps</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Download</span> <span class="n">python</span> <span class="n">script</span> <span class="n">from</span> <span class="n">build</span>
</span></span><span class="line"><span class="cl">      <span class="n">uses</span><span class="p">:</span> <span class="n">actions</span><span class="o">/</span><span class="n">download</span><span class="o">-</span><span class="n">artifact</span><span class="err">@</span><span class="n">v2</span>
</span></span><span class="line"><span class="cl">      <span class="n">with</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span><span class="p">:</span> <span class="n">traffic</span><span class="o">.</span><span class="n">py</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Install</span> <span class="n">AWS</span> <span class="n">CLI</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">curl</span> <span class="s2">&#34;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&#34;</span> <span class="o">-</span><span class="n">o</span> <span class="s2">&#34;awscliv2.zip&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">unzip</span> <span class="n">awscliv2</span><span class="o">.</span><span class="n">zip</span>
</span></span><span class="line"><span class="cl">        <span class="n">sudo</span> <span class="o">./</span><span class="n">aws</span><span class="o">/</span><span class="n">install</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Set</span> <span class="n">up</span> <span class="n">AWS</span> <span class="n">credentials</span>
</span></span><span class="line"><span class="cl">      <span class="n">shell</span><span class="p">:</span> <span class="n">bash</span>
</span></span><span class="line"><span class="cl">      <span class="n">env</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">AWS_ACCESS_KEY_ID</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">AWS_ACCESS_KEY_ID</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">        <span class="n">AWS_SECRET_ACCESS_KEY</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">AWS_SECRET_ACCESS_KEY</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">mkdir</span> <span class="o">-</span><span class="n">p</span> <span class="o">~/.</span><span class="n">aws</span>
</span></span><span class="line"><span class="cl">        <span class="n">touch</span> <span class="o">~/.</span><span class="n">aws</span><span class="o">/</span><span class="n">credentials</span>
</span></span><span class="line"><span class="cl">        <span class="n">echo</span> <span class="s2">&#34;[default]</span>
</span></span><span class="line"><span class="cl">        <span class="n">aws_access_key_id</span> <span class="o">=</span> <span class="o">$</span><span class="n">AWS_ACCESS_KEY_ID</span>
</span></span><span class="line"><span class="cl">        <span class="n">aws_secret_access_key</span> <span class="o">=</span> <span class="o">$</span><span class="n">AWS_SECRET_ACCESS_KEY</span><span class="s2">&#34; &gt; ~/.aws/credentials</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Upload</span> <span class="n">to</span> <span class="n">S3</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="n">aws</span> <span class="n">s3</span> <span class="n">cp</span> <span class="n">traffic</span><span class="o">.</span><span class="n">py</span> <span class="n">s3</span><span class="p">:</span><span class="o">//$</span><span class="p">{{</span><span class="n">secrets</span><span class="o">.</span><span class="n">S3_BUCKET</span><span class="p">}}</span><span class="o">/</span><span class="n">traffic_</span><span class="o">$</span><span class="p">{</span><span class="n">GITHUB_SHA</span><span class="p">}</span><span class="o">.</span><span class="n">py</span> <span class="o">--</span><span class="n">region</span> <span class="n">us</span><span class="o">-</span><span class="n">east</span><span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Update</span> <span class="n">Glue</span> <span class="n">job</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">aws</span> <span class="n">glue</span> <span class="n">update</span><span class="o">-</span><span class="n">job</span> <span class="o">--</span><span class="n">job</span><span class="o">-</span><span class="n">name</span> <span class="s2">&#34;Traffic ETL&#34;</span> <span class="o">--</span><span class="n">job</span><span class="o">-</span><span class="n">update</span> \
</span></span><span class="line"><span class="cl"><span class="s2">&#34;Role=AWSGlueServiceRole-TrafficCrawler,Command={Name=glueetl,ScriptLocation=s3://${{secrets.S3_BUCKET}}/traffic_${GITHUB_SHA}.py},Connections={Connections=redshift}&#34;</span> \
</span></span><span class="line"><span class="cl"><span class="o">--</span><span class="n">region</span> <span class="n">us</span><span class="o">-</span><span class="n">east</span><span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Cleanup</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="n">rm</span> <span class="o">-</span><span class="n">rf</span> <span class="o">~/.</span><span class="n">aws</span>
</span></span></code></pre></div><p>We use “needs: build” to specify that this job depends on the “build” job. This determines the order in which jobs are run. The first step is to download the Python script we generated in the previous job.</p>
<p>Next, we install the AWS CLI using the <a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html">steps recommended by Amazon</a>. The AWS CLI relies on credentials in order to make API calls, so we need to set those up. For this, we use GitHub’s <a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets">encrypted secrets</a> which allow you to store sensitive information within your repository or organization. This prevents our credentials from leaking into code or workflow logs. In particular, we’ll use an <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html">AWS access key</a> to authenticate the CLI. In our notebook repository, we’ll create two new secrets, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, which contain the respective access key tokens. Our workflow then injects these into an ~/.aws/credentials file, which is where the AWS CLI looks for credentials.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/10/github_glue_secrets.png"></p>
<p>With our credentials set up, we can now use the CLI to make API calls to AWS. The first thing we need to do is copy the Python script to an S3 bucket. In the workflow above, I’ve parameterized this using a secret called S3_BUCKET, but you could also just hardcode this or parameterize it using a configuration file. This bucket acts as a staging directory for our Glue scripts. You’ll also notice that I append the Git commit SHA to the name of the file uploaded to S3. This way, you’ll know exactly what version of the code the script contains and the bucket will retain a history of each script. This is useful when you need to debug a job or revert to a previous version.</p>
<p>Once the script is uploaded, we need to update the Glue job. This requires the job to be already bootstrapped in Glue, but you could modify the workflow to update the job or create it if it doesn’t yet exist. For simplicity, we’ll just assume the job is already created. Our update command specifies the name of the job to update and a long –job-update string argument that looks like the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Role=AWSGlueServiceRole-TrafficCrawler,Command={Name=glueetl,ScriptLocation=s3://${{secrets.S3_BUCKET}}/traffic_${GITHUB_SHA}.py},Connections={Connections=redshift}
</span></span></code></pre></div><p>This configures a few different settings on the job, two of which are required. “Role” sets the IAM role associated with the job. This is important since it determines what resources your Glue job can access. “Command” sets the job command to execute, which is basically whether it’s a Spark ETL job (“glueetl”), Spark Streaming job (“gluestreaming”), or a Python shell job (“pythonshell”). Since we are running a PySpark job, we set the command name to “glueetl” and then specify the script location, which is the path to our newly uploaded script. Lastly, we set a connection used by the job. This isn’t a required parameter but is important if your job accesses any Glue data catalog connections. In my case, that’s a Redshift database connection I’ve created in Glue, so update this accordingly for your job. The Glue update-job command is definitely the most unwieldy part of our workflow, so refer to the <a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/glue/update-job.html">documentation</a> for more details.</p>
<p>The last step is to remove the stored credentials file that we created. This step isn’t strictly necessary since the job container is destroyed once the workflow is complete, but in my opinion is a good security hygiene practice.</p>
<p>Now, all that’s left to do is see if it works. To do this, simply commit the workflow file which should kick off the GitHub Action. In the Actions tab of your repository, you should see a running workflow. Upon completion, the build job output should look something like this:</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/10/github_glue_build.png"></p>
<p>And the deploy output should look something like this:</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/10/github_glue_deploy.png"></p>
<p>At this point, you should see your Python script in the S3 bucket you configured, and your Glue job should be pointing to the new script. You’ve successfully deployed your Glue job and have automated the process so that each new commit will deploy a new version! If you wanted, you could also extend this workflow to <a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/glue/start-job-run.html">start</a> the new job or create a separate workflow that runs on a <a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#scheduled-events">set schedule</a>, e.g. to kick off a nightly batch ETL process.</p>
<p>Hopefully you’ve found this useful for automating your own processes around AWS Glue or Jupyter notebooks. GitHub Actions provides a convenient and integrated solution for implementing CI/CD pipelines. With it, we can build a nice development workflow for getting Glue ETL code to production with continuous deployment.</p>
]]></content:encoded></item><item><title>Implementing ETL on GCP</title><link>https://bravenewgeek.com/implementing-etl-on-gcp/</link><pubDate>Wed, 15 Jul 2020 15:53:17 -0500</pubDate><guid>https://bravenewgeek.com/implementing-etl-on-gcp/</guid><description>&lt;p&gt;ETL (Extract-Transform-Load) processes are an essential component of any data analytics program. This typically involves loading data from disparate sources, transforming or enriching it, and storing the curated data in a data warehouse for consumption by different users or systems. An example of this would be taking customer data from operational databases, joining it with data from Salesforce and Google Analytics, and writing it to an OLAP database or BI engine.&lt;/p&gt;</description><content:encoded><![CDATA[<p>ETL (Extract-Transform-Load) processes are an essential component of any data analytics program. This typically involves loading data from disparate sources, transforming or enriching it, and storing the curated data in a data warehouse for consumption by different users or systems. An example of this would be taking customer data from operational databases, joining it with data from Salesforce and Google Analytics, and writing it to an OLAP database or BI engine.</p>
<p>In this post, we’ll take an honest look at building an ETL pipeline on GCP using Google-managed services. This will primarily be geared towards people who may be familiar with SQL but may feel less comfortable writing code or building a solution that requires a significant amount of engineering effort. This might include data analysts, data scientists, or perhaps more technical-oriented business roles. That is to say, we’re mainly looking at low-code/no-code solutions, but we’ll also touch briefly on more code-heavy options towards the end. Specifically, we’ll compare and contrast Data Fusion and Cloud Dataprep. As part of this, we will walk through the high-level architecture of an ETL pipeline and discuss common patterns like data lakes and data warehouses.</p>
<h2 id="general-architecture">General Architecture</h2>
<p>It makes sense to approach ETL in two phases. First, we need a place to land raw, unprocessed data. This is commonly referred to as a <em>data lake</em>. The data lake’s job is to serve as a landing zone for all of our business data, even if the purpose of some of that data is not yet clear. The data lake is also where we can de-identify or redact sensitive data before it moves further downstream.</p>
<p>The second phase is processing the raw data and storing it for particular use cases. This is referred to as a <em>data warehouse</em>. The data here feeds end-user queries and reports for business analysts, BI tools, dashboards, spreadsheets, ML models, and other business activities. The data warehouse structures the data in a way suitable for these specific needs.</p>
<p>On GCP, our data lake is implemented using <a href="https://cloud.google.com/storage">Cloud Storage</a>, a low-cost, exabyte-scale object store. This is an ideal place to land massive amounts of raw data. We can also use <a href="https://cloud.google.com/dlp">Cloud Data Loss Prevention</a> (DLP) to alert on or redact any sensitive data such as PII or PHI. Once use cases have been identified for the data, we then transform it and move it into our curated data warehouse implemented with <a href="https://cloud.google.com/bigquery">BigQuery</a>.</p>
<p>At a high level, our analytics pipeline architecture looks something like the following. The components in green are pieces implemented on GCP.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/etl_pipeline-1024x314.jpg"></p>
<p>We won’t cover <em>how</em> data gets ingested into the data warehouse. This might be a data-integration tool like Mulesoft or Informatica if we’re moving data from on-prem. It might be an automated batch process using <a href="https://cloud.google.com/storage/docs/gsutil">gsutil</a>, a Python script, or <a href="https://cloud.google.com/storage-transfer-service">Transfer Service</a>. Alternatively, it might be a more real-time push process that streams data in via Cloud Pub/Sub. Either way, we’ll assume we have some kind of mechanism to load our data into Cloud Storage.</p>
<p>We will focus our time discussing the “Transform Process” step in the diagram above. This is where Data Fusion and Cloud Dataprep fit in.</p>
<h2 id="data-fusion">Data Fusion</h2>
<p><a href="https://cloud.google.com/data-fusion">Data Fusion</a> is a code-free data integration tool that runs on top of Hadoop. The user is intended to define ETL pipelines using a graphical plug-and-play UI with preconfigured connectors and transformations. Data Fusion is actually a managed version of an open source system called <a href="https://cdap.io">Cask Data Analytics Platform</a> (CDAP) which Google acquired in 2018. It’s a relatively new product in GCP, and it shows. The UX is rough and there are a lot of sharp edges. For example, when an instance starts up, you can occasionally hit cryptic errors because the instance has not actually initialized fully. Case in point, try deciphering what this error means:</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/data_fusion_error-e1594845085536.png"></p>
<p>The theory of letting users with no programming experience implement and run ETL pipelines is appealing. However, the reality is that you will end up trying to understand Hadoop debug logs and opaque error messages when things go wrong, which happens frequently.</p>
<p>The pipelines created in Data Fusion run on <a href="https://cloud.google.com/dataproc">Cloud Dataproc</a>. This means every time you run a pipeline, you first need to wait for a Dataproc cluster to spin up—which is <em>slow</em>. Google’s recommendation to speed this up is to configure a runtime profile that uses a pre-existing Dataproc cluster. This has several downsides, one of which is simply the cost of keeping a Dataproc cluster running <em>in addition to</em> your Data Fusion instance. But what is the point of keeping a cluster running that only gets used for nightly batch processes or ad hoc pipeline development? The other is the technical and operations overhead required to configure and manage a cluster. This requires provisioning an appropriately sized cluster, creating an SSH key for it, and adding the key to the cluster so that Data Fusion can connect to it. For a product designed to allow relatively non-technical people to build out pipelines, this is a tall order. You’ll also quickly see how rough the UX is when walking through these steps.</p>
<p>The other downside of Data Fusion is that it’s actually <a href="https://cloud.google.com/data-fusion/pricing">pretty expensive</a>. CDAP consists of a whole bunch of components. When you start a Data Fusion instance, it creates an internal GKE cluster to run all of these components. In addition to this, it relies on Cloud Storage, Cloud SQL, Persistent Disks, Elasticsearch, and Cloud KMS. The net result is that instances take approximately 10-20 minutes to start (now closer to 10 with recent improvements) and, for many, they’re not something you run and forget about.</p>
<p>A Basic Edition instance costs about $1,100 per month, while an Enterprise Edition instance costs $3,000 per month. For larger organizations, that might be a nominal cost, but it stings a bit when you realize that is just the cost to run the pipeline <em>editor</em>. The pipelines themselves run on Dataproc, which is an entirely separate—and significant—line item. What’s worse is that you have to keep the Data Fusion instance running in order to actually execute the ETL pipelines you develop in it. Additionally, the Basic Edition will only let you run pipelines on demand. In order to schedule pipelines or trigger them in a more streaming fashion, you have to use the Enterprise Edition. As a result, I often encounter teams wanting to schedule startup and shutdown for both the Dataproc clusters and Data Fusion instances to avoid unnecessary spend. This has to be done with code.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/data_fusion_pipeline-e1594845255356-1024x463.png"></p>
<p>Data Fusion Pipeline Editor</p>
<p>Pipelines are immutable, which means every time you need to tweak a pipeline, you first have to make a copy of it. Immutability sounds nice in theory, but in practice it means you end up with dozens of pipeline iterations as you build out your process. And in order to save your pipeline when a Data Fusion instance is deleted—say because you’re shutting it down nightly to save on costs—you have to export it to a file and then import it to the new instance. Recycling instances will still lose the job information for previous pipeline runs, however. There is no way to “pause” an instance, which makes pipeline management a pain.</p>
<p>Data Fusion itself is fairly robust in what you can do with it. It can extract data from a broad set of sources, including Cloud Storage, perform a variety of transformations, and load results into an assortment of destinations such as BigQuery. That said, I’m still a bit skeptical about no-code solutions for non-technical users. I still often find myself dropping in a JavaScript transform in order to actually do the manipulations on the data that I need versus trying to do it with a combination of preconfigured drag-and-drop widgets. Most of the analysts I’ve seen using it also just want to use SQL to do their transformations. Trying to join two data sources using a UI is frankly just more difficult than writing a SQL join. The <a href="https://github.com/data-integrations/wrangler">data wrangler</a> uses a goofy scripting language called <a href="https://commons.apache.org/proper/commons-jexl/reference/syntax.html">JEXL</a> that is poorly documented and inconsistently implemented. To put it bluntly, the UI and UX in Data Fusion (technically CDAP) is painful, and I often find myself wishing I could just write some Python. It just <em>feels</em> like an open source product that doesn’t see much investment.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/data_fusion_wrangler-e1594845358934-1024x363.png"></p>
<p>Data Fusion Wrangler</p>
<p>Data Fusion is a bit of an oddball when viewed in the context of how GCP normally approaches services until you realize it was an acquisition of a company built around an open source framework. In that light, it feels very similar to <a href="https://cloud.google.com/composer">Cloud Composer</a>, another product built around an open source framework, Apache Airflow, which feels equally kludgy. Most of Google’s data products are highly refined with an emphasis on serverless and developer experience. Services like BigQuery, Dataflow, and Cloud Pub/Sub come to mind here. Data Fusion is the polar opposite. It’s clunky, the CDAP infrastructure is heavy and expensive, and it still requires low-level operations like when you’re configuring a Dataproc cluster.</p>
<p>Dataproc itself feels like a service for handling legacy Hadoop workloads since it has a lot of operations overhead. For newer workloads, I would target Dataflow which is closer to a “serverless” experience like BigQuery and is evidently on the roadmap as a runtime target for Data Fusion.</p>
<p>The CDAP UX is quirky, confusing, inconsistent, and generally unpleasant. The moment anything goes awry, which is often and unwittingly the case, you’re thrust into the world of Hadoop to divine what went wrong. I’m a raving fan of much of GCP’s managed services. On the whole, I find them to be better engineered, better thought-out, and better from a developer experience perspective compared to other cloud platforms. Data Fusion ain’t it.</p>
<h2 id="cloud-dataprep">Cloud Dataprep</h2>
<p><a href="https://cloud.google.com/dataprep">Cloud Dataprep</a> is actually a third-party application offered by Trifacta through GCP. In fact, it’s really just a GCP-specific SKU of Trifacta’s <a href="https://www.trifacta.com/products/wrangler-editions/">Wrangler</a> product. The downside of this is that you have to agree to a third-party vendor’s terms and conditions. For some, this will likely trigger a whole separate sourcing process. This is a challenge for a lot of enterprise organizations.</p>
<p>If you can get past the procurement conundrum, you’ll find Dataprep to be a highly polished and refined product. In comparison to Data Fusion, it’s a breath of fresh air and is superior in nearly every aspect. The UI is pleasant, the UX is—for the most part—coherent and intuitive, it’s cheaper, and it’s a proper serverless product. Dataprep <em>feels</em> like what I would expect from a first-class managed service on GCP.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/dataprep_pipeline-1024x320.png"></p>
<p>Dataprep Flow Editor</p>
<p>Dataprep is similar to Data Fusion in the sense that it allows you to build out pipelines with a graphical interface which then target an underlying runtime. In the case of Dataprep, it targets Dataflow rather than Dataproc. This means we benefit from the features of Dataflow, namely auto-provisioning and scaling of infrastructure. Jobs tend to run much more quickly and reliably than with Data Fusion. Another key difference is that, unlike Data Fusion, Dataprep doesn’t require an “instance” to develop pipelines. It is more like a SaaS application that relies on Dataflow. Today, using the app to develop pipelines is <a href="https://cloud.google.com/dataprep/pricing">free of charge</a>. You only incur charges from Dataflow resource usage. Unfortunately, this is changing as Trifacta is switching to a <a href="https://www.trifacta.com/products/pricing/cloud-dataprep/">tiered monthly subscription model</a> later this year. This will put base costs more in-line with Data Fusion, but I suspect the reliance on Dataflow will bring overall costs down.</p>
<p>The pipeline management in Dataprep is simpler than in Data Fusion. Pipelines in Dataprep are called “flows.” These are mutable and private by default but can be shared with other users. Because Dataprep is a SaaS product, you don’t need to worry about exporting and persisting your pipelines, and job data from previous flow executions is retained.</p>
<p>Dataprep has some drawbacks though. Broadly speaking, it’s not as feature-rich as Data Fusion. It can only integrate with Cloud Storage and BigQuery, while Data Fusion supports a wide array of data sources and sinks. You can do more with Data Fusion, while with Dataprep, you’re more or less confined to the wrangler. Because of this, Dataprep is well-suited to lighter weight processes and data cleansing—joining data sources, standardizing formats, identifying missing or mismatched values, deduplicating rows, and other things like that. It also works well for data exploration and slicing and dicing.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/dataprep_wrangler-e1594845629106-1024x443.png"></p>
<p>Dataprep Wrangler</p>
<p>I often find teams using both Data Fusion and Dataprep. Data Fusion gets used for more advanced ETL processes and Dataprep for, well, data preparation. If it’s available to them, teams usually start with Dataprep and then switch to Data Fusion if they hit a wall with what it can do.</p>
<h2 id="alternatives">Alternatives</h2>
<p>Data Fusion and Dataprep attempt to provide a managed solution that lets users with little-to-no programming experience build out ETL pipelines. Dataprep definitely comes closer to realizing that goal due to its more refined UX and reliance on Dataflow rather than Dataproc. However, I tend to dislike managed “workflow engines” like these. Cloud Composer and <a href="https://aws.amazon.com/glue">AWS Glue</a>, which is Amazon’s managed ETL service, are other examples that fall under this category.</p>
<p>These types of services usually sit in a weird in-between position of trying to provide low-code solutions with GUIs but needing to understand how to debug complex and sophisticated distributed computing systems. It seems like every time you try something to make building systems easier, you wind up needing to understand the “easier” thing <em>plus</em> the “hard” stuff it was trying to make easy. This is what Joel Spolsky refers to as the <a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/">Law of Leaky Abstractions</a>. It’s why I prefer to write code to solve problems versus relying on low-code interfaces. The abstractions can work okay in some cases, but it’s when things go wrong or you need a little bit more flexibility where you run into problems. It can be a touchy subject, but I’ve found that the most effective data programs within organizations are the ones that have software engineers or significant programming and systems development skill sets. This is especially true if you’re on AWS where there’s more operations and networking knowledge required.</p>
<p>With that said, there are some alternative approaches to implementing ETL processes on GCP that move away from the more low/no-code options. If your team consists mostly of software engineers or folks with a development background, these might be a better option.</p>
<p>My go-to for building data processing pipelines is <a href="https://cloud.google.com/dataflow">Cloud Dataflow</a>, which is a serverless system for implementing stream and batch pipelines. With Dataflow, you don’t need to think about capacity and resource provisioning and, unlike Data Fusion and Dataproc, you don’t need to keep a standby cluster running as there is no “cluster.” The compute is automatically provisioned and autoscaled for you based on the job. You can use code to do your transformations or use SQL to join different data sources.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/etl_dataflow.png"></p>
<p>ETL Pipeline with Dataflow</p>
<p>For batch ETL, I like a combination of Cloud Scheduler, Cloud Functions, and Dataflow. Cloud Scheduler can kick off the ETL process by hitting a Cloud Function which can then trigger your Dataflow template. Alternatively, you could use a streaming Dataflow pipeline in combination with Cloud Scheduler and Pub/Sub to launch your batch ETL pipelines. Google has an example of this <a href="https://cloud.google.com/blog/products/gcp/designing-etl-architecture-for-a-cloud-native-data-warehouse-on-google-cloud-platform">here</a>.</p>
<p>For streaming ETL, data can be fed into a streaming Dataflow pipeline from Cloud Pub/Sub and processed as usual. This data can even be joined with files in Cloud Storage or tables in BigQuery using SQL. This is what I found myself and many of the clients I’ve worked with wanting to do in Data Fusion and Dataprep. Sometimes you just want to write SQL, which leads to another solution.</p>
<p>BigQuery provides a good mechanism for <em>ELT</em>—that is extracting the data from its sources, loading it into BigQuery, and <em>then</em> performing the transformations on it. This is a good option if you’re dealing with primarily batch-driven processes and you have a SQL-heavy team as the transformations are expressed purely through SQL. The transformation queries can either be scheduled directly in BigQuery or triggered in an automated way using the API, such as running the transformations after data loading completes.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/07/elt_bigquery.png"></p>
<p>ELT Pipeline with BigQuery</p>
<p>I mentioned earlier that I’m not a huge fan of managed workflow engines. This is speaking to high-level abstractions and heavy, monolithic frameworks specifically. However, I <em>am</em> a fan of lightweight, composable abstractions that make it easy to build scalable and fault-tolerant workflows. Examples of this include <a href="https://aws.amazon.com/step-functions/">AWS Step Functions</a> and <a href="https://cloud.google.com/tasks">Google Cloud Tasks</a>. On GCP, Cloud Tasks can be a great alternative to Dataflow for building more code-heavy ETL processes if you’re not tied in to Apache Beam. In combination with <a href="https://cloud.google.com/run">Cloud Run</a>, you can build out highly elastic workflows that are entirely serverless. While it’s not the obvious choice for implementing ETL on GCP, it’s definitely worth a mention.</p>
<h2 id="conclusion">Conclusion</h2>
<p>There are several options when it comes to implementing ETL processes on GCP. What the right fit is depends on your team’s skill set, the use cases, and your affinity for certain tools. Cost and operational complexity are also important considerations. In practice, however, it’s likely you’ll end up using a <em>combination</em> of different solutions.</p>
<p>For low/no-code solutions, Data Fusion and Cloud Dataprep are your only real options. While Data Fusion is rough from a usability perspective and generally more expensive, it’s likely where Google is putting significant investment. Dataprep is more refined and cost-effective but limited in capability, and it brings a third-party vendor into the mix. Using BigQuery itself for ELT is also an option for SQL-minded teams. But for teams with a strong engineering background, my recommended starting point is Cloud Dataflow or even Cloud Tasks for certain types of processing work.</p>
<p>Together with Cloud Pub/Sub, Cloud Data Loss Prevention, Cloud Storage, BigQuery, and GCP’s other managed services, these solutions provide a great way to implement analytics pipelines that require minimal operations investment.</p>
]]></content:encoded></item><item><title>Using Google-Managed Certificates and Identity-Aware Proxy With GKE</title><link>https://bravenewgeek.com/using-google-managed-certificates-and-identity-aware-proxy-with-gke/</link><pubDate>Wed, 24 Jun 2020 11:31:44 -0500</pubDate><guid>https://bravenewgeek.com/using-google-managed-certificates-and-identity-aware-proxy-with-gke/</guid><description>&lt;p&gt;Ingress on Google Kubernetes Engine (GKE) uses a Google Cloud Load Balancer (GCLB). GCLB provides a single anycast IP that fronts all of your backend compute instances along with a lot of other &lt;a href="https://cloud.google.com/load-balancing"&gt;rich features&lt;/a&gt;. In order to create a GCLB that uses HTTPS, an SSL certificate needs to be associated with the ingress resource. This certificate can either be &lt;a href="https://cloud.google.com/load-balancing/docs/ssl-certificates/self-managed-certs"&gt;self-managed&lt;/a&gt; or &lt;a href="https://cloud.google.com/load-balancing/docs/ssl-certificates/google-managed-certs"&gt;Google-managed&lt;/a&gt;. The benefit of using a Google-managed certificate is that they are provisioned, renewed, and managed for your domain names by Google. These managed certificates can also be configured directly with GKE, meaning we can configure our certificates the same way we declaratively configure our other Kubernetes resources such as deployments, services, and ingresses.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Ingress on Google Kubernetes Engine (GKE) uses a Google Cloud Load Balancer (GCLB). GCLB provides a single anycast IP that fronts all of your backend compute instances along with a lot of other <a href="https://cloud.google.com/load-balancing">rich features</a>. In order to create a GCLB that uses HTTPS, an SSL certificate needs to be associated with the ingress resource. This certificate can either be <a href="https://cloud.google.com/load-balancing/docs/ssl-certificates/self-managed-certs">self-managed</a> or <a href="https://cloud.google.com/load-balancing/docs/ssl-certificates/google-managed-certs">Google-managed</a>. The benefit of using a Google-managed certificate is that they are provisioned, renewed, and managed for your domain names by Google. These managed certificates can also be configured directly with GKE, meaning we can configure our certificates the same way we declaratively configure our other Kubernetes resources such as deployments, services, and ingresses.</p>
<p>GKE also supports <a href="https://cloud.google.com/iap">Identity-Aware Proxy</a> (IAP), which is a fully managed solution for implementing a zero-trust security model for applications and VMs. With IAP, we can secure workloads in GCP using identity and context. For example, this might be based on attributes like user identity, device security status, region, or IP address. This <a href="https://bravenewgeek.com/zero-trust-security-on-gcp-with-context-aware-access">allows users to access applications securely from untrusted networks</a> without the need for a VPN. IAP is a powerful way to implement authentication and authorization for corporate applications that are run internally on GKE, Google Compute Engine (GCE), or App Engine. This might be applications such as Jira, GitLab, Jenkins, or <a href="https://blog.realkinetic.com/admin-portals-314d7f56b9b9">production-support portals</a>.</p>
<p>IAP works in relation to GCLB in order to secure GKE workloads. In this tutorial, I’ll walk through deploying a workload to a GKE cluster, setting up GCLB ingress for it with a global static IP address, configuring a Google-managed SSL certificate to support HTTPS traffic, and enabling IAP to secure access to the application. In order to follow along, you’ll need a GKE cluster and domain name to use for the application. In case you want to skip ahead, all of the Kubernetes configuration for this tutorial is available <a href="https://github.com/RealKinetic/iap-gke">here</a>.</p>
<h2 id="deploying-an-application-behind-gclb-with-a-managed-certificate">Deploying an Application Behind GCLB With a Managed Certificate</h2>
<p>First, let’s deploy our application to GKE. We’ll use a <a href="https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/tree/master/hello-app">Hello World application</a> to test this out. Our application will consist of a Kubernetes deployment and service. Below is the configuration for these:</p>
<script src="https://gist.github.com/tylertreat/42376a257dab55e13e7320e9f607587c.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/42376a257dab55e13e7320e9f607587c">Gist</a>.</p>
<script src="https://gist.github.com/tylertreat/e43e6f64023c8eeb6472210c9d978488.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/e43e6f64023c8eeb6472210c9d978488">Gist</a>.</p>
<p>Apply these with kubectl:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl apply -f .
</span></span></code></pre></div><p>At this point, our application is not yet accessible from outside the cluster since we haven’t set up an ingress. Before we do that, we need to create a static IP address using the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ gcloud compute addresses create web-static-ip --global
</span></span></code></pre></div><p>The above will reserve a static external IP called “web-static-ip.” We now can create an ingress resource using this IP address. Note the “kubernetes.io/ingress.global-static-ip-name” annotation in the configuration:</p>
<script src="https://gist.github.com/tylertreat/372d30dbba97f650349f027987b52388.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/372d30dbba97f650349f027987b52388">Gist</a>.</p>
<p>Applying this with kubectl will provision a GCLB that will route traffic into our service. It can take a few minutes for the load balancer to become active and health checks to begin working. Traffic won’t be served until that happens, so use the following command to check that traffic is healthy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ curl -i http://&lt;web-static-ip&gt;
</span></span></code></pre></div><p>You can find <web-static-ip> with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ gcloud compute addresses describe web-static-ip --global
</span></span></code></pre></div><p>Once you start getting a successful response, update your DNS to point your domain name to the static IP address. Wait until the DNS change is propagated and your domain name now points to the application running in GKE. This could take 30 minutes or so.</p>
<p>After DNS has been updated, we’ll configure HTTPS. To do this, we need to create a Google-managed SSL certificate. This can be managed by GKE using the following configuration:</p>
<script src="https://gist.github.com/tylertreat/6cdbafd9bfdd0fbbb8d06203403936ae.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/6cdbafd9bfdd0fbbb8d06203403936ae">Gist</a>.</p>
<p>Ensure that “example.com” is replaced with the domain name you’re using.</p>
<p>We now need to update our ingress to use the new managed certificate. This is done using the “networking.gke.io/managed-certificates” annotation.</p>
<script src="https://gist.github.com/tylertreat/d6a8fd131d3451f660446fba99a672e5.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/d6a8fd131d3451f660446fba99a672e5">Gist</a>.</p>
<p>We’ll need to wait a bit for the certificate to finish provisioning. This can take up to 15 minutes. Once it’s done, we should see HTTPS traffic flowing correctly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ curl -i https://example.com
</span></span></code></pre></div><p>We now have a working example of an application running in GKE behind a GCLB with a static IP address and domain name secured with TLS. Now we’ll finish up by enabling IAP to control access to the application.</p>
<h2 id="securing-the-application-with-identity-aware-proxy">Securing the Application With Identity-Aware Proxy</h2>
<p>If you’re enabling IAP for the first time, you’ll need to configure your project’s OAuth consent screen. The steps <a href="https://cloud.google.com/iap/docs/enabling-kubernetes-howto#oauth-configure">here</a> will walk through how to do that. This consent screen is what users will see when they attempt to access the application before logging in.</p>
<p>Once IAP is enabled and the OAuth consent screen has been configured, there should be an OAuth 2 client ID created in your GCP project. You can find this under “OAuth 2.0 Client IDs” in the “APIs &amp; Services” &gt; “Credentials” section of the cloud console. When you click on this credential, you’ll find a client ID and client secret. These need to be provided to Kubernetes as secrets so they can be used by a <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/backendconfig">BackendConfig</a> for configuring IAP. Apply the secrets to Kubernetes with the following command, replacing “xxx” with the respective credentials:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl create secret generic iap-oauth-client-id \
</span></span><span class="line"><span class="cl">--from-literal=client_id=xxx \
</span></span><span class="line"><span class="cl">--from-literal=client_secret=xxx
</span></span></code></pre></div><p>BackendConfig is a Kubernetes custom resource used to configure ingress in GKE. This includes features such as IAP, Cloud CDN, Cloud Armor, and others. Apply the following BackendConfig configuration using kubectl, which will enable IAP and associate it with your OAuth client credentials:</p>
<script src="https://gist.github.com/tylertreat/98d37d03d9bd1e6f4b24271fc2824e61.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/98d37d03d9bd1e6f4b24271fc2824e61">Gist</a>.</p>
<p>We also need to ensure there are service ports associated with the BackendConfig in order to trigger turning on IAP. One way to do this is to make all ports for the service default to the BackendConfig, which is done by setting the “beta.cloud.google.com/backend-config” annotation to “{“default”: “config-default”}” in the service resource. See below for the updated service configuration.</p>
<script src="https://gist.github.com/tylertreat/8293014f66e3679294f9528f3336a7d7.js"></script>
<p>View the code on <a href="https://gist.github.com/tylertreat/8293014f66e3679294f9528f3336a7d7">Gist</a>.</p>
<p>Once you’ve applied the annotation to the service, wait a couple minutes for the infrastructure to settle. IAP should now be working. You’ll need to assign the “IAP-secured Web App User” role in IAP to any users or groups who should have access to the application. Upon accessing the application, you should now be greeted with a login screen.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/06/Screen-Shot-2020-06-19-at-4.11.09-PM.png"></p>
<p>Your Kubernetes workload is now secured by IAP! Do note that VPC firewall rules can be configured to <em>bypass</em> IAP, such as rules that allow traffic internal to your VPC or GKE cluster. IAP will provide a warning indicating which firewall rules allow bypassing it.</p>
<p><img loading="lazy" src="/wp-content/uploads/2020/06/Screen-Shot-2020-06-19-at-4.37.13-PM-1024x893.png"></p>
<p>For an extra layer of security, IAP sets <a href="https://cloud.google.com/iap/docs/signed-headers-howto">signed headers</a> on inbound requests which can be verified by the application. This is helpful in the event that IAP is accidentally disabled or misconfigured or if firewall rules are improperly set.</p>
<p>Together with GCLB and GCP-managed certificates, IAP provides a great solution for serving and securing internal applications that can be accessed anywhere without the need for a VPN.</p>
]]></content:encoded></item></channel></rss>