環境
- java: 17
- Gradle: 8.1.1
- NewRelic java agent: 8.6.0
モチベーション
NewRelic java agent の公式のインストール手順では、JAR ファイルをダウンロードすることになっています。
今どき JAR ファイルを手作業でダウンロードというのは前時代的ですし、依存性を追跡できないのでアップデートも億劫になりがちです。
また、CI の過程で JAR を都度ダウンロードさせる場合は、(CI が頻発することで) 公式のホスティングサイトに多大なトラフィックが流れていかないよう、ダウンロード結果をキャッシュしておくのがマナーですが、この実装も地味に面倒です。
やはり maven や gradle の dependency として管理したいところです。
newrelic.jar デカすぎ問題
そうは言っても、JAR なんてたまにダウンロードして commit しておくだけじゃんという反論もあると思います。
まあそうなんですが、 newrelic.jar ファイルって結構デカいんです。
(執筆時点で 22MB)
私の環境では、デプロイに AWS CodePipeline を使っているのですが、ソースリポジトリに newrelic.jar が含まれている状態だと Source artifact のサイズ制限に引っかかってビルドステージがエラーになってしまいました。
https://docs.aws.amazon.com/codepipeline/latest/userguide/limits.html
なので、CI/CD 中にダウンロードせざるを得ない状況でした。
そもそも JAR は Maven central に登録されているのか?
NewRelic java agent のリポジトリ (newrelic/newrelic-java-agent) の README を見ると、以下のようにあります。
Java agent releases follow semantic versioning conventions. See Java agent release notes for full details on releases and downloads. Java agent artifacts can also be found on Maven.
リンクを辿ると com.newrelic.agent.java
という Group ID で複数のアーティファクトが登録されているのがわかります。
これらのアーティファクトのうち、 newrelic-agent
が javaagent 本体、 newrelic-api
が NewRelic APM の API のようです。
ということで、Maven central には JAR ファイルが登録されているようです。
プロジェクト設定
maven / gradle の依存性管理に任せられる条件は整っていることを確認できたので、
プロジェクトの設定をしていきます。
ディレクトリ構成
ディレクトリ構成は、以下の通りとします。
(gradle wrapper 等のファイルは省略してます)
${PROJECT_ROOT}
|
+-- build.gradle
+-- settings.gradle
+-- newrelic/
| |
| +-- newrelic.yml
| +-- extensions/
| |
| +-- extension-xxx.xml
|
+-- src/
ポイントは、 newrelic
ディレクトリの下に NewRelic の設定ファイルが置いてある点です。
公式の newrelic.zip を解凍して設定ファイルだけ残したような状態です。
(NewRelic の用語的には、これが newrelic.home
ディレクトリに相当)
それ以外は普通の gradle プロジェクトです。
build.gradle
以下のように設定しました。
buildscript {
ext {
newrelicVersion = "8.6.0"
}
}
configurations {
runtimeAgent
}
dependencies {
implementation "com.newrelic.agent.java:newrelic-api:${newrelicVersion}"
runtimeAgent "com.newrelic.agent.java:newrelic-agent:${newrelicVersion}"
}
bootRun {
jvmArgs = [
"-javaagent:${configurations.runtimeAgent.singleFile}",
'-Xshare:off'
]
// newrelic.yml が newrelic-agent.jar と違うディレクトリにあるので指定が必要
systemProperty 'newrelic.home', 'newrelic'
}
//
// 以下は NewRelic の Custom instrumentation 設定ファイル (extensions/*.xml) の
// validation をしたい場合のみ必要。
//
task newrelicValidate() {
ext {
fileLocation = 'newrelic/extensions'
targets = fileTree(fileLocation).include('**/*.xml')
}
description "Validate NewRelic instrument file (${fileLocation})"
targets.each { xml ->
doLast {
javaexec {
classpath = sourceSets.main.runtimeClasspath + configurations.runtimeAgent
mainClass = 'com.newrelic.bootstrap.BootstrapAgent'
args = ['instrument', '-file', xml.absolutePath, '-debug', 'true']
}
}
}
dependsOn tasks.named('compileJava')
onlyIf { !targets.isEmpty() }
}
//
// 以下は Jib でコンテナをビルドする場合のみ必要。
//
task jibPrepare(type: Copy) {
ext {
destDir = "${project.buildDir}/newrelic"
}
doFirst {
mkdir destDir
}
from 'newrelic'
from "${configurations.runtimeAgent.singleFile}"
into destDir
}
jib {
// ...snip...
container {
jvmFlags = [
"-javaagent:./newrelic/${configurations.runtimeAgent.singleFile.name}",
'-Xshare:off'
]
appRoot = '/app'
workingDirectory = '/app'
extraDirectories {
paths {
path {
from = "${project.buildDir}/newrelic"
into = '/app/newrelic'
}
}
}
}
}
tasks.jibDockerBuild.dependsOn jibPrepare
動作確認
設定できたので、ビルドして動かしてみます。
bootRun
bootRun でサーバを起動してみます。
$ NEW_RELIC_LICENSE_KEY=xxxxxxxxx ./gradlew bootrun
> Task :bootRun
2023-10-25T19:14:22,922+0900 [58328 1] com.newrelic INFO: New Relic Agent: Loading configuration file "newrelic/./newrelic.yml"
(...snip...)
2023-10-25T19:14:26,319+0900 [58328 1] com.newrelic INFO: New Relic Agent v8.6.0 has started
2023-10-25 19:14:33.535 INFO Application - Started Application in 0.984 seconds
ログから Agent がきちんと起動していることがわかります。
image の内容
私は Jib でコンテナイメージをビルドしているので、作成されたイメージの中に JAR が含まれているか確認します。
$ ./gradlew jibDockerBuild
(...snip...)
$ docker run --entrypoint '' --rm -it xxx:xxx ls -l /app/newrelic
total 37176
drwxr-xr-x 2 root root 4096 Jan 1 1970 extensions
-rw-r--r-- 1 root root 38040677 Jan 1 1970 newrelic-agent-8.6.0.jar
-rw-r--r-- 1 root root 19784 Jan 1 1970 newrelic.yml
所定の位置にファイルが配置されています。
まとめ
NewRelic java agent の JAR ファイルを Gradle で管理する方法を紹介しました。
試してませんが、同じ方法で Datadog や OpenTelemetry の javaagent も設定できるんじゃないかと思います。
別の方法として、 javaagent を簡単に設定するための Gradle プラグインを開発している方もいるようです。
まだ知名度がそんなに高くなさそうなので、 version 1.0 になったら採用を検討しようと思います。