[Java] 60. Spring boot에서 Apache 연결과 로드벨런싱을 설정하는 방법


Study/Java  2022. 2. 28. 18:43

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


이 글은 Spring boot에서 Apache 연결과 로드벨런싱을 설정하는 방법에 대한 글입니다.


Spring boot에서는 Tomcat이 내장되어 있기 때문에 바로 빌드하고 배포해도 웹 서버가 생성이 됩니다. 즉, 복잡한 Tomcat 설정이 필요가 없습니다.

이 웹이라는 것은 브라우저에서 요청과 응답 처리로 접속부터 처리 파싱까지 여러가지 처리가 있습니다. 그런데 트래픽(접속자)가 많아지면 Tomcat 하나로 서버가 버틸 수가 없기 때문에 Apache와 Tomcat으로 분할하여 역할을 나눕니다.

역할이라는 것은 브라우저의 요청과 응답 처리와 여러가지 프로토콜 처리를 Apache에 맡가고 Tomcat은 Html 파싱과 세션 관리의 역할을 맡습니다.

그래서 Apache 서버와 Tomcat을 연결하는 방법이 있는데 이전 글에서 Apache와 Tomcat을 연결하는 방법에 대해 설명한 적이 있습니다.

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


그런데 Spring boot에서는 Tomcat에 프레임워크 안에 포함되어 있는 형태이네요.. 그래서 Tomcat 설정(이전 server.xml)를 프로젝트 안에서 설정을 해야 합니다.

먼저 이전 프로젝트에서 @Configuration 어노테이션을 선언한 AJPConfig 클래스 파일을 생성합니다.

package com.example.demo.Controller;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.ajp.AbstractAjpProtocol;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 설정 클래스
@Configuration
public class AJPConfig {
  // application.properties에서 설정 값을 받아온다.
  @Value("${tomcat.ajp.port}")
  private int port;
  // bean 설정
  @Bean
  public ServletWebServerFactory servletContainer() {
    // AJP/1.3 프로토콜의 Connector를 생성
    var ajpConnector = new Connector("AJP/1.3");
    // 포트 설정을 application.properties에서 설정
    ajpConnector.setPort(port);
    // ajp 로그 설정 이전 server.xml의 allowTrace 어트리뷰트
    ajpConnector.setAllowTrace(false);
    // http와 https 처리.. 현재는 필요없기 때문에 주석 처리
    // ajpConnector.setScheme("http");
    // SSL연결 시에 사용
    // ajpConnector.setSecure(false);
    // server.xml에서 secretRequired 설정
    ((AbstractAjpProtocol<?>)ajpConnector.getProtocolHandler()).setSecretRequired(false);
    // tomcat 설정
    var tomcat = new TomcatServletWebServerFactory();
    // 추가
    tomcat.addAdditionalTomcatConnectors(ajpConnector);
    
    return tomcat;
  }
}

AJPConfig 클래스를 위처럼 작성하고 application.properties에 tomcat.ajp.port를 추가합니다.

# 서버 포트 설정
server.port=8081
# ajp 포트 설정
tomcat.ajp.port=9091

Apache 설정의 경우는 mod_jk.so 파일을 설치하고 httpd.config 파일를 수정하고 workers.properties 파일을 추가합니다. (window의 경우는 Apache Lounge에서 다운로드 합니다. 링크 - https://www.apachelounge.com/download/)

# 모드 로드
LoadModule jk_module modules/mod_jk.so
# 모드 설정
<IfModule mod_jk.c>
  JkWorkersFile conf/workers.properties
  JkShmFile run/mod_jk.shm
  JkLogFile logs/mod_jk.log
  JkLogLevel info
  JkLogStampFormat "[%y %m %d %H:%M:%S] "
</IfModule>	
# 80 포트일 경우
<VirtualHost *:80>
  JkMount /* springboot
  JkMount /jkmanager/* jkstatus
  ServerName localhost
</VirtualHost>
# 가상 디렉토리 jkmanager로 접속하는 경우
<Location /jkmanager/>
  JkMount statusmanager
  # 로컬만 접속
  Require ip 127.0.0.1
</Location>
# 리스트 키
worker.list=springboot,jkstatus
# 로드밸런싱 상태
worker.jkstatus.type=status 
# ajp 포트
worker.springboot.port=9091
# 호스트명
worker.springboot.host=localhost
# 타입
worker.springboot.type=ajp13
# 부하 분산 비율
worker.springboot.lbfactor=1

이제 아파치를 기동하고 이클립스에서 Spring boot를 기동합니다.

ajp 프로토콜이 기동되는 것을 확인할 수 있습니다.

아파치로 80을 접속하니 톰켓에 연결된 것을 확인할 수 있습니다.

이제부터 Apache와 Tomcat을 연결해서 운영을 합니다.

그런데 트래픽(접속자)가 더 늘어나서 Apache와 Tomcat만으로 버티기가 힘들어 지는 시기가 옵니다. 그러면 보통 웹의 요청과 응답의 처리에서 가장 시간이 많이 걸리는 부분이 Tomcat의 Html 파싱 부분이 오래 걸립니다.

사양에 따른 그 결과가 항상 다르기 때문에 요청에 따른 Html를 작성해기 때문입니다. 그럼 Apache와 Tomcat 환경에서 Tomcat을 여러 개로 늘려서 하나의 Apache에 연결하는 데, 그것을 로드밸런싱이라고 합니다.

이제 이 프로젝트를 두 개의 톰켓 서버로 나누고 아파치에서 로드밸런싱을 하겠습니다.

먼저 두개의 서버가 작동되는 것을 확인하기 위헤 index 화면을 수정하겠습니다.

package com.example.demo.Controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트럴러 어트리뷰트
@Controller
// Controller 클래스
public class HomeController {
  // application.properties에서 설정 값을 받아온다.
  @Value("${tomcat.ajp.port}")
  private int port;

  // 매핑 주소
  @RequestMapping(value = { "/", "/index.html" })
  public String index(Model model) {
    // 데이터를 템플릿에 전달한다.
    model.addAttribute("data", port);
    // 템플릿 파일명
    return "Home/index";
  }
}

화면에 application.properties에서 설정하는 ajp 포트 번호를 화면에 표시하는 것으로 수정했습니다.

그리고 이클립스에서는 두 개의 서버를 실행할 수 없기 때문에 테스트할 수 있는 디렉토리로 소스를 복사하겠습니다.

저는 d드라이브의 work 폴더에 복사했습니다.

그리고 각 폴더의 application.properties에서 http를 8081와 8082로 설정하고 ajp를 9091과 9092로 서로 겹치지 않게 설정합니다.


설정을 하고 윈도우의 경우에는 프로젝트에 mvnw.cmd 파일이 있는데 그것을 이용하여 빌드하고 실행하겠습니다.

mvnw clean install spring-boot:run

테스트가 실행되고 로그 메시지가 표시되는데 특별히 에러가 발생하지 않으면 정상적으로 실행이 된것 입니다.


일단은 톰켓은 실행이 된 것 같고 그럼 다시 Apache 설정을 수저하고 실행하겠습니다.

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

위처럼 works.properties 파일에 로드벨런싱 설정을 합니다.


이제 apache를 실행해서 브라우져에서 접속해 봅시다.

그럼 localhost에 접속할 때마다 결과가 9091이 나올 때가 있고 9092가 나올 때도 있습니다.

그럼 접속 수에 따라 Aphache에서 두개의 tomcat으로 분산 처리를 하는 것을 확인할 있습니다.


위의 httpd.conf 설청에서 jkmanager를 설정했습니다.

그럼 localhost/jkmanager/로 접속을 해 봅시다.

Apache에서 Tomcat 서버의 로드벨런싱하는 상황이 표시되네요..


설명이 복잡했습니다만 실제로 해보면 예전보다 설정이 훨씬 간단해 졌다는 것을 느낄 수 있네요. Tomcat 자체도 프레임워크 안에 있다 보니 빌드 공정이나 배포 전략 설정이 훨씬 쉬울 듯 싶네요..

다음 글에서는 로드밸런싱 된 서버에서 세션 공유를 할 수 있는 세션 클러스터링에 대해 설명하겠습니다.


여기까지 Spring boot에서 Apache 연결과 로드벨런싱을 설정하는 방법에 대한 글이었습니다.


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