The ksonnet framework is centered around a couple of key concepts and terms. This reference gives a brief explanation of each. To get a more tangible sense of how these concepts tie together, see the Tutorial.
- Ksonnet
- Related
The following diagram shows how the ksonnet framework works holistically:
ksonnet's package management schema can be summed up as follows:
registry > package > prototype
Ex: incubator
repo > Redis package > redis-stateless
prototype
A ksonnet application represents a well-structured directory of Kubernetes manifests. This directory is autogenerated by ks init
. These manifests typically tie together in some way—for example, they might collectively define a web service like the following:
An environment consists of four elements, some of which can be pulled from your current kubeconfig context:
What | Example | Description | How is this tracked? |
---|---|---|---|
(1) Name | dev | A string used to identify a particular environment. Must be unique within a ksonnet app. | A directory under environments/ (e.g. environments/dev/ ). A name with slashes results in a hierarchical file structure. For example, us-west/staging creates environments/us-west/staging/ . |
(2) Server | https://cluster-name-with-hash.us-west-2.elb.amazonaws.com | The address and port of a Kubernetes API server. In other words, identifies a unique cluster. | Inside app.yaml |
(3) Namespace | dev | Specifically, a Kubernetes namespace. | Inside app.yaml |
(4) Kubernetes API version | version:v1.7.1 | The version of your cluster's Kubernetes API server, defaults to v1.8.0. | Used to generate appropriate files in lib/ksonnet-lib/ based on the specified version of the Kubernetes OpenAPI spec |
ksonnet allows you to deploy any particular application to multiple environments. Below is a visualization of two environments that represent different namespaces on the same cluster:
If you're wondering why you might deploy a common set of manifests to different environments, here are some potential use cases:
- Release Management (dev vs test vs prod)
- Multi-AZ (us-west-2 vs us-east-1)
- Multi-cloud (AWS vs GCP vs Azure)
A ksonnet application can be broken down into a series of discrete components:
Components can be as simple as a single Kubernetes resource (e.g. a Deployment) or as complex as a complete logging stack (e.g. EFK). More concretely, a component corresponds to a Kubernetes manifest in components/
. There are two ways to add this manifest:
-
Typically, you autogenerate it with the
ks generate
command. In this case, the manifest is expressed in a language called Jsonnet. -
Alternatively, you can manually drop in a file into
components/
. In this case, you can use either JSON or Jsonnet, as long the file is saved with the*.jsonnet
extension. (JSON is a subset of Jsonnet so no other changes are needed. YAML is not currently supported, but you can use an open-source CLI tool such as yaml2json to convert from YAML to JSON).This approach allows you to introduce ksonnet to existing codebases.
How does the autogeneration process work? When you use ks generate
, the component is generated from a prototype. The distinction between a component and a prototype is a bit subtle. If you are familiar with object oriented programming, you can roughly think of a prototype as a "class", and a component as its instantiation:
All of the component files in an app can be deployed to a specified environment using ks apply
.
Prototypes are examples that you can use as the basis for your components. By ks generate
-ing components from prototypes, you can quickly move from a blank app to multiple complete manifests.
Although you can use these components right away, they are really intended as a starting point. If a prototype-generated component does not fully address your needs, you can directly modify its Jsonnet afterwards (e.g. adding parameters).
By itself, a prototype is a pre-written but incomplete manifest:
Why is this useful? Prototypes allow you to avoid copying and pasting boilerplate code, and to focus on the parts of your configuration that are specific to your app. Specifically, during ks generate
, you can specify certain command-line parameters to "fill-in-the-blanks" of a prototype and output a complete manifest into the components/
directory:
Out of the box, ksonnet comes with some system prototypes (like io.ksonnet.pkg.deployed-service
) that you can explore with the various ks prototype
commands. See package and registry for information on downloading or sharing additional prototypes.
Parameters allow you to customize your prototypes — both at the time of their creation, and after the fact. You can use the various ks param
commands to view or modify current parameters. Params can be set globally or per-environment.
Under the hood, the ks param
commands update a couple of local Jsonnet files, so that you always have a version-controllable representation of what you ks apply
onto your Kubernetes cluster. These are structured as follows:
- App params (
components/params.libsonnet
) — treated like defaults- (Optional) Global params
- Manually specified by editing the Jsonnet
- Used to make a variable accessible to multiple components (e.g. if the number of Redis and nginx replicas are related)
- Component-specific params
- e.g. 80 for
deployment-example.port
- Populated from
ks generate
- e.g. 80 for
- (Optional) Global params
- Per-environment params (
environments/<env-name>/params.libsonnet
) — override app params, similar to inheritance- Component-specific params only
For example, you can use params to ensure that you have 3 Redis replicas in your prod environment and 1 in dev, because prod needs to handle higher traffic.
Modules provide a way for you to share components across environments. More concisely, a module refers to a subdirectory in components/
containing its own params.libsonnet
.
For example, ks module create <module-name>
creates components/<module-name>/params.libsonnet
.
ksonnet will use the components defined in components/
by default. Modules provide a clean way to make your configurations more modular. If you want to use another set of components or use specific components, modules provides a way to do this without initializing another ksonnet app.
A module can:
- be used across multiple environments similar to components.
- have a nested structure to group components in a more selective way.
- be used in conjunction with additional modules for a given environment.
Prototypes often come in sets, where each prototype offers a slightly different "flavor" of a common base (e.g. redis-persistent
and redis-stateless
are two possible ways of deploying a Redis datastore).
Given that ksonnet is designed around modularity, a common pattern is to refactor most of the shared prototype code into a single parts library. This might look something like the following:
{
parts:: {
deployment:: {
persistent(...)::
...,
nonPersistent(...)::
...,
base(...)::
...,
},
networkPolicy:: {
allowExternal(...)::
...,
denyExternal(...)::
...,
},
...
}
}
Once a library like this is established, different parts like redis.parts.deployment.persistent
can be mixed and matched to produce new prototypes. Intuitively, this looks a bit like the following:
NOTE: Parts do not have to be 1:1 with Kubernetes API resources like Deployments. This is just a relatively intuitive way to organize them.
Prototypes that are distributed via ksonnet packages typically adhere to the pattern described above.
A package contains:
- A set of related prototypes (e.g.
redis-persistent
,redis-stateless
) - Associated helper libraries that define the prototype parts (e.g.
redis.libsonnet
)
Packages allow you to easily distribute and reuse code in any ksonnet application, using the various ks pkg
commands. The CLI writes package code into the vendor/
directory.
To be recognized and imported by ksonnet, packages need to follow a specific schema. See the annotated file tree below, as an example:
.
├── README.md // Human-readable description of the package
├── parts.yaml // Provides metadata about the package
├── prototypes // Can be imported and used to generate components
│ ├── redis-all-features.jsonnet
│ ├── redis-persistent.jsonnet
│ └── redis-stateless.jsonnet
└── redis.libsonnet // Helper library, includes prototype parts
parts.yaml
metadata is used to populate the output of the ks prototype describe
command. The official packages in ksonnet/parts/incubator
also use parts.yaml
to autogenerate README.md
documentation.
You can take a look at the nginx and Redis packages as additional examples.
Registries are essentially repositories for packages. (We mean registry here in the same sense as registries for container images). Registries are identified by a registry.yaml
in their root that declares a list of packages.
You can use default or custom registries:
-
By default, ksonnet allows you do download packages from the
ksonnet/parts/incubator
registry. -
You can set up a registry with three different protocols:
- Github - a Github URI
- Filesystem - a valid path to a local registry
- Helm - a URI to a Helm repository
A registry contains a
registry.yaml
file with directories containing packages similar to the following structure:. ├── nginx // nginx package │ ├── README.md │ ├── nginx.libsonnet │ ├── parts.yaml │ └── prototypes │ ├── nginx-server-block.jsonnet │ ... ├── redis // redis package │ ├── README.md │ ├── parts.yaml │ ├── prototypes │ │ ├── redis-all-features.jsonnet │ │ ... │ └── redis.libsonnet └── registry.yaml // Lists all packages in the registry
- For more details on the package schema, see the package definition.
- For hand-on references (e.g. for
registry.yaml
andparts.yaml
), see the files inksonnet/parts/incubator
.
Use the various ks registry
commands to list available registries, add new ones, and see what packages they contain.
When you’re trying to run code on a Kubernetes cluster, there’s a relatively clean separation between:
- "What you run" (your code, containerized in an image)
- "How you run it" (YAML or JSON manifests)
The "how" — your YAML and JSON manifests — declare the API resources that your code uses to actually run ON Kubernetes. Example resources include Pods and Deployments.
One of ksonnet's key features is that it allows you to write more concise manifests, using a language called Jsonnet.
ksonnet uses Jsonnet to express all of your components/
manifests (and associated configuration files, like components/params.libsonnet
).
Jsonnet is a data templating language — for simplicity, think of it as a fancier superset of JSON, one that supports features like variables and object concatenation. If you've ever had to copy and paste large chunks of YAML or JSON manifests, Jsonnet avoids this problem!
You can see how much more concise it is by comparing the same manifest, written in two different languages:
Note that we've omitted Jsonnet's import
lines in this example.
Jsonnet is also JSON-compatible, meaning that you can drop parts of your legacy manifests into your ksonnet manifests, without having to rewrite them all at once. (It is more common to have Kubernetes manifests in YAML than JSON, but there are several open-source CLI tools such as yaml2json that can do this conversion for you).