kube_cluster 0.2.1 → 0.3.1
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +8 -10
- data/examples/01-basic-redis-pod/manifest.rb +3 -3
- data/examples/02-manifest-with-middleware/manifest.rb +37 -0
- data/examples/02-manifest-with-middleware/middleware/labels.rb +4 -0
- data/examples/02-manifest-with-middleware/middleware/namespace.rb +4 -0
- data/examples/02-manifest-with-middleware/templates/config_map.rb +13 -0
- data/examples/02-manifest-with-middleware/templates/deployment.rb +59 -0
- data/examples/02-manifest-with-middleware/templates/horizontal_pod_autoscaler.rb +30 -0
- data/examples/02-manifest-with-middleware/templates/ingress.rb +38 -0
- data/examples/02-manifest-with-middleware/templates/service.rb +12 -0
- data/examples/{version2 → 03-app-with-database}/demo.rb +2 -2
- data/examples/{version2 → 03-app-with-database}/postgresql.rb +4 -4
- data/examples/{version2 → 03-app-with-database}/ruby_on_rails.rb +1 -1
- data/kube_cluster.gemspec +1 -1
- data/lib/kube/cluster/manifest.rb +13 -64
- data/lib/kube/cluster/middleware/annotations.rb +32 -0
- data/lib/kube/cluster/middleware/hpa_for_deployment.rb +111 -0
- data/lib/kube/cluster/{manifest/middleware → middleware}/ingress_for_service.rb +36 -34
- data/lib/kube/cluster/middleware/labels.rb +59 -0
- data/lib/kube/cluster/middleware/namespace.rb +31 -0
- data/lib/kube/cluster/middleware/pod_anti_affinity.rb +61 -0
- data/lib/kube/cluster/middleware/resource_preset.rb +64 -0
- data/lib/kube/cluster/middleware/security_context.rb +84 -0
- data/lib/kube/cluster/middleware/service_for_deployment.rb +71 -0
- data/lib/kube/cluster/middleware/stack.rb +43 -0
- data/lib/kube/cluster/middleware.rb +69 -0
- data/lib/kube/cluster/resource.rb +78 -0
- data/lib/kube/cluster/version.rb +1 -1
- data/lib/kube/cluster.rb +21 -0
- metadata +27 -21
- data/examples/database/manifest.rb +0 -238
- data/examples/web-app/manifest.rb +0 -215
- data/lib/kube/cluster/manifest/middleware/annotations.rb +0 -32
- data/lib/kube/cluster/manifest/middleware/hpa_for_deployment.rb +0 -109
- data/lib/kube/cluster/manifest/middleware/labels.rb +0 -59
- data/lib/kube/cluster/manifest/middleware/namespace.rb +0 -31
- data/lib/kube/cluster/manifest/middleware/pod_anti_affinity.rb +0 -61
- data/lib/kube/cluster/manifest/middleware/resource_preset.rb +0 -64
- data/lib/kube/cluster/manifest/middleware/security_context.rb +0 -84
- data/lib/kube/cluster/manifest/middleware/service_for_deployment.rb +0 -69
- data/lib/kube/cluster/manifest/middleware.rb +0 -178
- data/lib/kube/cluster/manifest/stack.rb +0 -56
- /data/examples/{version2 → 03-app-with-database}/helpers.rb +0 -0
- /data/examples/{version2 → 03-app-with-database}/my_app.rb +0 -0
data/lib/kube/cluster.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "cluster/connection"
|
|
|
7
7
|
require_relative "cluster/instance"
|
|
8
8
|
require_relative "cluster/resource"
|
|
9
9
|
require_relative "cluster/manifest"
|
|
10
|
+
require_relative "cluster/middleware"
|
|
10
11
|
require 'kube/ctl'
|
|
11
12
|
|
|
12
13
|
module Kube
|
|
@@ -18,5 +19,25 @@ module Kube
|
|
|
18
19
|
def self.connect(kubeconfig:)
|
|
19
20
|
Instance.new(kubeconfig: kubeconfig)
|
|
20
21
|
end
|
|
22
|
+
|
|
23
|
+
# Returns an anonymous subclass of Kube::Cluster::Resource for the
|
|
24
|
+
# given Kubernetes kind, mirroring Kube::Schema[kind] but with
|
|
25
|
+
# dirty tracking, persistence, and resource helper methods.
|
|
26
|
+
#
|
|
27
|
+
# Kube::Cluster["Deployment"].new { metadata.name = "web" }
|
|
28
|
+
#
|
|
29
|
+
def self.[](kind)
|
|
30
|
+
@resource_classes ||= {}
|
|
31
|
+
@resource_classes[kind] ||= begin
|
|
32
|
+
schema_class = Kube::Schema[kind]
|
|
33
|
+
Class.new(Resource) do
|
|
34
|
+
@schema = schema_class.schema
|
|
35
|
+
@defaults = schema_class.defaults
|
|
36
|
+
|
|
37
|
+
def self.schema = @schema || superclass.schema
|
|
38
|
+
def self.defaults = @defaults || superclass.defaults
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
21
42
|
end
|
|
22
43
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kube_cluster
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan K
|
|
@@ -57,14 +57,14 @@ dependencies:
|
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 1.
|
|
60
|
+
version: 1.3.0
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 1.
|
|
67
|
+
version: 1.3.0
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: kube_kit
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -121,13 +121,19 @@ files:
|
|
|
121
121
|
- bin/test
|
|
122
122
|
- docker-compose.yml
|
|
123
123
|
- examples/01-basic-redis-pod/manifest.rb
|
|
124
|
-
- examples/
|
|
125
|
-
- examples/
|
|
126
|
-
- examples/
|
|
127
|
-
- examples/
|
|
128
|
-
- examples/
|
|
129
|
-
- examples/
|
|
130
|
-
- examples/
|
|
124
|
+
- examples/02-manifest-with-middleware/manifest.rb
|
|
125
|
+
- examples/02-manifest-with-middleware/middleware/labels.rb
|
|
126
|
+
- examples/02-manifest-with-middleware/middleware/namespace.rb
|
|
127
|
+
- examples/02-manifest-with-middleware/templates/config_map.rb
|
|
128
|
+
- examples/02-manifest-with-middleware/templates/deployment.rb
|
|
129
|
+
- examples/02-manifest-with-middleware/templates/horizontal_pod_autoscaler.rb
|
|
130
|
+
- examples/02-manifest-with-middleware/templates/ingress.rb
|
|
131
|
+
- examples/02-manifest-with-middleware/templates/service.rb
|
|
132
|
+
- examples/03-app-with-database/demo.rb
|
|
133
|
+
- examples/03-app-with-database/helpers.rb
|
|
134
|
+
- examples/03-app-with-database/my_app.rb
|
|
135
|
+
- examples/03-app-with-database/postgresql.rb
|
|
136
|
+
- examples/03-app-with-database/ruby_on_rails.rb
|
|
131
137
|
- flake.lock
|
|
132
138
|
- flake.nix
|
|
133
139
|
- kube_cluster.gemspec
|
|
@@ -136,17 +142,17 @@ files:
|
|
|
136
142
|
- lib/kube/cluster/connection.rb
|
|
137
143
|
- lib/kube/cluster/instance.rb
|
|
138
144
|
- lib/kube/cluster/manifest.rb
|
|
139
|
-
- lib/kube/cluster/
|
|
140
|
-
- lib/kube/cluster/
|
|
141
|
-
- lib/kube/cluster/
|
|
142
|
-
- lib/kube/cluster/
|
|
143
|
-
- lib/kube/cluster/
|
|
144
|
-
- lib/kube/cluster/
|
|
145
|
-
- lib/kube/cluster/
|
|
146
|
-
- lib/kube/cluster/
|
|
147
|
-
- lib/kube/cluster/
|
|
148
|
-
- lib/kube/cluster/
|
|
149
|
-
- lib/kube/cluster/
|
|
145
|
+
- lib/kube/cluster/middleware.rb
|
|
146
|
+
- lib/kube/cluster/middleware/annotations.rb
|
|
147
|
+
- lib/kube/cluster/middleware/hpa_for_deployment.rb
|
|
148
|
+
- lib/kube/cluster/middleware/ingress_for_service.rb
|
|
149
|
+
- lib/kube/cluster/middleware/labels.rb
|
|
150
|
+
- lib/kube/cluster/middleware/namespace.rb
|
|
151
|
+
- lib/kube/cluster/middleware/pod_anti_affinity.rb
|
|
152
|
+
- lib/kube/cluster/middleware/resource_preset.rb
|
|
153
|
+
- lib/kube/cluster/middleware/security_context.rb
|
|
154
|
+
- lib/kube/cluster/middleware/service_for_deployment.rb
|
|
155
|
+
- lib/kube/cluster/middleware/stack.rb
|
|
150
156
|
- lib/kube/cluster/resource.rb
|
|
151
157
|
- lib/kube/cluster/resource/dirty_tracking.rb
|
|
152
158
|
- lib/kube/cluster/resource/persistence.rb
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Database (PostgreSQL) Example
|
|
5
|
-
#
|
|
6
|
-
# Demonstrates the Ruby equivalents of Bitnami common chart patterns:
|
|
7
|
-
# - Secret lifecycle management (_secrets.tpl)
|
|
8
|
-
# - StorageClass resolution (_storage.tpl)
|
|
9
|
-
# - StatefulSet with persistent volumes
|
|
10
|
-
# - Headless service for stable DNS
|
|
11
|
-
# - NetworkPolicy for database isolation
|
|
12
|
-
# - Standard labels and naming (_labels.tpl, _names.tpl)
|
|
13
|
-
# - Resource presets (_resources.tpl)
|
|
14
|
-
#
|
|
15
|
-
# Usage:
|
|
16
|
-
# ruby examples/database/manifest.rb
|
|
17
|
-
# ruby examples/database/manifest.rb > database.yaml
|
|
18
|
-
|
|
19
|
-
require "kube/schema"
|
|
20
|
-
require "securerandom"
|
|
21
|
-
|
|
22
|
-
# ── Naming ────────────────────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
APP_NAME = "postgresql"
|
|
25
|
-
RELEASE_NAME = "my-release"
|
|
26
|
-
NAMESPACE = "database"
|
|
27
|
-
FULLNAME = "#{RELEASE_NAME}-#{APP_NAME}"[0, 63].chomp("-")
|
|
28
|
-
|
|
29
|
-
# ── Labels ────────────────────────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
STANDARD_LABELS = {
|
|
32
|
-
"app.kubernetes.io/name": APP_NAME,
|
|
33
|
-
"app.kubernetes.io/instance": RELEASE_NAME,
|
|
34
|
-
"app.kubernetes.io/version": "16.4.0",
|
|
35
|
-
"app.kubernetes.io/component": "primary",
|
|
36
|
-
"app.kubernetes.io/managed-by": "kube_cluster",
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
MATCH_LABELS = STANDARD_LABELS.slice(
|
|
40
|
-
:"app.kubernetes.io/name",
|
|
41
|
-
:"app.kubernetes.io/instance",
|
|
42
|
-
:"app.kubernetes.io/component",
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
# ── Secrets (from _secrets.tpl) ───────────────────────────────────────────────
|
|
46
|
-
# Bitnami's secrets.passwords.manage generates random passwords, reuses existing
|
|
47
|
-
# ones on upgrade, and base64 encodes them. In Ruby we do this directly.
|
|
48
|
-
|
|
49
|
-
POSTGRES_PASSWORD = SecureRandom.alphanumeric(24)
|
|
50
|
-
REPLICATION_PASSWORD = SecureRandom.alphanumeric(24)
|
|
51
|
-
|
|
52
|
-
def base64(str)
|
|
53
|
-
[str].pack("m0")
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# ── Storage (from _storage.tpl) ───────────────────────────────────────────────
|
|
57
|
-
# Bitnami resolves storage class from global > persistence > default.
|
|
58
|
-
# The "-" convention means explicitly use the default storage class (empty string).
|
|
59
|
-
|
|
60
|
-
STORAGE_CLASS = "standard" # set to "-" for default, or nil to omit
|
|
61
|
-
STORAGE_SIZE = "10Gi"
|
|
62
|
-
|
|
63
|
-
# ── Resource presets ──────────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
RESOURCES = {
|
|
66
|
-
requests: { cpu: "500m", memory: "512Mi" },
|
|
67
|
-
limits: { cpu: "750m", memory: "768Mi" },
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
# ── Build manifests ───────────────────────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
manifest = Kube::Schema::Manifest.new
|
|
73
|
-
|
|
74
|
-
# -- Namespace --
|
|
75
|
-
|
|
76
|
-
manifest << Kube::Schema["Namespace"].new {
|
|
77
|
-
metadata.name = NAMESPACE
|
|
78
|
-
metadata.labels = STANDARD_LABELS.reject { |k, _| k == :"app.kubernetes.io/component" }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# -- Secret --
|
|
82
|
-
# Pattern from _secrets.tpl: base64-encoded credentials, separate keys for
|
|
83
|
-
# each password, supports existing secret reuse on upgrade.
|
|
84
|
-
|
|
85
|
-
manifest << Kube::Schema["Secret"].new {
|
|
86
|
-
metadata.name = FULLNAME
|
|
87
|
-
metadata.namespace = NAMESPACE
|
|
88
|
-
metadata.labels = STANDARD_LABELS
|
|
89
|
-
self.type = "Opaque"
|
|
90
|
-
self.data = {
|
|
91
|
-
"postgres-password": base64(POSTGRES_PASSWORD),
|
|
92
|
-
"replication-password": base64(REPLICATION_PASSWORD),
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
# -- Headless Service (for StatefulSet stable DNS) --
|
|
97
|
-
|
|
98
|
-
manifest << Kube::Schema["Service"].new {
|
|
99
|
-
metadata.name = "#{FULLNAME}-headless"
|
|
100
|
-
metadata.namespace = NAMESPACE
|
|
101
|
-
metadata.labels = STANDARD_LABELS
|
|
102
|
-
|
|
103
|
-
spec.clusterIP = "None"
|
|
104
|
-
spec.selector = MATCH_LABELS
|
|
105
|
-
spec.ports = [
|
|
106
|
-
{ name: "tcp-postgresql", port: 5432, targetPort: "tcp-postgresql" },
|
|
107
|
-
]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
# -- Primary Service (for client connections) --
|
|
111
|
-
|
|
112
|
-
manifest << Kube::Schema["Service"].new {
|
|
113
|
-
metadata.name = FULLNAME
|
|
114
|
-
metadata.namespace = NAMESPACE
|
|
115
|
-
metadata.labels = STANDARD_LABELS
|
|
116
|
-
|
|
117
|
-
spec.selector = MATCH_LABELS
|
|
118
|
-
spec.ports = [
|
|
119
|
-
{ name: "tcp-postgresql", port: 5432, targetPort: "tcp-postgresql" },
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# -- StatefulSet --
|
|
124
|
-
# Uses storage class resolution pattern, secret references, resource presets,
|
|
125
|
-
# pod anti-affinity for spreading replicas.
|
|
126
|
-
|
|
127
|
-
manifest << Kube::Schema["StatefulSet"].new {
|
|
128
|
-
metadata.name = FULLNAME
|
|
129
|
-
metadata.namespace = NAMESPACE
|
|
130
|
-
metadata.labels = STANDARD_LABELS
|
|
131
|
-
|
|
132
|
-
spec.serviceName = "#{FULLNAME}-headless"
|
|
133
|
-
spec.replicas = 1
|
|
134
|
-
spec.selector.matchLabels = MATCH_LABELS
|
|
135
|
-
|
|
136
|
-
spec.template.metadata.labels = STANDARD_LABELS
|
|
137
|
-
spec.template.spec.containers = [
|
|
138
|
-
{
|
|
139
|
-
name: APP_NAME,
|
|
140
|
-
image: "docker.io/postgres:16.4-alpine",
|
|
141
|
-
ports: [
|
|
142
|
-
{ name: "tcp-postgresql", containerPort: 5432 },
|
|
143
|
-
],
|
|
144
|
-
resources: RESOURCES,
|
|
145
|
-
env: [
|
|
146
|
-
{ name: "POSTGRES_PASSWORD", valueFrom: { secretKeyRef: { name: FULLNAME, key: "postgres-password" } } },
|
|
147
|
-
{ name: "PGDATA", value: "/var/lib/postgresql/data/pgdata" },
|
|
148
|
-
],
|
|
149
|
-
volumeMounts: [
|
|
150
|
-
{ name: "data", mountPath: "/var/lib/postgresql/data" },
|
|
151
|
-
],
|
|
152
|
-
livenessProbe: {
|
|
153
|
-
exec: { command: ["pg_isready", "-U", "postgres"] },
|
|
154
|
-
initialDelaySeconds: 30,
|
|
155
|
-
periodSeconds: 10,
|
|
156
|
-
timeoutSeconds: 5,
|
|
157
|
-
failureThreshold: 6,
|
|
158
|
-
},
|
|
159
|
-
readinessProbe: {
|
|
160
|
-
exec: { command: ["pg_isready", "-U", "postgres"] },
|
|
161
|
-
initialDelaySeconds: 5,
|
|
162
|
-
periodSeconds: 10,
|
|
163
|
-
timeoutSeconds: 5,
|
|
164
|
-
failureThreshold: 6,
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
]
|
|
168
|
-
|
|
169
|
-
# Pod anti-affinity: hard anti-affinity to guarantee one pod per node
|
|
170
|
-
# (from _affinities.tpl: common.affinities.pods.hard)
|
|
171
|
-
spec.template.spec.affinity = {
|
|
172
|
-
podAntiAffinity: {
|
|
173
|
-
requiredDuringSchedulingIgnoredDuringExecution: [
|
|
174
|
-
{
|
|
175
|
-
labelSelector: { matchLabels: MATCH_LABELS },
|
|
176
|
-
topologyKey: "kubernetes.io/hostname",
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
},
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
# Storage class resolution (from _storage.tpl)
|
|
183
|
-
storage_class = STORAGE_CLASS == "-" ? "" : STORAGE_CLASS
|
|
184
|
-
|
|
185
|
-
spec.volumeClaimTemplates = [
|
|
186
|
-
{
|
|
187
|
-
metadata: { name: "data" },
|
|
188
|
-
spec: {
|
|
189
|
-
accessModes: ["ReadWriteOnce"],
|
|
190
|
-
storageClassName: storage_class,
|
|
191
|
-
resources: { requests: { storage: STORAGE_SIZE } },
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
# -- NetworkPolicy --
|
|
198
|
-
# Isolate the database: only allow ingress from pods with the app label,
|
|
199
|
-
# deny everything else. This is a common production hardening pattern.
|
|
200
|
-
|
|
201
|
-
manifest << Kube::Schema["NetworkPolicy"].new {
|
|
202
|
-
metadata.name = FULLNAME
|
|
203
|
-
metadata.namespace = NAMESPACE
|
|
204
|
-
metadata.labels = STANDARD_LABELS
|
|
205
|
-
|
|
206
|
-
spec.podSelector = { matchLabels: MATCH_LABELS }
|
|
207
|
-
spec.policyTypes = ["Ingress", "Egress"]
|
|
208
|
-
spec.ingress = [
|
|
209
|
-
{
|
|
210
|
-
from: [
|
|
211
|
-
{
|
|
212
|
-
podSelector: {
|
|
213
|
-
matchLabels: { "app.kubernetes.io/name": "web-app" },
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
-
ports: [
|
|
218
|
-
{ protocol: "TCP", port: "5432" },
|
|
219
|
-
],
|
|
220
|
-
},
|
|
221
|
-
]
|
|
222
|
-
# Allow DNS egress + nothing else
|
|
223
|
-
spec.egress = [
|
|
224
|
-
{
|
|
225
|
-
to: [
|
|
226
|
-
{ namespaceSelector: {} },
|
|
227
|
-
],
|
|
228
|
-
ports: [
|
|
229
|
-
{ protocol: "UDP", port: "53" },
|
|
230
|
-
{ protocol: "TCP", port: "53" },
|
|
231
|
-
],
|
|
232
|
-
},
|
|
233
|
-
]
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
# ── Render ────────────────────────────────────────────────────────────────────
|
|
237
|
-
|
|
238
|
-
puts manifest.to_yaml
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "kube/schema"
|
|
5
|
-
|
|
6
|
-
MATCH_LABELS = STANDARD_LABELS.slice(
|
|
7
|
-
:"app.kubernetes.io/name",
|
|
8
|
-
:"app.kubernetes.io/instance",
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
REGISTRY = "docker.io"
|
|
12
|
-
REPOSITORY = "nginx"
|
|
13
|
-
TAG = "1.27.3-alpine"
|
|
14
|
-
IMAGE = "#{REGISTRY}/#{REPOSITORY}:#{TAG}"
|
|
15
|
-
|
|
16
|
-
RESOURCE_PRESETS = {
|
|
17
|
-
"nano": { requests: { cpu: "100m", memory: "128Mi" }, limits: { cpu: "150m", memory: "192Mi" } },
|
|
18
|
-
"micro": { requests: { cpu: "250m", memory: "256Mi" }, limits: { cpu: "375m", memory: "384Mi" } },
|
|
19
|
-
"small": { requests: { cpu: "500m", memory: "512Mi" }, limits: { cpu: "750m", memory: "768Mi" } },
|
|
20
|
-
"medium": { requests: { cpu: "500m", memory: "1024Mi" }, limits: { cpu: "750m", memory: "1536Mi" } },
|
|
21
|
-
"large": { requests: { cpu: "1.0", memory: "2048Mi" }, limits: { cpu: "1.5", memory: "3072Mi" } },
|
|
22
|
-
"xlarge": { requests: { cpu: "1.0", memory: "3072Mi" }, limits: { cpu: "3.0", memory: "6144Mi" } },
|
|
23
|
-
"2xlarge": { requests: { cpu: "1.0", memory: "3072Mi" }, limits: { cpu: "6.0", memory: "12288Mi" } },
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module AutoConfig
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
class MyApp < Kube::Schema::Manifest
|
|
30
|
-
include Helpers
|
|
31
|
-
include AutoConfig
|
|
32
|
-
|
|
33
|
-
def initialize(namespace: DEFAULT_NAMESPACE, labels: STANDARD_LABELS)
|
|
34
|
-
@app_name = "web-app"
|
|
35
|
-
@fullname = "#{RELEASE_NAME}-#{APP_NAME}"[0, 63].chomp("-")
|
|
36
|
-
@namespace = namespace
|
|
37
|
-
|
|
38
|
-
@labels = {
|
|
39
|
-
"app.kubernetes.io/name": @app_name
|
|
40
|
-
"app.kubernetes.io/managed-by": "kube_cluster",
|
|
41
|
-
}
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
stack do
|
|
45
|
-
use Middleware::Namespace
|
|
46
|
-
use Middleware::Labels
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
class Namespace < Kube::Schema["Namespace"]
|
|
50
|
-
def initialize(namespace:)
|
|
51
|
-
build {
|
|
52
|
-
metadata.name = namespace
|
|
53
|
-
}
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
class ConfigMap < Kube::Schema["ConfigMap"]
|
|
58
|
-
def initialize(namespace:)
|
|
59
|
-
build {
|
|
60
|
-
metadata.name = "#{namespace}-config"
|
|
61
|
-
spec.data = {
|
|
62
|
-
RAILS_ENV: "production",
|
|
63
|
-
LOG_LEVEL: "info",
|
|
64
|
-
WORKERS: "4",
|
|
65
|
-
PORT: "3000",
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
class Deployment < Kube::Schema["Deployment"]
|
|
72
|
-
def initialize(namespace:)
|
|
73
|
-
build {
|
|
74
|
-
metadata.name = namespace
|
|
75
|
-
|
|
76
|
-
spec.replicas = 3
|
|
77
|
-
spec.selector.matchLabels = MATCH_LABELS
|
|
78
|
-
|
|
79
|
-
spec.template.metadata.labels = STANDARD_LABELS
|
|
80
|
-
spec.template.metadata.annotations = {
|
|
81
|
-
# Checksum pattern from _utils.tpl -- triggers rolling restart on config change
|
|
82
|
-
"checksum/config": "{{ sha256sum of configmap data }}",
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
spec.template.spec.containers = [
|
|
86
|
-
{
|
|
87
|
-
name: APP_NAME,
|
|
88
|
-
image: IMAGE,
|
|
89
|
-
ports: [{ name: "http", containerPort: 3000, protocol: "TCP" }],
|
|
90
|
-
resources: RESOURCES,
|
|
91
|
-
env: [
|
|
92
|
-
{ name: "PORT", value: "3000" },
|
|
93
|
-
],
|
|
94
|
-
envFrom: [
|
|
95
|
-
{ configMapRef: { name: "#{FULLNAME}-config" } },
|
|
96
|
-
],
|
|
97
|
-
livenessProbe: {
|
|
98
|
-
httpGet: { path: "/healthz", port: "http" },
|
|
99
|
-
initialDelaySeconds: 15,
|
|
100
|
-
periodSeconds: 10,
|
|
101
|
-
},
|
|
102
|
-
readinessProbe: {
|
|
103
|
-
httpGet: { path: "/readyz", port: "http" },
|
|
104
|
-
initialDelaySeconds: 5,
|
|
105
|
-
periodSeconds: 5,
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
]
|
|
109
|
-
|
|
110
|
-
# Pod anti-affinity (from _affinities.tpl)
|
|
111
|
-
# Soft anti-affinity: prefer spreading pods across nodes but don't enforce it
|
|
112
|
-
spec.template.spec.affinity = {
|
|
113
|
-
podAntiAffinity: {
|
|
114
|
-
preferredDuringSchedulingIgnoredDuringExecution: [
|
|
115
|
-
{
|
|
116
|
-
weight: 1,
|
|
117
|
-
podAffinityTerm: {
|
|
118
|
-
labelSelector: {
|
|
119
|
-
matchLabels: MATCH_LABELS,
|
|
120
|
-
},
|
|
121
|
-
topologyKey: "kubernetes.io/hostname",
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
},
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
class Service < Kube::Schema["Service"]
|
|
132
|
-
def initialize(namespace:)
|
|
133
|
-
build {
|
|
134
|
-
metadata.name = namespace
|
|
135
|
-
|
|
136
|
-
spec.selector = MATCH_LABELS
|
|
137
|
-
spec.ports = [
|
|
138
|
-
{ name: "http", port: 80, targetPort: "http", protocol: "TCP" },
|
|
139
|
-
]
|
|
140
|
-
}
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
class Ingress < Kube::Schema["Ingress"]
|
|
145
|
-
def initialize(namespace:)
|
|
146
|
-
build {
|
|
147
|
-
metadata.name = namespace
|
|
148
|
-
metadata.annotations = {
|
|
149
|
-
"cert-manager.io/cluster-issuer": "letsencrypt-prod",
|
|
150
|
-
"nginx.ingress.kubernetes.io/ssl-redirect": "true",
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
spec.ingressClassName = "nginx"
|
|
154
|
-
spec.tls = [
|
|
155
|
-
{
|
|
156
|
-
hosts: ["app.example.com"],
|
|
157
|
-
secretName: "#{namespace}-tls",
|
|
158
|
-
},
|
|
159
|
-
]
|
|
160
|
-
spec.rules = [
|
|
161
|
-
{
|
|
162
|
-
host: "app.example.com",
|
|
163
|
-
http: {
|
|
164
|
-
paths: [
|
|
165
|
-
{
|
|
166
|
-
path: "/",
|
|
167
|
-
pathType: "Prefix",
|
|
168
|
-
backend: {
|
|
169
|
-
service: {
|
|
170
|
-
name: FULLNAME,
|
|
171
|
-
port: { name: "http" },
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
]
|
|
179
|
-
}
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
class HorizontalPodAutoscaler < Kube::Schema["HorizontalPodAutoscaler"]
|
|
184
|
-
def initialize(namespace:)
|
|
185
|
-
build {
|
|
186
|
-
metadata.name = namespace
|
|
187
|
-
|
|
188
|
-
spec.scaleTargetRef.apiVersion = "apps/v1"
|
|
189
|
-
spec.scaleTargetRef.kind = "Deployment"
|
|
190
|
-
spec.scaleTargetRef.name = namespace
|
|
191
|
-
|
|
192
|
-
spec.minReplicas = 3
|
|
193
|
-
spec.maxReplicas = 10
|
|
194
|
-
spec.metrics = [
|
|
195
|
-
{
|
|
196
|
-
type: "Resource",
|
|
197
|
-
resource: {
|
|
198
|
-
name: "cpu",
|
|
199
|
-
target: { type: "Utilization", averageUtilization: 75 },
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
type: "Resource",
|
|
204
|
-
resource: {
|
|
205
|
-
name: "memory",
|
|
206
|
-
target: { type: "Utilization", averageUtilization: 80 },
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
]
|
|
210
|
-
}
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
puts MyApp.new.to_yaml
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kube
|
|
4
|
-
module Cluster
|
|
5
|
-
class Manifest < Kube::Schema::Manifest
|
|
6
|
-
class Middleware
|
|
7
|
-
# Merges annotations into +metadata.annotations+ on every resource.
|
|
8
|
-
# Existing annotations are preserved; the supplied annotations act
|
|
9
|
-
# as defaults that can be overridden per-resource.
|
|
10
|
-
#
|
|
11
|
-
# stack do
|
|
12
|
-
# use Middleware::Annotations,
|
|
13
|
-
# "prometheus.io/scrape": "true",
|
|
14
|
-
# "prometheus.io/port": "9090"
|
|
15
|
-
# end
|
|
16
|
-
#
|
|
17
|
-
class Annotations < Middleware
|
|
18
|
-
def initialize(**annotations)
|
|
19
|
-
@annotations = annotations.transform_keys(&:to_sym).transform_values(&:to_s)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def call(resource)
|
|
23
|
-
h = resource.to_h
|
|
24
|
-
h[:metadata] ||= {}
|
|
25
|
-
h[:metadata][:annotations] = @annotations.merge(h[:metadata][:annotations] || {})
|
|
26
|
-
rebuild(resource, h)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|