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


Archive»



Data Preprocessing in ML Pipeline


본글은 구글 클라우드 블로그에 포스팅한 글을, 재 포스팅 허가를 받은 후 포스팅한 글입니다.

다른 좋은 글들도 많으니 아래 출처 링크를 참고해 주새요

출처 링크


머신러닝 파이프라인에서, 데이터는 모델 학습 및 서빙의 입력에 알맞게 가공되어야 한다. 이를 전처리라고 하는데, 이번 글에서는 전처리에 대한 개념과 이에 대한 구현 옵션등에 대해서 알아보도록 한다.

처리 단계별 데이터 분류

머신러닝에서 데이터 전처리는 모델 학습에 사용되는 데이터 형태로 데이터를 가공하는 과정을 이야기한다.

데이터 전처리는 여러 단계로 이루어지는데, 단계별로 처리된 데이터에 대해서 다음과 같이 명명한다. 

Raw data

초기에 수집된 원본 데이터로 분석이나, 머신러닝 학습 용도로 전혀 전처리가 되지 않은 데이터를 의미한다.

하둡과 같은 데이터 레이크에 저장된 데이터나, 기본적인 처리를 통해서 테이블 구조로 데이터 레이크에 저장된 데이터가 Raw 데이터에 해당한다.

Prepared data

Prepared data는 Data engineering 전처리에 의해서, 학습을 위한 데이터만 추출한 서브셋 데이터를 의미한다. 예를 들어 서울 20대 사용자의 구매 패턴을 머신러닝 모델로 만들고자 할때, 서울 20대 사용자 데이터만 추출한 경우 이 데이터를 Prepared data라고 한다. 단순하게 서브셋만을 추출하는 것이 아니라, 깨끗한 상태의 데이터로 정재된 데이터인데, 정재의 의미는 비어 있는 행이나 열을 삭제한 데이터를 의미한다. 

Engineered feature

이렇게 정제된 데이터는 머신러닝 학습과 서빙에 적절한 형태로 재가공 되어야 하는데 이를 Feature Engineering 이라고 한다. 예를 들어 숫자와 같은 값을 0~1 사이로 맵핑 시키거나 , 카테고리 밸류 예를 들어 남자/여자를 0,1과 같은 값으로 맵핑 시키고, 전체 데이터를 학습,평가용으로 7:3 분할하여 저장하는 것이 이에 해당 한다. 



<그림. 데이터 전처리 단계 및 단계별 생성된 데이터 >

데이터 전처리 기법

그러면, 이 데이터 전처리 과정에서 구체적으로 어떤 기법으로 데이터를 처리할까? 몇가지 대표적인 기법을 정리해보면 다음과 같다. 

  • Data cleansing : 데이터에서 값이 잘못되거나 타입이 맞지 않는 행이나 열을 제거하는 작업을 한다. 

  • Instance selection & partitioning : 데이터를 학습,평가,테스트용 데이터로 나누는 작업을 한다. 단순히 나누는 작업 뿐만 아니라, 데이터를 샘플링 할때, 그 분포를 맞추는 작업을 병행한다. 예를 들어 서울/대구/부산의 선거 투표 데이타가 있을때, 인구 비율이 9:2:3이라고 할때, 전체 인구를 랜덤하게 샘플링해서 데이타를 추출하는 것이 아니라, 서울/대구/부산의 인구 비율에 따라서 서울에서 9, 대구에서 2, 부산에서 3의 비율로 샘플링을 할 수 있다. 이를 stratified partitioning 이라고 한다. 또는 데이터 분포상에서 특정 카테고리의 데이터 비율이 적을때, 이 카테고리에 대해서 샘플의 비율을 높이는 minority classed oversampling 등의 기법을 이 과정에서 사용한다. 

  • Feature tuning : 머신러닝 피처의 품질을 높이기 위해서 0~1값으로 값을 normalization 시키거나, missing value를 제거 하거나, 아웃라이어등을 제거하는 등의 과정을 수행한다.

  • Representation transformation : 피처를 숫자로 맵핑 시키는 작업을 한다. 카레고리컬 피처를 one hot encoding 등을 통해서 숫자로 맵핑하거나, 텍스트를 embedding 을 통해서 숫자로 변환하는 작업등을 수행한다. 

  • Feature extraction : PCA와 같은 차원 감소 기법을 이용하여, 전체 피처의 수를 줄이는 작업을 수행하거나, 피처를 해시값으로 변환하여, 더 효율적인 피쳐를 사용하는 작업을 한다. . 

  • Feature selection : 여러개의 피처(컬럼)중에 머신러닝에 사용할 피처만을 선별한다. 

  • Feature construction : 기존의 피처를 기반으로 polynomial expansion 이나,  feature crossing 등의 기법을 이용하여 새로운 피처를 만들어낸다. 

데이터 전처리 단위

Instance level transformation & Full pass transformation

데이터 전처리를 할때 어떤 단위로 데이터를 전처리 할지에 대한 정의이다. 예를 들어 숫자 데이터의 값을 0~1 사이로 맵핑하고자 하면, 그 데이터의 최소/최대 값을 알아야 0~1사이로 맵핑할 수가 있는데, 최소/최대값을 추출하려면, 전체 데이터에 대한 스캔이 필요하다. 반대로 NULL 값을 0으로 변환하는 작업은 전체 데이터에 대한 스캔이 필요없고 개별 데이터만 변환하면 된다. 앞에 설명한 전체 데이터에 대한 스캔이 필요한 방식을 full pass transformation 이라고 하고, 전체 데이터를 볼 필요 없이 개별 데이터에 대해 변환하는 작업을 instance level transformation이라고 한다. 


Window aggregation

전체 데이터의 볼륨이 클 경우 이를 윈도우 단위로 잘라서 처리할 수 있는 방법이 있는데, 예를 들어 10분 단위로 데이터를 처리해서, 10분 단위로 최소/최대 값을 구하거나 또는 10분 단위로 어떤 값의 평균값을 대표값으로 사용하는 것들이 이에 해당한다. 

일반적으로 입력값은 (entity, timestamp, value) 형태가 되며, 전처리된 출력 값은 다음과 같이. (entity, time_index, aggregated_value_over_time_window) 엔터티(피쳐)에 대해서 윈도우별로 처리된 값을 저장하는 형태가 된다.  보통 이런 window aggregation 방식은 리얼 타임 스트리밍 데이터에서 시간 윈도우 단위로 데이터를 처리하는 경우에 많이 사용이 되며, Apache Beam과 같은 스트리밍 프레임워크를 이용하여 구현한다. 

구글 클라우드에서 데이터 전처리 방식

이러한 데이터 전처리는 다양한 컴포넌트를 이용해서 처리할 수 있는데, 어떤 방식이 있는지 살펴보기 전에 먼저 구글 클라우드 기반의 머신러닝 학습 파이프라인 아키텍처를 살펴보자.  아래는 일반적인 구글 클라우드 기반의 머신러닝 파이프라인 아키텍처이다. 


<그림. 구글 클라우드 플랫폼 기반의 일반적인 머신러닝 학습 파이프라인 아키텍처 >


  1. 원본 데이터는 빅쿼리에 저장된다. (테이블 형태의 데이터가 아닌 이미지나 텍스트등은 클라우드 스토리지(GCS)에 저장된다.)

  2. 저장된 원본 데이터는 Dataflow를 이용해서 머신러닝 학습에 알맞은 형태로 전처리 된다. 학습/평가/테스트 셋으로 나누는 것을 포함해서, 가능하면 텐서플로우 파일형태인 *.tfrecord 형태로 인코딩 된후에, GCS 에 저장된다. 

  3. 텐서플로우등으로 모델을 개발한 후에, trainer-package로 패키징을 하고, AI Platform 트레이닝에 이 모델을 업로드 한다. 업로드된 모델을 앞서 전처리된 데이터를 이용해서 학습이되고, 학습이 된 모델은 GCS에 저장된다. (텐서플로우에서 SavedModel로 저장한다.)

  4. GCS 에 저장된 모델은 AI Plaform 서빙 엔진에 배포되고 REST API를 이용하여 서빙된다.

  5. 클라이언트에서는 이 REST API를 이용하여 학습된 모델에 대한 서빙을 이용한다.

  6. 전체 워크플로우에 대한 파이프라인 관리는 Apache Airflow 매니지드 서비스인 Composer 를 이용한다. 또는 머신러닝에 특화된 파이프라인이기 때문에, AI Platform pipeline을 사용하는 것이 좋다.

Option A: 빅쿼리에서 데이터 전처리

일반적으로 빅쿼리를 이용한 전처리는 다음과 같은 시나리오에 유용하다.

  • Sampling : 데이터에서 랜덤하게 일부 데이터셋만 가지고 오는 용도

  • Filtering : 학습에 필요한 데이터만 WHERE 문을 이용해서 가지고 오는 용도

  • Partitioning : 데이터를 학습/평가/테스트 용도로 나누는 용도

주로 빅쿼리는 Dataflow로 데이터를 인입하기 전체 최초 전처리 용도로 사용이 되는데, 주의할점은 빅쿼리에 전처리 로직이 많을 경우 향후 서빙에서 재 구현이 필요할 수 있다. 무슨 이야기인가 하면, 서빙시에도 입력 데이터에 대한 동일한 전처리가 필요한데, 빅쿼리에서 SQL로 작성한 전처리 로직은 서빙시에는 사용할 수 없기 때문에, 자바나 파이썬으로 전처리 로직을 다시 구현해야 하는 이중작업이 될 수 있다. 물론 서빙이 빅쿼리에 있는 데이터를 사용하는 배치 서빙일 경우 문제가 없지만, 리얼타임으로 단건의 데이터에 대해서 서빙을 하는 경우에는 빅쿼리에서 서빙용 데이터를 전처리할 수 없다. 


그럼에도 불구하고 배치 서빙용인 경우 전처리를 빅쿼리를 이용할 경우 편리하고 특히 Dataflow 에 데이터를 입력하기전에 Full pass transformation 이 필요한 전체 통계 데이터 (예를 들어 평균,분산,최소/최대값)은 SQL을 통해서 쉽게 뽑아낼 수 있는 장점이 있다. 

Option B: Dataflow 에서 데이터 전처리

복잡한 데이터 변환 로직이 있는 경우등에 효율적으로 사용할 수 있는 방식인데, Instance level transformation 뿐만 아니라, full pass transformation, 그리고 window aggregation 타입 모두를 지원할 수 있다.

Dataflow는 Apache Beam 오픈소스 기반의 런타임이지만, 다양한 구현 방식을 지원하고 있다.

  • Apache Beam을 사용하는 방법 : 가장 일반적인 방식으로 Apache Beam Java/Python SDK 을 이용하여 데이터 변환 로직을 구현할 수 있다.  

  • Tensorflow Transformation 을 사용하는 방법 : 텐서플로우의 경우 Tensorflow Transformation (이하 TFT) 이라는 이름으로 데이터 변환 프레임워크를 제공한다. TFT는 Apache Beam 기반으로 동작하는데, 텐서플로우 코드를 기반으로 하기 때문에, 머신러닝 개발자 입장에서는 접근이 상대적으로 쉬운 장점이 있다. 

  • Dataflow SQL을 사용하는 방법 : 앞의 두 방식의 경우에는 Java나 Python 기반의 코딩이 필요한데, 이런 코딩 없이 Window aggregation이나, 기타 복잡한 로직을 구현하고자 할때 사용할 수 있는 방식이 Dataflow SQL이다.SQL을 사용하여 구현하지만, Dataflow의 함수등을 사용할 수 있는 장점이 있다. 

  • Dataflow Template + UDF를 사용 하는 방법 : 복잡한 변환이 아니라 단순한 맵핑이나 문자열 변환들을 어렵지 않게 구현하는 방식으로 Dataflow는 Pre-built in 된 Template을 제공한다. 이 템플릿 중에는 비즈니스 로직을 자바스크립트로 넣을 수 있는 UDF 라는 방식을 지원하는데, Apache Beam 형태로 구현할 필요 없이 단순한 변환 로직을 자바스크립트로 구현하여 GCS에 파일을 저장하고, 설정 정보에서 자바 스크립트 파일만 지정하면되기 때문에, 쉽게 사용할 수 있다. 


서빙시에도 다양한 아키텍처 구현이 가능한데, Pub/Sub 큐를 통해서 데이터를 실시간으로 인입한 데이터를 머신러닝 모델로 서빙한후에, Pub/Sub으로 내보내는 near realtime 서빙이 가능하고 또는 bigtable에 서빙 결과를 저장하여 마치 serving 결과에 대한 캐쉬식으로 사용하는 구조도 가능하다.




<그림. 스트림 데이터를 이용하여 서빙을 제공하는 아키텍처>

Option C: Tensorflow 모델 내에서 데이터 전처리

아니면 데이터 전처리를 Tensorflow 모델 코드내에서 하는 방식이 있다.

  • feature_column 를 이용하여 피처를 임베딩하거나, 버킷화 하는 방식이 있고

  • 아니면 데이터를 피딩하는  input functions(train_input_fn, eval_input_fn, and serving_input_fn) 안에 데이터 전처리 로직을 구현하는 방법이 있다. 

  • Custom estimator를 사용하는 경우에는 model_fn 자체에 데이터 전처리 로직을 넣을 수 있다. 

이렇게 텐서 플로우 코드단에 전처리 기능을 넣는 경우는 Instance level transformation은 가능하지만 다른 방식에 대해서는 불가능하다. 그렇지만 이미지 데이터를 학습전에 rotation하거나 flip 하는 argumentation 등은 텐서플로우 코드에서 하게 되면 동적으로 데이터를 학습 단계에 argumentation할 수 있기 때문에 효율이 좋은 장점이 있다. 

Option D: DataPrep을 이용한 데이터  전처리

구글 클라우드 플랫폼에서는 데이터의 특성을 분석하고 간단한 변환을 지원하기 위한 wrangling 도구로 DataPrep을 제공한다. Engineered feature 단계까지 데이터를 가공하는 것은 어려울 수 있겠지만, Raw data를 Prepared data 형태로 cleansing 하는 용도로는 충분히 사용할 수 있으며, 특히 시각화를 통한 데이터 분포나 아웃라이어 분석이나 단순 변환등에는 효과적으로 사용할 수 있다.


<그림 DataPrep 을 이용한 Wrangling 과정 예시> 

Option E: DataProc을 이용한 데이터 전처리

DataProc은 Hadoop/Spark 에 대한 구글 매니지드 서비스이다. Apache Beam을 사용하는 Dataflow와 같이 코딩을 기반으로 한다는 점은 같지만, 기존에 Hadoop/Spark 에코 시스템에 익숙한 사용자들의 경우에는 기존의 에코 시스템과 개발 코드를 재활용할 수 있다는 장점을 가지고 있다. 

데이터 전처리시 고려할점

그러면 이러한 기술을 이용해서 데이터를 전처리할때, 고려해야하는 점은 무엇이 있을까?

학습/서빙 데이터에 대한 스큐(skew)

모델을 학습하여, 서비스에 배포한후에, 향후 들어오는 데이터로 서빙을 하게 되는데, 이때 학습에서 사용한 데이터와 서빙시 사용한 데이터의 특성이 다를때 이를 training-serving skew 라고 한다. 

예를 들어 피처 A가 학습시에 범위가 1~255 였는데, 서빙시에 1~500 사이로 들어오게 되면 이 모델의 서빙 결과는 정확하지 못하게 된다.

(참고 : 이런 문제를 해결하기 위해서 데이터의 분포나, 수학적 통계값을 저장해 놓은 후에, 서빙전에 검증하는 방식을 사용할 수 있으며 이는 Tensorflow data validation으로 구현이 가능하다. )

Full pass transformation

Option C의 텐서플로우 모델내의 데이터 변환 로직은 Full pass transformation을 지원하지 않기 때문에, feature scaling이나, normalization 적용이 불가능하다. 이러한 전처리 기법은 최소/최대값등의 통계 데이터가 필요한데, 이러한 데이터는 모델 학습전에 계산되어야 하고, 계산된 데이터는 어디에든 저장되어 있어야 하며, 학습과/서빙 단계에 모두 일관되게 사용될 수 있어야 한다. 

성능 향상을 위한 Up front data loading 

Option C 텐서플로우 모델내에 데이터 변환 로직을 구현할때, 고려해야 하는 사항이다.

모델 코드 상에 데이터 전처리 로직이 있을 경우, 아래 그림과 같이 데이터 변환 작업이 끝나면, 그 데이터로 모델을 학습 시키는 구조가 된다. 


<그림. 데이터 전처리가 모델 학습전에 발생하여, 대기하는 현상>


이 경우에 데이터가 전처리되고 있는 동안에는 학습이 이루어지지 않기 때문에 자원이 낭비되는 문제가 발생하고, 모델의 학습 시간에 전처리 시간까지 포함되기 때문에 전체 학습시간이 상대적으로 오래걸린다. 


Option B의 데이터 플로우를 사용하는 것처럼 미리 여러 학습에 사용될 데이터를 전처리를 해놓거나 아니면 아래 그림과 같이 병렬적으로 데이터 플로우에서 데이터를 전처리하면서 모델은 학습에만 전념하도록 하면, 모델의 전체학습 시간을 줄일 수 있다. 


<그림. 병렬로 데이타 전처리를 해서 모델 학습을 최적화 하는 방식>

이를 up front data loading 이라고 하는데, 텐서플로우에서는 Prefetching, Interleave, Parallel mapping 등을 tf.data.DataSet에서 다양한 방식으로 이를 지원하고 있다. 


Tensorflow Transform

텐서플로우 프레임웍은 이러한 데이터 변환을 위해서 Tensorflow Transform (이하 TFT) 라는 프레임웍을 데이터 전처리 기능을 제공한다. 이 TFT를 구글 클라우드에서 실행하게 되면, Dataflow를 기반으로 실행할 수 있다. (Option B) 

tf.Transform 이라는 패키지로 제공된다. TFT는 instant level transformation 뿐만 아니라, full pass transformation, window aggregation 을 지원하는데, 특히 full pass transformation을 지원하기 위해서 데이터를 변환하기 전에 Analyze 라는 단계를 거치게 된다. 

아래 그림이 TFT가 작동하는 전반적인 구조를 기술한것인데,



Analyze 단계에서는 데이터의 통계적인 특성 (최소,최대,평균 값등)을 추출하고, Transform 단계에서는 이 값을 이용하여, 데이터 변환을 수행한다. 각 단계는 tft_beam.AnalyzeDataset , tft_beam.TransformDataset 로 실행될 수 있으며, 이 두 단계를 tft_beam.AnalyzeAndTransformDataset 로 합쳐서 한번에 실행하는 것도 가능하다. 


  • Analyze 단계 : Analyze 단계에서는 통계적인 값을 Full pass operation 을 통해서 계산해내는 것이외에도, transform_fn을 생성해내는 작업을 한다. transform_fn은 텐서플로우 그래프로, 데이터 변환에 대한 instance level operation 을 계산해낸 통계값을 사용해서 수행한다. 

  • Transform 단계 : 데이터 변환 단계에서는 transform fn을 인입 데이터에 적용하여, instance level로 데이터를 변환하는 작업을 수행한다. 


모델 학습시 데이터에 대한 전처리는 학습 데이터뿐만 아니라, 평가 (Eval) 데이터에도 동일하게 적용이 되어야 하는데, Analyze는 학습데이터에만 적용되서 데이터의 특성을 추출하고, 평가 데이터에는 별도로 Analyze를 수행하지 않고, 학습 데이터에서 추출된 데이터 특성을 그대로 사용한다

TFT pipeline export  

transform_fn으로 구성된 데이터 변환 파이프라인은 내부적으로 텐서 플로우 그래프로 변환이 되는데, 학습된 텐서플로우 모델을 export 하여 SavedModel로 저장할때, 이 transform_fn 그래프가  서빙용 데이터 입력함수인 serving_input_fn에 붙어서 같이 export 된다. 이 말은, 학습에서 사용한 데이터 전처리 로직인 transform_fn이 그대로 서빙단에도 같이 적용된다는 이야기이다. 물론 full-pass transformation에서 계산한 통계값도 상수형태로 저장하게 된다. 그래서 입력값에 대해서 학습과 서빙시 같은 변환 로직을 사용할 수 있게 된다.

데이터 전처리 옵션 정리

앞서 설명한 데이터 변환 전처리 옵션을 Instance level transformation, full pass level transformation, window aggregation 에 따라 정리해보면 다음과 같다. 


Disclaimer

본 글의 작성자는 Google 직원입니다. 그러나 본 글의 내용은 개인의 입장에서 작성된 글이며, Google의 입장을 대변하지 않으며, Google이 본 컨텐츠를 보장하지 않습니다.


References






Instance-level transformation

(stateless transformation)

Full pass during training

instance -level during serving

(stateful transformation)

Real-time (window) aggregations

during training and serving 

(streaming transformation)

배치 서빙

온라인 서빙

배치 서빙

온라인 서빙

배치 서빙

온라인 서빙

BigQuery (SQL)

OK

같은 데이터 변환 로직을 학습과 서빙 단계에 적용 가능

가능은 하지만 권장하지 않음


서빙시에는 BigQuery가 아니라 다른 방식으로 데이터 변환 로직을 구현해야 하기 때문에 결과적으로 학습/서빙 Skew를 유발할 수 있음

가능


BigQuery에서 수학적 통계값(최소/최대)를 계산하여, 이 값을 이용하면 가능하다.

그러나 계산된 값을 별도로 저장해서 학습/서빙시에 사용해야 하기 때문에 구현이 번거롭다.

N/A

가능은 하지만 권장하지 않음


BigQuery의 윈도우 함수등을 이용하여 구현은 가능하지만, 서빙시에는 BigQuery가 아닌 다른 툴로 구현을 해야 하기 때문에 학습/서빙 Skew가 발생할 수 있음

Dataflow (Apache Beam)

OK

서빙시 데이터가 Pub/sub을 통해서 데이터 블로우로 들어오면 가능하지만, 그렇지 않은 경우 학습/서빙 데이터간 Skew가 발생할 수 있음

가능


Dataflow에서 수학적 통계값(최소/최대)를 계산하여, 이 값을 이용하면 가능하다.

그러나 계산된 값을 별도로 저장해서 학습/서빙시에 사용해야 하기 때문에 구현이 번거롭다.

OK


동일한 Apache Beam 기반의 데이터 변환 로직이 학습을 서빙시 적용이 가능함

Dataflow (Apache Beam + TFT)

권장함


학습과 서빙의 Skew를 방지할 수 있고, 학습/서빙전 데이터를 미리 준비할 수 있음

권장함


데이터 변환 로직과, 모델 학습시에 계산된 통계 결과 텐서플로우 그래프 형태로 저장되서, 서빙 모델을 export할시에 같이 저장됨

Tensorflow
(input_fn & serving_input_fn)

가능은 하지만 권장하지 않음


학습과 서빙 효율성을 생각하면, 학습전에 데이터를 변환하는게 좋음

가능은 하지만 권장하지 않음


학습과 서빙 효율성을 생각하면, 학습전에 데이터를 변환하는게 좋음

불가능

불가능


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

Data mesh

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


Data mesh는 빅데이터 분석 시스템의 아키텍쳐 스타일로, 마이크로 서비스 아키텍처 (이하 MSA)컨셉과 유사하게 데이터 분석 시스템을 각각의 분산된 서비스 형태로 개발 관리하는 아키텍쳐 모델이다. 

이번 글에서는 차세대 데이터 분석 시스템 아키텍처인 Data mesh에 대해서 알아본다. 

데이터 분석 시스템의 역사

Data mesh에 대해서 이해하려면 기존의 데이터 분석 시스템의 아키텍처와 그 역사에 대해서 이해하라 필요가 있다.데이터 분석 시스템은, DataWare house를 거쳐 현재는 Data Lake 형태가 주류를 이루고 있으며, 차세대로는 Data Mesh가 각광 받고 있다. 각각 아키텍처 스타일을 보면 다음과 같다.

Data warehouse

Data Warehouse는 전통적인 RDBMS 형태에서 데이터를 모아서 분석하는 아키텍처로 파일이나 데이터 베이스 (OLTP)시스템에 저장된 데이터를 일반적으로 ETL이나 CDC 방식으로 Data Warehouse 로 수집한 후에, Data Warehouse에서 데이터를 저장하고 분석하는 방식이다. 


각 비즈니스 부서에 따라서 데이터 분석에 대한 요구 사항이 많을 경우에 Data warehouse 에서 그 부서에만 필요한 데이터를 Data Mart라는 형태의 별도의 분석용 데이터 베이스에 저장하고 비즈니스 부서는 그 Data Mart 만 사용하는 구조를 사용하는 경우도 있다.


<그림. Data Warehouse의 일반적인 아키텍처>


이 구조는 전통적인 RDBMS 를 활용하는 아키텍처이기 때문에, 테이블처럼 구조화된 데이터 (structure data)를 처리하는데 유리하다. 통상적으로 상용 벤더에서 제공되는 솔루션을 기반으로 구축이 되는데, 

이러한 Data Warehouse는 RDBMS의 특성으로 인하여 빅데이터에 대해서 횡적인 스케일에 한계가 있고, 상용 소프트웨어와 이를 지원하기 위한 인증된 하드웨어를 사용해야하기 때문에 인프라 비용이 높다는 단점을 가지고 있다. 

Data lake

데이터의 볼륨이 늘어나고 다양화되어감에 따라 빅데이터 분석의 요구 사항이 발생하였고, 이러한 문제를 해결하기 위한 아키텍처 구조가 Data Lake 이다. 기존의 Data Warehouse가 테이블 형태의 정형 데이터 (structured data)를 지원했다면, Data Lake는 데이터 형식에 제한이 없이 텍스트나, 이미지등의 비정형 데이터 (unstructured data)에 대한 저장과 분석을 지원하며, JSON과 같은 반정형 데이터 (semi-structured data)까지 지원하는 특성을 가지고 있다.


데이터 처리 측면에서는 Data Warehouse가 기존의 RDBMS등의 소스로 부터 배치로 데이터를 주기적으로 적재하여 처리 및 분석 했다면, Data Lake 기반의 분석 시스템의 특징은 로그 스트림이나 모바일 앱 이벤트로그와 같은 실시간 스트리밍성 데이터에 대한 실시간 처리가 가능하다는 강점을 가지고 있다. 


이렇게 처리된 데이터는 결과 데이터 저장소에 저장이 되서 직접 분석이 되거나 (원본 데이터를 전처리 과정을 끝난 후에, 데이터 분석가들이 Hive 등의 쿼리를 이용해서 분석하는 시나리오를 예를 들 수 있다.) 또는 정형 데이터의 경우에는 기존의 Data Warehouse나 Data Mart로 복사되어 비즈니스 사용자가 좀 더 편리하게 분석할 수 있도록 서비스를 제공한다.



<그림. 일반적인 Data Lake 시스템 아키텍처 예시>


이러한 Data Lake 시스템은 일반적으로 Hadoop/Spark 기반으로 구축되며, Data Lake의 저장소로는 HDFS (Hadoop File System)을 사용하고, 분석 엔진으로는 Hadoop 이나 Spark 을 사용하며, 실시간 스트리밍 처리는 Kafka 와 같은 대용량 큐를 사용하고, 뒷단에 처리 시스템으로 Spark Streaming 등을 사용하는 것이 일반적인 아키텍처 구조이다. 

기존 아키텍처의 문제점

Data Lake나, Data Warehouse 아키텍처 시스템은 하나의 중앙 집중화된 시스템에 데이터를 모으고 분석하는 형태이고, 데이터를 분석하는 주체가 중앙 집중화된 데이터 분석 팀이라는 특징을 가지고 있다. 

한군데 데이터를 모두 모아서 한 조직이 분석한다는 개념은 이론적으로 봤을때는 완벽한 개념으로 보이지만 실무적으로 봤을때 문제가 있는 아키텍처 구조이다.

도메인 지식의 부족

데이터를 중앙에 모아놓고, 데이터 분석팀이 데이터 분석을 진행할때, 이 데이터 분석가들은 데이터 분석 업무 자체에는 전문성을 가질 수 있으나, 데이터의 특성을 이해하기 위한 도메인 지식이 부족하다. 예를 들어 영업/마케팅/회계등. 도메인 지식이 부족한 상태에서는 데이터에 대한 인사이트를 뽑아내기 어렵다. 그래서 현업팀 (각 도메인별)과 커뮤니케이션을 하면서 요구사항 기반으로 도메인의 지식을 습득하여 데이터를 분석하는데, 이로 인해서 하나의 분석팀이 여러 현업팀을 상대해야 하기 때문에, 커뮤니케이션의 지속성이 떨어지고 이로 인해서 도메인에 대한 이해도가 떨어지기 때문에, 결과적으로 신속하고 깊은 수준이 데이터 분석이 어려워 진다.


<그림. 하나의 데이터 분석팀이 여러 현업 부서와 커뮤니케이션 하는 모델>


여기에 더해서 현업팀과 데이터 분석팀은 별도의 부서이기 때문에, 부서간의 커뮤니케이션이 필요한 만큼 서로간의 업무를 이해하기 어려워지게 된다. 특히나 다른 부서는 다른 골을 가지고 있기 때문에, 새로운 데이터 분석 시스템을 올린다고 했을때 쉽게 성공하지 못하는 이유이다. 예를 들어 회사에서 차세대 데이터 분석 시스템을 만들겠다고 데이터 분석팀이 과제를 시작한다고 했을 때, 이 시스템들은 각 현업 부서로 부터 데이터도 수집해와야 하고, 요구 사항을 수집도 해야 하지만, 협업 부서 (예를 들어 영업팀)는 해당 부서의 골(매출 향상)에 가장 최우선 목적을 두는 만큼 기대했던것 만큼 충분한 성과를 이루어내기가 어렵다. 

단일화된 기술 체계

중앙 집중화된 단일 데이타 분석 시스템의 경우에는 단일 시스템이기 때문에 단일화된 분석 기술 솔루션을 사용한다. 그러나 데이터나 도메인의 특성에 따라서 유용한 솔루션이 다른 경우가 많다. 예를 들어 디지털 마케팅의 경우 Adobe 등과 같이 디지털 마케팅에 최적화된 플랫폼등이 있을 수 있는데, 중앙 집중화된 데이터 분석 플랫폼은 이런 다양한 기술 체계를 수용하기가 어렵다. 

예산 및 인력 부족 

데이터 분석팀은 전통적으로 이윤을 남기는 영업 조직이나 마케팅 조직이 아닌 연구성 조직에 가깝기 때문에, 이윤 조직(profit center) 보다는 비용만 쓰는 비용 조직 (cost center)으로 인식 되는 경우가 많다. 데이터 분석으로 인한 수익에 대한 기여 부분을 수치화 하기가 어렵기 때문인데, 독립적으로 수익을 내지 못하기 때문에, 회사내의 투자된 비용에 따라 조직을 운영하는 경우가 많다. 특정 도메인(부서)를 위한 데이터 분석 시스템을 만들기 위해서, 그 부서로 부터 자금을 투자 받아 시스템등을 운영하는 케이스가 있는데, 이도 결과적으로 외부 투자와 지원에 의존적인 구조이다.

이렇다 보니, 일반적으로 데이터 분석 조직은 필요한 인력과 장비에 대한 투자를 충분히 하기가 어렵고 이로 인해서 인력 부족으로 원하는 만큼 데이터 분석을 하기 어려운 경우가 많다. 

실제 필드 상황

이미 실제 필드에서는 이러한 상황을 잘 이해하고 있기 때문에, 중앙 집중화된 데이터 분석 플랫폼이 있음에도 불구하고 각 부서에서 데이터 분석 플랫폼을 따로 만드는 경우가 있다. 예산이 충분하고 데이터에 대한 인사이트가 있는 부서의 경우, 자신의 부서를 위한 데이터 분석 플랫폼을 올리는 경우인데, 

예를 들어 마케팅 팀에서 마케팅 데이터 분석 플랫폼을 새로 만들고, 웹 사이트를 위한 웹 분석 플랫폼, 고객 지원 서비스에 대한 데이터 분석 플랫폼들이 따로 생기는 경우이다. 


Data Mesh

이러한 문제를 해결 하기 위한 데이터 분석 시스템 아키텍처가 Data Mesh 이다. 

기존 데이터 분석 플랫폼이 아래와 같이 모든 도메인에 대해서 단일 시스템과 단일 분석팀을 사용하였다. 이를 Monolithic (모노리틱) 구조라고 한다. 


<그림. Monolithic data analytics platform architecture > 


아래와 같이 도메인(업무) 별로 시스템과 팀을 분리하는 구조를 Data Mesh 아키텍처라고 한다. 데이터 분석팀과 분석 시스템이 각 업무 별로 할당되어 있는 Distributed 구조가 된다. 


<그림. Distributed data analytics platform architecture>


핵심은 부서별로 독립된 시스템과 팀을 보유하고, 데이터 생성자와 소비자 (현업) 역시 한 팀에 묶어서 요구 사항에 대한 반영을 빠르게 하고, 독립된 예산과 팀으로 움직여서 비즈니스 여건에 맞는 시스템을 빠르게 개발할 수 있다는 장점이 있으며, 해당 도메인에 적합한 기술을 사용함으로써, 기술적인 최적화가 가능하다는 장점을 가지고 있다. 

이 아키텍처 구조는 애플리케이션 아키텍처인 마이크로 서비스 아키텍처와 같은 철학과 특징을 가지고 있다. 마이크로 서비스 아키텍처로 업무 단위로 서비스를 나누고 각 팀안에서 기획에서 부터 개발/운영을 모두 담당하게 함으로써 속도를 높이는 아키텍처 라고 하면, Data Mesh도 데이터 도메인별로 팀과 시스템을 나누는 방식으로 해서 해당 데이터에 대한 이해도와 속도를 높이는 장점을 제공하는 것이다. 


Data Mesh는 기동성을 높인다는 의미에서는 장점이 있으나 반대로 단점도 있다.  다음은 몇몇 단점과 함께 Data Mesh 시스템이 가지고 있어야 하는 기능에 대해서 설명한다. 

타부서간의 데이터 조회 지원

특히 다른 부서간에 데이터를 억세스 하고자 할때 이런 단점이 있다. 예를 들어 아래 그림과 같이 마케팅 팀이 세일즈 팀의 데이터를 접근하고자 할때, 전혀 다른 시스템이기 때문에 추가적인 계정 생성과 접근 권한을 받아야 하는 문제가 필요하고, 특히 마케팅 데이터 분석 시스템과 영업 데이터 분석 시스템의 분석 도구나 UI 등이 달라서 타 부서 데이터를 접근하는 것이 어려울 수 있다.  

 

<그림. 타 부서의 데이터를 조회 해야하는 요구 사항>


이런 문제를 해결하기 위해서는 데이터 분석 도구를 통일하는 방법이 있는데, 아래 그림과 같이 분석 시스템 앞단에 분석용 UI (시각화나 쿼리 인터페이스)를 통합하여, 같은 인터페이스로 여러 데이터를 쿼리 하도록 하고, 데이터에 대한 접근 통제도 분석용 UI단에서 하는 방식이다. 


<그림. 여러 부서의 데이터를 통합된 분석용 UI로 조회하는 구조>


이런 UI는 멀티 백앤드를 지원하는 타블루, Looker 등을 사용하여 이기종 데이터 분석단에도 통합된 경험을 제공할 수 있다. 유사한 오픈소스로는 Hue 등이 있다.

타부서 데이터에 대한 통합 데이터 분석

이렇게 다른 부서의 데이터를 조회하게 하도록 지원하더라도, 다른 부서 데이터를 참고해서 (JOIN)해서 데이터를 분석하고자 하는 요건이 있을 수 있다. 예를 들어 마케팅 캠페인을 한 사용자 목록과 세일즈 데이터를 JOIN 해서, 마케팅이 실제 판매에 어떤 영향을 주었는지등을 분석하는 시나리오이다. 

 

<그림. 타 부서의 데이터를 JOIN하여 분석하는 페더레이션 시나리오>


이 시나리오는 각각 독립적인 두개의 이기종 데이터 분석 시스템간의 Federation을 요구로 한다. 기존에는 이런 시나리오를 ETL등을 이용해서, 특정 테이블만 상대쪽에 복사해놓고 하는 방법을 사용하였다. 지금도 유효한 방법이지만, 플랫폼이 지원해준다면, 별도로 데이터를 복사하지 않고 문제를 풀어나갈 수 있다. 


예를 들어 구글의 BigQuery의 경우 특정 데이터셋(테이블의 집합)을 타 부서의 프로젝트로 공유를 해줄 수 있다.  이 경우 별도의 ETL 작업이 불필요 하며, 이 기종 분석 시스템에 대해서도 구글의 MySQL/PostgreSQL 매니지드 서비스인 CloudSQL에 저장되어 있는 데이터나 구글의 NoSQL인 BigTable에 있는 데이터를 쿼리할 수 있다. (이를 Federation 이라고 한다. )

이 보다 핵심 기능은 GCS (Google Cloud Storage)에 있는 파일을 직접 쿼리할 수 있는데, Parquet 과 같은 파일 포맷을 지원한다. Parquet은 Hive 등에서 데이터 저장 파일포맷으로 사용되는데, 정리해서 이야기 하면 별도의 연동 ETL이 없이 Hadoop eco 시스템으로 구축되어 있는 데이터를 조회하여 통합 (JOIN)분석을 할 수 있다는 이야기가 된다. 


이렇게 서로 다른 시스템간의 데이터를 서로 상호 조회할 수 있는 기능을 Federation이라고 하고, Data Mesh에서 매우 중요한 항목으로 취급된다. 

데이터 카탈로그 서비스 

이렇게 조직간의 데이터를 서로 크로스로 조회하고, 연관 분석을 할 수 있게 되면 다음 문제는 여러 부서간의 방대한 데이터에 대해서 어디에 어떤 데이터가 있는지를 찾을 수 있어야 한다. 데이터 거버넌스 측면에서 데이터 검색 및 메타 데이터 관리 기능에 대한 컴포넌트가 반드시 필요하다. 

필요한 데이터를 사용자가 찾고 쉽게 액세스할 수 있어야 하며, 여기에 더불어 사용자에 따라서 데이터 접근 권한을 관리할 수 있는 기능이 필요하다. 

또한 보안 관점에서 데이터에 대한 액세스 히스토리를 통해서 누가 언제 어떤 데이터를 조회하였는지 확인할 수 있어야 한다. 

이러한 메터데이터에 대한 관리를 할 수 있는 소프트웨어로는 오픈소스에 Apache Atlas와 상용 솔루션으로는  Colibra 등이 있고, 구글 클라우드에서는 Data Catalog라는 서비스로 구글 클라우드에 저장되는 데이터 (빅쿼리, GCS, Pub/Sub 메시징 큐)에 대한 메타 데이터 저장 검색 및 정책에 따른 접근 관리 기능을 제공한다. 

실시간 스트리밍 데이터

현대 빅데이터 시스템의 특징 중의 하나는, 정형,비정형의 데이터를 실시간 형태로 처리하는 스트리밍 데이터 처리가 추가된다는데 있다. 예를 들어 웹 접속 로그를 실시간으로 수집하여 분석하거나, 매장의 제품 판매 내용을 실시간으로 수집해서 분석하는 것과 같은 유스 케이스인데, 이러한 실시간 데이터 처리는 대용량 메시지 큐를 사용한다. 


<그림. 실시간 스트리밍 데이터 처리 아키텍처>


이 메세지 큐의 특징은 1:1 메시지 딜리버리뿐만 아니라 1:N 메시지 딜리버리를 지원해야 한다는 것인데, 

실시간 메시지 큐는 데이터를 저장하는 데이터 베이스 성격이 아니지만, 데이터를 다룬다는 점에서 데이터 엣셋으로 분류되어서 데이터 카달로그에 등록되어서 관리되어야 한다. 

Devops

Data Mesh 시스템도 마이크로 서비스와 유사하게 Devops의 개념을 도입하는 것이 좋은데, 이유는 속도 중심의 아키텍처 스타일로 각 도메인에 대한 데이터 분석을 각 팀이 수행하기 때문인데, 여기에 빠른 속도를 더하기 위해서는 운영과 개발을 함께 하는 구조가 되는 것이 좋다. 

단순하게 개발과 운영을 그팀에서 한다는 개념으로 소화를 하면 안되고, 제대로된 Devops를 하기 위해서는 데이터 분석 시스템이 플랫폼화가 되어 있어야 한다.

예를 들어서 마케팅 데이터 분석 시스템을 개발/운영 하는 팀이 있다고 할때, 이 시스템을 Hadoop으로 개발한다고 하자. 마케팅 데이터 분석 팀은 필요한 하드웨어 구매에서 부터, Hadoop 설치, 데이터 카탈로그, 분석 로직 개발, 시각화든 인프라에서 부터 툴 셋업 및 그위에 분석 업무까지 모두 개발해야 하는데, 이는 시간이 많이 걸리는 작업이고, 타 부서의 분석팀도 동일한 작업을 계속해야 한다. 

그래서 플랫폼의 필요성이 대두되는데, 분석가들은 분석 업무와 비즈니스 로직을 구현하는데만 집중하고, 하부에 인프라와 솔루션은 플랫폼 형태로 제공되서 Self Service 형태로 분석가들이 인프라를 구성하고 사용할 수 있는 구조가 되어야 한다.


이 개념 아키텍처를 도식화 한것이 아래 그림이다. 



< 그림. 데이터 플랫폼 아키텍처와 Devops 팀의 역할 관계 개념도 >


특히 이 공통 플랫폼을 개발하고 운영 하는 팀이 Devops 팀이고, 이 팀의 역할은 플랫폼을 개발해서 이를 각 데이터 분석팀이 사용할 수 있는 구조로 해주고 플랫폼을 운영 유지보수 하는 역할을 한다. 


참고 자료


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

  1. 루크 2021.01.05 17:58  댓글주소  수정/삭제  댓글쓰기

    깔끔한 정리, 항상 유용하게 잘 보고 있습니다. 감사 합니다.

Kubeflow pipeline 과 TFX 메모

빅데이타/머신러닝 | 2020. 11. 17. 00:27 | Posted by 조대협

Kubeflow pipeline 과 TFX 메모


TFX

TFX는 Tensorflow Extended 로, 머신러닝 파이프라인을 구현하는데 필요한 여러가지 컴포넌트들을 지원한다.

  • TFDV : 모델 Validation으로, EDA뿐만 아니라 데이터의 특성을 파악하여 저장한후에, 향후에 데이터가 들어 왔을때 Validation이 가능하다
  • TFT : 데이터 Transformation 작업을 한다
  • TFMA : 학습된 모델에 대한 평가 작업을 한다
  • TF-Serving : Prediction RunTime 엔진을 제공한다.
파이프라인 엔진
이렇게 각각의 컴포넌트가 있을때, 이 컴포넌트를 묶어서 오케스트레이션을 해야하는데, 이를 위해서 제공되는 것이 TFX pipeline이다. 실제 런타임 엔진이 있는 것이 아니라 SDK형태를 제공하고, 실제 런타임은 Kubeflow pipeline이나, Airflow를 사용한다.

TFX Standard component
이 파이프라인에서 각 TFX 컴포넌트를 손쉽게 부르려면, TFX 파이썬 라이브러리를 직접호출할 수 없으니, TFX 파이프라인에 맞도록 컴포넌트 형태로 구현을 해서 사용해야 하는데, TFX 컴포넌트들은 standard component 라는 형태로 미리 구현이 되어 있어서 불러만 쓰면 된다. 

Metadata
또한 파이프라인 각 단계별로 아웃풋 결과를 다음 태스크의 인풋으로 전달하기 위해서는 중간 저장소가 필요하고, 파이프라인의 진행 상태등을 저장할 메타 저장소가 필요한데, 이를 지원해주는 것이 metadata 스토어이다

문제점
이렇게 보면, 이미 만들어져 있는 컴포넌트들을 조합해서 파이프라인을 만들고 자동으로 메타데이타 까지 저장해주기 때문에 좋을거 같은데, 여기서 문제가 발생한다.
텐서플로우 세계에서만 좋다는것.
즉 이 프레임웍은 텐서플로우에만 최적화가 되어 있다. PyTorch, XGBoost와 같은 다른 프레임웍을 쓰거나 AWS,GCP,Azure 등의 매니지드 서비스를 호출할 수 없다. 물론 TFX Pipeline에서 Custom component 를 만들어서 호출하면 되지만, 말 그대로 만들어야 된다.

해결책
그래서 해결책으로 고려할 수 있는 것이 tfx pipeline을 사용하지 않고,kubeflow pipeline을 네이티브로 사용하는 방법이다. 이 방법을 사용하면 다른 프레임웍이나 AWS, GCP 서비스를 쉽게 사용할 수 있다. 특히 prebuilt-in 컴포넌트 형태로 이런 서비스를 컴포넌트 화를 해놓았기 때문에 사용이 편리하다. https://github.com/kubeflow/pipelines/tree/master/components
그런데, 여기서 또 다른 문제가 발생한다, 이 컴포넌트들에는 TFX 컴포넌트들이 없다 이 좋은 기능을 또 사용을 못한다는 말인데, 역시 kubeflow pipeline도 커스텀 컴포넌트를 지원한다. 
tfx에 비해서 상대적으로 컴포넌트 개발이 쉽다. python function을 컴포넌트로 정의할 수 있기 때문에 쉽게 개발이 가능하고, 찾아보면 python function 컴포넌트 형태로 TFDV,TFMA등을 호출한 사례를 심심치 않게 찾아볼 수 있다. 

이 경우 고려해야 할 사항이 tfx 의 경우 SDK를  Apache Beam을 사용하기 때문에, 런타임을 Flink나 GCP Data Flow를 사용하면 스케일이 가능한데, 이 부분은 좀 연구를 해봐야 할 분야인듯..

Kubeflow pipeline으로 정리되던 파이프라인 기술이 TFX가 나오면서, 특히 TFX가 파이프라인을 구현하면서 복잡해지는 것 같은 과도기적인 느낌이 오는데, 이는 좀 더 지켜봐야하지 않을까 싶다.




본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

  1. 2020.12.13 21:49  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

영어 발음 연습 방법

조대협의 소프트웨어 개발 | 2020. 2. 14. 17:11 | Posted by 조대협



1. IAP 연습하기

2. 파닉스가 중요 <-- 고무줄로 연습하기 (롱/숏이 중요) https://www.youtube.com/watch?v=eu6FtIo7CqQ

3. f,p,r,l,b,v, <-- 단어 연습하면서 천천히 반복.

4. 중간에 쉼쉬지 말고 한번에..


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

  1. 2020.09.03 15:40  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다


문제 정의 

프로메테우스가 좋은 모니터링 시스템이긴 하지만 두가지 결정적인 문제점을 가지고 있다. 결정적으로 클러스터링 구조를 지원하지 않기 때문에, 확장성과 가용성 문제를 가지고 있다. 

확장성 측면에서는 디스크를 증설하거는 것과 같은 하드웨어 스펙 증설로 어느정도는 해결이 가능하지만 데이타 볼륨이 늘어나고 모니터링 대상이 늘어나면 하나의 프로메테우스 인스턴스로는 감당이 어렵다. 


이런 문제를 해결하는 방법으로는 Federation 이라는 방법을 사용한다. 프로메테우스 인스턴스를 여러개를 기동한 다음에, 중앙에 다른 프로메테우스로 부터 메트릭을 수집하는 다른 프로메테우스를 놓는 방식이고, 데이타 양에 대한 문제는 데이타의 해상도 (예를 들어 전면에 데이타 수집 서버가 10초 단위로 수집 했다면, 중앙 서버에서는 1분 단위로 수집 한다는 등)를 줄이거나 평균이나 합과 같은 대표값을 사용해서 해결할 수 있다. 




다른 문제로는 가용성 문제를 들 수 있다. 프로메테우스는 하나의 서버로 기동되기 때문에 그 서버가 장애로 내려가거나 또는 패치나 서버 리스타트와 같은 유지보수 업무에 의해서 프로메테우스 서버가 내려가더라도 그 시간동안에는 매트릭을 수집할 수 없다는 단점을 가지고 있다. 클러스터링 기능이 없기 때문에 이를 해결하기 위해서는 프로메테우스 인스턴스를 두개 이상 띄운 다음에 같은 대상 시스템으로 부터 매트릭을 수집하는 방식인데, 이렇게 하면, 한 서버가 내려가더라도 다른 서버에 매트릭정보가 수집이 된다.



그러나 역시 불편하고 아키텍쳐가 왠지 제대로 되어 보이지는 않는다.


Thanos (타노스)

그래서 이런 문제를 해결하기 위한 오픈소스가 타노스이다.

기본적인 구조는 다음과 같은 컨셉이다.


여러개의 프로메테우스로 부터 매트릭을 조합해서 타노스에서 전체 프로메테우스의 메트릭을 볼 수 있도록 해주고, 수집된 메트릭을 스케일이 가능한 스토리지에 저장해서 특정 프로메테우스 인스턴스가 다운이 되더라도 그 인스턴스가 담당하는 메트릭을 조회할 수 있도록 해준다. 


개념 이해를 돕기 위해서 아키텍쳐를 살펴보자

Thanos Side car & Querier

먼저 데이타 수집과 쿼리 방식을 보면, 프로메테우스 서버에 타노스 에이전트가 인스톨 되서 데이타를 조회할 수 있게 해준다. 프로메테우스는 데이타를 로컬 디스크에 저장하기 때문에 타노스 에이전트 (Thanos Sidecar : 마이크로 서비스 아키텍쳐 패턴중 사이드카 패턴을 사용하기 때문에, 타노스 사이드카라고 부른다.)는 디스크에 저장된 내용을 읽어서 필요시에 쿼리 엔진 (Thanos Querier)에 전달한다. 

여러개의 프로메테우스 인스턴스가 있더라도, 각 프로메테우스 마다 사이드카 에이전트가 설치되서 쿼리 엔진에 전달하기 때문에 사용자 입장에서는 하나의 타노스 쿼리 엔진(UI)만 가지고도 전체 프로메테우스를 통해서 모든 모니터링 대상의 메트릭을 조회할 수 있도록 되는 것이다. 


HA 지원

기본적인 HA 지원 방식은 기존 방식과 다르지 않다. 아래 그림과 같이 프로메테우스 인스턴스를 동시에 두개를 띄워서 같은 모니터링 대상을 모니터링 해서 각각의 지표를 저장하는 방식이다.


그러면 타노스로 인해 오는 장점은 무엇인가? 기존 방식의 경우에는 프로메테우스 인스턴스 각각의 메트릭으로 모니터링 해야 하지만 타노스는 특정 그룹의 프로메테우스 인스턴스들의 지표들의 하나의 인스턴스로 처리해서 메트릭을 보여준다. 즉 두개의 인스턴스에서 수집된 메트릭을 합쳐서(merge) 해서 볼 수 있도록 해주고, 당연히 같은 모니터링 대상을 모니터링 하기 때문에 중복되는 메트릭 값이 있을 수 있는데, 이 중복 값을 제거 해주는 De-duplication 기능을 가지고 있다. 

오랜된 값 저장

앞에서 언급은 하지 않았지만 프로메테우스의 다른 문제점 중의 하나는 로컬 디스크를 사용하기 때문에 일정 기간이 지난 오래된 데이터는 삭제가 된다. 그래서 오래된 데이터에 대한 조회가 불가능하다. 

타노스 입장에서는 오래된 데이터 저장 문제 뿐만 아니라 여러 프로메테우스를 동시에 모니터링 하게 되면 마찬가지로 메모리와 로컬 디스크의 용량 문제로 인해서 여러 프로메테우스를 모니터링 할 수 없는 문제가 발생하는데, 이를 해결하기 위해서 타노스는 외부 스토리지를 사용한다. 


프로메테우스에서 수집된 데이타는 2시간 정도 메모리에 저장이 되었다가 로컬 디스크로 덤프가 된다. 저장된 파일을 타노스 에이전트가 수집해서 외부 스토리지에 저장한다. 외부 스토리지는 Ceph와 같은 분산형 파일 시스템이나 Google Cloud Storage, AWS S3와 같은 클라우드 오브젝트 스토리지를 사용한다. 


그리고 쿼리 엔진에서 근래의 데이타를 조회할때는 프로메테우스 인스턴스에 설치된 타노스 사이드카 에이전트를 통하지만 오래된 데이터는 스토리지에 저장된 데이터는 Thano Storage Gateway라는 컴포넌트를 통해서 조회된다. 이 컴포넌트는 스토리지에 저장된 데이타를 Storage API를 통해서 쿼리엔진과 통신 하는 역할을 한다. Gateway는 단순 쿼리를 API로 저장하는 역할뿐만 아니라 중간에 캐쉬를 제공하여, 빠른 응답 시간을 제공한다. 


이 구조를 이용함으로써 메트릭 데이터의 보관 주기를 늘릴 수 있게 된다. 


스토리지에 저장된 메트릭을 장기 보관하게 되면, 디스크 용량에 대한 문제도 있지만 아주 오래된 데이타(1~2년전)의 데이터를 조회하고자 하면, 많은 데이터를 스캔해야하기 때문에 성능에 있어서 많은 문제가 생길 수 있다. 그래서 스토리지에 저장된 데이타를 관리하는 컴포넌트로 Compactor 라는 컴포넌트가 있다. 기본적으로 데이타 파일을 압축할 뿐만 아니라, 다운 샘플링을 하는데, 다운 샘플링이란, 매트릭이 1분 단위로 샘플링 되었다면, 10분이나 1시간 단위로 샘플링 기준을 다운해서 (해상도를 낮춰서) 전체 데이타 저장 용량을 낮추는 방법이다. 


이외에도 Alert과 룰 관리, UI를 관리하는 Thanos Ruler 라는 컴포넌트가 있으나 여기서는 자세히 설명하지 않는다. 상세한 설명은 타노스 공식 문서를 참고하기 바란다. https://thanos.io


사실 프로메테우스의 한계를 해결하기 위해서 오픈소스 쪽에서 타노스가 좋은 솔루션이기는 하지만, 개인적으로 운영환경에 올린다면 이렇게 복잡한 설정 대신 프로메테우스를 앞에 놓고 (구글 클라우드 엔지니어니까는) 뒤에 타노스 대신 구글 스택 드라이버를 놓는 것이 여러모로 편하겠다는 생각은 좀 든다. https://cloud.google.com/monitoring/kubernetes-engine/prometheus


타노스 온라인 튜토리얼 https://katacoda.com/bwplotka/courses/thanos


참고 자료 : https://www.youtube.com/watch?v=Fb_lYX01IX4



본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요



프로메테우스 #3. 그라파나를 이용한 시각화

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


그라파나(Grafana)는 메트릭을 시각화 해주는 오픈소스 도구이다. Graphite, Prometheus, InfluxDB등 다양한 데이타베이스와 메트릭수집 시스템을 지원하고, 하나의 대쉬보드에 동시에 여러 메트릭 시스템들의 지표를 표시할 수 있고 무엇보다 설치 및 사용 방법이 쉽기 때문에 널리 사용되고 있다

특히 프로메테우스를 잘 지원하고 있기 때문에, 프로메테우스의 메트릭을 그래프로 시각화 하는데도 많이 사용된다. 

그라파나의 설치는 비교적 간단한 편이기 때문에 여기서는 별도로 설명하지 않는다. 설치 방법은 공식 문서 https://grafana.com/docs/grafana/latest/installation/debian/ 를 참고하기 바란다. 

이 문서에서 사용한 테스트 환경은 katakoda.com에서 그라파나 튜토리얼 환경을 이용하였다. https://www.katacoda.com/courses/prometheus/creating-dashboards-with-grafana


앞의 문서 (https://bcho.tistory.com/1373) 에 따라서 프로메테우스를 설치하였으면, 그라파나를 설치한다. 

다음 그라파나 웹 관리 화면으로 접속을 하고 로그인을 하면 아래와 같은 화면을 볼 수 있다. 



이 화면에서 Add Data source를 선택해서 프로메테우스 서버를 새로운 데이타 소스로 등록할것이다.

아래 메뉴에서 데이타 소스의 이름을 “Prometheus”로 설정하고, 타입을 Prometheus로 선택한다.

다음 Http Setting 부분의 Url 부분에, Prometheus 서버의 주소를 적어넣는다. 이 예제에서는 로컬에 프로메테우스를 기동 시켰기 때문에, http://localhost:9090 을 입력하면 된다. 



이제 프로메테우스 서버와 그라파나가 연결되었다. 

이제 그래프를 그려볼 예정인데, 초기 화면으로 돌아가서 “Create your first dash board” 메뉴를 선택한다. 다음 New Dash 보드 메뉴를 선택하여 비어 있는 대쉬 보드를 하나 만든다. 



다음에 그래프를 하나 선택하면 아래와 같이 빈 그래프가 나오는데, 이 그래프를 프로메테우스의 메트릭과 연결할것이다. 




그래프에서 상단의 “Panel Title” 를 누르면 아래 그림과 같이 메뉴가 나오는데, 여기서 “Edit” 메뉴를 선택한다.  그러면 아래와 같이 설정을 할 수 있는 화면이 나오는데, 여기서 “Metric” 메뉴를 선택한다.




Metric 메뉴에서는 시각화 하고 싶은 프로메테우스의 필드를 선택하면 되는데, PrometheusQL을 사용해서 정의할 수 도 있다. 여기서는 간단하게 node의 CPU정보를 시각화 하기 위해서 node_cpu 를 선택하였다. 그러면 아래와 같이 node_cpu 메트릭에 대한 정보를 그래프로 그려주는 것을 확인할 수 있다. 



그라파나에서는 널리 사용되는 시스템에 대한 대쉬보드를 템플릿 형태로 만들어서 사용자들이 서로 공유할 수 있도록 하는데, 템플릿은  https://grafana.com/dashboards 에 가면 찾아볼 수 있다.

여기서는 프로메테우스 node_exporter에 의해서 제공되는 메트릭을 모니터링할 수 있는 대쉬보드를 import 해서 사용해보자. 


초기화면에서 대쉬보드 생성 메뉴로 들어 간 후에, 아래 그림에서 import dashboard 메뉴를 선택하자


그러면 아래와 같이 import 화면이 나오는데, 대쉬 보드 설정을 json 파일로 업로드 할 수 도 있지만, 그라파나 대쉬보드 웹사이트에 있는 경우에는 아래 처럼 URL이나 dashboard id를 넣으면 된다.

아래 그림처럼 https://grafana.com/dashboards/22 를 필드에 입력한다.



로딩된 대쉬 보드를 보면 다음과 같다.





지금까지 프로메테우스에 대한 소개와 내부 구조, 그리고 간단한 사용법 및 시각화 방법에 대해서 알아보았다. 이 정도면 기본적인 모니터링 시스템 구성에는 문제가 없지만, 프로메테우스는 앞선 글에서도 언급 하였듯이 싱글 서버가 기동되는 구조이기 때문에, 확장성과 장애에 취약한 단점을 가지고 있는데, 다음글에서는 이 문제를 어떻게 해결할 수 있는지에 대해서 알아보도록 하겠다.

본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

프로메테우스

#2 Hello Prometheus 조대협 (http://bcho.tistory.com)

프로메테우스에 대해서, 이해하기 위해서 간단한 테스트를 진행하는데, 테스트는 http://www.katacoda.com/ 를 이용하였다. 웹상에서 쿠버네티스, 프로메테우스,텐서플로우등 다양한 기술을 별도의 설정이나 서버없이 해볼 수 있기 때문에, 기술에 대한 개념을 잡는데 매우 유용하다. 

설정 파일 정의 

프로메테우스의 설정은 prometheus.yml 파일에 정의 한다. 아래는 간단한 예제이다. 


global:

  scrape_interval:     15s

  evaluation_interval: 15s


scrape_configs:

  - job_name: 'prometheus'


    static_configs:

      - targets: ['127.0.0.1:9090', '127.0.0.1:9100']

        labels:

          group: 'prometheus'


scrap_interval은 타겟 시스템으로 부터 메트릭을 읽어오는 주기이다. 읽어온 메트릭을 기반으로 룰에 따라서 alert을 보낼 수 있는데, alert을 보낼지 말지 메트릭을 보고 판단하는 주기가 evaluation_interval 이다. 


scrap_configs 부분이 데이타 수집 대상과 방법을 정의하는 부분인데, 먼저 job에 대한 개념을 이해해야 한다. job은 대상 그룹에서 메트릭을 수집해오는 내용을 정의하는데, job은 하나의 시스템이 아니라 특성이 같은 하나의 시스템 그룹정도의 개념으로 생각하면 된다. 예를 들어 오토 스케일링 그룹으로 묶여 있는 웹서버 1,2,3을 수집한다고 했을때, 이 3개의 메트릭을 수집하는 것이 하나의 job으로 정의되고, 각각의 서버를 instance라고 한다. 


위에서는 127.0.0.1:9090, 127.0.0.1:9100 에서 메트릭을 수집하도록 job을 하나 정의 했는데, prometheus 가 기동되고 있는 서버로 부터 메트릭을 읽어오도록 설정하였다. 앞에는 프로메테우스 시스템 자체의 메트릭이고, 두번째는 프로메테우스가 기동되고 있는 VM(또는 서버,또는 도커)의 시스템 메트릭을 수집하도록 정의하였다.

프로메테우스 서버와 node exporter 기동

설정의 끝났으면 프로메테우스 서버를 기동하다. 설치에는 여러가지 방법이 있지만, 간단하게 도커 이미지를 이용하여 기동하였다. 

docker run -d --net=host \

     -v /root/prometheus.yml:/etc/prometheus/prometheus.yml \

     --name prometheus-server \

     prom/prometheus


프로메테우스를 기동하였으면, 프로메테우스 모니터링 대상이 되는 프로메테우스의 노드(VM)의 지표를 수집하여 프로메테우스로 전송하는 node exporter를 설치해서 기동해야 한다. node exporter는 머신의 CPU,메모리,네트웍등과 같은 메트릭을 수집해서 프로메테우스가 가지고 갈 수 있도록 한다.

마찬가지로 도커 이미지로 기동하는데, quay.io/prometheus/node-exporter 의 이미지를 사용한다. 


docker run -d \

   -v "/proc:/host/proc" \

   -v "/sys:/host/sys" \

   -v "/:/rootfs" \

   --net="host" \

   --name=prometheus \

   quay.io/prometheus/node-exporter:v0.13.0 \

     -collector.procfs /host/proc \

     -collector.sysfs /host/sys \

     -collector.filesystem.ignored-mount-points "^/(sys|proc|dev|host|etc)($|/)"


node exporter 가 제대로 작동하는지 확인하려면, 

% curl https://{프로메테우스 서버 IP}:9100/metrics

명령을 실행해보면 다음과 같이 메트릭 정보들이 리턴되는 것을 확인할 수 있다. 

흥미로운것이 metric 수집을 HTTP GET 방식으로 하도록 되어 있다. 

이 관점에서 봤을때 구현이 매우 쉬울것 같은 장점도 있고, 별도의 포트를 오프하지 않아도 되는 장점은 있지만, 보안적으로 어떻게 접근 제어등을 할지는 나중에 별도로 한번 찾아봐야 하지 않을까 싶다. 


# HELP go_gc_duration_seconds A summary of the GC invocation durations.

# TYPE go_gc_duration_seconds summary

go_gc_duration_seconds{quantile="0"} 3.9293000000000005e-05

go_gc_duration_seconds{quantile="0.25"} 3.9293000000000005e-05

go_gc_duration_seconds{quantile="0.5"} 5.0119000000000006e-05

go_gc_duration_seconds{quantile="0.75"} 5.0119000000000006e-05

go_gc_duration_seconds{quantile="1"} 5.0119000000000006e-05

go_gc_duration_seconds_sum 8.9412e-05

go_gc_duration_seconds_count 2

# HELP go_goroutines Number of goroutines that currently exist.

# TYPE go_goroutines gauge

go_goroutines 8

# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.

# TYPE go_memstats_alloc_bytes gauge

go_memstats_alloc_bytes 6.234016e+06

# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.

# TYPE go_memstats_alloc_bytes_total counter


대쉬보드 접속

프로메테우스와 exporter 가 기동이되었으면, 메트릭이 제대로 수집이 되는지 확인하자. 

http://{프로메테우스 서버 ip}:9090 에 접속하면, 대쉬보드가 나오는데, 검색 쿼리 부분에 보고 싶은 메트릭에 대한 쿼리를 넣으면, 테이블이나 그래프 형태로 볼 수 있다.

아래는 node_cpu 항목을 넣어서 프로메테우스가 기동 중인 서버의 CPU 사용률을 모니터링해서 그래프로 나타낸 화면이다. 



본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

프로메테우스 #1 기본 개념과 구조

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


프로메테우스는 오픈 소스 기반의 모니터링 시스템이다. 

ELK 와 같은 로깅이 아니라, 대상 시스템으로 부터 각종 모니터링 지표를 수집하여 저장하고 검색할 수 있는 시스템이다. 

구조가 간단해서 운영이 쉽고, 강력한 쿼리 기능을 가지고 있으며, 그라파나(Grafana) 를 통한 시각화를 지원한다. 무엇보다 넓은 오픈 소스 생태계를 기반으로 해서, 많은 시스템을 모니터링할 수 있는 다양한 플러그인을 가지고 있는 것이 가장 큰 장점이다. 

특히 이런 간편함 때문에 특히나 쿠버네티스의 메인 모니터링 시스템으로 많이 사용되면서 요즘 특히 더 주목을 받고 있다. 

기본 구조

프로메테우스의 기본적인 아키텍처 부터 살펴보자

먼저 수집 저장 저장 아키텍처를 보면 다음과 같다. 




메트릭 수집 부분

수집을 하려는 대상 시스템이 Target system이다. MySQL이나, Tomcat 또는 VM 과 같이 여러가지 자원이 모니터링 대상이 될 수 있다. 이 대상 시스템에서 메트릭을 프로메테우스로 전송하기 위해서는 Exporter 라는 것을 사용한다. (다른 방법도 있지만 이는 나중에 따로 설명한다. )

풀링 방식

프로메테우스가 Target System에서 메트릭을 수집하는 방식은 풀링 방식을 사용한다. 프로메테우스가 주기적으로 Exporter로 부터 메트릭 읽어와서 수집하는 방식이다. 보통 모니터링 시스템의 에이전트 들은 에이전트가 모니터링 시스템으로 메트릭을 보내는 푸쉬 방식을 사용한다. 특히 푸쉬 방식은 서비스가 오토 스켈링등으로 가변적일 경우에 유리하다. 풀링 방식의 경우 모니터링 대상이 가변적으로 변경될 경우, 모니터링 대상의 IP 주소들을 알 수 가 없기 때문에 어려운 점이 있다. 예를 들어 웹서버 VM 2개의 주소를 설정 파일에 넣고 모니터링을 하고 있었는데, 오토 스케일링으로 인해서 VM이 3개가 더 추가되면, 추가된 VM들은 설정 파일에 IP가 들어 있지 않기 때문에 모니터링 대상에서 제외 된다. 

이러한 문제를 해결하기 위한 방안이 서비스 디스커버리라는 방식인데, 특정 시스템이 현재 기동중인 서비스들의 목록과 IP 주소를 가지고 있으면 된다. 예를 들어 앞에서 VM들을 내부 DNS에 등록해놓고 새로운 VM이 생성될때에도 DNS에 등록을 하도록 하면, DNS에서 현재 기동중인 VM 목록을 얻어와서 그 목록의 IP들로 풀링을 하면 되는 구조이다.

서비스 디스커버리 (Service discovery)

그래서 프로메테우스도 서비스 디스커버리 시스템과 통합을 하도록 되어 있다. 앞에서 언급한 DNS나, 서비스 디스커버리 전용 솔루션인 Hashicorp사의 Consul 또는 쿠버네티스를 통해서, 모니터링해야할 타겟 서비스의 목록을 가지고 올 수 있다. 

Exporter

Exporter는 모니터링 에이전트로 타겟 시스템에서 메트릭을 읽어서, 프로메테우스가 풀링을 할 수 있도록 한다. 재미 있는 점은 Exporter 는 단순히 HTTP GET으로 메트릭을 텍스트 형태로 프로메테우스에 리턴한다. 요청 당시의 데이타를 리턴하는 것일뿐, Exporter 자체는 기존값(히스토리)를 저장하는 등의 기능은 없다. 

Retrieval

서비스 디스커버리 시스템으로 부터 모니터링 대상 목록을 받아오고, Exporter로 부터 주기적으로 그 대상으로 부터 메트릭을 수집하는 모듈이 프로메테우스내의 Retrieval 이라는 컴포넌트이다.

저장

이렇게 수집된 정보는 프로메테우스 내의 메모리와 로컬 디스크에 저장된다. 뒷단에 별도의 데이타 베이스등을 사용하지 않고, 그냥 로컬 디스크에 저장하는데, 그로 인해서 설치가 매우 쉽다는 장점이 있지만 반대로 스케일링이 불가능하다는 단점을 가지고 있다.  대상 시스템이 늘어날 수 록 메트릭 저장 공간이 많이 필요한데, 단순히 디스크를 늘리는 방법 밖에 없다. 


프로메테우스는 구조상 HA를 위한 이중화나 클러스터링등이 불가능하다. (클러스터링 대신 샤딩을 사용한다. HA는 복제가 아니라 프로메테우스를 두개를 띄워서 같은 타겟을 동시에 같이 저장 하는 방법을 사용한다. 이 문제에 대한 해결 방법은 Thanos 라는 오픈 소스를 사용하면 되는데, 이는 다음에 다시 설명하도록 한다. )

서빙

이렇게 저장된 메트릭은 PromQL 쿼리 언어를 이용해서 조회가 가능하고, 이를 외부 API나 프로메테우스 웹콘솔을 이용해서 서빙이 가능하다. 또한 그라파나등과 통합하여 대쉬보드등을 구성하는 것이 가능하다. 


이 외에도 메트릭을 수집하기 위한 gateway, 알람을 위한 Alert manager 등의 컴포넌트등이 있지만, 기본적인 엔진 구조를 이해하는데는 위의 컴포넌트들이 중요하기 때문에, 이 컴포넌트들은 다른 글에서 설명하도록 한다.


프로메테우스 아키텍처에서 주의할점

간단하게 프로메테우스 아키텍처를 살펴보았는데, 이 구조에서 몇가지 생각해볼만한 점이 있다. 

어디까지나 근사치라는 점

일단 풀링 주기를 기반으로 메트릭을 가지고 오기 때문에, 풀링하는 순간의 스냅샷이라는 것이다. 15초 단위로 폴링을 했다고 가정했을때, 15초 내에 CPU가 올라갔다 내려와서, 풀링 하는 순간에는 CPU가 내려간 값만 관측이 될 수 있다. 스냅삿에 대한 연속된 모음일 뿐이고, 근사값의 형태라는 것을 기억할 필요가 있다. 

싱글 호스트

프로메테우스는 싱글 호스트 아키텍처이다. 확장이 불가능하고, 저장 용량이 부족하면 앞에서 언급한데로 디스크 용량을 늘리는 것 밖에 방안이 없다. 특히나 문제점은 프로메테우스 서버가 다운이 되거나 또는 설정 변경등을 위해서 리스타트등을 하더라도 그간에 메트릭은 저장이 되지 않고 유실이 된다는 점이다.




본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

  1. 스토커 2020.06.10 13:19  댓글주소  수정/삭제  댓글쓰기

    헐!!! 간만에 조대협님 블로그 왔더니 디자인이 이쁘게 리뉴얼 되었군요. 폰트도 이쁜걸로 바뀌고(개인적으로 전 폰트는 너무 노땅 ㅋ). 항상 양질의 게시물 잘보고 있습니다.

Locust 와 쿠버네티스를 이용한 분산 부하 테스트

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

분산 부하 테스트

locust는 여러개의 worker를 이용하여, 부하를 대량으로 발생 시키는 분산 부하 테스트가 가능하다. 특히 분산 클러스터 구성 설정이 매우 간단하다는 장점을 가지고 있다. 


마스터 노드의 경우에는 아래와 같이 --master 옵션을 지정하여 마스터 노드로 구동하면 되고, 

% locust -f {task file name} --host={target host address} --master


워커 노드의 경우에는 실행 모드를 slave로 하고, 마스터 노드의 주소만 명시해주면 된다. 

% locust -f {task file name} --host={target host address} --slave --master-host={master node의 주소}


이렇게 클러스터를 구성하면 아래와 같은 구조를 갖는다


특히 워크노드를 추가할때 별도의 설정이 필요없이 워커노드를 추가하면서 마스터 노드의 주소만 주면 되기 때문에, 스케일링이 편리하다. 

쿠버네티스에서 분산 부하 테스트

설정이 간단하고, 부하 테스트의 특성상 필요할때만 클러스터를 설치했다가 사용이 끝나면 없애는 구조로 사용을 하려면, 쿠버네티스에서 기동하면 매우 편리하다. 


특히 설치 및 설정을 Helm Chart로 만들어놓으면, 필요할때 마다 손쉽게 locust 클러스터를 설치했다가 사용이 끝나면 손쉽게 지울 수 있는데, Helm 공식 리파지토리에 등록된 locust chart는 https://github.com/helm/charts/tree/master/stable/locust 아쉽게도 locust 0.9를 기준으로 한다. 현재는 1.3 버전이 최신 버전인데, 0.9와 1.3은 설정고 TaskSet의 코딩 방식이 다소 변경되서, locust 0.9를 사용하게 되면, 현재 메뉴얼에 있는 일부 코드들을 사용할 수 없다. 

그래서, 스스로 Helm Chart를 만들어서 사용하기를 권장한다. 


좋은 소식중의 하나는 구글 클라우드에서 Locust 를 쿠버네티스에서 분산 부하 테스팅을 할 수 있는 가이드 문서를 제공하고 있으니, 이를 참조하면 손쉽게 사용이 가능하다.

https://cloud.google.com/solutions/distributed-load-testing-using-gke

단, helm chart 형태로는 제공하지 않으니, 스스로 Chart를 만들기를 권장한다. Chart를 만들때 기존 Chart 구조를 참고하기를 권장하는데, https://github.com/helm/charts/tree/master/stable/locust 를 보면, locust의 이미지 버전이나, 설정을 Helm의 values.yaml로 빼거나 또는 CLI parameter로 처리할 수 있게 하였고, 특히 아래 task files를 설정하는 방법이 매우 흥미로운데, 


task file을 도커 이미지 내에 넣는 방식이 아니라 file (또는 directory) 형태의 configmap으로 빼서, locust pod가 기동될때, 이 파일(디렉토리)를 마운트해서 스크립트를 읽어서 적용하는 구조를 가지고 있다.


위에서 언급한 구글 클라우드 플랫폼의 가이드는 task 파일을 docker 이미지로 매번 빌드하기 때문에, 다소 설치가 복잡해질 수 있다는 단점이 있다. 


그러면 구글 클라우드 플랫폼에서 제공하는 예제 파일들을 살펴보자

https://github.com/GoogleCloudPlatform/distributed-load-testing-using-kubernetes 리포에 저장이 되어 있는데, 

/docker-image 디렉토리

이 디렉토리에는 locust 도커 컨테이너를 만들기 위한 설정 파일들이 들어 있다. 

Dockerfile의 내용을 보면 아래와 같다.

# Start with a base Python 3.7.2 image

FROM python:3.7.2

 

# Add the licenses for third party software and libraries

ADD licenses /licenses

 

# Add the external tasks directory into /tasks

ADD locust-tasks /locust-tasks

 

# Install the required dependencies via pip

RUN pip install -r /locust-tasks/requirements.txt

 

# Expose the required Locust ports

EXPOSE 5557 5558 8089

 

# Set script to be executable

RUN chmod 755 /locust-tasks/run.sh

 

# Start Locust using LOCUS_OPTS environment variable

ENTRYPOINT ["/locust-tasks/run.sh"]


주의 깊게 살펴볼 부분은 아래 두줄이다. 

ADD locust-tasks /locust-tasks

RUN pip install -r /locust-tasks/requirements.txt


첫번째는 locust task 스크립트를 /locust-tasks 디렉토리에 복사하는 부분이다. 

그리고 두번째는 스크립트에서 사용하는 파이썬 라이브러리를 설치하는 부분이다. 

kubernetes-config 디렉토리


locust-master-controller.yaml

apiVersion: "extensions/v1beta1"

kind: "Deployment"

metadata:

 name: locust-master

 labels:

   name: locust-master

spec:

 replicas: 1

 selector:

   matchLabels:

     app: locust-master

 template:

   metadata:

     labels:

       app: locust-master

   spec:

     containers:

       - name: locust-master

         image: gcr.io/[PROJECT_ID]/locust-tasks:latest

         env:

           - name: LOCUST_MODE

             value: master

           - name: TARGET_HOST

             value: https://[TARGET_HOST]

         ports:

           - name: loc-master-web

             containerPort: 8089

             protocol: TCP

           - name: loc-master-p1

             containerPort: 5557

             protocol: TCP

           - name: loc-master-p2

             containerPort: 5558

             protocol: TCP


이 파일은 master node를 배포하기 위한 deployment 정의다. 주의 깊게 살펴볼 부분은 아래 두 라인이다. image는 앞에서 만든 task 스크립트가 포함된 locust 도커 이미지를 정의 하는 부분이고, 두번째는 부하 테스트 대상이 되는 타겟 호스트 주소를 정의하는 부분이다. 


image: gcr.io/[PROJECT_ID]/locust-tasks:latest

value: https://[TARGET_HOST]


다음은 locust-master-service.yaml 파일이다.

kind: Service

apiVersion: v1

metadata:

 name: locust-master

 labels:

   app: locust-master

spec:

 ports:

   - port: 8089

     targetPort: loc-master-web

     protocol: TCP

     name: loc-master-web

   - port: 5557

     targetPort: loc-master-p1

     protocol: TCP

     name: loc-master-p1

   - port: 5558

     targetPort: loc-master-p2

     protocol: TCP

     name: loc-master-p2

 selector:

   app: locust-master

 type: LoadBalancer


앞서 정의한 master node를 External IP (Load Balancer)로 밖으로 빼서, 웹 콘솔 접속등을 가능하게 하는 설정인데, 주의할점은 locust 웹 콘솔은 별도의 사용자 인증 기능이 없기 때문에, 위의 설정을 그대로 사용하면, 인증 기능이 없는 웹콘솔이 인터넷에 그래도 노출이 되기 때문에, 권장하지 않는다.


ClusterIP로 Service를 정의한 후, kubectl의 port forwarding 기능을 사용하거나 또는 앞에 인증용 프록시 서버를 두는 것을 권장한다. 구글 클라우드의 경우에는 Identity Aware Proxy(IAP)라는 기능을 이용하여, 구글 클라우드 계정으로 로그인을 해야 웹사이트를 접근할 수 있는 프록시 기능을 제공한다. 


마지막으로 locust-worker-controller.yaml 파일을 정의한다.

# Copyright 2015 Google Inc. All rights reserved.

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

#     http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

 

apiVersion: "extensions/v1beta1"

kind: "Deployment"

metadata:

 name: locust-worker

 labels:

   name: locust-worker

spec:

 replicas: 5

 selector:

   matchLabels:

     app: locust-worker

 template:

   metadata:

     labels:

       app: locust-worker

   spec:

     containers:

       - name: locust-worker

         image: gcr.io/[PROJECT_ID]/locust-tasks:latest

         env:

           - name: LOCUST_MODE

             value: worker

           - name: LOCUST_MASTER

             value: locust-master

           - name: TARGET_HOST

             value: https://[TARGET_HOST]

 


이 파일을 worker node 에 대항 설정으로 master node와 마찬가지로 target_host와 locust 이미지 경로만 지정해주면 된다. 


이 파일들을 배포하면, locust 가 실행 준비가 되고, 앞에서 정의한 master node의 서비스 주소로 접속하면 locust 웹 콘솔을 이용하여 부하 테스트를 진행할 수 있다. 


만약 동적으로 locust worker node의 수를 조절하고 싶으면, 

% kubectl scale deployment/locust-worker --replicas=20


명령을 이용하여 worker node pod의 수를 조절할 수 있다. 

본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

부하테스트를 위한 Locust

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


백앤드 개발을 하다보면 많이 사용되는 도구 중의 하나가 부하 테스트 툴인데, 대표적인 도구로는 Apache Jmeter, nGrinder,SOAP UI 등의 도구가 있지만 다소 사용이 어렵고 스케일링을 하는데 어려움이 있는데, locust라는 도구는 설치와 사용이 편리하고, 테스트 시나리오를 파이썬 스크립트로 작성을 하기 때문에 다양한 시나리오 구현이 가능하다. 특히 쿠버네티스에 쉽게 배포할 수 있도록 Helm으로 패키지화가 되어 있기 때문에, 필요한 경우 대규모 부하테스트 환경을 설치하고 테스트가 끝나면 쉽게 지워버릴 수 있다. 

(참고 : locust는 영어로 메뚜기라는 뜻인데, 부하를 주는 것을 swarming 이라고 표현하는게 메뚜기 떼로 부하를 비교한것이 재미있다.)


퀵스타트

간단하게 설치 및 테스트를 해보자. 

설치

설치는 파이썬만 설치되어 있으면 간단하게 pip 명령을 이용해서 설치 할 수 있다.

%python -m pip install locust

명령으로 설치 하면 된다.

설치 확인은 locust --version 명령으로 확인하면 된다.  이 방식이외에도, 이미 빌드된 도커 이미지를 사용해서 테스트를 하거나 또는 쿠버네티스에서 Helm으로 설치해서 실행하는 방법도 가능하다. 쿠버네티스에서 Helm을 사용하는 방법은 나중에 자세하게 다루도록 한다. 

간단한 부하 스크립트 생성

locust 부하테스트는 파이썬 스크립트를 실행하는 방식으로 하기 때문에, 부하테스트용 스크립트 파일을 작성해야 한다. 아래는 대상 호스트의 / 에 HTTP GET으로 요청을 보내는 스크립트이다. 스크립트 작성방법은 후에 다시 설명하도록 한다. 


from locust import HttpLocust,TaskSet,task,between

class MyTaskSet(TaskSet):

   @task

   def index(self):

       self.client.get("/")

class MyLocus(HttpLocust):

   task_set = MyTaskSet

   wait_time = between(3,5)

실행하기

locust 실행은 locust -f {스크립트 파일명} {웹포트번호} 형식으로 실행하면 된다.

locust 서버가 기동되고, 부하를 줄 준비가 된 상태가 된다. 실제 부하 생성은 locust 웹콘솔에 접속해서 해야 한다.  아래 스크립트는 8080트로 locust 웹 클라이언트를 기동하도록 한 명령이다. 

%locust -f ./locustfile.py --port 8080


http://localhost:8080 으로 접속하면 아래와 같이 웹 콘솔이 나온다. 


첫번째 인자는, 몇개의 클라이언트를 사용할것인지 (메뚜기를 몇마리 만들것인지)를 정의하고, 두번째는 Hatch rate 라고 해서, 초마다 늘어나는 클라이언트 수 이다. 처음에는 클라이언트가 1대로 시작되서, 위의 설정의 경우 초마다 하나씩 최대 30개까지 늘어난다.

그리고 마지막은 테스트할 웹 사이트 주소를 입력한다.


Start Swarming 버튼을 누르게 되면 부하테스트가 시작되서 아래와 같이 부하 테스트 화면이 실시간으로 출력된다. 



몇 위의 화면은 Total Request Per Second로, 초당 처리량이고, 두번째는 응답 시간, 그리고 마지막은 현재 클라이언트 수 를 모니터링 해준다. 


No Web

위의 부하테스트는 웹 콘솔을 열어서, 웹 사이트로 부하를 주는 시나리오인데, 웹 부하 테스트가 아니라, 데이타베이스의 부하테스트와 같은 경우에는 웹 사이트의 주소를 정의할 수 없다. 이런 경우에는 CLI로 실행이 가능한데, --no-web 옵션을 주면 된다.

%locust -f ./nested-locust.py --no-web -c 2 -r 1 


위의 명령은 nested-locust.py 파일을 실행하되, --no-web으로 웹으로 부하를 주지 않고, -c 2 로 2개의 클라인트에 대해서 -r 1 1초마다 클라이언트를 (2까지) 하나씩 올리는 부하 테스트 시나리오이다. 웹 UI는 테스트 상태를 모니터링 하고 사람이 쓰기 좋지만 만약에 테스트 과정을 CI/CD 파이프라인내에 넣거나 또는 자동화하고 싶을때는 이렇게 CLI 를 이용할 수 있다. 

코딩 방법

사용방법을 이해하였으면, 이제 스크립트 작성 방법을 살펴보자.

스크립트는 Locust (클라이언트/메뚜기)를 정의해서, Locust의 행동 시나리오 (TaskSet)를 정의해야 한다. 

Locust

지원되는 Locust 종류는 HttpLocust와, Locust 두가지가 있다. HttpLocust는 웹 부하테스트용 클라이언트이고 범용으로 사용할 수 있는 클라이언트 (예를 들어 데이타 베이스 테스트)용으로 Locust라는 클래스를 제공한다. 하나의 부하테스트에서는 한 타입의 클라이언트만이 아니라 여러 타입의 클라이언트를 동시에 만들어서 실행할 수 있다.

예를 들어서 하나의 부하 테스트에서 안드로이드용 클라이언트와, iOS용 클라이언트를 동시에 정의해서 부하 비율을 정의해서 테스트(안드로이드,iOS = 7:3으로) 하는 것이 가능하다.

TaskSet

클라이언트(메뚜기)를 정의했으면, 이 클라이언트가 어떻게 부하를 줄지 시나리오를 정의해야 하는데, 이를 TaskSet이라고 한다. 

TaskSet는 Task의 집합으로 예를 들어, 테스트 시나리오에 리스트 페이지 보기, 상품 선택하기, 댓글 달기 등의 Task등을 정의할 수 있다.

Task

Task는 @task 라는 어노테이션으로 정의하면, TaskSet이 실행될때, TaskSet안에 있는 task를 random 하게 선택해서 실행한다. 아래 예제를 보자


class MyTaskSet(TaskSet):

    wait_time = between(5, 15)


    @task(3)

    def task1(self):

        pass


    @task(6)

    def task2(self):

        pass


위의 예제는 task1과, task2 를 랜덤하게 선택해서 실행하게 하는데, 괄호안의 숫자는 가중치가 된다. 즉 위의 TaskSet은 task1과 task2를 3:6의 비율로 실행하도록 한다.

wait_time

Task를 실행한 다음에 다른 Task를 수행할때 까지 delay 타임을 줄 수 있는데, TaskSet 클래스에 wait_time이라는 attribute로 정의되어 있다. 여기에 값을 지정해주면, 그 시간만 기다렸다가 다음 task를 수행한다. 위의 예제에서는 between(5,15)를 이용해서 5~15초 사이 시간 만큼 랜덤하게 기다렸다가 다음 task를 실행하도록 하였다. 

TaskSequence  & seq_task

TaskSet의 다른 종류로 TaskSequence 라는 클래스가 있는데, TaskSet이 task를 랜덤하게 실행한다고 하면, TaskSequence는 Task를 순차적으로 실행한다. 웹 테스트를 보면, 웹사이트에서 로그인하고, 초기 화면을 들어가고, 상품 목록 페이지로 이동하고 다음 상품 상세 페이지를 보는 것과 같은 순차적인 테스트가 필요할 수 있는데, 이를 지원하기 위한것이 TaskSequence 이다.

TaskSet과 다르게, TaskSequence내에서 순차적으로 실행해야하는 task는 seq_task()로 정의하고, 괄호 안에는 그 순서를 정의한다. 

아래 예제를 보자


class MyTaskSequence(TaskSequence):

    @seq_task(1)

    def first_task(self):

        pass


    @seq_task(2)

    def second_task(self):

        pass


    @seq_task(3)

    @task(10)

    def third_task(self):

        pass


위의 예제는 first_task를 실행하고, 두번째는 second_task를 실행한 후에, 세번째는 third_task가 실행되는데, @task(10) 으로 지정되어 있기 때문에, 10번이 실행된다. 

Nesting

Locust에서 TaskSet은 다른 TaskSet을 호출(Nest) 하는 것이 가능하다. 

예를 들어 부하 테스트 시나리오에서, 게시판에 글을 쓰는 시나리오와 상품을 구입하는 시나리오가 하나의 클라이언트에서 동시에 일어난다고 했늘때, 게시판에 글을 쓰는 TaskSet과, 상품을 구입하는 TaskSet을 정의하고 전체 시나리오에서 이 두 TaskSet을 호출하는 것과 같이 반복적이고 재사용적인 시나리오를 처리하는데 사용할 수 있다. 

아래 예제를 보자, 아래 예제는 UserBahavior TaskSet에서 Nested 라는 TaskSet을 호출하는 예제이다. 


from locust import Locust,TaskSet,task,between


class Nested(TaskSet):

        @task(1)

        def task1(self):

                print("task1")


        @task(1)

        def task2(self):

                print("task2")


        @task(1)

        def stop(self):

                print("stop")

                self.interrupt()



class UserBehavior(TaskSet):

        tasks = {Nested:2}


        @task

        def index(self):

                print("user behavior task")



UserBehavior에는 index라는 Task와 Nested TaskSet이 정의되어 있고, Nested TaskSet은 가중치가 2로 되어 있기 때문에, index task에 비해서 2배 많이 호출된다.

Nested TaskSet은 task1,task2,stop을 가지고 있느넫, 각각 가중치가 1로 이 셋중 하나가 랜덤으로 실행되는데, Nested TaskSet이 실행이 시작되면, 계속 Nested TaskSet 안의 task들만 실행이 되고, 그 상위 TaskSet인 UserBehavior가 원칙적으로는 다시 호출되지 않는다. 즉 Nested TaskSet의 task들로 루프를 도는데, 그래서 stop task에 self.intrrupt를 호출해서, Nested TaskSet 호출을 멈추고, UserBehavior TaskSet으로 리턴하도록 하였다. 

Hook

TaskSet은 실행 전후에, Hook을 정의할 수 있다.

setup & teardown

setup과, teardown은 TaskSet이 생성되었을때와 끝나기전에 각각 한번씩만 수행된다. 

locust가 실행되면, TaskSet 별로 정의된 setup 메서드가 실행되고, 프로그램을 종료를 하면, 종료하기전에 teardown 메서드가 실행된다. 

setup과 teardown은 테스트를 위한 준비와 클린업등에 사용할 수 있는데, 예를 들어 테스트를 위한 데이타 베이스 초기화 등에 사용할 수 있다. 주의 할점은 클라이언트를 여러개 만든다고 하더라도, TaskSet의 setup과 teardown은 각각 단 한번씩만 실행이 된다.

on_start,on_stop

setup과 teardown이 전체 클라이언트에서 한번만 실행된다면, 클라이언트마다 실행되는 Hook은 on_start와  on_stop이 된다. 

on_start는 클라이언트가 생성될때 마다 한번씩 실행된다. 


from locust import Locust,TaskSet,task,between


class UserBehavior(TaskSet):

def setup(self):

print("nested SETUP");


def teardown(self):

print("nested TEAR DOWN");


def on_start(self):

print("nested on start");


def on_stop(self):

print("nested on stop");


@task(1)

def task1(self):

print("task1")


@task(1)

def task2(self):

print("task2")



class User(Locust):

task_set = UserBehavior

wait_time = between(1,2)


코드를 

%locust -f ./locust.py --no-web -c 2 -r 1 

명령으로 2개의 클라이언트를 실행하게 되면 on_start는 단 두번 실행이 된다.

Locust setup & teardown

TaskSet의 Hook에 대해서 알아봤는데,  테스트 클라이언트인 Locust class도, setup과 teardown hook을 가지고 있다. 하나의 Locust 클라이언트는 여러개의 TaskSet을 가지고 있기 때문에, TaskSet마다 정의된 setup과 teardown이 실행되지만, Locust는 하나만 존재하기 때문에, 전역적으로 setup과  teardown은 정확하게 단 한번만 수행이 된다. 

Locust와 TaskSet의 실행 순서를 보면 다음과 같다. 


실행 순서

  • Locust setup

  • TaskSet setup

  • TaskSet on_start

  • TaskSet tasks…

  • TaskSet on_stop

  • TaskSet teardown

  • Locust teardown




본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

Istio Traffic management

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


Istio의 기능중의 하나가, 들어오는 트래픽을 여러 서비스로 라우팅을 하거나, 또는 트래픽에 테스트를 위해서 인위적으로 장애를 만들어 내는 것과 같은 트래픽 관리 기능이 있다. 

이러한 기능을 사용하려면, Istio에서 트래픽 관리에 사용되는 컴포넌트들의 컨셉을 알아야 한다. 초기에 Kubernetes의 트래픽 관리 기능인 Service, Ingress와 개념이 헷갈렸는데,  잘 정리해놓은 문서가 있어서 개념을 잘 정리할 수 있었다. 

Istio 트래픽 관리 컴포넌트는 크게 Gateway, VirtualService, DestinationRule 3가지로 정의된다.

Gateway

Gateway는 외부로부터 트래픽을 받는 최앞단으로, 트래픽을 받을 호스트명과 포트, 프로토콜을 정의한다. 웹서버에서 버추얼 호스트를 정의하는 것과 같이 정의 된다.  

아래는 Gateway의 예제이다. 


apiVersion: networking.istio.io/v1alpha3

kind: Gateway

metadata:

  name: ext-host-gwy

spec:

  selector:

    app: my-gateway-controller

  servers:

  - port:

      number: 443

      name: https

      protocol: HTTPS

    hosts:

    - ext-host.example.com

    tls:

      mode: SIMPLE

      serverCertificate: /tmp/tls.crt

      privateKey: /tmp/tls.key



이 설정은 ext-host.example.com 로 HTTPS 프로토콜로 443 포트를 통해서 트래픽을 받겠다고 정의한것이다. 

Virtual Service

들어온 트래픽을 서비스로 라우팅 하는 기능이다. 쿠버네티스의 Service가 목적지가 되는 것이고, 실제로 Virtual Service의 기능은 URI 기반으로 라우팅을 하는 Ingress와 유사하다고 보면 된다.


클라이언트가 쿠버네티스의 Service를 호출할때 라우팅 규칙에 따라서 적절한 쿠버네티스 Service로 라우팅 해주는 기능을 한다. 

아래 예제를 보자


apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

  name: bookinfo

spec:

  hosts:

  - "*"

  gateways:

  - bookinfo-gateway

  http:

  - match:

    - uri:

        exact: /productpage

    - uri:

        prefix: /static

    - uri:

        exact: /login

    - uri:

        exact: /logout

    - uri:

        prefix: /api/v1/products

    route:

    - destination:

        host: productpage

        port:

          number: 9080


예제 출처: https://github.com/istio/istio/blob/master/samples/bookinfo/networking/bookinfo-gateway.yaml

hosts는 클라이언트가 요청한 request에 대해서 어떤 host를 호출한것을 처리할것인지를 결정한다. 여기서는 hosts: “*”로 하였기 때문에, 모든 서비스 호출에 대해서 이 Virtual Host가 라우팅 처리를 하는데, 모든 서비스 호출은 gateways: bookinfo-gateway를 이용하여, 어느 gateway에서 들어온 요청을 처리하는지 지정할 수 있다. 

즉 이 VirtualService는 bookinfo-gateway 를 통해서 들어온 모든 요청에 대해서 처리하게 된다. 


그 다음 http: 부분에서 URL 별로 라우팅을 하도록 정의하였다.

http: match: 부분을 보면 /product, /login,/logout은 exact로 정의가 되어 있기 때문에 이 URL로 매칭 되는 요청이나 또는 /static, /api/v1/products는 prefix로 정의되어 있기 때문에, /static, /api/v1/products URI로 시작하는 요청에 대해서 라우팅을 처리한다.


라우팅 Destination은 route: destination에 정의하는데 productpage 쿠버네티스 Service의 9080, 즉 products.{namespace}.svc.cluster.local 서비스로 포워딩이 된다. 


앞의 예제는 gateway를 통해서 들어온 트래픽을 처리하는 방법이었다. 다른 예제를 보자. 아래 Virtual Service는 gateway와 연동되어 있지 않고 reviews.prod.svc.cluster.local 서비스를 호출하는 요청에 대한 라우팅 규칙을 정의하였다.


apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

  name: reviews-route

spec:

  hosts:

  - reviews.prod.svc.cluster.local

  http:

  - name: "reviews-v2-routes"

    match:

    - uri:

        prefix: "/wpcatalog"

    - uri:

        prefix: "/consumercatalog"

    rewrite:

      uri: "/newcatalog"

    route:

    - destination:

        host: reviews.prod.svc.cluster.local

        subset: v2

  - name: "reviews-v1-route"

    route:

    - destination:

        host: reviews.prod.svc.cluster.local

        subset: v1

예제 출처 :https://istio.io/docs/reference/config/networking/virtual-service/ 


reviews.prod.svc.cluster.local을 호출하였을때 URI가 /wpcatalog 나 /consumercatalog로 시작한다면 이를 /newcatalog로 바꾼 후에, reviews.prod.svc.cluster.local 쿠버네티스 서비스를 호출하는데, subset이 v2로 되어 있기 때문에, reviews.prod.svc.cluster.local pod 중에 v2 subset으로만 라우팅 한다.  (subset이라는 부분이 있는데, 이 부분은 추후에 설명한다.)

만약 이 URI와 매칭되는 것이 없다면, 

  - name: "reviews-v1-route"

    route:

    - destination:

        host: reviews.prod.svc.cluster.local

        subset: v1

에 의해서 다른 모든 트래픽은 reviews.prod.svc.cluster.local pod 중에 v1 subset으로만 라우팅 된다.


다른 예제를 하나 더 보자

apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

  name: my-productpage-rule

  namespace: istio-system

spec:

  hosts:

  - productpage.prod.svc.cluster.local # ignores rule namespace

  http:

  - timeout: 5s

    route:

    - destination:

        host: productpage.prod.svc.cluster.local

예제 출처 : https://istio.io/docs/reference/config/networking/virtual-service/


이 예제는 productpage.prod.svc.cluster.local 쿠버네티스 서비스를 호출 하는 Virtual Service 인데, 별도의 라우팅 없이 productpage.prod.svc.cluster.local 를 그대로 호출하도록 정의하였는데, timeout: 5s 로 줘서, 해당 서비스가 5초 동안 응답이 없으면 timeout 처리를 하도록 하였다. 


Virtual Service의 개념을 이해할때 자칫 헷갈릴 수 있는데, 아래 두가지를 기억하면 이해에 도움이 된다. 

  • 쿠버네티스의 Ingress 처럼 들어오는 트래픽에 대해서 URI 나 Header 등 요청에 따른 라우팅을 한다

  • Virtual Service는 Gateway를 통해서 들어오는 트래픽을 받아서 Service로 라우팅 시킬 수 도 있고 또는 쿠버네티스 Service로 가는 요청에 대해서 라우팅을 할 수 도 있다. 

DestinationRule

Virtual Service가 쿠버네티스 Service로 트래픽을 보내도록 라우팅을 했다면, 그 다음 Destination Rule은 그 서비스로 어떻게 트래픽을 보낼것인가를 정의한다. (하나의 Detination Rule은 하나의 Service에 대해서만 정의한다)


Virtual Service가 라우팅을 해서 Service로 트래픽을 보내면, 실제로는 그 안에 있는 Pod 들에 라우팅이 되는데, 어느 Pod 들로 트래픽을 보낼지, 그리고, 어떻게 보낼지를 정의한다. 


아래 예제는 my-svc.{namespace}.svc.cluster.local 서비스로 보내는 트래픽을 어떻게 보낼지를 정의하였는데, my-svc.{namespace}.svc.cluster.local  를 version이라는 pod 의 label에 따라서 v1,v2,v3 로 그룹핑을 하였다. 이를 subset이라고 한다. 


apiVersion: networking.istio.io/v1alpha3

kind: DestinationRule

metadata:

  name: my-destination-rule

spec:

  host: my-svc

  trafficPolicy:

    loadBalancer:

      simple: RANDOM

  subsets:

  - name: v1

    labels:

      version: v1

  - name: v2

    labels:

      version: v2

    trafficPolicy:

      loadBalancer:

        simple: ROUND_ROBIN

  - name: v3

    labels:

      version: v3

예제 출처 : https://istio.io/docs/concepts/traffic-management/#destination-rules


그 후에 pod 간 로드 밸런싱 정책은 

  host: my-svc

  trafficPolicy:

    loadBalancer:

      simple: RANDOM

와 같이 디폴트는 RANDOM 로드 밸런싱을 정의하였지만 subset v2의 경우에는

  - name: v2

    labels:

      version: v2

    trafficPolicy:

      loadBalancer:

        simple: ROUND_ROBIN


와 같이 ROUND_ROBIN을 사용하도록 하였다. 

Destination Rule을 사용하면, 서비스로 트래픽을 보내더라도, pod의 label에 따라서 서비스내의 pod들에 골라서 트래픽을 보낼 수 있고, pod간의 로드밸런싱이나 여러 정책을 정의하는 것이 가능하다.


대략적으로 이 개념들을 정리해서 하나의 그림에 그려보면 다음과 같다



읽어볼만 한글


참고할 사항

Ingress-gateway라는 용어가 종종 나오는데, 이는 Kubernetes에서 Gateway 를 구현하기 위한 실제 Pod이다. Service + Deployment로 되어 있고, 스케일을 위해서 HPA를 적용할 수 있다.


Istio를 사용한다고 해서 외부에서 들어오는 트래픽을 반드시 Istio gateway를 사용해야 하는 것은 아니다. 기존의 Kubernetes Ingress를 그대로 사용할 수 도 있고, Kong 과 같은 API gateway를 사용하는 것도 가능하다.


다른 Service간의 통신은 아래 그림과 같이 pod → 다른 Service의 Pod를 호출할때, 중간에 Load balancer를 거치거나 하지 않고 아래 그림과 같이 Envoy Proxy를 통해서 바로 이루어진다. 생각해보니, ClusterIP는 원래 Load balancer를 거치지 않았어..


그림 원본 https://medium.com/@zhaohuabing/which-one-is-the-right-choice-for-the-ingress-gateway-of-your-service-mesh-21a280d4a29c


예제 코드 참고 https://istio.io/docs/concepts/traffic-management/


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

  1. bae 2020.02.13 17:47  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 훌륭한 블로그 항상 잘 참조하고 있습니다.
    https://istio.io/docs/reference/config/installation-options/#mixer-options의
    mixer.policy.autoscaleEnabled option을 보았을 때 mixer의 autoscale이 가능한 것 같은데
    autoscale의 기준이나 기준을 설정하는 방법에 대해 여쭤보고 싶습니다.

리눅스 방화벽과 NAT를 위한 ipTables


서버에서 라우팅 설정을 하다보면, 다른 포트로 받아야 하는데, 로드밸런서나 방화벽등의 문제로 포트를 변경할 수 없는 경우가 있어서 A포트로 들어오는 트래픽을 B포트로 변경하고 싶을때가 있다.

또는 서버로 들어오는 트래픽을 IP등으로 선별적으로 받는 것과 같은 방화벽 역할이 필요한 경우가 있는데, 방화벽을 설치하지 않고 서버단에서 간단하게 하는 방법이 필요한 경우가 있는데, 이러한 용도를 위해서 사용할 수 있는 것이 ipTables이다. 


ipTables는 리눅스 firewall로 incoming & outgoing traffic을 rule에 따라 filtering 하는 기능을 가지고 있다. ipTables는 table이라는 단위를 가지고 있는데, 이 table 마다 룰을 정해서 룰에 매치되는 트래픽에 대해서 핸들링할 수 있는 기능이 있다. 

주로 사용되는 테이블은 다음과 같다

  • Filter table :  가장 많이 사용되는 테이블로, 트래픽에 대한 컨트롤을 하는 방화벽 역할로 사용된다.

  • NAT table :  들어오는 패킷을 다른 포트나 다른 호스트 서버로 라우팅 하는 역할을 한다.

  • Mangle table : 패킷의 헤더를 변경하는데 사용할 수 있다. (예 TTL 등)


각 테이블은 체인(CHAIN)으로 구성이 되는데, 각 단계별로 하는 역할이 다르다.


Filter table

먼저 Filter Table을 보면 다음과 같다. 

%iptabls -L -v

명령어를 이용하면 필터 테이블을 볼 수 있는데, 크게 Input,Forward,Output 체인으로 구성이 되어 있다. 

예를 들어 보면

%sudo iptables -A INPUT -i lo -j ACCEPT

는 모든 들어오는 트래픽에 대해서 Loopback interface는 모든 트래픽을 허용하도록 INPUT CHAIN을 설정한 것이다. 

만약 http,https,SSH 만 허용하려면 다음과 같이 사용하면 된다. 

%sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

%sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

%sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT


특정 IP에서 들어오는 트래픽을 허용하고자 하면 

sudo iptables -A INPUT -s 192.168.1.3 -j ACCEPT

게 설정하면 된다. 

반대로 특정 IP에서 들어오는 트래픽만 막고자 한다면 DROP을 하면 되는데, 

sudo iptables -A INPUT -s 192.168.1.3 -j DROP

명령을 사용하면 된다. 


등록된 rule을 삭제하려면

sudo iptables -L --line-numbers

명령을 사용하면, 등록된 룰이 번호와 함께 나오고 

sudo iptables -D INPUT 3

명령으로 3번째 룰을 삭제할 수 있다. 


iptables 명령어는 수정한 테이블과 룰은 메모리에만 남아 있기 때문에 재기동하면 삭제된다. 

그래서 영구 반영하기 위해서는

sudo /sbin/iptables-save

명령으로 저장해야 한다. 

NAT table

다음으로 유용하게 사용할 수 있는것이 NAT 테이블인데 들어온 트래픽을 다른 호스트로 라우팅 시키거나 또는 같은 호스트내의 다른 포트로 포워딩 시키는 것이 가능하다. 

패킷은 들어오면 아래와 같은 체인들을 거쳐가는데, 그림에서 보면 nat와 filter 체인이 섞여 있는 것을 볼 수 있다. 


트래픽은 들어오면 NAT의 PRE ROUTING을 거쳐, FILTER의 INPUT을 거쳐서 애플리케이션으로 들어갔다가 나올때 FILTER의 OUTPUT 체인과, NAT의 OUTPUT 체인을 거쳐서 최종적으로 NAT의 POSTROUTING 체인을 거쳐서 목적지로 라우팅 된다. 



그림 출처 : https://www.systutorials.com/816/port-forwarding-using-iptables/


이 과정에서 NAT 테이블의 체인 중 주로 사용되는 CHAIN을 보면

  • PREROUTING : 들어온 트래픽을 다른 호스트나 또는 같은 호스트의 다른포트로 포워딩 할때 사용된다.

  • POSTROUTING : 호스트에서 아웃바운드로 나가는 트래픽을 다른 IP나 포트로 포워딩 한다. 


예를 들어보면 아래는 80포트로 들어온 트래픽을 같은 호스트의 4000포트로 포워딩하는 설정이다. 


sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 4000


참고로 이렇게 하면 외부에서 80 포트로 들어오는 트래픽은 4000으로 라우팅 되지만, localhost:80 으로 호출을하면 이는 라우팅이 되지 않는데, 이유는 localhost는 PREROUTING CHAIN을 타지 않기 때문이다. 그래서 만약 localhost에도 동일하게 작동하게 하려면 Output chain을 아래와 같이 설정해줘야 한다.

iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

참고 : https://askubuntu.com/questions/444729/redirect-port-80-to-8080-and-make-it-work-on-local-machine


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

API 게이트 웨이

What is API 게이트웨이

API 게이트웨이는 API 클라이언트와 서비스 사이에 프록시 처럼 위치하여, 클라이언트로 부터 온, API 요청을 받아서, 서비스로 라우팅 하는 역할을 한다. 

각각의 서비스에서 구현해야 하는 기능을 API 게이트웨이단으로 통합함으로써, 서비스 개발을 간편하게 할 수 있고, 메세지 포맷 변경이나, 플로우 컨트롤과 같은 고급 기능을 사용하여, 기존 API 호출에 추가적인 기능을 더할 수 있는 유연성을 더할 수 있게 된다.


여러가지 기능이 있겠지만, 크게 아래와 같이 5가지로 나눠볼 수 있다.

  • 인증

  • 모니터링,로깅

  • 플로우 컨트롤

  • 메시지 변경

  • 오케스트레이션(매쉬업)





<출처 : 마이크로소프트 애저 홈페이지>


인증

API를 호출할때, API 호출에 대한 인증을 수행한다. 서버간의 API 통신일 경우에는 간단하게 API 키를 사용하는 방법등이 있고, 다수의 클라이언트 (모바일 단말, 웹 자바스크립트 클라이언트)의 경우에는 사용자 계정 인증 후에, JWT 토큰을 발급하는 방식이 있다. 

모니터링 & 로깅

API 호출에 대한 모니터링을 통해서 API 별 응답시간, 장애율,호출 횟수를 모니터링 할 수 있게 하고, 경우에 따라서 개별 API 호출을 로깅한다. 

Metering & Charging

API를 대외로 서비스 하는 경우, API의 호출량을 모니터링 해야 하는데, 이 양을 기반으로 해서 API 서비스 호출 횟수를 통제하거나 또는 유료 API의 경우에는 과금을 해야 하기 때문에, 대외로 서비스를 하는 오픈 API 플랫폼인 경우에는 이러한 기능이 필요하다. 

메시지 플로우 컨트롤

메시지 플로우 컨트롤은 클라이언트로 부터 들어온 메시지의 흐름을 바꾸는 방법인데, 예를 들어 같은 API라도 버전에 따라서 구버전과 새버전으로 트래픽을 9:1로 라우팅하는 카날리 배포 방식이라던지, 들어온 API를 클라이언트의 위치에 따라 미국이나,한국에 있는 서비스로 라우팅을 하는 등의 로직을 구현할 수 있다. 

Limiting (throttling)

조금 더 고급화된 구현방법으로는 Limiting 기법이 있는데, 특정 API에 대해서 정해진 양 이상의 트래픽을 받지 못하도록 하여, 서비스를 보호하거나 또는 유료 API 서비스인 경우 QoS (Quality Of Service)를 보장하기 위해서 특정 양까지만 트래픽을 허용하도록 컨트롤할 수 있다. 

메시지 변환

메시지 변환은 고급 기능 중의 하나인데, 헤더에 있는 내용을 메시지 바디로 붙이거나, 다른 API를 호출해서 응답메시지를 두 API를 합해서 응답을 해주는 기능등을 구현할 수 있다. 

프로토콜 변환

그외에도 서로 다른 프로토콜에 대한 변환이 가능한데, 예를 들어 클라이언트로 부터는 gRPC로 호출을 받고, API서버로는 REST로 호출한다던가. SOAP/XML 기반의 메시지를 REST로 변환하는 등의 기능 구현이 가능하다. 

호출 패턴 변환

API 서버가 큐를 기반으로한 비동기 메시지 처리 패턴일때, API 게이트 웨이에서 이를 동기화 처리 패턴으로 바꿔서, 클라이언트 입장에서는 동기로 호출을 하면, API 서버에서는 비동기로 호출하는 형태와 같이 호출 패턴을 변화시킬 수 있다. 

오케스트레이션(매시업)

API 클라이언트가 한번 호출을 할때, 동시에 여러개의 API를 호출하도록 API 게이트웨이에서 호출을 해주는 방식이다. 예를 들어 API 클라이언트가 “상품 구매" 라는 API를 호출 하였을때, API 게이트웨이가 “상품 구매" API 서비스를 호출하고, “상품 추천" 이라는 API도 호출해서, 클라이언트로 구매 완료 메시지와 함께 추천 상품 리스트를 한번에 리턴하는 방식이다. 


경량형 게이트웨이와 중량형 게이트웨이

API 게이트 웨이는 근래에 마이크로 서비스가 각광을 받으면서 언급되기 시작하고 있지만, 사실 API 게이트 웨이라는 기술 자체는 오래된 기술이다. API 게이트 웨이 이전에는 SOA 제품군의 일부로 ESB (Enterprise Service Bus)가 SOAP/XML 웹서비스 기반으로 이러한 역할을 해주었는데, 잘못된 구현 패턴을 사용해서 문제가 된 케이스가 많다. 예를 들어서 과도하게 오케스트레이션 기능을 사용하였을 경우 API 게이트웨이 자체에 부하가 걸려서 전체 API 서비스에 성능 문제를 야기 하거나, 또는 파일 업로드 요청을 API 게이트 웨이를 통해서 함으로써 게이트웨이의 부하때문에 성능 문제가 생기는 경우가 있었다. 

SOAP/XML에서 REST API로 오면서, API 게이트웨이도 이러한 문제를 피하는 패턴형식으로 API 게이트 웨이가 포지셔닝이 달라졌다. 사실 매시업이나, 메시지 라우팅, 메시지 변환등과 같은 기능들은 사실 사용되는 일이 그렇게 많지 않고, 클라이언트단에서 처리해도 충분하기 때문에, 이러한 고급 기능 보다는 API 인증, 로깅/모니터링 위주의 최소한의 기능만 가지면서 경량화된 형태로 배포 및 운영이 간편한 경량형 API 게이트 웨이들이 소개 되기 시작하였고, 반대로 오픈 API를 고객을 대상으로 제공하면서 메시지 변환이나, 과금등 고급 기능을 제공하는 중량형 API 게이트 웨이들도 꾸준히 제품들이 제공되고 있다.


이러한 제품들에 대한 명시적인 구분은 없지만, 필자의 기준으로 봤을때, 경량형 API 게이트 웨이는 Kong등을 들 수 있고, 중량형 API 게이트 웨이는 APIgee, MuleSoft 등을 들 수 있다. 


전통적으로 중량형 API 게이트웨이의 아키텍쳐 구조는 API 게이트웨이가 클러스터 형태로 배포 된 후에, 그 뒤에 API 서버들이 배포되는 형태라면, 경량형 API 게이트웨이는 nginx나 node.js와 같은 가벼운 프록시 서버로 배포하고, 용량이 필요하다면 개별의 인스턴스들을 스케일링으로 늘리는 방법을 사용한다. 근래에는 중량형 API 게이트 웨이도 이러한 구조를 많이 참고해서 구조를 개선하고 있다. 

쿠버네티스와 API 게이트 웨이

이러한 API 게이트웨이들이 쿠버네티스의 등장과 함께 주목 받으면서, 쿠버네티스 지원을 넓혀나가고 있는데, 이 과정에서 흥미로운 아키텍쳐 구성을 보인다. 쿠버네티스는 이미 Service라는 리소스를 통해서 마이크로 서비스를 정의할 수 있고, Service를 묶어서 로드밸런싱을 하는 Ingress라는 리소스를 가지고 있다. 즉 Ingress는 Service간의 라우팅을 할 수 있는 일종의 초경량 API 게이트웨이로써의 기능을 가지는 구조인데, API 게이트웨이의 쿠버네티스 연동은, 새롭게 애플리케이션을 배포하고 설정하는 방식이 아니라 쿠버네티스 리소스 자체에 API 게이트웨이를 녹이는 방식으로 접근하고 있다.


아래 Kong API 게이트웨이의 구조를 보면, Kong 전용으로 컨테이너를 배포하지 않고, Kong API 게이트웨이용 Ingress를 정의해서 사용자가 Ingress를 사용하게 되면 API 게이트웨이를 사용할 수 있게 하였고, 메시지 변환이나 기타 기능이 필요할 경우 이를 CRD(Custom Resource Definition) 로 미리 등록해놔서 이를 사용하도록 하고 있다. 



<Kong API 게이트 웨이 아키텍처 출처  > 


구글의 Apigee의 경우에는 Apigee Hybrid라는 이름으로 Kubernetes를 지원하는데, 구조는 Istio Ingress를 앞단에 둬서 라우터로 이용하고 API 게이트웨이에서 처리하는 각종 로직을 Ingress 뒤에 Pod(아래 그림 4번)로 배포하여 기능을 수행하도록 하고 있다. 

API 호출을 통과 시키는 프록시는 이렇게 단순화 시키고, 관리 콘솔은 구글 클라우드로 위치 시켜서 운영을 최대한 단순화 시켰다. 


<출처 : 구글 apigee hybrid 문서 > 

구글의 API 게이트 웨이 - Cloud Endpoint

구글의 Cloud Endpoint는 경량형 API이다. 

기본적으로 nginX로 배포되서 프록시 처럼 동작하며, 기능은 API 인증과 모니터링/로깅정도 밖에 없지만 구조와 기능이 단순하기 때문에 사용이 편리하며, 또한 nginX 기반의 배포 방식으로 인해서 구글 클라우드 뿐만 아니라, 다른 클라우드나 ON-PREM에도 사용이 가능하다는 장점이 있다. 


구조는 다음과 같이 되어 있다.



<그림. Cloud Endpoint ESP 아키텍쳐 / 출처 >


ESP는 Cloud Endpoint의 Proxy, 즉 nginX를 이야기 한다. 프록시는 실제로 쿠버네티스 클러스터에 배포되고, 모니터링과 로깅, 컨트롤러는 구글 클라우드의 콘솔을 이용하게 된다. 

쿠버네티스에 배포하는 형태는 다음 그림과 같다. 

애플리케이션 컨테이너와 함께, 같은 Pod로 패키징 되고, ESP에서는 애플리케이션으로 트래픽을 localhost로 포워딩한다. 


<그림. ESP의 쿠버네티스 배포 형태 >


이런 구조를 가지기 때문에, Ingress, Istio 등 다른 쿠버네티스 플랫폼들과 유연하게 통합이 된다. 

API 인증은 여러가지 방법을 지원하는데, Firebase authentication, Google ID,SAML(SSO) 방식으로 사용자를 인증한후에, API 키(JWT)를 발급하는 방법이나, 서버간일 경우 Service Account를 사용하는 방법 또는 단순하게 API KEY를 사용하는 방법이 있고 또는 Custom 인증을 구현하여 JWT 토큰으로 인증할 수 있다. 인증 방식은 이 문서를 참고하면 된다.

로깅/모니터링

로깅과 모니터링은 End Point 콘솔에서 볼 수 있으며, 아래와 같이 서비스 단위로 응답 시간과 에러율등을 출력해준다


OPEN API (Swagger) 기반 포탈

그리고 중요한 기능중의 하나인데, API 스펙 문서를 포탈 형태로 보여준다. Swagger 문서 형태로 작성을 하면 아래와 같이 독립된 웹사이트 형태로 API 스펙 문서를 보여준다.


<그림. Cloud endpoint developer portal / 출처


멀티/하이브리드 클라우드

마지막으로, Cloud End Point는 ngnix 기반으로 배포가 가능하기 때문에, 구글 클라우드 뿐 아니라 타 클라우드나 온프렘 데이타 센터내에도 프록시(ESP)를 배포해서 사용하는 것이 가능하다. 


참고 문서


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

오픈소스 API 게이트웨이 Kong

#3 쿠버네티스 Kong

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


Kong Kubernetes

API 게이트웨이가 마이크로서비스의 중요 컴포넌트이다 보니, Kong이 마이크로 서비스에 적합한 K8S (aka. 쿠버네티스)에 어떻게 적용될 수 있는지가 궁금해서 아키텍쳐 관련 설명 내용을 찾아보았다. https://konghq.com/blog/kong-kubernetes-ingress-controller/ (아래 그림은, 이 동영상에서 캡춰하여 사용하였다.) 에 보면 Kong summit에서 발표된 영상이 있는데, 정리가 매우 잘되어 있다. 


기본 컨셉을 먼저 요약해보자면, Kong의 리소스들은 K8S의 리소스로 등록되어 사용되게 된다. API 게이트 웨이는 Ingress로 등록되고, Plugin들은 CRD (Kubernetes Custom Resource Definition)으로 등록되어 적용된다. 즉 별도의 Kong 설정을 하는 것이 아니라 쿠버네티스 리소스로 쿠버네티스 아키텍쳐에 따라서 등록이 된다는 이야기이다. 별도의 서비스나 서버를 기동하는 것이 아니라 K8S의 일부로 사용할 수 있도록 하고 있다. (동영상에서도 나오지만 Kong이 K8S 1’st citizen임을 계속 강조하고 있다.)


Kong의 쿠버네티스내의 배포 아키텍쳐는 다음과 같다. 

먼저 관리 서버 부분인 Control plane과, API 게이트 웨이 부분인 Data plane으로 나뉘어 진다. 


Control plane에는 Ingress controller가 있고, 이는 K8S의 API 서버와 통신을 통해서 명령을 받는다. 그리고 이 설정 정보를 psql 데이타 베이스에 저장한다. 

저장된 설정 정보는 Data plane 즉, API 게이트웨이 인스턴스에 반영이 된다. 

(만약에 psql이 죽어도 dataplane에 있는 프록시는 설정을 저장하고 있기 때문에 문제 없다.)


Kong의 리소스 맵핑

그러면 Kong API 게이트웨이에서 사용하는 리소스들은 어떻게 쿠버네티스 리소스로 맵핑이 될가?

쿠버네티스의 리소스를 Kong의 리소스로 맵핑(Translate) 해서 서비스 한다. 

아래는 Kong에서 사용하는 리소스들이다. 


이를 쿠버네티스 리소스로 맵핑한것은 다음과 같다. 




  • Route는 Ingress rule로 맵핑이 되고

  • Service 는 쿠버네티스의 Service로 맵핑 된다.

  • Target은 쿠버네티스의 pod가 되고

  • Upstream은 healthcheck와 LB로 정의 된다. 


게이트웨이에서 사용되는 플러그인 (라우팅이나 RateLimiting, Logging, 인증등)은 K8S의 CRD로 정의되어 있다.  CRD를 사용하게 되면 Ingress 에 반영되어서 사용된다.  

아래는 Rate limiting 플러그인을 K8S에서 CRD로 정의해서 사용한 예제이다

플러그인 CRD들은 ingress에 붙어서 사용된다. 




Ingress는 CRD와 함께 Limit rating, Logging, Routing등을 제공한다. 이러한 기능들은 대부분 Istio와 같은 Service mesh에도 있는데, Istio와 같은 서비스 메쉬와 비교할 경우, JWT 등  인증이 안되는 부분이 있는데, 이 부분을 보면 다소 차이가 있다. 


결론적으로 정리를 해보자면, Kong API 게이트웨이는 K8S에서는 Ingress로 정의되고, 플러그인은 CRD로 제공된다. Control Plane을 통해서 설정 정보가 저장되는 형태이다. 


쿠버네티스에 Kong을 설치하는 가이드는  https://docs.konghq.com/install/kubernetes/ 있는데, 설치는 Helm이나 Kustomize로 도 가능한데, 데이타 베이스에 대한 내용을 보면, 다음과 같다. 


"We recommend running Kong in the in-memory (also known as DB-less) mode inside Kubernetes as all the configuration is stored in Kubernetes control-plane. This setup drastically simplifies Kong’s operations as one doesn’t need to worry about Database provisioning, backup, availability, security, etc."


별도의 데이타 베이스 설치는 권장하지 않고, DB-less 모드로 설정하기를 권장하고 있다. 

YAML 파일로 설정을 정의해서 변경할 경우, 설정이 어떻게 전체 게이트웨이로 전파가되는지 등은 실제 테스트를 해봐야 알 수 있을듯



본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

오픈소스 API 게이트웨이 Kong

#2 아키텍쳐와 간단한 테스트

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

Kong 아키텍쳐

Kong API 서버의 배포 아키텍쳐는 다음과 같다. 


출처 : stackoverflow.com

Kong API 게이트웨이 각각 nginx 로 되어 있고, DB 모드의 경우에는 별도의 데이타 베이스 인스턴스를 사용한다. 지원되는 데이타 베이스로는 카산드라나 postgres를 사용할 수 있다. 

데이타 베이스를 사용하더라도 변경된 설정은 인스턴스에 바로 반영되지 않는다. 각 인스턴스가 설정 내용을 캐슁하기 때문인데, 캐쉬가 리프레쉬되어야 설정 내용이 반영된다. 

클러스터에서 Kong API 게이트웨이 인스턴스간의 로드 밸런싱을 하기 위해서는 각 인스턴스 앞에 로드밸런서를 배치 시킨다. 만약에 Session Affinity가 필요하다면 L4에서 IP나 Session based affinity를 설정해야 한다. 


Kong API 게이트웨이에서 각종 기능들은 nginX의 Lua 스크립트 모듈은 OpenResty를 이용한다. 

간단한 API 테스트

개념 이해가 끝났으면 간단한 테스트를 해보자

파이썬으로 간단한 API 서버를 구현하였다. app.py로 simpleapi:9001/api URL로 HTTP GET으로 간단하게 서빙하는 API가 있다고 할때


kong.yml을 다음과 같이 구현한다. 

_format_version: "1.1"


services:

- name: simpleapi

  url: http://simpleapi:9001

  routes:

  - name: simpleapi-route

    paths:

    - /simpleapiservice



simpleapi라는 이름으로 서비스를 등록하고 API 서버의 호스트 경로는 http://simpleapi:9001로 등록한다.

위의 설정은 {Kong IP}/simpleapiservice URL을 —> http://simpleapi:9001로 라우팅하도록 한 설정이다.

%curl localhost:8000/simpleapiservice/api


실행하면 http://simpleapi:9001/api 를 호출한 결과가 리턴된다. 


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요


xip.io is dns proxing service. It is easy to assign dns name to ip address.

This is way to assign Domain to Knative Service


kubectl patch configmap config-domain --namespace knative-serving --patch \

  '{"data": {"example.com": null, "35.225.xxx.216.xip.io": ""}}'



본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

Kong API gateway #1 - 설치와 둘러보기

카테고리 없음 | 2019. 11. 10. 23:58 | Posted by 조대협

오픈소스 API 게이트웨이 Kong

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


Kong API 게이트 웨이를 설치 하는 방법은 여러가지가 있지만, 여기서는 테스트 환경용으로 로컬 환경에 도커 이미지로 간단하게 설치 하는 방법에 대해서 알아본다. 

(도커 기반 Kong API 게이트 웨이 설치 방법 참고 :  https://docs.konghq.com/install/docker/)

전체 설치 방법은 https://docs.konghq.com/install 를 참고하기 바란다. 

설치 하기

Kong 설치하기

Kong API 게이트웨이는 데이타베이스가 필요하지만, 간편하게 데이타 베이스가 없는 dbless 모드로 설치를 한다. 웹 인터페이스를 이용하기 위해서 Konga (https://pantsel.github.io/konga/) 를 추가로 설치할 것인다. Konga도 도커 컨테이너로 설치한다. 이때 Konga도 가 Kong API 게이트웨이로 통신을 해야 하기 때문에,둘을 같은 네트워크로 묶어서 서로 통신하도록 하겠다.

(참고 : 도커 컨테이너는 네트워크 공간이 각각 분리되기 때문에, 다른 컨테이너간에 통신을 하기 위해서는 네트워크 공간을 묶어줄 필요가 있다. )

네트워크 생성하기

Kong API 게이트 웨이 Admin API를  Konga에서 호출할 수 있도록 같은 네트워크로 묶기 위해서 “kong-net”이라는 네트워크를 생성한다

$ docker network create kong-net

디스크 볼륨 생성하기

여기서는 데이타베이스 없이 Kong API 게이트웨이를 설치하기 때문에, 디스크 볼륨을 정의하여 디스크에 설정 정보를 저장하도록 한다. 아래와 같이 디스크 볼륨을 생성한다.  

$ docker volume create kong-vol

설정 파일 작성하기 

디폴트 설정 파일이 있어야 게이트웨이가 기동이 되기 때문에 디폴트 설정 파일을 만든다.

설정 파일은 앞서 만든 kong-vol 볼륨에 kong.yml 로 저장이 된다. 파일을 생성하기 위해서 볼륨의 호스트 파일상의 경로를 알아보기 위해서 docker volume inspect 명령을 사용한다. 


$ docker volume inspect kong-vol


[

     {

         "CreatedAt": "2019-05-28T12:40:09Z",

         "Driver": "local",

         "Labels": {},

         "Mountpoint": "/var/lib/docker/volumes/kong-vol/_data",

         "Name": "kong-vol",

         "Options": {},

         "Scope": "local"

     }

 ]


디렉토리가 /var/lib/docker/volumes/kong-vol/_data임을 확인할 수 있다. 호스트(로컬 PC)의 이 디렉토리에서 아래와 같이 kong.yml 파일을 생성한다. 

kong.yml 파일 (주의 kong.yaml이 아님)

_format_version: "1.1"


services:

- name: my-service

  url: https://example.com

  plugins:

  - name: key-auth

  routes:

  - name: my-route

    paths:

    - /


consumers:

- username: my-user

  keyauth_credentials:

  - key: my-key


생성이 끝났으면 아래 도커 명령어를 이용해서 Kong API 게이트웨이를 기동 시킨다.

% docker run -d --name kong \

     --network=kong-net \

     -v "kong-vol:/usr/local/kong/declarative" \

     -e "KONG_DATABASE=off" \

     -e "KONG_DECLARATIVE_CONFIG=/usr/local/kong/declarative/kong.yml" \

     -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \

     -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \

     -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \

     -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \

     -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \

     -p 8000:8000 \

     -p 8443:8443 \

     -p 8001:8001 \

     -p 8444:8444 \

     kong:latest


설치가 제대로 되었는지

%curl http://localhost:8001 

명령을 이용하여 확인한다. 

Konga설치 하기

다음은 웹 UI를 위해서 Konga를 설치한다. https://pantsel.github.io/konga/

여러가지 설치 방법이 있지만 간단하게 하기 위해서 역시 도커로 설치한다. 

% docker run -d -p 1337:1337 --network=kong-net --name konga -v /var/data/kongadata:/app/kongadata -e "NODE_ENV=production"              pantsel/konga


이때, Kong API 서버 8001 포트로 통신을 해서 관리 API를 호출해야 하기 때문에, network를 "kong-net”으로 설정해서 실행한다. 설치가 끝나면 http://localhost:1337을 접속하면 Konga UI가 나오고, 관리자 계정과 비밀 번호를 입력할 수 있다.


로그인을 하면 Kong API 게이트웨이와 연결을 해야 하는데, 이름(임의로 적어도 된다.) Kong API 게이트웨이 Admin URL을 적어야 한다. 이때 http://kong:8001 을 적는다. (kong은 Kong API 게이트 웨이를 도커 컨테이너로 기동할때 사용한 컨테이너 이름이다. localhost로 하면 Konga 컨테이너의 로컬 호스트를 찾기 때문에 정상적으로 작동하지 않는다. )


이제 설정할 준비가 되었다. 


참고 : kong-dashboard 도 테스트 해봤지만, UI가 Konga가 훨씬 나은듯 해서, 글 내용을 변경함

% docker run --network=kong-net -itd --rm -p 8080:8080 pgbi/kong-dashboard start --kong-url http://kong:8001 --basic-auth {사용자이름}={비밀번호}


쿠버네티스와 Kong 통합 둘러보기

Kong API 게이트웨이를 사용하는 방법은 VM 기반의 일반 서버 앞에 설치해서 사용해도 되지만 쿠버네티스에 같이 사용할 경우 쿠버네티스 기반의 마이크로 서비스 아키텍쳐 패턴 구현에 더 용이 한다. 생각보다 잘 되어 있어서 놀랐다. 

설치 방법은 https://docs.konghq.com/install/kubernetes/ 에 설명되어 있는데, Helm이나 Kustomize로 설치가 가능하고, Kong은 CRD 형태로 지정된 Ingress로 프로비저닝이 된다. 

그리고 설정은 별도의 데이타 베이스 설치가 필요 없고 쿠버네티스의 컨트롤 플레인(마스터)에 저장되기 때문에 운영이 비교적 간단하다.


데이타베이스가 없을 경우, API 메트릭 저장하는 부분에 대해서 우려를 했는데, 아래와 같이 외장 플러그인을 지원한다. 




데이타 독 통합이 지원되고, 프로메테우스를 통하여 메트릭 저장이 가능하다. 그리고 Kong 용 프로메테우스/그라파나 대쉬보드도 훌륭하게 지원되고 있고, 분산 트렌젝션 추적을 위한 ZipKin도 이미 지원하고 있다. 


반면 로깅이 약간 애매한데, ELK(EFK)지원은 직접적으로는 보이지는 않는다. 



대신 파일 로그가 있기 때문에, 이 파일을 읽을 수 있는 Fluentd를 설치하면 될것으로 보이지만, 아무래도 컨테이너를 새로 말아야 하는 부담이 있기도 하고, 이를 통해서 메트릭이나 API 전용 로깅을 하는데는 별도의 설정이 필요할듯 하다. 


일단 GKE (구글 클라우드)의 경우에는 마켓 플레이스를 통해서 쉽게 설치가 가능하니 한번 테스트해볼만한 가치는 있을듯


11월 10일 업데이트

DBless 모드를 사용하게 되면, 동적으로 설정 변경이 불가능하다. Konga를 설치하더라도 설정 내용을 볼수만 있고 설정 변경은 불가능하다. 그외에도 지원되는 플러그인등에 대한 제약 사항이 있기 때문에 이 부분을 주의해야 한다.

제약 사항


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

도커 볼륨

클라우드 컴퓨팅 & NoSQL/도커 & 쿠버네티스 | 2019. 11. 10. 21:38 | Posted by 조대협

도커 볼륨에 대한 메모


도커 컨테이너에서 파일을 저장하게 되면 디폴트로는 도커 컨테이너 Writable 레이어에 저장된다. 컨테이너 레이어에 저장이 되기 때문에, 영속적이지 못하고 컨테이너가 내려가게 되면 지워지는 임시 저장소 이다. 


컨테이너의 디스크를 컨테이너가 내려가더라도 영속적으로 저장하고 관리할 수 있는데, 이를 볼륨(Volume)이라고 한다.


%docker create volume {볼륨 이름} 

명령어를 이용해서 생성이 가능하다.

생성된 볼륨들은


% docker volume ls 

명령으로 확인이 가능하다

볼륨에 대한 상세 설정을 보려면 


%docker volume inspect {볼륨 이름}

을 사용하면 된다. 

docker volume inspect myvol

[

    {

        "CreatedAt": "2019-11-10T21:28:26+09:00",

        "Driver": "local",

        "Labels": {},

        "Mountpoint": "/var/lib/docker/volumes/myvol/_data",

        "Name": "myvol",

        "Options": {},

        "Scope": "local"

    }

]


위의 예제를 보면 생성된 볼륨의 내용을 볼 수 있는데, 로컬(호스트머신)의 어느 디렉토리에 볼륨이 저장되는지 확인할 수 있다. 위의 예제에서는 /var/lib/docker/volumes/myvol/_data 에 저장이 된다.
생성된 볼륨을 컨테이너의 파일 시스템에 마운트 시키는 방법은 다음과 같다.
$ docker service create \
     --mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'
    --name myservice \
    <IMAGE>

src에 생성한 볼륨 이름을 dst에는 이 볼륨을 마운트 시킬 컨테이너 내부의 디렉토리를 지정하면 된다. 

Bind Mount
볼륨의 경우에는 도커 컨테이너에서만 그 파일을 내용을 볼 수 있고, 다른 컨테이너나 또는 호스트에서는 그 파일(디렉토리)의 내용을 볼 수 없다. 호스트나 다른 컨테이너에서도 접근할 수 있는 볼륨이 있는데, 이를 Bind mount라고 한다.


<그림. 도커 컨테이너 디스크 볼륨 종류와 개념 >


볼륨 생성 방법 자체는 동일하나 도커 컨테이너에 마운트 할때 Type을 "type=bind" 라고 



$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest


tmpfs


볼륨 종류중에는 tmpfs라는 타입이 있는데, 일반 볼륨과는 다르게 호스트의 디스크 공간을 사용하는 것이 아니라 내용을 메모리에 저장하는 일종의 램 디스크라고 보면 된다.


storagedriver

마지막으로, 호스트의 디스크 이외의 다른 디스크, 예를 들어 NFS, iSCSI 등을 사용할 수 있는데, 이는 이 디스크 타입을 접근하기 위한 storagedriver를 사용해야 한다. 



참고 자료 & 이미지 예제 출처 : https://docs.docker.com/storage/https://docs.docker.com/storage/bind-mounts/

본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

KNative 를 보던중에, Autoscaling 처리를 어떻게 하는지 확인해보니,

기본은 Knative instance (한 Pod 겠지) 당 처리할 수 있는 concurrent request 수를 정해놓고, 이를 넘을 경우에 auto scaling 하는 방식이다. CPU가 아니라 connection 수를 통해서 한다. 

containerConcurrency limits the amount of concurrent requests are allowed into the application at a given time (hard limit), and is configured in the revision template.

스케일링 설정은


spec:
  template:
    metadata:
      autoscaling.knative.dev/minScale: "2"
      autoscaling.knative.dev/maxScale: "10"

min, max 값을 지정하는데, min 이 0 이면 콜드 스타트가 발생할 수 있기 때문에, 0 보다는 큰 수를 주는 것이 좋다. 


디폴트가 이 concurrent connection 수를 이용하는 방식인데, CPU 베이스도 가능하다.

spec:
  template:
    metadata:
      autoscaling.knative.dev/metric: concurrency
      autoscaling.knative.dev/class: hpa.autoscaling.knative.dev

위의 metric 모드를 사용하게 되면, concurrency 모드를 사용하게 되고, class 모드를 사용하게되면, HPA (Horizontal Pod Autoscaler)를 사용하게 된다. 


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요


1. 클러스터는 Private IP 모드로 생성

이렇게 되면, Node는 External IP를 가지지 못한다. 즉 외부 접근을 막을 수 있다.

  • In bound : Pod로 들어오는 트래픽은 Node에  External IP가 없더라도, Service를 통해서 들어올 수 있다.
  • out bound : Cloud NAT를 설정하면 된다.
2. Master Node에 대한 접근 제안
Master authorized network 를 설정한후, authorized network에 master node를 사용할 (kubectl) 대역을 지정한다.


본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요