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


Archive»


 
 

Apt.ly를 이용한 데비안 리포지토리 생성


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




앞의 글에서 Jenkins + Maven 조합을 통해서 애플리케이션 설치 파일을 데비안 패키지로 패키징하는 방법에 대해서 알아보았다. 이제 이 패키지를 서버에 설치하는 방법을 살펴본다.

패키지를 설치하는 방법은 간단하게 데비안 패키지 파일을 설치하고자 하는 서버에 복사해놓은 다음에, sudo apt-get install을 이용해서 설치하는 방법도 있지만, 설치하고자 하는 서버마다 복사하기가 번거롭기 때문에 조금 더 쉬운 접근을 위해서 데비안 패키지 서버를 올리는 방법이 있다. 우리가 JDK나  node.js 등 다양한 유닉스 패키지를 apt-get 을 이용하여 설치가 가능한것은 미리 데비안 패키지 리파지토리 서버가 지정되어 있고, 그 서버내에 패키지들이 등록되어 있기 때문인데, 애플리케이션 패키지를 같은 방법으로 설치할 수 있게 하려면 애플리케이션 데비안 패키지 파일을 등록할 리포지토리 서버를 설정하면 된다.


지금까지 구현해온 파이프라인은 아래 그림과 같다

  1.  gitHub 로 부터 Jenkins가 자바 코드를 자바 코드를 당겨온다

  2. 이 코드를 Maven을 이용하여 빌드한다

  3. Maven은 코드 빌드가 끝나고 이를 데비안 패키지로 패키징 한다.

  4. Jenkins는 데비안 패키징 파일을 로컬 리포지토리인 apt.ly에 저장한다.

  5. Spinnaker에서 설치할때 데비안 패키지를 apt.ly에서 당겨서 설치한다.


앞의 글에서 까지 1,2,3과정까지 진행을 하였고, 이 글에서는 4번 과정을 구현할 예정이다.

다양한 오픈소스가 있지만, 플랫폼 종속성이 없고 손쉽게 설치가 가능한 apt.ly (www.aptly.info) 를 기준으로 설명을 하고자 한다.


apt.ly 설치

여기서 설명하는 설치는 데비안 리눅스 9을 기준으로 하여 설명한다.

설치는 앞의 글에서 설치한 Jenkins 서버에 그대로 설치하도록 한다.

설치 방법은 간단하다. www.aptly.info 사이트에서 설치 메뉴얼을 체크하여, 현재 사용하는 리눅스 버전에 맞는 바이너리를 wget을 이용해서 다운 로드 받은 후에, chmod +x 로 실행 권한만 주면 된다.


$ wget https://dl.bintray.com/smira/aptly/0.9.5/debian-squeeze-x64/aptly

$ chmod +x aptly


명령어가 설치 되었으면 리파지토리를 생성해야 한다.

리포지토리 생성 과 확인

리포지 토리 생성은 aptly repo crete {리포지토리 명} 을 입력하면 된다.  아래 명령은 “terry-repo”라는 이름의 리포지토리를 생성한것이다.

%./aptly repo create terry-repo


아래 명령은 terry-repo 라는 리포지토리에 대한 정보를 조회 하는 명령이다.

%./aptly repo show -with-packages terry-repo


아래는 실제 실행결과 인데, 테스트를 위해서 helloterry_1.0_all 이라는 패키지를 등록해놨기 때문에 하나의 패키지가 등록되서 보이는 것을 확인할 수 있다.



apt.ly 에 패키지 등록하기

리포지토리가 생성되었으면, maven 에서 빌드한 패키지를 apt.ly 리포지토리에 등록해보자

등록하는 방법은 aptly repo add -force-replace {리포지토리명} {데비안 패키지 파일명} 식으로 사용하면 된다.

아래는 terry-repo에 helloworld.deb 파일을 등록하는 명령이다.

%./aptly repo add -force-replace terry-repo helloworld.deb

apt.ly 리포지토리 퍼블리슁하기

패키지를 등록했으면 외부에서 억세스사 가능하도록 리포지토리 퍼블리쉬를 해야 하는데, 퍼블리쉬는 어떤 버전의 OS와 CPU 타입에 설치할 수 있는지등의 메타 정보를 함께 등록한다.

명령어 사용법은 aptly publish repo -distribution=”{OS 버전 정보}" -architecture=”{CPU 타입}” -skip-signing=true {리포지토리명}

식으로 사용한다.

원래 데비안 패키지를 외부로 배포를 할때는 패키지의 변경(원하지 않은)을 막기 위해서 패키지에 사이닝을 하는데, 여기서는 -skip-signing 을 이용하여 사이닝 단계를 건너뛰도록 하였다. 이 리파지토리는 외부에서 억세스하는 용도가 아니라 내부에서 CI/CD 파이프라인 단계에서만 사용되기 때문에 사이닝을 생략하였다.


아래 명령은 데비안 stretch 버전에 amd64 (intel CPU)에 terry-repo 이름으 리포지토리를 퍼블리슁한것이다.

%./aptly publish repo -distribution=stretch -architectures="amd64" -skip-signing=true terry-repo


apt.ly 서버 기동

퍼블리슁이 되었다고 당장 리포지토리를 접근 가능한것이 아니다. apt-get을 이용한 인스톨은 HTTP 프로토콜을 이용해서 접근하기 때문에 apt.ly 파일 저장소를 접근 가능하게 하는 웹서버를 올려야 한다.

간단한 방법으로는 aptly serve 명령어를 이용해서 웹서버를 올리는 방법이 있다.

아래 명령어 처럼 aptly serve -listen={IP:포트}를 적으면 된다.


% ./aptly serve -listen=:9090 > aptly.log &


이 보다는 제대로 서비스를 하기위해서는 웹서버에 올리는게 좋은데,

(참고 : https://www.spinnaker.io/guides/tutorials/codelabs/hello-deployment/)

% sudo apt-get install nginx

를 통해서 nginx 를 설치한 후에, /etc/nginx/sites-enabled/default 파일을 다음과 같이 편집한다.


server {
       listen 9999 default_server;
       listen [::]:9999 default_server ipv6only=on;
       root /var/lib/jenkins/.aptly/public;
       index index.html index.htm;
       server_name localhost;
       location / {
               try_files $uri $uri/ =404;
       }
}


이때 root에 aptly의 public 디렉토리를 명시해줘야 하는데, aptly를 설치한 디렉토리의 .aptly/public 이 되는게 일반적이다. 여기서는 /var/lib/jenkins 디렉토리 아래에 리포지토리를 만들었기 때문에 /var/lib/jenkins/.aptly/public 디렉토리를 홈 디렉토리로 설정하였다.


nginx 를 기동하면 http 9999번 포트로 데비안 패키지 서비스를 시작한다.

apt.ly 를 통한 패키지 설치

데비안 패키지 서버를 설치하고 패키지를 등록했으면 실제로 패키지를 다른 서버에서 인스톨 해보자

다른 서버에서 이 패키지 서버에 대한 정보를 알고 있어야 하는데 (서버 주소) 이 정보는 /etc/apt/sources.list 라는 파일에 아래와 같은 형태로 등록 되어 있다.


deb http://deb.debian.org/debian/ stretch main

deb-src http://deb.debian.org/debian/ stretch main

deb http://security.debian.org/ stretch/updates main

deb-src http://security.debian.org/ stretch/updates main

deb http://deb.debian.org/debian/ stretch-updates main

deb-src http://deb.debian.org/debian/ stretch-updates main


이 파일에 앞서 설정한 데비안 리포지토리 서버 (apt.ly) 서버의 주소와 정보를 입력해주면 된다.

만약 http://myserver-ip:9999 포트로 서버를 올렸다면 아래와 같은 정보를 /etc/apt/sources.list 에 추가해주면 된다.


deb http://myserver-ip:9999 stretch main


설정이 끝났으면

%sudo apt-get update

명령을 실행하면 아래와 같이 새로운 리포지토리에서 정보를 읽어오는 것을 확인할 수 있다.


모든 준비가 끝났다.

인스톤을 해보자. 인스톨은 sudo apt-get install을 이용하면 된다.

앞서 등록한 패키지 명이 helloterry 였기 때문에 간단하게 아래와 같이 sudo apt-get install helloterry 명령어를 실행하면 된다.



이외에도 유사한 툴로 pulp (https://docs.pulpproject.org/user-guide/introduction.html#what-pulp-can-do)

클라우드 서비스로는 cloudsmith.io (https://cloudsmith.io/)등이 있다. 

작은 규모의 팀이라면 관리 문제도 있으니 클라우드 서비스를 쓰는 것도 좋은 방안이 되지 않을까 한다.



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과 동일)를 지원하는데, 그렇다면, 클라우드에서 제공하는 전용 솔루션을 쓰는 것이 좋은가? 아니면 오픈소스나 벤더에 종속적이지 않은 솔루션을 사용하는 것이 좋은가

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


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




Packer와 Ansible을 이용하여, node.js 이미지 생성하기


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


앞서 글에서 패커를 이용한 이미지 생성 및, 이미지 타입(http://bcho.tistory.com/1226) 에 대해서 알아보았다. 이번 글에서는 node.js 가 깔려있는 파운데이션 타입의 구글 클라우드 VM이미지를 패커와 앤서블을 이용해서 구현해 보도록 한다. 이 글을 이해하기 위해서는 http://bcho.tistory.com/1225 에 대한 이해가 필요하다.


구성은 다음과 같다. 패커를 이용하여, Debian OS 기반의 이미지를 만든 후에, 패커의 Provisioner를 이용하여 Ansible을 설치하고, 이 설치된 Ansible을 이용하여 node.js등을 설치하는 playbook 을 실행하는 순서로 node.js용 이미지를 만든다.  



패커 스크립트는 다음과 같다.

builder 부분은 예전과 같다.(http://bcho.tistory.com/1225) Debian 이미지를 기반으로 VM을 생성한다.

VM 생성후에, 소프트웨어 설치등을 정의하는 부분은 provisioner 라는 부분에 정의되는데, 두 타입의 Provisioner가 사용되었다. 첫번째는 shell 타입이고 두번째는 ansible-local 형태의 provisioner이다.


{

 "variables":{

   "project_id":"terrycho-sandbox",

   "prefix":"debian-9-nodejs"

 },

 "builders":[

  {

   "type":"googlecompute",

   "account_file":"/Users/terrycho/keys/terrycho-sandbox-projectowner.json",

   "project_id":"{{user `project_id`}}",

   "source_image":"debian-9-stretch-v20180105",

   "zone":"us-central1-a",

   "ssh_username":"ubuntu",

   "image_name":"{{user `prefix`}}-{{timestamp}}",

   "machine_type":"n1-standard-4"

  }

 ],

 "provisioners":[

   {

     "type":"shell",

     "execute_command":"echo 'install ansible' | {{ .Vars }} sudo -E -S sh '{{ .Path }}'",

     "inline":[

               "sleep 30",

               "apt-add-repository ppa:rquillo/ansible",

               "/usr/bin/apt-get update",

               "/usr/bin/apt-get -y install ansible"

               ]

   },

   {

      "type":"ansible-local",

      "playbook_file":"./nodejs_playbook.yml"

   }


 ]


}


첫번째 provisioner에서는 ansible을 apt-get으로 설치하기 위해서 sudo 권한으로 apt-get update를 실행하여, 리파지토리 정보를 업데이트 한후에, apt-get -y install ansible을 이용하여, ansible을 설치한다.


두번째 provisioner는 ansible-local provisioner로, 앞단계에서 설치된 ansible을 로컬에서 실행하여, playbook을 실행해주는 코드이다.

ansible은 Configuration management & Deployment 도구로, 나중에 기회가 되면 다른글을 이용해서 소개하도록 한다.

이 코드에서 호출된 nodejs_playbook.yml 파일의 내용은 다음과 같다.

- hosts: all

 tasks:

       - name : create user node

         become : true

         user :

             name: nodejs

             state : present

       - name : update apt-get install

         shell : curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

       - name : install node.js LTS

         become : true

         #become_user: nodejs

         apt : pkg=nodejs state=installed update_cache=true


hosts:all로, ansible에 등록된 모든 호스트에 대해서 스크립트를 실행하도록 한다. 여기서는 별도의 호스트를 등록하지 않았고, ansible-local 타입으로 실행하였기 때문에, 이 호스트 (localhost)에만 스크립트가 실행된다.

크게 3단계로 실행이 되는데, 첫번째가 nodejs라는 사용자를 만드는 단계로, user 라는 모듈을 사용하여 nodejs라는 사용자를 생성하였다. 이 사용자 계정은 향후 애플리케이션이 배포되었을때, nodejs를 실행할 계정으로 사용된다. 사용자 계정을 만들기 위해서는 root 계정을 획득해야하기 때문에, become: true로 하여 sudo 로 명령을 실행하도록 하였다.

두번째는 node.js를 인스톨하기 위해서 설치전 사전 스크립트를 실행하는 부분이다. apt-get install을 디폴트 상태에서 실행하게 되면 node.js 4.x 버전이 인스톨된다. 최신  8.X 버전을 인스톨하기 위해서, 스크립트를 실행한다. 앤서블 모듈중에서 shell 모듈을 이용하여 쉘 명령어를 실행하였다.

세번째 마지막은 apt 모듈을 이용하여, node.js를 인스톨하도록 한다.


스크립트 작업이 끝났으면, 이미지를 생성해보자

%packer build node.json


으로 실행을 하면 이미지가 생성된다. 생성된 이미지는 구글 클라우드 콘솔의 GCE (Google Compute Engine)의 Images 메뉴에서 확인이 가능하다.

다음과 같이 debian-9-nodejs-*로 새로운 이미지가 생성된것을 확인할 수 있다.



생성된 이미지가 제대로 되었는지를 확인하기 위해서, 이 이미지로 VM을 생성해서 nodejs 버전을 확인해보면 다음과 같이 8.9.4 가 인스톨 되었음을 확인할 수 있다.

또한 nodejs로 된 계정이 생성되었는지를 확인하기 위해서 /etc/passwd 내에 사용자 정보가 생성되었는지를 확인해보면 아래와 같이 nodejs 이름으로 계정이 생성되었음을 확인할 수 있다.



참고 : https://blog.codeship.com/packer-ansible/



피닉스 패턴의 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 타입을 만드는 방법을 알아보도록 하겠다.


피닉스 서버

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


근래에 들어서 인프라 스트럭쳐를 소프트웨어로 정의하는 Infrastructure As a Code (줄여서 IaC라고 부름)를 관심있게 보고 있는데, CI/CD의 단순 연장선상의 하나의 툴링정도로 생각했는데, 생각보다 상당히 넓은 생태계라서 좀더 깊게 보고 있다.

IaC는 일반적인 툴이나 단순한 프로세스가 아니라 하나의 사상이기 때문에 이를 제대로 이해하기 위해서는 툴링 관점의 접근 보다는 사상과 배경에 대해서 제대로 이해할 필요가 있다.

IaC 개념을 이해하는데 도움이 되는 개념으로 Snowflakes Server (스노우플레이크 서버)와 Phoenix Server(피닉스 서버) 두 가지 개념에 대해서 알아볼 필요가 있다.

Snowflakes Server (스노우 플레이크 서버)

예전에 일반적으로 서버를 운영하는 방법은 서버를 설치한 후 OS를 인스톨한 후에, 필요한 소프트웨어와 애플리케이션을 설치하여 운영하는 형태였다. 여기에 문제가 생긴 경우 패치를 하거나 정기적인 보안 패치 튜닝들을 해당 서버에 지속적으로 적용하고, 애플리케이션은 CI/CD 등의 툴을 이용하여 배포하는 구조를 가지고 있었다.


이렇게 한번 설치한 서버에 계속해서 설정을 변경하고 패치를 적용하는 등의 업데이트를 지속적으로 적용하여 운영하는 서버를 스노우 플레이크 서버 (눈송이 서버)라고 하는데, 이렇게 설정된 서버는 다시 똑같이 설정하기가 매우 어렵다. 모든 설정과정이 문서화가 잘되어 있으면 모르겠지만 대부분 문서화가 꼼꼼한 경우도 드물뿐더러,  담당자가 바뀌거나 관리 조직이 바뀌는 경우에는 그 이력이 제대로 유지되는 경우가 없다. 그래서 장비를 업그레이드 하거나 OS를 새로 인스톨해서 같은 환경을 꾸미고자 할때 예전 환경과 동일한 환경을 구성하기가 어렵고 그래서, 누락된 설정이나 패치등에 의해서 장애가 발생하는 경우가 많다.

이렇게 한번 설정을 하고 다시 설정이 불가능한, “마치 눈처럼 녹아버리는" 서버의 형태를 스노우 플레이크 서버라고 한다.


재 구성의 문제뿐 아니라, 이런 스노우 플레이크 서버는 구성 편차를 유발하기도 하는데, 여러대의 웹서버를 운영하고 있는 조직에서, 문제가 있어서 특정 서버를 패치한 경우, 다른 동일한 웹서버를 모두 패치 하지 않는 이상 구성이 달라진다.  이는 또 운영상의 문제를 일으킬 수 있다.

Phoenix Server (피닉스 서버)

그래서 나온 서버 패턴이 피닉스 서버 패턴인데, 피닉스(불사조)는 불멸로도 알려져있지만 정확히는 불속에서 다시 태어나는 re-born (재탄생)의 개념을 가지고 있다. 이 재탄생의 개념을 서버 설정 방식에 적용한 패턴이 피닉스 서버 패턴이다.

새로운 소프트웨어를 인스톨하거나 설정을 변경할때 기존 서버에 변경 작업을 더 하는 것이 아니라, 처음 OS 설치에서 부터, 소프트웨어 인스톨, 설정 변경까지를 다시 반복하는 것이다.

예를 들어 우분투 16, 톰캣 7.0 버전으로 운영되고 있는 서버가 있을때, 이 서버에 로그 수집 모듈은 fluentd를 설치하는 케이스가 있다고 가정하자.


스노우플레이크 서버 패턴의 경우에는 이미 운영되고 있는 서버에 새롭게 fluentd를 일일이 설치하거나 자동화가 되어 있는 경우에는 ansible 이나 chef등의 configuration management 도구를 이용하여 fluentd를 설치하게 된다.


피닉스 서버 패턴의 경우에는 새 VM을 다시 만들고, 우분투 16 OS를 설치하고, 톰캣 7.0을 설치하고 fluentd를 설치한 다음. 이 VM으로 기존 VM 을 교체한다.


매번 전체 설치를 반복한다면 매우 많은 시간이 들텐데, 어떻하냐? 물론 매번 이렇게 새롭게  모든 스택을 설치하지 않는다. 어느정도 공통 스택은 가상머신의 베이스 이미지 (VM Base Image)로 만들어놓고, 이 이미지를 이용하여 VM을 생성한 후에, 차이가 나는 부분만 설정을 하는 구조를 설정하는 구조를 사용하게 되고 이 과정은 스크립트 코드를 이용해서 자동화하기 때문에, 그렇게 많은 시간이 소요되지 않는다.

피닉스 서버 패턴에서는 매번 전체를 스크립트를 이용해서 설치하기 때문에, 다음과 같은 장점을 가지게 된다.


  • 스크립트에 모든 설정 정보가 유지되게 된다.

  • 이 스크립트 코드를, git와 같은 소스 코드 관리 시스템을 이용해서 관리하게 되면, 어떤 부분을 누가 어떻게 수정을 했는지 추적이 가능하게 된다.

피닉스 서버 패턴을 이용한 배포 구조

그러면 이 피닉스 서버 패턴을 이용한 배포 구조를 보도록 하자

앞의 예제와 같이 Ubuntu 16, Tomcat 7이 설치된 VM서버 2대가 서비스되고 있고, 이 서버들은 앞단에 로드밸런서에 연결되어 있다고 생각하자. 이 VM들은 Ubuntu16, Tomcat 7이 설치되어 있는 Base Image로 생성했다고 가정하자.



Fluentd를 설치하기 위해서는 피닉스 서버 패턴 처럼, Fluentd가 설치된 새로운 base image를 생성하고, 이 이미지를 이용하여 새로운 VM들을 만든다. 이 VM들 그룹은 아직 서비스가 되기 전으로, 이 그룹을 Pre-production 그룹이라고 한다.



Pre-production 그룹이 정상적으로 들어왔으면 로드밸런서를 변경하여 기존의 구 버전 VM으로 들어가던 트래픽을 pre-production 그룹으로 옮겨준다.


그리고 마지막으로, 기존의 운영환경을 지워주면 아래와 같이 새 환경으로 서비스 하는 환경만 남게 된다.


이렇게 서버가 구 버전에서 새버전으로 재탄생(re-born)하는 것이 피닉스 서버 패턴이다.


이 패턴의 문제는 VM 이미지를 빌드하기 때문에, 빌드 및 배포 시간이 상대적으로 오래 걸리고, 또한 배포 당시 기존 Production 환경과 Pre-production 환경이 공존하기 때문에, 다소 추가 비용이 소요될 수 있다.


이 피닉스 서버 패턴에서 중요한것은 기존 이미지를 이용하여 변경을 주고, 이를 다시 이미지로 만들어낼 수 있지만, 이렇게 하면 스노우플레이크 서버이지 피닉스 서버가 아니다. 피닉스 서버는 기존 이미지를 재 사용하는게 아니라 처음부터 모든 스택을 새로 설치해야 하기 때문에, 처음부터 모든 스택을 설치하여 이미지를 만들 수 있는 자동화 툴이 필요하다. 다음 글에서는 이미지 생성을 자동화 해주는 HashiCorp의 Packer라는 오픈소스에 대해서 알아보도록 하겠다.


클러스터 상에서 하둡 배포 아키텍쳐


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


오늘 빅데이타 관련 교육을 받다가 클라우드 상에서 하둡 클러스터 활용에 대한 영감을 받은 부분이 있어서 정리해보고자 한다. 하둡의 경우에는 On-prem 환경에 적절하게 디자인이 된 오픈 소스라서, 이걸 클라우드에서 사용할 경우에도 on-prem에서 사용하는 형태와 유사하게 사용하는 경우가 많다. 일종의 습관 또는 관성이라고 해야 하나? 인프라가 바뀌면 그 장점에 맞는 아키텍쳐를 선택해야 하는데, 이 부분을 놓치고 있지 않았나 싶다.

Job별 클러스터를 생성하는 아키텍쳐

job을 수행하는 방법을 보면, 일반적으로 On-Prem에서 사용하는 방법은 하나의 하둡 클러스터에 Job을 실행하고 Job이 끝나면 다음 Job을 수행하는 방식을 사용한다.



이러한 경험(습관)때문인지 보통 클라우드 상에 하둡 클러스터를 만들어놓고 그 클러스터에 On-prem과 마찬가지 방식으로 Job을 넣고 그 작업이 끝나면 같은 클러스터에 Job을 넣는 방식을 사용한다.

그런데 클라우드의 특성상 자원이 거의 무한대인데, 여러개의 Job을 하나의 클러스터에서 순차적으로 실행할 필요가 있을까?


Job별로 클러스터를 생성해서 연산을 하고 Job이 끝나면 클러스터를 끄는 모델을 사용하면, 동시에 여러 작업을 빠르게 처리할 수 있지 않을까?



이 경우 문제가 될 수 있는 것이 같은 데이타셋을 가지고 여러가지 Job을 실행할 경우 기존의 On-Prem의 경우에는 동시에 같은 데이타셋에 접근할 경우 오버로드가 심하기 때문에, 어렵지만 클라우드의 경우에는 바로 HDFS를 사용하는 것보다는 GCS와 같은 클라우드 스토리지를 사용하는데, 여기서 오는 장점이 이런 클라우드 스토리지에 저장된 데이타는 동시에 여러 하둡 클러스터에서 접근이 가능하다.


이러한 아키텍쳐를 생각하게된 이유는 구글 클라우드의 특성 때문인데, 구글 클라우드의 과금 모델은 분당 과금 모델을 사용한다. 그래서 Job별로 클러스터를 전개했다가 작업이 끝난 다음에 끄더라도 비용 낭비가 없다. 시간당 과금인 경우에는 클러스터를 껐다켰다 하면 비용 낭비가 발생할 수 있기 때문에 바람직하지 않을 수 있다. 예를 들어서 1시간 10분이 걸리는 작업과 20분이 걸리는 작업을 각각 다른 클러스터에서 돌린다면 2시간, 그리고 1시간 비용이 과금되기 때문에 총 3시간이 과금된다.

그러나 구글의 분당 과금을 적용하면 총 1시간30분이 과금되기 때문에 비용이 1/2로 절감된다.


또한 구글 클라우드의 경우 하둡 클러스터 배포시간이 통상 90초이내로 빠르기때문에, Job마다 클러스터를 생성하고 끈다고 해도, 실행 시간이 크게 늘어나지 않는다.


클러스터별로 인프라를 최적화

만약에 위에처럼 Job별로 클러스터를 나눈다면, Job 의 특성에 맞춰서 클러스터의 인스턴스 수나 구조(?)를 변경하는 것도 가능하다.

즉 많은 컴퓨팅 작업이 필요한 Job이라면 클러스터에 많은 인스턴스를 넣고, 그렇지 않다면 인스턴스 수를 줄일 수 있다.




조금 더 나가면, 상태 정보 같이 HDFS에 저장하는 데이타가 적을 경우 워커노드가 중간에 정지되더라도 전체 연산에 크게 문제가 없을 경우에는 Preemptible VM과 같이 가격은 싸지만 저장 기능이 없는 VM의 수를 늘려서 전체 비용을 낮출 수 있다.




여기서 한걸음 더 나가면, 구글 클라우드의 인스턴스 타입중의 하나가 Cutomizable 인스턴스로 CPU와 메모리 수를 마음대로 조정할 수 있다. 즉 Job이 메모리를 많이 사용하는 Spark과 같은 Job일 경우 클러스터를 구성하는 인스턴스에 CPU보다 메모리를 상대적으로 많이 넣고 구성하는 것들이 가능하다.


아키텍쳐 설계도 의미가 있었지만, 그동안 간과하고 있었던 점이 기술이나 인프라가 바뀌면 활용 하는 방법도 다시 고민해야 하는데, 그동안의 관성 때문인지 기존 시스템을 활용하는 방법을 그대로 의심 없이 사용하려 했다는 것에 다시한번 생각할 필요가 있었던 주제였다.






Docker Kubernetes의 UI

클라우드 컴퓨팅 & NoSQL/google cloud | 2016.11.26 23:38 | Posted by 조대협

Docker Kubernetes UI


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


오늘 도커 밋업에서 Kubernetes 발표가 있어서, 발표전에 데모를 준비하다 보니, 구글 클라우드의 Kubernetes 서비스인 GKE (Google Container Engine)에서 Kubernetes UI를 지원하는 것을 확인했다.


Google Container Service (GKE)


GKE는 구글 클라우드의 도커 클라우드 서비스이다. 도커 컨테이너를 관리해주는 서비스로는 Apache mesos, Docker Swarm 그리고 구글의 Kuberenetes 가 있는데, GKE는 이 Kuberentes 기반의 클라우드 컨테이너 서비스이다.


대부분의 이런 컨테이너 관리 서비스는 아직 개발중으로 운영에 적용하기에는 많은 부가적인 기능이 필요한데, 사용자 계정 인증이나, 로깅등이 필요하기 때문에, 운영환경에 적용하기는 아직 쉽지 않은데, GKE 서비스는 운영 환경에서 도커 서비스를 할 수 있도록 충분한 완성도를 제공한다. 이미 Pocketmon go 서비스도 이미 GKE를 사용하고 있다.


Kubernetes UI


예전에 Kubernetes를 테스트할 때 단점은 아직 모든 관리와 모니터링을 대부분 CLI로 해야 하기 때문에 사용성이 떨어지는데, 이번 GKE에서는 웹 UI 콘솔을 제공한다.


구글 GKE 콘솔에서 Kuberentes 클러스터를 선택하며 우측에 Connect 버튼이 나오는데, 


이 버튼을 누르면, Kubernetes 웹 UI를 띄울 수 있는 명령어가 출력된다.

아래와 같이 나온 명령어를 커맨드 창에서 실행시키고 htt://localhost:8001/ui 에 접속하면 Kubernetes 웹 콘솔을 볼 수 있다. 


Kubernetes 의 웹콘솔은 다음과 같은 모양이다.



Kubernetes의 주요 컴포넌트인 Pods, Service, Replication Controller , Nodes 등의 상태 모니터링은 물론이고, 배포 역시 이 웹 콘솔에서 가능하다.


예를 들어  gcr.io/terrycho-sandbox/hello-node:v1 컨테이너 이미지를 가지고, Pod 를 생성하고, Service를 정의해서 배포를 하려면 다음과 같은 명령을 이용해야 한다.


1. hello-node 라는 pod를 생성한다. 

% kubectl run hello-node --image=gcr.io/terrycho-sandbox/hello-node:v1 --port=8080


2. 생성된 pod를 service를 정의해서 expose 한다.

kubectl expose deployment hello-node --type="LoadBalancer"


이런 설정들을 CLI로 하면 익숙해지면 쉽지만 익숙해지기전까지는 번거로운데,

아래 그림과 같이, 간단하게 웹 UI에서 Pod와 서비스들을 한번에 정의할 수 있다.





배포가 완료된 후에는 각 Pod의 상황이나, Pod를 호스팅하고 있는 Nodes 들의 상황등 다양한 정보를 매우 쉽게 모니터링이 가능하다. (cf. CLI를 이용할 경우 CLI 명령어를 잘 알아야 가능하다.)


GKE에 대한 튜토리얼은 https://cloud.google.com/container-engine/docs/tutorials  에 있는데,

추천하는 튜토리얼은

가장 간단한 튜토리얼 node.js 웹앱을 배포하는  http://kubernetes.io/docs/hellonode/

와 WordPress와 MySQL을 배포하는 https://cloud.google.com/container-engine/docs/tutorials/persistent-disk/

을 추천한다.


도커가 아직까지 운영 환경에 사례가 국내에 많지 않고, GKE도 GUI 가 없어서 그다지 지켜보지 않았는데, 다시 파볼만한 정도의 완성도가 된듯.


참고로 테스트를 해보니 VM을 3개 만들어놓고 컨테이너를 7개인가 배포했는데, VM은 3개로 유지된다. 즉 하나의 VM에 여러개의 컨테이너가 배포되는 형태인데, 작은 서비스들이 많은 경우에는 자원 사용 효율이 좋을듯. 이런 관점에서 봤는때는 VM 기반의 서비스보다 컨테이너 서비스를 쓰는 장점이 확실히 보이는듯 하다




Google 앱앤진에 node.js 애플리케이션을 배포하기


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

PaaS 서비스란?

PaaS란 Platform as a service의 약자로, 간단하게 설명하면, Linux VM등에 직접 node.js나 기반 환경을 설치하고 디스크와 네트워크 구성, 오토스케일링등의 구성이 필요 없이, 개발자가 작성한 코드만 올려주면, 이 모든 것을 클라우드에서 대행해주는 서비스의 형태이다.

인프라 운영을 위한 별도의 작업이 필요하지 않기 때문에, 숫자가 적은 기업이나 개발에만 집중하고 싶은 기업에는 적절한 모델이라고 볼 수 있다.

근래에 스타트업 비지니스가 발달하고 또한 사용하는 기술 스택들이 복잡해짐에 따라 각각의 기술 스택에 대한 설치나 운영에 대한 인력을 두기보다는 PaaS와 같은 매니지드 서비스를 이용해서 애플리케이션을 개발하는  방식이 스타트업을 중심으로 많이 이루어지고 있다.

구글 클라우드 소개

구글 클라우드는 전세계에 걸쳐 유투브와 구글 검색, 이메일등의 서비스를 하는 구글의 인프라의 경험을 구글 외부의 개발자들에게 개방하기 위해서 개발된 클라우드 서비스이다.

글로벌을 대상으로 서비스를 하던 경험을 기반으로 글로벌 그리고 대용량 서비스에 적절한 특징들을 많이 가지고 있는데, 그 중에서 눈여겨 볼만한것이 네트워크와 빅데이타 서비스이다.

네트워크

구글은 자체 서비스를 위해서 전세계에 걸쳐서 많은 네트워크 망을 가지고 있다. 특히 전세계에 걸쳐 70개 이상의 Pop (Point of Presence)라는 서버들을 보유하고 있는데, 구글 클라우드에서 동작하는 애플리케이션은 클라이언트가 직접 그 데이타 센터의 서버로 인터넷을 통해서 접속하는 것이 아니라, 먼저 가장 가까운 Pop 서버로 연결이 된 후, Pop 서버와 구글 클라우드 데이타 센터간은 구글의 전용 광케이블을 이용해서 접속이 되기 때문에 빠른 네트워크 성능을 보장한다.

예를 들어 한국에서 미국의 서버를 접속하게 되면 일반 인터넷망을 타는 것이 아니라, 가까운 일본에 있는 구글 Pop 서버를 접속한 후에, 구글 광케이블망을 통해서 미국 데이타 센터에 접속하게 된다. 얼마전 7월에 일본-미국을 연결하는 20TB의 구글 클라우드 전용망이 오픈되었기 때문에, 훨씬 더 빠른 접속 속도를 보장받을 수 있다.

이런 특징 때문에, 구글 클라우드를 사용할 경우 여러 국가에 서비스를 제공하는 글로벌 서비스의 경우 이러한 망 가속의 잇점을 볼 수 있다.


또한 내부 네트워크 역시, 1 CPU당 2 GB의 대역폭을 제공한다. (실제 테스트해보면 1.6~1.8 GB정도가 나온다) 최대 8 CPU에 16 GB의 대역폭 까지 지원하기 때문에, 내부 노드간 연산이 많거나 또는 노드간 통신이 많은 클러스터링 솔루션을 사용할때 별도의 비용을 지불하지 않고도 넉넉한 네트워크 대역폭 사용이 가능하다.

빅데이타

구글은 사람들에게 알려진바와 같이 데이타를 기반으로 한 서비스에 강하며 특히 빅데이타를 처리하는 능력이 뛰어난 회사이다. 이러한 빅데이타 처리 기술이 클라우드 서비스로 제공되는데, 알파고와 같은 예측 통계학적인 머신러닝이나 딥러닝 이외에도, 분석 통계학적인 데이타 분석 서비스가 많은데 그중에서 특출난 서비스들로 Pub/Sub, Dataflow, BigQuery 등이 있다.

Pub/Sub은 전달 보장이 가능한 대용량 큐 시스템이다. 오픈소스 Kafka와 같은 서비스라고 보면 되고, 이러한 분산큐를 직접 깔아서 운영하기 어려운 사람들에게 쉽게 대용량 큐 서비스를 제공한다.

Dataflow는 스트리밍 및 배치 분석 시스템인데, Spark Streaming이나 Apache Flink와 같은 스트리밍 프레임웍과 유사하다고 보면된다. 윈도우,트리거,워터마크와 같은 스트리밍 서비스에서 발전된 개념을 이미 내장하고 있다.

마지막으로 BigQuery는 대용량 데이타 저장/분석 시스템으로 SQL과 같은 문법으로 데이타 분석이 가능하며, 데이타 저장 비용은 구글에서도 가장 싸다는 데이타 저장소인 CloudStorage 보다 저렴하며, 1000억개, 4TB의 레코드에 대해서 like 검색을 하는데 소요되는 시간이 불과 30여초로, 빠른 성능을 보장한다. (이 30초 동안에 CPU가 수천개, DISK역시 수천개가 사용된다.)


구글 클라우드 앱앤진

오늘 살펴볼 서비스는 구글 클라우드 중에서도 앱앤진이라는 PaaS 서비스이다. 구글은 처음 클라우드 서비스를 시작할때 PaaS 기반의 서비스를 제공하였고, 내부적으로도 PaaS 서비스를 오랫동안 제공해온 만큼, 진보된 형태의 PaaS 플랫폼을 가지고 있다.

그중에서 이번에는 새롭게 앱앤진 Flex environment 라는 새로운 서비스를 소개 하였다.

Flex environment에는 기존의 Java,PHP뿐 아니라, 요즘 스타트업등에서 많이 사용되는 Python, Ruby on rails 그리고 node.js를 지원한다.

그리고 재미있는 점 중의 하나는 일반적인 PaaS가 VM에 대한 로그인 (telnet 이나 SSH)를 지원하지 않는데 반하여, Flex environment 는 SSH 로그인을 제공함으로써, 고급 사용자들에게 많은 기능과 디버깅에 있어서 편의성을 제공한다.

계정 가입

자아 그러면 이제, 구글 앱앤진 Flex environment를 이용하여, node.js 애플리케이션을 배포해보도록 하자.

먼저 GCP 클라우드를 사용하기 위해서는 구글 계정에 가입한다. 기존에 gmail 계정이 있으면 gmail 계정을 사용하면 된다. http://www.google.com/cloud 로 가서, 좌측 상당에 Try it Free 버튼을 눌러서 구글 클라우드에 가입한다.




다음 콘솔에서 상단의 Google Cloud Platform 을 누르면 좌측에 메뉴가 나타나는데, 메뉴 중에서 “결제" 메뉴를 선택한후 결제 계정 추가를 통해서 개인 신용 카드 정보를 등록한다.


개인 신용 카드 정보를 등록해야 모든 서비스를 제한 없이 사용할 수 있다.  단 Trial의 경우 자동으로 한달간 300$의 비용을 사용할 수 있는 크레딧이 자동으로 등록되니, 이 범위를 넘지 않으면 자동으로 결제가 되는 일이 없으니 크게 걱정할 필요는 없다. (사용자가 Plan을 올려야 실제 카드에서 결재가 된다. 그전에는 사용자의 카드에서 결재가 되지 않으니 걱정하지 마시기를)


프로젝트 생성

계정 생성 및 결제 계정 세팅이 끝났으면 프로젝트를 생성한다.

프로젝트는 VM이나 네트워크 자원, SQL등 클라우드 내의 자원을 묶어서 관리하는 하나의 집합이다. 여러 사람이 하나의 클라우드를 사용할때 이렇게 프로젝트를 별도로 만들어서 별도로 과금을 하거나 각 시스템이나 팀별로 프로젝트를 나눠서 정의하면 관리하기가 용이하다.


화면 우측 상단에서 프로젝트 생성 메뉴를  선택하여 프로젝트를 생성한다.



프로젝트 생성 버튼을 누르면 아래와 같이 프로젝트 명을 입력 받는 창이 나온다. 여기에 프로젝트명을 넣으면 된다.


node.js 애플리케이션 준비

클라우드 프로젝트가 준비되었으면, 이제 구글 클라우드에 배포할 node.js 애플리케이션을 준비해보자.

Node.js 애플리케이션은 Express 프레임웍을 이용한 간단한 웹 애플리케이션을 작성해서 배포하도록 한다.

기본적으로 nodejs와 npm이 설치되어 있다고 가정한다. 이 글에서는 node.js 4.4.4 버전과 npm 2.15.1 버전을 기준으로 작성하였다.

Express generator 설치

Express 프로젝트는 Express 프로젝트 고유의 디렉토리 구조와 의존되는 파일들을 가지고 있다. 그래서 프로젝트를 생성하려면 Express generator가 필요하다. Express generator가 Express 프로젝트에 맞는 프로젝트 구조를 생성해주는데, Express generator를 설치하려면 아래 그림과 같이

% npm install express-generator -g

명령을 사용하면 설치가 된다.



프로젝트 생성

Express generator 설치가 끝났으면 이제, express 프로젝트를 생성해보자.

다음 명령어를 이용하여 helloappengine이라는 Express 프로젝트를 생성한다.

% express --session --ejs --css stylus helloappengine




Express 프로젝트 및 Express 프레임웍에 대해서는 http://bcho.tistory.com/887 http://bcho.tistory.com/888 글을 참고하기 바란다.

코드 수정

생성된 코드에서  ~/routes/index.js 파일을 다음과 같이 수정한다.


var express = require('express');

var router = express.Router();


/* GET home page. */

router.get('/', function(req, res, next) {

 res.render('index', { title: 'Hello Appengine' });

 console.log('Hello Appengine');

});


module.exports = router;


의존성 모듈 설치와 node.js 런타임 버전 정의

Express 애플리케이션 개발이 다 끝났다. Express 애플리케이션을 실행하려면, Express 프로젝트에 필요한 모듈들을 설치해야 하는데, 의존 모듈들은 ~/package.json에 이미 자동으로 생성되어 있다.

이 파일을 다음과 같이 수정한다.

“engines”라는 항목에 node.js의 버전을 아래와 같이 명기하는데, 이는 구글 앱앤진이 이 애플리케이션을 수행할때 어느 버전의 node.js를 가지고 수행을 할지를 지정한다.


{

 "name": "helloappengine",

 "version": "0.0.0",

 "private": true,

 "scripts": {

   "start": "node ./bin/www"

 },

 "engines":{

    "node":"4.4.4"

 },

 "dependencies": {

   "body-parser": "~1.15.1",

   "cookie-parser": "~1.4.3",

   "debug": "~2.2.0",

   "ejs": "~2.4.1",

   "express": "~4.13.4",

   "morgan": "~1.7.0",

   "serve-favicon": "~2.3.0",

   "stylus": "0.54.5"

 }

}




이 의존성 모듈 설치는 package.json 파일이 있는 디렉토리에서 아래와 같이

% npm install

명령어만 실행해주면 자동으로 설치된다.


테스트

샘플 애플리케이션 개발 및, 이를 실행하기 위한 환경 설정이 모두 끝났다.

그러면 이제 샘플 애플리케이션을 로컬에서 실행해보자.

실행은 ~/ 디렉토리에서 다음과 같은 명령을 실행하면된다.

% node ./bin/www



실행한 후, 결과를 확인하려면 http://localhost:3000 번으로 접속하면 아래와 같은 결과가 나오는 것을 확인할 수 있다.



구글 앱앤진으로 배포 준비

애플리케이션 개발 및 테스트가 끝났으면 이제 구글 앱앤진으로 이 애플리케이션을 배포해보자.

app.yaml 파일 작성

구글 앱앤진 Flex environment 로 애플리케이션을 배포하기 위해서는 애플리케이션에 대한 기본 정보를 app.yaml에 정의해야 한다. 이 파일은 배포할 node.js 애플리케이션이 있는 ~/ 디렉토리에 위치 시킨다.

runtime은 어떤 언어 (node.js , ruby 등)을 지정하고, VM으로 실행할지를 vm:true 로 정의한다.


runtime: nodejs
vm: true


Google Cloud SDK 설치

다음으로 배포를 하려면, gcloud라는 명령을 사용해야 하는데, 이 명령어는 Google Cloud SDK를 설치해야 사용할 수 있다. Google Cloud SDK는 https://cloud.google.com/sdk/downloads 에서 다운 받아서 설치한다.

Google cloud SDK 설정하기

Google cloud SDK 설치가 끝났으면, 혹시 google cloud SDK가 업데이트 되었을 수 있으니, 다음 명령어를 이용하여 최신 SDK로 업데이트 한다. (알파,베타 버전과 같은 신 기능이 들어가면 업데이트가 된다.)


% gcloud component update


라는 명령어를 수행하면 다음과 같이 업데이트 된 내용이 나오면서 업데이트 를 할 것인지 물어보고 “Y”를 선택하면 자동으로 Google Cloud SDK를 업데이트 한다.



Google Cloud SDK 배포가 끝났으면 gcloud 명령을 이용하기 위해서 초기화 작업을 해야 한다. 초기화 작업은 gcloud 명령을 사용했을때, 내 구글 클라우드 프로젝트 중 어느 프로젝트에 명령을 내릴 것인지, 그리고 어떠한 구글 계정을 사용할것인지 그리고 명령을 내릴때 어떤 리전을 디폴트로 해서 명령을 내릴것인지를 정하는 것이다. 사용자에 따라서 여러개의 프로젝트를 가질 수 도 있고, 또한 관리 목적상 여러개의 클라우드 계정을 가질 수 도 있기 때문이다.

초기화 방법은 다음과 같이 명령어를 실행하면 된다

% gcloud init

이 명령을 사용하면, 앞에서 언급한 바와 같이, gcloud 명령을 내릴 때 사용할 계정과, 디폴트 프로젝트 그리고, 리전을 선택하게 된다.


이제 배포를 위한 모든 준비가 끝났다.

구글 앱앤진으로 배포

이제 배포를 해보자. 배포는 매우매우 쉽다.

앞서 작성한 Express 애플리케이션이 있는 ~/ 디렉토리에 가서 다음 명령어를 수행하면 된다.

% gcloud app deploy

명령을 실행하면 다음과 같이 node.js 애플리케이션이 배포 되는 과정을 볼 수 있다.

배포는 수분이 소요된다. (내부적으로는 Docker 컨테이너를 이용하여 배포가 된다.)

서비스 기동확인

배포가 완료되면 자동으로 서비스가 기동이 된다.

서비스 기동 확인은 http:{프로젝트명}.appspot.com 으로 자동으로 서비스가 뜨게 된다.

해당 URL을 접속해보자. 여기서는 프로젝트명이 “useful-hour-138023”이다.


서비스가 정상적으로 기동 됨을 확인하였다.

모니터링

구글 클라우드 콘솔에 들어가 보면 현재 기동중인 node.js 앱을 확인할 수 있다.

콘솔에서 “App Engine”을 선택하면 다음과 같은 화면이 나온다.



아래 Instances 부분을 보면 Flexible 이라는 이름으로 현재 2개의 인스턴스가 기동되고 있고, 평균 QPS (Query Per Second : 초당 처리량)이 10.898 인것을 확인할 수 있다.


매니지드 서비스이기 때문에, 부하가 늘어나면 자동으로 인스턴스를 추가하고 줄어들면 자동으로 삭제하여 부하에 탄력적으로 대응을 자동으로 해준다.


로그 모니터링은 클라우드 콘솔에서 “Logging”이라는 메뉴를 선택하면 아래와 같은 화면이 나온다.


앱앤진 관련 request log 및 기타 로그들이 다 나오는데, 앞서 샘플코드에서 console.log(“Hello Appengine”) 명령어를 이용하여 Stdout으로 “Hello Appengine”이라는 문자열을 출력하도록 하였기 때문에, 이 문자열이 제대로 출력되었는지를 확인해보자.

App Engine 을 선택하고, 다음 “stdout”을 선택하면 위와 같이 앱앤진의 Stdout으로 출력한 로그들만 볼 수 있는데, 위와 같이 “Hello Appengine” 문자열이 출력된것을 확인할 수 있다.

수정 및 재 배포

그러면 다음으로 애플리케이션을 수정해서 배포해보자. routes/index.js를 다음과 같이 수정해보자


var express = require('express');

var router = express.Router();


/* GET home page. */

router.get('/', function(req, res, next) {

 res.render('index', { title: 'Hello Appengine is updated' });

 console.log('Hello Appengine is updated');

});


module.exports = router;



코드 수정이 끝났으면 배포를 해보자. 재 배포 역시 간단하게

%gcloud app deploy

명령어를 수행하면 간단하게 배포가 된다.

배포가 완료된 후 URL로 들어가서 확인을 해보면


애플리케이션이 업데이트 된것을 확인할 수 있다.

롤백

앱앤진의 장점 중에 하나가 배포도 쉽지만, 롤백도 매우 쉽게 가능하다는 것이다.

클라우드 콘솔에서 App Engine을 선택하고, 아래와 같이 Versions라는 메뉴를 선택하면 아래와 같은 그림을 볼 수 있다.




현재 두개의 버전이 배포되어 있고, 위의 최신 버전에 Traffic Allocation이 100%로 되어 있는 것을 확인할 수 있다. 이는 새로 배포된 버전으로만 트래픽이 100% 가고 있다는 의미인데, 롤백은 트래픽을 예전 버전으로 라우팅 해주고, 새버전의 서비스를 정지 시키면 된다.


아래 그림과 같이 예전 버전을 선택한 후에, 상단 메뉴에서 “Migrate Traffic”을 선택한다.



그러면 아래와 같이 트래픽이 이전 버전으로 100% 가고 있음을 확인할 수 있다.

그리고 나서 새 버전을 선택한 후 상단의 STOP 버튼을 눌러주면 아래 그림과 같이 새버전의 상태가 Serving 에서 Stopped로 변경된것을 확인할 수 있다.




예전 버전으로 롤백이 잘 되었는지를 확인해보자.

아래 그림처럼 예전 버전으로 롤백이 되었음을 확인할 수 있다.



버전별로 트래픽 분산하기

앱앤진의 장점 중의 하나가 여러 버전을 유지할 수 있고, 버전간에 트래픽을 자유롭게 조절할 수 있다. 예를 들어서 v1으로 90%, v2로 10% 의 트래픽을 보내는 것등이 가능하다.


이렇게 트래픽을 조절하는 것은 크게 2가지 방법으로 활용이 가능한데, 서버단의 A/B 테스팅과 카날리 테스팅이다.

A/B 테스팅은 사용자를 두개 이상의 집단으로 나눈후, 기능 A,B 에 대한 반응을 본 후, 반응이 좋은 기능을 선택하는 방식으로, 모바일 클라이언트단에서 많이 사용되고, 서버단에는 구현이 쉽지 않았는데, 트래픽을 버전별로 나눠서 주는 기능을 사용하면 손쉽게 A/B 테스팅을 수행할 수 있다.


다음으로 카날리 테스트는 옛날에 광부들이 광산에 들어갈때 카나리아 새를 데리고 들어가서 유독가스가 나오면 카나리아 새가 먼저 죽는 것을 보고 위험을 감지하는 방법에서 유래된것으로 서버 배포에서는 전체 사용자를 대상으로 새 버전을 배포하는 것이 아니라 1~2%의 사용자에게 배포해보고 문제가 없으면 전체 사용자에게 배포하는 개념으로, 마찬가지로 v2에 1~2%의 트래픽만 할당하고 나머지 98~99%는 v1에 할당하는 방식으로, 해서 v2에 대한 안정성 검증을 한 후에, v2를 전체 배포 하는데 활용할 수 있다.


버전간 트래픽 비율을 지정하는 방법은 클라우드 콘솔의 앱앤진 메뉴에서 Version을 선택하고 상단 메뉴에서 → 가 두개 겹쳐진 아이콘 (맨 우측)

를 선택하면 아래와 같이 Split traffic 이라는 메뉴가 나온다.

여기서 1개 이상의 Version을 추가한 후에, 각 버전으로 보낼 트래픽의 비중 (%)을 정의한다.






위의 예제에서는 xxx4403 버전으로 30%, xxxx3136 버전으로 70%의 트래픽을 보내도록 하였다.

설정을 완료한 후에 SAVE를 누르고, 다시 Version 부분을 보면, 수분 있다가 아래 그림과 같이 xxxx3136 버전에 70% 트래픽이 xxx4403 버전으로 가게 된다.


부가 기능에 대해서

구글 클라우드에서 앱앤진만 쓸 수 있는 몇가지 좋은 서비스 들이 있는데, 다음과 같다.

매니지드 memcached

앱앤진에서는 구글 매니지드 memcached 서비스를 사용할 수 있다 별도의 설정 없이 바로 사용이 가능하며, 최대 100GB 까지 사용이 가능하다. (100GB 이상도 요청하면 사용이 가능) Shared memcached의 경우에는 별도의 비용없이 일반적인 캐쉬로 사용이 가능하지만, 용량이나 성능등이 보장이 되지 않으니 주의가 필요하다.

https://cloud.google.com/appengine/docs/python/memcache/

보안 스캐닝 (Security Scanning)

매우 유용한 기능중의 하나가 보안 스캐닝 기능이다 앱앤진 메뉴에서 Security Scanning이라는 메뉴를 선택하면 앱앤진으로 개발한 웹 사이트에 대한 보안 스캐닝을 할 수 있는데, 한번만 스캐닝할 수 도 있고 자동 스케쥴 방식으로, 주기적으로 스캐닝 하는 것도 가능하다.


보안 정책이 수시로 업데이트 되기 때문에, 정기적으로 보안 스캔을 하는 것을 권장한다.

텍스트 검색 (Search)

node.js에는 지원하지 않지만, Python,Java,PHP,Go (앱앤진 Standard environment)에서만 지원되는 기능중 하나는 구글 검색 엔진과 같은 검색 엔진을 지원한다. 텍스트 검색 HTML 그리고 GEO_POINT (위/경도 기반 검색)이 가능하다.

https://cloud.google.com/appengine/docs/java/search/?hl=en_US

결론

지금까지 간략하게나마 구글 클라우드에 대한 소개, 앱앤진에 대한 개념 및, 간단한 node.js express 애플리케이션을 작성해서 배포해봤다. 다음글은 앱앤진에 대한 조금 더 구체적인 환경에 대해서 알아보도록 하겠다.


Heroku 클라우드에 node.js 애플리이션을 배포하기


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



가장 빠르게 이해하는 방법은 직접 해보는 것이다. Heorku 클라우드에 대한 이해를 위해서 express를 이용해 구현된 node.js 애플리케이션을 직접 배포해서 실행해보자

이 예제는 node.js 4.3.1 버전과 express 4.13.4 버전을 기준으로 작성되었다.


Heroku 가입

Heroku사용을 위해서 Heroku계정을 만들자. http://www.heroku.com 에 접속해서 Sign up 메뉴로 들어가면, 간단하게 이름과 이메일 정도의 정보만 넣으면 간단하게 계정을 만들 수 있다.



Figure 1 Heroku 가입 화면


다른 클라우드의 경우 향후 유료 사용을 위해서 회원 가입시 처음부터 신용카드 정보를 요구 하는 경우가 있어서 신용카드가 없는 학생등은 가입이 안되는 경우가 있지만, Heroku의 경우 별다른 부담없이 무료로 계정을 생성해서 사용할 수 있다. 지금부터 진행할 예제는 무료 계정에서 한개의 웹 서버를 기동하여 node.js 애플리케이션을 기동하는 예제로 별도의 비용이 필요없다.


계정이 생성되었으면 Heroku 웹 콘솔에 로그인해보자




Figure 2 Heroku 대쉬 보드


첫 대쉬보드에서는 각 개발플랫폼에서 개발을 시작하는 방법에 대한 가이드를 제공한다.


node.js 애플리케이션 준비


Heroku 계정이 준비되었으면, Heroku에 배포할 node.js 애플리케이션을 준비해보자. 

“Hello Heroku”를 출력하는 간단한 node.js 애플리케이션을 작성하자.

다음과 같이 express generator를 이용해서 node.js 애플리케이션을 생성한다.


% express -–session --ejs --css stylus helloheroku

% cd helloheroku

% npm install


애플리케이션을 설치한 후에, 애플리케이션 디렉토리로 들어가서 npm install 명령을 이용하여, 애플리케이션에서 사용되는 의존성을 가지고 있는 모듈들을 설치한다.


다음으로 /routes/index.js를 다음과 같이 수정한다.


var express = require('express');

var router = express.Router();


/* GET home page. */

router.get('/', function(req, res, next) {

  res.render('index', { title: 'Heroku' });

  console.log(' hello heroku ');

});


module.exports = router;


Heroku에 배포하는 node.js 애플리케이션을 개발할때, 주의할점 중의 하나가 package.json이다.

보통 로컬 개발환경에 npm 패키지들이 이미 설치 되어 있는 경우 실행이 제대로 되고, 문제가 없기 때문에, package.json에 필요한 모듈에 대한 정의를 빼먹는 경우가 많다. Heroku에 애플리케이션을 배포할때는 의존 패키지들이 설치되어 있지 않기 때문에 이를 설치 해야 하는데,  Heroku는 자동으로 package.json에 정의된 npm 모듈들을 설치해 준다. 반드시 배포전에 package.json에 필요한 모듈들이 제대로 정의되었는지를 확인해보자


소스코드가 준비되었으면 Procfile 이라는 것을 준비해야 한다. 위에서 만든 애플리케이션의 루트”/” 디렉토리에 다음과 같은 내용으로 Procfile을 작성하자.


web: node ./bin/www


Procfile은 애플리케이션을 Heorku에 배포 해서 실행할때, Heroku 클라우드가 맨 처음 실행하는 명령어를 지정하는 파일이다. 보통 애플리케이션을 기동하기 위한 명령어를 기술한다.

앞서 작성한 Procfile을 보면, “web:” 이라고 기술하였는데, 배포하고자 하는 애플리케이션이 웹 애플리케이션임을 정의한다. web 타입의 애플리케이션만 Heroku의 내부 HTTP 로드밸런서에 연결이 되서 웹 요청을 받을 수 있다.

다음으로 “node ./bin/www” 라고 지정하였는데, node.js 애플리케이션을 기동하기 위한 명령어이다. 우리가 작성한 애플리케이션은 express애플리케이션으로 기동 로직이 ./bin/www 에 들어 있다.


추후에 추가로 설명하겠지만, node.js는 모니터링등을 위해서 forever나 pm2와 같은 추가적인 도구를 이용해서 기동을 하는 경우가 있다. 실행을 할때 forever ./bin/www 식으로 실행하는데, Heroku에서 실행하려면 다음과 같이 실행하면 된다.


web: ./node_modules/.bin/forever ./bin/www


이제 배포를 위한 node.js 애플리케이션 준비가 끝났다. Heorku에 배포해보자.


Heroku 툴벳 설치


계정이 준비되고 애플리케이션 개발이 끝났으면, Heroku에 접속해서 배포를 진행해야 한다. Heroku는 툴벳이라는 커멘트라인 인터페이스 (CLI : Command line interface)를 이용해서 사용한다. 

툴벳은 https://toolbelt.heroku.com/ 에서 다운로드 받을 수 있다.




Figure 3 Heroku 툴벳 다운로드 페이지


툴벳을 인스톨했으면 실행해보자. 프롬프트 상에서 

%heroku 

명령어를 수행하면 간단한 사용법이 나온다.

이 툴벳을 통해서 Heroku에 명령을 내리기 위해서는 자신의 계정으로 로그인을 해야 한다. 로그인은 “heroku login”이라는 명령을 사용해서, Heroku 가입시 생성한 계정으로 로그인을 하면 된다.




Figure 4 툴벳에서 Heroku 로그인


Heroku 배포 준비


로그인이 끝났으면, Heroku내에 앱을 생성한다. Heroku의 앱은 애플리케이션 단위로, 이 앱단위로 배포를 하고 운영을 하게 된다. 

앱 생성 방법은 다음과 같다.


%heroku app:create {앱이름}


이 예제에서는 helloherokuterry 라는 이름의 앱을 생성할것이다.

이 앱 이름은 Heroku 클라우드에 걸쳐서 유일한 이름으로 사용을 해야 하기 때문에, 위의 이름으로 앱을 생성하고자 하면 이미 저자가 해당 이름을 사용하고 있기 때문에, 아마도 에러가 날것이다. 다른 적절한 이름을 이용해서 앱을 생성하자


아래는 helloherokuterry 라는 이름의 앱을 생성하고, “heroku apps” 를 이용하여 현재 생성된 앱 리스트를 확인해서 helloherokuterry 라는 앱이 제대로 생성이 되었는지 확인 하는 화면이다.




Figure 5 Heroku에 애플리케이션을 생성하는 화면


만약에 잘못 앱을 생성하였으면 다음 명령어를 이용해서 앱을 삭제할 수 있다.


%heroku app:delete {앱이름}


앱이 생성되었으면, 작성한 애플리케이션을 배포해야 하는데, heroku의 코드 배포는 git 소스코드 관리 시스템을 사용한다.

앱 디렉토리에서 git 리포지토리를 생성한다.

%git init .

다음, 작성한 애플리케이션들을 git에 추가한다.

%git add *

추가된 애플리케이션 코드들을 commit 한다.

%git commit


이제 로컬 git 리파지토리에 애플리케이션 코드들이 저장되었다. 이 코드들을 Heroku에 전송하자.

git에 Heroku 클라우드 상의 git 저장소를 리모트 저장소로 지정해야 한다.

Heroku의 git 저장소를 리모트 저장소로 지정하는 방법은 다음과 같다. 


%heroku git:remote -a {앱이름}


여기서는 애플리케이션의 이름이 helloherokuterry이기 때문에 다음과 같이 리모트 저장소를 추가하겠다.

% heroku git:remote –a helloherokuterry


heroku의 git 리파지토리가 리모트 저장소로 지정되었다.

아래 명령어를 이용해서 로컬에 저장된 코드를 heroku로 올려보자


%git push heroku master


명령어를 실행하면 소스코드를 Heroku에 배포 하는 것을 확인할 수 있다.




Figure 6 git를 이용하여 Heroku에 앱을 배포하는 화면


배포는 단순하게 소스 코드만을 올리는 것이 아니라 올라간 소스코드를 확인해서, package.json에 지정된 의존성 있는 모듈들을 설치 한다. 아울러 환경변수를 세팅한다.

Heroku의 경우 node.js에 대해서 디폴트 환경 변수를 정의하는데

NPM과 node.js에 대한 환경 변수를 다음과 같이 설정한다. 모드를 운영 모드로 설정하는 부분이다.


remote:        NPM_CONFIG_LOGLEVEL=error

remote:        NPM_CONFIG_PRODUCTION=true

remote:        NODE_ENV=production

remote:        NODE_MODULES_CACHE=true


배포에서 git를 사용하였는데, 혹시나 git에 대해 생소한 분들을 위해서 간략하게 배포 흐름을 정리하고 넘어가자.  




Figure 7 git를 이용한 Heroku 배포 개념도


개발환경에서 작성된 소스코드는 git의 로컬 저장소 (Local repository)로 저장되기전에 git add명령어를 이용하여 스테이징 단계 (stage)로 이동된다. 소스를 저장소에 저장하기 전 단계로, 전체 코드 중에서 어떤 파일들을 저장할지를 선택하고 검토하는 일종의 중간단계이다.

스테이징이 끝나면, git commit 명령을 이용하여 로컬 저장소 즉 내 PC에 있는 git 저장소에 소스 코드를 저장한다.

다음 이 코드를 원격에 있는 Heroku의 git 저장소로 전송을 해야 하는데, 이를 푸쉬(push)라고 한다. git push 명령어를 이용해서 실행하며, 코드가 Heroku의 git 저장소에 저장이 되면, 기동을 하기 위한 환경 변수 설정이나 npm 모듈들을 설치 하는 작업을 수행한다.


이제 애플리케이션 실행을 위한 모든 준비가 끝났다. 배포한 애플리케이션을 기동해 보자


서비스 기동 및 모니터링


서버의 기동은 Heroku에서 web dyno수를 0에서 1로 늘려주면 된다.


%heroku ps:scale web=1


서버가 기동된것을 확인하였으면 웹으로 접속해서 애플리케이션이 제대로 동작하고 있음을 확인하자.

Heroku app의 URL은 http://{heroku app 이름}.herokuapp.com 이다.

우리가 만든 예제는 helloherokuterry라는 앱 이름을 사용했기 때문에, https://helloherokuterry.herokuapp.com/ 가 접속 URL이 된다

또는 간단하게 명령 프롬프트 창 내에서 heroku open 이라는 명령어를 사용하면 해당 URL을 브라우져를 열어서 자동으로 열어준다.


%heroku open




Figure 8 Heroku open 명령어를 이용하여 기동중인 웹사이트를 오픈




Figure 9 Hello Heroku 실행 화면


드디어 Heroku에 node.js 애플리케이션을 배포하고 기동시켰다.

애플리케이션의 구동 상태와 로그를 확인해보자

현재 몇개의 dyno 가 구동되고 있는지를 확인하려면 다음 명령어를 실행하면 된다.


%heroku ps




Figure 10 ps 명령어를 이용하여 동작중인 서버를 확인


현재 web 타입의 1개의 dyno 가 실행되고 있음을 확인할 수 있다.

다음은 애플리케이션 로그를 확인해보자


%heroku logs –tail


이 명령어는 node.js에서 나오는 로그를 모니터링 할 수 있게 해준다



Figure 11 node.js의 로그를 확인


위의 로그를 보면 03:11:32에 서버가 기동 되었음을 확인할 수 있고

03:11:34초에 http://helloherokuterry.herokuapp.com/  URL이 호출되었음을 확인할 수 있다.

그리고 그 아래 애플리케이션에서 console.log(‘hello heroku’)로 출력한 로그도 같이 출력됨을 확인할 수 있다.


테스트가 끝났으면 실행중인 서버를 내려보자.

서버를 내리는 방법은 ps:scale 명령어를 이용하여 web dyno수를 1에서 0으로 변경해주면 된다

%heroku ps:scale web=0

명령을 실행하고 ps 명령어를 이용해서 현재 기동중인 dyno 수를 확인해보면, 아무 서버도 기동되지 않음을 확인할 수 있다.



Figure 12 동작중인 dyno를 종료하는 화면


로그에서도 아래와 같이 dyno를 끄는 로그를 확인할 수 있다.



Figure 13 dyno 종료 로그를 확인



대충보는 Storm #3-Storm 싱글 클러스터 노드 설치 및 배포

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

 

지난번에는 간략하게, Storm을 이용한 HelloStorm 애플리케이션을 개발용 클러스터인 Local Cluster에서 구동해봤다. 이번에는 운영용 클러스터를 설정하고, 이 운영 클러스터에 지난번에 작성한 HelloStorm 토폴리지를 배포해보도록 한다.

Storm 클러스터의 기본 구조

Storm 클러스터를 기동하기 전에, 클러스터가 어떤 노드들로 구성이 되는지 먼저 알아보도록 하자 Storm 클러스터는 기본적으로 아래와 같은 3가지 구성요소로 구성이 되어 있다.

먼저 주요 노드인 Nimbus Supervior 노드에 대해서 알아보자, Nimbus Supervisor 노드는 각각 하나의 물리 서버로 생각하면 된다.


Nimbus

Nimbus는 마스터 노드로 주요 설정 정보를 가지고 있으며, Nimbus 노드를 통해서 프로그래밍 된 토폴로지를 Supervisor 노드로 배포한다. 일종의 중앙 컨트롤러로 생각하면 된다. Storm에서는 중앙의 하나의 Nimbus 노드만을 유지한다.

Supervisor

Supervisor 노드는 실제 워커 노드로, Nimbus로 부터 프로그램을 배포 받아서 탑재하고, Nimbus로 부터 배정된 작업을 실행하는 역할을 한다. 하나의 클러스터에는 여러개의 Supervisor 노드를 가질 수 있으며, 이를 통해서 여러개의 서버를 통해서 작업을 분산 처리할 수 있다.

Zookeeper

이렇게 여러개의 Supervisor를 관리하기 위해서, Storm Zookeeper를 통해서 각 노드의 상태를 모니터링 하고, 작업의 상태들을 공유한다.

Zookeeper는 아파치 오픈소스 프로젝트의 하나로, 분산 클러스터의 노드들의 상태를 체크하고 공유 정보를 관리하기 위한 분산 코디네이터 솔루션이다.

전체적인 클러스터의 구조를 살펴보면 다음과 같다.



<그림. Storm 클러스터의 구조>

하나의 싱글 머신에 Nimbus가 설치되고, 다른 각각의 머신에 Supervisor가 하나씩 설치된다. 그리고, ZooKeeper 클러스터를 통해서 기동이 된다.

※ 실제 물리 배포 구조에서는 Nimbus Supervisor, ZooKeeper등을 하나의 서버에 분산배포할 수 도 있고, 여러가지 다양한 배포구조를 취할 수 있으나, Supervisor의 경우에는 하나의 서버에 하나의 Supervisor만을 설치하는 것을 권장한다. Supervisor의 역할이 하나의 물리서버에 대한 작업을 관리하는 역할이기 때문에, 한 서버에 여러 Supervisor를 설치하는 것은 적절하지 않다.

 

설치와 기동

Storm 클러스터를 기동하기 위해서는 앞에서 설명한바와 같이 ZooKeeper가 필요하다. Zookeeper를 다운로드 받은 후에, ~/conf/zoo_sample.cfg 파일을 ~/conf/zoo.cfg로 복사한다.

다음으로 ZooKeeper를 실행한다.

% $ZooKeeper_HOME/bin/zkServer.cmd


<그림. 주키퍼 기동 로드>


다음으로 Nimbus 노드를 실행해보자. Storm을 다운 받은 후 압축을 푼다.

다음 $APACHE_STORM/bin 디렉토리에서

%storm nimbus

를 실행하면 nimbus 노드가 실행된다. 실행 결과나 에러는 $APACHE_STORM/logs 디렉토리에 nimbus.log라는 파일로 기록이 된다.

정상적으로 nimbus 노드가 기동이 되었으면 이번에는 supervisor 노드를 기동한다.

%storm supervisor

Supervisor에 대한 노드는 $APACHE_STORM/logs/supervisor.log 라는 이름으로 기록된다.

Storm은 자체적으로 클러서터를 모니터링 할 수 있는 UI 콘솔을 가지고 있다. UI를 기동하기 위해서는

%storm ui

로 실행을 해주면 UI가 기동이 되며 http://localhost:8080 에 접속을 하면 관리 콘솔을 통해서 현재 storm의 작동 상태를 볼 수 있다.



<그림. Storm UI를 이용한 기동 상태 모니터링>


현재 하나의 PC nimbus supervior,UI를 모두 배포하였기 때문에 다음과 같은 물리적인 토폴로지가 된다.



<그림. 싱글 서버에 nimbus supervisor를 같이 설치한 예>

싱글 클러스터 노드에 배포 하기

싱글 노드 클러스터를 구축했으니, 앞의 1장에서 만든 HelloStorm 토폴로지를 이 클러스터에 배포해보도록 하자.

전장의 예제에서 만든 토폴로지는 Local Cluster를 생성해서, 자체적으로 개발 테스트용 클러스터에 토폴로지를 배포하도록 하는 코드였다면, 이번에는 앞에서 생성한 Storm 클러스터에 배포할 수 있는 토폴로지를 다시 만들어야 한다. 이 코드의 차이는 기존 코드와는 다르게 LocalCluster를 생성하지 않고, 기동중인 클러스터에 HelloTopoloy Submit하도록 한다.

package com.terry.storm.hellostorm;

 

import backtype.storm.Config;

import backtype.storm.StormSubmitter;

import backtype.storm.generated.AlreadyAliveException;

import backtype.storm.generated.InvalidTopologyException;

import backtype.storm.topology.TopologyBuilder;

 

public class HelloTopology {

        public static void main(String args[]){

               TopologyBuilder builder = new TopologyBuilder();

               builder.setSpout("HelloSpout", new HelloSpout(),2);

               builder.setBolt("HelloBolt", new HelloBolt(),4).shuffleGrouping("HelloSpout");

              

               Config conf = new Config();

               // Submit topology to cluster

               try{

                       StormSubmitter.submitTopology(args[0], conf, builder.createTopology());

               }catch(AlreadyAliveException ae){

                       System.out.println(ae);

               }catch(InvalidTopologyException ie){

                       System.out.println(ie);

               }

              

        }

 

}

<그림. HelloTopology.java>


토폴로지 클래스를 만들었으면 이를 빌드해보자

%mvn clean install

을 실행하면 ~/target 디렉토리에 토폴로지 jar가 생성된것을 확인할 수 있다.



jar 파일을 배포해보도록 하자.

배포는 storm {jar} {jar파일명} {토폴로지 클래스명} {토폴로지 이름} 명령을 실행하면 된다.

% storm jar hellostorm-0.0.1-SNAPSHOT.jar com.terry.storm.hellostorm.HelloTopology HelloTopology

배포 명령을 내리면 $APACHE_STORM_HOME/logs/nimbus.log에 다음과 같이 HelloTopology가 배포되는 것을 확인할 수 있다.


2015-01-25T07:35:03.352+0900 b.s.d.nimbus [INFO] Uploading file from client to storm-local\nimbus\inbox/stormjar-8c25c678-23f5-436c-b64e-b354da9a3746.jar

2015-01-25T07:35:03.365+0900 b.s.d.nimbus [INFO] Finished uploading file from client: storm-local\nimbus\inbox/stormjar-8c25c678-23f5-436c-b64e-b354da9a3746.jar

2015-01-25T07:35:03.443+0900 b.s.d.nimbus [INFO] Received topology submission for HelloTopology with conf {"topology.max.task.parallelism" nil, "topology.acker.executors" nil, "topology.kryo.register" nil, "topology.kryo.decorators" (), "topology.name" "HelloTopology", "storm.id" "HelloTopology-1-1422138903"}

2015-01-25T07:35:03.507+0900 b.s.d.nimbus [INFO] Activating HelloTopology: HelloTopology-1-1422138903

2015-01-25T07:35:03.606+0900 b.s.s.EvenScheduler [INFO] Available slots: (["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6702] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6701] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6700])

2015-01-25T07:35:03.652+0900 b.s.d.nimbus [INFO] Setting new assignment for topology id HelloTopology-1-1422138903: #backtype.storm.daemon.common.Assignment{:master-code-dir "storm-local\\nimbus\\stormdist\\HelloTopology-1-1422138903", :node->host {"226ceb74-c1a3-4b1a-aab5-2384e68124c5" "terry-PC"}, :executor->node+port {[3 3] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703], [6 6] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703], [5 5] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703], [4 4] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703], [7 7] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703], [2 2] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703], [1 1] ["226ceb74-c1a3-4b1a-aab5-2384e68124c5" 6703]}, :executor->start-time-secs {[7 7] 1422138903, [6 6] 1422138903, [5 5] 1422138903, [4 4] 1422138903, [3 3] 1422138903, [2 2] 1422138903, [1 1] 1422138903}}

2015-01-25T07:37:48.901+0900 b.s.d.nimbus [INFO] Updated HelloTopology-1-1422138903 with status {:type :inactive}

해당 토폴로지가 배포되었는지를 확인하려면 storm list라는 명령어를 사용하면 현재 기동되고 있는 토폴로지 목록을 확인할 수 있다.




<그림. storm list 명령으로 기동중인 토폴로지를 확인>


실행 결과는 $APACHE_STORM_HOME/logs 디렉토리를 보면 worker-xxx.logs 라는 파일이 생긴것을 확인해 볼 수 있는데, 파일 내용을 보면 다음과 같다.

2015-01-25T07:35:08.908+0900 STDIO [INFO] Tuple value ishello world

2015-01-25T07:35:08.908+0900 STDIO [INFO] Tuple value ishello world

우리가 앞서 구현한 Bolt에서 System.out으로 출력한 내용이 출력된다.

동작을 확인하였으면, 이제 기동중인 토폴로지를 정지 시켜 보자. 토폴로지의 정지는 storm deactivate {토폴로지명} 을 사용하면 된다. 아까 배포한 토폴로지 명이 HelloTopology였기 때문에 다음과 같이 토폴로지를 정지 시킨다.

%storm deactivate HelloTopology

그 후에 다시 storm list 명령을 이용해서 토폴로지 동작 상태를 확인해보면 다음과 같다.



< 그림. Storm  토폴로지 정지와 확인 >


만약에 Topology를 재 배포 하려면 storm kill로 해당 토폴로지를 삭제한 후에, 다시 배포 해야 한다. 이때 주의할점은 storm kill로 삭제해도 바로 삭제가 안되고 시간 텀이 있으니 약간 시간을 두고 재 배포를 해야 한다.


지금까지 간단하게, 운영용 클러스터를 구성하고, 운영 클러스터에 토폴로지를 배포 하는 것에 대해서 알아보았다.

HelloStorm 코드 구현, 클러스터 노드 구축 및 배포를 통해서 간단하게 스톰이 무엇을 하는 것인지는 파악했을 것으로 안다. 다음 장에는 조금 더 구체적으로 Storm의 개념과 스톰의 아키텍쳐등에 대해서 살펴보도록 하겠다.


 


Amazon Opsworks 소개

시스템에 설치와, 애플리케이션을 자동화

배경

얼마전에, Amazon에서 새로운 클라우드 서비스인 Opsworks를 발표하였다.

[출처:Amazon Opsworks 소개 페이지]

비단 클라우드 뿐만 아니라, 서버 시스템을 개발하다보면, 당면 하는 과제중의 하나가, 소프트웨어 설치와, 애플리케이션의 배포이다.

예전에야 큰 서버 한대에, WAS 하나 설치하고, DB를 다른 서버에 설치해서  사용했지만, 요즘 같은 시대에는 x86 서버 여러대에 WAS를 분산 배치하고, 여러 솔루션들 설치해서 사용하고, 시스템의 구조 역시 훨씬 더 복잡해 졌다. 그래서, 이러한 제품 설치를 자동화 하는 영역이 생겼는데, 이를 Configuration Management(이하 CM)이라고 한다. CM의 개념은 Microsoft System Center Configuration Manager의 제품 소개나 White Paper를 보면, 정리가 잘 되어 있다. Unix 진영의 Configuration Management 영역은 오픈소스로 Puppet이나 Chef가 주류를 이룬다. 그중에서 Chef Recipe 라는 것을 제공하여, 특정 제품에 대해서 설치를 자동화 하는 스크립트를 제공한다. 일일이 제품마다 설치 스크립트를 만들지 않아도, Recipe만 입수하면 손 쉽게 제품을 설치할 수 있다. 특히나 클라우드 환경에서는 개발 환경이나 스테이징,QA 환경을 다이나믹하게 만들었다가 없앴다 하기 때문에 더군다나, 이러한 Configuration Management가 아주 중요하다.

 기존에는 Amazon 이미지인 AMI를 이용하여, 솔루션이 미리 설치된 AMI를 만들어 놓고, bootstrap 기능을 이용하여, 서버가 기동 될때, 특정 환경 변수를 설정하게 하거나

CloudFormation을 이용하여 설치를 자동화 할 수 있었다. 그러나 AMI 방식은 너무 단순해서 복잡한 설정이 어렵고 처음에는 반드시 직접 설치해야 하는 부담이 있었고, CloudFormation은 그 복잡도가 너무 높아서 사용이 어려웠다.

이번에 발표된 Opsworks는 이 중간즘 되는 솔루션으로 보면 된다.

Stack, Layer 그리고 Instance의 개념

Opsworks는 기본적으로 오픈소스 Chef를 이용하여 구현되었다. Chef를 이용해서 아마존에 맞게 개념화 해놓았는데, 먼저 Opsworks의 개념을 보자. Opsworks를 이해하려면 3가지 개념, Stack, Layer 그리고 Instance의 개념을 이해해야 한다.

우리가 PHP 웹서버 + MYSQL로 이루어진 웹 애플리케이션을 개발한다고 하자, 그리고 앞단은 HAProxy를 사용해서 로드 밸런싱을 한다고 하면, 이 애플리케이션은 크게, HA Proxy로 이루어진 Load Balancer Layer 그리고, PHP 웹서버를 사용하는 Application Server Layer 그리고, MySQL로 이루어진 DB Layer 3가지 계층으로 이루어진다.

3 가지 계층은 (HAProxy+PHP Web Server+MySQL) 다른 웹 개발에도 반복적으로 사용될 수 있다.


이렇게 각 계층을 Layer라고 하며, 이 전체 계층을 반복적으로 재사용하기 위한 묶음을 Stack이라고 한다. 그리고, 각 계층에 실제 서버를 기동했을 경우 각 서버를 Instance라고 한다. 예를 들어 하나의 HA Proxy 서버를 띄우고, 이 아래 4개의 PHP Web Server를 기동했다면, 이 시스템은 PHP Web Server로 된 Application Server 계층에 4개의 Instance를 갖는게 된다.

Chef & Predefined Cookbook
Opsworks
는 앞에서 언급했듯이 Chef (http://www.opscode.com/chef/기반이다.

Chef에 사용되는 설치 스크립트는 Ruby 언어로 되어 있으며, Opsworks에서는 콘솔에서 편리하게 사용할 수 있도록 Pre-defined Layer를 정해놨다.

Layer

Product

Load Balancing

HAProxy

Applications & Web Server

Node.js RubyOnRails, static web server, PHP

DBMS

MySQL

Cache

Memcached

Monitoring

Ganglia

 이외의 부분은 Custom Layer라고 해서 사용자가 직접 정의해야 하며, Chef를 만든 OpsCode Receipe를 참고하여 설정할 수 있다.

애플리케이션의 배포

이렇게 Stack 구성이 완료되면, 기동후에, 여기에 배포되는 애플리케이션을 배포할 수 있다. 자바로 치면 war, ear 파일등이 된다.

아마존이 충분히 서비스를 고려했다는 점은, 여기서도 나오는데, 애플리케이션 배포중에 중요한 일중 하나가, 배포가 잘못되었을때 기존 버전으로 roll back이 가능해야 한다는 것이다. 이를 지원하기 위해서 보통 배포 서비스를 설계할때는 애플리케이션을 저장할 수 있는 repository를 별도 설계해서, 여러 버전을 저장해놓고, 필요할 경우 Roll Back을 하는데, Opsworks는 이를 지원하기 위해서 다양한 Repository를 지원한다.-AWS S3, 일반 HTTP URL, GitHub ,Subversion

현재 Opsworks에서 지원하는 배포 가능한 앱은 Ruby on rails, PHP, JavaScript(Node.js), Static등을 지원하며, Custom App을 통해서 여러 앱 타입을 지원하도록 할 수 있다.

더 살펴봐야 할것

Opsworks 스크립트를 통해서 할 수 없는 것중 하나는 ELB(Elastic Load Balancer)등의 세팅을 할 수 없다. 오로지 EC2위에 설치하는 것만 가능하다. 설치와 배포를 AWS 인프라와 어떻게 엮어서 할 수 있을까가 과제이다.

아직 베타 서비스이고 사례가 많지는 않지만, 설치와 배포가 중요한 클라우드 환경에서, Chef라는 주요 오픈 소스를 기반으로한 서비스인 만큼 시간을 가지고 지켜볼만한 하다.

 

참고 : http://docs.aws.amazon.com/opsworks/latest/userguide/walkthroughs.html

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

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

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


Fabric을 이용한 간단한 Tomcat deploy

프로그래밍/Python | 2013.01.29 18:46 | Posted by 조대협

Tomcat war deploy시, 가장 이상적인 방법은 

tomcat stop > copy war > start 순서이다.

아래는 간단하게 Python 기반의 Fabric을 이용하여, 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()


기타 참고할만한 명령어
- sudo
- get : 파일 copy 해옴 
- local : local machine에서 명령어 수행
- with cd('{dir}') : 특정 디렉토리를 베이스로 명령어등을 수행할때 사용


※ 참고 : 외부에서 Host 명을 dynamic하게 받는 방법

--------------------------------

외부에서 host list 받는 방법

def deploy(lookup_param):

    # This is the magic you don't get with @hosts or @roles.

    # Even lazy-loading roles require you to declare available roles

    # beforehand. Here, the sky is the limit.

    host_list = external_datastore.query(lookup_param)

    # Put this dynamically generated host list together with the work to be

    # done.

    execute(do_work, hosts=host_list)

    

실행할때는

$ fab deploy:app


------ 또는 -----------

# Marked as a publicly visible task, but otherwise unchanged: still just

# "do the work, let somebody else worry about what hosts to run on".

@task

def do_work():

    run("something interesting on a host")


@task

def set_hosts(lookup_param):

    # Update env.hosts instead of calling execute()

    env.hosts = external_datastore.query(lookup_param)

    

$ fab set_hosts:app do_work

One benefit of this approach over the previous one is that you can replace do_work with any other “workhorse” task:


$ fab set_hosts:db snapshot

$ fab set_hosts:cassandra,cluster2 repair_ring

$ fab set_hosts:redis,environ=prod status