171 lines
8.5 KiB
Text
171 lines
8.5 KiB
Text
[[sticky-sessions]]
|
|
=== Sticky sessions
|
|
|
|
Typical cluster deployment consists of the load balancer (reverse proxy) and 2 or more {project_name} servers on private network. For performance purposes,
|
|
it may be useful if load balancer forwards all requests related to particular browser session to the same {project_name} backend node.
|
|
|
|
The reason is, that {project_name} is using infinispan distributed cache under the covers for save data related to current authentication session and user session.
|
|
The Infinispan distributed caches are configured with one owner by default. That means that particular session is saved just on one cluster node and the other nodes need
|
|
to lookup the session remotely if they want to access it.
|
|
|
|
For example if authentication session with ID `123` is saved in the infinispan cache on `node1`, and then `node2` needs to lookup this session,
|
|
it needs to send the request to `node1` over the network to return the particular session entity.
|
|
|
|
It is beneficial if particular session entity is always available locally, which can be done with the help of sticky sessions.
|
|
The workflow in the cluster environment with the public frontend load balancer and two backend {project_name} nodes can be like this:
|
|
|
|
* User sends initial request to see the {project_name} login screen
|
|
* This request is served by the frontend load balancer, which forwards it to some random node (eg. node1). Strictly said, the node doesn't need to be random,
|
|
but can be chosen according to some other criterias (client IP address etc). It all depends on the implementation and configuration of underlying load balancer (reverse proxy).
|
|
* {project_name} creates authentication session with random ID (eg. 123) and saves it to the Infinispan cache.
|
|
* Infinispan distributed cache assigns the primary owner of the session based on the hash of session ID.
|
|
See link:http://infinispan.org/docs/8.2.x/user_guide/user_guide.html#distribution_mode[Infinispan documentation] for more details around this.
|
|
Let's assume that infinispan assigned `node2` to be the owner of this session.
|
|
* {project_name} creates the cookie `AUTH_SESSION_ID` with the format like `<session-id>.<owner-node-id>` . In our example case, it will be `123.node2` .
|
|
* Response is returned to the user with the {project_name} login screen and the AUTH_SESSION_ID cookie in the browser
|
|
|
|
From this point, it is beneficial if load balancer forwards all the next requests to the `node2` as this is the node, who is owner of the authentication session with ID `123`
|
|
and hence Infinispan can lookup this session locally. After authentication is finished, the authentication session is converted to user session, which will be also saved on
|
|
`node2` because it has same ID `123` .
|
|
|
|
The sticky session is not mandatory for the cluster setup, however it is good for performance for the reasons mentioned above. You need to configure your loadbalancer to sticky
|
|
over the `AUTH_SESSION_ID` cookie. How exactly do this is dependent on your loadbalancer.
|
|
|
|
It is recommended on the {project_name} side to use the system property `jboss.node.name` during startup, with the value corresponding
|
|
to the name of your route. For example, `-Djboss.node.name=node1` will use `node1` to identify the route. This route will be used by
|
|
Infinispan caches and will be attached to the AUTH_SESSION_ID cookie when the node is the owner of the particular key. An example of the
|
|
start up command with this system property can be seen in <<_example-setup-with-mod-cluster,Mod Cluster Example>>.
|
|
|
|
Typically, the route name be the same name as your backend host, but it is not necessary. You can use a different route name,
|
|
for example if you want to hide the host name of your {project_name} server inside your private network.
|
|
|
|
==== Disable adding the route
|
|
|
|
Some load balancers can be configured to add the route information by themselves instead of relying on the back end {project_name} node.
|
|
However, as described above, adding the route by the {project_name} is recommended. This is because when done this way performance improves,
|
|
since {project_name} is aware of the entity that is the owner of particular session and can route to that node, which is not necessarily the local node.
|
|
|
|
You are permitted to disable adding route information to the AUTH_SESSION_ID cookie by {project_name}, if you prefer, by adding the following
|
|
into your `RHSSO_HOME/standalone/configuration/standalone-ha.xml` file in the {project_name} subsystem configuration:
|
|
|
|
[source,xml]
|
|
----
|
|
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
|
|
...
|
|
<spi name="stickySessionEncoder">
|
|
<provider name="infinispan" enabled="true">
|
|
<properties>
|
|
<property name="shouldAttachRoute" value="false"/>
|
|
</properties>
|
|
</provider>
|
|
</spi>
|
|
|
|
</subsystem>
|
|
----
|
|
|
|
[[_example-setup-with-mod-cluster]]
|
|
==== Example cluster setup with mod_cluster
|
|
|
|
In the example, we will use link:http://mod-cluster.jboss.org/[Mod Cluster] as load balancer. One of the key features of mod cluster is, that there is not much
|
|
configuration on the load balancer side. Instead it requires support on the backend node side. Backend nodes communicate with the load balancer through the
|
|
dedicated protocol called MCMP and they notify loadbalancer about various events (eg. node joined or left cluster, new application was deployed etc).
|
|
|
|
Example setup will consist of the one {appserver_name} {appserver_version} load balancer node and two {project_name} nodes.
|
|
|
|
Clustering example require MULTICAST to be enabled on machine's loopback network interface. This can be done by running the following commands under root privileges (on linux):
|
|
|
|
[source]
|
|
----
|
|
route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
|
|
ifconfig lo multicast
|
|
----
|
|
|
|
|
|
===== Load Balancer Configuration
|
|
|
|
Unzip the {appserver_name} {appserver_version} server somewhere. Assumption is location `EAP_LB`
|
|
|
|
Edit `EAP_LB/standalone/configuration/standalone.xml` file. In the undertow subsystem add the mod_cluster configuration under filters like this:
|
|
|
|
[source,xml]
|
|
----
|
|
<subsystem xmlns="urn:jboss:domain:undertow:4.0">
|
|
...
|
|
<filters>
|
|
...
|
|
<mod-cluster name="modcluster" advertise-socket-binding="modcluster"
|
|
advertise-frequency="${modcluster.advertise-frequency:2000}"
|
|
management-socket-binding="http" enable-http2="true"/>
|
|
</filters>
|
|
----
|
|
|
|
and `filter-ref` under `default-host` like this:
|
|
|
|
[source,xml]
|
|
----
|
|
<host name="default-host" alias="localhost">
|
|
...
|
|
<filter-ref name="modcluster"/>
|
|
</host>
|
|
----
|
|
|
|
Then under `socket-binding-group` add this group:
|
|
|
|
[source,xml]
|
|
----
|
|
<socket-binding name="modcluster" port="0"
|
|
multicast-address="${jboss.modcluster.multicast.address:224.0.1.105}"
|
|
multicast-port="23364"/>
|
|
----
|
|
|
|
Save the file and run the server:
|
|
|
|
[source]
|
|
----
|
|
cd $WILDFLY_LB/bin
|
|
./standalone.sh
|
|
----
|
|
|
|
|
|
===== Backend node configuration
|
|
|
|
Unzip the {project_name} server distribution to some location. Assuming location is `RHSSO_NODE1` .
|
|
|
|
Edit `RHSSO_NODE1/standalone/configuration/standalone-ha.xml` and configure datasource against the shared database.
|
|
See <<_rdbms-setup-checklist, Database chapter >> for more details.
|
|
|
|
In the undertow subsystem, add the `session-config` under the `servlet-container` element:
|
|
|
|
[source,xml]
|
|
----
|
|
<servlet-container name="default">
|
|
<session-cookie name="AUTH_SESSION_ID" http-only="true" />
|
|
...
|
|
</servlet-container>
|
|
----
|
|
|
|
Then you can configure `proxy-address-forwarding` as described in the chapter <<_setting-up-a-load-balancer-or-proxy, Load Balancer >> .
|
|
Note that mod_cluster uses AJP connector by default, so you need to configure that one.
|
|
|
|
That's all as mod_cluster is already configured.
|
|
|
|
The node name of the {project_name} can be detected automatically based on the hostname of current server. However for more fine grained control,
|
|
it is recommended to use system property `jboss.node.name` to specify the node name directly. It is especially useful in case that you test with 2 backend nodes on
|
|
same physical server etc. So you can run the startup command like this:
|
|
|
|
[source]
|
|
----
|
|
cd $RHSSO_NODE1
|
|
./standalone.sh -c standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=node1
|
|
----
|
|
|
|
Configure the second backend server in same way and run with different port offset and node name.
|
|
|
|
[source]
|
|
----
|
|
cd $RHSSO_NODE2
|
|
./standalone.sh -c standalone-ha.xml -Djboss.socket.binding.port-offset=200 -Djboss.node.name=node2
|
|
----
|
|
|
|
Access the server on `http://localhost:8080/auth` . Creation of admin user is possible just from local address and without load balancer (proxy) access,
|
|
so you first need to access backend node directly on `http://localhost:8180/auth` to create admin user.
|