EFS PVC Setup Guide

Ohouse 플랫폼에서 새로운 서비스에 EFS 기반 PVC를 붙이는 end-to-end 가이드. agentic-ai-platform + aap-coordinator 적용 사례를 예시로 설명.


전체 그림

EFS PVC 하나를 Pod에 붙이려면 4개 레포가 협업해야 한다. 각 레포는 서로 다른 레이어를 담당.

레포 역할 도구
bucket-iac AWS EFS filesystem + Kubernetes StorageClass 생성 Terraform
infra-helm PVC 정의 Helm chart Helm
ohouse-cloud 위 chart를 어느 클러스터·네임스페이스에 배포할지 ArgoCD Application으로 선언 ArgoCD
ohouse-helm-manifest 앱 Deployment에서 PVC를 마운트 (extraVolumes / extraVolumeMounts) Helm

배포 순서도 위 표 순서 그대로. 아래 단계에서 건너뛰면 다음 단계 리소스가 Pending에 빠진다.


Step 1. bucket-iac — EFS + StorageClass 생성

Terraform의 efs_sc_set 모듈에 서비스 key를 추가하면 모듈이 자동으로 efs-sc-<key> 이름의 StorageClass를 만든다.

1-1. EFS filesystem 등록

bucket-iac/admin/efs.tf:

module "efs" {
  # ...
  for_each = {
    # ...
    agentic-ai-platform = { ... }
  }
}

output "agentic_ai_platform_efs" {
  value = module.efs["agentic-ai-platform"].efs_id
}

1-2. StorageClass 등록

bucket-iac/eks/mgmt/{dev,prod}.tf:

efs_sc_set = {
  agentic-ai-platform = {
    efs_id = local.agentic_ai_platform_efs
  }
}

결과: 클러스터에 efs-sc-agentic-ai-platform StorageClass 생성. (key에 환경 접미사가 없으면 dev/prod 모두 동일한 이름)


Step 2. infra-helm — PVC chart 생성

infra-helm/<service>-components/ chart로 PVC를 선언. SC는 이미 bucket-iac에서 만들었으므로 chart에서는 PVC만 정의한다.

디렉토리 구조

infra-helm/agentic-ai-platform-components/
├── Chart.yaml
├── dev.values.yaml
├── prod.values.yaml
└── templates/pvc-efs.yaml

Chart.yaml

apiVersion: v2
name: agentic-ai-platform-components
version: "1.0.0"
description: Infrastructure components for agentic-ai-platform (EFS)
type: application

templates/pvc-efs.yaml

{{- if .Values.efs.enabled -}}
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: {{ .Values.efs.pvcName }}
  namespace: {{ .Release.Namespace }}
spec:
  storageClassName: {{ .Values.efs.storageClassName }}
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: {{ .Values.efs.size }}
{{- end }}

dev.values.yaml / prod.values.yaml

efs:
  enabled: true
  storageClassName: efs-sc-agentic-ai-platform
  pvcName: agentic-ai-platform-efs-pvc
  size: 100Gi

참고: EFS는 실제 용량 제한이 없다. resources.requests.storage 값은 PVC 스펙 필수 필드라 넣을 뿐 실질적 제한이 되지 않음. 관례적으로 100Gi 정도.

commerce-rca-components는 SC도 chart에서 만드는 레거시 패턴. 새 chart는 SC 만들지 말 것. SC는 bucket-iac 단일 소스.


Step 3. ohouse-cloud — ArgoCD Application 선언

infra-helm chart를 클러스터에 배포하라고 ArgoCD에 지시하는 Application YAML.

파일 위치

ohouse-cloud/argocd-root/infra/templates/application/apps/<service>-components-{dev,prod}.yaml

디렉토리 선택 기준:

  • apps/: 일반 서비스용 (기본)
  • database/: DB 성격 서비스 (postgres/mysql/mariadb)
  • platform/: 플랫폼 공통 인프라

agentic-ai-platform-components-dev.yaml

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: 'agentic-ai-platform-components-dev'
  annotations:
  {{- if .Values.notiChannel }}
    notifications.argoproj.io/subscribe.on-deployed.slack: {{ .Values.notiChannel }}
    notifications.argoproj.io/subscribe.on-sync-failed.slack: {{ .Values.notiChannel }}
    notifications.argoproj.io/subscribe.on-health-degraded.slack: {{ .Values.notiChannel }}
  {{- end }}
spec:
  project: {{ .Values.project }}
  source:
    repoURL: 'https://github.com/bucketplace/infra-helm.git'
    path: agentic-ai-platform-components
    targetRevision: master
    helm:
      releaseName: agentic-ai-platform-components
      valueFiles:
        - dev.values.yaml
  destination:
    namespace: apps
    name: mgmt-dev-infra-eks
  syncPolicy:
    syncOptions:
      - CreateNamespace=true

Prod는 destination.namevalueFiles만 교체

  • name: mgmt-prod-infra-eks
  • valueFiles: [prod.values.yaml]

구조 원칙: 이 레포는 ApplicationSet을 쓰지 않고, dev/prod 각각 Application YAML을 복붙한다. 레포 전체가 이 패턴.


Step 4. ohouse-helm-manifest — 앱에서 PVC 마운트

앱의 app-values.yamlextraVolumes + extraVolumeMounts를 추가한다.

aap-coordinator/app-values.yaml

extraVolumeMounts:
- name: "sessions"
  mountPath: "/home/aap/sessions"
extraVolumes:
- name: "sessions"
  persistentVolumeClaim:
    claimName: "agentic-ai-platform-efs-pvc"
  • 하나의 PVC를 여러 앱이 동시에 마운트 가능 (ReadWriteMany)
  • mountPath는 앱이 기대하는 경로. 앱이 non-root 유저면 해당 유저 홈 하위가 안전

extraVolumes vs extraVolumeMounts

같은 Pod spec이지만 다른 레벨을 건드린다.

구분 대상 레벨 쓰임
extraVolumes Pod (spec.volumes[]) Pod가 어떤 저장소를 소유하는지 선언 (PVC/configMap/secret/emptyDir)
extraVolumeMounts Container (spec.containers[].volumeMounts[]) 그 저장소를 컨테이너 내부 어느 경로에 붙일지

렌더링 결과

spec:
  template:
    spec:
      containers:
      - name: aap-coordinator
        volumeMounts:                       # extraVolumeMounts
        - name: sessions
          mountPath: /home/aap/sessions
      volumes:                              # extraVolumes
      - name: sessions
        persistentVolumeClaim:
          claimName: agentic-ai-platform-efs-pvc

왜 분리돼 있나

Pod에 container가 여러 개(sidecar 등) 있을 때 같은 volume을 각 container가 다른 경로에 마운트할 수 있어야 해서. Volume은 Pod가 소유, Mount는 Container가 선언. 실사용은 대부분 1:1이니 name만 맞춰서 쌍으로 추가한다고 생각하면 된다.


Root / Non-root 컨테이너 고려사항

Root 실행 (USER 지시어 없는 Dockerfile)

  • EFS Access Point UID/GID 무관하게 read/write 가능
  • securityContext에 아무것도 안 넣어도 동작
  • 단, 컨테이너 보안 모범사례에 어긋남

Non-root 실행 (권장)

Dockerfile에 유저 추가:

FROM alpine:3.21 AS app
# ...
RUN adduser -D -u 1000 aap
COPY --from=builder /bin/${SERVICE} /bin/${SERVICE}
USER aap
WORKDIR /home/aap
CMD /bin/${SERVICE} server
  • COPYUSER aap 이전에 (aap는 /bin에 쓰기 권한 없음)
  • WORKDIR /home/aap로 설정 → $HOME 자동 매핑
  • EFS 마운트 경로도 /home/aap/ 하위로 두면 권한 이슈 최소화

EFS Access Point UID 불일치 시

첫 배포 후 컨테이너 들어가서 확인:

kubectl exec -it <pod> -n apps -- ls -la /home/aap/sessions

소유자 UID가 컨테이너 유저와 다르면 securityContext.fsGroup으로 맞춘다:

podSecurityContext:
  fsGroup: 1000

배포 순서 (중요)

bucket-iac (SC)
  └─ infra-helm (PVC chart)
      └─ ohouse-cloud (ArgoCD Application)
          └─ ohouse-helm-manifest (extraVolumes)

앞 단계가 ArgoCD로 sync되어 리소스가 실제 클러스터에 생성된 뒤에 다음 단계를 merge할 것. 역순으로 merge되면 Pod가 PVC를 찾지 못해 Pending에 빠진다.


검증 커맨드

# SC 존재 확인
kubectl get sc efs-sc-agentic-ai-platform

# PVC Bound 상태 확인
kubectl get pvc -n apps agentic-ai-platform-efs-pvc

# PVC provisioning 이벤트 확인
kubectl describe pvc -n apps agentic-ai-platform-efs-pvc

# 마운트 확인
kubectl exec -it <pod> -n apps -- df -h | grep efs
kubectl exec -it <pod> -n apps -- ls -la /home/aap/sessions

참고 사례

패턴 chart 특징
librechat-components PVC만 chart에서 생성 (SC는 bucket-iac) 권장 패턴
commerce-rca-components SC + PVC 모두 chart에서 생성 레거시, bucket-iac 도입 전 방식
bucket-runner persistentVolumeClaims 리스트로 여러 PVC 동시 선언 여러 PVC 필요한 경우
🔒 Admin 로그인