Walkthrough
Let's do something useful: generate some Kubernetes-bound YAML. We'll create the resource descriptors for a tiny Pod running nginx
along with a Service.
We will recreate the hello-kerbi project from the examples folder. Read along or clone the folder to play with it locally. The directory structure by the end of the tutorial will be:
├───kerbifile.rb
├───pod-and-service.yaml.erb
├───consts.rb
├───helpers.rb
├───values
│ ├───values.yaml
│ └───production.yaml
1. Basic Pod & Service
Starting simple, almost with static YAML, only interpolating release_name
:
class HelloWorld < Kerbi::Mixer
def mix
push file("pod-and-service")
end
end
Kerbi::Globals.mixers << HelloWorld
A few Observations:
release_name
gets its value - demo
- from our command line argument
file
("pod-and-service")
omits the .yaml.erb
extension and still works.
push
``file()
is just passing an Array<Hash>
returned by file()
, explained here.
require
``"kerbi"
is nowwhere to be found. That's normal, the kerbi
executable handles it.
2. Adding Values
The whole point of templating engines is to modulate the output based on information passed at runtime. Like in Helm, the primary mechanism for this is values.
pod:
image: nginx
service:
type: ClusterIP
Running kerbi template default .
yields the output you would expect. We can also choose to also apply our production.yaml
by using -f
flag in the command:
$ kerbi template demo . -f production.yaml
This makes our Service become a LoadBalancer:
kind: Service
#...
spec:
type: LoadBalancer
#...
Finally, we can use --set
to achieve the same effect, but without creating a new values file:
$ kerbi template demo . --set service.type=LoadBalancer
3. Patching After Loading
Suppose we want to add labels and annotations to our Pod and Service. Because this will happen a lot, we can use patched_with
to patch several resources in one shot, rather than per-resource.
class HelloWorld < Kerbi::Mixer
def mix
patched_with file("common/metadata") do
push file("pod-and-service")
end
end
end
Kerbi::Globals.mixers << HelloWorld
Notice how patched_with()
method accepts the same Hash | Array<Hash>
as push()
that we have been using up to now. This means you can use the same methods to construct arbitrarily complex patches.
4. Getting DRY & Organized
The great think about Kerbi is that it's just a normal Ruby program! You can do whatever makes sense for your project, such as DRYing up our ERB. We'll do three such things to inspire you:
Start using an outer namespace -
HelloKerbi
- to prevent any name collisionsCreate a module to store constants -
HelloKerbi::Consts
Create a helper module we use in our template files -
HelloKerbi::Helpers
require_relative 'consts'
require_relative 'helpers'
module HelloKerbi
class MainMixer < Kerbi::Mixer
include Helpers
def mix
patched_with file("common/metadata") do
push file("pod-and-service")
end
end
end
end
Kerbi::Globals.mixers << HelloKerbi::MainMixer
5. Interactive Console
Another thing that sets Kerbi apart is the ability to touch your code. Using the kerbi console
command, we'll open up an IRB session do what we please with our code:
$ kerbi console
irb(kerbi):001:0> HelloKerbi
=> HelloKerbi
irb(kerbi):002:0> values
=> {:pod=>{:image=>"nginx"}, :service=>{:type=>"ClusterIP"}}
irb(kerbi):004:0> mixer = HelloKerbi::Mixer.new(values)
=> #<HelloKerbi::Mixer:0x000056438ba4c3b8 @output=[], @release_name="default", @patch_stack=[], @values={:pod=>{:image=>"nginx"}, :service=>{:type=>"ClusterIP"}}>
irb(kerbi):005:0> mixer.run
=> {:apiVersion=>"v1", :kind=>"Pod", :metadata=>{:name=>"hello-kerbi", :namespace=>"default", :annotations=>{:author=>"xavier"}, :labels=>{:app=>"hello-kerbi"}}, :spec=>{:containers=>[{:name=>"main", :image=>"nginx:alpine"}]}}
{:apiVersion=>"v1", :kind=>"Service", :metadata=>{:name=>"hello-kerbi", :namespace=>"default", :annotations=>{:author=>"xavier"}, :labels=>{:app=>"hello-kerbi"}}, :spec=>{:type=>"ClusterIP", :selector=>{:app=>"hello-kerbi"}, :ports=>[{:port=>80}]}}
6. State
You need a way to keep track of the values you use to generate your latest manifest. If you applied a templated manifest that used --set backend.image=2
and then later --set frontend.image=2
, then the second invokation would revert backend.image
to its default from values.yaml
. Big problem.
Kerbi has an inbuilt state mechanism that lets you store the values it computes during $ kerbi template
, and then retrieve those values again. Kerbi uses a ConfigMap
(or Secret
if you tell it to)in your cluster to store the data. Tell Kerbi to create that ConfigMap
:
$ kerbi release init demo
namespaces/demo: Created
demo/configmaps/kerbi-db: Created
We now have one release:
$ kerbi release list
NAME BACKEND NAMESPACE RESOURCE STATES LATEST
demo ConfigMap demo kerbi-demo-db 0
6. Writing State
Now let's template again, but with a new option --write-state
:
$ kerbi template demo . \
--set pod.image=ruby \
--write-state @new-candidate \
> manifest.yaml
Let's use Kerbi's state inspection commands: list
and show
:
$ kerbi state list demo
TAG REVISION MESSAGE ASSIGNMENTS OVERRIDES CREATED_AT
[cand]-brave-toner 2 1 4 seconds ago
The meanings of special words like @candidate
, @new-candidate
, and @latest
are covered in the State System guide.
7. Promoting the Candidate State
Now for the sake of realism, let's run kubectl apply -f manifest
. That worked, so we feel good about these values. Let's promote our latest state:
$ kerbi state promote demo @candidate
Updated state[brave-toner].tag from [cand]-brave-toner => brave-toner
The name of our state has changed:
$ kerbi state list demo
TAG MESSAGE ASSIGNMENTS OVERRIDES CREATED_AT
0.0.1 2 1 a minute ago
8. Retrieving State
It's finally time to make use of the state we saved. Let's template the manifest again, with a new value assignment, but also with the old pod.image=ruby
assignment:
$ kerbi template demo . \
--read-state @latest \
--write-state @new-candidate \
> manifest.yaml
We see in the manifest that the values from the old state (pod.image=ruby
) were successfully applied which is what we wanted to do. Inspecting the state shows we have a new entry, as expected:
$ kerbi state list
TAG REVISION MESSAGE ASSIGNMENTS OVERRIDES CREATED_AT
[cand]-warm-tap 2 2 11 seconds ago
0.0.1 2 1 10 minutes ago
9. In Your CD Pipeline
Putting it all together, the following shows what a simple Kubernetes deployment script using Kerbi could look like.
$ kerbi state init demo .
$ kerbi config set k8s-auth-type <your-strategy>
$ kerbi template demo . \
--set <however you get your vars in> \
--read-state @latest \
--write-state @new-candidate \
> manifest.yaml
$ kubectl apply --dry-run=server -f manifest.yaml \
&& kerbi state promote demo @candidate \
&& kubectl apply -f manifest.yaml
10. Revisions
Work in progress.
The following is a preview. The command group $ kerbi revision
does not work.
$ kerbi revision push 1.0.1
Last updated