Generic templated configuration management for Kubernetes, Terraform and other things
371
Kapitan is a tool to manage complex deployments using mixed templating frameworks.
Use your template of choice to manage your Kubernetes manifests, your documentation, your Terraform configuration or even simplify your scripts.
How is it different from Helm? Please look at our FAQ!
Kapitan needs Python 3.6+ (it still works with Python 2.7 but support has been removed in v0.12.0).
Install Python 3:
Linux: sudo apt-get update && sudo apt-get install -y python3.6-dev
Mac: brew install python3
Install Kapitan via pip:
$ pip3 install git+https://github.com/deepmind/kapitan.git --process-dependency-links
The example below compiles 3 targets inside the examples/kubernetes folder.
Each target represents a different namespace in a minikube cluster
These targets generate the following resources:
StatefulSet for ElasticSearch Master nodeStatefulSet for ElasticSearch Client nodeStatefulSet for ElasticSearch Data nodeService to expose ElasticSearch discovery portService to expose ElasticSearch service portStatefulSet for mySQLService to expose mySQL service portSecret for mySQLsetup.sh to configure kubectl context for this targetkubectl.sh to control this target
$ cd examples/kubernetes
$ kapitan compile
Compiled minikube-mysql
Compiled minikube-es
Compiled minikube-es-2
A component is an aplication that will be deployed to a kubernetes cluster. This includes all necessary kubernetes objects (StatefulSet, Services, ConfigMaps) defined in jsonnet. It may also include scripts, config files and dynamically generated documentation defined using Jinja templates.
This is a hierarchical database of variables that are passed to the targets during compilation.
By default, Kapitan will look for an inventory/ directory to render the inventory from.
There are 2 types of objects inside the inventory:
Classes define variables that are shared across many targets. You can have for example a component.elasticsearch class with all the default values for targets using elasticsearch. Or a production or dev class to enable / disable certain features based on the type of target.
You can always override values further up the tree (i.e. in the inventory target file or in a class that inherits another class)
Classifying almost anything will help you avoid repetition (DRY) and will force you to organise parameters hierarchically.
For example, the snipppet below, taken from the example elasticsearch class, declares what parameters are needed for the elasticsearch component:
$ cat inventory/classes/component/elasticsearch.yml
parameters:
elasticsearch:
image: "quay.io/pires/docker-elasticsearch-kubernetes:5.5.0"
java_opts: "-Xms512m -Xmx512m"
replicas: 1
roles:
master:
image: ${elasticsearch:image}
java_opts: ${elasticsearch:java_opts}
replicas: ${elasticsearch:replicas}
...
A target usually represents a single namespace in a kubernetes cluster and defines all components, scripts and documentation that will be generated for that target.
Inside the inventory target files you can include classes and define new values or override any values inherited from the included classes. For example:
$ cat inventory/targets/minikube-es.yml
classes:
- cluster.minikube
- component.elasticsearch
parameters:
target_name: minikube-es
kapitan:
vars:
target: ${target_name}
namespace: ${target_name}
compile:
- output_path: manifests
input_type: jsonnet
input_paths:
- components/elasticsearch/main.jsonnet
output_type: yaml
- output_path: scripts
input_type: jinja2
input_paths:
- scripts
- output_path: .
input_type: jinja2
input_paths:
- docs/elasticsearch/README.md
secrets:
recipients:
- dummy@recipient
namespace: ${target_name}
elasticsearch:
replicas: 2
Targets can also be defined inside the inventory.
.
├── components
│ ├── elasticsearch
│ │ ├── configmap.jsonnet
│ │ ├── deployment.jsonnet
│ │ ├── main.jsonnet
│ │ └── service.jsonnet
│ └── nginx
│ ├── configmap.jsonnet
│ ├── deployment.jsonnet
│ ├── main.jsonnet
│ ├── nginx.conf.j2
│ └── service.jsonnet
├── inventory
│ ├── classes
│ │ ├── cluster
│ │ │ ├── cluster1.yml
│ │ │ └── cluster2.yml
│ │ ├── component
│ │ │ ├── elasticsearch.yml
│ │ │ ├── nginx.yml
│ │ │ └── zookeeper.yml
│ │ └── environment
│ │ ├── dev.yml
│ │ └── prod.yml
│ └── targets
│ ├── dev-cluster1-elasticsearch.yml
│ ├── prod-cluster1-elasticsearch.yml
│ └── prod-cluster2-frontend.yml
├── lib
│ ├── kapitan.libjsonnet
│ └── kube.libjsonnet
└── targets
├── dev-cluster1-elasticsearch
│ └── target.yaml
├── prod-cluster1-elasticsearch
│ └── target.yaml
└── prod-cluster2-frontend
└── target.yaml
$ kapitan -h
usage: kapitan [-h] [--version] {eval,compile,inventory,searchvar,secrets} ...
Kapitan is a tool to manage kubernetes configuration using jsonnet templates
positional arguments:
{eval,compile,inventory,searchvar,secrets}
commands
eval evaluate jsonnet file
compile compile targets
inventory show inventory
searchvar show all inventory files where var is declared
secrets manage secrets
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
Additional parameters are available for each positional argument. For example:
$ kapitan compile -h
usage: kapitan compile [-h] [--search-path JPATH] [--verbose] [--no-prune]
[--quiet] [--output-path PATH] [--target-path PATH]
[--parallelism INT] [--secrets-path SECRETS_PATH]
[--reveal] [--inventory-path INVENTORY_PATH]
optional arguments:
-h, --help show this help message and exit
--search-path JPATH, -J JPATH
set search path, default is "."
--verbose, -v set verbose mode
--no-prune do not prune jsonnet output
--quiet set quiet mode, only critical output
--output-path PATH set output path, default is "."
--targets TARGETS [TARGETS ...], -t TARGETS [TARGETS ...]
targets to compile, default is all
--parallelism INT, -p INT
Number of concurrent compile processes, default is 4
--secrets-path SECRETS_PATH
set secrets path, default is "./secrets"
--reveal reveal secrets (warning: this will write sensitive
data)
--inventory-path INVENTORY_PATH
set inventory path, default is "./inventory"
This will compile all targets to compiled folder.
Accessing the inventory from jsonnet compile types requires you to import jsonnet/kapitan.libjsonnet, which includes the native_callback functions glueing reclass to jsonnet (amongst others).
The jsonnet snippet below imports the inventory for the target you're compiling and returns the java_opts for the elasticsearch data role:
local kap = import "lib/kapitan.libjsonnet";
local inventory = kap.inventory();
{
"data_java_opts": inventory.parameters.elasticsearch.roles.data.java_opts,
}
Jinja2 types will pass the "inventory" and whatever target vars as context keys in your template.
This snippet renders the same java_opts for the elasticsearch data role:
java_opts for elasticsearch data role are: {{ inventory.parameters.elasticsearch.roles.data.java_opts }}
Such as reading the inventory within jsonnet, Kapitan also provides a function to render a Jinja2 template file. Again, importing "kapitan.jsonnet" is needed.
The jsonnet snippet renders the jinja2 template in templates/got.j2:
local kap = import "lib/kapitan.libjsonnet";
{
"jon_snow": kap.jinja2_template("templates/got.j2", { is_dead: false }),
}
It's up to you to decide what the output is.
Rendering the inventory for the minikube-es target:
$ kapitan inventory -t minikube-es
...
classes:
- cluster.common
- cluster.minikube
- component.elasticsearch
environment: base
parameters:
cluster:
id: minikube
name: minikube
type: minikube
user: minikube
elasticsearch:
image: quay.io/pires/docker-elasticsearch-kubernetes:5.5.0
java_opts: -Xms512m -Xmx512m
masters: 1
replicas: 2
roles:
client:
image: quay.io/pires/docker-elasticsearch-kubernetes:5.5.0
java_opts: -Xms512m -Xmx512m
masters: 1
replicas: 2
data:
image: quay.io/pires/docker-elasticsearch-kubernetes:5.5.0
java_opts: -Xms512m -Xmx512m
masters: 1
replicas: 2
ingest:
image: quay.io/pires/docker-elasticsearch-kubernetes:5.5.0
java_opts: -Xms512m -Xmx512m
masters: 1
replicas: 2
master:
image: quay.io/pires/docker-elasticsearch-kubernetes:5.5.0
java_opts: -Xms512m -Xmx512m
masters: 1
replicas: 2
kapitan:
compile:
- input_paths:
- components/elasticsearch/main.jsonnet
input_type: jsonnet
output_path: manifests
output_type: yaml
- input_paths:
- scripts
input_type: jinja2
output_path: scripts
- input_paths:
- docs/elasticsearch/README.md
input_type: jinja2
output_path: .
secrets:
recipients:
- dummy@recipient
vars:
namespace: minikube-es
target: minikube-es
kubectl:
insecure_skip_tls_verify: false
minikube:
cpus: 4
memory: 4096
version: v0.25.0
mysql:
hostname: localhost
namespace: minikube-es
target_name: minikube-es
vault:
address: https://localhost:8200
show all inventory files where var is declared
$ kapitan searchvar parameters.elasticsearch.replicas
./inventory/targets/minikube-es-2.yml 2
./inventory/targets/minikube-es.yml 2
./inventory/classes/component/elasticsearch.yml 1
Helm?Before developing Kapitan, we turned to Helm in an attempt to improve from our old Jinja based templating system.
We quickly discovered that Helm did not fit well with our workflow, for the following reasons (which were true at the time of the evaluation):
Helm uses Go templates to define Kubernetes (yaml) manifests. We were already unsatisfied by using Jinja and we did not see a huge improvement from our previous system, the main reason being: YAML files are not suitable to be managed by text templating frameworks.Helm does not have a solution for sharing values across charts, if not through subcharts. We wanted to be able to have one single place to define all values for all our templates. Sharing data between charts felt awkward and complicated.Helm is component/chart based. We wanted to have something that would treat the whole of our deployments as a whole.In short, we feel Helm is trying to be apt-get for Kubernetes charts, while we are trying to take you further than that.
With Kapitan, we worked to de-compose several problems that most of the other solutions are treating as one.
Content type
Image
Digest
Size
108.6 MB
Last updated
almost 6 years ago
docker pull ramaro/kapitan