はじめに
この記事はUzabase Advent Calendar 2025の19日目の記事です。
最近elasticsearchの機械学習関連の機能を試したくて、ローカル環境を構築しようとすると、セキュリティ周りにハマりました。本記事はそれについて共有します。
elasticsearch7系のローカル環境
昔はelasticsearch7系のローカル環境を構築したことがあります。もしelasticsearchだけの場合は、
docker run --rm -e "discovery.type=single-node" -p 9200:9200 elasticsearch:7.17.28
docker runですぐ起動できます。唯一設定する必要があるのは discovery.type=single-node という環境変数です。
elasticsearchはデフォルトが3台nodeでclusterを構築することになります。single-nodeに設定しないと、起動したcontainerはずっと他の同じcluster名のnode instanceを探していき、clusterのstatusはyellowのままで、greenに切り替えないことになります。
しかも、複数nodeで起動する場合virtual memoryが結構要請し、下記のエラーになることもあります。
bootstrap check failure [1] of [2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
これでelasticsearchは起動するし、http://localhost:9200 でアクセスできますが、それだけだと結構不便です。elasticsearchへは基本的にhttpリクエストでアクセスするし、設定、index構築、DSLクエリーなど、全部複雑なjsonでリクエストしないといけないので、生で書くのはづらいことになります。
kibanaという便利な運用ツールが有り、GUIでjsonを書けるし、色々suggestもしてくれてとても助かります。なので、elasticsearchとkibanaをセットで起動するのが便利です。下記はdocker-composeのサンプルです。
version: "3"
services:
elasticsearch:
image: elasticsearch:7.17.28
container_name: elasticsearch
environment:
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
- "discovery.type=single-node"
ports:
- 9200:9200
- 9300:9300
healthcheck:
test:
[
"CMD-SHELL",
"curl -s http://localhost:9200/_cluster/health?wait_for_status=green&timeout=5s",
]
interval: 10s
timeout: 10s
retries: 15
networks:
- es_kibana_nw
kibana:
image: kibana:7.17.28
container_name: kibana
environment:
- "ELASTICSEARCH_HOSTS=http://elasticsearch:9200"
ports:
- 5601:5601
networks:
- es_kibana_nw
depends_on:
elasticsearch:
condition: service_healthy
networks:
es_kibana_nw:
elasticsearchとkibanaは別々のcontainerで起動するのは、kibanaからelasticsearchへアクセスするためにはnetworkを用意しないといけいないです。
あとは、elasticsearchが起動したあとにkibanaを起動させるためには、healthcheckとdepends_onを用意してあります。healthcheckはelasticsearchのclusterの状況がgreen担っているかどうかを確認しています。
これで docker compose up で起動し、http://localhost:5601 でkibanaにアクセスできます。
左半分はリクエストを書くし、Ctrl+Enterで実行して、右半分はその結果が確認できます。空行を入れれば、複数リクエストを用意できるし、それがブラウザのlocal storageに保存されて、削除しない限りkibanaを再起動してもずっと残ってくれます。とても便利だと思います。
elasticsearch9系のローカル環境
せっかくなので今年リリースされた9系にして、機械学習関連の機能を試したいと思います。上記のdocker-compose.yamlに書いてあるelasticsearchとkibanaのversionを 9.2.2 にして、docker compose up実行したら、ログには特にエラーとかは表示されなかったですが、elasticsearchは起動しきれてなかったです。(localhost:9200でアクセスできなかった)。
よくログを見たら、下記の警告がずっと繰り返して吐き出してきます。
elasticsearch | {"@timestamp":"2025-12-18T15:27:35.404Z", "log.level": "WARN", "message":"received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress=/***.***.***.***:9200, remoteAddress=/***.***.***.***:56140}", "ecs.version": "1.2.0","service.name":"ES_ECS","event.dataset":"elasticsearch.server","process.thread.name":"elasticsearch[ee2b95f6c53f][transport_worker][T#2]","log.logger":"org.elasticsearch.http.netty4.Netty4HttpServerTransport","elasticsearch.cluster.uuid":"zK-E3WZHQceX6WmUHrJl3g","elasticsearch.node.id":"4WGXatD8TViqsFy9Q3CqNw","elasticsearch.node.name":"ee2b95f6c53f","elasticsearch.cluster.name":"docker-cluster"}
ちょっと調べたら、elasticsearch8系以後は大幅にセキュリティが強化されて、ssl通信もデフォルト有効になっていました。一旦 xpack.security.http.ssl.enabled=false を設定すれば、elasticsearchは起動できました。
が、localhost:9200にアクセスしたら、basic認証が求められました。
なに?アカウントは設定してないよ!そこでgemini先生にもっと詳しく教えてもらおうと思いました。
どうやら、8系からセキュリティが一気に強化されて、ssl通信と認証がデフォルトで有効になりました。7系までは、インストールしてすぐ9200を叩けば誰でもデータが見れる状態がデフォルトでした。しかし、これが原因でインターネット上に公開されたデータベースが乗っ取られる事件が多発したため、Elastic社は 8.0 から「インストールした瞬間から鍵がかかっている」状態に方針を転換しましたらしいです。
一応 xpack.security.enabled: false にすれば、7系と同じやり方で両方起動できますが、アプリ側にローカルだと認証は要らなくて、本番環境は認証必要という余計な複雑さを与えたくないし、負けた感も半端ないし、ssl以外はセキュリティを適用する方法でローカル環境を構築しようと思いました。
まずは ELASTIC_PASSWORD を環境変数に設定してみました。(デフォルトアカウントelasticのパスワードになります)
docker compose upで起動して、localhost:9200にアクセスし、elastic/passw0rdを入力するとelasticsearchはアクセスできました。
が、localhost:5601はずっとぐるぐるして画面が開けませんでした。ログを確認すると下記のエラーが吐き出されています。
kibana | [2025-12-18T16:06:45.149+00:00][ERROR][elasticsearch-service] Unable to retrieve version information from Elasticsearch nodes. security_exception
kibana | Root causes:
kibana | security_exception: missing authentication credentials for REST request [/_nodes?filter_path=nodes.*.version%2Cnodes.*.http.publish_address%2Cnodes.*.ip]
kibanaからelasticsearchへアクセスするためには、 ELASTICSEARCH_SERVICEACCOUNTTOKEN というトーケンの環境変数を設定しないといけないです。
そのトーケンはelasticsearchが起動したら、下記のリクエストで発行してくれます。
curl -u "elastic:passw0rd" -X POST "http://localhost:9200/_security/service/elastic/kibana/credential/token/token-for-kibana"
# response
{"created":true,"token":{"name":"token-for-kibana","value":"AAEAAWVsYXN0aWMva2liYW5hL3Rva2VuLWZvci1raWJhbmE6NFFkcHRVRDhUTUM1TE1wb3VqOEs5Zw"}}
そこで問題です! kibanaが起動するとき必要なトーケンは、elasticsearchが起動して特定なリクエストを投げて得られないといけないので、まずelasticsearchを起動して、トーケンを生成して、kibanaを起動するというマニュアルで実現するとdocker composeにする意味もなくなるし、面倒くさいしやりたくないなと思います。
それならば、kibanaの起動シェルをカスタマイズするほうが楽かもなと思いました。kibanaのdockerfileでは最終的には /usr/local/bin/kibana-docker というシェルで起動していて、自前でシェルを用意して、トーケンを生成してから起動させるようにすれば良いかなと思いました。
#!/bin/bash
set -e
TOKEN_RESPONSE=$(curl -s -u "elastic:${ELASTIC_PASSWORD}" -X POST "${ELASTICSEARCH_HOSTS}/_security/service/elastic/kibana/credential/token/token-for-kibana")
TOKEN=$(echo "$TOKEN_RESPONSE" | grep -oP '\"value\":\"\K[^\"]+')
ELASTICSEARCH_SERVICEACCOUNTTOKEN="$TOKEN" /usr/local/bin/kibana-docker
kibanaのdocker compose設定でも、そのシェルをvolume mountして起動コマンドとして使えばよいのではと思います。
できました!localhost:5601にアクセスして、elastic/passw0rdでログインしてようやくkibanaが使えるようになりました。
ただ、何も変更してないのに、docker compose upで起動したら、kibanaが起動したり起動しなかったりして不安定な状況でした。
調べてみたら、elasticsearchのトーケン発行は .security-7 というセキュリティ用indexが構築されてからじゃないと失敗してしまうことが分かりました。
つまり、docker composeではkibanaの起動前提はelasticsearchのclusterがgreenになるのは不十分で、.security-7というindexまで確認しないといけないです。
これでようやく安定しました!
さいごに
最終的には
docker-compose.yaml
version: "3"
services:
elasticsearch:
image: elasticsearch:9.2.2
container_name: elasticsearch
environment:
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
- "discovery.type=single-node"
- "xpack.security.http.ssl.enabled=false"
- "ELASTIC_PASSWORD=passw0rd"
ports:
- 9200:9200
- 9300:9300
healthcheck:
test:
[
"CMD-SHELL",
"curl -s http://localhost:9200/.security-7/_search",
]
interval: 10s
timeout: 10s
retries: 15
networks:
- es_kibana_nw
kibana:
image: kibana:9.2.2
container_name: kibana
environment:
- "ELASTICSEARCH_HOSTS=http://elasticsearch:9200"
- "ELASTIC_PASSWORD=passw0rd"
volumes:
- ./kibana-entrypoint_9_2_2.sh:/usr/local/bin/kibana-entrypoint.sh:ro
entrypoint: ["/bin/bash", "-c", "/usr/local/bin/kibana-entrypoint.sh"]
ports:
- 5601:5601
networks:
- es_kibana_nw
depends_on:
elasticsearch:
condition: service_healthy
networks:
es_kibana_nw:
kibana-entrypoint_9_2_2.sh
#!/bin/bash
set -e
TOKEN_RESPONSE=$(curl -s -u "elastic:${ELASTIC_PASSWORD}" -X POST "${ELASTICSEARCH_HOSTS}/_security/service/elastic/kibana/credential/token/token-for-kibana")
TOKEN=$(echo "$TOKEN_RESPONSE" | grep -oP '\"value\":\"\K[^\"]+')
ELASTICSEARCH_SERVICEACCOUNTTOKEN="$TOKEN" /usr/local/bin/kibana-docker
この2つファイルを同じ場所に置いておけば、docker compose upで起動できるようになりました!
できればsslも有効にしたかったですが、環境によってアプリの実装差異はないし、自前のssl証明書を用意したり設定したりするのも結構面倒だなと思って、コスパが悪くて割愛しました。
これで、9系のelasticsearchとkibanaのローカル開発環境について共有しました。機械学習関連はこれから試して次回書こうと思いました。
ここまで読んでくれてありがとうございます。ちょっとだけでも助けになれると幸いです。なにか間違いがあったら是非コメントください。よろしくお願いします。






