[Window] apache-tomcat에서 로드 밸런싱(Load balancing)하는 방법과 세션 클러스터링(세션 공유)


Development note/Window  2020. 1. 28. 09:00

안녕하세요. 명월입니다.

 

이 글은 apache-tomcat에서 로드 밸런싱(Load balancing)하는 방법과 세션 클러스터링(세션 공유)에 대한 글입니다.

 

이전에 제가 apache에서 tomcat을 연동하는 방법에 대해 설명한 적이 있습니다.

링크 - [CentOS] apache-tomcat 연동하기

 

이 때는 apache-tomcat의 1:1의 연동이였는데 이번에는 apache에서 여러대의 tomcat를 연결하여 트래픽을 분산시키는 방법입니다. 운영 중인 웹 사이트에 접속자가 많아지기 시작하면 첫번째로 구성해 볼 수 있는게 이 apache-tomcat의 분산입니다.

동적 웹 사이트라고 하면 의외로 이 web servlet에서 처리 트래픽이 많이 늘어납니다. 왜냐하면 servlet에서 데이터 베이스도 접속해야 하고, 여러 설정 파일을 읽어야하고, 복잡한 사양일 수로 이 servlet이 하는 일이 꽤 많습니다.

이걸 여러 대의 서버로 운영을 하면서 apache로 트래픽을 묶어서 로드 밸런싱, 즉 트래픽에 맞게 servlet을 분산하면 사이트가 트래픽에 의해 느려지는 건 조금은 극복할 수 있지 않을까 싶네요.

apache 단계에서도 트래픽이 많아지면 DNS 단계에서 apache 분산을 해야 하겠네요.. 저도 거기까지는 아직 작업해 본 경험이 없어서 정확하게 말하기가 어렵습니다.

 

apache에서 mod_jk로 연결하기 위해서는 workers.properties를 수정해야 합니다.

# work 리스트
worker.list=router,jkstatus
# apache에 연결되어 있는 mod_jk에 대한 상태(로드 밸런싱 표시)
worker.jkstatus.type=status
# type lb는 로드 밸런싱이라는 뜻이다.
worker.router.type=lb
# 동일한 sessionID일 경우, 한쪽 tomcat에만 지속적으로 관리하여야 하는 경우.
worker.router.sticky_session=true
# 로드 밸런싱 리스트
worker.router.balance_workers=worker1,worker2	
# 1번째 로드 벨런싱 톰켓
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8109
# 처리 할당 비율, 즉, 2번째에 1이라고 설정을 했기 때문에 1:1 비율로 로드 벨런싱이 이루어진다.
worker.worker1.lbfactor=1	
# 2번째 로드 벨런싱 톰켓
worker.worker2.type=ajp13
worker.worker2.host=localhost
worker.worker2.port=8209
worker.worker2.lbfactor=1

httpd.conf에서는 worker list를 설정하면 됩니다.

LoadModule jk_module modules/mod_jk.so
<IfModule mod_jk.c>	
  JkWorkersFile conf/workers.properties
  JkLogFile logs/mod_jk.log
  JkLogLevel info
  JkLogStampFormat "[%y %m %d %H:%M:%S] "
</IfModule>
<VirtualHost *:80>
<!-- 로드 밸런싱 할 router 설정 -->
JkMount /* router
<!-- mod_jk 상태 페이지 -->
JkMount /jkmanager/* jkstatus
ServerName localhost
</VirtualHost>
<!-- 상태 페이지는 127.0.0.1로 접속할 경우만 허용한다. -->
<Location /jkmanager/>
  JkMount statusmanager
  Require ip 127.0.0.1
</Location>

위 예제를 보면 jkmanager를 별도로 설정했습니다. jkmanager는 apache - tomcat을 연결할 때의 로드 벨런싱 상태를 확인할 수 있는 페이지입니다. apache-tomcat에서 여러대의 서버를 관리할 때 제대로 연결이 되는 지 확인할 수 있는 페이지입니다.

만약 한쪽의 tomcat이 문제가 생기면 상태 페이지에서 그 결과를 알려줍니다.

여기까지 설정이 되면 로드 밸런싱은 완료가 됩니다. 그러나 실제 웹 서버 운영을 하게 되면 로드 밸런싱 설정만 할 경우에 세션이 공유가 되지 않기 때문에 이상한 현상이 발생합니다. 대표적으로 갑자기 로그 아웃이 되는 경우가 되겠네요.

왜나하면 tomcat1로 해서 로그인을 했습니다. tomcat1에서는 로그인 정보를 session에 넣어서 session 확인으로 로그인 여부를 체크할 것입니다.

그러나 tomcat2에서는 로그인 세션이 없기 때문에, tomcat1에서 tomcat2로 변경이 된다고 하면 세션이 서로 다르기 때문에 로그 아웃이 되어 버리는 현상이 발생하는 것입니다.

 

그래서 tomcat1과 tomcat2의 세션을 공유해야 하는 데 그것이 세션 클러스터링입니다.

tomcat의 예전 버젼에서는 세션 클러스터링 설정이 꽤 복잡한 것 같습니다. 그러나 저는 tomcat 9로 확인했습니다만 Cluster가 주석되어 있는 것을 해제만 하는 것으로 세션 클러스터링이 됩니다.

...
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
...

그리고 각 web.xml에 태그 요소를 하나 추가합니다.

...
<distributable/>
...

저는 테스트를 위해 tomcat1의 index 파일에는 server1이라고 표시하고 tomcat2의 index 파일에는 server2라고 표시했습니다.

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
@WebServlet("/Index")
public class Index extends HttpServlet {
  private static final long serialVersionUID = 1L;
 
  public Index() {
    // TODO Auto-generated constructor stub
  }
 
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpSession s = request.getSession();
    // query string에 session이란 값을 받는다.
    String session = request.getParameter("session");
    // session이 null이면 그대로 통과
    if (session != null && session.length() > 0) {
      // session 파라미터가 clear면 세션을 지운다.
      if ("clear".equals(session)) {
        s.setAttribute("session", null);
      } else {
        // session 파라미터를 세션에 넣는다.
        s.setAttribute("session", session);
      }
    }
    response.getWriter().append("Server1\r\n");
 
    Object obj = s.getAttribute("session");
    // session이라는 키가 세션에 존재하면 화면에 표시한다.
    if (obj != null) {
      response.getWriter().append(obj.toString());
    }
  }
  // POST 방식과 GET은 같은 처리
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
  }
}

제가 여러 브라우져로 접속을 했는데 server1이라고 나오는 경우도 있고 server2라고 나오는 경우가 있네요. apache에서 트래픽에 따라 tomcat을 분산해 줍니다.

참고로 여기서 제가 tomcat1은 8180로 web port로 기동했고 tomcat2는 8280으로 web port로 기동했습니다.

server1에 session 값을 test로 저장했는데 server2에도 session 값이 있는 것을 확인할 수 있습니다.

로그로도 세션 클러스터링이 움직이는 것을 확인할 수 있습니다.

그리고 아까 설정한 mod_jk 상태 페이지를 보겠습니다.

이렇게 ajp13으로 tomcat이 두개가 붙어있는 것을 확인할 수 있습니다.

 

여기서 제가 tomcat2 서버를 중지했습니다. 다시 mod_jk를 보면 worker2가 IDLE이 된 것을 확인할 수 있습니다. IDLE이 전부 서버가 빠진 것이라고 할 수는 없지만, 응답이 안된다는 거는 확인할 수 있습니다.

다시 tomcat2를 기동하면 제대로 작동합니다.

이 뜻은 어느 한쪽의 서버가 문제가 생겨서 접속이 되지 않는 상태가 되더라도 나머지 한개가 제대로 작동을 하면 사이트가 정상 작동을 하므로 웹 서비스 운영상에도 안정성을 대폭 올릴 수 있습니다.

apache 2대 - tomcat 2대 이런 식으로 움직이게 되면 운영 상에 서버 다운은 거의 없겠네요..

참조 - https://tomcat.apache.org/connectors-doc/common_howto/loadbalancers.html

참조 - https://tomcat.apache.org/connectors-doc/common_howto/workers.html

 

여기까지 apache-tomcat에서 로드 밸런싱(Load balancing)하는 방법과 세션 클러스터링(세션 공유)에 관한 설명이었습니다.

 

궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.