예전에 선언형과 명령형에 대한 이야기를 한 적이 있다. Kustomize는 선언형으로 쿠버네티스 오브젝트를 사용자가 원하는 대로 변경할 수 있게 도와주는 도구이다. 쿠버네티스의 버전 1.14 이후로, kubetctl에서 kustomize 명령어를 지원하게 되었다. 가장 큰 특징은 overlay 구조로 기본이 되는 구조에 추가적인 오브젝트 설정들을 오버라이딩하여, 다양한 스테이지에 배포할 수 있다는 점이다. base 디렉토리에 기본이 되는 쿠버네티스 리소스를 정의하고, overley를 통해서 환경별(dev, prod, alpha 등) 차이점을 정의하여 여러 가지 환경에 맞게 사용할 수 있게 도와주는 역할을 한다.
이러한 Kustomize가 없다면, 직접 하나하나씩 kubectl apply -f 명령어를 통해서 선언해야할까? 사실은 Helm에서 지원하는 helm chart 기능을 통해서 선언하는 방식으로 쿠버네티스 오브젝트를 배포할 수 있었다. 하지만 Helm 버전이 v3가 된 지금은 나름 괜찮았지만, 그 당시(Helm v2)는 굉장히 쓰기 어려웠기 때문에 많은 사람들이 Kustomize를 즐겨 썼고, 나중에는 쿠버네티스에 공식적으로 통합되었다.
이번 포스팅에서는 Kustomize에 대해서 간단하게 다루려고 한다.
본론
Kustomize의 등장
Kustomize는 말 그대로 커스터마이즈의 앞 글자를 C가 아니라 K로 바꾼 것이다. 쿠버네티스 오브젝트를 원하는 대로 커스터마이징 하는 것을 도와주는 독립형 구조라고 한다. 공식 홈페이지에 쓰여있는 독립형 구조라는 키워드는, Kustomize가 다른 쿠버네티스 매니페스트를 전혀 건드리지 않고, Kustomization.yaml에 쓰인 리소스만 실행하기 때문에 붙는 것으로 보인다. 이러한 툴이 등장하게 된 계기는 다음과 같다. 쿠버네티스를 통해서 다양한 환경을 구성하다 보면, 여러 스테이지에 서버를 배포해야 하는 일이 생기게 된다. 조직의 규모와 제품의 특성에 따라 이러한 배포 환경을 어떻게 구성하는지는 다르지만 단순화하면 Dev -> Staging -> Prod 정도로 구현할 수 있다.
앞서 살짝 설명하였지만, 좀 더 자세히 설명하면 쿠버네티스에서는 일반적으로 선언형을 쓰는 것을 권장한다. 그러한 과정을 도와주는 것이 Helm Chart 였지만 쓰기가 불편했다. 그러한 시대에 혜성같이 등장한 것이 Kustomize로 간편하게 선언형의 방식으로 쿠버네티스 오브젝트들을 배포할 수 있었다.
이러한 Kustomize에 대한 설명을 하기 전에 앞서 특정 서비스를 배포해야하는 시나리오를 가정하고 Kustomize를 통해서 해결하는 과정을 예로 들어 설명해보려고 한다.
Branch Convention
github 혹은 gitlab과 같은 서비스를 통해서 .git을 기반으로 많은 사용자들이 작업하는 경우 브랜치에 따라서 배포 시나리오를 다르게 가져갈 수 있다. 아래는 구글링을 통해서 branch convention을 검색한 결과이다. 조직별로 다양하게 깃허브 브랜치를 관리하는 규칙이 정해져 있을 텐데, Kustomize를 도입하기 전에 아래와 같은 브랜치 컨밴션을 가지고 있는 조직에서 배포 시나리오를 구성해야 한다고 생각해 보자.
위와 같은 구조를 보면 버전과 함께 사용자에게 배포되는 브랜치는 Master인 것 같고, 일반적으로 개발 환경의 브랜치는 Develop 브랜치인 것으로 확인할 수 있다. 또한 본격적으로 Master를 통해서 배포되기 전에 테스트를 진행하는 브랜치는 Release로 보인다. 데브옵스 엔지니어는 다양한 환경 브랜치의 CI/CD 파이프라인을 구축함과 동시에 각 스테이지별로 그에 맞는 설정을 추가적으로 해줘야 한다. 예를 들어 URL을 다르게 한다거나 연결하는 데이터베이스를 다르게 한다거나, Prod 환경과는 다르게 Dev 환경에서는 적은 컴퓨팅 리소스만 사용하게 하는 등 다양한 방법을 통해서 리소스를 관리하게 된다.
이제 이러한 브랜치들을 배포한다면 어떻게 할까? 일반적으로는 세 가지 브랜치 전부 배포를 할 것이다. 예시를 위해서 여기서는 두 개만 배포하는 시나리오를 가정하자. master는 Production 환경, Release는 Staging 환경이라고 지정하도록 하겠다.
Production는 말할 것도 없고, 본격적으로 서비스가 사용자에게 배포되기 전에 개발 환경에서 정말로 이 서비스가 안전한 지에 대한 검증이 필요하다. 이러한 환경을 스테이징(Staging) 환경이라고 하는데, 프로덕션과 가장 유사한 환경을 구축함으로써 성능 테스트, 부하 테스트 등을 수행한다. 프로덕션과 스테이징의 차이점은 약간의 configuration만 있을 것이다. 이러한 약간의 차이점을 어떻게 쿠버네티스에서는 선언형으로 관리할 수 있을까?
Helm Chart
Helm은 먼저 쿠버네티스에서 사용하는 패키지 매니저와 비슷한 역할로, 다양한 플러그인을 손쉽게 설치할 수 있다. apt나 pip와 같은 패키지 매니저가 간단하게 패키지를 설치해 줄 수 있듯이, Helm은 다양한 플러그인의 Repo를 등록하는 방식을 사용한다. 이렇게 사전에 어느 정도 구성된 프리셋을 가져와서 손쉽게 애플리케이션을 배포하는 데 도움을 준다.
이러한 Helm에서 Chart라는 기능을 통해서 다양한 오브젝트를 yaml 파일로써 배포할 수 있게 도와주는 기능이 있었다. 하지만 Kustomize가 kubectl에 통합되기 이전에는 Helm이 v2 버전이었는데, 이 당시에는 멀티 스테이지 배포가 굉장히 힘들었다. Tiller라는 서버 컴포넌트가 클러스터 내에 필요하였는데 이러한 Tiller를 다루는 것도 힘들었고, Helm만의 독특한 개념들 ( Chart, Release, Revision )등을 알아야 하는 등의 높은 러닝 커브로 인하여 Helm Chart는 다루기 힘들었다.
그 당시에 혁명적으로 나왔던 것이 바로 Kustomize이다. 기본적으로 정의된 베이스에 무언가를 오버라이딩해서 배포하는 것은 비단 kustomize 뿐만 아니라, 일반적은 프로그래밍 기법이다. 이러한 방법을 적용하여 base와 overlay로 디렉토리를 나누어 상속받아서 쓸 수 있게 하였으며, 독립형 구조로써 Kustomization.yaml 에 담긴 내용을 Kustomization.yaml에 상속시켜 주기 때문에 다른 쿠버네티스 매니페스트들에 의존성이 하나도 없다.
그렇다면 이제 어떻게 kustomize가 멀티 스테이지를 구성하는지 알아보자.
Kustomize가 멀티 스테이지를 구성하는 방법
Kustomize의 공식 홈페이지는 아래와 같다. kustomize의 철학이나 원리 등 다양한 정보들을 확인할 수 있다.
- [kustomize.io] main : https://kustomize.io/
Kustomize - Kubernetes native configuration management
Overview Kustomize traverses a Kubernetes manifest to add, remove or update configuration options without forking. It is available both as a standalone binary and as a native feature of kubectl. Purely declarative approach to configuration customization Na
kustomize.io
Kustomize가 내세우는 가장 큰 특징은 위와 같이 사이트에서 확인할 수 있다. 예전에는 서드파티 플러그인이었다가 kubectl에 통합되게 되었으며 이러한 문서도 당연히 쿠버네티스 홈페이지에 실려있다. 쿠버네티스 공식 사이트에서 kustomize에 대해서 예시를 드는 부분은 바로 다음과 같은 디렉토리 구조이다. base와 overlay를 두어, 기본적인 서버를 구성하는 요소들은 전부 base에 두고, stage를 달리하는 요소는 overlay 안에 스테이지에 따라(Prod, Dev) 둔다. 그리고 base에 있는 kustomize.yaml 파일을 overlay에 있는 각 스테이지마다 있는 kustomization.yaml에 등록하여 이러한 base 이미지를 상속받에서 쓸 수 있게 만든다. 위에서 이야기하는 Manage an arbitrary number of distinctly customized Kubernetes configurations가 kustomize를 설명하는 핵심 문장인 것 같다. 공식 홈페이지에 있는 그림을 보면 아래와 같다.
이렇게 하나의 base를 두고, 여러 가지의 patch를 만들어둔다. 그리고 왼쪽의 그림처럼 Kustomization.yaml에 이러한 patch들을 등록해 주면 손쉽게 설정이 완료된다. 이러한 과정을 직접 구현해 보며 이해해 보자.
구현하려고 하는 시나리오를 가정해 보자. 서버를 간단하게 확인할 수 있게 nginx라는 이미지를 deployment로 배포할 예정이고, 스테이징 서버로 접근하기 위해서는 앞에 서브도메인을 stage로 붙이고, 그 외에 prod인 경우에는 앞에 prod를 붙인다고 가정을 해보자. 추가적으로 스테이지별로 다른 configmap을 사용해야 한다고 가정하자.
대충 위와 같은 시나리오를 구현하기 위해서는 이러한 구조가 필요하다. 또한 base에 있는 kustomization.yaml의 내용은 아래와 같다.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
그리고 이러한 base에 있는 설정을 받는 overlay의 kustomization.yaml은 아래와 같다.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- configmap.yaml
- ingress.server.yaml
이렇게 base의 kustomization을 상속받아 또 새롭게 선언하는 방식인데, 그림으로 아래와 같이 간단하게 나타낼 수 있다.
이렇게 상속관계를 통해서 다양한 스테이지를 손쉽게 추가할 수 있으며, prod나 stage 서버뿐만 아니라 dev 서버가 필요한 경우 간단하게 또 추가할 수 있다. stage 박스 옆에 새롭게 하나 더 만들 수 있으며, 몇 가지 설정 관련 파일만 추가해 주면 된다.
이렇게 만들어진 kustomization.yaml 파일을 실행하는 방법은 명령어는 아래와 같다. 마지막에 파라미터로는 kustomization.yaml 파일이 있는 디렉토리를 지정해 주면 된다.
kubectl apply -k .
선언형으로 진행되기 때문에 바뀐 내용이 없다면 위와 같이 unchanged로 찍히게 된다. 그리고 kustomize로 선언된 오브젝트들의 확인은 다음 명령어를 통해서 진행할 수 있다. 해당 명령어는 kustomize를 통해서 선언된 모든 오브젝트의 목록을 확인할 수 있기 때문에 결과는 다음과 같이 나온다.
kubectl kustomize .
apiVersion: v1
data:
key: this-is-prod-overlay
kind: ConfigMap
metadata:
name: nginx-prod
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 80
targetPort: 80
selector:
app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx-deployment
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
memory: 128Mi
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels:
name: nginx
name: nginx-prod-ingress
spec:
rules:
- host: prod.localhost
http:
paths:
- backend:
service:
name: nginx-nginx
port:
number: 80
path: /
pathType: Prefix
이 외에도 다양한 Kustomize에 대한 사용 방법은 쿠버네티스의 공식 문서에도 자세히 쓰여있어 확인해 볼 수 있다.
- [kubernetes] Kustomize를 이용한 쿠버네티스 오브젝트의 선언형 관리 : https://kubernetes.io/ko/docs/tasks/manage-kubernetes-objects/kustomization/
Kustomize를 이용한 쿠버네티스 오브젝트의 선언형 관리
Kustomize는 kustomization 파일을 통해 쿠버네티스 오브젝트를 사용자가 원하는 대로 변경하는(customize) 독립형 도구이다. 1.14 이후로, kubectl도 kustomization 파일을 사용한 쿠버네티스 오브젝트의 관리를
kubernetes.io
앞서, kustomize를 사용하였을 때, 바뀐 내용이 없는 경우에는 unchanged로 떴고, 변경 사항이 있는 경우에만 created로 생성이 되었다. 이렇게 선언형으로 클러스터의 상태와 kustomziation.yaml을 통해서 읽어 들인 파일들의 상태가 다른 경우에 바뀐 부분만 반영하는데, 이러한 선언형의 장점을 통해서 다양한 써드파티 플러그인과 시너지를 낼 수 있다. 이에 대해서 바로 살펴보자.
Kustomize의 활용
Kustomize를 써서 한 번에 오브젝트들을 스테이지별로 선언할 수 있다는 것을 알았다. 이러한 Kustomize는 더 나아가서 어떠한 장점을 제공할까? 생각해 보면 선언형으로 진행하는 것을 편리하게 만들 수 있기 때문에 GitOps를 통한 써드파티들과 엄청난 시너지를 보여준다. 대표적인 것이 ArgoCD와 Flux이다.
GitOps?
GitOps는 DevOps의 실천 방법 중 하나로, Git을 단일 진실 공급원(Single Source of Truth)으로 사용하여 인프라를 관리하는 방식을 말한다. 일반적으로 git을 통해서 작업하는 API 서버와 다르게, 어떤 설정은 AWS에 GUI로 관리되어 있고, 어떤 설정은 직접 호스트 서버에 systemctl로 떠있고, 어떤 설정은 Jenkins로 되어있다거나 파편화되어 있으면, 인수인계하는 과정이나 트러블 슈팅이 굉장히 번거로워진다.
따라서 모든 인프라 관련된 코드를 하나의 깃허브 레포지토리로 하여, 인프라에 대한 모든 정보를 관리하는 것을 GitOps라고 한다. 이러한 과정에서 git을 통해서 인프라를 관리하기 때문에 rollback도 git을 통해 수행할 수 있다는 장점이 있다. 또한 Github actions 혹은 다른 Webhook을 사용하여 ArgoCD나 FluxCD와 같이 GitOps를 돕는 플러그인과 연계하면 엄청난 시너지를 보인다. 그리고 이런 GitOps를 실천하기 위해서는 전체 시스템이 선언적으로 기술되어야 하는데, 이러한 규칙을 Kustomize를 통해서 Kubernetes와 ArgoCD를 매끄럽게 이어주게 된다.
ArgoCD
이러한 GitOps를 도와주는 가장 유명한 애드온은 ArgoCD이다. ArgoCD는 주기적으로 Git 저장소를 바라보면서 변동 사항이 있다면 이를 감지하고 변경된 GitOps 코드를 폴링 하여 새롭게 쿠버네티스 클러스터를 선언한다. 이러한 방식 이외에도 인프라에 변동이 있어서 새롭게 커밋되면 webhook을 통해서 ArgoCD를 트리거할 수도 있다. 아래의 사진은 ArgoCD Docs에서 가져온 Argo의 동작 방식을 나타낸 그림이다.
이렇게 Kustomize는 독립형 구조로 동작하며, 선언형 프로그래밍 패러다임을 돕는 큰 역할을 하게 되었다.
마치며
단일 진실 공급원(SSOT)은 정말... 정말 중요한 것 같다. 특히 인프라에 있어서 콘솔로 관리하는 경우가 대다수이기 때문에 인프라 관련된 코드가 AWS나 Jenkins과 같은 콘솔에 흩어져 있으면 도저히 인프라를 건드릴 엄두가 안 난다.
참고
- [kustomize.io] main : https://kustomize.io/
- [kubernetes] Kustomize를 이용한 쿠버네티스 오브젝트의 선언형 관리
감사합니다.
'DevOps > kubernetes' 카테고리의 다른 글
[CKA] 기출 문제 정리 (0) | 2025.01.12 |
---|---|
ETCD Leader Election이란? (1) | 2024.12.18 |
Kubernetes의 Ingress와 Service 차이점에 대한 고찰 (0) | 2024.12.17 |
로컬 쿠버네티스 클러스터 환경 세팅 ( feat. minikube, lens ) (1) | 2024.12.16 |
[K8s homeserver 구축 - 3] github actions를 통한 CI (0) | 2023.11.10 |