共计 3223 个字符,预计需要花费 9 分钟才能阅读完成。
前言
和小伙伴的一起参加的人工智能比赛进入了决赛之后的一段时间里面,一直在构思将数据预处理过程和深度学习这个阶段合并起来。然而在合并这两部分代码的时候,遇到了一些问题,为此还特意写了脚本文件进行处理。现在比赛过去,我觉得应该把这部分的东西写出来,看看是否有其他朋友会遇到这方面问题,希望对他们有帮助。如有不对之处,请大家指正,谢谢!
比赛遇到的集群各节点的部署痛点
一个前提
在初赛的时候,为了快捷提供数据接口给后面的深度学习模型建立使用,我们将数据预处理独立出来,使用了最为简单的 Python 操作。在此,考虑到的是,我们的代码需要移植到评委所用的电脑当中进行验证,可能存在没有 import 某些库的情况,最后导致程序运行失败。
麻烦之处
在进入到决赛后,由于这两块的结合在一起的迫切需求,我们不得不又重新 import 我们库。然而在这块当中,如果我们只在 Master 节点,问题很简单,直接将库打包好,写个脚本就完事了。而在百度的 BMR 的 spark 集群里面,因为 Slaves 节点不能访问网络(如下图),因而我们要登录了 Master 节点之后,然后通过 Master 内网 ssh 到 Slaves 上,进而才能打开我们的脚本部署好我们的程序运行环境。
提出
这样子来说,我们有没有一个很好的办法,能通过 Master 上运行一个脚本,达到了整个集群的所有节点都自动部署我们的程序运行环境呢?经过阅读书籍《spark 最佳实践》,了解到了 Python 的第三方库 fabric。
fabric
首先请允许介绍一下 fabric,具体使用方法可以查阅官方 API Doc,在此我介绍一下我将使用到某一小部分。
执行本地任务
fabric 提供了一个 local("shell")
的接口,shell 就是 Linux 上的 shell 命令。例如
from fabric.api import local
local('ls /root/') #ls root 文件夹下的文件列表
执行远程任务
fabric 的强大之处,不是它在本地做到执行命令,本地执行的事儿可以用原生 shell 来解决,而是能在远程的服务器上执行命令,哪怕远程的服务器上没有安装 fabric。它是通过 ssh 方式实现的,因而我们需要定义一下三个参数:
env.hosts = ['ipaddress1', 'ipaddress2']
env.user = 'root'
env.password = 'fuckyou.'
通过设置 ip,用户名以及用户名密码,我们可以使用run("shell")
,达到在远程服务器上执行我们所需要执行的任务。例如
from fabric.api import run, env
env.hosts = ['ipaddress1', 'ipaddress2']
env.user = 'root'
env.password = 'fuckyou.'
run('ls /root/') #ls root 文件夹下的文件列表
打开某一个文件夹
有时候我们需要精准地打开某一个文件夹,之后执行该文件下的某一个脚本或者文件。这是,我们得使用以下两个接口:
本地
with lcd('/root/local/'):
local('cat local.txt') # cat 本地 '/root/local/' 下的local.txt 文件
远程
with cd('/root/distance/'):
run('cat distance.txt') # cat 远程 '/root/distance/' 下的distance.txt 文件
执行 fabric 任务
我们可以通过命令行
fab --fabfile=filename.py job_func
# filename.py 为使用 fabric 写的 Python 文件
# job_func 为带有 fabric 的函数,即主要执行的函数
# 以上两个名称都是可以自取,下面的介绍当中,我的为 job.py 与 job
socket
为什么会用到 socket 呢?在上一篇文章当中,我提及到在百度 BMR 的集群中,他们设置集群 Slaves 都是通过 slaves 的 hostname 的,而不是通过 ip。而因为在使用 fabric 设置环境中的 hosts 的时候需要用到 ip,那我们得通过 hostname,进而找到 ip。
你或许有疑问,为什么不直接设定 Slaves 的 IP 呢?但是百度 BMR 每次创建 spark 集群,它提供的内网 IP 都是不断在变动的,呈现出 IP 末端递增。
综上,还是使用 hostname 获取 IP
gethostbyname 接口
我们可以用过 gethostbyname('hostname')
接口,传入 hostname,然后得到一个 IPV4 的 ip 地址。
使用 fabric 编写各节点自动部署脚本
获取 Slaves 的 hostname
和上一篇文章说道的一样,我们 Slaves 的 hostname 是藏在了百度 BMR 的这里:
'/opt/bmr/Hadoop/etc/hadoop/slaves'
将 hostname 转化为 ip,设置 fabric 的 env 参数
host_list = []
f = open(path, 'r')
slaves_name = f.read().split('\n')
for i in range(1, slaves_name.__len__()-1):
temp_name = slaves_name[i]
temp_ip = socket.gethostbyname(temp_name)
ip_port = temp_ip + ":22"
host_list.append(ip_port)
del temp_name
del temp_ip
del ip_port
env.user = 'root'
env.password = '*gdut728'
env.hosts = host_list
编写需要自动部署的 job
在这里,我要自动部署的是:
1 下载 Python 第三方库 jieba
2 在本地解压下载好的 jieba 压缩包
3 在本地,进入到解压好的文件夹中,安装 jieba
4 将下载好的压缩包传送到 Slaves 节点上
5 在远程端,解压下载好的 jieba 压缩包
6 在远程端,进入到解压好的文件夹中,安装 jieba
将上面步骤转化为代码,即
def job():
local_command = "wget https://pypi.python.org/packages/71/46/c6f9179f73b818d5827202ad1c4a94e371a29473b7f043b736b4dab6b8cd/jieba-0.39.zip#md5=ca00c0c82bf5b8935e9c4dd52671a5a9"
local(local_command)
jieba_unzip = "unzip jieba-0.39.zip"
jieba_path = "/root/jieba-0.39/"
jieba_install = "python setup.py install"
local(jieba_unzip)
with lcd(jieba_path):
local("ls")
local(jieba_install)
with lcd('/root/'):
put("jieba-0.39.zip", '/root')
run(jieba_unzip)
with cd(jieba_path):
run("ls")
run(jieba_install)
结言
最后,在我上篇文章提到的 shell 脚本中,最前面加上
yum -y install fabric && fab --fabfile=job.py job
输入./start-hadoop-spark.sh
,即可无忧无虑地部署好我要使用的程序运行环境。因为懒,因为麻烦,于是用 Python 外加 shell 写了自动部署的脚本。在这个过程中,学习到不少知识,也遇到不少麻烦,写下文章,希望可以减轻大家配置的烦恼~
结果如下:
Master:
Slaves1:
Slaves2:
本文永久更新链接地址:http://www.linuxidc.com/Linux/2018-01/150609.htm