本章节继续介绍HDFS的一些概念和功能。
一、配额
- HDFS允许管理员为某个目录设置文件名字配额,即是指在文件的拓扑树中以该目录为根的所有文件数目总和上限。超过配额时是不能够在该目录里新建目录或上传文件了。最大的配额是Long.Max_Value。在统计一个目录的所有文件数目时,目录本身也包括在内,故如果设置名字配额为1的话,将会使该目录保持为空。
管理命令:
设置名字配额:hdfs dfsadmin -setQuota <N> <directories> ......
删除名字配额:hdfs dfsadmin -clrQuota <directories> ......
- HDFS允许管理员为某个目录设置空间配额,即指在文件的拓扑树中以该目录为根的所有文件的字节数总和的上限。最大配额是Long.Max_Value。创建空文件或目录不会计算配额,但当在分配文件的block时,要写入整个block时,文件将会超出配额限制,此时block分配就会失败,这个block就无法写入文件。文件的副本都会计算进配额里。
管理命令:
设置空间配额:hdfs dfsadmin -setSpaceQuota <N> <directories> ......
删除空间配额:hdfs dfsadmin -clrSpaceQuota <directories> ......
- HDFS运行管理员设置目录的存储类型配额,即指在文件的拓扑树中以该目录为根的所有文件能够使用的存储类型。如果要使用存储类型配额,必须为该目录设置存储策略。
管理命令:
设置存储类型配额:hdfs dfsadmin -setSpaceQuota <N> -storageType <storageType><directories> ......
删除存储类型配额:hdfs dfsadmin -clrSpaceQuota -storageType <storageType> <directories> ......
配额信息被持久保存在fsimage文件里。如果在启动时,fsimage已经违反了配额限制,就会打印一些告警信息。在设置和删除配额时都会产生日志记录。
HDFS提供了查询目录详细统计信息的命令:
hadoop fs -count -q [-h] [-v] [-t [comma-separated list of storagetypes]] <directory>...<directory>
可以统计目标目录包含的总的目录数、文件数、总的字节大小、各种配额值及剩余值等信息。
二、权限管理
HDFS提供了与POSIX模型相似的权限模型。每个文件或目录都有所有者权限、所属组内用户权限、其它用户权限三种权限限制。对于文件,r表示可以读取文件、w表示可以写文件及追加文件。对于目录,r表示可以列出目录内容、w表示可以创建或删除文件或目录、x表示可以访问目录的子目录。
与POSIX模型不同的是,HDFS的权限模型中没有设置用户标识、没有设置用户组标识,对于文件也没有“可执行”这样的概念。可以为目录设置粘滞位,用来防止除管理员用户、所属用户、所属组内用户之外的其它用户删除和移动目录里的文件。对文件设置粘滞位是没有效果的。HDFS的文件权限和目录权限是不同的。
当创建文件或目录时,它的所有者即是客户端进程的用户,它的所属组即是其父目录的所属组。
HDFS也支持POSIX ACLs细粒度角色权限控制。
从hadoop0.22开始,hadoop提供了两种用户身份验证方式,配置参数是hadoop.security.authentication,可选值如下:
- simple:这种模式的客户端进程用户身份是由操作系统来决定的。
- kerberos:这种模式的客户端进程的用户身份是由kerberos证书来决定的。即你的客户端的kerberos票据里的principal就是你操作HDFS的用户。
用户身份确定了,接下来就是决定用户组。在后面会详细介绍hadoop group mapping服务。
用户权限检查,所有的HDFS操作都会对文件路径上的所有部分进行权限校验,而不是单单对最后一部分进行权限检查。例如请求的文件路径是/foo/bar/file,那么操作用户对/、/foo、/foo/bar目录都必须有X权限。
运行NameNode进程的用户即是HDFS的super-user。
通过设置dfs.namenode.acls.enabled为true,使HDFS支持ACLs。
- 基本acl记录:user::permission,group::permission,other::permission
- 可以指定用户:user:username:permission
- 可以指定组:group:groupname:permission
- 对于目录可以设置默认acl,表示该目录下的所有新建的子目录或文件都会继承此acl:default:user::permission,default:user:username:permission,default:group::permission,default:group:groupname:permission,default:other::permission
- 可以设置mask,默认是mask:rwx,每个生效的acl都会按此mask进行过滤,类似每个acl记录的权限与此mask设置的权限进行按位与运算:mask::permission
ACL的shell命令:
- hdfs dfs -getfacl [-R] <path> 获取文件或目录的acl记录,-R参数表示递归所有文件;
- hdfs dfs -setfacl [-R] [-b |-k ][-m |-x <acl_spec>]|[--set <acl_spec>] <path> 为文件或目录设置acl。-R表示递归所有文件;-b表示删除除基本acl之外的所有acl记录;-k表示删除default acl;-m表示修改某个acl的权限值,修改的acl作为新记录,原先的acl记录保留;-x表示删除某个acl记录;--set是设置acl,设置acl时,acl列表必须包含基本acl。
权限相关参数配置:
- dfs.permissions.enabled true
- dfs.web.ugi webuser,webgroup
- dfs.permissions.superusergroup supergroup
- fs.permissions.umask-mode 0022
- dfs.cluster.administrators 管理员
- dfs.namenode.acls.enabled true
- dfs.namenode.posix.acls.inheritance.enabled 默认true,表示新建的文件或目录将会继承父目录的默认acl。如果设置为false,则会使用客户端的umask。
三、hadoop group mapping
hadoop提供了多种group mapping机制,配置参数是hadoop.security.group.mapping。可选配置值如下:
- org.apache.hadoop.security.JniBasedUnixGroupsMappingWithFallback:这个是默认的实现机制。当JNI可用时,它就会使用hadoop的api来决定用户所属的用户组列表。如果JNI不可用的话,就使用ShellBasedUnixGroupsNetgroupMapping机制。
- org.apache.hadoop.security.JniBasedUnixGroupsNetgroupMappingWithFallback:与上一个实现机制类似,如果JNI可用,就使用hadoop API选择用户组。如果JNI不可用,就使用ShellBasedUnixGroupsNetgroupMapping机制。
- org.apache.hadoop.security.ShellBasedUnixGroupsMapping:这种实现机制与linux下执行‘sh -c group’结果相同。
- org.apache.hadoop.security.ShellBasedUnixGroupsNetgroupMapping:这种实现机制和linux下执行getent命令的结果相同。
- org.apache.hadoop.security.LdapGroupsMapping:这是由LDAP服务来决定用户组。使用的是LDAP上的用户组,而不是Unix系统的用户组。支持SSL连接,支持POSIX用户组语义。
- org.apache.hadoop/security.CompositeGroupsMapping:这是一种将其他用户组映射服务提供者混合起来使用的一种机制。
hadoop的用户到组的映射是由NameNode来完成的。
可以通过参数hadoop.user.group.static.mapping.overrides来配置一些用户到组的映射关系,覆盖其它的用户组映射。其格式如:user1=group1,group2;user2=;user3=group2;......
hadoop会缓存由用户组映射服务提供的组信息。缓存保存时长是通过配置hadoop.security.groups.cache.secs参数决定,默认是300s。缓存到期后,下一次请求组内成员的线程就会到组映射服务的提供者那里重新查询当前的用户组信息。当某个线程正在从组映射服务上查询组信息时,该线程就进入阻塞,其它任何请求相同用户组的线程都会使用之前缓存的信息。如果一次刷新用户组缓存信息的操作执行失败了,就会让下一次线程来再次执行刷新操作。当刷新用户组信息的操作执行超过hadoop.security.groups.cache.secs*10秒后依然失败,缓存依然没有更新的话,缓存的信息就会清空,所有请求用户组信息的线程都会阻塞直到重新加载成功。
为了防止因缓存过期而出现的线程阻塞,必须设置hadoop.security.groups.cache.backgroud.reload为true。同时可以设置hadoop.security.groups.cache.backgroud.reload.threads参数,这个参数默认值是3,这将会在后台创建一个线程池用来刷新缓存。这两个参数作用就是,在缓存记录过期后,新的请求缓存记录的操作执行时,后台就会向刷新缓存的任务队列中添加一个任务,当该刷新任务执行失败后,新的请求来时就会再次向队列中添加一个新的任务。
为了防止未知用户不断请求NameNode,hadoop提供消极缓存策略,这样当从用户组映射服务查询用户组记录为空,就直接返回空的用户组,不会再进行更多的查询。参数hadoop.security.groups.negative-cache.secs,默认是30秒,表示当查询用户组为空时,30秒内不会进行新的查询。
四、存储类型和存储策略
存储类型:Archive、Disk、SSD和RAM_Disk。其中,Archive存储类型是高密度、廉价存储,用于归档存储。RAM_Disk存储支持在内存中写一个文件副本。Disk存储是默认存储类型。
存储策略:Hot、Warm、Cold、All_SSD、One_SSD和lazy_Persist。
- Hot:那些常用的和正在处理的数据存储使用这个策略。当一个block是Hot的,它所有的副本都按Disk类型存储。
- Warm:部分Hot及部分Cold。当一个block是Warm,它的一部分副本将按Disk存储,剩余的按Archive存储。
- Cold:仅用于参与有限计算的数据的存储。这些数据基本是不再使用的数据,或者是将要从热存储转移到冷存储的归档数据。当一个block是冷的,它所有的副本均被按Archive类型存储。
- All_SSD:所有副本使用SSD存储。
- One_SSD:一个副本使用SSD存储,剩余的使用Disk。
- Lazy_Persist:将block的一个副本写进内存中,按RAM_Disk存储,剩余的按Disk惰性的保存。
一个存储策略正式的应该由以下几个部分构成:
- 策略ID。
- 策略名字。
- block安置的存储类型列表。
- 创建文件时的后备存储类型列表。
- 副本的后备存储类型列表。
当存储空间足够时,block的副本都会按照上面第3条规定的存储类型来存储。当上面第3条的存储类型将要超出空间限制时,超出部分的文件创建和备份将会分别按照上面第4及第5条所列的存储类型列表存储。
标准的存储策略表:
策略ID | 策略名 | 块的存储类型列表(n个副本) | 后备存储类型列表(创建文件时) | 后备存储类型列表(副本文件) |
15 | Lazy_Persist | RAM_DISK:1,DISK:n-1 | DISK | DISK |
12 | All_SSD | SSD:n | DISK | DISK |
10 | One_SSD | SSD:1,DISK:n-1 | SSD,DISK | SSD,DISK |
7 | Hot(默认) | DISK:n | <none> | ARCHIVE |
5 | Warm | DISK:1,ARCHIVE:n-1 | ARCHIVE,DISK | ARCHIVE,DISK |
2 | Cold | ARCHIVE:n | <none> | <none> |
Lazy_Persist策略对于只有一个副本的block而言,当数据写进内存后datanode就会立即返回客户端写成功,之后datanode采用一种异步的方式惰性的将内存的数据flush到磁盘,这既是其存在丢失数据的风险原因之所在。但,当block存在多个副本,那么datanode在写完内存后,会将所有副本都写进磁盘,所有副本写成功才算客户端写成功(这是hdfs写数据机制),因此对于多副本的block而言,这个策略在写性能上没有提升。hdfs虽然极力保证lazy persist的持久性,但是依然会存在极低的丢失数据的风险。
对于使用条状式存储的EC(纠错码)文件,适合的存储策略是All_SSD、Hot、Cold。如果你想要为EC文件设置其它的存储策略,在block的创建和移动时,设置的策略也不会作用。
策略相关命令:
- 设置策略:hdfs storagePolicies -setStoragePolicy -path <path> -policy <policy>,为某个目录设置存储策略。
- 策略撤销:hdfs storagePolicies -unsetStoragePolicy -path <path>,撤销某个目录的存储侧率配置。
- 查询策略:hdfs storagePolicies -getStoragePolicy -path <path>,获取某个目录的策略配置。
策略相关配置项:
- dfs.storage.policy.enabled:使用或禁用存储策略特征。默认是true。
- dfs.datanode.data.dir:配置datanode的数据存放路径。多个路径使用逗号分割。例如[DISK]/disk01,[SSD]/disk02,[ARCHIVE]/disk03,[RAM_DISK]/disk04。
对于归档数据,hadoop提供了数据迁移工具mover。命令行格式:hdfs mover [-p <files or dirs> | -f <local files>],其中-p参数后面跟的是要迁移的以空格分隔的hdfs文件或目录列表,-f表示迁移包含hdfs文件或目录的本地文件,如果-p和-f都没有指定,默认是迁移根目录。这个工具会周期检查文件或目录,如果其违反了存储策略,就按存储策略将其迁移到合适的存储类型。一般是选择在同节点内进行迁移工作,但是如果同节点没有合适的存储类型就会选择其他节点。
五、机架感知
HDFS在备份文件时,有一个机架感知能力,这个特征就是,通常文件的备份因子是3,那么在客户端写文件时,第一个副本写策略是,假如客户端所在主机有DN,则第一个副本就写入本地DN,但如果客户端不在任何一台DN上,则会选择一台随机的DN写入;第二个副本写策略是,选择与第一个副本不同的机架上的一台DN写入;第三个副本写策略是,选择与第二个副本同机架的其它DN写入。如果配置的备份因子数大于3,那么从第4个副本开始,写策略就是,随机写入所有DN,但要求每个机架上副本数上限是(replicas-1)/racks+2,其中replicas是复制因子,racks是机架数。NN要求DN上不得存在重复的副本,故复制因子的大小不得大于DN的总数。默认情况下,HDFS是不启用机架感知能力的。
机架感知能力是通过将block的副本安放在不同的机架上,以达到容灾目的。这样在集群的网络交换机出现故障或者数据分区时,它会保证数据的可靠性。
HDFS想启用机架感知能力,必须要在core-site.mxl里配置相应的参数。
NN的主守护进程会通过传递DN的主机名或IP到配置文件里配置的外部脚本或java类来获得集群的机架ID。通过java类或外部脚本,都需要保证DN主机与机架之间的一对一关系。NN会在内存中保存机架/主机的拓扑映射,其格式是"/rack/host"。
- java方式获取机架ID。需要配置net.topology.node.switch.mapping.impl。值是一个java类。默认的实现是org.apache.hadoop.net.ScriptBasedMapping类,这个实现方式就需要配置外部脚本。当然也可以自己定制实现类,定制的实现类必须实现org.apache.hadoop.net.DNSToSwitchMapping接口。
- 外部脚本获取机架ID。这种方式下,配置文件必须配置net.topology.script.file.name,值是一个脚本的全路径,可以是shell也可以是python。NN调用脚本时允许传递多个DN的主机名或IP,这个可以通过配置项net.topology.script.number.args来配置,默认是100。
使用定制java类的方式来获取机架ID要比使用外部脚本的方式有更好的性能。如果net.topology.node.switch.mapping.impl及net.topology.script.file.name均没有设置,则默认的机架ID均是/default-rack,这种情况下NN在选择block副本的DN时均是随机的,性能将会受到影响。
六、DataNode的热切换驱动
DN支持热切换驱动。用户可以在不停止运行DN的情况下添加或替换HDFS数据卷。典型的热交换过程:
- 如果有新的存储目录,用户需要先格式化他们并适当挂载。
- 用户需要更新dfs.datanode.data.dir配置,用来提示DN将会被使用的数据卷。
- 用户运行dfsadmin -reconfig datanode HOST:PORT start,进行重新配置。同时可以通过命令dfsadmin -reconfig datanode HOST:PORT status来查询运行状态。
- 一旦运行重配置完成,用户就可以安全的umount哪些被移除的数据卷,并最终拆除磁盘。