使用IntelliJ从源代码中构建Elasticsearch,并进行调试执行
您好,我是Elastic的顾问杉森。这是我在Qiita上的第一篇文章,请多多指教。
在这篇文章中,我想介绍一下一个所有工程师都想尝试却因为麻烦而一直搁置的任务,也就是从源代码开始构建Elasticsearch并在IntelliJ中进行调试。虽然没有太多困难,但是初次构建可能需要一些时间,请注意这一点。
这篇文章是为那些像我这样不太熟悉IntelliJ的人准备的。请老手们温柔地用眼光关注。
The premise. (前提)
本文所述基本假设为以下:
-
- Macのローカルでビルド(IntelMac, macOS Montereyで確認しています)
-
- JavaはSDKManでインストールしたJDK17をつかう
-
- IntelliJ IDEA 2022.1.2 (Community Edition)
- mainブランチのHEADからビルド(執筆時点ではElasticsearchバージョン8.4)
安装JDK
这次安装JDK将使用SDKMan。按照SDKMan官方网站的指示:
$ curl -s "https://get.sdkman.io" | bash
可以通过安装来完成。
在Elasticsearch 8系中需要使用JDK 17或以上版本。我们可以使用SDKMan来查找并安装Java。
$ sdk list java
因为没有个人推荐的发行版,所以这次只是安装我曾经使用过的17.0.3-zulu。
$ sdk install java 17.0.3-zulu
我会确认是否成功安装。
$ which java
/Users/daixque/.sdkman/candidates/java/current/bin/java
$ java -version
openjdk version "17.0.3" 2022-04-19 LTS
OpenJDK Runtime Environment Zulu17.34+19-CA (build 17.0.3+7-LTS)
OpenJDK 64-Bit Server VM Zulu17.34+19-CA (build 17.0.3+7-LTS, mixed mode, sharing)
你看起来很坚强。
弹性搜索的构建
然后下载Elasticsearch的代码并进行构建。构建的方法和其他相关信息可以在README.asciidoc和CONTRIBUTING.md文件中找到。
首先,从Github上将代码进行克隆。由于代码规模较大,所以需要一些时间(根据环境可能需要大约10分钟左右)。
$ git clone git@github.com:elastic/elasticsearch.git
Elastic 最近将开发的主分支从 master 迁移到了 main。如果克隆成功,请检出 main 分支。
$ cd elasticsearch
$ git checkout -b main origin/main
顺便提一下,本文撰写时的 main 分支的首个提交是 d7b6a32d666f6232d8c4a0ce4e089b270e84e0c1。
我们要进行构建。正如您所知,首次构建将花费相当长的时间来收集依赖库(大约30分钟?),所以请耐心等待。
然而,如后面所解释的那样,您也可以使用IntelliJ进行构建。如果您想缩短下载库的时间等,可以将其导入IntelliJ并跳过调试运行阶段。
按照README.asciidoc中所述,执行以下命令。
$ ./gradlew localDistro
当您构建完成后,请使用以下命令启动。
$ ./gradlew run
让我们从另一个终端使用以下命令确认连接。当您使用gradle启动时,会添加基本身份验证,但您可以使用用户名elastic和密码password访问。
$ curl elastic:password@localhost:9200
如果一切顺利,响应应该以以下方式输出。
{
"name" : "runTask-0",
"cluster_name" : "runTask",
"cluster_uuid" : "ZaPn71FLQbyu3HpbCgW3YQ",
"version" : {
"number" : "8.4.0-SNAPSHOT",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "d7b6a32d666f6232d8c4a0ce4e089b270e84e0c1",
"build_date" : "2022-07-08T14:25:14.712327Z",
"build_snapshot" : true,
"lucene_version" : "9.3.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
Elasticsearch成功编译完毕,没有遇到任何困难。
将其导入到 IntelliJ 并进行调试执行。
请参考Elastic官方博客文章,详细了解如何将此次构建的Elasticsearch代码库导入IntelliJ。
请在IntelliJ中的“Open”选项中选择elasticsearch文件夹并打开。
然后会自动开始进行依赖关系的导入和编译处理。此外,关于二进制构建,可以直接在命令行中使用gradlew进行构建,并且当然也可以使用IntelliJ的Gradle在本地执行localDistro任务。
当导入完成后,您需要在运行/调试配置中进行调试执行的设置。
一般情况下,在导入项目的时候,IntelliJ 应该会自动适当地进行设置。在我的环境中,状态如下,并且保持这样就可以了。要点是调试模式要选择「监听远程 JVN」,同时自动重启选项要被勾选上。
然后,首先在IntelliJ中进行调试执行。
在这个时间点上,IntelliJ并没有发生任何特别的事情。
下一步,我们将在命令行中以调试模式启动Elasticsearch。
$ ./gradlew run --debug-jvm
如果IntelliJ的调试未启动,则此启动将失败,请在启动之前启动IntelliJ并重新运行gradlew。如果成功,将在IntelliJ的调试控制台中输出以下类似消息。
Connected to the target VM, address: 'localhost:5007', transport: 'socket'
那么,让我们在这里设置一个断点,实际上跟踪处理的流程。
这次我们将在org.elasticsearch.rest.action.RestMainAction#convertMainResponse方法中设置一个断点。
我们来再次调用下面的API试试。
curl elastic:password@localhost:9200
哇,实际上可以停下来并查看内部变量的状态啊。如果能够做到这一点,那么在许多情况下应该已经足以追踪内部操作的动态了吧。
将已构建的模块附加到IntelliJ。
如果你想在源代码级别确认Elasticsearch的行为,我认为大体上都是可以的。但是,当我在研究配置文件周围的事情时,我还不理解如何在调试运行中更改elasticsearch.yml的加载方式。所以,本次我们将尝试通过调试运行已构建的模块,并将其附加到IntelliJ上。
从这里开始,操作与普通的Elasticsearch二进制版本相同,请参考官方的安装步骤等进行操作。
构建的模块将被创建在以下位置。
build/distribution/local/elasticsearch-8.4.0-SNAPSHOT
我要确认文件夹里的内容。
$ ls
LICENSE.txt NOTICE.txt README.asciidoc bin config jdk.app lib logs modules plugins
配置文件是config/elasticsearch.yml。根据需要进行修改。由于这次是为了本地调试的目的,所以为了方便进行操作验证,我们将允许没有加密的HTTPS连接,并不需要考虑安全性问题。
xpack.security.http.ssl:
enabled: false
现在,为了在IntelliJ中进行附加,您需要添加JVM启动选项。请参考“Run/Debug Configuration”对话框中显示的示例,在config/jvm.options文件中追加以下行。请注意,地址因环境而异,请务必注意。
-agentlib:jdwp=transport=dt_socket,server=n,address=192.168.1.8:5007,suspend=y
然后,我们可以使用以下命令启动Elasticsearch。
$ bin/elasticsearch
如果成功启动,将在日志中输出以下信息。这些是使用此构建所需的重要信息,请记下来。(即使遗失,您也可以使用bin文件夹下的工具进行恢复。)
✅ Elasticsearch security features have been automatically configured!
✅ Authentication is enabled and cluster connections are encrypted.
ℹ️ Password for the elastic user (reset with `bin/elasticsearch-reset-password -u elastic`):
YX91nt7z*pp4LhTJy36R
ℹ️ HTTP CA certificate SHA-256 fingerprint:
10c57bd1987ab2f9144358e34456007b6fd08f3cd9614cd05b9d06df901c785a
ℹ️ Configure Kibana to use this cluster:
• Run Kibana and click the configuration link in the terminal when Kibana starts.
• Copy the following enrollment token and paste it into Kibana in your browser (valid for the next 30 minutes):
eyJ2ZXIiOiI4LjQuMCIsImFkciI6WyIxOTIuMTY4LjE0Ny4zNzo5MjAwIl0sImZnciI6IjEwYzU3YmQxOTg3YWIyZjkxNDQzNThlMzQ0NTYwMDdiNmZkMDhmM2NkOTYxNGNkMDViOWQwNmRmOTAxYzc4NWEiLCJrZXkiOiJIWi1POVlFQk9Ga18tUl8zdkwzczpPQXZOSkltUlRZR2RWeGFjZlJoN3RRIn0=
ℹ️ Configure other nodes to join this cluster:
• On this node:
⁃ Create an enrollment token with `bin/elasticsearch-create-enrollment-token -s node`.
⁃ Uncomment the transport.host setting at the end of config/elasticsearch.yml.
⁃ Restart Elasticsearch.
• On other nodes:
⁃ Start Elasticsearch with `bin/elasticsearch --enrollment-token <token>`, using the enrollment token that you generated.
当然,这些信息本来是安全的。由于这次只是用于临时构建,所以我们将其公开,但是请严格管理生产环境的信息。现在,我将使用这个用户访问API。
$ curl -u "elastic:YX91nt7z*pp4LhTJy36R" localhost:9200
运行得很好。如果设置了断点,应该能够在那里正确跟踪处理过程。如果输出类似于curl:(52)服务器的空回复,则请检查是否禁用了HTTPS。
辛苦了!现在你可以随时进行Elasticsearch的调试,即使碰到bug也可以修复后提交PR!顺便一提,如果真的要提交PR,请参考CONTRIBUTING.md。
故障排查
在一次构建过程中出现了以下错误的现象。
Execution failed for task ':build-tools-internal:jar'.
> Entry org/elasticsearch/gradle/internal/AntFixtureStop$_setFixture_closure1.class is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.0.2/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.
...
当发生这种情况时,删除build-tools-internal/build目录似乎可以使构建恢复正常。