Files
obsidian/infra/nas-storage.md
kappa 72750cfc9d obsidian: 정본에서 변경 이력 분리, history/ 도입
- history/README.md: 변경 이력/인시던트 기록 규약
- history/2026-04-10-edge-cleanup.md: 오늘의 엣지 정리·인시던트·복구 전체 연대기
- infra/cloudflare.md: 연대기 주석/strikethrough/인시던트 서사 제거, 현재 사실만
- infra/crowdsec-safeline.md: 인시던트 bullet 제거, 과거 이력은 history/ 참조
- services/bunnycdn-security.md: sitekey 이력표 제거, 현재 위젯 정보만
- infra/nas-storage.md: reverse proxy 섹션의 날짜 주석 제거
2026-04-10 11:38:36 +09:00

9.5 KiB

title, updated, tags
title updated tags
NAS StorageClass (NFS + iSCSI) 2026-04-10
infra
k3s
storage
nfs
iscsi
synology

개요

Synology NAS를 K3s NFS StorageClass로 사용. Longhorn(블록)과 병행하여 파일 저장소, 웹소스 등 대용량/RWX 워크로드에 사용.

NAS 정보

항목
장비 Synology NAS
IP (kr1/kr2) 192.168.205.100
IP (hp2) 192.168.9.100 (현재 미연결, 해결 예정)
디스크 11TB (사용 2%)
NFS export /volume1/k3s-nfs
NFS 옵션 rw,async,no_wdelay,crossmnt,no_root_squash,insecure_locks,sec=sys,anonuid=1025,anongid=100

K3s 설정

항목
StorageClass nfs (default 아님, 명시 지정 필요)
Provisioner nfs-subdir-external-provisioner (Helm)
Namespace nfs-provisioner
NFS 경로 /volume1/k3s-nfs
마운트 옵션 soft,timeo=50,retrans=3
archiveOnDelete true (PVC 삭제 시 데이터 archived- 접두사로 보존)
nodeAffinity incus-kr1, incus-kr2만 (hp2 NAS 연결 해결 전까지)

Helm 설치 명령

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --namespace nfs-provisioner --create-namespace \
  --set nfs.server=192.168.205.100 \
  --set nfs.path=/volume1/k3s-nfs \
  --set storageClass.name=nfs \
  --set storageClass.defaultClass=false \
  --set storageClass.reclaimPolicy=Delete \
  --set storageClass.archiveOnDelete=true \
  --set nfs.mountOptions[0]=soft \
  --set nfs.mountOptions[1]=timeo=50 \
  --set nfs.mountOptions[2]=retrans=3

PVC 사용 예시

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-data
spec:
  storageClassName: nfs
  accessModes: [ReadWriteMany]
  resources:
    requests:
      storage: 10Gi

성능 (fio 벤치마크, 2026-04-05)

테스트 IOPS 대역폭 레이턴시
Random Read 4K 20.6K 84 MB/s 1.5 ms
Random Write 4K 18.1K 74 MB/s 1.8 ms
Seq Read 1M 263 276 MB/s 30 ms
Seq Write 1M 275 288 MB/s 29 ms

용도별 StorageClass 선택

용도 StorageClass 이유
DB (PostgreSQL, etcd, Redis) longhorn 저레이턴시 블록 필요
파일 업로드, 사용자 데이터 nfs 대용량, RWX 지원
웹소스, 정적 파일 nfs 대용량, 여러 Pod 공유
로그, 임시 데이터 longhorn/local-path 빠른 쓰기

파일 소유권

  • no_root_squash 설정으로 root 컨테이너는 소유권 문제 없음
  • 비-root 컨테이너는 Pod securityContext.fsGroup으로 제어

NFS hard vs soft 교훈 (2026-04-04)

kr2에서 NAS NFS가 hard 마운트 + NAS 연결 끊김으로 load 1959까지 폭주한 사건 발생. D-state 프로세스(mountpoint, NFS manager)가 커널 전체를 잠식.

  • hard: NAS 끊기면 무한 대기 → 서버 먹통
  • soft: 타임아웃 후 에러 반환 → 서버 생존

모든 NFS 마운트는 soft,timeo=50,retrans=3 필수.

iSCSI StorageClass (democratic-csi)

Synology NAS의 iSCSI를 K3s 블록 스토리지로 사용. democratic-csi가 PVC 생성/삭제 시 자동으로 iSCSI Target + LUN을 관리.

항목
StorageClass synology-iscsi
CSI Driver democratic-csi (Helm)
Namespace democratic-csi
iSCSI Portal 192.168.205.100:3260
Base IQN iqn.2000-01.com.synology:NAS.k3s.
LUN 타입 BLUN (Btrfs thin provisioning)
Volume /volume1
인증 kaffa 계정 (HTTPS API)
자동 관리 PVC 생성 → Target+LUN 생성, PVC 삭제 → Target+LUN 삭제

Helm 설치

values 파일: /tmp/democratic-csi-values.yaml (kr1)

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
helm repo add democratic-csi https://democratic-csi.github.io/charts/
helm install synology-iscsi democratic-csi/democratic-csi \
  --namespace democratic-csi --create-namespace \
  -f /tmp/democratic-csi-values.yaml

전체 StorageClass 요약

StorageClass 방식 용도 HA
longhorn 로컬 NVMe 블록 DB, 고성능 블록 노드 간 레플리카
synology-iscsi NAS iSCSI 블록 블록 스토리지 (NAS) RAID5
nfs NAS NFS 파일 파일, 웹소스, RWX RAID5
local-path 로컬 디스크 캐시, 임시 없음

Jumbo Frame (MTU 9000) 설정 (2026-04-05)

모든 NAS 전용 경로에 MTU 9000 적용. 스위치 포함 전체 경로가 JF 지원.

장비 인터페이스 MTU 설정 위치
kr1 enp7s0 (PCIe 2.5GbE) 9000 /etc/systemd/network/20-enp7s0.network
kr2 enx803f5dd34c9f (USB 2.5GbE) 9000 /etc/systemd/network/30-usb-2g5.network
NAS eth2 (2.5GbE) 9000 Synology DSM 네트워크 설정

kr2 USB NIC 드라이버 (r8152 DKMS)

kr2의 USB 2.5GbE 어댑터(Realtek RTL8157, 0bda:8157)는 커널 기본 cdc_ncm 드라이버로 잡히면 Half Duplex + MTU 1500 제한. DKMS r8152 드라이버(v2.21.4)를 설치하여 Full Duplex + Jumbo Frame 지원.

  • DKMS 패키지: linux-headers-$(uname -r) 설치 시 자동 빌드
  • udev 규칙: /etc/udev/rules.d/50-usb-realtek-net.rules — USB config를 1로 설정하여 r8152가 바인딩
  • 커널 업데이트 시 DKMS가 자동으로 재빌드

주의사항

  • Jumbo Frame은 경로 전체(NIC → 스위치 → NIC)가 지원해야 함. 미지원 스위치가 중간에 있으면 프레임 드롭
  • ping -M do -s 8972 로 end-to-end JF 동작 확인 가능

DSM Reverse Proxy — nas.inouter.com

LAN 전용으로 Synology DSM 웹 UI 접근. nas.inouter.com*.inouter.com 와일드카드 폴백으로 k3s.inouter.com (192.168.9.53, Traefik MetalLB VIP) 로 해석되고, Traefik 에서 이 호스트를 Synology NAS 로 reverse proxy 한다.

항목
Namespace lan-proxies (신규 — LAN-only 리버스 프록시 전용)
Service nas (selector 없는 ClusterIP) + EndpointSlice → 192.168.9.100:5000
Backend Synology DSM HTTP (포트 5000, 평문)
Traefik IngressRoute nas (web entrypoint, redirect → https), nas-tls (websecure entrypoint)
Middleware redirect-https (redirectScheme.scheme=https permanent=true)
TLS 기존 wildcard wildcard-inouter-tls (cert-manager + emberstack reflector 자동 복제)
외부 노출 LAN-only — 와일드카드 *.inouter.com 가 사설 IP 192.168.9.53 로 폴백되므로 인터넷 라우팅 없음

ExternalName 대신 Service+EndpointSlice 사용 이유

요구사항은 "ExternalName Service" 였으나 Kubernetes ExternalName 은 RFC1123 DNS 호스트네임만 허용하고 IP(192.168.9.100)는 거부. 동등한 K8s-native 패턴인 selector-less Service + 수동 EndpointSlice 로 구현. CoreDNS 가 ClusterIP 를 정상 발행하고 kube-proxy 가 EndpointSlice 의 외부 IP 로 분산.

매니페스트

원본: /tmp/nas-reverse-proxy.yaml (heimdall). 핵심 발췌 — 전체 6 객체.

apiVersion: v1
kind: Namespace
metadata:
  name: lan-proxies
  labels:
    purpose: lan-only-reverse-proxy
---
apiVersion: v1
kind: Service
metadata: { name: nas, namespace: lan-proxies }
spec:
  type: ClusterIP
  ports:
    - { name: dsm-http, port: 5000, targetPort: 5000, protocol: TCP }
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: nas
  namespace: lan-proxies
  labels: { kubernetes.io/service-name: nas }
addressType: IPv4
ports:
  - { name: dsm-http, port: 5000, protocol: TCP }
endpoints:
  - addresses: [192.168.9.100]
    conditions: { ready: true }
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: redirect-https, namespace: lan-proxies }
spec:
  redirectScheme: { scheme: https, permanent: true }
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata: { name: nas, namespace: lan-proxies }
spec:
  entryPoints: [web]
  routes:
    - kind: Rule
      match: Host(`nas.inouter.com`)
      middlewares: [{ name: redirect-https }]
      services: [{ name: nas, port: 5000 }]
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata: { name: nas-tls, namespace: lan-proxies }
spec:
  entryPoints: [websecure]
  routes:
    - kind: Rule
      match: Host(`nas.inouter.com`)
      services: [{ name: nas, port: 5000 }]
  tls:
    secretName: wildcard-inouter-tls

동작 검증

curl -sk --resolve nas.inouter.com:80:192.168.9.53  http://nas.inouter.com/   # → 301 https://nas.inouter.com/
curl -sk --resolve nas.inouter.com:443:192.168.9.53 https://nas.inouter.com/  # → 200, 12094B
#   <title>NAS - Synology DiskStation</title>

주의사항

  • LAN-only. *.inouter.com 가 사설 192.168.9.53 으로 가는 한 외부 인터넷에서는 도달 불가. 외부 노출 시 보안/인증 재검토 필요 (Synology DSM 직노출은 CVE 노출면 큼).
  • DSM HTTPS(5001) 도 동일 호스트에서 200 응답하지만 자기서명 인증서 + 이중 TLS 종료라 백엔드는 평문 5000 사용.
  • Traefik 리버스 프록시 패턴은 lan-proxies ns 에 추가 LAN 서비스(예: 스위치/라우터/NAS 부속 UI) 적층 가능.

관련 문서