티스토리 뷰

Cloud/Kubernetes

Kustomize

jacobbaek Jacob_baek 2021. 4. 19. 22:32

기존에 helm chart를 통해 Application 배포를 수행했었다. helm을 사용하면서 아쉬웠던 부분은 helm value로 지정되어 있지 않거나 특정 resource를 추가하기 위해서는 helm chart 자체의 개선이 필요했다. 이로 인해 시간이 좀더 소요되었었고 이슈가 있을 경우 helm chart의 구조를 모르는 경우는 분석에 또 시간이 소요되었다.

이러한 이슈를 다르게 접근한 Kustomize라는 도구를 알게 되었고 이 단점들을 어느정도 보완해줄수 있는 도구라는 생각이 들었다.

Kustomize 란

Kustomize는 사실 2017년부터 만들어졌고 소개되어왔다. 현재는 kubectl에 1.14부터 포함되어 사용이 가능하다.

Kustomize는 선언적(declarative)를 지향한다. Kubernetes 자체의 컨셉이 이부분과 연관되어 있기에 yaml구성방식도 이 컨셉을 가져와 적용한것이라 보면 좋을것 같다.

기본적으로 kustomization.yaml 파일을 사용하여 불러올 yaml들을 지정하여 기존에 Application 배포에 사용되던(혹은 새로 만들) yaml 파일들을 그대로 사용할수 있게 해준다.
또한 stage(production, staging, dev 등)별로 별도의 데이터를 기반으로 관리가 가능하다.

NOTE
kustomize를 처음접하는 경우 아래 ppt를 한번 훝어보길 권장한다.
전체적인 kustomize의 사용과 어떤 개념들이 적용되었는지 등을 알수 있기 때문이다.
영어이지만 이해될만한 수준으로 작성되어 있어 처음 참고하기 좋은 자료이다.

특징

  • Bespoke config (stage별(dev,staging,production과 같이) 선언 및 사용이 가능)
  • off the shelf config (기성품처럼 선반에 진열된 제품을 바로 끄집어 내어 사용할수 있는 개념으로 bases에 저장된 기본 yaml파일을 바로 가져와 사용한다는 개념)
  • configmap을 rolling update 할수 있다. (즉, 기존에 새로운 configmap 변경에 따라 deployment가 새로운 configmap을 읽어들이기 된다.)
  • 기존에 사용되던 manifest를 그대로 사용할수 있다.

기본구조 및 사용법

기본구조

다음과 같은 구조를 일반적으로 지님
(아래는 kustomize example에 있는 ldap의 구조임)

.
├── base
│   ├── deployment.yaml
│   ├── env.startup.txt
│   ├── kustomization.yaml
│   └── service.yaml
├── integration_test.sh
├── overlays
│   ├── production
│   │   ├── deployment.yaml
│   │   └── kustomization.yaml
│   └── staging
│       ├── config.env
│       ├── deployment.yaml
│       └── kustomization.yaml
└── README.md
  • base : 기본 yaml file들이 저장되는 경로 (해당폴더에 kustomization.yaml 파일이 필히 존재해야 함)
  • overlays : 하위 디렉토리에서 느낄수 있듯이 stage 별로 나누거나 공용으로 사용되는 base에 특정한 value를 추가해야 하는 경우 사용된다.

사용법

kustomize는 다음 2가지 방식으로 사용할수 있다.

맨마지막 argument로 주어진 base 혹은 overlays/staging 디렉토리에 있는 kustomization.yaml을 기반으로 resource들을 생성한다.

kubectl apply -k base
# kubectl apply -k overlays/staging

kustomize build 명령을 통해 실제 적용될 최종 yaml을 만들어내고 이를 바로 apply -f로 resource 들을 생성한다.

kustomize build . | kubectl apply -f -
# kustomize build overlay/production | kubectl apply -f -
# kustomize build overlay/production | kubectl delete -f -

kustomize
kustomize binary를 kubernetes 환경에서 default로 사용되어지는 kubectl과는 다르게 추가로 다운로드 및 path에 지정된 경로에 넣어주어 사용할수 있다. 해당 kustomize는 앞서 소개한 build 외에도 edit, create, diff 등을 사용할수 있다.
또한 중요한 것이 아직 kubectl에 포함된 kustomize는 하위버전이라 에러가 발생되는경우가 많았다. 하여 맘편히 kustomize 명령을 그대로 사용하는것을 권장한다.

기본 문법

kustomize를 사용하는 목적은 base에 지정된 각종 yaml들을 object처럼 여기저기서 불러와 손쉽게 관리 및 적용하기 위함이라 생각된다.
결국 kustomization.yaml을 어떻게 작성하고 overlays내에 그에 맞는 yaml파일을 만들어놓느냐의 문제라 생각된다.
하여 kustomization.yaml내에 어떤 syntax를 사용하여 custom 한 yaml을 만들어낼수 있는지 알아보도록 하겠다.

1. namePrefix / nameSuffix

namePrefix : production- 과 같이 시작
nameSuffix : -001 과 같이
name 항목에 지정된 prefix/suffix를 더해서 resource를 생성한다.

2. commonLables

생성될 resource내의 metadata 항목에 지정한 labels을 추가한다.
아래와 같이 kustomization.yaml에 추가가능하며 kustomization에 선언된 모든 자원에 아래 label이 추가된다.

commonLabels:
  app: nginx

3. commonAnnotations

생성될 resource내의 metadata 항목에 지정한 annotations을 추가한다.
commonLables와 같이 kustomization.yaml에 선언된 모든 자원에 Annotation이 추가된다.

4. configMapGenerator

지정한 파일내에 선언되어 있는 key=value을 configmap으로 생성한다.

namespace: monitoring

configMapGenerator:
- name: grafana-configmap-ldap
  files:
  - ldap.toml
  • files : 실제 파일 리스트
  • literal(files 대신) 사용 가능 : 이는 file로 불러오지 않고 지정된 data를 그대로 configmap을 생성한다.

configMapGenerator로 생성된 configmap은 특정 hash value를 suffix로 붙게 된다.
즉 test-configmap 이라는 name을 지정하게되면 test-configmap-xxxxxx 와 같은 suffix가 붙여져 적용된다.
이는 deployment를 configmap 변경에 따라 업데이트 하기 위함이다.

참고로 이점은 configmap을 사용함에 있어 kustomize의 큰 장점이라 생각한다.

여기서 궁금증이 생겼는데 configMapGenerator로 생성하면 앞서 이야기한데로 suffix가 붙게 되어 deployment에서는 suffix가 붙은 configmap을 volume으로 만들고 mount해야 하는데 이 과정이 제대로 동작안할 거라 생각했다. 실제로 예상한대로 동작이 안되었었고 이유는 namespace를 지정하지 않아 default namespace에 생성되어서 volume 생성과정이 실패한것이었고 동일 namespace에 생성한후 는 이상없이 동작되었다.
(확인해보고자 한다면 kustomize build 를 실행해보면 생성할 yaml을 만들어 출력해주는데 여기서 configmapgenerator로 생성하는 yaml을 확인해보면 된다. 실제 suffix 붙은 configmap을 deployment 및 configmap에서 그대로 출력하는것을 볼수 있다.)

또한 deployment 생성시 아래와 같은 rollingupdate에 대한 전략이 추가되어야 deployment가 configmap에 의해 update 되면서 기존 pod는 삭제하게 된다.

  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate

만약 이런 suffix가 붙는게 싫다면 아래 옵션을 추가하면 지정된 name 그대로 생성된다.
다만 이렇게 되면 위에 장점으로 언급된 deployment를 configmap 변경에 따라 업데이트는 안하게 되므로
이는 전략적으로 사용이 필요하다.

namespace: monitoring

configMapGenerator:
- name: grafana-configmap-ldap
  files:
  - ldap.toml

generatorOptions:
  disableNameSuffixHash: true

5. secretGenerator

지정한 파일내에 선언되어 있는 value를 secret으로 생성한다.

configMap과 동일하게 변경후 kustomize 를 통한 build, kubectl을 통한 apply가 이루어지게 되면 해당 하는 pod도 자동으로 terminate되고 다시 동작되게 된다.

secretGenerator:
- name: com-jacobbaek-secret
  files:
    - tls.crt=secret/tls.crt
    - tls.key=secret/tls.key
  type: "kubernetes.io/tls"

참고
envs 는 파일 내에 Key/Value 구조의 content를 그대로 K/V로 data 내에 base64로 encoding되어 생성한다.
files는 파일 이름이 Key가 되고 으로 value로 content가 base64로 encoding되어 생성된다.

6. patches

patches는 특정 항목 patch를 위해 json 혹은 yaml 파일로 선언되어 있는 파일과 그에 맞는 target을 지정해주어 patch가 이루어지도록 한다.
path는 앞서 이야기한데로 동일한 디렉토리내에 json,yaml과 같은 방식으로 구성가능하며 base에 선언되어 있는 kind, apiversion, name 등을 기입해야 한다.

patches:
- path: patch_deploy.json
  target:
    group: apps
    version: v1
    kind: Deployment
    name: jenkins-server
- path: patch_ingress.yaml
  target:
    group: networking.k8s.io
    version: v1
    kind: Ingress
    name: jenkins-ingress

앞서 정의한 json file은 다음과 같은 형태를 띄게 된다.

[
    {
        "op": "replace",
        "path": "/spec/template/spec/containers/0/env/0",
        "value": {
            "name": "JENKINS_ADMIN_ID",
            "value": "admin"
        }
    },
    {
        "op": "replace",
        "path": "/spec/template/spec/containers/0/env/1",
        "value": {
            "name": "JENKINS_ADMIN_PASSWORD",
            "value": "password"
        }
    },

yaml의 예제이다.

- op: replace
  path: "/metadata/annotations"
  value: |
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.org/redirect-to-https: true
    nginx.org/location-snippets: |
      add_header X-Forwarded-Proto https;

참고페이지

6. patchesStrategicMerge

변경할 yaml파일을 별도의 문법사용없이 기존 kubernetes application 배포시 사용되는 yaml파일과 동일한 방식으로 작성된 yaml 파일의 리스트를 지정하여 병합되도록 한다. (즉, 특정 항목만 patch 하기 위해서는 patchesJson6902를 사용할것)

7. patchesJson6902

앞선 patches와 동일한 방식으로 동작된다.
(아직 차이점을 찾지는 못했다. 찾는데로 업데이트 예정.)

patchesJson6902:
- targe:
  group: apps
  version: v1
  kind: Deployment
  name: nginx
  # path: patch.yaml <==== patch.yaml 파일을 아내내용을 기반으로 사전에 만들어 적용되도록함
patch: |-
- op: add
  path: /spec/

patchesJson6902는 kustomization.yaml내에 특정 경로와

만약 generator를 이용했을 경우 이에 대한 patch는 다음과 같이 수행할수 있다.

참고로 새로운 기존 base에 정의되어 있지 않은 spec 내 item을 신규로 추가해야할 경우 아래와 같은 에러를 만날수 있다.

error: error validating "STDIN": error validating data: ValidationError(Deployment.spec.template.spec.containers[0].envFrom): invalid type for io.k8s.api.core.v1.Container.envFrom: got "map", expected "array"; if you choose to ignore these errors, turn validation off with --validate=false

이와 같은 경우 []를 잘 지정해주어야 에러가 발생되지 않는다. 즉, 위 예제의 envFrom에 op: add 하는 경우 value내 데이터들을 []로 감싸주어야 한다.
json의 경우 []로 value를 감싸주어야 하며 yaml의 경우 각 항목의 시작을 - 로 시작하면 array형태로 받아들인다.

그외 examples

hostAliaes 추가

- op: add
  path: /spec/template/spec/hostAliases
  value:
    - ip: "192.168.100.100"
      hostnames:
      - "gitlab.jacobbaek.com"

image만 변경할수 있는 newimage

images:
- name: busybox    # <==== base에 선언된 image name
  newName: alpine  # <==== 신규 image name
  newTag: 3.6      # <==== 신규 tag information

참고
namespace에 대한 kustomize yaml file을 사용하지 않는것을 권장한다.
만약 지정된 namespace에 다른 kustomize에 의해 관리되지 않는 resource가 존재할 경우
삭제시 namespace에 관리되지 않는 resource들까지 모두 삭제될수 있기에 별도로 생성하여 관리하는것을 추천한다.

Kustomize 사용해보기

생성 및 수정

[root@deploy gitlab]# kustomize build overlays/production/ | kubectl apply -f -
namespace/gitlab unchanged
service/gitlab-service unchanged
persistentvolumeclaim/gitlab-config-pvc unchanged
persistentvolumeclaim/gitlab-data-pvc unchanged
persistentvolumeclaim/gitlab-logs-pvc unchanged
deployment.apps/gitlab unchanged
ingress.networking.k8s.io/gitlab-ingress unchanged

만약 특정 자원만 수정했다면 기존 자원들은 unchanged로 남고 수정된 자원만 변경된다.

삭제

[root@deploy gitlab]# kustomize build overlays/production/ | kubectl delete -f -
namespace/gitlab deleted
service/gitlab-service deleted
persistentvolumeclaim/gitlab-config-pvc deleted
persistentvolumeclaim/gitlab-data-pvc deleted
persistentvolumeclaim/gitlab-logs-pvc deleted
deployment.apps/gitlab deleted
ingress.networking.k8s.io/gitlab-ingress deleted

NOTE
kustomize best practices가 소개되어 있는 사이트로 참고할 내용이 있다.

helm chart를 kustomize 로

아래와 같은 명령으로 helm chart를 kustomize 실행이 가능한 버전으로 변경할수 있다.
(물론 100% kustomize에 최적화 된 환경은 아니다.)

# helm fetch -untar bitnami/jenkins
# mkdir base
# helm template jenkins/ > base/install.yaml
# cat << EOF >> base/kustomization.yaml
resources:
- install.yaml
EOF
# kustomize build base | kubectl apply -f -

혹은 아래와 같이 resource 별로 manifest를 생성할 수 있다.
kustomization은 수작업이 필요하다.

jacob@dubaekPC:~/workspace/prometheus$ helm template prometheus-community/prometheus --output-dir base
wrote base/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml
wrote base/prometheus/templates/alertmanager/serviceaccount.yaml
wrote base/prometheus/templates/node-exporter/serviceaccount.yaml
wrote base/prometheus/templates/pushgateway/serviceaccount.yaml
wrote base/prometheus/templates/server/serviceaccount.yaml
wrote base/prometheus/templates/alertmanager/cm.yaml
wrote base/prometheus/templates/server/cm.yaml
wrote base/prometheus/templates/alertmanager/pvc.yaml
wrote base/prometheus/templates/server/pvc.yaml
wrote base/prometheus/charts/kube-state-metrics/templates/role.yaml
wrote base/prometheus/templates/alertmanager/clusterrole.yaml
wrote base/prometheus/templates/pushgateway/clusterrole.yaml
wrote base/prometheus/templates/server/clusterrole.yaml
wrote base/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml
wrote base/prometheus/templates/alertmanager/clusterrolebinding.yaml
wrote base/prometheus/templates/pushgateway/clusterrolebinding.yaml
wrote base/prometheus/templates/server/clusterrolebinding.yaml
wrote base/prometheus/charts/kube-state-metrics/templates/service.yaml
wrote base/prometheus/templates/alertmanager/service.yaml
wrote base/prometheus/templates/node-exporter/svc.yaml
wrote base/prometheus/templates/pushgateway/service.yaml
wrote base/prometheus/templates/server/service.yaml
wrote base/prometheus/templates/node-exporter/daemonset.yaml
wrote base/prometheus/charts/kube-state-metrics/templates/deployment.yaml
wrote base/prometheus/templates/alertmanager/deploy.yaml
wrote base/prometheus/templates/pushgateway/deploy.yaml
wrote base/prometheus/templates/server/deploy.yaml

Pros and Cons

Issue

json: cannot unmarshal object

kubectl 을 통한 apply 시 아래와 같은 json unmarshal을 할수 없다는 error가 나오는 경우가 있다.

jacob@jacob-laptop:~/workspaces/kustomize/openldap$ kubectl apply -k overlays/sample/
error: json: cannot unmarshal object into Go struct field Kustomization.patchesStrategicMerge of type patch.StrategicMerge

이상하게도 kustomize 명령을 통해 적용시 별문제 없이 반영이 되어진다.

jacob@jacob-laptop:~/workspaces/kustomize/openldap$ kustomize build overlays/sample/ | kubectl apply -f -
namespace/mgmt created
secret/openldap-pass-hdtd7f9g79 created
service/openldap created

아래 보고된 이슈로 보이며 이유는 모르겠지만 아직까지 해결이 된거로 보이지는 않는다.

참고로 해당 이슈가 발생했던 당시의 버전은 다음과 같다.

jacob@jacob-laptop:~/workspaces/kustomize/openldap$ kustomize version
{Version:kustomize/v4.0.0 GitCommit:a414f75f1b6bd01f888bb99360c69a4221116bf8 BuildDate:2021-02-12T22:53:04Z GoOs:linux GoArch:amd64}
jacob@jacob-laptop:~/workspaces/kustomize/openldap$ kubectl version
Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.4", GitCommit:"e87da0bd6e03ec3fea7933c4b5263d151aafd07c", GitTreeState:"clean", BuildDate:"2021-02-20T02:22:41Z", GoVersion:"go1.15.8", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7+k3s1", GitCommit:"5a00e38db4c198fb0725a6b709380aed8053d637", GitTreeState:"clean", BuildDate:"2021-01-14T23:09:21Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}

kustomize로 삭제

kustomize build 로 삭제를 하려 하며 생성을 기준으로 만들어놓은 kustomize로 인해 삭제 과정중 stuck 되는 현상이 발생될수 있다.
이는 삭제 순서의 문제로 순서를 반대로 하면 정상 삭제가 된다. 이를 kustomize에서는 --reorder라는 옵션을 통해 지원하고 있다.
아래 예제를 참고하여 명령을 수행하여 삭제할수 있다.

kustomize build --reorder legacy base/ | kubectl delete -f -

참고사이트

'Cloud > Kubernetes' 카테고리의 다른 글

ArgoCD backup cronjob  (0) 2021.06.28
Custom domain lookup on Kubernetes  (0) 2021.05.07
Kustomize  (0) 2021.04.19
initcontainer with multicommand  (0) 2021.04.13
Velero  (0) 2021.04.06
ingress with subpath  (0) 2021.03.23
댓글
댓글쓰기 폼