제9장 Reverse Proxy

내용 목차

9.1. 개요
9.1.1. 적용 예시
9.2. 사용 방법
9.2.1. Proxy 서버 설정
9.2.2. Context-path 설정
9.2.3. Proxy Filter 설정
9.2.4. Deploy
9.3. 설정 예

본 장에서는 Reverse Proxy의 기본 개념과 사용 방법을 예제를 통해서 설명한다.

9.1. 개요

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

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

9.2. 사용 방법

본 절에서는 JEUS의 Reverse Proxy를 사용하기 위한 방법을 설명한다.

Reverse Proxy를 사용하기 위해서는 webhome\app_home에 ReverseProxy 기능을 활성화시키는 애플리케이션을 디플로이해야 한다. 여기에서는 애플리케이션의 이름을 ReverseProxy라고 가정한다.

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

  1. Proxy 서버 설정

  2. Context Path 설정

  3. Proxy Filter 설정

  4. 디플로이

9.2.1. Proxy 서버 설정

Reverse Proxy 사용을 위해 먼저, ReverseProxy\WEB-INF\config\ 아래에 proxy될 서버의 내용을 담은 data.xml 파일을 생성한다. 이 파일에서 설정하는 내용은 proxy해야 할 실제적인 서버의 내용이다. Proxy할 서버의 주소와 이 서버로 서비스할 규칙(rule)을 설정할 수 있다.

서버의 종류는 다음 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된다.

      • <rewrite>

        절대 주소를 rewrite할지 여부를 결정한다(true|false).

      • <rule>

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

        다음은 요청을 구분하는 JEUS의 proxy 규칙이다.

        항목설명
        <directory-rule>

        특정 디렉터리 요청에 적용되는 규칙이다.

        www.proxy.com이 proxy 서버이고, www.proxy.com/remote/라는 요청에 대해서 특정 서버를 proxy하고 싶을 때 사용한다.

        <accept-everthing-rule>

        <directory-rule>에서 정의된 규칙 이외의 모든 요청에 적용되는 규칙이다.

        각 규칙들은 위에서 순서대로 적용되기 때문에 이 규칙은 가장 마지막 항목에 넣어야 한다.

  • <cluster-server>

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

각 설정은 “9.3. 설정 예”를 참고하여 조금 수정해서 작성한다.

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

[예 9.1] <<proxy-config.dtd>>

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

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

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

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

<!-- server: cluster된 하나의 서버를 말함 -->
<!ELEMENT server (domain-name, rewriting?, path?)>

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

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

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

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

<!-- rewriting: proxy 된 문서 내용 중 link 들 중에 절대 주소를 rewriting 할 지
결정 여부. -->
<!ELEMENT rewriting (#PCDATA)>


9.2.2. Context-path 설정

WEB-INF/jeus-web-dd.xml 파일의 <jeus-web-dd><context-path>를 수정하면 proxy 서비스를 제공할 <context-path>를 설정할 수 있다. "/"로 주면 해당 서버의 아래의 모든 요청에 대해서 적용된다.

9.2.3. Proxy Filter 설정

JEUS는 Reverse Proxy 기능을 다음의 2가지 Filter Class를 통해서 제공하고 있다.

  • ProxyFilter

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

  • RewriteFilter

    해당 서버에서 받은 결과물에서 링크를 자신의 주소로 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 Filter만 사용하거나 ProxyFilter와 Rewriter Filter를 함께 사용할 수 있다.

각 경우에 따라서 WEB-INF/web.xml 파일을 다음과 같이 설정해야 한다.

  • Proxy Filter만 사용할 때

    <?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_2_4.xsd"
        version="2.4">
    
      <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 Filter와 Rewrite Filter를 함께 사용할 때

    <?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_2_4.xsd"
        version="2.4">
    
        <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>

9.2.4. Deploy

위의 설정이 끝난 애플리케이션을 JEUS에 디플로이한다. 디플로이는 “제6장 Web Context(웹 애플리케이션)”를 참조한다.

9.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</directory>
   <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/interna로의 요청은 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>