[CI/CD Study 8주차] Vault on K8S
- -
CoudNet@ 팀의 가시다님께서 리딩하시는 CI/CD Study 8주차 스터디 내용 정리
이번 주차는 스터디의 마지막 8주차로 Vault 에 대해 더 자세히 배운 시간이었습니다.
이번 글에서는 Vault 를 K8S 에서 사용할 때
K8S 애플리케이션이 Vault Secret 을 요청하고, 사용하는 방법에 대해 데모 애플리케이션을 배포하여 실습해보겠습니다.
1. Vault 설치 - 실습 환경 구성
1.1. Kind 설치
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
ingress-ready: true
extraPortMappings:
- containerPort: 30000 # Vault Web UI
hostPort: 30000
- containerPort: 30001 # Sample application
hostPort: 30001
EOF
# 기본 실습 도구 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
1.2. Vault 설치
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
# 네임스페이스 생성
kubectl create namespace vault
# Vault Values 생성
cat <<EOF > vault-values.yaml
global:
enabled: true
tlsDisable: true
server:
standalone:
enabled: true
config: |
ui = true
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_disable = 1
}
storage "file" {
path = "/vault/data"
}
dataStorage:
enabled: true
size: "10Gi"
mountPath: "/vault/data"
auditStorage:
enabled: true
size: "10Gi"
mountPath: "/vault/logs"
service:
enabled: true
type: NodePort
nodePort: 30000
ui:
enabled: true
injector:
enabled: false
EOF
# Vault 설치
helm upgrade vault hashicorp/vault -n vault -f vault-values.yaml --install --version 0.31.0
1.3. Vault Unseal
Vault 를 처음 배포하면 Sealed 상태로 배포됩니다.
kubectl exec -ti vault-0 -n vault -- vault status
Key Value
--- -----
Seal Type shamir
Initialized false
Sealed true
Total Shares 0
Threshold 0
Unseal Progress 0/0
Unseal Nonce n/a
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type file
HA Enabled false
command terminated with exit code 2
Sealed 를 해제해보겠습니다.
Vault Unseal
지금은 Vault 를 StandAlone 상태로 배포했기 때문에 키가 1개만 필요합니다.

Vault 키 확인
kubectl exec vault-0 -n vault -- vault operator init \
-key-shares=1 \
-key-threshold=1 \
-format=json > cluster-keys.json
# 키값확인
cat cluster-keys.json| jq
{
"unseal_keys_b64": [
"cljwCu53QVZqKPxujqSsskRoYHH0GDP2hTyivNzsNKA="
],
"unseal_keys_hex": [
"7258f00aee7741566a28fc6e8ea4acb244686071f41833f6853ca2bcdcec34a0"
],
"unseal_shares": 1,
"unseal_threshold": 1,
"recovery_keys_b64": [],
"recovery_keys_hex": [],
"recovery_keys_shares": 0,
"recovery_keys_threshold": 0,
"root_token": "hvs.0pELTlphXI5UtMnwsiGtt7eC"
}
# Vault Unseal
VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" cluster-keys.json)
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type file
Cluster Name vault-cluster-6d0e7d85
Cluster ID f7148be6-bc3f-3a0d-d8f5-03c22dfb4286
HA Enabled false
Sealed 상태가 false 로 변경된 것을 알 수 있습니다.
1.4. Vault Login
# 맥 유저
brew install hashicorp/tap/vault
vault --version
# Vault 주소 (NodePort)
export VAULT_ADDR='http://localhost:30000'
# Vault Root 키
jq -r ".root_token" cluster-keys.json
# Vault 로그인
vault login
#
Token (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.
Key Value
--- -----
token hvs.0pELTlphXI5UtMnwsiGtt7eC
token_accessor kn5wg61vjub5D21tYh2ylH4L
token_duration ∞
token_renewable false
token_policies ["root"]
identity_policies []
policies ["root"]
2. Vault 사용

Vault 가 K8s 시크릿 생성 및 인증을 처리하는 과정
1. Vault 에 Secret 를 요청 처리를 위해 사전에 Role(Policy) 설정
2. 파드 생성 시, 서비스 어카운트 토큰(JWT) 생성
3. 파드의 애플리케이션이 Vault 에 로그인
a. 애플리케이션은 JWT를 전달하여 Vault 로그인 요청
b. Vault 는 정보 확인을 위해 K8S API 서버에 TokenReview API 호출
c. K8S API 서버는 서비스 어카운트의 이름과 네임스페이스를 반환
d. Vault 는 ‘서비스 어카운트 이름, 네임스페이스’를 Vault 해당 시크릿에 정책과 매칭 확인
e. 확인 후 Vault 는 Auth Token 을 애플리케이션에게 반환
4. 파드의 애플리케이션이 Vault 에 Secret 요청 과정
a. 애플리케이션은 '3' 에서 받은 Auth Token 으로 Vault 해당 시크릿 정보를 요청
b. Vault 는 Auth Token 확인 및 매칭 정책 확인
c. 확인 후 Vault 는 최종적으로 해당 시크릿 정보를 반환
2.1. KV Engine 활성화
vault secrets enable -path=secret kv-v2
# 시크릿 생성
vault kv put secret/webapp/config username="static-user" password="static-password"
#
====== Secret Path ======
secret/data/webapp/config
======= Metadata =======
Key Value
--- -----
created_time 2025-12-13T12:00:51.142693052Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
UI 화면

2.2. k8s 인증 활성화
vault auth enable kubernetes
# K8S API 서버 정보 설정
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc"
# 설정 정보 확인
vault read auth/kubernetes/config
Key Value
--- -----
disable_iss_validation true
disable_local_ca_jwt false
issuer n/a
kubernetes_ca_cert n/a
kubernetes_host https://kubernetes.default.svc
pem_keys []
token_reviewer_jwt_set false
use_annotations_as_alias_metadata false
2.3. Vault k8s 정책 생성
애플리케이션 클라이언트가 secret/webapp/config 에서 정의된 비밀 데이터에 접근하려면,
앞서 생성했던 secret/data/webapp/config 에 대한 읽기 정책이 있어야 합니다.
아래와 같이 생성한 kv 시크릿에 대한 읽기 권한을 부여합니다.
# 정책 생성
vault policy write webapp - <<EOF
path "secret/data/webapp/config" {
capabilities = ["read"]
}
EOF
K8S Service Account 연동
생성한 정책을 k8s Service Account 에 연동합니다.
# K8S Service Account 와 Vault Policy 연동
vault write auth/kubernetes/role/webapp \
bound_service_account_names=vault \
bound_service_account_namespaces=default \
policies=webapp \
ttl=24h \
audience="https://kubernetes.default.svc.cluster.local"
2.4. Web Application 생성
앞서 생성했던 Vault Secret 을 활용 하는 데모 App 을 배포하고, Vault 와 연동이 잘 되는 지 확인해보겠습니다.
# Vault SA 생성
kubectl create sa vault
# 데모 앱 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
labels:
app: webapp
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
serviceAccountName: vault
containers:
- name: app
image: hashieducation/simple-vault-client:latest
imagePullPolicy: Always
env:
- name: VAULT_ADDR
value: 'http://vault.vault.svc:8200'
- name: JWT_PATH
value: '/var/run/secrets/kubernetes.io/serviceaccount/token'
- name: SERVICE_PORT
value: '8080'
volumeMounts:
- name: sa-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
volumes:
- name: sa-token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 600 # 10분 만료
---
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
selector:
app: webapp
type: NodePort
ports:
- port: 80
targetPort: 8080
protocol: TCP
nodePort: 30001
EOF
Application Token 확인
# 애플리케이션에 연결된 ServiceAccount (Vault) 토큰 값
kubectl exec -it deploy/webapp -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
# Vault Secret 을 사용하도록 호출 테스트
curl 127.0.0.1:30001
$ password:static-password username:static-user
# 애플리케이션 로그 확인 > Vault 호출 확인
kubectl logs -l app=webapp -f

Vault Secret 변경 후 App 호출
애플리케이션이 사용하는 Vault Secret 값을 변경하고, 다시 호출해보겠습니다.
# Vault Secret 변경
vault kv put secret/webapp/config username="changed-user" password="changed-password"
====== Secret Path ======
secret/data/webapp/config
======= Metadata =======
Key Value
--- -----
created_time 2025-12-13T12:21:26.475834763Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 2
# Application 호출
curl 127.0.0.1:30001
$ password:changed-password username:changed-user
Vault 의 Secret 이 변경됐음에도 애플리케이션에서 접근이 가능한 것을 확인했습니다.
'CICD' 카테고리의 다른 글
| [CI/CD Study 8주차] Vault Production - HA, LDAP, SSL/TLS (1) | 2025.12.13 |
|---|---|
| [CI/CD Study 7주차] Vault 개요 - Vault Agent, Transit 엔진 (1) | 2025.11.30 |
| [CI/CD Study 6주차] ArgoCD 실전 활용 - App of Apps, ApplicationSet, 인증 통합 (1) | 2025.11.23 |
| [CI/CD Study 5주차] Argo Rollout (0) | 2025.11.16 |
| [CI/CD Study 5주차] ArgoCD SSO - Keycloak (0) | 2025.11.16 |