我希望在Kubernetes上的Elasticsearch中有一个用户字典

首先

在我的Kubernetes上的家用elasticsearch中,我使用了由elastic提供的kuromoji插件。
由于这个插件的词典很旧,例如”云”这样的词完全不支持,所以我在用户词典中进行了对应。

前提条件

以下是我們在這篇文章中建立的環境。我們每次都會更新elasticsearch到最新版本。

用户词典的定义 de

考虑到以更类似于Kubernetes的方式进行智能实现,我们决定使用ConfigMap来管理字典文件。通过在ConfigMap中将目录挂载到volumeMount,即使不重新启动服务,也可以动态地将其反映到Pod中。此外,与持久存储相比,使用起来更加方便,并且可以让多个Pod引用相同的内容。

创建ConfigMap

可以从Pod中引用的文件名被设定为”userdict.txt”。
需要注意的是,ConfigMap的大小默认限制为3MB。我们稍微调查了一下,虽然可以进行更改,但这可能会对整个Kubernetes的性能产生影响。因此,如果用户词典的注册词非常多的话,最好准备持久性存储作为解决方案。

apiVersion: v1
data:
  userdict.txt: |
    東京スカイツリー,東京 スカイツリー,トウキョウ スカイツリー,カスタム名詞
kind: ConfigMap
metadata:
  name: elastic-dict
  namespace: elastic
# kubectl apply -f elastic-dict.yaml

将数据文件挂载到elasticsearch容器中。

Elasticsearch的前提条件是以下文章中的内容。编辑这里的statefulset的yaml文件。

在将卷挂载到容器时,请使用指定目录进行挂载。
如果使用subPath将单个文件挂载到现有目录中,即使更新了ConfigMap(用户字典),更新内容也不会反映在Pod内部。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
  namespace: elastic
spec:
  selector:
    matchLabels:
      app: elasticsearch
  serviceName: "elasticsearch"
  replicas: 3
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      initContainers:
      # 略
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
        # 略
        volumeMounts:
# volumeMountsにelastic-dictをmountする定義を追記
        - name: elastic-dict
          mountPath: /usr/share/elasticsearch/config/dict
      volumes:
# volumesにelastic-dictのConfigMapを追記
        - name: elastic-dict
          configMap:
            name: elastic-dict
    # 略

由于害怕,所以先完全停止(使用delete),然后再应用。(应用也没有意义.)

# kubectl delete -f elasticsearch-sts-sec.yaml
# kubectl apply -f elasticsearch-sts-sec.yaml

更新用户词典

我会确认一下能否正确地将其挂载到Pod上。

# kubectl exec -it elasticsearch-0 -n elastic -- cat /usr/share/elasticsearch/config/dict/userdict.txt
東京スカイツリー,東京 スカイツリー,トウキョウ スカイツリー,カスタム名詞

由于ConfigMap资源,可以使用kubectl edit命令进行更新。

# kubectl edit configmap/elastic-dict -n elastic

进入vi编辑界面,在yaml文件中添加单词。
使用自定义名词将”云”加入。

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  userdict.txt: |
    東京スカイツリー,東京 スカイツリー,トウキョウ スカイツリー,カスタム名詞
    クラウド,クラウド,クラウド,カスタム名詞
kind: ConfigMap
metadata:
  creationTimestamp: "2021-03-25T14:35:02Z"
  name: elastic-dict
  namespace: elastic
  resourceVersion: "79233490"
  uid: bf3f976a-d279-41ec-bd1c-b4f264fd5b16

当您输入「:wq」并保存退出时,将显示消息「configmap/elastic-dict 已编辑」。

我会检查 Pod 是否已经被修改。(在 Kubernetes 的 Pod 上最多需要 120 秒才能反映出来。)

# kubectl exec -it elasticsearch-0 -n elastic -- cat /usr/share/elasticsearch/config/dict/userdict.txt
東京スカイツリー,東京 スカイツリー,トウキョウ スカイツリー,カスタム名詞
クラウド,クラウド,クラウド,カスタム名詞

如果确认从Pod获取到用户词典的更新,为了重新读取用户词典,将关闭索引然后再次打开。

# curl -XPOST -u elastic http://elasticsearch:9200/testindex/_close?pretty
# curl -XPOST -u elastic http://elasticsearch:9200/testindex/_open?pretty

如果您已经拥有具有解析过的字段的文档,并且想要使用更新的用户词典重新进行解析,则可以使用”_update_by_query”指定所有文档。

# curl -H 'Content-Type: application/json' -XPOST -u elastic http://elasticsearch:9200/testindex/_update_by_query?conflicts=proceed\&pretty -d '
{ 
  "query": {
    "match_all": {}
  }                
}'

我已经进行到这一步了,但是我意识到使用kubernetes的API来更新用户字典会很难与外部工具协调。
不过,我认为用户字典的”更新”频率较低,所以我打算用这种方法来管理。

额外的 – 在分析器中确认

我正在创建并验证一个具有以下用户词典设置的索引,尽管其记录可能有前后不一。

# curl -H 'Content-Type: application/json' -XPUT -u elastic http://elasticsearch:9200/testindex?pretty -d '
{
  "settings": {
  "index": {
      "analysis": {
        "tokenizer": {
          "custom_tokenizer": {
            "type": "kuromoji_tokenizer",
            "mode": "search",
            "discard_punctuation": "true",
            "user_dictionary": "dict/userdict.txt"
          }
        },
        "analyzer": {
          "custom_analyzer": {
            "type": "custom",
            "tokenizer": "custom_tokenizer"
          }
        }
      }
    }
  }
}'

用分析器进行确认的命令如下所示。

# curl -H 'Content-Type: application/json' -XPOST -u elastic http://elasticsearch:9200/testindex/_analyze?pretty -d '
{
  "analyzer": "custom_analyzer",
  "text": ["東京スカイツリーの上部にクラウドが"]
}'

结果如下。

{
  "tokens" : [
    {
      "token" : "東京",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "スカイツリー",
      "start_offset" : 2,
      "end_offset" : 8,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "の",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "上部",
      "start_offset" : 9,
      "end_offset" : 11,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "に",
      "start_offset" : 11,
      "end_offset" : 12,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "クラウド",
      "start_offset" : 12,
      "end_offset" : 16,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "が",
      "start_offset" : 16,
      "end_offset" : 17,
      "type" : "word",
      "position" : 6
    }
  ]
}

正确地在云端进行了分析。

最后

我构建过后发现,经常更新用户词典的目的不太适合。
而且,当ConfigMap更新较慢时,很难在下一个操作的索引中反映出来。
不过,虽然有3MB的限制,但在kubernetes内部无需”拥有文件”这一点对我来说是一个有吸引力的操作方式。

偶尔回应一些在Elasticsearch上遇到困惑的人们的吐槽,非常感谢Ohtani先生!

※本文及与Ohtani先生无关。

bannerAds