반응형

helm chart 를 만들기 위해서는 여러 기능들을 알아야 하지만 그 중에서 가장 많이 쓰고 헷갈리는 기능에 대해서 살펴 본다.

기본적으로 실습할 수 있는 환경을 먼저 만들고 하나씩 공부해 본다.

$ helm create flow-control
$ cd flow-control

yaml 형태의 출력을 확인해 본다.

$ helm template .
---
# Source: flow-control/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: release-name-flow-control
  labels:
    helm.sh/chart: flow-control-0.1.0
    app.kubernetes.io/name: flow-control
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
automountServiceAccountToken: true
---
# Source: flow-control/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: release-name-flow-control
  labels:
    helm.sh/chart: flow-control-0.1.0
    app.kubernetes.io/name: flow-control
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: flow-control
    app.kubernetes.io/instance: release-name
---
# Source: flow-control/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: release-name-flow-control
  labels:
    helm.sh/chart: flow-control-0.1.0
    app.kubernetes.io/name: flow-control
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: flow-control
      app.kubernetes.io/instance: release-name
  template:
    metadata:
      labels:
        helm.sh/chart: flow-control-0.1.0
        app.kubernetes.io/name: flow-control
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      serviceAccountName: release-name-flow-control
      securityContext:
        {}
      containers:
        - name: flow-control
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}
---
# Source: flow-control/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "release-name-flow-control-test-connection"
  labels:
    helm.sh/chart: flow-control-0.1.0
    app.kubernetes.io/name: flow-control
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['release-name-flow-control:80']
  restartPolicy: Never

제대로 출력된다면 필요없는 파일은 삭제하고 초기화 하자.

$ rm -rf template/*
$ cat /dev/null > values.yaml

0. yaml 은 들여 쓰기가 중요하다.

가장 간단한 configmap 을 만들어서 value 값을 출력한다.

$ values.yaml
---
favorite:
  drink: coffee
  food: pizza

$ vi template/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}
    mug: "true"
  {{ end }}

$ helm template .
--- output ---
Error: YAML parse error on flow-control/templates/configmap.yaml: error converting YAML to JSON: yaml: line 8: did not find expected key

Use --debug flag to render out invalid YAML

template 을 제너레이션하면 에러가 발생한다. 왜 그럴까?

configmap.yaml 에 mug: "true" 가 2칸 들여써 있어서 발생하는 에러이다. 이런 에러를 조심하면서 아래 실습을 해보자.

1. 조건문과 빈라인 없애기

configmap.yaml 을 수정해서 제대로 yaml 을 생성해 보자.

$ vi template/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}
  mug: "true"
  {{ end }}

$ helm template .
---
# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
                    --------------> 빈라인 발생한다.
  mug: "true"
                    --------------> 빈라인 발생한다.

.Release.Name 은 helm 으로 설치할 때 파라미터로 넘기는 값인데 여기서는 설치가 아니므로 기본 값인 release-name 으로 치환 되었고, .Values.favorite.drink 는 values.yaml 에 지정한 키값으로 그에 해당하는 밸류 값이 제너레이트 될 때 출력된다.

함수의 연속적 사용은 | 라인으로 호출 가능하며 default 는 키에 대한 값이 없을 때, quote 는 값을 " 으로 묶을 때 upper 는 값을 대문자로 변환할 때 사용하는 내장 함수이다.

비교 구문은 if eq 값1 값2 와 같이 사용할 수 있다.

출력하지 않는 곳에서는 빈라인이 발생하는데 이 부분을 다음과 같이 없애 줄 수 있다.

{{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}

하지만 가독성이 떨어지므로 {{- 와 같이 표현하면 빈라인이 없어지면서 윗라인에 나란히 붙는 것과 같은 효과를 낼 수 있다.

  {{- if eq .Values.favorite.drink "coffee" }}
  mug: "true"
  {{- end }}

다시 yaml 을 생성해 보면 아래와 같이 빈라인이 없어졌음을 알 수 있다.

$ helm template .
---
# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: "true"

조건문 대신 다음과 같이 with 를 사용하여 조건문과 키밸류 스쿱을 지정할 수 있다.

### configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.hobby }}
  sports: {{ .sports }}
  {{- end }}

### values.yaml
hobby:
  sports: golf

with 와 함께 사용한 키는 해당 키에 대한 값이 있을 때만 with ~ end 로 감싼 구문이 출력된다. 또한 감싼 구문 안에서는 스쿱이 재정의되어 hobby 아래의 키인 sports 를 .sports 로 바로 사용할 수 있다.

yaml 을 생성하면 다음과 같은 결과가 나온다.

$ helm template .

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  myvalue: "Hello World"
  sports: golf

만약 values 에 sports 를 없애면 아래와 같이 출력되지 않는다.

### values.yaml
hobby: {}

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  myvalue: "Hello World"

hobby 아래의 키는 키밸류인 디셔너리 타입을 넣기 때문에 아래의 값을 모두 없애기 위해서{} 빈 딕셔너리 값으로 지정했다. 만약 아래의 값이 리스트라면 [] 와 같이 빈 리스트 값을 지정할 수 있다.

with ~ end 로 감싼 구문에서 root 영역의 value 를 활용하고 싶을 수 있다. 이 때는 $ 를 붙혀서 영역을 최상위 root 로 접근할 수 있다. 아래 예제에서 $.Release.Name 을 참고한다.

### configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.hobby }}
  sports: {{ .sports }}
  release: {{ $.Release.Name }}
  {{- end }}

YAMLJSON 의 수퍼셋이기 때문에 JSON 으로 표현하여 가독성을 높혀줄 수 도 있다. pod 를 만들 때 yaml 에 args 를 추가할 수 있는데. 이 때 JSON 을 쓰면 읽기에 편하다.

args:
  - "--dirname"
  - "/foo"  

위의 내용은 JSON 으로 아래와 같이 바꿀 수 있다.

args: ["--dirname", "/foo"]

2. range 함수를 이용한 반복문

range 를 이용하여 반복문을 사용할 수 있다.

### values.yaml
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

### configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  toppings: |-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}

pizzaToppings 의 값은 리스트이다(- 기호가 값으로 붙었기 때문에 리스트임을 알 수 있다). 리스트로 값을 가져와서 출력하기 때문에 아래와 같은 결과가 나온다.

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  toppings: |-
    - "Mushrooms"
    - "Cheese"
    - "Peppers"
    - "Onions"

한가지 yaml 에서 toppings: 의 뒤에 따라온 |- 기호의 의미는 멀티 라인 스트링을 받겠다는 의미이다.

tuple 을 사용하여 튜플로 만들어 쓸 수 도 있다.

### configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  sizes: |-
    {{- range tuple "small" "medium" "large" }}
    - {{ . }}
    {{- end }}

### 출력 결과
# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  sizes: |-
    - small
    - medium
    - large

$:= 를 이용하여 변수를 지정할 수 있다. 아래는 리스트에서 받은 값을 index 변수와 value 변수로 받아서 활용하는 부분이다.

### values.yaml
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

### configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  toppings: |-
    {{- range $index, $value := .Values.pizzaToppings }}
    - {{ $index }}: {{ $value }}
    {{- end }}

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  toppings: |-
    - 0: mushrooms
    - 1: cheese
    - 2: peppers
    - 3: onions

map 값을 변수로 받아 처리할 수 도 있다.

### values.yaml
favorite:
  drink: coffee
  food: pizza

### configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  favorite: |-
    {{- range $key, $value := .Values.favorite }}
    {{ $key }}: {{ $value }}
    {{- end }}

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
data:
  favorite: |-
    drink: coffee
    food: pizza

3. template 선언 및 활용

부분적으로 사용자 정의 template 을 만들어서 활용 할 수 있다.

define - tempate 사용

template 은 define 으로 선언하고 tempate 으로 활용할 수 있다.

### configmap.yaml
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" }}
data:
  myvalue: "Hello World"

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
  labels:
    generator: helm
    date: 2023-10-31
data:
  myvalue: "Hello World"

위의 예제는 labels 에 date: 날짜 를 추가로 넣는 부분을 named template 을 만들어서 사용한 예제이다.

chat 를 만들 때 configmap.yaml 과 같이 쿠버네티스 리소스들은 template 디렉토리 아래에 위치 시킨다고 했다. 이 디렉토리에 위치한 yaml 파일들은 자동으로 렌더링에 포함되는데 _ 로 시작하는 파일은 렌더링에서 제외한다. 그래서 보통 define 으로 정의한 함수들은 _helper.tpl 파일을 만들어서 이곳에 위치 시킨다.

define 으로 정의된 named template (함수) 은 template 으로 호출되기 전까지는 렌더링 되지 않는다. 이제 이 함수를 _helper.tpl 파일로 옮겨서 렌더링 결과를 살펴보자.

# _helper.tpl
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" }}
data:
  myvalue: "Hello World"

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
  labels:
    generator: helm
    date: 2023-10-31
data:
  myvalue: "Hello World"

위의 예제에서 define 함수 내에서 .Values 와 같이 value 를 가져오는 것은 하지 않았다. 아래와 같이 {{ .Chart.Name }} 을 사용한다면 위의 방식으로는 값을 표현할 수 없다.

# _helper.tpl
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
    chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}
{{- end }} 

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
  labels:
    generator: helm
    date: 2023-10-31
    chart:
    version:
data:
  myvalue: "Hello World"

이는 template 으로 호출할 때 뒤에 value 를 보내지 않아서 발생한 부분이다. 즉 {{- template "mychart.labels" . }} 과 같이 마지막에 현재의 scope value 인 . 을 넘겨 주어야 제대로 된 값이 출력된다.

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" . }}
data:
  myvalue: "Hello World"

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
  labels:
    generator: helm
    date: 2023-10-31
    chart: flow-control
    version: 0.1.0

define - include 사용

template 은 있는 그대로 output 을 보여주기 때문에 들여쓰기의 문제가 있을 수 있다.

# _helper.tlp
{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{ template "mychart.app" . }}
data:
  myvalue: "Hello World"
{{ template "mychart.app" . }}

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
  labels:
    app_name: flow-control
app_version: "0.1.0"
data:
  myvalue: "Hello World"
app_name: flow-control
app_version: "0.1.0"

app_name 과 app_version 이 출력된 것을 보면 define 에 정의된 들여쓰기 대로 그대로 출력되어 원하는 대로 출력되지 않는다.

includenindent 를 사용하면 원하는 들여쓰기가 가능하다.

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{ template "mychart.app" . }}
data:
  myvalue: "Hello World"
{{ template "mychart.app" . }}

# Source: flow-control/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: release-name-configmap
  labels:
    app_name: flow-control
    app_version: "0.1.0"
data:
  myvalue: "Hello World"
  app_name: flow-control
  app_version: "0.1.0"

끝으로 helm install 할 때 들여쓰기가 잘못되면 렌더링 오류가 나서 최종 결과를 볼 수 가 없다. 이를 해결할 수 있는 옵션이 -disable-openapi-validation 이다.

$ helm install --dry-run --disable-openapi-validation mychart ./
반응형
Posted by seungkyua@gmail.com
,
반응형

쿠버네티스에 서비스를 배포하기 위해 사용하는 대표적인 방법중에 하나가 바로 Helm chart 이다. 한마디로 말해서 Helm chart 는 쿠버네티스 용도의 패키징된 s/w 라 할 수 있다. Helm chart 문법은 go template 을 활용하였기 때문에 go template 을 안다면 조금 더 쉽게 이해할 수 있다.

여기서 설명하는 내용은 사전에 쿠버네티스 클러스터가 있으며 kubectl 로 api 에 접근할 수 있는 환경이 있다는 것을 가정한다.

그럼 도커 이미지를 가지고 쿠버네티스에 Helm 으로 배포할 수 있는 Helm chart 를 만들어 보자.

먼저 Helm (v3)을 설치한다.

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh

$ helm version
version.BuildInfo{Version:"v3.4.0", GitCommit:"7090a89efc8a18f3d8178bf47d2462450349a004", GitTreeState:"clean", GoVersion:"go1.14.10"}

Helm 버전은 틀릴 수 있는데 v.3.0.0 이상이면 상관없다.

 

1. 기본 코드 생성하기

아무 디렉토리로 이동해서 아래의 명령어로 기본 코드를 생성한다. (여기서 예제는 gRpc 기반의 tks-contract 이라는 프로그램을 기반으로 했다)

$ helm create tks-contract

helm create 명령어는 tks-contract 디렉토리를 만들고 아래와 같은 구조로 디렉토리와 샘플 코드를 자동으로 만들어 준다. 그 구조는 다음과 같다.

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

3 directories, 10 files

Chart.yaml 은 chart 에 대한 기본적인 정보가 있는 파일이다. chart 명, chart 버전, chart 설명 등을 적을 수 있으며, 지금은 별로 바꿀 내용이 없다.

charts 디렉토리는 의존성을 관리한다. 예를 들면 DB를 이용하는 웹 애플리케이션 chart 를 만들 때 DB 가 설치되어야 한다면 여기에 chart 를 넣거다 chart repo 에 존재하는 chart 의 링크를 기술 할 수 있다. 이 것도 지금은 바꿀 내용이 없다.

templates 디렉토리는 가장 중요한 디렉토리다. 이 디렉토리 안에는 쿠버네티스 리소스 yaml 파일들이 (예를 들면, Deployment, Service 와 같은 리소스 정의 파일) 위치한다.

values.yaml 파일은 한마디로 변수들을 정의한 파일이다. templates 디렉토리 안에 있는 yaml 파일들의 특정 변수 값을 치환하고 싶을 때 값을 선언해 놓는 곳이다. 일반적으로 key: value 형태로 기술한다.

 

2. Template 작성 방법

먼저 간단한 templates 디렉토리 안의 service.yaml 을 변경해 보자.

apiVersion: v1
kind: Service
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.args.port }}
      protocol: TCP
  selector:
    {{- include "tks-contract.selectorLabels" . | nindent 4 }}

go 템플릿을 사용하기 때문에 {{ }} 기호로 값을 치환한다는 것을 알 수 있다. 일반적으로 values.yaml 파일에 정의된 값을 치환할 수 있게 적혀있다. 가령 {{ .Values.namespace }} 는 values.yaml 파일의 namespace 를 키로 하는 값을 가져와서 치환하라는 의미이다. 실제 values.yaml 에는 아래와 같이 되어 있다.

namespace: tks
service:
  type: LoadBalancer
  port: 9110

values.yaml 파일의 service 아래의 type 을 키로 하는 값, 즉 LoadBalancer 라는 값을 가져오기 위해서는 {{ .Values.service.type }} 으로 적어주면 된다.

마지막 라인의 {{- include "tks-contract.selectorLabels" . | nindent 4 }} 와 같이 include 는 정의된 템플릿 호출을 의미한다. 즉 tks-contract.selectorLabels 라는 정의된 템플릿을 호출했다고 할 수 있다. 템플릿 정의들은 보통 templates 디렉토리 아래 _helpers.tpl 파일에 정의하며 define 으로 선언되어 있다. _helpers.tpl 파일의 일부를 살펴보자.

{{/*
Selector labels
*/}}
{{- define "tks-contract.selectorLabels" -}}
app.kubernetes.io/service: tks
app.kubernetes.io/name: {{ include "tks-contract.name" . }}
{{- end }}

{{/* */}} 은 주석처리이다. 그 아래에 {{- define "tks-contract.selectorLabels" -}} ... {{- end }} 은 템플릿 정의이다.

{{- include "tks-contract.selectorLabels" . | nindent 4 }} 에서 {{- 는 맨앞에 쓰였을 때 {{ }} 코드가 차지하는 영역의 줄바꿈과 공백을 없애라는 뜻이며 . 은 values.yaml 에 있는 모든 변수들을 아규먼트로 넘긴다는 뜻이다. 마지막으로 | 는 shell 에서의 파이프라인과 동일하고 nindent 4 는 결과를 프린트 할 때 공백 4개를 멀티라인으로 계속 들여쓰라는 의미이다.

정리해 보면 service.yaml 과 _helper.tpl 파일을 이 활용되어 아래와 같이 작동된다는 것을 알 수 있다.

## service.yaml
  selector:
    {{- include "tks-contract.selectorLabels" . | nindent 4 }}

+

## _helpers.tpl
{{- define "tks-contract.selectorLabels" -}}
app.kubernetes.io/service: tks
app.kubernetes.io/name: {{ include "tks-contract.name" . }}
{{- end }}


================ 결과 ===============
  selector:
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract

app.kubernetes.io/service: tks 와 app.kubernetes.io/name: tks-contract 가 들여쓰기 4 만큼 프린트 됐다. 물론 _helpers.tpl 내의 템플릿 정의를 찾아보면 {{ include "tks-contract.name" . }} 의 결과 값은 tks-contract 이다.

그리고 NOTES.txt 에 적힌 내용은 chart 가 배포되고 나서 터미널에 프린트된다.

 

3. 전체 소스 작성

코드를 원하는 값으로 수정하고 배포될 때의 최종 yaml 이 어떻게 될지 확인해보자.

먼저 Chart.yaml 이다.

$ vi tks-contract/Chart.yaml
apiVersion: v2
name: tks-contract
description: A Helm chart for tks-contract
type: application
version: 0.1.0
appVersion: 0.1.0

_helpers.tpl 은 selectorLabels 를 원하는 값을 넣기 위해서 49 라인만 수정하였다.

$ vi tks-contract/_helpers.tpl
...
{{/*
Selector labels
*/}}
{{- define "tks-contract.selectorLabels" -}}
app.kubernetes.io/service: tks
app.kubernetes.io/name: {{ include "tks-contract.name" . }}
{{- end }}
...

deployment.yaml 은 container 의 args 값을 추가했다.

$ vi tks-contract/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "tks-contract.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "tks-contract.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "tks-contract.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: tks-contract
              containerPort: {{ .Values.args.port }}
              protocol: TCP
          command:
            - /app/server
          args: [
            "-port", "{{ .Values.args.port }}",
            "-dbhost", "{{ .Values.args.dbUrl }}",
            "-dbport", "{{ .Values.args.dbPort }}",
            "-dbuser", "{{ .Values.args.dbUser }}",
            "-dbpassword", "{{ .Values.args.dbPassword }}",
            "-info-address", "{{ .Values.args.tksInfoAddress }}",
            "-info-port", "{{ .Values.args.tksInfoPort }}"
          ]
          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 }}

조건절은 {{- if }} 혹은 {{- if not }} {{ end }} 로 사용할 수 있고 {{- with 키 }} {{ end }} 는 with 절 안에서는 구조상 해당 키 아래의 값들을 사용하겠다는 의미이다. {{- toYaml 키 }} 는 키의 값을 그대로 Yaml 로 프린트 해 준다.

nodeSelector 를 예를 들어 보자.

## deployment.yaml
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}

+

## values.yaml
nodeSelector:
  taco-tks: enabled


============ 결과 =================
      nodeSelector:
        taco-tks: enabled

values.yaml 에 nodeSelector 키 아래에 taco-tks: enabled 라는 값을 그대로 toYaml 로 프린트 한다.

service.yaml 전체는 다음과 같다.

$ vi tks-contract/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.args.port }}
      protocol: TCP
  selector:
    {{- include "tks-contract.selectorLabels" . | nindent 4 }}

values.yaml 에 전체 값을 넣는다.

$ vi tks-contract/values.yaml

replicaCount: 1

namespace: tks

image:
  repository: docker.io/seungkyu/tks-contract
  pullPolicy: Always
  tag: "latests"

imagePullSecrets: []
nameOverride: "tks-contract"
fullnameOverride: "tks-contract"

serviceAccount:
  create: true
  annotations: {}
  name: "tks-info"

args:
  port: 9110
  dbUrl: postgresql.decapod-db.svc
  dbPort: 5432
  dbUser: tksuser
  dbpassword: tkspassword
  tksInfoAddress: tks-info.tks.svc
  tksInfoPort: 9110

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: LoadBalancer
  port: 9110

ingress:
  enabled: false
  annotations: {}
  hosts:
    - host: chart-example.local
      paths: []
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

nodeSelector:
  taco-tks: enabled

tolerations: []

affinity: {}

마지막으로 배포후 프린트될 내용은 NOTES.txt 을 수정한다.

$ vi tks-contract/templates/NOTES.txt
TKS-Contract
{{- if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace {{ .Values.namespace }} svc -w {{ include "tks-contract.fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Values.namespace }} {{ include "tks-contract.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
  gRPC Call => $SERVICE_IP:{{ .Values.service.port }}
{{- end }}

 

4. 결과 확인

작성된 chart 는 아래 명령어로 최종 yaml 이 어떻게 변환되어 배포되는지 dry-run 으로 확인할 수 있다.

$ helm upgrade -i tks-contract ./tks-contract --dry-run --debug

---
# Source: tks-contract/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tks-info
  labels:
    helm.sh/chart: tks-contract-0.1.0
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
    app.kubernetes.io/version: "0.1.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: tks-contract/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: tks-contract
  namespace: tks
  labels:
    helm.sh/chart: tks-contract-0.1.0
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
    app.kubernetes.io/version: "0.1.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: LoadBalancer
  ports:
    - port: 9110
      targetPort: 9110
      protocol: TCP
  selector:
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
---
# Source: tks-contract/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tks-contract
  namespace: tks
  labels:
    helm.sh/chart: tks-contract-0.1.0
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
    app.kubernetes.io/version: "0.1.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/service: tks
      app.kubernetes.io/name: tks-contract
  template:
    metadata:
      labels:
        app.kubernetes.io/service: tks
        app.kubernetes.io/name: tks-contract
    spec:
      serviceAccountName: tks-info
      securityContext:
        {}
      containers:
        - name: tks-contract
          securityContext:
            {}
          image: "docker.io/seungkyu/tks-contract:latests"
          imagePullPolicy: Always
          ports:
            - name: tks-contract
              containerPort: 9110
              protocol: TCP
          command:
            - /app/server
          args: [
            "-port", "9110",
            "-dbhost", "postgresql.decapod-db.svc",
            "-dbport", "5432",
            "-dbuser", "tksuser",
            "-dbpassword", "",
            "-info-address", "tks-info.tks.svc",
            "-info-port", "9110"
          ]
          resources:
            {}
      nodeSelector:
        taco-tks: enabled

NOTES:
TKS-Contract
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace tks svc -w tks-contract'
  export SERVICE_IP=$(kubectl get svc --namespace tks tks-contract --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
  gRPC Call => $SERVICE_IP:9110
반응형
Posted by seungkyua@gmail.com
,
반응형


2018년 3월 26일 CNC & Kubernetes Meetup 발표 자료


 facebook group : https://www.facebook.com/groups/k8skr/


• 일시: 2018년 3월 26일(월) 오후 7시 30분 - 9시 30분

• 장소: 역삼역 7번 출구 GS타워 12층 AWS코리아 (강남구 논현로 508)

• 세션 내용:


19:30-20:10 Kubernetes 를 이용한 Cloud native platform 개발 (어형부형)



20:10-20:50 Helm chart 를 활용한 App 배포 방법 (안승규)








반응형
Posted by seungkyua@gmail.com
,
반응형

OpenStack 발표자료 (From Kubernetes to OpenStack)





반응형
Posted by seungkyua@gmail.com
,
반응형

OpenStack Day Korea 2017 에서 발표한 자료




반응형
Posted by seungkyua@gmail.com
,