NetApp Tech Blog에 갔다가 흥미로운 주제의 글이 보여서 AI 번역(+약간 수정)의 힘을 빌려 읽어보았습니다.
출처: https://community.netapp.com/t5/Tech-ONTAP-Blogs/B2B-What-Happens-When-a-NFS-Share-is-Mounted/ba-p/462010
“Back to Basics(B2B)”라는 새로운 블로그 시리즈를 시작하게 되어 기쁩니다. 세상이 너무 빨리 돌아가고, 삶이 끊임없이 변하고, 모든 것을 망치지 않고 계속 돌아가야 하기 때문에 종종 간과되는 기본 개념들을 다시 살펴보는 것이 목표입니다.
NFS 공유를 마운트하는 것은 간단한 작업처럼 보일 수 있지만, Linux 클라이언트에서 이 작업을 수행할 때 실제로 어떤 일이 일어나는지 생각해 본 적이 있나요? 이 질문에 답하기 위해 NFS 프로토콜을 자세히 살펴보고, 몇 가지 강력한 도구를 사용하여 클라이언트 관점에서 내부 구조를 살펴보겠습니다. 자, 시작해 볼까요!
Network File System (NFS)
NFS의 역사를 간략하게 살펴보겠습니다. 자세한 내용은 생략하고 싶으시다면, 핵심 요점은 다음과 같습니다.
- NFS는 수십 년간의 엔지니어링 경험을 바탕으로 검증된 성숙하고 실전에서 검증된 프로토콜입니다. 파일 공유를 위한 업계 표준 솔루션으로, AI/ML 파이프라인과 같은 최신 워크로드에 적합하며, NetApp에서 완벽하게 지원합니다. 즉, 시스템에 독점 에이전트, 모듈 또는 “매직” 구성 요소를 설치할 필요 없이 NetApp 스토리지에서 최고 수준의 성능을 얻을 수 있습니다.
1984년, 썬 마이크로시스템즈는 마치 로컬에 있는 파일처럼 네트워크를 통해 파일에 접근할 수 있는 솔루션을 개발했습니다. 네트워크 파일 시스템(NFS)으로 알려진 이 솔루션은 처음에는 썬 내부에서 사용되었습니다.
1989년, RFC 1094에 따라 NFSv2가 출시되면서 NFS가 처음으로 대중에게 공개되었습니다. 이 버전은 UDP를 사용하는 스테이트리스 프로토콜을 도입했으며 32비트 파일 크기와 오프셋(파일을 4GB로 제한)을 지원했습니다[ 1 ]. 또한 단순성과 이식성 덕분에 널리 채택되었습니다.
1995년까지 NFSv3는 RFC 1813을 통해 도입되어 64비트 파일 크기 및 오프셋 지원, 선택적 전송 프로토콜로서의 TCP, 향상된 성능을 위한 비동기 쓰기와 같은 중요한 개선 사항을 제공했습니다[ 2 ].
그런 다음 2003년 RFC 3530에 따라 발표된 NFSv4.0으로 대대적인 재설계가 이루어졌습니다. 이 버전은 NFS를 Kerberos 및 ACL(액세스 제어 목록)과 같은 보안 메커니즘에 대한 내장 지원, 프로토콜에 파일 잠금 및 마운팅 통합, 네트워크 왕복을 줄이기 위한 복합 작업을 통합하는 상태 저장 프로토콜로 전환했습니다[ 3 ].
2010년에 RFC 5661에 따라 NFSv4.1이 출시되어 저장소에 대한 확장 가능하고 병렬적인 액세스를 위한 병렬 NFS(pNFS), 향상된 복구 및 성능을 위한 세션 모델 (클라이언트 ID 트렁킹 및 세션 트렁킹) 이 도입되었으며 위임 및 레이아웃 처리가 개선되었습니다[ 4 ].
그런 다음 2016년에 RFC 7862에 따라 NFSv4.2가 공개되어 파일 복사 작업을 서버로 오프로드하는 서버 측 복사, 홀이 있는 파일의 효율적인 저장을 위한 스파스(sparse) 파일 지원, 서버 측 캐싱을 최적화하는 애플리케이션 I/O 힌트 등 보안을 개선하는 여러 기능과 공간 관리와 같은 여러 가지 최신 기능이 추가되었습니다[ 5 ].
이제 역사를 살펴보았으니, Linux 클라이언트에서 NFS 공유를 마운트하면 어떤 일이 일어나는지 자세히 알아보겠습니다. 이 연습에서는 NFSv4.1을 사용하겠습니다. pNFS 및 세션 트렁킹과 같은 기능은 최신 AI/ML 워크로드에 필요한 성능과 확장성을 제공하는 데 필수적이기 때문입니다.
후드 아래를 들여다보다
마운트 작업 중 NFS 클라이언트와 NFS 서버 간의 상호 작용을 이해하려면 먼저 sunrpc, nfs, nfsv4 모듈에 대한 커널 추적을 활성화해야 합니다. 추적을 활성화하기 전에 이러한 모듈이 커널에 로드되었는지 확인해야 합니다. modprobe 명령을 사용하여 이 작업을 수행할 수 있습니다 .
sudo modprobe sunrpc sudo modprobe nfs sudo modprobe nfsv4 sudo modprobe nfs_layout_nfsv41_files
모듈이 로드되면 커널 추적 하위 시스템의 각 이벤트에 대해 enabled 파일에 1을 작성하여 추적을 활성화합니다.
echo 1 | sudo tee /sys/kernel/tracing/events/sunrpc/enabled echo 1 | sudo tee /sys/kernel/tracing/events/nfs/enabled echo 1 | sudo tee /sys/kernel/tracing/events/nfs4/enabled
다음으로, 터미널 창을 두 개 열었습니다. 첫 번째 창에서는 커널의 추적 하위 시스템에서 추적 정보를 지속적으로 읽는 명령을 실행했습니다.
cat /sys/kernel/tracing/trace_pipe
이를 통해 실시간 추적 이벤트가 발생하는 대로 스트리밍되므로 NFS 클라이언트와 서버 간의 상호 작용을 실시간으로 관찰할 수 있습니다.
두 번째 창에서 다음 mount 명령을 실행했습니다.
sudo mount -t nfs4 -o vers=4.1,rsize=65536,wsize=65536,proto=tcp 10.26.128.221:/vol_cp_5node /cp
원래 추적 출력은 899줄이었습니다. 간결하고 집중적인 정보를 제공하기 위해, 클라이언트 관점에서 NFS 클라이언트와 서버 간의 통신 흐름을 이해하는 데 필요한 관련 이벤트만 포함하도록 데이터를 필터링했습니다.
추적은 mount 명령이 mount 옵션을 구문 분석하는 것으로 시작됩니다.
<...>-3331 [012] ..... 949.293271: nfs_mount_option: option source
<...>-3331 [012] ..... 949.293276: nfs_mount_option: option vers
<...>-3331 [012] ..... 949.293277: nfs_mount_assign: option vers=4.1
<...>-3331 [012] ..... 949.293278: nfs_mount_option: option rsize
<...>-3331 [012] ..... 949.293279: nfs_mount_option: option wsize
<...>-3331 [012] ..... 949.293280: nfs_mount_option: option proto
<...>-3331 [012] ..... 949.293280: nfs_mount_assign: option proto=tcp
<...>-3331 [012] ..... 949.293282: nfs_mount_option: option addr
<...>-3331 [012] ..... 949.293282: nfs_mount_assign: option addr=10.26.128.221
mount.nfs-3331 [012] ..... 949.293283: nfs_mount_option: option clientaddr
mount.nfs-3331 [012] ..... 949.293283: nfs_mount_assign: option clientaddr=10.26.56.192
mount.nfs-3331 [012] ..... 949.293286: nfs_mount_path: path='/vol_cp_5node'다음으로, xprt_create와 rpc_clnt_new가 포트 2049에서 NFS 서버와 통신하도록 RPC 클라이언트를 초기화하는 RPC 전송을 준비합니다.
mount.nfs-3331 [012] ..... 949.293390: xprt_create: peer=[10.26.128.221]:2049 state=BOUND
mount.nfs-3331 [012] ..... 949.293405: rpc_clnt_new: client=00000000 peer=[10.26.128.221]:2049 program=nfs server=10.26.128.221 xprtsec=none flags=DISCRTRY|INFINITE_SLOTS|NO_RETRANS_TIMEOUT
mount.nfs-3331 [012] ..... 949.293407: rpc_task_begin: task:00000001@00000000 flags=NULLCREDS|DYNAMIC|SOFT|SOFTCONN|CRED_NOREF runstate=ACTIVE status=0 action=0x0
mount.nfs-3331 [012] ..... 949.294496: rpc_connect_status: task:00000001@00000000 status=-11클라이언트와 서버 간의 통신이 설정되었으므로 이제 시스템은 NFS 세션을 설정해야 합니다. 먼저 서버에서 NFS 클라이언트에 고유 ID를 부여합니다. 클라이언트는 서버로 EXCHANGE_ID RPC를 전송합니다.
mount.nfs-3331 [012] ..... 949.295698: rpc_request: task:00000002@00000000 nfsv4 EXCHANGE_ID (sync)
… 그리고 서버는 클라이언트 ID(xid=0x734ec2e0)로 응답합니다.
mount.nfs-3331 [012] ..... 949.296538: rpc_stats_latency: task:00000002@00000000 xid=0x734ec2e0 nfsv4 EXCHANGE_ID backlog=11 rtt=719 execute=841
mount.nfs-3331 [012] ..... 949.296541: nfs4_exchange_id: error=0 (OK) dstaddr=10.26.128.221다음으로, 세션을 생성할 차례입니다. 클라이언트는 EXCHANGE_ID의 클라이언트 ID를 사용하여 세션을 요청합니다.
10.26.128.221-m-3333 [001] ..... 949.297657: rpc_request: task:00000004@00000000 nfsv4 CREATE_SESSION (sync) 10.26.128.221-m-3333 [001] ..... 949.298389: rpc_stats_latency: task:00000004@00000000 xid=0x754ec2e0 nfsv4 CREATE_SESSION backlog=10 rtt=583 execute=731
그러면 서버는 세션 ID(0x87e0d01f), 슬롯 수, 시퀀스 ID, 콜백 정보와 같은 세션 매개변수로 응답합니다. 이 세션은 이후의 모든 NFS 작업에 사용됩니다.
10.26.128.221-m-3333 [001] ..... 949.298393: nfs4_create_session: error=0 (OK) dstaddr=10.26.128.221 10.26.128.221-m-3333 [001] ..... 949.299115: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=1 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
그런 다음 클라이언트는 LOOKUP_ROOT 및 SECINFO_NO_NAME 요청을 서버로 전송합니다. LOOKUP_ROOT 작업은 내보낸 NFS 파일 시스템의 루트 디렉터리 파일 핸들을 가져오고, SECINFO_NO_NAME은 서버가 특정 경로 구성 요소에 액세스하기 위해 지원하는 보안 메커니즘(인증 방법)을 확인합니다. 이러한 작업은 클라이언트 측에서 NFS 네임스페이스의 시작점을 설정하는 데 중요합니다. 클라이언트가 루트 파일 핸들을 수집하고 검증하면 디렉터리 트리 탐색을 시작할 수 있습니다.
mount.nfs-3331 [012] ..... 949.299259: rpc_request: task:00000001@00000001 nfsv4 SECINFO_NO_NAME (sync)
mount.nfs-3331 [012] ..... 949.300992: rpc_stats_latency: task:00000001@00000001 xid=0x774ec2e0 nfsv4 SECINFO_NO_NAME backlog=718 rtt=1612 execute=2444
mount.nfs-3331 [012] ..... 949.305147: nfs4_setup_sequence: session=0x87e0d01f slot_nr=0 seq_nr=3 highest_used_slotid=0
mount.nfs-3331 [012] ..... 949.305148: rpc_request: task:00000002@00000001 nfsv4 LOOKUP_ROOT (sync)
mount.nfs-3331 [012] ..... 949.306163: rpc_stats_latency: task:00000002@00000001 xid=0x784ec2e0 nfsv4 LOOKUP_ROOT backlog=10 rtt=782 execute=1020
mount.nfs-3331 [012] ..... 949.306164: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=3 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
mount.nfs-3331 [012] ..... 949.306168: nfs4_lookup_root: error=0 (OK) fileid=00:00:64 fhandle=0x507c44a0 valid=TYPE|MODE|NLINK|OWNER|GROUP|SIZE|FSID|FILEID|ATIME|MTIME|CTIME|CHANGE|0x400200다음으로, 클라이언트는 FSINFO 및 SERVER_CAPS 요청을 사용하여 서버에 파일 시스템 기능을 조사하고, 파일 유형, 크기, 권한, 타임스탬프 등을 검색하는 GETATTR 작업을 실행하여 속성을 가져옵니다.
mount.nfs-3331 [012] ..... 949.306786: nfs4_setup_sequence: session=0x87e0d01f slot_nr=0 seq_nr=5 highest_used_slotid=0
mount.nfs-3331 [012] ..... 949.306786: rpc_request: task:00000004@00000001 nfsv4 FSINFO (sync)
mount.nfs-3331 [012] ..... 949.307693: rpc_stats_latency: task:00000004@00000001 xid=0x7a4ec2e0 nfsv4 FSINFO backlog=3 rtt=897 execute=907
mount.nfs-3331 [012] ..... 949.307693: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=5 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
mount.nfs-3331 [012] ..... 949.307694: nfs4_fsinfo: error=0 (OK) fileid=00:00:64 fhandle=0x507c44a0 valid=TYPE|MODE|NLINK|OWNER|GROUP|SIZE|FSID|FILEID|ATIME|MTIME|CTIME|CHANGE|0x400200
mount.nfs-3331 [012] ..... 949.307696: nfs4_setup_sequence: session=0x87e0d01f slot_nr=0 seq_nr=6 highest_used_slotid=0
mount.nfs-3331 [012] ..... 949.307697: rpc_request: task:00000005@00000001 nfsv4 SERVER_CAPS (sync)
mount.nfs-3331 [012] ..... 949.308249: rpc_stats_latency: task:00000005@00000001 xid=0x7b4ec2e0 nfsv4 SERVER_CAPS backlog=3 rtt=452 execute=552
mount.nfs-3331 [012] ..... 949.308249: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=6 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
mount.nfs-3331 [012] ..... 949.309855: nfs4_setup_sequence: session=0x87e0d01f slot_nr=0 seq_nr=10 highest_used_slotid=0
mount.nfs-3331 [012] ..... 949.309855: rpc_request: task:00000009@00000001 nfsv4 GETATTR (sync)
mount.nfs-3331 [012] ..... 949.310555: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=10 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
mount.nfs-3331 [012] ..... 949.310557: nfs4_getattr: error=0 (OK) fileid=00:2f:64 fhandle=0x507c44a0 valid=TYPE|MODE|NLINK|OWNER|GROUP|SIZE|FSID|FILEID|ATIME|MTIME|CTIME|CHANGE|0x400200그런 다음 클라이언트 액세스 권한을 확인하고 전체 경로 /vol_cp_5node를 확인합니다 .
mount.nfs-3331 [012] ..... 949.310575: nfs_access_enter: fileid=00:2f:64 fhandle=0x507c44a0 version=1881467492116116952
mount.nfs-3331 [012] ..... 949.310577: nfs_access_exit: error=-10 (CHILD) fileid=00:2f:64 fhandle=0x507c44a0 type=4 (DIR) version=1881467492116116952 size=4096 cache_validity=0x0 () nfs_flags=0x0 () mask=0x81 permitted=0xffffffff
mount.nfs-3331 [012] ..... 949.310577: nfs_access_enter: fileid=00:2f:64 fhandle=0x507c44a0 version=1881467492116116952
mount.nfs-3331 [012] ..... 949.310580: nfs4_setup_sequence: session=0x87e0d01f slot_nr=0 seq_nr=11 highest_used_slotid=0
mount.nfs-3331 [012] ..... 949.310580: rpc_request: task:0000000a@00000001 nfsv4 ACCESS (sync)
mount.nfs-3331 [012] ..... 949.311414: rpc_stats_latency: task:0000000a@00000001 xid=0x804ec2e0 nfsv4 ACCESS backlog=3 rtt=688 execute=834
mount.nfs-3331 [012] ..... 949.311415: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=11 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
mount.nfs-3331 [012] ..... 949.311421: nfs4_access: error=0 (OK) fileid=00:2f:64 fhandle=0x507c44a0
mount.nfs-3331 [012] ..... 949.311422: nfs_access_exit: error=0 (OK) fileid=00:2f:64 fhandle=0x507c44a0 type=4 (DIR) version=1881467492116116952 size=4096 cache_validity=0x0 () nfs_flags=0x4 (ACL_LRU_SET) mask=0x1 permitted=0x7
mount.nfs-3331 [012] ..... 949.311428: nfs_lookup_enter: flags=0x5 (FOLLOW|AUTOMOUNT) name=00:2f:64/vol_cp_5node fileid=0
mount.nfs-3331 [012] ..... 949.311429: nfs4_setup_sequence: session=0x87e0d01f slot_nr=0 seq_nr=12 highest_used_slotid=0
mount.nfs-3331 [012] ..... 949.312312: nfs4_sequence_done: error=0 (OK) session=0x87e0d01f slot_nr=0 seq_nr=12 highest_slotid=63 target_highest_slotid=63 status_flags=0x0 ()
mount.nfs-3331 [012] ..... 949.312317: nfs4_lookup: error=0 (OK) name=00:2f:64/vol_cp_5node
mount.nfs-3331 [012] ..... 949.312320: nfs_lookup_exit: error=0 (OK) flags=0x5 (FOLLOW|AUTOMOUNT) name=00:2f:64/vol_cp_5node fileid=105마지막으로 마운트가 성공적으로 완료되고 RPC 리소스가 해제됩니다.
mount.nfs-3331 [012] ..... 949.315199: nfs_mount_option: option source
mount.nfs-3331 [012] ..... 949.315201: nfs_mount_path: path='/vol_cp_5node'
mount.nfs-3331 [012] ..... 949.316737: rpc_clnt_shutdown: client=00000002
mount.nfs-3331 [012] ..... 949.316738: rpc_clnt_release: client=00000002
kworker/12:1-450 [012] ..... 949.316760: rpc_clnt_free: client=00000002
mount.nfs-3331 [012] ..... 949.328981: rpc_clnt_shutdown: client=00000001
mount.nfs-3331 [012] ..... 949.328990: rpc_clnt_release: client=00000001
kworker/12:1-450 [012] ..... 949.329007: rpc_clnt_free: client=00000001추적 메시지가 잔뜩 쌓였는데, 이제 뭘 해야 할까?
추적 메시지 순서를 분석하여 통신 흐름을 이해하는 것은 저에게 매우 중요합니다. 배포 및 성능 문제 해결 능력이 크게 향상됩니다. 몇 가지 실제 사례를 들어 보겠습니다.
NFS 마운트 명령을 실행했는데 명확한 이유 없이 중단되는 경우를 가정해 보겠습니다. 이 경우 명령을 종료할 수 있습니다. 다음으로, sunrpc 및 nfs 하위 시스템에 대한 추적 기능을 활성화하고 마운트 작업을 다시 실행하십시오. 추적 기능을 활성화하면 통신 흐름을 관찰하고 프로세스가 어디에서 중단되는지 정확히 파악할 수 있습니다.
이제 사용자 활동이 미묘하게 증가하여 시스템이 평소보다 더 많은 I/O를 수행하기 시작했다고 가정해 보겠습니다. NFS 하위 시스템에 대한 추적을 활성화하고 nfs4_setup_sequence 및 nfs4_sequence_done 추적 이벤트에서 highest_slotid , target_highest_slotid , highest_used_slotid를 모니터링할 수 있습니다 .
nfs4_setup_sequence의 highest_used_slotid 가 nfs4_sequence_done에서 보고된 highest_slotid 와 자주 일치하는 경우, 시스템에 세션 슬롯 병목 현상이 발생했음을 나타냅니다. 이러한 경우, 동시성과 성능을 개선하기 위해 사용 가능한 슬롯 수를 늘리는 것을 고려해야 합니다. NFSv4 슬롯 테이블이 무엇이고 어떻게 작동하는지 모르시겠습니까? 걱정하지 마세요. 이 시리즈의 다음 게시물에서 자세히 설명하겠습니다.
추적은 단순히 로그를 수집하는 것이 아닙니다. 시스템 내부 작동 방식을 파악하는 것입니다. 멈춘 마운트 문제를 해결하든 세션 슬롯 병목 현상을 진단하든, 추적을 통해 추측에서 벗어나 정보에 기반한 조치를 취할 수 있습니다. 이러한 메시지를 더 잘 이해할수록 시스템을 더욱 효율적으로 조정하고 확장할 수 있습니다.
마무리하기
다음 게시물에서는 pNFS가 활성화된 NFS 서버에서 파일을 읽거나 쓸 때 실제로 어떤 일이 일어나는지 살펴보겠습니다. 데이터 경로를 추적하고, 레이아웃 작업의 작동 방식을 살펴보고, 클라이언트가 여러 데이터 서버와 협력하여 성능을 향상시키는 방법을 알아보겠습니다. NFS가 노드 간 I/O를 어떻게 확장하는지 궁금하셨다면, ” Back to Basics” 시리즈의 다음 게시물을 기대해 주세요.
참고자료
[1] Nowicki, B. (1989). RFC 1094: NFS Network File System Protocol Specification. Available from: <https://www.rfc-editor.org/rfc/pdfrfc/rfc1094.txt.pdf>
[2] Callaghan, B., Pawlowaki, B., Staubach, P. (1995). RFC 1813: NFS Version 3 Protocol Specification. Available from: <https://www.rfc-editor.org/rfc/pdfrfc/rfc1813.txt.pdf>
[3] Shepler, S., et al. (2003). RFC 3530: Network File System (NFS) version 4 Protocol. Available from: <https://www.rfc-editor.org/rfc/pdfrfc/rfc3530.txt.pdf>
[4] Shepler, S., Eisler, M., Noveck, D. (2010). RFC 5661: Network File System (NFS) Version 4 Minor Version 1 Protocol. Available from: <https://www.rfc-editor.org/rfc/pdfrfc/rfc5661.txt.pdf>
[5] Haynes, T. (2016). RFC 7862: Network File System (NFS) Version 4 Minor Version 2 Protocol. Available from: <https://www.rfc-editor.org/rfc/pdfrfc/rfc7862.txt.pdf>