作者:David Pilato
我最近在讨论论坛上收到一个问题,关于如何修改官方 Docker 镜像以提供一个现成的 Elasticsearch 集群,其中已经包含一些数据。
说实话,我不喜欢这个想法,因为你必须通过提 entrypoint.sh 的分叉版本来破解 Elasticsearch 服务的启动方式。 这将使你的维护和升级变得更加困难。
相反,我发现使用其他解决方案来实现相同的目标会更好。
首先,我们将考虑使用 Elasticsearch Docker 镜像并遵循文档:
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.7.0
docker network create elastic
docker run --name es01 --net elastic -p 9200:9200 -it docker.elastic.co/elasticsearch/elasticsearch:8.7.0
请注意,我们没有在此处挂载 data 目录,因此该集群的数据将是短暂的,并且一旦节点关闭就会消失。 启动后,我们可以使用生成的密码检查它是否运行良好:
curl -s -k -u elastic:CHANGEME https://localhost:9200 | jq
请使用在安装时候显示的 Elasticsearch 密码来替换上面的 CHANGEME。上面的命令给出:
{
"name": "697bf734a5d5",
"cluster_name": "docker-cluster",
"cluster_uuid": "cMISiT__RSWkoKDYql1g4g",
"version": {
"number": "8.7.0",
"build_flavor": "default",
"build_type": "docker",
"build_hash": "09520b59b6bc1057340b55750186466ea715e30e",
"build_date": "2023-03-27T16:31:09.816451435Z",
"build_snapshot": false,
"lucene_version": "9.5.0",
"minimum_wire_compatibility_version": "7.17.0",
"minimum_index_compatibility_version": "7.0.0"
},
"tagline": "You Know, for Search"
}
因此,我们希望有一个可用的数据集。 让我们采用我在演示 Elasticsearch 时经常使用的示例数据集:人员数据集。 我创建了[一个生成器](https://github.com/dadoonet/injector) 来创建一些假数据。
首先,让我们下载注入器:
wget https://repo1.maven.org/maven2/fr/pilato/elasticsearch/injector/injector/8.7/injector-8.7.jar
然后我们将使用以下选项在磁盘上生成数据集:
mkdir data
java -jar injector-8.7.jar --console --silent > data/persons.json
我们有 1000000 个 json 文档,数据集如下所示:
head -2 data/persons.json
{"name":"Charlene Mickael","dateofbirth":"2000-11-01","gender":"female","children":3,"marketing":{"cars":1236,"shoes":null,"toys":null,"fashion":null,"music":null,"garden":null,"electronic":null,"hifi":1775,"food":null},"address":{"country":"Italy","zipcode":"80100","city":"Ischia","countrycode":"IT","location":{"lon":13.935138341699972,"lat":40.71842684204817}}}
{"name":"Kim Hania","dateofbirth":"1998-05-18","gender":"male","children":4,"marketing":{"cars":null,"shoes":null,"toys":132,"fashion":null,"music":null,"garden":null,"electronic":null,"hifi":null,"food":null},"address":{"country":"Germany","zipcode":"9998","city":"Berlin","countrycode":"DE","location":{"lon":13.164834451298645,"lat":52.604673827377155}}}
我们这里有 100 万个文档,因此我们无法真正使用批量请求来发送它。 我们需要:
#!/bin/bash
ELASTIC_PASSWORD=CHANGEME
mkdir tmp
echo "Split the source in 10000 items"
split -d -l10000 ../data/persons.json tmp/part
BULK_REQUEST_FILE="tmp/bulk_request.ndjson"
FILES="tmp/part*"
for f in $FILES
do
rm $BULK_REQUEST_FILE
echo "Preparing $f file..."
while read p; do
echo -e '{"index":{}}' >> $BULK_REQUEST_FILE
echo -e "$p" >> $BULK_REQUEST_FILE
done <$f
echo "Calling Elasticsearch Bulk API"
curl -XPOST -s -k -u elastic:$ELASTIC_PASSWORD https://localhost:9200/person/_bulk -H 'Content-Type: application/json' --data-binary "@$BULK_REQUEST_FILE" | jq '"Bulk executed in \(.took) ms with errors=\(.errors)"'
done
有关脚本输入文档的方法,你也可以参考文章 “Elasticsearch:如何使用 shell 脚本来写入数据到 Elasticsearch 中”。
这基本上打印:
Preparing tmp/part00 file...
Calling Elasticsearch Bulk API
"Bulk executed in 1673 ms with errors=false"
Preparing tmp/part01 file...
Calling Elasticsearch Bulk API
"Bulk executed in 712 ms with errors=false"
...
Preparing tmp/part99 file...
Calling Elasticsearch Bulk API
"Bulk executed in 366 ms with errors=false"
在我的机器上,运行它需要 8 分钟多。 大部分时间都花在编写批量请求上。 可能还有很大的改进空间,但我必须承认我不太擅长编写 shell 脚本。 哈? 你已经猜到了吗?
Logstash 可以完成与我们手动完成的类似工作,但还提供更多功能,例如错误处理、监控,我们甚至不需要编写代码... 我们将在这里再次使用 Docker:
docker pull docker.elastic.co/logstash/logstash:8.7.0
让我们为此编写一个作业:
input {
file {
path => "/usr/share/logstash/persons/persons.json"
mode => "read"
codec => json { }
exit_after_read => true
}
}
filter {
mutate {
remove_field => [ "log", "@timestamp", "event", "@version" ]
}
}
output {
elasticsearch {
hosts => "${ELASTICSEARCH_URL}"
index => "person"
user => "elastic"
password => "${ELASTIC_PASSWORD}"
ssl_certificate_verification => false
}
}
我们现在可以运行该作业:
docker run --rm -it --name ls01 --net elastic \
-v $(pwd)/../data/:/usr/share/logstash/persons/:ro \
-v $(pwd)/pipeline/:/usr/share/logstash/pipeline/:ro \
-e XPACK_MONITORING_ENABLED=false \
-e ELASTICSEARCH_URL="https://es01:9200" \
-e ELASTIC_PASSWORD="CHANGEME" \
docker.elastic.co/logstash/logstash:8.7.0
在我的机器上,运行它只需要不到 2 分钟。
你可以更轻松地使用 docker compose 命令来根据需要运行所有内容,并向用户公开一个可供使用的集群,而不是手动运行所有这些内容。 这是一个简单的 .env 文件:
ELASTIC_PASSWORD=CHANGEME
STACK_VERSION=8.7.0
ES_PORT=9200
以及 docker-compose.yml:
version: "2.2"
services:
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
ports:
- ${ES_PORT}:9200
environment:
- node.name=es01
- cluster.initial_master_nodes=es01
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- bootstrap.memory_lock=true
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test:
[
"CMD-SHELL",
"curl -s -k https://localhost:9200 | grep -q 'missing authentication credentials'",
]
interval: 10s
timeout: 10s
retries: 120
logstash:
depends_on:
es01:
condition: service_healthy
image: docker.elastic.co/logstash/logstash:${STACK_VERSION}
volumes:
- type: bind
source: ../data
target: /usr/share/logstash/persons
read_only: true
- type: bind
source: pipeline
target: /usr/share/logstash/pipeline
read_only: true
environment:
- ELASTICSEARCH_URL=https://es01:9200
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- XPACK_MONITORING_ENABLED=false
我们仍然在 ../data 目录中保存有 person.json 文件。 它被安装为 /usr/share/logstash/persons/persons.json ,就像在前面的示例中一样。 因此,我们使用与之前相同的 pipeline/persons.conf 文件。 要运行它,我们现在只需输入:
docker compose up
并等待 with-compose-logstash-1 容器退出:
with-compose-logstash-1 | [2023-04-21T15:17:55,335][INFO ][logstash.runner ] Logstash shut down.
with-compose-logstash-1 exited with code 0
这表明我们的服务现在已准备好运行并完全加载了我们的示例数据集。
有关这个部分的内容,更多阅读请参考:数据集成的强大联盟:Elasticsearch、Kibana、Logstash、MySQL。
你还可以使用创建快照 API 将 Elasticsearch 中的现有数据集备份到共享文件系统或 S3,然后使用 Restore Restore API 将其恢复到新集群。 假设你已经注册了一个名为 example 的存储库。 你可以使用以下命令创建快照:
# We force merge the segments first
POST /person/_forcemerge?max_num_segments=1
# Snapshot the data
PUT /_snapshot/sample/persons
{
"indices": "person",
"include_global_state": false
}
因此,无论何时启动新集群,你都可以使用以下命令恢复快照:
POST /_snapshot/sample/persons/_restore
你只需要小心使用此方法,当你将其升级到新的主要版本时,你拥有的快照仍然可以在集群中恢复。 例如,如果你使用版本 6.3 创建了快照,则无法在 8.2 中还原它。 有关更多详细信息,请参阅快照索引兼容性。 但好消息! 借助存档索引,Elasticsearch 现在能够访问较旧的快照存储库(返回到版本 5)... 你只需要了解一些限制即可。 为了保证你的快照始终完全兼容,你可能需要使用相同的脚本使用最新版本再次创建索引快照。 请注意,在这种情况下,Force Merger API 调用很重要,因为它将使用最新的 Elasticsearch/Lucene 版本重写所有段。
还记得我们启动集群时的情况吗?
docker run --name es01 --net elastic -p 9200:9200 -it docker.elastic.co/elasticsearch/elasticsearch:8.7.0
我们没有绑定挂载 data 和 config 目录。 但实际上我们可以这样做:
docker run --name es01 --net elastic -p 9200:9200 -it -v persons-data:/usr/share/elasticsearch/data -v persons-config:/usr/share/elasticsearch/config docker.elastic.co/elasticsearch/elasticsearch:8.7.0
我们可以检查刚刚创建的 Docker volume:
docker volume inspect persons-data persons-config
[
{
"CreatedAt": "2023-05-09T10:20:14Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/persons-data/_data",
"Name": "persons-data",
"Options": null,
"Scope": "local"
},
{
"CreatedAt": "2023-05-09T10:19:51Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/persons-config/_data",
"Name": "persons-config",
"Options": null,
"Scope": "local"
}
]
如果你想使用完全相同的命令行再次启动 Elasticsearch 节点,你可以稍后再次重用此挂载点。 如果需要与其他用户共享卷,可以将 /var/lib/docker/volumes/persons-config/ 和 /var/lib/docker/volumes/persons-data/ 中的数据备份到 /tmp/volume-backup:
然后你可以与其他用户共享 /tmp/volume-backup/persons.tgz 文件并让他们恢复它。
docker volume create persons-config
docker volume create persons-data
docker run --rm -it -v /tmp/volume-backup:/backup -v /var/lib/docker:/docker alpine:edge tar xfz /backup/persons.tgz -C /
并再次启动容器:
docker run --name es01 --net elastic -p 9200:9200 -it -v persons-data:/usr/share/elasticsearch/data -v persons-config:/usr/share/elasticsearch/config docker.elastic.co/elasticsearch/elasticsearch:8.7.0
当然,你可以使用之前创建的快照来配置新的 Elasticsearch Cloud 实例,而不是自行启动和管理本地 Elasticsearch 实例。 以下代码假设你已经定义了 API key。
POST /api/v1/deployments?validate_only=false
{
"resources": {
"elasticsearch": [
{
"region": "gcp-europe-west1",
"plan": {
"cluster_topology": [
{
"zone_count": 2,
"elasticsearch": {
"node_attributes": {
"data": "hot"
}
},
"instance_configuration_id": "gcp.es.datahot.n2.68x10x45",
"node_roles": [
"master",
"ingest",
"transform",
"data_hot",
"remote_cluster_client",
"data_content"
],
"id": "hot_content",
"size": {
"resource": "memory",
"value": 8192
}
}
],
"elasticsearch": {
"version": "8.7.1"
},
"deployment_template": {
"id": "gcp-storage-optimized-v5"
},
"transient": {
"restore_snapshot": {
"snapshot_name": "__latest_success__",
"source_cluster_id": "CLUSTER_ID"
}
}
},
"ref_id": "main-elasticsearch"
}
],
"kibana": [
{
"elasticsearch_cluster_ref_id": "main-elasticsearch",
"region": "gcp-europe-west1",
"plan": {
"cluster_topology": [
{
"instance_configuration_id": "gcp.kibana.n2.68x32x45",
"zone_count": 1,
"size": {
"resource": "memory",
"value": 1024
}
}
],
"kibana": {
"version": "8.7.1"
}
},
"ref_id": "main-kibana"
}
]
},
"settings": {
"autoscaling_enabled": false
},
"name": "persons",
"metadata": {
"system_owned": false
}
}
只需将 CLUSTER_ID 替换为从中获取快照的源集群即可。 集群启动后,你就拥有了一个功能齐全的实例,可以在互联网上使用你想要的默认数据集。 完成后,你可以使用以下命令轻松关闭部署:
POST /api/v1/deployments/DEPLOYMENT_ID/_shutdown
同样,只需将 DEPLOYMENT_ID 替换为你在创建部署时看到的部署 ID。
与往常一样,对于 Elastic,特别是 Elasticsearch,你有多种方法可以实现你的目标。 我在这里列出了其中一些,但可能还有其他一些方法:
根据你的品味和限制,你可以选择其中一种想法并根据你的需求进行调整。 如果你有其他好主意要分享,请在 Twitter 上告诉我们或讨论区。 许多很棒的想法/功能都来自社区。 分享你的!
原文:Preload Elasticsearch with your dataset | Elastic Blog