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) 관점에서 현대적인 클라우드 네이티브 아키텍처의 모범 사례를 구현했다.¶
관련 글
- K3s 위에 AI 플랫폼 올리기: 인프라 설계부터 배포까지
ArgoCDDevOpsDocker Compose - ArgoCD 멀티 고객사 배포 아키텍처 — ApplicationSet 시행착오와 단일 진입점 설계
App of AppsApplicationSetArgoCD - XGEN K3s 인프라 완전 해부 (5) — Istio 서비스 메시와 Observability 스택
DevOpsGrafanaIstio - XGEN K3s 인프라 완전 해부 (4) — CI/CD 파이프라인: Jenkins 빌드에서 ArgoCD 배포까지
App of AppsArgoCDCI/CD - XGEN AWS EKS 신규 고객사 배포기 — 온프레미스에서 클라우드로
AWSArgoCDEKS