NFS
NFS
1.NFS简介
1.1 什么是NFS
NFS,是Network File System的简写,即网络文件系统。网络文件系统是FreeBSD支持的文件系统中的一种。NFS允许一个系统在网络(一般是局域网)上的计算机之间通过TCP/IP网络共享资源。通过使用NFS,用户和应用程序可以像访问本地文件一样访问远端系统上的文件。
NFS很像Windows系统的网络共享、安全功能、网络驱动器映射,这也和Linux系统里的samba服务类似。只不过一般情况下,Windows网络共享服务或samba服务用于办公局域网共享,而互联网中小型网站集群架构后端常用NFS数据共享。nfs适用于Linux与Unix之间实现文件共享,不能实现Linux与Windows间的文件共享功能。
1.2 NFS的优点
-
节省本地存储空间,将常用的数据存放在一台NFS服务器上且可以通过网络访问,那么本地终端将可以减少自身存储空间的使用
-
用户不需要在网络中的每个机器上都建有Home目录,Home目录可以放在NFS服务器上且可以在网络上被访问使用
-
一些存储设备如软驱、CDROM和Zip(一种高储存密度的磁盘驱动器与磁盘)等都可以在网络上被别的机器使用。这可以减少整个网络上可移动介质设备的数量
1.3 NFS体系
NFS体系至少有两个主要部分:
一台NFS服务器和若干台客户机,
客户机通过TCP/IP网络远程访问存放在NFS服务器上的数据。
在NFS服务器正式启用前,需要根据实际环境和需求,配置一些NFS参数。
1.4 NFS的应用场景
nfs有很多实际应用场景,以下是一些常用的场景:
- 多个机器共享一台CDROM或其他设备。这对于在多台机器中安装软件来说更加便宜与方便
- 在大型网络中,配置一台中心NFS服务器用来放置所有用户的home目录可能会带来便利。这些目录能被输出到网络以便用户不管在哪台工作站上登录,总能得到相同的home目录
- 不同客户端可在NFS上观看影视文件,节省本地空间
- 在客户端完成的工作数据,可以备份保存到NFS服务器上用户自己的路径下
1.5NFS实现原理
1.用户进程访问NFS客户端,使用不同的函数对数据进行处理
2.NFS客户端通过TCP/IP的方式传递给NFS服务端。
3.NFS服务端接收到请求后,会先调用portmap进程进行端口映射。
4.nfsd进程用于判断NFS客户端是否拥有权限连接NFS服务端。
5.Rpc.mount进程判断客户端是否有对应的权限进行验证。
6.idmap进程实现用户映射和压缩
7.最后NFS服务端会将对应请求的函数转换为本地能识别的命令,传递至内核,由内核驱动硬件。
rpc是一个远程过程调用,那么使用nfs必须有rpc服务
2.NFS工作机制
NFS是C/S模式,首先要有一台服务端跑NFS服务,然后各个客户端直接挂载共享目录使用。NFS服务本身不会监听端口,服务端和客户端通信是通过RPC协议来完成的。
2.1 RPC
RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。它是一项广泛用于支持分布式应用程序(不同组件分布在不 同计算机上的应用程序)的技术。
RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。
rpc工作机制如上图所示,下面来描述一下它:
- 客户端程序发起一个RPC系统调用基于TCP协议发送给另一台主机(服务端)
- 服务端监听在某个套接字上,当收到客户端的系统调用请求以后,将收到的请求和其所传递的参数通过本地的系统调用执行一遍,并将结果返回给本地的服务进程
- 服务端的服务进程收到返回的执行结果后将其封装成响应报文,再通过rpc协议返回给客户端
- 客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行
2.2 NIS
NIS(Network Information System),网络信息系统,是对主机帐号等系统提供集中管理的网络服务。
用户登录任何一台NIS客户机都会从NIS服务器进行登录认证,可实现用户帐号的集中管理。
NIS协议是明文的,所以NIS一般不建议在公网中使用而通常在局域网中使用。
此章主要是讲NFS,所以NIS的配置这里就不详说了,有兴趣的朋友可以去网上搜索。
2.3 nfs工作机制
//NFS服务器端运行着四个进程:
nfsd
mountd
idmapd
portmapper
idmapd //实现用户帐号的集中映射,把所有的帐号都映射为NFSNOBODY,但是在访问时却能以本地用户的身份去访问
mountd //用于验证客户端是否在允许访问此NFS文件系统的客户端列表中,在则允许访问(发放一个令牌,持令牌去找nfsd),否则拒绝访问
//mountd的服务端口是随机的,由rpc服务(portmapper)提供随机端口号
nfsd //nfs的守护进程,监听在2049/tcp和2049/udp端口上
//不负责文件存储(由NFS服务器本地内核负责调度存储),用于理解客户端发起的rpc请求,并将其转交给本地内核,而后存储在指定的文件系统上
portmapper //NFS服务器的rpc服务,其监听于111/TCP和111/UDP套接字上,用于管理远程过程调用(RPC)
下面通过一个例子来说明NFS的简单工作流程:
需求:查看file文件的信息,此file存储在远程NFS服务端主机上(挂载在本地目录/shared/nfs中)
- 客户端发起查看file信息的指令(ls file)给内核,内核通过NFS模块得知此文件并不是本地文件系统中的文件,而是在远程NFS主机上的一个文件
- 客户端主机的内核通过RPC协议把查看file信息的指令(系统调用)封装成rpc请求通过TCP的111端口发送给NFS服务端主机的portmapper
- NFS服务端主机的portmapper(RPC服务进程)告诉客户端说NFS服务端的mountd服务在某某端口上,你去找它验证
因为mountd在提供服务时必须要向portmapper注册一个端口号,所以portmapper是知道其工作于哪个端口的
- 客户端得知服务端的mountd进程端口号后,通过已知的服务端mountd端口号请求验证
- mountd收到验证请求后验证发起请求的客户端是否在允许访问此NFS文件系统的客户端列表中,在则允许访问(发放一个令牌,持令牌去找nfsd),否则拒绝访问
- 验证通过后客户端持mountd发放的令牌去找服务端的nfsd进程,请求查看某文件
- 服务端的nfsd进程发起本地系统调用,向内核请求查看客户端要查看的文件的信息
- 服务端的内核执行nfsd请求的系统调用,并将结果返回给nfsd服务
- nfsd进程收到内核返回的结果后将其封装成rpc请求报文并通过tcp/ip协议返回给客户端
NFS原理图
3. exports文件的格式
nfs的主配置文件是/etc/exports,在此文件中,可以定义NFS系统的输出目录(即共享目录)、访问权限和允许访问的主机等参数。该文件默认为空,没有配置输出任何共享目录,这是基于安全性的考虑,如此即使系统启动了NFS服务也不会输出任何共享资源。
exports文件中每一行提供了一个共享目录的设置,其命令格式为:
<输出目录> [客户端1(选项1,选项2,...)] [客户端2(选项1,选项2,...)]
其中,除输出目录是必选参数外,其他参数均是可选项。另外,格式中的输出目录和客户端之间、客户端与客户端之间都使用空格分隔,但客户端与选项之间不能有空格。
客户端是指网络中可以访问这个NFS共享目录的计算机。客户端的指定非常灵活,可为单个主机的IP或域名,亦可为某个子网或域中的主机等。
选项用来设置共享目录的访问权限、用户映射等。exports文件中的选项比较多,一般可分为三类:
- 访问权限选项(用于控制共享目录的访问权限)
- 用户映射选项
- 默认情况下,当客户端访问NFS服务器时,若远程访问的用户是root用户,则NFS服务器会将其映射成一个本地的匿名用户(该用户为nfsnobody),并将其所属的用户组也映射成匿名用户组(该用户组也为nfsnobody),如此有助于提高系统的安全性。
- 其他选项
访问权限选项:
访问权限选项 | 说明 |
---|---|
ro | 设置输出目录只读 |
rw | 设置输出目录可读写 |
用户映射选项:
用户映射选项 | 说明 |
---|---|
all_squash | 将远程访问的所有普通用户及所属组都映射为匿名用户或用户组(nfsnobody) |
no_all_squash | 不将远程访问的所有普通用户及所属用户组都映射为匿名用户或用户组(默认设置) |
root_squash | 将root用户及所属用户组都映射为匿名用户或用户组(默认设置) |
no_root_squash | 不将root用户及所属用户组都映射为匿名用户或用户组 |
anonuid=xxx | 将远程访问的所有用户都映射为匿名用户,并指定该匿名用户为本地用户帐户(UID=xxx) |
anongid=xxx | 将远程访问的所有用户组都映射为匿名用户组,并指定该匿名用户组为本地用户组(GID=xxx) |
常用的其他选项:
其他选项 | 说明 |
---|---|
secure | 限制客户端只能从小于1024的TCP/IP端口连接NFS服务器(默认设置) |
insecure | 允许客户端从大于1024的TCP/IP端口连接NFS服务器 |
sync | 将数据同步写入内存缓冲区或磁盘中,效率较低,但可保证数据一致性 |
async | 将数据先保存在内存缓冲区中,必要时才写入磁盘 |
wdelay | 检查是否有相关的写操作,如果有则这些写操作一起执行,可提高效率(默认设置) |
no_wdelay | 若有写操作则立即执行,应与sync配置使用 |
subtree_check | 若输出目录是一个子目录,则NFS服务器将检查其父目录的权限(默认设置) |
no_subtree_check | 即使输出目录是一个子目录,NFS服务亦不检查其父目录的权限,可提高效率 |
nohide | 若将一个目录挂载到另一个目录之上,则原来的目录通常就被隐藏起来或看起来像空的一样。要禁用这种行为,需启用hide选项 |
4. nfs管理
nfs安装:
//安装
yum -y install nfs-utils
//启动
systemctl start nfs-server //systemctl start nfs
使用shoumount命令测试NFS服务器的输出目录状态:
//语法:showmount [选项] [NFS服务器名称或地址]
//常用的选项有:
-a //显示指定NFS服务器的所有客户端主机及其所连接的目录
-d //显示指定的NFS服务器中已被客户端连接的所有输出目录
-e //显示指定的NFS服务器上所有输出的共享目录
在客户端挂载NFS文件系统:
mount -t nfs SERVER:/path/to/sharedfs /path/to/mount_point
在客户端设置开机自动挂载nfs:编辑/etc/fstab文件,添加如下格式的内容
SERVER:/PATH/TO/EXPORTED_FS /mnt_point nfs defaults,_netdev 0 0
客户端挂载时可以使用的特殊选项:
- rsize:其值是从服务器读取的字节数(缓冲)。默认为1024。若使用比较高的值,如8192,可以提高传输速度
- wsize:其值是写入到服务器的字节数(缓冲)。默认为1024。若使用比较高的值,如8192,可以提高传输速度
exportfs //维护exports文件导出的文件系统表的专用工具
-a //输出在/etc/exports文件中所设置的所有目录
-r //重新读取/etc/exports文件中的设置,并使其立即生效,无需重启服务
-u //停止输出某一目录
-v //在输出目录时将目录显示到屏幕上
检查输出目录所使用的选项:
在配置文件/etc/exports中,即使在命令行中只设置了一两个选项,但在真正输出目录时,实际上还带有很多默认的选项。通过查看/var/lib/nfs/etab文件,可以看到具体使用了何选项
[root@localhost ~]# cat /var/lib/nfs/etab
....此处省略
5.配置实例
手动搭建一个nfs服务器,要求如下
- 开放/nfs/shared目录,供所有用户查阅资料
- 开放/nfs/upload目录为192.168.32.0/24网段的数据上传目录,并将所有用户及所属的用户组都映射为nfs-upload,其UID与GID均为300
5.1服务端配置
服务端的ip是192.168.32.130,客户机的ip是192.168.32.125
#开始前关闭selinux和防火墙,或者自行添加相关规则
[root@server ~]# systemctl stop firewalld
[root@server ~]# systemctl disable firewalld
[root@server ~]# sed -ir '/^SELINUX/s/SELINUX.*/SELINUX=disabled/' /etc/selinux/config
[root@server ~]# setenforce 0
#安装nfs
[root@server ~]# yum -y install nfs-utils
#启动服务,
[root@server ~]# systemctl start nfs
[root@server ~]# systemctl enable nfs
#开放/nfs/shared仅供查阅资料
#创建要共享的目录
[root@server ~]# mkdir -p /nfs/shared /nfs/upload
#编辑主配置文件,/nfs/shared设为只读
[root@server ~]# vim /etc/exports
/nfs/shared *(ro)
开放/nfs/upload目录为192.168.32.0/24网段的数据上传目录,并将所有用户及所属的用户组都映射为nfs-upload,其UID与GID均为300
#创建映射用户nfs-upload
[root@server ~]# groupadd -g 300 nfs-load
[root@server ~]# useradd -r -M -s /sbin/nologin -u 300 -g 300 nfs-load
#编辑配置文件,加入配置
[root@server ~]# vi /etc/exports
/nfs/upload 192.168.32.0/24(rw,anongid=300,anonuid=300)
[root@server ~]# cat /etc/exports
/nfs/shared *(ro)
/nfs/upload 192.168.32.0/24(rw,anongid=300,anonuid=300)
#给映射的用户有/nfs/upload/目录的权限
[root@server ~]# setfacl -m u:nfs-load:rwx /nfs/upload/
#重新读取/etc/exports文件中的设置,并使其立即生效
[root@server ~]# exportfs -r
5.2客服端配置
#安装nfs
[root@server ~]# yum -y install nfs-utils
#查看服务端共享的目录
[root@client ~]# showmount -e 192.168.32.130
Export list for 192.168.32.130:
/nfs/shared *
/nfs/upload 192.168.32.0/24
#挂载使用
[root@client ~]# mkdir -p /nfs/shared /nfs/upload
# -t nfs 可以省略,默认选项
[root@client ~]# mount -t nfs 192.168.32.130:/nfs/shared /nfs/shared/
[root@client ~]# mount -t nfs 192.168.32.130:/nfs/upload/ /nfs/upload/
[root@client ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 898M 0 898M 0% /dev
tmpfs 910M 0 910M 0% /dev/shm
tmpfs 910M 9.5M 901M 2% /run
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 1.7G 16G 10% /
/dev/sda1 1014M 150M 865M 15% /boot
tmpfs 182M 0 182M 0% /run/user/0
192.168.32.130:/nfs/shared 17G 1.6G 16G 9% /nfs/shared
192.168.32.130:/nfs/upload 17G 1.6G 16G 9% /nfs/upload
#永久挂载
在/etc/fstab添加
192.168.32.130:/nfs/shared /nfs/shared nfs defaults 0 0
192.168.32.130:/nfs/upload /nfs/upload nfs defaults 0 0
#重新加载配置文件
[root@client ~]# mount -a
5.3脚本搭建nfs并在客户端上挂载
脚本和存放ip地址,用户名和密码的文本都在同一目录
[root@server ~]# cat info
192.168.32.130:root:123456
192.168.32.135:root:123456
[root@server ~]# cat nfs.sh
#!/bin/bash
yum -y install expect > /dev/null
yum -q -y install nfs-utils > /dev/null
systemctl enable --now nfs
#关闭防火墙和selinux
systemctl disable --now firewalld
sed -ir '/^SELINUX/s/SELINUX.*/SELINUX=disable/' /etc/sysconfig/selinux
read -p "输入要共享的目录: " dir
read -p "输入共享的选项,如rw: " options
read -p "客户端挂载的目录: " c_dir
read -p "输入服务端IP: " server_ip
########################################################3
#创建共享目录
if [ ! -d $dir ];then
mkdir -p $dir
fi
#定义函数,确保添加信任
function ssh-keygen(){
/usr/bin/expect <<EOF
spawn ssh-copy-id $1@$2
expect {
"*(yes/no)?" {send "yes\r";exp_continue}
"*password:" {send "$3\r"}
}
expect eof
EOF
}
#判断是否生成过密钥,没有就生成密钥
if [ ! -f ~/.ssh/id_rsa ];then
/usr/bin/expect <<EOF
spawn ssh-keygen -t rsa
expect {
"(*/id_rsa):" {send "\r";exp_continue}
"(empty for no passphrase):" {send "\r";exp_continue}
"passphrase again:" {send "\r"}
}
expect eof
EOF
fi
> /etc/exports
#将用户,ip和密码依次传入函数ssh-keygen中,上传公钥
for info in $(cat info);do
ip=$(echo $info|awk -F: '{print $1}')
user=$(echo $info|awk -F: '{print $2}')
pass=$(echo $info|awk -F: '{print $3}')
ssh-keygen $user $ip $pass
cat >>/etc/exports<<EOF
$dir $ip($options)
EOF
ssh $user@$ip "yum -q -y install nfs-utils > /dev/null;mkdir -p $c_dir;grep "$server_ip:$dir" /etc/fstab || echo $server_ip:$dir $c_dir nfs defaults 0 0 >> /etc/fstab ;mount -a"
done
setfacl -m u:nfsnobody:rwx $dir
exportfs -r
执行脚本
#服务端执行
[root@server ~]# ./nfs.sh
输入要共享的目录: /nfs
输入共享的选项,如rw: rw
客户端挂载的目录: /opt
输入服务端IP: 192.168.32.125
spawn ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:WNBEXeOrxWdZnDBnl41me4n1usw7k+g0uBds5qP/wcY root@server
The key's randomart image is:
+---[RSA 2048]----+
| .+o. .oo ++|
| .. .. .O++|
| . .ooo=|
| o . o.+o|
| . S .+ +o |
| +==. |
| o+o=Eo |
| o=oB. |
| o=ooo+ |
+----[SHA256]-----+
spawn ssh-copy-id root@192.168.32.130
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
The authenticity of host '192.168.32.130 (192.168.32.130)' can't be established.
ECDSA key fingerprint is SHA256:mxRfnjvv98dVsD4gCq/koRMg7r05Sh43473F7Y7PoMw.
ECDSA key fingerprint is MD5:22:f7:ba:1e:0a:4c:a7:1b:17:98:2d:dd:f0:f5:70:10.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.32.130's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@192.168.32.130'"
and check to make sure that only the key(s) you wanted were added.
spawn ssh-copy-id root@192.168.32.135
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
The authenticity of host '192.168.32.135 (192.168.32.135)' can't be established.
ECDSA key fingerprint is SHA256:BFDlHGdhFuhdlBPVKeGY6OhFc/KEMiVvKW1cKrir4uM.
ECDSA key fingerprint is MD5:85:bf:36:c6:8c:13:55:9d:70:d1:77:dc:8e:31:1f:b6.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.32.135's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@192.168.32.135'"
and check to make sure that only the key(s) you wanted were added.
[root@server ~]# echo "hello" > /nfs/aaa
[root@server ~]# ls /nfs
aaa
客户端验证
[root@client-1 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 475M 0 475M 0% /dev
tmpfs 487M 0 487M 0% /dev/shm
tmpfs 487M 7.6M 479M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 1.4G 16G 9% /
/dev/sda1 1014M 137M 878M 14% /boot
tmpfs 98M 0 98M 0% /run/user/0
192.168.32.125:/nfs 17G 2.2G 15G 13% /opt
[root@client-1 ~]# ls /opt/
aaa
[root@client-1 ~]# cat /opt/aaa
hello
[root@client-2 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 475M 0 475M 0% /dev
tmpfs 487M 0 487M 0% /dev/shm
tmpfs 487M 7.7M 479M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 1.4G 16G 8% /
/dev/sda1 1014M 136M 879M 14% /boot
tmpfs 98M 0 98M 0% /run/user/0
192.168.32.125:/nfs 17G 2.2G 15G 13% /opt
[root@client-2 ~]# ls /opt/
aaa
[root@client-2 ~]# cat /opt/aaa
hello