最近折腾了一套在Kubernetes上搭建Hue+HDFS+Hive实验环境的活儿,中间踩了不少坑。这套组合在物理机上很常见,但搬到K8s之后,问题就变成"HDFS这种有状态服务怎么编排""Hive拆成Metastore和HiveServer2该怎么连""Hue到底要配多少个地址才能把HDFS和Hive都串起来"。这篇记录的是完整部署过程,包括镜像选型、配置注入、持久化方案和排障链路,给想在K8s里搭建Hue+HDFS+Hive实验环境的朋友做个参考,尤其适合刚接触容器化大数据、希望在本地或测试集群里快速跑通一套可视化数据分析环境的读者。
1. 先搞清楚这套组合里每个角色的"脾气"
很多人在K8s里部署失败,不是配置写错了,而是没理解每个组件在容器环境里的边界。Hue、HDFS、Hive这三个名字放在一起叫"大数据实验环境",但它们的运行模型完全不同,先理清楚再动手,后面会顺很多。
1.1 HDFS不是普通无状态应用
HDFS的NameNode保存着整个集群的文件系统元数据,DataNode保存真实数据块。这两个角色天然是有状态服务:NameNode一旦重启,必须能找到自己之前写过的元数据目录;DataNode必须能挂载到同一块数据盘,否则节点重启后数据就"消失"了。在物理机上这是靠目录和进程守护解决的,而在K8s里就必须依赖StatefulSet加PVC,不能随便用Deployment去跑。
实验环境里我建议把HDFS副本数直接设成1,而且dfs.replication也设为1。原因很简单:三副本是为生产容错准备的,实验环境里一台机器模拟存储语义就够了,三副本只会白白占用磁盘空间和网络带宽,还会让单节点上出现"块等待复制"的告警。
1.2 Hive实际上要拆成两个进程再配一个数据库
Hive不是一个独立计算引擎,它只是把SQL翻译成MapReduce/Tez/Spark任务,并且把表结构、分区、字段信息这些元数据存进关系数据库。
所以Hive在服务层面要拆成两部分:
- Metastore:负责读写元数据库(我用的是MySQL),对外提供Thrift接口,默认端口9083。
- HiveServer2:真正接收JDBC和Thrift请求的入口,默认端口10000,Hue连的是它,不是直接连Metastore。
很多人以为部署一个Hive容器就够了,结果连Hue的时候怎么都连不上。Hive的官方Cli和HiveServer2是两套东西,实验环境里建议两个都跑在同一个镜像里,用不同命令启动就行。
1.3 Hue是"前台",它自己不存数据
Hue的角色更像是一个浏览器端的集成工作台。它要同时"够到"HDFS和Hive:
- 访问HDFS:通过NameNode的WebHDFS HTTP接口,默认端口9870。
- 访问Hive:通过HiveServer2的Thrift接口,默认端口10000。
同时Hue自己也需要一个数据库来存账号信息、查询历史、作业记录,实验环境里我直接用同一个MySQL实例,单独建一个名为hue的库。
1.4 部署前的端口速查表
把整个环境里需要打通的端口列成一张表,后面配置每写一个主机名和端口,照着表核对就行。
| 组件 | 端口 | 用途 |
|---|---|---|
| HDFS NameNode RPC | 9820 | HDFS客户端写入,Hive/Hue底层依赖 |
| HDFS NameNode HTTP | 9870 | Web UI和WebHDFS API |
| HDFS DataNode HTTP | 9864 | DataNode状态页 |
| Hive Metastore | 9083 | Hive元数据Thrift服务 |
| HiveServer2 | 10000 | JDBC/Thrift查询入口 |
| Hue | 8888 | Web控制台 |
| MySQL | 3306 | Hive元数据库和Hue业务库 |
这张表里的端口不是随便写的,都是各组件默认端口。K8s里只要Service映射准确,容器间通信基本不会出问题,出问题的大多是主机名写错或者端口写成了老版本的8020、50070。
2. 存储底座先行:HDFS容器化部署的核心编排思路
HDFS是整个环境的底座,Hive和Hue都依赖它。所以我把HDFS放到最先部署,等它稳定了再往上叠其他组件。
2.1 有状态服务为什么必须用StatefulSet
K8s里Deployment创建的Pod名称是随机后缀,比如namenode-6f4b5c7d9d-xk2p1,Pod重建后名称和IP都会变。HDFS客户端如果持有一个旧IP,就会立刻失联。
StatefulSet解决的正是这个问题:Pod名称稳定,比如hdfs-namenode-0,而且在配合Headless Service时,这个名称可以直接作为稳定的DNS主机名被其他Pod解析。
我用以下方式组织HDFS的K8s资源:
- 一个Headless Service,名为
hdfs-nn-svc,用于NameNode服务发现。 - 一个StatefulSet,管理NameNode,挂载PVC。
- 一个StatefulSet,管理DataNode,也挂载PVC,实验环境可以先只跑一个副本。
- 一个ConfigMap,保存
core-site.xml和hdfs-site.xml。
2.2 配置注入的关键点:别再把配置打进镜像里
Hadoop有一个特点:配置文件主要在core-site.xml、hdfs-site.xml里。很多教程会让人改完配置再build一个镜像,这样也能跑,但每次调参数都要重新build,非常不灵活。
更好的做法是用ConfigMap挂载配置目录,把配置文件覆盖到镜像里的/opt/hadoop/etc/hadoop/下。这样改配置只需要kubectl apply更新ConfigMap,然后重启Pod。
core-site.xml里最核心的配置就是默认文件系统地址:
<property> <name>fs.defaultFS</name> <value>hdfs://hdfs-nn-svc:9820</value> </property>这里的主机名hdfs-nn-svc对应的是K8s Service名,这样即使NameNode Pod重启,主机名也不会变。端口用9820而不要用老的8020,Hadoop 3.x已经全面切换到9820。
hdfs-site.xml里几个关键项:
<property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///hdfs/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file:///hdfs/data</value> </property>dfs.namenode.name.dir和dfs.datanode.data.dir这两个目录一定要放在PVC挂载的路径下。不然Pod一重启,NameNode会认为自己从没初始化过,直接报"storage directory does not exist"。
2.3 一个最小可用的NameNode编排片段
不用把完整YAML都贴出来,拿最关键的部分说。NameNode用StatefulSet,关键点在于以下结构:
apiVersion: apps/v1 kind: StatefulSet metadata: name: hdfs-namenode spec: serviceName: hdfs-nn-svc replicas: 1 selector: matchLabels: app: hdfs-namenode template: metadata: labels: app: hdfs-namenode spec: containers: - name: namenode image: hadoop:3.3.4 command: ["hdfs", "namenode"] ports: - containerPort: 9820 - containerPort: 9870 volumeMounts: - name: nn-data mountPath: /hdfs/name - name: hadoop-conf mountPath: /opt/hadoop/etc/hadoop volumes: - name: hadoop-conf configMap: name: hdfs-config volumeClaimTemplates: - metadata: name: nn-data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi对应的Headless Service:
apiVersion: v1 kind: Service metadata: name: hdfs-nn-svc spec: clusterIP: None selector: app: hdfs-namenode ports: - name: rpc port: 9820 - name: http port: 9870Headless Service的clusterIP: None是重点。没有它,Pod的DNS名称不会被解析到固定Pod;有了它,hdfs-nn-svc才会返回NameNode Pod的地址。DataNode和Hive连接时通过这个服务名就能找到NameNode。
2.4 NameNode第一次启动的特殊处理
这里有一个实验环境最容易踩的坑:NameNode首次启动前需要格式化元数据目录。
如果不格式化,NameNode直接启动会报错找不到current/VERSION文件。在K8s里执行格式化很简单:
kubectl exec -it hdfs-namenode-0 -- hdfs namenode -format -force执行完成后,如果StatefulSet还在等启动,把它删掉重新创建,Pod会自动按新PVC再启一次。格式化这一步只在数据目录为空的时候做,如果之前已经格式化过又带着PVC,不要再执行,否则会清空元数据。
启动完成后,两个验证手段:
- 看Pod日志:
kubectl logs -f hdfs-namenode-0,日志里出现STARTUP_MSG并且没有FATAL错误就算正常。 - 浏览器访问
http://<NodeIP>:9870,能看到NameNode Web UI,且Safemode显示为Off。
如果显示Safemode is ON,说明NameNode在等待数据块上报,单节点实验环境可以手动退出安全模式:
kubectl exec -it hdfs-namenode-0 -- hdfs dfsadmin -safemode leaveDataNode的部署结构和NameNode类似,只是挂载点换成/hdfs/data,镜像启动命令是hdfs datanode。DataNode不需要格式化,它会自动从NameNode拉取集群信息。
3. Hive的Metastore是灵魂,先把元数据服务立起来
我的部署顺序是:MySQL在HDFS之后立即部署,然后部署Metastore,再部署HiveServer2。Hive的全部配置都围绕一个核心文件hive-site.xml展开。
3.1 MySQL需要提前做的三件事
实验环境里,MySQL可以用一个Deployment快速跑起来,但有三件事不能偷懒:
第一,必须给Hive单独建库建账号,而且权限要精确。Hive的元数据表会比较多,如果混用账号,后面排查问题会很痛苦。
第二,字符集要设为utf8mb4,不然建表时如果表名或注释里有中文,容易出现字符集不支持的报错。
第三,初始化元数据表结构。Hive的Metastore连接MySQL时,期望库里已经有VERSION等表。初始化用Hive安装包自带的脚本:
# 先把脚本拷到MySQL容器里执行 kubectl cp hive-schema.sql mysql-0:/tmp/ kubectl exec -it mysql-0 -- mysql -uroot -p -e "source /tmp/hive-schema.sql;"这个脚本对应的是hive-site.xml里javax.jdo.option.ConnectionURL指向的库。注意版本要匹配:Hive 3.x对应3.x的schema脚本,不要拿2.x的脚本去初始化。
3.2 从Hive的角度看必须配的几张表
Metastore启动时会读取hive-site.xml,里面真正决定生死的是这几项:
<property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://mysql-svc:3306/hive?createDatabaseIfNotExist=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true</value> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.cj.jdbc.Driver</value> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>hive</value> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>hivepass</value> </property> <property> <name>hive.metastore.uris</name> <value>thrift://hive-metastore-svc:9083</value> </property>连接串里的几个参数要特别注意:
useSSL=false:实验环境不需要SSL,不写这个参数,Metastore默认会尝试SSL连接,可能报证书错误。serverTimezone=UTC:MySQL驱动对时区很敏感,不设置时区会报The server time zone value的错误。实验环境直接用UTC,避免不同容器时区不一致的问题。allowPublicKeyRetrieval=true:MySQL 8.0及以上版本在非SSL连接下,如果用户密码采用caching_sha2_password认证,不加这个参数会报Public Key Retrieval is not allowed。- 驱动类名用
com.mysql.cj.jdbc.Driver,这是MySQL 8.0驱动的写法。如果是老驱动包,就要用com.mysql.jdbc.Driver。驱动jar包必须放到Hive容器的lib目录下,常见的报错是ClassNotFoundException,一看就是漏了jar包。
3.3 Metastore和HiveServer2的部署方式
我用了同一个Hive镜像,通过两个Deployment分别跑Metastore和HiveServer2,两个都挂同一个配置目录。
Metastore的启动命令:
hive --service metastoreHiveServer2的启动命令:
hive --service hiveserver2这里有一个小坑:如果Hive镜像的entrypoint默认是hive命令行,直接覆盖command时容易丢环境变量。我的做法是在镜像里保留HIVE_HOME等变量,然后手动指定:
container: - name: metastore image: hive:3.1.3 command: ["/bin/bash", "-c"] args: - | export HADOOP_HOME=/opt/hadoop export HIVE_HOME=/opt/hive $HIVE_HOME/bin/hive --service metastoreHiveServer2在启动时不需要连接MySQL,但它会通过hive.metastore.uris连到Metastore。所以Metastore必须先启动,并且HiveServer2容器里必须能解析hive-metastore-svc这个服务名。
3.4 HiveServer2启动到底慢不慢
HiveServer2启动比较慢,特别是第一次,因为要初始化一堆线程池和会话管理器。我一开始以为它卡死了,反复重启,后来看日志发现只是启动慢而已。
耐心等待是有回报的,启动完成后用beeline验证:
kubectl exec -it hive-server2-xxxxx -- /opt/hive/bin/beeline -u "jdbc:hive2://localhost:10000" -n root能进入0: jdbc:hive2://localhost:10000>提示符,说明HiveServer2本身是通的。当时我没急着测查询,先确认这个连接,再往上接Hue,这样排查范围就小很多。
4. Hue接入:把HDFS和Hive串成一个可视化入口
Hue是整套环境的"脸面"。它部署本身不难,难在让它在同一个页面里既能看到HDFS文件,又能打开Hive查询编辑器。
4.1 Hue要配的三套"接线"
理解Hue的连接关系比记住配置项更重要。Hue必须打通三层:
第一层是Hue自己的数据库。配置文件里的[[database]]段,我指向同一个MySQL实例的hue库。
第二层是HDFS连接。Hue通过WebHDFS协议访问HDFS,它不需要直接走RPC端口9820,而是走NameNode的HTTP端口9870。
第三层是Hive连接。Hue通过HiveServer2的Thrift端口10000连接,连接串里的主机名是HiveServer2的Service名。
Hue最坑的一点是:这三层只要有任意一层没通,页面就会以各种奇怪的方式报错。HDFS不通时,File Browser会一直转圈;Hive不通时,SQL编辑器里看不到数据库和表。所以先把前两层都验证好,再配Hue,排障会快很多。
4.2 hue.ini里的几个关键段落
Hue的配置文件是hue.ini,不同版本差异比较大,我以Hue 4.x为例,重点改以下段落。
[desktop]段下的数据库配置:
[[database]] engine=mysql host=mysql-svc port=3306 user=hue password=huepass name=hue[[hdfs]]段配置NameNode地址:
[[hdfs_clusters]] [[[default]]] fs_defaultfs=hdfs://hdfs-nn-svc:9820 webhdfs_url=http://hdfs-nn-svc:9870/webhdfs/v1注意fs_defaultfs写的是RPC地址,webhdfs_url写的是HTTP地址。这两个都要改成K8s服务名。Hue在初始化和测试连接时,会先去请求WebHDFS服务,如果这个URL错了,它会直接报"无法连接到HDFS"。
[[hive]]段配置HiveServer2地址:
[[hive]] hive_server_host=hive-server2-svc hive_server_port=10000 hive_conf_dir=/etc/hive/conf这里的主机名hive-server2-svc必须能被Hue容器解析到,端口就是HiveServer2的10000。如果Hive配置了SASL或LDAP认证,还需要额外写hive_server_authentication,但实验环境保持默认的None认证即可,也就是不用改。
4.3 配置文件注入:避免手改镜像的笨办法
Hue镜像里预装了一份默认的hue.ini,直接改镜像再重新构建也能用,但每次调参数都重新build实在太浪费。我用ConfigMap挂载到Hue容器配置目录,和Hadoop的做法一致。
新建ConfigMap之后,把它挂载到/usr/share/hue/desktop/conf/下面,覆盖原来的hue.ini。Hue启动时会自动读取这个目录下的配置。
启动Hue用的是官方镜像默认入口,不需要额外写command,只要环境变量里有HUE_CONF_DIR指向挂载目录就行。如果镜像版本比较老,入口脚本不读环境变量,那就直接覆盖command:
command: ["/usr/share/hue/build/env/bin/hue", "runserver", "0.0.0.0:8888"]Hue容器起来后,第一次访问页面要创建超级管理员账号,这个账号后续会用来登录和保存查询历史。
4.4 登录后如何验证端到端确实打通
登录Hue之后,不要急着写SQL,先把HDFS的链路验证一遍:进入File Browser,尝试创建一个目录。如果能正常创建,说明WebHDFS连接没问题;如果报错或者一直转圈,优先检查webhdfs_url的地址是不是写的hdfs-nn-svc。
HDFS通了之后,再验证Hive链路。打开右上角的SQL编辑器,正常的话左侧能看到默认数据库default。执行一个简单的建表和加载数据操作:
CREATE TABLE demo_tbl (id INT, name STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';然后先准备好一个本地CSV文件,在File Browser里把它上传到/tmp目录,再执行:
LOAD DATA INPATH '/tmp/demo.csv' INTO TABLE demo_tbl; SELECT * FROM demo_tbl;能跑出查询结果,才算整套环境真正通了。这时Hue等于同时控制了HDFS存储和Hive查询,实验环境的目标也就达到了。
4.5 Hue页面上的常见"半通不通"现象
Hue部署完之后,遇到最多的是这几种情况,我把它们和对应的排查方向列出来:
| 现象 | 大概率原因 |
|---|---|
| HDFS File Browser一直转圈 | webhdfs_url主机名或端口不对 |
File Browser报Connection refused | NameNode Service没有暴露9870端口 |
| SQL编辑器左侧看不到表 | HiveServer2地址/端口不通,或Metastore没启动 |
| Hue登录后500错误 | Hue自己的数据库连接配置错误 |
| 创建账号成功后退出或闪回 | hue.ini数据库字段有问题 |
这些现象有一个共同点:服务名和端口写错。排查时先用kubectl exec进去容器里手动curl或ping一下目标Service,先把网络层排除掉,再去查配置。
5. 从"能跑"到"好用":网络、存储和资源的实际踩坑记录
能正常登录Hue之后,我一度以为部署结束了,结果后面又折腾了两三天,大部分时间都花在稳定性和资源限制上。这部分记录下来,比前面的部署还要有价值。
5.1 容器间服务发现:最容易被忽略的DNS问题
整套环境里,所有的跨容器通信都依赖K8s的Service名称解析。我在部署时并不在每个容器里配好所有主机名,而是直接使用K8s服务名,这样Pod重建后依然能解析到新Pod。
但这里有个容易踩的坑:跨命名空间的服务名解析。如果Hive和HDFS不在同一个Namespace,hdfs-nn-svc就解析不了,必须写成hdfs-nn-svc.<namespace>.svc.cluster.local这种全限定名。实验环境建议把HDFS、Hive、Hue全部放在同一个Namespace里,省掉这一层麻烦。
另一个坑是Services的类型。NameNode的RPC服务我用了Headless Service,而HiveServer2和Hue之间我用普通ClusterIP Service。如果HIVE或Hue依赖外部负载均衡,就需要NodePort或Ingress,但容器内部通信用ClusterIP就够。
建议所有内部通信都走Service名,不要走Pod的IP。Pod IP在重启后一定会变,而Service名基本稳定。
5.2 JVM内存限制与OOM Killed
这套组件都是Java进程,JVM默认堆内存设置往往比K8s容器limits大。我在一个2核4G的节点上跑的时候,DataNode直接被OOMKilled,反复重启。
原因是Hadoop脚本会根据物理机内存自动设置HADOOP_HEAPSIZE,默认可能是物理内存的1/4。如果K8s Pod的limits是2G,但宿主机是32G,那Hadoop以为能占8G,实际容器只有2G,直接触发OOM。
解决办法是在容器的环境变量里强制指定堆大小:
env: - name: HADOOP_HEAPSIZE value: "1024" - name: HADOOP_NAMENODE_INIT_HEAPSIZE value: "1024"对于HiveServer2,它默认的堆也可能很大,加上Metastore也需要内存。我最后给整套环境设的资源配额如下:
| 组件 | requests | limits |
|---|---|---|
| NameNode | 512Mi | 1Gi |
| DataNode | 512Mi | 1Gi |
| Hive Metastore | 512Mi | 1Gi |
| HiveServer2 | 1Gi | 2Gi |
| Hue | 512Mi | 1Gi |
| MySQL | 512Mi | 1Gi |
K8s里面给Java进程设置limits时,最好留20%的内存给JVM以外的开销(线程栈、元空间、堆外内存)。实验环境内存紧张,把limits刚好等于requests有时会让Pod频繁被杀死。
5.3 镜像版本的"隐性不兼容"
部署时选镜像版本,不是随便拉最新的就行。我碰到的问题是Hive镜像自带的Hadoop客户端库和HDFS服务端Hadoop版本不一致,导致Metastore连HDFS时一直报RPC协议版本不匹配。
我把版本锁定成一套经过验证的组合:
| 组件 | 版本 |
|---|---|
| Hadoop | 3.3.4 |
| Hive | 3.1.3 |
| Hue | 4.10.0 |
| MySQL | 8.0 |
这个组合比较稳。Hive 3.1.3本身就基于Hadoop 3.x,Hue 4.x对Hive 3.x的支持也比较好。不要用Hive 2.x配Hadoop 3.x,容易出现协议兼容问题。
版本锁定之后还有个好处:镜像tag一旦固定,不会出现"昨天能用、今天重新拉变成新版坏了"的情况。实验环境里最怕这种不确定性。
5.4 实验环境里的安全模式、格式化操作
NameNode安全模式是个很经典的"半熟"状态。在一开始部署时我遇到安全模式一直退不出去的情况,原因不是数据丢失,而是DataNode上报的块数没有达到阈值。
在单节点实验环境里,几个处理顺序很重要:
- 先确认DataNode进程正常,日志里没有FATAL错误。
- 在NameNode容器里执行
hdfs dfsadmin -report,看DataNode是否被识别。 - 如果DataNode正常且安全模式还是On,直接执行
hdfs dfsadmin -safemode leave。
安全模式不是故障,它是HDFS启动流程中的正常阶段,只是单节点环境中进入这个阶段后,如果没有手动干预,会一直等到超时。超时时间默认30秒,有时候等在页面里看到On只是因为还没过够时间。先等一分钟,再去操作。
5.5 排障一定要先看日志再动手
这可能是整套环境里最平凡的教训,但也最值钱。每次排查我都坚持一个顺序:
- 先看K8s事件:
kubectl describe pod <pod-name>,看有没有镜像拉取失败、健康检查失败、端口占用。 - 再看容器标准输出:
kubectl logs <pod-name>,定位Java进程的异常栈。 - 最后看组件内的日志文件:比如Hive的
/tmp/root/hive.log、HDFS的/opt/hadoop/logs/。
之前Hue一直报SQL编辑器连接失败,我反复改hue.ini没效果,后来打开hive.log才发现是Metastore的连接串里时区参数写错了。很多问题是跨组件传递的,光看调用方日志不一定能找到根因。
6. 实验环境的日常维护和数据持久化
实验环境不是一次部署完就结束,后面还要反复修改配置、清理数据、甚至整体推倒重建。怎么让这套环境保持"实验"的性质,又能存住有价值的数据,是最后要解决的问题。
6.1 数据放在哪,Pod删了会不会丢
整套环境里最核心的数据有两类:
第一类是HDFS里的真实文件数据。只要PVC还在,Pod不管怎么删怎么重建,数据都不会丢。关键是不要把PVC和StatefulSet一起删掉。在K8s里,kubectl delete statefulset不会删除PVC,但如果有人习惯用kubectl delete all清理资源,PVC也会被关联清理,数据就没了。
第二类是Hive的元数据,它存在MySQL里。实验环境里对MySQL数据盘同样用PVC持久化。为了双保险,我还会定期备份元数据库:
kubectl exec -it mysql-0 -- mysqldump -uroot -p hive > hive_meta_backup.sql这个备份文件很小,却是整套环境的索引,一旦元数据损坏,就算HDFS里还有原始文件,也很难快速恢复表和分区的语义。
6.2 用一套脚本管理"一键重建"
实验环境的目的是快速尝试,所以我尽量避免手工逐个apply YAML。把所有资源文件按顺序放在一个目录里,然后用一个Shell脚本串联起来:
#!/bin/bash set -e kubectl apply -f namespace.yaml kubectl apply -f mysql-configmap.yaml kubectl apply -f mysql-deployment.yaml kubectl apply -f hdfs-configmap.yaml kubectl apply -f hdfs-services.yaml kubectl apply -f hdfs-statefulset.yaml kubectl apply -f hive-configmap.yaml kubectl apply -f hive-deployment.yaml kubectl apply -f hue-configmap.yaml kubectl apply -f hue-deployment.yaml注意顺序:配置资源放最前面,依赖服务放后面。第一次执行时MySQL要先起来,等hive-schema.sql初始化完成,再启动Metastore,不然Metastore会一直重试连接数据库。
6.3 外部访问怎么暴露
实验环境部署完之后,总要能在浏览器里打开Hue。Hue的Service我用NodePort暴露:
apiVersion: v1 kind: Service metadata: name: hue-svc spec: type: NodePort selector: app: hue ports: - port: 8888 targetPort: 8888 nodePort: 30888访问方式就是http://<节点IP>:30888。如果机器上有Ingress控制器,也可以把Hue暴露成域名,但实验环境没必要引入额外依赖。HDFS的NameNode UI我也用NodePort暴露到30890,方便看存储状态。
这里有个安全提醒:暴露到外部前,一定要确认Hue的访问是受账号登录保护的。Hue默认会要求创建账号并登录,但如果之前把Hue的认证配置改成了无认证模式,直接暴露到外网是不安全的。实验环境最好只在可信内网里使用。
6.4 这套环境还能往上叠什么
整套Hue+HDFS+Hive跑通之后,实验环境的地基是稳的,后续可以往上扩展很多组件,我聊一下我自己计划的方向:
- 加Spark:Hue可以通过Livy来提交Spark作业,只要在HLS/Livy服务部署完成后,改Hue配置里的
[livy]段。 - 加Tez引擎:Hive默认执行引擎是MapReduce,可以切成Tez,查询速度快不少,而且不需要额外部署独立服务。
- 加调度工具:Hue自带Oozie配置,可以配合Oozie做定时任务。
这些扩展的入口其实都在Hue这个统一工作台上,框架性的连接关系都和我们今天搭的这套一致。我自己的体会是,先把HDFS和Hive打通,再往上加东西,比一步到位要稳得多。
最后再分享一个日常维护的小经验:每次改完配置,不要直接重启Pod,先用kubectl exec进容器里执行一下组件的-version或者--help,确认配置加载没有问题。真正出问题的时候,这一分钟能救回大半天时间。整套环境里最值钱的不是YAML文件,而是你对每个组件日志路径和启动行为的熟悉程度。