(This is part of a larger series on scaling autonomous components. If you like, you can start at the beginning.)
Today’s the daaaaaaay! Watch me try to flex my k8s knowledge here.
Okay, from the last time, we learned that scaling Eventide components requires starting the consumers with an identifier and that it makes sense to include the group_size and group_member in that identifier. It’s up to you to figure out how to inject those values into your running component, but here’s how I’ve done it before with k8s.
You can’t do this with a regular Deployment because Deployments create random identifiers for your pods. If you have 2 pods running, you need one to know that it’s instance 0 and the other to know that it’s instance 1. If, say, instance 1 goes down, the pod that replaces it needs to also know that it’s instance 1.
I won’t post the entire yaml file, but we use the StatefulSet construct instead of a Deployment. Within the yaml, you’ll have a metadata object with a name key. Suppose we’re dealing with the account-component , and that will look something like:
# stuff before
metadata:
# other metadata
name: account-component
Sibling to metadata you’ll have spec. Spec has a replicas key. We’ll set it to 2 for our example.
spec will also have template, with child spec (yes, repeated), with child containers. containers is an array, and its members will have attribute env, another array. You’ll put whatever env vars you need in there, but for our purposes, we’ll include two, namely GROUP_SIZE and POD_ID. All this will look something like:
# stuff before
metadata:
# other metadata
name: account-component
spec:
# stuff
replicas: 2
template:
spec:
containers:
- name: account-component
# other properties
env:
- name: GROUP_SIZE
value: '2'
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
There’s more that goes into this file than this, of course, but I trust that if you’re going this far, you know enough about k8s to fill in the rest. These are just the essential bits to make it work for our consumer group.
With that in place then, k8s will supply you GROUP_SIZE and POD_ID as env vars, both strings, mind you. POD_ID will take your [metadata.name](http://metadata.name) and append -<n> where n goes from 0 to your number of replicas minus 1.
To use these in your component’s start.rb, you’d do some like:
module AccountComponent
module Start
group_size = ENV['GROUP_SIZE'].to_i
pod_id = ENV['POD_ID']
group_member = pod_id.split('-').last.to_i
identifier = "#{group_size}-#{group_member}"
Consumers::Commands.start("account:command", group_size:, group_member:, identifier:)
# other consumer...
end
end
Note that we have to split the POD_ID env var because it is the concatenation of the name and the index. As of this writing, there isn’t a value k8s can supply that is just the index. Maybe once Eventide has taken the world by storm, we can flex our collective might and get them to add that index so we can all scale our components. Also, the GROUP_SIZE is a string, so we have to convert it to an integer.
You may want to extract that fiddling with the env vars into a different file, especially if you need to repeat this pattern, but that’s the gist of using StatefulSets to have concurrent instances of a component running in k8s.
I’ve never been a fan of articles that post things like these yaml snippets without the full context. It’s a fairly large file though. Let me know if you’d like to see the entire example, and I’ll figure out how to post it somewhere.
