Modular Kubernetes Programming

Sidhartha Mani
Koki
Published in
4 min readJan 30, 2018

Modular. A system made up of individually evolvable pieces that can be assembled in different ways is said to be modular.

In programming, writing modular systems enables evolution at low cost. Each independent piece, called a module, can be changed on its own. The concept of modularity extends to Kubernetes manifests as well.

For instance, if an environment variable is used by a Pod, and the same environment variable is used by another Pod, then changes to this variable need to be propagated to both PodSpecs (the definition of each Pod). Following the principles of modular design, if the system allows it, one might only need to update one module — even though the change affects two components.

Modularity also establishes clear boundaries between different parts of the system. This allows people to focus only on the parts that matter to them.

For instance, the various concerns encapsulated in a PodSpec may require attention from different members of a software development team. A modular design for the Kubernetes manifests will allow each person to focus on their strengths, without understanding or knowing what the rest of the PodSpec looks like.

Moreover, Modules can be composed to form new specs. The cost of creating specs (i.e. manifests) from existing modules is much lower than creating a spec from scratch.

As of today, Kubernetes manifests by themselves are not modular. However, with Koki’s Short format, they can be.

A simple module can be used to create complex systems (Modular Orgiami — By Jacek Halicki — src)

Koki Short’s Module System

Koki Short aims to enhance the experience of reading, writing and modifying Kubernetes manifests. This is achieved not only by making individual Kubernetes resource fields more human-readable, but also by supporting modular resources.

It is possible to draw any boundary around or within a resource and call it a module in the Koki Short format. For instance, consider this PodSpec:

apiVersion: v1
kind: Pod
metadata:
labels:
db: rethinkdb
role: admin
name: rethinkdb-admin
spec:
containers:
- image: gcr.io/google_containers/rethinkdb:1.16.0_1
name: rethinkdb
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- containerPort: 8080
name: admin-port
- containerPort: 28015
name: driver-port
- containerPort: 29015
name: cluster-port
volumeMounts:
- mountPath: /data/rethinkdb_data
name: rethinkdb-storage
volumes:
- name: rethinkdb-storage
emptyDir: {}

The equivalent short syntax is: (obtained by short -f manifest.yaml)

pod:
containers:
- env:
- from: metadata.namespace
key: POD_NAMESPACE
expose:
- admin-port: 8080
- driver-port: 28015
- cluster-port: 29015
image: gcr.io/google_containers/rethinkdb:1.16.0_1
name: rethinkdb
volume:
- mount: /data/rethinkdb_data
store: rethinkdb-storage
labels:
db: rethinkdb
role: admin
name: rethinkdb-admin
version: v1
volumes:
rethinkdb-storage: empty_dir

This is more readable, and shorter (hence the name ‘short’). Now, consider the scenario where the admin service (which is listening on admin-port) is changed to listen on a different port.

The change has to be made in all Pods that are talking to the admin service. This can be achieved by creating a module for the admin port. First, let’s define the boundary of the module: (in bold)

pod:
containers:
- env:
- from: metadata.namespace
key: POD_NAMESPACE
expose:
- admin-port: 8080
- driver-port: 28015
- cluster-port: 29015
image: gcr.io/google_containers/rethinkdb:1.16.0_1
name: rethinkdb
volume:
- mount: /data/rethinkdb_data
store: rethinkdb-storage
labels:
db: rethinkdb
role: admin
name: rethinkdb-admin
version: v1
volumes:
rethinkdb-storage: empty_dir

We’ve defined the boundary as the value of admin-port— currently 8080. Let’s create a new module that holds this value. We’ll call it admin-port-module.yaml:

admin_port: 8080

In the original Pod spec, let’s use our new module instead. This is achieved using the imports keyword.

imports:
- admin_port: ./admin-port-module.yaml

pod:
containers:
- env:
- from: metadata.namespace
key: POD_NAMESPACE
expose:
- admin-port: ${admin_port}
- driver-port: 28015
- cluster-port: 29015
image: gcr.io/google_containers/rethinkdb:1.16.0_1
name: rethinkdb
volume:
- mount: /data/rethinkdb_data
store: rethinkdb-storage
labels:
db: rethinkdb
role: admin
name: rethinkdb-admin
version: v1
volumes:
rethinkdb-storage: empty_dir

Now, admin-port’s value is imported from the file admin-port-module.yaml. Let’s reconstitute the complete Kubernetes manifest (Pod spec) by combining all the modules:

$ short -k -f manifest.short.yamlapiVersion: v1
kind: Pod
metadata:
labels:
db: rethinkdb
role: admin
name: rethinkdb-admin
spec:
containers:
- env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: gcr.io/google_containers/rethinkdb:1.16.0_1
name: rethinkdb
ports:
- containerPort: 8080
name: admin-port
protocol: TCP
- containerPort: 28015
name: driver-port
protocol: TCP
- containerPort: 29015
name: cluster-port
protocol: TCP
resources: {}
volumeMounts:
- mountPath: /data/rethinkdb_data
mountPropagation: ""
name: rethinkdb-storage
volumes:
- emptyDir: {}
name: rethinkdb-storage

The containerPort for admin-port is set correctly. Now, if multiple manifests were to share the admin-port, they could benefit from using the module file instead of duplicating the port value in multiple places.

Conclusion

This articles touches upon the basics of the module system in Koki Short. You can learn more about Short or download examples to play with. You can also try out the Chrome extension to translate live on your browser!

Stay tuned for more deep dives into Modular Kubernetes programming using Koki Short!

--

--