blob: 992edadf4637e17f9261b3cd8ab22d6bf45efc10 [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",
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 ],
Sergiusz Bazanskid5338922019-08-09 14:13:50 +0200168 type: "LoadBalancer",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200169 },
170 },
171
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200172 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
173 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200174 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200175 selector: {
176 matchLabels: {
177 "app.kubernetes.io/component": "cockroachdb",
178 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200179 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200180 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200181 },
182 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200183
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200184 servers: [
185 {
186 local server = self,
187 service: kube.Service(cluster.name("server-" + el.name)) {
188 metadata+: cluster.metadata + {
189 annotations+: {
190 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
191 "prometheus.io/scrape": "true",
192 "prometheus.io/path": "_status/vars",
193 "prometheus.io/port": std.toString(cluster.cfg.portHttp),
194 },
195 },
196 target_pod:: server.deploy.spec.template,
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200197 spec+: {
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200198 ports: [
199 { name: "grpc", port: cluster.cfg.portServe, targetPort: cluster.cfg.portServe },
200 { name: "http", port: cluster.cfg.portHttp, targetPort: cluster.cfg.portHttp },
201 ],
202 publishNotReadyAddresses: true,
203 clusterIP: "None",
204 },
205 },
206 deploy: kube.Deployment(cluster.name("server-" + el.name)) {
207 metadata+: cluster.metadata {
208 labels+: {
209 "app.kubernetes.io/component": "server",
210 "kubernetes.hackerspace.pl/cockroachdb-server": el.name,
211 },
212 },
213 spec+: {
214 template+: {
215 metadata: server.deploy.metadata,
216 spec+: {
217 dnsPolicy: "ClusterFirst",
218 serviceAccountName: cluster.serviceAccount.metadata.name,
219 nodeSelector: {
220 "kubernetes.io/hostname": el.node,
221 },
222 containers: [
223 kube.Container("cockroachdb") {
224 image: cluster.cfg.image,
225 imagePullPolicy: "IfNotPresent",
226 resources: {
227 requests: {
228 cpu: "2",
229 memory: "6Gi",
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200230 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200231 limits: {
232 memory: "6Gi",
233 },
234 },
235 ports_: {
236 "grpc": { containerPort: cluster.cfg.portServe },
237 "http": { containerPort: cluster.cfg.portHttp },
238 },
239 livenessProbe: {
240 httpGet: {
241 path: "/health",
242 port: "http",
243 },
244 initialDelaySeconds: 30,
245 periodSeconds: 5,
246 },
247 readinessProbe: {
248 httpGet: {
249 path: "/health?ready=1",
250 port: "http",
251 },
252 initialDelaySeconds: 10,
253 periodSeconds: 5,
254 failureThreshold: 2,
255 },
256 volumeMounts: [
257 {
258 name: "datadir",
259 mountPath: "/cockroach/cockroach-data",
260 },
261 {
262 name: "certs",
263 mountPath: "/cockroach/cockroach-certs/node.crt",
264 subPath: "tls.crt",
265 },
266 {
267 name: "certs",
268 mountPath: "/cockroach/cockroach-certs/node.key",
269 subPath: "tls.key",
270 },
271 {
272 name: "certs",
273 mountPath: "/cockroach/cockroach-certs/ca.crt",
274 subPath: "ca.crt",
275 },
276 ],
277 env_: {
278 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
279 },
280 command: [
281 "/cockroach/cockroach", "start",
282 "--logtostderr",
283 "--certs-dir", "/cockroach/cockroach-certs",
284 "--advertise-host", "%s.cluster.local" % server.service.host,
285 "--cache", "25%", "--max-sql-memory", "25%",
286 "--join", std.join(",", ["%s.cluster.local:%d" % [s.service.host, cluster.cfg.portServe] for s in cluster.servers]),
287 "--listen-addr=0.0.0.0:%d" % cluster.cfg.portServe,
288 "--http-addr=0.0.0.0:%d" % cluster.cfg.portHttp,
289 ],
290 },
291 ],
292 terminationGracePeriodSeconds: 60,
293 volumes: [
294 {
295 name: "datadir",
296 hostPath: {
297 path: cluster.cfg.hostPath,
298 },
299 },
300 {
301 name: "certs",
302 secret: {
303 secretName: cluster.pki.nodeCertificate.spec.secretName,
304 defaultMode: kube.parseOctal("400"),
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200305 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200306 },
307 ],
308 },
309 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200310 },
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200311 }
312 }
313 for el in cluster.cfg.topology
314 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200315
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200316 initJob: kube.Job(cluster.name("init")) {
317 metadata+: cluster.metadata,
318 spec: {
319 template: {
320 metadata+: cluster.metadata,
321 spec+: {
322 serviceAccountName: cluster.serviceAccount.metadata.name,
323 containers: [
324 kube.Container("cluster-init") {
325 image: cluster.cfg.image,
326 imagePullPolicy: "IfNotPresent",
327 env_: {
328 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
329 },
330 command: [
331 "/bin/bash",
332 "-ecx",
Sergiusz Bazanskie53e39a2019-06-20 23:36:35 +0200333 "/cockroach/cockroach init --host=%s.cluster.local:%d" % [cluster.servers[0].service.host, cluster.cfg.portServe],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200334 ],
335 volumeMounts: [
336 {
337 name: "certs",
338 mountPath: "/cockroach/cockroach-certs/ca.crt",
339 subPath: "ca.crt",
340 },
341 {
342 name: "certs",
343 mountPath: "/cockroach/cockroach-certs/client.root.crt",
344 subPath: "tls.crt",
345 },
346 {
347 name: "certs",
348 mountPath: "/cockroach/cockroach-certs/client.root.key",
349 subPath: "tls.key",
350 },
351 ],
352 },
353 ],
354 restartPolicy: "OnFailure",
355 volumes: [
356 {
357 name: "certs",
358 secret: {
359 secretName: cluster.pki.clientCertificate.spec.secretName,
360 defaultMode: kube.parseOctal("400")
361 }
362 },
363 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200364 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200365 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200366 },
367 },
368
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200369 client: kube.Deployment(cluster.name("client")) {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200370 metadata+: cluster.metadata {
371 labels+: {
372 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200373 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200374 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200375 spec+: {
376 template: {
377 metadata: cluster.client.metadata,
378 spec+: {
379 terminationGracePeriodSeconds: 5,
380 containers: [
381 kube.Container("cockroachdb-client") {
382 image: cluster.cfg.image,
383 env_: {
384 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
385 "COCKROACH_HOST": cluster.publicService.host,
386 "COCKROACH_PORT": std.toString(cluster.cfg.portServe),
387 },
388 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
389 volumeMounts: [
390 {
391 name: "certs",
392 mountPath: "/cockroach/cockroach-certs/ca.crt",
393 subPath: "ca.crt",
394 },
395 {
396 name: "certs",
397 mountPath: "/cockroach/cockroach-certs/client.root.crt",
398 subPath: "tls.crt",
399 },
400 {
401 name: "certs",
402 mountPath: "/cockroach/cockroach-certs/client.root.key",
403 subPath: "tls.key",
404 },
405 ],
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200406 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200407 ],
408 volumes: [
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200409 {
410 name: "certs",
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200411 secret: {
412 secretName: cluster.pki.clientCertificate.spec.secretName,
413 defaultMode: kube.parseOctal("400")
414 }
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200415 },
416 ],
417 },
Sergiusz Bazanskidec401c2019-06-21 22:31:13 +0200418 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200419 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200420 },
421 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200422}