本系列主要会记录笔者在学习和使用 Presto 过程中学习记录及所遇到的问题和解决的方法,会不定期更新,同时也欢迎各位同鞋在文末留言一起探讨使用心得~
概述
什么是 Presto On Yarn ?为啥要有 Presto On Yarn ,能解决什么问题?实现 Presto On Yarn 需要做些什么?
Presto On Yarn 是指将 Presto 集群运行在 Yarn 集群上面将集群资源统一交由 Yarn 进行管理;
在实际工作环境中,Presto 集群进行单独部署需要单独的占用额外资源,而 Presto 主要用于查询功能,在夜晚时间集群资源处于空闲状态;而大数据集群的资源则主要用于夜晚任务调度运行,在白天则使用的资源较少空闲资源较多;因此大数据集群和 Presto 集群的资源在白天和晚上正好形成了互补,所以将 Presto 集群部署在 Yarn 上很完美的解决了资源空闲的问题,同时 Presto On Yarn 还支持通过命令快速增加和移除 Presto 节点的功能,方便快速运维;
在调研过程中,要实现 Presto On Yarn 我们需要借助 Apache Slider 来帮助我们实现,同时在 Presto On Yarn 的过程中笔者遇到的问题有:
- 由于 Presto 节点端口是写在配置文件中的一个固定值,当一个 Yarn 节点上启动两个 Container 来运行 Presto 节点时可能出现端口冲突问题,这里笔者使用动态端口的方式进行解决;
关于这个问题官网推荐使用 yarn 的 label 功能来控制 Container 的分配,大家可以去试试。由于笔者担心影响其他任务运行所以没有去使用这种方法;同时想到使用这种方法当我们需要的 Presto 节点超出 Yarn 节点时应该也会出现一个 Yarn 节点需要运行两个 Container 来运行 Presto 节点
- 同理在配置文件中我们需要指定一个 Presto 运行时的 etc 和 data 目录,当一个 Yarn 节点上启动两个 Container 来运行 Presto 节点时该目录也会出现冲突,这里笔者将 etc 和 data 目录指定到 Yarn 运行时的程序目录下,这样每个 Container 就会有自己唯一的一个路径不会冲突;
- 还有就是在 Presto On Yarn 模式下,重启集群后 Coordinator 节点地址会发生变化,在其他人员使用的时候就需要更改连接地址,使用起来不太友好,笔者这里采用的解决方法是将 Coordinator 固定单独部署,在 Yarn 节点上只运行 Worker 节点,这样做同时也方便了后面整合 Ranger 实现权限控制。
部署
资源准备
Apache Slider 编译
- 编译环境
- mavne 3.6.3
- JDK1.8
- apache slider 0.92
- 源码下载 http://archive.apache.org/dist/incubator/slider
- 源码编译
- pom.xml 文件修改 修改 java.version 至 1.8 默认是 1.7
<project.java.src.version>1.8</project.java.src.version>
- hadoop 版本修改,建议最好不要使用 hadoop3.x 会有很多的错误(笔者遇到)
- 编译
mvn clean package -DskipTests
编译完成后在 slider-assembly/target 目录有我们需要的 slider-0.90.2-incubating-all.tar.gz
Presto-yarn 编译
- 编译环境
- maven 3.6.3
- JDK1.8
- trino-yarn
- 源码下载 gitub 地址:https://github.com/trinodb/trino-yarn.git
- 源码编译
mvn clean package -Dpresto.version=333
# 编译成功
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for presto-yarn 1.6-SNAPSHOT:
[INFO]
[INFO] presto-yarn ........................................ SUCCESS [ 0.910 s]
[INFO] presto-yarn-package ................................ SUCCESS [ 27.899 s]
[INFO] presto-yarn-test ................................... SUCCESS [01:33 min]
[INFO] presto-yarn-docs ................................... SUCCESS [ 14.138 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:18 min
[INFO] Finished at: 2021-06-17T14:15:08+08:00
[INFO] ------------------------------------------------------------------------
如果编译时有 presto-ml 包找不到可以自行下载安装至本地 maven 仓库
下载地址:https://repo1.maven.org/maven2/io/prestosql/presto-ml/333/
安装至本地 maven 仓库
mvn install:install-file -DgroupId=io.prestosql -DartifactId=presto-ml -Dversion=333 -Dpackaging=jar -Dfile=/Downloads/trino-ml-353.jar
编译成功后在 presto-yarn-package/target 目录有我们需要的 presto-yarn-package-1.6-SNAPSHOT-333.zip
基于 Apache Slider 部署 Presto on Yarn
Apache Slider 部署
- 解压 slider-0.90.2-incubating-all.tar.gz
tar -zxvf slider-0.90.2-incubating-all.tar.gz
- 配置 进入conf目录
- slider-client.xml 主要配置如下
<property>
<name>slider.yarn.queue</name>
<value>default</value>
<description>slider 提交任务的 yarn 队列</description>
</property>
<property>
<name>slider.zookeeper.quorum</name>
<value>node1:2181,node2:2181</value>
<description>zk 地址 存储 slider 提交任务</description>
</property>
<property>
<name>fs.defaultFS</name>
<value>hdfs://HDPCluster/user/yarn/.slider</value>
<description>slider 相关资源包hdfs存放地址</description>
</property>
- slider-env.xml
export JAVA_HOME=${JAVA_HOME} # java 路径
export HADOOP_CONF_DIR=/etc/hadoop/conf # hadoop 集群配置路径 用于读取集群信息
Presto 配置
解压 presto-yarn-package-1.6-SNAPSHOT-333.zip 得到 appConfig-default.json 和 resource-default.json 两个文件,分别进行配置,参考地址:http://prestodb.io/presto-yarn/installation-yarn-configuration-options.html
- appConfig.json
{
"schema": "http://example.org/specification/v2.0.0",
"metadata": {
},
"global": {
# 这里 site.global. 都是关于 Presto 集群的参数
"site.global.app_user": "yarn", # 提交 presto 任务的用户
"site.global.user_group": "hadoop", # 提交 presto 任务的用户
"site.global.data_dir": "/var/lib/presto/data", # presto 任务存放数据的目录 每个节点都应该先建好
"site.global.config_dir": "/var/lib/presto/etc", # presto 任务存放配置信息的目录 每个节点都应该先建好
"site.global.app_name": "presto-server-333",# 很重要要跟 presto-server-333.tar.gz同名 不然会启动不了
"site.global.app_pkg_plugin": "${AGENT_WORK_ROOT}/app/definition/package/plugins/",
"site.global.singlenode": "true", # 为 true 表示该节点既是 coordinator 也是 worker
"site.global.coordinator_host": "${COORDINATOR_HOST}",
"site.global.presto_query_max_memory": "50GB", # 表示单个查询在分布在所有相关节点上能用的内存之和的最大值
"site.global.presto_query_max_memory_per_node": "600MB", # 表示单个查询在单个节点上用户内存能用的最大值
"site.global.presto_query_max_total_memory_per_node": "600MB", # 表示单个查询在单个节点上用户内存能用的最大值和系统内存量。其中系统内存是读取器、写入器和网络缓冲区等在执行期间使用的内存。
"site.global.presto_server_port": "8089", # coordinator 端口 默认 8080
"site.global.catalog": "{'hive': ['connector.name=hive-hadoop2', 'hive.metastore.uri=thrift://SRV-Apollo-Node3.YFDYF.Biz:9083'],'tpch': ['connector.name=tpch']}", # catalog 配置
"site.global.jvm_args": "['-server', '-Xmx1024M', '-XX:+UseG1GC', '-XX:G1HeapRegionSize=32M', '-XX:+UseGCOverheadLimit', '-XX:+ExplicitGCInvokesConcurrent', '-XX:+HeapDumpOnOutOfMemoryError', '-XX:OnOutOfMemoryError=kill -9 %p']",
"site.global.log_properties": "['com.facebook.presto=INFO']",
"application.def": ".slider/package/PRESTO/presto-yarn-package-1.6-SNAPSHOT-333.zip", #
"java_home": "/usr/local/jdk11" # jdk 版本 必须 >= 11
},
"components": {
"slider-appmaster": {
"jvm.heapsize": "128M"
}
}
}
- resource.json
{
"schema": "http://example.org/specification/v2.0.0",
"metadata": {
},
"global": {
"yarn.vcores": "1"
},
"components": {
"slider-appmaster": {
},
# 由于 COORDINATOR 单独部署这里可以去掉
# "COORDINATOR": {
# "yarn.role.priority": "1",
# "yarn.component.instances": "1", # 实例数
# "yarn.component.placement.policy": "1",
# "yarn.memory": "1500" # 内存
# "yarn.label.expression": "coordinator" # yarn label 否则需要yarn 支持 node labels 并且需要给相应的队列配置lebel ,可用于 控制 提交节点
# },
"WORKER": {
"yarn.role.priority": "2",
"yarn.component.instances": "3", # 实例数
"yarn.component.placement.policy": "1",
"yarn.memory": "1500",
}
}
}
使用 Slider 提交 presto on yarn
环境准备
- jdk 环境准备 上传 jdk 11 版本至所有节点,解压至 appConfig.json 中 java_home 配置的目录
- 启动脚本修改,Presto On Yarn 的 Presto 启动时的配置文件准备主要由 presto-yarn-package-1.6-SNAPSHOT-353.zip 里面的
package/scripts 目录下的 *.py 脚本控制,关于参数我们自己在 presto-yarn-package-1.6-SNAPSHOT-353.zip 下的 package/files/presto-server-333.tar.gz 自己建好文件及相关配置信息打包进去 ,除了 node.properties 的 node.data-dir 和 node.id 由脚本生成写入;
创建配置文件并生成 presto-server-333.tar.gz
# 解压 presto-yarn-package-1.6-SNAPSHOT-353.zip
unzip presto-yarn-package-1.6-SNAPSHOT-353.zip
cd package/files
# 解压 presto-server-333.tar.gz
tar -zxvf presto-server-333.tar.gz
cd presto-server-333
# 创建配置文件
mkdir etc
cd etc
vim config.properties
## -- config.properties 配置
coordinator=false
http-server.http.port=8080
query.max-memory=50GB
query.max-memory-per-node=1GB
query.max-total-memory-per-node=2GB
discovery.uri=http://192.168.4.54:8080
vim node.properties
## -- node.properties 配置
node.environment=prestoonyarn
vim jvm.properties
## -- jvm.properties 配置
## 注意 -Xmx20G 配置为 20 G 我们在 resource.json 中 worker 的 yarn.memory 要大于等于此值
-server
-Xmx20G
-DHADOOP_USER_NAME=hive
-XX:+UseG1GC
-XX:G1HeapRegionSize=32M
-XX:+UseGCOverheadLimit
-XX:+ExplicitGCInvokesConcurrent
-XX:+HeapDumpOnOutOfMemoryError
-XX:OnOutOfMemoryError=kill -9 %p
# 创建 catalog 目录 及 catalog 信息
# 创建运行时 data 目录
cd presto-server-333
mkdir data
# 将 presto-server-333 打包成 presto-server-333.tar.gz
tar -cvf presto-server-333.tar.gz presto-server-333
开始的 presto-server-333
创建完后的 presto-server-333
*修改启动脚本 .py 脚本
cd package/scripts
主要的脚本如下:
params.py
# 修改如下两项
conf_dir = format("{presto_root}/ect2")
data_dir=format("{presto_root}/data")
configure.py 用来生成配置文件,由于我们与多配置文件已经打包进 presto-server 的压缩包了所以该文件修改比较多
def set_configuration(component=None, root_dir=None):
import params
print '-------------------data_dir' + format("{data_dir}")
if (os.path.exists(format("{data_dir}"))):
shutil.rmtree(format("{data_dir}"))
print '-------------------data_dir' + format("{conf_dir}")
if (os.path.lexists(format("{conf_dir}"))):
os.remove(format("{conf_dir}"))
#_directory(params.conf_dir, params)
_directory(params.data_dir, params)
_directory(params.pid_dir, params)
_directory(params.log_dir, params)
# 写入node.id 和 node.data-dir
with open(root_dir, 'a') as fw:
fw.write("node.id=%s\n" % uuid.uuid1())
fw.write("node.data-dir=%s\n" % params.data_dir)
if params.addon_plugins:
plugins_dict = ast.literal_eval(params.addon_plugins)
for key, value in plugins_dict.iteritems():
plugin_dir = os.path.join(params.presto_plugin_dir, key)
if not os.path.exists(plugin_dir):
os.makedirs(plugin_dir)
for jar in value:
shutil.copy2(os.path.join(params.source_plugin_dir, jar), plugin_dir)
def _directory(path, params):
Directory(path,
owner=params.presto_user,
group=params.user_group,
recursive=True
)
presto_server.py 主要新增解决端口问题方法
class PrestoServer(Script):
def __init__(self, component):
self.component = component
def install(self, env):
self.install_packages(env)
pass
def configure(self, root_dir):
set_configuration(self.component, root_dir=root_dir)
# 解决端口问题
def check_port(self):
import params
port = params.presto_server_port
flag = True
while flag:
res = socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect_ex(('127.0.0.1', port))
if res == 0:
port += 1
else:
params.presto_server_port = port
break
def start(self, env):
import params
env.set_params(params)
self.check_port()
root_dir = format('{presto_root}/etc/node.properties')
self.configure(root_dir=root_dir)
os.symlink(os.path.join(format('{presto_root}'), 'etc'),format('{conf_dir}'))
#self.configure()
#os.symlink(format('{conf_dir}'), os.path.join(format('{presto_root}'), 'etc'))
process_cmd = format("PATH={java8_home}/bin:$PATH {presto_root}/bin/launcher run --node-config {conf_dir}/node.properties --jvm-config {conf_dir}/jvm.config --config {conf_dir}/config.properties >> {log_file} 2>&1")
Execute(process_cmd,
logoutput=True,
wait_for_finish=False,
pid_file=params.pid_file,
poll_after=3
)
def stop(self, env):
# Slider doesnt yet support stopping the actual app (PrestoServer) process
# but only stopping the yarn application. Slider is not wired up yet to call this function.
pass
def status(self, env):
import params
env.set_params(params)
check_process_status(params.pid_file)
if __name__ == "__main__":
self.fail_with_error('Component name missing')
最后打成 zip 包
zip -q -r presto-yarn-package-1.6-SNAPSHOT-353.zip .
任务提交
- slider 安装 PRESTO 包 会提交到 slider-client.xml 中配置的 fs.defaultFS 目录
./bin/slider package --install --name PRESTO --package /data/presto-yarn/presto-yarn-package-1.6-SNAPSHOT-353.zip
- 提交 presto 任务
./bin/slider create presto-query --template /data/presto-yarn/presto-yarn-package/appConfig.json --resources /data/presto-yarn/presto-yarn-package/resources.json
- 查看任务信息
- 在 yarn 8088 页面查看是否提交成功,AM 界面可查看一些日志和集群信息
- 使用 ./bin/slider status presto-query 其中 presto-query 就是 create 的使用的名称
查看 Presto 页面,Worker 启动成功就可以啦
遇到的错误
- failed to onnect please check openssl library
# 修改 cert-verification.cfg 默认是 paltform_default
vim /etc/python/cert-verification.cfg
verify=disable
- yarn 任务提交不上 即 Slider-Master 页面 coordinator 和 worker actual 为0,这里我碰到的错误是 yarn 队列 label 可能没配置好,我把 resource.json 中的 yarn.label.expression 去掉就解决了
- 使用 hadoop3.x 编译Slider后提交任务,出现很多jar包冲突,解决过程就是去查找对应的jar包,不多复述了,建议使用 hadoop2.x 编译就行,同样可以在 hadoop3.x 的 yarn 上去提交任务
- 使用命令行连接查询(后面会讲怎样使用命令行去连接 trino )时 show tables;show catalog 等操作可以成功,运行查询语句是报错
Query 20210331_052257_00010_ajs84 failed: java.net.UnknownHostException: HDPCluster
# 解决 在hive catlog 中 添加参数
hive.config.resource=xxx/hdfs-site.xml,xxx/core-site.xml,xxx/hive-site.xml
结语
本文主要分享了读者在实际工作中解决 Presto 和大数据集群资源空闲互补问题,同时也分享了 Presto On Yarn 的部署思路和遇到的一些问题的解决方案,可能不是最好的方案,尤其是在启动脚本那块,笔者对 Python 不太熟悉,可能看上去会有点瓦,哈哈,欢迎由熟悉的大佬能给出更好的建议呀 ~ 欢迎留言交流呦
Prsto 系列
Presto 系列之(一)初识 Presto
Presto 系列之(二)Presto 安装部署
欢迎各位关注笔者公众号:此文在公众号同步推送,后续系列也会在近期推出呦~