Ubuntu 安装 Hadoop 和 Kerberos

花了两天多时间,终于让 Hadoop 可以安安稳稳的跑几个 MapReduce 的程序了,安装的过程很折腾,但我觉得应该用不着花这么长时间的。

很久以前就深知 RTFM 的重要性,但很多时候第一反应仍然是去 Google,而不是去官网找 Documentation,诚然,有些东西的文档的确很烂,但其实 Google 出来的 Blog 转载来转载去也就那么几份,很多里面都存在问题,毕竟需求是不一样的,态度也是不一样的。

这篇文章内容大部分参照自官方文档,并且包含若干我的配置错误,以及解决方法。

这个安装过程适用于配置 Kerberos V5 1.8.4 to Cloudera CDH3 on Ubuntu 11.04

安装 JDK 6

1. 添加源到 ubuntu source list

1
$ sudo add-apt-repository "deb http://archive.canonical.com/ natty partner"

natty 是ubuntu 11.04 的 codename,可以使用 “lsb_release -c” 查看 ubuntu 对应版本的 codename,如不是 natty,请更改。

最新的 ubuntu 11.10,好像已经不提供第三方 JDK 了,只有 openjdk 可用了。

2. 安装

1
2
$ sudo apt-get update
$ sudo apt-get install sun-java6-jdk

如果还装过其他 JDK 的话,需要将该 jdk 设成默认:

1
$ sudo update-java-alternatives -s java-6-sun

安装 Cloudera CDH 3

我是单机使用的,所以选择安装 Pseudo-Distributed Mode

参考文档:Cloudera CDH3 Installation 中 Installing CDH3 on Ubuntu Systems 一节。

因为 CDH3 ubuntu 最新的支持包是 10.10 Maveric,所以我选用的是这个包,目前没有发现有其他问题。

在添加 ubuntu 源后,进行如下操作

1
$ apt-cache search hadoop

有输出则代表源添加成功,不要按照文档里面安装 hadoop-0.20

1
$ sudo apt-get install hadoop-0.20

而是应该安装

1
$ sudo apt-get install hadoop-0.20-conf-pseudo

见 Cloudera 文档 CDH3 Deployment in Pseudo-Distributed Mode

这个 Hadoop 伪分布式配置是 5 个 daemons 跑在一个 node 上,分别是 namenode, jobtracker, secondarynamenode, datanode, trasktracker.

启动所有 node:

1
$ for x in /etc/init.d/hadoop-*; do sudo $x start; done

运行如下命令来检验 Hadoop 是否启动成功

1
$ hadoop fs -ls /

上述命令会打印出 hdfs 根目录信息,如果成功,那就可以开始 MapReduce 吧,可以跑下面几个例子:

Pi

1
$ hadoop jar /usr/lib/hadoop/hadoop-*-examples.jar pi 2 100000

Grep

1
2
3
4
$ hadoop fs -mkdir input
$ hadoop fs -put /etc/hadoop-0.20/conf/*.xml input
$ hadoop-0.20 fs -ls input
$ hadoop-0.20 jar /usr/lib/hadoop-0.20/hadoop-*-examples.jar grep input output 'dfs[a-z.]+'

如果例子都没有问题,那么停止所有 node,准备安装 Kerberos

1
$ for x in /etc/init.d/hadoop-* ; do sudo $x stop ; done

安装 Kerberos

Kerberos 是什么我就不在这里累赘了,请猛点 Kerberos

1. 安装

下载 Kerberos V5 1.8.4 下载地址

安装很传统,解压,cd 到 src,接着 ./configure; make; sudo make install 就 OK 了。

2. 添加配置文件

/etc 下面创建 krb5.conf 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[kdc]
profile = /usr/local/var/krb5kdc/kdc.conf

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

default_tgs_enctypes = des3-hmac-sha1
default_tkt_enctypes = des3-hmac-sha1
permitted_enctypes = des3-hmac-sha1

[libdefaults]
default_realm = LOCAL.DOMAIN
forwardable = yes
ccache_type = 4
proxiable = true
renew_lifetime = 1d
clockskew = 1000000000
#dns_lookup_kdc = true
#dns_lookup_realm = true

[realms]
LOCAL.DOMAIN = {
kdc = localhost
admin_server = localhost
default_domain = localhost
}
[domain_realm]
.local.domain = LOCAL.DOMAIN
local.domain = LOCAL.DOMAIN

[login]
krb4_convert = true
krb4_get_tickets = false

[realms] 设置 KDC 相关服务的地址,因为是单机,所以用的是 localhost

(如果是配集群的话,只有 kdc 需要配 [kdc] 里面的内容,其他 node 包括 client 都需要有这个文件,并且添加除了 [kdc] 以外的内容在这个里面。后面的其他配置文件,非 kdc 不需要。另外如果在 client 里面设置 kdc 地址采用的是 IP 地址,那么需要在 hosts 设置 “hostname IP”,否则很有可能出现这样的错误 log: “Not attempting to re-login since the last re-login was attempted less than 600 seconds before.”,这是因为解析到了错误的 hostname,导致 principal instance 错误,kdc 认证时不能识别而出错。)

接着创建 kdc.conf 文件到 /usr/local/var/krb5kdc 目录下, 就是 krb5.conf 里面 profile 的路径,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[kdcdefaults]
v4_mode = nopreauth
kdc_ports = 750,88
kdc_tcp_ports = 88

[realms]
LOCAL.DOMAIN = {
acl_file = /usr/local/var/krb5kdc/kadm5.acl
dict_file = /usr/share/dict/words
admin_keytab = /usr/local/var/krb5kdc/kadm5.keytab
kdc_ports = 750,88
max_life = 7d 0h 0m 0s
max_renewable_life = 1d 0h 0m 0s
master_key_type = des3-hmac-sha1
supported_enctypes = des3-hmac-sha1:normal des-cbc-crc:normal des:normal des:v4 des:norealm des:onlyrealm
default_principal_flags = +preauth
}

然后创建 kadm5.acl 文件到同一目录,见上面 acl_file 路径,内容如下

1
*/[email protected]  *

3. 创建 Kerberos 数据库

1
$ /usr/local/sbin/kdb5_util create -r LOCAL.DOMAIN -s

生成 kadmin/admin kadmin/changepw 两个用户的 keytab 文件到 krb5kdc 目录

1
2
3
$ sudo kadmin.local
kadmin.local: ktadd -k /usr/local/var/krb5kdc/kadm5.keytab kadmin/admin kadmin/changepw
kadmin.local: quit

运行

1
2
$ sudo /usr/local/sbin/krb5kdc
$ sudo /usr/local/sbin/kadmind

Kerberos 就正常工作了

可以运行 kadmin 或者 kadmin.local 进行测试 (运行 kadmin.local 需要 sudo)

1
2
$ kadmin
kadmin: listprincs

会显示所有 principals

4. Hadoop Kerberos 安全认证配置

参考文档 Configuring Hadoop Security in CDH3

可以跳过前面步骤,直接从 Step 4 开始。不过提醒下,一定要装这个包(必须和 hadoop 版本一致),要不后面会报一些奇奇怪怪的错误,这个地方浪费了我不少时间。

1
$ sudo apt-get install hadoop-0.20-sbin

Cloudera CDH3 默认会建立两个用户 hdfs 和 mapred,而网上的摘抄转载大多是建一个 hadoop 用户,然后创建 hadoop principal 以及创建 keytab,那 datanode 和 nodenode 启动时都会因为 Kerberos 验证不过而失败,因为 Hadoop 是使用的 hdfs 用户来启动的,而 keytab 里面的用户是 hadoop。

有些还说要往 hadoop_env.sh 里面添加 HADOOP_DATANODE_USER=hadoop 的,这样会因为连写 log 的权限都没有而导致 datanode 启动失败的,因为其用户是 hdfs。

上面两个错误都让我花了不少时间去定位。

强烈建议按照 Step 4 的方式来生成 hdfs, mapred 的 principal 以及 keytab 权限设置,生成 keytab 方法可以采用这个

1
2
$sudo kadmin.local
kadmin.local: ktadd -k hdfs.keytab hdfs/ubuntu host/ubuntu

参考 Step 6, 7 配置 Hadoop 的 core-site.xml 和 hdfs-site.xml

配置完成之后启动所有 node

1
$ for x in /etc/init.d/hadoop-*; do sudo $x start; done

查看 namenode 和 datanode 的 log,如果有下述 log ,就代表 Hadoop 已经在使用 Kerberos 验证了。

1
hdfs/[email protected] using keytab file /etc/hadoop/conf/hdfs.keytab

可以验证下:

1
$ hadoop fs -ls

如果你是 jdk 6 update 26,那么恭喜你,你很可能碰见下面错误:

1
2
 any Kerberos tgt)]
provided (Mechanism level: Failed to find any Kerberos tgt)]

这个是已知 bug

Workaround 是在 kinit 成功之后,执行

1
$ kinit -R

运行之后就可以访问 hdfs 了。我当时死活成功不了,然后找了另一台机器同样设置做了一遍,workaround 是可以的。折腾了好久,最后把 Kerberos 数据库删掉,重新创建了一个,然后重新添加所有 principals 后,就好了。

1
$ /usr/local/sbin/kdb5_util destroy -r LOCAL.DOMAIN -f

HDFS 可以正常访问了,接下来按照 Step 12 的方式来配置 MapReduce

注意在 mapred-site.xml 添加配置时,Guide 里面只是关于 Kerberos 的,还需要添加 mapred.local.dir 路径,必须和同目录下 taskcontroller.cfg 里面的设置一致。

1
mapred.local.dir=/tmp/mapred-tmp

taskcontroller.cfg 必须设置,如果不设置,tasktrackernode 压根就不会启动的

hadoop.log.dir 就填 hadoop 的 logs 目录 /usr/lib/hadoop/logs

mapred.local.dir 填一个本地目录就可以,但必须是把上面这段配置添加到 mapred-site.xml 里面后重启 node,否则 tasktracker.xml 会报文件不存在或不能读写而启动失败的。

做完 Step 12,重启所有 node,启动成功后,运行前面的 PI 例子,成功就代表 MapReduce 也木有问题了。

Mission Complete!

Have Fun!


在上面的这些都成功之后,又遇到了几个错误

1. Secondarynamenode 做 checkpoint 时认证出错

1
2
3
che/hdfs/dfs/namesecondary from failed checkpoint.

ticket for: host/localhost

我 hdfs-site.xml 里面明明配的都是 host/ubuntu。

hdfs-site.xml 里面默认的是 host/_HOST 这类格式的 principal 设置,我之前没有改,启动 node 时,datanode 是 host/ubuntu,namenode 成了 host/localhost, 所有我怀疑是这个 _HOST 宏展开的问题,试着加了好几个配置,都不行,最后没办法了,只得添加了 host/localhost 到 hdfs.keytab,算是 workaround 吧,我觉得正式环境应该不会有这样的问题,所以不想折腾了。

2. Secondarynamenode 做 checkpoint 时 fetch image 出错

1
2
3
/cache/hdfs/dfs/namesecondary from failed checkpoint.

header is not provided by the namenode when trying to fetch https://localhost:50470/getimage?getimage=1

在加了 Kerberos 后,Hadoop 做 checkpoint 的时候走的是 Kerberized SSL,所以需要在 krb5.conf 里面的 [libdefaults] 里面添加 allow_weak_crypto 设置

1
2
3
4
[libdefaults]
...
allow_weak_crypto=true
...

这个问题折腾了俩小时,添加了很多配置在 hdfs-site.xml 里面,都不行。

最后还是找到了答案,但吃惊的是依然来自一直在用的这个文档 Configuring Hadoop Security in CDH3 Step 11 给出的链接

Appendix D – Enabling Debugging Output for the Sun Kerberos Classes

3. 执行 hadoop fsck 出错

1
2
3
$ hadoop fsck /
Exception in thread "main" java.io.IOException: session keys with des3-cbc-hmac-sha1-kd encryption type are not supported for TLS Kerberos cipher suites
...

解决了 2,这个问题也木有了。

真是不细看文档会死人的。。。

在此,感谢 Cloudera,是个靠谱的公司。