Tutorial 10
Reuse packages across instances and environments
Once a package uses scope for resource naming and options for configuration, you can instantiate it as many times as you need: across multiple environment namespaces (app-v1 and app-v2, or staging and production) or multiple times inside the same namespace.
In this tutorial, you will inspect tutorials/10-reuse-packages/cluster.nix, which deploys two independent backend + gateway stacks (app-v1 and app-v2) from the exact same packages.echo-server and reverse-proxy-package.nix definitions.
Prerequisites
Section titled “Prerequisites”- Finish the typed package options tutorial.
- Keep the same
kix-examplescheckout.
Inspect 10-reuse-packages
Section titled “Inspect 10-reuse-packages”Open tutorials/10-reuse-packages/cluster.nix. The cluster defines two namespaces (app-v1 and app-v2) and instantiates packages.echo-server and reverseProxyPackage in both instances.app-v1 and instances.app-v2:
27 namespaces = {28 app-v1 = { };29 app-v2 = { };30 };31
32 # ── v1 stack ───────────────────────────────────────────────33 # The two `package = ...` lines in v1 match the two34 # `package = ...` lines in v2. Only `config` and namespace35 # differ. That's the multi-instance pattern.36 instances.app-v1 = {37 backend = {38 package = packages.echo-server; # ← same package as in v239 config.message = "Response from app-v1";40 };41 gateway = {42 package = reverseProxyPackage; # ← same package as in v243 config = {44 proxyPath = "/v1/";45 maxBodySize = "10m";46 };47 deps.backend = ref.app-v1.backend;48 };49 };50
51 # ── v2 stack ───────────────────────────────────────────────52 instances.app-v2 = {53 backend = {54 package = packages.echo-server; # ← same package as in v155 config.message = "Response from app-v2";56 };57 gateway = {58 package = reverseProxyPackage; # ← same package as in v159 config = {60 proxyPath = "/v2/";61 maxBodySize = "10m";62 };63 deps.backend = ref.app-v2.backend;64 };65 };Look at what stays identical and what changes between the two stacks:
- Both stacks use the exact same
packages.echo-serverandreverseProxyPackageblueprints. app-v1configuresmessage = "Response from app-v1"andproxyPath = "/v1/", and wiresdeps.backend = ref.app-v1.backend.app-v2configuresmessage = "Response from app-v2"andproxyPath = "/v2/", and wiresdeps.backend = ref.app-v2.backend.
Inside reverseProxyPackage, the build function still reads backend.out.fqdn and backend.out.port. For app-v1/gateway, backend.out.fqdn resolves to backend.app-v1.svc.cluster.local; for app-v2/gateway, it resolves to backend.app-v2.svc.cluster.local. (Because each gateway and backend pair lives in the same namespace, you could even omit deps.backend here and let same-namespace resolution match backend automatically, though writing deps.backend = ref.app-v1.backend keeps the wiring explicit.)
Check And Inspect The Dependency Tree
Section titled “Check And Inspect The Dependency Tree”Run kix check and kix graph on 10-reuse-packages:
❱ kix check 10-reuse-packages❱ kix graph 10-reuse-packages --format tree In the dependency tree output, verify that the two proxy chains are isolated in their respective namespaces:
- Under
Namespace/app-v1,Service/backend@app-v1feedsConfigMap/gateway@app-v1andDeployment/gateway@app-v1. - Under
Namespace/app-v2,Service/backend@app-v2feedsConfigMap/gateway@app-v2andDeployment/gateway@app-v2.
You will see this two-namespace layout again in tutorial 12 when we run cluster-wide scorecard rules across app-v1 and app-v2.
Why scope Also Allows Multiple Instances In One Namespace
Section titled “Why scope Also Allows Multiple Instances In One Namespace”What if you want two instances of the same package inside a single namespace, such as an api-gateway and an admin-gateway in app-v1?
In tutorials/10-reuse-packages/reverse-proxy-package.nix, look at how the package names its ConfigMap, Deployment, and Service:
let name = scope.instanceName; in { configmap = scope.mkResource { apiVersion = "v1"; kind = "ConfigMap"; inherit name; # ... };Because the package derives name from scope.instanceName (instead of hardcoding "reverse-proxy" or "gateway"), every instance in a namespace gets its own distinct resource names and selector labels:
- An instance key
api-gatewayreceivesscope.instanceName = "api-gateway"andscope.selectorLabelswithapp.kubernetes.io/instance = "api-gateway". - An instance key
admin-gatewayreceivesscope.instanceName = "admin-gateway"andscope.selectorLabelswithapp.kubernetes.io/instance = "admin-gateway".
Neither their resource names (ConfigMap/api-gateway vs. ConfigMap/admin-gateway) nor their pod label selectors collide, so both instances can run side by side in the same namespace and bind to different backends via deps.backend:
1instances.app-v1 = {2 api-backend = {3 package = packages.echo-server;4 config.message = "Response from API backend";5 };6
7 admin-backend = {8 package = packages.echo-server;9 config.message = "Response from Admin backend";10 };11
12 api-gateway = {13 package = reverseProxyPackage;14 deps.backend = ref.app-v1.api-backend;15 config = {16 proxyPath = "/api/";17 maxBodySize = "10m";18 };19 };20
21 admin-gateway = {22 package = reverseProxyPackage;23 deps.backend = ref.app-v1.admin-backend;24 config = {25 proxyPath = "/admin/";26 maxBodySize = "50m";27 };28 };29};What You Learned
Section titled “What You Learned”You reused the same package definitions across multiple instances:
- placing instances under
instances.<namespace>(instances.app-v1andinstances.app-v2) deploys independent copies of a stack into separate namespaces while keepingout.fqdnscoped to each namespace - deriving resource names from
scope.instanceNameprevents collisions when a package is instantiated more than once in the same namespace -
scope.mkDeploymentincludesapp.kubernetes.io/instance = scope.instanceNameinscope.selectorLabels, so each instance’s Service selects only its own pods -
deps.<arg> = ref.<namespace>.<instance>lets each instance of a package bind to its intended provider
In the next tutorial, you will look at how clusters declare their underlying Kubernetes environment using flavors, roles, and kix.mkImport.