blob: dff2ebd945ddd943f54dbd78cc8712a4f61d1c83 [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:: {
Patryk Jakuszewedf14cc2021-01-23 23:00:29 +010045 image: "cockroachdb/cockroach:v20.2.4",
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",
293 "--logtostderr",
294 "--certs-dir", "/cockroach/cockroach-certs",
295 "--advertise-host", "%s.cluster.local" % server.service.host,
296 "--cache", "25%", "--max-sql-memory", "25%",
Serge Bazanskibdd403c2021-10-28 23:37:38 +0000297 "--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 +0200298 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
299 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
300 ],
301 },
302 ],
303 terminationGracePeriodSeconds: 60,
304 volumes: [
305 {
306 name: "datadir",
307 hostPath: {
308 path: cluster.cfg.hostPath,
309 },
310 },
311 {
312 name: "certs",
313 secret: {
314 secretName: cluster.pki.nodeCertificate.spec.secretName,
315 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200316 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200317 },
318 ],
319 },
320 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200321 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200322 }
323 }
324 for el in cluster.cfg.topology
325 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200326
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200327 initJob: kube.Job(cluster.name("init")) {
328 metadata+: cluster.metadata,
329 spec: {
330 template: {
331 metadata+: cluster.metadata,
332 spec+: {
333 serviceAccountName: cluster.serviceAccount.metadata.name,
334 containers: [
335 kube.Container("cluster-init") {
336 image: cluster.cfg.image,
337 imagePullPolicy: "IfNotPresent",
338 env_: {
339 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
340 },
341 command: [
342 "/bin/bash",
343 "-ecx",
Serge Bazanski3b893532021-03-17 21:48:28 +0000344 "/cockroach/cockroach init --host=%s.cluster.local:%d || true" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200345 ],
346 volumeMounts: [
347 {
348 name: "certs",
349 mountPath: "/cockroach/cockroach-certs/ca.crt",
350 subPath: "ca.crt",
351 },
352 {
353 name: "certs",
354 mountPath: "/cockroach/cockroach-certs/client.root.crt",
355 subPath: "tls.crt",
356 },
357 {
358 name: "certs",
359 mountPath: "/cockroach/cockroach-certs/client.root.key",
360 subPath: "tls.key",
361 },
362 ],
363 },
364 ],
365 restartPolicy: "OnFailure",
366 volumes: [
367 {
368 name: "certs",
369 secret: {
370 secretName: cluster.pki.clientCertificate.spec.secretName,
371 defaultMode: kube.parseOctal("400")
372 }
373 },
374 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200375 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200376 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200377 },
378 },
379
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200380 Client(name):: {
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200381 certificate: kube.Certificate(cluster.name("client-%s" % name)) {
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200382 metadata+: cluster.metadata,
383 spec: {
384 secretName: cluster.name("client-%s-certificate" % name),
385 duration: "43800h0m0s", // 5 years
386 issuerRef: {
387 name: cluster.pki.clusterIssuer.metadata.name,
388 },
389 commonName: name,
390 },
391 },
392 },
393
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200394 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200395 metadata+: cluster.metadata {
396 labels+: {
397 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200398 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200399 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200400 spec+: {
401 template: {
402 metadata: cluster.client.metadata,
403 spec+: {
404 terminationGracePeriodSeconds: 5,
405 containers: [
406 kube.Container("cockroachdb-client") {
407 image: cluster.cfg.image,
408 env_: {
409 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
410 "COCKROACH_HOST": cluster.publicService.host,
411 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
412 },
413 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
414 volumeMounts: [
415 {
416 name: "certs",
417 mountPath: "/cockroach/cockroach-certs/ca.crt",
418 subPath: "ca.crt",
419 },
420 {
421 name: "certs",
422 mountPath: "/cockroach/cockroach-certs/client.root.crt",
423 subPath: "tls.crt",
424 },
425 {
426 name: "certs",
427 mountPath: "/cockroach/cockroach-certs/client.root.key",
428 subPath: "tls.key",
429 },
430 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200431 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200432 ],
433 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200434 {
435 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200436 secret: {
437 secretName: cluster.pki.clientCertificate.spec.secretName,
438 defaultMode: kube.parseOctal("400")
439 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200440 },
441 ],
442 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200443 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200444 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200445 },
446 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200447}