I am testing how to move our applications to docker. In order to take advantage of k8s, we want to have session replication.

First, I am testing with Tomcat Cluster:

Dockerfile

# cat Dockerfile 
FROM tomcat:7.0.94-jre7-alpine
MAINTAINER q1zhang@odu.edu

ADD index.jsp /usr/local/tomcat/webapps/ROOT
ADD web.xml /usr/local/tomcat/conf
ADD server.xml /usr/local/tomcat/conf
ADD content.xml /usr/local/tomcat/conf
ADD rootweb.xml /usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml
ADD pre.sh /usr/local/tomcat

expose 8080
WORKDIR /usr/local/tomcat

ENTRYPOINT bash ./pre.sh && catalina.sh run

index.jsp, used for testing, to display current hostname and session id.

# cat index.jsp 
<%@ page language="java" %>
<html>
  <head><title>Test</title></head>
  <body>
    <h1><font color="red">#HOSTNAME#</font></h1>
      <table align="centre" border="1">
      <tr>
        <td>Session ID</td>
        <% session.setAttribute("magedu.com","magedu.com"); %>
        <td><%= session.getId() %></td>
      </tr>
      <tr>
        <td>Created on</td>
        <td><%= session.getCreationTime() %></td>
      </tr>
    </table>
  </body>
</html>

pre.sh, used to change some configuration files before start tomcat.

# cat pre.sh 
sed -i "s/#HOSTNAME#/`hostname`/g" /usr/local/tomcat/webapps/ROOT/index.jsp
sed -i "s/#HOSTNAME#/localhost/g" /usr/local/tomcat/conf/server.xml
sed -i "s/#IPADDRESS#/`ifconfig eth0|grep "inet addr"|awk '{print $2}'|sed 's/addr://g'`/g" /usr/local/tomcat/conf/server.xml

server.xml, copy from the image, /usr/local/tomcat/conf, and add the configuration for the cluster.

      <Host name="#HOSTNAME#"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="#HOSTNAME#_access_log." suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
        <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                channelSendOptions="8">

        <Manager className="org.apache.catalina.ha.session.DeltaManager"
                expireSessionsOnShutdown="false"
                notifyListenersOnReplication="true"/>

        <Channel className="org.apache.catalina.tribes.group.GroupChannel">
        <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="228.0.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                        address="#IPADDRESS#" 
                        port="4000"
                        autoBind="100"
                        selectorTimeout="5000"
                        maxThreads="6"/>

        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
        <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
        </Sender>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
        </Channel>
        <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                filter=""/>
        <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

        <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                tempDir="/tmp/war-temp/"
                deployDir="/tmp/war-deploy/"
                watchDir="/tmp/war-listen/"
                watchEnabled="false"/>

        <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
        <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>
      </Host>

web.xml, copy from the image, /usr/local/tomcat/conf, and add <distributable/>

web.xml
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <distributable/>
</web-app>

rootweb.xml, copy from the image, /usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml, and add <distributable/>
rootweb.xml
  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
  <distributable/> 
</web-app>

content.xml, copy from the image, /usr/local/tomcat/conf, and set distributable to true.

<Context distributable="true">

Create the image and run 2 instances.

# docker build -t myapp:v2 .
# docker run --name myapp1 -it --rm -p 8888:8080 myapp:v2 &
# docker run --name myapp2 -it --rm -p 8889:8080 myapp:v2 &

In the nginx:

# cat /etc/nginx/conf.d/proxy.conf
upstream web{
  server 83.16.16.73:8888 weight=1;
  server 83.16.16.73:8889 weight=1;
}

server {
  listen 80 default_server;
  index index.jsp index.html;
  location / {
    proxy_pass http://web;
  }
}

First access:

Refresh, then, you can see that session is on the another node, with the same session id