ES 集群安全加固 & ES9 部署 & Zookeeper 集群

6610 字
33 分钟
ES 集群安全加固 & ES9 部署 & Zookeeper 集群
ES 集群安全加固 & ES9 部署 & Zookeeper 集群

ES 集群安全加固 & ES9 部署 & Zookeeper 集群#

[TOC]


ES7 集群启用 HTTPS#

在上一篇笔记中,我们通过 xpack.security.enabled 在 传输层(9300)启用了 TLS 加密——节点间通信已加密,需要用户名+密码才能访问 API

但 HTTP 层(9200)仍然是明文传输,存在风险

本章用自建 CA 签发证书的方式,为 ES 的 9200 端口启用 HTTPS

自建 CA 并签发证书#

Terminal window
1)自建 CA 证书
root@Elk01 ~# /usr/share/elasticsearch/bin/elasticsearch-certutil ca --out /etc/elasticsearch/elastic-stack-ca.p12 --pass "" --days 36500
--pass "": # 空密码,生产环境建议设密码
--days 36500: # 100 年有效期
root@Elk01 ~# ls -l /etc/elasticsearch/elastic-stack-ca.p12
-rw------- 1 root elasticsearch 2672 Aug 2 09:08 elastic-stack-ca.p12
2)基于自建 CA 生成 ES HTTPS 证书
root@Elk01 ~# /usr/share/elasticsearch/bin/elasticsearch-certutil cert \
--ca /etc/elasticsearch/elastic-stack-ca.p12 \
--out /etc/elasticsearch/elastic-certificates-https.p12 \
--pass "" --days 36500 --ca-pass ""
root@Elk01 ~# ls -l /etc/elasticsearch/elastic-*.p12
-rw------- 1 root elasticsearch 3596 Aug 2 09:08 elastic-certificates-https.p12 # HTTPS
-rw-r----- 1 root elasticsearch 3596 Aug 1 09:31 elastic-certificates.p12 # transport(已有)
-rw------- 1 root elasticsearch 2672 Aug 2 09:08 elastic-stack-ca.p12 # CA
3)修改权限
root@Elk01 ~# chmod 640 /etc/elasticsearch/elastic-certificates-https.p12
# CA 证书只给 root 用(签发新证书时),ES 进程不读它;保持默认的 600 就行
Note

Elasticsearch 进程以 elasticsearch 用户运行,证书文件必须让该用户可读 属主 root、属组 elasticsearch、权限 640 即可

Important

==与 transport 层证书的区别==

transport (9300)http (9200)
证书elastic-certificates.p12elastic-certificates-https.p12
作用节点间加密通信客户端(Kibana/Filebeat/curl)加密通信
配置项xpack.security.transport.ssl.*xpack.security.http.ssl.*
生成方式直接 cert 即可(自签名)需要先建 CA(ca),再基于 CA 签发(cert --ca)
验证模式certificate浏览器/curl 用 -k 跳过

修改 ES 集群配置#

Terminal window
root@Elk01 ~# vim /etc/elasticsearch/elasticsearch.yml
# 在文件末尾追加:
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: elastic-certificates-https.p12
参数作用
xpack.security.http.ssl.enabled启用 HTTP 层 SSL/TLS 加密
xpack.security.http.ssl.keystore.path证书路径(相对于 ES 的 config 目录)

同步配置与证书到其他节点#

Terminal window
1)拷贝证书文件 & 配置文件(三台必须一致)
root@Elk01 ~# scp -p /etc/elasticsearch/elastic{-certificates-https.p12,search.yml} 10.0.0.7:/etc/elasticsearch/
# CA 证书只在签发 HTTPS 证书时用到,签完后其他节点不需要它
root@Elk01 ~# scp -p /etc/elasticsearch/elastic{-certificates-https.p12,search.yml} 10.0.0.8:/etc/elasticsearch/
-p: # 保留权限
2)Elk02/03 确认权限
root@Elk02 ~# ls -l /etc/elasticsearch/elastic-certificates-https.p12
-rw-r----- 1 root elasticsearch 3596 Aug 2 09:08 elastic-certificates-https.p12

重启集群并验证#

Terminal window
1)三台同时重启
root@Elk01 ~# systemctl restart elasticsearch.service
root@Elk02 ~# systemctl restart elasticsearch.service
root@Elk03 ~# systemctl restart elasticsearch.service
2)使用 HTTPS 访问
root@Elk01 ~# curl -s -u elastic:passwd -k https://10.0.0.6:9200/_cat/nodes?v
-s: # 静默,不显示 curl 进度条和连接信息
-k: # 跳过自签名证书校验
ip heap.percent ram.percent cpu node.role master name
10.0.0.7 54 77 24 cdfhilmrstw - Elk02
10.0.0.6 31 92 21 cdfhilmrstw * Elk01
10.0.0.8 72 76 32 cdfhilmrstw - Elk03
✅️ 3 节点在线,HTTPS 已生效
3)普通 HTTP 已不可用
root@Elk01 ~# curl http://10.0.0.6:9200/
curl: (52) Empty reply from server
# HTTP 返回空连接——TLS 已接管 9200
Tip

--insecure 和 -k 等价,都是跳过证书校验 生产环境应该配置客户端信任 CA,而不是用 -k 跳过


Kibana 对接 ES HTTPS 集群#

Terminal window
1)修改 Kibana 配置
root@Elk01 ~# vim /etc/kibana/kibana.yml
server.host: "0.0.0.0"
# 将 elasticsearch.hosts 的协议改为 https,再追加跳过证书校验:
elasticsearch.hosts: ["https://10.0.0.6:9200","https://10.0.0.7:9200","https://10.0.0.8:9200"]
elasticsearch.ssl.verificationMode: none
i18n.locale: "zh-CN" # 中文界面
elasticsearch.username: "kibana_system"
elasticsearch.password: "iYLp0QpWVR0IUnpidbi6"
# elasticsearch.username / elasticsearch.password 保持不变
配置项值说明
elasticsearch.hostshttps://...改为 HTTPS 协议
elasticsearch.ssl.verificationModenone跳过自签名证书校验
Terminal window
2)重启 Kibana
root@Elk01 ~# systemctl restart kibana.service
root@Elk01 ~# ss -ntl | grep 5601
LISTEN 0 511 0.0.0.0:5601 0.0.0.0:*
3)验证状态
root@Elk01 ~# curl -s -u elastic:passwd 'http://10.0.0.6:5601/api/status' | jq '.status.overall.state'
"green"
✅️ Kibana 已通过 HTTPS 对接 ES 集群
# 下图也能说明 --> 成功对接

WebUI 验证
WebUI 验证

Tip

💡 除了命令行创建 api-key,也可以 WebUI 操作:Kibana → Stack Management → 安全 → API密钥 → 创建

如果已启用 HTTPS,api-key 功能默认已开启,无需额外配置 ✅

Filebeat 基于 api-key 认证#

Important

为什么用 api-key 而非用户名/密码?

  • api-key ==不能登录 Kibana==,即使泄露也无法进入管理界面
  • 可以按 api-key 粒度控制索引级权限
  • 可以随时吊销,不影响其他组件
Terminal window
1)创建 api-key(赋予写入 kpyun-apikey-fb* 的权限)
root@Elk01 ~# curl -s -u elastic:passwd -k -X POST 'https://10.0.0.6:9200/_security/api_key' \
-H 'Content-Type: application/json' \
-d '{
"name": "kpyun-apikey-fb",
"role_descriptors": {
"filebeat_writer": {
"cluster": ["monitor", "manage_index_templates", "manage_ilm"],
"index": [{"names": ["kpyun-apikey-fb*"], "privileges": ["create_index", "create_doc", "index", "read"]}]
}
}
}' | jq
# 下面是输出结果
{
"id": "aT1lwZ8BC8dvYVpT5Imd",
"name": "kpyun-apikey-fb",
"api_key": "kyxEBSQmS_aqOvKUJXUORg",
"encoded": "YVQxbHdaOEJDOGR2WVZwVDVJbWQ6a3l4RUJTUW1TX2FxT3ZLVUpYVU9SZw=="
}
Tip

💡 cluster 集群权限选 monitor + manage_index_templates + manage_ilm 就够了:

  • monitor — 查看集群状态
  • manage_index_templates — 加载索引模板(Filebeat 启动时需要)
  • manage_ilm — 管理索引生命周期

💡 索引权限沿用上一篇笔记的 RBAC 组合:create_index、create_doc、index、read

api-key ==不能登录 Kibana==,即使泄露也进不了管理界面,所以权限可以适当放宽

  • ⚠️ 已创建的 api-key ==不能修改权限==,只能新建一个
    • 所以创建前想清楚需要什么权限,避免重复劳动
Terminal window
2)api-key 的三种格式
格式示例用途
beats 格式aT1lwZ8BC8dvYVpT5Imd:kyxEBSQmS_aqOvKUJXUORgFilebeat api_key 字段
logstash 格式aT1lwZ8BC8dvYVpT5Imd:kyxEBSQmS_aqOvKUJXUORgLogstash api_key 字段
base64 格式YVQxbHdaOEJDOGR2WVZwVDVJbWQ6...部分工具 / HTTP Header
JSON 格式{"id":"...","api_key":"..."}原始返回
Tip

💡 beats 格式和 logstash 格式的值本身完全一样(都是 id:api_key)

  • 只是写配置文件时的字段语法不同
    • Filebeat 用 YAML(api_key: "xxx"),Logstash 用 Ruby DSL(api_key => "xxx")
Terminal window
3)解码 base64 验证
root@Elk01 ~# echo YVQxbHdaOEJDOGR2WVZwVDVJbWQ6a3l4RUJTUW1TX2FxT3ZLVUpYVU9SZw== | base64 -d; echo
aT1lwZ8BC8dvYVpT5Imd:kyxEBSQmS_aqOvKUJXUORg
# base64 解码 = id : api_key
4)编写 Filebeat 配置
root@Elk02 ~# vim /etc/filebeat/config/tcp-to-es_api-key.yaml
filebeat.inputs:
- type: tcp
host: "0.0.0.0:9000"
output.elasticsearch:
hosts:
- "https://10.0.0.6:9200"
- "https://10.0.0.7:9200"
- "https://10.0.0.8:9200"
# username: "elastic"
# password: "passwd"
# ↑ 注释掉这两行即可
# 基于api_key方式认证,相比于上面的用户/密码认证更安全(生产环境推荐)
api_key: "aT1lwZ8BC8dvYVpT5Imd:kyxEBSQmS_aqOvKUJXUORg"
index: "kpyun-apikey-fb-%{+yyyy-MM-dd}"
ssl.verification_mode: none # 跳过证书校验
setup.ilm.enabled: false
setup.template.name: "kpyun-apikey-fb"
setup.template.pattern: "kpyun-apikey-fb*"
setup.template.overwrite: false
setup.template.settings:
index.number_of_shards: 3
index.number_of_replicas: 0
5)启动 Filebeat 并发送测试数据
root@Elk02 ~# killall -9 filebeat
root@Elk02 ~# nohup filebeat -e -c /etc/filebeat/config/tcp-to-es_api-key.yaml > /tmp/fb-api-key.log 2>&1 &
root@Elk02 ~# tail -1 /tmp/fb-api-key.log
Connection to backoff(elasticsearch(https://10.0.0.7:9200)) established
root@Elk02 ~# echo 'hello api_key test' | nc -w 1 10.0.0.7 9000
6)验证数据写入
root@Elk01 ~# curl -s -u elastic:passwd -k 'https://10.0.0.6:9200/_cat/indices/kpyun-apikey-fb*?v'
health status index docs.count
green open kpyun-apikey-fb-2026-08-02 1
✅️ Filebeat 通过 api_key 认证 → HTTPS → ES 集群,数据写入成功

Logstash 基于 api-key 访问 ES 集群#

Terminal window
1)创建 Logstash 专用的 api-key
root@Elk01 ~# curl -s -u elastic:passwd -k -X POST 'https://10.0.0.6:9200/_security/api_key' \
-H 'Content-Type: application/json' \
-d '{
"name": "kpyun-apikey-lg",
"role_descriptors": {
"logstash_writer": {
"cluster": ["monitor", "manage_index_templates", "manage_ilm"],
"index": [{"names": ["kpyun-apikey-lg*"], "privileges": ["create_index", "create_doc", "index", "read"]}]
}
}
}' | jq
# 下面是输出结果
{
"id": "az1zwZ8BC8dvYVpTColQ",
"name": "kpyun-apikey-lg",
"api_key": "nJdBvTx4RkypYb7PUI0yTA",
"encoded": "YXoxendaOEJDOGR2WVZwVENvbFE6bkpkQnZUeDRSa3lwWWI3UFVJMHlUQQ=="
}
2)编写 Logstash 配置
root@Elk03 ~# vim /etc/logstash/conf.d/tcp-to-es_api-key.conf
input {
tcp {
port => 8888
}
}
filter {
mutate {
remove_field => [ "@version", "port", "host" ]
}
}
output {
elasticsearch {
hosts => ["https://10.0.0.6:9200","https://10.0.0.7:9200","https://10.0.0.8:9200"]
index => "kpyun-apikey-lg-%{+yyyy-MM-dd}"
# user => "elastic"
# password => "passwd"
# ↑ logstash 用 user/password,不是 username/password
# 同样注释掉
# 指定api-key的方式认证
api_key => "az1zwZ8BC8dvYVpTColQ:nJdBvTx4RkypYb7PUI0yTA"
# 使用api-key则必须启动ssl
ssl => true
# 跳过ssl证书验证
ssl_certificate_verification => false
}
}
3)启动 Logstash 并测试
root@Elk03 ~# logstash -f /etc/logstash/conf.d/tcp-to-es_api-key.conf --config.test_and_exit
Configuration OK
✅️ 配置语法检查通过
root@Elk03 ~# nohup logstash -f /etc/logstash/conf.d/tcp-to-es_api-key.conf > /tmp/ls-api-key.log 2>&1 &
root@Elk03 ~# tail -1 /tmp/ls-api-key.log
:count=>1, :running_pipelines=>[:main]
root@Elk03 ~# echo 'logstash api_key test' | nc -w 1 10.0.0.8 8888
4)验证
root@Elk01 ~# curl -s -u elastic:passwd -k 'https://10.0.0.6:9200/_cat/indices/kpyun-apikey-lg*?v'
health status index docs.count
green open kpyun-apikey-lg-2026-08-02 1
✅️ Logstash 通过 api_key 写入 ES 集群成功
Caution

⚠️ 三组件认证字段名不同

组件认证字段示例
Filebeatusername: / password:username: "elastic"
Logstashuser => / password =>user => "elastic"
Kibanaelasticsearch.username: / elasticsearch.password:elasticsearch.username: "kibana_system"

💡 再加上 api-key 认证时,Filebeat 和 Logstash 都叫 api_key,Kibana 不支持 api-key——容易搞混


ES7 集群优化#

JVM 堆内存调优#

Terminal window
1)查看当前 JVM 堆大小
root@Elk01 ~# ps -ef | grep elasticsearch | egrep "Xmx|Xms"
-Xms512m
-Xmx512m
root@Elk01 ~# free -h | grep Mem
Mem: 1.8Gi 1.4Gi 128Mi 527Mi 416Mi
# 当前是 512m(在 Elk01 时手动设的),内存依然偏紧
2)创建子目录 & 子配置文件
root@Elk01 ~# mkdir -p /etc/elasticsearch/jvm.options.d
⚠️ '每台VM都要创建这个目录'
# ES 的启动脚本会扫描 jvm.options.d/ 并合并其中的内容
# 所以不用改主配置文件(/etc/elasticsearch/jvm.options)
root@Elk01 ~# echo -e "-Xms512m\n-Xmx512m" > /etc/elasticsearch/jvm.options.d/heap.options
3)保持 512m 无需调整
root@Elk01 ~# systemctl restart elasticsearch
`别忘记重启elasticsearch服务`
root@Elk01 ~# egrep 'Xms|Xmx' /etc/elasticsearch/jvm.options.d/heap.options
-Xms512m
-Xmx512m
✅️ `-Xms` 和 `-Xmx` 设成相同值,避免运行时堆扩展带来的性能抖动
Important

📌 ES 默认行为:自动将 JVM 堆设为物理内存的 ==50%==(上限 32GB)

  • 这台 VM 有 2G 内存 → 若没手动设,ES 会吃 ~1GB,系统直接 OOM
  • 所以我们在 Elk01 部署时就手动设了 -Xms512m -Xmx512m

官方建议:

  • 堆内存不要超过 ==32GB==(超过后 JVM 指针压缩失效,性能反而下降)
  • 推荐设 ==26GB== 左右,给 OS 留足文件系统缓存——Lucene 搜索依赖 heap 外的 page cache
  • 生产环境 64G 内存通常设 -Xms31g -Xmx31g

禁用通配符删除索引#

Terminal window
root@Elk01 ~# curl -s -u elastic:passwd -k -X DELETE 'https://10.0.0.6:9200/kpyun-test*'
{"acknowledged":true}
# ES 7.x 版本默认是可以使用通配符全部删除
# ES 8+(包括9.x) 已经默认禁止使用
1)修改配置
root@Elk01 ~# cat >> /etc/elasticsearch/elasticsearch.yml <<EOF
action.destructive_requires_name: true
EOF
# 禁止使用通配符 * 或 _all 删除索引,防止误操作
2)同步到其他节点 + 重启
root@Elk01 ~# scp /etc/elasticsearch/elasticsearch.yml 10.0.0.7:/etc/elasticsearch/
root@Elk01 ~# scp /etc/elasticsearch/elasticsearch.yml 10.0.0.8:/etc/elasticsearch/
root@Elk01 ~# systemctl restart elasticsearch
# 三台同时重启
3)验证
root@Elk01 ~# curl -s -u elastic:passwd -k -X DELETE 'https://10.0.0.6:9200/kpyun-test*'
{"error":{"type":"illegal_argument_exception","reason":"Wildcard expressions or all indices are not allowed"},"status":400}
✅️ 通配符删除已被拦截

关闭自动创建索引#

Terminal window
root@Elk01 ~# curl -s -u elastic:passwd -k -X POST 'https://10.0.0.6:9200/kpyun-test-1/_doc' \
-H 'Content-Type: application/json' -d '{"msg":"test"}' | jq
{
"_index": "kpyun-test-1",
"_type": "_doc",
"_id": "wK5hwp8BkORV4nxJNCYI",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
1)修改配置
root@Elk01 ~# cat >> /etc/elasticsearch/elasticsearch.yml <<EOF
action.auto_create_index: false
EOF
# 禁止自动创建不存在的索引
2)同步 + 重启
root@Elk01 ~# scp /etc/elasticsearch/elasticsearch.yml 10.0.0.7:/etc/elasticsearch/
root@Elk01 ~# scp /etc/elasticsearch/elasticsearch.yml 10.0.0.8:/etc/elasticsearch/
root@Elk01 ~# systemctl restart elasticsearch
# 三台同时重启
3)验证
# 这次是kpyun-test-2
root@Elk01 ~# curl -s -u elastic:passwd -k -X POST 'https://10.0.0.6:9200/kpyun-test-2/_doc' \
-H 'Content-Type: application/json' -d '{"msg":"test"}' | jq
{
"error": {
"root_cause": [
"type": "index_not_found_exception",
"reason": "no such index [kpyun-test-2] and [action.auto_create_index] is [false]",
"index_uuid": "_na_",
"index": "kpyun-test-2"
},
"status": 404
}
✅️ `索引不存在时拒绝写入,而非自动创建`
4)手动创建索引后,方可写入
root@Elk01 ~# curl -s -u elastic:passwd -k -X PUT 'https://10.0.0.6:9200/kpyun-test-2' \
-H 'Content-Type: application/json' | jq
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "kpyun-test-2"
}
root@Elk01 ~# curl -s -u elastic:passwd -k -X POST 'https://10.0.0.6:9200/kpyun-test-2/_doc' \
-H 'Content-Type: application/json' -d '{"msg":"test"}' | jq
{
"_index": "kpyun-test-2",
......
"result": "created",
......
Note
Terminal window
# %{+yyyy-MM-dd} 是 Logstash/Filebeat 的日期占位符,Logstash 输出到 ES 时会自动解析成实际日期
⚠️ 但 curl 和 ES REST API 不认识这个语法
用 shell 的 date 命令生成实际日期字符串:
root@Elk01 ~# curl -s -u elastic:passwd -k -X PUT \
"https://10.0.0.6:9200/kpyun-es-apikey-haha-$(date +%Y-%m-%d)" \
-H "Content-Type: application/json"
# $(date +%Y-%m-%d) 会在 shell 层面被替换为 2026-08-06 这样的实际日期
Important

📌 三条优化总结

优化项配置作用
JVM 堆-Xms256m -Xmx256m防止 OOM,节省内存
禁用通配符删除action.destructive_requires_name: true防止 DELETE /xxx* 误删大量索引
关闭自动创建索引action.auto_create_index: false必须手动创建索引才能写入,防止索引膨胀

ES9 集群部署#

Important

==ES7 vs ES8/9 关键差异==

特性ES7ES8+ / ES9
默认 HTTPS❌ 需手动配置✅ 安装时自动生成 TLS 证书
默认认证❌ 需手动启用 xpack.security.enabled✅ 安装时自动生成 elastic 密码
重置密码elasticsearch-setup-passwords autoelasticsearch-reset-password -u elastic
节点加入配置 discovery.seed_hosts 即可需要 enrollment token(elasticsearch-reconfigure-node)
Kibana 对接手动配 elasticsearch.hosts + 密码用 enrollment token + verification code
索引模式必须先创建索引模式才能看数据无需索引模式,自动生成临时视图

环境准备#

主机IP角色配置
ESnew-0110.0.0.9ES9 + Kibana92C / 4G / 30G
ESnew-0210.0.0.10ES92C / 4G / 30G
ESnew-0310.0.0.11ES92C / 4G / 30G

ES9 单点部署#

Terminal window
1)下载软件包
jiuzhao@Ubuntu ~$ wget -P /home/jiuzhao/下载 https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-9.4.4-amd64.deb
2)拷贝到主机并安装
jiuzhao@Ubuntu ~$ scp elasticsearch-9.4.4-amd64.deb ESnew-01:/tmp
root@ESnew-01 ~# dpkg -i /tmp/elasticsearch-9.4.4-amd64.deb
Selecting previously unselected package elasticsearch.
...
-------- Security autoconfiguration information -------- # 安全配置摘要
Authentication and authorization are enabled. # 认证已启用
TLS for the transport and HTTP layers is enabled and configured. # transport + HTTP 均已加密
The generated password for the elastic built-in superuser is : N2-zXzCrxgf7AH+wbsR0
# ↑ 自动生成的 elastic 密码,和 ES7 不同——不需要 elasticsearch-setup-passwords
Note

📌 对比 ES7:安装完就有 HTTPS + 认证 + TLS 证书,不需要一堆手动配置 ES9.4.4 安装时会自动输出安全配置信息:

  • elastic 内置用户的密码(一次性的,安装日志中能看到)

  • elasticsearch-reset-password -u elastic 重置 elastic 密码的命令

  • elasticsearch-create-enrollment-token -s kibana/node 为 Kibana/Node 生成 token 的命令

Terminal window
3)修改网络配置
root@ESnew-01 ~# grep network.host /etc/elasticsearch/elasticsearch.yml
#network.host: 192.168.0.1
root@ESnew-01 ~# sed -i 's@#network.host: 192.168.0.1@network.host: 0.0.0.0@' /etc/elasticsearch/elasticsearch.yml
4)限制 JVM 堆(2G 内存必须做)
root@ESnew-01 ~# ls -lhd /etc/elasticsearch/jvm.options.d/
# ES-9.x 默认有这个子目录(无需手动创建)
root@ESnew-01 ~# echo -e '-Xms512m\n-Xmx512m' > /etc/elasticsearch/jvm.options.d/heap.options
5)启动服务
root@ESnew-01 ~# systemctl enable --now elasticsearch.service
root@ESnew-01 ~# ps -ef | grep elastic | egrep 'Xms|Xmx'
... -Xms512m -Xmx512m ...
root@ESnew-01 ~# ss -lntup | grep -E '9200|9300'
LISTEN 0 4096 *:9200 *:*
LISTEN 0 4096 *:9300 *:*
Important

📌 ES9 默认 elasticsearch.yml 与 ES7 的差异

Terminal window
# egrep -v '^#|^$' /etc/elasticsearch/elasticsearch.yml
# ES9 安装后自带的配置(无需手动添加)
xpack.security.enabled: true # 认证已启用
xpack.security.enrollment.enabled: true # enrollment token 已启用
xpack.security.http.ssl: # HTTPS 已启用
enabled: true
keystore.path: certs/http.p12 # TLS 证书(自动生成)
xpack.security.transport.ssl: # transport 层 TLS
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12

💡 ES9 开箱即安全——安装完毕就是 HTTPS + 认证,不像 ES7 需要一大堆手动配置

Terminal window
6)验证单点
root@ESnew-01 ~# echo y | /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
# 输入 y 确认,重置 elastic 密码
New value: lvMEbeFwna3K+BTbjux*
root@ESnew-01 ~# curl -s -k -u elastic:lvMEbeFwna3K+BTbjux* https://10.0.0.9:9200/_cat/nodes
10.0.0.9 51 94 32 cdfhilmrstw * ESnew-01
✅️ ES9 单点运行正常

ES9 集群部署#

Terminal window
1)在另外两台安装 ES9(同样装完即可,不要改配置)
root@ESnew-02 ~# dpkg -i /tmp/elasticsearch-9.4.4-amd64.deb
root@ESnew-03 ~# dpkg -i /tmp/elasticsearch-9.4.4-amd64.deb
2)ESnew-01:添加集群配置
root@ESnew-01 ~# vim /etc/elasticsearch/elasticsearch.yml
# 追加以下内容:
cluster.name: kpyun-ES # 集群名称
discovery.seed_hosts: ["10.0.0.9", "10.0.0.10", "10.0.0.11"] # 集群节点列表
cluster.initial_master_nodes: ["ESnew-01", "ESnew-02", "ESnew-03"] # 初始主节点候选
# 初始化集群列表:仅在集群首次启动时需要
# 避免脑裂,告诉集群初始化的时候有哪些节点有资格竞选Master
# 集群组建成功后,新加入的节点不需要配置此项!
Caution

⚠️ ES9 的 cluster.initial_master_nodes 必须用主机名,不能用 IP

ES9 安装时根据主机名生成了 TLS 证书,节点名由主机名决定 如果用 IP,节点发现时 hostname 匹配不上,集群组建失败

/etc/elasticsearch/elasticsearch.yml
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12
cluster.name: kpyun-ES
discovery.seed_hosts: ["10.0.0.9", "10.0.0.10", "10.0.0.11"]
cluster.initial_master_nodes: ["ESnew-01", "ESnew-02", "ESnew-03"]
http.host: 0.0.0.0

💡 三台都要配 /etc/hosts 做主机名解析:

Terminal window
root@ESnew-01 ~# cat >> /etc/hosts <<EOF
10.0.0.9 ESnew-01
10.0.0.10 ESnew-02
10.0.0.11 ESnew-03
EOF
# 三台都要加!
# 测试验证
root@ESnew-03 ~# ping -W1 -c2 ESnew-01
PING ESnew-01 (10.0.0.9) 56(84) bytes of data.
64 bytes from ESnew-01 (10.0.0.9): icmp_seq=1 ttl=64 time=0.398 ms
64 bytes from ESnew-01 (10.0.0.9): icmp_seq=2 ttl=64 time=0.376 ms
--- ESnew-01 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1064ms
rtt min/avg/max/mdev = 0.376/0.387/0.398/0.011 ms
Terminal window
3)拷贝 ESnew-01 的配置与证书到其他节点
root@ESnew-01 ~# scp /etc/elasticsearch/elasticsearch.yml ESnew-02:/etc/elasticsearch/
root@ESnew-01 ~# scp /etc/elasticsearch/elasticsearch.yml ESnew-03:/etc/elasticsearch/
# TLS 证书(certs/ 目录 + elasticsearch.keystore)也要同步
root@ESnew-01 ~# scp -r /etc/elasticsearch/certs ESnew-02:/etc/elasticsearch/
root@ESnew-01 ~# scp /etc/elasticsearch/elasticsearch.keystore ESnew-02:/etc/elasticsearch/
root@ESnew-01 ~# scp -r /etc/elasticsearch/certs ESnew-03:/etc/elasticsearch/
root@ESnew-01 ~# scp /etc/elasticsearch/elasticsearch.keystore ESnew-03:/etc/elasticsearch/
5)三台同时清数据 + 设置 JVM + 启动
root@ESnew-01 ~# echo -e '-Xms512m\n-Xmx512m' > /etc/elasticsearch/jvm.options.d/heap.options
root@ESnew-02 ~# echo -e '-Xms512m\n-Xmx512m' > /etc/elasticsearch/jvm.options.d/heap.options
root@ESnew-03 ~# echo -e '-Xms512m\n-Xmx512m' > /etc/elasticsearch/jvm.options.d/heap.options
root@ESnew-01 ~# rm -rf /var/{log,lib}/elasticsearch/* && systemctl restart elasticsearch.service
# 把数据和日志全删除了 --> 相当于我没有启动过
root@ESnew-02 ~# rm -rf /var/{log,lib}/elasticsearch/* && systemctl enable --now elasticsearch.service
root@ESnew-03 ~# rm -rf /var/{log,lib}/elasticsearch/* && systemctl enable --now elasticsearch.service
⚠️ 三台同时启动,避免脑裂
Warning

⚠️ ES9 集群组建的坑点:

  1. 三台必须同时启动,否则先启动的节点会自举为单节点集群
  2. cluster.initial_master_nodes 必须用主机名
    • 仅在集群首次启动时需要,新加入的节点不需要配置此项(避免脑裂)
  3. /etc/hosts 必须有主机名解析
  4. TLS 证书必须一致(从第一台拷贝到其他节点)
Terminal window
6)重置密码并验证集群
root@ESnew-01 ~# echo y | /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
New value: UF+Q8qcdqvUQF8i=7ESk
root@ESnew-01 ~# curl -s -k -u elastic:UF+Q8qcdqvUQF8i=7ESk https://10.0.0.9:9200/_cat/nodes?v
ip heap.percent ram.percent cpu node.role master name
10.0.0.10 75 60 38 cdfhilmrstw * ESnew-02
10.0.0.11 81 58 35 cdfhilmrstw - ESnew-03
10.0.0.9 75 55 24 cdfhilmrstw - ESnew-01
✅️ 3 节点集群就绪,ESnew-02 被选为主节点
root@ESnew-01 ~# curl -s -k -u elastic:UF+Q8qcdqvUQF8i=7ESk https://10.0.0.9:9200/_cat/health
1785635069 01:44:29 elasticsearch green 3 3 6 3 0 0 0 0 - 100.0%
# green ✅️
Terminal window
7)改管理员密码为简单好记的
root@ESnew-01 ~# curl -s -k -u elastic:UF+Q8qcdqvUQF8i=7ESk \
-XPUT 'https://10.0.0.9:9200/_security/user/elastic/_password' \
-H 'Content-Type: application/json' -d '{"password":"passwd"}'
# 返回 {} = 成功
root@ESnew-01 ~# curl -s -k -u elastic:passwd https://10.0.0.9:9200/_cat/nodes | wc -l
3
✅️ 新密码生效

验证 ES9 集群高可用#

Terminal window
1)查看当前主节点
root@ESnew-01 ~# curl -s -k -u elastic:passwd https://10.0.0.9:9200/_cat/master
qZZAe2XpS7aSYVLyZguqrA 10.0.0.10 10.0.0.10 ESnew-02
# ESnew-02 是 master
2)停止 master 节点
root@ESnew-02 ~# systemctl stop elasticsearch.service
3)观察自动切换
root@ESnew-01 ~# curl -s -k -u elastic:passwd https://10.0.0.9:9200/_cat/master
VZdqXATfRFGBSHkkrJ780w 10.0.0.9 10.0.0.9 ESnew-01
✅️ ESnew-01 自动接管为 master
root@ESnew-01 ~# curl -s -k -u elastic:passwd https://10.0.0.9:9200/_cat/health
yellow 2 2 4 3 0 0 2 0 - 66.7%
# yellow:因为只有 2/3 节点存活
Tip

💡 一个 ES 集群,若想容忍 N 个节点故障,最少需要 ==2N+1== 个节点

  • 容忍 1 台故障 → 3 台(如本例,坏 2 台就不可用了)
  • 容忍 2 台故障 → 5 台
  • 过半以上需要存活,否则集群 ==read-only==(无法选举 master)
    • 无法形成法定人数(quorum),选举不出新 master
    • 无法选举出新 master 时,集群拒绝写入,只保留读能力
Terminal window
4)恢复 ESnew-02
root@ESnew-02 ~# systemctl start elasticsearch.service
root@ESnew-01 ~# curl -s -k -u elastic:passwd https://10.0.0.9:9200/_cat/master
VZdqXATfRFGBSHkkrJ780w 10.0.0.9 10.0.0.9 ESnew-01
# 非抢占式,master 依旧是ESnew-01
root@ESnew-01 ~# curl -s -k -u elastic:passwd https://10.0.0.9:9200/_cat/health
green 3 3 6 3 0 0 0 0 - 100.0%
✅️ 恢复 green

Kibana9 对接 ES9 集群#

Terminal window
1)安装 Kibana9
jiuzhao@Ubuntu ~$ scp kibana-9.4.4-amd64.deb ESnew-01:/tmp
root@ESnew-01 ~# dpkg -i /tmp/kibana-9.4.4-amd64.deb
2)基础配置(只需加两行,其余保持默认)
root@ESnew-01 ~# egrep -v '^#|^$' /etc/kibana/kibana.yml
logging:
appenders:
file:
type: file
fileName: /var/log/kibana/kibana.log # Kibana 自身日志 → JSON 格式
layout:
type: json
root:
appenders:
- default # 控制台输出
- file # 上面的日志文件
pid.file: /run/kibana/kibana.pid # systemd 判断 Kibana 是否存活
server.host: "0.0.0.0" # ← 手动添加
i18n.locale: "zh-CN" # ← 手动添加
# ES9 的 Kibana 不需要手动配 elasticsearch.hosts 和密码
# 通过 enrollment token + verification code 自动对接 ✅️
3)生成加密密钥(ES9 Kibana 必配)
# ES9 的 Kibana 比 ES7 多了加密密钥要求,否则 cases / fleet / reporting 等插件报错
root@ESnew-01 ~# /usr/share/kibana/bin/kibana-encryption-keys generate
...Settings:...
xpack.encryptedSavedObjects.encryptionKey: ba1a8f57b65545d60e87ffdb48de7bebf4c4aeb31548ba397922161324cc0be9
xpack.reporting.encryptionKey: 240a397f6cb9126ca6f6b24a6e7b099e0eb4cf32abed7479828db415a51e4774
xpack.security.encryptionKey: fa9c723e88d0952cb12fe22bb1388c497cd1bc61ad36367f5c3dbfbd8f8ae865
# ↑ 三个 key 分别加密存储对象 / 报表 / 会话,必须手动写回 kibana.yml ⚠️
root@ESnew-01 ~# vim /etc/kibana/kibana.yml
# 加密密钥不会被 enrollment 自动回填——enrollment 只负责 ES 连接信息
4)启动 Kibana
root@ESnew-01 ~# systemctl enable --now kibana.service
root@ESnew-01 ~# ss -lntup | grep 5601
LISTEN 0 511 0.0.0.0:5601 0.0.0.0:*
5)生成 Kibana enrollment token
root@ESnew-01 ~# /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana
eyJ2ZXIiOiI4LjE0LjAiLCJhZHIiOlsiMTAuMC4wLjk6OTIwMCJdLCJmZ3IiOiI5MmQzYzEzOWUwNzBkMmRhMzI3MDYzNWI1NTM5NzYwNzlhZWYwYjFlNDBmMDQ0YTY3MjlmMDk2OGUyMzAxYWNmIiwia2V5IjoiYTZQSHhaOEJJZUI4N3pXdHpoLUg6a1AtZXhhWHBST19ub0xheWszVnltUSJ9
6)获取 Kibana verification code
root@ESnew-01 ~# /usr/share/kibana/bin/kibana-verification-code
Your verification code is: 034 336
7)访问 WebUI
# 浏览器打开 http://10.0.0.9:5601/
# ① 输入 enrollment token → ② 输入验证码 034 336 → ③ 登录 elastic:passwd
===============================================
完成后 Kibana 自动回写 kibana.yml:
elasticsearch.hosts: [https://10.0.0.9:9200] # ES 地址
elasticsearch.serviceAccountToken: AAEAAWVsYXN0aWMv... # 服务账户 token
elasticsearch.ssl.certificateAuthorities: [/var/lib/kibana/...] # CA 证书路径
xpack.fleet.outputs: [...] # Fleet 输出配置
# ↑ 这些全都是 enrollment 自动回填的,不需要手动写
Tip

💡 重装 Kibana 步骤

Terminal window
systemctl stop kibana.service
dpkg -P kibana
# 确认干净
rm -rf /etc/kibana/ /var/lib/kibana/ /var/log/kibana/
userdel -r kibana 2>/dev/null; groupdel kibana 2>/dev/null
# 重装
dpkg -i /tmp/kibana-9.4.4-amd64.deb
Note

完整的 kibana 配置文件

Terminal window
root@ESnew-01 ~# cat /etc/kibana/kibana.yml
logging:
appenders:
file:
type: file
fileName: /var/log/kibana/kibana.log # Kibana 自身日志 → JSON 格式
layout:
type: json
root:
appenders:
- default # 控制台输出
- file # 上面的日志文件
pid.file: /run/kibana/kibana.pid # systemd 判断 Kibana 是否存活
server.host: "0.0.0.0" # ← 手动添加
i18n.locale: "zh-CN" # ← 手动添加
# ES9 的 Kibana 需要额外设置加密密钥,否则 cases 插件报错
xpack.encryptedSavedObjects.encryptionKey: ba1a8f57b65545d60e87ffdb48de7bebf4c4aeb31548ba397922161324cc0be9
xpack.reporting.encryptionKey: 240a397f6cb9126ca6f6b24a6e7b099e0eb4cf32abed7479828db415a51e4774
xpack.security.encryptionKey: fa9c723e88d0952cb12fe22bb1388c497cd1bc61ad36367f5c3dbfbd8f8ae865
# This section was automatically generated during setup.
elasticsearch.hosts: [https://10.0.0.9:9200]
elasticsearch.serviceAccountToken: AAEAAWVsYXN0aWMva2liYW5hL2Vucm9sbC1wcm9jZXNzLXRva2VuLTE3ODU3Mjk3MDQ1MTU6SUppcW4ybjZUenk5dzZqdS1xNDBoUQ
elasticsearch.ssl.certificateAuthorities: [/var/lib/kibana/ca_1785729704793.crt]
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: [https://10.0.0.9:9200], ca_trusted_fingerprint: 92d3c139e070d2da3270635b553976079aef0b1e40f044a6729f0968e2301acf}]

Zookeeper 集群部署#

Note

📌 什么是 Zookeeper

Zookeeper 是一个分布式协调服务,维护配置信息、命名服务、分布式同步

主要的应用场景:Kafka、HBase、HDFS HA、YARN HA、Solr

官网:https://zookeeper.apache.org/

单点部署#

Terminal window
1)下载软件包
jiuzhao@Ubuntu ~$ wget -P /home/jiuzhao/下载 https://dlcdn.apache.org/zookeeper/zookeeper-3.8.6/apache-zookeeper-3.8.6-bin.tar.gz
2)拷贝并解压
jiuzhao@Ubuntu ~$ scp apache-zookeeper-3.8.6-bin.tar.gz Elk01:/tmp
root@Elk01 ~# tar xf /tmp/apache-zookeeper-3.8.6-bin.tar.gz -C /usr/local/
3)配置环境变量
root@Elk01 ~# vim /etc/profile.d/zk.sh
#!/bin/bash
export ZK_HOME=/usr/local/apache-zookeeper-3.8.6-bin
export JAVA_HOME=/usr/share/elasticsearch/jdk
export PATH=$PATH:${ZK_HOME}/bin:${JAVA_HOME}/bin
# 复用 ES7 自带的 JDK,不需要单独安装 Java
root@Elk01 ~# source /etc/profile.d/zk.sh
root@Elk01 ~# java --version
openjdk 22.0.2 2024-07-16
4)准备配置文件
root@Elk01 ~# cp /usr/local/apache-zookeeper-3.8.6-bin/conf/zoo_sample.cfg /usr/local/apache-zookeeper-3.8.6-bin/conf/zoo.cfg
5)启动 + 验证
root@Elk01 ~# zkServer.sh start
Starting zookeeper ... STARTED
root@Elk01 ~# zkServer.sh status
Mode: standalone

基本使用#

Note

📌 Zookeeper 的数据模型类似文件系统的层级路径,每个节点(Znode)是一个 ==key==(路径),可以存储一个 ==value==(数据)

Terminal window
create /school Redhat # key=/school, value=Redhat(create = mkdir + echo 合一)
create /empty # value 不是必需的,为空则 get 返回 null
# 但实际使用中一般都会赋值

和文件系统一样,/school 既是数据容器,也能挂载子节点 /school/class(层级结构)

文件系统Zookeeper
mkdir /schoolcreate /school Redhat
ls /ls /
cat /schoolget /school
echo kpyun > /schoolset /school kpyun
rm /schooldelete /school

💡 ZK 的 create = mkdir + echo 合一,路径和数据一步写完

Terminal window
1)创建节点
root@Elk01 ~# zkCli.sh -server 10.0.0.6:2181
[zk: 10.0.0.6:2181(CONNECTED) 0] create /school Redhat
Created /school
# key=/school,value=Redhat
2)查看节点列表
[zk: 10.0.0.6:2181(CONNECTED) 1] ls /
[school, zookeeper]
3)查看节点数据
[zk: 10.0.0.6:2181(CONNECTED) 2] get /school
Redhat
4)修改节点数据
[zk: 10.0.0.6:2181(CONNECTED) 3] set /school kpyun
[zk: 10.0.0.6:2181(CONNECTED) 4] get /school
kpyun
5)删除节点
[zk: 10.0.0.6:2181(CONNECTED) 5] delete /school
[zk: 10.0.0.6:2181(CONNECTED) 6] ls /
[zookeeper]
6)创建层级节点
[zk: 10.0.0.6:2181(CONNECTED) 7] create /school Redhat
[zk: 10.0.0.6:2181(CONNECTED) 8] create /school/class test01
# /school/class 是 /school 的子节点,路径层级用 '/' 分隔
[zk: 10.0.0.6:2181(CONNECTED) 9] ls /school
[class]
7)递归删除(节点下有子节点时,普通 delete 会报错)
[zk: 10.0.0.6:2181(CONNECTED) 10] delete /school
Node not empty: /school # ❌ 不能删除非空节点
[zk: 10.0.0.6:2181(CONNECTED) 11] deleteall /school
# deleteall 会递归删除该节点及其所有子节点
[zk: 10.0.0.6:2181(CONNECTED) 12] ls /
[zookeeper]

Zookeeper 集群部署#

数据同步 / 选举

数据同步 / 选举

选举

Elk01 (follower)

myid=1

:2181 :3888

Elk02 (leader👑)

myid=2

:2181 :2888 :3888

Elk03 (follower)

myid=3

:2181 :3888

数据同步 / 选举

数据同步 / 选举

选举

Elk01 (follower)

myid=1

:2181 :3888

Elk02 (leader👑)

myid=2

:2181 :2888 :3888

Elk03 (follower)

myid=3

:2181 :3888

Tip

2888 仅 leader 监听(follower → leader 数据同步),3888 全部节点监听(选举投票)

端口谁监听作用
2181全部节点客户端连接端口,供应用程序连接使用的端口,所有节点均对外提供服务;
2888仅 leader**集群内部通信端口(follower → leader)**数据同步、事务请求转发
3888全部节点集群选举端口,选举投票通信,Leader 宕机时用于重新选举
Terminal window
1)停单点
root@Elk01 ~# zkServer.sh stop
2)修改集群配置
root@Elk01 ~# vim /usr/local/apache-zookeeper-3.8.6-bin/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper
# 修改数据目录
clientPort=2181
server.1=10.0.0.6:2888:3888
server.2=10.0.0.7:2888:3888
server.3=10.0.0.8:2888:3888
3)同步程序到其他节点
root@Elk01 ~# scp -r /usr/local/apache-zookeeper-3.8.6-bin/ 10.0.0.7:/usr/local/
# 我一个tar包 -C /usr/local/ 解压在了这里(东西都在这)
root@Elk01 ~# scp -r /usr/local/apache-zookeeper-3.8.6-bin/ 10.0.0.8:/usr/local/
root@Elk01 ~# scp /etc/profile.d/zk.sh 10.0.0.7:/etc/profile.d/
root@Elk01 ~# scp /etc/profile.d/zk.sh 10.0.0.8:/etc/profile.d/
4)各节点准备 myid(与 zoo.cfg 中的 server.N 对应)
root@Elk01 ~# mkdir /var/lib/zookeeper && echo 1 > /var/lib/zookeeper/myid
root@Elk02 ~# mkdir /var/lib/zookeeper && echo 2 > /var/lib/zookeeper/myid
root@Elk03 ~# mkdir /var/lib/zookeeper && echo 3 > /var/lib/zookeeper/myid
Important

📌 myid 中的数字必须和 zoo.cfg 中 server.N 的 N 一致

server.1=10.0.0.6 → Elk01 的 /var/lib/zookeeper/myid = 1

Terminal window
5)三台同时启动
root@Elk01 ~# source /etc/profile.d/zk.sh && zkServer.sh start
root@Elk02 ~# source /etc/profile.d/zk.sh && zkServer.sh start
root@Elk03 ~# source /etc/profile.d/zk.sh && zkServer.sh start
6)查看集群状态
root@Elk01 ~# zkServer.sh status
Mode: follower
root@Elk02 ~# zkServer.sh status
Mode: leader
root@Elk02 ~# ss -ntl | egrep "2181|2888|3888"
LISTEN 0 50 [::ffff:10.0.0.7]:2888 *:*
LISTEN 0 50 *:2181 *:*
LISTEN 0 50 [::ffff:10.0.0.7]:3888 *:*
# 2888 仅 leader 监听,Elk02 是 leader
root@Elk03 ~# zkServer.sh status
Mode: follower
# Elk02 先于 Elk03 启动,完成了选举——启动顺序也会影响 leader 归属
✅️ 1 leader + 2 follower,ZK 集群就绪
Important

💡 -server 连接单个节点 vs 集群:

Terminal window
# 仅连一个节点(挂了就断了)
zkCli.sh -server 10.0.0.6:2181
# 连集群(当前节点挂了自动切到下一个)
zkCli.sh -server 10.0.0.6:2181,10.0.0.7:2181,10.0.0.8:2181

客户端拿到列表后依次尝试,==连上哪个算哪个==,生产环境必须写完整集群地址保证高可用

Terminal window
7)客户端连接测试
root@Elk01 ~# zkCli.sh -server 10.0.0.6:2181,10.0.0.7:2181,10.0.0.8:2181
[zk: 10.0.0.6:2181,10.0.0.7:2181,10.0.0.8:2181(CONNECTED) 0] ls /
[zookeeper]
# 在 Elk01 上创建数据
[zk: 10.0.0.6:2181,10.0.0.7:2181,10.0.0.8:2181(CONNECTED) 1] create /kpyun-zk test-data
Created /kpyun-zk
# 在 Elk02 上读取(跨节点数据一致)
root@Elk02 ~# zkCli.sh -server 10.0.0.7:2181
[zk: 10.0.0.7:2181(CONNECTED) 0] get /kpyun-zk
test-data
✅️ 数据在集群所有节点间自动同步
8)高可用验证
root@Elk02 ~# zkServer.sh stop
# 把leader杀死 --> 触发选举
root@Elk03 ~# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/apache-zookeeper-3.8.6-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: leader
root@Elk03 ~# ss -ntl | egrep "2181|2888|3888"
LISTEN 0 50 [::ffff:10.0.0.8]:2888 *:*
LISTEN 0 50 *:2181 *:*
LISTEN 0 50 [::ffff:10.0.0.8]:3888 *:*
root@Elk01 ~# zkCli.sh -server 10.0.0.6:2181,10.0.0.8:2181
# 这里连接的是一整个集群
[zk: 10.0.0.6:2181,10.0.0.8:2181(CONNECTED) 0] ls /
[kpyun-zk, zookeeper]
# 暂停了一个节点后服务依旧可以写入数据
[zk: 10.0.0.6:2181,10.0.0.8:2181(CONNECTED) 1] create /Class C413
Created /Class
[zk: 10.0.0.6:2181,10.0.0.8:2181(CONNECTED) 2] ls /
[Class, kpyun-zk, zookeeper]

Leader 选举机制#

Note

📌 选举规则(两种场景)

场景决定性因素
集群首次启动启动顺序 + myid + zxid
Leader 宕机重选==只有 myid 和 zxid==(节点同时感知,无启动顺序干扰)
  1. 先比较 zxid(事务 ID),==越大越优先==
  2. zxid 相同时,比较 myid,==越大越优先==
  3. 当半数以上节点参与选举完成,leader 确认

🌰 本例:Elk02 宕机后,Elk03(myid=3)凭借最大 myid 当选新 leader

Tip

💡 集群节点数必须为奇数

一个集群如果要容忍 N 台故障,需要 2N + 1 台节点

  • 容忍 1 台故障 → 3 台
  • 容忍 2 台故障 → 5 台

Znode 类型#

类型创建命令特征
永久节点create /path data会话断开后不删除
临时节点create -e /path data会话断开 30s 后自动删除
Terminal window
1)创建临时节点
[zk: ...(CONNECTED) 1] create -e /office https://www.baidu.com
Created /office
2)查看节点元数据
[zk: ...(CONNECTED) 2] stat /office
ephemeralOwner = 0x2100009cac2d0000 # 当前 session ID,断开 30s 后自动删除
[zk: ...(CONNECTED) 3] stat /kpyun-zk
ephemeralOwner = 0x0 # 0 = 永久节点

Watch 机制(了解)#

Terminal window
1)终端 1:监听 /kpyun-zk 子节点变化
root@Elk01 ~# zkCli.sh -server 10.0.0.8:2181
[zk: ...(CONNECTED) 0] ls -w /kpyun-zk
[]
# -w 参数 = 对该路径的子节点变化进行监听
2)终端 2:在 /kpyun-zk 下创建新节点
root@Elk03 ~# zkCli.sh -server 10.0.0.6:2181
[zk: ...(CONNECTED) 0] create /kpyun-zk/test
Created /kpyun-zk/test
3)终端 1 自动收到通知
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/kpyun-zk
✅️ 检测到子节点变化

课后练习#

Terminal window
1)编写 systemd 服务文件(三台一样,放在 /etc/systemd/system/)
root@Elk01 ~# cat > /etc/systemd/system/zookeeper.service <<"EOF"
[Unit]
# 服务描述
Description=Apache Zookeeper Server
# 官方文档地址
Documentation=https://zookeeper.apache.org/
# 等待网络就绪后再启动
After=network-online.target
# 主动声明依赖网络在线目标
Wants=network-online.target
[Service]
# zkServer.sh 内部 fork 出 java 守护进程,用 forking
Type=forking
# 环境变量:ZK_HOME / JAVA_HOME(复用 ES 自带 JDK)
Environment=ZK_HOME=/usr/local/apache-zookeeper-3.8.6-bin
Environment=JAVA_HOME=/usr/share/elasticsearch/jdk
Environment=PATH=/usr/local/apache-zookeeper-3.8.6-bin/bin:/usr/share/elasticsearch/jdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
# 启动命令
ExecStart=/usr/local/apache-zookeeper-3.8.6-bin/bin/zkServer.sh start
# 停止命令
ExecStop=/usr/local/apache-zookeeper-3.8.6-bin/bin/zkServer.sh stop
# 进程异常退出时(非 0 退出码或被信号kill杀死),systemd 自动拉起来
Restart=on-failure
# 重启间隔 3 秒
RestartSec=3
[Install]
# 开机自启
WantedBy=multi-user.target
EOF
2)同步到其他节点
root@Elk01 ~# scp /etc/systemd/system/zookeeper.service 10.0.0.7:/etc/systemd/system/
root@Elk01 ~# scp /etc/systemd/system/zookeeper.service 10.0.0.8:/etc/systemd/system/
3)三台手动停进程 → 转 systemd 接管
root@Elk01 ~# zkServer.sh stop
root@Elk01 ~# systemctl daemon-reload && systemctl enable --now zookeeper
root@Elk01 ~# systemctl status zookeeper | grep Active
Active: active (running)
✅️ 服务由 systemd 接管,开机自启
# Elk02 / Elk03 执行相同操作
4)验证
root@Elk02 ~# systemctl is-active zookeeper
active
root@Elk02 ~# zkServer.sh status | grep Mode
Mode: leader
Tip

💡 服务文件放 /etc/systemd/system/ 还是 /usr/lib/systemd/system/?

  • /etc/systemd/system/:系统管理员自建服务的推荐位置,优先级更高,便于管理
  • /usr/lib/systemd/system/:软件包安装时自带的服务位置
  • 两者 systemd 都能识别,功能等价;自建服务放 /etc 更规范
  • Type=forking:zkServer.sh 内部会 fork 出 java 守护进程,systemd 用 forking 判断”fork 成功 = 已启动”

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Profile Image of the Author
久棹
不是先学好了再干,而是先干起来再学习,干中学!
分类
站点统计
文章
115
分类
15
标签
272
总字数
306,562
运行时长
0 天
最后活动
0 天前
文章目录