블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 

'프록시'에 해당되는 글 2

  1. 2018.11.23 Istio #2 - Envoy proxy
  2. 2013.03.19 Eclipse에서 Spring Maven 개발환경 설정하기
 


Istio #2 - Envoy Proxy


조대협 (http://bcho.tistory.com)

그럼 앞에서 설명한 서비스 매쉬의 구조를 구현한 Istio를 살펴보기전에, Istio에 사용되는 envoy 프록시에 대해서 먼저 알아보자.
(이 글은 예전에 포스팅한 내용이지만, Istio 글의 흐름상 다시 포스팅 한다.)

Envoy Proxy

먼저 istio에 사용되는 envory proxy를 살펴보자. Envoy 프록시는 Lyft사에서 개발되었으면 오픈소스로 공개되었다.

기존 프록시 L4기능 뿐 아니라 L7 기능도 지원하면서 HTTP 뿐아니라 HTTP 2.0,TCP,gRPC까지 다양한 프로토콜을 지원한다.


성능 지표를 보면 아래 Twillo에서 2017년에 테스트 한 자료를 참고할만 한데, (원본 https://www.twilio.com/blog/2017/10/http2-issues.html) HAProxy 보다 약간 느린것을 확인할 수 있다. 아무래도 L4가 아닌 L7단의 로드밸런서이다 보니 다소 성능 감소는 부담해야 한다.




(참고. 위의 문서를 보면 Envoy HTTP2 의 성능이 낮게 나오는데, 이는 Envory 자체 문제라가 보다는 HTTP/2가 Connection을 reuse하는 특성에서 온다고 볼 수 있는데, 성능에 대한 이슈가 있는 만큼 HTTP/2를 사용할 경우에는 별도의 검증 등이 필요하리라 본다.)


주요 기능적인 특성을 보면 다음과 같다.


  • HTTP, TCP, gRPC 프로토콜을 지원

  • TLS client certification 지원

  • HTTP L7 라우팅 지원을 통한 URL 기반 라우팅, 버퍼링, 서버간 부하 분산량 조절등

  • HTTP2 지원

  • Auto retry, circuit breaker, 부하량 제한등 다양한 로드밸런싱 기능 제공

  • 다양한 통계 추적 기능 제공 및 Zipkin 통합을 통한 MSA 서비스간의 분산 트렌젝션 성능 측정 제공함으로써 서비스에 대한 다양한 가시성 (visibility)을 제공

  • Dynamic configuration 지원을 통해서, 중앙 레파지토리에 설정 정보를 동적으로 읽어와서 서버 재시작없이 라우팅 설정 변경이 가능함

  • MongoDB 및 AWS Dynamo 에 대한 L7 라우팅 기능 제공


등 매우 다양한 기능을 제공한다.

Envoy 배포 아키텍처

Envoy 프록시는 배포 위치에 따라서 다양한 기능을 수행할 수 있는데, 크게 다음과 같이 4가지 구조에 배포가 가능하다.


<그림. Envoy 배포 방식>

Front envoy proxy

특정 서비스가 아니라, 전체 시스템 앞의 위치하는 프록시로, 클라이언트에서 들어오는 호출을 받아서 각각의 서비스로 라우팅을 한다. URL 기반으로 라우팅을 하는 기능 이외에도, TLS(SSL) 처리를 하는 역할들을 할 수 있다. 통상적으로 nginx나 apache httpd가 리버스프록시로 이 용도로 많이 사용되었다.

Service to service ingress listener

특정 서비스 앞에 위치하는 배포 방식으로 서비스로 들어오는 트래픽에 대한 처리를 하는데, 트래픽에 대한 버퍼링이나 Circuit breaker 와 같은 역할을 수행한다.

Service to service egress listener

특정 서비스 뒤에서 서비스로부터 나가는 트래픽을 통제 하는데, 서비스로 부터 호출 대상이 되는 서비스에 대한 로드 밸런싱, 호출 횟수 통제 (Rate limiting)와 같은 기능을 수행한다.

External service egress listener

내부서비스에서 외부 서비스로 나가는 트래픽을 관리하는 역할인데, 외부 서비스에 대한 일종의 대행자(Delegator)와 같은 역할을 한다.


시스템 앞 부분이나 또는 시스템을 구성하는 서비스의 앞뒤에 배치할 수 있는 구조지만, 서비스 앞뒤로 붙는다고 실제로 배포를 할때 하나의 서비스 앞뒤로 두개의 envoy proxy를 배치하지는 않는다.

다음과 같이 하나의 서비스에 하나의 Envoy를 배치 한후, ingress/egress 두 가지 용도로 겸용해서 사용한다.



Envoy 설정 구조

다음은 Envoy 설정 파일을 살펴 보자

Envoy의 설정은 크게 아래 그림과 같이 크게 Listener, Filter, Cluster 세가지 파트로 구성된다.



  • Listener
    Listener는 클라이언트로 부터 프로토콜을 받는 부분으로, TCP Listener, HTTP Listener 등이 있다.

  • Filter
    Filter는 Listener 로 부터 많은 메시지를 중간 처리하는 부분으로, 압축이나 들어오는 Traffic 에 대한 제한 작업등을 한후, Router를 통해서 적절한 클러스터로 메시지를 라우팅 하는 역할을 한다.

  • Cluster
    Cluster는 실제로 라우팅이 될 대상 서버(서비스)를 지정한다.


이렇게 Listener를 통해서 메시지를 받고, Filter를 이용하여 받은 메시지를 처리한 후에, 라우팅 규칙에 따라서 적절한 Cluster로 라우팅을 해서 적절한 서비스로 메시지를 보내는 형식이다.


Envoy 설치

Envoyproxy를 빌드하고 설치하는 방법은 여러가지가 있다. 소스코드로 부터 빌드를 하는 방법이나 이미 빌드된 바이너리를 사용해서 설치하는 방법 그리고 이미 빌딩된 도커 이미지를 사용하는 방법이 있다.

소스코드로 빌드하는 방법의 경우에는 bazel (make와 같은 빌드 도구) 빌드를 이용해서 빌드해야 하고, 빌드된 바이너리는 특정 플랫폼에 대해서만 미리 빌드가 되어 있기 때문에, 모든 플랫폼에 사용하기가 어렵다.

마지막으로는 도커 이미지 방식이 있는데, 이 방식이 배포면에서 여러모로 편리하기 때문에 도커 이미지를 이용한 배포 방식을 설명하도록 하겠다.


다음 명령어 처럼

docker pull을 이용하여 envoyproxy 도커 이미지 최신 버전을 가지고 오고, 다음 docker run 명령을 이용하여, 해당 이미지  (envoyproxy/envoy:latest)를 기동한다. 이때 -p 10000:10000 포트를 도커의 10000번 포트를 VM의 10000포트로 포워딩하도록 설정한다.


$ docker pull envoyproxy/envoy:latest
$ docker run --rm -d -p 10000:10000 envoyproxy/envoy:latest
$ curl -v localhost:10000


배포가 끝났으면, curl을 이용하여 localhost:10000번에 호출 하는 테스트를 하도록 한다.

설정에는 디폴트로, 10000 번 포트로 들어오는 모든 트래픽을 *.google.com으로 라우팅 하도록 설정되어 있다.


원본 설정 파일은 https://github.com/envoyproxy/envoy/blob/master/configs/google_com_proxy.v2.yaml 에 있고,  상세 내용을 보면 아래와 같다.


  • admin:
    이 부분은 envoyproxy의 admin 서버를 기동하는 부분으로, envoy 서버의 각종 설정이나 상태 정보를 127.0.0.1:9901로 들어오는 요청은 admin 기능으로 라우팅하도록 한다.

  • static_resources:
    Listener와 Filter 설정에 해당하는 부분으로, 아래 부면, listeners로 정의가 되어 있고 socket_address 부분에 0.0.0.0에 포트 10000 으로 들어오는 요청을 처리하도록 하였다.

    다음 filter_chain 부분에 filter들을 연속해서 정의하는데, http_connection_manager를 이용하여 모든 트래픽을 service_google이라는 클러스터로 라우팅 하도록 설정하였다.

  • clusters:
    마지막으로 clusters 부분에는 “service_google”이라는 클러스터를 정의했으며, 이 호스트의 URL은 google.com 443 포트로 정의하였다.


admin:

access_log_path: /tmp/admin_access.log

address:

  socket_address: { address: 127.0.0.1, port_value: 9901 }


static_resources:

listeners:

- name: listener_0

  address:

    socket_address: { address: 0.0.0.0, port_value: 10000 }

  filter_chains:

  - filters:

    - name: envoy.http_connection_manager

      config:

        stat_prefix: ingress_http

        route_config:

          name: local_route

          virtual_hosts:

          - name: local_service

            domains: ["*"]

            routes:

            - match: { prefix: "/" }

              route: { host_rewrite: www.google.com, cluster: service_google }

        http_filters:

        - name: envoy.router

clusters:

- name: service_google

  connect_timeout: 0.25s

  type: LOGICAL_DNS

  # Comment out the following line to test on v6 networks

  dns_lookup_family: V4_ONLY

  lb_policy: ROUND_ROBIN

  hosts: [{ socket_address: { address: google.com, port_value: 443 }}]

  tls_context: { sni: www.google.com }


다음글에서는 Istio에 대해서 알아보도록 하겠다.



Spring Maven 개발 환경 설정

 

※주 : Maven Eclipse Juno 버전 이상이 설치된 것을 가정으로 함


Pre-quest

이클립스 설치 : Juno  버전 이상 사용

Maven 플러그인 설치 : Help > Eclipse Market Place에서 Maven Integration for Eclipse 설치



1. 이클립스 설정

일반 기업의 경우 방화벽이 있는 경우가 많기 때문에 반드시 Proxy 설정 확인하고 시작할것

 

   eclipse proxy 설정

: Juno 버전 이상 Windows > Preference > Network 메뉴에서 Active Provider Mannual로 변경 한후, HTTP HTPS proxy entriy에 서버를 등록

   Maven에서도 해줘야 함

: Juno 버전 이상 Windows > Preference > Maven 메뉴에서 setting.xml의 위치를 찾아서 다음과 같이 proxy 설정 부분을 추가 작성

<proxies>

    <proxy>

      <id>optional</id>

      <active>true</active>

      <protocol>http</protocol>

      <username>proxyuser</username>

      <password>proxypass</password>

      <host>프록시 서버 주소</host>

      <port>포트</port>

    </proxy>

  </proxies>

         <proxy>

                  <id>samsung</id>

                  <active>true</active>

                  <protocol>http</protocol>

                  <host>프록시 서버 주소</host>

                  <port>포트</port>

         </proxy>

         <proxy>

                  <id>samsung-https</id>

                  <active>true</active>

                  <protocol>https</protocol>

                  <host>프록시 서버 주소</host>

                  <port>포트</port>

         </proxy>

  </proxies>

 

 

2. 프로젝트 생성

 

이클립스에서 New > Other > Maven > Maven Project 선택

Filter에서 maven-archetype-quickstart 를 선택

Ÿ   group id 에는 프로젝트명 (일반적으로 패키지명으로 맵핑됨)

Ÿ   artifact id에는 디렉토리명

Ÿ   Package에는 패키지 명을 정의

 

또는 아래와 같이 Command Line으로 생성후, 나중에 이클립스에서 Import

mvn archetype:generate -DarchetpeArtifacId=maven-archetype-quickstart -DarchetypeVersion=1.1  -DinteractiveMode=false  -DgroupId=spring-tutorial -DartifactId=ch1_simple -Dversion=1.0-SNAPSHOT -Dpackage=bcho.spring.tutorial.ch1

 

3. pom.xml 편집

spring-beans, spring-core, spring-context depdency에 추가

그리고, JUnit4로 테스트를 진행 예정이기 때문에 spring-test와 junit 4.11을 추가

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>spring-tutorial</groupId>

  <artifactId>helloworld</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>jar</packaging>


  <name>helloworld</name>

  <url>http://maven.apache.org</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  </properties>


  <dependencies>

 

  <dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-beans</artifactId>

   <version>3.2.2.RELEASE</version>

  </dependency>

  <dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-context</artifactId>

  <version>3.2.2.RELEASE</version>

  </dependency>

  <dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-core</artifactId>

  <version>3.2.2.RELEASE</version>

  </dependency>

  <dependency>

  <groupId>junit</groupId>

  <artifactId>junit</artifactId>

  <version>4.11</version>

  </dependency>

  <dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-test</artifactId>

  <version>3.2.2.RELEASE</version>

  </dependency>

  </dependencies>

</project>


4. context.xml 작성

/src/main/resources/spring-context.xml 을 작성하고, bean을 정의

 

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

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">



<bean id="hello" class="spring_tutorial.helloworld.HelloWorld">

<property name="name" value="Byungwook"></property>

</bean>

</beans>

 

5. Bean Class 작성

main/java/HelloWorld.java 작성

package spring_tutorial.helloworld;


public class HelloWorld {

    String hello;

    public void setName(String name){

           this.hello = name;

    }

    public String getName(){

    return hello;

    }

    public void sayHelloWorld(){

           System.out.println("hello :"+hello);

    }

}

 

6. 수행 클래스 작성

package spring_tutorial.helloworld;


import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;


/**

 * Hello world!

 *

 */

public class App 

{

    public static void main( String[] args )

    {

      ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");

           

           HelloWorld hw = (HelloWorld)context.getBean("hello");

           hw.sayHelloWorld();

    }

}


7. Run as 에서 Java Application으로 실행

8. 테스트 클래스 작성


package spring_tutorial.helloworld;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:spring-context.xml")


public class AppTest 

{

  @Autowired HelloWorld hello;

  @Test public void helloTest(){

 assert( hello.getName().equals("Byungwook"));

  }

}

9. 테스트 실행
프로젝트에서 Run as > Maven test 를 선택
또는 프롬프트 상에서 mvn test를 수행

10. Goal에 대해서
Goal은 ant의 target과 같은 개념으로, mvn의 경우 pre-defined goal을 가지고 있다.
mvn compile : 컴파일 수행
mvn package : jar 파일로 패키징
mvn test : JUnit 테스트 수행
mvn install : local repository (PC내의 디렉토리)에 해당 jar 파일을 저장 
mvn deploy  : remote repository (Nexus)에 jar 파일 저장
mvn clean : 컴파일 내용 모두 삭제
mvn build : 위의 예제는 일반적은 spring-java 코드로 build goal을 제공하지 않는다. (Eclipse에서 run as하면 나와서 헷갈리게 만드는데) build goal을 지원하는 프로젝트를 만들거나 또는 pom.xml안에 인위적으로 build goal을 지정해줘야 한다.

install과 deploy에 의해서 repository에 저장된 jar파일들은 dependency 체크등에서 불려서 사용될 수 있다.




참고 사항

1. 가끔 Eclipse에서 Maven을 수행 시키면 아래와 같은 에러가 나는 경우가 있다.

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile (default-testCompile) on project helloworld: Compilation failure

[ERROR] Unable to locate the Javac Compiler in:

[ERROR] C:\Program Files\Java\jre7\..\lib\tools.jar


이는 Eclipse가 loading될때, JDK에 있는 java가 아니라 JRE에 있는 java를 가지고 수행되었을 경우 발생하는데, maven이 javac compiler를 찾을 수 없다는 메세지이다.

이를 해결하려면 eclipse.ini에 JDK의 경로를 아래와 같이 명시적으로 지정해줘야 한다.

-vm

C:\Program Files\Java\jdk1.7.0_07\bin\javaw.exe

2. 위의 설정을 했음에도 불구하고, eclipse에서 안될때가 있는데, 이 경우에는 Runs As에서 메이븐 설정에서 JRE 부분에 JDK 경로를 지정해주면 된다.



3. 디렉토리 구조 참고





http://www.mkyong.com/spring3/spring-3-hello-world-example/

http://androi.tistory.com/37