最近在做一個演示專案 https://github.com/cnscud/cavedemo, 自然為了方便, 也做了docker打包, 發現zookeeper的映象沒有匯入初始化資料的功能, 於是自己做了一個映象, 還是蠻簡單的.

映象已經發布在 https://hub.docker.com/r/cnscud/zookeeper 官方倉庫.

使用方法1: 命令列直接執行

沒引數的話和官方映象一樣, 也可以指定引數, 建議用docker-compose.

  1. docker run --name zk1 -d -p 2181:2181 cnscud/zookeeper:zk3.6-0.1

使用docker-compose, 方便設定引數

下面是一個例子: samples/docker-compose1/docker-compose.yml:

  1. version: '3'
  2. services:
  3. cnscud-zookeeper:
  4. image: cnscud/zookeeper:zk3.6-0.1
  5. volumes:
  6. - ./init.data:/init.data/
  7. - ./rundata/data:/data
  8. - ./rundata/logs:/datalog
  9. container_name: cnscud-sample-zk1
  10. environment:
  11. ZOOKEEPER_CLIENT_PORT: 2181
  12. ZOOKEEPER_TICK_TIME: 2000
  13. ports:
  14. - "2181:2181"
  15. restart: on-failure
  16. ## use the hostname in project
  17. hostname: cnscud-sample-zk1.cnscud.com

你可以在 samples/docker-compose1 目錄下發現所需的所有檔案: https://github.com/cnscud/cnscud-docker/tree/main/docker-zookeeper/samples/docker-compose1

初始化資料使用說明

我們可以看到, 在volume裡綁定了一個 /init.data/ 目錄, 那麼還有什麼特定規則哪?

需要準備一個 指令碼檔案 "import.data.zk.sh", 放在這個目錄裡, 用來初始化資料, 至於檔案內容, 就根據你的需要來編寫了.

下面是一個例子, 是從檔案裡讀取節點內容, 並設定到zookeeper的節點上去.

  1. #!/bin/bash
  2. ## for init zookeeper data, you need update this file.
  3. ##
  4. ## author: felix zhang https://github.com/cnscud/ 2021.8.22
  5. ##
  6. ## please make sure the file 755
  7. ##
  8. CMD=`which zkCli.sh`
  9. find="1"
  10. if [ -z $CMD ]
  11. then
  12. find="0"
  13. fi
  14. if [ $find = "0" ]
  15. then
  16. CMD="$ZK_HOME/bin/zkCli.sh"
  17. fi
  18. echo $CMD
  19. if [ -z $CMD ]
  20. then
  21. echo "not found zkCli.sh, please check!!!"
  22. exit 1
  23. fi
  24. $CMD create /xpower "1"
  25. $CMD create /xpower/cache "1"
  26. $CMD create /xpower/config "1"
  27. $CMD create /xpower/dbn "1"
  28. ## read from file!!!
  29. ## read from file!!!
  30. ## read from file!!!
  31. $CMD create /xpower/cache/redis.test "`cat /init.data/redis.test.conf`"
  32. $CMD create /xpower/config/kafka "`cat /init.data/kafka.conf`"
  33. $CMD create /xpower/dbn/cavedemo "`cat /init.data/mysql.cavedemo.conf`"

我們可以看到這個指令碼做了幾件事:

  • 查詢zkCli.sh, 並設定正確的路徑
  • 建立各級節點
  • 建立最終的節點, 節點的內容是從檔案讀取的, 可以支援換行和雙引號等, 要特別注意這個語法.

在這個腳本里, 使用zookeeper自帶的zkCli.sh 指令碼建立你的節點, 可以通過下面的命令從檔案讀入節點內容:

"cat /init.data/kafka.conf"

要注意這個腳本里用到的檔案要實現放到統一目錄下, 或者其他繫結的目錄下.

更多配置選項

  1. 因為繼承的是官方的zookeeper docker映象, 所以更多資訊可以參考 https://hub.docker.com/_/zookeeper.

原理分析

我們用 docker inspect 或者直接檢視git倉庫可以看到官方docker的設定:

  1. ENTRYPOINT ["/docker-entrypoint.sh"]
  2. CMD ["zkServer.sh", "start-foreground"]

如果我們需要匯入資料, 則需要先後臺啟動zookeeper, 執行匯入操作, 然後重啟zookeeper, 不過要放在foreground方式啟動.

看看這個重寫的dockerfile:

  1. FROM zookeeper:3.6
  2. ##
  3. ## docker image for zookeeper with init data feature.
  4. ##
  5. ## author: Felix Zhang<cnscud@gmail.com> 2021.8.23
  6. ## website: https://github.com/cnscud/cnscud-docker
  7. LABEL OG=cnscud.com
  8. LABEL maintainer="Felix Zhang<cnscud@gmail.com>"
  9. LABEL reference="https://github.com/cnscud/cnscud-docker"
  10. ## my entrypoint script
  11. ADD cnscud-docker-entrypoint.sh /
  12. ## timezone
  13. ENV TZ=Asia/Shanghai
  14. RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
  15. ## declare the volumen for init data
  16. VOLUME /init.data
  17. ## declare the port
  18. EXPOSE 2181
  19. ## ===================================
  20. ## from official zookeeper as reference
  21. # ENTRYPOINT ["/docker-entrypoint.sh"]
  22. # CMD ["zkServer.sh" "start-foreground"]
  23. ENTRYPOINT ["/cnscud-docker-entrypoint.sh"]
  24. CMD ["zkServer.sh","start-foreground"]

裡面重新聲明瞭ENTRYPOINT, 換成了自定義版本的 cnscud-docker-entrypoint.sh. 裡面還聲明瞭VOLUME /init.data .

當然我們儘量複用原有的指令碼, 而不是改的面目全非(畢竟那麼亂...), 我們來看看:

  1. #!/bin/bash
  2. ## script for support import data with zookeeper
  3. ## author: felix zhang 2021.8.22
  4. ## please make sure the file 755
  5. # set -e
  6. initedfile="/data/zk.cnscud.inited"
  7. initcmdfile='/init.data/import.data.zk.sh'
  8. needcallinit="Y"
  9. #
  10. if [ ! -f "$initedfile" ]
  11. then
  12. needcallinit="Y${needcallinit}"
  13. else
  14. echo "[cnscud] data had inited, will not init again."
  15. fi
  16. if [ -f "$initcmdfile" ]
  17. then
  18. needcallinit="Y${needcallinit}"
  19. else
  20. echo "[cnscud] not found script for init data, skip."
  21. fi
  22. echo "check needcallinit is: $needcallinit"
  23. ## start my import data ====================
  24. if [ $needcallinit = "YYY" ]
  25. then
  26. ## start in backgroup by original entry point
  27. /docker-entrypoint.sh zkServer.sh start
  28. ## waiting for zookeeper
  29. sleep 10
  30. echo "[cnscud] call init data now..."
  31. date > "$initedfile"
  32. ##import data
  33. sh $initcmdfile >> $initedfile
  34. /docker-entrypoint.sh zkServer.sh stop
  35. ## mark
  36. echo "[cnscud] mark: init was finished!"
  37. date >> "$initedfile"
  38. echo "done" >> "$initedfile"
  39. fi
  40. ## end my import data ====================
  41. echo "[cnscud] Starting Zookeeper in foreground mode..."
  42. /docker-entrypoint.sh $@

我們可以看到這個程式碼其實很簡單, 雖然很多行:

  • 首選判斷是否已經執行過, 用 /data/zk.cnscud.inited 檔案方式來標記
  • 如果沒有初始化過, 則後臺方式啟動zkServer
  • 執行初始化指令碼
  • 停止 zkServer
  • 重新按預設引數或傳入的引數啟動zkServer.

感想

其實很簡單, 只不過第一次釋出映象到docker hub, 沒想到社群非常開放, 任何人都能釋出映象上去, 就導致很混亂, 很多映象沒有說明, 沒有人維護, 但下載量很大. 希望不要像npm那麼出現問題.

docker很好, 準備好記憶體和硬碟~!

感謝

感謝google, stackoverflow, withpy 以及我的家人.