提示:转载请附原文链接
前言
近期想用python写个数据任务的调度工具,使用pyhive连接华为大数据平台的hive不成功,在彭老师的协助下最终完成了hive的连接,特此记录一下过程。
一、准备工作
-
PyHive源码包:我用的 PyHive-0.6.5
-
安装系统依赖(CentOS,其他linux类似): yum安装以下包,cyrus-sasl、cyrus-sasl-lib、cyrus-sasl-devel、cyrus-sasl-md5、cyrus-sasl-plain、cyrus-sasl-gssapi (Kerberos需要用GSSAPI, 来自cyrus-sasl-gssapi)
运行
sasl2-shared-mechlist
命令包含GSSAPI
应该就可以了
二、处理过程
1. 前期报错情况
代码如下(ip、端口、主机名均替换为非真实值):
from pyhive import hive
conn = hive.Connection(host="hive_server2_host", port=10000, auth="KERBEROS", kerberos_service_name="hive")
报错如下:
TTransportException Traceback (most recent call last)
/tmp/ipykernel 23431/908701831.py in <module>
----> 1 conn = hive.Connection(host=”hive_server2_host", port=10000, auth="KEBEROS", kerberos_service_name="hive" )
/data/anaconda3/lib/python3.9/site-packages/PyHive-0.6.5-py3.9. egg/pyhive/hive.py in __init__ (self, host, port, scheme, username, database, auth, configuration, kerberos_ service_name, password, check_hostname, ssl_cert, thrift_transport)
241
242 try:
--> 243 self._transport.open()
244 open_session_req = ttypes.TOpenSessionReq(
245 client protocol=protocol version,
/data/anaconda3/lib/python3.9/site-packages/thrift_sasl/__init__.py in open(self)
82 ret, chosen_mech, initial_response = self.sasl.start(self.mechanism)
83 if not ret:
--> 84 raise TTransportException(type=TTransportException.NOT_OPEN,
85 message=("Could not start SASL: %s" % self.sasl.getError()))
86
TTransportException: Could not start SASL: b'Error in sasl_client_start (-1) SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure. Minor code may provide more information (Server hive/hive_server2_host@FI1.COM not found in Kerberos database)
简要分析:报错中提示“Server hive/hive_server2_host@FI1.COM not found in Kerberos database”,但我实际使用beeline的时候,看到的提示是:“Connecting to jdbc:hive2://…;principle=hive/hadoop_fi1_host@FI1.COM”,那么可能需要修改pyhive源码,尝试让验证时使用“hadoop_fi1_host”的priciple而不是“hive_server2_host”
2. 修改源码并测试
2.1 分析一下需要修改哪些位置
解压:PyHive-0.6.5.tar.gz,然后找到目录下的 pyhive/hive.py
。从以上报错可以看到,在self._transport.open()
时产生了错误,那么我们找到使用KERBEROS认证时源码中哪里定义了self._transport
:
if auth == 'NOSASL':
# NOSASL corresponds to hive.server2.authentication=NOSASL in hive-site.xml
self._transport = thrift.transport.TTransport.TBufferedTransport(socket)
elif auth in ('LDAP', 'KERBEROS', 'NONE', 'CUSTOM'):
# Defer import so package dependency is optional
import sasl
import thrift_sasl
if auth == 'KERBEROS':
# KERBEROS mode in hive.server2.authentication is GSSAPI in sasl library
sasl_auth = 'GSSAPI'
else:
sasl_auth = 'PLAIN'
if password is None:
# Password doesn't matter in NONE mode, just needs to be nonempty.
password = 'x'
def sasl_factory():
sasl_client = sasl.Client()
# 这里 第217行
sasl_client.setAttr('host', host)
if sasl_auth == 'GSSAPI':
sasl_client.setAttr('service', kerberos_service_name)
elif sasl_auth == 'PLAIN':
sasl_client.setAttr('username', username)
sasl_client.setAttr('password', password)
else:
raise AssertionError
sasl_client.init()
return sasl_client
# 这里 第227行
self._transport = thrift_sasl.TSaslClientTransport(sasl_factory, sasl_auth, socket)
第227行的self._transport = thrift_sasl.TSaslClientTransport(sasl_factory, sasl_auth, socket)
中,sasl_factory
是一个函数,用来创建sasl_client并设置相关属性。然后可以看到第217行设置了sasl的hostsasl_client.setAttr('host', host)
。
另外在源码中发现与Kerberos相关的host设置的地方还有(第160行):
elif auth == "KERBEROS" and kerberos_service_name:
self._set_kerberos_header(thrift_transport, kerberos_service_name, host)
对Kerberos了解不多,因此不确定此处是否需要修改,如果前面那处不行再作尝试。
2.2 修改源码
修改 class Connection
的 __init__
函数,添加krb_host
参数,如下所示:
class Connection(object):
"""Wraps a Thrift session"""
def __init__(
self,
host=None,
port=None,
scheme=None,
username=None,
database='default',
auth=None,
configuration=None,
kerberos_service_name=None,
password=None,
check_hostname=None,
ssl_cert=None,
thrift_transport=None,
krb_host=None
):
修改sasl_factory()
函数(sasl_client.setAttr('host', host)
处如果krb_host
不为None就使用krb_host
):
def sasl_factory():
sasl_client = sasl.Client()
if krb_host is not None:
sasl_client.setAttr('host', krb_host)
else:
sasl_client.setAttr('host', host)
if sasl_auth == 'GSSAPI':
sasl_client.setAttr('service', kerberos_service_name)
elif sasl_auth == 'PLAIN':
sasl_client.setAttr('username', username)
sasl_client.setAttr('password', password)
else:
raise AssertionError
sasl_client.init()
return sasl_client
2.3 从源码安装pyhive并测试
# 卸载原有的
pip uninstall pyhive
# 从源码安装
cd PyHive-0.6.5
python setup.py install
测试情况
from pyhive import hive
conn = hive.Connection(
host="hive_server2_host",
port=10000,
auth="KERBEROS",
kerberos_service_name="hive",
krb_host="hadoop_fi1_host"
)
cursor = conn.cursor()
cursor.execute("show databases")
print(cursor.fetchone())
cursor.close()
conn.close()
结果如下:
('default',)
成功!
三、踩坑
1. 环境引入问题
第一次按上述方法修改完成后依然报错:
TTransportException: Could not start SASL: b'Error in sasl_client_start (-1) SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure. Minor code may provide more information (Cannot find KDC for realm "FI1.COM")
一度以为以上的修改思路不对,百般尝试都不顺利。后在彭老师提醒下可能是环境引入的问题(我在jupyter中使用os.system或subprocess尝试的环境引入),建议在终端中先引入环境再在python中尝试。
然后在终端中尝试:
[user@centos ~]$ source /opt/hadoop_client/bigdata_env
[user@centos ~]$ kinit -kt ~/etc/user.keytab username
[user@centos ~]$ python
>>> from pyhive import hive
>>> conn = hive.Connection(host="hive_server2_host", port=10000, auth="KERBEROS", kerberos_service_name="hive", krb_host="hadoop_fi1_host")
一切正常,不再报错。
分析原因应该是,使用的 os.system
运行的 source
和 kinit
,运行的效果都在 os.system
新建的进程中,而不是作用于当前运行python的进程中,所以导致了不生效。
还有一个导致我一直测试不出来的就是是使用nohup在后台运行的jupyter服务,服务起得很早,后面大数据平台配置有变更,而我没有重启jupyter服务。
不管是运行python文件或开jupyter,都要先引入环境,然后再运行py文件或jupyter。
总结
以上就是本次记录的内容,特别注意的是环境引入的坑。
感谢彭老师花费时间协助解决问题。