서론
이전 포스팅에서는 "개발환경 없는 회사에서 NKS로 개발환경 구성하기"라는 키워드를 기준으로 포스팅하였다면, 본 포스팅부터는 프로덕션환경과 개발환경 모두를 다루기 때문에 "Cloud Kubernetes 환경 구축"이라는 키워드로 변경하였습니다.
간단하게나마 GitOps에 대한 개념을 알아본 후에 mgt-cluster에 설치한 ArgoCD, GitLab을 토대로 GitOps방식의 CI/CD를 설계 및 구현해 보겠습니다. ArgoCD 및 gitLab 설치는 이전 포스팅을 참고해 주시기 바랍니다.
[Kubernetes 도입] 개발환경 없는 회사에서 NKS로 개발환경 구성하기 Chapter 7. gitops와 argocd 도입을 위
서론지금까지는 Naver Cloud Platform(이하 NCP)에서 제공해 주는 Source Series(Commit, Build,Deploy, PipeLine)을 사용하여 CI/CD를 진행했다면, 쿠버네티스의 장점을 고루 살린 GitOps와 ArgoCd를 사용하여 CI/CD를 마
min-nine.tistory.com
[Kubernetes 도입] 개발환경 없는 회사에서 NKS로 개발환경 구성하기 Chapter 8. NKS에 GitLab, GitLab-SSH, GitL
서론소스코드 저장소 호스팅의 대표주자는 GitLab, GitHub가 있으며 기본 기능은 source의 버전 관리를 해준다는 것으로 같지만 차이가 확실합니다. 깃헙과 깃랩의 가장 큰 차이점은 데브옵스에 있는
min-nine.tistory.com
GitOps와 DevOps에 대한 개념은 아래 포스팅을 참고해 주시기 바랍니다.
알기 쉽게 정의한 DevOps와 GitOps
서론소프트웨어 개발과 운영이 점점 더 빠르게 변화하는 환경에서, 개발팀과 운영팀이 협업하여 안정적이고 신속하게 서비스를 제공할 수 있도록 돕는 방법론들이 주목받고 있습니다. 이 포스
min-nine.tistory.com
본론
GitOps PipeLine Example
기본적으로 GitOps 파이프라인은 아래 사진과 같은 형식으로 설계합니다.
1. application Source Code를 수정 및 Push/MR or PR 합니다.
2. SourceCode Repository의 Push/MR or PR Event를 감지하여 변경된 내용을 기반으로 Build 합니다.
3. Build 된 Application을 Container화 하여 Image로 만듭니다.
4 만들어진 Image를 Container Registry에 Push 합니다.
5. 변경된 Image Tag 혹은 Config 등을 관리하는 별도의 Repository에 해당 사항을 Hook이나 기타 Trigger를 통하여 update 해줍니다. (저의 경우 Helm으로 관리하기에 Helm Repository라고 칭하겠습니다)
6. Helm Repository를 관찰하고 있는 Deploy Operator(저의 경우 ArgoCD)에서 변경 사항을 감지합니다.
7. (Auto 설정 경우) 감지된 내용을 기반으로 ArgoCD에서 설정한 K8s Cluster의 Namespce에 알맞게 배포합니다.
8. (Manual 설정 경우) 별도의 Trigger를 기반으로 ArgoCD에서 설정한 K8s Cluster의 Namespce에 알맞게 배포합니다. 저는 Manual 방식으로 설계했는데 이유는 아래에서 언급하겠습니다.
CI/CD Flow 설계
예전에 Docker, Jenkins를 활용한 CI/CD 구축 포스팅에서도 언급했던 것과 같이 구축하는 방법은 아주 많습니다. 설계자가 상황에 적합한 툴과 기술들을 사용해서 설계하는 게 중요하다고 생각하고, 저는 GitLab과 ArgoCD를 활용하여 아래 그림과 같이 cloud kubernetes 환경에서의 CI/CD를 설계하였습니다.
저는 Manual 방식으로 설계하였는데, 저희 회사의 Application에 해당하는 k8s obejct 생성 관련 Helm 양식이 매우 비슷합니다. 사용할 이미지 명칭, Container, Deploy, Service 등의 명칭만 제외한다면 나머지 설정값은 너무나도 동일했습니다. Auto방식의 경우 Application Source Repository와 Helm Repository가 1:1 관계여야 한다고 생각하는데, 동일한 내용의 많은 helm 레포지토리를 관리하는 게 불필요하다고 판단하여 비슷한 형식의 application은 한 개의 helm repository로 관리하기로 정했기 때문입니다.
Helm Charts 생성 (Spring Boot Application)
Springboot 기반의 API Application을 기준으로 spring_boot_helm이라는 명칭의 Helm을 생성하였습니다.
# helm chart 생성
helm create spring_boot_helm
# custom-values 폴더 생성 및 초기 셋팅
cd spring_boot_helm
mkdir custom-values
cd custom-values
# 중복되는 springboot application에 맞는 폴더 생성
mkdir mingyu-api #mingyu2-api mingyu3-api ...
cd mingyu-api
# 각 브랜치 및 환경에 맞는 설정을 갖는 values 생성
touch develop-values.yaml
touch prod-values.yaml
생성된 기본 구조는 아래와 같습니다. 이제 이 helm을 토대로 mingyu-api의 develop 환경에는 develop-values.yaml을, production 환경에는 prod-values.yaml을 토대로 deployment와 service, ingress 등을 배포하게 됩니다.
.
├── Chart.yaml
├── README.md
├── custom-values
│ └── mingyu-api
│ ├── develop-values.yaml
│ └── prod-values.yaml
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
Temlates 수정하기
우선, Deployment와 Service만 사용해 줄 것이기 때문에 Deployment.yaml 파일과 service.yaml 파일을 custom-values의 내용으로 빌드될 수 있도록 아래와 같이 수정해 줬습니다. helm의 template 생성 가이드는 HELM 공식 문서에 상세히 기술되어 있습니다. 저의 경우 values 파일을 기반의 빌드를 설계하여 아래처럼 작성하였습니다.
Chart Template Guide
Helm - The Kubernetes Package Manager.
helm.sh
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.deployment.name }}
namespace: {{ .Values.deployment.namespace }}
labels:
{{- toYaml .Values.deployment.labels | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- toYaml .Values.replicasetLabels | nindent 6 }}
template:
metadata:
labels:
{{- toYaml .Values.podLabels | nindent 8 }}
annotations:
{{- toYaml .Values.podAnnotations | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
# 원본에서 사용한 command
command:
{{- toYaml .Values.command | nindent 12 }}
ports:
- name: http
containerPort: {{ .Values.service.containerPort }}
protocol: TCP
envFrom:
{{- toYaml .Values.envFrom | nindent 12 }}
# LivenessProbe / ReadinessProbe
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
# 볼륨 마운트 추가 필요 시
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
imagePullSecrets:
{{ toYaml .Values.imagePullSecrets | nindent 8 }}
# 볼륨 추가 필요 시
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
# Node selector, Toleration, Affinity 등
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.service.name }}
namespace: {{ .Values.service.namespace }}
spec:
type: {{ .Values.service.type }}
selector:
{{- toYaml .Values.service.selector | nindent 4 }}
ports:
- name: http
port: {{ .Values.service.servicePort }}
targetPort: http
protocol: TCP
ingress.yaml
저의 경우 ingress는 application당 하나씩 생성하지 않고, aws의 ALB를 활용하기 때문에 values의 ingress.enabled 속성을 false로 추가해 주었습니다. 그리고 ingress.yaml파일의 위아래에 if를 통하여 사용하지 않도록 설정해 주었습니다.
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
....
{{- end }}
custom-values.yaml 작성하기
아래는 custom-values/mingyu-api/develop-values.yaml 파일 내용입니다. 아래 기술한 값들을 토대로 deployment, service, ingress 등의 template을 통해 k8s object를 컨트롤할 수 있는 yaml 파일이 생성되고 배포되며 관리됩니다.
# ------------------------------------------------------------------------------
# [기본 설정: 배포 이름/네임스페이스/라벨 등]
nameOverride: "mingyu-api" # Helm Chart 이름
deployment:
name: mingyu-api-deployment # Deployment 이름
namespace: test # 배포할 namespace
labels:
app: mingyu-api-deployment
# ------------------------------------------------------------------------------
# [Pod/컨테이너 개수]
replicaCount: 1
# ------------------------------------------------------------------------------
# [이미지 설정]
image:
repository: vjnibbzs.kr.private-ncr.ntruss.com/mingyu-api-dev
pullPolicy: Always
# Chart.yaml의 appVersion이 "1.0.2"지만, 필요 시 명시적으로 아래 tag를 지정할 수도 있습니다.
tag: "latest"
# ------------------------------------------------------------------------------
# [이미지 풀 시크릿] - Private Registry 사용 시 필요한 경우
imagePullSecrets:
- name: regcred
# ------------------------------------------------------------------------------
# [service 설정]
service:
name: mingyu-api-service
namespace: test
type: NodePort
# Service가 바라보는(매핑하려는) 컨테이너 포트
containerPort: 80
# Service 자체가 노출될 포트 (클라이언트가 접근하는 포트)
servicePort: 80
selector:
app: mingyu-api-deployment
# ------------------------------------------------------------------------------
# [Ingress 설정]
ingress:
enabled: false
className: "alb"
annotations:
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
alb.ingress.kubernetes.io/ssl-certificate-no: "{your-ncp-ssl-certificate-no}"
alb.ingress.kubernetes.io/description: "ingress controller"
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/healthcheck-path: "/health"
alb.ingress.kubernetes.io/healthcheck-interval-seconds: "120"
hosts:
- host: dev-mingyu-api.test.co.kr
paths:
- path: /*
pathType: Prefix
tls: []
# 예) tls:
# - secretName: chart-example-tls
# hosts:
# - dev-mingyu-api.test.co.kr
# ------------------------------------------------------------------------------
# [리소스 설정]
resources:
requests:
cpu: "300m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# ------------------------------------------------------------------------------
# [Probes]
# 원본에 맞추어 /health로 체크
livenessProbe:
httpGet:
path: /health/liveness
port: http
initialDelaySeconds: 90
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/readiness
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
# ------------------------------------------------------------------------------
# [Pod, SA, SecurityContext 등]
serviceAccount:
create: false
automount: true
name: ""
podAnnotations: {}
replicasetLabels: # replicaset이 관리할 pod 라벨
app: mingyu-api-deployment
podLabels:
app: mingyu-api-deployment # pod의 라벨
podSecurityContext: {}
securityContext: {}
# ------------------------------------------------------------------------------
# [추가적으로 Autoscaling, Volumes, Toleration, Affinity 설정 등 필요 시]
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
volumes: []
volumeMounts: []
nodeSelector: {}
tolerations: []
affinity: {}
# ------------------------------------------------------------------------------
# [컨테이너 실행 명령]
# 원본 Deployment에서 사용했던 command.
command:
- "/bin/sh"
- "-c"
- "java -Dspring.profiles.active=dev -jar app.jar"
# ------------------------------------------------------------------------------
# [envFrom 등 환경변수 세팅 (ConfigMap 사용 시)]
envFrom:
- configMapRef:
name: mingyu-api-config-dev
Gitlab에서 Helm Repository 생성 및 관리하기
이제 생성한 spring_boot_helm 폴더를 관리할 git repository와 helm chart 전용 package repository를 생성해 줍니다. 생성 방법은 공식 문서에 자세히 나와있습니다. 저의 경우 helm cm-push를 활용하였습니다.
Helm charts in the package registry | GitLab Docs
Helm charts in the package registry Tier: Free, Premium, UltimateOffering: GitLab.com, GitLab Self-Managed, GitLab DedicatedStatus: Beta The Helm chart registry for GitLab is under development and isn’t ready for production use due to limited functionali
docs.gitlab.com
helm repo add --username HELM_TOKEN \
--password {HELM_TOKEN_VALUES} \
https://gitlab.mingyu.co.kr/api/v4/project/{helm-rpository-project-id}/packages/helm/stable
helm cm-push spring_boot_helm spring_boot_helm
업로드한 helm charts를 다운로드 받고 싶을 때는 아래 주소를 활용할 수 있습니다.
https://gitlab.mingyu.co.kr/api/v4/projects/{helm-repository-project-id}/packages/helm/stable
결론
뭔가 복잡하고 어려운 것 처럼 느껴지지만, 이것이 왜 필요하고 왜 사용되었는지 생각해 가면서 하나하나 차근차근 설계하고 설치하고 설정하다 보면 재미를 느끼게 되고 차츰 본인의 지식이 될 것입니다. 저 스스로에게 하는 말입니다 :)
'Infrastructure > CICD' 카테고리의 다른 글
GitLab & ArgoCD를 활용한 GitOps방식의 CI/CD 구축하기 (3) - GitlabCI & ArgoCD 연동하여 CI/CD 구축 (Manual) (0) | 2025.03.10 |
---|---|
GitLab & ArgoCD를 활용한 GitOps방식의 CI/CD 구축하기 (2) - ArgoCd에 Cluster 추가하기 (0) | 2025.03.08 |
[CI/CD 구축] AWS EC2에 Docker를 활용한 Jenkins 파이프라인 구축 (2) | 2024.02.07 |
[CI/CD 구축] AWS, Docker, GitLab을 사용하여 CI/CD 구축하기 3편(끝) (0) | 2022.12.23 |
[CI/CD 구축] AWS, Docker, GitLab을 사용하여 CI/CD 구축하기 2편 (0) | 2022.12.16 |
댓글