Page tree
Skip to end of metadata
Go to start of metadata


도커를 사용하고 쿠버네티스를 오케스트레이션으로 활용할 때엔 단일 클러스터가 아니라 여러 클러스터로 구성하는 경우가 많다.

하나 이상의 클러스터를 운영하다 보면 같은 어플리케이션을 여러 클러스터에 배포해야하는 경우가 있는데, 이를 위해서는 메니페스트 파일을 클러스트 수 만큼 관리를 해야한다.

현실적으로 이런 부분이 어려운데, 이를 해결하기 위해 만들어진 프로젝트가 helm 프로젝트이다.


헬름은 기본적으로 client-server 구조로 구성되어있다.

서버는 tiller 라는 도구를 가지고 있으며 tiller 가 helm client 으로부터 명령을 전달받아 쿠버네티스 클러스터에 패키지 설치, 업데이트, 삭제 등의 작업을 수행한다.


각 용어를 정리하자면 아래와 같다.

manifest쿠버네티스의 여러 리소스를 관리하는 파일
chart

manifest 를 생성하고 관리하는 템플릿 패키지

chart 의 구성에는 여러 파일들이 포함되어있는데, 이 중에 values.yaml 이라는 파일이 있고 이 파일안에는 애플리케이션 동작을 제어하는 설정의 기본값들이 들어있다.

사용자는 이 values.yaml 에서 변경하고자 하는 값들을 따로 정의할 수 있는 커스텀 value 파일을 구성할 수 있다.

tillerhelm 명령에 따라 설치 등의 작업을 하는 서버 애플리케이션의 도구
repository

local, stable, incubator 등으로 구성되어있으며 chart 들이 tgz 파일로 저장되어있다. helm은 repository 를 통해 chart 를 가지고와 쿠버네티스 클러스터에 여러 리소스들을 구성한다.

local 은 헬름 클라이언트가 설치된 로컬을 뜻하며, stable 은 안정버전에 이른 차트가 존재하는 외부 리포지토리, incubator 는 stable 요건을 만족하지 못한 아직은 시험적인 차트들이 있다.

local repository 는 헬름이 실행되는 호스트 머신에 위치한다.


기존에 쿠버네티스의 배포 및 수정 등을 하는데에는 kubectl 을 이용해왔는데,

여러 클러스터를 걸쳐 운영을 위해서 등장한 helm 이 이를 대체한다 생각하면 된다.

대신 kubectl 은 이미 배포되어 운영중인 쿠버네티스의 리소스들을 수정하는데 사용된다 보면 된다.

그림을 참조하면 이해하기가 쉬운데 스터디 교재의 275 페이지에 있는 그림을 참조하자.


헬름의 설치는 아래와 같이 진행할 수 있다

$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get > get_helm.sh
$ chmod 700 get_helm.sh
$ ./get_helm.sh


이번 스터디의 목표는 헬름을 이용하여 아래와 같은 작업을 해보겠다.

  1. stable repository 를 이용하여 차트를 구성하고 삭제

  2. local repository 를 이용하여 차트를 구성하고 삭제

위 작업은 kubernetes 설치시에 기본 클러스터(question) 로 제공된 docker-for-desktop 을 이용하여 진행한다.

$kubectl config get-contexts

위를 이용하면 이용가능한 클러스터 컨텍스트 확인이 가능하니 참고하자.


헬름을 사용하려면 먼저 대상 클러스터에서 초기화 작업을 해야 한다.

$kubectl config use-context docker-for-desktop
$helm init

위처럼 초기화를 하면 앞서 얘기하였던 tiller 라는 서버 어플리케이션이 kube-system 네임스페이스에 배포된다. 이로서 helm 명령에 따라 쿠버네티스 클러스터에 각종 리소스를 구성할 준비가 되었다.

1. stable repository 를 이용하여 차트를 구성하고 삭제해보자.

우선 첫 작업으로는 헬름에서 사용할 차트를 구성하는 것이다. 사용할 차트는 redmine 차트를 사용해보겠다.

앞서서 chart 에 대해 언급할때에 사용자가 직접 커스텀 value 를 정의할 수 있다고 하였는데 이를 적용해보자.

redmineUsername: slipp_munsu
redminePassword: slipp
redmineLanguage: ko

serviceType: NodePort

위처럼 커스텀 value 를 작성하고 redmine.yaml 로 저장하자.

  • 여기서 service type 을 NodePort 라고 지정한 것을 확인할 수 있다. 이것의 의미는 쿠버네티스 리소스 NodePort 를 활용하여 외부와 통신하겠다는것을 의미하므로 해당 클러스터에 구동중인 서비스 중 NodePort 가 있는지 체크할 것.


그리고 아래와 같이 입력하여 레드마인을 설치하자. --name 을 주어 클러스터의 서비스 식별자를 부여하자. 식별자이기때문에 클러스터에 단 하나만 사용된다.

stable/redmine 은 stable repository 에서 redmine을, 그리고 --version 을 지정함으로 버전 4.0.0을 설치하겠다는 의미이다.

$helm install -f redmine.yaml --name redmine stable/redmine --version 4.0.0


설치가 끝났다면 아래와 같이 입력하여 릴리스 네임의 목록을 확인할 수 있으며, 이어서 redmine 의 실행환경을 확인할 수 있다. 입력은 라인별로 하자.

$helm ls
$kubectl get service,deployment --selector release=redmine


redmine 리소스가 정상적으로 생성되었는지 확인이 되었다면, NodePort 를 활용한 결과로 발급받은 port 를 확인하여 웹브라우저를 이용하여 접근해보자.

일전에 작성한 커스텀 value에 입력하였던 user name, password 를 활용하여 로그인을 해보자.

여기까지 해보았다면 이제 다음 스터디를 위해 redmine 리소스를 제거해보자.

$helm delete redmine


삭제를 하였지만, 이전에 rollback 을 경험하였기에 롤백을 기대할 수 있다. 아래의 명령을 라인별로 해보자.

릴리즈 name 뒤의 숫자는 revision 버전이다.

$helm ls --all
$helm rollback redmine 1
$helm ls

롤백이후에는 NodePort 서비스의 재설치로 port 가 달라지니 주의해야한다. 직접 웹브라우저로 확인하고 싶다면, 롤백 이후 리소스 설치 및 배포까지 시간이 걸리니 참고하자.


결국 롤백까지 확인할 수 있다. 혹시 해당 리소스를 완전히 말끔히 제거하고 싶다면

$helm del --purge redmine
$helm ls --all


2. local repository 를 이용하여 사용자 차트를 구성하고 삭제

우선 리파지토리 구성을 보도록 하자.

$helm repo list

repository 에는 stable, remote, 그리고 incubator 가 있다 하였는데 목록에는 incubator 가 없다. 기본 설정된 repository 는 stable, local 뿐이며 혹시 incubator 를 추가하고 싶다면

$helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com


local repository 가 보이긴 하지만, 접근을 할 수 없다. repository 가 있으나 이를 서버로서 활용하도록 지정을 하지 않았기 때문이다. 로컬 리파지토리에 항상 접근할 수 있도록 helm local repository server 를 백그라운드로 실행하자.

$helm serve &


차트를 생성하려면 먼저 차트의 디렉터리 구조를 만들어야 하는데, 헬름에 이 템플릿을 만들어주는 기능이 있다.

익숙한 echo 라는 이름의 차트를 만들고 싶다면, 아래와 같이 명령하면 된다. (위 서버구동으로 인해 서버구동 명령을 내린 터미널에선 더이상 작업이 힘들다면 새로운 터미널로 작업중이던 패스로 이동하여 진행하자)

$helm create echo
$ls -R echo


위와 같이 echo 라는 이름의 차트를 생성하게 된다면 echo 라는 디렉터리가 생기고 아래와 같은 구조를 띄게 된다. (특정 디렉터리를 순회하며 디렉터리&파일 구조를 보기 쉽게 → tree 설치)

.

├── echo

│   ├── Chart.yaml

│   ├── charts

│   ├── templates

│   │   ├── NOTES.txt

│   │   ├── _helpers.tpl

│   │   ├── deployment.yaml

│   │   ├── ingress.yaml

│   │   ├── service.yaml

│   │   └── tests

│   │       └── test-connection.yaml

│   └── values.yaml

├── get_helm.sh

└── redmine.yaml


위와 같은 구조를 통해 어떤 서비스들을 설정하고, 그것을 어떻게 배포할지, 그리고 배포된 시스템을 외부에 어떻게 노출할지 등을 알 수 있게 해주는 템플릿임을 알 수 있다.

교재를 십분 활용하여 이 차트를 이용하여 우리는 nginx 컨테이너로 gihyodocker/nginx:latest, echo 컨테이너로 gihyodocker/echo:latest 의 기존 이미지를 활용하자.

아래는 nginx 로 http request 를 받아 이를 같은 파드 내의 echo container 로 프록싱해주는 구성의 Deployment.yaml 이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo
  template:
    metadata:
      labels:
        app: echo
    spec:
      containers:
      - name: nginx
        image: "gihyodocker/nginx:latest"
        imagePullPolicy: Always
        livenessProbe:
          httpGet:
            path: /
            port: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
        ports:
          - name: http
          containerPort: 80
        env:
        - name: BACKEND_HOST
          value: "localhost:8080"
      - name: echo
        image: "gihyodocker/echo:latest"
        imagePullPolicy: Always
        ports:
          - containerPort: 8080
        env:
          - name: HTTP_PORT
          value: "8080"


위와 같은 구성을 자동으로 생성된 템플릿 deployment.yaml 에 적용하려면 아래의 container 설정을 적용해주면 된다. 위의 코드블록은 템플릿 그 자체이고 그 아래는 container 설정을 적용하기 위한 container 코드이다.

container 의 위치는 spec.template.spec 이하 이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "echo.fullname" . }}
  labels:
{{ include "echo.labels" . | indent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "echo.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "echo.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
    {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}


위의 템플릿에서 containers 를 아래와 같이 수정해주자. 

....
      containers:
        - name: nginx
          image: "{{ .Values.nginx.image.repository }}:{{ .Values.nginx.image.tag }}"
          imagePullPolicy: {{ .Values.nginx.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: {{ .Values.nginx.healthCheck }}
              port: http
          readinessProbe:
            httpGet:
              path: {{ .Values.nginx.healthCheck }}
              port: http
          env:
            - name: BACKEND_HOST
              value: {{ .Values.nginx.backendHost | quote }}
        - name: echo
          image: "{{ .Values.echo.image.repository }}:{{ .Values.echo.image.tag }}"
          imagePullPolicy: {{ .Values.echo.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.echo.httpPort }}
          env:
            - name: HTTP_PORT
              value: {{ .Values.echo.httpPort | quote }}
....

template 을 보자 많이 낯설 수 있는데,

이전에 values.yaml 이라는 파일이 있고 이 파일안에는 애플리케이션 동작을 제어하는 설정의 기본값들이 있다는 것을 이해하면 연상되는 무엇이 생각날 것이다.

우리가 수정한 코드를 자세히 보면 nginx 의 repository, image tag, pull policy, healthCheck 그리고 echo 에 대한 정보들이다.

그리고 수정한 부분을 면밀히 살펴보면 각 키의 시작이 .Values 로 시작을 한다. 이는 values.yaml 의 값을 참조하겠다는 것이다.

values.yaml 파일을 아래와 같이 설정정보를 끼워넣어주어 커스텀한 value 를 설정해보자.

....
replicaCount: 1

nginx:
  image:
    repository: gihyodocker/nginx
    tag: latest
    pullPolicy: Always
  healthCheck: /
  backendHost: localhost:8080

echo:
  image:
    repository: gihyodocker/echo
    tag: latest
    pullPolicy: Always
  httpPort: 8080
....


배포에 대해 구성을 정의하였으니 이제 서비스에 대한 구성을 정의해보자.

echo 라는 이름으로 서비스를 구성하고 80번 포트를 nginx 컨테이너의 80번 포트로 포워딩하자.

(책에서는 ports 의 containerPort 를 임의로 default 값 80을 정의해주었으나 본 문서엔 그런 정의가 빠져있으므로 모두 기입해주자. 책 286의 중간 코드블록과 287번 상단의 내용, 첫번째 코드블록에 대한 이야기이다.)

....
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.externalPort }}
      targetPort: http
      protocol: TCP
      name: {{ .Values.service.name }}


....


위와 같이 service 의 ports 타입에 대해서만 커스텀하게 수정해주자. 그리고 이에 맞춰서 values.yaml 을 수정해주자

아래 코드 블록중 위는 기존이고 아래는 수정버전이다.

....
service:
  type: ClusterIP
  port: 80
....
service:
  name: nginx
  type: ClusterIP
  externalPort: 80


이어서 볼 것은 ingress 인데 인그레스는 쿠버네티스 클러스터가 동작하는 플랫폼에 의존하는 면이 크기 때문에 대부분의 차트에서 비활성화 상태이다.

values.yaml 을 확인해보면 ingress: enabled: false 임을 확인할 수 있다.


위와 같이 차트를 작업하였다면 차트를 패키징 해보자. Chart.yaml 파일에 차트의 이름과 버전을 정의할 수 있다. 이 정보는 패키징 과정에서 사용된다. 처음 chart 를 생성할 때에 이미 정의되었을 것이다.

패키징은 아래와 같이 진행하면 되는데, 진행 전에 Chart.yaml 을 본다면 아래와 같이 입력되어있을 것이다.

그렇다면 패키징 이후에 패키징 네임은 echo-0.1.0.tgz 으로 될 것이다. (차트명-버전.tgz)

패키징은 패키지 디렉토리 상위에서 진행하자. (echo 차트 디렉터리 상위)

apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: echo
version: 0.1.0


이제 패키징된 차트를 설치해보자.

이미 쿠버네티스 리소스의 구성 및 설정 기본값들이 차트에 들어 있기 때문에 로컬 환경에서 변경이 필요한 설정값이 담긴 커스텀 value 파일만 갖추면 된다.

앞서서 ingress 에 대한 설정을 하지 않았는데, 특정 host 로 요청이 들어왔을 때 그 요청을 echo 서비스로 위임하는 ingress 를 설정해보자.

values.yaml 의 ingress 부분을 아래 처럼 수정해보자.

ingress:
  enabled: true
  annotations: {}
  hosts:
    - host: ch06-echo.gihyo.local
      path: [/]




절대 주의할 점은 echo 는 local repository 에 설치한 상태이다.

따라서 local repository 가 구동되어있는지 체크해야 한다. (그렇지 않다면 repository 가 구동중이 아니므로 install 하려 할 때에 chart 를 못찾는다는 에러를 계속 보게 될 것이다.)

만약 계속 에러가 난다면 helm serve & 을 통해서 local repository 를 구동시켜주자.

아래는 구성된 차트를 릴리즈하는 명령어이다.

helm install -f values.yaml --name echo local/echo


아래는 삽질을 좀 하면서 몇몇 팁...

helm get {} : release 된 chart 를 가져온다

helm search {} : 설치가 가능한 chart 를 가져온다.

helm delete {release chart name} : 릴리즈 chart 제거

helm repo remove {localhost} : localhost 라는 네임의 chart repository 자체를 삭제.. (대체 설치 가능한 chart 는 어떻게 삭제할 수 있는가...)

  • No labels