Amazon EKS에서 aws-ebs-csi-driver 사용시 노드에서 확인되는 `/dev/termination-log` 파일에 대한 분석
본 기사에서는 Amazon EKS에서 aws-ebs-csi-driver 설치시 노드에서 확인되는 /dev/termination-log
파일에 대한 분석한 내용에 대해서 설명합니다.
많은 고객들이 Amazon EKS에서 aws-ebs-csi-driver를 사용해 볼륨의 수명 주기를 관리하고 있습니다.
여기서 aws-ebs-csi-driver는 컨테이너 오케스트레이터가 Amazon EBS 볼륨의 수명 주기를 관리하는 데 사용하는 CSI 인터페이스를 제공하는데, 드라이버를 설치하면 별다른 설정을 하지 않았음에도 노드에 /dev/termination-log
파일이 생성되는 현상이 발생합니다.
본 기사에서는 해당 파일이 어떤 용도인지 그리고 생성된 이유가 무엇인지 분석하려고 합니다.
컨테이너의 "/dev/termination-log" 파일이란
/dev/termination-log
파일은 일반적으로 Pod의 종료 메시지 [1]를 기록하는 terminationMessagePath 필드의 기본값을 나타냅니다.
이는 컨테이너가 치명적인 이벤트로 인해서 종료되었을 때 종료 메시지를 기록할 수 있는 위치로 컨테이너 파일 시스템에 위치합니다.
Kubernetes는 Pod의 spec > containers > terminationMessagePath
필드에 지정된 컨테이너 파일 시스템의 위치에 종료 메시지를 작성하도록 합니다. [2] [3]
즉 해당 파일은 컨테이너의 Overlay FS에 기록되는 것으로 일반적으로 이 노드의 해당 파일이 생성되지는 않습니다.
해당 파일이 노드에서 확인되는 이유
해당 파일이 노드에서 확인되는 이유는 ebs-csi-driver의 구성과 관련되어 있습니다.
ebs-csi-driver 설치시 각 노드에 설치되는 DaemonSet인 ebs-csi-node의 manifest를 보면 아래와 같습니다.
$ kubectl get daemonset -n kube-system ebs-csi-node -o yaml
apiVersion: apps/v1
kind: DaemonSet
spec:
selector:
matchLabels:
app: ebs-csi-node
app.kubernetes.io/name: aws-ebs-csi-driver
...
template:
spec:
containers:
- name: ebs-plugin
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/lib/kubelet
mountPropagation: Bidirectional
name: kubelet-dir
- mountPath: /csi
name: plugin-dir
- mountPath: /dev
name: device-dir
...
volumes:
- hostPath:
path: /dev
type: Directory
name: device-dir
...
여기서 device-dir이라는 hostPath /dev
위치를 ebs-plugin 컨테이너의 /dev
위치에 마운트하는 것을 확인할 수 있습니다.
즉, termination-log 파일을 생성하는 위치가 hostPath로 마운트 된 위치이기에 노드에서 /dev/termination-log
가 생성되는 것입니다.
해당 파일에 아무것도 작성되지 않는 이유
위의 설명을 보면 노드에 생성된 /dev/termination-log
파일에 ebs-plugin 컨테이너의 종료 메시지가 기록될 것처럼 보이나 실제로 해당 파일에는 아무런 데이터가 존재하지 않습니다.
이러한 현상이 발생하는 이유는 해당 파일이 일시적으로 생성되었으나 컨테이너의 /dev/termination-log
파일과 같은 파일이 아니며, 컨테이너의 /dev/termination-log
파일은 다른 위치에 다시 마운트 되기 때문입니다.
이는 컨테이너 내부 및 노드에 생성된 /dev/termination-log
파일의 inode 값[4]을 확인해보면 알 수 있습니다.
- 컨테이너 내부의
/dev/termination-log
root@termination-demo:/# echo "test" >> /dev/termination-log root@termination-demo:/# ls -la /dev/termination-log -rw-rw-rw-. 1 root root 5 Dec 11 00:43 /dev/termination-log root@termination-demo:/# stat /dev/termination-log File: /dev/termination-log Size: 5 Blocks: 8 IO Block: 4096 regular file Device: 259,1 Inode: 27302570 Links: 1 Access: (0666/-rw-rw-rw-) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2024-12-11 00:40:18.737373221 +0000 Modify: 2024-12-11 00:43:04.899250691 +0000
- 노드에 생성된 /dev/termination-log
[root@ip-10-30-108-223 dev]# ll /dev/termination-log -rw-r--r--. 1 root root 0 Dec 11 00:40 /dev/termination-log [root@ip-10-30-108-223 dev]# stat /dev/termination-log File: /dev/termination-log Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 5h/5d Inode: 482 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Context: system_u:object_r:device_t:s0 Access: 2024-12-11 00:40:18.867374760 +0000 Modify: 2024-12-11 00:40:18.867374760 +0000
즉, 두 파일 자체가 서로 다른 파일이기에 컨테이너 내부에서 종료 메시지를 작성하더라도 반영되지 않는 것입니다.
어째서 HostPath /dev 아래에 생성된 /dev/termination-log 파일은 다른 파일로 인식되는가
이는 Kubernetes에서의 terminationMessagePath
처리 로직에 의해서 발생하는 현상입니다.
Kubernetes의 Node Component인 Kubelet에서는 컨테이너의 terminationMessagePath
를 별도의 path에 저장하도록 컨테이너의 볼륨 구성을 재설정하는 과정을 거칩니다.
이 과정에서 컨테이너의 terminationMessagePath
에 지정된 위치를 노드의 /var/lib/kubelet/pods/{POD_ID}/containers/{CONTAINER_NAME}/{RANDOM_NUM}
위치에 마운트 하도록 컨테이너의 볼륨 명세를 수정하는데 이로 인해 terminationMessagePath
의 상위 위치를 별도의 볼륨에 마운트 하더라도 마운트 된 위치에 파일이 작성되지 않는 것입니다.
이와 관련된 동작은 Kubelet의 코드에서 makeMounts 함수에서 확인하실 수 있습니다. [5]
```
// Because the PodContainerDir contains pod uid and container name which is unique enough,
// here we just add a random id to make the path unique for different instances
// of the same container.
cid := makeUID()
containerLogPath := filepath.Join(opts.PodContainerDir, cid)
fs, err := m.osInterface.Create(containerLogPath)
```
추가적으로 위에서 확인한 것처럼 inode값을 비교하는 방법 및 컨테이너의 명세를 확인해 검증할 수 있습니다.
-
inode값 비교
# 컨테이너 내부의 `/dev/termination-log` $ kubectl exec -it termination-demo -- bash root@termination-demo:/# ls -la /dev/termination-log -rw-rw-rw-. 1 root root 0 Dec 12 01:49 /dev/termination-log root@termination-demo:/# stat /dev/termination-log File: /dev/termination-log Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 259,1 Inode: 29380146 Links: 1 Access: (0666/-rw-rw-rw-) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2024-12-12 01:49:02.167742534 +0000 Modify: 2024-12-12 01:49:02.167742534 +0000
# 노드에 생성된 `/var/lib/kubelet/pods/{POD_ID}/containers/{CONTAINER_NAME}/{RANDOM_NUM}` [root@ip-10-30-65-76 termination-demo-container]# pwd /var/lib/kubelet/pods/5b207fae-1273-4197-8676-f27b74a9f090/containers/termination-demo-container [root@ip-10-30-65-76 termination-demo-container]# stat 9c6e1dba File: 9c6e1dba Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 10301h/66305d Inode: 29380146 Links: 1 Access: (0666/-rw-rw-rw-) Uid: ( 0/ root) Gid: ( 0/ root) Context: system_u:object_r:init_var_lib_t:s0 Access: 2024-12-12 01:49:02.167742534 +0000 Modify: 2024-12-12 01:49:02.167742534 +0000
-
ctr 명령어를 이용해 컨테이너의 명세 확인
[root@ip-10-30-65-76 termination-demo-container]# ctr -n k8s.io container list CONTAINER IMAGE RUNTIME bc5a05a9241c6d7a74677a42e859ae6339958e8caa2f37f113ab51deec4a9b4d docker.io/library/debian:latest io.containerd.runc.v2 ... [root@ip-10-30-65-76 termination-demo-container]# ctr -n k8s.io container info bc5a05a9241c6d7a74677a42e859ae6339958e8caa2f37f113ab51deec4a9b4d { "ID": "bc5a05a9241c6d7a74677a42e859ae6339958e8caa2f37f113ab51deec4a9b4d", "Labels": { "io.cri-containerd.kind": "container", "io.kubernetes.container.name": "termination-demo-container", "io.kubernetes.pod.name": "termination-demo", "io.kubernetes.pod.namespace": "default", "io.kubernetes.pod.uid": "5b207fae-1273-4197-8676-f27b74a9f090" }, "Image": "docker.io/library/debian:latest", "Spec": { "ociVersion": "1.1.0", "mounts": [ { "destination": "/dev", "type": "bind", "source": "/dev", "options": [ "rbind", "rprivate", "rw" ] }, { "destination": "/dev/termination-log", "type": "bind", "source": "/var/lib/kubelet/pods/5b207fae-1273-4197-8676-f27b74a9f090/containers/termination-demo-container/e100dbca", "options": [ "rbind", "rprivate", "rw" ] }, ... ], ... }
결론 및 정리
결론적으로 aws-ebs-csi-driver 사용시 각 노드에 생성되는 /dev/termination-log
파일은 드라이버의 HostPath /dev
볼륨 설정과 Kubelet의 terminationMessagePath
볼륨 처리 동작으로 인해 생성되는 것으로, Kubelet은 Pod에 별도의 볼륨을 설정하지 않더라도 내부적으로 terminationMessagePath
에 대한 볼륨을 추가하는 동작이 존재하는 것을 확인할 수 있었습니다.
참고
[1] https://kubernetes.io/ko/docs/tasks/debug/debug-application/determine-reason-pod-failure/
[3] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#container-v1-core
관련 콘텐츠
- 질문됨 3달 전lg...
- AWS 공식업데이트됨 2년 전
- AWS 공식업데이트됨 2년 전
- AWS 공식업데이트됨 6달 전