在Kubernetes中部署YARN

2016-07-24 Klaus Ma 更多博文 » 博客 » GitHub »

原文链接 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 有几个地方还有待改进:

  1. 在Dockerfile 使用了多次 COPY,这样会增加镜像的layer数目
  2. 目前使用了预先生成的 .ssh 文件,可以通过 master_bootstrap.sh 实时生成
  3. master_bootstrap.sh 文件中,每次都会对HDFS进行格式化,如果外挂存储,则应该每次都重新格式化
  4. 目前将 ResourceManager/NodeManager 启动在同一个容器中,后续会分别部署在不同的容器中
  5. 目前的 YARN/HDFS 还只能后台运行,需要改进检测部分:当没有java程序运行时,容器退出

DataNode和NodeManager的Dockerfile与之相似,但除了启动的服务不一样外,对YARN/HDFS配置文件的修改也有所区别,细节会在下面的篇幅介绍;具体可以参考 Dockefile.agentagent_bootstrap.sh

连接ResourceManager与NodeManager

在上面的篇幅中介绍了如何启动YARN/HDFS集群,但是各个节点启动后还需要彼此通信才能工作。因些需要修改YARN/HDFS相应的配置文件:core-site.xml, yarn-site.xmlhdfs-site.xml.

ResourceManager/NodeManager通信相关的配置在yarn-site.xmlcore-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_masteryarn_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 的负担

相关讨论