Replicate Data between Two Hazelcast Clusters with Hazelcast Platform Operator
Learn how to keep data in sync across two Hazelcast clusters.
Context
In this tutorial, you’ll do the following:
-
Deploy two Hazelcast clusters.
-
Create two Hazelcast map configurations on one of the clusters.
-
Synchronize map data between the two Hazelcast clusters.
Before you Begin
Before starting this tutorial, make sure that you have the following:
-
A running Kubernetes cluster
-
The Kubernetes command-line tool, kubectl
-
A deployed Hazelcast Platform Operator
Step 1. Start the Hazelcast Cluster
-
Clone the examples repository
git clone https://github.com/hazelcast-guides/hazelcast-platform-operator-wan-replication.git cd hazelcast-platform-operator-wan-replication
The sample code(excluding CLC) for this tutorial is in the
clients
directory. -
Create a secret with your Hazelcast Enterprise License.
kubectl create secret generic hazelcast-license-key --from-literal=license-key=<hz-license-key>
-
Create the Hazelcast clusters.
-
Run the following command to create the first cluster.
cat <<EOF | kubectl apply -f - apiVersion: hazelcast.com/v1alpha1 kind: Hazelcast metadata: name: hazelcast-first spec: licenseKeySecret: hazelcast-license-key exposeExternally: type: Unisocket discoveryServiceType: LoadBalancer EOF
-
Run the following command to create the second cluster.
cat <<EOF | kubectl apply -f - apiVersion: hazelcast.com/v1alpha1 kind: Hazelcast metadata: name: hazelcast-second spec: licenseKeySecret: hazelcast-license-key exposeExternally: type: Unisocket discoveryServiceType: LoadBalancer EOF
-
-
Check the status of the clusters to make sure that both clusters are running.
kubectl get hazelcast
NAME STATUS MEMBERS EXTERNAL-ADDRESSES WAN-ADDRESSES hazelcast-first Running 3/3 35.193.112.105:5701 35.193.112.105:5710 hazelcast-second Running 3/3 34.29.242.190:5701 34.29.242.190:5710
-
Find the addresses of the clusters.
kubectl get service hazelcast-first hazelcast-second
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hazelcast-first LoadBalancer 10.96.229.87 172.18.0.222 5701:30282/TCP,5702:31120/TCP,8081:30985/TCP,5710:30009/TCP 1m hazelcast-second LoadBalancer 10.96.34.62 172.18.0.207 5701:31233/TCP,5702:31065/TCP,8081:30897/TCP,5710:31353/TCP 1m
The field
EXTERNAL-IP
is the address of your Hazelcast cluster.
Step 2. Create a WAN Replication Configuration
-
Create two maps on the first cluster. In this example, the following maps are created:
-
map-1
-
map-2
.cat <<EOF | kubectl apply -f - apiVersion: hazelcast.com/v1alpha1 kind: Map metadata: name: map-1 spec: hazelcastResourceName: hazelcast-first --- apiVersion: hazelcast.com/v1alpha1 kind: Map metadata: name: map-2 spec: hazelcastResourceName: hazelcast-first EOF
-
-
Create the configuration for WAN replication:
-
Use the first cluster as the source cluster by adding its name as a resource in the WAN Replication configuration. Adding the cluster name as a resource starts WAN replication for both the maps that you created earlier.
-
Add the second cluster as the target cluster to receive the WAN Replication events.
Run the following command to apply the configuration.
cat <<EOF | kubectl apply -f - apiVersion: hazelcast.com/v1alpha1 kind: WanReplication metadata: name: wan-replication spec: resources: - name: hazelcast-first kind: Hazelcast targetClusterName: dev endpoints: "<SECOND-CLUSTER-EXTERNAL-IP>" EOF
-
Step 3. Put Entries to the Maps on the First Cluster
In this step, you’ll fill the maps on the first, source cluster.
-
Configure the Hazelcast client to connect to the first cluster, using its address.
Before using CLC, it should be installed in your system. Check the installation instructions for CLC: Installing the Hazelcast CLC. Run the following command for adding the first cluster config to the CLC.
clc config add hz-1 cluster.name=dev cluster.address=<FIRST-CLUSTER-EXTERNAL-IP>
ClientConfig config = new ClientConfig(); config.getNetworkConfig().addAddress("<FIRST-CLUSTER-EXTERNAL-IP>");
const { Client } = require('hazelcast-client'); const clientConfig = { network: { clusterMembers: [ '<FIRST-CLUSTER-EXTERNAL-IP>' ] } }; const client = await Client.newHazelcastClient(clientConfig);
import ( "log" "github.com/hazelcast/hazelcast-go-client" ) func main() { config := hazelcast.Config{} cc := &config.Cluster cc.Network.SetAddresses("<FIRST-CLUSTER-EXTERNAL-IP>") ctx := context.TODO() client, err := hazelcast.StartNewClientWithConfig(ctx, config) if err != nil { panic(err) } }
import logging import hazelcast logging.basicConfig(level=logging.INFO) client = hazelcast.HazelcastClient( cluster_members=["<FIRST-CLUSTER-EXTERNAL-IP>"], use_public_ip=True, )
var options = new HazelcastOptionsBuilder() .With(args) .With((configuration, options) => { options.Networking.UsePublicAddresses = true; options.Networking.SmartRouting = false; options.Networking.Addresses.Add("<FIRST-CLUSTER-EXTERNAL-IP>"); }) .Build();
-
Start to fill the maps.
Run the following command for each map, using the map name as an argument to fill each map with entries. Use the map names
map-1
andmap-2
.for i in {1..10}; do clc -c hz-1 map set --name <MAP-NAME> key-$i value-$i; done
Run the following command for each map to check if the sizes are expected.
clc -c hz-1 map size --name <MAP-NAME>
Start the application for each map, using the map name as an argument to fill each map with random entries. Use the map names
map-1
andmap-2
.cd clients/java mvn package java -jar target/*jar-with-dependencies*.jar fill <MAP-NAME>
You should see the following output.
Successful connection! Starting to fill the map (<MAP-NAME>) with random entries. Current map size: 2 Current map size: 3 Current map size: 4 .... ....
Start the application for each map, using the map name as an argument to fill each map with random entries. Use the map names
map-1
andmap-2
.cd clients/nodejs npm install npm start fill <MAP-NAME>
You should see the following output.
Successful connection! Starting to fill the map (<MAP-NAME>) with random entries. Current map size: 2 Current map size: 3 Current map size: 4 .... ....
Start the application for each map, using the map name as an argument to fill each map with random entries. Use the map names
map-1
andmap-2
.cd clients/go go run main.go fill <MAP-NAME>
You should see the following output.
Successful connection! Starting to fill the map (<MAP-NAME>) with random entries. Current map size: 2 Current map size: 3 Current map size: 4 .... ....
Start the application for each map, using the map name as an argument to fill each map with random entries. Use the map names
map-1
andmap-2
.cd clients/python pip install -r requirements.txt python main.py fill <MAP-NAME>
You should see the following output.
Successful connection! Starting to fill the map (<MAP-NAME>) with random entries. Current map size: 2 Current map size: 3 Current map size: 4 .... ....
Start the application for each map, using the map name as an argument to fill each map with random entries. Use the map names
map-1
andmap-2
.cd clients/dotnet dotnet build dotnet run fill <MAP-NAME>
You should see the following output.
Successful connection! Starting to fill the map (<MAP-NAME>) with random entries. Current map size: 2 Current map size: 3 Current map size: 4 .... ....
Step 3. Verify the Replication of Map Entries
In this step, you’ll check the sizes of the maps on the second, target cluster to make sure that WAN replication events have been received.
-
Configure the Hazelcast client to connect to the second cluster, as you did in Configure the Hazelcast Client.
Run the following command for adding the second cluster config to the CLC.
clc config add hz-2 cluster.name=dev cluster.address=<SECOND-CLUSTER-EXTERNAL-IP>
ClientConfig config = new ClientConfig(); config.getNetworkConfig().addAddress("<SECOND-CLUSTER-EXTERNAL-IP>");
const { Client } = require('hazelcast-client'); const clientConfig = { network: { clusterMembers: [ '<SECOND-CLUSTER-EXTERNAL-IP>' ] } }; const client = await Client.newHazelcastClient(clientConfig);
import ( "log" "github.com/hazelcast/hazelcast-go-client" ) func main() { config := hazelcast.Config{} cc := &config.Cluster cc.Network.SetAddresses("<SECOND-CLUSTER-EXTERNAL-IP>") ctx := context.TODO() client, err := hazelcast.StartNewClientWithConfig(ctx, config) if err != nil { panic(err) } }
import logging import hazelcast logging.basicConfig(level=logging.INFO) client = hazelcast.HazelcastClient( cluster_members=["<SECOND-CLUSTER-EXTERNAL-IP>"], use_public_ip=True, )
var options = new HazelcastOptionsBuilder() .With(args) .With((configuration, options) => { options.Networking.UsePublicAddresses = true; options.Networking.SmartRouting = false; options.Networking.Addresses.Add("<SECOND-CLUSTER-EXTERNAL-IP>"); }) .Build();
-
Start the application for each map, using the map name as an argument to check the map size, and to check that WAN replication was successful. Use the map names
map-1
andmap-2
.clc -c hz-2 map size --name <MAP-NAME>
cd clients/java mvn package java -jar target/*jar-with-dependencies*.jar size <MAP-NAME>
You should see the following output:
Successful connection! Current map (<MAP-NAME>) size: 12
cd clients/nodejs npm install npm start size <MAP-NAME>
You should see the following output:
Successful connection! Current map (<MAP-NAME>) size: 12
cd clients/go go run main.go size <MAP-NAME>
You should see the following output:
Successful connection! Current map (<MAP-NAME>) size: 12
cd clients/python pip install -r requirements.txt python main.py size <MAP-NAME>
You should see the following output:
Successful connection! Current map (<MAP-NAME>) size: 12
cd clients/dotnet dotnet build dotnet run size <MAP-NAME>
You should see the following output:
Successful connection! Current map (<MAP-NAME>) size: 12