This module contains the native agent used to profile a java application and report the results to Drill4J admin services.
- bootstrap: See description below
- java-agent: Java-agent itself
- pt-runner: Module with scripts to run demo application
- commonMain: consist of empty
expected
classes (transformers, plugin related and request-processing related classes) and few utility classes (serialization functions andClassSource
models) - jvmMain: classes for initial class scanning and transformation
com.epam.drill.agent.classloading
: initial class scanning without transformationcom.epam.drill.agent.instrument
: java-classes instrumentation for http-headers processingcom.epam.drill.agent.instrument.Transformer
:ServletContextListeneer
instrumentation for web-app classes initial scanning
com.epam.drill.request
: http-request headers processing (mainly related withdrill-session-id
)
- nativeMain:
com.epam.drill.agent
: agent configuration and state classescom.epam.drill.agent.classloading
: stubs to call jvm-part of the same package (using KNI)com.epam.drill.agent.instrument
: stubs to call jvm-part of the same package (using KNI)com.epam.drill.core
:Starter.kt
: JVMTI entrypoint, call agent configuration classes and registers hooks on JVM initialization and class-loadingCallbackRegister.kt
: Internal callback initializationSymbolsRegister.kt
: Native functions to call from plugins usingcom.epam.drill.plugin.api.Native
class
com.epam.drill.core.callbacks.vminit
: Agent initialization callbacks (connects to admin, reads package settings)com.epam.drill.core.callbacks.classloading
: Classloading callback (applies default transformers from agent-jvm-part and transformers from loaded plugins)com.epam.drill.core.plugin.loader
: Plugin load and initialization related classes
To run the demo application (Petclinic) with a built agent following command may be used:
./gradlew :pt-runner:run
The Petclinic application will be started on port 8080, and agent will be configured to connect to the admin back-end on port 8090.
Problems before:
- Need to update version agent, but restart of app with agent takes a lot of time, and it breaks CI/CD process. We can find the moment when app will be restarted, but it is not convenient.
- On Windows, we can't remove files(agent) while process is running
Bootstrap solve these problems, now we can change dirs to java agent. After restart app we will use new java agent.
Alpine-linux uses MUSL implementation of libc, so it imposes following restrictions:
- Agent should be built without http-hook support (
nativeAgentHookEnabled = false
, in gradle.properties) - Additional configuration in Alpine linux should be performed before run agent:
apk add gcompat
apk add libc6-compat
apk add libgcc
ln -s /lib/libc.so.6 /lib/libresolv.so.2
Simplest and least invasive way to add agent into k8s application is to use init-containers. It this case no additional images or modifications of existing application images is required.
There is an example of configuration for k8s and Helm:
apiVersion: apps/v1
kind: Deployment
...
spec:
...
template:
...
spec:
...
initContainers:
{{- if .Values.drill4j.enabled }}
- name: "install-drill4j-agent"
image: "busybox"
imagePullPolicy: "IfNotPresent"
command: [ 'sh', '-c', "wget https://github.com/Drill4J/java-agent/releases/download/v{{ .Values.drill4j.version }}/agent-linuxX64-{{ .Values.drill4j.version }}.zip -O /opt/drill4j-agent/agent-linuxX64-{{ .Values.drill4j.version }}.zip && unzip /opt/drill4j-agent/agent-linuxX64-{{ .Values.drill4j.version }}.zip -d /opt/drill4j-agent && rm /opt/drill4j-agent/agent-linuxX64-{{ .Values.drill4j.version }}.zip" ]
volumeMounts:
- name: drill4j-agent
mountPath: "/opt/drill4j-agent"
{{- end }}
...
containers:
...
{{- if .Values.drill4j.enabled }}
- name: JAVA_TOOL_OPTIONS
value: "-agentpath:/opt/drill4j-agent/linuxX64-{{ .Values.drill4j.version }}/libdrill_agent.so=adminAddress={{ .Values.drill4j.adminAddress }},agentId={{ .Values.drill4j.agentId }},groupId={{ .Values.drill4j.groupId }},packagePrefixes={{ .Values.drill4j.packagePrefixes }},buildVersion={{ .Values.sometag }}"
{{- end }}
...
volumeMounts:
...
{{- if .Values.drill4j.enabled }}
- name: drill4j-agent
mountPath: "/opt/drill4j-agent"
{{- end }}
...
volumes:
...
{{- if .Values.drill4j.serviceapi.enabled }}
- name: drill4j-agent
emptyDir: {}
{{- end }}