Advanced Helm chart 만들기 - chart hook편
Helm chart 의 Life cycle 을 이해하면 조금 더 고급스러운 chart 를 만들 수 있다. 예를 들면 이전 chart 의 애플리케이션에서 postgresql DB를 사용하기 때문에 사용자, 데이터베이스, 테이블을 생성해야 하는 경우를 생각해 보자. 차트가 배포될 때 애플리케이션이 실행되기 이전에 해당 작업들을 할 수 있다면 chart 배포 한 번으로 모든 배포를 끝낼 수 있으니 멋진 일이다.
Helm 에서는 chart 가 실행되기 이전에 혹은, 실행된 이후에 작업을 정의할 수 있도록 hook 기능을 제공한다. 이러한 hook 의 종류는 아래와 같다.
Annotation 값 | 설 명 |
---|---|
pre-install |
Executes after templates are rendered, but before any resources are created in Kubernetes |
post-install |
Executes after all resources are loaded into Kubernetes |
pre-delete |
Executes on a deletion request before any resources are deleted from Kubernetes |
post-delete |
Executes on a deletion request after all of the release's resources have been deleted |
pre-upgrade |
Executes on an upgrade request after templates are rendered, but before any resources are updated |
post-upgrade |
Executes on an upgrade request after all resources have been upgraded |
pre-rollback |
Executes on a rollback request after templates are rendered, but before any resources are rolled back |
post-rollback |
Executes on a rollback request after all resources have been modified |
test |
Executes when the Helm test subcommand is invoked ( view test docs) |
hook 을 정의하는 것은 Annotation 으로 하며, 앞에서 설명한 시나리오의 경우에는 pre-install hook 을 사용하는 것이 적절하기 때문에 "helm.sh/hook": pre-install 로 설정한다. 또한 이런 단발성 호출의 경우에는 Job 으로 설정하는 것이 좋다.
apiVersion: batch/v1
kind: Job
metadata:
...
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "1"
"helm.sh/hook-delete-policy": before-hook-creation
hook-weight 는 낮을 수록 우선순위가 높으며, hook-delete-policy 의 before-hook-creation 은 hook 으로 실행된 리소스를 다음 hook 이 실행될 때 까지 지우지 않고 남겨둔다는 의미이다. policy 는 아래와 같이 3가지가 있다.
Annotation Value | Description |
---|---|
before-hook-creation |
Delete the previous resource before a new hook is launched (default) |
hook-succeeded |
Delete the resource after the hook is successfully executed |
hook-failed |
Delete the resource if the hook failed during execution |
그럼 postgresql 을 설치하고 사용자, 데이터베이스, 테이블을 생성하는 pre-install hook 을 작성해 보자.
1. postgresql 설치
bitnami chart repo 에서 postgresql 을 다운받아 설치한다.
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm repo update
$ helm upgrade -i postgresql bitnami/postgresql --version 10.8.0 -n decapod-db --create-namespace \
--set postgresqlPassword=password \
--set persistence.enabled=true \
--set persistence.storageClass=rbd \
--set persistence.size=10Gi
chart 의 value 값을 필요한 부분만 override 하였다. db user 는 postgres 이며 db password는 password 로 설치했다. 테스트 환경에서는 외부 스토리지를 제공하고 있어 10Gi 로 설정하였다.
$ kubectl get pods -n decapod-db
NAME READY STATUS RESTARTS AGE
postgresql-postgresql-0 1/1 Running 0 35h
$ kubectl get svc -n decapod-db
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
postgresql ClusterIP 10.233.46.14 <none> 5432/TCP 35h
postgresql-headless ClusterIP None <none> 5432/TCP 35h
2. pre-install hook 만들기
chart 가 설치될 때 한 번 실행되면 되므로 Job 타입으로 리소스를 작성한다. psql 명령을 사용할 수 있는 컨테이너 이미지를 활용하고 수행할 명령어들은 쉘 스크립트로 작성하였다.
{chart_source_home}/templates/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "tks-contract.fullname" . }}
namespace: {{ .Values.namespace }}
labels:
{{- include "tks-contract.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
metadata:
name: {{ include "tks-contract.fullname" . }}
spec:
restartPolicy: Never
containers:
- name: pre-install-job
image: "bitnami/postgresql:11.12.0-debian-10-r44"
env:
- name: DB_ADMIN_USER
value: {{ .Values.db.adminUser }}
- name: PGPASSWORD
value: {{ .Values.db.adminPassword }}
- name: DB_NAME
value: {{ .Values.db.dbName }}
- name: DB_USER
value: {{ .Values.args.dbUser }}
- name: DB_PASSWORD
value: {{ .Values.args.dbPassword }}
- name: DB_URL
value: {{ .Values.args.dbUrl }}
- name: DB_PORT
value: "{{ .Values.args.dbPort }}"
command:
- /bin/bash
- -c
- -x
- |
# check if ${DB_NAME} database already exists.
psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -lqt | cut -d \| -f 1 | grep -qw ${DB_NAME}
if [[ $? -ne 0 ]]; then
psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -c "CREATE DATABASE ${DB_NAME};"
fi
# check if ${DB_USER} user already exists.
psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -tc '\du' | cut -d \| -f 1 | grep -qw ${DB_USER}
if [[ $? -ne 0 ]]; then
psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -c "create user ${DB_USER} SUPERUSER password '${DB_PASSWORD}';"
fi
# check if contracts table in tks database already exists.
psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -d ${DB_NAME} -tc '\dt' | cut -d \| -f 2 | grep -qw contracts
if [[ $? -ne 0 ]]; then
echo """
\c ${DB_NAME};
CREATE TABLE contracts
(
contractor_name character varying(50) COLLATE pg_catalog."default",
id uuid primary key,
available_services character varying(50)[] COLLATE pg_catalog."default",
updated_at timestamp with time zone,
created_at timestamp with time zone
);
CREATE UNIQUE INDEX idx_contractor_name ON contracts(contractor_name);
ALTER TABLE contracts CLUSTER ON idx_contractor_name;
INSERT INTO contracts(
contractor_name, id, available_services, updated_at, created_at)
VALUES ('tester', 'edcaa975-dde4-4c4d-94f7-36bc38fe7064', ARRAY['lma'], '2021-05-01'::timestamp, '2021-05-01'::timestamp);
CREATE TABLE resource_quota
(
id uuid primary key,
cpu bigint,
memory bigint,
block bigint,
block_ssd bigint,
fs bigint,
fs_ssd bigint,
contract_id uuid,
updated_at timestamp with time zone,
created_at timestamp with time zone
);
""" | psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER}
fi
{chart_source_home}/values.yaml
args:
port: 9110
dbUrl: postgresql.decapod-db.svc
dbPort: 5432
dbUser: tksuser
dbPassword: password
db:
adminUser: postgres
adminPassword: password
dbName: tks
컨테이너에서 필요한 값들(db superuser admin 과 password, db url, db port 등)은 env 로 전달한다. 참고로 postgresql 은 psql 로 접속할 때 패스워드를 전달하는 아규먼트가 없다. 대신 환경 변수로 다음과 같이 설정하면 패스워드를 사용하여 접속할 수 있다. ($ export PGPASSWORD=password)
- 먼저 DB 에 해당 database 가 존재하는 조회하고 없으면 database 를 생성한다.
- DB User 가 존재하는지 조회하고 없으면 새로운 DB User 를 생성한다.
- Table 이 존재하는지 조회하고 없으면 Table 을 생성한다. Table 을 생성하기 위해 psql 로 접속 시에 해당 database 에 접속할 수 있지만 (-d database명 옵션 사용), "\c database명" 으로 database 를 선택할 수 있다는 것을 보여주기 위해 이 부분을 추가하였다.
chart 를 배포하면 다음과 같이 job 이 실행됨을 알 수 있다.
$ kubectl get pods -n tks
NAME READY STATUS RESTARTS AGE
tks-contract-h5468 0/1 Completed 0 31h
$ kubectl logs tks-contract-h5468 -n tks
+ psql -h postgresql.decapod-db.svc -p 5432 -U postgres -lqt
+ cut -d '|' -f 1
+ grep -qw tks
+ [[ 0 -ne 0 ]]
+ psql -h postgresql.decapod-db.svc -p 5432 -U postgres -tc '\du'
+ cut -d '|' -f 1
+ grep -qw tksuser
+ [[ 0 -ne 0 ]]
+ psql -h postgresql.decapod-db.svc -p 5432 -U postgres -d tks -tc '\dt'
+ cut -d '|' -f 2
+ grep -qw contracts
+ [[ 0 -ne 0 ]]