blob: ac4c965e21cf9d12f3f6047ebf3647cd6a09bd66 [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";
38local cm = import "cert-manager.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:: {
45 image: "cockroachdb/cockroach:v19.1.0",
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,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020056 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020057
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020058 namespaceName:: if cluster.cfg.namespace != null then cluster.cfg.namespace else name,
59
60 metadata:: {
61 namespace: cluster.namespaceName,
62 labels: {
63 "app.kubernetes.io/name": "cockroachdb",
64 "app.kubernetes.io/managed-by": "kubecfg",
65 "app.kubernetes.io/component": "cockroachdb",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020066 },
67 },
68
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020069 namespace: {
70 [if cluster.cfg.ownNamespace then "ns"]: kube.Namespace(cluster.namespaceName),
71 },
72
73 name(suffix):: if cluster.cfg.ownNamespace then suffix else name + "-" + suffix,
74
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020075 pki: {
76 selfSignedIssuer: cm.Issuer(cluster.name("selfsigned")) {
77 metadata+: cluster.metadata,
78 spec: {
79 selfSigned: {},
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020080 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020081 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020082
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020083 selfSignedKeypair: cm.Certificate(cluster.name("cluster-ca")) {
84 metadata+: cluster.metadata,
85 spec: {
86 secretName: cluster.name("cluster-ca"),
87 duration: "43800h0m0s", // 5 years
88 isCA: true,
89 issuerRef: {
90 name: cluster.pki.selfSignedIssuer.metadata.name,
91 },
92 commonName: "cockroachdb-cluster-ca",
93 },
94 },
95
96 clusterIssuer: cm.Issuer(cluster.name("cluster-ca")) {
97 metadata+: cluster.metadata,
98 spec: {
99 ca: {
100 secretName: cluster.pki.selfSignedKeypair.metadata.name,
101 },
102 },
103 },
104
105 nodeCertificate: cm.Certificate(cluster.name("node")) {
106 metadata+: cluster.metadata,
107 spec: {
108 secretName: "cockroachdb-node-cert",
109 duration: "43800h0m0s", // 5 years
110 issuerRef: {
111 name: cluster.pki.clusterIssuer.metadata.name,
112 },
113 commonName: "node",
114 dnsNames: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200115 cluster.publicService.metadata.name,
116 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200117 cluster.publicService.host,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200118 std.join(".", [cluster.publicService.host, "cluster.local" ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200119 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
120 ] + [
121 "%s.cluster.local" % s.service.host
122 for s in cluster.servers
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200123 ],
124 },
125 },
126
127 clientCertificate: cm.Certificate(cluster.name("client")) {
128 metadata+: cluster.metadata,
129 spec: {
130 secretName: cluster.name("client-certificate"),
131 duration: "43800h0m0s", // 5 years
132 issuerRef: {
133 name: cluster.pki.clusterIssuer.metadata.name,
134 },
135 commonName: "root",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200136 },
137 },
138 },
139
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200140 serviceAccount: kube.ServiceAccount(cluster.name("cockroachdb")) {
141 metadata+: cluster.metadata,
142 },
143
144 role: kube.Role(cluster.name("cockroachdb")) {
145 metadata+: cluster.metadata,
146 rules: [
147 {
148 apiGroups: [ "" ],
149 resources: [ "secrets" ],
150 verbs: [ "get" ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200151 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200152 ],
153 },
154
155 roleBinding: kube.RoleBinding(cluster.name("cockroachdb")) {
156 metadata+: cluster.metadata,
157 roleRef_: cluster.role,
158 subjects_: [cluster.serviceAccount],
159 },
160
161 publicService: kube.Service(cluster.name("public")) {
162 metadata+: cluster.metadata,
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200163 target_pod:: cluster.servers[0].deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200164 spec+: {
165 ports: [
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200166 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
167 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200168 ],
Sergiusz Bazanskid5338922019-08-09 14:13:50 +0200169 type: "LoadBalancer",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200170 },
171 },
172
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200173 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
174 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200175 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200176 selector: {
177 matchLabels: {
178 "app.kubernetes.io/component": "cockroachdb",
179 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200180 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200181 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200182 },
183 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200184
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200185 servers: [
186 {
187 local server = self,
188 service: kube.Service(cluster.name("server-" + el.name)) {
189 metadata+: cluster.metadata + {
190 annotations+: {
191 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
192 "prometheus.io/scrape": "true",
193 "prometheus.io/path": "_status/vars",
194 "prometheus.io/port": std.toString(cluster.cfg.portHttp),
195 },
196 },
197 target_pod:: server.deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200198 spec+: {
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200199 ports: [
200 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
201 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
202 ],
203 publishNotReadyAddresses: true,
204 clusterIP: "None",
205 },
206 },
207 deploy: kube.Deployment(cluster.name("server-" + el.name)) {
208 metadata+: cluster.metadata {
209 labels+: {
210 "app.kubernetes.io/component": "server",
211 "kubernetes.hackerspace.pl/cockroachdb-server": el.name,
212 },
213 },
214 spec+: {
215 template+: {
216 metadata: server.deploy.metadata,
217 spec+: {
218 dnsPolicy: "ClusterFirst",
219 serviceAccountName: cluster.serviceAccount.metadata.name,
220 nodeSelector: {
221 "kubernetes.io/hostname": el.node,
222 },
223 containers: [
224 kube.Container("cockroachdb") {
225 image: cluster.cfg.image,
226 imagePullPolicy: "IfNotPresent",
227 resources: {
228 requests: {
229 cpu: "2",
230 memory: "6Gi",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200231 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200232 limits: {
233 memory: "6Gi",
234 },
235 },
236 ports_: {
237 "grpc": { containerPort: cluster.cfg.portServe },
238 "http": { containerPort: cluster.cfg.portHttp },
239 },
240 livenessProbe: {
241 httpGet: {
242 path: "/health",
243 port: "http",
244 },
245 initialDelaySeconds: 30,
246 periodSeconds: 5,
247 },
248 readinessProbe: {
249 httpGet: {
250 path: "/health?ready=1",
251 port: "http",
252 },
253 initialDelaySeconds: 10,
254 periodSeconds: 5,
255 failureThreshold: 2,
256 },
257 volumeMounts: [
258 {
259 name: "datadir",
260 mountPath: "/cockroach/cockroach-data",
261 },
262 {
263 name: "certs",
264 mountPath: "/cockroach/cockroach-certs/node.crt",
265 subPath: "tls.crt",
266 },
267 {
268 name: "certs",
269 mountPath: "/cockroach/cockroach-certs/node.key",
270 subPath: "tls.key",
271 },
272 {
273 name: "certs",
274 mountPath: "/cockroach/cockroach-certs/ca.crt",
275 subPath: "ca.crt",
276 },
277 ],
278 env_: {
279 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
280 },
281 command: [
282 "/cockroach/cockroach", "start",
283 "--logtostderr",
284 "--certs-dir", "/cockroach/cockroach-certs",
285 "--advertise-host", "%s.cluster.local" % server.service.host,
286 "--cache", "25%", "--max-sql-memory", "25%",
287 "--join", std.join(",", ["%s.cluster.local:%d" % [s.service.host, cluster.cfg.portServe] for s in cluster.servers]),
288 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
289 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
290 ],
291 },
292 ],
293 terminationGracePeriodSeconds: 60,
294 volumes: [
295 {
296 name: "datadir",
297 hostPath: {
298 path: cluster.cfg.hostPath,
299 },
300 },
301 {
302 name: "certs",
303 secret: {
304 secretName: cluster.pki.nodeCertificate.spec.secretName,
305 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200306 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200307 },
308 ],
309 },
310 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200311 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200312 }
313 }
314 for el in cluster.cfg.topology
315 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200316
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200317 initJob: kube.Job(cluster.name("init")) {
318 metadata+: cluster.metadata,
319 spec: {
320 template: {
321 metadata+: cluster.metadata,
322 spec+: {
323 serviceAccountName: cluster.serviceAccount.metadata.name,
324 containers: [
325 kube.Container("cluster-init") {
326 image: cluster.cfg.image,
327 imagePullPolicy: "IfNotPresent",
328 env_: {
329 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
330 },
331 command: [
332 "/bin/bash",
333 "-ecx",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200334 "/cockroach/cockroach init --host=%s.cluster.local:%d" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200335 ],
336 volumeMounts: [
337 {
338 name: "certs",
339 mountPath: "/cockroach/cockroach-certs/ca.crt",
340 subPath: "ca.crt",
341 },
342 {
343 name: "certs",
344 mountPath: "/cockroach/cockroach-certs/client.root.crt",
345 subPath: "tls.crt",
346 },
347 {
348 name: "certs",
349 mountPath: "/cockroach/cockroach-certs/client.root.key",
350 subPath: "tls.key",
351 },
352 ],
353 },
354 ],
355 restartPolicy: "OnFailure",
356 volumes: [
357 {
358 name: "certs",
359 secret: {
360 secretName: cluster.pki.clientCertificate.spec.secretName,
361 defaultMode: kube.parseOctal("400")
362 }
363 },
364 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200365 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200366 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200367 },
368 },
369
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200370 Client(name):: {
371 certificate: cm.Certificate(cluster.name("client-%s" % name)) {
372 metadata+: cluster.metadata,
373 spec: {
374 secretName: cluster.name("client-%s-certificate" % name),
375 duration: "43800h0m0s", // 5 years
376 issuerRef: {
377 name: cluster.pki.clusterIssuer.metadata.name,
378 },
379 commonName: name,
380 },
381 },
382 },
383
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200384 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200385 metadata+: cluster.metadata {
386 labels+: {
387 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200388 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200389 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200390 spec+: {
391 template: {
392 metadata: cluster.client.metadata,
393 spec+: {
394 terminationGracePeriodSeconds: 5,
395 containers: [
396 kube.Container("cockroachdb-client") {
397 image: cluster.cfg.image,
398 env_: {
399 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
400 "COCKROACH_HOST": cluster.publicService.host,
401 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
402 },
403 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
404 volumeMounts: [
405 {
406 name: "certs",
407 mountPath: "/cockroach/cockroach-certs/ca.crt",
408 subPath: "ca.crt",
409 },
410 {
411 name: "certs",
412 mountPath: "/cockroach/cockroach-certs/client.root.crt",
413 subPath: "tls.crt",
414 },
415 {
416 name: "certs",
417 mountPath: "/cockroach/cockroach-certs/client.root.key",
418 subPath: "tls.key",
419 },
420 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200421 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200422 ],
423 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200424 {
425 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200426 secret: {
427 secretName: cluster.pki.clientCertificate.spec.secretName,
428 defaultMode: kube.parseOctal("400")
429 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200430 },
431 ],
432 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200433 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200434 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200435 },
436 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200437}