How-to guide / Author packages and clusters
Create a local package
Use a local package when a workload belongs to your project rather than the shared package catalogue. A package groups its configuration interface and Kubernetes resources so the cluster can create one or more instances from it.
This guide creates web-package.nix beside cluster.nix. The complete example
is available in the
how-to/application scenario.
Create the package file
Section titled “Create the package file”Start with a function that accepts the helpers used by the package. Add package metadata to the returned attribute set:
17 meta = {18 version = "1.0.0";19 description = "Small nginx application used by the Kix how-to guides";20 };The version identifies the package definition. Change it when you make a
meaningful package release. The description appears in package inspection and
generated reference material.
Add the package resources
Section titled “Add the package resources”The package’s build value creates named parts. This example begins with a
ConfigMap containing the page nginx will serve:
74 content = scope.mkResource {75 apiVersion = "v1";76 kind = "ConfigMap";77 inherit name;78 data."index.html" = "${config.message}\n";79 };scope.mkResource adds the instance namespace, Kix metadata, and an out
interface. Other parts should refer to self.content.out.name rather than
repeating the eventual Kubernetes name.
Add the Deployment:
83 deployment =84 {85 inherit name;86 spec = {87 replicas = config.replicas;88 selector.matchLabels = scope.selectorLabels;89 template.spec.containers = [90 {91 name = "nginx";92 image = "docker.io/library/nginx:1.27-alpine";93 imagePullPolicy = "IfNotPresent";94 ports = [ (kix.port "http" 80) ];95 env = kix.mkEnvVars {96 APP_ENV = config.environment;97 };98 readinessProbe.httpGet = {99 path = "/";100 port = "http";101 };102 resources = {103 requests = {104 cpu = "10m";105 memory = "16Mi";106 };107 limits.memory = "64Mi";108 };109 }110 ];111 };112 }113 |> kix.withMounts [ contentMount ]114 |> scope.mkDeployment;scope.mkDeployment fills in the standard workload metadata and validates the
Deployment shape. The example uses scope.selectorLabels for the workload’s
selector and pod-template labels. Kix also adds
app.kubernetes.io/name to workloads built through the scope helpers.
Finish with a Service and select the package root:
122 service = self.deployment |> kix.service.fromWorkload { inherit name; } |> scope.mkResource;123
124 root = self.service;kix.service.fromWorkload reads the selector and container ports from the
Deployment, so the Service cannot drift onto a different set of pods. It also
carries the workload dependency into the Service before scope.mkResource
seals it.
The root is the resource that represents the instance to its consumers. A service-shaped package normally uses its Service as the root.
Import the package into the cluster
Section titled “Import the package into the cluster”Import the file before calling kix.buildCluster:
12 webPackage = import ./web-package.nix;Assign the imported value to an instance just like a catalogue package:
1instances.apps.web = {2 package = webPackage;3};Kix calls the package function for the instance and supplies scope, lib,
and kix. You do not need to call the package function yourself.
Check the package
Section titled “Check the package”Run the cluster checks after adding the package and instance:
❱ kix check how-to-application
TOOL RESULT DETAILS
eval pass 17 manifests evaluated
scorecard pass 0 errors, 11 warnings, 2 info Then render the manifests and inspect the resources owned by the instance:
❱ kix build how-to-application Show outputHide output · 508 lines
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
kix.run/identity-hash: gvrlspcfgh9a2qnsx5kqxw9f4l3x52jj
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
name: activations.kix.run
spec:
group: kix.run
names:
kind: Activation
listKind: ActivationList
plural: activations
singular: activation
scope: Cluster
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
type: string
kind:
type: string
metadata:
type: object
spec:
type: object
x-kubernetes-preserve-unknown-fields: true
status:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
served: true
storage: true
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
kix.run/identity-hash: pk4hih6jm4yj336rlaqk2qycz2k46xvs
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
name: packageinstances.kix.run
spec:
group: kix.run
names:
kind: PackageInstance
listKind: PackageInstanceList
plural: packageinstances
singular: packageinstance
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
type: string
kind:
type: string
metadata:
type: object
spec:
type: object
x-kubernetes-preserve-unknown-fields: true
status:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
served: true
storage: true
subresources:
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,asa4ywz8yjjql7grm2rnm774q96ncs3m
kix.run/identity-hash: hd2br513fjyfgbl9sf97ank37hqzrmzl
kix.run/package: preview
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: preview
app.kubernetes.io/managed-by: kix
app.kubernetes.io/name: preview
name: preview
namespace: how-to-app
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: preview
app.kubernetes.io/name: preview
template:
metadata:
labels:
app.kubernetes.io/instance: preview
app.kubernetes.io/name: preview
spec:
containers:
- env:
- name: APP_ENV
value: preview
image: docker.io/library/nginx:1.27-alpine
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: http
protocol: TCP
readinessProbe:
httpGet:
path: /
port: http
resources:
limits:
memory: 64Mi
requests:
cpu: 10m
memory: 16Mi
volumeMounts:
- mountPath: /usr/share/nginx/html
name: content
readOnly: true
volumes:
- configMap:
name: preview
name: content
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kix.run/depends-on: 4x75h2z7rsj3dvrdg9vzqc8l789xvm4w,8767b7nzgc1x5bpfa9gv71cpk9z8h0iw
kix.run/identity-hash: kr7i34wz1ja58c0vf9gizx8161ji0idk
kix.run/package: production
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: production
app.kubernetes.io/managed-by: kix
app.kubernetes.io/name: production
name: production
namespace: how-to-app
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/instance: production
app.kubernetes.io/name: production
template:
metadata:
labels:
app.kubernetes.io/instance: production
app.kubernetes.io/name: production
spec:
containers:
- env:
- name: APP_ENV
value: production
image: docker.io/library/nginx:1.27-alpine
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: http
protocol: TCP
readinessProbe:
httpGet:
path: /
port: http
resources:
limits:
memory: 64Mi
requests:
cpu: 10m
memory: 16Mi
volumeMounts:
- mountPath: /usr/share/nginx/html
name: content
readOnly: true
volumes:
- configMap:
name: production
name: content
---
apiVersion: batch/v1
kind: Job
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,cd3l507fvaf0wg5azfza49x6wjsiiynz,dlzbv1nahmar8f7b9jm6rmyvmqczmzdb,requires:jh345cnnffy1bnj4cz5x0hwq6ibxvkkr
kix.run/identity-hash: 4sa9i19dly4hh06r5alinlr7pf289igd
kix.run/package: production
kix.run/package-namespace: how-to-app
kix.run/rerun: on-change
labels:
app.kubernetes.io/component: health-check
app.kubernetes.io/instance: production
app.kubernetes.io/managed-by: kix
app.kubernetes.io/name: production-health
name: production-health
namespace: how-to-app
spec:
activeDeadlineSeconds: 150
backoffLimit: 0
template:
metadata:
labels:
app.kubernetes.io/component: health-check
app.kubernetes.io/name: production-health
spec:
automountServiceAccountToken: false
containers:
- command:
- /bin/sh
- -eu
- /probe/health-check.sh
env:
- name: APP_URL
value: http://production.how-to-app.svc.cluster.local:80
- name: RETRY_FOR
value: "60"
image: docker.io/curlimages/curl:8.14.1@sha256:9a1ed35addb45476afa911696297f8e115993df459278ed036182dd2cd22b67b
name: probe
resources:
limits:
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- mountPath: /probe
name: production-health-script
readOnly: true
restartPolicy: Never
securityContext:
runAsGroup: 65534
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
volumes:
- configMap:
name: production-health-script
name: production-health-script
---
apiVersion: kix.run/v1alpha1
kind: Activation
metadata:
annotations:
kix.run/built-via: /nix/store/kb43x7wid50nygsilzn17z1bll2g2bxh-kix-activation-how-to-application.json
kix.run/depends-on: 84pfjn0kg74qqvp291b01dzd35ngql6z,jh345cnnffy1bnj4cz5x0hwq6ibxvkkr,mclb0sk2adzjddj0mnckn0n715c5kgq4,r5sgwjk8w3m41l0xaj0s16xxsacd0nk6,requires:gvrlspcfgh9a2qnsx5kqxw9f4l3x52jj
kix.run/identity-hash: 6rhi3wpk9imlx73n8kbcwbmkxmry1744
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
kix.run/cluster: how-to-application
name: how-to-application-6rhi3wpk9iml
spec:
instances:
how-to-app:
preview: preview
production: production
kube-system:
platform-dns: platform-dns
platform-storage: platform-storage
---
apiVersion: kix.run/v1alpha1
kind: PackageInstance
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,91lbws8gjsbxfvr8p7kjx0dc7nzybhqs,requires:pk4hih6jm4yj336rlaqk2qycz2k46xvs
kix.run/identity-hash: 84pfjn0kg74qqvp291b01dzd35ngql6z
kix.run/package: _cluster
kix.run/package-namespace: _cluster
kix.run/root: preview
labels:
app.kubernetes.io/instance: preview
app.kubernetes.io/managed-by: kix
name: preview
namespace: how-to-app
spec:
instanceName: preview
namespaceName: how-to-app
version: "1.0.0"
---
apiVersion: kix.run/v1alpha1
kind: PackageInstance
metadata:
annotations:
kix.run/depends-on: 4sa9i19dly4hh06r5alinlr7pf289igd,8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,cd3l507fvaf0wg5azfza49x6wjsiiynz,requires:pk4hih6jm4yj336rlaqk2qycz2k46xvs
kix.run/identity-hash: mclb0sk2adzjddj0mnckn0n715c5kgq4
kix.run/inverse-deps: Job/production-health
kix.run/package: _cluster
kix.run/package-namespace: _cluster
kix.run/root: production
labels:
app.kubernetes.io/instance: production
app.kubernetes.io/managed-by: kix
name: production
namespace: how-to-app
spec:
instanceName: production
namespaceName: how-to-app
version: "1.0.0"
---
apiVersion: kix.run/v1alpha1
kind: PackageInstance
metadata:
annotations:
kix.run/depends-on: sj0fv5bs2hnh9zcqdh62ph34l2wm0b1m,requires:pk4hih6jm4yj336rlaqk2qycz2k46xvs
kix.run/identity-hash: jh345cnnffy1bnj4cz5x0hwq6ibxvkkr
kix.run/import-apiversion: apps/v1
kix.run/import-fqdn: kube-dns.kube-system.svc.cluster.local
kix.run/import-kind: Deployment
kix.run/import-name: coredns
kix.run/mode: import
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/instance: platform-dns
app.kubernetes.io/managed-by: kix
name: platform-dns
namespace: kube-system
spec:
instanceName: platform-dns
mode: import
namespaceName: kube-system
---
apiVersion: kix.run/v1alpha1
kind: PackageInstance
metadata:
annotations:
kix.run/depends-on: sj0fv5bs2hnh9zcqdh62ph34l2wm0b1m,requires:pk4hih6jm4yj336rlaqk2qycz2k46xvs
kix.run/identity-hash: r5sgwjk8w3m41l0xaj0s16xxsacd0nk6
kix.run/import-apiversion: storage.k8s.io/v1
kix.run/import-kind: StorageClass
kix.run/import-name: standard
kix.run/mode: import
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/instance: platform-storage
app.kubernetes.io/managed-by: kix
name: platform-storage
namespace: kube-system
spec:
instanceName: platform-storage
mode: import
namespaceName: kube-system
---
apiVersion: v1
data:
index.html: |
Hello from preview
kind: ConfigMap
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw
kix.run/identity-hash: asa4ywz8yjjql7grm2rnm774q96ncs3m
kix.run/package: preview
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: preview
app.kubernetes.io/managed-by: kix
name: preview
namespace: how-to-app
---
apiVersion: v1
data:
index.html: |
Hello from production
kind: ConfigMap
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw
kix.run/identity-hash: 4x75h2z7rsj3dvrdg9vzqc8l789xvm4w
kix.run/package: production
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: production
app.kubernetes.io/managed-by: kix
name: production
namespace: how-to-app
---
apiVersion: v1
data:
health-check.sh: |
#!/bin/sh
# region:healthCheckScript
set -u
deadline=$(( $(date +%s) + RETRY_FOR ))
while true; do
if curl --fail --silent --show-error --max-time 10 "$APP_URL" >/dev/null; then
echo "ok GET $APP_URL"
exit 0
fi
echo "FAIL GET $APP_URL"
if [ "$(date +%s)" -ge "$deadline" ]; then
echo "gave up after ${RETRY_FOR}s"
exit 1
fi
sleep 5
done
# endregion:healthCheckScript
kind: ConfigMap
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw
kix.run/identity-hash: dlzbv1nahmar8f7b9jm6rmyvmqczmzdb
kix.run/package: production
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: production
app.kubernetes.io/managed-by: kix
name: production-health-script
namespace: how-to-app
---
apiVersion: v1
kind: Namespace
metadata:
annotations:
kix.run/identity-hash: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
kubernetes.io/metadata.name: how-to-app
name: how-to-app
---
apiVersion: v1
kind: Namespace
metadata:
annotations:
kix.run/identity-hash: sj0fv5bs2hnh9zcqdh62ph34l2wm0b1m
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
kubernetes.io/metadata.name: kube-system
name: kube-system
---
apiVersion: v1
kind: Service
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,hd2br513fjyfgbl9sf97ank37hqzrmzl
kix.run/identity-hash: 91lbws8gjsbxfvr8p7kjx0dc7nzybhqs
kix.run/package: preview
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: preview
app.kubernetes.io/managed-by: kix
name: preview
namespace: how-to-app
spec:
ports:
- name: http
port: 80
protocol: TCP
selector:
app.kubernetes.io/instance: preview
app.kubernetes.io/name: preview
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
annotations:
kix.run/depends-on: 8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,kr7i34wz1ja58c0vf9gizx8161ji0idk
kix.run/identity-hash: cd3l507fvaf0wg5azfza49x6wjsiiynz
kix.run/package: production
kix.run/package-namespace: how-to-app
labels:
app.kubernetes.io/instance: production
app.kubernetes.io/managed-by: kix
name: production
namespace: how-to-app
spec:
ports:
- name: http
port: 80
protocol: TCP
selector:
app.kubernetes.io/instance: production
app.kubernetes.io/name: production
type: ClusterIP The rendered cluster contains the ConfigMap, Deployment, and Service declared by the package, along with Kix’s cluster bookkeeping resources.