Вопрос или проблема
Я пытался настроить NFS-шару на моем Synology NAS, но когда подключаю ее к своему кластеру RKE2, кажется, что доступ только для чтения или какая-то проблема с правами доступа для моих подов.
Моя текущая настройка
Synology NAS: DS923+
DSM Version: DSM 7.2.2-72806
Kubernetes node versions: v1.25.12+rke2r1 и v1.26.9+rke2r1 (кластер из трех мастер-узлов)
Kubernetes nodes OS: Ubuntu Server 22.04.5 LTS (Jammy Jellyfish)
pfSense Firewall: 2.7.0-RELEASE
Настройка брандмауэра
Брандмауэр находится между узлами и NAS с ПРАВИЛОМ РАЗРЕШЕНИЯ на этих портах ОТ Kubernetes Net К НАС Net:
548 AFP
137 CIFS/SMB
445 CIFS/SMB
20 FTP
3260 iSCSI
3263 iSCSI
3265 iSCSI
111 NFS
892 NFS
2049 NFS
69 TFTP
5005 HTTPS
138 CIFS/SMB
139 CIFS/SMB
21 FTP
5006 HTTPS
5555 Synology DiskStation Management API
875 NFS Locking
Настройка NAS
Я следовал этому руководству (virtualizationhowto) для настройки, так что настройки должны быть такими же, как показано в руководстве.
Панель управления настройками NFS:
[x] Включить службу NFS
Максимальный протокол NFS: NFSv4.1
Права доступа общего папки NFS:
Имя хоста или IP: 10.0.8.0/24
Права: Чтение/запись
Squash: Назначить всех пользователей администратору
Безопасность: sys
[x] Включить асинхронный режим
[x] Разрешить подключения с непривилегированных портов (порты выше 1024)
[x] Разрешить пользователям доступ к подключенным подпапкам
Настройки общего тома:
Имя: k8sdata
Расположение: Том 1: Btrfs
[x] скрыть эту общую папку в "Моих сетевых местах"
[ ] Скрыть подпапки и файлы от пользователей без разрешений
[ ] Включить корзину
Когда я попробовал проверить настройку NFS с помощью mount -t nfs x.x.x.x:/volume1/k8sdata /mnt, все подключилось без проблем. Я смог создавать файлы и редактировать их также.
Для настройки подключения NFS в Kubernetes:
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=x.x.x.x \
--set nfs.path=/exported/path
Настройка развертывания
Я использовал Jellyfin для этого примера, но получаю тот же результат с nginx.
PVC.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media
namespace: media
spec:
storageClassName: nfs-client
accessModes: [ReadWriteOnce]
resources: { requests: { storage: 18Ti } }
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: jellyfin-app
name: jellyfin-app
namespace: media
spec:
progressDeadlineSeconds: 600
replicas: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
app: jellyfin-app
template:
metadata:
labels:
app: jellyfin-app
spec:
nodeSelector:
cputype: kvm64
volumes:
- name: media-ssd
persistentVolumeClaim:
claimName: media
securityContext:
fsGroupChangePolicy: "OnRootMismatch"
containers:
- env:
- name: JELLYFIN_PublishedServerUrl
value: <IP одного из мастеров Kubernetes> # IP адрес вашего сервера Jellyfin (см. конфигурацию службы)
- name: PGID
value: "\x36\x35\x35\x34\x31" # < ASCII код для '65541'
- name: PUID
value: "\x31\x30\x34\x34" #< ACII код для '1044'
- name: TZ
value: Europe/Oslo
image: ghcr.io/linuxserver/jellyfin:10.9.11
imagePullPolicy: Always
name: jellyfin-app
ports:
- containerPort: 8096
name: http-tcp
protocol: TCP
- containerPort: 8920
name: https-tcp
protocol: TCP
- containerPort: 1900
name: dlna-udp
protocol: UDP
- containerPort: 7359
name: discovery-udp
protocol: UDP
resources: {}
stdin: true
tty: true
volumeMounts:
- mountPath: /config
subPath: "configs/jellyfin"
name: media-ssd
- mountPath: /data
subPath: "downloads/transmission"
name: media-ssd
- mountPath: /usr/share/jellyfin/web
subPath: "configs/jellyfin-web/"
name: media-ssd
dnsPolicy: ClusterFirst
restartPolicy: Always
Я пытался менять значения PGID и PUID, но безуспешно.
id admin
на Synology показывает:
uid=1024(admin) gid=100(users) groups=100(users),101(administrators)
и другой пользователь, которого я использую на Synology, имеет uid=1026 с тем же gid и группами.
Поэтому я также пробовал такую конфигурацию в развертывании:
- name: PGID
value: "\x31\x30\x31" #101 #"\x36\x35\x35\x34\x31" # < ASCII код для '65541'
- name: PUID
value: "\x31\x30\x32\x34" # 1026 #"\x31\x30\x34\x34" #< ACII код для '1044'
service.yaml
kind: Service
apiVersion: v1
metadata:
name: jellyfin-app-tcp # < имя службы
namespace: media # < пространство имен, где размещается служба
spec:
selector:
app: jellyfin-app # < ссылка на развертывание (соединяет службу с развертыванием)
ports:
- port: 80 # < порт для открытия снаружи на сервере
targetPort: 8096 # < целевой порт. порт на поде для прохода
name: http-tcp # < справочное имя для порта в yaml-ке развертывания
protocol: TCP
- port: 8920
targetPort: 8920
name: https-tcp
type: ClusterIP
sessionAffinity: ClientIP
certificate-ingress.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cert-ingress
namespace: media
spec:
secretName: cert-ingress
issuerRef:
name: acme-issuer
kind: ClusterIssuer
dnsNames:
- REDACTED.REDACTED.com
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jellyfin-wan
namespace: media
spec:
ingressClassName: nginx
tls:
- hosts:
- REDACTED.REDACTED.com
secretName: cert-ingress
rules:
- host: REDACTED.REDACTED.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jellyfin-app-tcp
port:
number: 80
Описание PVC:
Name: media
Namespace: media
StorageClass: nfs-client
Status: Bound
Volume: pvc-acd165ef-2224-4691-a2a1-bfd84f3f5382
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: cluster.local/nfs-subdir-external-provisioner
volume.kubernetes.io/storage-provisioner: cluster.local/nfs-subdir-external-provisioner
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 18Ti
Access Modes: RWO
VolumeMode: Filesystem
Used By: jellyfin-app-6cf67755b4-r2hrr
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ExternalProvisioning 48m (x2 over 48m) persistentvolume-controller waiting for a volume to be created, either by external provisioner "cluster.local/nfs-subdir-external-provisioner" or manually created by system administrator
Normal Provisioning 48m cluster.local/nfs-subdir-external-provisioner_nfs-subdir-external-provisioner-7ddbf6bcf9-sxg9j_f5477ff7-35be-4b93-9d82-de781bf7af56 External provisioner is provisioning volume for claim "media/media"
Normal ProvisioningSucceeded 48m cluster.local/nfs-subdir-external-provisioner_nfs-subdir-external-provisioner-7ddbf6bcf9-sxg9j_f5477ff7-35be-4b93-9d82-de781bf7af56 Successfully provisioned volume pvc-acd165ef-2224-4691-a2a1-bfd84f3f5382
Вывод kubectl logs -n media jellyfin-app-6cf67755b4-r2hrr
:
14:21:12] [ERR] [1] Main: Ожидается, что сервер будет размещать веб-клиент, но предоставленный каталог содержимого либо недействителен, либо пустой: /usr/share/jellyfin/web. Если вы не хотите размещать веб-клиент на сервере, вы можете установить флаг командной строки '--nowebclient', или установить 'hostwebclient=false' в настройках конфигурации
[14:21:13] [INF] [1] Main: Версия Jellyfin: 10.9.11
[14:21:13] [INF] [1] Main: Переменные окружения: ["[JELLYFIN_APP_TCP_SERVICE_PORT, 80]", "[JELLYFIN_APP_TCP_PORT_80_TCP_PROTO, tcp]", "[JELLYFIN_TCP_PORT, tcp://10.43.107.94:8096]", "[JELLYFIN_LOG_DIR, /config/log]", "[JELLYFIN_TCP_PORT_8096_TCP_PORT, 8096]", "[JELLYFIN_APP_TCP_SERVICE_HOST, 10.43.45.121]", "[JELLYFIN_CONFIG_DIR, /config]", "[JELLYFIN_APP_TCP_PORT_80_TCP_PORT, 80]", "[JELLYFIN_CACHE_DIR, /config/cache]", "[JELLYFIN_APP_TCP_PORT_8920_TCP_ADDR, 10.43.45.121]", "[JELLYFIN_TCP_SERVICE_HOST, 10.43.107.94]", "[JELLYFIN_PublishedServerUrl, 10.0.8.2]", "[JELLYFIN_TCP_SERVICE_PORT, 8096]", "[JELLYFIN_APP_TCP_PORT_8920_TCP_PORT, 8920]", "[JELLYFIN_TCP_SERVICE_PORT_HTTP_TCP, 8096]", "[JELLYFIN_TCP_PORT_8096_TCP_PROTO, tcp]", "[JELLYFIN_TCP_PORT_8096_TCP_ADDR, 10.43.107.94]", "[JELLYFIN_APP_TCP_PORT, tcp://10.43.45.121:80]", "[JELLYFIN_APP_TCP_PORT_80_TCP, tcp://10.43.45.121:80]", "[JELLYFIN_APP_TCP_PORT_8920_TCP_PROTO, tcp]", "[JELLYFIN_APP_TCP_SERVICE_PORT_HTTPS_TCP, 8920]", "[JELLYFIN_TCP_PORT_8096_TCP, tcp://10.43.107.94:8096]", "[JELLYFIN_DATA_DIR, /config/data]", "[JELLYFIN_WEB_DIR, /usr/share/jellyfin/web]", "[JELLYFIN_APP_TCP_SERVICE_PORT_HTTP_TCP, 80]", "[JELLYFIN_APP_TCP_PORT_80_TCP_ADDR, 10.43.45.121]", "[JELLYFIN_APP_TCP_PORT_8920_TCP, tcp://10.43.45.121:8920]"]
[14:21:13] [INF] [1] Main: Аргументы: ["/usr/lib/jellyfin/bin/jellyfin.dll", "--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg"]
[14:21:13] [INF] [1] Main: Операционная система: Ubuntu 24.04.1 LTS
[14:21:13] [INF] [1] Main: Архитектура: X64
[14:21:13] [INF] [1] Main: 64-битный процесс: True
[14:21:13] [INF] [1] Main: Взаимодействие с пользователем: True
[14:21:13] [INF] [1] Main: Количество процессоров: 10
[14:21:13] [INF] [1] Main: Путь к данным программы: /config/data
[14:21:13] [INF] [1] Main: Путь к каталогу логов: /config/log
[14:21:13] [INF] [1] Main: Путь к каталогу конфигураций: /config
[14:21:13] [INF] [1] Main: Путь к кэшу: /config/cache
[14:21:13] [INF] [1] Main: Путь к веб-ресурсам: /usr/share/jellyfin/web
[14:21:13] [INF] [1] Main: Каталог приложения: /usr/lib/jellyfin/bin/
[14:21:13] [ERR] [1] Main: Ожидается, что сервер будет размещать веб-клиент, но предоставленный каталог содержимого либо недействителен, либо пустой: /usr/share/jellyfin/web. Если вы не хотите размещать веб-клиент на сервере, вы можете установить флаг командной строки '--nowebclient', или установить 'hostwebclient=false' в настройках конфигурации
Папки создаются, но файлы практически не создаются.
tree /mnt/media-media-pvc-acd165ef-2224-4691-a2a1-bfd84f3f5382
.
├── configs
│ ├── jellyfin
│ │ ├── cache
│ │ ├── data
│ │ │ ├── data
│ │ │ ├── plugins
│ │ │ │ └── configurations
│ │ │ └── transcodes
│ │ ├── log
│ │ │ └── log_20250208.log
│ │ └── logging.default.json
│ └── jellyfin-web
└── downloads
└── transmission
12 directories, 2 files
Вопросы
Так что, думаю, мои вопросы следующие:
-
Почему я получаю доступ только для чтения/проблемы с разрешениями на NFS-монтаж в Kubernetes?
-
Есть ли какие-либо конфигурации, которые я упускаю в экспорте NFS или настройке Kubernetes, которые могли бы вызвать это?
-
Может ли проблема быть связана с разрешениями файловой системы на Synology NAS или чем-то в Kubernetes (например, securityContext, fsGroup и т.д.)?
Любая помощь будет очень полезна!!
Ответ или решение
Для успешного подключения Synology NAS к вашему кластеру RKE2 на Kubernetes, важно разобраться в нескольких аспектах, связанных с конфигурацией NFS и параметрами безопасности как на стороне кластера, так и на стороне NAS. Рассмотрим основные причины и пути решения проблемы доступа, с которой вы столкнулись.
Теория
-
Аутентификация и авторизация в NFS:
- NFS (Network File System) — это сетевой протокол, позволяющий серверу и клиенту обмениваться файлами. Одной из ключевых особенностей NFS является использование uid и gid для контроля прав доступа. Проблема часто возникает из-за несоответствия идентификаторов пользователей и групп (PUID и PGID) между контейнерами Kubernetes и сервером NAS.
-
Параметры доступа (accessModes):
- В Kubernetes, PersistentVolumeClaims (PVC) имеют различные режимы доступа, такие как ReadWriteOnce (RWO), ReadOnlyMany (ROX) и ReadWriteMany (RWX). Неправильный выбор accessModes может ограничить доступ к ресурсу.
-
SecurityContext и fsGroup:
- Эти параметры управляют тем, как контейнеры взаимодействуют с файловой системой. Важным аспектом является назначение правильного fsGroup, чтобы обеспечить доступ контейнеров к файлам с нужными правами.
Пример
Ваши настройки указаны следующим образом:
- NAS: Вы используете Synology DS923+ с DSM версии 7.2.2. NFS настроен на NFSv4.1.
- Кластер Kubernetes: С версии RKE2 установлены несколько мастер-узлов на Ubuntu Server 22.04.
Ваши действия:
- Конфигурация NFS на Synology позволяет доступы для IP-адресов в диапазоне 10.0.8.0/24, с правами чтения и записи, и, что важно, вы установили squash для отображения всех пользователей как admin.
- Вы используете helm-чарт
nfs-subdir-external-provisioner
для установки внешнего провайдера на кластер Kubernetes. Однако, столкнулись с проблемой только чтения на уровне контейнеров.
Применение
Рекомендации по устранению проблемы:
-
Проверка разрешений NFS на NAS:
- Убедитесь, что файлы и директории на NAS имеют правильные права доступа. Убедитесь, что uid=1024 и gid=100 действительно обладают правами на чтение и запись в указанные директории.
- Проверьте, что параметр squash установлен корректно. Map all users to admin может создавать конфликт, если идентификаторы пересекаются с уже имеющимися.
-
Корректировка SecurityContext и fsGroup в Kubernetes:
- Убедитесь, что в ваших деплойментах securityContext указан правильно и что используется корректное значение fsGroup, соответствующее gid пользователя NAS (что, судя по логам, gid=100).
-
Тестирование доступа вне Kubernetes:
- Подключитесь к NFS-серверу напрямую из любой машины в сети (например, из узла кластера), как это было сделано через команду
mount
, для проверки на уровне ОС, чтобы изолировать проблему между сетевым доступом и Kubernetes.
- Подключитесь к NFS-серверу напрямую из любой машины в сети (например, из узла кластера), как это было сделано через команду
-
Обновление статуса PVC:
- Проверьте статус PVC и объемы журнала событий для любой ошибки или предупреждений. Это даст больше информации о том, какие проблемы возникают при первичном подключении.
-
Отладка параметров helm-чарта:
- Перепроверьте параметры, заданные в helm-команде для развертывания nfs-subdir-external-provisioner, убедитесь, что адрес сервера и путь не содержат ошибок и соответствуют тем, что работают при ручном подключении NFS.
Потенциальные улучшения:
-
Включите и проверьте логи как на стороне NFS-сервера, так и внутри Kubernetes. Это может дать более информативную подсказку относительно проблем с переговорами и аутентификацией протоколов.
-
Опыт других пользователей показывает, что иногда помогает установка NFS обратно на более стабильный NFSv3.
В результате, целостный подход к анализу всех уровней взаимодействия — от сети до параметров NFS, а также внутренняя политика управления доступом и безопасности Kubernetes, помогут устранить текущие проблемы с доступом к Synology NAS.