Dev./Kubernetes & Helm

Helm: Apply Actual Application

Ivan'show 2023. 10. 9.
728x90
반응형

App 적용하기

sqlite 버전이 아닌 실제 버전으로 pg DB 와 연결하자

# values.yaml

# tag 를 sqlite 이서 일반 버전으로
image: 
  repository: likelion-cr-mh.kr.ncr.ntruss.com/lion-app
  tag: 0.2.0
  pullPolicy: IfNotPresent

imagePullSecrets:
  - name: regcred

nameOverride: ""
fullnameOverride: ""

replicaCount: 1

service:
  type: LoadBalancer
  port: 80
  targetPort: 8000

# pg 를 쓸 예정이기 때문에 db 용 서비스 value 정의
dbService:
  type: NodePort
  port: 5432

containerPort: 8000

configMap:
  django: "django-config"

secret:
  django: "django-secret"
  db: "lion-db"
# _helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "lion.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "lion.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{- define "lion.db.fullname" -}}
{{- $name := .Chart.Name }}
{{- if contains $name .Release.Name }}
{{- printf "%s-%s" "db" .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s-%s" "db" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "lion.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "lion.labels" -}}
{{ include "lion.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "lion.selectorLabels" -}}
app.kubernetes.io/name: {{ include "lion.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
release: {{ .Release.Name }}
{{- end }}

{{- define "lion.db.labels" -}}
helm.sh/chart: {{ include "lion.chart" . }}
{{ include "lion.db.selectorLabels" . }}
release: {{ .Release.Name }}
app.kubernetes.io/managed-by: helm
{{- end }}

# labels 가 다중으로 입력되지 않는 에러 때문에 우선 1개만 따로 뺴서 사용
{{- define "lion.db.selectorLabels" -}}
app: db-{{ .Release.Name }}
{{- end}}
# db-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: {{ include "lion.db.fullname" . }}
  labels:
    app: {{ include "lion.db.labels" . | nindent 4}}
spec:
  containers:
    - name: {{ include "lion.db.fullname" . }}
      image: postgres:13
      env:
      - name: PGDATA
        value: "var/lib/postgresql/data/separate"
      envFrom:
      - secretRef:
          name: {{ include "lion.db.fullname" . }}
      ports: 
        - containerPort: 5432
      volumeMounts:
      - name: postgres-data
        mountPath: "var/lib/postgresql/data"
  volumes:
  - name: postgres-data
    persistentVolumeClaim:
      claimName: db-pod-pvc
# db-pvc.yaml
# persistance volume claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-pod-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: nks-block-storage
# db-secret.yaml
apiVersion: v1
kind: Secret
# db, be 에서 모두 가져다 쓰는 정보
metadata:
  name: {{ include "lion.db.fullname" . }}
type: Opaque
data:
  POSTGRES_DB: cG9zdGdyZXM=
  POSTGRES_USER: cG9zdGdyZXM=
  POSTGRES_PASSWORD: cG9zdGdyZXM=
  POSTGRES_PORT: NTQzMg==
# db-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "lion.db.fullname" . }}
  labels:
    {{- include "lion.db.labels" . | nindent 4 }}
spec:
  type: {{ .Values.dbService.type }}
# port 5432:5432
  ports:
    - port: {{ .Values.dbService.port }}
      targetPort: {{ .Values.dbService.port }} 
      protocol: TCP
  selector:
    {{- include "lion.db.selectorLabels" . | nindent 4 }}
# django-config.yaml -> 모종의 이유로 secret 파일이 생성되지 않아 당분간 같이 사용
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "lion.fullname" . }}
data:
  DJANGO_SETTINGS_MODULE: "lion_app.settings.staging"
  DJANGO_SECRET_KEY: "DJANGO_SECRET_KEY" # not supposed to be here
  DB_HOST: {{ include "lion.db.fullname" . }}
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "lion.fullname" . }}
  labels:
    {{- include "lion.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "lion.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "lion.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          envFrom: 
            - configMapRef:
                name: {{ include "lion.fullname" . }}
            - secretRef:
                name: {{ include "lion.db.fullname" . }}
          ports:
            - name: http
              containerPort: {{ .Values.containerPort }}
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /health/
              port: {{ .Values.containerPort }}
            initialDelaySeconds: 5
          readinessProbe:
            httpGet:
              path: /health/
              port: {{ .Values.containerPort }}

잘 연결이 되 었는지 확인하기 위해 슈퍼유저를 한번 생성해보자

# 
kimminhyeok@Ivans-Mac k8s % k get po
NAME                            READY   STATUS    RESTARTS   AGE
db-staging-lion                 1/1     Running   0          38m
staging-lion-79796968d5-9r89l   1/1     Running   0          38m
kimminhyeok@Ivans-Mac k8s % k exec -it staging-lion-79796968d5-9r89l -- /bin/sh
/app # python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@admin.com
Password: 
Password (again): 
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
/app #

values 의 값들을 함수로 인코딩

secret 에서는 인코딩 된 정보를 요구한다. 그래서 values 에 인코딩된 정보를 넣어 두었는데, 이렇게되면 개발하는 사람도 헷갈릴 수 있다. 그래서 변수에는 인코딩되지 않은 값을 넣고 값을 직접 쓸 때 인코딩하는 방식으로 해보자.

# valeus.yaml
secret:
  django:
    DJANGO_SECRET_KEY: -zb#q7o+&-cz9-pz9%j@c@#@5@2l2kklbnp8i6kpu9$r=58y0j
  db:
    POSTGRES_DB: postgres
    POSTGRES_USER: postgres
    POSTGRES_PASSWORD: postgres
    POSTGRES_PORT: "5432"
# db-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "lion.db.fullname" . }}
type: Opaque
# rage 형태로 변경 후 
data:
  {{- range $key, $val := .Values.secret.db }}
    {{ $key }}: {{ $val | b64enc | quote }}
  {{- end }}
k get secret db-staging-lion -o yaml
# result
apiVersion: v1
data:
  POSTGRES_DB: cG9zdGdyZXM=
  POSTGRES_PASSWORD: cG9zdGdyZXM=
  POSTGRES_PORT: NTQzMg==
  POSTGRES_USER: cG9zdGdyZXM=
kind: Secret
metadata:

인코딩 되어서 들어간 내용 확인가능

 

Pure Kubernetes vs Helm

기존 쿠바네티스 방식은 yaml 파일에 모든 설정 값을 하드 코딩으로 적어 세밀한 컨트롤이 가능하게 리소스를 핸들링 하지만, helm 은 패키지 관리와 템플릿으로 리소스를 관리할 수 있어 운영을 단순화시켜준다.

# pure kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  ...
# helm
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.app.name }}
spec:
  ...

헬름은 러닝커브가 좀 있다는 부분을 감안하고 학습해두면 실제로 리소스를 핸들링하는 과정에서 시간단축을 많이 할 수 있을 것 같다.

stg, prod 구분해서 환경변수 핸들링

계속해서 values 값을 수정하면서 핸들링 하다보면 실수가 발생할 수 있다. 그래서 prod 와 stg 두개의 values로 쉽게 핸들링 할 수 있게 해보자.

먼저 values 를 stg_values 와 prod_values 로 만들어서 장고의 settings 파일을 바꾸 저장해두자.

#
DJANGO_SETTINGS_MODULE: "lion_app.settings.staging"

#
DJANGO_SETTINGS_MODULE: "lion_app.settings.prod"
helm install staging ./lion/ --values ./lion/stg_values.yaml
helm install prod ./lion/ --values ./lion/prod_values.yaml
kimminhyeok@Ivans-Mac k8s % k get po
NAME                            READY   STATUS    RESTARTS   AGE
db-prod-lion                    1/1     Running   0          24m
db-staging-lion                 1/1     Running   0          43s
prod-lion-78cc6bdd9c-r2qd7      1/1     Running   0          24m
staging-lion-79796968d5-bg5pn   1/1     Running   0          43s

이렇게 하면 두개의 다른 변수 정보들로 관리하고 운영할 수 있다.

namespace 로 구분 짓기

네임 스페이스라는 신박한 개념이 있다. 네이밍을 일일히 하는 과정들 번거롭다 생각한 누군가 아예 분할 시켜버릴 수 있는 파워풀한 아이템을 가져온 것 같다.

helm install lion ./lion/ --values ./lion/stg_values.yaml -n stg --create-namespace
helm install lion ./lion/ --values ./lion/prod_values.yaml -n prod --create-namespace

release name 은 구분지을 필요 없다. 뒤에 네임스페이스로 구분하면 되니까.

네임스페이스까지 보여주는 리스트 업

k get po -A
kimminhyeok@Ivans-Mac k8s % k get po -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
default       db-prod-lion                              1/1     Running   0          24m
default       db-staging-lion                           1/1     Running   0          31s
default       prod-lion-78cc6bdd9c-r2qd7                1/1     Running   0          24m
default       staging-lion-79796968d5-bg5pn             1/1     Running   0          31s
...
...
prod          db-lion                                   1/1     Running   0          18m
prod          lion-f597857d5-7w68c                      1/1     Running   0          18m
stg           db-lion                                   1/1     Running   0          18m
stg           lion-f597857d5-xs2hn                      1/1     Running   0          18

같은 이름으로 보이지만 네임스페이스가 달라서 구분이 된다. 단, 네임스페이스로 규칙을 만들 경우 어느 네임스페이스에서 작업을 하고 있는지 인지하면서 작업을 해야한다.

단, metadata 안쪽에 namespace 를 정의해서 사용할 떄는 release name 이 같으면 안된다!

metadata:
  name: {{ include "lion.fullname" . }}
  labels:
    {{- include "lion.labels" . | nindent 4 }}
	namespace: {{ .Values.namespace }}
728x90
반응형

댓글