Download PDF
Download page Install the Java Agent in Containers.
Install the Java Agent in Containers
This page explains how to install the Java Agent in a containerized environment. See Container Installation Options to for your options. There are three options to install the Java Agent with containers:
Use Auto-Instrumentation
This scenario applies to containers running in Kubernetes where the Cluster Agent is installed.
This option uses the Auto-Instrumentation feature of the Cluster Agent. It is the recommended option because it offers the highest level of automation and the simplest operational experience for instrumenting Java applications in a Kubernetes cluster.
To get started, see Auto-Instrument Applications with the Cluster Agent.
Use Init Containers
This option uses Kubernetes init containers to copy the agent binaries into the application container. It is the recommended option when Auto-Instrumentation is not possible. In this use case, it assumes there are two containers:
- Application image is built without any Java Agent binaries.
- Second init container image is built and only contains the Java Agent binaries.
The deployment spec for the application is configured to copy the agent binaries to the running application container. To use init containers to copy the agent binaries:
- Build the Java Application Image
- Build the Java Agent Init Container Image
- Redirect Agent Output to Stdout
- Add the Init Container to the Deployment Spec
- Set the Java Agent Environment Variables
- Add the -javaagent Argument to the Deployment Spec
- (OpenShift only) Set the APPDYNAMICS_AGENT_UNIQUE_HOST_ID Environment Variable
- (On-Premises Controller only) Copy the Controller Certificates to the Container
Build the Java Application Image
When building the Java application image, do not include the Java Agent binaries.
Build the Java Agent Init Container Image
The Java Agent image is built separately from the application image and can be reused across multiple Java application deployments. This Dockerfile is an example of building the Java Agent init container image using a multi-stage build. Alternatively, the init container can reference a pre-built image from AppDynamics on Docker Hub.
Redirect Agent Output to Stdout
It is a best practice to redirect agent output to stdout
in containerized environments. If you build your own Java Agent init container image, you can redirect the Java agent output to stdout
by updating the ver<version>/conf/logging/
file that is provided in the download package. Add two log4j2.xml
<AppenderRef ref="Console"/>
elements as shown in the example log4j2.xml file. If you use a pre-built image from AppDynamics on Docker Hub, you can replace the default ver<version>/conf/logging/log4j2.xml
file based on the example log4j2.xml file using a ConfigMap. Follow the same procedure shown for Copy the Controller Certificates to the Container.
Add the Init Container to the Deployment Spec
Edit the deployment spec to add the required sections that allow you to copy the agent binaries from the init container to the application image.
The following snippet from a deployment spec shows the required volumes
, volumeMounts
, and initContainer
definitions. The code example assumes the Java application image is published to myrepo/java-app:v1
and the init container image uses the pre-built image docker.io/appdynamics/java-agent:20.6.0
:
kind: Deployment
spec:
containers:
- name: java-app
image: myrepo/java-app:v1
volumeMounts:
- mountPath: /opt/appdynamics
name: appd-agent-repo
initContainers:
- command:
- cp
- -r
- /opt/appdynamics/.
- /opt/temp
name: appd-agent
image: docker.io/appdynamics/java-agent:20.8.0
volumeMounts:
- mountPath: /opt/temp
name: appd-agent-repo
volumes:
- name: appd-agent-repo
emptyDir: {}
Set the Java Agent Environment Variables
To set all of the required Java Agent environment variables, you must follow these steps as explained in Best Practices to Configure Agents in Kubernetes.
- Use ConfigMaps to Configure the App Server Agent
Set Application-Specific Configuration in the Deployment Spec
Use ConfigMaps to Configure the App Server Agent
Use a ConfigMap to set the Java Agent environment variables that are shared across applications in a namespace:
apiVersion: v1 data: APPDYNAMICS_AGENT_APPLICATION_NAME: "eCommerce" APPDYNAMICS_AGENT_ACCOUNT_NAME: "<value>" APPDYNAMICS_CONTROLLER_HOST_NAME: "<value>" APPDYNAMICS_CONTROLLER_PORT: "<value>" APPDYNAMICS_CONTROLLER_SSL_ENABLED: "<value>" APPDYNAMICS_JAVA_AGENT_REUSE_NODE_NAME: "true" APPDYNAMICS_JAVA_AGENT_REUSE_NODE_NAME_PREFIX: "<value>" kind: ConfigMap metadata: name: ecommerce-java-config
YMLApply the ConfigMap to the namespace:
kubectl -n ecommerce apply -f ecommerce-java-config.yaml
BASHUpdate the deployment spec to reference the ConfigMap:
spec: containers: - name: java-app envFrom: - configMapRef: name: ecommerce-java-config ...
YML
Use Secrets for the Controller Access Key
Create a Secret using
kubectl
:kubectl -n ecommerce create secret generic appd-agent-secret --from-literal=access-key=<access-key>
BASHUpdate the deployment spec to reference the Secret:
spec: containers: - name: java-app env: - name: APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY valueFrom: secretKeyRef: name: appd-agent-secret key: access-key
YML
Set Application-Specific Configuration in the Deployment Spec
Set the application-specific tier name environment variable APPDYNAMICS_AGENT_TIER_NAME in the deployment spec:
spec:
containers:
- name: java-app
env:
- name: APPDYNAMICS_AGENT_TIER_NAME
value: ecommerce-service
Add the -javaagent
Argument to the Deployment Spec
Edit the deployment spec to add the -javaagent
argument to the application container startup command as shown in the example:
spec:
containers:
command: ["/bin/sh"]
args: ["-c", "java -javaagent:/opt/appdynamics/javaagent.jar -jar /myapp.jar"]
Note that for some Java frameworks such as Spring Boot, you can leverage the standard JAVA_TOOL_OPTIONS
environment variable to include the -javaagent
argument.
(OpenShift Only) Set the APPDYNAMICS_AGENT_UNIQUE_HOST_ID
Environment Variable
For Java applications running in OpenShift, set the APPDYNAMICS_AGENT_UNIQUE_HOST_ID
environment variable to enable APM correlation with the Cluster Agent. Since the APPDYNAMICS_AGENT_UNIQUE_HOST_ID
value depends on a runtime value, set this environment variable in the container startup command.
For example, for an OpenShift 3.10 or 3.11 environment, set the environment variable as shown:
spec:
containers:
command: ["/bin/sh"]
args: ["-c", "APPDYNAMICS_AGENT_UNIQUE_HOST_ID=$(sed -rn '1s#.*/##; 1s/docker-(.{12}).*/\\1/p' /proc/self/cgroup) && java -javaagent:/opt/appdynamics/javaagent.jar -jar /myapp.jar"]
For your use case, see values documented in Manually Configure App Agents to Correlate with the Cluster Agent.
(On-Premises Controller Only) Copy the Controller Certificates to the Container
If on-premises Controller certificates are required, define a ConfigMap to reference the cert
file and use a volume mount
in the deployment spec to mount the ConfigMap contents to the container.
$ kubectl create configmap appd-cert --from-file=cacerts.jks
Add the cert file, in this example, appd-cert
, to the container file system using volumes
and volumeMounts
as shown in the deployment spec snippet:
kind: Deployment
spec:
containers:
image: myrepo/java-app:v1
volumeMounts:
- name: appd-cert
subPath: cacerts.jks
mountPath: /opt/appdynamics/ver4.5.11.26665/conf/cacerts.jks
volumes:
- name: appd-cert
configMap:
name: appd-cert
Example Configuration for Using an Init Container
A complete example of a deployment spec that uses an init container to copy the agent binaries can be found on Github: java-app.yaml
.
Use a Dockerfile
This option uses a Dockerfile to copy the Java Agent to the Docker image at build time. To use the option, create a single image that contains both the application and Java Agent binaries.
To copy the agent into the application image during the Docker image build:
- Download and Unzip the Java Agent
- Redirect Agent Output to Stdout
- Copy the Agent Binaries to the Image
- Set the Java Agent Environment Variables
- Add the -javaagent Argument to the Startup Command
- (OpenShift only) Set the APPDYNAMICS_AGENT_UNIQUE_HOST_ID Environment Variable
- (On-Premises Controller only) Copy the Controller Certs to the Image
Download and Unzip the Java Agent
Download the Java Agent from or programmatically download the agent. The agent is packaged as a zip file. In a shell, unzip the AppServer Agent to the AppServerAgent
directory:
$ unzip AppServerAgent-<version>.zip -d AppServerAgent
Redirect Agent Output to Stdout
It is a best practice to redirect agent output to stdout
in containerized environments. To redirect the output of the Java Agent to stdout
, edit the ver<version>/conf/logging/log4j2.xml
file and add two <AppenderRef ref="Console"/>
elements as shown in the example log4j2.xml file.
Copy the Agent Binaries to the Image
Edit the Dockerfile to copy the unpackaged agent binaries to the target folder:
COPY AppServerAgent/ /opt/appdynamics/
Set the Java Agent Environment Variables
If you are running the application in a non-Kubernetes environment (using docker
run
for example), set the agent environment variables in the Dockerfile. For example:
ENV APPDYNAMICS_AGENT_APPLICATION_NAME=<value>
ENV APPDYNAMICS_AGENT_TIER_NAME=<value>
ENV APPDYNAMICS_AGENT_ACCOUNT_NAME=<value>
ENV APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY=<value>
ENV APPDYNAMICS_CONTROLLER_HOST_NAME=<value>
ENV APPDYNAMICS_CONTROLLER_PORT=<value>
ENV APPDYNAMICS_CONTROLLER_SSL_ENABLED=<value>
ENV APPDYNAMICS_JAVA_AGENT_REUSE_NODE_NAME=true
ENV APPDYNAMICS_JAVA_AGENT_REUSE_NODE_NAME_PREFIX=<value>
For Kubernetes applications, omit these environment variables from the Dockerfile and set them using ConfigMaps and Secrets as described in Best Practices to Configure Agents in Kubernetes.
The reuse node name and prefix environment variables are required to support unique naming for multiple container instances for the same application image. See Reuse Node Name.
Add the -javaagent
Argument to the Startup Command
Edit the Dockerfile to include the -javaagent
argument in the image start command based on the location where the agent binaries were copied. For example:
ENV JAVA_OPTS -javaagent:/opt/appdynamics/javaagent.jar
CMD ["java", "$JAVA_OPTS", "-jar", "/myapp.jar"]
Note that for some Java frameworks such as Spring Boot, the existing JAVA_TOOL_OPTIONS
environment variable can be leveraged to include the -javaagent
argument.
(OpenShift Only) Set the APPDYNAMICS_AGENT_UNIQUE_HOST_ID
Environment Variable
For Java applications running in OpenShift, set the APPDYNAMICS_AGENT_UNIQUE_HOST_ID
environment variable to enable APM correlation with the Cluster Agent. Since the value depends on a runtime value, set this environment variable in the image startup script using the values documented in Manually Configure App Agents to Correlate with the Cluster Agent. For example, for an OpenShift 3.10 or 3.11 environment, add this startup script startup.sh
to the Docker image:
#!/bin/bash
# OpenShift 3.10 or 3.11:
APPDYNAMICS_AGENT_UNIQUE_HOST_ID=$(sed -rn '1s#.*/##; 1s/docker-(.{12}).*/\\1/p' /proc/self/cgroup)
exec java $JAVA_OPTS -jar /myapp.jar
and update the startup command in the Dockerfile to reference it:
CMD ["/startup.sh"]
(On-Premises Controller Only) Copy the Controller Certs to the Image
For Java Agents communicating with an on-premises Controller, edit the Dockerfile to copy the cert file containing the on-premises certs to the agent conf
folder. For example:
COPY ./onprem-cacerts.jks /opt/appdynamics/ver4.5.11.26665/conf/cacerts.jks
See Enable SSL for the Java Agent.
Example Configuration for Using a Dockerfile
This Dockerfile builds a single image with the application and agent binaries included and this Kubernetes deployment spec references that Docker image. This Dockerfile uses a multi-stage build and downloads and unzips the agent.
Example Log4j Configuration File
This updated ver<version>/conf/logging/log4j2.xml
configuration file redirects the Java Agent logs to stdout. It adds <AppenderRef ref="Console"/>
to the Root element.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="2">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%t] %d{ABSOLUTE} %5p %c{1} - %m%n" />
</Console>
<ADRRAFAppender name="DefaultAppender" fileName="agent.log">
<PatternLayout pattern="[%t] %d{DATE} %5p %c{1} - %m%n" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
<RegexFilter regex=".*REST.*" onMatch="DENY" onMismatch="ACCEPT" />
</ADRRAFAppender>
<ADRRAFAppender name="BCTAppender" fileName="ByteCodeTransformer.log">
<PatternLayout pattern="[%t] %d{DATE} %5p - %m%n" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
</ADRRAFAppender>
<ADRRAFAppender name="RESTAppender" fileName="REST.log">
<PatternLayout pattern="[%t] %d{DATE} %5p %c{1} - %m%n" />
<RegexFilter regex=".*REST.*" onMatch="ACCEPT" onMismatch="DENY" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
</ADRRAFAppender>
<ADRRAFAppender name="DynamicServiceAppender" fileName="dynamic-service.log">
<PatternLayout pattern="[%t] %d{DATE} %5p %c - %m%n" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
</ADRRAFAppender>
<ADRRAFAppender name="BusinessTransactionsLogger" fileName="BusinessTransactions.log">
<PatternLayout pattern="[%t] %d{DATE} %5p - %m%n" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
</ADRRAFAppender>
<ADRRAFAppender name="DataPipelineAppender" fileName="data-pipeline.log">
<PatternLayout pattern="[%t] %d{DATE} %5p %c - %m%n" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
</ADRRAFAppender>
<ADRRAFAppender name="APIAppender" fileName="api.log">
<PatternLayout pattern="[%t] %d{DATE} %5p %c - %m%n" />
<SizeBasedTriggeringPolicy size="20 MB" />
<ADRolloverStrategy max="5" />
</ADRRAFAppender>
</Appenders>
<Loggers>
<!-- to control the logging level of the agent log files, change "level" attribute. level="all|trace|debug|info|warn|error"-->
<AsyncLogger name="com.singularity" level="info" additivity="false">
<AppenderRef ref="DefaultAppender" />
<AppenderRef ref="RESTAppender" />
</AsyncLogger>
<AsyncLogger name="com.singularity.BusinessTransactions" level="info" additivity="false">
<AppenderRef ref="BusinessTransactionsLogger" />
</AsyncLogger>
<AsyncLogger name="com.singularity.dynamicservice" level="info" additivity="false">
<AppenderRef ref="DynamicServiceAppender" />
</AsyncLogger>
<AsyncLogger name="com.singularity.ee.service.datapipeline" level="info" additivity="false">
<AppenderRef ref="DataPipelineAppender" />
</AsyncLogger>
<AsyncLogger name="com.singularity.datapipeline" level="info" additivity="false">
<AppenderRef ref="DataPipelineAppender" />
</AsyncLogger>
<AsyncLogger name="com.singularity.BCTLogger" level="info" additivity="false">
<AppenderRef ref="BCTAppender" />
</AsyncLogger>
<AsyncLogger name="com.singularity.api" level="info" additivity="false">
<AppenderRef ref="APIAppender" />
</AsyncLogger>
<AsyncLogger name="com.singularity.segment.TxnTracer" level="info" additivity="false">
<AppenderRef ref="DefaultAppender" />
</AsyncLogger>
<Root level="error">
<AppenderRef ref="DefaultAppender" />
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>