I have create a monolithic application, using JHipster, and I want to run several replicas of it on a GCP Kubernetes Engine cluster.
As I would like to share data between replicas (or even services that are on that cluster), I've decided to use the built-int Hazelcast cache, but instead of using the recommended Eureka discovery-client I want to use K8 dns-discovery add-on.
So I've added to my pom.xml:
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-kubernetes</artifactId>
<version>1.1.0</version>
</dependency>
And played a bit with the generated CacheConfiguration.java file to support the hazelcast-kubernetes configuration (based on their github repo):
@Bean
public HazelcastInstance hazelcastInstance(JHipsterProperties jHipsterProperties) {
log.debug("Configuring Hazelcast");
HazelcastInstance hazelCastInstance = Hazelcast.getHazelcastInstanceByName("hazelcastK8");
if (hazelCastInstance != null) {
log.debug("Hazelcast already initialized");
return hazelCastInstance;
}
Config config = new Config();
config.setInstanceName("hazelcastK8");
config.getNetworkConfig().setPort(5701);
config.getNetworkConfig().setPortAutoIncrement(true);
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(false);
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
System.setProperty("hazelcast.local.localAddress", "127.0.0.1");
config.getNetworkConfig().getJoin().getAwsConfig().setEnabled(false);
} else { // In production we want to use hazelcast k8's dns discovery service
log.info("Configuring hazelcast DNS discovery by K8");
DiscoveryStrategyConfig strategyConfig =
new DiscoveryStrategyConfig(
new HazelcastKubernetesDiscoveryStrategyFactory()
);
// Default namespace
strategyConfig.addProperty("service-dns", "hazelcastk8.default.svc.cluster.local");
strategyConfig.addProperty("service-dns-timeout", 10);
config.getNetworkConfig().getJoin().getDiscoveryConfig().addDiscoveryStrategyConfig(strategyConfig);
}
config.getMapConfigs().put("default", initializeDefaultMapConfig());
config.setManagementCenterConfig(initializeDefaultManagementCenterConfig(jHipsterProperties));
config.getMapConfigs().put("com.company.distributed.domain.*", initializeDomainMapConfig(jHipsterProperties));
return Hazelcast.newHazelcastInstance(config);
}
To check if the data-grid is working as expected, I've written a simple scheduled task that prints the current value of an atomic-long:
@Service
@Profile(JHipsterConstants.SPRING_PROFILE_PRODUCTION)
public class FetchHazelcastClusterSequence {
private final HazelcastInstance hazelcastInstance;
private static final Logger log = LoggerFactory.getLogger(FetchHazelcastClusterSequence.class);
public FetchHazelcastClusterSequence(HazelcastInstance hazelcastInstance) {
this.hazelcastInstance = hazelcastInstance;
}
@Timed
@Scheduled(fixedDelay = 5000)
public void start() {
log.info("Fetching cluster sequence, the current value is: {}", this.hazelcastInstance.getAtomicLong("sequence").getAndIncrement());
}
}
But as I've started the application, with a default configuration of 3 replicas, 3 pods were created, but each pod log showed as if it is the only member in the cluster and thus printing the same values of the "sequence".
Am I missing some configuration?
@lior-ziv, your config is missing one vital config param:
1 - When using SPRING_PROFILE_DEVELOPMENT
profile, you don't have any discovery mechanism enabled. You need to enable TCP-IP
discovery otherwise all members will start a single node cluster.
2 - When using SPRING_PROFILE_PRODUCTION
profile, you have Kubernetes discovery plugin but you didn't enable discovery at all. This is why it also create single member cluster each time you start a new instance.
Correct config should be like this:
//Move this like outside of if block since it'll be disabled for both cases.
config.getNetworkConfig().getJoin().getAwsConfig().setEnabled(false);
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
config.getNetworkConfig().getJoin().getTcpIpConfig().addMember("127.0.0.1");`
} else {
// In production we want to use hazelcast k8's dns discovery service
System.setProperty("hazelcast.discovery.enabled", "true");
log.info("Configuring hazelcast DNS discovery by K8");
DiscoveryStrategyConfig strategyConfig =
new DiscoveryStrategyConfig(
new HazelcastKubernetesDiscoveryStrategyFactory()
);
// Default namespace
strategyConfig.addProperty("service-dns", "hazelcastk8.default.svc.cluster.local");
strategyConfig.addProperty("service-dns-timeout", 10);
config.getNetworkConfig().getJoin().getDiscoveryConfig().addDiscoveryStrategyConfig(strategyConfig);
}