Create a local package
This content is for the v0.1 version. Switch to the latest version for up-to-date documentation.
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:
meta = { version = "1.0.0"; description = "Small nginx application used by the Kix how-to guides"; };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:
content = scope.mkResource { apiVersion = "v1"; kind = "ConfigMap"; inherit name; data."index.html" = "${config.message}\n"; };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:
deployment = { inherit name; spec = { replicas = config.replicas; selector.matchLabels = scope.selectorLabels; template.spec.containers = [ { name = "nginx"; image = "docker.io/library/nginx:1.27-alpine"; imagePullPolicy = "IfNotPresent"; ports = [ (kix.port "http" 80) ]; env = kix.mkEnvVars { APP_ENV = config.environment; }; readinessProbe.httpGet = { path = "/"; port = "http"; }; resources = { requests = { cpu = "10m"; memory = "16Mi"; }; limits.memory = "64Mi"; }; } ]; }; } |> kix.withMounts [ contentMount ] |> scope.mkDeployment;scope.mkDeployment fills in the standard workload metadata and validates the
Deployment shape. The example also uses scope.selectorLabels so the workload
and its Service agree on the Pod selector.
Finish with a Service and select the package root:
service = self.deployment |> kix.service.fromWorkload { inherit name; } |> scope.mkResource;
root = self.service;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:
webPackage = import ./web-package.nix;Assign the imported value to an instance just like a catalogue package:
instances.apps.web = { package = webPackage;};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 16 manifests evaluated
kubeconform pass skipped (this validation tool is not yet integrated with Kix)
pluto pass skipped (this validation tool is not yet integrated with Kix)
kyverno pass skipped (this validation tool is not yet integrated with Kix)
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 output
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: '67bgr7ggiw82bhlc2c3ddxma573vpibj'
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
ttlSecondsAfterFinished: 3600
---
apiVersion: kix.run/v1alpha1
kind: Activation
metadata:
annotations:
kix.run/built-via: /nix/store/8knghw2ziwh65bl6x0iyxr4iy83j8l05-k8s-activation-how-to-application
kix.run/depends-on: '84pfjn0kg74qqvp291b01dzd35ngql6z,avvdnz5yy5602xd1095ga9z40qbfkkwc,jh345cnnffy1bnj4cz5x0hwq6ibxvkkr,requires:gvrlspcfgh9a2qnsx5kqxw9f4l3x52jj'
kix.run/identity-hash: gb5d6ry45b5ll664ahagxldh9na9z3y5
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-gb5d6ry45b5l
spec:
instances:
how-to-app:
preview: preview
production: production
kube-system:
platform-dns: platform-dns
---
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: '67bgr7ggiw82bhlc2c3ddxma573vpibj,8767b7nzgc1x5bpfa9gv71cpk9z8h0iw,cd3l507fvaf0wg5azfza49x6wjsiiynz,requires:pk4hih6jm4yj336rlaqk2qycz2k46xvs'
kix.run/identity-hash: avvdnz5yy5602xd1095ga9z40qbfkkwc
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: 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.