@intentius/chant-lexicon-k8s 0.0.22 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.json +9 -4
- package/dist/manifest.json +1 -1
- package/dist/skills/chant-k8s-aks.md +146 -0
- package/{src/skills/kubernetes-patterns.md → dist/skills/chant-k8s-deployment-strategies.md} +1 -1
- package/dist/skills/chant-k8s-eks.md +156 -0
- package/dist/skills/chant-k8s-gke.md +246 -0
- package/{src/skills/kubernetes-security.md → dist/skills/chant-k8s-security.md} +1 -1
- package/dist/skills/chant-k8s.md +65 -2
- package/package.json +5 -4
- package/src/composites/adot-collector.ts +34 -22
- package/src/composites/agic-ingress.ts +14 -6
- package/src/composites/aks-external-dns-agent.ts +29 -18
- package/src/composites/alb-ingress.ts +14 -6
- package/src/composites/autoscaled-service.ts +25 -20
- package/src/composites/azure-disk-storage-class.ts +14 -6
- package/src/composites/azure-file-storage-class.ts +14 -6
- package/src/composites/azure-monitor-collector.ts +34 -22
- package/src/composites/batch-job.ts +25 -17
- package/src/composites/cockroachdb-cluster.ts +164 -58
- package/src/composites/composites.test.ts +371 -365
- package/src/composites/config-connector-context.ts +18 -11
- package/src/composites/configured-app.ts +21 -15
- package/src/composites/cron-workload.ts +25 -20
- package/src/composites/ebs-storage-class.ts +14 -6
- package/src/composites/efs-storage-class.ts +14 -6
- package/src/composites/external-dns-agent.ts +26 -20
- package/src/composites/filestore-storage-class.ts +14 -6
- package/src/composites/fluent-bit-agent.ts +30 -24
- package/src/composites/gce-ingress.ts +14 -6
- package/src/composites/gce-pd-storage-class.ts +14 -6
- package/src/composites/gke-external-dns-agent.ts +34 -21
- package/src/composites/gke-fluent-bit-agent.ts +34 -22
- package/src/composites/gke-gateway.ts +19 -12
- package/src/composites/gke-otel-collector.ts +34 -22
- package/src/composites/irsa-service-account.ts +22 -14
- package/src/composites/metrics-server.ts +41 -26
- package/src/composites/monitored-service.ts +26 -19
- package/src/composites/namespace-env.ts +26 -17
- package/src/composites/network-isolated-app.ts +21 -16
- package/src/composites/node-agent.ts +33 -22
- package/src/composites/secure-ingress.ts +19 -11
- package/src/composites/sidecar-app.ts +17 -12
- package/src/composites/stateful-app.ts +21 -12
- package/src/composites/web-app.ts +25 -21
- package/src/composites/worker-pool.ts +40 -26
- package/src/composites/workload-identity-sa.ts +22 -14
- package/src/composites/workload-identity-service-account.ts +22 -16
- package/src/plugin.ts +40 -614
- package/src/serializer.ts +7 -0
- package/src/skills/chant-k8s-deployment-strategies.md +183 -0
- package/src/skills/chant-k8s-gke.md +56 -1
- package/src/skills/chant-k8s-patterns.md +245 -0
- package/src/skills/chant-k8s-security.md +237 -0
- package/src/skills/chant-k8s.md +305 -0
package/src/serializer.ts
CHANGED
|
@@ -88,6 +88,13 @@ const API_GROUP_VERSIONS: Record<string, string> = {
|
|
|
88
88
|
Storage: "storage.k8s.io/v1",
|
|
89
89
|
Autoscaling: "autoscaling/v2",
|
|
90
90
|
Admissionregistration: "admissionregistration.k8s.io/v1",
|
|
91
|
+
GKE: "cloud.google.com/v1",
|
|
92
|
+
NetworkingGKE: "networking.gke.io/v1",
|
|
93
|
+
NetworkingGKEBeta: "networking.gke.io/v1beta1",
|
|
94
|
+
// Common Kubernetes operator CRDs
|
|
95
|
+
CertManager: "cert-manager.io/v1",
|
|
96
|
+
ExternalSecrets: "external-secrets.io/v1",
|
|
97
|
+
Monitoring: "monitoring.coreos.com/v1",
|
|
91
98
|
};
|
|
92
99
|
|
|
93
100
|
function deriveGVKFromType(entityType: string): { apiVersion: string; kind: string } | null {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-k8s-deployment-strategies
|
|
3
|
+
description: Kubernetes deployment strategies, stateful workloads, RBAC, and networking patterns
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Kubernetes Infrastructure Patterns
|
|
8
|
+
|
|
9
|
+
## Deployment Strategies
|
|
10
|
+
|
|
11
|
+
### Rolling Update (default)
|
|
12
|
+
|
|
13
|
+
Gradually replaces pods with zero downtime. Configure surge and unavailability:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { WebApp } from "@intentius/chant-lexicon-k8s";
|
|
17
|
+
|
|
18
|
+
const { deployment, service } = WebApp({
|
|
19
|
+
name: "api",
|
|
20
|
+
image: "api:2.0",
|
|
21
|
+
replicas: 4,
|
|
22
|
+
strategy: {
|
|
23
|
+
type: "RollingUpdate",
|
|
24
|
+
rollingUpdate: { maxSurge: "25%", maxUnavailable: "25%" },
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Blue/Green Deployment
|
|
30
|
+
|
|
31
|
+
Run two full deployments and switch traffic by updating the Service selector:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { WebApp } from "@intentius/chant-lexicon-k8s";
|
|
35
|
+
|
|
36
|
+
const blue = WebApp({ name: "app-blue", image: "app:1.0", replicas: 3 });
|
|
37
|
+
const green = WebApp({ name: "app-green", image: "app:2.0", replicas: 3 });
|
|
38
|
+
|
|
39
|
+
// Switch traffic: update the Service selector to point at green
|
|
40
|
+
// kubectl patch svc app -p '{"spec":{"selector":{"version":"green"}}}'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Canary Deployment
|
|
44
|
+
|
|
45
|
+
Deploy a small replica set alongside the main deployment to test new versions:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { AutoscaledService, WebApp } from "@intentius/chant-lexicon-k8s";
|
|
49
|
+
|
|
50
|
+
const main = AutoscaledService({
|
|
51
|
+
name: "app",
|
|
52
|
+
image: "app:1.0",
|
|
53
|
+
port: 8080,
|
|
54
|
+
minReplicas: 9,
|
|
55
|
+
maxReplicas: 20,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const canary = WebApp({
|
|
59
|
+
name: "app-canary",
|
|
60
|
+
image: "app:2.0",
|
|
61
|
+
replicas: 1,
|
|
62
|
+
labels: { track: "canary" },
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Both share the same `app.kubernetes.io/name` label so the Service routes traffic to both.
|
|
67
|
+
|
|
68
|
+
## Stateful Workloads
|
|
69
|
+
|
|
70
|
+
### StatefulSet with Persistent Storage
|
|
71
|
+
|
|
72
|
+
Use `StatefulApp` for databases, caches, and other stateful services:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { StatefulApp } from "@intentius/chant-lexicon-k8s";
|
|
76
|
+
|
|
77
|
+
const { statefulSet, service } = StatefulApp({
|
|
78
|
+
name: "postgres",
|
|
79
|
+
image: "postgres:16",
|
|
80
|
+
port: 5432,
|
|
81
|
+
replicas: 3,
|
|
82
|
+
storageSize: "50Gi",
|
|
83
|
+
storageClass: "gp3-encrypted",
|
|
84
|
+
env: [
|
|
85
|
+
{ name: "POSTGRES_DB", value: "app" },
|
|
86
|
+
{ name: "POSTGRES_PASSWORD", valueFrom: { secretKeyRef: { name: "pg-creds", key: "password" } } },
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Headless Service for StatefulSet Discovery
|
|
92
|
+
|
|
93
|
+
`StatefulApp` creates a headless Service (`clusterIP: None`) automatically. Pods are addressable as `postgres-0.postgres.namespace.svc.cluster.local`.
|
|
94
|
+
|
|
95
|
+
### PersistentVolumeClaim Retention
|
|
96
|
+
|
|
97
|
+
StatefulSet PVCs persist after pod deletion by default. To clean up:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
kubectl delete pvc -l app.kubernetes.io/name=postgres
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## RBAC Patterns
|
|
104
|
+
|
|
105
|
+
### Least-Privilege ServiceAccount
|
|
106
|
+
|
|
107
|
+
Use `WorkerPool` or `BatchJob` composites which create scoped RBAC automatically:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { WorkerPool } from "@intentius/chant-lexicon-k8s";
|
|
111
|
+
|
|
112
|
+
const { deployment, serviceAccount, role, roleBinding } = WorkerPool({
|
|
113
|
+
name: "queue-worker",
|
|
114
|
+
image: "worker:1.0",
|
|
115
|
+
replicas: 3,
|
|
116
|
+
rbacRules: [
|
|
117
|
+
{ apiGroups: [""], resources: ["configmaps"], verbs: ["get", "list"] },
|
|
118
|
+
{ apiGroups: ["batch"], resources: ["jobs"], verbs: ["create"] },
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Namespace-Scoped vs Cluster-Scoped
|
|
124
|
+
|
|
125
|
+
- Use `Role` + `RoleBinding` for namespace-scoped permissions (default in composites)
|
|
126
|
+
- Use `ClusterRole` + `ClusterRoleBinding` for cross-namespace access (node agents, monitoring)
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { NodeAgent } from "@intentius/chant-lexicon-k8s";
|
|
130
|
+
|
|
131
|
+
const { daemonSet, serviceAccount, clusterRole, clusterRoleBinding } = NodeAgent({
|
|
132
|
+
name: "log-agent",
|
|
133
|
+
image: "fluent-bit:2.2",
|
|
134
|
+
rbacRules: [
|
|
135
|
+
{ apiGroups: [""], resources: ["pods", "namespaces"], verbs: ["get", "list", "watch"] },
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Networking Patterns
|
|
141
|
+
|
|
142
|
+
### Default-Deny with Selective Allow
|
|
143
|
+
|
|
144
|
+
Use `NamespaceEnv` for namespace-level isolation, then `NetworkIsolatedApp` for per-app rules:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { NamespaceEnv, NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
|
|
148
|
+
|
|
149
|
+
const ns = NamespaceEnv({
|
|
150
|
+
name: "prod",
|
|
151
|
+
defaultDenyIngress: true,
|
|
152
|
+
defaultDenyEgress: true,
|
|
153
|
+
cpuLimit: "8",
|
|
154
|
+
memoryLimit: "16Gi",
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const app = NetworkIsolatedApp({
|
|
158
|
+
name: "api",
|
|
159
|
+
image: "api:1.0",
|
|
160
|
+
port: 8080,
|
|
161
|
+
namespace: "prod",
|
|
162
|
+
allowIngressFrom: [
|
|
163
|
+
{ podSelector: { "app.kubernetes.io/name": "gateway" } },
|
|
164
|
+
],
|
|
165
|
+
allowEgressTo: [
|
|
166
|
+
{ podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Service Mesh (Istio/Linkerd)
|
|
172
|
+
|
|
173
|
+
For mTLS between services, use a sidecar-injected namespace:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
kubectl label namespace prod istio-injection=enabled
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Then deploy with standard composites. The mesh proxy is injected automatically.
|
|
180
|
+
|
|
181
|
+
### DNS-Based Service Discovery
|
|
182
|
+
|
|
183
|
+
Services are discoverable at `<service>.<namespace>.svc.cluster.local`. For headless services (StatefulSets), individual pods are at `<pod>.<service>.<namespace>.svc.cluster.local`.
|
|
@@ -119,6 +119,12 @@ const result = GkeExternalDnsAgent({
|
|
|
119
119
|
});
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
**Props:** `gcpServiceAccountEmail` (required), `gcpProjectId` (required), `domainFilters` (required), `txtOwnerId?`, `source?` (string or string[], default: `"service"`), `name?` (default: `"external-dns"`), `namespace?` (default: `"kube-system"`), `image?` (default: `"registry.k8s.io/external-dns/external-dns:v0.14.2"`), `labels?`, `defaults?`
|
|
123
|
+
|
|
124
|
+
**Returns:** `{ deployment, serviceAccount, clusterRole, clusterRoleBinding }`
|
|
125
|
+
|
|
126
|
+
To watch both Services and Ingresses, pass `source: ["service", "ingress"]`.
|
|
127
|
+
|
|
122
128
|
### GkeFluentBitAgent — DaemonSet for Cloud Logging
|
|
123
129
|
|
|
124
130
|
```typescript
|
|
@@ -143,6 +149,55 @@ const result = GkeOtelCollector({
|
|
|
143
149
|
});
|
|
144
150
|
```
|
|
145
151
|
|
|
152
|
+
### CockroachDbCluster — multi-node CockroachDB StatefulSet
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { CockroachDbCluster } from "@intentius/chant-lexicon-k8s";
|
|
156
|
+
|
|
157
|
+
const crdb = CockroachDbCluster({
|
|
158
|
+
name: "cockroachdb",
|
|
159
|
+
namespace: "crdb-east",
|
|
160
|
+
replicas: 3,
|
|
161
|
+
image: "cockroachdb/cockroach:v24.3.4",
|
|
162
|
+
storageSize: "100Gi",
|
|
163
|
+
storageClassName: "pd-ssd",
|
|
164
|
+
cpuLimit: "2",
|
|
165
|
+
memoryLimit: "8Gi",
|
|
166
|
+
locality: "region=us-east1,zone=us-east1-b",
|
|
167
|
+
joinAddresses: ["cockroachdb-0.east.crdb.example.com", "cockroachdb-0.west.crdb.example.com"],
|
|
168
|
+
secure: true,
|
|
169
|
+
skipInit: false, // true on non-bootstrapping regions
|
|
170
|
+
skipCertGen: true, // true when certs are provisioned externally
|
|
171
|
+
advertiseHostDomain: "east.crdb.example.com",
|
|
172
|
+
extraCertNodeAddresses: [
|
|
173
|
+
"cockroachdb-0.east.crdb.example.com",
|
|
174
|
+
"cockroachdb-1.east.crdb.example.com",
|
|
175
|
+
"cockroachdb-2.east.crdb.example.com",
|
|
176
|
+
],
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
export const {
|
|
180
|
+
serviceAccount, role, roleBinding, clusterRole, clusterRoleBinding,
|
|
181
|
+
publicService, headlessService, pdb, statefulSet,
|
|
182
|
+
initJob, // only when skipInit: false
|
|
183
|
+
certGenJob, // only when skipCertGen: false
|
|
184
|
+
} = crdb;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Key props:**
|
|
188
|
+
- `secure` — enables TLS node-to-node and client comms (default: `false`)
|
|
189
|
+
- `skipInit` — skip `cockroach init`; set `true` on all regions except the one that bootstraps the cluster
|
|
190
|
+
- `skipCertGen` — skip cert generation Job; use when certs are managed externally (e.g. `generate-certs.sh`)
|
|
191
|
+
- `advertiseHostDomain` — hostname suffix CockroachDB advertises to peers; must resolve via ExternalDNS or similar
|
|
192
|
+
- `extraCertNodeAddresses` — SANs added to node certs for cross-region RPC; list all pod FQDNs that peers will dial
|
|
193
|
+
- `locality` — CockroachDB locality string (`region=...,zone=...`); used for data placement and rebalancing
|
|
194
|
+
- `joinAddresses` — seed peer addresses used at startup; include one node from each region
|
|
195
|
+
|
|
196
|
+
**`defaults`** allow deep-merging arbitrary fields onto any generated resource:
|
|
197
|
+
- `defaults.serviceAccount` — e.g. add `iam.gke.io/gcp-service-account` annotation for Workload Identity
|
|
198
|
+
- `defaults.publicService` — e.g. add `cloud.google.com/backend-config` + `cloud.google.com/app-protocols` annotations for GCE Ingress
|
|
199
|
+
- `defaults.headlessService` — e.g. add `external-dns.alpha.kubernetes.io/hostname` for ExternalDNS registration
|
|
200
|
+
|
|
146
201
|
### ConfigConnectorContext — Config Connector namespace bootstrap
|
|
147
202
|
|
|
148
203
|
```typescript
|
|
@@ -151,7 +206,7 @@ import { ConfigConnectorContext } from "@intentius/chant-lexicon-k8s";
|
|
|
151
206
|
const { context } = ConfigConnectorContext({
|
|
152
207
|
googleServiceAccountEmail: "cc-sa@my-project.iam.gserviceaccount.com",
|
|
153
208
|
namespace: "config-connector",
|
|
154
|
-
stateIntoSpec: "
|
|
209
|
+
stateIntoSpec: "Absent",
|
|
155
210
|
});
|
|
156
211
|
```
|
|
157
212
|
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-k8s-patterns
|
|
3
|
+
description: Advanced Kubernetes deployment patterns and composites
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Advanced Kubernetes Patterns
|
|
8
|
+
|
|
9
|
+
## Sidecar Patterns
|
|
10
|
+
|
|
11
|
+
### SidecarApp — multi-container Deployment
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SidecarApp } from "@intentius/chant-lexicon-k8s";
|
|
15
|
+
|
|
16
|
+
// Envoy sidecar proxy
|
|
17
|
+
const { deployment, service } = SidecarApp({
|
|
18
|
+
name: "api",
|
|
19
|
+
image: "api:1.0",
|
|
20
|
+
port: 8080,
|
|
21
|
+
sidecars: [
|
|
22
|
+
{
|
|
23
|
+
name: "envoy",
|
|
24
|
+
image: "envoyproxy/envoy:v1.28",
|
|
25
|
+
ports: [{ containerPort: 9901, name: "admin" }],
|
|
26
|
+
resources: { requests: { cpu: "100m", memory: "128Mi" }, limits: { cpu: "200m", memory: "256Mi" } },
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
initContainers: [
|
|
30
|
+
{ name: "migrate", image: "api:1.0", command: ["python", "manage.py", "migrate"] },
|
|
31
|
+
],
|
|
32
|
+
sharedVolumes: [{ name: "tmp", emptyDir: {} }],
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Common sidecar use cases:
|
|
37
|
+
- **Envoy proxy** — service mesh, mTLS, traffic management
|
|
38
|
+
- **Log forwarder** — Fluent Bit sidecar for app-specific log routing
|
|
39
|
+
- **Auth proxy** — OAuth2 Proxy for authentication
|
|
40
|
+
- **Config watcher** — reload config on ConfigMap changes
|
|
41
|
+
|
|
42
|
+
## Config and Secret Mounting
|
|
43
|
+
|
|
44
|
+
### ConfiguredApp — automatic volume wiring
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { ConfiguredApp } from "@intentius/chant-lexicon-k8s";
|
|
48
|
+
|
|
49
|
+
const { deployment, service, configMap } = ConfiguredApp({
|
|
50
|
+
name: "api",
|
|
51
|
+
image: "api:1.0",
|
|
52
|
+
port: 8080,
|
|
53
|
+
// Mount ConfigMap as volume
|
|
54
|
+
configData: { "app.conf": "key=value\nother=setting" },
|
|
55
|
+
configMountPath: "/etc/api",
|
|
56
|
+
// Mount existing Secret as volume
|
|
57
|
+
secretName: "api-creds",
|
|
58
|
+
secretMountPath: "/secrets",
|
|
59
|
+
// Inject as environment variables
|
|
60
|
+
envFrom: { secretRef: "api-env-secret", configMapRef: "api-env-config" },
|
|
61
|
+
// Run migrations before the app starts
|
|
62
|
+
initContainers: [
|
|
63
|
+
{ name: "migrate", image: "api:1.0", command: ["./migrate.sh"] },
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Volume patterns
|
|
69
|
+
|
|
70
|
+
| Pattern | Use Case | ConfiguredApp Props |
|
|
71
|
+
|---------|----------|---------------------|
|
|
72
|
+
| ConfigMap as file | Config files, templates | `configData` + `configMountPath` |
|
|
73
|
+
| Secret as file | TLS certs, credentials | `secretName` + `secretMountPath` |
|
|
74
|
+
| ConfigMap as env | Simple key-value config | `envFrom.configMapRef` |
|
|
75
|
+
| Secret as env | Database URLs, API keys | `envFrom.secretRef` |
|
|
76
|
+
|
|
77
|
+
## TLS / cert-manager
|
|
78
|
+
|
|
79
|
+
### SecureIngress — multi-host TLS with cert-manager
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { SecureIngress } from "@intentius/chant-lexicon-k8s";
|
|
83
|
+
|
|
84
|
+
const { ingress, certificate } = SecureIngress({
|
|
85
|
+
name: "app-ingress",
|
|
86
|
+
hosts: [
|
|
87
|
+
{
|
|
88
|
+
hostname: "api.example.com",
|
|
89
|
+
paths: [
|
|
90
|
+
{ path: "/v1", serviceName: "api-v1", servicePort: 80 },
|
|
91
|
+
{ path: "/v2", serviceName: "api-v2", servicePort: 80 },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
hostname: "admin.example.com",
|
|
96
|
+
paths: [{ path: "/", serviceName: "admin", servicePort: 80 }],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
clusterIssuer: "letsencrypt-prod",
|
|
100
|
+
ingressClassName: "nginx",
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Features:
|
|
105
|
+
- Multiple hosts and paths per Ingress
|
|
106
|
+
- Automatic cert-manager Certificate when `clusterIssuer` set
|
|
107
|
+
- TLS secret auto-provisioned by cert-manager
|
|
108
|
+
|
|
109
|
+
### cert-manager setup (prerequisite)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Install cert-manager
|
|
113
|
+
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
|
|
114
|
+
|
|
115
|
+
# Create ClusterIssuer for Let's Encrypt
|
|
116
|
+
kubectl apply -f - <<EOF
|
|
117
|
+
apiVersion: cert-manager.io/v1
|
|
118
|
+
kind: ClusterIssuer
|
|
119
|
+
metadata:
|
|
120
|
+
name: letsencrypt-prod
|
|
121
|
+
spec:
|
|
122
|
+
acme:
|
|
123
|
+
server: https://acme-v02.api.letsencrypt.org/directory
|
|
124
|
+
email: admin@example.com
|
|
125
|
+
privateKeySecretRef:
|
|
126
|
+
name: letsencrypt-prod-key
|
|
127
|
+
solvers:
|
|
128
|
+
- http01:
|
|
129
|
+
ingress:
|
|
130
|
+
class: nginx
|
|
131
|
+
EOF
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Observability
|
|
135
|
+
|
|
136
|
+
### MonitoredService — Prometheus monitoring
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { MonitoredService } from "@intentius/chant-lexicon-k8s";
|
|
140
|
+
|
|
141
|
+
const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService({
|
|
142
|
+
name: "api",
|
|
143
|
+
image: "api:1.0",
|
|
144
|
+
port: 8080,
|
|
145
|
+
metricsPort: 9090,
|
|
146
|
+
metricsPath: "/metrics",
|
|
147
|
+
scrapeInterval: "15s",
|
|
148
|
+
alertRules: [
|
|
149
|
+
{
|
|
150
|
+
name: "HighErrorRate",
|
|
151
|
+
expr: 'rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05',
|
|
152
|
+
for: "5m",
|
|
153
|
+
severity: "critical",
|
|
154
|
+
annotations: { summary: "High error rate on {{ $labels.instance }}" },
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "HighLatency",
|
|
158
|
+
expr: 'histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1',
|
|
159
|
+
for: "10m",
|
|
160
|
+
severity: "warning",
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Prerequisites
|
|
167
|
+
|
|
168
|
+
- Prometheus Operator installed (for ServiceMonitor/PrometheusRule CRDs)
|
|
169
|
+
- Prometheus configured to discover ServiceMonitors
|
|
170
|
+
|
|
171
|
+
## Network Isolation
|
|
172
|
+
|
|
173
|
+
### NetworkIsolatedApp — per-app firewall rules
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
|
|
177
|
+
|
|
178
|
+
const { deployment, service, networkPolicy } = NetworkIsolatedApp({
|
|
179
|
+
name: "api",
|
|
180
|
+
image: "api:1.0",
|
|
181
|
+
port: 8080,
|
|
182
|
+
allowIngressFrom: [
|
|
183
|
+
{ podSelector: { "app.kubernetes.io/name": "frontend" } },
|
|
184
|
+
{ namespaceSelector: { "kubernetes.io/metadata.name": "monitoring" } },
|
|
185
|
+
],
|
|
186
|
+
allowEgressTo: [
|
|
187
|
+
{ podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
|
|
188
|
+
{ podSelector: { "app.kubernetes.io/name": "redis" }, ports: [{ port: 6379 }] },
|
|
189
|
+
],
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Combining with NamespaceEnv
|
|
194
|
+
|
|
195
|
+
Use NamespaceEnv for namespace-level default-deny, then NetworkIsolatedApp for per-app allow rules:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Namespace: deny all by default
|
|
199
|
+
const ns = NamespaceEnv({ name: "prod", defaultDenyIngress: true, defaultDenyEgress: true });
|
|
200
|
+
|
|
201
|
+
// App: allow specific traffic
|
|
202
|
+
const app = NetworkIsolatedApp({
|
|
203
|
+
name: "api",
|
|
204
|
+
image: "api:1.0",
|
|
205
|
+
namespace: "prod",
|
|
206
|
+
allowIngressFrom: [{ podSelector: { "app.kubernetes.io/name": "gateway" } }],
|
|
207
|
+
allowEgressTo: [{ podSelector: { "app.kubernetes.io/name": "db" }, ports: [{ port: 5432 }] }],
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Blue/Green and Canary
|
|
212
|
+
|
|
213
|
+
These patterns use standard K8s resources — no special composite needed.
|
|
214
|
+
|
|
215
|
+
### Blue/Green
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Two Deployments with different versions
|
|
219
|
+
const blue = WebApp({ name: "app-blue", image: "app:1.0", labels: { version: "blue" } });
|
|
220
|
+
const green = WebApp({ name: "app-green", image: "app:2.0", labels: { version: "green" } });
|
|
221
|
+
|
|
222
|
+
// Service points to active version — switch by changing selector
|
|
223
|
+
// Active: blue → green (update the Service selector)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Canary
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// Main deployment (90% traffic)
|
|
230
|
+
const main = AutoscaledService({ name: "app", image: "app:1.0", minReplicas: 9, maxReplicas: 20, ... });
|
|
231
|
+
|
|
232
|
+
// Canary deployment (10% traffic) — same app label, fewer replicas
|
|
233
|
+
const canary = WebApp({ name: "app-canary", image: "app:2.0", replicas: 1, labels: { track: "canary" } });
|
|
234
|
+
// Both share the same Service selector ("app.kubernetes.io/name": "app") for traffic splitting
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Gateway API (future direction)
|
|
238
|
+
|
|
239
|
+
Gateway API is the successor to Ingress. Key differences:
|
|
240
|
+
- **HTTPRoute** replaces Ingress rules
|
|
241
|
+
- **Gateway** replaces IngressClass
|
|
242
|
+
- Built-in traffic splitting, header matching, URL rewriting
|
|
243
|
+
- Currently in beta — use Ingress/SecureIngress/AlbIngress for production today
|
|
244
|
+
|
|
245
|
+
When Gateway API reaches GA, new composites will be added. For now, use CRD import if you need Gateway API resources.
|