배경

GC Log와 Heap Dump를 보관해야 하는 상황이 발생했다. 인프라상 EKS Fargate로 배포하고 있기 때문에 컨테이너가 종료되면 노드에 직접 접근할 수 없고, 컨테이너 내부에만 남긴 GC Log와 Heap Dump도 같이 사라진다.

그래서 PV, PVC, EFS를 사용해 Dump 파일을 영구적으로 보관하도록 구성했다.

EKS Fargate Pod의 Ephemeral Storage는 Pod가 종료되면 삭제된다. EBS Volume은 Fargate Pod에 Mount할 수 없고, 이번처럼 여러 Pod에서 사용할 수 있는 영구 File Storage가 필요하다면 EFS를 사용할 수 있다.

사전 구성

EFS를 Mount하기 전에 아래 항목이 준비되어 있어야 한다.

  1. EKS와 통신할 수 있는 VPC에 EFS File System이 있어야 한다.
  2. Fargate Pod가 배포되는 Availability Zone에 EFS Mount Target이 있어야 한다.
  3. EFS Mount Target의 Security Group은 Fargate Pod가 사용하는 Security Group에서 오는 TCP 2049를 허용해야 한다.
  4. EFS Access Point를 사용한다면 Root Directory와 POSIX UID/GID 권한을 애플리케이션의 실행 User에 맞춰야 한다.

Network와 권한이 맞지 않으면 Pod Event에서 FailedMount가 발생하고 Container가 시작되지 않는다.

PV와 PVC

Fargate에서는 EFS PV를 미리 생성해서 PVC와 연결한다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: dump-efs-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: ""
  csi:
    driver: efs.csi.aws.com
    # EFS 전체를 사용한다면 File System ID만 입력한다.
    volumeHandle: fs-xxxxxxxxxxxxxxxxx
    # Access Point를 사용한다면 아래 형식으로 입력한다.
    # volumeHandle: fs-xxxxxxxxxxxxxxxxx::fsap-xxxxxxxxxxxxxxxxx
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dump-efs-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: ""
  volumeName: dump-efs-pv
  resources:
    requests:
      storage: 5Gi

여기서 storage: 5Gi는 Kubernetes가 PV와 PVC를 Binding하기 위해 필요한 값이다. EFS의 실제 용량을 5Gi로 제한하는 설정은 아니다.

persistentVolumeReclaimPolicy: Retain을 사용하면 PVC나 PV를 삭제하더라도 EFS의 실제 파일은 자동으로 삭제되지 않는다. Dump 파일을 실수로 삭제하지 않기 위해 이 방식이 적합하다.

Pod에 Mount하기

기존 예시에서 mountPathsubPathvolumes가 아니라 Container의 volumeMounts 아래에 들어가야 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: sample-app
          image: example/sample-app:latest
          volumeMounts:
            - name: dump
              mountPath: /dump
              subPath: dump/{{ .Values.name }}
      volumes:
        - name: dump
          persistentVolumeClaim:
            claimName: dump-efs-pvc
  • mountPath: /dump는 Container 내부에서 GC Log와 Heap Dump를 저장할 경로다.
  • subPath: dump/{{ .Values.name }}는 EFS 전체가 아니라 dump/<애플리케이션 이름> 경로만 /dump에 Mount한다.
  • {{ .Values.name }}은 Helm이 Manifest를 Rendering할 때 실제 값으로 변경한다.

하나의 EFS와 PVC를 여러 애플리케이션이 같이 사용하더라도 subPath를 나누면 아래처럼 파일을 구분할 수 있다.

/dump/app-a
/dump/app-b
/dump/app-c

Pod별 경로 분리

같은 Deployment의 Replica가 모두 동일한 subPath를 사용하면 같은 Directory에 파일을 기록한다. Heap Dump 파일명이 같거나 GC Log Rotation 이름이 겹치면 파일이 덮어써질 수 있다.

Pod별 Directory가 필요하다면 Downward API와 subPathExpr를 사용할 수 있다.

containers:
  - name: sample-app
    env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
    volumeMounts:
      - name: dump
        mountPath: /dump
        subPathExpr: dump/$(POD_NAME)
volumes:
  - name: dump
    persistentVolumeClaim:
      claimName: dump-efs-pvc

subPathsubPathExpr는 동시에 사용할 수 없다. Helm Value처럼 배포 시점에 경로가 정해진다면 subPath를 사용하고, Pod 이름처럼 Runtime 정보가 필요하다면 subPathExpr를 사용한다.

JVM Dump 경로 설정

Volume을 /dump에 Mount했다면 JVM도 같은 경로에 파일을 남기도록 설정해야 한다.

Java 9 이상에서는 아래와 같이 구성할 수 있다.

-Xlog:gc*:file=/dump/gc.log:time,uptime,level,tags:filecount=10,filesize=100M
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/dump

HeapDumpPath를 Directory로 지정하면 JVM이 해당 경로 아래에 Heap Dump 파일을 생성한다. Replica가 같은 경로를 공유한다면 Pod 이름이나 Timestamp가 포함되도록 파일과 Directory 구성을 나누는 것이 안전하다.

Heap Dump에는 애플리케이션의 실제 메모리 값과 민감정보가 포함될 수 있다. EFS 암호화, 접근 권한, 보관 기간과 삭제 기준도 같이 관리해야 한다.

확인 방법

kubectl get pv,pvc
kubectl describe pvc dump-efs-pvc
kubectl describe pod <POD_NAME>
kubectl exec <POD_NAME> -- df -h /dump
kubectl exec <POD_NAME> -- ls -al /dump

확인할 내용은 다음과 같다.

  • PV와 PVC가 Bound 상태인지 확인한다.
  • Pod Event에 FailedMount가 없는지 확인한다.
  • Container의 /dump가 EFS로 Mount됐는지 확인한다.
  • GC Log 또는 Test 파일을 생성한 뒤 Pod를 재시작해도 파일이 남아 있는지 확인한다.
  • 여러 Replica가 동일한 파일을 덮어쓰고 있지 않은지 확인한다.

정리

  • EKS Fargate의 Ephemeral Storage는 Pod가 종료되면 같이 삭제된다.
  • EFS를 PV와 PVC로 연결하면 GC Log와 Heap Dump를 Pod 수명과 분리해서 보관할 수 있다.
  • Fargate에서 EFS PV는 Static Provisioning으로 구성한다.
  • mountPathsubPath는 Container의 volumeMounts 아래에 작성한다.
  • 여러 애플리케이션은 subPath, 여러 Replica는 subPathExpr로 경로를 분리할 수 있다.
  • Network, NFS 2049, UID/GID, 파일 충돌과 Dump 보안까지 같이 확인해야 한다.

참고