[kubernetes] GKEにElasticsearch環境を構築する
目次
Introduction
全文検索機能を作りたくて筆者は以前CloudRunの中にElasticSearchを入れて運用しようとしましたがなんたって辛い…
頑張ればなんとかいけるのですが、KibanaやElasticsearchへのimportデーモンなど疎通が難しく。また料金を抑えようとしてCloudRunのコールドスタートを使用しようとしたのですが、なんたって相性が悪すぎる…使い方がそもそも無理あるのでデファクトスタンダードであるkubernetes上でElasticsearchを構築したのでその備忘録になります。
今回作成するもの
- GKE上にElasticsearch環境の構築
- mongoAtlasからデータを同期する
- helmなどは使用しない
完成系
環境
$ gcloud -v
Google Cloud SDK 420.0.0
gke-gcloud-auth-plugin 0.4.0
$ kubectl version --short
Client Version: v1.26.1
Kustomize Version: v4.5.7
GKE: 1.24.9-gke.3200
ECK: v2.6.1
Elasticsearch: v8.6.2
Kibana: v8.6.2
monstache 6.7.11
前提
- kubectlの基本的なコマンドを使用できる
- Elasticsearchの概要を知っている
- gcpプロジェクトを作成している
- gcloudコマンドをinstallしている
- gke-gcloud-auth-pluginをinstallしている
- MongoDBAtlasに登録している
handsOn
今回は、「わかりやすく解説するため」&「解説する主題ではない部分」とのことから機密情報部分(Secret)を直書きします。読者の皆様は機密情報には気をつけてください。
GKEセッティング
まずは、gcpにログインしプロジェクトセットします
$ gcloud auth login
$ gcloud config set project {PROJECT_ID}
次にElasticsearch用のGKEクラスターを作成していきます。
料金節約のためspotで作成します。ここからGCEの料金がかかるので注意です
$ gcloud container clusters create es-cluster \
--zone=asia-northeast1-a \
--spot \
--num-nodes=2 \
--machine-type=e2-medium
数分待つとクラスターができます。
作成されたら以下のコマンドでGKEを操作できるか確認してください
$ kubectl config current-context
gke_{project-id}_asia-northeast1-a_es-cluster
ECK(Elasticsearch)環境構築
次に以下のチュートリアルに沿ってECK環境をinstallしていきます https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-quickstart.html
まずは、ECKのCustom Resource Definitions(CRD)を作成
$ kubectl create -f https://download.elastic.co/downloads/eck/2.6.1/crds.yaml
次にeck-operatorをinstallします
$ kubectl apply -f https://download.elastic.co/downloads/eck/2.6.1/operator.yaml
必要なリソースのinstallが終わったらElasticsearchを作成します
k8s
L es.yaml(new)
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: es-sample
spec:
version: 8.6.2
nodeSets:
- name: default
count: 1
config:
node.store.allow_mmap: false
$ kubectl apply -f k8s/es.yaml
少し時間が経ったら作成されていることが確認できます
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
es-sample-es-default-0 1/1 Running 0 2m19s
外部から接続できるようにロードバランサーを追加します
今回はhttps化しないでいきます
k8s
L es.yaml(modify)
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: es-sample
spec:
version: 8.6.2
nodeSets:
- name: default
count: 1
config:
node.store.allow_mmap: false
http:
service:
spec:
type: LoadBalancer
tls:
selfSignedCertificate:
disabled: true
Elasticsearchのpodが立ったらrootパスワードを取得します
$ kubectl get secret
es-sample-es-elastic-user Opaque 1 12m
(他secret)
$ kubectl get secret es-sample-es-elastic-user -o go-template='{{.data.elastic | base64decode}}'
5Esma90y3459ppeh7ubsnU78 # 今回handsOn後Clusterを削除するのでパスワードを記載します
ElasticSearchに疎通してみましょう
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
es-sample-es-http LoadBalancer 10.120.11.252 34.146.210.75 9200:32020/TCP 10m
(他service)
$ curl -u "elastic:5Esma90y3459ppeh7ubsnU78" "http://34.146.210.75:9200"
{
"name" : "es-sample-es-default-0",
"cluster_name" : "es-sample",
"cluster_uuid" : "YNw1s2ywRoKukHh9mWdiTA",
"version" : {
"number" : "8.6.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "2d58d0f136141f03239816a4e360a8d17b6d8f29",
"build_date" : "2023-02-13T09:35:20.314882762Z",
"build_snapshot" : false,
"lucene_version" : "9.4.2",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
これでElasticsearchの疎通はできましたね!
しかしデータを入れないとElasticsearchの意味はありません
次にmongoDBとElasticsearchを同期させる方法について記載します
mongoDBとElasticsearchを同期させる
今回はデータベースとしてmongoDB Atlasを利用します。既にmongoAtlasをセッティングしている前提で説明します。
同期させるデーモンにはmonstacheを利用します。
MongoDB-Atlasのデータサンプル

まず初めにElasticsearchにmonstache用のId,Passwordを設定します。
k8s
L es.yaml
es-realm-secret.yaml(new)
kind: Secret
apiVersion: v1
metadata:
name: es-realm-secret
stringData:
users: |-
monstache:password
users_roles: |-
superuser:monstache
$ kubectl apply -f k8s/es-realm-secret.yaml
$ kubectl get secret es-realm-secret -o go-template='{{.data.users | base64decode}}'
monstache:password
パスワードは今回テスト用なのでそのままにしますが、secretを利用する際はちゃんとmaskして管理してください
また権限は今回、最高権限にしているので各自調整してください
このrealmで作成したsecretをElasticsearchに読み込ませます
k8s
L es.yaml(modify)
es-realm-secret.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: es-sample
spec:
version: 8.6.2
auth:
fileRealm:
- secretName: es-realm-secret
nodeSets:
- name: default
count: 1
config:
node.store.allow_mmap: false
http:
service:
spec:
type: LoadBalancer
tls:
selfSignedCertificate:
disabled: true
$ kubectl apply -f k8s/es.yaml
$ curl -u "monstache:password" "http://34.146.210.75:9200"
{
# Elasticsearch-Info
}
monstache用のパスワードでも疎通できることが確認できましたね
次にmonstache自体を構築していきます
最初にmonstacheの軽い説明をします。monstacheとはmongoDBとElasticsearchをリアルタイムで同期させることのできるデーモンになります。比較的簡単な設定項目で動かせることがメリットになります。
余談ですが最初は、Elastic社が出しているLogstashを利用しようとしたのですが、mongoDBからデータを取得するプラグインのlogstash-input-mongodbが正式版でないことと、コミットが2017年から止まっている(2023/03現在)ので候補から外しました。
ではmonstacheを作成していきます まずmonstacheとElasticsearchとで疎通するための情報をConfigMapとSecretで作成していきます
k8s
L es.yaml
es-realm-secret.yaml
monstache.yaml(new)
apiVersion: v1
kind: ConfigMap
metadata:
name: monstache-mount-config
data:
config.toml: |-
direct-read-namespaces = ["sample-database.sample-collection"]
change-stream-namespaces = ["sample-database.sample-collection"]
[[mapping]]
namespace = "sample-database.sample-collection"
index = "sample-collection"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: monstache-config
data:
elasticsearch-url: http://es-sample-es-internal-http:9200
---
apiVersion: v1
kind: Secret
metadata:
name: monstache-secret
stringData:
mongodb-url: "mongodb+srv://sample-database:password@sample-project.xxxxxxx.mongodb.net"
es-user: monstache
es-password: password
elasticsearch-urlのhttp://es-sample-es-internal-http:9200
はelasticsearchのserviceです。cluster内ではservice名を指定することでも通信することが可能になります。
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
es-sample-es-default ClusterIP None <none> 9200/TCP 43h
es-sample-es-http LoadBalancer 10.120.6.54 34.146.210.75 9200:30861/TCP 43h
es-sample-es-internal-http ClusterIP 10.120.5.234 <none> 9200/TCP 43h # このserviceのNAME
es-sample-es-transport ClusterIP None <none> 9300/TCP 43h
kubernetes ClusterIP 10.120.0.1 <none> 443/TCP 3d
次にmonstacheのDeploymentを作成します
monstacheに設定する環境変数は以下を参照ください
https://rwynn.github.io/monstache-site/config/#elasticsearch-user
k8s
L es.yaml
es-realm-secret.yaml
monstache.yaml(modify)
apiVersion: apps/v1
kind: Deployment
metadata:
name: monstache-deployment
labels:
app: monstache
spec:
replicas: 1
selector:
matchLabels:
app: monstache
template:
metadata:
labels:
app: monstache
spec:
containers:
- name: monstache
image: rwynn/monstache:6.7.11
ports:
- containerPort: 80
command: ["monstache", "-f", "/monstache/config.toml"]
env:
- name: MONSTACHE_ES_URLS
valueFrom:
configMapKeyRef:
name: monstache-config
key: elasticsearch-url
- name: MONSTACHE_MONGO_URL
valueFrom:
secretKeyRef:
name: monstache-secret
key: mongodb-url
- name: MONSTACHE_ES_USER
valueFrom:
secretKeyRef:
name: monstache-secret
key: es-user
- name: MONSTACHE_ES_PASS
valueFrom:
secretKeyRef:
name: monstache-secret
key: es-password
volumeMounts:
- name: monstache-volume
mountPath: /monstache
volumes:
- name: monstache-volume
configMap:
name: monstache-mount-config
--- # 以下は同じ
apiVersion: v1
kind: ConfigMap
metadata:
name: monstache-mount-config
data:
config.toml: |-
direct-read-namespaces = ["sample-database.sample-collection"]
change-stream-namespaces = ["sample-database.sample-collection"]
[[mapping]]
namespace = "sample-database.sample-collection"
index = "sample-collection"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: monstache-config
data:
elasticsearch-url: http://es-sample-es-internal-http:9200
---
apiVersion: v1
kind: Secret
metadata:
name: monstache-secret
stringData:
mongodb-url: "mongodb+srv://sample-database:password@sample-project.xxxxxxx.mongodb.net"
es-user: monstache
es-password: password
applyします
$ kubectl apply -f k8s/monstache.yaml
monstacheが正しく動いているか確認してみましょう
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
es-sample-es-default-0 1/1 Running 0 44h
monstache-deployment-5f9b8fdb4-9rnrt 1/1 Running 0 5s
$ kubectl logs monstache-deployment-5f9b8fdb4-9rnrt
INFO 2023/03/16 06:49:41 Started monstache version 6.7.11
INFO 2023/03/16 06:49:41 Go version go1.17.4
INFO 2023/03/16 06:49:41 MongoDB go driver v1.10.3
INFO 2023/03/16 06:49:41 Elasticsearch go driver 7.0.31
INFO 2023/03/16 06:49:41 Successfully connected to MongoDB version 5.0.15
INFO 2023/03/16 06:49:41 Successfully connected to Elasticsearch version 8.6.2
INFO 2023/03/16 06:49:41 Listening for events
INFO 2023/03/16 06:49:41 Watching changes on collection sample-database.sample-collection
問題なく動いてそうですね
Elasticsearchにちゃんとデータが入っているかみてみましょう
rootユーザーでElasticsearchにアクセスします
$ curl -u "elastic:5Esma90y3459ppeh7ubsnU78" http://34.146.210.75:9200/_cat/indices
yellow open sample-collection H9Z5mfJLQJCfIXuSXZbCwg 1 1 2 0 5kb 5kb
$ curl -u "monstache:password" http://34.146.210.75:9200/sample-collection/_search | jq .
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "sample-collection",
"_id": "641270d0bee8466c1c3edf95",
"_score": 1,
"_source": {
"name": "サンプルドキュメント2"
}
},
{
"_index": "sample-collection",
"_id": "6412709cbee8466c1c3edf94",
"_score": 1,
"_source": {
"name": "サンプルドキュメント1"
}
}
]
}
}
ちゃんとデータが入っていることが確認できました!これでElasticsearchのセットアップは完了になります。
あとはアプリケーションからSDK経由でデータを取得したり、 全文検索エンジンで使用したりするなど様々な使用用途があるのでお好きに
Kibana(ECK)構築
ついでですが、KibanaもECKだといい感じにElasticsearchと通信してくれます。
k8s
L es.yaml
es-realm-secret.yaml
monstache.yaml
kibana.yaml(new)
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana-sample
spec:
version: 8.6.2
count: 1
elasticsearchRef:
name: es-sample
http:
service:
spec:
type: LoadBalancer
tls:
selfSignedCertificate:
disabled: true
終わりに
今回は、Elasticsearchの構築、mongoDBとの連携まで行いました。意外とmongoDBとの連携記事がないので参考になればなと思います。
まだipでElasticsearchにアクセスしているので、次の記事ではドメインでのアクセスとssl化する方法について執筆できればなと思います。
最後に念を押しておきます。
機密情報を管理する際はSecretで作成したからといってGitHubに平文でpushするのはやめましょう。後々苦労します…
あと、LoadBalancerは個人で使用するにはかなり高いので作成しっぱなしには気をつけてください。後々後悔します…