前言
这篇文章主要记录一下我管理 HDFS 集群的时候遇到一些坑的填坑办法,用来提醒自己,也希望启迪他人。
NameNode 管理失效的 DataNode
一般在一段时间( 默认是 10 分钟 )Namenode 收不到 DataNode 的 heartbeats 就认为 DN 失效了。
这时候:NN 会查找哪些 block 在这个失效的 DN 上,并定位其他拥有这些 block 备份的 DN,这些 DN 会将这些 block 复制到另外的 DN 上,以维持设置的复制因子数。同时将失效的 DN 下线。
配置 DN的存储平衡
当时遇到的一个坑是机器 dn 上面的磁盘大小不一致,然后首先是想到配置存储平衡看能不能让 dn 自己搞定。
存储平衡可以配置两个东西:
- DN 内不同容量的磁盘允许多大的误差就被认为是平衡的
- 有多大比例的 block 会写到 DN 中容量比较大的那块磁盘里面。
需要配置的属性如下:
- 使存储平衡生效
dfs.datanode.fsdataset.volume.choosing.policy/org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy
- 这个 10G 是指一个 DN 内磁盘最大和最小的容量差要在 10G 之内
dfs.datanode.available-space-volume-choosing-policy.balanced-space-threshold/ 10737418240 (default)
- 75% 的新 blk 会发送的容量更大的磁盘上
dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction/ 0.75(defalut)
- 每一个 dn 的每一个目录保留的非 hdfs 空间
dfs.datanode.du.reserved/ 10GB
配置后重启 DN 即可。
后面发现平衡的效果并不是特别理想,不理想的原因其实是因为总可用的容量并不是 hdfs report 的那个总可用容量。
下面做一个描述。
看到上图,这个盘是 243g。
但是仔细一看,used+avail = 231g size=243g ( 很明显 used+avail !=size )
再看一下 hdfs report 出来的结果:
一看好像没啥特别,但是仔细一观察,DFS Remaining 怎么就比磁盘的 avail 还要大呢?这是要写爆盘的节奏吗?!
其实这里的原理是,Configured Capacity 是等于磁盘总容量减去我们上面配置的那个 du.reserved 的值,也就是 243-10,就是 233 左右,然后 hdfs 认为已经给你预留了,剩下的都可以写数据了。
但是,实际上是不行的。因为这个磁盘本来可用就少了 12g。
所以就会出现你信心满满的配置好了上面的属性之后,仍然会写爆磁盘的情况。
解决办法是将 du.reserved 的值设置大一点,令到 hdfs 做完减法之后 DFS Remaining 要少于 avail 就好了。
但是对于追求稳定完美的运维来说,好像还是不那么舒服,因为总是感觉一边大一边小,资源利用率不是那么高。
于是我们就想着把磁盘都搞成一样大的。
通过讨论,我们做了如下操作:
一个dn上磁盘大小不一致的时候, 可以将数据目录移到大的磁盘上,然后做一个软连接到原来的目录上。( 将软连接写到配置文件中即可,当然这是在有数据的时候。)也可以使用将dn下线的办法再更换数据目录。( 软连接的权限不影响,实体目录的权限才需要配置正确。)
这样我们就可以把小的磁盘去掉,留一个软连接就好了。
配置本地读( Short-circuit read )
在 hdfs-site.xml 文件中添加如下配置:
<property>
HDFS balancers
balancer 会移动 block 和 data 直到每一个 DN 的用量和集群的用量不超过一个给定的阈值。
( 每一个 DN 的用量指该节点中使用的空间占整个节点的空间的百分比,而集群的用量是指整个集群的使用百分比。)
balancer 并不会平衡一个 DN 内部不同磁盘的容量。
( 比如一个 DN 中有两个目录数据量不一样,但是 balancer 不会令到两个目录数据平均分。)
- 运行 balancer 的命令:
sudo -u hdfs hdfs balancer -threshold 5(5%)
( 不加任何参数的情况下默认的 threshold 为 10%,这个 10% 的意思是比如整个集群的用量是 40%,balancer 会保证每个 DN 的磁盘用量会在 30% 和 50%( 占那个 DN 的总量 )之间。)
- 同时还可以调整 balancer 的网络带宽
hdfs dfsadmin -setBalancerBandwidth <newbandwidth>
配置 NFS gateway
NFSv3 gateway 允许客户端将 HDFS mount 到本地文件系统。这个 gateway 可以是集群中的任意一台机器。
安装配置步骤:
- 安装所需的软件包:
sudo yum install nfs-utils nfs-utils-lib hadoop-hdfs-nfs3 rpcbind*
2. 在 NN 上的 hdfs-site.xml 中配置如下属性:
<property>
3. 在 NFS server 中的 hdfs-site.xml 添加:
<property>
4. 在 NN 的 core-site.xml 中添加代理用户:
<property>
5. 重启 NN
6. 在 NFS 服务器上:
sudo service nfs stop
sudo service hadoop-hdfs-nfs3 start
7. 检查是否正常工作:
;
8. 验证 hdfs 是否可以被 mount,使用 showmount 命令:
;
9. 挂载 hdfs:
vers
10. 这个服务只识别 UID 和 GID,如果不同机器的用户 UID 和 GID 不同,mount 上去之后,文件在不同机器便会显示属于不用的用户。
11. 如果使用了 kerberos 认证,则需要在 nfs server 的 hdfs-site.xml 文件里面加上以下属性:
<property>
设置 HDFS 的 Quotas
hdfs quotas 有以下两种形式:
- 限制某个目录下的文件数量
- 限制某个目录的空间大小
上面两种形式是独立的,文件或者目录的创建如果会引起quota超出则会失败。
- 启用 hdfs space quotas:
hdfs dfsadmin -setSpaceQuota n dir
( 上面 n 是字节数,dir 是需要设置限额的目录。后面可以接多个目录,n 则会对这多个目录生效。)
- 取消 hdfs space quotas:
hdfs dfsadmin -clrSpaceQuota dir
( dir 是需要设置的目录,后面可接多个 )
- 启用 hdfs name quotas:
hdfs dfsadmin -setQuota n dir
( n 代表目录和文件数量,dir 是需要设置的目录,后面可接多个,n 则会对多个生效。比如 n 是 1,则不能往目录放任何东西,1 代表目录本身,要从 2 开始 )
- 取消 hdfs name quotas:
hdfs dfsadmin -clrQuotas dir
( dir 是需要设置的目录,后面可接多个 )
配置可挂载的HDFS
- 安装 hadoop-hdfs-fuse:
sudo yum install hadoop-hdfs-fuse
2. 创建挂载点:
; hadoop-fuse-dfs dfs://<nameservice_id>
3. 卸载挂载点:
umount <mount_point>
配置中央缓存管理
这是一个精确的缓存机制,它允许用户缓存 hdfs 上某一个路径下的文件。
可以防止常用的数据被清出内存,NN 管理缓存便于作业的安排与调度,同时可以提高整个集群的内存利用率( 通过指定某些副本,从而免去全部副本缓存 buffer 的开销 )。
在上面的架构中,NN 负责协调集群中所有 DN 的内存。
NN 会定期收到来自集群中的每一个 DN 描述各自块缓存的情况。NN 管理 DN 缓存是通过使用捎带确认标志的心跳信号传送缓存或者接收缓存命令。NN 通过查看它自身的缓存指令集去决定什么文件或目录应该被缓存。缓存指令被持久存储在 FsImage 和 EditLog 上面,并且可以使用命令行或者 java 接口去增删改。
NN 同时也保存了一系列缓存池,用来分类管理属于资源管理的缓存命令和执行权限的缓存命令。NN 会定期重复扫描命名空间和现有的缓存目录去决定哪些块需要被缓存或者解除缓存并分配缓存到 DN。用户增加或者删除缓存指令或者删除缓存池同样会触发重复扫描。
缓存指令定义了一个需要被缓存的路径。路径可以是文件或者目录。目录并不会被循环缓存,也即是只有第一层目录的文件会被缓存。
缓存指令的参数有诸如缓存复制因子和过期时间。这里的复制因子表示 blk 需要被缓存的副本数。如果出现多个缓存指令指向同一个文件,则会取最大的复制因子数。
缓存池是用于将缓存指令分组管理。缓存池的权限管理就类似于 UNIX。写权限允许用户增加和删除池中的缓存指令。读权限允许用户列出池中的缓存指令和一些元数据。执行权限未被使用。
缓存池同样被用来作资源管理。缓存池可以限制池中所有缓存指令可缓存的字节数和最大的过期时间。
缓存指令的命令:
- 新增:
[-force
( path 指需要被缓存的目录或者文件的路径,pool-name 指路径加到那个池中,这里需要拥有写权限。force 指跳过检查缓存池的资源限制。replication 指复制因子,默认是 1。time-to-live 指缓存生效时间。可以使用秒,分,时或者日作为单位,例如:30m,4h,20d。never 则是永不过期。默认是 never)
- 删除:
hdfs cacheadmin -removeDirective <id>
( id 指需要被删除的缓存指令的 id,可以使用 list 查看,需要有写权限 )
hdfs cacheadmin -removeDirectives <path>
( path 指路径中的所有缓存指令,需要写权限 )
- 列举:
[-stats
( path 指该路径的缓存指令,需要读权限。pool 指该池中属于那个路径的指令。stats 指统计信息 )
缓存池命令:
- 新增:
[-owner <owner>
( name 指池的名字。owner 可指定,默认是当前用户。group 指池的分组。mode 指权限,默认是 0755。limit 指池中所有指令总共可以缓存的字节数,默认是无限制。maxTtl 指池中指令最大的过期时间,默认没有最大值)
- 修改:
[-owner <owner>
- 删除:
hdfs cacheadmin -removePool <name>
- 列举:
[-stats
( stats 指统计信息 )
配置缓存:
- libhadoop.so
- 设置 hdfs-site.xml 里面的 dfs.datanode.max.locked.memory 属性( 需要考虑为 DN,jvm heap 和操作系统的页缓存预留内存)
- 调整 ulimit -l 出来的值必须要比上面设置的属性值要大或者值是没有限制。( 需要注意的是,上面属性的 value 的单位是 b,而 ulimit -l 的单位是 KB )