콘텐츠로 이동

XGEN 2.0 인프라: K8s + ArgoCD 운영 배포

2026.01 | K3s, ArgoCD, Istio, Grafana, Prometheus, GitOps

개요

XGEN 2.0을 운영 환경에 배포하면서 가장 중요한 목표는 무중단 서비스관측 가능성(Observability)이었다. 단순한 컨테이너 배포를 넘어서 장애 상황을 즉시 감지하고 대응할 수 있는 완전한 DevOps 파이프라인을 구축해야 했다.

아키텍처 설계

K3s 기반 경량화 클러스터

운영 서버의 리소스 효율성을 위해 K3s를 선택했다. 전체 클러스터 구성:

# k3s 설치 스크립트
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" \
  K3S_TOKEN="your-cluster-token" \
  INSTALL_K3S_VERSION="v1.28.5+k3s1" \
  sh -s - \
  --disable traefik \
  --disable servicelb \
  --write-kubeconfig-mode 644 \
  --node-taint CriticalAddonsOnly=true:NoExecute

Service Mesh (Istio) 구성

마이크로서비스 간 통신과 트래픽 관리를 위해 Istio를 도입했다:

# istio-config.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: control-plane
spec:
  values:
    pilot:
      env:
        EXTERNAL_ISTIOD: false
    gateways:
      istio-ingressgateway:
        type: NodePort
        ports:
        - port: 15021
          targetPort: 15021
          name: status-port
          nodePort: 30021
        - port: 80
          targetPort: 8080
          name: http2
          nodePort: 30080
        - port: 443
          targetPort: 8443
          name: https
          nodePort: 30443

GitOps with ArgoCD

코드 변경부터 운영 배포까지 완전 자동화된 파이프라인을 구축했다:

# argocd/applications/xgen-services.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: xgen-services
  namespace: argocd
spec:
  project: xgen-project
  source:
    repoURL: 'https://gitlab.x2bee.com/xgen2.0/xgen-infra.git'
    targetRevision: main
    path: k8s/services
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: xgen
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
    - ApplyOutOfSyncOnly=true

Observability 스택 구축

Prometheus + Grafana 모니터링

시스템 메트릭과 비즈니스 메트릭을 통합 수집하는 환경을 구축했다:

# k8s/observability/prometheus.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
  namespace: observability
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
  template:
    spec:
      containers:
      - name: prometheus
        image: prom/prometheus:v2.45.0
        ports:
        - containerPort: 9090
        volumeMounts:
        - name: config
          mountPath: /etc/prometheus/prometheus.yml
          subPath: prometheus.yml
        - name: storage
          mountPath: /prometheus
        args:
        - '--config.file=/etc/prometheus/prometheus.yml'
        - '--storage.tsdb.path=/prometheus'
        - '--web.console.libraries=/etc/prometheus/console_libraries'
        - '--web.console.templates=/etc/prometheus/consoles'
        - '--storage.tsdb.retention.time=15d'
        - '--web.enable-lifecycle'
      volumes:
      - name: config
        configMap:
          name: prometheus-config
      - name: storage
        persistentVolumeClaim:
          claimName: prometheus-storage

통합 대시보드 구성

운영진이 한 눈에 시스템 상태를 파악할 수 있는 대시보드를 제작했다:

{
  "dashboard": {
    "title": "XGEN Overview",
    "panels": [
      {
        "title": "Service Health",
        "type": "stat",
        "targets": [
          {
            "expr": "up{job=~\"xgen-.*\"}"
          }
        ]
      },
      {
        "title": "CPU Usage by Service",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(container_cpu_usage_seconds_total{namespace=\"xgen\",container!=\"POD\"}[5m]) * 100"
          }
        ]
      },
      {
        "title": "Memory Usage",
        "type": "graph", 
        "targets": [
          {
            "expr": "container_memory_working_set_bytes{namespace=\"xgen\",container!=\"POD\"} / 1024 / 1024"
          }
        ]
      },
      {
        "title": "Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(istio_requests_total{destination_namespace=\"xgen\"}[5m])"
          }
        ]
      }
    ]
  }
}

Loki를 이용한 중앙화 로깅

모든 서비스의 로그를 중앙에서 수집하고 검색할 수 있는 시스템을 구축했다:

# k8s/observability/promtail-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: promtail
  namespace: observability
spec:
  selector:
    matchLabels:
      app: promtail
  template:
    spec:
      serviceAccount: promtail
      containers:
      - name: promtail
        image: grafana/promtail:2.9.0
        args:
        - -config.file=/etc/promtail/config.yml
        env:
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        volumeMounts:
        - name: config
          mountPath: /etc/promtail
        - name: varlog
          mountPath: /var/log
          readOnly: true
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: positions
          mountPath: /tmp/positions
      volumes:
      - name: config
        configMap:
          name: promtail-config
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: positions
        emptyDir: {}

CI/CD 파이프라인

Jenkins 기반 빌드 자동화

GitLab 커밋부터 K8s 배포까지 완전 자동화된 파이프라인을 구축했다:

// Jenkinsfile
pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = "registry.x2bee.com"
        KUBECONFIG = "/var/jenkins_home/.kube/config"
    }

    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', 
                    url: 'https://gitlab.x2bee.com/xgen2.0/xgen-frontend.git',
                    credentialsId: 'gitlab-credentials'
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    def image = docker.build("${DOCKER_REGISTRY}/xgen/frontend:${BUILD_NUMBER}")
                    image.push()
                    image.push("latest")
                }
            }
        }

        stage('Update K8s Manifests') {
            steps {
                sh """
                    sed -i 's|image:.*frontend:.*|image: ${DOCKER_REGISTRY}/xgen/frontend:${BUILD_NUMBER}|g' k8s/services/frontend-deployment.yaml
                    git add k8s/services/frontend-deployment.yaml
                    git commit -m "Update frontend image to ${BUILD_NUMBER}"
                    git push origin main
                """
            }
        }

        stage('Trigger ArgoCD Sync') {
            steps {
                sh """
                    argocd app sync xgen-services --auth-token ${ARGOCD_TOKEN}
                """
            }
        }
    }
}

ArgoCD 프로젝트 설정

보안과 네임스페이스 격리를 위한 RBAC 설정:

# argocd/projects/xgen-project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: xgen-project
  namespace: argocd
spec:
  sourceRepos:
  - 'https://gitlab.x2bee.com/xgen2.0/*'
  destinations:
  - namespace: 'xgen*'
    server: 'https://kubernetes.default.svc'
  - namespace: 'observability'
    server: 'https://kubernetes.default.svc'
  clusterResourceWhitelist:
  - group: ''
    kind: Namespace
  - group: rbac.authorization.k8s.io
    kind: ClusterRole
  - group: rbac.authorization.k8s.io
    kind: ClusterRoleBinding
  namespaceResourceWhitelist:
  - group: ''
    kind: Service
  - group: ''
    kind: ConfigMap
  - group: ''
    kind: Secret
  - group: apps
    kind: Deployment
  - group: networking.istio.io
    kind: VirtualService
  - group: networking.istio.io
    kind: DestinationRule

서비스 배포 전략

무중단 배포 (Rolling Update)

모든 서비스에 대해 무중단 배포 전략을 적용했다:

# k8s/services/backend-gateway-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: xgen-backend-gateway
  namespace: xgen
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      app: xgen-backend-gateway
  template:
    metadata:
      labels:
        app: xgen-backend-gateway
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "3000"
        prometheus.io/path: "/metrics"
    spec:
      containers:
      - name: backend-gateway
        image: registry.x2bee.com/xgen/backend-gateway:latest
        ports:
        - containerPort: 3000
        env:
        - name: K3S_ENV
          value: "true"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: xgen-secrets
              key: database-url
        resources:
          requests:
            memory: "512Mi"
            cpu: "200m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

HPA (Horizontal Pod Autoscaler)

트래픽 증가에 자동으로 대응하는 오토스케일링을 구성했다:

# k8s/services/backend-gateway-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: xgen-backend-gateway-hpa
  namespace: xgen
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: xgen-backend-gateway
  minReplicas: 3
  maxReplicas: 6
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

알림 시스템 구축

Grafana Slack 연동

장애 발생 시 즉시 개발팀에 알림이 전달되는 시스템을 구축했다:

# grafana/provisioning/alerting/contact-points.yaml
apiVersion: 1
contactPoints:
  - name: slack-alerts
    type: slack
    settings:
      url: "YOUR_SLACK_WEBHOOK_URL"
      channel: "#xgen-alerts"
      title: "XGEN 알림"
      text: |
        {{ range .Alerts }}
        **{{ .Annotations.summary }}**
        {{ .Annotations.description }}
        Status: {{ .Status }}
        {{ end }}

Alert Rules

주요 장애 상황에 대한 경보 규칙들을 정의했다:

# grafana/provisioning/alerting/rules.yaml
groups:
  - name: xgen-critical
    interval: 30s
    rules:
    - alert: PodNotRunning
      expr: up{job=~"xgen-.*"} == 0
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "XGEN Pod가 실행되지 않음"
        description: "{{ $labels.instance }}에서 {{ $labels.job }} 서비스가 1분간 응답하지 않습니다."

    - alert: HighMemoryUsage
      expr: container_memory_working_set_bytes{namespace="xgen"} / container_spec_memory_limit_bytes > 0.8
      for: 2m
      labels:
        severity: warning
      annotations:
        summary: "높은 메모리 사용률"
        description: "{{ $labels.pod }}에서 메모리 사용률이 {{ $value | humanizePercentage }}입니다."

    - alert: HighErrorRate
      expr: rate(istio_requests_total{destination_namespace="xgen",response_code!~"2.."}[5m]) > 0.05
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "높은 에러율 감지"
        description: "XGEN 서비스에서 5% 이상의 에러율이 감지되었습니다."

트러블슈팅 & 최적화

Istio TLS 인증서 문제 해결

운영 배포 초기에 발생한 TLS 인증 문제를 해결했다:

# istioctl을 이용한 TLS 문제 진단
istioctl proxy-config cluster xgen-backend-gateway-xxx -n xgen

# 인증서 갱신
istioctl experimental authz check \
  xgen-frontend-xxx.xgen \
  -a GET \
  -x xgen-backend-gateway.xgen.svc.cluster.local:3000

네임스페이스 Terminating 상태 해결

ArgoCD 배포 중 발생한 네임스페이스 삭제 문제 해결:

#!/bin/bash
# force-delete-namespace.sh
NAMESPACE=$1
kubectl get namespace $NAMESPACE -o json | \
  jq '.spec.finalizers = []' | \
  kubectl replace --raw "/api/v1/namespaces/$NAMESPACE/finalize" -f -

리소스 최적화

실제 사용량 기반으로 리소스 설정을 최적화했다:

# 최적화된 리소스 설정 (실측값 기반)
resources:
  requests:
    memory: "512Mi"    # 실제 평균: 320Mi
    cpu: "200m"        # 실제 평균: 150m
  limits:
    memory: "2Gi"      # 피크: 1.2Gi
    cpu: "1000m"       # 피크: 800m

운영 성과

시스템 안정성

  • 무중단 배포로 가용성 99.9% 달성
  • 평균 배포 시간 15분 → 3분으로 단축
  • 장애 감지 시간 30분 → 1분으로 개선

개발 생산성

  • GitOps로 배포 실수 90% 감소
  • 롤백 시간 20분 → 30초로 단축
  • 통합 모니터링으로 디버깅 시간 70% 절약

리소스 효율성

  • K3s 도입으로 시스템 오버헤드 40% 감소
  • HPA로 리소스 사용률 최적화 (평균 75%)
  • 통합 로깅으로 디스크 사용량 60% 절감

XGEN 2.0 인프라는 단순한 배포 환경을 넘어서 DevOps 문화의 기반이 되었다. 개발자는 코드에 집중하고, 운영진은 비즈니스 메트릭에 집중할 수 있는 환경을 만들어냈다. 특히 관측 가능성(Observability) 관점에서 현대적인 클라우드 네이티브 아키텍처의 모범 사례를 구현했다.

관련 글