# 部署
# 版本
- v3.4.0
- 于 2011 年发布。
- 增加 autopurge 配置参数,用于自动清理数据目录。
- v3.5.0
- 于 2014 年发布。
- 增加 AdminServer 模块,通过内置的 Jetty 服务器提供 HTTP API 。
- 比如访问 URL
/commands
可获取可用的命令列表,访问 URL/commands/stats
可获取 zk 的状态。 - 建议用 AdminServer 代替以前的四字母命令。
- 比如访问 URL
- v3.6.0
- 于 2020 年发布。
- 增加 sessionRequireClientSASLAuth 配置参数,强制客户端进行 SASL 认证。
- v3.7.0
- 于 2021 年发布。
- 增加 enforeQuota 配置参数,让 quota 具有强制性。
# 部署
下载二进制版:
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz
解压后运行:
bin/zkServer.sh start # 在后台启动 start-foreground # 在前台启动 stop restart status # 显示状态 version # 显示版本信息
或者用 docker-compose 部署:
version: '3' services: zookeeper: container_name: zookeeper image: zookeeper:3.7.0 restart: unless-stopped environment: ZOO_MY_ID: 1 # 该 zk 的编号,会自动写入 $dataDir/myid 文件 # JVMFLAGS: -Xmx1G -Xms1G ports: - 2181:2181 - 2888:2888 - 3888:3888 # - 8080:8080 volumes: - ./conf:/conf - ./data:/data
# 配置
配置文件 conf/zoo.cfg
示例:
# clientPort=2181 # 监听一个供客户端连接的端口
dataDir=/data # 数据快照的存储目录
# dataLogDir=/datalog # 事务日志的存储目录,默认与 dataDir 一致。可采用不同的磁盘设备,从而避免竞争磁盘 IO ,提高 zk 的速度、吞吐量
# snapCount=100000 # 记录多少条事务日志时就保存一次快照。实际上会根据随机数提前保存快照,避免多个 zk 节点同时保存快照
autopurge.purgeInterval=24 # 每隔 n 小时,自动清理一次快照文件、事务日志文件。默认值为 0 ,禁用该功能
# autopurge.snapRetainCount=3 # 每次 autopurge 时,每种文件只保留 Count 数量。默认值、最小值都为 3
# tickTime=2000 # 时钟间隔,用作 zk 的基本时间单位,单位为 ms 。也是向其它 server、client 发送心跳包的时间间隔,而 TCP 会话超时是 2*tickTime
# initLimit=5 # 各个 server 初始化连接到 leader 的超时时间,单位为 tickTime
# syncLimit=2 # 各个 server 与 leader 之间通信(请求、回复)的超时时间,单位为 tickTime 。超过该时间则视作失去同步
# admin.enableServer=true # 是否启用 AdminServer
# admin.serverPort=8080 # AdminServer 监听的端口
# 4lw.commands.whitelist=srvr,stat # 一个白名单,声明允许使用哪些四字母命令,可通过 telnet 连接发送命令
metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider # 启用 Prometheus Metrics Provider
# metricsProvider.httpPort=7000
# 声明 zk 集群的 server 列表,每行的格式为 server.<id>=<host>:<port1>:<port2>[:role];[<external_host>:]<external_port>
# - id 是一个数字编号,不可重复
# - host 是各个 server 的 IP 地址
# - port1 是各个 server 连接到 leader 的目标端口,port2 是各个 server 之间进行 leader 选举的端口
# - [<external_host>:]<external_port> 是另一个监听的 Socket ,供客户端访问
# 例如:当前 server id 为 1 时,会根据 server.1 的配置来监听 Socket ,根据其它 server 的配置去通信
server.1=10.0.0.1:2888:3888;2181 # 对于当前 server 而言,该 IP 地址会用于绑定 Socket ,可改为 0.0.0.0
server.2=10.0.0.2:2888:3888;2181
server.3=10.0.0.3:2888:3888;2181
- zk server 的目录结构示例:
├── conf │ ├── configuration.xsl │ ├── log4j.properties │ └── zoo.cfg └── data ├── myid └── version-2 ├── acceptedEpoch # 该文件记录上一次接受的 NEWEPOCH 消息的 epoch 编号。下一次收到 NEWEPOCH 消息时,其 epoch 编号必须更大,才会接受 ├── currentEpoch # 该文件记录上一次接受的 NEWLEADER 消息的 epoch 编号。下一次收到 NEWLEADER 消息时,其 epoch 编号必须更大,才会接受 ├── log.100000001 # 事务日志 ├── log.200000001 ├── log.2000004b3 ├── snapshot.0 # 数据快照 ├── snapshot.100000000 ├── snapshot.100000230 └── snapshot.2000004b2
- 每个 zk server 启动时,会根据
$dataDir/myid
文件的值确定自己的 server 编号。 - 当 zk server 启动时,如果 dataDir 目录不存在,它会自动创建该目录,导致使用错误的 myid 、空的 znode ,可能发生脑裂。
- 每个 zk server 启动时,会根据
- 如果想将一个 server 声明为 observer 角色,需要在其配置文件中加入:然后在所有 server 的配置文件中声明:
peerType=observer
server.1:1=10.0.0.1:2888:3888;2181:observer
# ACL
znode 默认允许任何客户端读写。可以给单个 znode 设置 ACL 规则,控制其访问权限。
- znode 的 ACL 规则不支持递归设置,也不支持继承,因此并不方便,不建议使用。
相关命令:
getAcl <path> # 读取 znode 的 ACL 规则 setAcl <path> <acl> # 设置 znode 的 ACL 规则(只能设置一次,再次设置则不生效) -R # 递归处理子节点 addauth <scheme> <user:pwd> # 创建用户
ACL 规则的格式为
scheme:user:permissions
- scheme 表示认证模式,分为以下几种:
world # 只定义了 anyone 用户,表示所有客户端,包括未登录的 auth # 通过 addauth digest 创建的用户 digest # 与 auth 类似,但需要以哈希值形式输入密码,格式为 digest:<user>:<pwd_hash>:<permissions> ip # 限制客户端的 IP 地址,比如 ip:10.0.0.0/8:r sasl # 要求客户端通过 kerberos 的 SASL 认证 super # 超级管理员,需要在 zk server 的启动命令中声明
- permissions 是一组权限的缩写:
Admin # 允许设置 ACL Create # 允许创建子节点 Delete # 允许删除当前节点 Read # 允许读取 Write # 允许写
- scheme 表示认证模式,分为以下几种:
例:
[zk: localhost:2181(CONNECTED) 0] getAcl /test # 新建 znode 的 ACL 不会继承父节点,而是默认为 `world:anyone:cdrwa` 'world,'anyone : cdrwa [zk: localhost:2181(CONNECTED) 1] setAcl /test world:anyone:a [zk: localhost:2181(CONNECTED) 2] get /test Insufficient permission : /test # 报错表示权限不足 [zk: localhost:2181(CONNECTED) 3] addauth digest tester:123456 # 创建用户,如果已存在该用户则是登录 [zk: localhost:2181(CONNECTED) 4] setAcl /test auth:tester:cdrwa [zk: localhost:2181(CONNECTED) 5] getAcl /test 'digest,'tester:Sc9QxOxG72+Wzo/j15TxX5UOqQs= # 密码以哈希值形式保存 : cdrwa
- 创建一个新终端时,再次执行 addauth 命令,就会登录指定用户。
# SASL
zk server 支持通过 JAAS 框架启用 SASL 认证。
- 默认不要求身份认证,可以被其它 server、client 直接连接,因此不安全。
- 可启用以下 SASL 认证机制:
- DIGEST-MD5
- Kerberos
例:启用 DIGEST-MD5 认证
在 zoo.cfg 中加入:
quorum.auth.enableSasl=true # server 之间连接时是否启用 SASL 认证。默认为 false quorum.auth.learnerRequireSasl=true quorum.auth.serverRequireSasl=true # 强制要求其它 server 连接到当前 server 时进行 SASL 认证。默认为 false authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider # 在被 client 连接时启用 SASL 认证 sessionRequireClientSASLAuth=true # 强制要求 client 连接到当前 server 时进行 SASL 认证。默认为 false
- 如果启用了客户端 SASL 认证,但不强制要求认证,则未通过认证的客户端依然可以访问 zk ,读写数据。除非节点设置了 ACL 规则,只允许 SASL 用户访问。
- 如果强制要求认证,而客户端未进行认证,则 zk 会拒绝连接,并记录报错日志:
Client authentication scheme(s) [ip] does not match with any of the expected authentication scheme [sasl], closing session.
创建一个 jaas.conf 配置文件:
# 定义一些用户。在当前 server 被其它 server 连接时,允许对方使用这些用户 QuorumServer { org.apache.zookeeper.server.auth.DigestLoginModule required # user_test="******" # 按 user_<NAME>=<PASSWORD> 的格式定义用户 user_server="******"; }; # 指定当前 server 连接其它 server 时使用的用户 QuorumLearner { org.apache.zookeeper.server.auth.DigestLoginModule required username="server" password="******"; }; # 定义一些用户。在当前 server 被 client 连接时,允许对方使用这些用户 Server { org.apache.zookeeper.server.auth.DigestLoginModule required user_client="******"; };
将 jaas.conf 拷贝到每个 zk 的配置目录下,并添加 java 启动参数来启用它:
export SERVER_JVMFLAGS="-Djava.security.auth.login.config=/conf/jaas.conf"
客户端连接 zk 时,需要使用以下 JAAS 配置文件:
Client { org.apache.zookeeper.server.auth.DigestLoginModule required username="client" password="******"; };
可以声明以下环境变量,再执行 zkCli.sh ,试试能否作为客户端连接 zk :
export CLIENT_JVMFLAGS="-Djava.security.auth.login.config=/conf/jaas.conf"