---
title: "Getting started with Kubernetes and Buildkite: A bubbly trip in the clouds"
date: "2023-08-31"
author: "Peter Buckley"
description: "Learn how to use the new Kubernetes (k8s) stack to deploy Buildkite agents to any cloud, including using kind, AKS, EKS, and GKE."
tags: "Guide, Kubernetes"
readingTime: "16 minute read"
---

# Getting started with Kubernetes and Buildkite: A bubbly trip in the clouds

Buildkite has always been much easier to use on AWS than other clouds, but times are changing. There's a new kid on the block, a Kubernetes stack in beta, that lets you deploy agents to any old cloud. Grab a bottle-o-bubbly, hop in your canoe, and join me on a journey to set it all up!

<p>So you&apos;ve heard of this CI/CD tool called Buildkite. You might even know that it has a fantastic story on AWS where you BYOC (bring your own compute) by running your own EC2 instances in an Auto Scaling Group. It&apos;s dead simple. You can literally click a button, and blammo, you&apos;ve got a CFN stack of autoscaling agents and AWAY. YOU. GO.</p><p>But honestly, Buildkite has a bit of a problem when it comes to other clouds. Not necessarily cumulus or stratus—not exactly sure why your mind went there first—but I mean other clouds like GCP and Azure. If you&apos;re already using another cloud provider, it&apos;s a bit like you&apos;re in a flying canoe up a cloud creek without a cloud paddle (but on the bright side, YOU&apos;RE STILL IN A FLYING CANOE!). You&apos;re essentially on your own to bring that compute for CI/CD agents to the table. You might even think you have to write your own auto scaler (and you might be right, there, but it ain&apos;t so bad—ask me how I know sometime).</p><p>Yet, dear reader, I bring you hope of a third way. And just in case I wasn&apos;t crystal clear, the first and second ways are: </p><ol><li><p>Using the <a href="https://github.com/buildkite/elastic-ci-stack-for-aws">elastic-ci-stack-for-aws</a>.</p></li><li><p>Writing your own autoscaling logic based on <a href="https://github.com/buildkite/buildkite-agent-metrics">buildkite-agent-metrics</a>.</p></li></ol><p>The third way is the <a href="https://github.com/buildkite/agent-stack-k8s">agent-stack-k8s</a>, a way to BYOC in a nice little container package that I like to call Kubernetes (k8s). Surely you&apos;ve heard tell in the days of yore of that tool Kubernetes, the developer-friendly container orchestration platform? Somehow the juxtaposition of &quot;developer-friendly&quot; next to Kubernetes feels like stabbing a glowing hot spoon in my eye, but that&apos;s a whole ‘nother blog post for another day.</p><p>In this post, I&apos;ll show you that third way—how to stand up your own autoscaling CI/CD agents on k8s, on any cloud. This post is geared toward kube newbs (myself included there) and experts alike. Whether you’ve stood up dozens of clusters or you’ll be spinning up the <code>dear-reader-inaugural-k8s-cluster-number-0</code>, I’ve got you covered.</p><h2>How to use a Kubernetes cluster for your CI/CD agents</h2><p>Once you have one or more working k8s clusters, what can we do to bring the beautiful flowing pipelines of CI/CD together in harmony with the vast container ships of k8s? I&apos;m glad I asked because I have some ideas to share with you!</p><p>Buildkite&apos;s <code>agent-stack-k8s</code> gives you a ready-made Helm chart that you can simply deploy to any k8s cluster on any cloud (or even your friendly neighborhood local datacenter&apos;s k8s cluster). And by the power of <a href="https://www.youtube.com/watch?v=av7lw3jqEgU"><s>grayskull</s></a> <code>agent-stack-k8s</code>, you can run a fully autoscaling fleet of CI/CD agents on your k8s cluster and, therefore, on your cloud of choice. That cloud being Azure or GCP, not cumulus or stratus, why does my mind keep going there? You, too?</p><p>Does this sound too good to be true? I also thought so at first (and honestly, still a bit at second), but I&apos;ll tell you how:</p><ol><li><p>Get a k8s cluster. You could even get more than one—I&apos;ve got clusters in kind, AKS, EKS, and GKE.</p></li><li><p>Register some secrets.</p></li><li><p>Install the Helm chart that describes the <code>agent-stack-k8s</code>.</p></li><li><p>Create a bunch of Buildkite pipelines. They can be static YAML or dynamic pipelines—you get to choose.</p></li><li><p>Run the pipelines to your heart&apos;s (or wallet&apos;s) content.</p></li><li><p>...</p></li><li><p>Profit!</p></li></ol><p>And worry not—I’ll explain all of that in the rest of this post.</p><h2>Set up a Kubernetes cluster</h2><p>So, I lied. The first step sounds so simple and self-contained, but it is actually much like the plot of Hamlet (spoiler alert on a 400-year-old work of art!) in that it contains a play WITHIN A PLAY. Steps within steps… should be par for the course for any of you computer professionals out there.</p><p>I&apos;ll take you through the steps needed to get yourself a k8s cluster in four different clouds. If you already have a k8s cluster, and you don’t appreciate my style of humor, feel free to skip this part and jump right to the part where you <a href="#add-some-secrets">add some secrets to use in the pipeline</a>.</p><p>Can I be real a second, for just a millisecond? Take off my author hat and tell you how I feel? There are loads of other blogs and docs out there that tell you how to spin up a working k8s cluster and install a web service or some tiny thing. But I want you to have a working cluster for a fleet (well, a svelte fleet, at least) of autoscaling CI agents. And the other articles I’ve seen <em>don’t</em> give us a working cluster for those purposes.</p><h3>Start a k8s cluster in kind</h3><p>These commands work to get a cluster up and running on <a href="https://kind.sigs.k8s.io/">kind</a> (local k8s). This is a super simple way to start if you’re new to k8s, clouds, etc.</p><p>First, create a k8s cluster and name it <code>demisec</code>. That’s the name that came to me for our dual purpose of demoing the cluster and handling secrets. And rest assured that this cluster will neither be too sweet nor too dry. It’s simply demisec. 🍾</p><p>On the command line, navigate to a new project directory, then create the cluster by running:</p><pre data-language="shell"><code>kind create cluster --name demisec-cluster --config ./two-workers.yml</code></pre><p>Output:</p><pre><code>Creating cluster &quot;demisec-cluster&quot; ...
 ✓ Ensuring node image (kindest/node:v1.27.3) 🖼
 ✓ Preparing nodes 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to &quot;kind-demisec-cluster&quot;
You can now use your cluster with:

kubectl cluster-info --context kind-demisec-cluster

Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/</code></pre><p>What about that file, <code>two-workers.yml</code>? Why <em>two</em> workers, you may ask? Wouldn’t one have worked for this not-too-bitter, not-too-sweet, demo avec secrets? Yes, mayhaps, but one is the loneliest number. This is what I put in that file:</p><pre data-language="yaml"><code># three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: worker
  - role: worker</code></pre><p>Next, set your context to <code>kind-demisec-cluster</code> so subsequent <code>kubectl</code> commands target the cluster you just created:</p><pre data-language="shell"><code>kubectl config use-context kind-demisec-cluster</code></pre><p>To confirm your cluster is up and running, you can query it for info:</p><pre data-language="shell"><code>kubectl cluster-info</code></pre><p>Output:</p><pre><code>Kubernetes control plane is running at https://127.0.0.1:59790
CoreDNS is running at https://127.0.0.1:59790/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use &apos;kubectl cluster-info dump&apos;.</code></pre><p>And, check that it has nodes:</p><pre data-language="shell"><code>kubectl get nodes</code></pre><p>Output:</p><pre><code>NAME                            STATUS   ROLES           AGE     VERSION
demisec-cluster-control-plane   Ready    control-plane   3h14m   v1.27.3
demisec-cluster-worker          Ready    &lt;none&gt;          3h14m   v1.27.3
demisec-cluster-worker2         Ready    &lt;none&gt;          3h14m   v1.27.3</code></pre><p>That’s it, you have a Kubernetes cluster running locally in kind. Congratulations! Uncork some champagne! Or better yet, saber the top right off with the blade of your choice!</p><h3>Start a k8s cluster in AKS</h3><p>These commands work to get a cluster up and running in Azure Kubernetes Service (AKS). I’m brandy new to Microsoft Azure, so I followed their basic <a href="https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-cli">tutorial</a> to get my AKS cluster up and running. I’m a man of many words, if you haven’t noticed already, so the CLI suits me. I’ll use the <code>az</code> command I installed from the Microsoft tutorial.</p><p>You need the <code>OperationsManagement</code> and <code>OperationalInsights</code> permissions to spin up an AKS cluster. To check if you have these permissions, run the following on the command line:</p><pre data-language="shell"><code>az provider show -n Microsoft.OperationsManagement -o table</code></pre><pre data-language="shell"><code>az provider show -n Microsoft.OperationalInsights -o table</code></pre><p>Can confirm, when I first did it, my account/user was <code>NotRegistered</code> for these permissions. To register the permissions, you run the following:</p><pre data-language="shell"><code>az provider register --namespace Microsoft.OperationsManagement</code></pre><pre data-language="shell"><code>az provider register --namespace Microsoft.OperationalInsights</code></pre><p>The commands returned that “registration is in progress,” but in practice, it didn’t take more than a minute or two for the registration to complete, and I could proceed forthwith.</p><p>The next step is to create the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal">resource group</a> in which to place your cluster. Much like the bag of holding, a resource group holds related resources.</p><pre data-language="shell"><code>az group create --name bubbly-group --location eastus</code></pre><p>Output:</p><pre><code>{
  &quot;id&quot;: &quot;/subscriptions/76e2d6c2-6f12-4128-b666-e2bceb29be77/resourceGroups/bubbly-group&quot;,
  &quot;location&quot;: &quot;eastus&quot;,
  &quot;managedBy&quot;: null,
  &quot;name&quot;: &quot;bubbly-group&quot;,
  &quot;properties&quot;: {
    &quot;provisioningState&quot;: &quot;Succeeded&quot;
  },
  &quot;tags&quot;: null,
  &quot;type&quot;: &quot;Microsoft.Resources/resourceGroups&quot;
}</code></pre><p>And now it’s time for the big one—the command that creates your actual AKS cluster:</p><pre data-language="shell"><code>az aks create --resource-group bubbly-group --name demisec-cluster --enable-managed-identity --node-count 2 --enable-addons monitoring --generate-ssh-keys</code></pre><p>So, why did I choose two nodes? Well, early on in my (admittedly limited) experience trying to spin up k8s clusters, I hit what I thought was a weird resource limit where a single node cluster just would <em>not</em> schedule my buildkite-agent pods. At the time, I frantically tried various combinations of node numbers and workers and cluster sizes, and most of it didn’t work. Thus, I am in the habit of trying more nodes than I need to start with. It helped that I was in the free-$200-credit phase of my brandy new Azure account, so I could make it rain k8s nodes with those free coupla hundies.</p><p>Output (failure, on the first attempt):</p><pre><code>Conflict(
  {
    &quot;error&quot;: {
      &quot;code&quot;: &quot;MissingSubscriptionRegistration&quot;,
      &quot;message&quot;: &quot;The subscription is not registered to use namespace &apos;microsoft.insights&apos;. See https://aka.ms/rps-not-found for how to register subscriptions.&quot;,
      &quot;details&quot;: [
        {
          &quot;code&quot;: &quot;MissingSubscriptionRegistration&quot;,
          &quot;target&quot;: &quot;microsoft.insights&quot;,
          &quot;message&quot;: &quot;The subscription is not registered to use namespace &apos;microsoft.insights&apos;. See https://aka.ms/rps-not-found for how to register subscriptions.&quot;
        }
      ]
    }
  }
)</code></pre><p>I looked up this error for a bit and got mostly nowhere. The link Microsoft provides to <a href="https://aka.ms/rps-not-found">https://aka.ms/rps-not-found</a> took me to a lovely page with a whole bunch of other errors that didn’t match mine. I got this inkling that maybe the person who wrote a blog post about AKS requiring <code>Microsoft.OperationalInsights</code> might not have been on the same page as the person who created an error message about <code>microsoft.insights</code>. And as it turns out, there actually is another required provider named <code>Microsoft.Insights</code>. Don’t worry about the case mismatch. It’s a long-standing inconsistency that Microsoft continues propagating. So I registered and showed another provider.</p><pre data-language="shell"><code>az provider register --namespace Microsoft.Insights</code></pre><pre data-language="shell"><code>az provider show -n Microsoft.Insights -o table</code></pre><p>Now… finally, my AKS create is working. It only took six minutes, which is not too shabby by my watch. YMMV depending on region, time of day, cluster resources, etc. And honestly, if you’re trying to get a cluster spun up and you don’t have time to multi-task and do something else… well, give me a call because I have some work you can help me with. I’m always looking for people who are real go-getters and want to optimize processes.</p><pre data-language="shell"><code>az aks create --resource-group bubbly-group --name demisec-cluster --enable-managed-identity --node-count 2 --enable-addons monitoring --generate-ssh-keys</code></pre><p>Output:</p><pre><code>Resource provider &apos;Microsoft.ContainerService&apos; used by this operation is not registered. We are registering for you.
Registration succeeded.
\ Running ..</code></pre><p>Something I realized here was that if Microsoft can register resource providers for me depending on the command I’m running… why couldn&apos;t they just register all three resource providers that are depended on by the command I’m running? They need a TPM. Herding cats. All I’m saying.</p><p>Next up, I got my credentials. I don’t know for certain that I needed <code>–admin</code>, but I was in a hurry. I had already waited an entire six minutes for the cluster to come up. What am I, made of minutes?</p><pre data-language="shell"><code>az aks get-credentials --admin --name demisec-cluster --resource-group bubbly-group</code></pre><p>Output:</p><pre><code>Merged &quot;demisec-cluster-admin&quot; as current context in /Users/peter/.kube/config</code></pre><p>The above <code>az</code> command sets your current k8s context so the subsequent <code>kubectl</code> commands know you’re talking to the cluster you just created.</p><h3>Start a k8s cluster in EKS</h3><p>These commands work to get a cluster up and running in <a href="https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html">AWS Elastic Kubernetes Service (EKS)</a>. You might notice there are fewer commands to run here compared to AKS and GKE. Part of that is my familiarity with AWS, and part of it is their permission model (including a shortcut they take, detailed below). These instructions presuppose you have AWS credentials in place for your IAM user, and the <code>awscli</code> and <code>eksctl</code> commands installed.</p><p>AWS docs will tell you to run <code>eksctl create cluster --name demisec-cluster --region us-east-2 --fargate</code> but that will give you a cluster where you can’t schedule pods on your nodes. So don’t do that. Their docs also talk at length about required IAM permissions and service linked roles, all of which are important and security job number zero, but ain’t nobody got time for that. I attached the built-in <code>AdministratorAccess</code> permission policy to my IAM user (in a sandbox AWS account, of course). Another shortcut I learned—the <a href="https://docs.aws.amazon.com/eks/latest/userguide/security_iam_id-based-policy-examples.html">IAM principal</a> that creates an EKS cluster will have <code>system:masters</code> permissions BUT WILL NOT APPEAR IN ANY VISIBLE CONFIGURATION, NO-WHERE, NO-HOW. Just make sure to keep track of it! Like, in a paper notebook you keep on your desk (yes, I checked, it’s actually 2023 and this is real cloud computing advice, <em>vive le papier eternel!</em>)</p><pre data-language="shell"><code>eksctl create cluster --name demisec-cluster --region us-east-2 --nodegroup-name bubbly-nodes --nodes-min 3 --nodes-max 6 --max-pods-per-node 5 -N 4</code></pre><p>Output:</p><pre><code>09:45:54 [ℹ]  eksctl version 0.147.0
09:45:54 [ℹ]  using region us-east-2
09:45:54 [ℹ]  setting availability zones to [us-east-2b us-east-2a us-east-2c]
…
10:09:06 [ℹ]  node &quot;ip-192-168-67-142.us-east-2.compute.internal&quot; is ready
10:09:06 [ℹ]  node &quot;ip-192-168-69-135.us-east-2.compute.internal&quot; is ready
10:09:06 [ℹ]  kubectl command should work with &quot;~/.kube/config&quot;, try &apos;kubectl get nodes&apos;
10:09:06 [✔]  EKS cluster &quot;demisec-cluster&quot; in &quot;us-east-2&quot; region is ready</code></pre><p>The above <code>eksctl create cluster</code> command helpfully includes setting your current k8s context so the subsequent <code>kubectl</code> commands know you’re talking to the cluster you just created.</p><p>Can confirm, just about 20 minutes later for me, I do have nodes:</p><pre data-language="shell"><code>kubectl get nodes</code></pre><p>Output:</p><pre><code>NAME                                           STATUS   ROLES    AGE     VERSION
ip-192-168-26-160.us-east-2.compute.internal   Ready    &lt;none&gt;   3m45s   v1.25.9-eks-0a21954
ip-192-168-34-234.us-east-2.compute.internal   Ready    &lt;none&gt;   3m44s   v1.25.9-eks-0a21954
ip-192-168-35-1.us-east-2.compute.internal     Ready    &lt;none&gt;   3m44s   v1.25.9-eks-0a21954
ip-192-168-65-243.us-east-2.compute.internal   Ready    &lt;none&gt;   3m44s   v1.25.9-eks-0a21954</code></pre><h3>Start a k8s cluster in GKE</h3><p>Now let’s do the same thing, but this time in <a href="https://cloud.google.com/kubernetes-engine/docs/deploy-app-cluster">Google Kubernetes Engine (GKE)</a>. Some advice: don’t use autopilot—I tried it, couldn’t schedule pods, then hit quota limits, no good. I’ll just save you a bunch of time and give you an actual working cluster if you follow the below commands. For those of us keeping an eye on the clock, cluster spin up took ~9 min on GKE.</p><p>First, set your gcloud project:</p><pre data-language="shell"><code>gcloud config set project main-bubbly-396813</code></pre><p>Then log in:</p><pre data-language="shell"><code>gcloud auth login</code></pre><p>Next, install the <a href="https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke">components</a> you need to authenticate to k8s within GKE:</p><pre data-language="shell"><code>gcloud components install gke-gcloud-auth-plugin</code></pre><p>And source the path <a href="https://stackoverflow.com/a/74733176">inclusions</a> for your shell:</p><pre data-language="shell"><code>source &quot;$(brew --prefix)/share/google-cloud-sdk/path.bash.inc&quot;</code></pre><p>I had some trouble in GCP with my per-region <code>Quota &apos;IN_USE_ADDRESSES&apos; exceeded</code>. From the console, after switching to a paid account, you can request that to be up’d to 32 (instead of the default 8). Then, you’ll be able to spin up a 4 node cluster with sufficient memory allowing you to schedule pods for your CI agent fleet.</p><pre data-language="shell"><code>gcloud container clusters create demisec-cluster --location=us-central1 --machine-type=e2-micro --disk-size=32 --cluster-ipv4-cidr=10.0.0.0/21 --num-nodes=4 --max-memory=64 --max-cpu=8 --enable-autoprovisioning --min-memory=32 --default-max-pods-per-node=16 --enable-ip-alias</code></pre><p>Set your k8s context so subsequent <code>kubectl</code> commands know you’re talking to the cluster you just created:</p><pre data-language="shell"><code>kubectl config use-context gke_main-bubbly-396813_us-central1_demisec-cluster</code></pre><div><h2 id="add-some-secrets">Add some secrets</h2></div><p>I wanted to make this part of the tutorial closer to how you’d handle secrets when automating cluster setup, so I decided to use secrets in k8s config YAML files rather than specify them as arguments to the <code>helm upgrade –install</code>.</p><p>We create the namespace first so we have a place to apply our secrets. Our example commands in documentation for <code>helm upgrade –install…</code> use a flag called <code>–create-namespace</code> but it seems like a chicken&amp;egg problem if you want to use your own sequence. Maybe k8s would handle this with ease, packing the chicken in separate plastic bags and making sure your eggs are on top of your pile of groceries… but personally, I like to keep my chickens&amp;eggs in sorted order, if for nothing else than for my own sanity.</p><p>Our <a href="https://github.com/buildkite/agent-stack-k8s#readme">readme</a> says that you can pass the arguments on the command line to the <code>helm upgrade install</code>,  but for production and automation usage, a file containing secrets is more practical.</p><pre data-language="shell"><code>kubectl create namespace buildkite</code></pre><p>Output:</p><pre><code>namespace/buildkite created</code></pre><p>If you had deep dark secrets, wouldn’t you store them in a truthful mirror? I know I would, if I did. That’s why I named this named secret <code>truthful-mirror</code>. Perfect for storing deep dark secrets. And I was listening to Elvis Costello at the time, pure coincidence, I’m sure.</p><pre data-language="yaml"><code>apiVersion: v1
kind: Secret
metadata:
  name: truthful-mirror
  namespace: buildkite
type: Opaque
data:
  BUILDKITE_AGENT_TOKEN: &lt;base64_encoded_agent_token&gt;
  BUILDKITE_TOKEN: &lt;base64_encoded_graphql_token&gt;</code></pre><p>We apply the config file containing two secrets needed for the agent stack: </p><ul><li><p>Buildkite agent token</p></li><li><p>Buildkite GraphQL token</p></li></ul><pre data-language="shell"><code>kubectl apply -f mr-costello.yml</code></pre><p>Output:</p><pre><code>secret/truthful-mirror created</code></pre><p>Next, we apply the config file containing the ssh private key needed for our agent stack to clone our very own private GitHub repos. If you are using a public repo or cloning via https instead of ssh, you don’t need this step:</p><pre data-language="shell"><code>kubectl apply -f git-secrets.yml</code></pre><p>Output:</p><pre><code>secret/yer-private-git-ssh-key created</code></pre><p>The <code>git-secrets.yml</code> file:</p><pre data-language="yaml"><code>apiVersion: v1
data:
  SSH_PRIVATE_RSA_KEY: &lt;base64_encoded_private_ssh_key&gt;
  SSH_PUBLIC_RSA_KEY: &lt;base64_encoded_public_ssh_key&gt;
immutable: false
kind: Secret
metadata:
  name: yer-private-git-ssh-key
  namespace: buildkite
type: Opaque</code></pre><h2>Install the Helm chart</h2><p>A <a href="https://helm.sh/docs/topics/charts/">Helm chart</a> is like a manifest for apps that run in k8s. There are other ways to get your app deployed to k8s, but this is our recommended option, and it comes pre-packaged in patented frustration-free packaging, so no cuts, scrapes, or scissors are required. The chart specifies everything you need for the agent stack containers to run like a well-oiled machine. Helm uses the chart to deploy your resources in place.</p><p>When installing the chart, make sure to replace <code>&lt;YOUR_ORG_SLUG&gt;</code> with your Buildkite organization slug, and replace <code>&lt;YOUR_CLUSTER_UUID&gt;</code> with the UUID of the cluster you&apos;d like to house this k8s-based queue within. For more details on retrieving your cluster UUID, take more than a gander (maybe an intense stare, for a suitable duration?) at this section of the agent-stack-k8s <a href="https://github.com/buildkite/agent-stack-k8s?tab=readme-ov-file#deploy-with-helm">readme</a>. You can keep the queue name as <code>montagne-de-reims</code> so the <strong>Add to Buildkite</strong> button of our demo pipeline should just work™.</p><pre data-language="shell"><code>helm upgrade --install agent-stack-k8s oci://ghcr.io/buildkite/helm/agent-stack-k8s --namespace buildkite --set config.org=&lt;YOUR_ORG_SLUG&gt; --set config.cluster-uuid=&lt;YOUR_CLUSTER_UUID&gt; --set config.tags=&apos;{queue=montagne-de-reims}&apos; --set agentStackSecret=truthful-mirror --version 0.24.1</code></pre><p>Output:</p><pre><code>oci://ghcr.io/buildkite/helm/agent-stack-k8s --namespace buildkite --set config.org=demo --set config.cluster-uuid=&lt;YOUR_CLUSTER_UUID&gt; --set config.tags=&apos;{queue=montagne-de-reims}&apos; --set agentStackSecret=truthful-mirror --version 0.24.1
Release &quot;agent-stack-k8s&quot; does not exist. Installing it now.
Pulled: ghcr.io/buildkite/helm/agent-stack-k8s:0.24.1
Digest: sha256:1f888b75050232efa0f173692ba37c018e1d3ed2b83f93876887b202d9d9ae5a
NAME: agent-stack-k8s
LAST DEPLOYED: Thu Feb 27 12:08:15 2024
NAMESPACE: buildkite
STATUS: deployed
REVISION: 1
TEST SUITE: None</code></pre><p>I want to call especial attention to two passed arguments here:</p><ul><li><p>First, the <code>--version 0.24.1</code> argument, because pinning your stack to a version is a best practice. You can leave off the version and it should get you latest, but if you’ve ever been bitten by the Docker latest tag considered <a href="https://vsupalov.com/docker-latest-tag/"><u>harmful</u></a>, you know how long it takes for that bite to heal. There’s serious venom in that whale’s teeth! If you’re using the <code>agent-stack-k8s</code> in production, I expect you’re going to IaC this set of commands right up and you can source control the <code>--version</code> number that you use and then carefully test moving to newer versions as you go. You can get the list of versions for the <code>agent-stack-k8s</code> on its <a href="https://github.com/buildkite/agent-stack-k8s/releases"><u>releases</u></a> page.</p></li><li><p>Second, the <code>--set config.tags=&apos;{queue=montagne-de-reims}&apos;</code>. The queue for <code>agent-stack-k8s</code> defaults to <code>kubernetes</code> but is easily overridden, for example, if you wanted to name your queue after one of the five champagne-producing regions. When running the stack at scale, it’s very likely you’ll find uses for multiple stacks that are isolated from each other. Maybe you’ll have one stack with <code>--set config.tags=&apos;{queue=terraform}&apos;</code> to run your IaC, and a <code>--set config.tags=&apos;{queue=buildymcbuildface}&apos;</code> to build the rest of your projects. Or perhaps you’d want a <code>--set config.tags=&apos;{queue=terraform-prod}&apos;</code> and <code>--set config.tags=&apos;{queue=terraform-dev}&apos;</code> to segment out different IaC for different classes of cloud accounts, each with the appropriate credential and network access to their corresponding resources.</p></li></ul><h2>Run a pipeline</h2><p>You’ve got your cluster up and running, secrets in place, and Helm chart with <code>agent-stack-k8s</code> ready to go. But how can we tell it’s all working? Why, by running a pipeline in Buildkite!</p><p><a href="https://github.com/pbuckley/spar-k8s-ling">https://github.com/pbuckley/spar-k8s-ling</a> is a public GitHub repo with an example pipeline based on this blog post. You can click <strong>Add to Buildkite</strong> to test it out with your <code>agent-stack-k8s</code>!</p><div>Image of A successful run of the k8s pipeline shown in the Buildkite UI</div><h2>Clean up</h2><p>Before you go, don’t forget to clean up when you’re done! If you learn nothing else from my example, I hope you learn that you don’t want to leave this thing on and run up an AWS bill from a blog post (like I did). Maybe not the world’s most expensive blog post, but definitely a contender for The Top 100 Most Expensive Blog Posts.<br></p><p>For kind, run:</p><pre data-language="shell"><code>kind delete cluster --name demisec-cluster</code></pre><p>For AKS, run:</p><pre data-language="shell"><code>az group delete --name bubbly-group --yes --no-wait</code></pre><pre data-language="shell"><code>az aks delete --name demisec-cluster --resource-group bubbly-group</code></pre><p>For EKS, run:</p><pre data-language="shell"><code>eksctl delete cluster --region=us-east-2 --name=demisec-cluster</code></pre><p>For GKE, run:</p><pre data-language="shell"><code>gcloud container clusters delete demisec-cluster --location us-central1</code></pre><h2>Conclusions and Oh Reservoirs</h2><p>Our tale has come to its inevitable conclusion, dear reader, and with the closing of the page, comes my time to exit, stage right, even. I hope this blog post has been in some way informative and you’ve learned something along the way. Maybe you’ll take this newfound knowledge and by the power of <s>grayskull</s> <code>agent-stack-k8s</code> and Buildkite go forth and autoscale across the clouds. But not the cumulus nor stratus, in no way should you ever attempt to autoscale across those multi-clouds, that would be dangerous. You’d fall right through! Without a parachute! Yikes!</p>