或许你也像我一样,工作后才发现学校里学的技术是多么落后。C++ 和 MySQL 或许已经不再是 IT 公司的宠儿。互联网时代对数据库的要求,也与传统有着很大的差别。
而这差别中比较突出的一点就是数据量的问题,在数据量非常庞大的今天关系型数据库可能无法实现存储并处理海量数据,但是分布式数据库可以实现这些要求。举个例子,关系型数据库对于程序的扩展性很差,数据字段确定后不能再扩展一些相关字段的;但对于分布式数据库来说,数据库中的字段可以随意添加,扩展性极强,不用担心快速迭代过程中的业务拓展。
正因此,一些大公司也纷纷转投 Hbase 的怀抱,比如某猫。
Hbase 表的特点:
- 大:一个表可以有数十亿行,上百万列;
- 无模式:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的增加,同一张表中不同的行可以有截然不同的列;
- 面向列:面向列(族)的存储和权限控制,列(族)独立检索;
- 稀疏:空(null)列并不占用存储空间,表可以设计的非常稀疏;
- 数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳;
- 数据类型单一:Hbase中的数据都是字符串,没有类型。
言归正传,接下来讲讲我的 Hbase 体验。
Hbase 实战
手里有一台可以随意折腾的机器,16G 内存 4 核 1T 磁盘,装有 fedora24 系统。之前用过 vagrant & virtualbox 来搭建集群,效果还不错。但可能是自己的配置不是很合适,只起了 5 个虚拟机来做 hbase 集群。后来又用 docker 折腾,开到 7 个容器来跑 hbase 集群,十分好用。
心满意足之后,就想着试试一键部署———于是有了本文。
部署结构
具体各个软件的配置文件都会出现在下面。
本地机器主要软件版本
- docker: Docker version 1.10.3, build 19b5791/1.10.3,启动虚拟机用的。
- ansible: ansible 2.1.0.0,python 写的部署工具,好处是 remote 机器不需要安装客户端什么的。事实上,用到的一些功能时还是需要安装一些包的,具体可以在后面的 Dockerfile 中看到。
- expect: 在使用 ansible 前需要本地到 remote 的免密钥 ssh 登入,expect 的作用是免去每次输入密码的麻烦。
remote需要用到的一些包
以下列出一些我在用的软件包,基本上是最新的 stable 版,可自行根据需要调整。
jdk-8u60-linux-x64.tar.gz
zookeeper-3.4.8.tar.gz
hadoop-2.7.2.tar.gz
hbase-1.1.5-bin.tar.gz
Docker image
有一堆真机器的土豪也可以简单看看,这里面还是会涉及到一些机器环境设置的 : D
1.首先,你要有docker。
sudo dnf install -y docker
sudo systemctl start docker
sudo systemctl enable docker
2.不用 sudo 使用 docker 的方法
# 添加docker用户组
sudo groupadd docker
# 把自己加到docker用户组中
sudo gpasswd -a myusername docker
# 重启docker后台服务
sudo service docker restart
# 注销,然后再登陆
exit
# 不是用ssh远程登入的话,就直接注销再登入
3.Dockerfile
为了跑 hbase 集群,我们需要的是尽可能小又足够用的容器,也就是需要一个尽可能小又足够用的镜像。
先拉一个镜像下来docker pull fedora。这个是后面的Dockerfile用到的镜像基础,一般而言都是官方最新的。如果 dockerhub 网络不好的话,就去 fedora 官网下载,迅雷的话会很快哦。
然后docker load < [imageFileName]。至于为什么是 fedora 而不是 centos、ubuntu 或者 arch 什么的,仅仅是个人偏好(如果是要用 ambari 部署 HDP 集群的话,请避免 fedora,目前暂不支持)。
然后建一个文件夹,touch Dockerfile,写上下面内容。
# make image for zookeeper, hadoop and hbase
FROM docker.io/fedora:24
MAINTAINER LukeEuler "513973890@qq.com"
RUN dnf install -y openssh-server sudo openssh-clients procps-ng hostname which
RUN dnf install -y python libselinux-python tar
RUN dnf install -y pexpect
RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config \
&& useradd litchi \
&& echo "litchi:123456" | chpasswd \
&& echo "litchi ALL=(ALL) ALL" >> /etc/sudoers \
&& ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key \
&& ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key \
&& mkdir /var/run/sshd
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
然后可以建镜像了,docker build -t litchi:hbase [path/to/Dockerfile],过程会有点慢。
path/to/Dockerfile 里除了 Dockerfile 以外,请务必不要放其他奇怪的东西。
简单说明 Dockerfile
其中:
* 第三行的FROM是说这个新镜像是从什么镜像上生成的,也就是一开始让大家 pull 的。
* 第五行请自行修改或删除,也请大家记住我的称呼和联系方式(笑)。
* 第七行是安装一些集群运作所必要的软件,大概(可以精简的话请务必分享哦)
* 第八行和第九行是安装一些为 ansible 部署提供方便的软件,都是大家认识的,所以可以放心使用。
* 后面的就是为打开 sshd 服务做准备的。
* 另外这个镜像添加了user: litchi & passward: 123456,请自行修改,以保密。
* 肯定有人问为什么是 litchi,因为夏天到了,荔枝可好吃啦~
测试一下litchi:hbase是否可用
# 后台启动容器
docker run -d -P --name=test -h test litchi:hbase
# 查看容器ip和其他参数
docker inspeck test
ssh litchi@[ip]
ansible
sudo dnf install -y ansible官网上有很多其他安装方式,请自行摸索。
sudo dnf install -y expect装这个是因为bash不提供交互应答的地方(如果读者知道更好的方法,请 email 我)
修改/etc/hosts文件,添加:
172.17.0.2 n1
172.17.0.3 n2
172.17.0.4 n3
172.17.0.5 n4
172.17.0.6 n5
172.17.0.7 n6
172.17.0.8 n7
这是 docker 启动容器后自动安顺序分配的 ip,如果不同,请自行修改。之后就不用记写 ip 了,机智如我。
然后修改一下/ect/ansible.hosts
[zookeeper]
n[3:7]
[hbase]
n[1:7] ansible_sudo_pass='123456'
[journalNode]
n[3:5]
下面就是我折腾的结果了,一键部署的主要执行文件,和预先写好的集群配置文件
主要执行文件
start.sh
放到你需要的位置,记得chmod +x start.sh
#!/bin/bash
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
for i in {1..7}
do
docker run -d -P --name=n$i -h n$i \
--add-host n1:172.17.0.2 \
--add-host n2:172.17.0.3 \
--add-host n3:172.17.0.4 \
--add-host n4:172.17.0.5 \
--add-host n5:172.17.0.6 \
--add-host n6:172.17.0.7 \
--add-host n7:172.17.0.8 \
litchi:hbase
done
rm /home/luke/.ssh/known_hosts
/home/luke/git/note/projects/shell/sshCopyId.expect.sh
ansible-playbook /home/luke/git/note/ansible/build.yml
注意:我会默认清理docker的容器以避免冲突,所以如果有需要运行的其他容器,请妥善处理(都要起 hbase 集群了,应该干不了其他事了吧,大概)。
最后两行分别写了 sshCopyId.expect.sh 和 build.yml 的绝对路径,请根据需要修改。
sshCopyId.expect.sh
请把他放到 start.sh 标明的位置,记得chmod +x sshCopyId.expect.sh。主要是实现本地到 remote 的免密钥 ssh 登入,是 ansible 部署的前提。
#!/usr/bin/expect
set password 123456
for {set i 1} {$i < 8} {incr i 1} {
spawn ssh-copy-id litchi@n$i
expect {
"*yes/no*" { send "yes\r"; exp_continue }
"*assword:*" { send "$password\r" }
}
interact
}
build.yml
请把他放到start.sh标明的位置。另外,它不是执行文件,是 ansible 的一个部署配置文件——playbook,请这么称呼它。放到这里,完全是因为写起来方便,不服来打我啊~
---
- hosts: hbase
remote_user: litchi
tasks:
- file: path=/home/litchi/.ssh/id_rsa state=absent
name: remove id_rsa
- file: path=/home/litchi/.ssh/id_rsa.pub state=absent
name: remove id_rsa.pub
- shell: ssh-keygen -t rsa -f /home/litchi/.ssh/id_rsa -N ''
name: ssh-keygen
- file: path=/home/litchi/.ssh/known_hosts state=absent
name: remove known_hosts
- expect:
command: ssh-copy-id litchi@n{{ item }}
responses:
"(yes/no)": "yes"
"password": "123456"
name: ssh-copy-id between each other among {n1..n7}
with_sequence: count=7
- hosts: hbase
remote_user: litchi
tasks:
- unarchive: src=/home/luke/packages/jdk-8u60-linux-x64.tar.gz dest=/home/litchi
name: upload & tar jdk
# actually, i do not know which way is better, maybe i will test it later
# - name: translate jdk
# copy: src=/home/luke/packages/jdk-8u60-linux-x64.tar.gz dest=/home/litchi/jdk.tar.gz
# - name: tar jdk
# shell: tar xvzf jdk.tar.gz
# args:
# chdir: /home/litchi/
- unarchive: src=/home/luke/packages/hadoop-2.7.2.tar.gz dest=/home/litchi
name: upload & tar hadoop
- unarchive: src=/home/luke/packages/hbase-1.1.5-bin.tar.gz dest=/home/litchi
name: upload & tar hbase
- hosts: zookeeper
remote_user: litchi
tasks:
- unarchive: src=/home/luke/packages/zookeeper-3.4.8.tar.gz dest=/home/litchi
name: upload & tar zookeeper
- copy: src=/home/luke/git/note/messSource/zoo.cfg dest=/home/litchi/zookeeper-3.4.8/conf/zoo.cfg
name: add zoo.cfg
- file: path=/home/litchi/zookeeper state=directory
name: mkdir zookeeper
- file: path=/home/litchi/zookeeper/myid state=touch
name: touch myid
- shell: hostname | cut -c 2 >> /home/litchi/zookeeper/myid
name: write X in myid
- hosts: hbase
remote_user: litchi
tasks:
- lineinfile: dest=/home/litchi/hadoop-2.7.2/etc/hadoop/hadoop-env.sh regexp={JAVA_HOME} line="export JAVA_HOME=/home/litchi/jdk1.8.0_60/"
name: edit JAVA_HOME
- file: path=/home/litchi/hadoop-2.7.2/etc/hadoop/core-site.xml state=absent
name: remove old core-site.xml
- copy: src=/home/luke/git/note/messSource/core-site.xml dest=/home/litchi/hadoop-2.7.2/etc/hadoop/core-site.xml
name: add new core-site.xml
- file: path=/home/litchi/hadoop-2.7.2etc/hadoop/hdfs-site.xml state=absent
name: remove old hdfs-site.xml
- copy: src=/home/luke/git/note/messSource/hdfs-site.xml dest=/home/litchi/hadoop-2.7.2/etc/hadoop/hdfs-site.xml
name: add new hdfs-site.xml
- file: path=/home/litchi/hadoop-2.7.2/etc/hadoop/slaves state=absent
name: remove old slaves
- copy: src=/home/luke/git/note/messSource/slaves dest=/home/litchi/hadoop-2.7.2/etc/hadoop/slaves
name: add new slaves
- hosts: hbase
remote_user: litchi
tasks:
- lineinfile: dest=/home/litchi/hbase-1.1.5/conf/hbase-env.sh regexp="# export JAVA_HOME=/usr/java/jdk1.6.0/" line="export JAVA_HOME=/home/litchi/jdk1.8.0_60/"
name: edit hbase-env.sh JAVA_HOME
- lineinfile: dest=/home/litchi/hbase-1.1.5/conf/hbase-env.sh regexp="# export HBASE_MANAGES_ZK=true" line="export HBASE_MANAGES_ZK=false"
name: edit hbase-env.sh HBASE_MANAGES_ZK true => false
- file: path=/home/litchi/hbase-1.1.5/conf/hbase-site.xml state=absent
name: remove old hbase-site.xml
- copy: src=/home/luke/git/note/messSource/hbase-site.xml dest=/home/litchi/hbase-1.1.5/conf/hbase-site.xml
name: add new hbase-site.xml
- file: path=/home/litchi/hbase-1.1.5/conf/regionservers state=absent
name: remove old regionservers
- copy: src=/home/luke/git/note/messSource/regionservers dest=/home/litchi/hbase-1.1.5/conf/regionservers
name: add new regionservers
- file: path=/home/litchi/hbase-1.1.5/conf/backup-masters state=touch
name: touch backup-masters
- shell: echo -e "n2" >> /home/litchi/hbase-1.1.5/conf/backup-masters
name: write backup-masters
- hosts: zookeeper
remote_user: litchi
tasks:
- shell: PATH=/home/litchi/jdk1.8.0_60/bin:$PATH /home/litchi/zookeeper-3.4.8/bin/zkServer.sh start
name: start zookeeper
- hosts: n1
remote_user: litchi
tasks:
- shell: /home/litchi/hadoop-2.7.2/bin/hdfs zkfc -formatZK
name: formatZK @n1
- hosts: journalNode
remote_user: litchi
tasks:
- shell: /home/litchi/hadoop-2.7.2/sbin/hadoop-daemon.sh start journalnode
name: start journalnode @n3,n4,n5
- hosts: n1
remote_user: litchi
tasks:
- shell: /home/litchi/hadoop-2.7.2/bin/hdfs namenode -format
name: format namenode @n1
- shell: scp -r /home/litchi/hadoop/ litchi@n2:~/
name: copy /home/litchi/hadoop/ => litchi@n2:~/
- command: /home/litchi/hadoop-2.7.2/sbin/start-dfs.sh
name: start hdfs
- command: /home/litchi/hadoop-2.7.2/sbin/stop-dfs.sh
name: stop hdfs
- command: /home/litchi/hadoop-2.7.2/sbin/start-dfs.sh
name: start hdfs
- command: /home/litchi/hadoop-2.7.2/sbin/hadoop-daemon.sh start zkfc
name: start zkfc
- command: /home/litchi/hbase-1.1.5/bin/start-hbase.sh
name: start hbase
集群的配置文件
请根据build.yml中的写法放到合适的位置,本机的位置/home/luke/git/note/messSource,务必做相应的修改。
core-site.xml
hadoop.tmp.dir
/home/litchi/tmp
fs.defaultFS
hdfs://CHC
io.file.buffer.size
4096
ha.zookeeper.quorum
n3,n4,n5,n6,n7
hbase-site.xml
hbase.rootdir
hdfs://n1:9000/hbase
hbase.cluster.distributed
true
hbase.zookeeper.quorum
n3,n4,n5,n6,n7
dfs.replication
3
hdfs-site.xml
dfs.nameservices
CHC
dfs.ha.namenodes.CHC
os1,os2
dfs.namenode.rpc-address.CHC.os1
n1:9000
dfs.namenode.http-address.CHC.os1
n1:50070
dfs.namenode.rpc-address.CHC.os2
n2:9000
dfs.namenode.http-address.CHC.os2
n2:50070
dfs.namenode.shared.edits.dir
qjournal://n3:8485;n4:8485;n5:8485/CHC
dfs.journalnode.edits.dir
/home/litchi/hadoop/tmp/journal
dfs.ha.automatic-failover.enabled.CHC
true
dfs.client.failover.proxy.provider.CHC
org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider
dfs.ha.fencing.methods
sshfence
dfs.ha.fencing.ssh.private-key-files
/home/litchi/.ssh/id_rsa
dfs.ha.fencing.ssh.connect-timeout
30000
dfs.namenode.name.dir
/home/litchi/hadoop/tmp/name
dfs.datanode.data.dir
/home/litchi/hadoop/tmp/data
dfs.replication
3
dfs.webhdfs.enabled
true
regionservers
n3
n4
n5
n6
n7
slaves
n3
n4
n5
n6
n7
zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/litchi/zookeeper
clientPort=2181
server.3=n3:2888:3888
server.4=n4:2888:3888
server.5=n5:2888:3888
server.6=n6:2888:3888
server.7=n7:2888:3888
集群的软件
jdk-8u60-linux-x64.tar.gz
zookeeper-3.4.8.tar.gz
hadoop-2.7.2.tar.gz
hbase-1.1.5-bin.tar.gz
不要忘了把他们也下载下来,放到 build.yml 所指定的位置。如果版本不同,也请做相应的文字修改。
一键超人
/start.sh
后记
匆匆写完,必然有所错误与遗漏,望请指正,方便你我。