前几天公司有个需求要做全文索引,于是写了一个脚本使用 docker 一键部署 ELK 服务,内容如下:


#!/bin/bash  
set -e

echo "=================================================="  
echo "🚀 开始部署 ELK + MySQL 同步(中文分词 + 固定索引)"  
echo "=================================================="

\# ==================== 配置区(请修改)====================  
MYSQL_HOST="192.168.124.6" # ✏️ 修改为你的 MySQL IP  
MYSQL_USER="root" # 读取权限用户  
MYSQL_PASSWORD="123456" # 用户密码  
MYSQL_DB="ams" # 数据库名

ELASTIC_PASSWORD="i*B4j6eD+g0e" # ES 密码(至少 8 位,含大小写+数字)

ES_VERSION="8.11.3" # 必须与 IK 插件版本一致  
LOGSTASH_VERSION="8.11.3"  
\# ========================================================

\# 项目路径  
ROOT_DIR="/project/elastic-sync"  
mkdir -p "$ROOT_DIR"  
cd "$ROOT_DIR"

echo "📁 创建项目目录结构"  
mkdir -p config/mysql config data/es data/kibana logs/logstash plugins/ik

\# ——————- 下载 IK 分词插件 ———————  
IK_URL="https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-ik-${ES_VERSION}.zip"  
IK_DIR="$ROOT_DIR/plugins/ik"

if [ ! -f "$IK_DIR/plugin-descriptor.properties" ]; then  
echo "📥 正在下载 IK 分词插件 v${ES_VERSION}…"  
wget -q "$IK_URL" -O /tmp/ik.zip  
unzip -q /tmp/ik.zip -d "$IK_DIR"  
rm /tmp/ik.zip  
chown -R 1000:1000 "$IK_DIR"  
echo "✅ IK 插件安装完成"  
else  
echo "ℹ️ IK 插件已存在,跳过安装"  
fi

\# ——————- 生成 elasticsearch.yml ——————-  
cat > config/elasticsearch.yml << EOF  
cluster.name: production-cluster  
node.name: node-1  
node.roles: [ data, master, ingest ]

path:  
data: /usr/share/elasticsearch/data  
logs: /usr/share/elasticsearch/logs

network.host: 0.0.0.0  
http.port: 9200

http.cors.enabled: true  
http.cors.allow-origin: "*"

discovery.type: single-node

xpack.security.enabled: true  
xpack.security.http.ssl.enabled: false

xpack.monitoring.collection.enabled: true  
EOF

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 生成 logstash.yml &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
cat > config/logstash.yml << EOF  
http.host: "0.0.0.0"  
xpack.monitoring.enabled: false  
config.reload.automatic: false  
EOF

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 生成 logstash.conf &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
cat > config/logstash.conf << EOF  
input {  
jdbc {  
jdbc_connection_string => "jdbc:mysql://$MYSQL_HOST:3306/$MYSQL_DB?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai"  
jdbc_user => "$MYSQL_USER"  
jdbc_password => "$MYSQL_PASSWORD"  
jdbc_driver_library => "/usr/share/logstash/mysql/mysql-connector-java-8.0.30.jar"  
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"

jdbc_default_timezone => "Asia/Shanghai"

statement => "  
SELECT * FROM article  
WHERE updated_at >= :sql_last_value  
ORDER BY updated_at ASC  
"

use_column_value => true  
tracking_column => "updated_at"  
tracking_column_type => "timestamp"  
last_run_metadata_path => "/usr/share/logstash/.logstash_jdbc_last_run"

schedule => "\*/2 \* \* \* *"  
}  
}

filter {

\# 清洗 content 字段中的 HTML 标签  
if [content] {  
mutate {  
gsub => [  
"content", "<[^>]*>", "" # 删除所有 HTML 标签:<p>、<div>、<span> 等  
]  
}  
\# 可选:进一步清理多余的空白字符  
mutate {  
gsub => [  
"content", "\s+", " " # 多个空白字符(空格、换行、制表符)合并为一个空格  
]  
}  
mutate {  
strip => ["content"] # 去除首尾空格  
}  
}

\# 如果 del_flag 是 1,标记该记录用于删除  
if [del_flag] == 1 {  
mutate {  
add_tag => ["delete_document"]  
}  
}  
}

output {  
if "delete_document" in [tags] {  
elasticsearch {  
hosts => ["http://elasticsearch:9200"]  
user => "elastic"  
password => "$ELASTIC_PASSWOR"  
action => "delete"  
document_id => "%{id}"  
index => "articles"  
}  
} else {  
elasticsearch {  
hosts => ["http://elasticsearch:9200"]  
user => "elastic"  
password => "$ELASTIC_PASSWOR"  
index => "articles" # ✅ 固定索引  
document_id => "%{id}" # 支持 varchar id  
doc_as_upsert => true # 更新覆盖  
}  
}

stdout { codec => rubydebug }  
}  
EOF

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 生成 docker-compose.yml &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
cat > docker-compose.yml << EOF  
services:  
elasticsearch:  
image: docker.elastic.co/elasticsearch/elasticsearch:$ES_VERSION  
container_name: elasticsearch  
environment:  
&#8211; discovery.type=single-node  
&#8211; ES_JAVA_OPTS=-Xms2g -Xmx2g  
&#8211; xpack.security.enabled=true  
&#8211; xpack.security.http.ssl.enabled=false  
&#8211; ELASTIC_PASSWORD=$ELASTIC_PASSWORD  
ports:  
&#8211; "9200:9200"  
volumes:  
&#8211; ./data/es:/usr/share/elasticsearch/data  
&#8211; ./config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml  
&#8211; ./plugins/ik:/usr/share/elasticsearch/plugins/ik  
networks:  
&#8211; elastic  
restart: unless-stopped  
healthcheck:  
test: ["CMD-SHELL", "curl -f http://localhost:9200 || exit 1"]  
interval: 30s  
timeout: 10s  
retries: 3

kibana:  
image: docker.elastic.co/kibana/kibana:$LOGSTASH_VERSION  
container_name: kibana  
depends_on:  
elasticsearch:  
condition: service_healthy  
environment:  
&#8211; ELASTICSEARCH_HOSTS=["http://elasticsearch:9200"]  
&#8211; ELASTICSEARCH_USERNAME=elastic  
&#8211; ELASTICSEARCH_PASSWORD=$ELASTIC_PASSWORD  
&#8211; SERVER_NAME=kibana.example.com  
&#8211; I18N_LOCALE=zh-CN  
ports:  
&#8211; "5601:5601"  
volumes:  
&#8211; ./data/kibana:/usr/share/kibana/data  
networks:  
&#8211; elastic  
restart: unless-stopped

logstash:  
image: docker.elastic.co/logstash/logstash:$LOGSTASH_VERSION  
container_name: logstash  
depends_on:  
&#8211; elasticsearch  
volumes:  
&#8211; ./config/logstash.conf:/usr/share/logstash/pipeline/logstash.conf  
&#8211; ./config/logstash.yml:/usr/share/logstash/config/logstash.yml  
&#8211; ./logs/logstash:/var/log/logstash  
&#8211; ./config/mysql:/usr/share/logstash/mysql  
networks:  
&#8211; elastic  
restart: unless-stopped

networks:  
elastic:  
driver: bridge  
EOF

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 下载 MySQL JDBC 驱动 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
JDBC_JAR="config/mysql/mysql-connector-java-8.0.30.jar"  
if [ ! -f "$JDBC_JAR" ]; then  
echo "📥 下载 MySQL JDBC 驱动&#8230;"  
mkdir -p config/mysql  
wget -q https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.30/mysql-connector-java-8.0.30.jar -O "$JDBC_JAR"  
echo "✅ JDBC 驱动下载完成"  
fi

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 设置权限 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
echo "🔐 设置目录权限"  
chown -R 1000:1000 data/es plugins/ik  
chmod -R 755 config logs  
chmod -R 777 data/kibana

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 生成创建索引脚本 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
cat > create-index.sh << &#8216;EOF&#8217;  
#!/bin/bash  
echo "🔄 正在创建索引 &#8216;articles&#8217; 并配置 IK 分词器&#8230;"

curl -X PUT "http://localhost:9200/articles" \  
-u elastic:$ELASTIC_PASSWORD \  
-H "Content-Type: application/json" \  
-d &#8216;  
{  
"settings": {  
"index": {  
"number_of_shards": 1,  
"number_of_replicas": 1  
},  
"analysis": {  
"analyzer": {  
"ik_analyzer": {  
"type": "custom",  
"tokenizer": "ik_max_word",  
"filter": ["lowercase"]  
}  
}  
}  
},  
"mappings": {  
"properties": {  
"id": { "type": "keyword" },  
"title": {  
"type": "text",  
"analyzer": "ik_max_word",  
"search_analyzer": "ik_smart"  
},  
"content": {  
"type": "text",  
"analyzer": "ik_max_word",  
"search_analyzer": "ik_smart"  
},  
"author": { "type": "keyword" },  
"created_at": { "type": "date" },  
"updated_at": { "type": "date" }  
}  
}  
}&#8217; && echo "✅ 索引 &#8216;articles&#8217; 创建成功!"  
EOF

chmod +x create-index.sh

\# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 完成 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  
echo "=================================================="  
echo "🎉 部署准备完成!"  
echo "=================================================="  
echo ""  
echo "📌 下一步操作:"  
echo "1. 检查配置:nano setup-elastic-sync.sh (修改 MySQL 地址、用户、密码)"  
echo "2. 增加权限:chmod +x setup-elastic-sync.sh"  
echo "3. 执行脚本:sudo ./setup-elastic-sync.sh"  
echo "4. 启动服务:sudo docker compose up -d"  
echo "5. 创建索引:bash ./create-index.sh"  
echo "6. 访问 Kibana:http://你的服务器IP:5601"  
echo " &#8211; 用户:elastic"  
echo " &#8211; 密码:$ELASTIC_PASSWORD"  
echo ""  
echo "💡 首次运行会全量同步 articles 表,之后每 2 分钟增量同步"  
echo "🔍 在 Kibana 中搜索中文(如“阿里巴巴”),应能命中结果"  
echo ""  
echo "⚠️ 注意:必须先运行 create-index.sh 再让 Logstash 写入,否则分词无效!"

如果 kibana 用 elastic 用户连不上,通过 Elasticsearch 的 Security API 为内置用户 kibana_system 修改密码:


curl -X POST "http://localhost:9200/_security/user/kibana_system/_password" -u elastic:i*B4j6eD+g0e -H "Content-Type: application/json" -d &#8216;{  
"password": "kibana_secure_password_2025"  
}&#8217;

然后修改 docker-compose.yml 中 kibana 修改用户名:kibana_system,密码:kibana_secure_password_2025

另 Ubuntu 安装 docker 命令:


\# 关防火墙(可选)  
sudo systemctl disable ufw  
sudo apt-get update  
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common  
\# 下载并添加 Docker 的 GPG 密钥  
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg &#8211;dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg  
\# 添加 Docker 的 APT 软件源  
echo "deb [arch=$(dpkg &#8211;print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

\# 添加阿里云的 Docker GPG 密钥(可选,信任源)  
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg &#8211;dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg  
\# 使用阿里云镜像源  
echo "deb [arch=$(dpkg &#8211;print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update  
sudo apt-get install docker-ce docker-ce-cli containerd.io  
docker &#8211;version  
sudo systemctl is-enabled docker  
sudo systemctl enable docker  
sudo systemctl start docker  
sudo docker ps