blob: f5dbc9bf6f61fd25c9bd1ebee59aa4812d13ef3d [file] [log] [blame]
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +02001# Deploy a CockroachDB cluster in secure mode.
2# This creates an N-node cluster based on a given static topology.
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +02003
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +02004# Can be used either in own namespace or in an existing one:
5# crdb: cockroachdb.Cluster("q3kdb") {
6# cfg+: {
7# namespace: "q3k", // if not given, will create 'q3kdb' namespace
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +02008# topology: [
Sergiusz Bazanski184678b2019-06-22 02:07:41 +02009# { name: "a", node: "bc01n01.hswaw.net" },
10# { name: "b", node: "bc01n02.hswaw.net" },
11# { name: "c", node: "bc01n03.hswaw.net" },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +020012# ],
13# hostPath: "/var/db/cockroach-q3k",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020014# },
15#},
16#
17# After the cluster is up, you can get to an administrateive SQL shell:
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +020018# $ NS=q3k kubectl -n $NS exec -it $(kubectl -n $NS get pods -o name | grep client- | cut -d/ -f 2) /cockroach/cockroach sql
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020019# root@q3kdb-cockroachdb-0.q3kdb-internal.q3k.svc.cluster.local:26257/defaultdb>
20#
21# Then, you can create some users and databases for applications:
22# defaultdb> CREATE DATABASE wykop;
23# defaultdb> CREATE USER bialkov PASSWORD hackme;
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +020024# defaultdb> GRANT ALL ON DATABASE wykop TO bialkov;
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020025#
26# You are then ready to access the database via the public service from your application.
27#
28# PGCLIENTENCODING=utf8 psql -h q3kdb-public -p 26257 -U bialkov wykop
29# Password for user bialkov:
30# psql (10.9 (Ubuntu 10.9-0ubuntu0.18.04.1), server 9.5.0)
31# SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256, bits: 128, compression: off)
32# Type "help" for help.
33#
34# wykop=>
35
36
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +020037local kube = import "../../../kube/kube.libsonnet";
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020038local policies = import "../../../kube/policies.libsonnet";
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020039
40{
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020041 Cluster(name): {
42 local cluster = self,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020043
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020044 cfg:: {
Bartosz Stebel0173f502023-01-30 20:41:25 +010045 image: "cockroachdb/cockroach:v21.1.21",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +020046
47 # Must be unique per cluster.
48 portServe: 26257,
49 portHttp: 8080,
50 hostPath: error "hostPath must be defined",
51 topology: error "topology must be defined",
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +020052 clients: [],
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +020053
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020054 namespace: null,
55 ownNamespace: cluster.cfg.namespace == null,
Serge Bazanski509ab6e2020-07-30 22:43:20 +020056 extraDNS: [],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020057 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020058
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020059 namespaceName:: if cluster.cfg.namespace != null then cluster.cfg.namespace else name,
60
61 metadata:: {
62 namespace: cluster.namespaceName,
63 labels: {
64 "app.kubernetes.io/name": "cockroachdb",
65 "app.kubernetes.io/managed-by": "kubecfg",
66 "app.kubernetes.io/component": "cockroachdb",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020067 },
68 },
69
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020070 namespace: {
71 [if cluster.cfg.ownNamespace then "ns"]: kube.Namespace(cluster.namespaceName),
72 },
73
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020074 insecurePolicy: policies.AllowNamespaceInsecure(cluster.namespaceName),
75
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020076 name(suffix):: if cluster.cfg.ownNamespace then suffix else name + "-" + suffix,
77
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020078 pki: {
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +020079 selfSignedIssuer: kube.Issuer(cluster.name("selfsigned")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020080 metadata+: cluster.metadata,
81 spec: {
82 selfSigned: {},
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020083 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020084 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020085
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +020086 selfSignedKeypair: kube.Certificate(cluster.name("cluster-ca")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020087 metadata+: cluster.metadata,
88 spec: {
89 secretName: cluster.name("cluster-ca"),
90 duration: "43800h0m0s", // 5 years
91 isCA: true,
92 issuerRef: {
93 name: cluster.pki.selfSignedIssuer.metadata.name,
94 },
95 commonName: "cockroachdb-cluster-ca",
96 },
97 },
98
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +020099 clusterIssuer: kube.Issuer(cluster.name("cluster-ca")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200100 metadata+: cluster.metadata,
101 spec: {
102 ca: {
103 secretName: cluster.pki.selfSignedKeypair.metadata.name,
104 },
105 },
106 },
107
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200108 nodeCertificate: kube.Certificate(cluster.name("node")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200109 metadata+: cluster.metadata,
110 spec: {
111 secretName: "cockroachdb-node-cert",
112 duration: "43800h0m0s", // 5 years
113 issuerRef: {
114 name: cluster.pki.clusterIssuer.metadata.name,
115 },
116 commonName: "node",
117 dnsNames: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200118 cluster.publicService.metadata.name,
119 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200120 cluster.publicService.host,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200121 std.join(".", [cluster.publicService.host, "cluster.local" ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200122 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
123 ] + [
124 "%s.cluster.local" % s.service.host
125 for s in cluster.servers
Serge Bazanski509ab6e2020-07-30 22:43:20 +0200126 ] + cluster.cfg.extraDNS,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200127 },
128 },
129
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200130 clientCertificate: kube.Certificate(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200131 metadata+: cluster.metadata,
132 spec: {
133 secretName: cluster.name("client-certificate"),
134 duration: "43800h0m0s", // 5 years
135 issuerRef: {
136 name: cluster.pki.clusterIssuer.metadata.name,
137 },
138 commonName: "root",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200139 },
140 },
141 },
142
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200143 serviceAccount: kube.ServiceAccount(cluster.name("cockroachdb")) {
144 metadata+: cluster.metadata,
145 },
146
147 role: kube.Role(cluster.name("cockroachdb")) {
148 metadata+: cluster.metadata,
149 rules: [
150 {
151 apiGroups: [ "" ],
152 resources: [ "secrets" ],
153 verbs: [ "get" ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200154 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200155 ],
156 },
157
158 roleBinding: kube.RoleBinding(cluster.name("cockroachdb")) {
159 metadata+: cluster.metadata,
160 roleRef_: cluster.role,
161 subjects_: [cluster.serviceAccount],
162 },
163
164 publicService: kube.Service(cluster.name("public")) {
165 metadata+: cluster.metadata,
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200166 target_pod:: cluster.servers[0].deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200167 spec+: {
168 ports: [
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200169 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
170 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200171 ],
Sergiusz Bazanskid5338922019-08-09 14:13:50 +0200172 type: "LoadBalancer",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200173 },
174 },
175
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200176 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
177 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200178 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200179 selector: {
180 matchLabels: {
181 "app.kubernetes.io/component": "cockroachdb",
182 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200183 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200184 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200185 },
186 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200187
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200188 servers: [
189 {
190 local server = self,
191 service: kube.Service(cluster.name("server-" + el.name)) {
192 metadata+: cluster.metadata + {
193 annotations+: {
194 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
195 "prometheus.io/scrape": "true",
196 "prometheus.io/path": "_status/vars",
197 "prometheus.io/port": std.toString(cluster.cfg.portHttp),
198 },
199 },
200 target_pod:: server.deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200201 spec+: {
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200202 ports: [
203 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
204 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
205 ],
206 publishNotReadyAddresses: true,
207 clusterIP: "None",
208 },
209 },
210 deploy: kube.Deployment(cluster.name("server-" + el.name)) {
211 metadata+: cluster.metadata {
212 labels+: {
213 "app.kubernetes.io/component": "server",
214 "kubernetes.hackerspace.pl/cockroachdb-server": el.name,
215 },
216 },
217 spec+: {
Serge Bazanskibdd403c2021-10-28 23:37:38 +0000218 strategy+: {
219 type: "RollingUpdate",
220 rollingUpdate: {
221 maxSurge: 0,
222 maxUnavailable: 1,
223 },
224 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200225 template+: {
226 metadata: server.deploy.metadata,
227 spec+: {
228 dnsPolicy: "ClusterFirst",
229 serviceAccountName: cluster.serviceAccount.metadata.name,
230 nodeSelector: {
231 "kubernetes.io/hostname": el.node,
232 },
233 containers: [
234 kube.Container("cockroachdb") {
235 image: cluster.cfg.image,
236 imagePullPolicy: "IfNotPresent",
237 resources: {
238 requests: {
239 cpu: "2",
240 memory: "6Gi",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200241 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200242 limits: {
243 memory: "6Gi",
244 },
245 },
246 ports_: {
247 "grpc": { containerPort: cluster.cfg.portServe },
248 "http": { containerPort: cluster.cfg.portHttp },
249 },
250 livenessProbe: {
251 httpGet: {
252 path: "/health",
253 port: "http",
254 },
255 initialDelaySeconds: 30,
256 periodSeconds: 5,
257 },
258 readinessProbe: {
259 httpGet: {
260 path: "/health?ready=1",
261 port: "http",
262 },
263 initialDelaySeconds: 10,
264 periodSeconds: 5,
265 failureThreshold: 2,
266 },
267 volumeMounts: [
268 {
269 name: "datadir",
270 mountPath: "/cockroach/cockroach-data",
271 },
272 {
273 name: "certs",
274 mountPath: "/cockroach/cockroach-certs/node.crt",
275 subPath: "tls.crt",
276 },
277 {
278 name: "certs",
279 mountPath: "/cockroach/cockroach-certs/node.key",
280 subPath: "tls.key",
281 },
282 {
283 name: "certs",
284 mountPath: "/cockroach/cockroach-certs/ca.crt",
285 subPath: "ca.crt",
286 },
287 ],
288 env_: {
289 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
290 },
291 command: [
292 "/cockroach/cockroach", "start",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200293 "--certs-dir", "/cockroach/cockroach-certs",
294 "--advertise-host", "%s.cluster.local" % server.service.host,
295 "--cache", "25%", "--max-sql-memory", "25%",
Serge Bazanskibdd403c2021-10-28 23:37:38 +0000296 "--join", std.join(",", ["%s.cluster.local:%d" % [s.service.host, cluster.cfg.portServe] for s in cluster.servers if s.service.host != server.service.host]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200297 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
298 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
299 ],
300 },
301 ],
302 terminationGracePeriodSeconds: 60,
303 volumes: [
304 {
305 name: "datadir",
306 hostPath: {
307 path: cluster.cfg.hostPath,
308 },
309 },
310 {
311 name: "certs",
312 secret: {
313 secretName: cluster.pki.nodeCertificate.spec.secretName,
314 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200315 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200316 },
317 ],
318 },
319 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200320 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200321 }
322 }
323 for el in cluster.cfg.topology
324 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200325
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200326 initJob: kube.Job(cluster.name("init")) {
327 metadata+: cluster.metadata,
328 spec: {
329 template: {
330 metadata+: cluster.metadata,
331 spec+: {
332 serviceAccountName: cluster.serviceAccount.metadata.name,
333 containers: [
334 kube.Container("cluster-init") {
335 image: cluster.cfg.image,
336 imagePullPolicy: "IfNotPresent",
337 env_: {
338 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
339 },
340 command: [
341 "/bin/bash",
342 "-ecx",
Serge Bazanski3b893532021-03-17 21:48:28 +0000343 "/cockroach/cockroach init --host=%s.cluster.local:%d || true" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200344 ],
345 volumeMounts: [
346 {
347 name: "certs",
348 mountPath: "/cockroach/cockroach-certs/ca.crt",
349 subPath: "ca.crt",
350 },
351 {
352 name: "certs",
353 mountPath: "/cockroach/cockroach-certs/client.root.crt",
354 subPath: "tls.crt",
355 },
356 {
357 name: "certs",
358 mountPath: "/cockroach/cockroach-certs/client.root.key",
359 subPath: "tls.key",
360 },
361 ],
362 },
363 ],
364 restartPolicy: "OnFailure",
365 volumes: [
366 {
367 name: "certs",
368 secret: {
369 secretName: cluster.pki.clientCertificate.spec.secretName,
370 defaultMode: kube.parseOctal("400")
371 }
372 },
373 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200374 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200375 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200376 },
377 },
378
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200379 Client(name):: {
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200380 certificate: kube.Certificate(cluster.name("client-%s" % name)) {
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200381 metadata+: cluster.metadata,
382 spec: {
383 secretName: cluster.name("client-%s-certificate" % name),
384 duration: "43800h0m0s", // 5 years
385 issuerRef: {
386 name: cluster.pki.clusterIssuer.metadata.name,
387 },
388 commonName: name,
389 },
390 },
391 },
392
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200393 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200394 metadata+: cluster.metadata {
395 labels+: {
396 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200397 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200398 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200399 spec+: {
400 template: {
401 metadata: cluster.client.metadata,
402 spec+: {
403 terminationGracePeriodSeconds: 5,
404 containers: [
405 kube.Container("cockroachdb-client") {
406 image: cluster.cfg.image,
407 env_: {
408 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
409 "COCKROACH_HOST": cluster.publicService.host,
410 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
411 },
412 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
413 volumeMounts: [
414 {
415 name: "certs",
416 mountPath: "/cockroach/cockroach-certs/ca.crt",
417 subPath: "ca.crt",
418 },
419 {
420 name: "certs",
421 mountPath: "/cockroach/cockroach-certs/client.root.crt",
422 subPath: "tls.crt",
423 },
424 {
425 name: "certs",
426 mountPath: "/cockroach/cockroach-certs/client.root.key",
427 subPath: "tls.key",
428 },
429 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200430 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200431 ],
432 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200433 {
434 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200435 secret: {
436 secretName: cluster.pki.clientCertificate.spec.secretName,
437 defaultMode: kube.parseOctal("400")
438 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200439 },
440 ],
441 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200442 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200443 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200444 },
445 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200446}