1. 程式人生 > >Docker搭建Zookeeper&Kafka叢集

Docker搭建Zookeeper&Kafka叢集

最近在學習Kafka,準備測試叢集狀態的時候感覺無論是開三臺虛擬機器或者在一臺虛擬機器開闢三個不同的埠號都太麻煩了(嗯。。主要是懶)。

環境準備

一臺可以上網且有CentOS7虛擬機器的電腦

為什麼使用虛擬機器?因為使用的筆記本,所以每次連線網路IP都會改變,還要總是修改配置檔案的,過於繁瑣,不方便測試。(通過Docker虛擬網路的方式可以避免此問題,當時實驗的時候沒有了解到)

Docker 安裝

如果已經安裝Docker請忽略此步驟

  1. Docker支援以下的CentOS版本:
  2. CentOS 7 (64-bit):要求系統為64位、系統核心版本為 3.10 以上。
  3. CentOS 6.5(64-bit)或更高的版本:要求系統為64位、系統核心版本為 2.6.32-431 或者更高版本。
  4. CentOS 僅發行版本中的核心支援 Docker。

yum安裝

Docker 要求 CentOS 系統的核心版本高於 3.10 ,檢視上文的前提條件來驗證你的CentOS 版本是否支援 Docker 。

# 檢視核心版本
$ uname -a
#安裝 Docker
$ yum -y install docker
#啟動 Docker 後臺服務
$ service docker start
# 由於本地沒有hello-world這個映象,所以會下載一個hello-world的映象,並在容器內執行。
$ docker run hello-world

指令碼安裝

  1. 使用 sudo 或 root 許可權登入 Centos。
  2. 確保 yum 包更新到最新。
$ sudo yum update
  1. 獲取並執行 Docker 安裝指令碼。
$ curl -fsSL https://get.docker.com -o get-docker.sh
# 執行這個指令碼會新增 docker.repo 源並安裝 Docker。
$ sudo sh get-docker.sh

啟動Docker

$ sudo systemctl start docker
# 驗證 docker 是否安裝成功並在容器中執行一個測試的映象。
$ sudo docker run hello-world
$ docker ps

映象加速

開始讓我配置國內映象源的時候我是拒絕的,但是使用之後發現那下載速度 duang~

的一下就上去了。所以強烈建議大家配置國內映象源。
開啟/建立 /etc/docker/daemon.json 檔案,新增以下內容:

{
  "registry-mirrors": ["http://hub-mirror.c.163.com"]
}

Zookeeper叢集搭建

Zookeeper映象:zookeeper:3.4

映象準備

$ docker pull zookeeper:3.4

查詢映象可以去 https://hub.docker.com/
docker pull images:TAG // 代表拉取 TAG 版本的 image 映象

建立獨立Zookeeper容器

我們首先用最簡單的方式建立一個獨立的Zookeeper節點,然後我們根據這個例子創建出其他的節點。

$ docker run --name zookeeper -p 2181:2181 -d zookeeper:3.4

預設的,容器內配置檔案在, /conf/zoo.cfg,資料和日誌目錄預設在 /data/datalog,需要的話可以將上述目錄對映到宿主機。
引數解釋

  1. --name:指定容器名字
  2. -p:為容器暴露出來的埠分配埠號
  3. -d:在後臺執行容器並列印容器ID

叢集搭建

其它節點的Zookeeper容器建立方式與建立獨立容器類似,需要注意的是,要分別指定節點的id和修改檔案中多節點的配置,相應的建立命令如下:

新建docker網路

$ docker network create zoo_kafka
$ docker network ls

Zookeeper容器1

$ docker run -d \
     --restart=always \
     -v /opt/docker/zookeeper/zoo1/data:/data \
     -v /opt/docker/zookeeper/zoo1/datalog:/datalog \
     -e ZOO_MY_ID=1 \
     -p 2181:2181 \
     -e ZOO_SERVERS="server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888" \
     --name=zoo1 \
     --net=viemall-zookeeper \
     --privileged \
     zookeeper:3.4

Zookeeper容器2

$ docker run -d \
     --restart=always \
     -v /opt/docker/zookeeper/zoo2/data:/data \
     -v /opt/docker/zookeeper/zoo2/datalog:/datalog \
     -e ZOO_MY_ID=2 \
     -p 2182:2181 \
     -e ZOO_SERVERS="server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888" \
     --name=zoo2 \
     --net=viemall-zookeeper \
     --privileged \
     zookeeper:3.4

Zookeeper容器3

$ docker run -d \
     --restart=always \
     -v /opt/docker/zookeeper/zoo3/data:/data \
     -v /opt/docker/zookeeper/zoo3/datalog:/datalog \
     -e ZOO_MY_ID=3 \
     -p 2183:2181 \
     -e ZOO_SERVERS="server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888" \
     --name=zoo3 \
     --net=viemall-zookeeper \
     --privileged \
     zookeeper:3.4

這種方式雖然也實現了我們想要的,但是步驟過於繁瑣,而且維護起來麻煩(懶癌晚期),所以我們使用 docker-compose 的方式來實現。

docker-compose 搭建zookeeper叢集

新建docker網路

$ docker network create viemall-zookeeper
$ docker network ls

編寫 docker-compose.yml 指令碼

使用方式:

  1. 安裝 docker-compose
# 獲取指令碼
$ curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 賦予執行許可權
$chmod +x /usr/local/bin/docker-compose
  1. 任意目錄下新建 docker-compose.yml 檔案,複製以下內容
  2. 執行命令 docker-compose up -d

命令對照
|命令|解釋|
|-|-|
|docker-compose up|啟動所有容器|
|docker-compose up -d|後臺啟動並執行所有容器|
|docker-compose up --no-recreate -d|不重新建立已經停止的容器|
|docker-compose up -d test2|只啟動test2這個容器|
|docker-compose stop|停止容器|
|docker-compose start|啟動容器|
|docker-compose down|停止並銷燬容器|

docker-compose.yml下載地址:https://github.com/JacianLiu/docker-compose/tree/master/zookeeper
docker-compose.yml詳情

version: '2'
services:
  zoo1:
    image: zookeeper:3.4 # 映象名稱
    restart: always # 當發生錯誤時自動重啟
    hostname: zoo1
    container_name: zoo1
    privileged: true
    ports: # 埠
      - 2181:2181
    volumes: # 掛載資料卷
      - ./zoo1/data:/data
      - ./zoo1/datalog:/datalog 
    environment:
      TZ: Asia/Shanghai
      ZOO_MY_ID: 1 # 節點ID
      ZOO_PORT: 2181 # zookeeper埠號
      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888 # zookeeper節點列表
    networks:
      default:
        ipv4_address: 172.23.0.11

  zoo2:
    image: zookeeper:3.4
    restart: always
    hostname: zoo2
    container_name: zoo2
    privileged: true
    ports:
      - 2182:2181
    volumes:
      - ./zoo2/data:/data
      - ./zoo2/datalog:/datalog
    environment:
      TZ: Asia/Shanghai
      ZOO_MY_ID: 2
      ZOO_PORT: 2181
      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
    networks:
      default:
        ipv4_address: 172.23.0.12

  zoo3:
    image: zookeeper:3.4
    restart: always
    hostname: zoo3
    container_name: zoo3
    privileged: true
    ports:
      - 2183:2181
    volumes:
      - ./zoo3/data:/data
      - ./zoo3/datalog:/datalog
    environment:
      TZ: Asia/Shanghai
      ZOO_MY_ID: 3
      ZOO_PORT: 2181
      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
    networks:
      default:
        ipv4_address: 172.23.0.13

networks:
  default:
    external:
      name: zoo_kafka

驗證

從圖中我們可以看出,有一個Leader,兩個Flower,至此我們的Zookeeper叢集就已經搭建好了

Kafka叢集搭建

有了上面的基礎,再去搞Kafka叢集還是問題嗎?其實就是幾個變數值不同而已。

有了上邊的例子,就不費勁去搞單節點的Kafka了,直接使用docker-compose的方式,部署三個節點,其實方式大同小異,上邊也說到,其實就是一些屬性不同而已;這時候我們就不需要再去新建 Docker 網路了,直接使用前邊搭建 Zookeeper 叢集時建立的網路即可!

環境準備

Kafka映象:wurstmeister/kafka
Kafka-Manager映象:sheepkiller/kafka-manager

# 不指定版本預設拉取最新版本的映象
docker pull wurstmeister/kafka
docker pull sheepkiller/kafka-manager

編寫 docker-compose.yml 指令碼

使用方式:

  1. 安裝 docker-compose
# 獲取指令碼
$ curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 賦予執行許可權
$chmod +x /usr/local/bin/docker-compose
  1. 任意目錄下新建 docker-compose.yml 檔案,複製以下內容
  2. 執行命令 docker-compose up -d

命令對照
|命令|解釋|
|-|-|-|
|docker-compose up|啟動所有容器|
|docker-compose up -d|後臺啟動並執行所有容器|
|docker-compose up --no-recreate -d|不重新建立已經停止的容器|
|docker-compose up -d test2|只啟動test2這個容器|
|docker-compose stop|停止容器|
|docker-compose start|啟動容器|
|docker-compose down|停止並銷燬容器|

docker-compose.yml下載地址:https://github.com/JacianLiu/docker-compose/tree/master/zookeeper
docker-compose.yml詳細內容

version: '2'

services:
  broker1:
    image: wurstmeister/kafka
    restart: always
    hostname: broker1
    container_name: broker1
    privileged: true
    ports:
      - "9091:9092"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_LISTENERS: PLAINTEXT://broker1:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker1:9092
      KAFKA_ADVERTISED_HOST_NAME: broker1
      KAFKA_ADVERTISED_PORT: 9092
      KAFKA_ZOOKEEPER_CONNECT: zoo1:2181/kafka1,zoo2:2181/kafka1,zoo3:2181/kafka1
      JMX_PORT: 9988
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./broker1:/kafka/kafka\-logs\-broker1
    external_links:
    - zoo1
    - zoo2
    - zoo3
    networks:
      default:
        ipv4_address: 172.23.0.14

  broker2:
    image: wurstmeister/kafka
    restart: always
    hostname: broker2
    container_name: broker2
    privileged: true
    ports:
      - "9092:9092"
    environment:
      KAFKA_BROKER_ID: 2
      KAFKA_LISTENERS: PLAINTEXT://broker2:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker2:9092
      KAFKA_ADVERTISED_HOST_NAME: broker2
      KAFKA_ADVERTISED_PORT: 9092
      KAFKA_ZOOKEEPER_CONNECT: zoo1:2181/kafka1,zoo2:2181/kafka1,zoo3:2181/kafka1
      JMX_PORT: 9988
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./broker2:/kafka/kafka\-logs\-broker2
    external_links:  # 連線本compose檔案以外的container
    - zoo1
    - zoo2
    - zoo3
    networks:
      default:
        ipv4_address: 172.23.0.15

  broker3:
    image: wurstmeister/kafka
    restart: always
    hostname: broker3
    container_name: broker3
    privileged: true
    ports:
      - "9093:9092"
    environment:
      KAFKA_BROKER_ID: 3
      KAFKA_LISTENERS: PLAINTEXT://broker3:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker3:9092
      KAFKA_ADVERTISED_HOST_NAME: broker3
      KAFKA_ADVERTISED_PORT: 9092
      KAFKA_ZOOKEEPER_CONNECT: zoo1:2181/kafka1,zoo2:2181/kafka1,zoo3:2181/kafka1
      JMX_PORT: 9988
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./broker3:/kafka/kafka\-logs\-broker3
    external_links:  # 連線本compose檔案以外的container
    - zoo1
    - zoo2
    - zoo3
    networks:
      default:
        ipv4_address: 172.23.0.16

  kafka-manager:
    image: sheepkiller/kafka-manager:latest
    restart: always
    container_name: kafka-manager
    hostname: kafka-manager
    ports:
      - "9000:9000"
    links:            # 連線本compose檔案建立的container
      - broker1
      - broker2
      - broker3
    external_links:   # 連線本compose檔案以外的container
      - zoo1
      - zoo2
      - zoo3
    environment:
      ZK_HOSTS: zoo1:2181/kafka1,zoo2:2181/kafka1,zoo3:2181/kafka1
      KAFKA_BROKERS: broker1:9092,broker2:9092,broker3:9092
      APPLICATION_SECRET: letmein
      KM_ARGS: -Djava.net.preferIPv4Stack=true
    networks:
      default:
        ipv4_address: 172.23.0.10

networks:
  default:
    external:   # 使用已建立的網路
      name: zoo_kafka

驗證

我們開啟kafka-manager的管理頁面,訪問路徑是,宿主機ip:9000;

如果所示,填寫上Zookeeper叢集的地址,劃到最下邊點選save
點選剛剛新增的叢集,可以看到,叢集中有三個節點

搭建過程中遇到的問題

  1. 掛載資料卷無限重啟,檢視log提示:chown: changing ownership of ‘/var/lib/mysql/....‘: Permission denied
    解決方式:
    • 在docker run中加入 --privileged=true 給容器加上特定許可權
    • 臨時關閉selinux: setenforce 0
    • 新增selinux規則,改變要掛載的目錄的安全性文字
  2. kafka-manager報jmx相關錯誤,
    解決方法:
    • 在每一個kafka節點加上環境變數 JMX_PORT=埠
    • 加上之後發現連不上,又是網路連線的問題,於是又把每個jmx埠暴露出來,然後fire-wall放行, 解決問題。
    • KAFKA_ADVERTISED_HOST_NAME這個最好設定宿主機的ip,宿主機以外的程式碼或者工具來連線,後面的埠也需要設定暴露的埠。
[error] k.m.j.KafkaJMX$ - Failed to connect to service:jmx:rmi:///jndi/rmi://9.11.8.48:-1/jmxrmi java.lang.IllegalArgumentException: requirement failed: No jmx port but jmx polling enabled!
  1. 在容器中檢視topic時報以下錯誤(不僅僅是topic的命令,好像所有的都會出錯)
$ bin/kafka-topics.sh --list --zookeeper zoo1:2181/kafka1,zoo2:2181/kafka1,zoo3:2181/kafka1
# 以下是錯誤
Error: Exception thrown by the agent : java.rmi.server.ExportException: Port already in use: 7203; nested exception is:
        java.net.BindException: Address already in use

解決方法:
在命令前加上unset JMX_PORT;指令,上邊的命令改造為:

$ unset JMX_PORT;bin/kafka-topics.sh --list --zookeeper zoo1:2181/kafka1,zoo2:2181/kafka1,zoo3:2181/kafka1

附:Docker常用指令

# 檢視所有映象
docker images
# 檢視所有執行中的容器
docker ps
# 檢視所有容器
docker ps -a
# 獲取所有容器ip
$ docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)
# 檢視容器內部日誌
$ docker logs -f <容器ID>
# 進入容器內部
$ docker exec -it <容器ID> /bin/basj
# 建立容器 -d代表後臺啟動
docker run --name <容器名稱> -e <引數> -v <掛載資料卷> <容器ID>
# 重啟容器
docker restart <容器ID>
# 關閉容器
docker stop <容器id>
# 執行容器
docker start <容器id>