1. 背景
一般离线Hadoop集群和在线Hadoop集群都是分开部署的,他们的计算资源互相隔离。离线集群一般0:00~08:00作业较多,集群压力大,其他时间段集群较为空闲。实时集群高峰期一般为10:00~20:00,其他时间段较为空闲。空闲时资源利用率低,是对资源的浪费,而离线/实时集群在高峰期资源紧张时,往往选择通过加机器来实现扩容,又增加成本。
传统的集群部署方式,不仅没有利用空闲下来的资源,还经常购买机器增加成本,这种方案对于将本增效时代会被逐步淘汰。
阿里在Yarn on K8S上,有较为深入的实践:https://mp.weixin.qq.com/s/kqC7EtgWiOCiisyWdGiMmQ。其基本思想是集群A在资源紧张时,在空闲的集群B上运行NodeManager Pod,该Pod中的Nodemanager向集群A的ResourceMananger进行汇报,执行集群A中的作业。
2. 方案
ResourcveManager独立部署,Nodemanager在K8s集群中以pod运行:
3. 前置问题
nodemanager启动的container中,可能会随机启动多个端口,监听该端口。例如,flink on yarn的每个container都会随机监听一个端口提供服务:
因此,使用端口映射的方式部署service会非常困难。基于hostNetwork: true
配置,是pod使用主机网络,测试不部署service能否对外提供nginx服务:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 4
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
hostNetwork: true
# 使用主机网络
dnsPolicy: ClusterFirstWithHostNet
# 使用主机网络
# 该设置是使POD使用k8s的dns,dns配置在/etc/resolv.conf文件中
# 如果不加,pod默认使用所在宿主主机使用的DNS,这样会导致容器
# 内不能通过service name访问k8s集群中其他POD
containers:
- name: nginx
image: nginx:1.7.9
在外网查询部署pod的机器,发现正常机器中的nginx服务,即nginx端口映射到了主机所在的端口,符合预期:
4. 服务部署
集群规划如下。gdc-k8snode03-taylor直接启动resourcemanager,gdc-k8snode03-taylor启动nodemanager直连resourcemanager,gdc-k8snode02-taylor在pod中启动nodemanager连接reosurcemanager。构成了异构集群:
节点 | 角色 | 集群 |
---|---|---|
gdc-k8snode03-taylor | namenode datanode resourcemanager | 固定Yarn集群 |
gdc-k8snode03-taylor | nodemanager | 固定Yarn集群 |
gdc-k8snode02-taylor | nodemanager | k8s Yarn集群 |
如下,对于gdc-k8snode03-taylor节点,启动了ResourceMananger和NodeManager服务:
K8s部署Nodemanager,配置特权容器:
apiVersion: apps/v1
kind: Deployment
metadata:
name: debian
spec:
replicas: 4
selector:
matchLabels:
app: debian
template:
metadata:
labels:
app: debian
spec:
hostNetwork: true
# 使用主机网络
#dnsPolicy: ClusterFirstWithHostNet
dnsPolicy: Default
# 使用主机网络
# 该设置是使POD使用k8s的dns,dns配置在/etc/resolv.conf文件中
# 如果不加,pod默认使用所在宿主主机使用的DNS,这样会导致容器
# 内不能通过service name访问k8s集群中其他POD
containers:
- name: debian
image: debian
securityContext:
privileged: true
command: ["/bin/bash","-c","while true; do sleep 1000; done"]
在pod中启动nodemanager:
查看resourcemanager,发现k8s nodemanager正常展示在resourcemanager web页面中:
5. 作业测试
执行mapreduce测试作业:
root@gdc-k8snode03-taylor:~/hadoop-3.2.1# bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.2.1.jar pi 100 1
作业正常执行,且运行在了k8s nodemanager之中:
6. 流程缺陷
- 上述过程中,没有隔离网络和文件系统。容器和主机共用ip和端口,以及文件系统,这不符合上线标准。后续应该每个pod对外以独立的ip和端口进行通信。
- 上述流程pod直接使用本机的文件系统中的hadoop环境启动k8s nodemanager,后续可以将hadoop环境打成镜像启动pod。
7. 方案缺陷及改进展望
上面实现了最基本的Yarn On K8S方案,但是有以下缺陷:
- 需要挂盘。每个NM Pod需要挂载固定盘到机器中,以暂存Shuffle Write数据。对于大作业,可能每个NM Pod要挂载十几个盘,成本高,操作复杂。
- NM资源固定。默认情况下,启动NM时会指定NM的资源,但是当启动Pod的机器正在运行大的实时作业时,可能存在和NodeManager Pod争抢资源的情况,导致实时作业延迟。我们不希望NodeManager Pod影响所在节点正常运行的作业。
- 规则固定。启动Pod和停止Pod的时间是固定的,无法灵活使用集群资源。
- 挂载资源盘,配置NM资源,都会造成较高的人工运维成本。
展望:
- NodeManager资源弹性伸缩:通过Kube-ApiServer的List&Watch机制,获取节点可用资源,汇报给RM,从而实现NodeManager的动态扩缩容以及资源超发。
- 驱逐策略:通过收集每个Container的Metrics,对内存大、CPU占用高的container进行驱逐。尽量不驱逐AM,只驱逐普通container进程。
- 固定盘优化:基于RSS提供存算分离,将原本写入本地盘的Shuffle Write写入RSS,这样就无需挂载本地盘了。
- Pod弹性伸缩:收集Yarn集群和K8S集群的指标,如果Yarn集群资源较为紧张,而K8S集群资源空闲,则在K8s启动Pod对Yarn进行扩容。