blob: def9bcbaa16696a0b1135bbc02abed59edb3d448 [file] [log] [blame]
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +02001# Deploy a 3-node CockroachDB cluster in secure mode.
2
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +02003# Can be used either in own namespace or in an existing one:
4# crdb: cockroachdb.Cluster("q3kdb") {
5# cfg+: {
6# namespace: "q3k", // if not given, will create 'q3kdb' namespace
7# },
8#},
9#
10# After the cluster is up, you can get to an administrateive SQL shell:
11# $ kubectl -n q3k exec -it q3kdb-client /cockroach/cockroach sql
12# root@q3kdb-cockroachdb-0.q3kdb-internal.q3k.svc.cluster.local:26257/defaultdb>
13#
14# Then, you can create some users and databases for applications:
15# defaultdb> CREATE DATABASE wykop;
16# defaultdb> CREATE USER bialkov PASSWORD hackme;
17# defaultdb> GRANT ALL ON DATABASE wykop to bialkov;
18#
19# You are then ready to access the database via the public service from your application.
20#
21# PGCLIENTENCODING=utf8 psql -h q3kdb-public -p 26257 -U bialkov wykop
22# Password for user bialkov:
23# psql (10.9 (Ubuntu 10.9-0ubuntu0.18.04.1), server 9.5.0)
24# SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256, bits: 128, compression: off)
25# Type "help" for help.
26#
27# wykop=>
28
29
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +020030local kube = import "../../../kube/kube.libsonnet";
31local cm = import "cert-manager.libsonnet";
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020032
33{
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020034 Cluster(name): {
35 local cluster = self,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020036
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020037 cfg:: {
38 image: "cockroachdb/cockroach:v19.1.0",
39 namespace: null,
40 ownNamespace: cluster.cfg.namespace == null,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020041 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020042
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020043 namespaceName:: if cluster.cfg.namespace != null then cluster.cfg.namespace else name,
44
45 metadata:: {
46 namespace: cluster.namespaceName,
47 labels: {
48 "app.kubernetes.io/name": "cockroachdb",
49 "app.kubernetes.io/managed-by": "kubecfg",
50 "app.kubernetes.io/component": "cockroachdb",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020051 },
52 },
53
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020054 namespace: {
55 [if cluster.cfg.ownNamespace then "ns"]: kube.Namespace(cluster.namespaceName),
56 },
57
58 name(suffix):: if cluster.cfg.ownNamespace then suffix else name + "-" + suffix,
59
60 hosts:: ["%s-%d.%s.cluster.local" % [cluster.statefulSet.metadata.name, n, cluster.internalService.host] for n in std.range(0, cluster.statefulSet.spec.replicas)],
61
62 pki: {
63 selfSignedIssuer: cm.Issuer(cluster.name("selfsigned")) {
64 metadata+: cluster.metadata,
65 spec: {
66 selfSigned: {},
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020067 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020068 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +020069
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +020070 selfSignedKeypair: cm.Certificate(cluster.name("cluster-ca")) {
71 metadata+: cluster.metadata,
72 spec: {
73 secretName: cluster.name("cluster-ca"),
74 duration: "43800h0m0s", // 5 years
75 isCA: true,
76 issuerRef: {
77 name: cluster.pki.selfSignedIssuer.metadata.name,
78 },
79 commonName: "cockroachdb-cluster-ca",
80 },
81 },
82
83 clusterIssuer: cm.Issuer(cluster.name("cluster-ca")) {
84 metadata+: cluster.metadata,
85 spec: {
86 ca: {
87 secretName: cluster.pki.selfSignedKeypair.metadata.name,
88 },
89 },
90 },
91
92 nodeCertificate: cm.Certificate(cluster.name("node")) {
93 metadata+: cluster.metadata,
94 spec: {
95 secretName: "cockroachdb-node-cert",
96 duration: "43800h0m0s", // 5 years
97 issuerRef: {
98 name: cluster.pki.clusterIssuer.metadata.name,
99 },
100 commonName: "node",
101 dnsNames: [
102 "localhost",
103 "127.0.0.1",
104 cluster.publicService.metadata.name,
105 std.join(".", [cluster.publicService.metadata.name, cluster.metadata.namespace ]),
106 std.join(".", [cluster.publicService.host, "cluster.local" ]),
107 std.join(".", [ "*", cluster.internalService.metadata.name ]),
108 std.join(".", [ "*", cluster.internalService.metadata.name, cluster.metadata.namespace ]),
109 std.join(".", [ "*", cluster.internalService.host, "cluster.local" ]),
110 ],
111 },
112 },
113
114 clientCertificate: cm.Certificate(cluster.name("client")) {
115 metadata+: cluster.metadata,
116 spec: {
117 secretName: cluster.name("client-certificate"),
118 duration: "43800h0m0s", // 5 years
119 issuerRef: {
120 name: cluster.pki.clusterIssuer.metadata.name,
121 },
122 commonName: "root",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200123 },
124 },
125 },
126
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200127 serviceAccount: kube.ServiceAccount(cluster.name("cockroachdb")) {
128 metadata+: cluster.metadata,
129 },
130
131 role: kube.Role(cluster.name("cockroachdb")) {
132 metadata+: cluster.metadata,
133 rules: [
134 {
135 apiGroups: [ "" ],
136 resources: [ "secrets" ],
137 verbs: [ "get" ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200138 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200139 ],
140 },
141
142 roleBinding: kube.RoleBinding(cluster.name("cockroachdb")) {
143 metadata+: cluster.metadata,
144 roleRef_: cluster.role,
145 subjects_: [cluster.serviceAccount],
146 },
147
148 publicService: kube.Service(cluster.name("public")) {
149 metadata+: cluster.metadata,
150 target_pod:: cluster.statefulSet.spec.template,
151 spec+: {
152 ports: [
153 { name: "grpc", port: 26257, targetPort: 26257 },
154 { name: "http", port: 8080, targetPort: 8080 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200155 ],
156 },
157 },
158
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200159 internalService: kube.Service(cluster.name("internal")) {
160 metadata+: cluster.metadata + {
161 annotations+: {
162 "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
163 "prometheus.io/scrape": "true",
164 "prometheus.io/path": "_status/vars",
165 "prometheus.io/port": "8080",
166 },
167 },
168 target_pod:: cluster.statefulSet.spec.template,
169 spec+: {
170 ports: [
171 { name: "grpc", port: 26257, targetPort: 26257 },
172 { name: "http", port: 8080, targetPort: 8080 },
173 ],
174 publishNotReadyAddresses: true,
175 clusterIP: "None",
176 },
177 },
178
179 podDisruptionBudget: kube.PodDisruptionBudget(cluster.name("pod")) {
180 metadata+: cluster.metadata,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200181 spec: {
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200182 selector: {
183 matchLabels: {
184 "app.kubernetes.io/component": "cockroachdb",
185 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200186 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200187 maxUnavailable: 1,
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200188 },
189 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200190
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200191 statefulSet: kube.StatefulSet(cluster.name("cockroachdb")) {
192 metadata+: cluster.metadata {
193 labels+: {
194 "app.kubernetes.io/component": "server",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200195 },
196 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200197 spec+: {
198 serviceName: cluster.internalService.metadata.name,
199 replicas: 3,
200 template: {
201 metadata: cluster.statefulSet.metadata,
202 spec+: {
203 dnsPolicy: "ClusterFirst",
204 serviceAccountName: cluster.serviceAccount.metadata.name,
205 affinity: {
206 podAntiAffinity: {
207 preferredDuringSchedulingIgnoredDuringExecution: [
208 {
209 weight: 100,
210 podAffinityTerm: {
211 labelSelector: {
212 matchExpressions: [
213 {
214 key: "app.kubernetes.io/component",
215 operator: "In",
216 values: [ "cockroachdb" ],
217 },
218 ],
219 },
220 topologyKey: "kubernetes.io/hostname",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200221 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200222 },
223 ],
224 },
225 },
226 containers: [
227 kube.Container("cockroachdb") {
228 image: cluster.cfg.image,
229 imagePullPolicy: "IfNotPresent",
230 resources: {
231 requests: {
232 cpu: "2",
233 memory: "6Gi",
234 },
235 limits: {
236 memory: "6Gi",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200237 },
238 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200239 ports_: {
240 "grpc": { containerPort: 26257 },
241 "http": { containerPort: 8080 },
242 },
243 livenessProbe: {
244 httpGet: {
245 path: "/health",
246 port: "http",
247 },
248 initialDelaySeconds: 30,
249 periodSeconds: 5,
250 },
251 readinessProbe: {
252 httpGet: {
253 path: "/health?ready=1",
254 port: "http",
255 },
256 initialDelaySeconds: 10,
257 periodSeconds: 5,
258 failureThreshold: 2,
259 },
260 volumeMounts: [
261 {
262 name: "datadir",
263 mountPath: "/cockroach/cockroach-data",
264 },
265 {
266 name: "certs",
267 mountPath: "/cockroach/cockroach-certs/node.crt",
268 subPath: "tls.crt",
269 },
270 {
271 name: "certs",
272 mountPath: "/cockroach/cockroach-certs/node.key",
273 subPath: "tls.key",
274 },
275 {
276 name: "certs",
277 mountPath: "/cockroach/cockroach-certs/ca.crt",
278 subPath: "ca.crt",
279 },
280 ],
281 env_: {
282 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
283 },
284 command: [
285 "/bin/bash",
286 "-ecx",
287 "exec /cockroach/cockroach start --logtostderr --certs-dir /cockroach/cockroach-certs --advertise-host $(hostname -f) --http-addr 0.0.0.0 --cache 25% --max-sql-memory 25% --join " + std.join(",", cluster.hosts),
288 ],
289 },
290 ],
291 terminationGracePeriodSeconds: 60,
292 volumes: [
293 {
294 name: "datadir",
295 emptyDir: {},
296 },
297 {
298 name: "certs",
299 secret: {
300 secretName: cluster.pki.nodeCertificate.spec.secretName,
301 defaultMode: kube.parseOctal("400"),
302 },
303 },
304 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200305 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200306 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200307 podManagementPolicy: "Parallel",
308 updateStrategy: {
309 type: "RollingUpdate",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200310 },
311 },
312 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200313
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200314 initJob: kube.Job(cluster.name("init")) {
315 metadata+: cluster.metadata,
316 spec: {
317 template: {
318 metadata+: cluster.metadata,
319 spec+: {
320 serviceAccountName: cluster.serviceAccount.metadata.name,
321 containers: [
322 kube.Container("cluster-init") {
323 image: cluster.cfg.image,
324 imagePullPolicy: "IfNotPresent",
325 env_: {
326 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
327 },
328 command: [
329 "/bin/bash",
330 "-ecx",
331 "/cockroach/cockroach init --host=" + cluster.hosts[0],
332 ],
333 volumeMounts: [
334 {
335 name: "certs",
336 mountPath: "/cockroach/cockroach-certs/ca.crt",
337 subPath: "ca.crt",
338 },
339 {
340 name: "certs",
341 mountPath: "/cockroach/cockroach-certs/client.root.crt",
342 subPath: "tls.crt",
343 },
344 {
345 name: "certs",
346 mountPath: "/cockroach/cockroach-certs/client.root.key",
347 subPath: "tls.key",
348 },
349 ],
350 },
351 ],
352 restartPolicy: "OnFailure",
353 volumes: [
354 {
355 name: "certs",
356 secret: {
357 secretName: cluster.pki.clientCertificate.spec.secretName,
358 defaultMode: kube.parseOctal("400")
359 }
360 },
361 ],
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200362 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200363 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200364 },
365 },
366
367 clientPod: kube.Pod(cluster.name("client")) {
368 metadata+: cluster.metadata {
369 labels+: {
370 "app.kubernetes.io/component": "client",
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200371 },
Sergiusz Bazanski662a3cd2019-06-20 19:45:03 +0200372 },
373 spec: {
374 terminationGracePeriodSeconds: 5,
375 containers: [
376 kube.Container("cockroachdb-client") {
377 image: cluster.cfg.image,
378 env_: {
379 "COCKROACH_CERTS_DIR": "/cockroach/cockroach-certs",
380 "COCKROACH_HOST": cluster.hosts[0],
381 },
382 command: ["sleep", "2147483648"], //(FIXME) keep the client pod running indefinitely
383 volumeMounts: [
384 {
385 name: "certs",
386 mountPath: "/cockroach/cockroach-certs/ca.crt",
387 subPath: "ca.crt",
388 },
389 {
390 name: "certs",
391 mountPath: "/cockroach/cockroach-certs/client.root.crt",
392 subPath: "tls.crt",
393 },
394 {
395 name: "certs",
396 mountPath: "/cockroach/cockroach-certs/client.root.key",
397 subPath: "tls.key",
398 },
399 ],
400 },
401 ],
402 volumes: [
403 {
404 name: "certs",
405 secret: {
406 secretName: cluster.pki.clientCertificate.spec.secretName,
407 defaultMode: kube.parseOctal("400")
408 }
409 },
410 ],
411 },
Patryk Jakuszew5dfd4cc2019-05-22 23:54:02 +0200412 },
413 },
Sergiusz Bazanski224a50b2019-06-20 16:41:54 +0200414}