프로젝트/졸업작품 - 인프라

6. kustomize 를 통한 Spring 및 Flask 애플리케이션 구축

han1693516 2025. 7. 2. 18:43

이번 프로젝트에서는 local, dev 등 애플리케이션 실행 환경을 프로파일별로 분리하기 위해 kustomization 을 사용했습니다.

 

프로젝트 구조는 다음과 같습니다.

 

base 에는 deployment, service 등 애플리케이션 구동에 필요한 resource 를 넣었고 (pdb 는 파드 업데이트 중에도 최소 하나의 파드는 동작하도록 해 무중단 배포가 가능하도록 했습니다)

 

network 에는 ingress 를 넣어 alb나 ExternalDNS 관련 설정이 들어가도록 했습니다.

 

overlays 에는 각 profile 별 동작을 위한 yaml 을 넣었습니다.

 

📁 home-protector-infra
├── 📁 base/
│   ├── 📁 ai/
│   │   ├── 📄 deploy
│   │   ├── 📄 hpa
│   │   ├── 📄 pdb
│   │   ├── 📄 service
│   │   └── 📄 kustomization
│   └── 📁 backend/
│       ├── 📄 deploy
│       ├── 📄 hpa
│       ├── 📄 pdb
│       ├── 📄 service
│       └── 📄 kustomization
├── 📁 network/
│   ├── 📄 app-ingress.yaml
│   └── 📄 kustomization
└── 📁 overlays/
    ├── 📁 local/
    │   └── 📁 backend/
    │       ├── 📄 patch-env.yaml
    │       ├── 📄 sealed-secret.yaml
    │       └── 📄 kustomization.yaml
    └── 📁 develop/
        ├── 📁 ai/
        │   ├── 📄 patch-image.yaml
        │   └── 📄 kustomization
        ├── 📁 backend/
        │   ├── 📄 secret-workspace
        │   ├── 📄 patch-env.yaml
        │   ├── 📄 patch-image.yaml
        │   ├── 📄 sealed-secret.yaml
        │   └── 📄 kustomization.yaml
        └── 📄 kustomization

 

 

backend base 의 각 resource 이다. 사용을 위해서 kustomization 에 각 manifest 를 등록했다

 

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - backend-deployment.yaml
  - backend-service.yaml
  - backend-pdb.yaml
  - backend-hpa.yaml

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-application
  labels:
    app: backend-application

spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  selector:
    matchLabels:
      app: backend-application

  template:
    metadata:
      name: backend-application
      labels:
        app: backend-application
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/actuator/prometheus"

    spec:
      containers:
        - name: backend-application
          image: han16935/protector-be
          imagePullPolicy: IfNotPresent
          ports:
            - name : http
              containerPort: 8080

#          resources:
#              requests:
#                cpu: "500m"
#                memory: "256Mi"
#              limits:
#                cpu: "1000m"  # 최대 1 CPU 코어 제한
#                memory: "512Mi"

          envFrom:
            - configMapRef:
                name: backend-configmap
      restartPolicy: Always

 

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: backend-pdb
spec:
  minAvailable: 1              # 최소 1개 파드는 항상 실행 상태 유지
  selector:
    matchLabels:
      app: backend-application

 

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: backend-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: backend-application
  minReplicas: 2        # 최소 pod 개수
  maxReplicas: 4        # 최대 pod 개수
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 70

 

 

다음으로는 ingress 이다. 이도 마찬가지로 network 폴더 내부의 kustomization 에 등록해야 한다. external-dns.alpha.xxx 어노테이션을 통해 Route 53 에 관련 도메인을 등록할 수 있다. 아래 certificate-arn 의 경우 저 값 그대로 들어가면 ArgoCD 통해 애플리케이션을 배포할 때 에러를 일으키니 주의하자

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: home-protector-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: instance
    alb.ingress.kubernetes.io/group.name: home-protector
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/certificate-arn: "${AWS_HTTPS_CERT}" # 실제 값으로 바뀌어야
    alb.ingress.kubernetes.io/ssl-redirect : '443'
    external-dns.alpha.kubernetes.io/hostname: be.home-protector.click,prometheus.home-protector.click
spec:
  ingressClassName: alb
  rules:
    - host: "be.home-protector.click"
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend-service
                port:
                  number: 80
    - host: "prometheus.home-protector.click"


      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: prometheus-service
                port:
                  number: 80

 

 

overlays/develop/backend/kustomization.yaml 의 경우 우리는 base/backend 를 기반으로 애플리케이션 metadata 를 수정할 것이고, patch-xx 를 통해 값을 수정할 것이기에 kustomization 은 다음과 같이 작성한다

 

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../../base/backend
  - sealed-secret.yaml

patches:
  - path: patch-image.yaml
  - path: patch-env.yaml

generatorOptions:
  disableNameSuffixHash: true

 

patch-env 파일이다. 우리는 dev 환경에서 이 애플리케이션을 실행할 것이기에, 다음처럼 patch-env를 작성했다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-application
spec:
  template:
    spec:
      containers:
        - name: backend-application
          envFrom:
            - secretRef:
                name: backend-secret
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: dev

 

patch-image.yaml 파일이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-application
spec:
  template:
    spec:
      containers:
        - name: backend-application
          image: 637423458012.dkr.ecr.ap-northeast-2.amazonaws.com/home-protector:be039674777e3b04d51ea18408a59b9c68a045c4

 

우리는 base 내 deploy 내 이미지가 아닌, ECR 내 이미지를 사용할 것이기에 Github Actions workflow 에 태그 최신화 step 을 추가했다. 

 

      - name : Checkout k8s repository
        uses : actions/checkout@v4
        with:
          repository : GCU-Home-Protector/protector-infra
          ref : main
          token : ${{secrets.ACTION_TOKEN}}
          path : home-protector-infra

      - name : Update backend image
        if : success()
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run : |
          cd home-protector-infra/overlays/develop/backend
          sed -i "s|image: ${ECR_REGISTRY}/${ECR_REPOSITORY}.*$|image: ${ECR_REGISTRY}/${ECR_REPOSITORY}:${IMAGE_TAG}|" patch-image.yaml
          cat patch-image.yaml

      - name : Push updated manifest
        run : |
          cd home-protector-infra
          
          git config --global user.email "github-actions@github.com"
          git config --global user.name "github-actions"
          
          if [[ -n "$(git status --porcelain)" ]]; then
            git add overlays/develop/backend/patch-image.yaml
            git commit -m "chore : Update backend image tag to ${{ github.sha }}"
          else
            echo "No changes detected, creating empty commit to trigger ArgoCD"
            git commit --allow-empty -m "chore : Tag update fail"
          fi
          
          git push origin main

 

overlays/kustomization.yaml 은 다음과 같이 작성했다

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../overlays/develop/ai
  - ../../overlays/develop/backend
  - ../../network

 

 

실행을 위해 다음 명령어를 사용했다. 실행 되는 걸 확인하고 위에서부터 복붙한 거여서 잘못 복붙햇을 수도 잇다... 그러면 미안...

 

# Bastion <-> EKS 연결
aws eks update-kubeconfig --region $AWS_DEFAULT_REGION --name $CLUSTER_NAME 

# 필요한 값 환경변수로 세팅

# Sealed Secret Controller 생성
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update

helm install sealed-secrets-controller sealed-secrets/sealed-secrets \
  --namespace kube-system \
  --create-namespace
  
kubectl wait --namespace kube-system \
  --for=condition=available deployment/sealed-secrets-controller \
  --timeout=180s
  
kubectl get secret sealed-secrets-key -n kube-system \
  -o jsonpath="{.data.tls\.crt}" | base64 -d > sealed-secrets-key.pem
  
# kubeseal 설치
curl -LO "https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.30.0/kubeseal-0.30.0-linux-amd64.tar.gz"
tar -xvzf "kubeseal-0.30.0-linux-amd64.tar.gz" kubeseal

chmod +x kubeseal
sudo mv kubeseal /usr/local/bin/

cd "$ORIGIN_DIR"
rm -rf "$TMP_DIR"

# env 생성
cd protector-infra/overlays/backend/secret-workspace
chmod +x generate-env-file.sh
./generate-env-file.sh

# Sealed Secret 생성
export SEALED_SECRETS_CERT=~/protector-infra/overlays/develop/backend/secret-workspace/sealed-secrets-key.pem
chmod +x generate-sealed-secret.sh
./generate-sealed-secret.sh

# ECR 로그인
aws ecr get-login-password \
  --region ${AWS_DEFAULT_REGION} | docker login \
  --username AWS \
  --password-stdin ${ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com
  
 kubectl apply -k overlays/develop

 

 

 

AI 서버도 제대로 연결되고 있는 걸 볼 수 있다!