13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] Azure Kubernetes Service

  • [k8s] AKS 워크로드 성능 벤치마크: 노드 타입별 최적화 전략

    [k8s] AKS 워크로드 성능 벤치마크: 노드 타입별 최적화 전략

    [k8s] AKS 워크로드 성능 벤치마크: 노드 타입별 최적화 전략

    AKS 성능 벤치마크를 제대로 해보면, 같은 쿠버네티스(Kubernetes, 컨테이너 오케스트레이션) 클러스터라도 노드 타입 하나 바꿨을 뿐인데 체감이 꽤 달라집니다. 저도 처음엔 “어차피 Pod(파드, 컨테이너 실행 단위)만 잘 나뉘면 되는 거 아닌가?” 싶었거든요. 근데 실제로 운영해보니까 CPU bound(연산 집약형) 워크로드, memory bound(메모리 집약형) 워크로드, burst traffic(순간 트래픽) 대응이 전부 다르게 움직이더라고요. 특히 AKS 노드 타입을 대충 고르면 비용은 비용대로 나가고, 성능은 애매한 상황이 생기는 거 있죠.

    이번 글은 특정 벤치마크 수치를 과장해서 보여주는 글이 아닙니다. 대신 제가 실무와 홈랩에서 클러스터를 굴리면서 정리한 방식대로, 어떻게 AKS 성능 벤치마크를 설계하고, 어떤 워크로드에 어떤 노드 타입을 우선 검토해야 하는지, 그리고 쿠버네티스 워크로드 최적화를 어떤 순서로 접근하면 덜 삽질하는지 정리해보겠습니다. 숫자보다 중요한 건 비교 기준이거든요.

    AKS 성능 벤치마크를 위한 노드 타입별 아키텍처 다이어그램

    AKS 클러스터에서 일반형, 컴퓨트 최적화형, 메모리 최적화형 노드를 분리하고 워크로드를 비교 측정하는 전체 구조 예시입니다.

    왜 AKS 성능 벤치마크가 중요한가

    쉽게 말해, AKS는 관리형 쿠버네티스(Managed Kubernetes)라서 제어 플레인(control plane)은 편해졌지만, 노드 선택 책임까지 사라진 건 아닙니다. 오히려 운영이 쉬워진 만큼 워크로드 특성에 맞는 인프라 설계가 더 중요해졌어요.

    예를 들어 이런 상황 있으셨을 겁니다.

    • 웹 API는 평소엔 한가한데 특정 시간대에 응답 지연(latency, 지연 시간)이 튑니다.
    • 배치 작업(batch job)은 CPU를 많이 먹는데, 일반형 노드에서 오래 끕니다.
    • 캐시나 분석용 애플리케이션은 메모리를 넉넉히 잡아야 하는데 OOMKilled가 납니다.
    • 오토스케일링(autoscaling)이 걸리긴 하는데 생각보다 늦게 반응합니다.

    이럴 때 필요한 게 감으로 고르는 인프라가 아니라, 클라우드 네이티브 벤치마크 방식으로 실제 워크로드를 분류하고 비교하는 작업입니다. 저도 예전엔 노드 스펙만 보고 판단했다가, 정작 병목은 CPU가 아니라 스토리지 I/O 혹은 스케줄링 밀집도(bin packing)였던 적이 꽤 있었어요. 정말 여러 번 삽질했습니다 ㅎㅎ

    AKS 노드 타입을 어떻게 봐야 하나

    Azure K8s 성능을 볼 때는 노드를 단순히 “비싼 게 좋다”로 보면 안 됩니다. AKS 노드 타입은 대체로 다음 관점으로 나눠서 보면 훨씬 판단이 쉬워집니다.

    분류 대표 성격 잘 맞는 워크로드 주의할 점
    General Purpose(범용형) CPU/메모리 균형 일반 웹 서비스, API, 운영도구 특정 자원 한쪽이 치우치면 효율이 애매할 수 있음
    Compute Optimized(컴퓨트 최적화형) vCPU 비중 높음 트랜스코딩, 빌드, 배치, 계산 작업 메모리 요구량이 큰 앱은 오히려 불리할 수 있음
    Memory Optimized(메모리 최적화형) 메모리 비중 높음 캐시, 인메모리 DB, JVM 계열 앱 CPU 대비 비용 효율을 따로 봐야 함
    GPU/특수형 가속기 탑재 AI/ML, 병렬 연산 일반 서비스에는 과한 경우가 많음

    실무에서는 Azure VM 계열로 풀어보면 범용형은 D 시리즈, 컴퓨트 최적화형은 F 시리즈, 메모리 최적화형은 E 시리즈를 먼저 비교하는 경우가 많습니다. 물론 세부 세대와 로컬 디스크 유무, 네트워크 성능, 가용 영역 배치까지 들어가면 이야기가 더 길어지지만, 첫 AKS 성능 벤치마크는 너무 복잡하게 시작하지 않는 게 좋습니다.

    여기서 중요한 포인트! 벤치마크의 목적은 “최고 성능 머신 찾기”가 아니라, 내 워크로드에 가장 안정적으로 맞는 조합 찾기입니다.

    벤치마크 전에 먼저 정해야 할 기준

    AKS 성능 벤치마크를 시작하기 전에 저는 보통 아래 네 가지부터 적어둡니다.

    1. 측정 대상: API 서버인지, 배치 잡인지, 스트리밍 처리인지 구분합니다.
    2. 핵심 지표: 처리량(throughput), 응답 지연(latency), 재시작 횟수, CPU throttling 여부를 봅니다.
    3. 비교 조건: 동일한 리전(region), 동일한 애플리케이션 이미지, 동일한 requests/limits로 맞춥니다.
    4. 종료 조건: 어느 지점에서 “이 노드는 부적합”이라고 판단할지 기준을 세웁니다.

    이걸 안 정하면 나중에 그래프는 많은데 결론이 없어요. 저도 처음엔 대시보드만 잔뜩 띄워놓고 뿌듯했는데, 정작 뭘 비교한 건지 흐려져서 다시 했던 기억이 납니다.

    실전 구현: AKS 벤치마크용 클러스터와 워크로드 준비

    이제 실제로 해보겠습니다. 여기서는 가장 이해하기 쉬운 방식으로, 노드 풀(node pool, 용도별 노드 그룹)을 분리하고 동일 워크로드를 각각에 붙여 비교하는 흐름으로 가보겠습니다.

    1. AKS 클러스터와 기본 노드 풀 생성

    RESOURCE_GROUP="rg-aks-bench"
    CLUSTER_NAME="aks-bench-lab"
    LOCATION="koreacentral"
    
    az group create \
      --name $RESOURCE_GROUP \
      --location $LOCATION
    
    az aks create \
      --resource-group $RESOURCE_GROUP \
      --name $CLUSTER_NAME \
      --node-count 1 \
      --enable-managed-identity \
      --generate-ssh-keys
    
    az aks get-credentials \
      --resource-group $RESOURCE_GROUP \
      --name $CLUSTER_NAME

    처음부터 노드를 많이 올리기보다, 기본 클러스터를 만들고 나중에 목적별 node pool을 추가하는 게 관리가 편합니다.

    2. 용도별 AKS 노드 타입 추가

    az aks nodepool add \
      --resource-group $RESOURCE_GROUP \
      --cluster-name $CLUSTER_NAME \
      --name gp \
      --node-count 1 \
      --node-vm-size Standard_D4s_v5
    
    az aks nodepool add \
      --resource-group $RESOURCE_GROUP \
      --cluster-name $CLUSTER_NAME \
      --name cpu \
      --node-count 1 \
      --node-vm-size Standard_F4s_v2
    
    az aks nodepool add \
      --resource-group $RESOURCE_GROUP \
      --cluster-name $CLUSTER_NAME \
      --name mem \
      --node-count 1 \
      --node-vm-size Standard_E4s_v5

    예시는 범용형, 컴퓨트 최적화형, 메모리 최적화형을 나눈 것입니다. 실제 운영에서는 가용성, 예산, 리전 지원 여부를 같이 봐야 합니다.

    AKS 노드 타입에 따라 워크로드를 분리 배치하는 구성도

    서로 다른 AKS 노드 풀에 동일한 애플리케이션을 배치하고, nodeSelector로 대상을 분리하는 흐름을 보여주는 구성도입니다.

    3. 테스트용 애플리케이션 배포

    여기서는 가장 단순한 웹 애플리케이션 예시로 갑니다. 핵심은 애플리케이션 자체가 아니라 동일 조건으로 여러 노드 타입에 붙여 비교하는 것입니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: bench-web-gp
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: bench-web-gp
      template:
        metadata:
          labels:
            app: bench-web-gp
        spec:
          nodeSelector:
            agentpool: gp
          containers:
          - name: web
            image: nginx:stable
            resources:
              requests:
                cpu: "250m"
                memory: "256Mi"
              limits:
                cpu: "500m"
                memory: "512Mi"
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: bench-web-gp
    spec:
      selector:
        app: bench-web-gp
      ports:
      - port: 80
        targetPort: 80
      type: ClusterIP

    이 YAML은 `gp`, `cpu`, `mem` 용으로 이름과 `nodeSelector`만 바꿔서 각각 하나씩 배포하면 됩니다. 실제로 해보니까 여기서 가장 많이 틀리는 게 라벨명이더라고요. AKS 노드 풀 라벨은 환경에 따라 꼭 확인해보는 게 안전합니다.

    4. 리소스 사용량과 이벤트 확인

    kubectl get nodes --show-labels
    kubectl get pods -o wide
    kubectl top nodes
    kubectl top pods
    kubectl describe pod <pod-name>

    이 단계에서 확인할 건 세 가지입니다.

    • Pod가 의도한 node pool에 올라갔는지
    • CPU throttling 징후가 있는지
    • 메모리 압박이나 재시작이 있는지

    5. 부하 테스트 실행

    HTTP 워크로드라면 외부 부하 도구를 붙여도 되고, 간단히 클러스터 내부에서 반복 요청을 보내도 됩니다.

    kubectl run loadtest --rm -it \
      --image=busybox \
      --restart=Never -- /bin/sh
    
    while true; do
      wget -q -O- http://bench-web-gp; 
      sleep 0.2;
    done

    물론 이건 아주 가벼운 예시입니다. 실제 AKS 성능 벤치마크라면 동시성(concurrency), 지속 시간(duration), 오류율(error rate)을 더 정교하게 잡아야 합니다. 그래도 첫 비교는 이렇게 작게 시작하는 게 좋습니다. 처음부터 너무 큰 도구를 붙이면 원인 분석이 더 어려워지거든요.

    쿠버네티스 워크로드 최적화에서 놓치기 쉬운 포인트

    노드 타입만 바꾸면 끝일 것 같지만, 실은 워크로드 정의가 더 중요할 때가 많습니다.

    체크 항목 왜 중요한가 실무 팁
    requests/limits 스케줄링과 throttling에 직접 영향 너무 보수적으로 잡으면 밀집도가 떨어집니다
    HPA 트래픽 대응 자동화 CPU만 보지 말고 메모리나 커스텀 메트릭도 검토합니다
    Pod 분산 장애 도메인 분리 anti-affinity와 topology spread를 함께 봅니다
    스토리지 특성 상태 저장 앱의 체감 성능 좌우 디스크 병목이면 노드 변경 효과가 작을 수 있습니다

    예를 들어 CPU가 부족한 줄 알고 컴퓨트 최적화 노드로 옮겼는데, 알고 보니 컨테이너 `limits`가 너무 타이트해서 throttling만 심했던 경우도 있습니다. 저도 이거 한 번 당하고 나서는, 무조건 애플리케이션 정의와 노드 타입을 같이 봅니다.

    ⚠️ 실제로 겪었던 트러블슈팅

    여기서는 제가 자주 봤던 문제를 정리해볼게요.

    1. Pod가 원하는 노드에 안 올라가는 문제

    대부분 `nodeSelector`, `taint/toleration`, 리소스 부족 셋 중 하나입니다.

    kubectl describe pod <pod-name>

    `Events`를 보면 대개 이유가 나옵니다. 처음엔 저도 스케줄러가 이상한 줄 알았는데, 실제로는 라벨 오타가 제일 흔했어요.

    2. 벤치마크 결과가 들쭉날쭉한 문제

    이건 생각보다 흔합니다. 원인은 보통 아래 쪽입니다.

    • 테스트 시간대가 다름
    • 이미지 풀(image pull) 시간이 섞임
    • 오토스케일링 반응 시간이 포함됨
    • 클러스터 내부의 다른 작업이 간섭함

    그래서 저는 AKS 성능 벤치마크할 때 최소한 워밍업 구간과 실측 구간을 분리합니다. 이거 안 하면 첫 요청 지연이 평균값을 망치더라고요.

    3. 메모리 최적화 노드인데 성능 향상이 애매한 문제

    메모리를 많이 준다고 모든 앱이 빨라지진 않습니다. 애플리케이션이 실제로 heap(힙), cache(캐시), page cache를 활용하는 구조인지 봐야 합니다. 단순한 stateless API라면 메모리보다 CPU 클럭이나 네트워크 특성이 더 체감될 때가 많습니다.

    4. 비용은 늘었는데 개선 폭이 작은 문제

    이건 가장 뼈아픈 케이스죠. 그래서 AKS 성능 벤치마크 결과는 반드시 성능/비용 관점으로 같이 봐야 합니다. 절대 성능만 보고 결정하면 운영비가 금방 불어나니까요.

    AKS 성능 벤치마크 결과를 시각화한 대시보드 이미지

    CPU 사용률, 메모리 사용량, Pod 재시작, 응답 지연을 한 화면에서 비교하는 벤치마크 대시보드 예시입니다.

    검증: 어떤 결과를 보면 의미 있는가

    결과 검증은 단순 평균보다 분포를 보는 게 좋습니다. 특히 웹 서비스는 p95 latency(p95 지연 시간), 오류율, 재시작 여부가 중요합니다.

    1. 범용형 노드에서 안정적이지만 피크 구간에서 지연이 튀는지 봅니다.
    2. 컴퓨트 최적화형에서 CPU 사용률은 낮아졌는지, 처리량은 늘었는지 봅니다.
    3. 메모리 최적화형에서 OOMKilled가 줄고 캐시 적중 효과가 있는지 확인합니다.
    4. 같은 성능을 더 적은 노드 수로 낼 수 있는지도 함께 봅니다.

    제가 직접 해보니, 정답이 하나로 고정되기보다 워크로드 특성에 따라 꽤 명확하게 갈리더라고요.

    • 일반 웹/API는 범용형에서 시작해도 충분한 경우가 많습니다.
    • 빌드, 계산, 배치성 작업은 컴퓨트 최적화형이 먼저 후보가 됩니다.
    • 캐시, JVM, 데이터 집약형 앱은 메모리 최적화형 검토 우선순위가 올라갑니다.

    결론적으로 Azure K8s 성능은 노드 스펙 하나가 아니라, 워크로드 패턴과 리소스 설정, 스케일 전략의 합으로 결정됩니다.

    정리: AKS 노드 타입 선택 전략

    마지막으로 실무에서 바로 써먹기 좋은 방식으로 정리해보겠습니다.

    1. 기준 워크로드를 하나 정합니다. 가장 중요한 서비스부터입니다.
    2. 범용형 node pool을 기준선으로 삼습니다.
    3. 같은 앱을 컴퓨트 최적화형, 메모리 최적화형에 각각 배치합니다.
    4. `requests/limits`, HPA, 분산 정책을 동일하게 맞춥니다.
    5. 처리량, p95 지연, 재시작, 비용 관점을 같이 봅니다.
    6. 결과가 좋으면 그때 운영 표준으로 가져갑니다.

    이 순서대로 하면 적어도 “왜 느린지 모르겠는데 일단 큰 VM으로 바꾸자” 같은 결정을 줄일 수 있습니다. 사실 저도 예전엔 그렇게 했었는데요, 나중에 보면 대부분 비효율이었어요.

    혹시 지금 AKS에서 특정 워크로드가 계속 애매하게 느리다면, 이번엔 감으로 바꾸지 말고 AKS 성능 벤치마크 기준부터 잡아보세요. 그 다음에야 AKS 노드 타입 선택이 훨씬 쉬워집니다. 다음 글에서는 Cluster Autoscaler(클러스터 오토스케일러)와 HPA를 같이 묶어서, 실제 트래픽 변동 환경에서 어떻게 튜닝하는지 이어서 다뤄보겠습니다. 이전 글에서 다뤘던 Ingress(인그레스, 외부 트래픽 진입점) 최적화 내용과 연결해서 보셔도 흐름이 잘 이어질 겁니다.

    AKS 노드 타입 선택 기준과 최적화 전략 요약 인포그래픽

    범용형, 컴퓨트 최적화형, 메모리 최적화형 노드의 추천 워크로드와 판단 기준을 한눈에 요약한 인포그래픽입니다.

    FAQ: 자주 묻는 질문

    AKS 성능 벤치마크는 운영 환경에서 바로 해야 하나요?

    가능하면 운영과 유사한 별도 환경에서 먼저 하시는 걸 권합니다. 운영에서 바로 하면 다른 변수 때문에 결과 해석이 어려워져요.

    노드 타입만 바꾸면 성능 문제가 해결되나요?

    아닙니다. `requests/limits`, 애플리케이션 구조, 스토리지, 네트워크까지 같이 봐야 합니다.

    처음 시작할 때 가장 무난한 방법은 뭔가요?

    범용형을 기준선으로 잡고, CPU 중심 워크로드와 메모리 중심 워크로드를 분리해 비교해보는 게 가장 안전합니다.

  • [k8s] AKS에서 Vault on K8s 구축: 보안 강화 및 민감 정보 관리 방안

    [k8s] AKS에서 Vault on K8s 구축: 보안 강화 및 민감 정보 관리 방안

    [k8s] AKS에서 Vault on K8s 구축: 보안 강화 및 민감 정보 관리 방안

    AKS(Azure Kubernetes Service, 애저 쿠버네티스 서비스)를 운영하다 보면 결국 부딪히는 문제가 하나 있습니다. 바로 시크릿 관리예요. 처음에는 Kubernetes Secret(쿠버네티스 시크릿)만으로도 충분하다고 느끼실 수 있는데, 저도 처음엔 그랬거든요. 그런데 운영 환경이 커지고, 애플리케이션 수가 늘고, 누가 어떤 자격 증명(credential)을 어디서 쓰는지 추적해야 하는 순간이 오면 상황이 달라져요. 그때부터는 “이걸 계속 이렇게 들고 가도 될까?” 싶은 생각이 드더라고요.

    그래서 오늘은 Vault on K8s AKS 구성을 어떻게 접근하면 좋을지, 제가 실제로 비슷한 구조를 설계하고 검증할 때 중요하게 봤던 포인트를 중심으로 정리해보려 해요. 핵심은 단순히 HashiCorp Vault(해시코프 볼트)를 올리는 게 아니라, AKS 환경에서 인증(auth), 저장(storage), 정책(policy), 주입(injection) 흐름을 어떻게 안전하게 묶을지하는 거거든요. 특히 민감 정보가 Git, 환경 변수, 컨테이너 이미지 안에 흩어지기 시작한 팀이라면 한 번쯤 진지하게 볼 만한 주제입니다.

    AKS 클러스터, Vault 서버, 애플리케이션 Pod, Kubernetes Auth 흐름을 한눈에 보여주는 전체 구조 이미지입니다.

    1. 왜 AKS에서 Vault on K8s가 필요할까요

    쉽게 말해, Vault는 시크릿을 중앙에서 관리하고 접근을 통제하는 금고 역할을 하는 거예요. Kubernetes Secret도 이름은 시크릿이지만, 그 자체만으로는 운영 통제 수준이 부족하다고 느끼는 경우가 많아요. Base64 인코딩만으로는 보안이 해결되지 않거든요. 물론 etcd 암호화(encryption at rest) 같은 보완책이 있지만, 접근 통제와 감사(audit), 동적 자격 증명(dynamic credentials)까지 생각하면 Vault가 주는 이점이 분명합니다.

    • 중앙 집중형 관리: DB 비밀번호, API 토큰, 인증서 등을 한곳에서 관리할 수 있어요.
    • 정책 기반 접근 제어: 누가 어떤 경로(path)의 시크릿을 읽을 수 있는지 세밀하게 나눌 수 있습니다.
    • 감사 추적: 접근 기록을 남겨서 운영 가시성을 확보하기 좋아요.
    • 동적 시크릿 확장 가능성: 정적 비밀번호를 오래 들고 가지 않는 구조로 발전시키기 쉬워요.

    여기서 중요한 포인트! AKS에서 Vault를 쓴다고 해서 자동으로 다 안전해지는 건 아니에요. 어디에 저장할지, 어떻게 인증할지, 어떤 네트워크 경계 안에 둘지를 같이 설계해야 의미가 있어요. 저도 처음엔 “Vault만 올리면 끝 아닌가?” 싶었는데, 실제로 해보니까 그다음이 진짜 시작이더라고요.

    2. Vault, AKS, Kubernetes Auth를 쉽게 풀어보면

    개념을 너무 어렵게 잡으면 오히려 구현이 꼬여요. 쉽게 말하면 흐름은 이렇습니다.

    1. 애플리케이션 Pod가 AKS 안에서 실행돼요.
    2. Pod는 자신의 ServiceAccount(서비스 어카운트)를 이용해 Vault에 신원을 증명합니다.
    3. Vault는 Kubernetes Auth(쿠버네티스 인증)로 이 Pod가 누구인지 확인해요.
    4. 정책에 맞으면 필요한 시크릿을 내려줍니다.

    즉, 애플리케이션 입장에서는 “내가 누구인지 증명하고, 허용된 비밀만 받아간다”는 구조예요. 이 방식의 장점은 시크릿을 애플리케이션 매니페스트에 하드코딩하지 않아도 된다는 거거든요. 운영해보신 분들은 아실 텐데, Secret YAML 파일이 여기저기 복사되기 시작하면 관리가 정말 힘들어져요.

    항목 Kubernetes Secret Vault on K8s
    저장 위치 클러스터 내부 Vault 백엔드
    접근 제어 RBAC 중심 정책 기반 세분화 가능
    감사 추적 제한적 Audit Device(감사 장치) 활용 가능
    주입 방식 환경 변수, 볼륨 Agent Injector, CSI 등 확장 가능
    운영 복잡도 낮음 상대적으로 높음

    결국 선택의 기준은 단순해요. 작고 단순한 환경이면 Kubernetes Secret만으로도 충분합니다. 반대로 권한 분리, 감사, 로테이션, 멀티 서비스 운영이 중요해지면 Vault on K8s AKS 구성이 훨씬 낫습니다.

    3. AKS에서 Vault on K8s 아키텍처를 어떻게 잡으면 좋을까

    제가 추천하는 기본 방향은 이래요. 과하게 복잡하게 시작하지 말고, 운영상 필요한 최소 안전장치를 먼저 넣는 거예요.

    • Vault는 전용 네임스페이스에 배치합니다.
    • Integrated Storage Raft(통합 스토리지 래프트)를 사용해 내부 저장소를 구성해요.
    • Kubernetes Auth로 애플리케이션 인증을 처리합니다.
    • Vault Agent Injector 또는 템플릿 렌더링 방식으로 시크릿을 Pod에 주입해요.
    • NetworkPolicy(네트워크 정책)와 Ingress(인그레스, 외부 트래픽 진입점) 또는 내부 LoadBalancer 정책을 명확히 나누어요.

    실무에서는 Vault를 외부에 완전히 공개하지 않는 편이 훨씬 나아요. 가능하면 내부 네트워크에서만 접근하게 만들고, 운영자 접근도 제한하는 쪽이 좋거든요. 홈랩에서도 이 부분을 느슨하게 두면 결국 테스트 편의성이 보안 기준을 이겨버리더라고요. 처음엔 편한데 나중에 반드시 정리해야 해요.

    AKS에서 Vault 네임스페이스와 컴포넌트 배치 구조 이미지

    Vault 전용 네임스페이스, Raft 스토리지, Agent Injector, 애플리케이션 네임스페이스 분리를 보여주는 구성 이미지입니다.

    4. 실전 구현: AKS에 Vault 배포하기

    이제 실제 흐름으로 가봅시다. 여기서는 Helm(헬름)을 이용해 Vault를 AKS에 배포하는 예시를 기준으로 설명할게요. 세부 값은 환경마다 다르니, 그대로 복붙보다 구조를 이해하고 적용하시는 게 훨씬 중요합니다.

    4-1. 네임스페이스와 Helm 저장소 준비

    kubectl create namespace vault
    helm repo add hashicorp https://helm.releases.hashicorp.com
    helm repo update

    여기까지는 크게 어렵지 않아요. 문제는 그다음 values 설정이에요. 처음엔 기본값으로 올리고 싶어지는데, 운영 목적이라면 최소한 스토리지와 UI, Injector 여부는 같이 잡아주는 게 좋습니다.

    4-2. values.yaml 예시

    server:
      enabled: true
      ha:
        enabled: true
        replicas: 3
        raft:
          enabled: true
      dataStorage:
        enabled: true
        size: 10Gi
      service:
        enabled: true
        type: ClusterIP
      ingress:
        enabled: false
      resources: {}
    
    ui:
      enabled: true
    
    injector:
      enabled: true

    여기서는 HA(고가용성)와 Raft storage를 켰어요. Vault on K8s는 단일 인스턴스로도 테스트는 되지만, 실제 운영 관점에서는 장애 대응을 생각하지 않을 수가 없거든요. 물론 노드 수, 디스크 크기, 서비스 타입은 환경에 맞춰 조정해야 합니다.

    4-3. 배포

    helm install vault hashicorp/vault \
      --namespace vault \
      --values values.yaml

    배포 후에는 Pod 상태부터 확인하세요.

    kubectl get pods -n vault
    kubectl get svc -n vault

    이 시점에서 Pod가 뜬다고 끝이 아니에요. Vault는 초기화(initialization)와 언실(unseal) 과정을 거쳐야 하거든요. 이 부분에서 처음 삽질 많이 합니다 ㅎㅎ

    4-4. 초기화와 언실

    kubectl exec -it -n vault vault-0 -- vault operator init

    이 명령을 실행하면 unseal key와 initial root token이 출력돼요. 이 정보는 정말 민감하니까요, 테스트 환경이라고 터미널 히스토리에 남기고 방치하면 나중에 곤란해져요. 저도 예전에 PoC 환경이라고 가볍게 봤다가 정리하느라 번거로웠던 적이 있어요.

    이후 각 Pod에 대해 언실을 진행합니다.

    kubectl exec -it -n vault vault-0 -- vault operator unseal
    kubectl exec -it -n vault vault-1 -- vault operator unseal
    kubectl exec -it -n vault vault-2 -- vault operator unseal

    로그인도 해두세요.

    kubectl exec -it -n vault vault-0 -- vault login

    5. Kubernetes Auth와 시크릿 엔진 구성

    이제부터가 진짜 핵심이에요. Vault 서버를 띄우는 건 절반이고, AKS 워크로드가 안전하게 시크릿을 받아가게 만드는 과정이 나머지 절반입니다.

    5-1. KV 시크릿 엔진 활성화

    kubectl exec -it -n vault vault-0 -- vault secrets enable -path=secret kv-v2
    kubectl exec -it -n vault vault-0 -- vault kv put secret/app/config username="demo-user" password="demo-pass"

    여기서는 예시로 KV v2(Key-Value Version 2) 엔진을 사용했어요. 정적 시크릿 관리의 기본 출발점으로 가장 무난하거든요.

    5-2. Kubernetes Auth 활성화

    kubectl exec -it -n vault vault-0 -- vault auth enable kubernetes

    그다음 Kubernetes API와 통신할 수 있도록 설정해야 합니다. 실제 값은 클러스터 환경에 맞게 확인이 필요해요.

    kubectl exec -it -n vault vault-0 -- sh
    
    vault write auth/kubernetes/config \
      kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

    환경에 따라 서비스 계정 토큰과 CA 인증서 경로까지 함께 지정하는 구성이 필요할 수 있어요. 이 부분은 클러스터 설정과 배포 방식에 따라 달라지니, 반드시 현재 환경 기준으로 검증해야 합니다.

    5-3. 정책 생성

    kubectl exec -it -n vault vault-0 -- sh -c 'cat > /tmp/app-policy.hcl <<EOF
    path "secret/data/app/config" {
      capabilities = ["read"]
    }
    EOF
    vault policy write app-policy /tmp/app-policy.hcl'

    정책은 정말 최소 권한(least privilege)으로 가야 해요. “일단 다 읽게 해두고 나중에 줄이자”는 접근이 제일 위험하거든요. 운영에 들어가면 나중은 잘 안 와요.

    5-4. Role 생성

    kubectl exec -it -n vault vault-0 -- vault write auth/kubernetes/role/app-role \
      bound_service_account_names=app-sa \
      bound_service_account_namespaces=default \
      policies=app-policy \
      ttl=1h

    이렇게 하면 default 네임스페이스의 app-sa 서비스 계정을 사용하는 Pod만 해당 정책으로 인증받을 수 있어요. namespace와 service account를 분리해두면 실수 범위를 줄이기 좋거든요.

    Vault on K8s AKS의 Kubernetes Auth와 정책 매핑 흐름

    ServiceAccount, Vault Role, Policy, Secret Path가 어떻게 연결되는지 보여주는 인증 및 권한 흐름 이미지입니다.

    6. 애플리케이션 Pod에 시크릿 주입하기

    실제로 써보니까 운영팀과 개발팀 모두 만족도가 올라가는 구간이 바로 여기였어요. 개발자는 애플리케이션에서 민감 정보를 직접 들고 다니지 않아도 되고, 운영자는 중앙 통제가 가능해지거든요.

    Vault Agent Injector를 사용하는 예시는 아래처럼 가져갈 수 있습니다.

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: app-sa
      namespace: default
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demo-app
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: demo-app
      template:
        metadata:
          labels:
            app: demo-app
          annotations:
            vault.hashicorp.com/agent-inject: "true"
            vault.hashicorp.com/role: "app-role"
            vault.hashicorp.com/agent-inject-secret-config.txt: "secret/data/app/config"
            vault.hashicorp.com/agent-inject-template-config.txt: |
              {{- with secret "secret/data/app/config" -}}
              username={{ .Data.data.username }}
              password={{ .Data.data.password }}
              {{- end }}
        spec:
          serviceAccountName: app-sa
          containers:
            - name: app
              image: nginx
              volumeMounts: []

    이 예시는 Pod 안에 파일 형태로 시크릿을 렌더링하는 방식이에요. 환경 변수 주입만 고집하기보다, 파일 기반 주입을 먼저 고려해보세요. 이유는 단순해요. 환경 변수는 프로세스 정보나 디버깅 과정에서 노출 범위를 관리하기 까다로울 수 있거든요.

    배포 후에는 Pod 내부에서 실제로 파일이 생성됐는지 확인하세요.

    kubectl apply -f app.yaml
    kubectl get pods
    kubectl exec -it deploy/demo-app -- cat /vault/secrets/config.txt

    7. ⚠️ 운영 중 자주 만나는 문제와 트러블슈팅

    여기서는 제가 꼭 강조하고 싶은 실전 포인트를 적어볼게요. 문서대로만 하면 바로 될 것 같지만, 실제 AKS에서 붙일 때 자주 막히는 지점이 몇 군데 있어요.

    7-1. Pod는 떴는데 시크릿 파일이 안 생기는 경우

    • ServiceAccount 이름과 Vault Role의 bound_service_account_names가 일치하는지 확인하세요.
    • 네임스페이스가 Role 설정과 같은지 봐요.
    • Pod annotation 오타가 없는지 확인해요.
    • Injector Pod 로그를 확인합니다.
    kubectl logs -n vault deploy/vault-agent-injector

    이거 진짜 자주 나와요. 저도 처음엔 Vault 정책 문제인 줄 알았는데, 알고 보니 annotation 키 오타였던 적이 있었어요. 허무하죠. 근데 이런 게 제일 오래 갑니다.

    7-2. Vault 로그인 또는 Auth 설정이 실패하는 경우

    • Kubernetes API 접근 경로가 맞는지 봐요.
    • Vault 서버가 클러스터 내부 DNS와 API 서버에 접근 가능한지 확인하세요.
    • 서비스 계정 토큰과 CA 인증서 전달이 필요한 구성인지 점검해요.

    AKS는 관리형 Kubernetes라서 제어 플레인 세부 구현을 직접 만지지는 않지만, 그렇다고 인증 흐름이 자동으로 다 맞아떨어지지는 않아요. 특히 네트워크 제한이나 정책이 들어가면 더 꼼꼼히 봐야 합니다.

    7-3. Vault 자체의 고가용성은 올렸는데 운영 절차가 없는 경우

    이 부분도 많이 놓쳐요. HA로 배포했다고 해서 운영이 끝난 게 아니거든요.

    • 언실 절차를 누가 어떻게 수행할지
    • 루트 토큰 보관 정책을 어떻게 할지
    • 백업과 복구 테스트를 할지
    • 감사 로그 보존을 어디까지 할지

    보안 시스템은 설치보다 운영 절차가 훨씬 중요해요. 이건 정말 해보면 바로 체감됩니다.

    8. 검증 방법과 기대 결과

    구축이 끝났다면 그냥 “접속된다” 수준에서 멈추면 아쉬워요. 최소한 아래 항목은 꼭 검증해보세요.

    1. 권한이 있는 Pod만 시크릿을 읽을 수 있는지 확인하세요.
    2. 다른 ServiceAccount를 쓰는 Pod는 접근이 거부되는지 확인합니다.
    3. Vault 정책 변경 시 반영 범위를 확인해요.
    4. Pod 재기동 시 시크릿 주입이 일관되게 되는지 확인하세요.
    5. 감사 로그와 서버 로그에서 접근 기록을 추적할 수 있는지 봐요.

    예를 들어 권한 없는 Pod를 하나 띄워서 동일 경로를 읽어보는 식으로 검증할 수 있어요. 이런 부정 테스트(negative test)를 같이 해봐야 진짜 구성이 맞는지 보여요.

    kubectl run test-deny --rm -it --image=busybox --restart=Never -- sh

    운영 결과 관점에서 기대할 수 있는 건 명확합니다.

    • 애플리케이션 매니페스트에 민감 정보가 직접 들어가지 않아요.
    • 시크릿 접근 경로가 정책으로 정리돼요.
    • 누가 무엇을 읽는지 추적성이 좋아져요.
    • 향후 동적 시크릿이나 인증서 자동화로 확장하기 쉬워져요.

    특히 팀 규모가 커질수록 이점이 커져요. 처음엔 조금 번거롭지만, 나중에는 “왜 더 빨리 안 했지?” 싶을 때가 와요. 실제로 써보니까 시크릿이 여기저기 흩어져 있던 상태보다 운영 피로도가 훨씬 낮아지더라고요.

    AKS에서 Vault 시크릿 주입 검증 결과 이미지

    애플리케이션 Pod 내부 시크릿 파일 생성, 정책 기반 접근 허용/거부 검증 결과를 보여주는 이미지입니다.

    9. 정리와 다음 단계

    Vault on K8s AKS 구성의 핵심은 단순해요. Vault를 설치하는 것이 목적이 아니라, AKS에서 민감 정보를 안전하게 배포하고 통제하는 체계를 만드는 게 진짜 목표거든요. 처음엔 Kubernetes Secret만으로 충분해 보일 수 있어요. 저도 그렇게 시작했었는데, 서비스가 늘고 권한 관리가 복잡해지니까 한계가 분명히 보이더라고요.

    오늘 정리한 흐름을 다시 압축하면 이렇습니다.

    • Vault를 AKS에 전용 네임스페이스로 배치해요.
    • Raft 기반 저장소와 HA 구성을 검토해요.
    • Kubernetes Auth로 Pod 신원을 검증합니다.
    • 정책 기반으로 필요한 시크릿만 주입해요.
    • 운영 절차와 감사 체계를 같이 설계하세요.

    혹시 지금 HashiCorp Vault 도입을 고민 중이세요? 그렇다면 처음부터 모든 기능을 한 번에 붙이기보다, KV 시크릿 엔진 + Kubernetes Auth + 최소 정책부터 시작해보는 걸 추천해요. 그게 훨씬 덜 꼬여요. 드디어 됐다! 싶은 순간이 분명 옵니다.

    다음 글에서는 AKS에서 Vault와 외부 비밀 저장소를 어떻게 연계해서 운영 부담을 줄일지, 또는 Vault Agent Injector와 CSI 방식의 차이를 어떻게 판단하면 좋을지 이어서 다뤄볼 예정이에요. 이전 글에서 Kubernetes 기본 보안 설정을 정리했다면 그 내용과 함께 보셔도 흐름이 잘 맞을 거예요.

    FAQ: 자주 묻는 질문

    AKS에서 꼭 Vault가 필요할까요?

    작고 단순한 환경이라면 꼭 그렇지는 않아요. 다만 시크릿 관리 범위가 넓어지고 감사와 권한 분리가 중요해지면 검토 가치가 크답니다.

    Vault on K8s는 운영 난도가 높지 않나요?

    맞습니다. Kubernetes Secret보다 복잡해요. 대신 통제력과 확장성이 좋아져요. 그래서 작은 범위로 시작하는 게 중요합니다.

    시크릿은 환경 변수보다 파일 주입이 더 나은가요?

    상황에 따라 다르지만, 운영 노출 범위를 생각하면 파일 기반 주입이 더 다루기 편한 경우가 많아요.

    Vault on K8s AKS 도입 전후 비교 인포그래픽

    도입 전 분산된 시크릿 관리와 도입 후 중앙 통제 구조를 비교하는 요약 인포그래픽입니다.