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


Archive»


 
 

쿠버네티스용 Continuous Deployment 툴인 Skaffold

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

What is skaffold?

쿠버네티스 기반에서 개발을 하고 테스트를 하려면 일반적으로 다음과 같은 절차를 거쳐야 한다.

  • 소스 코드를 수정한 후, 

  • 수정한 코드를 컴파일 한 다음에

  • 컴파일한 소스 코드를 포함해서 Dockerfile을 이용해서 컨테이너로 패키징 한후에

  • 컨테이너를 레파지토리 새로운 버전 태그를 붙여서 업로드 하고

  • 쿠버네티스의 기존 Deployment나 Pod의 yaml 파일에 image 명을 바꾼후

  • kubectl -f apply 를 이용해서 변경된 파일을 반영하고,

  • 다음 public IP가 있는 서비스의 경우에는 public IP로 접속하고, 아닌 경우에는 SSH 터널링을 이용해서 해당 Pod나 Service에 접근해서 테스트를 한다.

한번의 소스 코드 수정을 하고 나서, 이러한 일련의 과정을 거쳐야 하는데, 코드 수정 하나를 반영하기 위해서 해야 할일들이 많다.

이러한 문제를 해결하기 위해서 코드 수정이 쿠버네티스에 반영되기 까지의 과정을 자동화를 통해서 단순화 해주는 프레임워크가 skaffold 이다. 

아래 그림은 skaffold의 개념인 워크 플로우를 도식화한 그림이다. 


<그림. Skaffold의 워크플로우>

출처 https://skaffold.dev/docs/concepts/


Skaffold가 실행되면, Skaffold는 소스 코드 리포지토리(ex git) 또는 로컬 디렉토리를 모니터링 한다. 소스 코드의 변화가 생기면 자동으로 코드를 빌드하고, 이를 도커 컨테이너로 패키징 한다. 

다음 컨테이너화된 이미지에 태그를 붙이는데, 단순하게 SHA256 해쉬를 생성하거나 또는 날짜나 Git commit ID를 태그로 사용한다. Git commit ID를 태그로 사용할 경우에는 코드 변경 내용이 어느 컨테이너 버전에 반영되어 있는지 추적할 수 있는 추적성을 제공한다.

태깅이된 컨테이너 이미지를 지정되어 있는 컨테이너 리파지토리에 저장하고, 미리 정의되어 있는 쿠버네티스 YAML 파일을 이용하여 변경 내용을 대상 쿠버네티스 클러스터에 반영한다. 이 과정은 개발자가 코드 변경만 하게 되면 전 과정이 자동으로 진행되기 때문에, 개발 환경으로의 코드 반영에 대해서 수동 작업이 필요없고, Skaffold를 종료 시키게 되면, 개발을 위해서 생성된 쿠버네티스 리소스 (Service, Pod 등)을 자동으로 삭제해주기 때문에, 매우 쾌적한 개발환경에서 개발이 가능하다.


원래 컨셉 자체는 CD(Continuous Deployment)의 용도이지만, 로컬 환경에서 변경된 소스코드를 바로 쿠버네티스에 배포 해주기 때문에 특히나 개발 환경 구축에 편리한 장점을 가지고 있다.

Hello Skaffold

상세한 개념 이해에 앞서서 먼저 간단한 예제를 통해서 어떻게 동작하는지 살펴보도록 하자

준비

예제 환경에는 쿠버네티스 클러스터가 이미 하나가 있어야 하고, kubectl이 로컬(랩탑)에 설치되어 쿠버네티스 클러스터와 연결이 되어 있는 상태이어야 한다. 

설치

설치 방법은 공식 문서 https://skaffold.dev/docs/getting-started/ 를 참고하면된다.

MAC의 경우에는 아래와 같이 curl 명령어를 이용해서 바이너리를 다운로드 받은 후 사용하면 된다.


curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64

chmod +x skaffold

sudo mv skaffold /usr/local/bin


예제코드 다운로드

예제 코드는 GoogleContainerTools 깃허브에서 다운로드 받을 수 있다.


%git clone https://github.com/GoogleContainerTools/skaffold


예제코드는 examples 디렉토리에 있는데, 그 안에 node 폴더의 내용을 이용하도록 한다.

%cd examples/node


예제 디렉토리 안에는 다음과 같은 파일들이 있다.

  • backend/Dockerfile :

  • backend/package-lock.json , package.json

  • backend/src/ : node.js 소스 코드

  • k8s/ : 쿠버네티스 리소스 배포용 yaml 파일

  • skaffold.yaml : Skaffold 워크플로우를 정의한  yaml 파일


node.js 소스 코드

src/index.js 는 node.js 소스 코드로, node.js express 웹 프레임웍을 이용해서 “Hello World!” 문자열을 리턴하는 웹 서버를 3000 번 포트로 기동 시키는 코드이다.


const express = require('express')

const { echo } = require('./utils');

const app = express()

const port = 3000


app.get('/', (req, res) => res.send(echo('Hello World!')))


app.listen(port, () => console.log(`Example app listening on port ${port}!`))

소스 코드 이외에 필요한 패키지들을 package.json에 정의되어 있다.

Dockerfile

Dockerfile의 내용은 다음과 같다. 


FROM node:10.15.3-alpine


WORKDIR /app

EXPOSE 3000

CMD ["npm", "run", "dev"]


COPY package* ./

RUN npm install

COPY . .


알파인 리눅스 기반의 node.js 10.15-3 이미지를 베이스 이미지로 하고, /app 디렉토리에 package.json 과 기타 소스코드를 복사 한후에, npm install로 의존성 패키지를 설치하고, package.json에 지정된 nodemon src/index.js 명령을 이용해서 앞에서 작성한 index.js node.js 코드를 실행하도록 한다. 

그리고 EXPOSE 3000 명령어를 이용해서 도커 컨테이너에서 3000번 포트로 Listen을 하도록 한다.

쿠버네티스 설정 파일

그러면 앞에서 만들어진 컨테이너로 쿠버네티스에서 서빙을 하기 위해서 쿠버네티스 리소스를 정의해야 하는데, ./k8s/deployment.yaml에서  Service와, Deployment를 정의 하도록 하였다.


apiVersion: v1

kind: Service

metadata:

  name: node

spec:

  ports:

  - port: 3000

  type: LoadBalancer

  selector:

    app: node

---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: node

spec:

  selector:

    matchLabels:

      app: node

  template:

    metadata:

      labels:

        app: node

    spec:

      containers:

      - name: node

        image: gcr.io/terrycho-personal/node-example

        ports:

        - containerPort: 3000


여기서 컨테이너 리포지토리의 경로는 자신이 사용하는 경로로 변경해야 한다. 이 예제에서는 구글 클라우드 리포지토리 (GCR)에 저장하도록  경로를 지정하였다.

Skaffold 설정 파일

다음으로 skaffold의 설정 파일을 보자.


apiVersion: skaffold/v1beta11

kind: Config

build:

  artifacts:

  - image: gcr.io/terrycho-personal/node-example

    context: backend

    sync:

      manual:

      # Sync all the javascript files that are in the src folder

      # with the container src folder

      - src: 'src/**/*.js'

        dest: .


컨테이너 이미지의 경로를 정해놓고, 소스 코드 파일을 어떻게 sync 할지를 지정한다. sync.manual 로 로컬 디렉토리의 src/**/*.js 파일이 변경이 되면 이를 반영하도록 지정하였다. 

실행

그러면 해당 파일을 실행해보도록 하자 skaffold dev 명령어를 실행하면 위의 파일들을 기반으로 컨테이너화를 하고, deployment.yaml 을 이용해서 쿠버네티스 리소스 (Service, Deployment)를 생성 한후, 컨테이너를 배포하여 서비스할 수 있는 형태로 준비한다. 

이때 --port-foward 옵션을 줄 수 있는데, 위의 예제의 경우에는 Service가 있고, Service 타입이 Load balancer이기 때문에, External IP를 가질 수 있어서, 쿠버네티스에서 배정된 External Service를 통해서 Pod에 접근해 서비스가 가능하지만, 일반적으로 개발을 할때는 Service 까지 배포할 필요가 없고 단순하게 Pod만 배포하고 싶은 경우가 있다. (단순하게 작업하기 위해서)

이때 --port-forward 옵션을 주면 Pod에서 외부로 오픈된 포트로 로컬 환경(랩탑) 포트 Forwarding을 해준다.

즉 이 예제에서는 로컬(랩탑)에서 localhost:3000번을 Pod의 3000번 포트로 포트 포워딩을 해주게 된다.  이 옵션을 추가해서 실행하게 되면 결과는 아래와 같이 된다. 


%skaffold dev --port-forward

Generating tags...

 - gcr.io/terrycho-personal/node-example -> gcr.io/terrycho-personal/node-example:v0.32.0-28-g6bd1d50a

Tags generated in 91.565177ms

Starting build...

Building [gcr.io/terrycho-personal/node-example]...

Sending build context to Docker daemon  101.4kB

Step 1/7 : FROM node:10.15.3-alpine

 ---> 56bc3a1ed035

Step 2/7 : WORKDIR /app

 ---> Running in d4579ab15f5a

Removing intermediate container d4579ab15f5a

 ---> 7c725818faae

Step 3/7 : EXPOSE 3000

 ---> Running in e05be7cb896b

Removing intermediate container e05be7cb896b

 ---> 49ac353388c6

Step 4/7 : CMD ["npm", "run", "dev"]

 ---> Running in 52b216a93e63

Removing intermediate container 52b216a93e63

 ---> 3b7878ac4c42

Step 5/7 : COPY package* ./

 ---> 457e460aae8d

Step 6/7 : RUN npm install

 ---> Running in ed391e236197


> nodemon@1.18.7 postinstall /app/node_modules/nodemon

> node bin/postinstall || exit 0


Love nodemon? You can now support the project via the open collective:

 > https://opencollective.com/nodemon/donate


npm WARN backend@1.0.0 No description

npm WARN backend@1.0.0 No repository field.

npm WARN backend@1.0.0 No license field.

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents):

npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})


added 265 packages from 161 contributors and audited 2359 packages in 5.381s

found 1 high severity vulnerability

  run `npm audit fix` to fix them, or `npm audit` for details

Removing intermediate container ed391e236197

 ---> f7a7217f7a60

Step 7/7 : COPY . .

 ---> 5b713462d7ac

Successfully built 5b713462d7ac

Successfully tagged gcr.io/terrycho-personal/node-example:v0.32.0-28-g6bd1d50a

The push refers to repository [gcr.io/terrycho-personal/node-example]

5723b6d27633: Preparing

4f0634afaa8a: Preparing

51a09ffa90b0: Preparing

4789f3ba672b: Preparing

28bf756b6f8e: Preparing

4c299e1e70d5: Preparing

f1b5933fe4b5: Preparing

4c299e1e70d5: Waiting

f1b5933fe4b5: Waiting

28bf756b6f8e: Layer already exists

4c299e1e70d5: Layer already exists

f1b5933fe4b5: Layer already exists

51a09ffa90b0: Pushed

4789f3ba672b: Pushed

5723b6d27633: Pushed

4f0634afaa8a: Pushed

v0.32.0-28-g6bd1d50a: digest: sha256:bccf087827dd536481974eb28465ce4ce69cf13121589e4a36264ef2279e9d1d size: 1786

Build complete in 27.759843507s

Starting test...

Test complete in 17.963µs

Starting deploy...

kubectl client version: 1.11

kubectl version 1.12.0 or greater is recommended for use with Skaffold

service/node created

deployment.apps/node created

Deploy complete in 1.606308148s

Watching for changes every 1s...

Port Forwarding node-6545db86c5-wkdc4/node 3000 -> 3000

[node-6545db86c5-wkdc4 node] 

[node-6545db86c5-wkdc4 node] > backend@1.0.0 dev /app

[node-6545db86c5-wkdc4 node] > nodemon src/index.js

[node-6545db86c5-wkdc4 node] 

[node-6545db86c5-wkdc4 node] [nodemon] 1.18.7

[node-6545db86c5-wkdc4 node] [nodemon] to restart at any time, enter `rs`

[node-6545db86c5-wkdc4 node] [nodemon] watching: *.*

[node-6545db86c5-wkdc4 node] [nodemon] starting `node src/index.js`

[node-6545db86c5-wkdc4 node] Example app listening on port 3000!


태그를 생성해서 gcr.io/terrycho-personal/node-example:v0.32.0-28-g6bd1d50a 이라는 이름으로 컨테이너를 만들어서 쿠버네티스에 배포하고 Port Forwarding node-6545db86c5-wkdc4/node 3000 -> 3000 에서 보는 것과 같이 Pod  node-6545db86c5-wkdc4의 3000 번 포트로 로컬 포트 3000번을 포워딩 하였다. 


kubectl 명령어를 이용해서 Deployment, Pod 그리고 Service들이 제대로 배포 되었는지를 확인해보면 다음과 같다. 


%kubectl get deploy

NAME                    DESIRED CURRENT UP-TO-DATE   AVAILABLE AGE

node                    1 1 1         1 3m


%kubectl get svc

NAME                               TYPE CLUSTER-IP EXTERNAL-IP      PORT(S) AGE

node                               LoadBalancer 10.23.254.227 35.187.196.76    3000:30917/TCP 3m


%kubectl get pod

NAME                                     READY STATUS RESTARTS AGE

node-6545db86c5-wkdc4                    1/1 Running 0 17m


모든 자원들이 제대로 올라온것을 확인했으면 해당 Pod에 localhost:3000 터널을 이용해서 접속해 보자. 



이 상태에서 src/index.js 내용을 에디터를 이용해서 변경해서 저장하면, skaffold 가 파일이 변경되었다는 것을 인지하고, 도커 컨테이너 빌드와 재 배포를 자동으로 수행한다. 아래는 index.js에서 “Hello World 2!”를 출력하도록 소스코드를 수정하여 저장한 후, 수정 내용이 반영된 결과이다. 


중지

개발을 끝내고 싶을 때는 실행중인 skaffold만 중지하면, 아래와 같이 개발을 위해서 생성했던 이미지와 쿠버네티스 리소스 (Deployment, Service, Pod)등을 클린업 해주기 때문에 깔끔하게 개발했던 환경을 정리할 수 있다. 


^CPruning images...

untagged image gcr.io/terrycho-personal/node-example@sha256:bccf087827dd536481974eb28465ce4ce69cf13121589e4a36264ef2279e9d1d

deleted image sha256:5b713462d7ac7ebb468a1f850fa470c34f7b3aaa91ecb9a98b768e892c7625af

deleted image sha256:164f2f7ad057816d45349ffc9e04c361018cb8ce135accdc437f9c7b0cec7460

untagged image gcr.io/terrycho-personal/node-example:v0.32.0-28-g6bd1d50a

untagged image gcr.io/terrycho-personal/node-example@sha256:608ceeae6724e84d30ea61fcfedbaf94aedd3fb6dfbf38c97d244af8c65cbe54

deleted image sha256:d13071d2094092d50236db037ac7e75efca479ed899ec09e3a9a0b61789d93da

deleted image sha256:f9ff5b561a440490345672fe892eeedcd28e05dc01c4675fea8cf5f465b87898

untagged image gcr.io/terrycho-personal/node-example:v0.32.0-28-g6bd1d50a-dirty

untagged image gcr.io/terrycho-personal/node-example@sha256:0ba775dee9f33dc74cea1158f2bc85190649b8102991d653bc73a8041c572600

deleted image sha256:18c830d66ee7c7d508e4edef2980bd786d039706e9271bd2f963a4ca27349950

deleted image sha256:98d168285f8df5c00d7890d1bb8fcc851e8a9d8f620c80f65c3f33aecb540d28

untagged image gcr.io/terrycho-personal/node-example@sha256:0bb49fc5b4ff6182b22fd1034129aed9d4ca5dde3f9ee2dd64ccd06d68d7c0f8

deleted image sha256:a63e7f73658f4f69d7a240f1d6c0fa9de6e773e0e10ab869d64d6aec37448675

deleted image sha256:7e59e5acb2511a5a5620cf552c40bdcba1613b94931fe2800def4c6c6ac6b2a6

deleted image sha256:f7a7217f7a60b23ee04cf50a9bac3524d45d2fd14b851feedd3dc6ffac8b953f

deleted image sha256:b3f0702d41a7df01932c3d3f0ab852ceae824d8acc14f70b7d38e694caa67dc1

deleted image sha256:457e460aae8d185410b47b0a62e027a7d09c7e876cdfaa0c06bcaf6069812974

deleted image sha256:f8e24fe06e6e2c04b5bf29f6eb748d99107781333dc229d03ff77865c1937f8c

deleted image sha256:3b7878ac4c42f541efb80af06415bec286254912f8c3a64e671e7f2f3808b91b

deleted image sha256:49ac353388c6c1e248ac41262ffef47a26ada4d28955e90308c575407f316c64

deleted image sha256:7c725818faaeb6db8d5650e037dc4fa0f38a95bc858ea396f729dfa9ccdc1e06

deleted image sha256:ea8f04b01ca5be4b4fda950557526c3a0707ebda06d0e9392501cba21cc17327

WARN[1877] builder cleanup: pruning images: Error: No such image: sha256:18c830d66ee7c7d508e4edef2980bd786d039706e9271bd2f963a4ca27349950 

Cleaning up...

deployment.apps "node" deleted

Cleanup complete in 2.677585866s

There is a new version (0.32.0) of Skaffold available. Download it at https://storage.googleapis.com/skaffold/releases/latest/skaffo


쿠버네티스 패키지 매니저 Helm

#2-5 Helm Chart 배포

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


Helm 차트를 작성했으면, 다른 사용자들이 사용하기 쉽게 차트를 차트 리포지토리 (Chart repository)에 배포할 수 있다. 

Helm 파일 패키징

파일을 배포하기 위해서는 먼저 차트 파일들을 *.tgz 파일 형태로 패키징해야 하는데, helm package 명령을 사용하면 된다. 

%helm package [차트 디렉토리] 

형태로 사용하면 된다. 앞의 예제인 helloworld 차트를 패키징 하려면 아래와 같은 명령을 사용하면 된다.

%helm package ./helloworld

Successfully packaged chart and saved it to: /home/terrychol/31.helm/helloworld-0.1.0.tgz


만약에 패키지된 파일에 대한 무결성을 보장하기 위해서(패키지된 파일이 변조되지 않음을 보장하는 방법) 패키지파일에 키로 사이닝을 하는 방법이 있다. helm package --sign … 옵션을 이용해서 사이닝을 한다. 패키지에 사이닝을 하면 *.prov 파일 (provenance file) 이 생성되고, 차트 패키지를 설치할때 helm install --verify 옵션을 이용하면 이 provenance 파일을 이용해서 파캐지의 무결성 (변조가 되었는지)을 확인한 후, 변조되지 않은 경우에만 설치를 한다. 

원리 자체는 PKI(비대칭키) 알고리즘을 이용해서 패키지에 사이닝 한후에, 차트를 인스톨할때 사이닝을 확인하여 패키지 변조 여부를 파악하는 방식이다. 

자세한 설정 방법은  https://helm.sh/docs/developing_charts/#helm-provenance-and-integrity

문서를 참고하기 바란다. 

Helm Chart repository server

패키징된 차트패키지 파일을 서버에 배포해야하는데, 서버는 일반적은 HTTP 서버면 모두 사용이 가능하다. github,일반 웹서버, AWS S3, Google Cloud Storage(aka GCS)등이 모두 가능한데, 디렉토리 구조면 repository server 구조에 맞춰서 저장해놓으면 된다.


리포지토리 서버의 디렉토리 구조는 다음과 같다.


charts/

  

  |- index.yaml

  

  |- alpine-0.1.2.tgz

  

  |- alpine-0.1.2.tgz.prov


  • *.tgz 파일은 차트 패키지 파일이고

  • *.prov 파일은 차트 패키지에 대한 provenance 파일이다.

  • 그리고 index.yaml에 리파지토리에 있는 패키지들에 대한 정보를 저장한다.


아래는 index.yaml 파일 샘플이다.


apiVersion: v1

entries:

  helloworld:

  - apiVersion: v1

    appVersion: "1.0"

    created: 2019-06-19T15:37:08.158097657+09:00

    description: A Helm chart for Kubernetes

    digest: f7fcd1078546939bd04b4f94282fb15b3d8d4c422e61b5b03b7e4061c1b61037

    name: helloworld

    urls:

    - http://127.0.0.1:8879/helloworld-0.1.0.tgz

    version: 0.1.0

  helloworld2:

  - apiVersion: v1

    appVersion: "1.0"

    created: 2019-06-19T15:37:08.158803299+09:00

    description: A Helm chart for Kubernetes

    digest: 69510159a58a1c5c228b6870546b67d852b09d299b36d4617bf9e9b971be01fd

    name: helloworld2

    urls:

    - http://127.0.0.1:8879/helloworld2-0.1.0.tgz

    version: 0.1.0

generated: 2019-06-19T15:37:08.15656402+09:00


helloworld와 helloworld2 가 포함되어 있고, 패키지 URL은 http://127.0.0.1:8879/ 이다.

간단하게 Helm 차트 리파지토리를 띄우는 방법은 패키지 (*.tgz)이 있는 디렉토리에서 helm serve 명령을 이용하면 디폴트로 현재 디렉토리에 있는 패키지들을 이용하여 index.yaml 파일을 자동으로 생성하고, 이를 8879 포트를 이용해서 서빙한다.

아래는 helm serve 명령을 이용해서 현재 디렉토리 “.”를 패키지 디렉토리로 해서 차트 리파지토리 서버를 기동한 결과이다.


%helm serve --repo-path .

Regenerating index. This may take a moment.

Now serving you on 127.0.0.1:8879



위의 명령을 사용하면 자동으로 현재 디렉토리에 있는 패키지 파일 (*.tgz)을 읽어서 index.yaml을 자동으로 생성해서 repository 서비스를 제공한다.

helm serve를 이용하는 것이 아니라 웹서버등을 이용할 경우에는 index.yaml을 별도로 생성해줘야 하는데, helm repo index 라는 명령을 이용하면 된다.


%helm repo index [디렉토리명]


을 실행하면, [디렉토리명]에 있는 helm 패키지 파일들에 대한 index.yaml을 생성한다. 이때 웹서버의 URL을 정해줄 수 있는데, 


%helm repo index [디렉토리명] --url [http or https URL for repository]


--url 옵션으로 웹서버의 URL을 주면, index.yaml에서 패키지 경로에 웹서버의 경로를 붙여준다.


%helm repo index . --url https://bwcho75.github.io/my-repo

apiVersion: v1

entries:

  helloworld:

  - apiVersion: v1

    appVersion: "1.0"

    created: 2019-06-19T18:16:29.31329401+09:00

    description: A Helm chart for Kubernetes

    digest: f7fcd1078546939bd04b4f94282fb15b3d8d4c422e61b5b03b7e4061c1b61037

    name: helloworld

    urls:

     https://bwcho75.github.io/my-repo/helloworld-0.1.0.tgz

    version: 0.1.0

  helloworld2:

  - apiVersion: v1

    appVersion: "1.0"

    created: 2019-06-19T18:16:29.314605303+09:00

    description: A Helm chart for Kubernetes

    digest: 69510159a58a1c5c228b6870546b67d852b09d299b36d4617bf9e9b971be01fd

    name: helloworld2

    urls:

     https://bwcho75.github.io/my-repo/helloworld2-0.1.0.tgz

    version: 0.1.0

generated: 2019-06-19T18:16:29.311471188+09:00


다음에 helm 클라이언트에서 이 repository를 사용하도록 하려면,이 repository를  리스트에 추가해야 한다. 

명령을 helm repo add [리파지토리 이름] [URL] 식으로 지정하면 된다.

리파지토리 이름은 사용자가 임의적으로 정하는 이름이고 URL은 Helm 리자지토리의 http URL 이다. 

아래는 myrepo라는 이름으로, http://localhost:8879 서버를 등록하는 방법이다. 

helm repo add myrepo http://localhost:8879

"myrepo" has been added to your repositories


팀내나 아니면 작은 시스템을 위한 Helm repository 라면, helm serv,git (or github) 또는 간단한 웹서버 정도로도 repository 운영이 가능하다. 새로운 차트의 등록은 위에 처럼  그러나 큰 규모로 운영을 하거나 외부에 까지 repository를 오픈할 경우에는 사용자 인증등 별도의 보안 기능이 있고, 매번 index.yaml을 재생성하는게 아니라, 추가 삭제할 수 있는 repository를 사용하는 것을 권장한다. 

ChartMuseum

Charmusem (https://chartmuseum.com) 은 오픈소스 Helm Chart Repository 서버이다. 인증 기능을 제공할 뿐만 아니라 파일 스토리지를 AWS S3, 구글 GCS등을 백앤드로 사용할 수 있다. 


기본 설치 및 사용은 도커로 패키징 되어있는 이미지를 사용하면 된다.

docker run --rm -it \

  -p 8080:8080 \

  -v $(pwd)/charts:/charts \

  -e DEBUG=true \

  -e STORAGE=local \

  -e STORAGE_LOCAL_ROOTDIR=/charts \

  chartmuseum/chartmuseum:v0.8.1


Helm repository 서버이외에도, Chartmuseum은 추가적으로 필요한 기능에 대해서 오픈소스로 제공하고 있다. 

이중에서 주의 깊게 볼만한것은 chartmuseum/ui 와 chartmuseum/helm-push 인데, ui는 chartmuseum 에 대한 웹 인터페이스를 제공한다.

<그림. Chartmuseum ui 웹 화면 >

Chartmuseum push는 CLI도구로, 로컬에 있는 Helm 차트 패키지를 Chartmuseum 에 설치할 수 있는 기능이다. Helm 클라이언트가 깔려 있는 로컬 환경(PC나 노트북)에 인스톨 해서 사용한다.

로컬환경에 설치를 한후에, 차트를 Chartmuseum repository에 차트를 저장하려면 

%helm push [차트디렉토리] [repository 서버명]

으로 실행하면 된다. [차트 디렉토리]는 차트 파일이 들어있는 디렉토리이고 [repository 서버명] 은 helm repo add로 등록한 repository 이다.  

%helm push ./helloworld chartmuseum

지금까지, Helm에 대해서 알아보았다. Helm 은 쿠버네티스를 사용할때, 같이 많이 사용되는 솔루션이고 특히 쿠버네티스에 애플리케이션 설정 및 배포 관점에서 매우 유용하다. 물론 전체 CI/CD 파이프라인을 모두 만들 수 는 없지만, Spinnaker나 Jenkins X 등의 툴과 함께 전체 CI/CD 파이프라인의 중요한 요소로서 사용된다. 



쿠버네티스 패키지 매니저 Helm

#2-4 Helm Chart Hook


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

Hook은 차트 설치나, 삭제와 같이 차트의 라이프 사이클 중에, 차크 개발자가 동작을 추가해줄 수 있도록 해주는 기능이다. mySQL을 차트로 설치한 후에, mySQL에 테이블을 생성하고 데이타를 로딩하거나, 차트로 Pod를 설치하기전에 Configmap이나 Secret 의 값을 세팅해놓는 것과 같은 작업을 예를 들 수 있다. 

등을 들 수 있다.


Hook 으로 실행되는 리소스는 따로 있는 것이 아니라, 기존의 쿠버네티스 리소스 (Job, Cron Job 등)에 metadata.annotations.”helm.sh/hook”  으로 태그를 달아주면, 이 리소스들은 Hook으로 정의되어 차트 인스톨 전후에 정해진 시점에 실행된다. 

아래는 차트 인스톨 전에  작업을 처리하도록 Hook을 정의한 예제이다. 


apiVersion: ...

kind: ....

metadata:

  annotations:

    "helm.sh/hook": "pre-install"

# ...


예를 들어 아래 그림과 같이 차트에, Deployment,Service,Job 등의 리소스를 차트에 정의해서 배포하고자 한다. 그중에, 인스톨 전과 후에 특정 작업을 Job을 이용해서 처리하고자 한다면, 그 Job들에 annotation으로, Helm Hook임을 명시해줘야 한다. 


<그림. Helm 차트내의 리소스와 Hook으로 정의된 리소스들>


이 차트를 실행하게 되면 다음과 같은 순서로 실행이 된다. 먼저 차트에 정의되어 있는 리소스 중에서 Hook 으로 정의된 리소스중, pre-install 로 정의된 리소스가 먼저 실행이 되고, 그 다음에 다른 리소스가 인스톨이 된다. 인스톨이 완료되고 나면 그후에 post-install 로 정의된 Hook이 실행되는 형태이다. 



<그림. Helm 차트에서 리소스들의 배포 순서>


Hook 종류

Hook 은 인스톨 전과 후 뿐만 아니라, 릴리즈를 삭제하거나 업그레이드 할때 등 다양한 시점에 Hook을 삽입할 수 있도록 정의되어 있다.  자세한 Hook 의 종류는 https://github.com/helm/helm/blob/master/docs/charts_hooks.md 문서를 참고하기 바란다. 정의된 Hook 중 몇가지를 보면 다음과 같다. 


  • pre-install: 리소스를 설치하기전에 실행된다. (정확히 이야기 하면 리소스를 설치하기 위한 템플릿이 렌더링 된 후에, 렌더링된 템플릿으로 리소스를 설치하기 전에 실행된다. )

  • post-install: Helm 차트에 의해서 리소스들이 모두 설치 된 후에 실행된다. 

  • pre-delete: 릴리즈의 리소스를 삭제할때, 삭제 전에 실행된다.

  • post-delete:릴리즈의 리소스를 삭제한 후에, 실행된다.  

  • pre-upgrade: 릴리즈를 업그레이드 하기 전,  ( 템플릿이 렌더링 된 후에) 리소스가 생성되기 바로 전에 실행된다.

  • post-upgrade: 릴리즈 업그레이드가 끝난 후에, 실행된다. 

  • pre-rollback: 릴리즈를 기존 버전으로 롤백 할때 실행된다. (템플릿이 렌더링 된 후에 )

  • post-rollback: 릴리즈에 대한 롤백이 완료된 후에 실행된다. 

  • crd-install:  CRD 리소스를 인스톨하는데, 다른 Hook이나 기타 모든 다른 태스크 보다 우선적으로 실행된다. 이 Hook은 다른 리소스에서 이 CRD를  참조하여사용할때 주로 사용된다. 

  • test-success: “helm test” 명령을 실행할때 수행되는데, 그 결과가 성공 (return code == 0)일때만 실행된다. 

  • test-failure: “helm test” 명령을 실행할때 수행되는데, 그 결과가 실패 (return code != 0)일때만 실행된다. 

Hook weight & policy

Hook에는 실행 시점을 정의하는 기능 뿐만 아니라, 여러개의 Hook이 정의되었을때 실행 순서를 정하거나 또는 Hook 실행이 끝났을때 Hook 리소스를 지울지 나둘지등의 정책을 결정하는 기능이 있다. 

Hook weight

Hook weight는 여러개의 Hook 이 있을때, Hook 들의 실행 순서를 정의하기 위해서 사용한다. 음/양수 모두를 사용할 수 있으며, 작은 수를 가지고 있는 Hook 부터 우선 실행된다.

Hook weight는 아래 그림과 같이 annotations 부분에 “helm.sh/hook-weight” 항목으로 지정할 수 있다. 

 annotations:

    "helm.sh/hook-weight": "5"

Hook delete policy

Hook 이 실행된 후에, Hook 리소스들을 지울지 말지를 설정할 수 있는데, Hook이 실행된 후에, Hook 리소스는 일반적으로는 남아 있다. 만약에 Hook 이 실행된 후에, Hook을 삭제 하고 싶으면 annotation에 “helm.sh/hook-delete-policy”에 아래와 같이 삭제 정책을 정하면 된다. 


annotations:

    "helm.sh/hook-delete-policy": hook-succeeded 

삭제 정책으로 사용할 수 있는 정책은 다음과 같다. 

  • "hook-succeeded" : Hook 이 성공적으로 실행이 되고 나면, 삭제하도록 한다. 

  • "hook-failed" : Hook 실행이 실패하였을 경우 삭제 한다. 

  • "before-hook-creation" : Hook 을 실행하기 전에, 기존의 Hook을 삭제하고 실행하도록 한다. 


쿠버네티스 패키지 매니저 Helm

#2-3. Charts (디렉토리 구조)

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

디렉토리 구조

Helm 차트의 디렉토리 구조는 다음과 같다. 직접 아래와 같은 디렉토리 구조에 파일을 각각 생성해도 되지만, 기본 템플릿을 helm create [차트명] 으로 생성할 수 있다.

아래는

%helm create mychart

명령으로 생성한 디렉토리의 구조이다.


mychart/

 Chart.yaml          # A YAML file containing information about the chart

 LICENSE             # OPTIONAL: A plain text file containing the license for the chart

 README.md           # OPTIONAL: A human-readable README file

 requirements.yaml   # OPTIONAL: A YAML file listing dependencies for the chart

 values.yaml         # The default configuration values for this chart

 charts/             # A directory containing any charts upon which this chart depends.

 templates/          # A directory of templates that, when combined with values,

                     # will generate valid Kubernetes manifest files.

 templates/NOTES.txt # OPTIONAL: A plain text file containing short usage notes

Chart.yaml 파일

Chart.yaml은 차트에 대한 기본적인 메타 정보를 정의 한다.

apiVersion: The chart API version, always "v1" (required)

name: The name of the chart (required)

version: A SemVer 2 version (required)

kubeVersion: A SemVer range of compatible Kubernetes versions (optional)

description: A single-sentence description of this project (optional)

keywords:

 - A list of keywords about this project (optional)

home: The URL of this project's home page (optional)

sources:

 - A list of URLs to source code for this project (optional)

maintainers: # (optional)

 - name: The maintainer's name (required for each maintainer)

   email: The maintainer's email (optional for each maintainer)

   url: A URL for the maintainer (optional for each maintainer)

engine: gotpl # The name of the template engine (optional, defaults to gotpl)

icon: A URL to an SVG or PNG image to be used as an icon (optional).

appVersion: The version of the app that this contains (optional). This needn't be SemVer.

deprecated: Whether this chart is deprecated (optional, boolean)

tillerVersion: The version of Tiller that this chart requires. This should be expressed as a SemVer range: ">2.0.0" (optional)

차트의 이름이나 제작자등이 들어가는데, 몇가지 중요한 필드들을 살펴보자.

  • name은 Helm 차트의 이름을 정의한다.

  • version을 정의하는 것은 두가지 필드가 있는데, version 필드가 있고, appversion 이라는 필드가 따로 있다. version 필드는 helm 차트 자체의 버전을 정의하고, appversion은 이 차트를 통해서 배포 되는 애플리케이션의 버전을 정의한다. 예를 들어 helm 차트를 처음 만들었으면, helm 차트 버전은 1.0.0이 되고, helm chart를 통해서 채팅 애플리케이션 1.2를 배포한다면 appVersion은 1.2가 된다. 버전에 대한 네이밍은 semver (https://semver.org/lang/ko/)을 따르도록 한다.

License 및 README 파일

License 파일에는 이 Helm 차트의 라이센스 그리고,README에는 간략한 설명들을 적어 놓는다.

requirement.yaml 파일

Requirement.yaml에는 이 helm 패키지를 설치하기 위해서 필요한 다른 차트들의 목록을 기술한다. 이렇게 기술된 차트들은 이 차트가 설치되기 전에 자동으로 설치된다.

requirement.yaml 파일의 구조는 다음과 같다.


dependencies:

 - name: apache

   version: 1.2.3

   repository: http://example.com/charts

 - name: mysql

   version: 3.2.1

   repository: http://another.example.com/charts


Requirement.yaml에는 이 차트에 대한 의존성을 정의하는데, 필요한 차트의 이름과 버전 그리고, 그 차트가 저장된 리파지토리 경로를 정의한다.

위의 예제를 보면 apache의 1.2.3 버전의 차트가 필요하고, 이 차트는 http://example.com/charts에 저장되어 있음을 알 수 있다. 리파지토리에 대해서는 뒤에서 다시 한번 자세히 설명하도록 한다.


참고로 의존성에 정의된 차트들을 모두 설치하게할 수 도 있지만, tag나 condition을 사용하여, 조건에 따라 설치를 조정할 수 있다. 예를 들어 node.js API서버와 MySQL로 되어 있는 2-tier 애플리케이션이 있을때, MySQL을 같이 설치하도록 의존성을 정해놓을 수 있지만, 이미 MySQL이 쿠버네티스 클러스터내에 설치되어 있을 경우에는 그것을 재활용하면 되기 때문에, 옵션에 따라서 MySQL을 설치하거나 설치하지 않도록 할 수 있다.

charts/ 디렉토리

의존성이 필요한 차트들을 requirement.yaml 에 정의해놓는 경우, requirement.yaml에 있는 경로에서 다운로드 받아서 설치하는데, 다운로드를 받게하지 않고,차트를 배포할때 아예 같이 묶어서 배포하고 싶을 경우에는  charts 디렉토리에 차트들의 파일을 저장해놓으면, 차트를 배포할때 charts 디렉토리에 있는 차트를 먼저 배포하게 된다.

templates/ 디렉토리

Template는 앞의 예제를 통해서  설명했듯이, Helm 차트의 탬플릿을 정의한다.  

values.yaml 파일

values.yaml에는 템플릿에 사용될 value 값들을 저장한다



쿠버네티스 패키지 매니저 HELM

#2-2. Chart 버전과 릴리즈

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

업그레이드와 롤백

Chart로 의해서 설치된 리소스들을 쿠버네티스에서 릴리즈라고 한다.

예를 들어 같은 차트로 MySQL을 쿠버네티스 클러스터 내에 여러번 설치 할 수 있다.  각각 설치된 MySQL들을 릴리즈라고 하고, 릴리즈에 설정이 변경된 경우에, 차트를 수정해서 변경을 반영할 수 있다. 변경이 반영될때 마다 새로운 버전이 생성된다.


처음 helm install로 설치를 할때 --name 옵션으로 저장한 설치 이름이 릴리즈 명이 되고, 이 릴리즈를 업데이트 하고 싶으면 helm upgrade {Helm 릴리즈명} {차트 디렉토리} 를 실행하면, 해당 릴리즈를 업데이트하고, 새로운 버전을 생성한다.

앞의 예제의 values.yaml 에서 replicaCount: 3 를 변경한 후, upgrade를 해보자

%helm upgrade helloworld ./helloworld/

명령을 실행하면 다음과 같이 기존 배포가 업데이트 된다.


Release "helloworld" has been upgraded. Happy Helming!

LAST DEPLOYED: Sun Jun  9 20:58:41 2019

NAMESPACE: default

STATUS: DEPLOYED


RESOURCES:

==> v1/Service

NAME            AGE

helloworld-svc  2d


==> v1beta2/Deployment

helloworld-deployment  2d


==> v1/Pod(related)


NAME                                    READY STATUS RESTARTS AGE

helloworld-deployment-696fc568f9-2dkpm  0/1 ContainerCreating 0 0s

helloworld-deployment-696fc568f9-fqqg8  1/1 Running 0 2d

helloworld-deployment-696fc568f9-r8qct  1/1 Running 0 2d


그리고 위와 같이 Deployment pod가 3개로 늘어난것을 확인할 수 있다.

업그레이드 한후 버전을 확인하려면  helm history {릴리즈 이름} 을 사용하면 되는데,

% helm history helloworld

를 실행하면 아래와 같이 2번 버전이 새로 생긴것을 확인할 수 있다.


REVISION UPDATED                  STATUS     CHART            DESCRIPTION     

1        Fri Jun  7 23:24:34 2019 SUPERSEDED helloworld-0.1.0 Install complete

2        Sun Jun  9 20:58:41 2019 DEPLOYED   helloworld-0.1.0 Upgrade complete


만약 예전 버전으로 돌리고 싶으면 rollback 명령을 사용하면 되는데, helm rollback {릴리즈 이름} {릴리즈 버전} 으로 실행하면 된다. helm rollback helloworld 1 는 helloworld 릴리즈를 1 버전으로 롤백 하는 명령어 이다.  명령어를 실행해보면 다음과 같이 롤백이 완료되는 것을 확인할 수 있고,

% helm rollback helloworld 1

Rollback was a success! Happy Helming!


helm history로 해당 릴리즈의 버전을 확인해보면, 3번 버전에서 1번으로 롤백을 한것을 확인할 수 있다.

%helm history helloworld

REVISION UPDATED                  STATUS     CHART            DESCRIPTION     

1        Fri Jun  7 18:24:34 2019 SUPERSEDED helloworld-0.1.0 Install complete

2        Sun Jun  9 20:58:41 2019 SUPERSEDED helloworld-0.1.0 Upgrade complete

3        Sun Jun  9 21:43:48 2019 DEPLOYED   helloworld-0.1.0 Rollback to 1


릴리즈

앞에서도 설명했듯이 차트 하나로 같은 클러스터에 같은 애플리케이션을 여러개를 설치할 수 있다. MySQL이나 Redis 메모리 서버들은 애플리케이션이 아니기 때문에  같은 이미지로 여러개의 릴리즈를 설치할 수 있다.

그런데 앞의 스크립트로 helloworld 차트를 한번 더 설치하면 에러가 날것이다. 이유는 차트에 정의된 Service와 Deployment 리소스의 이름이 동일하기 때문이다.

templates/helloworld.yaml 파일에서 Deployment 이름 정의 부분을 보면, 이름을 {{.Value.name}}을 사용하도록 하였다.


apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: {{ .Values.name }}-deployment

:


그래서 values.yaml에서 name을 변경하지 않는 이상, Deployment는 같은 이름을 가지게 된다.

이 문제를 해결하기 위해서 리소스들의 이름을 릴리즈 이름을 사용하도록 하면 된다. 릴리즈 이름은 {{ .Release.Name}} 을 사용하면, 릴리즈 이름을 리소스 이름으로 사용할 수 있다. helloworld.yaml을 아래와 같이 수정하면 된다.


apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: {{ .Release.Name }}

spec:


그런데, 이렇게 설치가 된 리소스들이 어떤 차트에 의해서 설치된 것인지 구별이 잘 안될 수 있기 때문에, 좀더 좋은 방법은 리소스 이름을 “차트 이름-릴리즈 이름" 형태로 하는 것이 좋다. 차트 이름은 {{ .Chart.Name }} 을 사용하면 된다. 아래는 “차트 이름-릴리즈 이름" 형태로 정의한 예제이다.


apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: {{ .Chart.Name }}-{{ .Release.Name }}


helm template 명령을 이용해서 테스트를 해보자. 같은 예제를  helloworld2 디렉토리에 새롭게 만들었다. (그래서 아래 명령을 보면 ./helloworld2 디렉토리를 차트 디렉토리로 실행하였다.) 그리고 릴리즈 이름을 --name을 이용해서 myrelease로 지정하였다.


% helm template --name myrelease ./helloworld2

---

# Source: helloworld2/templates/helloworld.yaml

apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: helloworld2-myrelease

spec:

 replicas: 3

:


metadata:

 name: helloworld-deployment

spec:

 replicas: 10

 minReadySeconds: 5

삭제

그리고 설치된 차트는 간단하게 helm delete 명령으로 삭제가 가능하다.

%helm delete helloworld

release "helloworld" deleted



쿠버네티스 패키지 매니저 HELM

#2-1 .Chart
조대협 http://bcho.tistory.com

Helm Chart

차트는 helm의 패키지 포맷으로, 하나의 애플리케이션을 설치하기 위한 파일들로 구성되어 있다. 예를 들어 tomcat을 설치하기 위한 쿠버네티스의 pod,service,deployment를 위한 YAML 파일등을 포함한다.

템플릿과 밸류

Helm 은 기본적으로 템플릿의 개념을 사용한다. 템플릿 파일을 만들어놓은 후에, 밸류 값을 채워 넣어서 쿠버네티스 리소스를 정의한 YAML 파일을 생성한다. 예제를 살펴보자

Helm 은 기본적으로 템플릿의 개념을 사용한다. 템플릿 파일을 만들어놓은 후에, 밸류 값을 채워 넣어서 쿠버네티스 리소스를 정의한 YAML 파일을 생성한다. 예제를 살펴보자.


먼저 templates/helloworld.yaml 파일을 정의한다.

apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: {{ .Values.name }}-deployment

spec:

 replicas: {{ .Values.replicaCount }}

 minReadySeconds: 5

 selector:

   matchLabels:

     app: {{ .Values.name }}

 template:

   metadata:

     name: {{ .Values.name }}-pod

     labels:

       app: {{ .Values.name }}

   spec:

     containers:

     - name: {{ .Values.name }}

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

       imagePullPolicy: Always

       ports:

       - containerPort: 8080

---

apiVersion: v1

kind: Service

metadata:

 name: {{ .Values.name }}-svc

spec:

 selector:

   app: {{ .Values.name }}

 ports:

   - name: http

     port: 80

     protocol: TCP

     targetPort: 8080

 type: LoadBalancer


그리고 ./values.yaml 파일을 아래와 같이 정의한다.


name: "helloworld"

replicaCount: 3


템플릿은 값을 채울 수 있는 말 그대로 템플릿이고, Value의 값을 이용해서 값을 채운다.

이를 개념적으로 표현해보면 다음과 같은 형태가 된다.



좌측은 템플릿 파일이고, 템플릿에서 밸류값은 별도의 파일에 정의한다.

정의한 키/밸류 형식으로 name:”helloworld”로 정의하였고, replicaCount: 2로 정의하였다

그리고 이 밸류값을 불러들이기 위해서는 {{.Value.키이름}} 식으로 템플릿내에 정의한다.

위의 예제에서 보면 name등에 {{.Value.name}} 으로 정의하였고, replicas 수는 {{ .Value.replicaCount }} 로 정의한다.

이렇게 정의된 템플릿에 밸류 내용을 정의하면 오른쪽 처럼 YAML 파일을 생성할 수 있다.


외부에서 Value를 받는 방법

Value값을 values,yaml에 지정해놨지만, 설치에 따라서 각 값을 변경하고 싶은 경우가 있다. 예를 들어 replicaCount를 3이 아니라 10으로 변경하고 싶을 경우에는 values.yaml 파일을 일일이 에디트 해야 한다. 나중에, 차트를 차트 리파지토리에 등록하기 위해서는 압축된 파일 형태를 사용하는데, 이 경우에는 그러면 차트 압축 파일을 다운로드 받은 후에 압축을 풀고나서, 내용을 수정하고 설치에 사용해야 하는 불편함이 있다.

이렇게 일일이 수정하지 않고 CLI에서 변경하고 싶은 인자만 간단하게 지정할 수 있는 방법이 없을까?

helm install 이나 upgrade시에  --set 옵션을 사용하면 된다. 예를 들어 values.yaml에 정의된 replicaCount를 10으로 변경하고자 하면 다음과 같이 하면 된다. %helm template --name myrelease --set replicaCount=10 ./helloworld


아래는 template 명령을 이용해서 테스트한 결과이다.   replicas가 10으로 변경된것을 볼 수 있다.

---

# Source: helloworld/templates/helloworld.yaml

apiVersion: apps/v1beta2

kind: Deployment


만약 설정값이 많아서 --set 을 이용해서 parameter로 넘기기가 어렵다면, 필요한 변수만 파일로 만들어서 넘길 수 있다.

예를 들어 myvalue.yaml 에 아래와 같이 name만 “fromValuefile” 로 정의를 한후에,


name: "fromValuefile"


helm install에서 -f 옵션으로 value 파일을 지정할 수 있다.

%helm install -f myvalues.yaml --name newrelease --dry-run --debug ./helloworld


결과를 보면 다음과 같다.

# Source: helloworld/templates/helloworld.yaml

apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: fromValuefile-deployment

spec:

 replicas: 3

:

myvalues.yaml에 지정한 name에 의해서 Deployment name이  fromValuefile-deployment로 변경되고, replica 수는 원래 values.yaml에 지정한대로 3을 사용한 것을 확인할 수 있다.


디렉토리 구조

개념을 이해 하였으면, 파일을 어디에 저장하는지 디렉토리 구조를 살펴보자, helloworld라는 차트를 만들 것인데, helloworld라는 디렉토리를 만든다.

그리고 그 아래 템플릿들은 templates 라는 디렉토리에 yaml 로 정의한다. 여기에 채울 value들은 helloworld/values.yaml 파일내에 저장한다.



그리고 helloworlds/Chart.yaml 이라는 파일을 생성해야 하는데, 이 파일에는 이 헬름 차트에 대한 버전이나 작성자, 차트 이름과 같은 메타 정보를 정의한다.


다음은 Chart.yaml 의 내용이다.

apiVersion: v1

appVersion: "1.0"

description: A Helm chart for Kubernetes

name: helloworld

version: 0.1.0

테스트(검증)

헬름 차트 작성이 끝났으면 제대로 작동하는지 검증을 해볼 수 있다. 먼저 문법적인 오류가 없는지 확인 하는 명령은 helm lint 명령을 사용하면 된다. 명령어 실행은 차트 디렉토리 위에서 해야 한다. 이 예제에서는 ../helloworld 디렉토리가 된다.

실행하면 다음과 같은 결과를 볼 수 있다.


%helm linit ./helloworld

==> Linting ./helloworld/

[INFO] Chart.yaml: icon is recommended


1 chart(s) linted, no failures


문법적인 오류가 없는지 점검을 해준다. 다음으로 탬플릿에 밸류가 제대로 적용되서 원하는 YAML을 제대로 생성해나가는지 검증해야 하는데, helm template이라는 명령어를 사용하면 된다. helm lint 명령과 마찬가지로 차트가 저장된 디렉토리의 상위 디렉토리 (../helloworld)에서 실행한다.

다음은 helm template 명령을 실행한 결과이다.


%helm template ./helloworld

# Source: helloworld/templates/helloworld.yaml

apiVersion: apps/v1beta2

kind: Deployment

metadata:

 name: helloworld-deployment

spec:

 replicas: 2

 minReadySeconds: 5

 selector:

   matchLabels:

     app: helloworld

 template:

   metadata:

     name: helloworld-pod

     labels:

       app: helloworld

   spec:

     containers:

     - name: helloworld

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

       imagePullPolicy: Always

       ports:

       - containerPort: 8080


내용 처럼 Value.name과  Value.replicaCount 값이 채워져서 Deployment 리소스에 대한 YAML 파일이 생성된것을 확인할 수 있다.

helm template 명령을 helm 클라이언트에서 tiller 서버 접속없이 template에 값이 채워지는지만 테스트를 하는 것이고, tiller 서버에 연결해서 테스트 할 경우에는 helm install --dry-run 옵션을 사용한다.  그리고, 내용을 확인하기 위해서 --debug 옵션을 추가한다.

helm install --name myrelease --dry-run --debug ./helloworld 이렇게 명령어를 사용하면 되는데, 장점은, 실제 서버에 연결해서 같은 릴리즈 버전이 있는지 등의 체크를 해주기 때문에, 실수를 막을 수 있다.

아래는 같은 릴리즈 버전으로 설치하는 것을 --dry-run으로 테스트한 결과이다.


% helm install --name myrelease --dry-run --debug ./helloworld

[debug] Created tunnel using local port: '56274'


[debug] SERVER: "127.0.0.1:56274"


[debug] Original chart version: ""

[debug] CHART PATH: /Users/terrycho/dev/workspace/kube/kubernetes-tutorial/31.helm/helloworld


Error: a release named myrelease already exists.

Run: helm ls --all myrelease; to check the status of the release

Or run: helm del --purge myrelease; to delete it


차트를 같은 이름(릴리즈명/뒤에서 다시 설명함)이 있기 때문에 설치할 수 없다 것을 테스트 단계에서 미리 확인할 수 있다.

실제 설치

그러면 생성한 헬름 차트를 이용해서 쿠버네티스에 리소스를 실제로 설치해보자. 설치 방법은 차트가 있는 디렉토리에서 helm install  명령을 이용해서 인스톨을 하면 된다. 이때 --name이라는 이름으로 설치된 차트 인스턴스의 이름을 설정해줘야 한다. 만약에 이름을 정해주지 않으면 임의의 이름이 자동으로 생성되어 사용된다.


%helm install --name helloworld ./helloworld/


명령을 실행하면 아래와 같이 deployment가 생성되는 것을 확인할 수 있다. 예제에서 설명은 하지 않았지만, 테스트에 사용된 코드에는 Service를 배포하는 부분이 함께 포함되어 있기 때문에 아래 실행결과를 보면 Service까지 같이 생성된것을 확인할 수 있다.


NAME:   helloworld

LAST DEPLOYED: Fri Jun  7 23:01:44 2019

NAMESPACE: default

STATUS: DEPLOYED


RESOURCES:

==> v1beta2/Deployment

NAME                   AGE

helloworld-deployment  1s


==> v1/Pod(related)


NAME                                    READY STATUS RESTARTS AGE

helloworld-deployment-696fc568f9-mc6mz  0/1 ContainerCreating 0 0s

helloworld-deployment-696fc568f9-nsw48  0/1 ContainerCreating 0 0s


==> v1/Service


NAME            AGE

helloworld-svc  1s


설치가 완료되었으면 리소스가 제대로 생성되었는지 확인을 해보기 위해서 kubectl get deploy 명령을 실행한다.


%kubectl get deploy

kubectl get deploy

NAME                    DESIRED CURRENT UP-TO-DATE   AVAILABLE AGE

helloworld-deployment   2 2 2         2 10m


위와 같이 Deployment 리소스가 생성 된것을 확인할 수 있다.

헬름을 통해서 설치된 차트들은 helm list 명령을 이용해서, 설치 상태를 확인할 수 있다.

helm list 명령을 실행해보면 아래와 같이 helloworld 차트가 설치된것을 확인할 수 있다.


%helm list

NAME               REVISION UPDATED                  STATUS   CHART            APP VERSION NAMESPACE

helloworld         1        Fri Jun  7 23:01:44 2019 DEPLOYED helloworld-0.1.0 1.0  




블로그 700만 돌파

조대협의 소프트웨어 개발 | 2019.06.04 23:33 | Posted by 조대협


2019년 6월 2일 700만 돌파 (6개월)

2018년12월 10일 600만 돌파 (7개월)

2018년 5월 10일 500만 돌파 (10개월)

2017년 7월17일 405만 돌파 

2016년10월20일300만 돌파

2015년12월16일200만 돌파



쿠버네티스 패키지 매니저 HELM

Helm의 일반적인 개념

Helm은 리눅스의 apt 툴이나, node.js의 npm과 같은 쿠버네티스용 패키지 매니지먼트 도구 이다. 일반적으로 하나의 소프트웨어를 쿠버네티스에 배포하려면, 간단하게 컨테이너만을 배포해서는 사용하기 어려운 경우가 많다. 외부로 IP를 노출 시키기 위해서 쿠버네티스 서비스를 배포해야 하고,쿠버네티스 pod를 관리할 deployment가 필요하며, 디스크 볼륨과 기타 정책등 부가 적인 부분을 추가로 배포해야 한다.

이미 네트워크나 디스크 설정이 완료된 상태에서 애플리케이션을 업데이트 하는 경우에는 쿠버네티스 deployment나 다른 배포 도구를 이용해도 되지만, 처음부터 모든 것을 설치해야 하는 반복적인 작업이 있는 경우에는 배포 도구로 불가능하다.

그래서 Helm은 애플리케이션 컨테이너 배포는 물론이고, 이에 필요한 쿠버네티스 리소스를 모두 배포해주는 역할을 하는데, 이 배포를 패키지 형태로 한다.

Infrastructure as a code (이하 IaaC)를 구현하는 Terraform + 패키지 매니저인 npm 정도의 개념이 합쳐져 있다고 보면 된다.




<Helm 개념도>

Helm의 구성을 보면 위의 그림과 같다.

먼저 CLI 툴인 클라이언트로 helm이 있다. 이 클라이언트는  Helm 서버 모듈과 통신을 하는데, Helm 서버를 Tiller라고 하고, Tiller는 쿠버네티스 클러스터 내에 설치된다.

helm 을 통해서 인스톨하는 패키지를 Chart라고 한다. Chart는 템플릿으로 설치하고자 하는 쿠버네티스 리소스의 설치 스크립트가 된다. 이 Chart들은 Helm Chart Repository에 저장할 수 있는다. Helm Chart repository는 HTTP server를 지원하는 서버로 Google Cloud Storage나 Git Hub page 또는 웹서버등을 사용할 수 있다.


Helm 설치

Helm 설치는 먼저 helm 클라이언트를 설치해야 한다. https://github.com/helm/helm 에 설치가이드가 나와 있는데, 맥 사용자의 경우 brew로 쉽게 설치가 가능하고, Linux의 경우 scoop등의 패키지 인스톨러를 이용하면 된다.


다음 helm 서버인 Tiller를 설치해야 한다.

Tiller 설치는 간단하게 다음 명령을 클라이언트에서 실행하면 된다. (이때, 이미 클라이언트에 kubectl 명령과 쿠버네티스 연결을 위한 설정이 다 되어있어야 한다.)

% helm init ---history-max 200

(Helm 설치 히스토리의 수를 정의해준다. 최대 히스토리 수를 정하지 않으면 히스토리가 무한으로 증가하게 된다.)


그러면 테스트로 MySQL를 Helm을 이용해서 설치해보자 먼저 Chart 저장되는 repository 를 최신으로 업데이트 한다.

%helm repo update 명령을 실행하면 된다.


다음 mySQL을 설치할것인데,

%helm install stable/mysql

명령을 실행하면 다음과 같이 MySQL이 설치된다. 설치가 끝나면 아래 결과와 같이 mySQL 연결을 테스틑 하는 방법과 ROOT 패스워드를 구하는 방법이 나온다.



위의 그림에서와 같이

%  MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default iron-kitten-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)

명령어를 실행하여, MySQL Root 패스워드를 알아낸다.

다음

% kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

을 실행해서, 우분투 이미지 기반의 컨테이너를 하나 만든후에, bash로 접속을 한다.

접속이 된 상태에서

% apt-get update && apt-get install mysql-client -y

명령을 이용해서 MySQL 클라이언트를 설치하고

% mysql -h iron-kitten-mysql -p

를 실행하고 앞에서 구한 비밀 번호를 넣으면 아래  그림과 같이 MySQL에 접속이 된다.



Helm에 의해서 설치된 차트 목록을 확인하려면 아래와 같이

% helm list

를 실행하면 된다. 아래 그림에서 보면 앞에서 설치한 mySQL이 iron-kitten이라는 이름으로 설치가 되어있는 것을 확인할 수 있다.


   

만약 설치된 helm chart를 삭제하기 위해서는

%helm delete {Chart명} 을 하면된다.

앞에서 설치한 iron-kitten mysql chart를 삭제하려면

%helm delete iron-kitten

을 실행하면 아래 그림과 같이 삭제되는 것을 확인할 수 있다.