클라우드 컴퓨팅 & NoSQL/도커 & 쿠버네티스

쿠버네티스 리소스 배포와 관리를 위한 ksonnet

Terry Cho 2019. 1. 14. 21:13

Ksonnet

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


쿠버네티스의 리소스 배포는 YAML 스크립트를 기반으로 한다.

하나의 마이크로 서비스를 배포하기 위해서는 최소한 Service, Deployment 두개 이상의 배포 스크립트를 작성해야 하고, 만약에 디스크를 사용한다면 Persistent Volume (aka PV)와 Persistent Volume Claim (PVC)등 추가로 여러 파일을 작성해서 배포해야 한다.

그런데 이러한 배포 작업을 보면, 사실 비슷한 성격의 마이크로 서비스간에는 중복 되는 부분이 많다.


예를 들어 간단한 웹서비스 (node.js나 springboot)를 배포할때는 Service의 타입을 지정하고, Deployment에 의해서 관리되는 Pod의 수, 그리고 컨테이너 이미지 정도만 지정하면 기본적인 배포는 가능하다. 여기에 필요하다면, 요구되는 리소스 (CPU, 메모리) 정도만 지정하면 된다.

그러나 이를 위해서는 Service와 Deployment를 정의한 긴 YAML 파일을 작성해야 한다.

그렇다면 이러한 반복작업을 단순화하고, 환경(운영/개발, 또는 미국/유럽과 같은 다른 리전등)에도 반복적으로 재활용할 수 있는 방법은 없을까?


이런 목적에서 여러가지 솔루션이 소개되는데, 그중에 널리 사용되고 있는 것은 패키지 매니저인 Helm이 있다. Helm은 node.js 의 npm이나, 리눅스의 apt 유틸리티와 유사하게, 패키지 형태로 쿠버네티스 리소스를 배포할 수 있도록 해준다. 이외에도 여러가지 솔루션이 있는데, 오늘 이야기하는 솔루션은 ksonnet이라는 오픈소스로, 템플릿엔진 형태의 특징을 가지고 있는 솔루션이다.


Ksonnet

ksonnet은 JSON 기반의 템플릿 엔진인 jsonnet (https://jsonnet.org/)을 기반으로 한다. 기본적으로 템플릿 엔진의 특성을 가지고 있는데, 특이점이 인프라를 지원하는데 있어서 일종의 프로그래밍 언어적인 컨셉을 지원한다.

그러면 기본적인 개념을 이해 해보도록 하자


Prototype, Parameter & Component



먼저 Prototype 이라는 개념을 이해 해야하는데, prototype의 템플릿의 개념으로, 객체 지향형 프로그램에서 클래스의 개념으로 생각하면 된다. 이 prototype을 이용해서 component를 만들어 낸다. component는 객체 지향형에서 객체의 개념으로 생각하면 된다. 이렇게 만들어진 컴포넌트에 속성(변수)를 설정하면 되는데, Deployment 이름이나, 컨테이너 이미지등 실배포에서 애플리케이션 서비스별로 필요한 구체적인 값을 입력한다. 이 값을 입력하는 방법은 parameter 라는 리소스로 정의해서, 필요한 값을 전달한다.

이렇게 값이 채워진 component는 각 서비스 배포에 필요한 쿠버네티스 리소스들을 정의한 쿠버네티스 manifest 파일로 생성된다. (참고 : YAML이 아니라 JSON으로 생성되는데, 쿠버네티스의 manifest는 YAML뿐만 아니라 JSON으로도 설정이 가능하다. )


prototype에서 component를 생성하는 방식은 앞에서도 설명하였듯이, 하나의 prototype에서 parameter만 바꿔서 생성하면 다른 서비스에 대한 component를 반복적으로 생성할 수 있다. 아래 개념 그림은 하나의 prototype에서 도커 이미지와 서비스 타입만 변경해서 Guestbook과 BookStore 서비스에 대해서 각각의 컴포넌트를 생성하는 개념이다.



Environment

이렇게 생성된 component는 쿠버네티스에 적용해서, 실제 쿠버네티스 리소스를 만드는데, ksonnet은 동시에 여러개의 쿠버네티스 환경을 지원할 수 있다. 이를 ksonnet 안에서는 environment 라는 리소스로 정의하는데, 여러개의 쿠버네티스 클러스터를 하나의 ksonnet 프로젝트 안에 등록해놓고, 생성된 component를 등록된 쿠버네티스 환경중 하나에 배포한다. ksonnet의 environment는 쿠버네티스 클러스터에 대한 정보 (예를 들어 마스터 서버의 IP)를 가지고 있다.



Part

그런데 Prototype도 유사점이 있다. 예를 들어 redis 배포를 예를 들었을때, 어떤 redis 배포는 외장 디스크 (PV)를 붙여서 배포할 수 도 있고, 아니면 내장 디스크 (PV 없이) 배포할 수 있다. 비슷한 타입의 prototype의 반복적인 작업을 막기 위해서 prototype은 재사용이 가능한 자원으로 조합해서 정의 될 수 있다. 이를 ksonnet에서는 part라고 정의한다.




위의 그림을 보면 우측에 두가지 프로토 타입이 정의된것을 볼 수 있다. 하나는 PVC(Persistent Volume Claim)이 있는 것이고 하나는 없는 것이데, 이외에 차이가 없고 Service나 Deployment는 정의는 동일 하기 때문에 이를 part로 정의한 후에, 이 part를 조합하여 다른 prototype을 구성할 수 있다.

Application

하나의 애플리케이션은 여러개의 마이크로 서비스로 구성이 된다. 그래서 하나의 애플리케이션을 배포하기 위해서는 여러개의 Prototype과 component들이 필요하게 되는데, 하나의 쿠버네티스 클러스터에는 열개의 애플리케이션이 설치될 수 있기 때문에, 이 애플케이션 단위로, 리소스를 묶을 수 있는 바운더리 개념이 필요하다. ksonnet에서는 이 개념을 application이라는 단위로 정의해서, 이 애플리케이션 안에, 여러개의 prototype, part, component, environment를 묶을 수 있게 한다.

그래서 이 애플리케이션 단위로 설치도 가능하고 반대로 애플리케이션 단위로 설치된 애플리케이션을 삭제할 수 있다.

Package

Part와 Prototype은 다른 애플리케이션에서도 재 사용될 수 있다. 예를 들어 redis 설치 스크립트나, nginx 설치 스크립트 역시 어느 애플리케이션에서나 재 사용될 수 있고, 머신러닝 패키지인 kueflow와 같은 복잡한 애플리케이션의 경우에는 이 prototype과 part들만 있으면 손쉽게 설치될 수 있다. 이렇게 각 모듈들을 재사용 가능하도록 묶어놓은 것을 ksonnet에서는 package라고 한다. Package 안에는 일반적으로 아래와 같이 재 사용이 가능한 part와 prototype들이 들어가 있다.


Registry

그러면 이 패키지들 어디에 저장하고 어떻게 내 애플리케이션에 반영할까? ksonnet에서는 npm이나 docker repository처럼 이러한 패키지들을 저장할 수 있는 레파지토리 개념을 제공하는데, 이것이 바로 registry다.

Github repository나, 파일 시스템 Helm repository를 registry로 사용할 수 있다.


작성한 package는 이 registry에 저장할 수 있으며, 저장된 package들은 package install 명령어를 이용해서 설치한다.



Hello ksonnet

ksonnet의 개념을 이해했으면, 실제 테스트를 통해서 구체적은 사용법을 알아보도록 하자. 테스트를 위해서는 쿠버네티스 클러스터가 설치되어 있어야 하는데, 쿠버네티스 환경이 없는 경우에는 온라인에서 테스트할 수 있는 환경이 https://ksonnet.io/tour/welcome/ 에 있으니,이를 활용하는 것도 하나의 방법이다.

여기서 사용한 예제는 ksonnet 공식 홈페이지에 나와 있는 Guestbook 예제이다. https://ksonnet.io/docs/tutorial/guestbook/ 함축적으로 설명을 해놓은 간단한 예제라서 디테일한 내용을 이해하기는 어렵지만 대략적인 컨셉과 흐름을 이해하는데는 큰 무리가 없다.

애플리케이션 생성

먼저 애플리케이션을 생성한다. ks init 명령을 이용하면 애플리케이션 디렉토리가 생성되고, 필요한 초기 리소스들이 생성된다. 일종의 boiler plate와 같은 개념으로 보면 된다.

%ks init guest book

생성이 되면 아래와 같은 디렉토리 구조로 생성이 된다.

components에는 컴포넌트 파일과 parameter 등이 저장되는데, 정의된 컴포넌트가 없기 때문에, 컴포넌트 관련 파일은 없이 비어 있다.

Environment 디렉토리는 쿠버네티스 환경에 대한 정보가 들어있는데, 기본 환경값은 defaults라는 디렉토리에 저장된다.



Environment 생성

현재 환경을 cloud 라는 이름으로 등록해보자. 환경 등록은 ks add env라는 명령을 사용하면 된다. 등록해야하는 대상 쿠버네티스 클러스터가 많을 경우, 다른 환경을 추가로 등록하면 된다.

%ks add env cloud

Component 생성

다음으로 컴포넌트를 생성하는데, 등록된 prototype을 기반을 component를 생성해야 하는데, ks application을 생성하면 디폴트로 등록되어 있는 prototype이 있다. 등록되어 있는 prototype 목록은 ks prototype list 명령으로 조회가 가능하다.


%ks prototype list




여기서 사용할 prototype은 deployed-service를 사용하는데, image와 type 만을 지정한다.

이 prototype은 웹서비스를 위해서 쿠버네티스 리소스인 Service 와 Deployment를 생성해준다.

deployed-service prototype을 이용해서, 컴포넌트를 생성하기 위해서는 ks generate deployed-service {컴포넌트명} 으로 생성하고, 필요한 parameter는 --{parameter name}={parameter value) 식으로 지정한다.


% ks generate deployed-service guestbook-ui \

 --image gcr.io/heptio-images/ks-guestbook-demo:0.1 \

 --type ClusterIP


Component 생성이 끝나면 ~/components/ 디렉토리에 컴포넌트 파일이 생성된다.


생성된 컴포넌트 파일의 일부를 보면 다음과 같다.  컴포넌트들이 생성되고, parameter 처리되어 있는 부분은 아래 마크한바와 같이 parameter value를 각각 항목마다 지정되어 있다.


~/components/guestbook-ui.libsonnet

중략

:

 {

   "apiVersion": "apps/v1beta2",

   "kind": "Deployment",

   "metadata": {

     "name": params.name

   },

   "spec": {

     "replicas": params.replicas,

     "selector": {

       "matchLabels": {

         "app": params.name

       },

     },

     "template": {

       "metadata": {

         "labels": {

           "app": params.name

         }

       },

       "spec": {

         "containers": [

           {

             "image": params.image,

             "name": params.name,

             "ports": [

               {

                 "containerPort": params.containerPort

               }

             ]

           }

         ]

       }



     "name": params.name

   },

   "spec": {

     "ports": [

       {

         "port": params.servicePort,

         "targetPort": params.containerPort

       }

     ],

     "selector": {

       "app": params.name

     },

     "type": params.type

   }

 },


이하 생략

그리고 parameter로 지정한 내용들은 별도의 param.libsonnet 이라는 파일에 정의된다. 아래 그림과 같이 image명이나 type들이 앞에서 ks generate 단계에서 지정한 내용으로 정의된것을 확인할 수 있다. 다른 값들은 prototype에서 정의된 디폴트 값으로 채워진다.


~/components/param.libsonnet

local env = std.extVar("__ksonnet/environments");

local params = std.extVar("__ksonnet/params").components["guestbook-ui"];

[

중략

 components: {

   // Component-level parameters, defined initially from 'ks prototype use ...'

   // Each object below should correspond to a component in the components/ directory

   "guestbook-ui": {

     containerPort: 80,

     image: "gcr.io/heptio-images/ks-guestbook-demo:0.1",

     name: "guestbook-ui",

     replicas: 1,

     servicePort: 80,

     type: "ClusterIP",

   },

중략

생성된 Manifest 확인하기

생성된 컴포넌트가 각 쿠버네티스 환경에 어떻게 적용되는지를 살펴보기 위해서는  ks show {환경이름} 명령을 실행하면, 쿠버네티스에 적용된 YAML manifest 파일을 먼저확인해볼 수 있다.

%ks show cloud


다음은 “cloud” 라는 환경에 반영할 수 있는 YAML manifest 파일이다.

아래 그림은 YAML 파일중에서 Deployment 파트인데,  아래 그림고 같이 name이나 label등 구체적인 설정 값들이 앞에서 지정한 parameter 값으로 채워진것을 확인할 수 있다.

apiVersion: apps/v1beta2

kind: Deployment

metadata:

 labels:

   ksonnet.io/component: guestbook-ui

 name: guestbook-ui

spec:

 replicas: 1

 selector:

   matchLabels:

     app: guestbook-ui

 template:

   metadata:

     labels:

       app: guestbook-ui

   spec:

     containers:

     - image: gcr.io/heptio-images/ks-guestbook-demo:0.1

       name: guestbook-ui

       ports:

       - containerPort: 80



중략

Manifest 적용

이렇게 컴포넌트가 정의되었으면, 대상 쿠버네티스를 선택해서 ks apply 명령으로 적용 하면 된다. 아래 명령어는 “cloud” 라는 환경에 생성된 컴포넌트들을 적용하는 명령이다.

%ks apply cloud

적용된 서비스 확인 및 테스트

쿠버네티스에 적용된 리소스를 확인해보자. kubectl get svc와 kubectl get pod를 실행해보면 아래와 같이 Service와 Pod들이 생성된것을 확인할 수 있다.

%kubectl get svc




%kubectl get pod




생성된 서비스를 테스트해보자. 서비스를 ClusterIP로 생성하였기 때문에, 외부 IP를 통해서 접속은 불가능하고, kubectl proxy를 이용해서 프록시를 세팅해서 접속해야 한다. 아래는 kubectl proxy 명령어를 이용해서 proxy를 생성하고 접속을 하는 예제이다.

%kubectl proxy > /dev/null &

%KC_PROXY_PID=$!

%SERVICE_PREFIX=http://localhost:8001/api/v1

%GUESTBOOK_URL=$SERVICE_PREFIX/namespaces/default/services/guestbook-ui/proxy/


%open $GUESTBOOK_URL



프록시를 통해서 접속한 웹사이트에 대한 결과이다.


간단하게 컨셉과 예제를 통해서 실제 사용 방법을 확인해보았다.

ksonnet은 쿠버네티스의 다른 YAML 파일 배포 시스템과 다르게 prototype,component,application,environment 등과 같이 잘 추상화된 개념을 가지고 있으며, 특히 배포를 위해서 쿠버네티스에 무엇인가를 설치할 필요가 없이 클라이언트에 ks 툴만 설치하면 되기 때문에, 쿠버네티스 설치에 대한 의존성이 없이 깔끔하게 설치 및 운영이 가능하다.

또한 파일 형태로 모든 리소스를 관리할 수 있기 때문에 gitOps와 같은 형상 관리 시스템을 통해서 리소스를 관리할 수 있기 때문에, 변경 추적이나 코드 재 사용등에 많은 장점을 가질 수 있다.


그리드형