Global outage event
If you're experiencing issues with your AWS services, then please refer to the AWS Health Dashboard. You can find the overall status of ongoing outages, the health of AWS services, and the latest updates from AWS engineers.
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-logroot@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
- Themen
- Container
- Sprache
- 한국어
Relevanter Inhalt
AWS OFFICIALAktualisiert vor einem Jahr
AWS OFFICIALAktualisiert vor einem Jahr
AWS OFFICIALAktualisiert vor 9 Monaten