톰캣의 구성 요소에 대해 알아보자

Cenibee·2021년 8월 21일
1
post-thumbnail

아파치 톰캣 구조 Docs를 위주로 참고하여 번역 및 정리하며 주관적인 생각이 포함된 포스트입니다. 잘못된 정보가 포함되어 있을 수 있으니 잘못된 부분은 지적해주시고, 참고하더라도 다른 자료들과 함께 참고해 주세요. 👍

0. 아파치 톰캣이란?

아파치 톰캣(Apache Tomcat, 톰캣)은 자카르타 서블릿(Jakarta Servlet), 자카르타 서버 페이지(Jakarta Server Pages), 자카르타 표현식(Jakarta Expression Language), 자카르타 웹 소켓(Jakarta WebSocket), 자카르타 애너테이션(Jakarta Annotaions) 그리고 자카르타 인증(Jakarta Authentication) 스펙을 구현하는 오픈소스 소프트웨어다. 이 스펙들은 자카르타 EE 플랫폼(Jakarta EE Platform)의 일부다.

자카르타 EE 플랫폼은 자바 EE 플랫폼(Java EE Platform)가 진화한 것이다. 톰캣 10 과 그 이후의 버전들은 자카르타 EE 에 해당하는 스펙을 구현한다. 톰캣 9 와 이전 버전들은 자바 EE 에 해당하는 스펙을 구현한다.

1. 톰캣을 구성하는 요소들

1.1. 서버

톰캣에서 서버는 모든 컨테이너를 표현하는 요소다. 톰캣은 기본 Server 인터페이스를 제공하며, 사용자가 커스터마이징 할 수도 있다.

1.2. 서비스

서비스는 서버내에 있는 중간 다리 컴포넌트로, 단 하나의 엔진과 하나 이상의 커넥터를 묶어놓은 컴포넌트다. 서비스 요소 기본 구현은 간단하고 충분한 기능을 갖지만, 사용자가 커스터마이징 할 수도 있다: Service 인터페이스

1.3. 엔진

엔진은 특정 서비스의 요청 처리 파이프라인을 나타낸다.하나의 서비스는 여러 커넥터를 가질 수도 있으며, 엔진은 이러한 커넥터로 부터 요청을 받아 처리하고 적절한 커넥터에 응답을 돌려줘서 사용자에게 전달한다. 흔한 케이스는 아니지만 Engine 인터페이스를 구현하여 커스텀 엔진을 제공할 수 있다.

1.4. 호스트

호스트는 톰캣 서버에서 www.yourcompany.com과 같은 네트워크 이름과 관련있다. 엔진은 여러 호스트를 포함할 수 있고, 각 호스트의 요소는 별칭을 지원한다(yourcompany.comabc.yourcompany.com으로 사용하는 등). StandardHost 인터페이스 구현만으로 다양한 기능들을 사용할 수 있지만, Host 인터페이스를 구현하여 커스텀 호스트를 생성할 수 있다.

1.5. 커넥터

커넥터는 사용자와의 소통을 담당한다. 톰캣에서는 여러 종류의 커넥터를 사용할 수 있다:

  • HTTP 커넥터 - 대부분의 HTTP 통신에 사용된다. 특히 독립형 톰캣을 실행하는 경우 사용한다.
  • AJP 커넥터 - AJP 프로토콜을 구현하여 Apache HTTPD 서버와 같은 웹 서버와 연결할 때 사용한다.

커스텀 커넥터를 생성하는 것은 큰 수고가 드는 작업이 될 수 있다.

1.6. 컨텍스트

컨텍스트는 웹 어플리케이션을 나타낸다. 한 호스트는 여러 컨텍스트를 포함할 수 있으며, 각 컨텍스트는 고유한 경로를 갖는다. StandardContext만으로 다양한 기능을 사용할 수 있지만, Context 인터페이스를 구현하여 커스텀 컨텍스트를 생성할 수 있다.

2. 구성 요소 조립

우선, 1.에서의 설명에 따라 각 요소간의 관계를 정리해보면:

  • 서버 : 서비스 = 1 : N
  • 서비스 : 엔진 : 커넥터 = 1 : 1: N
  • 엔진 : 호스트 = 1 : N
  • 호스트 : 컨텍스트 =1 : N

의 관계를 가진다. 커넥터는 서버 밖에서 사용자 요청을 받는 역할을 하니까 커넥터에 유의해서 구성 요소들을 조립하면 아래와 같은 그림이 나온다.

그림은 직접 그린건데... 퀄 보다는 의미에 집중하자...

플로우는 아래와 같다:

  1. 사용자의 요청은 커넥터로 들어온다.
  2. 커넥터는 해당 커넥터가 속한 서비스의 엔진으로 요청을 전달한다.
  3. 엔진은 요청에 알맞은 호스트를 선택하여 전달한다.
  4. 요청 경로에 따라 적절한 컨텍스트로 전달한다.
  5. 컨텍스트가 요청을 처리하여 응답을 생성하면 요청이 전달된 역방향으로 응답을 전달한다.

3. 구성 요소 설정

위에 설명한 구성 요소들은 어떻게 무엇을 보고 초기화가될까? 톰캣 바이너리 zip 을 받으면 conf/server.xml 설정파일이 존재하는데 여기서 위와 같은 구성 요소들을 설정할 수 있다.

기본 설정을 보면서 각 요소들이 어떻게 설정되는지 확인해본다.

설정 파일은 포스트를 작성중인 2021.08.20 날짜에 10.0 버전 최신인 10.0.10 버전 바이너리 ZIP의 설정 파일을 사용한다.

3.1. 서버

가장 먼저 가장 큰 영역인 서버를 보자.

<Server port="8005" shutdown="SHUTDOWN">
  ...
</Server>

서버는 모든 컨테이너를 나타내는 곳인만큼, server.xml에서 Server를 루트 엘리먼트로 사용한다.

Server 엘리먼트에서는 커스터마이징, 서버 종료 조건, 유틸리티 쓰레드 등을 속성으로 설정할 수 있다. 기본으로 설정된 속성들에 대해 알아보자면:

  • port: 종료(shutdown) 명령을 기다릴 TCP/IP 포트를 지정한다. address 속성이 지정되지 않으면 localhost를 기본으로 사용한다.
  • shutdown: 톰캣을 종료하기 위해 명시된 포트로 수신 받을 문자열을 정의한다.

즉, 톰캣이 기본 설정인 경우 8005 포트에 SHUTDOWN 문자열이 들어오면 톰캣이 종료된다.

❓ 진짜로 되나 궁금해서 만들어본 테스트 코드
public static void main(String[] args) throws IOException {
      try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
          while (true) {
              String message = reader.readLine();
              if ("EXIT".equals(message)) break;

              System.out.println("소켓 생성: localhost:8080");
              try (Socket socket = new Socket("localhost", 8005)) {
                  OutputStream sos = socket.getOutputStream();

                  System.out.println("메시지 전송: " + message);
                  sos.write(message.getBytes(StandardCharsets.US_ASCII));
                  sos.flush();
                  sos.close();
                  System.out.println("소켓 종료됨");
              }
          }
      }
  }

shutdown 바꾸고, port를 바꾸고 테스트해도 잘 동작한다~

3.2. 서비스

다음은 서비스다. 톰캣 기본 설정에서는 단 하나의 서비스가 존재하며, Catalina 라는 이름을 갖는다.

이 뒤로는 구조에 대해 한눈에 볼 수있게 상위 엘리먼트를 간단하게 선언했다.

<Server ...>
  
  <Service name="Catalina">
  	...
  </Service>
  
</Server>

Server는 하나 이상의 Service를 가질 수 있지만, 기본 설정으로는 Catalina 서비스만을 갖는다. Service에 대한 설정은 이름, 커스터마이징, 서비스 중단 대기시간을 설정하는 속성이 있다. ServiceService 자체의 설정보다 커넥터와 엔진 사이의 관계를 만들어주는데 더 큰 의미가 있어 다양한 설정은 없는 것으로 생각된다.

3.3. 커넥터

이번엔 커넥터다. 톰캣 기본 설정으로는 HTTP/1.1 커넥터만 활성화 되어있다. HTTP/2, AJP 커넥터가 주석으로 존재하지만 HTTP/2 커넥터는 HTTP/1.1 커넥터의 연장이므로 제외하고 AJP 커넥터를 추가로 가져와봤다.

<Server ...>
  <Service ...>
    
    <!----------------- HTTP/1.1 커넥터 ----------------->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    
    <!----------------- AJP 커넥터 (기본은 주석 처리) ----------------->
    <Connector protocol="AJP/1.3"
               address="::1"
               port="8009"
               redirectPort="8443" />
    
    ...
  </Service>
</Server>

Connector의 속성은 HTTP/AJP 중 어떤 프로토콜을 사용하느냐에 따라 상이하고, 각 프로토콜별 스펙에 따른 다양한 속성이 존재한다. 기본 설정된 속성들은 모두 공통 속성으로 보이니 간단하게 리스팅하고 넘어간다:

  • port: 커넥터가 소켓을 생성하고 리스닝할 TCP 포트를 지정한다.
  • protocol: HTTP / AJP 프로토콜을 설정할 수 있고, 각 프로토콜 별 세부 프로토콜도 설정할 수 있다.
  • redirectPort: 설정된 커넥터가 non-SSL 요청을 지원하고, 받은 요청이 SSL 통신을 요구하는 제한사항이 있는 경우, Catalina가 설정된 포트로 요청을 리다이렉트한다.
  • address: 서버가 하나 이상의 IP를 가지는 경우, 해당 커넥터가 어떤 IP 포트에서 리스닝을 할지 설정한다.
  • connectionTimeout: 커넥터가 연결을 수립한 이후 요청 URI가 올때까지 얼마나 기다릴 것인지를 밀리초 단위로 설정한다.

3.4. 엔진

커넥터가 있다면, 커넥터가 받은 요청을 처리해줄 엔진이 필요하다. 앞에서 살펴봤듯이 한 서비스에는 단 하나의 엔진만 존재해야한다.

<Server ...>
  <Service ...>
    <Connector ... />
    ...
    
    <Engine name="Catalina" defaultHost="localhost">
      ...
    </Engine>
    
  </Service>
</Server>

엔진도 이름, 기본 호스트, 클러스터를 위한 설정 등을 속성으로 설정 할 수 있다. 사용된 속성은 아래와 같다:

  • name: 엔진을 구분하는 논리적인 고유 이름이다. 로그나 에러 메시지에 사용된다.
  • defaultHost: 기본으로 사용할 호스트 이름이다.

엔진은 그 자체보다는 호스트를 포함한 <Realm>, <Listener>, <Valve> 엘리먼트로 구성된 파이프라인이 어떻게 구성되어 있는지가 더 중요해보인다. <Realm>, <Listener>, <Valve>는 톰캣의 핵심 구성 요소 보다는 부가적인 컴포넌트로 생각되어 본 포스트에서는 대략적인 역할만 짚고 넘어간다:

  • <Realm>: 유저의 이름, 비번, 역할에 대한 "데이터베이스"를 나타낸다.
  • <Listener>: 엔진의 라이프 사이클 리스너를 등록할 수 있다.
  • <Valve>: 요청 처리 파이프라인에 삽입되는 컴포넌트를 나타낸다. 엔진, 호스트, 컨텍스트에 삽입할 수 있다.

3.5. 호스트

server.xml에는 마지막으로 호스트가 포함된다. 네트워크 이름과 관련된 가상의 호스트를 나타낸다.

<Server ...>
  <Service ...>
    <Connector ... />
    <Engine name="Catalina" defaultHost="localhost">
      
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
      </Host>
      
      ...
    </Engine>
  </Service>
</Server>

호스트는 다양한 속성들을 설정할 수 있으며, 엔진과 동일하게 <Realm>, <Listener>, <Valve> 등의 내부 엘리먼트를 가질 수 있다. 사용된 속성에 대한 정보는 다음과 같다:

  • name: DNS에 등록된 해당 가상 호스트의 네트워크 이름을 설정한다.
  • appBase: 해당 호스트의 앱 기반이 될 디렉토리 경로를 설정한다. 절대 경로 or CATALINA_BASE로 부터 상대경로를 설정한다.
  • unpackWARs: appBase에 설정된 기반 폴더에 있는 WAR(web-application-archive) 파일의 압축을 해제할지 말지 설정한다.
  • autoDeploy: 톰캣이 실행되는 동안 주기적으로 웹 앱을 새로 만들거나 업데이트를 시킬지를 설정한다.

3.6. 컨텍스트

지금까지 server.xml에서 설정되는 구성요소들에 대해서 다뤄보았다. 하지만 호스트가 마지막이고 빠진게 있다. 바로 컨텍스트다.

컨텍스트는 <Context> 엘리먼트를 server.xml에 설정할 수 있지만, 이 방식은 좋은 방식이 아니므로 지양해야한다. 왜냐하면, 컨텍스트에 대한 설정은 다른 컴포넌트보다 자주 변경될 수 있다. 하지만 server.xml 설정은 톰캣을 재시작하기전까지는 새로고침을 할 수 없기 때문이다. 즉, 톰캣을 재시작 하지 않고도 컨텍스트에 대한 유연성을 제공하기 위해서는 컨텍스트를 server.xml로부터 분리해야한다.

그럼 컨텍스트에 대한 설정은 어디서 확인할 수 있을까? 컨텍스트는 말 그대로 "웹 앱"을 표현하기 때문에, 어떤 웹 앱(보통 WAR 파일)에 대해 설정돼야 한다. 톰캣에서는 각각의 웹 앱에서 컨텍스트를 설정할 수 있는 방법을 지원하며, 설정된 방법이 없는 경우 사용할 기본 설정도 제공한다. 컨텍스트의 설정은 아래 우선 순위로 설정된다:

  1. $CATALINA_BASE/conf/[enginename]/[hostname]/ 디렉터리에 [앱이름].xml 파일로 설정한다.
  2. 각각의 앱 파일내의 /META-INF/context.xml 파일로 설정한다.
  3. conf/server.xml 파일에서 <Host> 엘리먼트에 설정한다. (<Context>override 속성 기본값이 true라서 우선 순위가 제일 낮다.)

또한, 위 방법으로 제공되는 설정이 없는 경우에는 아래에서 기본 설정을 찾는다:

  1. $CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default 파일로 설정한다.
  2. $CATALINA_BASE/conf/context.xml 파일로 설정한다. 톰캣에서 기본으로 제공하는 파일이 존재한다.

✅ 기본으로 설정될 때, 동일한 파일로 설정되더라도 각 앱의 컨텍스트는 독립적이다.

따라서 별다른 설정을 하지 않은 경우 기본 설중 2. 방법을 이용해 컨텍스트를 설정한다. $CATALINA_BASE/conf/context.xml의 기본 설정은 다음과 같다:

<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
</Context>

컨텍스트도 다양한 속성을 설정할 수 있지만, 기본 설정에서는 별다른 속성은 없는것으로 보인다. 내부 엘리먼트로는 <WatchedResource>를 선언했다. 그 외에 사용할 수 있는 내부 엘리먼트들이 몇가지 있지만 본 포스트에서 다루기엔 내용이 너무 길어질것 같아서 사용된 엘리먼트만 간단하게 짚고 넘어간다:

  • <WatchedResource>: 정적 리소스중, 변경을 감지하여 웹 앱을 업데이트/새로고침하기 위해 모니터링할 리소스를 설정한다.

4. 고찰

아파치 톰캣의 구성 요소를 살펴봤다. 기존에 그냥 사용해오던 webapp 디렉토리, server.xml, web.xml이 실제로 어디에서 설정되는지, 또한 톰캣 자체가 어떤 추상화로 동작하는지에 대해 알 수 있는 계기가 된 것 같다. 다른 웹 로직이나 제우스를 접해본 경험이 있긴 했는데 뭔가 톰캣은 그냥 실행하면 되던데... 싶던 부분도 사실은 톰캣이 내부적으로 기본 설정이 제공해줘서 그랬다는 것도 알게된 것 같다. 모르고 사용했던 나를 부끄러워하고 이제는 추상 레벨은 이해했으니 조금 덜 부끄러워 해도 될 것 같다.


참조 모음

기본적으로 아파치 톰캣 아키텍쳐 Docs - Overview를 많이 참조 했으며, 세부 참조는 다음에 나열한다:
서버, 서비스, 엔진, HTTP 커넥터, AJP 커넥터, 컨텍스트

profile
한 줄 소개

0개의 댓글