在Kubernetes中部署YARN
原文链接 http://www.k8s.tips/tech/2016/07/24/yarn_in_k8s/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
在微服务的实践过程中,我们经常会问自己:什么样的应用应该做成微服务?将应用做成微服务时有什么需要注意的?本文针对在Kuberenetes中部署YARN集群遇到的问题,讨论微服务化需要做的工作以及什么样的应用是微服务友好的。
启动 YARN/HDFS 集群
在YARN默认配置中,集群节点通过 ssh 启动:
- 配置集群中的节点可以无密码互访 [1]
- 修改 slaves 文件,在该文件中加入集群节点的主机名或IP
- 通过 start-yarn.sh/start-dfs.sh 启动集群
在这个启动过程中,ssh 扮演了一个远程执行的角色;但远程执行、启动服务正是Kuberenets所做工作的一部分,因此需要对该过程进行如下改造:
- 配置YARN单机ssh的无密码互访;由于start-yarn.sh/start-dfs.sh的原因,即使本机的YARN服务,也需要通过ssh进行启动 > 虽然配置了本机ssh无密码互访,但是仍然会出现如下的提示:
The authenticity of host '9.111.143.205 (9.111.143.205)' can't be established.
ECDSA key fingerprint is SHA256:QQYvT8PFYzVL8oD5fIySTEH5LjPCN8BtayCDLsvp42k.
Are you sure you want to continue connecting (yes/no)?
在微服务的环境中不希望出现需要手动参与的环境,因些需要对unknown host进行设置;修改 ~/.ssh/config
如下:
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
- 不配置
slaves
文件;集群中的节点都由Kuberentes启动 - 通过
start-yarn.sh/start-dfs.sh
启动各个节点
在上述过程中,由于没有配置slaves
文件,start-yarn.sh/start-dfs.sh
会仅启动本机的服务,例如 ResourceManager 或 NodeManager。根据此步骤制作如下的 Dockerfile
:
Master 节点的 Dockerfile 如下:
FROM ubuntu:14.04
MAINTAINER Klaus Ma <klaus1982.cn@gmail.com>
RUN apt-get update && apt-get -y install openjdk-7-jdk openssh-server
COPY hadoop-2.7.2.tar.gz /opt/
RUN cd /opt/ && tar zxvf hadoop-2.7.2.tar.gz && rm hadoop-2.7.2.tar.gz
COPY ./master_bootstrap.sh /opt/bootstrap.sh
COPY core-site.xml.template /root/
COPY yarn-site.xml.template /root/
COPY hdfs-site.xml.template /root/
COPY .ssh /root/.ssh
RUN chmod +x /opt/bootstrap.sh
# Hdfs ports
EXPOSE 31010 50020 50070 50075 50090 8020 9000
# Mapred ports
EXPOSE 19888
#Yarn ports
EXPOSE 8030 8031 8032 8033 8040 8042 8088
#Other ports
EXPOSE 49707 2122
ENTRYPOINT /opt/bootstrap.sh
master_bootstrap.sh 文件内容如下:
#!/bin/sh
export JAVA_HOME=/usr
HADOOP_PREFIX=/opt/hadoop-2.7.2
HADOOP_YARN_HOME=$HADOOP_PREFIX
HADOOP_CONF_DIR=/opt/hadoop-2.7.2/etc/hadoop
sed "s/__HOSTNAME__/"$(hostname)"/g" /root/core-site.xml.template > $HADOOP_CONF_DIR/core-site.xml
sed "s/__HOSTNAME__/"$(hostname)"/g" /root/yarn-site.xml.template > $HADOOP_CONF_DIR/yarn-site.xml
sed "s/__HOSTNAME__/"$(hostname)"/g" /root/hdfs-site.xml.template > $HADOOP_CONF_DIR/hdfs-site.xml
mkdir /opt/hadoop272/dfs/name -p
mkdir /opt/hadoop272/dfs/data -p
$HADOOP_PREFIX/bin/hdfs namenode -format yarn_272_hdfs
$HADOOP_PREFIX/sbin/hadoop-daemon.sh --config $HADOOP_CONF_DIR --script hdfs start namenode
$HADOOP_PREFIX/sbin/yarn-daemon.sh --config $HADOOP_CONF_DIR start resourcemanager
while true; do sleep 1000; done
上面的Dockerfile 有几个地方还有待改进:
- 在Dockerfile 使用了多次
COPY
,这样会增加镜像的layer数目 - 目前使用了预先生成的
.ssh
文件,可以通过master_bootstrap.sh
实时生成 - 在
master_bootstrap.sh
文件中,每次都会对HDFS进行格式化,如果外挂存储,则应该每次都重新格式化 - 目前将
ResourceManager/NodeManager
启动在同一个容器中,后续会分别部署在不同的容器中 - 目前的 YARN/HDFS 还只能后台运行,需要改进检测部分:当没有java程序运行时,容器退出
DataNode和NodeManager的Dockerfile
与之相似,但除了启动的服务不一样外,对YARN/HDFS配置文件的修改也有所区别,细节会在下面的篇幅介绍;具体可以参考 Dockefile.agent 和 agent_bootstrap.sh
连接ResourceManager与NodeManager
在上面的篇幅中介绍了如何启动YARN/HDFS
集群,但是各个节点启动后还需要彼此通信才能工作。因些需要修改YARN/HDFS相应的配置文件:core-site.xml
, yarn-site.xml
和 hdfs-site.xml
.
与ResourceManager
/NodeManager
通信相关的配置在yarn-site.xml
和core-site.xml
中,为了使ResourceManager和NodeManager可以相互通信,在镜像中制件了如下的模板:
core-site.xml
<property>
<name>fs.defaultFS</name>
<value>hdfs://__HOSTNAME__:9000</value>
</property>
yarn-site.xml
<property>
<name>yarn.resourcemanager.address</name>
<value>__HOSTNAME__:8032</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>__HOSTNAME__:8030</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>__HOSTNAME__:8031</value>
</property>
<property>
<name>yarn.resourcemanager.admin.address</name>
<value>__HOSTNAME__:8033</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address</name>
<value>__HOSTNAME__:8088</value>
</property>
在不同的镜像中,__HOSTNAME__
会被替换为不同的值:在master_bootstrap.sh
中,被替换为容器的hostname;而在agent_bootstrap.sh
中,由被替换为yarn_master
。yarn_master
是ResourceManager在Kubernetes的Service,因此在配置了Kube-DNS后,各个 NodeManager 节点可以找到该ResourceManager节点,并与之通信。
在进行了上述配置后,ResourceManager与NodeManager可以正常通信;但是 NameNode 和 DataNode 仍然无法通信。这主要是由于 NameNode 会对连接进来的DataNode进行 ip/hostname 的检查,由于没有对DataNode配置相应DNS,NameNode会拒绝末知的 ip/hostname。HDFS提供了如下的配置,可以忽略 ip/hostname的检查:
hdfs-site.xml
<property>
<name>dfs.namenode.datanode.registration.ip-hostname-check</name>
<value>false</value>
</property>
但是上面的配置并不能完全解决问题:虽然 NameNode 不再进行 ip/hostname 检测,但在访问 DataNode 的时候会取得错误的ip: NameNode 使用主机 ip 而不是容器 ip 与 DataNode 通信。
已知的 Hadoop 镜像
不同厂商在 Docker Hub 上都提供了Hadoop 镜像,但多为单一镜像且需要手动配置才能正常工作,例如 ssh的无密码访问。目前还没有社区版本的Hadoop镜像,但已经创建了HADOOP-13397进行需求的跟踪。
镜像 | 公司 | 备注 |
---|---|---|
sequenceiq/hadoop-docker | Hortonwork | 单一镜像,手动配置 |
cloudera/quickstart | Cloudra | 单一镜像,手动配置 |
maprtech/mapr-sandbox-base | MapR | 单一镜像 |
问题总结
- Kubernetes/Marathon等软件已经为应用提供了强大的部署、启动等功能,因此当集群软件向Kubernetes/Marathon迁移时应该注意以下几点:
1. 避免使用集群自身的部署工具,例如,YARN中的ssh
2. 尽量做到信息隔离,例如,避免ssh无密码互访:无密码互访时,每个容器都需要知道其它所有容器的 ssh 信息
3. 为Docker提供前台运行的脚本,减少二次开发,例如,YARN Dockerfile 中的java检测脚本
- 在网络方面,以下几个方面可以有效的减少微服务化过程中遇到的问题:
1. 初始连接尽量为单向连接,后续的连接动态创建;避免相互依赖,例如,HDFS中`NameNode`和`DataNode`的连接,只需要 `yarn_master` 就可以建立通信
2. 尽量使用 IP 进行访问,避免对hostname依赖;不建议为hostname 建立DNS:每个新启动的docker都有新的hostname,docker的启停会加重 DNS 的负担
相关讨论
- http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html
- https://github.com/k82cn/outrider/tree/master/kubernetes/imgs/yarn
- https://issues.apache.org/jira/browse/HADOOP-13397
- https://github.com/kubernetes/application-images/pull/8
- http://mail-archives.apache.org/mod_mbox/hadoop-user/201607.mbox/%3CSG2PR04MB162977CFE150444FA022510FB6370%40SG2PR04MB1629.apcprd04.prod.outlook.com%3E