提示:转载请附原文链接


前言

近期想用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 运行的 sourcekinit,运行的效果都在 os.system 新建的进程中,而不是作用于当前运行python的进程中,所以导致了不生效。

还有一个导致我一直测试不出来的就是是使用nohup在后台运行的jupyter服务,服务起得很早,后面大数据平台配置有变更,而我没有重启jupyter服务。

不管是运行python文件或开jupyter,都要先引入环境,然后再运行py文件或jupyter。


总结

以上就是本次记录的内容,特别注意的是环境引入的坑。

感谢彭老师花费时间协助解决问题。

Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐