在 Kubernetes 中遇见 NFS 存储

当在 K8s 中使用到 NFS 类型的存储时,首先需要搭建一个 NFS 服务,另外注意一个挂载路径问题,就能愉快地玩耍它啦。安装NFS服务sudo apt install -y nfs-kernel-server rpcbind创建目录 ~/nfs 作为共享目录,并在该目录下,新建一个文件 date

当在 K8s 中使用到 NFS 类型的存储时,首先需要搭建一个 NFS 服务,另外注意一个挂载路径问题,就能愉快地玩耍它啦。

安装NFS服务

sudo apt install -y nfs-kernel-server rpcbind

创建目录 ~/nfs 作为共享目录,并在该目录下,新建一个文件 date > time.txt

配置NFS服务

添加共享目录配置:/etc/exports

# /etc/exports: the access control list for filesystems which may be exported
#		to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/home/ubuntu/nfs *(rw,sync,no_root_squash)

访问权限选项

  • rw 可读写
  • ro 只读

用户映射选项

  • all_squash 将远程访问的所有普通用户及所属组都映射为匿名用户或用户组(nfsnobody)
  • no_all_squash:与all_squash取反(默认设置)
  • root_squash:将root用户及所属组都映射为匿名用户或用户组(默认设置)
  • no_root_squash:与rootsquash取反
  • anonuid=xxx:将远程访问的所有用户都映射为匿名用户,并指定该用户为本地用户(UID=xxx)
  • anongid=xxx:将远程访问的所有用户组都映射为匿名用户组账户,并指定该匿名用户组账户为本地用户组账户(GID=xxx)

其它选项

  • secure:限制客户端只能从小于1024的tcp/ip端口连接nfs服务器(默认设置)
  • insecure:允许客户端从大于1024的tcp/ip端口连接服务器
  • sync:将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性
  • async:将数据先保存在内存缓冲区中,必要时才写入磁盘
  • wdelay:检查是否有相关的写操作,如果有则将这些写操作一起执行,这样可以提高效率(默认设置)
  • no_wdelay:若有写操作则立即执行,应与sync配合使用
  • subtree:若输出目录是一个子目录,则nfs服务器将检查其父目录的权限(默认设置)
  • no_subtree:即使输出目录是一个子目录,nfs服务器也不检查其父目录的权限,这样可以提高效率

其他操作

启动/重启NFS服务:/etc/init.d/nfs-kernel-server

sudo /etc/init.d/nfs-kernel-server start
sudo /etc/init.d/nfs-kernel-server restart

刷新NFS配置:exportfs

# 重新读取 /etc/exports 配置信息
exportfs -r

查看共享的目录:showmount

# 本机共享目录
showmount -e
# 已连上的NFS服务的共享目录
showmount -a
# 查询指定服务器上共享的目录
showmount -e <nfs-ip>

在别的机器上面进行 mount 操作

sudo mount -t nfs <nfs-server-ip>:/home/ubuntu/nfs /data/backups -o nolock

进入该目录,可以看到之前的 time.txt文件,进行修改后,可在服务器所在机器上看到变更。

$ cd /data/backups
$ ls
pvc-1e8ff556-d183-4471-92cd-3869ffbcb589  pvc-9f429e24-c6da-45c9-9860-f4e338f61068  time.txt
$ cat time.txt
Mon Jul 11 01:19:44 AM UTC 2022

卸载目录

sudo umount /data/backups

权限问题说明

  1. 客户端连接时候,对普通用户的检查
  • 如果明确设定了普通用户被映射的身份,那么此时客户端用户的身份转换为指定用户;
  • 如果NFS server上面有同名用户,那么此时客户端登录账户的身份转换为NFS server上面的同名用户;
  • 如果没有明确指定,也没有同名用户,那么此时用户身份被映射成nfsnobody;
  1. 客户端连接的时候,对root的检查
  • 如果设置no_root_squash,那么此时root用户的身份被映射为NFS server上面的root;
  • 如果设置了all_squash、anonuid、anongid,此时root 身份被映射为指定用户;
  • 如果没有明确指定,此时root用户被映射为nfsnobody;
  • 如果同时指定no_root_squash与all_squash 用户将被映射为 nfsnobody,如果设置了anonuid、anongid将被映射到所指定的用户与组;

Kubernetes中添加NFS支持

添加 StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: <server-ip>
  share: /
  mountPermissions: "0777"
  # csi.storage.k8s.io/provisioner-secret is only needed for providing mountOptions in DeleteVolume
  # csi.storage.k8s.io/provisioner-secret-name: "mount-options"
  # csi.storage.k8s.io/provisioner-secret-namespace: "default"
reclaimPolicy: Delete
volumeBindingMode: Immediate

遇到的坑

如果 NFS 服务 /etc/exports 里设置的共享路径是 /home/ubuntu/nfs,在 StorageClassshare 字段如果填 / 的话,挂载时会报权限不足。

通过 csi-driver-nfs 的日志可以发现,在创建 PV 时,用的路径是 /pod-xxxxxx

Pod 启动失败,报 0/1 nodes are available: 1 pod has unbound immediate PersistentVolumeClaims

PVC 处于 Pending 状态,报 failed to provision volume with StorageClass "nfs-csi": rpc error: code = Internal desc = failed to make subdirectory: mkdir /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2: read-only file system

csi-nfs-controller 日志:

I0720 09:53:52.378454       1 controllerserver.go:285] internally mounting 192.168.59.102:/ at /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2
I0720 09:53:52.378935       1 controllerserver.go:300] internally unmounting /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2
I0720 09:53:52.378942       1 nodeserver.go:163] NodeUnpublishVolume: unmounting volume 192.168.59.102##pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2# on /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2
I0720 09:53:52.378955       1 mount_helper_common.go:99] "/tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2" is a mountpoint, unmounting
I0720 09:53:52.378959       1 mount_linux.go:294] Unmounting /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2
W0720 09:53:52.387057       1 mount_helper_common.go:133] Warning: "/tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2" is not a mountpoint, deleting
I0720 09:53:52.387102       1 nodeserver.go:168] NodeUnpublishVolume: unmount volume 192.168.59.102##pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2# on /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2 successfully
E0720 09:53:52.387108       1 utils.go:93] GRPC error: rpc error: code = Internal desc = failed to make subdirectory: mkdir /tmp/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2/pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2: read-only file system
I0720 09:54:48.201408       1 utils.go:88] GRPC call: /csi.v1.Controller/CreateVolume
I0720 09:54:48.201568       1 utils.go:89] GRPC request: {"capacity_range":{"required_bytes":1000000000},"name":"pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2","parameters":{"csi.storage.k8s.io/pv/name":"pvc-ca0ab165-9aa5-42a4-ae15-f4fd036142d2","csi.storage.k8s.io/pvc/name":"data-primary-mariadb-cluster-primary-0","csi.storage.k8s.io/pvc/namespace":"default","mountPermissions":"0777","server":"192.168.59.102","share":"/"},"volume_capabilities":[{"AccessType":{"Mount":{}},"access_mode":{"mode":7}}]}

在其他 Linux 机器上面,使用 mount 命令尝试挂载:

$ sudo mount -t nfs 192.168.59.102:/ /tmp/nfs
$ ls /tmp/nfs/
home
$ ls /tmp/nfs/home
ubuntu
$ ls /tmp/nfs/home/ubuntu
nfs
$ ls /tmp/nfs/home/ubuntu/nfs
pvc-1e8ff556-d183-4471-92cd-3869ffbcb589  pvc-9f429e24-c6da-45c9-9860-f4e338f61068  time.txt
看到这里本能地想到了 StorageClass share 字段应当修改成 /home/ubuntu/nfs

如果填写 /,会将 NFS 服务上的 / 挂载在 K8s 所在机器的 /tmp/pvc-xxxx 下,当在 /tmp/pvc-xxx 目录中创建子目录时,即在 NFS 服务器上的 / 下创建目录,理所应当会失败。

Reference

Linux mount命令详解:挂载Linux系统外的文件 (biancheng.net)

NFS服务配置解析详解_weixin_33753845的博客-CSDN博客

Read more

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd