본문 바로가기

서버운영 (TA, ADMIN)/미들웨어

[서버관리] 톰캣과 아파치 웹서버 연동

아파치 웹 서버와의 연동



톰캣은 HTTP 서버 기능을 내장하고 있고 자바 NIO를 사용하는 커넥터를 구현하는 등 전보다 많은 성능 향상이 이루어졌지만 톰캣 단독으로 사용하기보다는 아파치 웹서버 등 별도의 웹서버와 연계해서 사용하는 경우가 많습니다. 이렇게 사용하는 데는 여러가지 이유가 있지만 주되 이유는 다음과 같습니다.


정적 콘텐츠 서비스 효율이 뛰어남

웹서버가 이미지 파일이나 동영상 등 정적 콘텐츠를 제공하는 데 더 성능이 뛰어난 것으로 알려져 있습니다. 주의할 점은 특정상황(톰캣에 APR native와 sendFile 사용)에서는 톰캣이 정적 콘텐츠 처리도 더 빠른 경우도 있으므로 Apache Benchmark나 jMeter 등의 성능 측정 도구로 직접 서비스 사이트를 측정할 필요가 있습니다.


유연한 클러스터링

하드웨어 기반의 부하 분산 장비(L4)가 없어도 아파치 웹서버와 연계해서 사용하면 여러 대의 아파치 웹 서버와 톰캣을 클러스터로 손쉽게 구성해 유연하게 서비스할 수 있습니다. 아파치 웹서버의 톰캣 연계 모듈은 특정 톰캣 인스턴스가 응답이 없으면 클러스터에서 제외하고 서비스하므로 한 대의 서버에 장애가 생겨도 정상적으로 서비스할 수 있습니다.


모듈 기반의 확장성

아파치 웹 서버는 다양한 모듈이 제공되므로 서버의 기능을 확장할 수 있으며, mod_headers, mod_rewrite 등의 다양한 모듈을 사용하면 유연한 서비스 제공이 가능합니다.


가상 호스트(Virtual Host)

톰캣도 Host 구문으로 가ㅓ상 호스트를 사용할 수 있지만 아파치 웹서버보다는 설정과 관리가 번거롭습니다. 또 아파치 웹 서버를 이용하면 하나의 서버에 여러 개의 가상 호스트를 설정해 가상 호스트마다 다른 웹 애플리케이션 서비스를 제공할 수 있습니다. 예로 자바 애플리케이션을 제공하는 가상 호스트인 app1.example.com으로 요청이 들어오면 톰캣에서 처리하고 wordpress.example.com으로 요청이 들어오면 PHP로 워드프레스 서비스를 제공하도록 설정할 수 있습니다.


보안

웹서버가 필요한 가장 중요한 이유 중 하나입니다. 먼저 권한 부분의 경우, 유닉스 설계상 1024 이하의 포트를 사용하려면 루트 사용자여야 합니다. 그러므로 톰캣을 80 포트에서 서비스 하려면 루트사용자로 구동해야 하며, 이는 보안상 많은 문제를 야기할 수 있습니다. 그렇다고 1024 이후의 포트를 사용하면 클라이언트들은 서비스 URL과 서비스 포트 번호를 외워야 한다는 어려움이 있습니다. 아파치 웹서버는 루트로 구동해도 자신 프로세스를 포크한 후에 apache 그룹과 계정으로 전환합니다.


톰캣은 특성상 MySQL, 오라클, MariaDB 등 데이터베이스 서버와 함께 사용하는 경우가 많으며, 데이터베이스 서버에는 기업의 중요한 정보가 보관되므로 외부에서 연결할 수 없게 격리된 내부 네크워크 환경을 사용합니다.


반면 웹 서버는 외부에 서비스를 제공해야 하므로 DMZ라고 하는 외부망과 내부망 사이에 위치해야 하며, 톰캣을 웹서버로 사용한다면 DMZ의 톰캣에서 데이터베이스 서버로 바로 연결하는 것을 허용해야 하므로 2차 방화벽이 있어도 의미가 없습니다.


이러한 구성에서 WAS가 해킹당할 경우 WAS에 있는 데이터베이스 서버 설정을 보면 데이터베이스 서버의 연결 정보를 알 수 있고, 연결이 가능하므로 데이터까지 유출될 수 있습니다.




하지만 아파치 웹서버를 DMZ 영역에 두고 서비스하면 위와 같은 우려가 없어집니다. 웹서버는 오로지 톰캣에만 연결할 수 있으므로 웹서버가 해킹당해도 데이터베이스 서버까지 침입하기가 힘듭니다.


거기에 SELinux까지 사용한다면 아파치 웹서버는 오로지 8080, 8009 포트에만 접근할 수 있으므로 톰캣에 서비스 용도 외에는 연결 자체가 불가능해지므로 웹서버가 공격받아도 2차 피해를 최소화할 수 있습니다.





연계 방식

연계 방식에는 크게 mod_jk와 mod_proxy라는 두 가지 연계 방식이 있습니다. 두 방식 모두 장단점이 있으며 환경이나 필요 여부에 따라 골라서 선택하면 됩니다. 먼저 두 연계방식의 특징은 다음과 같습니다.


mod_jk

- URL이나 콘텐츠별로 유연한 매핑이 가능(이미지 등 정적 콘텐츠와 PHP 스크립트는 웹 서버, 서블릿은 톰캣에서 처리)

- 전용 바이너리 프로토콜이므로 mod_proxy보다 빠른 경우가 많음.


mod_proxy

- 별도 모듈을 설치할 필요가 없고(아파치 기본 모듈) 설정이 간편함

- HTTP 리버스 프록시로 동작하므로 특정 WAS에 의존적이지 않으므로 HTTP를 제공하는 모든 WAS에 적용 가능.


mod_jk는 톰캣의 AJP 커넥터와 연계되는 모듈로, 톰캣 전용 바이너리 프로토콜인 AJP를 사용하므로 mod_proxy보다 속도가 빠른 편입니다. 하지만 mod_proxy보다 설정하기 어렵고 별도의 모듈을 컴파일해서 설치해야 하며, 톰캣 전용 프로토콜이므로 WAS가 다른 제품으로 변경되면 사용하기가 불가능합니다.


mod_proxy는 톰캣의 HTTP 커넥터와 연계되며 아파치에 기본 탑재된 모듈이므로 별도의 설치가 필요없고 HTTP 프록시이므로 WAS의 종류에 구애받지 않습니다. 그리고 mod_jk보다는 설정이 간편한 편입니다.


아파치의 모듈과 톰캣의 커넥터와 연결 관계는 아래의 그림과 같으며 보통 하나의 연결 방식을 사용하므로 사용하지 않는 아파치 모듈과 톰캣의 커넥터는 주석 처리하여도 정상 동작에는 문제가 없습니다.




mod_jk를 이용한 연계



mod_jk 설치

mod_jk는 yum으로는 제공되지 않으므로 이미 컴파일된 바이너리를 구하거나 소스를 받아서 컴파일한 후 설치해야 합니다.


01. 먼저 컴파일러와 웹 서버 개발 패키지를 설치해야 합니다. 다음 명령어로 설치합니다.


02. 톰캣 홈페이지에서 커넥터를 배포하는 곳(http://tomcat.apache.org/connectors-doc/)을 통해 최신 소스를 내려 받습니다.


03. 압축을 풀고 폴더로 이동합니다.


04. native 폴더로 이동한 후 Makefile을 생성하기 위해 autoconf 스크립트를 실행합니다. 실행 시 아파치 확장기능 설치를 도와주는 유틸리티인 apxs의 절대경로를 설정해야 정상적으로 컴파일할 수 있습니다.


05. make를 실행해 컴파일합니다.


06. 정상적으로 컴파일이 완료되면 make install로 설치를 진행한다.


07. 제대로 설치되면 /etc/httpd/modules/ 및에 mod_jk.so라는 파일이 생성될 것입니다. SELinux 보안 정책에 따라 아파치 웹서버는 모듈에 httpd_modules_t 보안 컨텍스트가 있는 모듈만 로딩할 수 있습니다. 정상적으로 설치되면 httpd_moduls_t 컨텍스트가 적용돼 있겠지만 다시 한번 확인해보고 없으면 restorecon으로 컨텐스트를 복구하자.



mod_jk 설정

이제 아파치 mod_jk 설정을 변경해 톰캣과 연동하는 방법입니다.


01. 아파치의 설정 파일이 있는 /etc/httpd로 이동한다.


02. 텍스트 편집기로 conf/httpd.conf 파일을 연다.


03. LoadModule 항목을 찾아 다음 내용을 추가하고 저장한다(굵게 표시한 부분이 추가할 부분이다.)


04. conf.d/mod_jk.conf을 텍스트 편집기로 열어 다음 내용을 추가하고 저장한다.


05. conf/workers_jk.properties를 텍스트 편집기로 열어서 다음 내용을 추가한다.


06. 가상 호스트 설정에 사용할 mod_jk 워커와 URL 패턴을 지정한다.


07. httpd 서비스를 재구동해 설정을 반영한다.


08. 이제 톰캣과 연결된 가상 호스트에 연결했을 때 톰캣의 초기 화면이 보인다면 제대로 설정된 것이다.



mod_proxy 연계



mod_proxy는 아파치 웹서버에 기본적으로 포함돼 있는 모듈로 포워드 프록시(Forward proxy)와 리버스 프록시(Reverse proxy) 역할을 수행하는 모듈입니다. 두 방식의 프록시에 대해 간단히 살펴보면 다음과 같습니다.



포워드 프록시


포워드 프록시는 위 그림과 같이 사용자가 특정 사이트(example.com)에 연결했을 때 사용자 PC가 직접 연결하는 것이 아니라 포워드 프록시 서버가 요청을 받아 사이트에 연결하고 그 결과를 클라이언트에 전달(forward)해 줍니다.


포워드 프록시는 대개 캐시 기능이 있으므로 자주 사용되는 콘텐츠라면 월등한 성능 향상을 가져올 수 있으며, 사용자는 정해진 사이트만 연결할 수 있는 등 웹 사용 환경을 제한할 수 있으므로 기업 환경 등에서 자주 사용됩니다.



리버스 프록시



클라이언트가 example.com 웹 서버에 데이터를 요청하면 리버스 프록시(Reverse Proxy)는 이 요청을 받아 내부 서버에서 데이터를 받은 후 이 데이터를 클라이언트에 저장하게 됩니다. 보통 기업의 네트워크 환경은 DMZ라고 하는 내부 네트워크와 외부 네트워크 사이에 위치하는 구간이 존재하며, 이 구간에는 메일 서버, 웹 서버, FTP 서버 등 외부 서비스를 제공하는 서버가 위치합니다.


WAS나 데이터베이스는 내부망에 위치하므로 DMZ에 위치시키면 보안에 매우 취약해지므로 프록시서버를 두고 프록시가 내부 서비스와 통신해 그 결과를 클라이언트에게 제공하는 방식으로 서비스하게 됩니다. 이러한 방식으로 동작하는 프록시를 리버스 프록시라고 합니다. 리버스 프록시는 HTTP 방식으로 WAS와 연결하므로 WAS 종류와 상관없이 웹 서버와 연계할 수 있습니다.


설정

RHEL에서는 mod_proxy가 기본 모듈에 해당하며, 자동으로 활성화돼 있으므로 별도로 설치할 필요가 없고 사용할 가상 호스트에 바로 설정해주면 됩니다.


01. 가상 호스트 설정 파일(conf/http-vhost.conf)을 텍스 편집기로 열고 다음 내용을 추가합니다.


ProxyRequests: 포워드 프록시로 사용할 경우 On으로, 리버스 프록시로 사용할 경우 Off로 설정한다.

ProxyPreserveHost: 브라우저가 보낸 Host: HTTP 헤더를 프록시 요청시 사용한다. 리버스일 때 On으로 설정해야 한다.

ProxyPass: 가장 중요한 키워드로, 프록시에 연결할 URL을 기술한다. 웹서버의 가상 호스트명이 tomcat.example.com이므로 위의 예는 클라이언트가 http://tomcat.example.com으로 요청했을때 아파치가 http://server1.example.com:8080/에 연결하게 된다.

ProxyPass /mirror/foo http://server1.example.com:8080/로 설정할 경우 클라이언트는 http://tomcat.example.com/mirror/foo로 연결해야 리버스 프록시에 연결할 수 있다.

ProxyPassReverse: WAS가 redirect HTTP 응답을 보냈을 경우 Location, Content-Location HTTP 헤더를 수정해 클라이언트에 전달한다. 리버스 프록시가 이 헤더를 수정하지 않으면 클라이언트는 재지정(redirect) 시 제대로 연결할 수 없으므로 꼭 설정해야 한다.


02. 웹서버를 재구동해서 설정을 반영한다.


03. 이제 톰캣과 연결된 가상 호스트에 접속했을 때 톰캣의 초기 화면이 보인다면 제대로 설정되 ㄴ것이다.



WEB-INF 디렉터리 보호

아파치와 톰캣을 연계할 때 단일 서버에서 구동한다면 주의해야 할 사항이 있습니다. 이미지나 js 같은 정적 파일은 아파치가 서비스하고, 서블릿이나 JSP는 톰캣이 서비스하도록 설정할 경우 웹 서버의 DocumentRoot를 톰캣의 웹 애플리케이션 경로와 동일하게 설정해야 하며, 이 경우 클라이언트가 다음과 같이 요청하면 중요한 웹 애플리케이션 파일들이 노출될 수 있습니다.


이 같은 실수를 막기 위해서 RedirectMatch 설정을 웹 서버 설정에 반드시 추가해야 합니다.


그리고 보안 실수 방지를 위해 아파치 웹서버와 톰캣을 동일 장비에서 구동할 경우 모든 파일 처리를 톰캣에게 맡기고 아파치 웹 서버는 단순 전달만 하도록 설정하는 것이 보안상 유리합니다.



8080 포트 닫기

어느 방식이든 연계가 끝나면 톰캣의 포트인 8080을 방화벽에서 열 필요가 없습니다. 앞에서 연 포트를 닫기위해 다음과 같은 절차를 수행합니다.


01. iptables 설정 파일인 /etc/sysconfig/iptables를 텍스트 편집기로 연다.

02. 설정 파일에서 8080이 지정된 줄(굵게 표시)을 삭제하고 저장한다.

03. 저장하고 방화벽 규칙을 반영한다.

04. 브라우저로 8080 포트로 연결해 차단됐는지 확인한다.



manager context 보안 적용

앞에서 익힌 아파치 웹 서버를 이용하면 URL이나 디렉터리별로 접근 권한을 설정할 수 있습니다. 이제 톰캣과 연계됏으,므로 manager, host-manager에 Location 지시자를 이용해 접근 통제를 걸 수 있습니다.


아파치의 설정 파일을 열고 다음 내용을 추가합니다.


- Order deny, allow: 접근 제어를 거부, 허용 순서로 지정

- Deny from all: 모든 클라이언트의 연결을 거부한다.

- Allow from 127.0.0.1 192.168.0.3 192.168.152.0/24 : localhost와 192.168.0.3, 192.168.152 대역을 가진 IP에서만 연결할 수 있도록 허용한다.


이제 웹 서버를 재구동하고 manager URL에 연결하면 403 Forbidden 에러가 발생하는 것을 확인할 수 있다. host-manager에 대해서도 마찬가지로 Location 지시자를 이요해 접근 통제를 걸 수 있습니다.



SELinux 설정

아파치와 톰캣을 연동할 때 한가지 주의할 부분은 톰캣의 커넥터가 사용하는 포트가 SELinux가 httpd에 허용한 포트인지 여부입니다. 허용한 포트가 아닐 경우 SELinux는 아파치 웹서버가 톰캣에 연결하는 것을 차단해 버리므로 정상적으로 웹서비스를 운영할 수 없습니다. 그리고 SELinux 정책에 위반되면 다음과 같은 로그가 audit.log에 기록됩니다.


위 내용은 httpd(comm="httpd")가 8080 포트(dest=8080)에 연결하려고 했고 httpd의 컨텍스트는 httpd_t이지만 대상 컨텍스트는 http_catch_port_t이므로 정책에 맞지 않아서 차단했다는 의미입니다. 이것은 흔히 하는 실수이나 서비스에 영향을 줄 수 있으므로 잊지말고 SELinux 설정을 수정해야 합니다.


그동안 톰캣은 NIO 지원, 메모리 누수 검출/예방, 보안 기능 강화 등 많은 개선이 이루어졌습니다. 톰캣에 대한 인식도 가벼운 서블릿/JSP 컨테이너에서 상용 서비스에 사용해도 손색없는 자바 애플리케이션 서버로 거듭나고 있습니다. 현재 개발하려는 자바 웹 애플리케이션이 J2EE 기반이 아니고 스프링 등 경량화된 프레임워크를 기반으로 한다면 톰캣은 훌륭한 WAS의 후보가 될 수 있습니다.