Running CockroachDB (CRDB) on Kubernetes (k8s) is a complementary affair. CRDB provides high availability at the data layer, and k8s provides high availability at the infrastructure layer.
For enterprises that are already leveraging k8s to run their applications & microservices and who have experience running and administering k8s, it can make sense to also run the database within k8s.
The docs for running CRDB on k8s on the Cockroach Labs' documentation site have an excellent set of steps for getting CRDB up and running. They are all you need to run a demo of CRDB on k8s.
However, if you're planning on running k8s in Production, there are a few other things you'll probably want to do. In this blog I'll explain how to go about this.
Expose CRDB outside of the k8s cluster
You can follow the docs for deploying CRDB in a single-region k8s deployment. When you deploy CRDB on k8s (through any of the available methods -- operator, helm chart, or via yaml configs), there are a few services created for you.
For instance, after installing CRDB via the CRDB k8s operator on GKE, I have the following services:
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cockroachdb ClusterIP None <none> 26258/TCP,8080/TCP,26257/TCP 59s cockroachdb-public ClusterIP 10.108.12.200 <none> 26258/TCP,8080/TCP,26257/TCP 59s kubernetes ClusterIP 10.108.0.1 <none> 443/TCP 7m5s
In k8s, there are four potential types of services:
- ClusterIP - this is the default if another type is not explicitly specified in the service's manifest; this type of service is internal to the k8s cluster
- Nodeport - this is a way to expose the service outside of the k8s cluster using a custom port on the various cluster nodes - this can be OK for development clusters but it's not typically how you want to do things in Production because you have to use non-standard ports
- LoadBalancer - this is another way to expose services to apps running outside of the k8s cluster; it's a better way for Production deployments because you can use the standard ports, but you need to have a process that can assign a Public IP to the load balancer; if you're running in a cloud-based k8s service (i.e., EKS, GKE, AKS, or OpenShift), this is handled for you, but if you're running on OSS k8s, you have to handle this yourself
- ExternalName - this is a way of assigning an external DNS name to a k8s service and is not really applicable for what we're talking about here.
SQL Port
You'll notice that in the services listed in our CRDB k8s cluster that we have one called "cockroachdb" of type "ClusterIP" and which has no Cluster IP assigned. This is called a "headless" service. The point of this service is to be the service associated with the statefulset called "cockroach". It is not intended to be used to access the CRDB cluster by any internal or external apps. You can see the reference to this service in the statefulset's manifest:
$ kubectl get sts cockroachdb -o yaml | grep "serviceName" serviceName: cockroachdb
The other cockroach service called "cockroachdb-public" is also of type ClusterIP but has a Cluster IP assigned to it. The point of this service is to be used by apps wanting to access CRDB that are running inside the k8s cluster.
In the CRDB docs, you'll see a section called "Use the built-in SQL Client" and you can see that they leverage this service:
kubectl exec -it cockroachdb-client-secure \ -- ./cockroach sql \ --certs-dir=/cockroach/cockroach-certs \ --host=cockroachdb-public
This is a perfectly acceptable way to setup some basic things in the cluster via the SQL prompt (like creating the first users and to verify basic read/write capabilities are working). However, this is not the mechanism you'd want to use in Production for your apps to access the CRDB cluster -- especially if your apps are running outside the k8s cluster. I'll talk about the right way to do this a little later on.
There is also a third service listed called "kubernetes" which is not used to access CRDB at all.
When you're running CRDB, there are three access points into the CRDB nodes:
- the SQL client port (26257 by default)
- the DB Console port (8080 by default), and
- the port used by other nodes to do node-to-node interactions (26258 by default).
All three of these ports are exposed by the "cockroachdb-public" service that we've been looking at.
DB Console port
We can technically get to the DB Console on each CRDB node from any pod running inside our k8s cluster, but that would involve running curl commands which aren't very useful.
Just to illustrate what I'm talking about, you can do something like this:
$ kubectl exec -it cockroachdb-0 -- curl -Lk http://cockroachdb-public:8080/ Defaulted container "db" out of: db, db-init (init) <!DOCTYPE html> <html> <head> <title>Cockroach Console</title> <meta charset="UTF-8"> <link href="favicon.ico" rel="shortcut icon"> </head> <body> <div id="react-layout"></div> <script src="bundle.js" type="text/javascript"></script> </body> </html>
You can see that we do actually get some HTML back from our curl command, but this is a lame way to interact with a website! So, in the CRDB docs, they recommend using the kubectl port-forward
command to expose this service to the computer where your kubectl command is running:
$ kubectl port-forward service/cockroachdb-public 8080 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080
Once you've run this command, you can go to the browser on your computer and get to http://localhost:8080/
and acccess the DB Console. The kubectl command is proxying your input to the CRDB nodes and their output back to your browser.
Again, this is a perfectly acceptable way to access the CRDB nodes for a demo and just to make sure they're running OK. But, for Production, you don't want to have everybody on your team port-forwarding into your nodes in order to monitor what's happening in CRDB.
Using an external load balancer
In order to access the SQL client and DB Console from outside the cluster, the best way to go is to create a k8s service of type LoadBalancer
.
Create a yaml file like this:
apiVersion: v1 kind: Service metadata: annotations: # put annotations here that affect how EKS creates things # service.beta.kubernetes.io/aws-load-balancer-internal: "true" labels: app: cockroachdb name: cockroachdb-lb spec: # if you don't specify the type, it will default to ClusterIP which won't expose the services outside of the k8s cluster type: LoadBalancer selector: # this selector is the label associated with your CRDB pods # if you're not sure -- run this: kubectl get pods --show-labels app: cockroachdb ports: - name: https port: 8080 protocol: TCP targetPort: 8080 - name: tcp port: 26257 protocol: TCP targetPort: 26257
Then, you can create the service by applying that yaml file with:
kubectl apply -f cockroachdb-lb.yaml
Notice that if you get a listing of your services right after creating it that you will see a service called "cockroachdb-lb" and it will have an External IP of "pending":
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cockroachdb ClusterIP None <none> 26258/TCP,8080/TCP,26257/TCP 33m cockroachdb-lb LoadBalancer 10.108.4.152 <pending> 8080:31016/TCP,26257:31395/TCP 4s cockroachdb-public ClusterIP 10.108.12.200 <none> 26258/TCP,8080/TCP,26257/TCP 33m kubernetes ClusterIP 10.108.0.1 <none> 443/TCP 39m
If you wait a few seconds and try again, you'll see that an External IP value is assigned:
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cockroachdb ClusterIP None <none> 26258/TCP,8080/TCP,26257/TCP 35m cockroachdb-lb LoadBalancer 10.108.4.152 34.139.126.177 8080:31016/TCP,26257:31395/TCP 97s cockroachdb-public ClusterIP 10.108.12.200 <none> 26258/TCP,8080/TCP,26257/TCP 35m kubernetes ClusterIP 10.108.0.1 <none> 443/TCP 41m
Because I'm running in GKE, Google Cloud handles creating a load balancer for me. If you look in the GCP Cloud Console, you can see the load balancer details.
If I describe the LB svc, I can look at the endpoints that have been exposed by the service:
$ kubectl describe svc cockroachdb-lb Name: cockroachdb-lb Namespace: default Labels: app=cockroachdb Annotations: cloud.google.com/neg: {"ingress":true} Selector: app=cockroachdb Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.108.4.152 IPs: 10.108.4.152 LoadBalancer Ingress: 34.139.126.177 Port: https 8080/TCP TargetPort: 8080/TCP NodePort: https 31016/TCP Endpoints: <none> Port: tcp 26257/TCP TargetPort: 26257/TCP NodePort: tcp 31395/TCP Endpoints: <none> Session Affinity: None External Traffic Policy: Cluster Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal EnsuringLoadBalancer 3m12s service-controller Ensuring load balancer Normal EnsuredLoadBalancer 2m36s service-controller Ensured load balancer
We can see here that there are no endpoints assigned. That's not good!
The way that the LB gets associated with pods is via the "selector" in its spec. My current selector is looking for pods with a label of app: cockroachdb
.
Let's see what labels our pods are actually using:
$ kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS cockroachdb-0 1/1 Running 0 37m app.kubernetes.io/component=database,app.kubernetes.io/instance=cockroachdb,app.kubernetes.io/name=cockroachdb,controller-revision-hash=cockroachdb-7b9668cd75,crdb=is-cool,statefulset.kubernetes.io/pod-name=cockroachdb-0 cockroachdb-1 1/1 Running 0 37m app.kubernetes.io/component=database,app.kubernetes.io/instance=cockroachdb,app.kubernetes.io/name=cockroachdb,controller-revision-hash=cockroachdb-7b9668cd75,crdb=is-cool,statefulset.kubernetes.io/pod-name=cockroachdb-1 cockroachdb-2 1/1 Running 0 37m app.kubernetes.io/component=database,app.kubernetes.io/instance=cockroachdb,app.kubernetes.io/name=cockroachdb,controller-revision-hash=cockroachdb-7b9668cd75,crdb=is-cool,statefulset.kubernetes.io/pod-name=cockroachdb-2
A better choice for the selector label would be app.kubernetes.io/name=cockroachdb
.
Let's edit the yaml to include that value and re-apply it:
apiVersion: v1 kind: Service metadata: annotations: # put annotations here that affect how EKS creates things # service.beta.kubernetes.io/aws-load-balancer-internal: "true" labels: app: cockroachdb name: cockroachdb-lb spec: # if you don't specify the type, it will default to ClusterIP which won't expose the services outside of the k8s cluster type: LoadBalancer selector: # this selector is the label associated with your CRDB pods # if you're not sure -- run this: kubectl get pods --show-labels app.kubernetes.io/name: cockroachdb ports: - name: https port: 8080 protocol: TCP targetPort: 8080 - name: tcp port: 26257 protocol: TCP targetPort: 26257
Notice that I have to enter app.kubernetes.io/name: cockroachdb
instead of app.kubernetes.io/name=cockroachdb
kubectl apply -f cockroachdb-lb.yaml
Now, let's look at our endpoints again:
$ kubectl describe svc cockroachdb-lb Name: cockroachdb-lb Namespace: default Labels: app=cockroachdb Annotations: cloud.google.com/neg: {"ingress":true} Selector: app.kubernetes.io/name=cockroachdb Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.108.4.152 IPs: 10.108.4.152 LoadBalancer Ingress: 34.139.126.177 Port: https 8080/TCP TargetPort: 8080/TCP NodePort: https 31016/TCP Endpoints: 10.104.0.4:8080,10.104.1.8:8080,10.104.2.7:8080 Port: tcp 26257/TCP TargetPort: 26257/TCP NodePort: tcp 31395/TCP Endpoints: 10.104.0.4:26257,10.104.1.8:26257,10.104.2.7:26257 Session Affinity: None External Traffic Policy: Cluster Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal EnsuringLoadBalancer 49s (x2 over 8m52s) service-controller Ensuring load balancer Normal EnsuredLoadBalancer 44s (x2 over 8m16s) service-controller Ensured load balancer
Yay! We have endpoints.
Now, if I try to access the nodes via the SQL port or via the DB Console, I should be able to do so from outside the cluster.
I can go to a browser and access https://34.139.126.177:8080/
and it works. (You'll want to substitute the actual value of your LB's External IP here.)
Also, I can access the nodes on the SQL port. For example:
$ cockroach sql --url 'postgres://roach:Q7gc8rEdS@34.139.126.177:26257/defaultdb?sslmode=require' # # Welcome to the CockroachDB SQL shell. # All statements must be terminated by a semicolon. # To exit, type: \q. # # Client version: CockroachDB CCL v22.2.5 (aarch64-apple-darwin21.2, built 2023/02/16 16:37:38, go1.19.4) # Server version: CockroachDB CCL v22.2.2 (x86_64-pc-linux-gnu, built 2023/01/04 17:23:00, go1.19.1) warning: server version older than client! proceed with caution; some features may not be available. # Cluster ID: 7539a31a-fc44-4f89-a154-cc60f8aaeddd # # Enter \? for a brief introduction. # roach@34.139.126.177:26257/defaultdb>
The SQL port in CRDB needs to be exposed by a L4/TCP load balancer (which is what we're doing above). The DB Console port can also be exposed this way (as we've demonstrated), but since it's an HTTP/HTTPS access point, it could also be exposed through an L7 endpoint, like k8s' Ingress. I'm not going to demonstrate that here in this blog, but it can certainly be done.
Fixing the Node Certs
Another thing to note here. In my example above, I'm able to connect to my server using the IP address because I specified sslmode=require
. This tells my Postgres driver/client that I want to use TLS to encrypt traffic to/from the cluster, but I don't want to do any hostname verification checks. We don't recommend connecting this way because it leave your cluster susceptible to man-in-the-middle (MITM) attacks.
In order to connect the "right way", I need to connect using sslmode=verify-full
and specify the ca.crt used to sign all the certs used in the CRDB cluster.
I can get a list of the certs used by my cluster by asking k8s to list out all the secrets being used:
$ kubectl get secrets NAME TYPE DATA AGE cockroachdb-ca Opaque 1 57m cockroachdb-node Opaque 3 57m cockroachdb-root Opaque 3 57m
If I look into the details of the cockroachdb-node secret, I can see the various cert and key files that it contains:
(note that I'm using jq which you can install on your Mac using brew install jq
)
$ kubectl get secrets cockroachdb-node -o json | jq '.data | map_values(@base64d)' | awk '{gsub(/\\n/,"\n")}1' { "ca.crt": "-----BEGIN CERTIFICATE----- MIIDJTCCAg2gAwIBAgIQC+85luldQT9+ctIxQ1BitjANBgkqhkiG9w0BAQsFADAr MRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENvY2tyb2FjaCBDQTAeFw0y MzAyMjUyMDEzMzhaFw0zMzAzMDUyMDEzMzhaMCsxEjAQBgNVBAoTCUNvY2tyb2Fj aDEVMBMGA1UEAxMMQ29ja3JvYWNoIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAp7fVT7JMbrzC0J9UeqN3r5YeW1FyYpVfpGWRiQICK8ZPv8NnzsaQ SgOig83c9wax/wHP+xK4ISoTPMLc75eM+YKoN5fU17Ki28iopJwgIakCjSXJxcAv cN0H6cn6BemL+qb9RS7Pffu8ohJKyLsNk7a/8xMNKUAPhgmBAYws4SOhG68/f1je Lk8hsPrqVlCDBGPwVQdhCYkKvavLA7qG0D/+F+FfNI7a/qldqn/u74DN69gie5w4 37bB1IecleX3Ks0Ype+AiNzcdllUBC22ttVREpymVj7K24ti5DeyGPeHND5F/q6F o8a/apYMPr+hbbPgsMjoreHlcCwgxk/zEwIDAQABo0UwQzAOBgNVHQ8BAf8EBAMC AuQwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUabb2eIdtS1cn3QY/pNrk v9Kyz8swDQYJKoZIhvcNAQELBQADggEBAI29Fz3SBzkSYdvhRWsXVjRL3XnteIvJ GwwdIXgEx/Uxc+QXnOGRF6yKqMAzJhU15qP0u1LgqHq56tkeTmQAeApMg6VTa2wj HibW77O8w8anukv5ThXeGs52FYTVzzQ/ao+y3R9cfyHQleoecohiFXYJ0RLKmj7n ywZ9CocP6VnRklMyegpNBp9VWnnKTsMOs+lEaGzPDiJBdPJ0Ym9946jwaojb1st3 pnApAgN/32Ak9bTrBVf6Zl2zj6n6rLD294+EMScpvVqqIqA4iJh9cpGbIEu2TO4x QrjTl5aBbP7e4VWQnVOSZgmeTJUnFm4L2kR53yFonmys0ZJ/14z0acw= -----END CERTIFICATE----- ", "tls.crt": "-----BEGIN CERTIFICATE----- MIID+jCCAuKgAwIBAgIQCjaSSuwS1yLAEuoysn7ZUDANBgkqhkiG9w0BAQsFADAr MRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENvY2tyb2FjaCBDQTAeFw0y MzAyMjUyMDEzMzhaFw0yODAzMDEyMDEzMzhaMCMxEjAQBgNVBAoTCUNvY2tyb2Fj aDENMAsGA1UEAxMEbm9kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AKT4igRxUZE5p7NDkDeSWqQjENX7W3tOTXoON1GyIjf8/j1xyQN2i/AMMFAdb5P9 f8mBFzsYes/WLgXWlPZQOal2MKJAOKJ1AYywKeZ+AqCYftIJlqm/1A/EdNn74Mv1 ykNU5f2YxdBAnl8MOIrIvWeghwzKv1PSYTiUDBFti9TNsQAvrwtXC8vrfir9rnz3 8j8QP1RMzQkySRUSsik0GGD/YMW5leTsEQKYxI+clkH7YM1pOUhw6b3SHbkZlYkO arsgv2qlnjMUN4j/6HqtOyzu5wjyOBXKxccGwNtIJB3Xq0w3wYN1E3TWDmi9jY1c T64w9KGgXLC8NR46MqjvfM0CAwEAAaOCASAwggEcMA4GA1UdDwEB/wQEAwIFoDAd BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUabb2eIdt S1cn3QY/pNrkv9Kyz8swgckGA1UdEQSBwTCBvoIJbG9jYWxob3N0ghJjb2Nrcm9h Y2hkYi1wdWJsaWOCGmNvY2tyb2FjaGRiLXB1YmxpYy5kZWZhdWx0gixjb2Nrcm9h Y2hkYi1wdWJsaWMuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbIINKi5jb2Nrcm9h Y2hkYoIVKi5jb2Nrcm9hY2hkYi5kZWZhdWx0gicqLmNvY2tyb2FjaGRiLmRlZmF1 bHQuc3ZjLmNsdXN0ZXIubG9jYWyHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBACAr 6MQL8dGjbhufGRcGjpKE/ctmwpoARvfFIvCg1S5/ZXPJTz4A9fp1B0bxKoHopaOO 6F2EH9B6qH6g3cFbD6au+QXc5f/kgxVJVJOewCOUDLRjH1i3Fcnd/zxvywQU6cIs ArfwWW+XoifirNQ6MwqxtPVzjMectGQUs1IpdDwLReO6eS9pFo4kHMZiJi5XTgjJ krDFMbFUW8qnul1w3UrxgikXeLKnuIDnegPpX4Xk0yYF1ycxA46ZORV+DybP3DG8 F6lH6wA3uF2E62Z/52XH7UUvtUaAIK937vbxXosufD8KwbXCNEcojlSDYtuKhtCq KcywMKGrVgdtd/nwxy4= -----END CERTIFICATE----- ", "tls.key": "-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEApPiKBHFRkTmns0OQN5JapCMQ1ftbe05Neg43UbIiN/z+PXHJ A3aL8AwwUB1vk/1/yYEXOxh6z9YuBdaU9lA5qXYwokA4onUBjLAp5n4CoJh+0gmW qb/UD8R02fvgy/XKQ1Tl/ZjF0ECeXww4isi9Z6CHDMq/U9JhOJQMEW2L1M2xAC+v C1cLy+t+Kv2ufPfyPxA/VEzNCTJJFRKyKTQYYP9gxbmV5OwRApjEj5yWQftgzWk5 SHDpvdIduRmViQ5quyC/aqWeMxQ3iP/oeq07LO7nCPI4FcrFxwbA20gkHderTDfB g3UTdNYOaL2NjVxPrjD0oaBcsLw1HjoyqO98zQIDAQABAoIBAQCGBtY6ncXi8rBo V6/HNkQlrcdz0W6VUxxm2T3gRZS/X+8+BD+HbLxsHbrym7eWyBEVqKcy/8RnLl7d p2QGaU8vejIw33QjqGPF5SlldWK1Dq+Z/OhGqO6kkLtOjfAoRFw7L7Jawc+UTatd FRSqzEP0+No/bkja1MTfrofPcOx1ygiTHsSm3JHy+rh/bxRxeU9J5JBWUD1KeRS4 FRsYqf7tgv6KzBktRRs29q/HeU4up0S9HyjbE9emc99g6ZfX2dpmqoDW0kBjo729 x0XP2KxmSGeAogTmpVBz6RjoDuCUAbtUjMAbpbDRJJqnm6R8fIj1e+mDpSwOS4QN dikzHQiBAoGBANacPfviPU81ddowy1HjPEko4C4Qy6brmPWuaeA6QUUL/MR+QrYN Usp4B7d8lsLnZEdHyeszDnxPaAzj4rE7uDhSSMJmfflNqQVmWR6jByQ8GgzDFS5/ Re3LYR26DJMHBNGZqCxQ7us7Aqc0+YeDT8/wlniOAlndvXQ7l1Tt7nGZAoGBAMTJ fk7Cs81SaalQQ05O7jfjwvi6kX+ISbBnBB6LyDCNkBoCwRKIHsiDIkKlFhGgwvim +K/Ugsg8GuqYb5qd1ag+Kb8ykQpbMjkvr6Mh1bArN3KWSQTaiFko7nJLLd7P2H0V WzrD/OUD0J2NKkzQLJcxuS8hc5YRj0DGWqzCVw1VAoGAAmj+yTVhOuJ+0FR79A95 PdkXq2zE3LsInLm4tqvwz7WywQIp/aForJ1seMMNbmLq3WIRAnMwVnUN1hc5FIR3 LSq/Zm+AOqyEmWrs1Us/aUjDgiEuu7byMhl2nb7ZJU2O4Eu5d8Xw6PNgtEAEDWGM I+mvxurRW/EBj6ybpniFlQECgYB/DXzQSyMdeI0htOGPyKRDT3lNb9+K0KqLCyf8 tNEuj+eu84JGfb4qRYg0MTQbc4kOU3eSxokd0LisKHk+AZO1yVTYzkQYxKKbi29B yxGVaYGmKOPCD3oi3qt8/Y8DIXyr3cMGIQ3BqwHhBwh9iZaQk5j1lgpzpKix8J8Q lXTw9QKBgQCNKN1p9UNxpX19ni7Mg+MVPRkZgC68DBXYxNDnAexFjlatV+dIo03u SxGBsB8Y1q4AbqHwWe8lSp+erADzeWtkYD4u9BSZl4WD50Mbev/Fut9dGJwnI+BJ 0ldr96qyslFD1RitRl5Xc6gOTcF4Bt/O5GRo5+2F4fDwJm6+dYIjJA== -----END RSA PRIVATE KEY----- " }
I'm going to copy and paste the ca.crt output into a file called ca.crt.
OK, now I can try to connect the right way:
$ cockroach sql --url 'postgres://roach:Q7gc8rEdS@34.139.126.177:26257/defaultdb?sslmode=verify-full&sslrootcert=ca.crt' # # Welcome to the CockroachDB SQL shell. # All statements must be terminated by a semicolon. # To exit, type: \q. # ERROR: failed to connect to `host=34.139.126.177 user=roach database=defaultdb`: failed to write startup message (x509: certificate is valid for 127.0.0.1, not 34.139.126.177) Failed running "sql"
Notice this time I pass my ca.crt file and use sslmode=verify-full
and I get an error saying that my node certs are not authorized to respond as 34.139.126.177.
To fix this, I need to re-issue my node certs to know about their load-balancer IP. If you want to create a DNS record like crdb.myproject.mydomain.com
, now is a good time to do that because we'll want to include that domain name on our new certs, too.
The process to generate the node certs is described in this doc.
Depending on how you installed CRDB in k8s in the first place, you might have these various certs and keys in place in your local file system, or you might have to download them from the certs (as we did above for the ca.crt file).
You don't need to re-create the ca key/crt pair, but you want to recreate the CRDB node cert. When you get to that step, add the IP address and DNS name (if you have one) as additional parameters to this command:
$ cockroach cert create-node \ > localhost 127.0.0.1 \ > cockroachdb-public \ > cockroachdb-public.default \ > cockroachdb-public.default.svc.cluster.local \ > *.cockroachdb \ > *.cockroachdb.default \ > *.cockroachdb.default.svc.cluster.local \ > 34.139.126.177 \ > crdb.myproject.mydomain.com \ > --certs-dir=certs \ > --ca-key=my-safe-directory/ca.key
This will create a node.crt/node.key pair. I am re-naming those to tls.key/tls.crt since that's what they're called in my installation.
You can examine the tls.crt file to see if it includes our IP and dns name by running this command:
$ openssl x509 -in certs/tls.crt -noout -text | egrep -A 2 'X509v3 Subject Alternative Name' X509v3 Subject Alternative Name: DNS:localhost, DNS:cockroachdb-public, DNS:cockroachdb-public.default, DNS:cockroachdb-public.default.svc.cluster.local, DNS:*.cockroachdb, DNS:*.cockroachdb.default, DNS:*.cockroachdb.default.svc.cluster.local, DNS:crdb.myproject.mydomain.com, IP Address:127.0.0.1, IP Address:34.139.126.177 Signature Algorithm: sha256WithRSAEncryption
Notice that our IP and domain name are listed.
Now we need to:
- Delete the existing secret
- Re-create the secret with the new cert/key files
- Restart the pods so they will pick up the new secrets/certs
After doing so, if I try to connect using the "right way", I am able to do so successfully:
$ cockroach sql --url 'postgres://roach:Q7gc8rEdS@34.139.126.177:26257/defaultdb?sslmode=verify-full&sslrootcert=certs/ca.crt' # # Welcome to the CockroachDB SQL shell. # All statements must be terminated by a semicolon. # To exit, type: \q. # # Client version: CockroachDB CCL v22.2.5 (aarch64-apple-darwin21.2, built 2023/02/16 16:37:38, go1.19.4) # Server version: CockroachDB CCL v22.2.2 (x86_64-pc-linux-gnu, built 2023/01/04 17:23:00, go1.19.1) warning: server version older than client! proceed with caution; some features may not be available. # Cluster ID: 7539a31a-fc44-4f89-a154-cc60f8aaeddd # # Enter \? for a brief introduction. # roach@34.139.126.177:26257/defaultdb>
Summary
When running CRDB in k8s in Production, you'll want to expose the CRDB nodes externally by using a load balancer. And, you'll want to re-create your node certs to references the load balancer details.
Happy CRDBing!
Top comments (0)