제8장 Reverse Proxy

내용 목차

8.1. 개요
8.1.1. 적용 예시
8.2. 사용 방법
8.2.1. Proxy 서버 설정
8.2.2. Context-path 설정
8.2.3. Proxy 필터 설정
8.2.4. Deploy
8.3. 설정 예

본 장에서는 웹 애플리케이션의 성능 향상을 위한 Reverse Proxy의 기본 개념과 사용 방법을 예제를 통해 설명한다.

8.1. 개요

Reverse Proxy는 실제 요청을 처리하는 서버의 앞 단에 존재하며, 실제 서버로 들어오는 요청을 대신 받아서 해당 서버에 전달하고 그 결과를 받아서 요청한 곳으로 전달해주는 역할을 하는 서버를 의미한다. Reverse Proxy는 보안(실제 서버를 외부에 숨길 때) 및 부하 분산(여러 서버에서 요청을 처리할 때) 등의 이유로 필요하다.

8.1.1. 적용 예시

example.com이라는 회사가 인터넷을 통해 접근이 가능한 public IP 주소와 DNS 엔트리를 가진 www.example.com이라는 웹 사이트를 가지고 있다고 가정하자.

이 회사는 또한 방화벽 내에 private IP 주소와 등록되지 않은 DNS 엔트리를 가진 여러 애플리케이션 서버를 가지고 있다. 이런 해당 네트워크 내의 애플리케이션 서버로 "internal1.example.com"과 "internal2.example.com"이 있다고 하자. 이 서버들은 public DNS 엔트리를 갖지 않으므로 회사 외부로부터 "internal1.example.com"으로 접근이 안되고 "no such host" 에러가 발생할 것이다.

그런데 이 애플리케이션 서버로 웹 접근을 해야 된다면 인터넷을 통해 직접적으로 Export할 수는 없고, 해당 웹 서버로 통합하기 위해 내부적으로 다음과 같이 매핑한다.

  • http://www.example.com/app1/any-path-here : http://internal1.example.com/any-path-here로 매핑한다.

  • http://www.example.com/app2/other-path-here : http://internal2.example.com/other-path-here로 매핑한다.

Reverse Proxy 사용을 위해 작성해야 하는 주요 파일은 다음과 같다.

파일명설명
web.xmlReverse Proxy 기능을 하는 필터를 설정한다. 서버 내용이 있는 파일을 지정한다.
jeus-web-dd.xml<jeus-web-dd><context-path> 설정에 서비스를 제공하는 경로를 지정한다.
config/data.xmlReverse Proxy를 하는 서버를 지정한다.
config/sample.xmldata.xml 파일 작성을 위한 샘플 파일이다.

해당 파일들은 WEB-INF 디렉터리 하위에 위치한다.

8.2. 사용 방법

Reverse Proxy를 사용하려면 ReverseProxy 기능을 활성화시키는 애플리케이션을 deploy해야 한다. 본 장의 예제에서는 애플리케이션의 이름을 ReverseProxy라고 가정한다.

다음은 Reverse Proxy 기능을 사용하기 위한 설정 과정이다.

  1. Proxy 서버 설정

  2. context-path 설정

  3. Proxy 필터 설정

  4. Deploy

8.2.1. Proxy 서버 설정

Reverse Proxy 사용을 위해 먼저, ReverseProxy/WEB-INF/config/ 아래에 Proxy될 서버의 내용을 담은 data.xml 파일을 생성한다. 이 파일에서 설정하는 내용은 Proxy해야 할 실제적인 서버의 내용이다. Proxy할 서버의 주소와 이 서버로 서비스할 규칙(rule)을 설정할 수 있다. 실제로 사용자가 Proxy 서버를 설정할 때에는 “8.3. 설정 예”를 참고하여 항목을 수정해야 한다.

서버의 종류는 다음 2가지이다.

  • <server>

    • 특정 요청에 대해서 하나의 서버를 Proxy할 때 설정한다.

    • <server>는 다음과 같은 하위 설정 항목을 가진다.

      항목설명
      <domain-name>

      실제 서버의 호스트명이다.

      "host name:port" 또는 "ip address:port" 등의 형식으로 설정할 수 있다.

      www.remote.com:8088
      <path>

      실제 서버의 특정 디렉터리 하위의 항목을 Proxy할 때 설정한다.

      <path>/content</path>로 설정하면 www.remote.com:8088/content가 Proxy된다.

      <send-proxy-specific-headers>헤더에 x-forwarded-*, via 헤더 등을 추가할지 여부를 설정한다.
      <rewrite>절대 주소를 Rewrite할지 여부를 true 또는 false로 반드시 설정해야 한다.
      <rule>

      서버에 적용될 요청을 정의할 수 있다. 이 규칙에 해당하는 요청에 대해서 해당 서버의 내용을 Proxy한다.

      JEUS는 다음의 2가지 Proxy 규칙을 지원한다.

      • <directory-rule> : 특정 디렉터리 요청에 적용되는 규칙이다. "www.proxy.com"이 Proxy 서버이고, "www.proxy.com/remote/"라는 요청에 대해서 특정 서버를 Proxy하고 싶을 때 사용한다.

      • <accept-everthing-rule> : <directory-rule>에서 정의된 규칙 이외의 모든 요청에 적용되는 규칙이다. 각 규칙들은 위에서 순서대로 적용되기 때문에 이 규칙은 가장 마지막 항목에 넣어야 한다.

      <cluster-server>같은 요청에 대해서 여러 서버가 Proxy할 경우에 설정한다. 이 태그 하위에 <server>라는 태그를 갖고, 이것의 설정은 <server>와 비슷하다.

data.xml은 다음과 같은 내용의 문법 정의를 ReverseProxy/WEB-INF/config/proxy-config.dtd에 가지고 있어야 한다. 이 파일의 목적은 data.xml 파일의 정형성을 검증하는 것이다.

[예 8.1] data.xml의 dtd : <<proxy-config.dtd>>

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT config (single-server*, cluster-server*)>

<!-- single-server: proxy로 오는 해당 요청 -->
<!ELEMENT
    single-server
    (domain-name, send-proxy-specific-headers?, rewriting?, path?, (directory-rule|accept-everything-rule))>

<!-- cluster-server: proxy로 오는 요청을 여러 서버에 round robin으로 보낼 때 설정 -->
<!ELEMENT cluster-server (server*, (directory-rule|accept-everything-rule), send-proxy-specific-headers)>  

<!-- server: 클러스터된 하나의 서버를 말함 -->
<!ELEMENT server (domain-name, path?)>

<!-- directory rule: proxy 서버에 대한 요청을 구분하기 위한 규칙. 디렉터리명으로
구분한다. path를 "/dir"으로 설정하면 HOSTNAME/dir으로 요청에 맞는 server로 요청이
전달된다. -->
<!ELEMENT directory-rule (path)>

<!-- path: 디렉터리 설정 "/"로 시작한다. -->
<!ELEMENT path (#PCDATA)>

<!-- accept-everthing-rule: 모든 요청에 대해서 해당 서버로 요청을 전달하기 위한 규칙.
설정에서 가장 하위에 설정되어야 함 -->
<!ELEMENT accept-everything-rule EMPTY>

<!-- domain-name: 서버의 주소. HOSTNAME:PORT
ex) www.server1.com, www.server2.com:9999 -->
<!ELEMENT domain-name (#PCDATA)>

<!--send-proxy-specific-headers: via 헤더, x-forwarded-* 헤더 사용 여부 결정. -->
<!ELEMENT send-proxy-specific-headers (#PCDATA)>
<!-- rewriting: proxy된 문서 내용 중 link들 중에 절대 주소를 rewriting할지 여부 결정. -->
<!ELEMENT rewriting (#PCDATA)>


8.2.2. Context-path 설정

WEB-INF/jeus-web-dd.xml 파일의 <jeus-web-dd><context-path>를 수정하면 Proxy 서비스를 제공할 <context-path>를 설정할 수 있다. "/"로 설정하면 해당 서버의 아래의 모든 요청에 대해서 적용된다. 이에 대한 설정은 “3.3.1. jeus-web-dd.xml 설정”을 참고한다.

8.2.3. Proxy 필터 설정

JEUS는 다음의 2가지 필터 클래스를 통해서 Reverse Proxy 기능을 제공하고 있다.

  • Proxy 필터

    data.xml에 정의된 요청을 받아서 해당 서버의 결과를 전달하는 역할을 한다.

  • Rewrite 필터

    해당 서버에서 받은 결과물에서 링크를 자신의 주소로 Rewrite하는 역할을 한다.

    예를 들어서 www.proxy.com/remote/index.html라는 요청에 www.server1.com/index.html을 요청자에게 보낸다고 할 때 실제 페이지 내용에 href="www.server1.com/links.html"과 같은 절대 주소나 href="/contents.html"과 같은 "/"로 시작하는 주소 등은 Proxy 서버의 주소에 알맞게 "www.proxy.com/remote/links.html"과 "/remote/contents.html"로 변경되어야 한다. 이렇게 html, javascript, css와 같은 문서 내의 주소를 변경하는 역할을 한다.

JEUS에서는 Proxy 필터만 사용하거나 Proxy 필터와 Rewriter 필터를 함께 사용할 수 있다. 각 경우에 따라 WEB-INF 디렉터리의 web.xml 파일을 다음과 같이 설정해야 한다.

  • Proxy 필터만 사용할 경우

    [예 8.2] Proxy 필터만 사용할 경우 : <<web.xml>>

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                            http://java.sun.com/xml/ns/j2ee/web-app_3_1.xsd"
        version="3.1">
    
      <display-name>j2ep</display-name>
      <description>
            A J2EE application implementing a reverse proxy.
      </description>
    
        <filter>
            <filter-name>Proxy</filter-name>
            <filter-class>jeus.servlet.reverseproxy.ProxyFilter</filter-class>
            <init-param>
             <param-name>dataUrl</param-name>
             <param-value>/WEB-INF/config/data.xml</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>Proxy</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>


  • Proxy 필터와 Rewrite 필터를 함께 사용할 경우

    [예 8.3] Proxy 필터와 Rewrite 필터를 함께 사용할 경우 : <<web.xml>>

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                            http://java.sun.com/xml/ns/j2ee/web-app_3_1.xsd"
        version="3.1">
    
        <display-name>j2ep</display-name>
        <description>
             A J2EE application implementing a reverse proxy.
        </description>
        <filter>
            <filter-name>Rewriter</filter-name>
            <filter-class>jeus.servlet.reverseproxy.RewriteFilter</filter-class>
            <init-param>
              <param-name>dataUrl</param-name>
              <param-value>/WEB-INF/config/data.xml</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>Rewriter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <filter>
            <filter-name>Proxy</filter-name>
            <filter-class>jeus.servlet.reverseproxy.ProxyFilter</filter-class>
        </filter>
    
        <filter-mapping>
            <filter-name>Proxy</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>

8.2.4. Deploy

위의 설정이 완료된 애플리케이션을 JEUS에 deploy한다. Deploy에 대한 자세한 내용은 JEUS Applications & Deployment 안내서”의 “제4장 애플리케이션 작성 및 Deploy”를 참고한다.

8.3. 설정 예

다음은 몇 가지 경우에 대한 Reverse Proxy/WEB-INF/config/data.xml의 설정 예이다.

CASE 1

Proxy 서버의 호스트명이 "www.proxy.com"이고, 모든 요청에 대해서 "www.server1.com/content"를 Proxy할 "www.proxy.com/index.html"을 요청하면 "www.server1.com/content/index.html"의 내용을 출력하는 설정은 다음과 같다.

<config>
   <single-server>
   <domain-name>www.server1.com</domain-name>
   <send-proxy-specific-headers>true</send-proxy-specific-headers>
   <path>/content</path>
   <rewriting>true</rewriting>
      <accept-everything-rule/>
   </single-server>
</config>

CASE 2

www.proxy.com/remote로의 요청은 www.server1.com을 proxy하고 www.proxy.com/internal로의 요청은 www.server2.com:8080을 proxy한다. 그외의 다른 요청에 대해서 www.server3.com을 proxy하는 설정은 다음과 같다.

<config>
   <single-server>
   <domain-name>www.server1.com</domain-name>
   <rewriting>true</rewriting>
   <directory-rule>
      <path>/remote</path>
   </directory-rule>
   </single-server>
 
   <single-server>
   <domain-name>www.server2.com:8080</domain-name>
   <rewriting>true</rewriting>
   <directory-rule>
      <path>/internal</path>
   </directory-rule>
   </single-server>

   <single-server>
   <domain-name>www.server3.com</domain-name>
   <rewriting>true</rewriting>
   <accept-everything-rule/>
   </single-server>
</config>

CASE 3

모든 요청에 대해 www.server1.com/remote와 www.server2.com/remote로 proxy한다.

<config>
   <cluster-server>
       <server>
           <domain-name>www.server1.com</domain-name>
           <path>/remote</path>
       </server>
       <server>
           <domain-name>www.server2.com</domain-name>
           <path>/remote</path>
       </server>
   <accept-everything-rule/>
   <send-proxy-specific-headers>true</send-proxy-specific-headers>
   </cluster-server>
</config>