How to Create an Active-active Database (CRDB) in Redis Enterprise for Google Kubernetes Engine (GKE)
The following is the high level workflow which you will follow:
- Create two GKE clusters
- Create Redis Enterprise Cluster / Install Nginx ingress controller / Apply the
activeActive
spec on each GKE cluster - Document the required parameters
- Formulate the CRDB creation JSON payload using the parameters from both RECs in a single JSON document
- POST the JSON payload to one of the REC's API endpoints. (Yes, just one; it will coordinate with the other(s).)
./create_cluster.sh glau-aa-us-west1-a us-west1-a
./create_cluster.sh glau-aa-us-east1-b us-east1-b
Deploy REC in "raas-us-west1-a" namespace of the GKE cluster in us-west1-a region:
./bundle.sh raas-us-west1-a
You should see the following after you run "kubectl get all":
NAME READY STATUS RESTARTS AGE
pod/redis-enterprise-operator-7f58bd467c-5jdqk 0/1 ContainerCreating 0 2s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/redis-enterprise-operator 0/1 1 0 3s
NAME DESIRED CURRENT READY AGE
replicaset.apps/redis-enterprise-operator-7f58bd467c 1 1 0 3s
Now create a Redis Enterprise Cluster named rec-us-west-1-a:
kubectl apply -f rec/rec-us-west1-a.yaml
Now you should see the following after you run "kubectl get all". It will take a few minutes to complete creation of the cluster:
NAME READY STATUS RESTARTS AGE
pod/rec-us-west1-a-0 2/2 Running 0 8m13s
pod/rec-us-west1-a-1 2/2 Running 0 6m17s
pod/rec-us-west1-a-2 2/2 Running 0 4m6s
pod/rec-us-west1-a-services-rigger-5cc8877ff5-2qkr7 1/1 Running 0 8m14s
pod/redis-enterprise-operator-7f58bd467c-5jdqk 1/1 Running 0 13m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/rec-us-west1-a ClusterIP None <none> 9443/TCP,8001/TCP,8070/TCP 8m15s
service/rec-us-west1-a-ui ClusterIP 10.40.25.222 <none> 8443/TCP 8m15s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/rec-us-west1-a-services-rigger 1/1 1 1 8m16s
deployment.apps/redis-enterprise-operator 1/1 1 1 13m
NAME DESIRED CURRENT READY AGE
replicaset.apps/rec-us-west1-a-services-rigger-5cc8877ff5 1 1 1 8m16s
replicaset.apps/redis-enterprise-operator-7f58bd467c 1 1 1 13m
NAME READY AGE
statefulset.apps/rec-us-west1-a 3/3 8m15s
Install Ingress Controller (Nginx) in "ingress-nginx" namespace of the GKE cluster in us-west1-a region:
kubectl apply -f ingress/nginx-ingress-controller.yaml
You should see the following after you run "kubectl get all -n ingress-nginx". It will take a few minutes to complete the deployment.
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-wbpb9 0/1 Completed 0 47m
pod/ingress-nginx-admission-patch-ltrfg 0/1 Completed 1 47m
pod/ingress-nginx-controller-6d95d4fc94-bswr7 1/1 Running 0 47m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.40.25.163 34.105.40.1 80:31456/TCP,443:32004/TCP 48m
service/ingress-nginx-controller-admission ClusterIP 10.40.23.4 <none> 443/TCP 48m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 48m
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-6d95d4fc94 1 1 1 48m
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 3s 48m
job.batch/ingress-nginx-admission-patch 1/1 4s 48m
Retrieve EXTERNAL-IP of the ingress-controller:
kubectl get service/ingress-nginx-controller -n ingress-nginx
Apply the activeActive spec to rec-us-west1-a (Redis Enterprise Cluster): REC at rec-us-west1-a:
activeActive:
apiIngressUrl: api-raas-us-west1-a.rec-us-west1-a.<EXTERNAL-IP>.nip.io
dbIngressSuffix: -raas-us-west1-a.rec-us-west1-a.<EXTERNAL-IP>.nip.io
method: ingress
ingressAnnotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
kubernetes.io/ingress.class: "nginx"
In our example, the spec looks like this:
activeActive:
apiIngressUrl: api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io
dbIngressSuffix: -raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io
method: ingress
ingressAnnotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
kubernetes.io/ingress.class: "nginx"
You can validate that these were applied by describing the rec as follows:
kubectl get rec -n raas-us-west1-a -o json | jq '.items[].spec.activeActive'
The output should look like the following:
{
"apiIngressUrl": "api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io",
"dbIngressSuffix": "-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io",
"ingressAnnotations": {
"kubernetes.io/ingress.class": "nginx",
"nginx.ingress.kubernetes.io/ssl-passthrough": "true"
},
"method": "ingress"
}
Check if ingress is created:
kubectl get ingress -n raas-us-west1-a
The output should look like this:
NAME CLASS HOSTS ADDRESS PORTS AGE
rec-us-west1-a <none> api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io 34.105.40.1 80 2m
This indicates that a route has been created to access the API endpoint externally. Grab creds for rec:
kubectl get secrets -n raas-us-west1-a rec-us-west1-a -o json | jq '.data | {username}[],{password}[] | @base64d'
Query the cluster through the API endpoint:
curl -k -u <username>:<password> https://api-raas-us-west1-a.rec-us-west1-a.<EXTERNAL-IP>.nip.io/v1/cluster
Ex. curl -k -u [email protected]:<some password> https://api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io/v1/cluster
At this point, the Redis Enterprise Cluster named rec-us-west1-a is set up for Active-Active Geo Replication.
Now, deploy REC in "raas-us-east1-b" namespace of the GKE cluster in us-east1-b region:
./bundle.sh raas-us-east1-b
kubectl apply -f rec/rec-us-east1-b.yaml
Install Ingress Controller (Nginx) in "ingress-nginx" namespace of the GKE cluster in us-east1-b region:
kubectl apply -f ingress/nginx-ingress-controller.yaml
Wait a couple minutes, then retrieve EXTERNAL-IP of the ingress-controller:
kubectl get service/ingress-nginx-controller -n ingress-nginx
Apply the activeActive spec to rec-us-east1-b (Redis Enterprise Cluster): REC at rec-us-east1-b:
activeActive:
apiIngressUrl: api-raas-us-east1-b.rec-us-east1-b.<EXTERNAL-IP>.nip.io
dbIngressSuffix: -raas-us-east1-b.rec-us-east1-b.<EXTERNAL-IP>.nip.io
method: ingress
ingressAnnotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
kubernetes.io/ingress.class: "nginx"
You can validate that these were applied by describing the rec as follows:
kubectl get rec -n raas-us-east1-b -o json | jq '.items[].spec.activeActive'
The output should look like the following:
{
"apiIngressUrl": "api-raas-us-east1-b.rec-us-east1-b.34.75.99.216.nip.io",
"dbIngressSuffix": "-raas-us-east1-b.rec-us-east1-b.34.75.99.216.nip.io",
"ingressAnnotations": {
"kubernetes.io/ingress.class": "nginx",
"nginx.ingress.kubernetes.io/ssl-passthrough": "true"
},
"method": "ingress"
}
Check if ingress is created:
kubectl get ingress -n raas-us-east1-b
Grab creds for rec:
kubectl get secrets -n raas-us-east1-b rec-us-east1-b -o json | jq '.data | {username}[],{password}[] | @base64d'
Query the cluster thru the API endpoint:
curl -k -u <username>:<password> https://api-raas-us-east1-b.rec-us-east1-b.<EXTERNAL-IP>.nip.io/v1/cluster
Ex. curl -k -u [email protected]:<some password> https://api-raas-us-east1-b.rec-us-east1-b.34.75.99.216.nip.io/v1/cluster
Required Parameters The following parameters will be required to form the JSON payload to create the CRDB.
Here is an example when creating a CRDB with database name glau-crdb:
Parameter Name in REST API | Example value |
---|---|
name |
rec-us-west1-a.raas-us-west1-a.svc.cluster.local |
url |
https://api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io |
credentials |
username: [email protected] , password: something |
replication_endpoint |
glau-crdb-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io:443 |
replication_tls_sni |
glau-crdb-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io |
Create the JSON payload for CRDB creation request as in this example using the required parameters. Save the file as crdb.json
in your current working directory.
{
"default_db_config": {
"name": "<db_name>",
"replication": false,
"memory_size": 10240000,
"aof_policy": "appendfsync-every-sec",
"shards_count": 1
},
"instances": [
{
"cluster": {
"url": "<site_a_api_endpoint>",
"credentials": {
"username": "<site_a_username>",
"password": "<site_a_password>"
},
"name": "<site_a_rec_name/fqdn>",
"replication_endpoint": "<site_a_replication_endpoint>:443",
"replication_tls_sni": "<site_a_replication_endpoint>"
}
},
{
"cluster": {
"url": "<site_b_api_endpoint>",
"credentials": {
"username": "<site_b_username>",
"password": "<site_b_password>"
},
"name": "<site_b_rec_name/fqdn>",
"replication_endpoint": "<site_b_replication_endpoint>:443",
"replication_tls_sni": "<site_b_replication_endpoint>"
}
}
],
"name": "<db_name>",
"encryption": true,
"compression": 0
}
In this step you will make the Active-Active DB request to just one cluster member. Why just one? This request is coordinated among members: You request one member to initiate the coordination by including the list of, and credentials for, each Active-Active DB member.
Apply the following to the API endpoint at just one cluster API endpoint:
curl -k -u [email protected]:<some password> https://api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io/v1/crdbs -X POST -H 'Content-Type: application/json' -d @crdb.json
Note: curl
some users are having difficulty specifying the payload with the -d
argument. Please consult your curl manual or try postman.
You should see a reply from the API as in the following which indicates the payload was well formed and the request is being actioned:
{
"id": "9644de6c-1bef-4ebe-b010-3a4e6199138b",
"status": "queued"
}
Note Did you get something other than queued
as a response? Then proceed to the troubleshooting section of the document.
You can get the status of the above task by issuing a GET on /v1/crdbs_tasks/<id>
. Here is an example of a successful task:
$ curl -k -u [email protected]:<some password> https://api-raas-us-west1-a.rec-us-west1-a.34.105.40.1.nip.io/v1/crdb_tasks/9644de6c-1bef-4ebe-b010-3a4e6199138b
{
"crdb_guid": "805be1c6-0b27-47b1-8461-00b8465981c9",
"id": "9644de6c-1bef-4ebe-b010-3a4e6199138b",
"status": "finished"
}
Verify if a CRDB instance is created on the Redis Enterprise Cluster (rec-us-west1-a):
kubectl exec -it rec-us-west1-a-0 -n raas-us-west1-a -c redis-enterprise-node -- /bin/bash
Once you are inside the container:
rladmin status
You should see the database metadata like the following:
Verify if the corresponding CRDB instance is created on the Redis Enterprise Cluster (rec-us-east1-b):
kubectl exec -it rec-us-east1-b-0 -n raas-us-east1-b -c redis-enterprise-node -- /bin/bash
Once you are inside the container:
rladmin status
Appendix A - Retrieving connection parameters for the two CRDB instances: Grab the database endpoints for the CRDB instances:
curl -k -u [email protected]:<some password> https://api-raas-us-west1-a.rec-us-west1-a.<EXTERNAL-IP>.nip.io/v1/bdbs | jq '.[0].endpoints[0].dns_name'
curl -k -u [email protected]:<some password> https://api-raas-us-east1-b.rec-us-east1-b.<EXTERNAL-IP>.nip.io/v1/bdbs | jq '.[0].endpoints[0].dns_name'
Grab the database port:
curl -k -u [email protected]:<some password> https://api-raas-us-west1-a.rec-us-west1-a.<EXTERNAL-IP>.1.nip.io/v1/bdbs | jq '.[0].endpoints[0].port'
curl -k -u [email protected]:<some password> https://api-raas-us-east1-b.rec-us-east1-b.<EXTERNAL-IP>.1.nip.io/v1/bdbs | jq '.[0].endpoints[0].port'
Connect to the database:
kubectl exec -it rec-us-west1-a-0 -n raas-us-west1-a -c redis-enterprise-node -- /bin/bash
redis-cli -h <database endpoint> -p <database port>
kubectl exec -it rec-us-east1-b-0 -n raas-us-east1-b -c redis-enterprise-node -- /bin/bash
redis-cli -h <database endpoint> -p <database port>
Appendix B - Accessing REC CM UI: For the cluster in us-west1-a region: Update ingress-us-west1-a-web-ui.yaml for your deployment Then access the CM UI using the following URL along with the credentials for the REC in us-west1-a region::
https://web-ui-raas-us-east1-b.rec-us-east1-b.<EXTERNAL-IP>.nip.io
-
Symptom: API endpoint not reachable The API endpoint is not reachable from one cluster to the other.
-
Open a shell side a one of the Redis Enterprise cluster pods:
kubectl exec -it rec-us-west1-a-0 -c redis-enterprise-node -n raas-us-west1-a -- /bin/bash $ curl -ivk https://api-raas-us-east1-b.rec-us-east1-b.34.75.99.216.nip.io ... HTTP/1.1 401 UNAUTHORIZED ... WWW-Authenticate: ... realm="rec-us-east1-b.raas-us-east1-b.svc.cluster.local"
It is expected that you will get a "401" response but check that the returned "realm" reflects the remote cluster's FQDN as in Required Parameters:
name
. -
Perform the same step as above from the other site, to the former.
-
If one or both of these steps above do not result in "401" with the appropriate "realm" then investigate your Nginx ingress controller setup related to the REC API endpoint.
-
-
API response 400, bad request:
{ "detail": "None is not of type 'object'", "status": 400, "title": "Bad Request", "type": "about:blank" }
- Your payload is not being passed to the API or the payload is not valid JSON. Please Lint your JSON or try Postman with built-in JSON validate.
-
The Active-Active DB request was accepted and completed, but replication is not taking place. This is likely the case if either or both DB ingress routes are not working properly.