hdfs+zookeeper+hbase分布式在k8s中部署

版权声明:本文为博主原创文章,转载请注明来源。开发合作联系[email protected] https://blog.csdn.net/luanpeng825485697/article/details/81985602

1、背景知识

先看下HBase的组成:

这里写图片描述

Master:Master主要负责管理RegionServer集群,如负载均衡及资源分配等,它本身也可以以集群方式运行,但同一时刻只有一个master处于激活状态。当工作中的master宕掉后,zookeeper会切换到其它备选的master上。

RegionServer:负责具体数据块的读写操作。

ZooKeeper:负责集群元数据的维护并监控集群的状态以防止单点故障。部署HBase时可以使用自带的ZooKeeper也可以使用独立的集群,是HBase跑起来的先决条件。

HDFS:写入HBase中的数据最终都持久化到了HDFS中,也是HBase运行的先决条件。

2、HDFS集群部署

HDFS由namenode和datanode组成,我们这里使用公有镜像,当然你也可以从DockerHub上pull合适的镜像再push到自己的私有仓库中。

然后创建如下hdfs.yaml文件:

apiVersion: v1
kind: Service
metadata:
  name: hdfs-namenode-service   # 设置服务名
  namespace: cloudai-2     # 设置命名空间
spec:
  selector:
    app: hdfs-namenode    # 根据label选择pod
  type: ClusterIP         # 表示只有集群内可以访问,只能通过集群ip访问      
  clusterIP: "10.233.8.10"   # 固定集群ip,datanode链接时要用这个集群ip,所以要保障这个ip在集群ip范围内,并且没有被占用
  ports:
    - name: rpc
      port: 4231                # # 设置namenode的端口号
      targetPort: 8020
    - name: p1
      port: 50020
    - name: p2
      port: 50090
    - name: p3
      port: 50070
    - name: p4
      port: 50010
    - name: p5
      port: 50075
    - name: p6
      port: 8031
    - name: p7
      port: 8032
    - name: p8
      port: 8033
    - name: p9
      port: 8040
    - name: p10
      port: 8042
    - name: p11
      port: 49707
    - name: p12
      port: 22
    - name: p13
      port: 8088
    - name: p14
      port: 8030
---
apiVersion: v1
kind: ReplicationController   # 复制控制器,和deployment一样的
metadata:
    name: hdfs-namenode-1
    namespace: cloudai-2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hdfs-namenode    #  设置pod的标签label
    spec:
      containers:
        - name: hdfs-namenode   # 设置容器名
          image: bioshrek/hadoop-hdfs-namenode:cdh5   # 直接拉取公共镜像,也可以自己先拉下来传到私有仓库中
          volumeMounts:
            - name: data1
              mountPath: /var/lib/hadoop-hdfs/cache/hdfs/dfs/name    # 挂载,本地机器目录,存储namenode的信息,主要为datenode中数据库的位置信息。
            - name: data2
              mountPath: /cloudai2/hdfs/data/shared_with_docker_container/cdh5/namenode   #  nn为name
          ports:
            - containerPort: 50020
            - containerPort: 50090
            - containerPort: 50070
            - containerPort: 50010
            - containerPort: 50075
            - containerPort: 8031
            - containerPort: 8032
            - containerPort: 8033
            - containerPort: 8040
            - containerPort: 8042
            - containerPort: 49707
            - containerPort: 22
            - containerPort: 8088
            - containerPort: 8030
            - containerPort: 8020
      nodeSelector:
        kubernetes.io/hostname: node1   # 根据node的label标签选择主机。
      volumes:
        - hostPath:
            path: /data1/kubernetes/hdfs-namenode/data1
          name: data1
        - hostPath:
            path: /data1/kubernetes/hdfs-namenode/data2
          name: data2
---
apiVersion: v1
kind: ReplicationController
metadata:
    name: hdfs-datanode-1
    namespace: cloudai-2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hdfs-datanode
        server-id: "1"
    spec:
      containers:
        - name: hdfs-datanode-1
          image: bioshrek/hadoop-hdfs-datanode:cdh5    # 直接拉取公共镜像,也可以自己先拉下来传到私有仓库中
          volumeMounts:
            - name: data1
              mountPath: /var/lib/hadoop-hdfs/cache/hdfs/dfs/name     # 设置name数据存储路径在本地的路径
            - name: data2
              mountPath: /cloudai2/hdfs/data/shared_with_docker_container/cdh5/datanode  # 设置hdfs数据存储路径在本机的路径 
          env:
            - name: HDFSNAMENODERPC_SERVICE_HOST
              value: "10.233.8.10"    # 设置namenode的ip
            - name: HDFSNAMENODERPC_SERVICE_PORT
              value: "4231"    # 设置namenode的端口号
          ports:
            - containerPort: 50020
            - containerPort: 50090
            - containerPort: 50070
            - containerPort: 50010
            - containerPort: 50075
            - containerPort: 8031
            - containerPort: 8032
            - containerPort: 8033
            - containerPort: 8040
            - containerPort: 8042
            - containerPort: 49707
            - containerPort: 22
            - containerPort: 8088
            - containerPort: 8030
            - containerPort: 8020
      nodeSelector:
        kubernetes.io/hostname: node1   # 固定到指定节点上
      volumes:
        - hostPath:
            path: /data1/kubernetes/hdfs-datanode1/data1
          name: data1
        - hostPath:
            path: /data1/kubernetes/hdfs-datanode1/data2
          name: data2
---
apiVersion: v1
kind: ReplicationController
metadata:
    name: hdfs-datanode-2
    namespace: cloudai-2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hdfs-datanode    # 设置pod的标签
        server-id: "2"
    spec:
      containers:
        - name: hdfs-datanode-2
          image: bioshrek/hadoop-hdfs-datanode:cdh5
          volumeMounts:
            - name: data1
              mountPath: /var/lib/hadoop-hdfs/cache/hdfs/dfs/name   # 设置name数据存储路径在本地的路径
            - name: data2
              mountPath: /cloudai2/hdfs/data/shared_with_docker_container/cdh5/datanode   # 设置hdfs数据存储路径在本机的路径 
          env:
            - name: HDFSNAMENODERPC_SERVICE_HOST
              value: "10.233.8.10"     # 设置namenode的ip
            - name: HDFSNAMENODERPC_SERVICE_PORT
              value: "4231"           # 设置namenode的端口号
          ports:
            - containerPort: 50020
            - containerPort: 50090
            - containerPort: 50070
            - containerPort: 50010
            - containerPort: 50075
            - containerPort: 8031
            - containerPort: 8032
            - containerPort: 8033
            - containerPort: 8040
            - containerPort: 8042
            - containerPort: 49707
            - containerPort: 22
            - containerPort: 8088
            - containerPort: 8030
      nodeSelector:
        kubernetes.io/hostname: node1   # 固定到指定节点上
      volumes:
        - name: data1
          hostPath:
            path: /data2/kubernetes/hdfs-datanode2/data1
        - name: data2
          hostPath:
            path: /data2/kubernetes/hdfs-datanode2/data2
---
apiVersion: v1
kind: ReplicationController
metadata:
    name: hdfs-datanode-3
    namespace: cloudai-2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hdfs-datanode
        server-id: "3"
    spec:
      containers:
        - name: hdfs-datanode-3
          image: bioshrek/hadoop-hdfs-datanode:cdh5
          volumeMounts:
            - name: data1
              mountPath: /var/lib/hadoop-hdfs/cache/hdfs/dfs/name
            - name: data2
              mountPath: /cloudai2/hdfs/data/shared_with_docker_container/cdh5/datanode   # 设置hdfs数据存储路径在本机的路径 
          env:
            - name: HDFSNAMENODERPC_SERVICE_HOST
              value: "10.233.8.10"      # 设置namenode的ip
            - name: HDFSNAMENODERPC_SERVICE_PORT
              value: "4231"             # 设置namenode的端口号
          ports:
            - containerPort: 50020
            - containerPort: 50090
            - containerPort: 50070
            - containerPort: 50010
            - containerPort: 50075
            - containerPort: 8031
            - containerPort: 8032
            - containerPort: 8033
            - containerPort: 8040
            - containerPort: 8042
            - containerPort: 49707
            - containerPort: 22
            - containerPort: 8088
            - containerPort: 8030
      nodeSelector:
        kubernetes.io/hostname: node1   # 固定到指定节点上
      volumes:
        - name: data1
          hostPath:
            path: /data3/kubernetes/hdfs-datanode3/data1
        - name: data2
          hostPath:
            path: /data3/kubernetes/hdfs-datanode3/data2

通过 kubectl create -f hdfs.yaml 即创建一个名为hdfs-namenode-service的service,四个分别名为hdfs-namenode-1、hdfs-datanode-1、hdfs-datanode-2、hdfs-datanode-3的RC。通过 kubectl get services/rc/pods 可以看到对应的service和pod都已经正常启动了。

datanode的镜像中包含hdfs用户,并且只有该用户有操作hdfs分布式存储的权限。所以如果要向hdfs资源管理器中添加文件必须使用hdfs用户。但是由于hdfs用户在容器资源管理器中的权限不足,所以有时在本地创建文件,可能会权限不足。

下面对HDFS进行测试是否可以正常使用:

# 查看HDFS pods
kubectl get pods

# 通过describe查看pods跑在哪个k8s node上
kubectl describe pod hdfs-datanode-3-h4jvt

# 进入容器内部
docker ps | grep hdfs-datanode-3
docker exec -it 2e2c4df0c0a9 /bin/bash

# 创建本地文件
echo "Hello" > hello
设置777权限
chown 777 hello
# 切换至 hdfs 用户
su hdfs

# 创建hdfs文件系统中的目录
hadoop fs -mkdir /test

# 将本地文件复制到HDFS文件系统中
hadoop fs -put hello /test
# 查看HDFS中的文件信息
hadoop fs -ls /test

# 你已经在一个datanode里面想hdfs文件系统中加入了一个目录和文件,hdfs文件系统是分布式的。所以在其他的datanode的pod里面应该也能访问到,所以你可以 docker exec 到其它datanode中查看文件信息,如:
root@hdfs-datanode-1-nek2l:/# hadoop fs -ls /test
Found 1 items
-rw-r--r--   2 hdfs hadoop          6 2015-11-27 08:36 /test/hello

删除hdfs目录
hadoop fs -rm -r /test

zookeeper集群部署

首先要知道zookeeper的配置文件

配置-zoo.cfg

这是zookeeper的主要配置文件,因为Zookeeper是一个集群服务,集群的每个节点都需要这个配置文件。为了避免出差错,zoo.cfg这个配置文件里没有跟特定节点相关的配置,所以每个节点上的这个zoo.cfg都是一模一样的配置。这样就非常便于管理了,比如我们可以把这个文件提交到版本控制里管理起来。其实这给我们设计集群系统的时候也是个提示:集群系统一般有很多配置,应该尽量将通用的配置和特定每个服务的配置(比如服务标识)分离,这样通用的配置在不同服务之间copy就ok了。ok,下面来介绍一些配置点:

clientPort=2181

client port,顾名思义,就是客户端连接zookeeper服务的端口。这是一个TCP port。

dataDir=/data

dataLogDir=/datalog

dataLogDir如果没提供的话使用的则是dataDir。zookeeper的持久化都存储在这两个目录里。dataLogDir里是放到的顺序日志(WAL)。而dataDir里放的是内存数据结构的snapshot,便于快速恢复。为了达到性能最大化,一般建议把dataDir和dataLogDir分到不同的磁盘上,这样就可以充分利用磁盘顺序写的特性。

下面是集群中服务的列表

server.1=127.0.0.1:20881:30881
server.2=127.0.0.1:20882:30882
server.3=127.0.0.1:20883:30883

在上面的例子中,我把三个zookeeper服务放到同一台机器上。上面的配置中有两个TCP port。后面一个是用于Zookeeper选举用的,而前一个是Leader和Follower或Observer交换数据使用的。我们还注意到server.后面的数字。这个就是myid(关于myid是什么下一节会介绍)。

上面这几个是一些基本配置。

还有像 tickTime,这是个时间单位定量。比如tickTime=1000,这就表示在zookeeper里1 tick表示1000 ms,所有其他用到时间的地方都会用多少tick来表示。

比如 syncLimit = 2 就表示fowller与leader的心跳时间是2 tick。

maxClientCnxns – 对于一个客户端的连接数限制,默认是60,这在大部分时候是足够了。但是在我们实际使用中发现,在测试环境经常超过这个数,经过调查发现有的团队将几十个应用全部部署到一台机器上,以方便测试,于是这个数字就超过了。

minSessionTimeout, maxSessionTimeout – 一般,客户端连接zookeeper的时候,都会设置一个session timeout,如果超过这个时间client没有与zookeeper server有联系,则这个session会被设置为过期(如果这个session上有临时节点,则会被全部删除,这就是实现集群感知的基础,后面的文章会介绍这一点)。但是这个时间不是客户端可以无限制设置的,服务器可以设置这两个参数来限制客户端设置的范围。

autopurge.snapRetainCount,autopurge.purgeInterval – 客户端在与zookeeper交互过程中会产生非常多的日志,而且zookeeper也会将内存中的数据作为snapshot保存下来,这些数据是不会被自动删除的,这样磁盘中这样的数据就会越来越多。不过可以通过这两个参数来设置,让zookeeper自动删除数据。autopurge.purgeInterval就是设置多少小时清理一次。而autopurge.snapRetainCount是设置保留多少个snapshot,之前的则删除。

不过如果你的集群是一个非常繁忙的集群,然后又碰上这个删除操作,可能会影响zookeeper集群的性能,所以一般会让这个过程在访问低谷的时候进行,但是遗憾的是zookeeper并没有设置在哪个时间点运行的设置,所以有的时候我们会禁用这个自动删除的功能,而在服务器上配置一个cron,然后在凌晨来干这件事。

以上就是zoo.cfg里的一些配置了。下面就来介绍myid。

配置-myid

在dataDir里会放置一个myid文件,里面就一个数字,用来唯一标识这个服务。这个id是很重要的,一定要保证整个集群中唯一。zookeeper会根据这个id来取出server.x上的配置。比如当前id为1,则对应着zoo.cfg里的server.1的配置。

  1. 而且在后面我们介绍leader选举的时候,这个id的大小也是有意义的。

OK,上面就是配置的讲解了,现在我们可以启动zookeeper集群了。进入到bin目录,执行 ./zkServer.sh start即可。

这里使用的zoo.cfg 文件内容如下:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/opt/zookeeper/data
#This option will direct the machine to write the transaction log to the dataLogDir rather than the dataDir. This allows a dedicated log device to be used, and helps avoid competition between logging and snaphots.
dataLogDir=/opt/zookeeper/log

# the port at which the clients will connect
clientPort=2181
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
autopurge.purgeInterval=1

config-and-run.sh是zookeeper的启动脚本。是我们进入容器要运行的脚本。

config-and-run.sh 文件内容如下:

#!/bin/bash

echo "$SERVER_ID / $MAX_SERVERS" 
if [ ! -z "$SERVER_ID" ] && [ ! -z "$MAX_SERVERS" ]; then
  echo "Starting up in clustered mode"
  echo "" >> /opt/zookeeper/conf/zoo.cfg
  echo "#Server List" >> /opt/zookeeper/conf/zoo.cfg
  for i in $( eval echo {1..$MAX_SERVERS});do
    HostEnv="ZOOKEEPER_${i}_SERVICE_HOST"
    HOST=${!HostEnv}
    FollowerPortEnv="ZOOKEEPER_${i}_SERVICE_PORT_FOLLOWERS"
    FOLLOWERPORT=${!FollowerPortEnv}
    ElectionPortEnv="ZOOKEEPER_${i}_SERVICE_PORT_ELECTION"
    ELECTIONPORT=${!ElectionPortEnv}
    if [ "$SERVER_ID" = "$i" ];then
      echo "server.$i=0.0.0.0:$FOLLOWERPORT:$ELECTIONPORT" >> /opt/zookeeper/conf/zoo.cfg
    else
      echo "server.$i=$HOST:$FOLLOWERPORT:$ELECTIONPORT" >> /opt/zookeeper/conf/zoo.cfg
    fi
  done
  cat /opt/zookeeper/conf/zoo.cfg

  # Persists the ID of the current instance of Zookeeper
  echo ${SERVER_ID} > /opt/zookeeper/data/myid
  else
      echo "Starting up in standalone mode"
fi

exec /opt/zookeeper/bin/zkServer.sh start-foreground

有了配置文件,我们就可以在zookeeper镜像的基础上将我们的配置文件添加进去。这样就是我们自定义的zookeeper镜像了。

在 fabric8/zookeeper 的image基础上进行修改,修改后Dockerfile文件内容如下:

这里遇到的主要问题是要修改zookeeper的下载地址,是的在国内可以安装。可以去http://mirror.bit.edu.cn/apache/zookeeper/上查看支持什么版本。

FROM jboss/base-jdk:7

USER root

ENV ZOOKEEPER_VERSION 3.4.10
EXPOSE 2181 2888 3888

RUN yum -y install wget bind-utils && yum clean all \
    && wget -q -O - http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-${ZOOKEEPER_VERSION}/zookeeper-${ZOOKEEPER_VERSION}.tar.gz | tar -xzf - -C /opt \
    && mv /opt/zookeeper-${ZOOKEEPER_VERSION} /opt/zookeeper \
    && cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg \
    && mkdir -p /opt/zookeeper/{data,log}

WORKDIR /opt/zookeeper
VOLUME ["/opt/zookeeper/conf", "/opt/zookeeper/data", "/opt/zookeeper/log"]

COPY config-and-run.sh ./bin/
COPY zoo.cfg ./conf/

CMD ["/opt/zookeeper/bin/config-and-run.sh"]

修改完后创建镜像并push到私有仓库中,我这里推送到了在dockerhub上申请的私有仓库。

创建zookeeper.yaml文件在k8s上创建zookeeper集群:

apiVersion: v1
kind: Service
metadata:
  name: zookeeper-1
  namespace: cloudai-2     # 设置命名空间
  labels:
      name: zookeeper-1
spec:
  ports:
    - name: client
      port: 2181
      targetPort: 2181
    - name: followers
      port: 2888
      targetPort: 2888
    - name: election
      port: 3888
      targetPort: 3888
  selector:
    name: zookeeper
    server-id: "1"
  type: ClusterIP          # 只能集群内部访问
  clusterIP: 10.233.9.1    # 指定服务的集群ip,必须保障可用并且没有被占用
---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper-2
  namespace: cloudai-2     # 设置命名空间
  labels:
      name: zookeeper-2
spec:
  ports:
    - name: client
      port: 2181
      targetPort: 2181
    - name: followers
      port: 2888
      targetPort: 2888
    - name: election
      port: 3888
      targetPort: 3888
  selector:
    name: zookeeper
    server-id: "2"
  type: ClusterIP
  clusterIP: 10.233.9.2   # 指定服务的集群ip,必须保障可用并且没有被占用
---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper-3
  namespace: cloudai-2     # 设置命名空间
  labels:
      name: zookeeper-3
spec:
  ports:
    - name: client
      port: 2181
      targetPort: 2181
    - name: followers
      port: 2888
      targetPort: 2888
    - name: election
      port: 3888
      targetPort: 3888
  selector:
    name: zookeeper
    server-id: "3"
  type: ClusterIP
  clusterIP: 10.233.9.3  # 指定服务的集群ip,必须保障可用并且没有被占用
---
apiVersion: v1
kind: ReplicationController
metadata:
  name: zookeeper-1
  namespace: cloudai-2     # 设置命名空间
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: zookeeper
        server-id: "1"
    spec:
      volumes:
        - hostPath:
            path: /data1/kubernetes/zookeeper/data1
          name: data
        - hostPath:
            path: /data1/kubernetes/zookeeper/log1
          name: log
      imagePullSecrets:
      - name: hubsecret
      containers:
        - name: server
          image: mldp/cloud:hbase-zookeeper-1.0.0
          env:
            - name: SERVER_ID
              value: "1"
            - name: MAX_SERVERS
              value: "3"
          ports:
            - containerPort: 2181
            - containerPort: 2888
            - containerPort: 3888
          volumeMounts:
            - mountPath: /opt/zookeeper/data
              name: data
            - mountPath: /opt/zookeeper/log
              name: log
      nodeSelector:
        kubernetes.io/hostname: node1    # 根据node的label标签选择主机。
---
apiVersion: v1
kind: ReplicationController
metadata:
  name: zookeeper-2
  namespace: cloudai-2     # 设置命名空间
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: zookeeper
        server-id: "2"
    spec:
      volumes:
        - hostPath:
            path: /data1/kubernetes/zookeeper/data2
          name: data
        - hostPath:
            path: /data1/kubernetes/zookeeper/log2
          name: log
      imagePullSecrets:
      - name: hubsecret
      containers:
        - name: server
          image: mldp/cloud:hbase-zookeeper-1.0.0
          env:
            - name: SERVER_ID
              value: "2"
            - name: MAX_SERVERS
              value: "3"
          ports:
            - containerPort: 2181
            - containerPort: 2888
            - containerPort: 3888
          volumeMounts:
            - mountPath: /opt/zookeeper/data
              name: data
            - mountPath: /opt/zookeeper/log
              name: log
      nodeSelector:
        kubernetes.io/hostname: node1    # 根据node的label标签选择主机。
---
apiVersion: v1
kind: ReplicationController
metadata:
  name: zookeeper-3
  namespace: cloudai-2     # 设置命名空间
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: zookeeper
        server-id: "3"
    spec:
      volumes:
        - hostPath:
            path: /data1/kubernetes/zookeeper/data3
          name: data
        - hostPath:
            path: /data1/kubernetes/zookeeper/log3
          name: log
      imagePullSecrets:
      - name: hubsecret
      containers:
        - name: server
          image: mldp/cloud:hbase-zookeeper-1.0.0
          env:
            - name: SERVER_ID
              value: "3"
            - name: MAX_SERVERS
              value: "3"
          ports:
            - containerPort: 2181
            - containerPort: 2888
            - containerPort: 3888
          volumeMounts:
            - mountPath: /opt/zookeeper/data
              name: data
            - mountPath: /opt/zookeeper/log
              name: log
      nodeSelector:
        kubernetes.io/hostname: node1   # 根据node的label标签选择主机。

通过 kubectl create -f zookeeper.yaml 创建三个service和对应的RC。注意container中已经把ZooKeeper的data和log目录映射到了主机的对应目录上用于持久化存储。

创建完之后即可进行测试:

# 进入zookeeper对应的容器后找到zkCli.sh,用该客户端进行测试
/opt/zookeeper/bin/zkCli.sh
[zk: localhost:2181(CONNECTED) 0]

# 连接到k8s创建的zookeeper service (三个service任意一个都行)
[zk: localhost:2181(CONNECTED) 0] connect 172.16.11.2:2181
[zk: 10.233.9.2:2181(CONNECTED) 1]

# 查看目录信息
[zk: 10.233.9.2:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: 10.233.9.2:2181(CONNECTED) 2] get /zookeeper

cZxid = 0x0
ctime = Thu Jan 01 00:00:00 UTC 1970
mZxid = 0x0
mtime = Thu Jan 01 00:00:00 UTC 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: 10.233.9.2:2181(CONNECTED) 3]

HBASE集群部署

以上准备工作做好后,下面部署具有两个master和两个regionserver的HBase集群,其中两个master分别位于两个节点上,两个regionserver也分别位于两个节点上;使用独立的HDFS和ZooKeeper服务。

首先需要创建HBase的镜像,先去去https://mirrors.tuna.tsinghua.edu.cn/apache/hbase看看有什么版本的。这里我们用的是1.2.6.1

先编写配置文件hbase-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
  <configuration>
    <property>
      <name>hbase.cluster.distributed</name>
      <value>true</value>
    </property>
    <property>
      <name>hbase.master.port</name>
      <value>@HBASE_MASTER_PORT@</value>
    </property>
    <property>
      <name>hbase.master.info.port</name>
      <value>@HBASE_MASTER_INFO_PORT@</value>
    </property>
    <property>
      <name>hbase.regionserver.port</name>
      <value>@HBASE_REGION_PORT@</value>
    </property>
    <property>
      <name>hbase.regionserver.info.port</name>
      <value>@HBASE_REGION_INFO_PORT@</value>
    </property>
    <property>
      <name>hbase.rootdir</name>
      <value>hdfs://@HDFS_PATH@/@ZNODE_PARENT@</value>
    </property>
    <property>
      <name>hbase.zookeeper.quorum</name>
      <value>@ZOOKEEPER_IP_LIST@</value>
    </property>
    <property>
      <name>hbase.zookeeper.property.clientPort</name>
      <value>@ZOOKEEPER_PORT@</value>
    </property>
    <property>
      <name>zookeeper.znode.parent</name>
      <value>/@ZNODE_PARENT@</value>
    </property>
  </configuration>

在编写配置文件start-k8s-hbase.sh

#!/bin/bash

export HBASE_CONF_FILE=/opt/hbase/conf/hbase-site.xml
export HADOOP_USER_NAME=hdfs
export HBASE_MANAGES_ZK=false

sed -i "s/@HBASE_MASTER_PORT@/$HBASE_MASTER_PORT/g" $HBASE_CONF_FILE
sed -i "s/@HBASE_MASTER_INFO_PORT@/$HBASE_MASTER_INFO_PORT/g" $HBASE_CONF_FILE
sed -i "s/@HBASE_REGION_PORT@/$HBASE_REGION_PORT/g" $HBASE_CONF_FILE
sed -i "s/@HBASE_REGION_INFO_PORT@/$HBASE_REGION_INFO_PORT/g" $HBASE_CONF_FILE
sed -i "s/@HDFS_PATH@/$HDFS_SERVICE:$HDFS_PORT\/$ZNODE_PARENT/g" $HBASE_CONF_FILE
sed -i "s/@ZOOKEEPER_IP_LIST@/$ZOOKEEPER_SERVICE_LIST/g" $HBASE_CONF_FILE
sed -i "s/@ZOOKEEPER_PORT@/$ZOOKEEPER_PORT/g" $HBASE_CONF_FILE
sed -i "s/@ZNODE_PARENT@/$ZNODE_PARENT/g" $HBASE_CONF_FILE

for i in ${HBASE_MASTER_LIST[@]}
do
   arr=(${i//:/ })
   echo "${arr[0]} ${arr[1]}" >> /etc/hosts
done

for i in ${HBASE_REGION_LIST[@]}
do
   arr=(${i//:/ })
   echo "${arr[0]} ${arr[1]}" >> /etc/hosts
done

if [ "$HBASE_SERVER_TYPE" = "master" ]; then
    /opt/hbase/bin/hbase master start > logmaster.log 2>&1
elif [ "$HBASE_SERVER_TYPE" = "regionserver" ]; then
    /opt/hbase/bin/hbase regionserver start > logregion.log 2>&1
fi

其中导出HADOOP_USER_NAME为hdfs用户,否则会报Permission Denied的错误;HBASE_MANAGES_ZK=false表示不使用HBase自带的ZooKeeper;HBASE_MASTER_LIST为HBase集群中除当前master外的其余master的服务地址和pod名的对应关系;HBASE_REGION_LIST为HBase集群中除当前regionserver外的其余regionserver的服务地址和pod名的对应关系;最后根据 HBASE_SERVER_TYPE 的取值来确定是启master还是regionserver。

最后编写Dockerfile

FROM java:7

# 这里要去https://mirrors.tuna.tsinghua.edu.cn/apache/hbase看看有什么版本的
ENV HBASE_VERSION 1.2.6.1
ENV HBASE_INSTALL_DIR /opt/hbase
# 配置环境变量
ENV JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
# 下载解压hbase到指定目录
RUN mkdir -p ${HBASE_INSTALL_DIR} && \
    curl -L https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/${HBASE_VERSION}/hbase-${HBASE_VERSION}-bin.tar.gz | tar -xz --strip=1 -C ${HBASE_INSTALL_DIR}

# 复制配置文件进入
ADD hbase-site.xml /opt/hbase/conf/hbase-site.xml
ADD start-k8s-hbase.sh /opt/hbase/bin/start-k8s-hbase.sh
RUN chmod +x /opt/hbase/bin/start-k8s-hbase.sh

WORKDIR /opt/hbase/bin

ENV PATH=$PATH:/opt/hbase/bin

CMD /opt/hbase/bin/start-k8s-hbase.sh

镜像生成后将镜像上传到私有仓库,我这里直接上传到自己的dockerhub


docker build -t hbase:1.0.0 .
docker tag hbase:1.0.0 mldp/cloud:hbase-1.0.0
docker push mldp/cloud:hbase-1.0.0

编写yaml文件。在k8s上部署

apiVersion: v1
kind: Service
metadata:
  name: hbase-master-1
  namespace: cloudai-2     # 设置命名空间
spec:
  selector:
    app: hbase-master
    server-id: "1"
  type: ClusterIP
  clusterIP: "10.233.9.11"   # 指定集群ip,固定ip为了能在后面更好的绑定上
  ports:
    - name: rpc
      port: 60000
      targetPort: 60000
    - name: info
      port: 60001
      targetPort: 60001
---
apiVersion: v1
kind: Service
metadata:
  name: hbase-master-2
  namespace: cloudai-2     # 设置命名空间
spec:
  selector:
    app: hbase-master
    server-id: "2"
  type: ClusterIP
  clusterIP: "10.233.9.12"  # 指定集群ip,固定ip为了能在后面更好的绑定上
  ports:
    - name: rpc
      port: 60000
      targetPort: 60000
    - name: info
      port: 60001
      targetPort: 60001
---
apiVersion: v1
kind: Service
metadata:
  name: hbase-region-1
  namespace: cloudai-2     # 设置命名空间
spec:
  selector:
    app: hbase-region
    server-id: "1"
  type: ClusterIP
  clusterIP: "10.233.9.13"  # 指定集群ip,固定ip为了能在后面更好的绑定上
  ports:
    - name: rpc
      port: 60010
      targetPort: 60010
    - name: info
      port: 60011
      targetPort: 60011
---
apiVersion: v1
kind: Service
metadata:
  name: hbase-region-2
  namespace: cloudai-2     # 设置命名空间
spec:
  selector:
    app: hbase-region
    server-id: "2"
  type: ClusterIP
  clusterIP: "10.233.9.14"   # 指定集群ip,固定ip为了能在后面更好的绑定上
  ports:
    - name: rpc
      port: 60010
      targetPort: 60010
    - name: info
      port: 60011
      targetPort: 60011
---
apiVersion: v1
kind: Pod
metadata:
  namespace: cloudai-2     # 设置命名空间
  name: hbase-master-1
  labels:
    app: hbase-master
    server-id: "1"
spec:
  imagePullSecrets:
  - name: hubsecret              # 镜像拉取秘钥
  containers:
    - name: hbase-master-1
      image: mldp/cloud:hbase-1.0.0
      ports:
        - containerPort: 60000
        - containerPort: 60001
      env:
        - name: HBASE_SERVER_TYPE     # hbase角色类型。根据 HBASE_SERVER_TYPE 的取值来确定是启master还是regionserver
          value: master
        - name: HBASE_MASTER_PORT    # hbase的master端口号
          value: "60000"
        - name: HBASE_MASTER_INFO_PORT  # hbase的master信息端口号
          value: "60001"
        - name: HBASE_REGION_PORT    # hbase的region端口号
          value: "60010"
        - name: HBASE_REGION_INFO_PORT   # hbase的region的信息端口号
          value: "60011"
        - name: HDFS_SERVICE   # hdfs服务,HDFS_SERVICE为HDFS服务经过skyDNS之后的对应域名,若未设置skyDNS则此处值设为HDFS服务对应的IP地址
          value: "10.233.8.10"   # hdfs的集群ip
        - name: HDFS_PORT   # hdfs端口号
          value: "4231"
        - name: ZOOKEEPER_SERVICE_LIST   # zookeeper列表,域名,同HDFS_SERVICE意义
          value: "10.233.9.1,10.233.9.2,10.233.9.3"   # zookeeper集群ip
        - name: ZOOKEEPER_PORT   # zookeeper端口号
          value: "2181"
        - name: ZNODE_PARENT   # znode_parent
          value: hbase
        - name: HBASE_MASTER_LIST   # 为HBase集群中除当前master外的其余master的服务地址和pod名的对应关系。格式为 <master服务IP地址>:<master对应Pod名>,多个项之间以空格分隔
          value: "10.233.9.12:hbase-master-2"
        - name: HBASE_REGION_LIST  # 为HBase集群中除当前regionserver外的其余regionserver的服务地址和pod名的对应关系
          value: "10.233.9.13:hbase-region-1 10.233.9.14:hbase-region-2"
  restartPolicy: Always  # 表示如果该Pod挂掉的话将一直尝试重新启动它
  nodeSelector:
    kubernetes.io/hostname: node1  # 选择部署的主机,两个master的pod部署在不同的机器上。才能防止宕机
---
apiVersion: v1
kind: Pod
metadata:
  namespace: cloudai-2     # 设置命名空间
  name: hbase-master-2
  labels:
    app: hbase-master
    server-id: "2"
spec:
  imagePullSecrets:
  - name: hubsecret
  containers:
    - name: hbase-master-1
      image: mldp/cloud:hbase-1.0.0
      ports:
        - containerPort: 60000
        - containerPort: 60001
      env:
        - name: HBASE_SERVER_TYPE
          value: master
        - name: HBASE_MASTER_PORT
          value: "60000"
        - name: HBASE_MASTER_INFO_PORT
          value: "60001"
        - name: HBASE_REGION_PORT
          value: "60010"
        - name: HBASE_REGION_INFO_PORT
          value: "60011"
        - name: HDFS_SERVICE
          value: "10.233.8.10"
        - name: HDFS_PORT
          value: "4231"
        - name: ZOOKEEPER_SERVICE_LIST
          value: "10.233.9.1,10.233.9.2,10.233.9.3"
        - name: ZOOKEEPER_PORT
          value: "2181"
        - name: ZNODE_PARENT
          value: hbase
        - name: HBASE_MASTER_LIST
          value: "10.233.9.11:hbase-master-1"
        - name: HBASE_REGION_LIST
          value: "10.233.9.13:hbase-region-1 10.233.9.14:hbase-region-2"
  restartPolicy: Always
  nodeSelector:
    kubernetes.io/hostname: node2  # 选择部署的主机,两个master的pod部署在不同的机器上。才能防止宕机
---
apiVersion: v1
kind: Pod
metadata:
    namespace: cloudai-2     # 设置命名空间
    name: hbase-region-1
    labels:
      app: hbase-region-1
      server-id: "1"
spec:
  imagePullSecrets:
  - name: hubsecret
  containers:
    - name: hbase-region-1
      image: mldp/cloud:hbase-1.0.0
      ports:
        - containerPort: 60010
        - containerPort: 60011
      env:
        - name: HBASE_SERVER_TYPE
          value: regionserver
        - name: HBASE_MASTER_PORT
          value: "60000"
        - name: HBASE_MASTER_INFO_PORT
          value: "60001"
        - name: HBASE_REGION_PORT
          value: "60010"
        - name: HBASE_REGION_INFO_PORT
          value: "60011"
        - name: HDFS_SERVICE
          value: "10.233.8.10"
        - name: HDFS_PORT
          value: "4231"
        - name: ZOOKEEPER_SERVICE_LIST
          value: "10.233.9.1,10.233.9.2,10.233.9.3"
        - name: ZOOKEEPER_PORT
          value: "2181"
        - name: ZNODE_PARENT
          value: hbase
        - name: HBASE_MASTER_LIST
          value: "10.233.9.11:hbase-master-1 10.233.9.12:hbase-master-2"
        - name: HBASE_REGION_LIST
          value: "10.233.9.14:hbase-region-2"
  restartPolicy: Always
  nodeSelector:
    kubernetes.io/hostname: node1  # 选择部署的主机,两个region的pod部署在不同的机器上。才能防止宕机
---
apiVersion: v1
kind: Pod
metadata:
    namespace: cloudai-2     # 设置命名空间
    name: hbase-region-2
    labels:
      app: hbase-region-2
      server-id: "2"
spec:
  imagePullSecrets:
  - name: hubsecret
  containers:
    - name: hbase-region-2
      image: mldp/cloud:hbase-1.0.0
      ports:
        - containerPort: 60010
        - containerPort: 60011
      env:
        - name: HBASE_SERVER_TYPE
          value: regionserver
        - name: HBASE_MASTER_PORT
          value: "60000"
        - name: HBASE_MASTER_INFO_PORT
          value: "60001"
        - name: HBASE_REGION_PORT
          value: "60010"
        - name: HBASE_REGION_INFO_PORT
          value: "60011"
        - name: HDFS_SERVICE
          value: "10.233.8.10"
        - name: HDFS_PORT
          value: "4231"
        - name: ZOOKEEPER_SERVICE_LIST
          value: "10.233.9.1,10.233.9.2,10.233.9.3"
        - name: ZOOKEEPER_PORT
          value: "2181"
        - name: ZNODE_PARENT
          value: hbase
        - name: HBASE_MASTER_LIST
          value: "10.233.9.11:hbase-master-1 10.233.9.12:hbase-master-2"
        - name: HBASE_REGION_LIST
          value: "10.233.9.13:hbase-region-1"
  restartPolicy: Always
  nodeSelector:
    kubernetes.io/hostname: node2  # 选择部署的主机,两个region的pod部署在不同的机器上。才能防止宕机

选择使用单Pod而不是ReplicationController,是因为k8s会在RC中Container的hostname后面加上随机字符以区分彼此,而单Pod的Pod name和hostname是一致的;restartPolicy设为Always算是为单Pod方式鲁棒性提供点小小的补偿吧;如果将Pod name设置为对应service的IP或域名怎样?然而hostname并不允许带点号;写入 /etc/hosts 中的IP选择了service的而非Pod的,因为Pod中的IP在运行前并不能获取到,而且在重启Pod后也会发生改变,而service的IP是不变的,因此选择了 serviceIP:PodName 这种对应关系。

说明:该yaml文件共创建了两个master服务、两个regionserver服务,以及对应的两个master Pods和两个regionserver Pods;Pod的restartPolicy设为Always表示如果该Pod挂掉的话将一直尝试重新启动它;以环境变量的形式将参数传递进Pod中,其中HDFS_SERVICE为HDFS服务经过skyDNS之后的对应域名,若未设置skyDNS则此处值设为HDFS服务对应的IP地址,ZOOKEEPER_SERVICE_LIST同理;HBASE_MASTER_LIST的值格式为

# 创建
$kubectl create -f hbase.yaml
service "hbase-master-1" created
service "hbase-master-2" created
service "hbase-region-1" created
service "hbase-region-2" created
pod "hbase-master-1" created
pod "hbase-master-2" created
pod "hbase-region-1" created
pod "hbase-region-2" created

# 查看pods
$kubectl get pods
。。。

# 查看service
$kubectl get service
。。。

可以通过docker exec进入到HBase对应的容器中进行表操作以测试HBase的工作状态:

# 进入node2的hbase-master-2容器中
[@bx_42_198 /opt/scs/openxxs]# docker exec -it f131fcf15a72 /bin/bash

# 使用hbase shell对hbase进行操作
[root@hbase-master-2 bin]# hbase shell
2015-11-30 15:15:58,632 INFO  [main] Configuration.deprecation: hadoop.native.lib is deprecated. Instead, use io.native.lib.available
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version 0.98.10.1-hadoop2, rd5014b47660a58485a6bdd0776dea52114c7041e, Tue Feb 10 11:34:09 PST 2015

# 通过status查看状态,这里显示的 2 dead 是之前测试时遗留的记录,无影响
hbase(main):001:0> status
2015-11-30 15:16:03,551 WARN  [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
servers, 2 dead, 1.5000 average load

# 创建表
hbase(main):002:0> create 'test','id','name'
row(s) in 0.8330 seconds

=> Hbase::Table - test

# 查看表
hbase(main):003:0> list
TABLE                                                                                                                                                                                
member                                                                                                                                                                               
test                                                                                                                                                                                 
row(s) in 0.0240 seconds

=> ["member", "test"]

# 插入数据
hbase(main):004:0> put 'test','test1','id:5','addon'
row(s) in 0.1540 seconds

# 查看数据
hbase(main):005:0> get 'test','test1'
COLUMN                                         CELL                                                                                                                                  
 id:5                                          timestamp=1448906130803, value=addon                                                                                                  
row(s) in 0.0490 seconds

# 进入199的hbase-master-1容器中查看从198上插入的数据
hbase(main):001:0> get 'test','test1'
2015-11-30 18:01:23,944 WARN  [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
COLUMN                                         CELL                                                                                                                                  
 id:5                                          timestamp=1448906130803, value=addon                                                                                                  
row(s) in 0.2430 seconds

参考:https://www.cnblogs.com/openxxs/p/5001721.html

猜你喜欢

转载自blog.csdn.net/luanpeng825485697/article/details/81985602