Flink on K8s与Hive on CDH Kerberos 认证

问题背景:

本篇文章主要探讨如何在 Kubernetes(K8s)环境下使用 Apache Flink 进行 Hive on CDH 的 Kerberos 认证。

flink在K8s环境下与Hive ON CDH 的kerberos认证

flink任务提交模式:kubernetes-application

k8s构建镜像模式:dockfile pod-template


准备工作:

1.示例代码:



import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.serialization.DeserializationSchema;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.connector.source.Source;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.SqlDialect;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.table.catalog.Catalog;
import org.apache.flink.table.catalog.hive.HiveCatalog;
import org.apache.flink.util.Collector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;



public class Kafka2Hive {
    public static final Logger log = LoggerFactory.getLogger(Kafka2Hive.class);

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        checkPointConfig(env, "kafka2hive");
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        Configuration configuration = tableEnv.getConfig().getConfiguration();
        configuration.setString("hive.exec.dynamic.partition", "true");
        configuration.setString("hive.exec.dynamic.partition.mode", "nonstrict");
        configuration.setString("hive.exec.dynamic.partitions", "10000");


        SingleOutputStreamOperator<String> source = env.fromSource((Source) getKafkaSource(new String[]{Constants.KAFKA_TOPIC}, "gid_it.k8s.hive"), WatermarkStrategy.noWatermarks(), "kafka-source-it").uid("it.").name("Kafka Source: it.");
        source.print();
        SingleOutputStreamOperator<SupplierPartsInspection> javaBeanDs = source.flatMap(new FlatMapFunction<String, SupplierPartsInspection>() {
            private SupplierPartsInspection supplierPartsInspection;

            public void flatMap(String value, Collector<SupplierPartsInspection> out) throws Exception {

                try {
                    JSONObject jsonObject = JSONObject.parseObject(value);
                    JSONArray content = jsonObject.getJSONArray("content");
                    this.supplierPartsInspection = new SupplierPartsInspection();
                    
                    this.supplierPartsInspection.setReportTime(jsonObject.getTimestamp("reportTime"));
                    this.supplierPartsInspection.setReportInspectStatus(jsonObject.getString("reportInspectStatus"));

                    //数据异常监测信息
                    this.supplierPartsInspection.setCheckErrorType(jsonObject.getString("checkErrorType"));
                    this.supplierPartsInspection.setCheckErrorMsg(jsonObject.getString("checkErrorMsg"));
                    this.supplierPartsInspection.setMsgUuid(jsonObject.getString("msgUuid"));


                    //冗余字段
                    this.supplierPartsInspection.setCrtId(1);
                    this.supplierPartsInspection.setCrtDt(new Timestamp(System.currentTimeMillis()));
                        out.collect(this.supplierPartsInspection);
                    }

                } catch (Exception e) {
                    log.error("flink-datasync>>>>>>>>>parse json value: {}", value);
                    log.error("flink-datasync>>>>>>>>>parse json error: {}" + e.getMessage());
                }

            }
        }).uid("javabean-SupplierPartsInspection").name("Map JavaBean: SupplierPartsInspection");


        //FLINK创建的视图会在内存中default_catalog.default_database
        tableEnv.createTemporaryView("kafka_supplier_parts_inspection", (DataStream) javaBeanDs);


        String catalogName = "flink-datasync_hive_catalog";

        HiveCatalog catalog = new HiveCatalog(catalogName, Constants.TARGET_DB_NAME, Constants.HIVE_CONF);
        tableEnv.registerCatalog(catalogName, (Catalog) catalog);

        tableEnv.useCatalog(catalogName);

        //设置为hive方言
        tableEnv.getConfig().setSqlDialect(SqlDialect.HIVE);
        tableEnv.executeSql("CREATE TABLE IF NOT EXISTS flink_k8s_kafka_hive (    line_inspect_status string  ) PARTITIONED BY (dt string) STORED AS parquet TBLPROPERTIES ( 'sink.partition-commit.delay'='0 s',  'sink.partition-commit.trigger'='process-time',  'partition.time-extractor.timestamp-pattern'='$dt 00:00:00' , 'sink.partition-commit.policy.kind'='metastore,success-file' )");
        tableEnv.executeSql("INSERT INTO TABLE  flink_k8s_kafka_hive    select hhPartNumber,           from_unixtime(unix_timestamp(), 'yyyy-MM-dd') from default_catalog.default_database.kafka_supplier_parts_inspection");


    }

    public static KafkaSource<String> getKafkaSource(String[] topic, String groupId) {
        return KafkaSource.builder()
                .setBootstrapServers(Constants.KAFKA_SERVER)
                .setTopics(topic)
                .setGroupId(groupId)
                //从kafka最早的偏移量开始消费
                .setStartingOffsets(OffsetsInitializer.earliest())
                .setValueOnlyDeserializer((DeserializationSchema) new SimpleStringSchema())
                .build();
    }



}

这个应用程序它使用 Apache Flink 从 Kafka 主题中读取数据,然后解析并转换它,最后将其写入 Hive 表。

2.resources目录:

 flink-conf文件:

配置keytab文件和krb5文件本地路径

配置票据principal用户名

配置kubernetes客户端用户(这个要和票据所有者,票据principal,以及票据文件所有者保持一致,否则会出现GSS认证失败

3.构建dockerfile 

主要是将keytab,hive-site,flink-conf,krb5,lib,conf文件复制到镜像中(切记将文件keytab相关文件权限修改为777)

4.使用 HostAliases 向 Pod /etc/hosts 文件添加条目

这一步主要是让k8s集群解析到kdc机器地址(镜像中的hosts文件是不能直接修改的

如果访问hive,也需要将全部Namenode地址配置上

5.krb5 配置

# cat /etc/krb5.conf

# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/

[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
default_realm = EXAMPLE.COM
default_ccache_name = KEYRING:persistent:%{uid}

[realms]
 EXAMPLE.COM = {
 kdc = kerberos.example.com
 admin_server = kerberos.example.com
 }

[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM

[kdc]

profile = /var/kerberos/krb5kdc/kdc.conf

#以上是默认的文件信息

配置解释:

[logging]  这个组里面配置的是一些与日志存放路径相关的信息。

  default: 用于配置krb5libs日志存放路径

  kdc: 用于配置krb5kdc日志存放路径

  admin_server: 用于配置kadmin服务日志存放路径

[libdefaults] 在使用Kerberos时使用的默认值。

dns_lookup_realm : 与dns参数有关,猜测用于域名解析,默认是false,当设置为true时,dns参数才起作用,有关dns参数获取其他参数可以查看官方文档。

ticket_lifetime: 获取到的票据存活时间,也就是该票据可以用多久,默认是1天

renew_lifetime:票据可更新的时间,也就是说该票据可以延迟到期的天数。默认是7天

forwardable:票据是否可以被kdc转发, 默认是true。

rdns :在进行dns解析的时候,正反向都可以,默认是true,如果dns_canonicalize_hostname参数被设置为false,那么rdns这个参数将无效。

default_realm:默认的领域,当客户端在连接或者获取主体的时候,当没有输入领域的时候,该值为默认值(列如:使用kinit admin/admin 获取主体的凭证时,没有输入领域,而传到kdc服务器的时候,会变成 admin/admin@EXAMPLE.COM ),这个值需要在[realms]中定义,如果有多个领域,都需要在realms中定义。默认以大写书写。

default_ccache_name:默认缓存的凭据名称,不推荐使用,当在客户端配置该参数的时候,会提示缓存错误信息。

[realms]   该组主要是配置领域相关的信息,默认只有一个,可以配置多个,如果配置多个(比如:HADOOP.COM, HBASE.COM),但是default_realm值定义为HADOOP.COM,那么客户端在获取与HBASE.COM相关的凭据时,需要指定具体的域名(比如客户端初始化凭据: kinit admin/admin@HBASE.COM).

EXAMPLE.COM: 简单说就是域名,对应用kerberos来说就是领域,相当于编程里面的namespace。

kdc : kdc服务器地址。格式  机器:端口, 默认端口是88

admin_server: admin服务地址 格式 机器:端口, 默认端口749

default_domain: 指定默认的域名

[domain_realm]   指定DNS域名和Kerberos域名之间映射关系。指定服务器的FQDN,对应的domain_realm值决定了主机所属的域。

[kdc]:

  kdc的配置信息。即指定kdc.conf的位置。
  profile   :kdc的配置文件路径,默认值下若无文件则需要创建。

udp_preference_limit = 1 禁止使用udp可以防止一个Hadoop中的错误
udp_preference_limit = 1 kdc原生支持tcp/udp协议,客户端访问kdc服务时,默认先使用udp协议发起请求,如果数据包过大或者请求失败,然后再换用tcp协议请求。网络条件不好,如果使用udp容易出现丢包现象。
所以如果网络环境不好, udp_preference_limit设置为1,不然会出现认证失败的情况

6.查看kdc日志

可以通过kdc日志和程序运行日志,具体分析问题所在,比如发送到kdc的票据principal是否与预先设置是否一致,kdc服务器与客户端集群网络是否联通,客户端集群是否正确解析kdc\hdfs地址等

问题描述

1.ERROR: Unable to obtain password from user

 2.ERROR: Identifier doesn't match expected value (906)

3.Caused by: javax.security.auth.login.LoginException: connect timed out

4.KrbException: Message stream modified (41)


解决方案:

1.保证本地和镜像中的keytab文件所有者和k8s system user 以及票据principal一致

1.在镜像路径/opt/kerberos/kerberos-keytab下存放keytab文件

1.在镜像路径/etc/下存放keytab文件

1.在镜像路径/opt/flink/conf/存放flink-conf文件

2.使用HostAliases 向 Pod /etc/hosts添加域名解析

2.将core-site.xml和hdfs-site.xml放入resources中并将hdfs-site和hive-site中的ip改为主机名

3.k8s集群与kdc所在集群网络不通

4.删除 krb5.conf 配置文件里的 renew_lifetime = xxx 这行配置即可

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值