Kapitan Blog
Published in

Kapitan Blog

Keep your ship together with Kapitan

NEW: Katacoda scenario!

Manage complexity with Kapitan

Kapitan Generator Libraries

Diesel Ship Marine Generator, for Power, 230v
Diesel Ship Marine Generator, for Power, 230v
  • [RELEASED] A kadet manifest generator library to quickly create Kubernetes “workloads” manifests by simply defining them in the inventory. Get started with something as simple as:
parameters:
components:
api-server:
image: gcr.io/your-company/api:latest
  • A kadet pipelines generatorlibrary to quickly create Spinnaker pipelines for the above defined workloads
  • A kadet terraform generator library to create Terraform configurations.
  • A set of helper scripts which will make easy to get up and running with Kapitan.

A sneak peek — generating manifests

Pre-requisites:

  • docker
  • gcloud (the example is on GCP)
  • kapitan
  • yq
  • kapitan generators (released)

Suggested read:

Your first target file

classes:
- common
parameters:
target_name: dev
components:
echo-server:
image: inanimate/echo-server
compiled/dev/
├── docs
├── manifests
│ └── echo-server-bundle.yml
└── scripts
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: echo-server
name: echo-server
spec:
replicas: 1
selector:
matchLabels:
app: echo-server
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: echo-server
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- echo-server
topologyKey: kubernetes.io/hostname
weight: 1
containers:
- image: inanimate/echo-server
imagePullPolicy: IfNotPresent
name: echo-server
restartPolicy: Always
terminationGracePeriodSeconds: 30

Exposing the service

classes:
- common
parameters:
target_name: dev
namespace: ${target_name}
components:
echo-server:
image: inanimate/echo-server
service:
type: ClusterIP
ports:
http:
service_port: 80
...
containers:
- image: inanimate/echo-server
imagePullPolicy: IfNotPresent
name: echo-server
ports:
- containerPort: 80
name: http
protocol: TCP
apiVersion: v1
kind: Service
metadata:
labels:
app: echo-server
name: echo-server
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: echo-server
sessionAffinity: None
type: ClusterIP
  • The service assumes that the echo-server runs on port 80. From the documentation, it looks as if the service is actually running on port 8080 instead.
  • We would want the service to be exposed using a LoadBalancer service, so let’s change that.
  • We would like a readiness probe
classes:
- common
parameters:
target_name: dev
namespace: ${target_name}
components:
echo-server:
image: inanimate/echo-server
service:
type: LoadBalancer
ports:
http:
service_port: 80
container_port: 8080
healthcheck:
type: http
port: http
probes: ['readiness']
path: /
timeout_seconds: 3
...
containers:
- image: inanimate/echo-server
imagePullPolicy: IfNotPresent
name: echo-server
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: http
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 3
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: echo-server
name: echo-server
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: echo-server
sessionAffinity: None
type: LoadBalancer

Adding Environment Variables

classes:
- common
parameters:
target_name: dev
namespace: ${target_name}
echo_server:
port: 8081
components:
echo-server:
image: inanimate/echo-server
env:
PORT: ${echo_server:port}
POD_NAME:
fieldRef:
fieldPath: metadata.name
POD_NAMESPACE:
fieldRef:
fieldPath: metadata.namespace
POD_IP:
fieldRef:
fieldPath: status.podIP
service:
type: LoadBalancer
ports:
http:
service_port: 80
container_port: ${echo_server:port}
      containers:
- env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: PORT
value: 8081
image: inanimate/echo-server
imagePullPolicy: IfNotPresent
name: echo-server
ports:
- containerPort: 8081
name: http
protocol: TCP

Adding secrets

classes:
- common
parameters:
target_name: dev
namespace: ${target_name}
echo_server:
port: 8081
components:
echo-server:
image: inanimate/echo-server
env:
PORT: ${echo_server:port}
POD_NAME:
fieldRef:
fieldPath: metadata.name
POD_NAMESPACE:
fieldRef:
fieldPath: metadata.namespace
POD_IP:
fieldRef:
fieldPath: status.podIP
SECRET_PASSWORD:
secretKeyRef:
key: echo_server_password
service:
type: LoadBalancer
healthcheck:
type: http
port: http
probes: ['readiness']
path: /
timeout_seconds: 3
ports:
http:
service_port: 80
container_port: ${echo_server:port}
secret:
items: ['echo_server_password']
data:
echo_server_password:
value: ?{plain:targets/${target_name}/echo_server_password||randomstr}
  • ?{plain:targets/${target_name}/echo_server_password||randomstr} will create a random string, and store it in on git. Because we have used the plain backend, we will be storing it in cleartext. User gkms or other secrets backend if you care about your secrets
  • The SECRET_PASSWORD env variable will have the content of the generated password. Now because we have decided to test the plain backend, you will see it in clear text in the manifest. Otherwise if would be encrypted and you would only see a secure tag.
  • The items instruction will also mount the secret as a volume, and only exposed the selected item. This means you will be also able to access the content of the secret using /opt/secrets/echo_server_password
compiled/dev/
├── docs
├── manifests
│ ├── echo-server-bundle.yml
│ └── echo-server-secret.yml
└── scripts
parameters:
echo_server:
port: 8081
components:
echo-server:
image: inanimate/echo-server
env:
PORT: ${echo_server:port}
POD_NAME:
fieldRef:
fieldPath: metadata.name
POD_NAMESPACE:
fieldRef:
fieldPath: metadata.namespace
POD_IP:
fieldRef:
fieldPath: status.podIP
SECRET_PASSWORD:
secretKeyRef:
key: echo_server_password
service:
type: LoadBalancer
healthcheck:
type: http
port: http
probes: ['readiness']
path: /
timeout_seconds: 3
ports:
http:
service_port: 80
container_port: ${echo_server:port}
secret:
items: ['echo_server_password']
data:
echo_server_password:
value: ?{plain:targets/${target_name}/echo_server_password||randomstr}
classes:
- common
- components.echo-server
parameters:
target_name: dev
namespace: ${target_name}
classes:
- common
- components.echo-server
parameters:
target_name: prod
namespace: ${target_name}
./kapitan compile
Compiled dev (0.29s)
Compiled prod (0.29s)
compiled/prod/
├── docs
├── manifests
│ ├── echo-server-bundle.yml
│ └── echo-server-secret.yml
└── scripts

Final words

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alessandro De Maria

#father #kapitan #devops. Head of SRE at Synthace. Ex DeepMind. Ex Google. Opinions are my own