Helm 으로 손쉽게 Kubernetes에 배포하기

Jiwan Ahn·2024년 2월 8일
0

Helm의 정의

: Kubernetes의 템플릿 파일들의 집합을 관리하는 오픈소스 패키지 매니저 툴.

주요 용어

  • 차트 (Chart): Kubernetes 애플리케이션을 실행하는데 필요한 파일들의 묶음
  • 레포지토리 (Repository): 차트들의 저장소로, 차트들을 모아두는 공유공간이다.
  • 릴리즈 (Release): 특정 Config를 이용해 실행 중인 인스턴스

Helm의 등장 이유

  • 어떤 한 애플리케이션을 배포하려면 이에 필요한 Deployment.yaml, Service.yaml 등, 수많은 YAML 파일을 관리해야 한다.
  • 애플리케이션의 수가 증가하고 이를 배포하는 팀이 늘어날 경우, 유지보수가 굉장히 힘들어진다.
    • 배포 환경이나 특정 상황에 따라 설정값을 수정해야 하는데, 일일이 YAML을 수정해야 해서 유지보수가 어려움

⇒ 이를 위해, 애플리케이션 배포에 필요한 템플릿들을 “Charts”로 묶고, 동적으로 설정값을 변경하여 손쉽게 배포를 할 수 있는 “Helm”이 등장

⇒ Charts 배포를 통해 외부에서도 손쉽게 Kubernetes 클러스터에 애플리케이션을 배포할 수 있게 됨.

Helm의 구조

Helm의 기본 구조는 V2와 V3가 상이하다.

Helm V2

  • Helm V2는 크게 역할이 두 가지로 나뉘어졌다.

Client

Helm Repository의 Chart를 관리한다. 즉, 로컬 서버에 차트를 만들거나 차트 저장소의 릴리즈들을 관리할 때 필요한 요청을 한다.

Tiller

Kubernetes Cluster의 Api Server와 직접 통신하여, Client의 요청을 받아 Api Server에 전달하여 실제로 요청을 처리한다. Client와는 gRPC를 통해 통신한다.

Helm V3

  • Helm V2에 존재했던 Tiller 서버가 사라지고, Client가 바로 Kubernetes Cluster의 Api Server에 요청을 전달한다.
    • Tiller는 네임스페이스에 의존적이어서, 다른 네임스페이스에 배포할 수 없는 한계점이 Kubernetes의 RBAC와 어긋나는 이유로 삭제되었다.
  • 따라서, Helm 차트를 설치할 때 네임스페이스를 지정해야 한다.

구성 요소

mysql의 차트를 예시로 한번 구조를 살펴보면 다음과 같다.

├── Chart.lock
├── Chart.yaml
├── README.md
├── charts
│   └── common
│       ├── Chart.yaml
│       ├── README.md
│       ...
│       └── values.yaml
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── extra-list.yaml
│   ├── metrics-svc.yaml
│   ├── networkpolicy.yaml
│   ├── primary
│   │   ├── configmap.yaml
│   │   ├── initialization-configmap.yaml
│   │   ├── pdb.yaml
│   │   ├── startdb-configmap.yaml
│   │   ├── statefulset.yaml
│   │   ├── svc-headless.yaml
│   │   └── svc.yaml
...
├── values.schema.json
└── values.yaml

위의 예시에서 눈여겨볼 파일은 values.yamltemplates다.

templates

templates 폴더안에는 Kubernetes에서 애플리케이션을 배포하기 위한 Yaml 파일들이 담겨있다. 즉, Deployment나 Service 등, 여러 오브젝트들의 선언 파일들이 들어있다고 생각하면 된다. 이를 templates 폴더에 담은 것이나 같다.

허나, 우리가 흔히 아는 Yaml과는 조금 다른 값이 채워져 있는 것을 볼 수 있다. statefulset의 파일을 보도록 하자.

apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }}
kind: StatefulSet
metadata:
  name: {{ include "mysql.primary.fullname" . }}
  namespace: {{ include "common.names.namespace" . | quote }}
  labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
    app.kubernetes.io/component: primary
  {{- if .Values.commonAnnotations }}
  annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
  {{- end }}
spec:
  replicas: 1
  podManagementPolicy: {{ .Values.primary.podManagementPolicy | quote }}
  {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.primary.podLabels .Values.commonLabels ) "context" . ) }}
  selector:

보다시피, {{ }} 형식으로 동적으로 값이 들어갈 수 있는 부분이 있다. 이 부분을 잘 보면, Values 에서 값을 가져오는 것을 볼 수 있는데, Helm은 이처럼 정적인 파일인 template에, values.yaml을 통해 동적으로 값을 삽입할 수 있도록 했다.

💡 즉, Helm은 정적인 template에 동적인 설정 값 (Value)로 오브젝트들을 패키지 형태로 관리한다.

values.yaml

위에서 설명한 정적인 파일 template에, 설정 값을 삽입하는 구성요소가 바로 values.yaml이다.

global:
  imageRegistry: ""
  ## E.g.
  ## imagePullSecrets:
  ##   - myRegistryKeySecretName
  ##
  imagePullSecrets: []
  storageClass: ""

## @section Common parameters
##

## @param kubeVersion Force target Kubernetes version (using Helm capabilities if not set)
##
kubeVersion: ""
## @param nameOverride String to partially override common.names.fullname template (will maintain the release name)
##
nameOverride: ""
## @param fullnameOverride String to fully override common.names.fullname template
##
fullnameOverride: 

다음은 mysql 차트의 values.yaml의 일부다. 이처럼, 커스터마이징 하고 싶은 설정값을 채움으로써 배포 환경이나 의도에 맞게 패키지를 세부적으로 설정할 수 있다.

특이한 점은, values.yaml은 정적인 template 파일에 동적으로 설정값을 삽입하는 구성요소다 보니, Override하고 싶은 필드만 명시해도 정상적으로 패키지를 배포할 수 있다.

예를 들어, mysql의 service를 ClusterIP가 아닌 NodePort로 배포하고 싶으면 단순히 해당 필드만 명시하면 된다.

service:
	type: NodePort
	nodePorts:
		mysql: 30200

그 후, 다음 명령어를 입력함으로써 설정값을 적용하면 된다.

$ helm install --values custom_values.yaml mysql stable/mysql
또는
$ helm install -f custom_values.yaml mysql stable/mysql

사용법

Repository 연결

helm repo add stable https://charts.helm.sh/stable

위의 명령어는 helm 차트 원격 레포지토리 중, stable 버전이 게시되어 있는 레포지토리를 추가하여, stable 이라는 이름으로 레포지토리를 저장하는 작업이다.

$ helm repo list
NAME                	URL
prometheus-community	https://prometheus-community.github.io/helm-charts
grafana             	https://grafana.github.io/helm-charts
traefik             	https://traefik.github.io/charts
sonarqube           	https://SonarSource.github.io/helm-chart-sonarqube
bitnami             	https://charts.bitnami.com/bitnami
apache-airflow      	https://airflow.apache.org
mongodb             	https://mongodb.github.io/helm-charts
jetstack            	https://charts.jetstack.io
elastic             	https://helm.elastic.co
gitlab              	https://charts.gitlab.io
stable              	https://charts.helm.sh/stable

이를 통해, helm 레포지토리가 추가되어 해당 레포지토리에 게시되어 있는 Chart들을 선택할 수 있다.

$ helm search repo stable
NAME                                 	CHART VERSION	APP VERSION            	DESCRIPTION
stable/acs-engine-autoscaler         	2.2.2        	2.1.1                  	DEPRECATED Scales worker nodes within agent pools
stable/aerospike                     	0.3.5        	v4.5.0.5               	DEPRECATED A Helm chart for Aerospike in Kubern...
stable/airflow                       	7.13.3       	1.10.12                	DEPRECATED - please use: https://github.com/air...
stable/ambassador                    	5.3.2        	0.86.1                 	DEPRECATED A Helm chart for Datawire Ambassador
stable/anchore-engine                	1.7.0        	0.7.3                  	Anchore container analysis and policy evaluatio...
stable/apm-server                    	2.1.7        	7.0.0                  	DEPRECATED The server receives data from the El...
stable/ark                           	4.2.2        	0.10.2                 	DEPRECATED A Helm chart for ark
stable/artifactory                   	7.3.2        	6.1.0                  	DEPRECATED Universal Repository Manager support...
stable/artifactory-ha                	0.4.2        	6.2.0                  	DEPRECATED Universal Repository Manager support...
stable/atlantis                      	3.12.4       	v0.14.0                	DEPRECATED A Helm chart for Atlantis https://ww...
...

저장한 레포지토리들의 차트를 최신화하려면 helm repo update를 통해 최신화할 수 있다.

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "apache-airflow" chart repository
...Successfully got an update from the "sonarqube" chart repository
...Successfully got an update from the "elastic" chart repository
...Successfully got an update from the "traefik" chart repository
...Successfully got an update from the "grafana" chart repository
...Successfully got an update from the "prometheus-community" chart repository
...Successfully got an update from the "gitlab" chart repository
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "mongodb" chart repository
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!

Helm으로 Mysql 설치

현재 홈서버의 Kubernetes는 Helm을 사용하지 않고, 기존 template으로 설정한 상태이다. 따라서 데이터를 미리 백업하고 재설치를 진행한다.

$ mysqldump -u root -p --all-databases > mysql_backup.sql

그 후, helm search repo mysql을 통해 가능한 mysql의 버전을 확인한다.

$ helm search repo mysql
NAME                                          	CHART VERSION	APP VERSION	DESCRIPTION
bitnami/mysql                                 	9.18.0       	8.0.36     	MySQL is a fast, reliable, scalable, and easy t...
prometheus-community/prometheus-mysql-exporter	2.4.0        	v0.15.1    	A Helm chart for prometheus mysql exporter with...
stable/mysql                                  	1.6.9        	5.7.30     	DEPRECATED - Fast, reliable, scalable, and easy...
stable/mysqldump                              	2.6.2        	2.4.1      	DEPRECATED! - A Helm chart to help backup MySQL...
stable/prometheus-mysql-exporter              	0.7.1        	v0.11.0    	DEPRECATED A Helm chart for prometheus mysql ex...
bitnami/phpmyadmin                            	14.2.0       	5.2.1      	phpMyAdmin is a free software tool written in P...
stable/percona                                	1.2.3        	5.7.26     	DEPRECATED - free, fully compatible, enhanced, ...
stable/percona-xtradb-cluster                 	1.0.8        	5.7.19     	DEPRECATED - free, fully compatible, enhanced, ...
stable/phpmyadmin                             	4.3.5        	5.0.1      	DEPRECATED phpMyAdmin is an mysql administratio...
bitnami/mariadb                               	15.2.0       	11.2.2     	MariaDB is an open source, community-developed ...
bitnami/mariadb-galera                        	11.2.0       	11.2.2     	MariaDB Galera is a multi-primary database clus...
stable/gcloud-sqlproxy                        	0.6.1        	1.11       	DEPRECATED Google Cloud SQL Proxy
stable/mariadb                                	7.3.14       	10.3.22    	DEPRECATED Fast, reliable, scalable, and easy t...

이 중에서 stable/mysql 버전은 DEPRECATED 되었다고 표시하므로, 최신 버전인 bitnami/mysql을 설치한다.

$ helm install -f values.yaml mysql bitnami/mysql
NAME: mysql
LAST DEPLOYED: Wed Jan 24 15:34:12 2024
NAMESPACE: mysql
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 9.18.0
APP VERSION: 8.0.36

** Please be patient while the chart is being deployed **

Tip:

  Watch the deployment status using the command: kubectl get pods -w --namespace mysql
...

$ kc get po
kNAME      READY   STATUS             RESTARTS      AGE
mysql-0   0/1     CrashLoopBackOff   6 (90s ago)   8m11s

$ kc logs -f mysql-0
mysql 15:40:54.04 INFO  ==>
mysql 15:40:54.04 INFO  ==> Welcome to the Bitnami mysql container
mysql 15:40:54.04 INFO  ==> Subscribe to project updates by watching https://github.com/bitnami/containers
mysql 15:40:54.05 INFO  ==> Submit issues and feature requests at https://github.com/bitnami/containers/issues
mysql 15:40:54.05 INFO  ==>
mysql 15:40:54.06 INFO  ==> ** Starting MySQL setup **
mysql 15:40:54.09 INFO  ==> Validating settings in MYSQL_*/MARIADB_* env vars
mysql 15:40:54.11 INFO  ==> Initializing mysql database
mkdir: cannot create directory '/bitnami/mysql/data': Permission denied

cannot create directory '/bitnami/mysql/data': Permission denied의 오류를 해결하기 위해 volumePermission을 true로 설정하고, root user와 password도 설정한다.

$ vi values.yaml

volumePermissions:
	enabled: true

auth:
	rootPassword: "~~~~"

다음 명령어를 실행하여 Mysql을 설치한다.

$ helm install -f values.yaml mysql bitnami/mysql

#만일 이미 설치되어 있다면
$ helm upgrade --install -f values.yaml mysql bitnami/mysql

NAME: mysql
LAST DEPLOYED: Wed Jan 24 16:39:25 2024
NAMESPACE: mysql
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 9.18.0
APP VERSION: 8.0.36

** Please be patient while the chart is being deployed **

Tip:
...

$ kc get all
NAME          READY   STATUS    RESTARTS   AGE
pod/mysql-0   1/1     Running   0          3m52s

NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/mysql            ClusterIP   10.104.132.205   <none>        3306/TCP   3m52s
service/mysql-headless   ClusterIP   None             <none>        3306/TCP   3m52s

NAME                     READY   AGE
statefulset.apps/mysql   1/1     3m52s

그 후, 파드 내부로 들어가서 데이터를 복구한다.

$ mysql -u root -p < mysql_backup.sql

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| boj_bot_db         |
| diareat            |
| family_app_db      |
| gstagram           |
| hackerthon         |
| information_schema |
| my_database        |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
10 rows in set (0.02 sec)

잘 복구된 것을 볼 수 있다.

Node Server를 Helm Chart로 생성

Helm으로 Node Server를 만들기 위해서는 먼저 Helm을 통해 기본 구조의 차트를 만들어야 한다.

$ helm create node_server

$ tree node_server
node_server
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

다음과 같이 기본적인 구조의 Chart가 생성된다.

그 후, template 형식에 맞게 기존의 Manifest 파일들을 수정해주어야 한다.

다음은 기존의 Manifest 파일 중, Deployment와 Service의 Spec이 명시된 Yaml이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-server
  namespace: node-server
spec:
  replicas: 2
  selector:
    matchLabels:
      app: node-server
  template:
    metadata:
      labels:
        app: node-server
    spec:
      containers:
        - name: node-server
          image: public.ecr.aws/l2c3u0x6/node_server:latest
          imagePullPolicy: IfNotPresent
          resources:
            requests:
              memory: "512Mi"
              cpu: "0.2"
            limits:
              memory: "2Gi"
              cpu: "1"
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: node-server-configmap
---
apiVersion: v1
kind: Service
metadata:
  name: interview
  namespace: interview
spec:
  selector:
    app: interview
  ports:
  - port: 80
    targetPort: 3000
    protocol: TCP
    nodePort: 30300
  type: NodePort
    

Go Template

Helm에서 동적으로 설정값을 넣기 위해서는 Template의 필드의 값 부분을 Go Template을 이용하여 대체해야 한다.

Manifest 파일을 Go Template에 맞게 수정하기 위해, 천천히 단계적으로 접근해보자.

그 전에 Helm에 내장된, 즉, 사전에 정의된 필드를 알면 좀 더 작성하기 쉽다.

Built-in Instance

  • Release: 패키지의 릴리즈 자체를 의미한다.
    • Release.Name: 릴리즈 이름
    • Release.Namespace: 릴리즈될 네임스페이스
    • Release.Revision: 릴리즈의 리비전 번호. 설치시 1이고, Upgrade 또는 Rollback시 번호가 증가한다.
  • Values: values.yaml을 통해 전달될 설정 값을 의미한다.
  • Chart: Chart.yaml을 통해 전달될 값을 의미한다.
  • Capabilites: 쿠버네티스 클러스터가 지원하는 기능에 대한 정보를 제공한다.
  • Template: 현재 실행 중인 템플릿의 정보를 포함한다.
    • Name: 현재 템플릿에 대한 전체 파일 경로
    • BasePath: 현재 템플릿에 대한 디렉토리 경로

Include

helm을 통해 템플릿을 정의할 때, 재활용 가능한 필드를 미리 템플릿 조각 (.tpl)에 정의한 후, 이를 사용할 때 Include 문을 사용한다.

예를 들어,

{{- define "example.labels" -}}
app.kubernetes.io/name: {{ include "example.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

이러한 템플릿 조각 파일 (example.tpl)을 정의했다면, 다음과 같이 재활용 할 수 있다.

metadata:
	labels:
		{{- include "example.lables" . | nindent 4}}

즉, 이는 아래의 내용과 동일하다. 여기서 nindent 4란 들여쓰기를 4번 하라는 뜻이다. Yaml파일은 들여쓰기에 매우 민감하기 때문에, 필드를 맞추기 위해서 들여쓰기 횟수를 명시해야 한다.

metadata:
	labels:
		app.kubernetes.io/name: {{ include "example.name" . }}
		app.kubernetes.io/instance: {{ .Release.Name }}

Template 제작

우선, Deployment의 name부터 시작해보겠다. 보통 helm install을 통해 패키지를 설치하고자 할 때는 다음과 같은 양식을 따른다.

helm install <설치하는 Package에 붙일 이름> <차트 명칭>

이 때, <설치하는 Package에 붙일 이름> 이 곧 애플리케이션의 이름이 되며, 이는 곧 metadata.name의 값이 되므로, 이를 대체해야 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "node-server.fullname" . }}
...

{{ include "node-server.fullname" . }}은 템플릿 조각 _helpers.tpl 에서 Chart에 명시되어 있는 이름과 같다. Chart.yaml에는 해당 패키지의 기본 metadata (이름, 버전)등이 명시되어 있으므로, 이를 일관되게 사용하기 위해 include를 통해 name을 주입했다.

apiVersion: apps/v1
kind: Deployment
metatdata:
	name: {{ include "node-server.fullname" . }}
	namepsace: {{ .Release.Namespace | quote }}

{{ .Release.Namespace | quote }} 의 경우는 사용자가 helm 패키지를 설치할 때 명시하는 Namespace를 지칭하며, helm v3부터는 패키지 설치시 Namespace를 지정해야 한다는 원칙에 따라 .Release.Namespace 를 그대로 사용했다. 또한 | quote 를 통해 쌍따옴표를 붙여 String으로 인식하도록 했다.

조건절

Go 템플릿은 조건절을 지원하기도 한다. 즉, 조건절에 따라 해당 필드를 포함할 수도, 포함하지 않을 수도 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
	name: {{ include "node-server.fullname" . }}
	namepsace: {{ .Release.Namespace | quote }}
spec:
	replicas: 2 ##

이 Replicas를 Autoscaling 옵션이 false 일 경우에만 필드에 포함하도록 바꾸면 다음과 같다.

apiVersion: apps/v1
kind: Deployment
metadata:
	name: {{ include "node-server.fullname" . }}
	namepsace: {{ .Release.Namespace | quote }}
spec:
	{{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}

{{- if not .Values.autoscaling.enabled }}의 뜻은 values.yaml에서 autoscaling 필드의 하위 필드 enabled가 false일 때만 해당 필드를 사용하겠다는 뜻이 된다. 즉, values.yaml에서,

namespace: interview

autoscaling:
	enabled: false
	minReplicas: 1
	maxReplicas: 3
	targetCPUUtilizationPercentage: 50
	targetMemoryUtilizationPercentage: 50

replicaCount: 2

다음과 같이 enabled: false일 때만 replicas의 수를 명시하겠다는 뜻이다. 만일 true이면 replicas 필드에 값이 기입된다.

위에서 사용한 방법으로 나머지 필드를 모두 채우면 다음과 같은 모습이 된다.

apiVersion: apps/v1
kind: Deployment
metadata:
	name: {{ include "node-server.fullname" . }}
	namepsace: {{ .Release.Namespace | quote }}
spec:
	{{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }
  {{- end }}
  selector:
    matchLabels:
      app: {{ .Values.labels.app }}
  template:
    metadata:
      labels:
        app: {{ .Values.labels.app }}

컨테이너 스펙 명시

이제 남은 필드는 컨테이너의 spec 필드다.

spec:
  containers:
  - name: node_server
    image: public.ecr.aws/l2c3u0x6/node_server:latest
    imagePullPolicy: Always
    resources:
			requests:
        memory: "512Mi"
        cpu: "0.2"
      limits:
        memory: "128Mi"
        cpu: "500m"
    ports:
    - containerPort: 3000

우선, Containers의 name필드는 Chart.yaml에 적힌 Name을 사용할 것이다.

spec:
  containers:
  - name: {{ Chart.Name }}

또한, 이미지의 경로 역시 하드코딩하는 것은 적절하지 않으므로, values.yaml에서 동적으로 불러오도록한다.

spec:
  containers:
  - name: {{ Chart.Name }}
    image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}

여기서 {{ .Values.image.tag | default .Chart.Appversion }}default는 말 그대로 기본값을 설정하는 것이다. 즉, image.tag가 명시되지 않으면 Chart.yaml의 Appversion을 사용하겠다는 것이다.

values.yaml은 다음과 같이 생성된다.

autoscaling:
	enabled: false
	minReplicas: 1
	maxReplicas: 3
	targetCPUUtilizationPercentage: 50
	targetMemoryUtilizationPercentage: 50

replicaCount: 2

image:
	repository: public.ecr.aws/l2c3u0x6/node_server
	tag: latest

이제 어느정도 감을 잡았으니, 나머지 필드를 포함하여 전체 템플릿 파일을 생성하면 다음과 같다.

apiVersion: apps/v1
kind: Deployment
metadata:
	name: {{ include "node-server.fullname" . }}
  namespace: {{ .Release.Namespace | quote }}
spec:
	{{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      app: {{ .Values.labels.app }}
  template:
    metadata:
      labels:
        app: {{ .Values.labels.app }}
		spec:
		  containers:
		  - name: {{ .Chart.Name }}
		    image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
		    imagePullPolicy: {{ .Values.image.imagePullPolicy }}
		    resources:
					requests:
						memory: {{ .Values.resources.requests.memory }}
						cpu: {{ .Values.resources.requests.cpu }}
		      limits:
		        memory: {{ .Values.resources.limits.memory }}
		        cpu: {{ .Values.resources.limits.cpu }}
		    ports:
		    - containerPort: {{ .Values.args.port }}
				envFrom:
        - configMapRef:
	          name: {{ include "node-server.fullname" . }}

이에 따른 values.yaml은 다음과 같다.

autoscaling:
	enabled: false
	minReplicas: 1
	maxReplicas: 3
	targetCPUUtilizationPercentage: 50
	targetMemoryUtilizationPercentage: 50

replicaCount: 2

labels:
	app: node_server

image:
	repository: public.ecr.aws/l2c3u0x6/node_server
	tag: latest	
	imagePullPolicy: IfNotPresent

resources:
	requests:
		memory: "256Mi"
		cpu: "0.2"
	limits:
		memory: "512Mi"
		cpu: "0.8"

args:
	port: 3000

마찬가지로 Service도 바꿔보도록 하자.

apiVersion: v1
kind: Service
metadata:
	name: {{ include "node-server.fullname" . }}
  namespace: {{ .Release.Namespace | quote }}
spec:
  selector:
    app: {{ .Values.labels.app }}
  ports:
  - port: {{ .Values.service.port }}
    targetPort: {{ .Values.service.targetPort }}
    protocol: {{ .Values.service.protocol }}
    nodePort: 30300
  type: NodePort

이제 Service Type이 걸린다. 종류에 따라 변경하려면 어떻게 해야 할까? 위에서 사용한 조건절을 이용하면 다음과 같다.

apiVersion: v1
kind: Service
metadata:
	name: {{ include "node-server.fullname" . }}
  namespace: {{ .Release.Namespace | quote }}
spec:
  selector:
    app: {{ .Values.labels.app }}
  ports:
  - port: {{ .Values.service.port }}
    targetPort: {{ .Values.service.targetPort }}
    protocol: {{ .Values.service.protocol }}
    {{- if eq .Values.service.type "NodePort" }}
    nodePort: {{ .Values.service.nodePort }}
    {{- end }}
  type: {{ .Values.service.type }}

nodePort 필드를 사용하는 Service Type은 NodePort Service 밖에 없다. 따라서, 만일 Service Type이 NodePort가 아니라면 해당 필드를 비활성화 시켜야 하므로, 조건절을 통해 필드를 선택적으로 활성화 시키는 것이다.

지금까지 values.yaml은 다음과 같다.

autoscaling:
	enabled: false
	minReplicas: 1
	maxReplicas: 3
	targetCPUUtilizationPercentage: 50
	targetMemoryUtilizationPercentage: 50

replicaCount: 2

labels:
	app: node_server

image:
	repository: public.ecr.aws/l2c3u0x6/node_server
	tag: latest	
	imagePullPolicy: IfNotPresent

resources:
	requests:
		memory: "256Mi"
		cpu: "0.2"
	limits:
		memory: "512Mi"
		cpu: "0.8"

args:
	port: 3000

service:
	port: 80
	targetPort: 3000
	protocol: TCP
	nodePort: 30300
	type: ClusterIP

ingress:
  enabled: true

마지막으로, 웹서버가 DB에 접근할 수 있도록, 컨피그맵 또한 Go template 형식에 맞게 수정한다.

apiVersion: v1
kind: ConfigMap
metadata:
	name: {{ include "node-server.fullname" . }}
  namespace: {{ .Release.Namespace | quote }}
data:
  DB_HOST: {{ .Values.configMap.DB_HOST | quote }}
  DB_USER: {{ .Values.configMap.DB_USER | quote }}
  DB_PASSWORD: {{ .Values.configMap.DB_PASSWORD | quote }}
  DB_NAME: {{ .Values.configMap.DB_NAME | quote }}

여기서 “quote”란 값의 양 끝에 쌍따옴표 “를 붙이는 명령어를 의미한다.

최종 YAML 파일은 다음과 같다.

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 3
  targetCPUUtilizationPercentage: 50
  targetMemoryUtilizationPercentage: 50

replicaCount: 2

labels:
  app: node-server

image:
  repository: public.ecr.aws/l2c3u0x6/node_server
  tag: latest
  imagePullPolicy: Always

resources:
  requests:
    memory: "256Mi"
    cpu: "0.2"
  limits:
    memory: "1Gi"
    cpu: "1"

args:
  port: 3000

service:
  port: 80
  targetPort: 3000
  protocol: TCP
  nodePort: 30300
  type: NodePort

ingress:
  enabled: false

configMap:
  DB_HOST: ...
  DB_USER: ...
  DB_PASSWORD: ...
  DB_NAME: ...

최종 차트 구조는 다음과 같다.

tree
.
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

Helm 차트 배포

배포하기 전, helm에서 패키지들을 배포하기 위해서는 .tgz 형식으로 패키지를 묶어야 한다.

그전에 먼저 다음 명령어를 실행하여, helm 차트가 제대로 실행이 되는지 확인한다.

$ helm upgrade -i node-server ./node-server --dry-run --debug
history.go:56: [debug] getting history for release node-server
Release "node-server" does not exist. Installing it now.
install.go:214: [debug] Original chart version: ""
install.go:231: [debug] CHART PATH: /home/synoti21/interview/node-server

NAME: node-server
LAST DEPLOYED: Thu Jan 25 15:35:21 2024
NAMESPACE: node-server
STATUS: pending-install
REVISION: 1
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
args:
...

검사를 해보니 잘 실행되는 것을 확인할 수 있다. 따라서 다음 명령어를 통해 패키지 압축파일을 생성한다.

$ helm package node-server

$ ls
node-server  node-server-0.1.0.tgz 

실제로 로컬 서버에 배포를 해보자.

$ helm install node-server ./node-server -n node-server
NAME: node-server
LAST DEPLOYED: Thu Jan 25 17:45:43 2024
NAMESPACE: node-server
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace node-server -o jsonpath="{.spec.ports[0].nodePort}" services node-server)
  export NODE_IP=$(kubectl get nodes --namespace node-server -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

$ kc get all
NAME                               READY   STATUS    RESTARTS   AGE
pod/node-server-5fb7588fc4-2kdqc   1/1     Running   0          2m46s
pod/node-server-5fb7588fc4-84xhm   1/1     Running   0          2m46s

NAME                  TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/node-server   NodePort   10.105.1.165   <none>        80:30300/TCP   2m46s

NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/node-server   2/2     2            2           2m46s

NAME                                     DESIRED   CURRENT   READY   AGE
replicaset.apps/node-server-5fb7588fc4   2         2         2       2m46s

잘 작동하는 것을 볼 수 있다!

profile
Engineer, to be a Pioneer.

0개의 댓글