Helm으로 쿠버네티스 리소스 배포하기

최초작성일 [2020.04.26]

1. Helm 이란?

Helm은 간단히 말하면 쿠버네티스 리소스를 패키지로 관리하는 툴이다. Helm을 사용하면 쿠버네티스 리소스를 배포하고 버저닝 할 수 있다. 예를 들어, 새로운 버전의 도커이미지로 쿠버네티스의 deployment를 새로 배포한다고 할 때 deployment 관련 매니페스트에 별도의 코드 변경을 가하지 않고 Helm을 이용하여 새로운 버전의 deployment를 배포할 수 있으며, 리비전도 남기기 때문에 필요 시 롤백도 할 수 있다.

Helm을 사용하기 전에 아래 3가지 개념에 대해 먼저 알아야 한다. (더 자세한 내용은 여기 참고.)

1. 차트(chart)

차트는 쿠버네티스 리소스 집합을 나타내는 파일들을 패키지로 묶은 단위다. 차트는 파드(pod)로만 구성되어 있을 수도 있고 service, configmap 등의 복잡한 오브젝트들로 구성되어 있을 수도 있다.

2. 리퍼지토리(repository)

차트를 저장하고 관리하는 곳으로, Helm이 동작하는 서버의 디스크가 될 수도 있고 AWS의 S3 버킷이 될 수도 있다. 여러 사람들이 같은 템플릿을 관리한다면 리퍼지토리가 필요하다.

3. 릴리즈(release)

쿠버네티스 클러스터에서 차트가 실행된 상태의 단위다. 같은 클러스터에 있는 1개의 차트에서 여러 개의 릴리즈를 생성할 수 있다.

2. Helm 설치하기

리눅스 서버에 Helm을 설치하고 실행해보자. 리눅스 버전은 Ubuntu 18.04 이다.

Helm의 깃헙 리퍼지토리에서 바이너리 파일 Linux amd64를 다운로드 한다. v.3.x.x 부터는 Helm을 초기화할 때 사용하는 helm init 명령어를 지원하지 않으므로 2.16.6버전으로 설치했다.

$ wget https://get.helm.sh/helm-v2.16.6-linux-amd64.tar.gz
$ tar -xzvf helm-v2.16.6-linux-amd64.tar.gz
$ ls
helm-v2.16.6-linux-amd64.tar.gz   linux-amd64
$ sudo mv linux-amd64/helm /usr/local/bin/helm
$ helm
The Kubernetes package manager

To begin working with Helm, run the 'helm init' command:

	$ helm init

This will install Tiller to your running Kubernetes cluster.
It will also set up any necessary local configuration.
...

3. Tiller를 위한 role & service account 생성하기

Tiller는 쿠버네티스 클러스터 내부에 설치되어 Helm 클라이언트와 통신하는 API 서버라고 보면 된다. Tiller가 차트를 가지고 쿠버네티스 클러스터에 deployment, service와 같은 리소스를 생성/삭제하려면 그와 관련된 권한이 필요하다. Tiller가 권한을 가지려면

  1. Role 오브젝트를 생성한다.

  2. Role을 부여할 ServiceAccount를 생성한다.

  3. 생성한 Role과 ServiceAccount를 RoleBinding 오브젝트를 통해 연결한다.

  4. Helm을 초기화할 때 ServiceAccount를 넘겨준다.

쿠버네티스에서 권한은 롤과 클러스터 롤로 구분한다.

role

특정 네임스페이스에 속하는 오브젝트들에 대한 권한을 정의할 때 사용한다.

cluster role

클러스터 단위의 권한을 정의할 때 사용한다. 네임스페이스에 속하지 않는 오브젝트들에 대한 권한 뿐만 아니라 클러스터 전반에 걸친 기능에 대한 권한이 필요할 때 클러스터 롤을 부여한다.

클러스터 롤은 쿠버네티스 컴포넌트가 사용하는 권한도 포함되기 때문에 꽤 많은 수의 클러스터 롤이 미리 생성되어 있다. 쿠버네티스에서 관리자 권한으로 모든 기능을 사용할 수 있는 cluster-admin이라는 롤이 그 예다. cluster-adminhelm-chart-account라는 서비스 어카운트에 부여해보자.

account.yaml이라는 매니페스트를 생성했다.

apiVersion: v1

kind: ServiceAccount

metadata:
  name: helm-chart-account
  namespace: kube-system


---


apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:
  name: cluster-rolebinding

subjects:
    # 권한을 부여할 대상은 ServiceAccount 이다.
  - kind: ServiceAccount
    # helm-chart-account 라는 이름의 서비스 어카운트에 권한을 부여한다.
    name: helm-chart-account
    namespace: kube-system

roleRef:
  apiGroup: rbac.authorization.k8s.io
  # cluster-admin 이라는 이름의 클러스터 롤을 대상에게 부여한다.
  kind: ClusterRole
  name: cluster-admin

위 매니페스트를 적용한다.

$ kubectl apply -f account.yaml
serviceaccount/helm-chart-account created
clusterrolebinding.rbac.authorization.k8s.io/cluster-rolebinding created

생성한 서비스 어카운트로 service 오브젝트를 조회해보자. --as 옵션은 쿠버네티스 명령어를 실행할 때 어떤 인증방식과 식별을 사용할 것인지 지정한다. helm-chart-account 라는 서비스 어카운트가 kube-system 네임스페이스에서 소속되었으므로 system:serviceaccount:kube-system:helm-chart-account 라는 값을 넘겨줬다.

$ kubectl get svc --as system:serviceaccount:kube-system:helm-chart-account
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP          7d8h
service-app   NodePort    10.104.73.103   <none>        8080:30080/TCP   3h48m

Helm을 초기화한다.

$ helm init --service-account helm-chart-account

4. 차트 생성하고 템플릿 바인딩하기

차트를 생성할 때는 아래의 명령어를 실행한다.

$ helm create chart <차트 이름>

my-helm-chart 라는 이름의 차트를 생성하면 아래와 같은 구조의 디렉토리와 파일이 생성된다.

my-helm-chart
  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
  values.yaml         # The default configuration values for this chart
  values.schema.json  # OPTIONAL: A JSON Schema for imposing a structure on the values.yaml file
  charts/             # A directory containing any charts upon which this chart depends.
  crds/               # Custom Resource Definitions
  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

한가지 예시를 들어보겠다. flask-gunicorn:0.0.1이라는 도커이미지로 deployment를 생성하는 매니페스트 deployment.yaml이 있다고 하자.

apiVersion: apps/v1

kind: Deployment

metadata:
  name: deployment-app

spec:
  ...
    spec:
      containers:
        - name: flask-gunicorn
          image: kde6260/flask-gunicorn:0.0.1
          ports:
            - containerPort: 9090

플라스크 애플리케이션 서버 코드에 변경이 있어서 도커이미지에 0.0.2라는 새로운 버전이 생겼다면 매니페스트의 template.spec.containers.imagekde6260/flask-gunicorn:0.0.2로 직접 고쳐야 한다. 하지만 Helm을 사용하면 직접 고치지 않아도 된다.

values.yaml에 아래 내용을 추가했다.

deployment:
  flaskGunicorn:
    # use --set option when deploying new version of image
    image: kde6260/flask-gunicorn:0.0.1

deployment.yaml을 아래와 같이 수정했다.

apiVersion: apps/v1

kind: Deployment

metadata:
  name: deployment-app

spec:
  ...
    spec:
      containers:
        - name: flask-gunicorn
          image: {{ .Values.deployment.flaskGunicorn.image }}
          ports:
            - containerPort: 9090

values.yaml에 정의된 속성을 템플릿에 바인딩할 때는 {{ .Values.< 속성 이름> }}과 같은 형식을 사용한다.

Helm의 values.yaml에 있는 속성을 위처럼 템플릿에 바인딩 시킨 뒤 (이미지 이름을 고정시키지 않고 동적으로 바인딩하므로 앞으로 템플릿이라고 하겠다.)helm upgrade ... --set deployment.flaskGunicorn.image=kde6260/flask-gunicorn:0.0.2 를 실행하면 파일에 코드를 직접 수정하지 않고 deployment를 배포할 수 있다. (helm upgrade ... --set=... 에 대해서는 뒤에서 다룬다.)

단, deployment.yaml$ helm create chart my-helm-chart를 통해 생성된 my-helm-chart/templates 디렉토리에 있어야 한다.

이제 생성한 차트를 리퍼지토리에 등록해보자.

5. chartmuseum으로 리퍼지토리 생성하기

Helm은 기본적으로 리퍼지토리 2개를 가지고 있다. 모든 리퍼지토리를 조회하려면 아래의 명령어를 실행한다.

$ helm repo list
NAME       	URL
stable     	https://kubernetes-charts.storage.googleapis.com
local      	http://127.0.0.1:8879/charts

팀원들과 차트를 공유하고 관리하려면 원격에 위치한 리퍼지토리가 필요할 것이다. chartmuseum은 원격으로 공유가능한 리퍼지토리를 생성하고 차트를 생성/수정/삭제하는 API를 제공한다.

chartmuseum을 설치한다.

$ curl -LO https://s3.amazonaws.com/chartmuseum/release/latest/bin/linux/amd64/chartmuseum
$ chmod +x ./chartmuseum
$ sudo mv ./chartmuseum /usr/local/bin/chartmuseum
$ chartmuseum
2020-04-26 14:01:52.775282 I | Missing required flags(s): --storage

설치한 chartmuseum으로 리퍼지토리를 생성한다. 리눅스 서버를 포트번호 8888번으로 노출시켜서 리퍼지토리에 접근할 것이므로 local로 생성했다.

$ chartmuseum --port=8888 --storage="local" --storage-local-rootdir="/chart-storage" &

생성한 리퍼지토리를 등록한다. chartmuseum이라는 이름으로 등록된 것을 확인할 수 있다.

$ helm repo add chartmuseum http://localhost:8888
$ helm repo list
NAME       	URL
stable     	https://kubernetes-charts.storage.googleapis.com
local      	http://127.0.0.1:8879/charts
chartmuseum	http://localhost:8888

이제 차트를 리퍼지토리에 등록해보자.

6. 차트를 리퍼지토리에 업로드하기

4. 차트 생성하고 템플릿 바인딩하기에서 차트와 관련된 파일을 생성하고 동적으로 변경되는 값들도 템플릿으로 만들었으나 이를 리퍼지토리에 등록하려면 차트를 패키지로 묶는 과정이 필요하다. 차트를 패키징하는 명령어는 아래와 같다.

$ helm package <차트 디렉토리 위> --version=<버전 이름>

my-helm-chart를 첫 패키징하는 것이므로 아래의 명령어를 실행한다.

$ helm package my-helm-chart/ --version=0.0.1

my-helm-chart-0.0.1.tgz라는 파일이 생성된 것을 확인할 수 있다.

$ ls
my-helm-chart-0.0.1.tgz

chartmuseum API로 생성된 파일을 리퍼지토리에 업로드한다.

curl --data-binary "@my-helm-chart-0.0.1.tgz" http://localhost:8888/api/charts

리퍼지토리의 루트 디렉토리인 ~/chart-storage 를 확인하면 파일이 업로드된 것을 확인할 수 있다.

$ ls ~/chart-storage/
my-helm-chart-0.0.1.tgz  index-cache.yaml

7. 업로드한 차트로 쿠버네티스 리소스 배포하기(릴리즈 생성하기)

chartmuseum 리퍼지토리에 업로드한 차트로 deployment를 생성해보자.

$ helm install -f <values.yaml 위치> --repo <리퍼지토리 이름> <차트 이름> --name <릴리즈 이름>

chartmuseum 리포지토리에 있는 my-helm-chart-0.0.1.tgz차트로 my-first-release 라는 이름의 릴리즈를 생성하겠다. values.yaml4.차트 생성하고 템플릿 바인딩하기 에서 수정한 values.yaml과 같다.

helm install -f helm-chart/values.yaml --repo chartmuseum helm-chart-0.0.1.tgz \
--name=my-first-release

템플릿 내용대로라면 deployment-app 이라는 이름의 deployment가 생성되어야 하고, pod의 이미지 버전은 kde6260/flask-gunicorn:0.0.1어야 한다.

$ kubectl get deployments
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
deployment-app   6/6     6            6           5h24m

$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
deployment-app-6fc7bcf865-8x5r2   2/2     Running   0          4h33m
deployment-app-6fc7bcf865-jmsh7   2/2     Running   0          4h33m
deployment-app-6fc7bcf865-nffk2   2/2     Running   0          4h33m
deployment-app-6fc7bcf865-ngzf2   2/2     Running   0          4h33m
deployment-app-6fc7bcf865-vxhdf   2/2     Running   0          4h33m
deployment-app-6fc7bcf865-z5tfj   2/2     Running   0          4h33m

$ kubectl describe pods deployment-app-6fc7bcf865-8x5r2
Name:         deployment-app-6fc7bcf865-8x5r2
Namespace:    default
Priority:     0
Node:         ip-10-0-3-217/10.0.3.217
Start Time:   Sun, 26 Apr 2020 10:04:04 +0000
Labels:       app=nginx-gunicorn-flask
              pod-template-hash=6fc7bcf865
Annotations:  cni.projectcalico.org/podIP: 192.168.184.93/32
              cni.projectcalico.org/podIPs: 192.168.184.93/32
Status:       Running
IP:           192.168.184.93
IPs:
  IP:           192.168.184.93
Controlled By:  ReplicaSet/deployment-app-6fc7bcf865
Containers:
  my-nginx:
    Container ID:   docker://f98f0dc40af97c02d055e4280f29a8d85d55d5094479ac1aba5b51bf8df058bf
    Image:          kde6260/my-nginx:stable
    Image ID:       docker-pullable://kde6260/my-nginx@sha256:30700d34592f30421f79c6dc5b8713b4b6b8c6472c843c6197e1b5a66a1dbba3
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sun, 26 Apr 2020 10:04:05 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-phpzx (ro)
  flask-gunicorn:
    Container ID:   docker://73fca9307eef7696b7af54c28f56cb96cd7465fd3069cb497ed77497722513c4
    Image:          kde6260/flask-gunicorn:0.0.1
    Image ID:       docker-pullable://kde6260/flask-gunicorn@sha256:8a97f252450934ec0398166f4f2de001e10a20f9da3db6ae8e1b6d2707f8f8cb

kde6260/flask-gunicorn:0.0.2 버전으로 배포해보자. 기존의 차트의 value를 수정하고 싶거나 릴리즈를 다른 버전의 차트로 변경하고 싶을 때 $ helm upgrade 명령어를 사용한다.

$ helm upgrade -f helm-chart/values.yaml --repo chartmuseum \
--set deployment.flaskGunicorn.image=kde6260/flask-gunicorn:0.0.2 \
 my-helm-chart ./helm-chart

참고자료

Last updated