blob: c80ee3f0e117937aa0207dc4438297cd73c7f2f0 [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
radex8b8f3872023-11-24 11:09:46 +010029# Password for user bialkov:
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020030# 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.
radex8b8f3872023-11-24 11:09:46 +010033#
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020034# 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,
Bartosz Stebel6f1fda42023-11-02 00:28:27 +0100166 target_pod:: cluster.servers[0].deploy.spec.template + {
167 // no easy way to *drop* a field in jsonnet: https://github.com/google/jsonnet/issues/312
168 // so hide it (the :: is critical, the null doesn't matter much)
169 metadata+: {
170 labels+: {
171 // this is different for each node, so we want to *not* select on it
172 "kubernetes.hackerspace.pl/cockroachdb-server":: null,
173 }
174 }
175 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200176 spec+: {
177 ports: [
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200178 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
179 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200180 ],
Sergiusz Bazanskid5338922019-08-09 14:13:50 +0200181 type: "LoadBalancer",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200182 },
183 },
184
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200185 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
186 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200187 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200188 selector: {
189 matchLabels: {
190 "app.kubernetes.io/component": "cockroachdb",
191 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200192 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200193 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200194 },
195 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200196
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200197 servers: [
198 {
199 local server = self,
200 service: kube.Service(cluster.name("server-" + el.name)) {
201 metadata+: cluster.metadata + {
202 annotations+: {
203 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
204 "prometheus.io/scrape": "true",
205 "prometheus.io/path": "_status/vars",
206 "prometheus.io/port": std.toString(cluster.cfg.portHttp),
207 },
208 },
radex8b8f3872023-11-24 11:09:46 +0100209 target:: server.deploy,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200210 spec+: {
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200211 ports: [
212 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
213 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
214 ],
215 publishNotReadyAddresses: true,
216 clusterIP: "None",
217 },
218 },
219 deploy: kube.Deployment(cluster.name("server-" + el.name)) {
220 metadata+: cluster.metadata {
221 labels+: {
222 "app.kubernetes.io/component": "server",
223 "kubernetes.hackerspace.pl/cockroachdb-server": el.name,
224 },
225 },
226 spec+: {
Serge Bazanskibdd403c2021-10-28 23:37:38 +0000227 strategy+: {
228 type: "RollingUpdate",
229 rollingUpdate: {
230 maxSurge: 0,
231 maxUnavailable: 1,
232 },
233 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200234 template+: {
235 metadata: server.deploy.metadata,
236 spec+: {
237 dnsPolicy: "ClusterFirst",
238 serviceAccountName: cluster.serviceAccount.metadata.name,
239 nodeSelector: {
240 "kubernetes.io/hostname": el.node,
241 },
242 containers: [
243 kube.Container("cockroachdb") {
244 image: cluster.cfg.image,
245 imagePullPolicy: "IfNotPresent",
246 resources: {
247 requests: {
248 cpu: "2",
249 memory: "6Gi",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200250 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200251 limits: {
252 memory: "6Gi",
253 },
254 },
255 ports_: {
256 "grpc": { containerPort: cluster.cfg.portServe },
257 "http": { containerPort: cluster.cfg.portHttp },
258 },
259 livenessProbe: {
260 httpGet: {
261 path: "/health",
262 port: "http",
263 },
264 initialDelaySeconds: 30,
265 periodSeconds: 5,
266 },
267 readinessProbe: {
268 httpGet: {
269 path: "/health?ready=1",
270 port: "http",
271 },
272 initialDelaySeconds: 10,
273 periodSeconds: 5,
274 failureThreshold: 2,
275 },
276 volumeMounts: [
277 {
278 name: "datadir",
279 mountPath: "/cockroach/cockroach-data",
280 },
281 {
282 name: "certs",
283 mountPath: "/cockroach/cockroach-certs/node.crt",
284 subPath: "tls.crt",
285 },
286 {
287 name: "certs",
288 mountPath: "/cockroach/cockroach-certs/node.key",
289 subPath: "tls.key",
290 },
291 {
292 name: "certs",
293 mountPath: "/cockroach/cockroach-certs/ca.crt",
294 subPath: "ca.crt",
295 },
296 ],
297 env_: {
298 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
299 },
300 command: [
301 "/cockroach/cockroach", "start",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200302 "--certs-dir", "/cockroach/cockroach-certs",
radex8b8f3872023-11-24 11:09:46 +0100303 "--advertise-host", "%s.cluster.local" % server.service.host,
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200304 "--cache", "25%", "--max-sql-memory", "25%",
Serge Bazanskibdd403c2021-10-28 23:37:38 +0000305 "--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 +0200306 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
307 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
308 ],
309 },
310 ],
311 terminationGracePeriodSeconds: 60,
312 volumes: [
313 {
314 name: "datadir",
315 hostPath: {
316 path: cluster.cfg.hostPath,
317 },
318 },
319 {
320 name: "certs",
321 secret: {
322 secretName: cluster.pki.nodeCertificate.spec.secretName,
323 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200324 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200325 },
326 ],
327 },
328 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200329 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200330 }
331 }
332 for el in cluster.cfg.topology
333 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200334
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200335 initJob: kube.Job(cluster.name("init")) {
336 metadata+: cluster.metadata,
337 spec: {
338 template: {
339 metadata+: cluster.metadata,
340 spec+: {
341 serviceAccountName: cluster.serviceAccount.metadata.name,
342 containers: [
343 kube.Container("cluster-init") {
344 image: cluster.cfg.image,
345 imagePullPolicy: "IfNotPresent",
346 env_: {
347 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
348 },
349 command: [
350 "/bin/bash",
351 "-ecx",
Serge Bazanski3b893532021-03-17 21:48:28 +0000352 "/cockroach/cockroach init --host=%s.cluster.local:%d || true" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200353 ],
354 volumeMounts: [
355 {
356 name: "certs",
357 mountPath: "/cockroach/cockroach-certs/ca.crt",
358 subPath: "ca.crt",
359 },
360 {
361 name: "certs",
362 mountPath: "/cockroach/cockroach-certs/client.root.crt",
363 subPath: "tls.crt",
364 },
365 {
366 name: "certs",
367 mountPath: "/cockroach/cockroach-certs/client.root.key",
368 subPath: "tls.key",
369 },
370 ],
371 },
372 ],
373 restartPolicy: "OnFailure",
374 volumes: [
375 {
376 name: "certs",
377 secret: {
378 secretName: cluster.pki.clientCertificate.spec.secretName,
379 defaultMode: kube.parseOctal("400")
380 }
381 },
382 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200383 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200384 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200385 },
386 },
387
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200388 Client(name):: {
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200389 certificate: kube.Certificate(cluster.name("client-%s" % name)) {
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200390 metadata+: cluster.metadata,
391 spec: {
392 secretName: cluster.name("client-%s-certificate" % name),
393 duration: "43800h0m0s", // 5 years
394 issuerRef: {
395 name: cluster.pki.clusterIssuer.metadata.name,
396 },
397 commonName: name,
398 },
399 },
400 },
401
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200402 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200403 metadata+: cluster.metadata {
404 labels+: {
405 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200406 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200407 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200408 spec+: {
409 template: {
410 metadata: cluster.client.metadata,
411 spec+: {
412 terminationGracePeriodSeconds: 5,
413 containers: [
414 kube.Container("cockroachdb-client") {
415 image: cluster.cfg.image,
416 env_: {
417 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
418 "COCKROACH_HOST": cluster.publicService.host,
419 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
420 },
421 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
422 volumeMounts: [
423 {
424 name: "certs",
425 mountPath: "/cockroach/cockroach-certs/ca.crt",
426 subPath: "ca.crt",
427 },
428 {
429 name: "certs",
430 mountPath: "/cockroach/cockroach-certs/client.root.crt",
431 subPath: "tls.crt",
432 },
433 {
434 name: "certs",
435 mountPath: "/cockroach/cockroach-certs/client.root.key",
436 subPath: "tls.key",
437 },
438 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200439 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200440 ],
441 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200442 {
443 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200444 secret: {
445 secretName: cluster.pki.clientCertificate.spec.secretName,
446 defaultMode: kube.parseOctal("400")
447 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200448 },
449 ],
450 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200451 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200452 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200453 },
454 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200455}