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


Archive»


 
 



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

#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"



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

댓글을 달아 주세요



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


쿠버네티스 스펙은 거의 다 보고 테스트 해봐서 멀티 클라우드와 쿠버네티스 에코 시스템을 살펴보다가 그동안 봐야지 하면서 쟁겨 놓았던 Rancher를 다시한번 볼 기회가 있어서 들여다 보았다.


퍼블릭 클라우드와 온프렘 상의 쿠버네티스의 설치 및 통합 관제를 가능하게 해주는 솔루션인데, 사실 이부분은 구글 클라우드의 Anthos와 개념상으로 겹치는 부분이 있어서 크게 메리트가 있을까 하는 의구심은 있었지만, 주변에 온프렘 설치를 Rancher를 많이 이용한다는 이야기를 들어서 한번 훝어 보았다.


일단 기본 설치는 간단하게 도커 이미지로 설치를 한 후에, 실행을하면 되기 때문에 별다른 설정이나 어려움은 없다. 새로운 클러스터 생성도 메이저 클라우드 3사를 모두 지원하고 있고, 온프렘도 vSphere 도 지원한다. 추가 클라우드 Provier를 설치하면, Baidu, 화웨이등 다른 클라우드들도 함께 지원한다. 



메뉴얼을 보니, RKE (https://rancher.com/docs/rke/latest/en/) 라고 Bare metal에 설치할 수 있는 쿠버네티스 버전도 있다. 

새로운 클러스터를 다양한 인프라에서 생성할 수 도 있지만, 반대로 이미 생성된 클러스터를 등록해서 통합 관제를 할 수 있다. 

계정 관리

일반 오픈소스 버전의 쿠버네티스는 계정 관리 시스템이 없고, 이를 외부 계정 관리 시스템과 연동해서 쓰도록 되어 있는데, Rancher를 사용할 경우, 일반 계정 시스템을 자체적으로 가지고 있고(로컬 데이타 베이스에 저장한다.) 그리고 필요하면 외부 계정시스템 (AD, GitHub,LDAP)을 사용하거나 SAML/SSO 인증등을 지원한다.


앱 카달로그

앱 카달로그를 Helm 연동을 통해서 지원하는데, 어려운 기능은 아니지만 아무래도 통합이 되어 있으니 꽤 괜찮다. 특히 멀티 클라우드 환경을 사용하면, 같은 패키지를 여러개의 쿠버네티스 클러스터에 설치할 일이 있을텐데, 이 부분이 꽤나 장점이 되지 않을듯 싶다.


모니터링

초기 대쉬 보드가 간단해서 우숩게 봤는데, 생각보다 꽤나 쓸만하다.


디폴트 대쉬 보드에서 위와 같이 기본적인 Node 정보들을 다 볼 수 있고, 그 아래 Cluster Metric에서는 Node 들의 상세 정보를, 그 아래 Kubernetes Components Metric을 들어가면 API 서버의 지표들이 나오는데, 이정보들은 모두 요약버전이고, Grafana 아이콘을 누르면 상당히 자세한 정보를 아래 그림과 같이 Grafana로 보여준다


<그림. Grafana 로 모니터링한 node 상세 정보>



<그림. 배포된 Pod에 대한 상세 정보 >


지표들을 보면 아주 상세하게 나오지는 않지만, 그래도 꽤 자세한 지표를 간단한 대쉬보드를 통해서 같은 뷰로 모니터링할 수 있다는 기능은 꽤 매력적이지 않나 싶다.


이러한 모니터링 기능들은 모니터링 기능을 활성화 해야 사용이 가능한데, 활성화 하는 부분을 보니 Prometheus 인터페이스를 사용한다. 그 말인즉슨, 현재 보이는 지표들 보다 훨씬 많은 지표를 표현할 수 있다는 말이 된다.

Alert

지표에 따른 알람 기능도 제공한다. 


산식을 보면, CPU 사용률이 100 이상인 것이 3분 지속되면 Alert 을 날리도록 하였다.

즉 지표를 정의하고,지표에 대한 목표값과 지속 시간을 정의한 개념인데, 이 기능이면 충분히 구글 SRE(DEVOPS 컨셉)의 SLI와  SLO를 현실적으로 구현이 가능하다.

Notifier

Alert 을 어디로 보낼까 했더니, Notification을 등록하는 기능이 있다.

Notifier 등록화면을 보니 이메일 뿐만 아니라 Pager Duty, Slack 그리고 Webhook 도 지원한다. 


위쳇도 지원되는데, 아쉽게도 카톡은 없다.


로깅



어쩔 수 없는 것은 알겠는데, 자체 로깅 시스템이 없고, 외부 로깅 시스템과 연동하도록 되어 있다.

ELK, Splunk를 지원하며, Kafka를 통해 로그를 빼내거나 Syslog/fluentd를 이용해서 로그 서버에 전송되 가능하다. 합리적인 디자인이라는 것에 동의는 하는데, 클릭 한번에 스택 드라이버등이나 기타 3’rd party log saas를 연동 시켜줬으면 어땠을까 하다.

기본 검토 의견

약간 모자르다. 생각을 많이 하고, 대부분의 기능을 충족시키는 것에는 동의하는데, 만들다가만 느낌이다. 특히 GUI 부분이나 기타 툴들이 무언가를 만들었다기 보다는 Grafana, Logging 시스템등 오픈 소스 시스템들을 누덕누덕 붙여놓은 느낌이다.


그렇지만 여러 클라우드의 여러 쿠버네티스 클러스터를 한눈에 관리 및 모니터링할 수 있는 점은 확실히 강점이 된다. 그리고 Prometheus 를 통한 같은 지표를 기반으로 모든 클러스터를 관리할 수 있고, Alert/Noti가 된다는 것은 확실히 장점이 되는것 같다.

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

댓글을 달아 주세요

쿠버네티스 고급 스케쥴링

#3 리소스 부족 (Resource starving) 관리


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


request와 limit의 개념이 있기 때문에 생기는 문제인데, request 된 양에 따라서 컨테이너를 만들었다고 하더라도, 컨테이너가 운영이되다가 자원이 모자르면 limit 에 정의된 양까지 계속해서 리소스를 요청하게 된다.

 컨테이너의 총 Limit의 양이 실제 시스템이 가용한 resource의 양보다 많을 수 있는 경우가 발생한다. 처음 CPU resource가 1 CPU 였는데, limit이 4이면,Pod 가 배포된 후에, 동작하다가 limit 양까지 증가되서 4 CPU가 되어 버릴 수 있다. 이때 node의 물리 CPU가 4 이면, CPU 리소스 부족이 발생할 수 있다. 이렇게 limit의 총량이 물리 resource의 총량 보다 많은 상황을 over committed 상태라고 한다.


Node에서 리소스 부족이 발생하면 쿠버네티스 클러스터는 이 상태를 해결하기 위해서 부족한 리소스를 회수하기 시작한다. 

리소스에 종류에 따라서 다른 동작을 하는데, 크게 Compressible 리소스와 Un-compressible 리소스에 따라 동작 방식이 다르다.

Compressible resource

Compressible resource는 CPU와 같이 할당된 리소스 양을 줄일 수 있는 리소스이다. 리소스 부족이 발생하면, node에 배포된 Pod의 CPU를 현재 사용하고 있는 양에서 초반 생성할때 할당된 request 크기까지 강제적으로 줄여서 리소스 부족을 해결한다. 이를 throttling 이라고 한다

이 경우 문제는 CPU 사용량일 request 이상으로 증가한 것은 그만큼 그 Pod가 애플리케이션을 동작시키기 위해서 CPU가 필요하였기 때문에 추가 할당되었던 것이다. Request 크기 까지 줄이면 할당된 CPU양을 강제적으로 줄이는 것이기 때문에, 성능 저하가 발생할 수 있다.

이런 문제를 예방하려면, Pod를 배포할때 request와 limit 값을 같이 주면, throttling은 발생하지 않지만 정확히 얼마의 CPU를 할당해야 하는지가 중요하다. 그래서 Pod 별로 적정한 CPU양을 계산하기 위해서는 개발/테스트 환경에서 request/limit 값을 서로 다르게 준 상태에서 부하 테스트를 통해서 적정 CPU 양을 찾아낸 후에 그 값을 적용하는 것이 좋다. 

Un-compressible resource

메모리나 로컬 디스크 공간은 compressible resource에 해당하지 않기 때문에, 강제적으로 Throttle을 할 수 없다. 그래서 쿠버네티스 클러스터는 리소스를 수거하기 위해서 리소스 사용량이 많은 Pod를 강제로 종료 시킨다. 

로컬 디스크의 경우에는 Pod를 강제 종료 시키기전에, 사용되지 않은 컨테이너 이미지등 사용되지 않은 공간을 삭제 해서 먼저 공간 확보를 시도하고, 그래도 공간이 모자르면 우선 순위에 따라서 Pod를 하나 삭제 한다. 삭제된 Pod는 컨트롤러 (ReplicaSet 등)에 의해서 관리가 되고 있으면 자동으로 다른 Node에서 생성된다. 

메모리의 경우에는 캐슁되는 이미지공간등이 없기 때문에, 바로 Pod를 우선순위에 따라서 삭제한다. 이를 Eviction이라고 한다.

우선 순위를 결정하는 로직은 복합적인 값들이 관여되는데, PodPriority 등, Pod 의 우선 순위등이 관련되는데, 대체적으로 Pod의 리소스 사용량이 request에 비해서 오버된 양이 가장 큰 Pod를 우선적으로 삭제 한다. 

예를 들어서 request가 3G, 4G 인 Pod가 각각 있고, 사용량이 5G, 4G 이면, request가 3G인 Pod가 사용량이 2G가 초과했고, 4G인 Pod는 초과하지 않았기 때문에, request가 3G인 Pod를 삭제(Evict)하게 된다. 

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: my-app-vpa

spec:

  targetRef:

    apiVersion: "extensions/v1beta1"

    kind:       Deployment

    name:       my-app

  updatePolicy:

    updateMode: "Auto"

출처 : https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler


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

Auto 모드로 하면, VPA가 적절한 CPU와 Memory을 측정한 후에, 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 가 추천되는 값이다.

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

댓글을 달아 주세요

쿠버네티스 고급 스케쥴링 기법

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

Affinity

Taint가 Pod가 배포되지 못하도록 하는 정책이라면, affinity는 Pod를 특정 Node에 배포되도록 하는 정책이다. affinity는 Node를 기준으로 하는 Node affinity와, 다른 Pod가 배포된 위치(node) 를 기준으로 하는 Pod affinity 두 가지가 있다. 

Node affinity 

Node affinity는 Pod가 특정 node로 배포되도록 하는 기능이다. 예전에 label에서 설명했던 node selector 도 node의 label과 pod의 selector label이 매칭되는 node에만 배포하도록 하기 때문에, 사실상 Node affinity와 같은 기능을 한다 

Node affinity는 Hard affinity와 Soft affinity가 있다. Node affinity는 Pod가 조건이 딱 맞는 node 에만 배포되도록 하는 기능이고, Soft affinity는 Pod가 조건에 맞는 node에 되도록(반드시가 아니라)이면 배포되도록 하는 기능이다. 앞에서 언급한 node selector는 Hard affinity에 해당한다. 


아래는 Pod 설정 YAML에서 node affinity를 적용한 예제이다. 


pod-with-node-affinity.yaml docs/concepts/configuration  

apiVersion: v1

kind: Pod

metadata:

  name: with-node-affinity

spec:

  affinity:

    nodeAffinity:

      requiredDuringSchedulingIgnoredDuringExecution:

        nodeSelectorTerms:

        - matchExpressions:

          - key: kubernetes.io/e2e-az-name

            operator: In

            values:

            - e2e-az1

            - e2e-az2

      preferredDuringSchedulingIgnoredDuringExecution:

      - weight: 1

        preference:

          matchExpressions:

          - key: another-node-label-key

            operator: In

            values:

            - another-node-label-value

  containers:

  - name: with-node-affinity

    image: k8s.gcr.io/pause:2.0



출처 : From : https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity


requiredDuringSchedulingIgnoredDuringExecution는 Hard affinity 정의 이다. nodeSelectorTerms 부분에, matchExpressions을 사용하여, label set-based selector 문법을 이용하면 된다.

위의 예제는 node에 label key “kubernetes.io/e2e-az-name” 의 값이 eze-az1 이나 eze-az2 인 node를 선택하도록 하는 설정이다. 


Node affinity는 여러 affinity를 동시에 적용할 수 있는데, 위의 예제에서는 두 개의 Affinity를 정의하였다. 두번째 Affinity는 Soft affinity로, preferredDuringSchedulingIgnoredDuringExecution: 으로 정의한다. Soft affinity는 앞서 언급한것과 같이 조건에 맞는 node로 되도록이면 배포될 수 있도록 그 node로 배포 선호도를 주는 기능이다. 이때 weight 필드를 이용해서 선호도를 조정할 수 있는데, weight은 1~100이고, node 의 soft affinity의 weight 값들을 합쳐서 그 값이 높은 node를 우선으로 고려하도록 우선 순위를 주는데 사용할 수 있다. 


특정 Node로 배포되게 하는 Affinity 설정도 있지만, 반대로 특정 Node로 배포되는 것을 피하도록 하는 AntiAffinity라는 설정도 있다. nodeAffinity 대신 nodeAntiAffinity라는 Notation을 사용하면 되고, Affinity와는 다르게 반대로, 조건에 맞는 Node를 피해서 배포하도록 되낟. 

Inter-Pod affinity

Node affinity가 node의 label을 기준으로 Pod가 배포될 node는 선택한다면, Inter pod affinity는 기존에 배포된 Pod를 기준으로 해서, 배포될 node를 결정한다. 

Pod affinity는 데이타 베이스의 Master / Slave pod 가 다른 node 에 배포되도록 하기 위해서 master pod가 배포된 node를 피해서 배포하게 한다던가. 클러스터 시스템에서 클러스터를 이루는 각각의 Pod가 다른 node에 배포도록 하는 등에 전략에 사용할 수 있다. 


Node affinity와 마찬가지로 Hard affinity와 Soft affinity가 있다. Node affinity아 마찬가지로 requiredDuringSchedulingIgnoredDuringExecution 로 hard affinity를,preferredDuringSchedulingIgnoredDuringExecution로 soft affinity를 정의한다.

Node를 선택할때 Inter pod affinity는 node affinity와 다르게 topology key 라는 것을 사용한다. 

Pod affinity는 Pod affinity에 의해서 해당 node를 선택한 후에, 그 node의 label을 하나 선택한다. 선택하는 label은 topology key로 지정하는데, 이 topology key에 매칭 되는 node 들을 배포 대상으로 선택한다. Node affinity와 마찬가지로 특정 Pod와 같이 배포되는 것을 피하는 AntiAffinity 도 있다. Pod affinity는 podAffinity라는 notation을 사용하고, Anti affinity는 PodAntiAffinity라는 Notation을 사용한다. 


개념이 복잡하기 때문에 아래 예제를 보면서 이해해보도록 하자


apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: cloud/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0


출처  https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity



와 같이 Pod 설정 파일이 있다고 했을때, 배포는 다음 그림과 같다. 



  PodAffinity에서 requiredDuringSchedulingIgnoredDuringExecution 로 Hard affinity를 정의하였다. 이렇게 되면, 기존에 배포된 Pod 중에서, key가 “security”이고, value가 “S1”인, Pod1이 배포된 node인 Node 1을 기준으로 하는데, topologyKey로 정의된 cloud/zone label의 값”z1”을 기준 Node인 Node 1에서 읽어서, Node 들 중에 label이 “cloud/zone=z1” 인 Node 들만을 후보로 선택해서, Node 1, Node 2 를 배포 가능 Node 로 선택한다.


Inter-Pod affinity 예제

다른 예제를 보자. 아래 예제는 redis-cache Pod를 3개 배포하는데, 각각 다른 Node에 분산되서 배포되도록 하는 예제이다. 


apiVersion: apps/v1

kind: Deployment

metadata:

  name: redis-cache

spec:

  selector:

    matchLabels:

      app: store

  replicas: 3

  template:

    metadata:

      labels:

        app: store

    spec:

      affinity:

        podAntiAffinity:

          requiredDuringSchedulingIgnoredDuringExecution:

          - labelSelector:

              matchExpressions:

              - key: app

                operator: In

                values:

                - store

            topologyKey: "kubernetes.io/hostname"

      containers:

      - name: redis-server

        image: redis:3.2-alpine


출처 : https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity


PodAntiAffinity를 사용한 예로, requiredDuringSchedulingIgnoredDuringExecution 를 이용한 Hard affinity이다. Pod의 label이 “app=store”가 있는 Pod가 배포되어 있는 Node 중에서 topologyKey가 kubernetes.io/hostname (쿠버네티스에서 자동으로 미리 저장하는 label로 Node의 이름을 정의하는 label 이다.) 로 되어 있기 때문에, node의 kubernetes.io/hostname label 값이 이 Node와 다른 Node를 배포 타겟으로 설정한다. 즉 “app=store” 이름으로 배포된 Pod가 없는 Node에 배포하는 설정이다.

이 Redis pod는 “app=store” 라는 label을 가지고 있는 Pod이기 때문에, 이미 Node에 이 Redis Pod가 배포되어 있으면, 그 Node에는 배포되지 않기 때문에 Redis Pod가 배포되지 않은 다른 Node에 중첩되서 배포되지 않도록 해준다. 


이 상태에서 nginx 서버를 배포해보자. Nginx pod를 3개 배포하는데, 각각을  서로 다른 node에 배포하도록 하되, 대신 redis가 배포된 node에 배포하도록 하는 설정이다.  


apiVersion: apps/v1

kind: Deployment

metadata:

  name: web-server

spec:

  selector:

    matchLabels:

      app: web-store

  replicas: 3

  template:

    metadata:

      labels:

        app: web-store

    spec:

      affinity:

        podAntiAffinity:

          requiredDuringSchedulingIgnoredDuringExecution:

          - labelSelector:

              matchExpressions:

              - key: app

                operator: In

                values:

                - web-store

            topologyKey: "kubernetes.io/hostname"

        podAffinity:

          requiredDuringSchedulingIgnoredDuringExecution:

          - labelSelector:

              matchExpressions:

              - key: app

                operator: In

                values:

                - store

            topologyKey: "kubernetes.io/hostname"

      containers:

      - name: web-app

        image: nginx:1.12-alpine


출처  https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity


먼저 podAntiAffinity로 “app:web-store” Pod가 배포되어 있는 node를 찾은 후에, 그 Node이 kubernetes.io/hostname 를 topologyKey로 해서, 그 node들을 제외한다.

즉 “app:web-store” 라벨을 가지고 있는 Pod 들이 배포된 Node를 제외하는 설정이다. 그런데, 이 Pod는 label을 “app:web-store” 라벨을 가지도록 되어 있기 때문에, 이 Pod가 배포되어 있는 Node에 배포하지 말고, 다른 Node에 분산해서 배포하라는 내용이다.


여기에 PodAffinity로 “app:store”인 Pod가 배포되어 있는 Node에서 topologyKey로 “kubernetes.io/hostname”을 사용하였기 때문에, “app:store”인 라벨을 가지고 있는 Pod가 배포되어 있는 Node를 찾아서 배포하라는 내용이다. 다시 말해서 “app:store”인 Pod와 같은 Node에 배포하라는 의미이다. 


아래는 실행결과 인데, web-server가 각각 다른 Node에 겹치지 않고 분리되서 부탁되어 있는 것을 볼 수 있고, redis-cache Pod도 역시 서로 다른 Node에 겹치지 않게 배포되어 있는 것을 확인할 수 있다. 그리고 web-server와 redis-cache pod들은 하나씩 같은 Node에 배포된것을 확인할 수 있다. 


NAME                           READY STATUS RESTARTS AGE       IP NODE

redis-cache-1450370735-6dzlj   1/1 Running 0 8m        10.192.4.2 kube-node-3

redis-cache-1450370735-j2j96   1/1 Running 0 8m        10.192.2.2 kube-node-1

redis-cache-1450370735-z73mh   1/1 Running 0 8m        10.192.3.1 kube-node-2

web-server-1287567482-5d4dz    1/1 Running 0 7m        10.192.2.3 kube-node-1

web-server-1287567482-6f7v5    1/1 Running 0 7m        10.192.4.3 kube-node-3

web-server-1287567482-s330j    1/1 Running 0 7m        10.192.3.2 kube-node-2

출처  https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

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

댓글을 달아 주세요

자연어 처리 - 단어 표현 방법

빅데이타/머신러닝 | 2019.08.04 18:28 | Posted by 조대협

자연어 처리

Word representation

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


머신러닝을 위해서 단어를 수치화 하는 방법을 Word Representation이라고 한다.

가장 쉬운 방법으로는 One-hot-encoding을 사용하는 방법이 있지만, 이 방법의 경우에는 단어간의 의미를 수식화할 수 없고, 특히나 단어가 많을 경우에는 단어를 표현하는 벡터(행렬)의 크기가 커지며, 그중에서 하나의 필드만 1로 처리하기 때문에 공간 소모가 심하다. 


그래서 분포 가설(Distributed hypothesis)를 기반으로한 방식이 많이 사용되는데, 분포 가설(Distributed hypothesis) 란, 같은 문맥의 단어는 비슷한 의미를 갖는다는 개념으로 두 가지 방식이 많이 사용된다.

Count based 

문장에 동시에 등장하는 단어의 수를 카운트 해서 이를 벡터화 하는 방식

동시에 등장하는 횟수를 Co-occurrence라고 하고, 이를 행렬화 하는 방식은 

  • SVD (Singular Value Decomposition) : 특이값 분해

  • LSA (Latent Semantic Analysis) : 잠재적 의미 분해

  • HAL (Hyperspace Analogue to Language)

등이 있음

Predictive

NN이나 모델을 이용해서, 특정 문맥에 어떤 단어가 나올것을 예측하면서 벡터를 만드는 방법

  • NNLM (Neural Network Language Model)

  • RNNLM(Recurrent Neural Network Language Model) 

  • Word2Vec

    • CBOW (Continuous Bag of Words)
      문장을 통해서 단어를 예측
      예) 오늘은 저녁에 ??나 먹어야 겠다. 

    • Skip-Gram ← 가장 많이 사용함
      단어를 통해 전체 문장을 예측
      예) ??? ???? 짜장면이나 ??? ??


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

댓글을 달아 주세요

쿠버네티스 고급 스케쥴링 기법

#1 스케쥴링과 Taint&Toleration

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

쿠버네티스 스케쥴링

쿠버네티스에서 Pod 를 생성 요청 했을때, Pod를 적정 node에 배치하는 것을 스케쥴링이라고 한다. Pod를 어느 node에 배치할것인가에 대해서는 생각 보다 많은 고려가 필요하다. 먼저 Pod가 생성하기 위한 충분한 리소스 (CPU와 메모리)가 남아 있어야 하고, 디스크 볼륨을 사용할 경우, attach하고자 하는 디스크 볼륨이 해당 node에서 접근이 가능해야 한다.

또한 애플리케이션 특성에 따라서, Pod의 배포에 대해 배려가 필요한 경우도 있다. 예를 들어 MySQL을 HA 모드로 배포하기 위해서 마스터, 슬레이브 노드 각각을 배포하고자 할때, 마스터 슬레이브가 같은 node에 배포되게 되면, 해당 node가 문제가 생기면 마스터,슬레이브 노드 모두가 서비스가 불가능해지기 때문에, HA에 의한 가용성을 지원할 수 없다. 그래서 마스터 슬레이브 노드를 다른 node에 배포해야 하고, 더 나아가 다른 rack, 클라우드의 경우에는 다른 Zone(존)에 배포될 필요가 있다. 

이 모든 것을 제어 하는 것을 스케쥴링이라고 한다. 

이 장에서는 쿠버네티스의 스케쥴링이 어떻게 작동을 하는지 그리고, 이 스케쥴링을 제어할 수 있는 고급 기법에 대해서 알아보고자 한다. 

스케쥴링 작동의 기본 원리

(참고 : 쿠버네티스의 스케쥴링 정책은 이 에 매우 잘 설명되어 있다. )

Pod 생성이 요청 되면, 쿠버네티스 스케쥴러는 먼저 필터라는 것을 이용해서, Pod가 배포될 수 있는 Node를 선정하는 작업을 한다.

크게 보면 세 종류의 필터를 사용하는데, 다음과 같다.

  • 볼륨 필터

  • 리소스 필터

  • 토폴로지 필터

각각을 살펴보자

볼륨필터

Pod를 생성할 때, 생성하고자 하는 Pod의 디스크 볼륨에 대해서 Node가 지원할 수 있는지를 확인한다. 

예를 들어 클라우드에서 생성되는 Pod가 zone 1에 생성된 디스크를 attach해야 하는 조건을 가지고 있을때, 특정 클라우드들의 경우 다른 zone의 디스크를 attach할 수 없기 때문에, zone 1 이외에 있는 Node들을 후보에서 제외하고, 해당 볼륨을 attach할 수 있는 Node 들만 후보로 남긴다.

또는 쿠버네티스에서는 사용자가 볼륨에 node-affinity를 정의해서 특정 node 에만 그 볼륨을 attach할 수 있도록 하는데, 이러한 조건에 부합하지 않는 Node들을 제거하고 후보 Node 리스트를 만든다.

리소스 필터

다음으로 적용되는 필터가 리소스 필터인데, 해당 Node 들이 Pod를 배포할만한 충분한 리소스 (CPU,Memory,Disk)가 있는지를 확인하는 단계이다.

CPU 와 메모리 여유분이 Pod가 요청한 만큼 충분한지, 그리고 Node의 디스크 공간도 확인하는데, 앞에서 언급한 디스크 볼륨과 다소 차이가 있는 것이, Node가 Pod를 실행하기 위해서는 Pod를 실행하기 위한 디스크 공간이 필요하다. Pod 의 컨테이너 이미지를 저장하기 위한 공간등이 이에 해당한다. 

CPU,Memory,Disk 뿐 아니라 네트워크 포트도 체크를 하는데, Pod가 Node 포트를 사용하는 Pod 일 경우, 예를 들어 Pod가  Node의 8080 포트를 사용하고자 하는데, 이미 해당 Node의 8080 포트가 다른 Pod에 의해서 점유된 경우, 새로운 Pod를 생성할 수 없기 때문에 그 Node를 Pod를 생성하기 위한 Pod 리스트에서 제외한다. 


일반적인 경우에는 볼륨 필터와 리소스 필터를 거친 Node들을 후보로 두고 이 중에서 적절한 Node를 선택해서 Pod를 배포한다. 

고급 스케쥴링 정책

Pod를 배포할때, 사용자가 특정 Node를 선택할 수 있도록 정책을 정의할 수 있다. 예를 들어 앞에서 언급한것과 같이 MySQL의 마스터, 슬레이브가 같은 Node에 배포되지 않도록 Pod의 스케쥴링 정책을 인위적으로 조정할 수 있다. 이를 고급 스케쥴링 기법이라고 하는데, 자세한 설명은 이 문서를 참고하기 바란다. 

Taint & Toleration

먼저 살펴볼 스케쥴링 정책은 Taint와 Toleration이다.

Taint는 Node에 정의할 수 있고, Toleration은 Pod에 정의할 수 있는데, 한마디로 쉽게 설명하면, Taint 처리가 되어 있는 Node에는 Pod가 배포되지 않는다. Taint 처리가 되어 있는 Node에는 Taint에 맞는 Toleration을 가지고 있는 Pod 만 배포될 수 있다.

Taint

Taint는 label과 유사하게 <key>=<value>:<effect> 형태로 정의되서 node에 적용된다. key와 value는 사용자가 마음대로 정할 수 있으며, effect는 NoSchedule, PreferNoSchedule,NoExecute 3가지로 정의할 수 있다. NoSchedule은 taint 처리가 되어 있는 node에 대해서는 Pod가 이에 맞는 toleration을 가지고 있다면 이 Node에는 그 Pod를 배포하지 못하도록 막는 effect 이다. (나머지 2가지 effect에 대해서는 뒤에서 설명한다.)

Node에 taint 를 적용하는 방법은 다음과 같다.

%kubectl taint node [NODE_NAME] [KEY]=[VALUE]:[EFFECT]

형태로 적용하면 된다.

예를 들어 gke-terry-gke11-default-pool-317bb64b-21kd Node에 key가 “node-type”이고, value가 “production”이고, Effect가 NoSchedule인 Taint를 적용하고자 하면 다음과 같이 명령을 실행하면 된다. 

 

%kubectl taint node gke-terry-gke11-default-pool-317bb64b-21kd node-type=production:NoSchedule

node/gke-terry-gke11-default-pool-317bb64b-21kd tainted


이렇게 taint를 적용한 후, Taint가 제대로 적용이 되었는지, kubectl get nodes gke-terry-gke11-default-pool-317bb64b-21kd -o yaml 명령을 이용해서 확인해보면 다음과 같이 taint가 적용되어 있는 것을 확인할 수 있다. 


apiVersion: v1

kind: Node

metadata:

: (중략)

   name: gke-terry-gke11-default-pool-317bb64b-21kd

: (중략)

spec:

: (중략)

  taints:

  - effect: NoSchedule

    key: node-type

    value: production


이렇게 Taint 처리가 된 Node는 알맞은 Toleration이 정의되지 않은 Pod는 배포될 수 없다. 


Node에 Taint를 적용하는 방법은 앞에서 설명한 것과 같이 node 이름을 정의해서 하나의 특정 Node에 적용하는 방법도 있지만, node 에 적용된 label을 이용하여, label이 일치 하는 여러개의 node에 동시에도 적용할 수 있다. 

방법은 아래와 같이 -l 옵션을 이용해서 적용하고자 하는 node의 label의 key/value를 적용하면 된다. 

%kubectl taint node -l [LABEL_KEY]=[LABEL_VALUE] [KEY]=[VALUE]:[EFFECT]


예를 들어서 아래와 같이 -l  옵션을 적용하면,

%kubectl taint node -l node-label=zone1 node-type=production:NoSchedule

Node 중에서 label이 node-label=zone1인 모든 node에, node-type=production:NoSchedule 인 Taint가 적용된다. 

Toleration

그러면 Taint 처리가되어 있는 Node에 Pod를 배포하기 위해서 사용하는 Toleration이란 무엇인가?

Toleration처리가 되어 있는 Node에 배포될 수 있는 일종의 티켓과 같은 개념이라고 생각하면 된다. Taint 처리가 되어 있는 Node에 Toleration이라는 티켓을 가지고 있으면, 그 Node에 Pod가 배포될 수 있다. (“배포된다"가 아니라, “배포될 수 있다" 라는 의미에 주의하도록 하자. 그 Node가 아니라 다른 조건에 맞는 Node가 있다면, 배포될 수 있다.)

Toleration의 정의는 Match operator를 사용하여 Pod Spec에 정의한다.

tolerations:

- key: "key"

  operator: "Equal"

  value: "value

  effect: "NoSchedule"


이렇게 정의하면, key,value,effect 3개가 Taint와 일치하는 Node에 Pod가 배포될 수 있다. 

조금 더 광범위하게 정의를 하려면, “Exist”를 사용하면 된다. 

tolerations:

- key: "key"

  operator: "Exists

  effect: "NoSchedule"


이렇게 정의하면, Taint에 위에서 정의한 Key가 있고, effect가 “NoSchedule”로 설정된 Node에 value 값에 상관 없이 배포될 수 있다.

또는 아래와 같이 tolerations 절에서 effect 항목을 제외하면, 해당 key로 Taint가 적용되어 있는 모든 Node에 대해서 이  Pod를 배포하는 것이 가능하다. 

tolerations:

- key: "key"

  operator: "Exists


Taints는 특정 nodes에 일반적인 Pod가 배폭되는 것을 막을 수 있다. 가장 좋은 예로는 쿠버네티스의 마스터 Node에 적용된 Taints가 이에 해당한다. 쿠버네티스 마스터 Node에는 관리를 위한 Pod만이 배포되어야 하기 때문에, 일반적인 Pod를 배포할 수 없도록 Taints가 이미 적용되어 있고, 마스터 Node에 Pod를 배포하기 위해서는 이에 맞는 Toleration을 가지고 있어야 한다. 

이 외에도 운영용 Node로 특정 Node들을 적용해놓고, 개발이나 스테이징 환경용 Pod이 (실수로라도) 배포되지 못하게 한다는 것등에 사용할 수 있다. 

Taint와 Toleration 개념 정리

앞에서 Taint와 Toleration의 개념과 사용법에 대해서 설명하였는데, 이를 이해하기 편하게 그림으로 정리해서 보자




<그림. Taints와 Toleration의 개념>

출처 : https://livebook.manning.com/#!/book/kubernetes-in-action/chapter-16/section-16-3-1


Master node에는 node-role.kubernetes.io/master 라는 key로 value 없이 effect만 “NoSchedule”로 Taint를 정의하였다. toleration을 가지지 못한 일반적인 Pod는 Master node에는 배포될 수 없고, Taint 처리가 되어 있지 않은 regular node에만 배포가 가능한다.

System pod의 경우 node-role.kubernetes.io/master 라는 key로, effect가 “NoSchedule”인 toleration을 가지고 있기 때문에, Taint가 없는 Regular node에는 당연히 배포가 가능하고, Toleration에 맞는 Taint를 가지고 있는 Master node에 배포될 수 있다. 

Taint Effect 

Taint와 Toleration에 대한 사용법과 개념을 이해하였으면, 이제 Taint effect에 대해서 조금 더 자세하게 알아보도록 하자. 앞에서도 설명했듯이 Taint에 적용할 수 있는 effect는 아래와 같이 3가지가 있다. 

  • NoSchedule : Pod가 배포되지 못한다. (Toleration이 일치하면 배포됨)

이 effect로 Taint가 적용된 Node는 일치하는 Toleration을 가지고 있는 Pod가 아닌 경우에는 배포되지 못한다. 단, 이는 새롭게 배포되는 Pod에만 적용되고 이미 배포되어 있는 Pod에는 적용되지 않는다. 다시 말해서, Node 1에 Pod 1이 돌고 있는데, 이 Node 1에 Taint를 적용하면, Taint 적용전에 배포되서 돌고 있는 Pod 1에는 영향을 주지 않는다. Pod 1는 알맞은 Toleration이 없더라도, 종료되서 새롭게 스케쥴링이 되지 않는 이상 Node 1에 배포된 상태로 동작한다. 


만약에 이미 돌고 있는 Pod들에게도 영향을 주려면 NoExecute 라는 effect 를 사용하면 된다. 

  • NoExecute :  돌고 있던 Pod들을 evit 하고(다른 node로 옮김), 새것들은 못들어 오게 한다.

이 effect는 NoSchedule과 유사하지만, 새롭게 배포되는 Pod 뿐만 아니라, 이미 그 Node 에서 돌고 있는 Pod 들에게도 영향을 줘서, NoExecute로 Taint가 적용되면, 이에 해당하는 Toleration을 가지고 있지 않는 Pod는 모두 evict 되서 그 node에서 삭제 된다. 물론 ReplicaSet/Deployment 등 Controller에 의해서 관리되는 Pod의 경우에는 Taint 처리가 되어 있지 않은 다른 Node에서 새롭게 생성된다. 


이 effect에 대해서는 tolerationSeconds 라는 패러미터를 고려해야 하는데, 이 Taint가 적용된 Node에 맞는 toleration 을 가지고 있는 Pod의 경우, 이 Node에 영구적으로 남아 있지만, Pod의 toleration에 tolerationSeconds 패러미터가 정의되어 있으면 이 시간만큼만 남아 있다가 evit 된다. 즉 Pod 1, Pod 2,Pod 3가 Node 1에서 돌다가,  Node 1 에 NoExecute effect로 Taint가 적용되었다고 했을때, Pod 1은 이 Taint에 맞는 Toleration을 가지고 있고

Pod 2는 이 Taint에 맞는 Toleration을 tolerationSeconds=300(초) 패러미터와 함께 정의되어 가지고 있다면

Pod 3는 아마 Toleration이 없다면 

Pod 1은 계속 Node1에 남아 있게 되고,  Pod 2는 Node 2에 300초 동안 남아있다가 evit (강제 종료)되며, Pod 3는 바로 강제 종료가 된다. 


  • PreferNoSchedule : 가급적 Pod 배포하지 않는다. 


마지막으로 소개할 effect는 PerferNoSchedule인데, NoSchedule의 소프트 버전으로 생각히면 된다. NoSchedule로  Taint 처리가 되어 있는 Node 라면, 스케쥴시에, Toleration을 가지고 있지 않은 Pod는 무조건 배포가 불가능하지만, PreferNoSchedule의 경우에는 Toleration이 없는 Pod의 경우에는 되도록이면 배포되지 않지만 리소스가 부족한 상황등에는 우선순위를 낮춰서, Toleration이 없는 Pod도 배포될 수 있도록 한다.

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

댓글을 달아 주세요