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


Archive»


 
 


문제 정의 

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

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


이런 문제를 해결하는 방법으로는 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가 내려간 값만 관측이 될 수 있다. 스냅삿에 대한 연속된 모음일 뿐이고, 근사값의 형태라는 것을 기억할 필요가 있다. 

싱글 호스트

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




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

댓글을 달아 주세요

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의 기준이나 기준을 설정하는 방법에 대해 여쭤보고 싶습니다.

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": ""}}'



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

댓글을 달아 주세요

도커 볼륨

클라우드 컴퓨팅 & 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) 대역을 지정한다.


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

댓글을 달아 주세요

효율적인 도커 이미지 만들기

#2 도커 레이어 캐슁을 통한 빌드/배포 속도 높이기

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


도커는 이미지 단위로 빌드를 하고 배포를 하지만, 도커의 이미지는 여러개의 레이어로 되어 있다. 아래와 같은 자바 애플리케이셔을 패키징한 도커 컨테이너 이미지가 있다고 하자

FROM openjdk:8-jre-alpine

ARG ./target/hellospring-0.0.1-SNAPSHOT.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]


이 이미지가 어떤 레이어로 구성되어 있는지를 보려면 %docker history {컨테이너 이미지명} 을 실행하면 이미지의 레이어를 볼 수 있는데,   각각의 명령항에 따라서 레이어가 생성된것을 볼 수 있다. 



그리고 각각의 크기를 확인할 수 있다. 위의 history를 보면, 




  • 맨 아래 5.53M가 알파인 리눅스의 베이스 이미지

  • 그리고 위에서 4번째 79.4M가 JRE 설치

  • 위에서 2번째가 33.8M로, 애플리케이션 파일 Jar을 복사한 내용

으로 확인할 수 있다. 계층은 여러개이지만, 주로 용량을 많이 사용하는 이미지는 이 3개의 계층임을 확인할 수 있다.


이 컨테이너에서 애플리케이션을 다시 컴파일 하고 패키징해서 push를 해보면 아래와 같이 33.82M에 해당하는 애플리케이션 Jar 만을 복사하고 나머지는 “Layer already exist”와 함께 별도로 레이어를 푸쉬하지 않는 것을 확인할 수 있다. 


도커 컨테이너는 효과적인 배포를 위해서 pull/push 시에, 레이어별로 캐슁을 해서, 변경이 없는 레이어에 대해서는 다시 pull/push를 하지 않는다

자바 애플리케이션을 여러개의 레이어로 나눈 경우

이 레이어별 캐슁 기능을 잘 활용하면, 애플리케이션 컨테이너의 push/pull 시간을 많이 단축시킬 수 있다. 첫번째 push/pull은 전체 이미지를 올려야하기 때문에 시간 차이는 없겠지만, 그 후 애플리케이션만을 업데이트할 경우에는 애플리케이션에 관련된 파일만 실제로 복사되도록하고, 나머지 레이어는 캐슁된 이미지를 사용하도록 하는 방법이다.


Springboot와 같은 자바 애플리케이션은 애플리케이션 파일은 /classes에 저장되지만, 나머지 참조하는 여러 jar 파일들이 있다. 이러한 jar 파일들은 변경이 없기 때문에, 다시 재배포할 필요가 없기 때문에 캐슁을 하도록 하는 것이 좋다.


그러나 앞의 예제에서는 이 모든 라이브러리 jar 파일들을 하나의 app.jar 에 묶어서 복사하는 구조이기 때문에, 라이브러리 jar 파일만을 별도로 캐슁할 수 없다. 

그래서, 아래와 같이 애플리케이션 jar 파일을 푼 다음에, 각각의 디렉토리를 별도로 복사하는 방법을 사용한다. mvn package 에 의해서 생성된 jar 파일을 target/depdency 라는 디렉토리에 풀고, 각 디렉토리를 Dockerfile에서 각각 복사하도록 설정하였다. 


FROM openjdk:8-jre-alpine

ARG DEPENDENCY=target/dependency

COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib

COPY ${DEPENDENCY}/META-INF /app/META-INF

COPY ${DEPENDENCY}/BOOT-INF/classes /app

COPY ${DEPENDENCY}/org /org


ENTRYPOINT ["java","-cp","app:app/classes/*","com.terry.HellospringApplication"]


이렇게 하게 되면, 사용자가 작성한 코드는 /classes 에 컴파일 된 상태로 저장되고, 나머지 라이브러리들은 /BOOT-INF/lib  등의 디렉토리에 저장된다. 이를 각각 Dockerfile에서 COPY 명령으로 복사하게 되면 별도의 레이어로 생성된다. 

아래는 위의 Dockerfile을 이용해서 빌드한 이미지의 레이어이다. 



앞의 예제의 경우 애플리케이션 jar 파일이 33.82M의 단일 레이어로 생성되었지만, 이 컨테이너는 176k,1.34k,1.84k,16.7M  4개의 레이어로 생성된것을 확인할 수 있다. 




이중에서 1.84k,16.7M 레이어는 Springboot 라이브러리이기 (jar파일) 때문에 변경이 없다. 그래서 캐슁이 가능한데, 


아래는 이 컨테이너를 한번 등록해놓고, 그 다음 애플리케이션 코드를 변경해서 새로 빌드하고 푸슁하는 과정이다. 


보는 것과 같이 위의 2 레이어만 새롭게 푸슁되는 것을 확인할 수 있다. 

이렇게 하면, 아래 OS 레이어, JRE, 그리고 Spring boot의 jar 라이브러리들은 모두 캐슁되고, 배포시에는 실제로 애플리케이션 class 파일만 전송되게 되기 때문에 배포 시간을 많이 단축할 수 있다.


이때 주의 할점은 기존 디렉토리에

BOOT-INF/classes

BOOT-INF/lib

META/lib

org


이런 디렉토리들이 있는데, mvn으로 새롭게 jar를 빌드한후 target/dependecy 파일에 전체 파일을 풀어버리게 되면 4개의 디렉토리가 모두 업데이트가 된다. BOOT-INF/lib,META/lib,org 디렉토리의 실제 파일 내용이 변경이 되지 않았다 하더라도, 새로운 파일로 업데이트하였기 때문에, 파일의 해쉬값이 변경되게 되고, Docker 는 빌드시에, 이 디렉토리가 변경이 되었다고 판단하고 기존의 캐슁된 레이어를 재활용하지 않고 새롭게 레이어를 생성해서 push 하기 때문에, 실제 캐쉬 효과를 볼 수 없다. 

그래서 빌드 과정에서 기존에 변경되지 않은 파일과 디렉토리는 건들지 않고, 변경된 파일만 업데이트 하도록 빌드를 구성해야 한다. 


여기서는 자바 애플리케이션을 기준으로 설명하였지만, Javascript나, 이미지와 같은 정적 파일이 있거나 라이브러리나 드라이브 설치등의 중복되는 부분이 많은 애플리케이션의 경우에는 같은 원리로 각각 레이어를 나눠 놓으면, 캐슁을 통한 push/pull 속도 절약의 효과를 볼 수 있다.


예제 파일은 스프링부트 홈페이지를 참고하였습니다. :)

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

댓글을 달아 주세요

효율적인 도커 이미지 만들기

#1 작은 도커 이미지 만들기

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


일반적으로 도커를 이용해서 자바 애플리케이션 컨테이너를 빌드하면 보통 사이즈가 500~700M 로 배우 큰 이미지가 생성된다. 이는 Ubuntu와 같은 일반 OS 이미지 위에, JDK/JRE를 설치하기 때문인데, 실제로 자바 애플리케이션만을 실행할때는 기타 툴들 (ftp,telnet, 기타 시스템 데몬)이 필요하지 않다.


도커 이미지 사이즈는 빌드와 배포 시간에 많은 영향을 주고, 쿠버네티스와 같은 컨테이너 스케쥴링 솔루션의 경우 도커 이미지가 디스크 공간을 잡아 먹기 때문에, 작은 컨테이너가 여러모로 이점이 많다. 

작은 도커 이미지 만들기

alpine linux는 경량화를 추구하면서 보안을 강화한 이미지로 꼭 필요한 라이브러리와 시스템 데몬이 포함되어 있기 때문에 일반적인 OS이미지에 비해서 그 사이즈가 매우작다.



<그림. 이미지 사이즈별 비교> 

Java alpine 이미지

alpine linux를 기본으로 해서, 애플리케이션 구동을 위한 이미지들이 이미 많이 있는데, java의 경우 openjdk 기반의 alpine 이미지가 있다. 이미지 명은 openjdk:<자바 버전> 식으로 되고, 만약 java-8인 경우 openjdk:8 식으로 정의된다. alpine 리눅스용 openjdk 이미지는 openjdk:<자바버전>-alpine 이 된다. 


아래는 openjdk:8과, openjdk:8-alpine 이미지로 만든 컨테이너의 사이즈 비교이다. 간단한 Hello World Spring boot jar 파일 (17M정도)를 포함하였다. 



사이즈는 openjdk:8 이미지가 552M, openjdk:8-alpine은 139M가 나오는 것을 확인할 수 있다.


JDK보다는 JRE

흔히들 하는 실수중의 하나가 자바 런타임용 컨테이너를 만들때, 컴파일러가 포함된 JDK 환경을 사용한다는 것이다. 보통 자바 런타임은 JDK 없이 JRE 만 있어도 충분하다. 아래는 JRE 8 Alpine 리눅스 이미지를 이용해서 만든 컨테이너 이다.  베이스 이미지 명은 openjdk:<자바 버전>-jre-alpine 을 사용한다. 




OpenJDK 8-alpine은 139M, OpenJDK 8 JRE alpine은 119M가 되는 것을 확인할 수 있다. (20M정도 더 적다)

그러면 항상 OpenJDK에 대한 alpine JRE 이미지를 사용할 수 있는가? 답은 아니다. https://hub.docker.com/_/openjdk?tab=tags 에 들어가면 공식적으로 배포되어 있는 이미지를 확인할 수 있는다. JDK 버전에 따라서 alpine 리눅스를 안정적으로 지원하지 않을 경우에는 해당 이미지가 제공되지 않고, 다른 Linux를 사용하기 때문에, 이미지 사이즈가 200M가 넘는다. 이럴 경우에는 대안으로 Docker slim 이미지를 사용하면 된다. 


그러면 이러한 이미지들은 믿을만 한가? 도커 컨테이너 리파지토리인 hub.docker.com에서 openjdk를 찾아보면 다음과 같이 “Docker Official Images” 라는 마크가 나온다. 


이 이미지는 도커에서 관리하는 이미지로, 도커환경에 최적화 되어 있고, 또한 보안 위협에 대한 내용을 스캔해서 안전하게 제거되는 이미지이다. 

“Each of the images in the Official Images is scanned for vulnerabilities. The results of these security scans provide valuable information about which images contain security vulnerabilities, and allow you to choose images that align with your security standards.”

출처 : https://docs.docker.com/docker-hub/official_images/


결론적으로 이야기 하자면, 도커 허브에서 제공되는 Official 이미지를 사용하되, alpine 리눅스 기반으로 되어 있는 이미지를 사용하는 것이 가장 좋다.

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

댓글을 달아 주세요

VM 수준의 보안을 제공하는 gVisor

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


Docker와 같은 일반적인 컨테이너 모델의 문제점은 Host Kernel을 공유하기 때문에, privilege escalation을 통해서 Host Kernel 을 접근해서 전체 시스템을 장악할 수 있다는 문제점이 있다. privilege escalation을 SeLinux나 컨테이너 정책으로 막는다고 하더라도, 버그등으로 인해서 Host Kernel 로의 접근을 허용할 수 있는 잠재적인 보안 위협이 있다.

컨테이너에 비해서 VM은 하드웨어 계층 부터 가상화를 해서, 전체를 Isolation 시키기 때문에, 보안적으로는 상대적으로 우세하지만, 전체적으로 무겁다는 단점을 가지고 있다.

이런 문제를 해결하기 위해서 VM과 컨테이너의 장점을 조합해서 만든 것이 gVisor 라는 오픈소스이다.


기본적으로 Host Kernel 위에서 KVM을 통해서 Kernel 을 Emulation한다. VM처럼 하드웨어 전체를 Emulate 하는 것이 아니라, Emulated 된 커널을 올릴 수 있도록 하고, 이 커널과 컨테이너는 User space에서 실행된다.

일반적인 컨테이너의 경우 컨테이너는 User space에서 실행되지만, system call을 통해서 Kernel space로 접근이 가능한데, gVisor의 경우에는 user space에 emulated 된 커널을 올려서, system call이 발생하더라도 user space를 벗어나지 않도록 해준다. 


아래 그림은 gVisor와 K8S의 관계 개념도인데, 쿠버네티스가 컨테이너를 생성 관리 하려면 컨테이너런 타임이 있어야 하는데,  gVisor의 runtime은 runsc이다. runsc는 OCI 규격을 준수하고 있기 때문에 쿠버네티스에서 기존의 컨테이너등과 호환이 가능하게 된다.   gVisor의 컨테이너는 컨테이너와 Sentry라는 가상화된 커널로 구성되며 이는 user space에서 기동된다. 


<출처 : https://www.youtube.com/embed/TQfc8OlB2sg


이러한 구조를 통해서 VM 수준의 Isolation을 제공함으로써 높은 보안을 제공한다. 

현재 Google Kubernetes Engine (이하 GKE)에서 옵션으로 활성화가 가능하며, Pod spec에 아래 그림과 같이 runtimeClassName 을 gvisor로 정해주면 된다.


apiVersion: apps/v1

kind: Deployment

metadata:

  name: httpd

  labels:

    app: httpd

spec:

  replicas: 1

  selector:

    matchLabels:

      app: httpd

  template:

    metadata:

      labels:

        app: httpd

    spec:

      runtimeClassName: gvisor

      containers:

      - name: httpd

        image: httpd


이렇게 하면 이 Pod는 gvisor를 통해서 isolation이 된다. 일반적인 애플리케이션인 java,node.js,ruby 나 redis나 mysql과 같은 범용 애플리케이션은 잘 동작하지만


gVisor는 높은 수준의 보안을 요구 하는 애플리케이션이나 특히 대외로 서비스 되는 Multi tenant application의 경우 높은 isolation 레벨을 제공해서, 다른 Pod 들이 영향을 받지 않도록 하는데 유용하게 사용할 수 있다. 


이러한 장점은 있지만 역시 만능은 아니고 제약 사항이 분명히 있다.

  • HostPath 볼륨 사용불가

  • Istio 사용불가

  • 일부 PSP 사용불가

  • Pod나 컨테이너 레벨 모니터링 불가

  • Kernel 의 특정 보안 모듈 secomp,Apparmor,Selinux 등 사용 불가

  • GPU 등 사용불가

자세한 제약 사항은 https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods#limitations 문서를 참고하기 바란다. 



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

댓글을 달아 주세요

  1. 조윤희 2019.09.24 08:22  댓글주소  수정/삭제  댓글쓰기

    GPU, TPU 지원되는 VM 이나, privilege 설정없이 TPU를 사용할수있는 docker는 없나요?

CRI & OCI


기본적으로 도커 기반의 쿠버네티스는 다음과 같은 구조로 작동을 했었다. Kubelet이 명령을 받으면, Docker runtime을 통해서 컨테이너를 생성하거나 삭제하는 것과 같은 생명 주기를 관리하는 구조를 가지고 있었다. 



그런데, Docker 이외에도 여러가지 컨테이너 기술이 나오면서 쿠버네티스에서 이런 다양한 컨테이너 런타임에 대한 지원 요건이 생기기 시작하였고, 다양한 컨테이너 런타임을 지원하기 위해서 그때 마다 Kubelet의 코드를 수정해야 하는 문제가 생겼다.


그래서, kubelet의 코드를 수정하지 않고, 다양한 컨테이너 런타임을 지원하기 위해서, kubelet과 컨테이너 런타임 사이의 인터페이스를 통일화하는 스펙이 등장하였는데, 이것이 CRI (Container Runtime Interface)이다. 컨테이너의 생성,삭제 등의 생명 주기를 관리하는 스펙으로 gRPC 기반의 API 스펙으로 되어 있고, 새로운 컨테이너 런타임은 CRI 스펙에 맞춰서 CRI 컴포넌트를 구현하면 되는 구조가 되었다. 


그래서, 컨테이너 런타임이 CRI 스펙에 맞춰서 구현이 되면, kubelet의 코드 변화 없이 새로운 컨테이너 런타임을 플러그인 구조로 추가할 수 있는 구조가 된것이다.  Docker의 경우에는 docker shim 이라는 CRI 인터페이스를 준수하는 구현체를 제공하고 있고, rkt 컨테이너의 경우는 rktlet 이라는 이름의 CRI 구현체를 제공하고 있다. 

그런데, 지원되는 컨테이너의 종류가 계속해서 늘어가고 있고, 그때마다, CRI를 다시 구현해야 하는 문제가 생김에 따라 컨테이너 런타임 자체를 표준화하고자 하는 노력이 있었는데, 그로 인해서 정해진 스펙이 OCI (Open Container Initiative) 이다. OCI 스펙을 맞춰서 구현된 컨테이너 런타임을 별도의 CRI 구현이 없이 OCI를 지원하는 CRI 구현체에 의해서 관리가 가능해진다. OCI 스펙에 따른 컨테이너 런타임을 관리하는 CRI 컴포넌트는 CRI-O 라는 컴포넌트로 구현되어 있다. 즉 OCI 스펙을 준수한다면, CRI-O를 통해서 kubelet으로 부터 명령을 받을 수 있는 구조가 된다. 



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

댓글을 달아 주세요

쿠버네티스 Skaffold에서는 하나의 Configuration에서 Profile을 통하여, 배포 파이프라인을 여러개 만들어서 다른 환경에 배포할 수 있고,

각 환경은 kubernetesContext를 이용해서, 쿠버네티스 클러스터를 고를 수 있다. 


build:
  artifacts:
  - image: gcr.io/k8s-skaffold/skaffold-example
deploy:
  kubectl:
    manifests:
    - k8s-pod
profiles:
- name: profile1
  activation:
    - env: MAGIC_VAR=42
- name: profile2
  activation:
    - env: MAGIC_VAR=1337
    - kubeContext: minikube
      command: dev


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

댓글을 달아 주세요



쿠버네티스 애플리케이션을 위한 개발환경 설정하기

#2 VS Code

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

마이크로소프트 VS Code

다음 소개할 도구는 마이크로소프트사의 VS Code 이다. 자바 언어가 아닌 Python,node.js 등의 개발에는 VS Code 가 많이 사용되는 데, 특히 쿠버네티스 관련 플러그인들이 많아서 쿠버네티스 연동에 있어서는 IntelliJ보다 훨씬 좋은 기능을 제공한다.


설치하기

VS Code에, 쿠버네티스 개발환경을 설치해보자, 설치전에는 kubectl, docker, skaffold 가 미리 설치되어 있어야 한다. 

다음 웹 브라우져에서 이 링크(vscode:extension/GoogleCloudTools.cloudcode)를 실행하면, VS 코드에서 아래와 같이 Cloud Code 설치 화면이 나온다.



여기서 우측 화면의 Cloud Code  부분에서 인스톨 버튼을 누르면, 플러그인이 설치 된다.

설치가 완료되면 VS Code 하단에 다음과 같이 Cloud Code 아이콘이 생성된다. 

테스트 애플리케이션 만들기

플러그인 설치가 끝났으면, 테스트 애플리케이션을 하나 만들어 보자

VS Code 하단의 Cloud Code 버튼을 누르면, 아래 그림과 같이 VS Code 상단에 Text Box로, 메뉴를 선택할 수 있는 창이 나온다. 여기서 New Application을 선택한다




 그중에서  아래 그림과 같이 node.js:Hello world를 선택한다.




생성된 프로젝트의 구조는 아래 그림과 같다.



  • /kubenetes-manifests 디렉토리에는 쿠버네티스 자원에 대한 정의가 yaml 파일로 되어 있다. 이 예제에서는 service와 deployment  두 자원이 정의 된다.

  • /Dockerfile 에는 도커 이미지를 빌딩하기 위한 설정이 되어 있가

  • /skaffold.yaml 은 컨테이너 이미지를 빌드 배포하기 위한 설정이 들어 있다. 


애플리케이션 배포하기

VS Code 아래 Cloud Code 메뉴에서 Deploy application을 선택하면, 이 node.js 애플리케이션을 컨테이너로 패키징해서 Skaffold 를 통해서 쿠버네티스 클러스터에 배포한다. 


이때 몇가지를 물어보는데, 

  • build : “default”를 선택한다. 이 옵션은 컨테이너를 어디서 빌드 할것인지에 대한 옵션인데, “default”는 로컬 환경에서 docker를 이용해서 컨테이너를 만드는 옵션이고, 필요하다면, 구글 클라우드의 Cloud Build를 사용할 수 도 있다. Cloud Build는 로컬에서 빌드를 하는 것이 아니라, 빌드에 필요한 모든 파일들을 Cloud Build 클라이언트가 tar로 묶어서, 구글 클라우드의 Cloud Build로 보내서 빌드하는 방식인데, 만약에 빌드 태스크가 많은 빌드 서버라면 (Jenkins와 같은), 하나의 서버에서 여러 빌드를 할 수 없기 때문에 분산 빌드가 필요한데, Cloud Build를 사용하면, 클라우드에서 빌드를 건건이 해주기 때문에 빌드 머신의 리소스를 사용하지 않고, 여러 빌드를 동시에 진행할 수 있는 장점이 있다. 

  • 다음은 쿠버네티스 클러스터를 고르게 되는데, 이미 kubectl 이 깔려 있다면, 현재 연결되어 있는 클러스터들 중 하나를 선택할 수 있다. 


다음 그림은 설정이 끝난 후에, node.js 애플리케이션을 도커로 패키징해서 skaffold를 통해 쿠버네티스 클러스터로 배포하는 과정이다. 




빌드 및 배포가 완료되면, 아래 그림과 같이 쿠버네티스 service 의 end point URL이 출력된다.


해당 URL을 웹 브라우져에서 열어보면 아래 그림과 같이 애플리케이션을 동작하는 결과를 확인할 수 있다.


로그 및 쿠버네티스 자원 보기

VS Code에는 Kubernetes Explorer 라는 기능이 같이 설치 되는데, 이 기능은 현재 개발환경에 연결되어 있는 쿠버네티스 클러스터와 그 자원 (namespace, deployment, pods 등)을 보여준다. 

아래 그림은 개발환경에서 쿠버네티스 클러스터의 자원들을 보는 화면이다. 



앞에서 배포한 애플리케이션의 로그를 보고 싶으면 이 화면에서, 배포된 Pod의 오른쪽 버튼으로 클릭하면 “Stream Log”라는 메뉴가 나온데, 이 메뉴를 클릭하면 로그를 볼 수 있다 

위의 예제에서는 node-hello-world-649.. Pod를 선택하였다. 

아래는 실제로 출력되는 로그 결과이다. 


컨테이너로 SSH 접속하기

VS Code는 IntelliJ에 비해서 쿠버네티스에 관련된 여러가지 부가 기능을 제공하는데, 유용한 기능중 하나가 Pod의 컨테이너로 직접 SSH로 접속할 수 있는 기능이 있다.


위의 그림과 같이 Kubernetes Cluster Explorer 에서  Pod 아래 SSH로 로그인하고자 하는 컨테이너를 선택한 후에, 오른쪽 버튼을 누르고, Get Terminal 이라는 메뉴를 실행하면 아래 그림과 같이 VS Code 안에서 바로 그 컨테이너로 SSH 터미널이 열린다. 


쿠버네티스 자원 설정 보기

앞에서 쿠버네티스 클러스터들의 자원 목록을 볼 수 있었다. 다음으로 유용한 기능중의 하나는 각 자원에 대한 설정 정보를 볼 수 있는 기능이 있는데, 쿠버네티스 리소스를 선택한 후 Edit in YAML을 선택하면 해당 쿠버네티스 리소스의 YAML 파일을 볼 수 있다. 


디버깅

마지막은 애플리케이션 디버깅 기능인데, IntelliJ와 마찬가지로 쿠버네티스 클러스터로 배포된 애플리케이션에 대해서 온라인 디버깅이 가능하다. 

우측에 디버그 아이콘

을 클릭하면 디버그 뷰로 바뀌는데, 

코드에 브레이크 포인트 찝어 놓고, 디버그 모드에서 디버깅 시작 버튼


을 누르면 디버깅 모드로 변경되는데, 이렇게 해놓고, 웹 사이트를 실행하면, 아래 그림과 같이 브레이크 포인트에서 멈추고, 디버깅을 할 수 있다.




디버그 종료는 상단 디버그 툴 창


우측 붉은 버튼을 누르면 종료된다.


지금까지 간단하게, IntelliJ와 VSCode IDE에서 쿠버네티스를 위한 개발 환경 설정 방법에 대해서 알아보았다. 쿠버네티스 자체에 대한 설정과 운영/모니터링도 중요하지만, 쿠버네티스에 잘 적응하기 위해서는 개발자들이 손쉽게 개발환경에 접근할 수 있는 환경이 되어야 하기 때문에, 조직의 개발팀에 맞는 개발 환경과 프로세스를 잘 정리하기를 권장한다.

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

댓글을 달아 주세요

쿠버네티스 애플리케이션을 위한 개발환경

#1 IntelliJ
조대협 (http://bcho.tistory.com)
일반적인 경우에는 로컬 환경에서 개발하고, 로컬에서 톰캣등을 띄워서 테스트하면 되지만, 만약 부가적으로 레디스나, DB등 복잡한 개발/테스트 환경이 필요한 경우에는 결국에는 컴파일된 애플리케이션을 서버로 올려서 테스트해야 한다. 쿠버네티스를 이용해서 개발된 애플리케이션을 컨테이너로 패키징하는 개발 환경을 꾸미게 되면 개발을 조금 더 효율적으로 할 수 있다.


그렇지만 반대로, 컨테이너로 패키징해서 매번 쿠버네티스 개발용 클러스터에 배포해야 하기 때문에 복잡성이 증대하는 단점이 있다.  아래 그림은 쿠버네티스 기반의 애플리케이션을 개발할는 일반적인 개발 과정이다.


코드 작성이 끝나면, 컴파일을 해야 하고, 컴파일된 애플리케이션을 컨테이너로 패키징 해야 한다. 이때, 컨테이너의 태그 이름을 매번 바꿔줘야 하는데, 태그 이름을 변경하면 마찬가지로 YAML 파일내의 설정도 새로운 컨테이너로명으로 변경해줘야 한다. 


그리고, 변경된 내용을 쿠버네티스에 반영하면 테스트를 할 수 있는데, Cluster IP 서비스나 일반 Pod의 경우에는 외부 IP를 가지지 않기 때문에 바로 호출을 할 수 없다. 그래서 kubectl 을 이용해서 노트북의 로컬 주소를 해당 서비스나 Pod를 호출할 수 있도록 프록시 설정을 해줘야 한다.


마찬가지로 로깅 역시, 쿠버네티스 컨테이너로 돌고 있는 애플리케이션의 로그를 바로 볼 수 없기 때문에, kubectl logs 명령을 이용해서, 별도로 로그를 모니터링해야 하고, 런타임 디버깅은 지원조차 되지 않는다. 


개발된 코드가 테스트가 끝난 후에는 테스트를 위해서 기동했던 Pod 들을 삭제하는 것이 좋은데, 일반적으로, 하나의 애플레케이션은 여러가지의 리소스 (PSP, Deployment, Service, Configmap …)로 이루어지기 때문에, 일일이 삭제하려면 손이 많이 간다. 


이런 문제를 단순화 해주는 도구로 skaffold 가 로컬 환경을 지원하기 때문에, 많은 작업을 자동화할 수 있지만, 여전히 환경이 불편하다. 개발자들이 원하는 것은 쉘이나 기타 툴을 왔다갔다 하지 않고, 개발툴 (IDE)안에서 모든것을 해결하고 싶어한다.

이런 문제를 해결하기 위해서 구글에서 발표한것이 구글 클라우드 코드 (Google Cloud Code) 이다.

구글 클라우드 코드

구글 클라우드 코드는 개발도구에 확장 플러그인으로 제공되는 도구로, 위의 개발에 필요한 과정을 쿠버네티스와 연동하여  IDE안에서 모두 통합해서 지원할 수 있도록 한다. 내부 컨테이너 빌드와 배포는 Skaffold를 사용하고 있기 때문에, 운영 환경이 skaffold를 사용하고 있으면 그 설정을 공유해서 사용할 수 있다.

사실상 Skaffold를 IDE에 통합하고 몇가지 부가 기능이라고 보면 이해하기가 쉽고 사용방법도 Skaffold 설정을 그대로 사용한다. 


현재 클라우드 코드는 intelliJ와 Visual Studio Code (이하 VSCode)를 지원한다.

IntelliJ

먼저 IntelliJ에서 클라우드 코드를 사용하기 위해서는 다음 툴들이 로컬에 먼저 설치되어 있어야 한다. Docker client, kubectl, skaffold 그리고, 개발용 쿠버네티스 클러스터가 필요하다. 개발용 클러스터는 일반 쿠버네티스 환경을 지원하며, minikube도 지원한다.

설치

클라우드 코드 플러그인 설치는 File > Setting > Plugins 메뉴로 들어가서 "Cloud Code”를 검색하여 설치하면 된다.



테스트 애플리케이션 만들기

설치가 끝났으면 간단하게, Spring boot 애플리케이션을 만들어 보자

예제는 구글 클라우드에서 제공하는 Spring boot 예제 파일을 사용한다.

git clone https://github.com/GoogleCloudPlatform/cloud-code-samples.git

에서 코드를 가지고 온후에 java-hello-world project를 Maven 프로젝트로 Import 한다. 

디렉토리에는 쿠버네티스와 쿠버네티스 배포에 관련된 몇가지 파일들이 정의되어 있다


kubernetes-manifests

디렉토리에는 이 서비스를 배포하기 위한 deployment와, service YAML 파일이 정의되어 있다. 


Dockerfile

루트 디렉토리에는 애플리케이션을 패키징하기 위한 Dockerfile 이 들어있다.


skaffold.yaml

이 파일은 skaffold로 배포를 하기 위한 skaffold 설정이다. 이 파일에서는 컨테이너 이미지명과, kubernetes yaml 파일 경로등을 지정한다.

애플리케이션 배포하기




애플리케이션을 배포하기 위해서는 먼저 개발용 쿠버네티스 클러스터를 등록해야 한다.

Tools > Cloud Code > Kubernetes > Add Kubernetes Support

를 선택하면, 현재 Kubectl context에 연결된 쿠버네티스 클러스터를 자동으로 등록한다.

쿠버네티스 클러스터가 등록되면 아래 그림과 같이 상단에, 쿠버네티스 관련 메뉴가 나온다. 


여기서 Kubernetes Deploy를 선택한 후에, 오른쪽에 실행 버튼을 누르면 컴파일을 하고 쿠버네티스 클러스터로 배포를 하게 된다. 

아래 화면은 실제로 Kubernetes Continuous Deploy 모드로 배포한 결과이다. 

일반 Kubernetes Deploy 모드는, 버튼을 누를때만 배포가 되고, Continuous Deploy 모드는 코드 변화를 감지하여 코드가 변경되면 자동으로 재 배포를 한다. 


위의 그림과 같이 배포가 완료되면, 하단에 자동으로 로그 창이 떠서 컨테이너에서 실행되는 애플리케이션의 로그를 볼 수 있다. (Pod를 여러개 배포하면 여러개의 Pod 로그가 뒤섞여서 나오지만 앞에 pod 이름이 나오니까는 크게 걱정하지 않아도 된다) 

배포가 완료되면 kubectl get pod를 실행하보면 아래와 같이 java-hello-xxx pod가 배포된것을 확인할 수 있다.


테스트를 위해서, Cloud Code는 local port(PC의) 해당 서비스 포트로 자동 포워딩을 해준다. 배포가 끝난후에, 화면 하단을 보면 Port forwarding server port 라는 메세지와 함께, 로컬 포트를 쿠버네티스 Pod 포트로 어떻게 포워딩을 하는지 보여준다. 

아래 메세지는 localhost:8080 → 쿠버네티스 Pod:8080 으로 포워딩하는 것을 알리는 메시지 이다. 


단 여러 Pod 를 배포하게 되면 포트포워딩이 섞일 수 있으니, 개발시 환경에서는 하나의 Pod 만 배포하거나, Proxy를 kubectl 등으로 별도로 띄워서 서비스로 프록시를 하는것이 좋다. 

로컬에서 브라우져를 열어서 localhost:8080에 접속해보면 아래와 같이 서비스에 접속이 되서 결과가 나온다. 


디버깅

쿠버네티스 애플리케이션을 개발할 때 어려운 점이, 서버에 배포된 시스템을 디버깅 하기가 어렵다는 점인데, (쿠버네티스가 아니더라도 서버에 배포된 시스템을 리모트로 디버깅하는 것은 쉽지 않다)

클라우드 코드의 장점중의 하나가 쿠버네티스 컨테이너에서 돌고 있는 애플리케이션을 디버깅할 수 있는 기능이다.


디버깅을 위해서는 아래 그림과 같이 디버깅을 하고자하는 브레이크 포인트에, 20라인 처럼 붉은색으로 브레이크 포인트 처리를 하고 (붉은 점이 있는 부분을 클릭하면 브레이크 포인트 처리가 된다)



디버깅은 상단 메뉴에서 Kubernetes Continuos Deploy 모드로 맞춘후에, 아래 그림과 같이 오른쪽 녹색 디버깅 버튼을 누르면 된다. 



그후 애플리케이션을 실행하면 아래 그림과 같이 브레이킹 포인트에서 애플리케이션 실행이 멈추고 디버깅에 필요한 쓰레드 정보, 변수명등을 보면서 디버깅이 가능하다. 




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

댓글을 달아 주세요



#26 쿠버네티스 오토 스케일러

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


쿠버네티스에서는 리소스 부족을 처리하기 위해서, 오토 스케일러를 사용할 수 있다.. 쿠버네티스는 용도에 따라 몇가지 다른 오토스케일러를 제공하는데 각각을 살펴보도록 하자

Vertical Pod Auto-scaler (VPA)

Pod의 Resource(CPU,Memory)  적절 request를 결정하는 다른 방법으로는 Vertical Pod Auto-scaler(VPA)를 사용하는 방법이 있다. 현재 beta 기능인데, 쿠버네티스 1.11 버전 이상에 별도로 추가 설치해야 한다. 

참고 : https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler

VPA는 리소스로 설정해서 Deployment에 적용할 수 있다.


apiVersion: autoscaling.k8s.io/v1beta2

kind: VerticalPodAutoscaler

metadata:

  name: api-server

spec:

  targetRef:

    apiVersion: "extensions/v1beta1"

    kind:       Deployment

    name:       api-server

  updatePolicy:

    updateMode: "Auto"


위의 예제는 api-server라는 Deployment에 api-server 라는 이름의 VPA를 적용한 예제인데, VPA는 Auto 모드와 Manual 모드가 있다. 설정은 updatePolicy.updateMode에서 지정하면 된다. 


<그림 VPA의 동작 원리>

 

Auto 모드의 경에는 VPA는 Pod의 리소스 사용량을 일정 주기 이상 모니터링 한 후에, 그 Metric을 기반으로, Pod resource의 request값을 변경 해주는 방식이다. 

 대신 주의해야할 점은 resource의 request를 변경하는 유일한 방법은 Pod를 리스타트 하는 방법이기 때문에 VPA를 Auto 모드로 적용한 경우 원하지 않은 Pod 리스타트가 발생할 수 있다.


만약에 Manual 모드로 설정하고, Pod를 운영하면, VPA가 직접 request 내용을 변경하지 않고, 적절하게 필요한 request 양을 추천 해준다. VPA를 만들어서 Deployment에 적용한 후 수분 정도 운영하다가 kubetctl get vpa 명령으로 내용을 보면 추천되는 request 양을 알려준다. 

% kubectl get vpa [VPA 이름]  --output yaml

다음은 위의 명령을 실행한 결과 예제이다. 

....

  recommendation:

    containerRecommendations:

    - containerName: my-rec-container

      lowerBound:

        cpu: 25m

        memory: 262144k

      target:

        cpu: 25m

        memory: 262144k

     upperBound:

        cpu: 7931m

        memory: 8291500k


위의 결과에서 출력된 target 값이 추천 resource 양이다. CPU 25m, 메모리 262144k 가 추천되는 값이다.

Horizontal Pod Autoscaler (HPA)

VPA가 Pod의 리소스를 더 할당해서 스케일링을 하는 기능이라면, Horizontal Pod Autoscaler (이하 HPA)는 Pod의 수를 늘려서 스케일링을 하는 기능이다.


HPA의 동작 원리는 다음과 같다. Pod 에서 메트릭을 저장해서 메트릭 서버에 저장한다. CPU나 메모리와 같은 리소스 지표들이 해당한다. 지표들이 지정해놓은 임계점을 지나면 Pod의 수를 늘려서 스케일링을 한다.



<그림. Horizontal Pod Autoscaler의 개념>


HPA는 메트릭을 모니터링하고, 실제로 Pod의 수를 늘리거나 줄이는것은 ReplicaSet이나 Deployment에 의해서 컨트롤 된다. 

적용 방법을 살펴보자, 아래와 같이 YAML 파일을 이용해서 HPA 리소스를 생성할 수 있다. 


apiVersion: autoscaling/v2beta2

kind: HorizontalPodAutoscaler

metadata:

  name: api-server

  namespace: default

spec:

  scaleTargetRef:

    apiVersion: apps/v1

    kind: Deployment

    name: api-server

  minReplicas: 1

  maxReplicas: 10

  metrics:

  - type: Resource

    resource:

      name: cpu

      target:

        type: Utilization

        averageUtilization: 50


HPA 설정에는 HPA 이름, 그리고 적용하고자 하는 Deployment의 이름, min,max Pod수 그리고, 측정 메트릭이 정의된다. 위의 예제는 api-server라는 HPA 이름으로 api-server 라는 deployment에 HPA를 적용하였고, CPU 사용률이 50% 이상이 되면 최대 10개의 Pod까지 스케일 업을 하도록 한 설정이다.


HPA는 아직 VPA와 같이 사용할 수 없다. 개발/테스트 환경에서 VPA를 이용해서 적절 resource 양을 측정한 다음, 운영환경에서는 HPA 정도만 적용하는 것을 권장한다.  

Cloud Auto-scaler

앞서 살펴본 VPA와 HPA가 Pod의 리소스 양을 늘려주거나 Pod의 수를 조정해서 부하를 감당하는 Pod 기반의 오토 스케일러라면 Cloud Autoscaler (이하 CA), Node의 수를 조정하는 오토 스케일러이다. AWS, Azure, Google Cloud 등 클라우드 인프라와 연동해서 동작하도록 되어 있고, 각 클라우드 서비스 마다 설정 방법이나 동작 방식에 다소 차이가 있으니, 자세한 내용은 클라우드 벤더별 문서를 참고하기 바란다.


기본적인 동작 방법은 같은데, Pod를 생성할때, Node들의 리소스가 부족해서 Pod를 생성할 수 없으면, Pod 들은 생성되지 못하고 Pending status로 대기 상황이 된다. Pending status 된 Pod를 CA가 감지 하면, Node를 늘리도록 한다. CA는 Pending status가 있는지를 감지 하기 위해서 (디폴트로) 10초 단위로 Pending status가 있는 Pod가 있는지를 체크한다.


Node Autoscaling 과정에서 주의해야 하는 점은 scale up 보다는  Node 수를 줄이는 scale down 이다. 

CA는 10 초 단위로 scale up 이 필요한지를 체크한 후에, scale up이 필요하지 않으면, scale down 이 가능한지를 체크한다. 

Node의 리소스(CPU,Memory)의 request 총합이 node 물리 리소스의 일정량 (디폴트는 50%)보다 작으면 scale down 을 고려 한다. 이때 리소스의 현재 사용량을 기반으로 하지 않음에 주의하기 바란다. Request를 넘어서 limit  아래까지 리소스가 사용되더라도 CA는 Node에 배포된 Pod들의 request 총합을 기준으로 한다. 

Scale down은 scale down 대상 node의 Pod를 다른 node로 옮기고, scale down node를 삭제하는 방식으로 작동하는데,Scale down 조건이 되면, Node는 그 Node에서 돌고 있는 Pod들을 옮길 수 있는지를 먼저 확인한다. 


  • 컨트롤러 (ReplicationSet,Job 등)에 의해서 관리되지 않는 naked pod는 옮길 수 없다.

  • 로컬 디스크를 사용하는 Pod는 옮길 수 없다. 

  • Affinity,Taint 등의 조건에 의해서 옮길 수 없는 경우 

  • 만약 Pod를 CA에 의해서 이동하고 싶지 않은 경우, Pod를 “cluster-autoscaler.kubernetes.io/safe-to-evict:True” 옵션을 부여하여, CA에 의해서 Pod가 옮겨지는 것을 막을 수 있다.  

  • 그리고 뒤에서 소개하는 Pod Disruption Budget (PDB)에 따라서 Pod를 삭제를 조정한다.


조건에 부합하여 모든 Pod를 옮길 수 있다면, scale down node로 부터 해당 Pod를 삭제하고, 그 Pod 들을 다른 Node에 생성하고, scale down 대상 node를 삭제한다. 


경우에 따라 특정 node를 CA에 의해서 scale down을 하고 싶지 않은 경우가 있을 수 있는데, 그 경우에는 node 에 다음과 같은 레이블을 적용하면 해당 Node는 Scale down에서 제외된다. 

"cluster-autoscaler.kubernetes.io/scale-down-disabled": "true"



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

댓글을 달아 주세요