1. 기본 프로젝트 구성
- 프론트는 next, 백엔드는 spring boot로 구성
- 소스코드의 원본 깃 레포지토리와 argoCD용 manifests 레포지토리를 구성하여 연동

- manifests 저장소의 yaml파일 (이외에도 업데이트를 위한 helm차트가 존재함)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: todo-list
namespace: argocd
spec:
project: default
source:
repoURL: 'https://github.com/ellanelee/manifests-repository.git'
targetRevision: 'main'
path: 'apps/todo-list/backend/helm/todo-list-chart'
helm:
parameters:
- name: 'backend.image.tag'
value: 'v1.0'
destination:
server: 'https://kubernetes.default.svc'
namespace: 'default'
syncPolicy:
automated:
prune: true
selfHeal: true

3. heml구성
1) 차트 생성
#helm chart생성
helm create <차트이름>
2) 생성된 차트의 구성변경
- 프론트엔드는 CI/CD에서 제외하고 백엔드만 배포
- Backend는 2개의 replicas로 지정 , DB관련 설정은 configmap적용
- DB는 stateful로 구성하고 3개의 replica지정, headless service 적용
- Backend와 DB의 접속정보는 secret으로 설정

3) values.yaml을 구성하고 내용에 맞게 helm 차트내 manifests구성
# bakend-deployment구성
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "todo-list-chart.fullname" . }}-backend-deployment
labels:
{{- include "todo-list-chart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.backend.replicaCount }}
selector:
matchLabels:
{{- include "todo-list-chart.backend.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "todo-list-chart.backend.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: todo-backend
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}"
# command: ["/bin/sh", "-c"] # 연장
# args: ["sleep 60"] #연장
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: {{ .Values.database.host }}
- name: CORS_ALLOWED_ORIGINS
value: "{{ .Values.backend.cors_allowed_origins }}"
envFrom:
- configMapRef:
name: {{ include "todo-list-chart.fullname" . }}-db-configmap
- secretRef:
name: {{ include "todo-list-chart.fullname" . }}-db-secret
#backend-service구성
apiVersion: v1
kind: Service
metadata:
name: {{ include "todo-list-chart.fullname" . }}-backend-service
labels:
{{- include "todo-list-chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.backend.service.type }}
ports:
- port: {{ .Values.backend.service.port }}
targetPort: {{ .Values.backend.service.targetPort }}
{{- if and (eq .Values.backend.service.type "NodePort") .Values.backend.service.nodePort.enabled }}
nodePort: {{ .Values.backend.service.nodePort.port }}
{{- end }}
selector:
{{- include "todo-list-chart.backend.selectorLabels" . | nindent 6 }}
# db configmap구성
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "todo-list-chart.fullname" . }}-db-configmap
labels: {{- include "todo-list-chart.labels" . | nindent 4 }}
data:
MYSQL_DATABASE: {{ .Values.database.name }}
MYSQL_USER: {{ .Values.database.user }}
#Secret구성
apiVersion: v1
kind: Secret
metadata:
name: {{ include "todo-list-chart.fullname" . }}-db-secret
labels: {{- include "todo-list-chart.labels" . | nindent 4 }}
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: {{ .Values.database.rootPassword | quote }}
MYSQL_PASSWORD: {{ .Values.database.password | quote }}
#db-headless-service구성
apiVersion: v1
kind: Service
metadata:
name: todo-headless-db-service
labels: {{- include "todo-list-chart.labels" . | nindent 4 }}
spec:
ports:
- port: 3306
selector:
app.kubernetes.io/name: {{ include "todo-list-chart.name" . }}-db
app.kubernetes.io/instance: {{ .Release.Name }}
clusterIP: None
#db-statefulset구성
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "todo-list-chart.fullname" . }}-db-statefulset
labels:
{{- include "todo-list-chart.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: {{ include "todo-list-chart.name" . }}-db
app.kubernetes.io/instance: {{ .Release.Name }}
serviceName: "todo-headless-db-service"
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "todo-list-chart.name" . }}-db
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
envFrom:
- secretRef:
name: {{ include "todo-list-chart.fullname" . }}-db-secret
- configMapRef:
name: {{ include "todo-list-chart.fullname" . }}-db-configmap
volumeMounts:
- name: todo-persistent-storage
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: todo-persistent-storage
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
4. GitHub workflow구성
- 깃헙의 workflow구성 전에 DB접근과 manifests저장소 접근을 위해 해당 레포지토리에 action key설정 필요
name: CI for Todo List Backend
# 워크플로우가 실행될 조건 (Triggers)
on:
push:
branches: ["main"]
paths:
- "apps/todo-list/backend/**"
# 실행될 작업(Job)들
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
# 1. 소스 코드 체크아웃
- name: Checkout source code
uses: actions/checkout@v4
# 2. JDK 17 설정
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
# 3. Gradle 캐싱. #의존성을 처리하면서 오래 걸리므로 일부 caching
- name: Gradle Caching
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
# 4. gradlew에 실행 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
working-directory: ./apps/todo-list/backend
# 5. Gradle로 프로젝트 빌드
- name: Build with Gradle
run: ./gradlew build -x test
working-directory: ./apps/todo-list/backend
# 6. Docker Hub에 로그인
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}. # 프로젝트의 settings에서 설정
password: ${{ secrets.DOCKERHUB_TOKEN }}
# 7. Docker 이미지 빌드 및 푸시
- name: Build and push Docker image
id: build-push
uses: docker/build-push-action@v5
with:
context: ./apps/todo-list/backend
file: ./apps/todo-list/backend/Dockerfile.prod
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/k8s-labs-todo-backend:${{ github.sha }}
# 8. 생성된 이미지 태그 출력
- name: Print image tag
run: echo "Image tagged with:${{ secrets.DOCKERHUB_USERNAME }}/k8s-labs-todo-backend:${{ github.sha }}"

5. argoCD 설치
# namespace만들기
kubectl create namespace argocd
namespace/argocd created
# 내용받아오기
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Argo cd 서버구동
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
#구동 및 접속 위치 확인
minikube service argocd-server -n argocd
|-----------|---------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|---------------|-------------|---------------------------|
| argocd | argocd-server | http/80 | http://192.168.49.2:30976 |
| | | https/443 | http://192.168.49.2:31686 |
|-----------|---------------|-------------|---------------------------|
🏃 Starting tunnel for service argocd-server.
|-----------|---------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|---------------|-------------|------------------------|
| argocd | argocd-server | | http://127.0.0.1:50627 |
| | | | http://127.0.0.1:50628 |
|-----------|---------------|-------------|------------------------|
[argocd argocd-server http://127.0.0.1:50627
http://127.0.0.1:50628]
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
#비번확인
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
- localhost:50627 (admin, 확인된 비번으로 로그인 )에서 App구동 정보 입력하여 구동
6. ArgoCD에서 배포 확인

k get pods
NAME READY STATUS RESTARTS AGE
todo-list-todo-list-chart-backend-deployment-588688b7b6-72hnv 1/1 Running 0 62s
todo-list-todo-list-chart-backend-deployment-588688b7b6-qrgv5 1/1 Running 0 72s
todo-list-todo-list-chart-db-statefulset-0 1/1 Running 0 11h
(⎈|minikube:default) elena@ijieun-ui-MacBookAir k8s-labs-todo-list-manifests % k
get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
todo-list-todo-list-chart-backend-deployment 2/2 2 2 13h
(⎈|minikube:default) elena@ijieun-ui-MacBookAir k8s-labs-todo-list-manifests % k get all
NAME READY STATUS RESTARTS AGE
pod/todo-list-todo-list-chart-backend-deployment-588688b7b6-72hnv 1/1 Running 2 (28s ago) 5h7m
pod/todo-list-todo-list-chart-backend-deployment-588688b7b6-qrgv5 1/1 Running 2 (29s ago) 5h7m
pod/todo-list-todo-list-chart-db-statefulset-0 1/1 Running 0 16h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d7h
service/todo-headless-db-service ClusterIP None <none> 3306/TCP 16h
service/todo-list-todo-list-chart-backend-service NodePort 10.96.246.214 <none> 8080:30001/TCP 18h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/todo-list-todo-list-chart-backend-deployment 2/2 2 2 18h
NAME DESIRED CURRENT READY AGE
replicaset.apps/todo-list-todo-list-chart-backend-deployment-5468f7f47c 0 0 0 7h1m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-54c9675bb9 0 0 0 6h17m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-55cf88ddfc 0 0 0 6h13m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-588688b7b6 2 2 2 5h7m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-58d84768cb 0 0 0 6h29m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-6dd9cd858f 0 0 0 17h
replicaset.apps/todo-list-todo-list-chart-backend-deployment-75fc864597 0 0 0 6h26m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-7ccb674bdc 0 0 0 17h
replicaset.apps/todo-list-todo-list-chart-backend-deployment-86f47f6cf4 0 0 0 5h11m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-88977bfb6 0 0 0 5h37m
replicaset.apps/todo-list-todo-list-chart-backend-deployment-dc48696fd 0 0 0 16h
NAME READY AGE
statefulset.apps/todo-list-todo-list-chart-db-statefulset 1/1 16h
-----------------------------------------------------------------------
2개의 간단한 프로젝트를 구현해서 CI/CD로 구성하는 실습을 진행했다.
Local의 minikube를 이용했으며, 1개의 프로젝트를 배포하는데 성공했다. 해당 프로젝트는 상당히 간단한 구조임에도 helm구성이나 배포를 스스로 해보는건 처음이라 어려웠다. 마지막의 pod생성 기록에 desired 0, current 0인 것들은 배포되었다가 오류로 인해 terminate된 pod들의 History이다. 사실 실패해서 그늘 속으로 사라진 파드는 더 많은데 .. 백업을 위해 10개까지만 보관된다고 한다.
arm구성의 기기를 사용하는데, github action은 amd기준의 이미지를 생성하므로 이미지를 불러오면서부터 에러가 났다. 원인을 찾으면서 ( 컨테이너 구성이 하드웨어에 영향을 받지 않는다고 배웠으므로 ) 상당한 시간을 소요했다.
helm을 처음 작성해본지라 indent의 문제라던가, headless service와 db-service를 한꺼번에 이용하면서 설정이 맞지 않아 원인을 찾고 해결하기 위해 db-service를 삭제하고 stateful리소스를 직접 host와 연결해줬는데 바람직한 설정인지는 모르겠다. 또한, persistent volume설정으로 인해 db설정의 초기치가 잡히면 수정값을 주어도 반영되지 않아(초기치는 재설정 되지 않는다는데 맞는 얘기인지..) db의 구성과 로그까지 확인해서 values.yaml을 수정한 후에 완료할수 있었다.
또 하나의 프로젝트는 O-Auth와 외부 저장소를 이용한 조금 더 복잡한 구조인데, DB연결 에러가 나고 있어 오류 로그를 살펴보고 있는 중이다. 쿠버 동아리에서 사전에 흟어주신 내용이 아니었다면 진행하기가 몹시 어려웠을텐데 사전에 여러가지 지식을 알려주신 김경*님에게 감사를 전하고 싶다.
'INFRA > Kubernetes' 카테고리의 다른 글
| 쿠버네티스_추가 (1) (0) | 2025.08.10 |
|---|