Tutorial 05
Basic graph and deploy loop
Check, preview, deploy, and inspect a small cluster before adding dependencies.
The last tutorial created a cluster from scratch. This tutorial uses that same cluster to look more closely at the Kix loop:
-
kix checkvalidates the evaluated cluster -
kix buildshows the rendered resources -
kix diffpreviews against live state -
kix deployapplies the checked result -
kix graphinspects what Kix knows
The goal is not to learn every graph detail yet. The goal is to see that Kix can evaluate a cluster before it touches Kubernetes.
Prerequisites
Section titled “Prerequisites”- Finish the first cluster from scratch tutorial.
- Keep the
04-from-scratchcluster entry inflake.nix. - Make sure the new cluster file is known to Git. New files in a Git flake are invisible to Nix until they are added to the index:
❱ git ls-files --error-unmatch tutorial-04/cluster.nix If that command fails, add the file:
❱ git add tutorial-04/cluster.nix Check Before Deploy
Section titled “Check Before Deploy”Run the Kix checks first:
❱ kix check 04-from-scratch
TOOL RESULT DETAILS
eval pass 11 manifests evaluated
scorecard pass 0 errors, 1 warnings, 0 info This evaluates the cluster and runs validation before anything is applied to Kubernetes. For this tiny cluster, the output should be short. The important part is the habit: check the evaluated cluster before deploying it.
If Kix fails here, fix the cluster file before moving on. A failed check means the deploy step should not run yet.
Inspect The Rendered Result
Section titled “Inspect The Rendered Result”Now render the cluster:
❱ kix build 04-from-scratch Show outputHide output · 311 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: h50qnfszgri8ml59sx8qy0sx3zbp79s9,qrh908pfdb2030zrxsp8018aiinicd2w
kix.run/identity-hash: jsfzxfyhb8r36fnk07p227k7nhy4w3pg
kix.run/package: hello-world
kix.run/package-namespace: tutorial-04
labels:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/managed-by: kix
app.kubernetes.io/name: hello-world
name: hello-world
namespace: tutorial-04
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/name: hello-world
template:
metadata:
labels:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/name: hello-world
spec:
containers:
- image: docker.io/nginxinc/nginx-unprivileged:1.30.5-alpine@sha256:ed04ec1ff34502c339ee5c3ae3f855442398edc1d05591e2b98981dcbbd20b1e
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
name: nginx
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
failureThreshold: 1
httpGet:
path: /healthz
port: 8080
periodSeconds: 5
resources:
limits:
cpu: '100m'
memory: '64Mi'
requests:
cpu: '50m'
memory: '32Mi'
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
volumeMounts:
- mountPath: /etc/nginx/conf.d
name: config
readOnly: true
- mountPath: /tmp
name: tmp
terminationGracePeriodSeconds: 30
volumes:
- configMap:
name: hello-world
name: config
- emptyDir: {}
name: tmp
---
apiVersion: kix.run/v1alpha1
kind: Activation
metadata:
annotations:
kix.run/built-via: /nix/store/z72qlbhkzpn9mqrks7ab21g1paihnj0p-k8s-activation-04-from-scratch
kix.run/depends-on: fxz6dabpxh1hwbjlkamf23jpqaa7jsca,jh345cnnffy1bnj4cz5x0hwq6ibxvkkr,r5sgwjk8w3m41l0xaj0s16xxsacd0nk6,requires:gvrlspcfgh9a2qnsx5kqxw9f4l3x52jj
kix.run/identity-hash: g941gkm4kxlhpsygx6ndv9cc5vndsdx9
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
kix.run/cluster: '04-from-scratch'
name: '04-from-scratch-g941gkm4kxlh'
spec:
instances:
kube-system:
platform-dns: platform-dns
platform-storage: platform-storage
tutorial-04:
hello-world: hello-world
---
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: kix.run/v1alpha1
kind: PackageInstance
metadata:
annotations:
kix.run/depends-on: l5if8rap4fvbpqsnrb970yzmmpzjl84r,qrh908pfdb2030zrxsp8018aiinicd2w,requires:pk4hih6jm4yj336rlaqk2qycz2k46xvs
kix.run/identity-hash: fxz6dabpxh1hwbjlkamf23jpqaa7jsca
kix.run/package: _cluster
kix.run/package-namespace: _cluster
kix.run/root: hello-world
labels:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/managed-by: kix
name: hello-world
namespace: tutorial-04
spec:
instanceName: hello-world
namespaceName: tutorial-04
version: '1.30.5'
---
apiVersion: v1
data:
default.conf: |
server {
listen 8080;
location / {
default_type text/plain;
return 200 'Hello from my first Kix cluster!\n';
}
location /healthz {
default_type text/plain;
return 200 'ok\n';
}
}
kind: ConfigMap
metadata:
annotations:
kix.run/depends-on: qrh908pfdb2030zrxsp8018aiinicd2w
kix.run/identity-hash: h50qnfszgri8ml59sx8qy0sx3zbp79s9
kix.run/package: hello-world
kix.run/package-namespace: tutorial-04
labels:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/managed-by: kix
name: hello-world
namespace: tutorial-04
---
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: Namespace
metadata:
annotations:
kix.run/identity-hash: qrh908pfdb2030zrxsp8018aiinicd2w
kix.run/package: _cluster
kix.run/package-namespace: _cluster
labels:
app.kubernetes.io/managed-by: kix
kubernetes.io/metadata.name: tutorial-04
name: tutorial-04
---
apiVersion: v1
kind: Service
metadata:
annotations:
kix.run/depends-on: jsfzxfyhb8r36fnk07p227k7nhy4w3pg,qrh908pfdb2030zrxsp8018aiinicd2w
kix.run/identity-hash: l5if8rap4fvbpqsnrb970yzmmpzjl84r
kix.run/package: hello-world
kix.run/package-namespace: tutorial-04
labels:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/managed-by: kix
name: hello-world
namespace: tutorial-04
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/instance: hello-world
app.kubernetes.io/name: hello-world
type: ClusterIP This prints the Kubernetes resources Kix built from your cluster definition. You should see familiar objects such as a Namespace, Deployment, and Service.
For now, use kix build as a window into what Kix evaluated. In normal use,
you usually let kix deploy render and apply the checked result for you.
Preview Against The Cluster
Section titled “Preview Against The Cluster”Ask Kix what is different from the live cluster:
❱ kix diff 04-from-scratch Show outputHide output · 6 lines
⠁ Fetching live cluster state... Discovering API resources...
Fetching managed resources (60 resource types)...
Fetched 10 resources across 60 resource types
Skipped 7 objects that belong to Kix cluster 02-hello-world.
2 packages: 2 unchanged, 0 changed, 0 added, 0 removed
No differences found. If your live cluster already matches the checked result, the diff should be
empty or say there is nothing to change. If the hello-world cluster from the
local setup tutorial is still
deployed in the same kind cluster, Kix reports that it skipped its objects:
kix diff compares only the Kix cluster you name.
Now make a visible edit in tutorial-04/cluster.nix:
config = { message = "Preview this before deploy.";};Check that Git sees the edit:
❱ git status --short Because tutorial-04/cluster.nix is already tracked, Nix can see this dirty
edit. You do not need to commit it.
Run the diff again:
❱ kix diff 04-from-scratch Show outputHide output · 27 lines
⠁ Fetching live cluster state... Discovering API resources...
Fetching managed resources (60 resource types)...
Fetched 10 resources across 60 resource types
Skipped 7 objects that belong to Kix cluster 02-hello-world.
2 packages: 1 unchanged, 1 changed, 0 added, 0 removed
tutorial-04
~ hello-world 1.27 (1 changed, 3 dep-affected)
~ ConfigMap/hello-world@tutorial-04
~ $.data.default.conf:
--- old
+++ new
@@ -2,7 +2,7 @@
listen 8080;
location / {
default_type text/plain;
- return 200 'Hello from my first Kix cluster!\n';
+ return 200 'Preview this before deploy.\n';
}
location /healthz {
default_type text/plain;
~ Deployment/hello-world@tutorial-04 (via dependency)
~ PackageInstance/hello-world@tutorial-04 (via dependency)
~ Service/hello-world@tutorial-04 (via dependency)
Activation: 04-from-scratch-ll6g0k5f2kqm -> 04-from-scratch-6w9czkc51502
(exit code: 2) This time Kix should show the change before it applies anything. That is the basic preview loop: edit locally, evaluate locally, inspect the change, then deploy.
Deploy The Checked Result
Section titled “Deploy The Checked Result”Deploy the change:
❱ kix deploy 04-from-scratch -y Show outputHide output · 25 lines
Building cluster '04-from-scratch'...
Cluster 04-from-scratch: 10 manifests
Connecting to cluster...
Active activation: 04-from-scratch-ll6g0k5f2kqm (ll6g0k5f...)
tutorial-04
~ hello-world 1.27 (1 changed, 3 dep-affected)
Plan: 1 updated, 1 unchanged
Resources: 1 real content, 3 dep-affected
plan: 10 nodes
~ ConfigMap/hello-world@tutorial-04 configured
✔ ConfigMap/hello-world@tutorial-04 ready
~ Deployment/hello-world@tutorial-04 configured
✔ Deployment/hello-world@tutorial-04 ready
~ Service/hello-world@tutorial-04 configured
✔ Service/hello-world@tutorial-04 ready
~ PackageInstance/hello-world@tutorial-04 configured
✔ PackageInstance/hello-world@tutorial-04 ready
~ Activation/04-from-scratch-6w9czkc51502 configured
✔ Activation/04-from-scratch-6w9czkc51502 ready
• activation '04-from-scratch-ll6g0k5f2kqm' → Superseded
• activation '04-from-scratch-6w9czkc51502' → Active
Deploy complete: 0 created, 5 configured, 5 unchanged, 0 failed Kix evaluates the cluster, computes what needs to change, and applies the checked result to Kubernetes.
Check the resource status:
❱ kix status 04-from-scratch
Skipped 7 objects that belong to Kix cluster 02-hello-world.
NAME NAMESPACE KIND READY STATUS AGE
04-from-scratch-6w9czkc51502 _cluster Activation True Active 3s
04-from-scratch-ll6g0k5f2kqm _cluster Activation True Active 11s
activations.kix.run _cluster CustomResourceDefinition True Active 45s
packageinstances.kix.run _cluster CustomResourceDefinition True Active 45s
kube-system _cluster Namespace True Active 52s
tutorial-04 _cluster Namespace True Active 11s
platform-dns kube-system PackageInstance True Active 43s
hello-world tutorial-04 ConfigMap True Active 11s
hello-world tutorial-04 Deployment True 1/1 11s
hello-world tutorial-04 PackageInstance True Active 7s
hello-world tutorial-04 Service True Active 7s Then port-forward and verify the response:
❱ kix pf 04-from-scratch hello-world 8080:80 In another terminal:
❱ curl http://localhost:8080/
Preview this before deploy. Stop the port-forward with Ctrl-C.
See The First Graph Shape
Section titled “See The First Graph Shape”Run:
❱ kix graph 04-from-scratch --format tree Show outputHide output · 30 lines
CustomResourceDefinition/activations.kix.run
└── Activation/04-from-scratch-g941gkm4kxlh
CustomResourceDefinition/packageinstances.kix.run
├── PackageInstance/hello-world@tutorial-04
│ └── Activation/04-from-scratch-g941gkm4kxlh
├── PackageInstance/platform-storage@kube-system (import)
│ └── Activation/04-from-scratch-g941gkm4kxlh
└── PackageInstance/platform-dns@kube-system (import)
└── Activation/04-from-scratch-g941gkm4kxlh
Namespace/kube-system
├── PackageInstance/platform-storage@kube-system (import)
│ └── Activation/04-from-scratch-g941gkm4kxlh
└── PackageInstance/platform-dns@kube-system (import)
└── Activation/04-from-scratch-g941gkm4kxlh
Namespace/tutorial-04
├── Service/hello-world@tutorial-04
│ └── PackageInstance/hello-world@tutorial-04
│ └── Activation/04-from-scratch-g941gkm4kxlh
├── ConfigMap/hello-world@tutorial-04
│ └── Deployment/hello-world@tutorial-04
│ └── Service/hello-world@tutorial-04
│ └── PackageInstance/hello-world@tutorial-04
│ └── Activation/04-from-scratch-g941gkm4kxlh
├── PackageInstance/hello-world@tutorial-04
│ └── Activation/04-from-scratch-g941gkm4kxlh
└── Deployment/hello-world@tutorial-04
└── Service/hello-world@tutorial-04
└── PackageInstance/hello-world@tutorial-04
└── Activation/04-from-scratch-g941gkm4kxlh
11 resources, 16 dependencies For this cluster the graph is still small. You may see Kix’s own activation
resources, the namespace, platform imports, and the hello-world package
resources. The useful habit is to read the graph as “what Kix knows about this
cluster,” not as raw YAML.
Do not worry about the deeper graph mechanics yet. Later tutorials add service dependencies, cross-namespace wiring, generated network policy, and scorecard rules. Each one gives the graph more useful information.
What You Learned
Section titled “What You Learned”You used the same local cluster definition through the main Kix loop:
-
kix checkcatches problems before deploy -
kix buildshows the rendered Kubernetes resources -
kix diffpreviews changes against the live cluster -
kix deployapplies the evaluated result -
kix statusandkix graphinspect what Kix knows afterward
The next tutorial adds the first real relationship: one local package depends on another service.