클라우드 엔지니어

Kubernetes에서 HashiCorp Vault 구축시 마주친 문제들과 해결법

해아's 2025. 9. 6. 19:03

Kubernetes에서 HashiCorp Vault 구축시 마주친 문제들과 해결법

들어가며

이론적으로는 완벽해 보이는 HashiCorp Vault 구축이지만, 실제 Kubernetes 환경에서 구현할 때는 예상치 못한 다양한 문제들을 마주치게 됩니다. 본 글에서는 Vault를 GitOps 방식으로 구축하면서 실제로 경험했던 문제들과 그 해결 과정을 상세히 공유합니다.

문제 1: Container 권한 및 파일 시스템 문제

증상

chown: /vault/data: Operation not permitted
su-exec: setgroups(1000): Operation not permitted

원인 분석

초기에 Vault 공식 문서를 따라 vault 사용자로 컨테이너를 실행했지만, Kubernetes의 보안 컨텍스트와 충돌이 발생했습니다.

# 문제가 있던 초기 설정
securityContext:
  runAsUser: 1000
  runAsGroup: 1000
command:
- "/bin/sh"
- "-ec"
- |
  chown -R vault:vault /vault/data
  su-exec vault:vault vault server -config=/vault/config/vault.hcl

해결 방법

Root 권한으로 실행하도록 보안 컨텍스트를 변경하고 명령어를 단순화했습니다.

# 해결된 설정
securityContext:
  runAsUser: 0
  runAsGroup: 0
  fsGroup: 0
command:
- "/bin/sh"
- "-ec"
- |
  # 권한 변경 없이 단순하게 시작
  cp /vault/config/vault.hcl /tmp/vault.hcl
  vault server -config=/tmp/vault.hcl

교훈

  • Kubernetes 환경에서는 컨테이너 보안 컨텍스트를 신중하게 설정해야 함
  • 복잡한 권한 변경보다는 단순한 접근이 더 효과적일 수 있음

문제 2: Pod 재시작으로 인한 Sealed 상태

증상

Error making API request.
Code: 503. Errors:
* Vault is sealed

원인 분석

Vault는 기본적으로 보안을 위해 시작할 때마다 sealed 상태가 됩니다. Pod가 재시작되면 매번 수동으로 unseal해야 하는 불편함이 있었습니다.

1차 해결: 수동 Unseal 프로세스 정립

export VAULT_ADDR="http://127.0.0.1:8200"
vault operator unseal <key1>
vault operator unseal <key2>  
vault operator unseal <key3>

2차 해결: Auto-Unseal 구현

Init Container를 사용해 자동 unseal 기능을 구현했습니다.

# auto-unseal ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: vault-auto-unseal
  namespace: vault-system
data:
  auto-unseal.sh: |
    #!/bin/sh

    export VAULT_ADDR="http://127.0.0.1:8200"

    # Vault가 시작될 때까지 대기
    until curl -s http://127.0.0.1:8200/v1/sys/health > /dev/null; do
        echo "Vault 서버 대기 중..."
        sleep 2
    done

    # Vault 상태 확인 후 unseal
    VAULT_STATUS=$(curl -s http://127.0.0.1:8200/v1/sys/seal-status | grep -o '"sealed":[^,]*' | cut -d':' -f2)

    if [ "$VAULT_STATUS" = "true" ]; then
        echo "Auto-unseal 시작..."
        vault operator unseal "$UNSEAL_KEY_1"
        vault operator unseal "$UNSEAL_KEY_2"
        vault operator unseal "$UNSEAL_KEY_3"
        echo "Auto-unseal 완료!"
    fi
# Deployment에 Init Container 추가
initContainers:
- name: vault-auto-unseal
  image: hashicorp/vault:1.15.2
  env:
  - name: VAULT_ADDR
    value: "http://127.0.0.1:8200"
  - name: UNSEAL_KEY_1
    valueFrom:
      secretKeyRef:
        name: vault-unseal-keys
        key: unseal-key-1
  # ... 추가 키들
  command:
  - /bin/sh
  - /scripts/auto-unseal.sh

교훈

  • 운영 환경에서는 자동화가 필수
  • Init Container는 Pod 초기화 작업에 매우 유용
  • 보안과 편의성의 균형점을 찾는 것이 중요

문제 3: Root Token 만료 및 권한 문제

증상

Error making API request.
Code: 403. Errors:
* permission denied

원인 분석

Pod 재시작 과정에서 저장해둔 root token이 만료되어 Kubernetes 인증 설정에 실패했습니다.

시행착오

  1. Generate Root Token 버튼 찾기: Vault UI에서 해당 기능을 찾을 수 없었음
  2. CLI 방식 시도: 복잡한 nonce/OTP 과정이 필요했음

해결 방법

가장 간단한 방법은 처음 초기화할 때 받은 Initial Root Token을 재사용하는 것이었습니다.

# 처음 vault operator init 실행 시 출력
Initial Root Token: <root-token>

ConfigMap과 Secret을 새로운 토큰으로 업데이트:

# root-token-secret.yaml 업데이트
stringData:
  root-token: "<root-token>"

교훈

  • 초기 설정 정보를 안전하게 보관하는 것이 중요
  • 복잡한 해결책보다 단순한 방법이 더 효과적일 수 있음

문제 4: kubectl 접근 제한 환경에서의 디버깅

증상

Unable to connect to the server: dial tcp 127.0.0.1:6443: connectex: No connection could be made because the target machine actively refused it.

원인 분석

GitOps 환경에서는 보안상 kubectl 직접 접근이 제한되어 있어 디버깅이 어려웠습니다.

해결 전략

  1. ArgoCD UI 활용: 리소스 상태 확인
  2. Vault UI 접속: NodePort를 통한 직접 접근
  3. Pod 내부 명령어: exec 대신 ConfigMap 스크립트 활용
# 디버깅용 스크립트를 ConfigMap으로 제공
apiVersion: v1
kind: ConfigMap
metadata:
  name: debug-scripts
data:
  check-status.sh: |
    #!/bin/sh
    export VAULT_ADDR="http://127.0.0.1:8200"
    echo "=== Vault Status ==="
    vault status
    echo "=== Auth Methods ==="
    vault auth list
    echo "=== Secret Engines ==="
    vault secrets list

교훈

  • GitOps 환경에서는 디버깅 전략을 미리 계획해야 함
  • UI 도구와 스크립트를 적절히 조합해 사용
  • 관찰 가능성(Observability)을 높이는 것이 중요

문제 5: Readiness Probe 실패와 서비스 불안정

증상

Readiness probe failed: Get "http://192.168.1.12:8200/v1/sys/health?standbyok=true&unsealed=true": dial tcp 192.168.1.12:8200: connect: connection refused

원인 분석

Vault가 sealed 상태일 때 readiness probe가 실패하여 서비스가 불안정해졌습니다.

해결 방법

Readiness probe 설정을 조정하여 unsealing 과정을 고려했습니다.

readinessProbe:
  httpGet:
    path: /v1/sys/health?standbyok=true&unsealed=true
    port: 8200
  initialDelaySeconds: 5  # 초기 지연 시간 최소화
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 2     # 실패 임계값 낮춤

livenessProbe:
  httpGet:
    path: /v1/sys/health?standbyok=true
    port: 8200
  initialDelaySeconds: 60  # 충분한 초기 지연 시간
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 2

교훈

  • Probe 설정은 애플리케이션 특성에 맞게 조정 필요
  • Sealed 상태를 고려한 헬스체크 전략 필요

문제 6: ArgoCD OutOfSync 상태

증상

OutOfSync (This resource is not present in the application's source. It will be deleted from Kubernetes if the prune option is enabled during sync.)

원인 분석

이전에 생성했던 PVC 리소스가 Git에서는 제거되었지만 클러스터에는 남아있어 OutOfSync 상태가 발생했습니다.

해결 과정

  1. 수동 삭제 시도: 일반적인 kubectl delete로 실패
  2. Finalizer 확인: PVC에 finalizer가 설정되어 삭제 방지
  3. 강제 삭제: --force 플래그로 해결
kubectl delete pvc oauth-tokens-pvc-v4 --force --grace-period=0
kubectl delete pv oauth-tokens-pv-v4 --force --grace-period=0

교훈

  • GitOps에서는 리소스 생명주기를 신중하게 관리해야 함
  • Finalizer가 설정된 리소스는 특별한 삭제 절차가 필요
  • Prune 정책을 활용해 불필요한 리소스 자동 정리

종합적인 해결 전략

1. 단계별 검증

# 1단계: Vault 상태 확인
vault status

# 2단계: 인증 방법 확인  
vault auth list

# 3단계: 시크릿 엔진 확인
vault secrets list

# 4단계: 정책 확인
vault policy list

2. 모니터링 및 알림

# Prometheus 메트릭 수집
annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "8200"
  prometheus.io/path: "/v1/sys/metrics"

3. 백업 및 복구 전략

# 정기적인 데이터 백업
apiVersion: batch/v1
kind: CronJob
metadata:
  name: vault-backup
spec:
  schedule: "0 2 * * *"  # 매일 오전 2시
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: vault:latest
            command:
            - /bin/sh
            - -c
            - |
              vault operator raft snapshot save /backup/vault-$(date +%Y%m%d).snap

결론

HashiCorp Vault 구축 과정에서 마주친 문제들을 통해 얻은 교훈:

  1. 단순함의 힘: 복잡한 해결책보다 단순한 접근이 더 효과적
  2. 자동화의 중요성: 수동 작업을 최소화하는 자동화 구현 필수
  3. 관찰 가능성: 충분한 모니터링과 로깅으로 문제 예방
  4. 점진적 구현: 한 번에 모든 기능을 구현하지 말고 단계적 접근
  5. 문서화: 문제 해결 과정을 체계적으로 기록

이러한 경험들이 앞으로 Vault를 도입하려는 분들께 도움이 되기를 바랍니다.


추가 리소스

유용한 명령어 모음

# Vault 상태 종합 확인
vault status && vault auth list && vault secrets list

# 시크릿 백업
vault kv get -format=json secret/data/myapp > backup.json

# 정책 백업  
vault policy read mypolicy > mypolicy.hcl

# 토큰 정보 확인
vault token lookup

권장 도구

  • ArgoCD: GitOps 배포 관리
  • Prometheus: 메트릭 수집
  • Grafana: 시각화 및 알림
  • Vault CLI: 로컬 관리 도구

태그

#Kubernetes #Vault #Troubleshooting #DevOps #GitOps #SecretManagement #Problem-Solving

728x90
반응형