Chapter 19. Example: Deploying a Standalone Server to a Cluster (JBoss EAP 6)

There is a sample script in the cliRoot/rhq-remoting-cli-4.9.0.JON320GA/samples directory the defines a series of functions that allow a JBoss EAP 6 standalone server to be added to an existing cluster.
JBoss EAP 6 has the idea of domains, which can be subdivided into server groups which share configuration. Server groups allows multiple server instances to have consistent, uniform configuration settings, to share profiles, and to deploy the same applications through a central command point.
However, JBoss EAP 6 still has the idea of a classic standalone server, a single JBoss instance that is unaffiliated with a domain or server group. A standalone server can be joined in a cluster, a loose association of standalone servers that work together to distribute the work load, either for load balancing or high availability. Unlike a domain, a cluster does not manage configuration or content.
JBoss ON provides the centralized management over configuration and content for standalone servers, introducing some of the ease of maintenance that EAP 6 domains offer. The cliRoot/rhq-remoting-cli-4.9.0.JON320GA/samples/modules/jbossas.js file defines some useful functions that simplify identifying cluster servers, deployed content, and relevant configuration settings.
The bulk of the functions defined are private and are well-documented within the cliRoot/rhq-remoting-cli-4.9.0.JON320GA/samples/modules/jbossas.js file, so they are out of the scope of this example. The main purpose of this is example is to review the two public functions which perform two distinct cluster management tasks:
  • addToCluster
  • copyDeployments

19.1. Adding a Standalone EAP 6 Server to a Cluster

A cluster is defined by servers which use the same multicast properties on the same network. If the servers are configured with the same settings, they all automatically associate with each other in a cluster.
There are three configuration properties for a standalone server within a cluster:
  • An identifying name for the JBoss instance to use within the cluster (the node-name)
  • Multicast settings, including the multicast port, a UDP port, and multicast address
  • Socket-binding group information used by mod_cluster
A cluster does not directly manage either configuration properties or deployed content. However, if two EAP 6 standalone servers are in the JBoss ON inventory, then JBoss ON can work somewhat as a bridge, comparing the configuration and content deployments between servers and copying between them. That comparison is what the addToCluster function uses to add a standalone server to a cluster. It uses the configuration properties in an existing cluster member and copies them over to the standalone server.
Actually running the script requires the name of the standalone server, the name of an existing cluster member, a node name for the standalone server, and a boolean that sets whether to copy over the deployments from the existing cluster to the new member.
Assuming that you already know resource IDs of the standalone server and an existing cluster member:
[root@server bin]# ./rhq-cli.sh -u rhqadmin -p rhqadmin
rhqadmin@localhost:7080$ var newAs7Resource = ProxyFactory.getResource(10381) 
rhqadmin@localhost:7080$ var existingClusterMemberResource = ProxyFactory.getResource(10577) 
rhqadmin@localhost:7080$ var newNodeName = jbas7-standalone1
rhqadmin@localhost:7080$ addToCluster(newAs7Resource, newNodeName, existingClusterMemberResource, true)
The addToCluster function makes some assumptions that the cache configuration between the two servers is compatible, both for concrete caches and the cache containers for individual subsystems.
The script runs through a few steps to copy the configuration from the cluster to the standalone server:
  1. It checks the plug-in connection properties in the cluster server and compares them to the plug-in connection properties in the standalone server. If necessary, it copies over the plug-in configuration from the cluster server and restarts the standalone server, loading the new configuration.
  2. It checks the given node name for the standalone server. If necessary, it changes the default node name to the one passed with the function.
  3. It then compares the socket-binding settings for the cluster and standalone servers. If necessary, it copies over the socket-binding configuration for the jgroups, messaging, and mod_cluster bindings from the cluster server and restarts the standalone server.
  4. If set, then the script copies the deployments from the cluster server to the standalone server and restarts the standalone server.
The first part of the function pulls the plug-in configuration (defined by the private function _getClusterSignificantConfig) for the cluster and then for the standalone server.
function addToCluster(newAs7Resource, newNodeName, existingClusterMemberResource, copyDeployments) {
    println("Reading config of the existing cluster member");
    var clusterConfig = _getClusterSignificantConfig(existingClusterMemberResource);

    println("Reading config of the new member");
    var memberConfig = _getClusterSignificantConfig(newAs7Resource);

    var memberResourceConfiguration = newAs7Resource.resourceConfiguration;
If the configuration properties are different, then the script copies over the new plug-in configuration and restarts the standalone server to load the new connection settings.
    if (memberConfig['config'] != clusterConfig['config']) {
        println("The configurations of the servers differ.\n" +
            "The new cluster member's configuration will be changed to match the configuration of the existing member.");

        //switch to the same configuration
        var pluginConfig = newAs7Resource.pluginConfiguration;
        pluginConfig.getSimple('config').setValue(clusterConfig['config']);
        newAs7Resource.updatePluginConfiguration(pluginConfig);

        //we need to restart straight away so that we see the changes to the
        //rest of the configuration caused by the change of current config.
        println("Restarting the new cluster member to switch it to the new configuration.");
        newAs7Resource.restart();

        //refresh the resource
        newAs7Resource = ProxyFactory.getResource(newAs7Resource.id);

        //refresh the cluster specific config after the restart with the new
        //config
        memberConfig = _getClusterSignificantConfig(newAs7Resource);
        memberResourceConfiguration = newAs7Resource.resourceConfiguration;
    }
It then applies the node name that was given with the script, if it is different than the one set by default.
    //now check what's the node name we see
    if (memberConfig['node-name'] != newNodeName) {
        println("Updating the node name of the new cluster member from '" + memberConfig['node-name'] + "' to '" + newNodeName + "'");
        _updateNodeName(memberResourceConfiguration, newNodeName);
        newAs7Resource.updateResourceConfiguration(memberResourceConfiguration);
     }
The next configuration area for the cluster is the socket-binding settings for important subsystems, jgroups, messaging, and mod_cluster.
    //now apply the socket binding changes for jgroups and other cluster
    //significant subsystems
    //first find the socket binding group config in the new member
    for(i in newAs7Resource.children) {
        var child = newAs7Resource.children[i];
        if (child.resourceType.name == 'SocketBindingGroup' &&
                child.resourceType.plugin == 'jboss-as-7') {
			

            println("Updating socket bindings of jgroups, messaging and modcluster subsystems");

            var portOffset = javascriptString(child.resourceConfiguration.getSimpleValue('port-offset', '0'));
            var clusterMemberPortOffset = clusterConfig['port-offset'];

            var newConfig = child.resourceConfiguration.deepCopy(false);

            _updateSocketBindings(newConfig, portOffset, clusterMemberPortOffset, clusterConfig['jgroups']);
            _updateSocketBindings(newConfig, portOffset, clusterMemberPortOffset, clusterConfig['messaging']);
            _updateSocketBindings(newConfig, portOffset, clusterMemberPortOffset, clusterConfig['modcluster']);

            child.updateResourceConfiguration(newConfig);
        }
    }

    println("Restarting the new member for the new socket bindings to take effect.");
    newAs7Resource.restart();
Although not strictly part of the cluster configuration, part of what JBoss ON can do is compare other parts of the resource setup, like deployed applications. Synchronizing the deployed applications between one server and another, even standalone instances, helps maintain consistency, and this can be done conveniently at the time that a server is added to a cluster simply by syncing the given cluster server's deployments.
    if (copyDeployments) {
        println("Copying the deployments to the new cluster member...");
        copyDeployments(existingClusterMemberResource, newAs7Resource);

        println("Restarting the new cluster member.");
        newAs7Resource.restart();
    }
}