blob: 4df69b60f01704fb1d54e3961f669ab3af9fcce7 [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: [
9# { name: "a", node: "bc01n01.hswaw.net", ip: "185.236.240.35" },
10# { name: "b", node: "bc01n02.hswaw.net", ip: "185.236.240.36" },
11# { name: "c", node: "bc01n03.hswaw.net", ip: "185.236.240.37" },
12# ],
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",
52
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020053 namespace: null,
54 ownNamespace: cluster.cfg.namespace == null,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020055 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020056
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020057 namespaceName:: if cluster.cfg.namespace != null then cluster.cfg.namespace else name,
58
59 metadata:: {
60 namespace: cluster.namespaceName,
61 labels: {
62 "app.kubernetes.io/name": "cockroachdb",
63 "app.kubernetes.io/managed-by": "kubecfg",
64 "app.kubernetes.io/component": "cockroachdb",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020065 },
66 },
67
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020068 namespace: {
69 [if cluster.cfg.ownNamespace then "ns"]: kube.Namespace(cluster.namespaceName),
70 },
71
72 name(suffix):: if cluster.cfg.ownNamespace then suffix else name + "-" + suffix,
73
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020074 pki: {
75 selfSignedIssuer: cm.Issuer(cluster.name("selfsigned")) {
76 metadata+: cluster.metadata,
77 spec: {
78 selfSigned: {},
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020079 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020080 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020081
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020082 selfSignedKeypair: cm.Certificate(cluster.name("cluster-ca")) {
83 metadata+: cluster.metadata,
84 spec: {
85 secretName: cluster.name("cluster-ca"),
86 duration: "43800h0m0s", // 5 years
87 isCA: true,
88 issuerRef: {
89 name: cluster.pki.selfSignedIssuer.metadata.name,
90 },
91 commonName: "cockroachdb-cluster-ca",
92 },
93 },
94
95 clusterIssuer: cm.Issuer(cluster.name("cluster-ca")) {
96 metadata+: cluster.metadata,
97 spec: {
98 ca: {
99 secretName: cluster.pki.selfSignedKeypair.metadata.name,
100 },
101 },
102 },
103
104 nodeCertificate: cm.Certificate(cluster.name("node")) {
105 metadata+: cluster.metadata,
106 spec: {
107 secretName: "cockroachdb-node-cert",
108 duration: "43800h0m0s", // 5 years
109 issuerRef: {
110 name: cluster.pki.clusterIssuer.metadata.name,
111 },
112 commonName: "node",
113 dnsNames: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200114 cluster.publicService.metadata.name,
115 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200116 cluster.publicService.host,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200117 std.join(".", [cluster.publicService.host, "cluster.local" ]),
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200118 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
119 ] + [
120 "%s.cluster.local" % s.service.host
121 for s in cluster.servers
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200122 ],
123 },
124 },
125
126 clientCertificate: cm.Certificate(cluster.name("client")) {
127 metadata+: cluster.metadata,
128 spec: {
129 secretName: cluster.name("client-certificate"),
130 duration: "43800h0m0s", // 5 years
131 issuerRef: {
132 name: cluster.pki.clusterIssuer.metadata.name,
133 },
134 commonName: "root",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200135 },
136 },
137 },
138
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200139 serviceAccount: kube.ServiceAccount(cluster.name("cockroachdb")) {
140 metadata+: cluster.metadata,
141 },
142
143 role: kube.Role(cluster.name("cockroachdb")) {
144 metadata+: cluster.metadata,
145 rules: [
146 {
147 apiGroups: [ "" ],
148 resources: [ "secrets" ],
149 verbs: [ "get" ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200150 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200151 ],
152 },
153
154 roleBinding: kube.RoleBinding(cluster.name("cockroachdb")) {
155 metadata+: cluster.metadata,
156 roleRef_: cluster.role,
157 subjects_: [cluster.serviceAccount],
158 },
159
160 publicService: kube.Service(cluster.name("public")) {
161 metadata+: cluster.metadata,
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200162 target_pod:: cluster.servers[0].deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200163 spec+: {
164 ports: [
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200165 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
166 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200167 ],
168 },
169 },
170
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200171 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
172 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200173 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200174 selector: {
175 matchLabels: {
176 "app.kubernetes.io/component": "cockroachdb",
177 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200178 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200179 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200180 },
181 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200182
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200183 servers: [
184 {
185 local server = self,
186 service: kube.Service(cluster.name("server-" + el.name)) {
187 metadata+: cluster.metadata + {
188 annotations+: {
189 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
190 "prometheus.io/scrape": "true",
191 "prometheus.io/path": "_status/vars",
192 "prometheus.io/port": std.toString(cluster.cfg.portHttp),
193 },
194 },
195 target_pod:: server.deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200196 spec+: {
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200197 ports: [
198 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
199 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
200 ],
201 publishNotReadyAddresses: true,
202 clusterIP: "None",
203 },
204 },
205 deploy: kube.Deployment(cluster.name("server-" + el.name)) {
206 metadata+: cluster.metadata {
207 labels+: {
208 "app.kubernetes.io/component": "server",
209 "kubernetes.hackerspace.pl/cockroachdb-server": el.name,
210 },
211 },
212 spec+: {
213 template+: {
214 metadata: server.deploy.metadata,
215 spec+: {
216 dnsPolicy: "ClusterFirst",
217 serviceAccountName: cluster.serviceAccount.metadata.name,
218 nodeSelector: {
219 "kubernetes.io/hostname": el.node,
220 },
221 containers: [
222 kube.Container("cockroachdb") {
223 image: cluster.cfg.image,
224 imagePullPolicy: "IfNotPresent",
225 resources: {
226 requests: {
227 cpu: "2",
228 memory: "6Gi",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200229 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200230 limits: {
231 memory: "6Gi",
232 },
233 },
234 ports_: {
235 "grpc": { containerPort: cluster.cfg.portServe },
236 "http": { containerPort: cluster.cfg.portHttp },
237 },
238 livenessProbe: {
239 httpGet: {
240 path: "/health",
241 port: "http",
242 },
243 initialDelaySeconds: 30,
244 periodSeconds: 5,
245 },
246 readinessProbe: {
247 httpGet: {
248 path: "/health?ready=1",
249 port: "http",
250 },
251 initialDelaySeconds: 10,
252 periodSeconds: 5,
253 failureThreshold: 2,
254 },
255 volumeMounts: [
256 {
257 name: "datadir",
258 mountPath: "/cockroach/cockroach-data",
259 },
260 {
261 name: "certs",
262 mountPath: "/cockroach/cockroach-certs/node.crt",
263 subPath: "tls.crt",
264 },
265 {
266 name: "certs",
267 mountPath: "/cockroach/cockroach-certs/node.key",
268 subPath: "tls.key",
269 },
270 {
271 name: "certs",
272 mountPath: "/cockroach/cockroach-certs/ca.crt",
273 subPath: "ca.crt",
274 },
275 ],
276 env_: {
277 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
278 },
279 command: [
280 "/cockroach/cockroach", "start",
281 "--logtostderr",
282 "--certs-dir", "/cockroach/cockroach-certs",
283 "--advertise-host", "%s.cluster.local" % server.service.host,
284 "--cache", "25%", "--max-sql-memory", "25%",
285 "--join", std.join(",", ["%s.cluster.local:%d" % [s.service.host, cluster.cfg.portServe] for s in cluster.servers]),
286 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
287 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
288 ],
289 },
290 ],
291 terminationGracePeriodSeconds: 60,
292 volumes: [
293 {
294 name: "datadir",
295 hostPath: {
296 path: cluster.cfg.hostPath,
297 },
298 },
299 {
300 name: "certs",
301 secret: {
302 secretName: cluster.pki.nodeCertificate.spec.secretName,
303 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200304 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200305 },
306 ],
307 },
308 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200309 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200310 }
311 }
312 for el in cluster.cfg.topology
313 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200314
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200315 initJob: kube.Job(cluster.name("init")) {
316 metadata+: cluster.metadata,
317 spec: {
318 template: {
319 metadata+: cluster.metadata,
320 spec+: {
321 serviceAccountName: cluster.serviceAccount.metadata.name,
322 containers: [
323 kube.Container("cluster-init") {
324 image: cluster.cfg.image,
325 imagePullPolicy: "IfNotPresent",
326 env_: {
327 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
328 },
329 command: [
330 "/bin/bash",
331 "-ecx",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200332 "/cockroach/cockroach init --host=%s.cluster.local:%d" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200333 ],
334 volumeMounts: [
335 {
336 name: "certs",
337 mountPath: "/cockroach/cockroach-certs/ca.crt",
338 subPath: "ca.crt",
339 },
340 {
341 name: "certs",
342 mountPath: "/cockroach/cockroach-certs/client.root.crt",
343 subPath: "tls.crt",
344 },
345 {
346 name: "certs",
347 mountPath: "/cockroach/cockroach-certs/client.root.key",
348 subPath: "tls.key",
349 },
350 ],
351 },
352 ],
353 restartPolicy: "OnFailure",
354 volumes: [
355 {
356 name: "certs",
357 secret: {
358 secretName: cluster.pki.clientCertificate.spec.secretName,
359 defaultMode: kube.parseOctal("400")
360 }
361 },
362 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200363 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200364 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200365 },
366 },
367
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200368 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200369 metadata+: cluster.metadata {
370 labels+: {
371 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200372 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200373 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200374 spec+: {
375 template: {
376 metadata: cluster.client.metadata,
377 spec+: {
378 terminationGracePeriodSeconds: 5,
379 containers: [
380 kube.Container("cockroachdb-client") {
381 image: cluster.cfg.image,
382 env_: {
383 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
384 "COCKROACH_HOST": cluster.publicService.host,
385 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
386 },
387 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
388 volumeMounts: [
389 {
390 name: "certs",
391 mountPath: "/cockroach/cockroach-certs/ca.crt",
392 subPath: "ca.crt",
393 },
394 {
395 name: "certs",
396 mountPath: "/cockroach/cockroach-certs/client.root.crt",
397 subPath: "tls.crt",
398 },
399 {
400 name: "certs",
401 mountPath: "/cockroach/cockroach-certs/client.root.key",
402 subPath: "tls.key",
403 },
404 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200405 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200406 ],
407 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200408 {
409 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200410 secret: {
411 secretName: cluster.pki.clientCertificate.spec.secretName,
412 defaultMode: kube.parseOctal("400")
413 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200414 },
415 ],
416 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200417 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200418 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200419 },
420 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200421}