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


Archive»


 
 


쿠버네티스 #10


배포 (Deployment)


조대협



롤링 업데이트

애플리케이션을 배포 하는 방법에 대해서 알아보자.

일반적으로 애플리케이션을 배포하는 방법은 블루/그린, 카날리 배포, 롤링 업데이트도 여러가지 방법이 있다.

그중에서 몇가지 패턴에 대해서 알아보도록 하자


롤링 업데이트는 가장 많이 사용되는 배포 방식 중의 하나이다.

새 버전을 배포하면서, 새 버전 인스턴스를 하나씩 늘려나가고, 기존 버전을 하나씩 줄여나가는 방식이다. 이 경우 기존 버전과 새버전이 동시에 존재할 수 있는 단점은 있지만, 시스템을 무 장애로 업데이트할 수 있다는 장점이 있다.


롤링업데이트를 쿠버네티스의 Replication Controller (RC)를 이용하는 방법을 보면 다음과 같다.

아래와 같이 RC가 v1 버전의 Pod들을 관리하고 있다고 보자. 아래와 같이 3개의 Pod가 서비스 되고 있다고 볼때,



v2를 배포하기 위해서, v2 Pod를 컨트롤한 RC를 만들고 replica의 수를 1로해서 v2 Pod를 하나 생성한다.. 그리고 RC v1에서, replica의 수를 3 → 2로 줄여서 v1 Pod의 수를 2개로 조정한다.



같은 방식으로 v1 Pod의 수를 1로 조절하고, v2 Pod의 수를 2로 늘린다.



마지막으로, Pod v1을 0으로 줄이고, RC v1을 삭제한다. 그리고 Pod v2을 하나 더 늘린다.




앞에서 봤듯이, 롤링 업데이트를 하려면 RC를 두개를 만들어야 하고, RC의 replica 수를 단계적으로 조절해줘야 한다. 또한 배포가 잘못되었을때 이를 롤백하려면 이 순서를 꺼꾸로 다시 해야 한다.

Deployment

여러가지 배포 방식을 RC를 이용해서 구현할 수 있지만, 운영이 복잡해지는 단점이 있다. 그래서 쿠버네티스에서는 일반적으로 RC를 이용해서 배포하지 않고 Deployment라는 개념을 사용한다.

앞에서 봤듯이 롤링 업데이트등을 할때, RC를 두개를 만들어야 하고, 하나씩 Pod의 수를 수동으로 조정해야 하기 때문에 이를 자동화해서 추상화한 개념이 Deployment 이다.


Deployment는 기본적으로 RC를 생성하고, 이를 관리하는 역할을 한다.


v1 버전을 배포했다가 v2를 배포하는 시나리오를 구현해보자

먼저 node.js로 v1과 v2 애플리케이션을 구현해보자


아래는 version 1 애플리케이션인 server.js 이다. 응답으로 “Hello World! I’m Server version 1” 을 리턴한다.


var os = require('os');


var http = require('http');

var handleRequest = function(request, response) {

 response.writeHead(200);

 response.end("Hello World! I'm Server version 1 .  "+os.hostname() +" \n");


 //log

 console.log("["+

Date(Date.now()).toLocaleString()+

"] "+os.hostname());

}

var www = http.createServer(handleRequest);

www.listen(8080);


Version 2 애플리케이션도 내용은 갔다. 응답 문자열을 아래와 같이 version 1에서 version 2로만 변경하였다.

 response.end("Hello World! I'm Server version 2. "+os.hostname() +" \n");


이 두 server.js를 도커 이미지로 만든 다음에,

gcr.io/terrycho-sandbox/deployment:v1 , gcr.io/terrycho-sandbox/deployment:v2 라는 이름으로 컨테이너 레지스트리에 등록한다.


다음 Pod를 컨트롤할 RC를 정의해야 하는데, Deployment를 사용하면 RC를 별도로 정의하지 않고, Deployment가 자동으로 RC를 생성하도록 한다.

아래는 hello-deployment.yaml 로 Deployment를 정의한 내용이다.  RC 정의와 크게 다르지 안고, selector 부분을 matchLabels를 사용하였다.


apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: hello-deployment

spec:

 replicas: 3

 minReadySeconds: 5

 selector:

   matchLabels:

     app: hello-deployment

 template:

   metadata:

     name: hello-deployment-pod

     labels:

       app: hello-deployment

   spec:

     containers:

     - name: hello-deployment

       image: gcr.io/terrycho-sandbox/deployment:v1

       imagePullPolicy: Always

       ports:

       - containerPort: 8080



롤링 업데이트시 Pod 배포가 빠르게 되기 때문에, 테스트 용도로 Pod 배포를 인위적으로 느리게 해서 배포 과정을 살펴보기 편하게 하도록 하겠다.

minReadySeconds를 5로 줬는데, Pod가 배포되고 서비스를 시작하는데 5초 정도 딜레이를 준다. 5초 단위로, Pod 가 배포 되는 과정을 살펴볼 수 있다.


다음 이 Pod들 앞에 Service를 배포해보자

아래는 hello-deployment-service.yaml 이다.


apiVersion: v1

kind: Service

metadata:

 name: hello-deployment-svc

spec:

 selector:

   app: hello-deployment

 ports:

   - name: http

     port: 80

     protocol: TCP

     targetPort: 8080

 type: LoadBalancer


배포

YAML 파일 작성이 끝났으면, Deployment와 Service를 배포해보자.

배포가 끝난후에, kubectl get pod 로 확인해보면 아래와 같이 pod 가 3개가 올라온것을 확인할 수 있다.



Service의 IP를 확인한후 curl 명령을 이용해서 요청을 보내보면 아래와 같이 version 1 서버로 요청이 가는 것을 확인할 수 있다.



업데이트

그러면 v2 이미지를 배포해보자. 여러가지 방법이 있는데, kubectl set image deployment라는 명령을 이용하면 이미지를 새 이미지로 변경할수 있다. 다음 명령어를 실행해보자


%kubectl set image deployment hello-deployment hello-deployment=gcr.io/terrycho-sandbox/deployment:v2


명령어를 실행하면 v1 → v2로 pod를 하나씩 롤링 업데이트를 한다. 위의 명령을 실행해놓고 kubectl get pod 명령을 실행해보면 아래 그림과 같이 pod를 하나씩 지워가면서 새로운 pod를 생성하는 것을 확인할 수 있다.




배포가 끝난후에, kubectl describe deploy hello-deployment 명령을 실행해서  deployment 내용을 살펴보자 아래 내용을 보면 Replica Set replica set hello-deployment-68bd497896의 replica수를 3-->0 개로 줄이고, 새 버전의 RS인 replica set hello-deployment-5756bb6c8f의 replica 수를 1-->3으로 올리는 것을 확인할 수 있다.



잘 보면 Replication Controller가 아니라 Replication Set (RS)을 사용하는 것을 볼 수 있는데, Deployment는 RC대신 RS를 사용한다.

그리고 하나 주목할만한점은 Pod의 이름은 [deployment 이름]-[RS의 해쉬 #]-[Pod의 해쉬#] 를 사용한다.


배포가 끝난 후에 확인해보면 위처럼 Pod의 이름이 hello-deployment-5756bb6c8f-* 으로, 새로 배포된 RS 의 이름과 동일한것을 볼 수 있다.


set image 명령은 기존 배포된 deployment의 이미지를 변경하도록 하지만,

Deployment의 이미지 수정과 같이 쿠버네티스의 객체 (Object)의 정보를 수정하는 방법은 여러가지 방법이 있다.


객체의 설정 업데이트

그러면 간단하게, 쿠버네티스 객체 정보를 수정하는 방법을 살펴보자

kubectl edit

edit 명령은 리소스의 설정 정보를 kubectl 이 설치되어 있는 머신의 에디터를 이용해서 에디트할 수 있다.

예를 들어 hello-deployment 라는 이름의 deployment 리소스의 설정을 에디트 하고 싶으면,

%kubectl edit deploy hello-deployment

명령을 실행하면, (맥북 기준) vi 에디터가 실행되고, hello-deployment  설정 정보를 vi 에디터로 편집할 수 있게해준다. 에디터에서 설정을 변경한 후 저장하면, 그 객체의 설정값이 업데이트 된다.


아래는 kubectl edit deploy hello-deployment 를 이용하여 컨테이너 이미지의 태그를 v2로 변경한 예이다.


설정을 변경하는데 유용하게 사용할 수 있지만, 특정 리소스의 설정 정보를 에디터에서 보여주기 때문에, 상세한 설정 (yaml)을 보는데 유용하게 이용할 수 있다.

kubectl replace

설정 파일 수정하거나, 설정 파일을 새로 만들어서 그 파일로 설정을 업데이트하고 싶을때는  replace 명령을 사용한다.

%kubectl replace -f [YAML 파일명]

으로 이용한다.

kubectl patch

patch 명령은 파일 업데이트 없이 기존 리소스의 설정 정보 중 여러개의 필드를 수정하고자 하는데 이용한다.

%kubectl patch [리소스 종류] [리소스명] --patch ‘[YAML이나 JSON 포맷으로 변경하고자 하는 설정]’ 을 넣어주면 된다.


예를 들어

%kubectl patch deployment hello-deployment --patch 'spec:\n template:\n  spec:\n containers:\n - name: hello-deployment\n image: gcr.io/terrycho-sandbox/deployment:v2’

명령은 deployment 중에 hello-deployment 리소스에 대해서 image 명을  image: gcr.io/terrycho-sandbox/deployment:v2 로 변경한 예이다.


특히 [YAML] 설정은 한줄에 써야 하는 만큼 띄어쓰기에 조심하는 것이 좋다.


patch나 edit의 경우 쉽게 설정을 업데이트할 수 있는 장점은 있지만, 업데이트에 대한 히스토리를 추적하기 어려운 만큼 가급적이면, 새로운 파일을 생성하고, 파일을 replace를 통해서 적용하는 것이 파일 단위로 변경 내용을 추적할 수 있기 때문에 이 방법을 권장한다.

롤백

Deployment은 롤링 업데이트이외에, 롤백도 손쉽게 지원한다.

먼저 배포된 버전을 체크해보려면 kubectl rollout history deployment/[Deployment 이름]을 실행하면 기존에 배포된 버전을 확인할 수 있다. 디폴트로는 2개의 버전을 유지하게 되어 있다.



1버전으로 롤백을 하려면 kubectl rollout undo deployment [ deployment 명 ] --to-revision=[롤백할 버전명] 을 하면, 그 버전으로 롤백이 된다.

명령을 실행하면 아래와 같이 이전 버전으로 롤백이 되는 것을 확인할 수 있다.



롤백등을 위해서 Deployment를  RS를 여러 버전을 유지하고 있다.

유지하는 버전의 수는  Deployment에서 설정이 가능하다.


버전 (태그)

배포 이야기가 나왔기 때문에, 버전/태그에 대해서 언급하도록 하겠다.

쿠버네티스에서 사용되는 컨테이너 이미지 명은 gcr.io/terrycho-sandbox/deployment:v2 와 같이 [컨테이너 이미지명]:[태그] 의 조합으로 구성되는데, 태그는 일반적으로 버전을 사용한다.

주의할점은 컨테이너를 새로 빌드할때 마다 버전을 올리도록 해야, 나중에 구분이 가능하기 때문에 컨테이너를 빌드할때 마다 다른 태그를 사용하는 것을 습관화할 필요가 있다.

새롭게 빌드한 컨테이너 이미지를 같은 태그를 사용하는 것은 향후에 어떤 내용이 변경이 되었는지를 추적하기 어렵기 때문에 좋은 방법은 아니다.


“latest” 태그

쿠버네티스에서 사용되는 도커 컨테이너 이미지 중에 latest 라는 특별한 의미를 가지는 태그가 있는데, docker pull gcr.io/terrycho-sandbox/deployment 과 같은 명령으로 컨테이너 이미지에 태그명을 정의하지 않으면 디폴트로 latest라고 태그된 이미지를 가지고 오도록 되어 있다.


보통 최신 이미지에 이 latest 태그를 추가한다. (하나의 컨테이너는 두개 이상의 이미지를 가질 수 있다.)

이렇게 하면 latest 태그를 이용하여 항상 최신 이미지를 읽어올 수 있는 장점이 있을 수 있으나, 불러온 컨테이너 이미지의 버전을 정확히 알기가 어렵기 때문에 권장하는 방법이 아니다.


쿠버네티스에서 컨테이너 이미지를 가지고 올때는 가급적이면 latest 태그를 사용하는 것 보다는 명시적으로 배포하고자 하는 버전을 태그로 정의하는 것을 권장한다.





쿠버네티스 #2

개념 이해 (1/2)


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


쿠버네티스를 공부하면서 가장 헷갈리는 부분이 용어와 컨셉이다. 이 컨셉만 잘 이해하면 쿠버네티스를 쉽게 이해하고 사용할 수 있지만, 적어도 내 기준에서는 문서들의 용어나 개념 설명이 다소 어려웠다.

쿠버네티스의 개념은 크게 오브젝트 두개의 개념에서 출발한다. 각각을 살펴보도록 하자

마스터와 노드

쿠버네티스를 이해하기 위해서는 먼저 클러스터의 구조를 이해할 필요가 있는데, 구조는 매우 간단하다. 클러스터 전체를 관리하는 컨트롤러로써 마스터가 존재하고, 컨테이너가 배포되는 머신 (가상머신이나 물리적인 서버머신)인 노드가 존재한다.


오브젝트

쿠버네티스를 이해하기 위해서 가장 중요한 부분이 오브젝트이다. 가장 기본적인 구성단위가 되는 기본 오브젝트(Basic object)와, 이 기본 오브젝트(Basic object) 를 생성하고 관리하는 추가적인 기능을 가진 컨트롤러(Controller) 로 이루어진다. 그리고 이러한 오브젝트의 스펙(설정)이외에 추가정보인 메타 정보들로 구성이 된다고 보면 된다.

오브젝트 스펙 (Object Spec)

오브젝트들은 모두 오브젝트의 특성 (설정정보)을 기술한 오브젝트 스펙 (Object Spec)으로 정의가 되고, 커맨드 라인을 통해서 오브젝트 생성시 인자로 전달하여 정의를 하거나 또는 yaml이나 json 파일로 스펙을 정의할 수 있다.

기본 오브젝트 (Basic Object)

쿠버네티스에 의해서 배포 및 관리되는 가장 기본적인 오브젝트는 컨테이너화되어 배포되는 애플리케이션의 워크로드를 기술하는 오브젝트로 Pod,Service,Volume,Namespace 4가지가 있다.


간단하게 설명 하자면 Pod는 컨테이너화된 애플리케이션, Volume은 디스크, Service는 로드밸런서 그리고 Namespace는 패키지명 정도로 생각하면 된다. 그러면 각각을 자세하게 살펴보도록 하자.

Pod

Pod 는 쿠버네티스에서 가장 기본적인 배포 단위로, 컨테이너를 포함하는 단위이다.

쿠버네티스의 특징중의 하나는 컨테이너를 개별적으로 하나씩 배포하는 것이 아니라 Pod 라는 단위로 배포하는데, Pod는 하나 이상의 컨테이너를 포함한다.


아래는 간단한 Pod를 정의한 오브젝트 스펙이다. 하나하나 살펴보면


apiVersion: v1

kind: Pod

metadata:

 name: nginx

spec:

 containers:

 - name: nginx

   image: nginx:1.7.9

   ports:

   - containerPort: 8090


  • apiVersion은 이 스크립트를 실행하기 위한 쿠버네티스 API 버전이다 보통 v1을 사용한다.

  • kind 에는 리소스의 종류를 정의하는데, Pod를 정의하려고 하기 때문에, Pod라고 넣는다.

  • metadata에는 이 리소스의 각종 메타 데이타를 넣는데, 라벨(뒤에서 설명할)이나 리소스의 이름등 각종 메타데이타를 넣는다

  • spec 부분에 리소스에 대한 상세한 스펙을 정의한다.

    • Pod는 컨테이너를 가지고 있기 때문에, container 를 정의한다. 이름은 nginx로 하고 도커 이미지 nginx:1.7.9 를 사용하고, 컨테이너 포트 8090을 오픈한다.


Pod 안에 한개 이상의 컨테이너를 가지고 있을 수 있다고 했는데 왜 개별적으로 하나씩 컨테이너를 배포하지 않고 여러개의 컨테이너를 Pod 단위로 묶어서 배포하는 것인가?


Pod는 다음과 같이 매우 재미있는 특징을 갖는다.


  • Pod 내의 컨테이너는 IP와 Port를 공유한다.
    두 개의 컨테이너가 하나의 Pod를 통해서 배포되었을때, localhost를 통해서 통신이 가능하다.
    예를 들어 컨테이너 A가 8080, 컨테이너 B가 7001로 배포가 되었을 때, B에서 A를 호출할때는 localhost:8080 으로 호출하면 되고, 반대로 A에서 B를 호출할때에넌 localhost:7001로 호출하면 된다.

  • Pod 내에 배포된 컨테이너간에는 디스크 볼륨을 공유할 수 있다.
    근래 애플리케이션들은 실행할때 애플리케이션만 올라가는것이 아니라 Reverse proxy, 로그 수집기등 다양한 주변 솔루션이 같이 배포 되는 경우가 많고, 특히 로그 수집기의 경우에는 애플리케이션 로그 파일을 읽어서 수집한다. 애플리케이션 (Tomcat, node.js)와 로그 수집기를 다른 컨테이너로 배포할 경우, 일반적인 경우에는 컨테이너에 의해서 파일 시스템이 분리되기 때문에, 로그 수집기가 애플리케이션이 배포된 컨테이너의 로그파일을 읽는 것이 불가능 하지만, 쿠버네티스의 경우 하나의 Pod 내에서는 컨테이너들끼리 볼륨을 공유할 수 있기 때문에 다른 컨테이너의 파일을 읽어올 수 있다.


위와 같이 애플리케이션과 애플리케이션에서 사용하는 주변 프로그램을 같이 배포하는 패턴을 마이크로 서비스 아키텍쳐에서 사이드카 패턴(Side car pattern)이라고 하는데, 이 외에도 Ambassador, Adapter Container 등 다양한 패턴이 있는데, 이는 나중에 다른 글에서 상세하게 설명하도록 한다.

Volume

Pod가 기동할때 디폴트로, 컨테이너마다 로컬 디스크를 생성해서 기동되는데, 이 로컬 디스크의 경우에는 영구적이지 못하다. 즉 컨테이너가 리스타트 되거나 새로 배포될때 마다 로컬 디스크는 Pod 설정에 따라서 새롭게 정의되서 배포되기 때문에, 디스크에 기록된 내용이 유실된다.

데이타 베이스와 같이 영구적으로 파일을 저장해야 하는 경우에는 컨테이너 리스타트에 상관 없이 파일을 영속적으로 저장애햐 하는데, 이러한 형태의 스토리지를 볼륨이라고 한다.

볼륨은 컨테이너의 외장 디스크로 생각하면 된다. Pod가 기동할때 컨테이너에 마운트해서 사용한다.


앞에서 언급한것과 같이 쿠버네티스의 볼륨은 Pod내의 컨테이너간의 공유가 가능하다.


웹 서버를 배포하는 Pod가 있을때, 웹서비스를 서비스하는 Web server 컨테이너, 그리고 컨텐츠의 내용 (/htdocs)를 업데이트하고 관리하는 Content mgmt 컨테이너, 그리고 로그 메세지를 관리하는 Logger라는 컨테이너이가 있다고 하자

  • WebServer 컨테이너는 htdocs 디렉토리의 컨테이너를 서비스하고, /logs 디렉토리에 웹 억세스 기록을 기록한다.

  • Content 컨테이너는 htdocs 디렉토리의 컨텐트를 업데이트하고 관리한다.

  • Logger 컨테이너는 logs 디렉토리의 로그를 수집한다.

이 경우 htdocs 컨텐츠 디렉토리는 WebServer와 Content 컨테이너가 공유해야 하고 logs 디렉토리는 Webserver 와 Logger 컨테이너가 공유해야 한다. 이러한 시나리오에서 볼륨을 사용할 수 있다.


아래와 같이 htdocs와 logs 볼륨을 각각 생성한 후에, htdocs는 WebServer와, Contents management 컨테이너에 마운트 해서 공유하고, logs볼륨은 Logger와 WebServer 컨테이너에서 공유하도록 하면된다.  



쿠버네티스는 다양한 외장 디스크를 추상화된 형태로 제공한다. iSCSI나 NFS와 같은 온프렘 기반의 일반적인 외장 스토리지 이외에도, 클라우드의 외장 스토리지인 AWS EBS, Google PD,에서 부터  github, glusterfs와 같은 다양한 오픈소스 기반의 외장 스토리지나 스토리지 서비스를 지원하여, 스토리지 아키텍처 설계에 다양한 옵션을 제공한다.

Service

Pod와 볼륨을 이용하여, 컨테이너들을 정의한 후에, Pod 를 서비스로 제공할때, 일반적인 분산환경에서는 하나의 Pod로 서비스 하는 경우는 드물고, 여러개의 Pod를 서비스하면서, 이를 로드밸런서를 이용해서 하나의 IP와 포트로 묶어서 서비스를 제공한다.


Pod의 경우에는 동적으로 생성이 되고, 장애가 생기면 자동으로 리스타트 되면서 그 IP가 바뀌기 때문에, 로드밸런서에서 Pod의 목록을 지정할 때는 IP주소를 이용하는 것은 어렵다. 또한 오토 스케일링으로 인하여 Pod 가 동적으로 추가 또는 삭제되기 때문에, 이렇게 추가/삭제된 Pod 목록을 로드밸런서가 유연하게 선택해 줘야 한다.

그래서 사용하는 것이 라벨(label)과 라벨 셀렉터(label selector) 라는 개념이다.


서비스를 정의할때, 어떤 Pod를 서비스로 묶을 것인지를 정의하는데, 이를 라벨 셀렉터라고 한다. 각 Pod를 생성할때 메타데이타 정보 부분에 라벨을 정의할 수 있다. 서비스는 라벨 셀렉터에서 특정 라벨을 가지고 있는 Pod만 선택하여 서비스에 묶게 된다.

아래 그림은 서비스가 라벨이 “myapp”인 서비스만 골라내서 서비스에 넣고, 그 Pod간에만 로드밸런싱을 통하여 외부로 서비스를 제공하는 형태이다.



이를 스펙으로 정의해보면 대략 다음과 같다.


kind: Service
apiVersion: v1
metadata:
 name: my-service
spec:
 selector:
   app: myapp
 ports:
 - protocol: TCP
   port: 80
   targetPort: 9376


  • 리소스 종류가 Service 이기 때문에, kind는 Service로 지정하고,

  • 스크립트를 실행할 api 버전은 v1으로 apiVersion에 정의했다.

  • 메타데이타에 서비스의 이름을 my-service로 지정하고

  • spec 부분에 서비스에 대한 스펙을 정의한다.

    • selector에서 라벨이 app:myapp인 Pod 만을 선택해서 서비스에서 서비스를 제공하게 하고

    • 포트는 TCP를 이용하되, 서비스는 80 포트로 서비스를 하되, 서비스의 80 포트의 요청을 컨테이너의 9376 포트로 연결해서 서비스를 제공한다.


Name space

네임스페이스는 한 쿠버네티스 클러스터내의 논리적인 분리단위라고 보면 된다.

Pod,Service 등은 네임 스페이스 별로 생성이나 관리가 될 수 있고, 사용자의 권한 역시 이 네임 스페이스 별로 나눠서 부여할 수 있다.

즉 하나의 클러스터 내에, 개발/운영/테스트 환경이 있을때, 클러스터를 개발/운영/테스트 3개의 네임 스페이스로 나눠서 운영할 수 있다. 네임스페이스로 할 수 있는 것은

  • 사용자별로 네임스페이스별 접근 권한을 다르게 운영할 수 있다.

  • 네임스페이스별로 리소스의 쿼타 (할당량)을 지정할 수 있다. 개발계에는 CPU 100, 운영계에는 CPU 400과 GPU 100개 식으로, 사용 가능한 리소스의 수를 지정할 수 있다.

  • 네임 스페이스별로 리소스를 나눠서 관리할 수 있다. (Pod, Service 등)


주의할점은 네임 스페이스는 논리적인 분리 단위이지 물리적이나 기타 장치를 통해서 환경을 분리(Isolation)한것이 아니다. 다른 네임 스페이스간의 pod 라도 통신은 가능하다.

물론 네트워크 정책을 이용하여, 네임 스페이스간의 통신을 막을 수 있지만 높은 수준의 분리 정책을 원하는 경우에는 쿠버네티스 클러스터 자체를 분리하는 것을 권장한다.


참고 자료 네임 스페이스에 대한 베스트 프랙틱스 : https://cloudplatform.googleblog.com/2018/04/Kubernetes-best-practices-Organizing-with-Namespaces.html

https://kubernetes.io/blog/2016/08/kubernetes-namespaces-use-cases-insights/

라벨

앞에서 잠깐 언급했던 것 중의 하나가 label 인데, 라벨은 쿠버네티스의 리소스를 선택하는데 사용이 된다. 각 리소스는 라벨을 가질 수 있고, 라벨 검색 조건에 따라서 특정 라벨을 가지고 있는 리소스만을 선택할 수 있다.

이렇게 라벨을 선택하여 특정 리소스만 배포하거나 업데이트할 수 있고 또는 라벨로 선택된 리소스만 Service에 연결하거나 특정 라벨로 선택된 리소스에만 네트워크 접근 권한을 부여하는 등의 행위를 할 수 있다.

라벨은 metadata 섹션에 키/값 쌍으로 정의가 가능하며, 하나의 리소스에는 하나의 라벨이 아니라 여러 라벨을 동시에 적용할 수 있다.


"metadata": {
 "labels": {
   "key1" : "value1",
   "key2" : "value2"
 }
}


셀렉터를 사용하는 방법은 오브젝트 스펙에서 selector 라고 정의하고 라벨 조건을 적어 놓으면 된다.

쿠버네티스에서는 두 가지 셀렉터를 제공하는데, 기본적으로 Equaility based selector와, Set based selector 가 있다.

Equality based selector는 같냐, 다르냐와 같은 조건을 이용하여, 리소스를 선택하는 방법으로

  • environment = dev

  • tier != frontend

식으로, 등가 조건에 따라서 리소스를 선택한다.

이보다 향상된 셀렉터는 set based selector로, 집합의 개념을 사용한다.

  • environment in (production,qa) 는 environment가 production 또는 qa 인 경우이고,

  • tier notin (frontend,backend)는 environment가 frontend도 아니고 backend도 아닌 리소스를 선택하는 방법이다.

다음 예제는 my-service 라는 이름의 서비스를 정의한것으로 셀렉터에서 app: myapp 정의해서 Pod의 라벨 app이 myapp 것만 골라서 이 서비스에 바인딩해서 9376 포트로 서비스 하는 예제이다.


kind: Service
apiVersion: v1
metadata:
 name: my-service
spec:
 selector:
   app: myapp
 ports:
 - protocol: TCP
   port: 80
   targetPort: 9376



컨트롤러

앞에서 소개한 4개의 기본 오브젝트로, 애플리케이션을 설정하고 배포하는 것이 가능한데 이를 조금 더 편리하게 관리하기 위해서 쿠버네티스는 컨트롤러라는 개념을 사용한다.

컨트롤러는 기본 오브젝트들을 생성하고 이를 관리하는 역할을 해준다. 컨트롤러는 Replication Controller (aka RC), Replication Set, DaemonSet, Job, StatefulSet, Deployment 들이 있다. 각자의 개념에 대해서 살펴보도록 하자.

Replication Controller

Replication Controller는  Pod를 관리해주는 역할을 하는데, 지정된 숫자로 Pod를 기동 시키고, 관리하는 역할을 한다.

Replication Controller (이하 RC)는 크게 3가지 파트로 구성되는데, Replica의 수, Pod Selector, Pod Template 3가지로 구성된다.

  • Selector : 먼저 Pod selector는 라벨을 기반으로 하여,  RC가 관리한 Pod를 가지고 오는데 사용한다.

  • Replica 수 :  RC에 의해서 관리되는 Pod의 수인데, 그 숫자만큼 Pod 의 수를 유지하도록 한다.예를 들어 replica 수가 3이면, 3개의 Pod만 띄우도록 하고, 이보다 Pod가 모자르면 새로운 Pod를 띄우고, 이보다 숫자가 많으면 남는 Pod를 삭제한다.

  • Pod를 추가로 기동할 때 그러면 어떻게 Pod를 만들지 Pod에 대한 정보 (도커 이미지, 포트,라벨등)에 대한 정보가 필요한데, 이는 Pod template이라는 부분에 정의 한다.




주의할점은 이미 돌고 있는 Pod가 있는 상태에서 RC 리소스를 생성하면 그 Pod의 라벨이 RC의 라벨과 일치하면 새롭게 생성된 RC의 컨트롤을 받는다. 만약 해당 Pod들이 RC에서 정의한 replica 수 보다 많으면, replica 수에 맞게 추가분의 pod를 삭제하고, 모자르면 template에 정의된 Pod 정보에 따라서 새로운 Pod를 생성하는데, 기존에 생성되어 있는 Pod가 template에 정의된 스펙과 다를지라도 그 Pod를 삭제하지 않는다. 예를 들어 기존에 아파치 웹서버로 기동중인 Pod가 있고, RC의 template은 nginx로 Pod를 실행하게 되어 있다하더라도 기존에 돌고 있는 아파치 웹서버 기반의 Pod를 삭제하지 않는다.


아래 예를 보자.


이 예제는 ngnix라는 이름의 RC를 정의한 것으로, label이 “app:ngnix”인 Pod들을 관리하고 3개의 Pod가 항상 운영되도록 설정한다.

Pod는 app:ngix 라는 라벨을 가지면서 이름이 ngnix이고 nginx 이미지를 사용해서 생성하고 컨테이너의 포트는 80 번 포트를 이용해서 서비스를 제공한다.

ReplicaSet

ReplicaSet은 Replication Controller 의 새버전으로 생각하면 된다.

큰 차이는 없고 Replication Controller 는 Equality 기반 Selector를 이용하는데 반해, Replica Set은 Set 기반의 Selector를 이용한다.

Deployment

Deployment (이하 디플로이먼트) Replication controller와 Replica Set의 좀더 상위 추상화 개념이다. 실제 운영에서는 ReplicaSet 이나 Replication Controller를 바로 사용하는 것보다, 좀 더 추상화된 Deployment를 사용하게 된다.

쿠버네티스 배포에 대한 이해

쿠버네티스의 Deployment 리소스를 이해하기 위해서는 쿠버네티스에서 Deployment 없이 어떻게 배포를 하는지에 대해서 이해를 하면 Deployment 를 이해할 수 있다.


다음과 같은 Pod와 RC가 있다고 하자


애플리케이션이 업데이트되서 새로운 버전으로 컨테이너를 굽고 이 컨테이너를 배포하는 시나리오에 대해서 알아보자. 여러가지 배포 전략이 있겠지만, 많이 사용하는 블루/그린 배포와 롤링 업데이트 방식 두가지 방법에 대해서 설명한다.

블루/그린 배포

블루/그린 배포 방식은 블루(예전)버전으로 서비스 하고 있던 시스템을 그린(새로운)버전을 배포한 후, 트래픽을 블루에서 그린으로 한번에 돌리는 방식이다.

여러가지 방법이 있지만 가장 손쉬운 방법으로는 새로운 RC을 만들어서 새로운 템플릿으로 Pod를 생성한 후에, Pod 생성이 끝나면, 서비스를 새로운 Pod로 옮기는 방식이다.


후에, 배포가 완료되고 문제가 없으면 예전 버전의 RC 와 Pod를 지워준다.

롤링 업그레이드

롤링 업그레이드 방식은 Pod를 하나씩 업그레이드 해가는 방식이다.

이렇게 배포를 하려면 먼저 새로운 RC를 만든후에, 기존 RC에서 replica 수를 하나 줄이고, 새로운 RC에는 replica 수를 하나만 준다.


라벨을 같은 이름으로 해주면 서비스는 자연히 새로운 RC에 의해 생성된 Pod를 서비스에 포함 시킨다.

다음으로 기존 RC의 replica를 하나 더 줄이고, 새로운 RC의  replica를 하나 더 늘린다.


그러면 기존 버전의 Pod가 하나더 서비스에서 빠지게 되고 새로운 버전의 Pod가 서비스에 추가된다.

마찬가지 작업을 반복하게 되면, 아래 그림과 같이 예전 버전의 Pod가 모두 빠지고 새 버전의 Pod만 서비스 되게 된다.


만약에 배포가 잘못되었을 경우에는 기존 RC의 replica 수를 원래대로 올리고, 새버전의 replicat 수를 0으로 만들어서 예전 버전의 Pod로 롤백이 가능하다.

이 과정은 kubectl rolling-update라는 명령으로 RC 단위로 컨트롤이 가능하지만, 그래도 여전히 작업이 필요하고, 배포 과정을 모니터링 해야 한다. 그리고 가장 문제는 kubectl rolling-update 명령은 클라이언트에서 실행 하는 명령으로, 명령어 실행중에 클라이언트의 연결이 끊어 지면 배포작업이 비정상적으로 끊어질 수 있는 문제가 있다.

그리고 마지막으로, 롤백과정 역시 수동 컨트롤이 필요할 수 있다.

그래서 이러한 과정을 자동화하고 추상화한 개념을 Deployment라고 보면 된다.

Deployment는 Pod 배포를 위해서 RC를 생성하고 관리하는 역할을 하며, 특히 롤백을 위한 기존 버전의 RC 관리등 여러가지 기능을 포괄적으로 포함하고 있다.



Deployment 에 대해서는 뒤에 다른 글에서 조금 더 자세하게 설명하도록 한다.


이글에서는 쿠버네티스를 이루는 기본적인 오브젝트와 이를 생성 제어하기 위한 기본적인 컨트롤러에 대해서 알아보았다.

다음 글에서는 조금 더 발전된 형태의 컨트롤러에 대해서 알아보기로 한다.




Spinnaker #3

Hello Spinnaker

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


Spinnaker에 대한 개념 이해 및 설치가 끝났으면, 이제 간단한 애플리케이션을 배포해보자.

여기서 사용하는 애플리케이션은 node.js로 8080 포트에 “This is Default” 라는 메세지를 출력하는 간단한 애플리케이션이다. VM이 기동되면 자동으로 이 node.js 서버가 기동되도록 설정을 해놓은 VM이미지를 만들어놓았다. 만약에 같은 테스트를 하고자 한다면 간단한 애프리케이션을 만들어도 좋고, nginx나 apache 웹서버를 설치해놓은 이미지를 사용해도 좋다.

Create Application

먼저 node.js 클러스터를 배포할 애플리케이션을 정의한다. 아래 처럼 메뉴에서 애플리케이션을 선택한 후에, 우측 상단의 Action 메뉴에서 Create Appliaction 메뉴를 선택한다.



다음 애플리케이션 정보에 애플리케이션명을 “hellospinnaker”로 입력하고, 관리자 이메일을 입력한다.





Load Balancer 생성

애플리케이션이 생성되었으면, 애플리케이션에서 사용할 로드밸런서를 사용한다. 구글 클라우드에는 여러 타입의 로드 밸런서가 있지만, 설정이 쉬운 Network 로드 밸런서를 사용하겠다.

Network Load Balancer는 TCP/UDP를 지원하는 Pass through (IP가 바뀌지 않는다.) 방식의 L4 로드 밸런서로, 구글의 망가속 기능을 사용하지 않는 리전 단위의 로드 밸런서이다.



로드 밸런서 타입을 선택했으면 상세 정보를 입력한다.

  • region을 선택한다. 여기서는 일본 리전인 asia-northeast1을 선택하였다.

  • 다음 로드밸런서의 포트를 선택해야 하는데, Listener 부분에서 TCP 프로토콜을 선택하고, 입력 포트를 8080으로 선택한다.

  • 그리고 마지막으로 중요한것은 Health Check 부분을 명시해야 하는데, Health check는 HTTP를 사용하게 된다. HTTP/GET request를 이용하여 Health check를 할 서버의 HTTP URL과 Port를 지정해야 한다. node.js 서버가 8080 포트를 통해 서비스 하기 때문에 Health Check도 8080 포트에 “/” 디렉토리로 지정한다.





Server 생성

로드 밸런서 설정이 끝났으면 여기에 붙일 서버 그룹을 정의해야 한다. 서버그룹 정의는 Clusters 메뉴에서 가능한데, 먼저 Clusters 메뉴로 들어간후, 우측 상단의 Create Server Group 버튼을 클릭하여, 서버 그룹 생성 화면을 불러온다.





서버 그룹에 정보에서는 아래 그림과 같이 region을 선택하고, VM을 생성할때 사용할 Image를 선택한다. 이 예제에서는 앞서 설명한것 처럼 node.js 애플리케이션을 “simple-node-server-default-image”라는 이미지로 준비해놓았다.

다음 Load Balancers 메뉴에서 로드 밸런서를 선택한다. 로드 밸런서는 앞 단계에서 만든 “hellospinnaker” 를 선택한다.





다음으로는 인스턴스 타입을 선택한다. 인스턴스 타입은 먼저 Zone 을 선택해야 선택할 수 있다. Zone은 두개의 존 이상에 걸치도록 설정하기 위해서 “Distribute instance multiple zones” 체크 박스를 클릭하면 선택한 리전에서 두개 이상의 존에 걸쳐서 인스턴스가 생성된다.

그리고 인스턴스 타입을 선택한다. 아래에서는 n1-standard-2 인스턴스를 선택하였다.

마지막으로 Number of instances에 기동시킬 인스턴스 수를 지정한다. 여기서는 4개의 인스턴스를 기동하도록 하였다.




서버 기동 확인

모든 설정이 끝났으면, 인스턴스가 기동되는 것을 확인할 수 있다. 아래 그림과 같이 인스턴스가 정상적으로 올라오면 초록색으로 표시가 된다. 만약 문제가 있어서 인스턴스가 올라오지 않으면 붉은 색으로 표시된다. (대부분 실패 하는 경우는 HeartBeat 설정이 제대로 되어 있지 않는 경우가 많다.)




실제로 구글 클라우드 콘솔의  Compute Engine탭을 확인해 보면 아래와 같이 VM들이 생성 된것을 확인할 수 있다. VM이름은 hellospinnaker-vxxx 라는 이름으로 생성이 되는것을 확인할 수 있다.



테스트

그러면 제대로 작동을 하는지 확인해보자. 로드밸런서의 IP를 확인해야 하는데,  생성된 로드밸런서를 클릭하면 로드밸런서의 IP가 아래 그림과 같이 우측에 나타난다.



이 IP로, HTTP 8080 포트로 접속을 해보면 아래 그림과 같이 접속이 되는 것을 확인할 수 있다.



지금까지 Spinnaker에 대한 제일 간단한 사용방법을 알아보았다.

실제 운영 환경에서는 이런식으로 사용하는 경우는 드물고, github등의 코드 Repository에서 코드가 변경되면 이를 Jenkins 등을 이용하여 빌드하고, 패키징 한 후에, VM등에 배포하는 파이프라인을 거치게 된다.

다음 글에서는 이러한 파이프라인을 하나 만들어 보도록 하겠다.



Spinnaker #1 - 소개


Spinnaker

Spinnaker 는 넷플릭스에서 개발하여 오픈 소스화한 멀티 클라우드를 지원하는 Continuous Delivery Platform 이다. 구글 클라우드, 아마존, 마이크로소프트등 대부분의 메이져 클라우드를 지원하며, Kubernetes 나, OpenStack 과 같은 오픈소스 기반의 클라우드 또는 컨테이너 플랫폼을 동시에 지원한다.

시나리오

Spinnaker 의 특징은 멀티 클라우드 지원성뿐만 아니라, 오케스트레이션 파이프라인 구조를 지원한다 특징인데,  배포 단계는 여러개의 스텝이 복합적으로 수행되는 단계이기 때문에, 복잡한 워크 플로우에 대한


관리가 필요하다.

하나의 배포 시나리오를 통해서 오케스트레이션 파이프라인에 대해서 이해해보도록 하자

  • 코드를 받아서 빌드를 하고,

  • 빌드된 코드를 VM에 배포하여 이미지로 만든 후에, 해당 이미지를 테스트한다.

  • 테스트가 끝나면, Red/Black 배포를 위해서 새버전이 배포된 클러스터를 생성한 후에

  • 새 클러스터에 대한 테스트를 끝내고

  • 새 클러스터가 문제가 없으면 트래픽을 새 클러스터로 라우팅한다.

  • 다음으로는 구버전 클러스터를 없앤다.

각 단계에서 다음 단계로 넘어가기 위해서는 선행 조건이 필요하다. 예를 들어 이미지가 빌드가 제대로 되었는지 안되었는지, 새 클러스터가 제대로 배포가 되었는지 안되었는지에 대한 선/후행 조건의 확인 들이 필요하다.

Spinnaker에서는 이러한 오케스트레이션 파이프라인을 “파이프라인”이라는 개념으로 구현하였다. 파이프라인 흐름에 대한 예를 보면 다음과 같다.


위의 파이프라인은 이미지를 찾아서 Red/Black 배포를 위해서 Production에 새로운 이미지를 배포하고, Smoke 테스트를 진행한 후에, 구 버전을 Scale down 시키고, 소스를 태깅 한다. 이때 구 버전을 Destory 하기 전에, Manual Approval (사람이 메뉴얼로 승인) 을 받고 Destory 하는 흐름으로 되어 있다.


또한  각 단계별로 하위 테스크가 있는 경우가 있다. 예를 들어 새로운 클러스터를 배포하기 위해서는 클라우드 내에 클러스터 그룹을 만들고, 그 안에 VM들을 배포한 후에, VM 배포가 완료되면 앞에 로드 밸런서를 붙이고, Health check를 설정해야 한다. 그리고 설정이 제대로 되었는지 체크를 한다음에 다음 단계로 넘어간다.


이러한 개념을 Spinnaker에서는 Stage / Steps/ Tasks/ Operation 이라는 개념으로 하위 태스크를 구현하였다. 개념을 보면 다음과 같다.



파이프라인 컴포넌트

파이프라인은 워크 플로우 형태로 구성이 가능하다. 아래 그림은 파이프라인을 정의하는 화면의 예시이다.


<그림. 파이프라인 예제>

출처 http://www.tothenew.com/blog/introduction-to-spinnaker-global-continuous-delivery/


파이프라인에서 스테이지별로 수행할 수 있는 테스크를 선택할 수 있다.  샘플로 몇가지 스테이지를 보면 다음과 같다.

  • Bake : VM 이미지를 생성한다.

  • Deploy : VM 이미지 (또는 컨테이너)를 클러스터에 배포한다.

  • Check Preconditions : 다음 단계로 넘어가기전에 조건을 체크한다. 클러스터의 사이즈 (EX. 얼마나 많은 VM이 생성되서 준비가 되었는지)

  • Jenkins : Jenkins Job 을 실행한다.

  • Manual Judgement : 사용자로 부터 입력을 받아서 파이프라인 실행 여부를 결정한다

  • Enable/Disable Server Group : 이미 생성된 Server Group을 Enable 또는  Disable 시킨다

  • Pipeline : 다른 파이프라인을 수행한다.

  • WebHook : HTTP 로 다른 시스템을 호출한다. 통상적으로 HTTP REST API를 호출하는 형


개념 구조


Spinnaker는 리소스를 관리하기 위해서, 리소스에 대한 계층구조를 정의하고 있다.



<그림. Spinnaker의 자료 구조 >

출처 : ttp://www.tothenew.com/blog/introduction-to-spinnaker-global-continuous-delivery/



가장 최상위에는 Project, 다음은 Application 을 가지고 있고, Application 마다 Cluster Service를 가지고 있고, 각 Cluster Service는 Server Group으로 구성된다. 하나하나 개념을 보자면,


Server Group 은, 동일한 서버(같은 VM과 애플리케이션)로 이루어진 서버군이다. Apache 웹서버 그룹이나 이미지 업로드 서버 그룹식으로 그룹을 잡을 수 도 있고, 이미지 서버 그룹 Version 1, 이미지 서버 그룹 Version 2 등으로 버전별로 잡는등 유연하게 서버군집의 구조를 정의할 수 있다.

이러한 서버 그룹은 Cluster 라는 단위로 묶일 수 있다.


아래 예제 그림을 통해서 개념을 좀더 상세하게 살펴보자


위의 그림은 이미지 서비스(Image service)를 제공하는 서비스를 Cluster로 정의한것이다.

위의 구조는 Image Service를 Service Group으로 정의했는데, v1,v2,v3 버전을 가지고 있고 각 버전이 Service Group으로 정의된다 (이런 이유는 멀티 버전을 이용한 카날리 테스트나 Red/Black 배포를 이용하기 위해서 여러 버전을 함께 운용하는 경우가 생긴다.)

그리고, 리전별로 별도의 Image Service를 각각 배포하는 모델이다.

리전과 멀티 클라우드의 개념은 Spinnaker 문서에 나온 자료 구조 이외에, 중요한 자료 구조인데, 리소스를 정의할때 클라우드 계정을 선택함으로써 클라우드를 선택할 수 있고, 서비스의 종류에 따라 리전을 선택하는 경우가 있는데 이 경우 리전별로 리소스를 분류해서 보여준다.


Cluster는 Application 내에서 생성될때 , Service Group을 생성시 입력하는  {Account}-{stack}-{Detail} 을 식별자로하여 Cluster를 식별한다. 같은 식별자를 가진 Service Group을 하나의 Cluster로 묶는다.

아래는 Service Group을 생성하는 화면으로 Account, Stack, Detail을 입력하는 메뉴가 있는 것을 확인할 수 있다.



아래 그림은 myapplication 이라는 이름을 갖는 Application 내에, 각각 MY-GOOGLE-ACCOUNT라는 account를 이용하여, myapplication-nodestack-cluster1과, myapplication-nodestack-cluster2 두개의 클러스터를 생성한 예제이다.





또는 자주 쓰는 구성 방식중 하나는 Red/Black (또는 Blue/Green  이라고도 함) 형태를 위해서 하나의 클러스터에 구버전과 새버전 서버 그룹을 각각 정의해놓고 구성하는 방법이 있다.


Application은 Cluster의 집합이고, Project는 Application의 집합이다.

개발하고 배포하고자 하는 시스템의 구조에 따라서 Project, Application, Cluster를 어떻게 정의할지를 고민하는 것이 중요하다.


예를 들어 하나의 서비스가 여러개의 애플리케이션으로 구성되어 있는 경우, 예를 들어 페이스북 처럼, 페이스북 앱, 웹 그리고 앱 기반 페북 메신져가 있는 경우에는 페이스북이라는 프로젝트 아래, 페이스북 앱 백앤드, 웹 백앤드, 앱 백앤드로 Application을 정의할 수 있고,각각의 Application에는 마이크로 서비스 아키텍쳐 (MSA) 방식으로 각각서 서비스를 Cluster로 정의할 수 있다.

아키텍쳐

마지막으로 Spinnaker의 내부 아키텍쳐를 살펴보도록 하자.

Spinnaker는 MSA (마이크로 서비스 아키텍쳐) 구조로 구성이 되어 있으며, 아래 그림과 같이 약 9 개의 컴포넌트로 구성이 되어 있다.



각 컴포넌트에 대해서 알아보도록 하자


  • Deck : Deck 컴포넌트는 UI 컴포넌트로, Spinnaker의 UI 웹사이트 컴포넌트이다.

  • Gate : Spinnaker는 MSA 구조로, 모든 기능을 API 로 Expose 한다, Gate는 API Gateway로, Spinnaker의 기능을 API로 Expose 하는 역할을 한다.

  • Igor : Spinnaker는 Jenkins CI 툴과 연동이 되는데, Jenkins에서 작업이 끝나면, Spinnaker Pipeline을 Invoke 하는데, 이를 위해서 Jenkins의 작업 상태를 Polling을 통해서 체크한다. Jenkins의 작업을 Polling으로 체크 하는 컴포넌트가 Igor이다.

  • Echo : 외부 통신을 위한 Event Bus로, 장애가 발생하거나 특정 이벤트가 발생했을때, SMS, Email 등으로 notification을 보내기 위한 Connector라고 생각하면 된다

  • Rosco : Rosco는 Bakering 컴포넌트로, Spinnaker는 VM또는 Docker 이미지 형태로 배포하는 구조를 지원하는데, 이를 위해서 VM이나 도커 이미지를 베이커링(굽는) 단계가 필요하다. Spinnaker는 Packer를 기반으로 하여 VM이나 도커 이미지를 베이커링 할 수 있는 기능을 가지고 있으며, Rosco가 이 기능을 담당 한다.

  • Rush : Rush는 Spinnaker에서 사용되는 스크립트를 실행하는 스크립트 엔진이다.

  • Front50 : Front 50은 파이프라인이나 기타 메타 정보를 저장하는 스토리지 컴포넌트이다.

  • Orca : Oraca는 이 모든 컴포넌트를 오케스트레이션하여, 파이프라인을 관리해주는 역할을 한다.

  • CloudDriver : 마지막으로 Cloud Driver는 여러 클라우드 플랫폼에 명령을 내리기 위한 아답터 역할을 한다.




CI/CD 레퍼런스 아키텍쳐


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


Continuous Deployment를  구현하기 위해서는 여러가지 프레임웍을 조합할 수 있다. 배포를 위한 Chef,Puppet과 같은 Configuration management tools, 그리고 네트워크, VM등을 코드로 설정하기 위한 Terraform 과 같은 Infrastructure as a code, VM 이미지를 만들기 위한 Packer 등 다양한 솔루션 조합이 가능한데, 이 글에서는 이러한 솔루션을 조합하여 어떻게 Continuous Deployment 파이프라인을 구현할 수 있는지에 대해서 설명하고, 구체적인 솔루션 제안을 통하여 레퍼런스 아키텍쳐를 제안하고자 한다.

1. Terraform + Ansible 기반의 Continuous Delivery

가장 기본적인 조합으로는 Terraform 을 이용해서 코드로 정의된 설정을 이용하여 인프라를 설정한 후에,

VM에, Ansible을 이용하여 애플리케이션 서버등의 소프트웨어를 설치한 후,  애플리케이션 코드를 배포하는 방식이다.

아래 그림은 Terraform으로 먼저 VM 인스턴스 그룹을 만든 후에, Load Balancer에 연결하고, CloudSQL (DB)인스턴스를 배포하는 구조이다.




이후에, 각 VM에 대한 설치는 Ansible을 이용하는 구조이다 Ansible은 Jenkins와 같은 CD 툴에 의해서 코드 변경등이 있으면 호출되서 자동화 될 수 있다.


이러한 구조는 전통적인 Continuous Delivery 기반의 애플리케이션 배포 자동화 구조이다.


2. Packer를 추가한 Foundation Image 사용방식

앞의 구조에서 VM은 애플리케이션 서버를 코드 배포 단계에서 배포할 수 도 있지만 애플리케이션 코드 이외에는 변경이 없기 때문에, Terraform으로 인프라를 배포할때, Packer와 Ansible을 이용하여, 애플리케이션이 설치되어 있는 이미지를 만들어놓고, 이를 이용해서 배포할 수 있다. (이미지를 만드는 과정을 베이킹 = 굽는다. 라고 한다.)

아래 그림을 보면, Terraform에서, Packer를 호출하고, Packer가 VM 이미지를 만드는데, 이 과정에서 Ansible을 이용하여, 애플리케이션 서버를 설치하도록 설정하는 구조를 가지고 있다.



위의 구조에서는 node.js server 애플리케이션 서버를 사용했지만, 실제 인프라를 구축할때는 redis나 웹서버등 다양한 애플리케이션의 설치가 필요하기 때문에, 이 구조를 사용하면 전체 인프라 구축을 코드로 정의하여 자동화를 할 수 있다.

3. Spinnaker를 이용한 Continuous Deployment 구조

코드만 배포하고 업데이트 할 경우, 서버의 패치 적용등의 자동화가 어렵기 때문에, 매번 배포시 마다, VM 설정에서 부터 OS 설치와 패치 그리고 애플리케이션 설치와 코드 배포까지 일원화하여 VM 단위로 배포할 수 있는데, 이를 Continuous Deployment 라고 한다.


솔루션 구성은 2번의 구조와 유사하나, Terraform으로는 VM과 로드밸런서를 제외한 다른 인프라를 설정하고 Spinnaker를 이용하여, 로드밸런서와 VM을 이용한 배포를 실행한다.


Spinnaker로 배포할 수 있는 범위는 방화벽, 로드밸런서, VM 과 같이 워크로드를 받는 부분인데, Spinnaker는 Packer와 Ansible과 협업하여, VM에 모든 스택을 설치하고, 이를 VM 단위로 배포할 수 있도록 해준다. 복잡한 네트워크 설정이나, CloudSQL과 같은 클라우드 전용 서비스는 Spinnaker로 설정이 불가능하기 때문에, 먼저 Terraform으로 기본 인프라를 설정하고, VM관련된 부분만을 Spinnaker를 사용한다.

이렇게 VM전체를 배포하는 전략을 피닉스 서버 아키텍쳐라고 한다. 피닉스 서버 패턴은 http://bcho.tistory.com/1224?category=502863 글을 참고하기 바란다.


Spinnaker를 이용한 배포 전략

Spinnker를 이용하면, VM 기반의 배포뿐 아니라, 다양한 배포 전략을 수행할 있다.



그림 https://sdtimes.com/cloud/google-open-source-platform-spinnaker-1-0/


Blue/Green deployment

블루 그린 배포 전략은 새버전의 서버그룹을 모두 배포 완료한 후에, 로드밸런서에서 트래픽을 구버전에서 새버전으로 일시에 바꾸는 방식이다.

Rolling deployment

롤링 배포는, 새버전의 서버를 만들어가면서 트래픽을 구버전 서버에서 새버전으로 점차적으로 옮겨가는 방식이다. 예를 들어 구서버가 10대가 있을때, 새 서버 1대가 배포되면, 구서버 9대와 새서버 1대로 부하를 옮기고, 새서버 2대가 배포되면 구서버:새서버에 8:2 비율로 부하를 주면서 7:3,6:4,5:5,.... 이런식으로 부하를 옮겨가며 전체 부하를 새 서버로 옮기는 방식이다.


블루 그린 배포 전략은 서버 대수의 2배수의 서버가 필요한 반면, 롤링 배포 방식은 같은 서버의 수 (위의 예의 경우 10대만 있으면 됨)를 가지고 배포를 할 수 있기 때문에 서버 자원이 한정되어 있는 경우에 유리하게 사용할 수 있다.

Canary deployment

카날리 배포를 설명하기 전에 카날리 테스트에 대한 용어를 이해할 필요가 있다.

카날리 테스트는 옛날에 광부들이 광산에서 유독가스가 나오는 것을 알아내기 위해서 가스에 민감한 카나리아를 광산안에서 키웠다고 한다. 카나리아가 죽으면 유독가스가 나온것으로 판단하고 조치를 취했다고 하는데, 이 개념을 개발에서 사용하는것이 카날리 테스트 방식이다.

예를 들어 사용자가 1000명이 접속해 있을때, 일부 사용자에게만 새 버전을 사용하도록 하고, 문제가 없으면 전체 사용자가 새 버전을 사용하도록 하는 방식인데, 안드로이드 앱 배포의 경우에도 10%의 사용자에게만 새 버전을 배포해보고 문제가 없으면 100%에 배포하는 것과 같은 시나리오로 사용된다.


이 개념을 배포에 적용한것이 카날리 배포 방식인데, 일부 서버에만 새 버전을 배포하여 운영한 후에, 문제가 없는 것이 확인되면 전체 서버에 새 버전을 배포하는 방식이다.

Docker를 이용한 배포 효율화

이러한 VM 기반의 Continuous deployment 구조는 피닉스 서버 패턴을 기반으로 하여, 모든 업데이트 추적이 가능하다는 장점을 가지고 있지만, 매번 VM을 베이킹해야 하기 때문에 시간이 많이 걸리고, VM 이미지는 사이즈가 커서 스토리지를 많이 사용한다는 단점이 있다.

이러한 배포 구조와 잘 맞는 것이 Docker (Docker 개념 http://bcho.tistory.com/805 ) 인데, Docker는 컨테이너 기반으로 경량화가 되어 있기 때문에, 이미지 베이킹 시간이 상대적으로 짧고, 이미지 사이즈가 작아서 저장이 용이하며, 이미지를 저장하기 위한 리파지토리와 같은 개념이 잘되어 있다.


Spinnaker의 경우 이런 Docker 기반의 피닉스 서버 패턴 기반의 배포를 지원하는데, 특히 Kubernetes 클러스터를 매우 잘 지원하기 때문에, 오히려 VM 기반의 배포 보다는 Docker + Kubernetes 배포 구조를 선택하는 것이 좋다.


이 경우 인프라 배포에 있어서는 애플리케이션을 서비스하는 VM워크로드는 도커를 사용하되, Redis, RDBMS와 같은 미들웨어 솔루션은 재 배포가 거의 발생하지 않기 때문에, VM에 배포하여 사용하는 것이 성능적으로 더 유리하기 때문에, 도커와 VM 을 하이브리드 구조로 배포하는 방식을 권장한다.


클라우드 전용 배포 솔루션  VS 오픈소스 (Terraform)

앞에서 설명한 아키텍쳐에서 사용한 솔루션은 모두 오픈 소스 기반이다. 클라우드 벤더의 경우에는 구글은 Deployment Manager와, 아마존은 CloudFormation을 이용하여, 코드 기반의 배포 (Terraform과 동일)를 지원하는데, 그렇다면, 클라우드에서 제공하는 전용 솔루션을 쓰는 것이 좋은가? 아니면 오픈소스나 벤더에 종속적이지 않은 솔루션을 사용하는 것이 좋은가

오픈소스의 배포툴의 경우에는 요즘 트랜드가 다른 영역으로 확장을 해가는 추세가 있기 때문에, 코드 기반의 인프라 배포 이외에도 애플리케이션 코드 배포등 점점 더 넓은 영역을 커버할 수 있는 장점이 있고, 오픈 소스 생태계내에서 다른 제품들와 연동이 쉬운점이 있다. 그리고 특정 클라우드 벤더나 인프라에 종속성이 없기 때문에 조금 더 유연하게 사용이 가능하지만, 클라우드 벤더에서 제공되는 새로운 서비스나 기능 변화를 지원하는 것에는 상대적으로 클라우드 벤더에서 제공하는 도구보다 느리다. 예를 들어 구글 클라우드에서 새로운 서비스가 나왔을때, 테라폼에서 이 기능을 지원하는데 까지는 시간이 걸린다는 것이다.


양쪽다 좋은 선택지가 될 수 있기 때문에, 현재 환경에 맞는 솔루션을 선택하는 것을 권장한다.




피닉스 패턴의 VM 이미지 타입


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


피닉스 서버 패턴을 이용해서 이미지를 만들때, 그러면 이미지에 어디까지 패키징이 되어야할지 결정할 필요가 있다. 정답은 없지만 몇가지 정형화된 패턴을 찾을 수 는 있다


OS Image

가상화 환경이나 클라우드를 사용하면 디폴트로 사용하는 패턴으로 이미지가 OS 단위로 되어 있는 패턴이다. 우분투 이미지, 윈도우 이미지와 같이 OS 단위로 이미지가 되어 있다.




피닉스 패턴을 사용할 경우 애플리케이션 배포시, 이미지를 이용해서 VM 을 생성하고 VM 이 기동될때, Configuration management 도구를 이용하여 소프트웨어 스택 (미들웨어, 라이브러리등)과 애플리케이션 코드를 배포하는 방식이다.

Foundation Image

Foundation Image는 이미지를 OS단위가 아니라 서비스 플랫폼, 예를 들어 Ruby on rails 환경, PHP환경과 같은 환경 별로 관리하는 방법이다.



일종의 PaaS와 같은 개념의 이미지로 생각되는데, 가장 적절한 절충안이 아닌가 싶다.


Immutable Image

마지막으로는 Immutable Image (불변) 이미지인데, 이 이미지 타입은 배포마다 매번 새롭게 이미지를 만드는 패턴이다.


항상 OS 부터 애플리케이션 까지 전체 스택이 같이 이미지화 되어 배포되기 때문에, 최신 업데이트를 유지하기가 좋지만, 빌드 시간이 많이 걸리고 관리해야 하는 이미지 양이 많아진다.

이 패턴으로 갈거면 도커를 쓰는게 오히려 정답이 아닐까 싶다.


 OS 이미지 패턴의 경우 VM이 올라오면서 소프트웨어들이 설치되고 애플리케이션이 설치되는 모델인데, 소프트웨어 특히 npm이나 pip들을 이용해서 라이브러리를 설치할때 외부 저장소를 이용하는 경우, 외부 저장소가 장애가 날 경우 소프트웨어 설치가 안되기 때문에 외부 시스템 장애에 대한 의존성을 가지고 있고 설치 시간이 길기 때문에 그다지 좋은 패턴으로는 판단이 안되고, immutable 패턴은 위에서도 언급했듯이 빌드 시간이 길고, 여러 이미지를 관리해야하기 때문에 그다지 권장하고 싶지 않지만, 전체를 매번 묶어서 배포함으로써 일관성 유지가 가능한 장점이 있기 때문에 만약에 해야 한다면 도커를 이용해서 구현하는 것이 어떨까 한다. Foundation Image 패턴이 가장적절한 패턴으로 판단되는데, 다음글에서는 Packer를 이용하여, Foundation Image 타입을 만드는 방법을 알아보도록 하겠다.


Continuous Deployment 
(Auto Deployment)


빌드와 테스트까지 자동화 했으면 그 다음 문제는 배포이다.

수동으로 배포하는 경우 한 두개의 서버라면 별 걱정이 없겠지만, 개발,테스트,운영 환경과 같이 여러 환경에 또한 각 환경에 수십대의 서버에 배포를 해야 한다면, 문제는 달라진다. 그래서 요즘에서 CI에 배포의 개념을 더한 CD (Continuous Delivery 또는 Continuous Deployment)라는 개념이 유행하는데, 이는 빌드가 완료된 후, 배포까지 자동화 하는 방법이다.



이런 배포를 지원하는 도구는 여러가지 타입이 있다.

   특정 솔루션에 종속적인 도구

Tomcat이나 WebLogic 같은 WAS의 경우 각 제품에 특화된 배포 도구를 가지고 있다. Tomcat의 경우 Tomcat Client Deployer (http://tomcat.apache.org/tomcat-6.0-doc/deployer-howto.html#Deploying using the Client Deployer Package ) 와 같은 도구가 있는데, Remote에서 war 파일을 배포해줄 수 있는 도구 이다.

이러한 도구들의 특징은 해당 솔루션에 최적화가 되어 있기 때문에, RunTime Deploy나 기타 해당 솔루션에 특화된 기능을 활용할 수 있기 때문에, 안정적인 배포가 가능하다는 장점을 가지고 있다.

그러나 반대로 솔루션에 관련된 애플리케이션 파일만 배포할 수 있다는 단점을 가지고 있다. 무슨 이야기인가 하면, war 파일 이외에, 다른 디렉토리에 configuration 파일을 배포하고 싶을 경우, 이런 파일들은 배포가 불가능하다는 것이다. 또한 여러개의 인스턴스에 동시 배포를 하고 싶을 때, 인스턴스들이 제품에서 제공하는 클러스터링 기능등을 사용하고 있지 않을 경우, 각 인스턴스들을 일일이 각각 배포해야 하는 단점이 있다. 쉽게 말해서 매우 제품에 종속적이라는 것이다.

   Configuration Management

다음으로는 Puppet이나 Chef와 같은 Configuration Management 도구 기반의 배포 방식이 있다. 이러한 도구들은 원래 태생이 Deployment보다는 초기 솔루션을 설치하거나, 다수의 서버나 솔루션에 대한 Configuration 정보를 중앙 관리하기 위해서이다. 이에 비해서 Deploy 과정은 대부분 파일을 복사하고 서버를 restart 시키는 과정 정도의 단순 작업이기 때문에, 만약에 이러한 Configuration Management 인프라를 갖추고 있거나, 또는 배포 과정이 솔루션의 설정을 포함하여 매우 복잡한 경우 일때는 매우 효율적으로 사용될 수 있으나, 반대로 이러한 인프라가 없는 상태에서 단순한 배포만하고자 할 경우에는 오히려 배보다 배꼽이 커질 수 도 있다.

   Remote Shell 기반의 도구

마지막으로, Remote Shell 기반의 배포 도구가 있다. SSH RSH과 같은 명령을 툴로 실행시켜 주는 도구 인데, 파일 복사에서 부터 커맨드 라인에서 입력하는 명령들을 원격으로 실행시켜 준다.

솔루션에 종속적이지 않으며 또한 자유도가 높으며 사용이 매우 편리하다.

Python 으로 된 도구로는 Python Fabric이라는 도구가 있고, Ruby쪽에서는 Capistrano라는 도구가 있다.

Case Study

필자의 경우 클라우드 프로젝트 이전에만 해도, 환경 자체가 그리 크지 않았기 때문에, 웹로직이나 Tomcat의배포 툴을 사용해서 배포를 진행했었다. 클라우드 환경 기반에서 프로젝트를 진행하고 또한 진행하는 프로젝트의 규모가 커짐에 따라서 이러한 배포 자동화가 꼭 필요하게 되었는데, 이 배포는 Configuration Deployment 두 가지로 나눠서 생각해볼 수 있다.

애플리케이션을 배포하기 전에 먼저 OS Tomcat과 같은 솔루션을 설치 하는 Configuration, 그 다음이 이 환경 위에 애플리케이션을 배포하는 Deployment이다.

Configuration의 경우에는 Puppet이나 Chef를 도입하고 싶었으나, 팀의 규모와 시간 관계상 도입이 어려웠고, 환경의 복잡도가 낮아서 별도의 Configuration management 도구는 도입하지 않기로 결정하였다.

대신 OS Tomcat Pre-install된 표준 VM 이미지를 만들어 놓고, 배포가 필요할때 마다 이 이미지를 Loading한 후에, 애플리케이션을 Deployment 하는 형태를 사용하였다.

이 방식은 표준 이미지를 만들어 놓고 계속 재 사용하기 때문에, 관리가 쉽지만 반대로 Configuration을 변경하고자 할 때, 이미 배포된 이미지들에 대한 Configuration을 일일이 다시 변경해야 하는 단점이 있다. (어느 정도 규모가 되면 Configuration Management 도구로 넘어가는 것이 좋은듯 하다.)

Fabric을 이용한 배포

그러면 Fabric을 이용한 Tomcat 애플리케이션에 대한 간단한 배포 시나리오를 살펴보도록 하자.

구성은 아래와 같다. Load Balancer 아래에 N개의 Tomcat 인스턴스들이 연결되어 있는 구조 이고, 배포는 다음과 같은 순서를 따르도록 한다.

   먼저 배포하고자 하는 Tomcat 인스턴스를 Load Balancer에서 제외한다.

   다음으로 배포하고자 하는 인스턴스를 Stop한다.

   배포하고자 하는 war 파일을 해당 Tomcat 인스턴스에 복사한다.

   그리고 해당 Tomcat 인스턴스를 리스타트한다.

   위의 1)~4) 과정을 다른 인스턴스에도 반복한다.



이 배포 방식은 간단하기는 하지만, 하나의 서비스내에서 배포 과정중에, 배포가 완료된 인스턴스와 배포 예정인 인스턴스에 애플리케이션이 다르기 때문에, 애플리케이션 변경이 많은 경우에는 적용하기가 어렵다 . 대규모 변경이 있는 경우에는 전체 클러스터를 내렸다가 전체 배포 후 서비스를 다시 시작하는 방식을 사용해야 하는데 이 경우에는 배포 중에 서비스에 대한 순간적인 정지가 발생할 수 있다.

무정지 배포 아키텍쳐

이런 문제를 해결 하기 위해서 일부 자바 기반의 application server의 경우 runtime redeployment (시스템을 운영중에, 정지없이 프로그램을 변경하는 행위로, 일부 자바 기반의 application server에서는 WAR와 같은 웹모듈이나, EJB 같은 모듈을 시스템을 무정지 상태로 재 배포 할 수 있는 기능을 제공한다.) 를 제공하는 제품들이 많다. redeployment 의 원리는 새로운 application load하고, classloader reload하는 형식인데, classloader reload이 위험도가 높은 작업이기도 하고, application을 작성할때, redeploy를 고려하지 않은 경우 정상적으로 runtime redeploy가 되지 않는 경우가 많다. 가장 확실한 redeploy 기법은 application server를 정지시킨후, 재배포한후에 restart하는 것이 가장 안정적이다. 이렇게 restart기반으로 redeploy를 할때, 시스템을 정지 상태를 최소화는 구조를 무정지 배포 아키텍쳐라고 하는데, 구조는 다음과 같다.




application server를 두개의 클러스터 그룹으로 나눈후, 각 클러스터 앞에 reverse proxy를 각각 배치 시킨다. 그리고 reverse proxy 앞에는 L4 스위치를 둬서 각 클러스터로 load balancing을 할 수 있도록 한다.

배포를 할 때는 Cluster A 앞에 있는 reverse proxy를 정지 시킨다. 이렇게 하면, 앞단의 L4 로드 밸런서에서 Cluster A request를 보내지는 않으나, Cluster A 자체는 살아 있다. 그 후에 Cluster A의 각 인스턴스에 애플리케이션을 재 배포 한후,Cluster A reverse proxy를 재기동 시킨다. 마찬 가지 방법으로 Cluster B에도 같은 방법으로 redeploy를 수행한다.

이 구조를 택하면 전체 서비스 중지 없이 그리고 애플리케이션 변화에 대해서 일관성 있게 한꺼번에 재 배포가 가능하다.

Fabric을 이용하여 AWS Tomcat war 파일 배포 하기

그러면 실제로 Fabric을 이용해서 어떻게 배포를 할 수 있을까?

다음은 아주 간단한 Fabric을 이용한 배포 스크립트 이다.

순서는 tomcat stop > copy war > start 와 같다. EC2상에서 pem (SSH)를 이용하여, 두대의 Host deploy하는 스크립트이다.

#fabfile.py

from fabric.api import run,env,execute,task

from fabric.operations import local,put

 

def tomcat_cluster():

        env.user ='root'

        env.hosts=['host1.server.com','host2.server.com'] # list of server setting

        env.key_filename='~/pem/pemfile.pem' # pem file

 

def hostname():

        run('uname -a')

 

def start():

        run('/etc/init.d/tomcat6 start')  # tomcat instance stop

 

def stop():

        run('/etc/init.d/tomcat6 stop') # tomcat instance stop

 

def copy():

        put('./dummy.war','/root/war') # file copy

 

def deploy():

        execute(stop)

        execute(copy)

        execute(start)

 

해당 파일을 fabfile.py에 저장후에

%fab tomcat_cluster deploy

명령어로 실행을 하면 아래와 같은 순서로 배포가 진행된다.

host1.stop()

host1.copy()

host1.start()

host2.stop()

host2.copy()

host2.start()

 

위의 예제는 Fabric의 설명을 하기 위한 아주 간단한 예제로, 필요에 따라서 수정해서 사용하기 바란다.

앞의 예제에서는 간단하게 war 파일만을 복사하는 형태로 배포 스크립트를 작성하였지만, maven 설명할때 언급했던 것 처럼, 기타 Configuration 파일을 함께 배포하고, roll back이나 버전 관리가 용이하게하기 위해서 rpm 형태로 배포하는 것을 권장한다. 또한 rpm 파일을 관리하기 위해서 내부적으로 자체 yum repository를 만들어서 관리하는 것이 좋다.

배포에서 고민해야 할것 들

배포주기

근 몇 년전만 해도, 배포는 몇 달간의 개발이 끝나면 테스트를 거쳐서 특정한 날짜를 잡아서 대규모 배포를 형태가 일반적이었다.

그러나 근래에는 전체적인 IT 트렌드가 SNS와 같은 B2C 서비스가 중심이 되면서, 경쟁 서비스에 비해서 좋은 서비스를 빠르게 제공하기 위해서 업데이트 주기가 매우 짧아지고 있다. 이런 이유에서 배포도 자동화가 필요하게 되었고 Continuous Delivery와 같은 개념이 근래에 유행하게 된것일 수도 있는데, SNS 서비스의 경우 빠르면 하루 단위로 배포하는 경우 까지 있다.

배포 주기가 짧은 경우에는 몇 가지 더 고려할 사항이 있는데, 배포는 코드 개발보다는 인프라 관리나 빌드에 관련된 부분이 많다. 그래서 배포의 주체는 이런 운영 주체가 되는 경우가 일반적인데, 배포시 문제가 생기는 경우가 있을 시에는 개발팀의 도움이 필요하고, 배포가 정상적으로 되었을 경우에는 개발팀으로 부터의 확인등이 필요하기 때문에 조직 구조상 운영팀과 개발팀이 분리된 조직에서는 잦은 배포가 여러가지로 어려운점이 많다. 그래서 근래에는 이런 개발과 운영 조직을 합쳐서 개발팀을 운영하는 DevOps (Deveopment + Operation)형태의 조직구조로 전환하여 개발,배포,운영을 통합하여 관리 하는 쪽으로 이동하고 있다.

이 경우 단순히 조직을 합치는 것뿐만 아니라 개발자에게는 인프라나 운영에 대한 이해 능력을 그리고 운영팀에는 개발에 대한 어느정도 선까지의 능력을 요구하게 되고 업무 프로세스등의 변화가 필요하기 때문에 조직을 융합시키는 차원이 아니라 조금 더 높은 수준의 접근이 필요하다.  

수동 배포

운영 환경 배포는 반드시 수동으로 하는 것을 권고한다.

지금까지 자동으로 배포하는 방법을 설명해놓고, 왠 갑자기 수동 배포를 언급하는가 싶을 수도 있을텐데, 이유는 다음과 같다.

배포는 앞서 본 것과 같이 쉘 명령등을 수행해서 여러가지 명령 (Shutdown, Copy, Restart)을 동시에 여러 서버에 수행한다. 즉 중간에 에러가 날 가능성이 매우 높다.

그래서 배포 작업을 수행할때는 반드시 사람이 지켜보면서 배포 스크립트를 수행하는 것이 좋다. 개발환경이나 QA 환경 같은 경우에는 거의 업무 시간에 빌드를 하면서 배포가 수행이 되기 때문에, 배포 오류가 났을 경우에는 사람이 인지하기가 쉽고, 에러가 났을때 서비스에 직접적인 영향을 주지는 않는다. 그래서 개발이나 QA 환경 같은 경우에는 사람이 지켜보지 않고, CI 프로세스의 일부로 빌드 테스트가 끝나면 자동으로 배포를 하도록 해도 된다.

그렇지만 운영 환경의 경우에는 서비스에 직접적인 영향을 주기 때문에, 반드시 사람이 지켜보면서 수동으로 배포를 시작 및 모니터링 하도록 하는 것이 좋다.

배포 roll back

서비스에 대한 배포시, 테스트를 아무리 잘했다하더라도, 에러가 발생할 수 있다.

에러가 발생하였을 때는 이전의 버전으로 신속하게 roll back할 수 가 있어야 하는데, 이렇게 하기 위해서는 애플리케이션의 이전 버전을 반드시 저장하고 있어야 하고, 배포 스크립트 역시 예전 버전을 다시 배포할 수 있는 기능을 구현해야 한다.

릴리즈 노트

마지막으로 배포가 끝나면 반드시 릴리즈 노트를 작성 및 함께 배포하는 것이 좋다.

배포란 개발이나 운영이 주로 주도하는 작업이기 때문에, 비지니스 쪽에서 필요한 기능 변화에 대한 릴리즈 노트의 중요성을 잃어 버리는 경우가 많은데,  서비스가 변경이 되었을때, 어떠한 기능이 추가 되었는지, 그리고 어떠한 버그가 FIX가 되었는지를 사용자에게 알려줄 필요가 있으며, 특히 서비스를 판매나 영업하는 입장에서는 어떤 변화가 있었는지를 확인하는 것이 중요하기 때문에, 이 부분을 반드시 챙기도록 한다.

릴리즈 노트는 배포 되는 대상 (독자)에 따라서 다르게 작성되야 하는데,

내부 릴리즈 노트는 다음과 같은 내용이 포함되는 것을 권고한다.

Ÿ   릴리즈 버전, 날짜 및 빌드 넘버

Ÿ   새로운 기능 및 설명

Ÿ   버그 number 및 버그 수정 내용

또는 JIRA와 같은 Tak Management 도구를 사용하는 경우, JIRA에 등록된 기능이나 Bug 수정 내용을 릴리즈 시기에 자동으로 JIRA로 부터 생성해 낼 수 있다. (JIRA에서 릴리즈 노트 생성하기 https://confluence.atlassian.com/display/JIRA/Creating+Release+Notes)

만약 서비스나 제품 사용자를 대상으로 하는 경우, 위의 릴리즈 노트의 내용을 사용할 수 도 있지만, 조금 더 직관적이고 읽기 쉬운 형태로 릴리즈 노트를 작성하는 것이 좋다.

다음은 몇몇 잘 정의된 릴리즈 노트의 샘플이다.

Ÿ   안드로이드 릴리즈 노트 : http://developer.android.com/sdk/RELEASENOTES.html

Ÿ   Fire Fox 릴리즈 노트 http://www.mozilla.org/en-US/firefox/23.0.1/releasenotes/

Ÿ   Maven 릴리즈 노트 http://maven.apache.org/release-notes-all.html

릴리즈 노트는 청중들에게 이번 릴리즈 기능의 변경 사항을 제대로 알리기 위함이다. JIRA와 같은 시스템의 기능을 이용하건, 아니면 일일이 손으로 새로 쓰건간에 반드시 보는 사람이 어떤 변화가 있었는지 쉽게 이해할 수 있는 형태라야 한다.

http://pds3.egloos.com/pds/200611/26/16/capistrano_niceview.pdf 여기 정리가 잘되어 있네요.

Fabric과 비슷하게 SSH 기반으로 작동하는 도구입니다.

위의 PT에 스크립트 작성하는 전체적인 흐름이 잘 정의 되어 있어서, Fabric 사용할때도 비슷하게 참조할 수 있겠습니다.


Python Fabric Install

프로그래밍/Python | 2013.01.28 18:55 | Posted by 조대협

AWS EC2 (Amazon Linux 기준)


1. Python install (dev package로 설치)

(반드시 dev package가 설치되어 있어야지, pycrypto 설치시 에러가 나지 않음. pycrptyo는 encryption 관련 라이브러리로 C 라이브러리를 사용하는데, 컴파일중, python.h를 사용한다. 이 헤더 파일은 dev package안에 포함되어 있음)


- yum install python-devel 


2. pip install

$ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py

$ [sudo] python get-pip.py


3. gcc가 인스톨 (pycrypto 설치를 위해서 필요함) 

yum install gcc


4. fabric install


pip install pycrypto fabric


5. 설치 확인


from fabric.api import run


def host_type():

    run('uname -s')


MongoDB Deployment 아키텍쳐를 간단하게 보면 다음과 같다.
mongos들을 앞단에 쭈욱 늘어놓고, 이는 라우터의 역할을 한다. mongos간의 load balancing은 앞단에 L4등의 로드 밸런서를 사용하고, Cache Hit율등을 높이기 위해서 L4는 Hash 방식등의 Sticky setting을 한다.
뒷단에 mongod를 배치하고, 최소한 3 copy replica 구조로 설정한다.



inter data center에 대한 replication을 설정하고, 이는 DR이나 Back up 용도로 사용한다
inter data center replication은 항상 여러가지 숙제를 주는데, 이 경우 backbone의 속도 차이로 인하여 data의 일관성이 깨질 수 있으니,
1. DR/Back up 용도로만 사용
2. data center간 초고속 백본만 설치
3. data center A에서는 read/write, B에서는 read만 (Query Off Loading 아키텍쳐) 수행하고, 이 경우에는 B 데이타 센터의 data 반영이 늦음을 예상해서 애플리케이션을 설계한다.

오늘 OKJSP의 테크 트렌드에서 본 좋은 제품 하나.. http://www.zeroturnaround.com/ Java Agent로 JVM을 Weaving하여, Zero downtime으로 redeployment를 가능하게 해준다. 얼마나 Production에서 제대로 동작할지는 모르겠지만, WLP.WLI는 몰라도, WLS에 전통적인 J2EE Application이나 Spring 기반의 Ap에서는 꽤나 쓸만할듯.