blob: 0b5818000bc14ca1f08225b74c3d1c73c60e3707 [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:: {
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
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020073 insecurePolicy: policies.AllowNamespaceInsecure(cluster.namespaceName),
74
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020075 name(suffix):: if cluster.cfg.ownNamespace then suffix else name + "-" + suffix,
76
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020077 pki: {
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +020078 selfSignedIssuer: kube.Issuer(cluster.name("selfsigned")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020079 metadata+: cluster.metadata,
80 spec: {
81 selfSigned: {},
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020082 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020083 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020084
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +020085 selfSignedKeypair: kube.Certificate(cluster.name("cluster-ca")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020086 metadata+: cluster.metadata,
87 spec: {
88 secretName: cluster.name("cluster-ca"),
89 duration: "43800h0m0s", // 5 years
90 isCA: true,
91 issuerRef: {
92 name: cluster.pki.selfSignedIssuer.metadata.name,
93 },
94 commonName: "cockroachdb-cluster-ca",
95 },
96 },
97
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +020098 clusterIssuer: kube.Issuer(cluster.name("cluster-ca")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020099 metadata+: cluster.metadata,
100 spec: {
101 ca: {
102 secretName: cluster.pki.selfSignedKeypair.metadata.name,
103 },
104 },
105 },
106
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200107 nodeCertificate: kube.Certificate(cluster.name("node")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200108 metadata+: cluster.metadata,
109 spec: {
110 secretName: "cockroachdb-node-cert",
111 duration: "43800h0m0s", // 5 years
112 issuerRef: {
113 name: cluster.pki.clusterIssuer.metadata.name,
114 },
115 commonName: "node",
116 dnsNames: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200117 cluster.publicService.metadata.name,
118 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200119 cluster.publicService.host,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200120 std.join(".", [cluster.publicService.host, "cluster.local" ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200121 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
122 ] + [
123 "%s.cluster.local" % s.service.host
124 for s in cluster.servers
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200125 ],
126 },
127 },
128
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200129 clientCertificate: kube.Certificate(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200130 metadata+: cluster.metadata,
131 spec: {
132 secretName: cluster.name("client-certificate"),
133 duration: "43800h0m0s", // 5 years
134 issuerRef: {
135 name: cluster.pki.clusterIssuer.metadata.name,
136 },
137 commonName: "root",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200138 },
139 },
140 },
141
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200142 serviceAccount: kube.ServiceAccount(cluster.name("cockroachdb")) {
143 metadata+: cluster.metadata,
144 },
145
146 role: kube.Role(cluster.name("cockroachdb")) {
147 metadata+: cluster.metadata,
148 rules: [
149 {
150 apiGroups: [ "" ],
151 resources: [ "secrets" ],
152 verbs: [ "get" ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200153 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200154 ],
155 },
156
157 roleBinding: kube.RoleBinding(cluster.name("cockroachdb")) {
158 metadata+: cluster.metadata,
159 roleRef_: cluster.role,
160 subjects_: [cluster.serviceAccount],
161 },
162
163 publicService: kube.Service(cluster.name("public")) {
164 metadata+: cluster.metadata,
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200165 target_pod:: cluster.servers[0].deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200166 spec+: {
167 ports: [
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200168 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
169 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200170 ],
Sergiusz Bazanskid5338922019-08-09 14:13:50 +0200171 type: "LoadBalancer",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200172 },
173 },
174
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200175 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
176 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200177 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200178 selector: {
179 matchLabels: {
180 "app.kubernetes.io/component": "cockroachdb",
181 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200182 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200183 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200184 },
185 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200186
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200187 servers: [
188 {
189 local server = self,
190 service: kube.Service(cluster.name("server-" + el.name)) {
191 metadata+: cluster.metadata + {
192 annotations+: {
193 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
194 "prometheus.io/scrape": "true",
195 "prometheus.io/path": "_status/vars",
196 "prometheus.io/port": std.toString(cluster.cfg.portHttp),
197 },
198 },
199 target_pod:: server.deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200200 spec+: {
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200201 ports: [
202 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
203 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
204 ],
205 publishNotReadyAddresses: true,
206 clusterIP: "None",
207 },
208 },
209 deploy: kube.Deployment(cluster.name("server-" + el.name)) {
210 metadata+: cluster.metadata {
211 labels+: {
212 "app.kubernetes.io/component": "server",
213 "kubernetes.hackerspace.pl/cockroachdb-server": el.name,
214 },
215 },
216 spec+: {
217 template+: {
218 metadata: server.deploy.metadata,
219 spec+: {
220 dnsPolicy: "ClusterFirst",
221 serviceAccountName: cluster.serviceAccount.metadata.name,
222 nodeSelector: {
223 "kubernetes.io/hostname": el.node,
224 },
225 containers: [
226 kube.Container("cockroachdb") {
227 image: cluster.cfg.image,
228 imagePullPolicy: "IfNotPresent",
229 resources: {
230 requests: {
231 cpu: "2",
232 memory: "6Gi",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200233 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200234 limits: {
235 memory: "6Gi",
236 },
237 },
238 ports_: {
239 "grpc": { containerPort: cluster.cfg.portServe },
240 "http": { containerPort: cluster.cfg.portHttp },
241 },
242 livenessProbe: {
243 httpGet: {
244 path: "/health",
245 port: "http",
246 },
247 initialDelaySeconds: 30,
248 periodSeconds: 5,
249 },
250 readinessProbe: {
251 httpGet: {
252 path: "/health?ready=1",
253 port: "http",
254 },
255 initialDelaySeconds: 10,
256 periodSeconds: 5,
257 failureThreshold: 2,
258 },
259 volumeMounts: [
260 {
261 name: "datadir",
262 mountPath: "/cockroach/cockroach-data",
263 },
264 {
265 name: "certs",
266 mountPath: "/cockroach/cockroach-certs/node.crt",
267 subPath: "tls.crt",
268 },
269 {
270 name: "certs",
271 mountPath: "/cockroach/cockroach-certs/node.key",
272 subPath: "tls.key",
273 },
274 {
275 name: "certs",
276 mountPath: "/cockroach/cockroach-certs/ca.crt",
277 subPath: "ca.crt",
278 },
279 ],
280 env_: {
281 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
282 },
283 command: [
284 "/cockroach/cockroach", "start",
285 "--logtostderr",
286 "--certs-dir", "/cockroach/cockroach-certs",
287 "--advertise-host", "%s.cluster.local" % server.service.host,
288 "--cache", "25%", "--max-sql-memory", "25%",
289 "--join", std.join(",", ["%s.cluster.local:%d" % [s.service.host, cluster.cfg.portServe] for s in cluster.servers]),
290 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
291 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
292 ],
293 },
294 ],
295 terminationGracePeriodSeconds: 60,
296 volumes: [
297 {
298 name: "datadir",
299 hostPath: {
300 path: cluster.cfg.hostPath,
301 },
302 },
303 {
304 name: "certs",
305 secret: {
306 secretName: cluster.pki.nodeCertificate.spec.secretName,
307 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200308 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200309 },
310 ],
311 },
312 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200313 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200314 }
315 }
316 for el in cluster.cfg.topology
317 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200318
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200319 initJob: kube.Job(cluster.name("init")) {
320 metadata+: cluster.metadata,
321 spec: {
322 template: {
323 metadata+: cluster.metadata,
324 spec+: {
325 serviceAccountName: cluster.serviceAccount.metadata.name,
326 containers: [
327 kube.Container("cluster-init") {
328 image: cluster.cfg.image,
329 imagePullPolicy: "IfNotPresent",
330 env_: {
331 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
332 },
333 command: [
334 "/bin/bash",
335 "-ecx",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200336 "/cockroach/cockroach init --host=%s.cluster.local:%d" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200337 ],
338 volumeMounts: [
339 {
340 name: "certs",
341 mountPath: "/cockroach/cockroach-certs/ca.crt",
342 subPath: "ca.crt",
343 },
344 {
345 name: "certs",
346 mountPath: "/cockroach/cockroach-certs/client.root.crt",
347 subPath: "tls.crt",
348 },
349 {
350 name: "certs",
351 mountPath: "/cockroach/cockroach-certs/client.root.key",
352 subPath: "tls.key",
353 },
354 ],
355 },
356 ],
357 restartPolicy: "OnFailure",
358 volumes: [
359 {
360 name: "certs",
361 secret: {
362 secretName: cluster.pki.clientCertificate.spec.secretName,
363 defaultMode: kube.parseOctal("400")
364 }
365 },
366 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200367 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200368 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200369 },
370 },
371
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200372 Client(name):: {
Sergiusz Bazanskie31d64f2019-10-02 20:59:26 +0200373 certificate: kube.Certificate(cluster.name("client-%s" % name)) {
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +0200374 metadata+: cluster.metadata,
375 spec: {
376 secretName: cluster.name("client-%s-certificate" % name),
377 duration: "43800h0m0s", // 5 years
378 issuerRef: {
379 name: cluster.pki.clusterIssuer.metadata.name,
380 },
381 commonName: name,
382 },
383 },
384 },
385
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200386 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200387 metadata+: cluster.metadata {
388 labels+: {
389 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200390 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200391 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200392 spec+: {
393 template: {
394 metadata: cluster.client.metadata,
395 spec+: {
396 terminationGracePeriodSeconds: 5,
397 containers: [
398 kube.Container("cockroachdb-client") {
399 image: cluster.cfg.image,
400 env_: {
401 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
402 "COCKROACH_HOST": cluster.publicService.host,
403 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
404 },
405 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
406 volumeMounts: [
407 {
408 name: "certs",
409 mountPath: "/cockroach/cockroach-certs/ca.crt",
410 subPath: "ca.crt",
411 },
412 {
413 name: "certs",
414 mountPath: "/cockroach/cockroach-certs/client.root.crt",
415 subPath: "tls.crt",
416 },
417 {
418 name: "certs",
419 mountPath: "/cockroach/cockroach-certs/client.root.key",
420 subPath: "tls.key",
421 },
422 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200423 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200424 ],
425 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200426 {
427 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200428 secret: {
429 secretName: cluster.pki.clientCertificate.spec.secretName,
430 defaultMode: kube.parseOctal("400")
431 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200432 },
433 ],
434 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200435 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200436 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200437 },
438 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200439}