配置Kubernates方式启动Zookeeper集群

2019-06-17  本文已影响0人  大猪小猪在菜盘

本文使用的ZooKeeper官方镜像版本为3.5,可以去Docker Hub查看此版本镜像的Dockerfile文件

1. 单机启动一个简单的三点集群

ZooKeeper 可以很方便的用docker-compose方式在单机启动一个三点以上的集群,样例编排脚本如下:

version: '3.1'

services:
  zoo1:
    image: zookeeper
    restart: always
    hostname: zoo1
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181

  zoo2:
    image: zookeeper
    restart: always
    hostname: zoo2
    ports:
      - 2182:2181
    environment:
      ZOO_MY_ID: 2
      ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=0.0.0.0:2888:3888;2181 server.3=zoo3:2888:3888;2181

  zoo3:
    image: zookeeper
    restart: always
    hostname: zoo3
    ports:
      - 2183:2181
    environment:
      ZOO_MY_ID: 3
      ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=0.0.0.0:2888:3888;2181

核心配置部分即为配置ZooKeeper地址的ZOO_SERVERS和配置ZooKeeper的服务ID即ZOO_MY_ID。

ZooKeeper一共需要用到三个端口,端口的功能如下:

1、2181:对cline端提供服务
2、3888:选举leader使用
3、2888:集群内机器通讯使用(Leader监听此端口)

调试的时候使用docker-compose启动一个单机的集群是非常合适的,到了测试环境和生产环境,集群化的部署和高可用已经使必然。我们接下来就过渡到K8S集群环境中部署ZooKeeper

2. K8S部署思路

网上有大牛们的类似搭建文章例如k8s部署zookeeper/kakfa集群,看了之后发现虽然能满足功能但是启动了三个Deployment再用Service去对外暴露统一域名这种方式。而ZooKeeper是一个有状态的集群,StatefulSet+Headless Service方式来创建工作负载更加合适。另外推荐用一个StatefulSet的原因是相比与多个Deployment,StatefulSet更加容易通过脚本和环境变量扩展。

StatefulSet+HeadlessService方式下,每个ZooKeeper节点都有类似如下的域名:

zookeeper-service-0.zookeeper-service.namespace.svc.cluster.local:2888:3888;2181 
zookeeper-service-1.zookeeper-service.namespace.svc.cluster.local:2888:3888;2181 
zookeeper-service-2.zookeeper-service.namespace.svc.cluster.local:2888:3888;2181 
...
zookeeper-service-N.zookeeper-service.namespace.svc.cluster.local:2888:3888;2181 

联想到官方镜像提供的一个docker-compose编排脚本中的ZOO_SERVERS环境变量格式,我们很容易联想到,在StatefulSet的环境变量配置中,将ZOO_SERVERS设置成为

zookeeper-service-N.zookeeper-service.namespace.svc.cluster.local:2888:3888;2181

问题来了,没法对单个POD设置这个地址,如果在StatefulSet中设置这个地址,所有的POD又将共享同一地址,没法启动集群。因此我们需要做改造

3. 适用于K8S的镜像改造

最容易想到的还是设法修改启动脚本,让每个POD在启动时候,拥有不同的参数来创建这个ZOO_SERVERS和ZOO_MY_ID。我们可以编写如下的脚本:

#!/bin/bash

#MINE_MIDWARE_NAMESPACE=ns-mine-midware
#MINE_ZOOKEEPER_SERVICE=mine-zookeeper-service
#MINE_ZOOKEEPER_REPLICAS=3
#MINE_ZOOKEEPER_POD=mine-zookeeper-service

echo ${MINE_MIDWARE_NAMESPACE}   #namespace
echo ${MINE_ZOOKEEPER_SERVICE}   #service
echo ${MINE_ZOOKEEPER_REPLICAS}  #replica count
echo ${MINE_ZOOKEEPER_POD}       #podname

ZK_HOSTNAME=$(hostname)

# 解析格式如下:"mine-zookeeper-service-2"
ZK_ID=$( echo ${T_HOSTNAME} | cut -d "-" -f4 | cut -d "-" -f3 )
ZK_HOST_POSTFIX="$MINE_ZOOKEEPER_SERVICE.$MINE_MIDWARE_NAMESPACE.svc.cluster.local"

if [[ -n "${MINE_ZOOKEEPER_REPLICAS-}" ]]
then
  i=0
  while [[ ${i} -lt ${MINE_ZOOKEEPER_REPLICAS} ]]; do
    tmp=""
    idx=$(($i+1))
    if [[ ${i} -eq ${T_ID} ]]; then
      tmp="server.$idx=0.0.0.0:2888:3888;2181 "
    else
      tmp="server.$idx=$MINE_ZOOKEEPER_POD-$i.$ZK_HOST_POSTFIX:2888:3888;2181 "
    fi
    ZOOKEEPER_SERVERS="$ZOOKEEPER_SERVERS$tmp"
    i=$(( i + 1 ))
  done
fi

ZOOKEEPER_SERVERS=${ZOOKEEPER_SERVERS%?}

export ZOO_MY_ID=$(($T_ID+1))
export ZOO_SERVERS=${ZOOKEEPER_SERVERS}

echo ${ZOO_MY_ID}
echo ${ZOO_SERVERS}

以上的脚本通过四个输入的环境变量

MINE_MIDWARE_NAMESPACE
MINE_ZOOKEEPER_SERVICE
MINE_ZOOKEEPER_REPLICAS
MINE_ZOOKEEPER_POD

来创建ZOO_SERVERS和ZOO_MY_ID。这四个环境变量可以配置在StatefulSet中。创建了这个脚本之后,我们再扩展一下官方的Dockerfile:

FROM zookeeper:3.5
MAINTAINER simon.zhu.chn@hotmail.com

ADD zk-config.sh /

ENTRYPOINT ["/bin/bash", "-c", "source zk-config.sh && /docker-entrypoint.sh"]

这样子我们就能再启动docker-entrypoint之前优先将环境变量输出提供给官方镜像使用。

4. 微该官方镜像

构建完镜像之后在K8S中启动集群,发现无法正常启动。
具体原因是因为官方镜像脚本的docker-entrypoint.sh最后一行中引用了CMD中的启动参数。因为我们已经修改了ENTRYPIONT,所以,官方镜像的启动配置的docker-entrypoint.sh也需要做修改。

首先我们去git上拉取官方的镜像源码,然后修改docker-entrypoint.sh脚本

#!/bin/bash

set -e

# Allow the container to be started with `--user`
if [[ "$1" = 'zkServer.sh' && "$(id -u)" = '0' ]]; then
    chown -R zookeeper "$ZOO_DATA_DIR" "$ZOO_DATA_LOG_DIR" "$ZOO_LOG_DIR" "$ZOO_CONF_DIR"
    exec gosu zookeeper "$0" "$@"
fi

# Generate the config only if it doesn't exist
if [[ ! -f "$ZOO_CONF_DIR/zoo.cfg" ]]; then
    CONFIG="$ZOO_CONF_DIR/zoo.cfg"

    echo "clientPort=2181" >> "$CONFIG"
    echo "dataDir=$ZOO_DATA_DIR" >> "$CONFIG"
    echo "dataLogDir=$ZOO_DATA_LOG_DIR" >> "$CONFIG"

    echo "tickTime=$ZOO_TICK_TIME" >> "$CONFIG"
    echo "initLimit=$ZOO_INIT_LIMIT" >> "$CONFIG"
    echo "syncLimit=$ZOO_SYNC_LIMIT" >> "$CONFIG"

    echo "autopurge.snapRetainCount=$ZOO_AUTOPURGE_SNAPRETAINCOUNT" >> "$CONFIG"
    echo "autopurge.purgeInterval=$ZOO_AUTOPURGE_PURGEINTERVAL" >> "$CONFIG"
    echo "maxClientCnxns=$ZOO_MAX_CLIENT_CNXNS" >> "$CONFIG"

    echo $ZOO_SERVERS

    for server in $ZOO_SERVERS; do
        echo ${server}
        echo "$server" >> "$CONFIG"
    done
fi

# Write myid only if it doesn't exist
if [[ ! -f "$ZOO_DATA_DIR/myid" ]]; then
    echo "${ZOO_MY_ID:-1}" > "$ZOO_DATA_DIR/myid"
fi

#注释掉了下面的这行,因为CMD方式启动出错
#exec "$@" 
#检查配置写入情况
cat $CONFIG
#添加这一行,直接使用脚本
zkServer.sh start-foreground

修改完之后,我们重新构建官方镜像:

docker build -t szzookeeper:3.5 .

构建完毕之后再次修改我们自定义构建镜像的文件,将引用镜像设置为我们刚构建好的小改官方镜像:

FROM szzookeeper:3.5
MAINTAINER simon.zhu.chn@hotmail.com

ADD zk-config.sh /

ENTRYPOINT ["/bin/bash", "-c", "source /zk-config.sh && /docker-entrypoint.sh"]

构建,提交至docker hub.

5. K8S启动ZooKeeper集群效果

这里我用Rancher搭建了K8S的两台机器集群,可以看到POD分布在了不同的NODE上

image.png

进入任意一个POD,使用命令查看ZooKeeper集群状态:可以看到这是一个follower节点

6. 待更新,挂载持久卷

上一篇 下一篇

猜你喜欢

热点阅读