Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(agent): update Cryostat Agent content #155

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 26 additions & 146 deletions get-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,10 @@ name the `9097` service port `jfr-jmx`. **Cryostat** will detect and use this po
to determine that this is a compatible Java application that it should monitor.

#### [Configuring Applications](#configuring-applications)
There are three methods of configuring your Java applications so that **Cryostat** is able to discover and monitor them:
There are two methods of configuring your Java applications so that **Cryostat** is able to discover and monitor them:

1. [using the **Cryostat Agent** for discovery and connectivity](#using-the-cryostat-agent)
2. [using platform mechanisms for discovery and Java Management Extensions (**JMX**) for connectivity](#using-jmx)
3. [using the **Cryostat Agent** for discovery and **JMX** for connectivity](#using-the-cryostat-agent-with-jmx)

The following sections will briefly explain how to accomplish each of these approaches by example. For simplicity the examples will assume your application
is built with **Maven**, packaged into an image with a `Dockerfile`, and running in **Kubernetes**, but the procedure will be similar for other toolchains and platforms as well.
Expand Down Expand Up @@ -309,6 +308,7 @@ or you may use the following snippet in your `pom.xml` to streamline this.
<groupId>io.cryostat</groupId>
<artifactId>cryostat-agent</artifactId>
<version>{{ site.data.versions.agent.version }}</version>
<classifier>shaded</classifier>
</artifactItem>
</artifactItems>
<stripVersion>true</stripVersion>
Expand All @@ -325,14 +325,14 @@ or you may use the following snippet in your `pom.xml` to streamline this.

<span style="color:red">**Note**:</span> You may be required to [authenticate to the GitHub Maven Packages registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#installing-a-package) in order to pull this **JAR**.

The next time we build our application, the **Cryostat Agent** **JAR** will be located at `target/dependency/cryostat-agent.jar`. Then we can update our **Dockerfile**:
The next time we build our application, the **Cryostat Agent** **JAR** will be located at `target/dependency/cryostat-agent-shaded.jar`. Then we can update our **Dockerfile**:

```Dockerfile
...
COPY target/dependency/cryostat-agent.jar /deployments/app/
COPY target/dependency/cryostat-agent-shaded.jar /deployments/app/
...
# We are using a framework where the JAVA_OPTS environment variable can be used to pass JVM flags
ENV JAVA_OPTS="-javaagent:/deployments/app/cryostat-agent.jar"
# Assume we are using an application framework where the JAVA_OPTS environment variable can be used to pass JVM flags
ENV JAVA_OPTS="-javaagent:/deployments/app/cryostat-agent-shaded.jar"
```

Next we must rebuild our container image. This is specific to your application but will likely look something like `docker build -t docker.io/myorg/myapp:latest -f src/main/docker/Dockerfile .`.
Expand Down Expand Up @@ -366,7 +366,14 @@ spec:
# choose to configure the Agent to communicate with a Cryostat in
# a different Namespace, too.
# (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)
value: https://cryostat.$(NAMESPACE).svc:8181
value: https://cryostat.$(NAMESPACE).svc.cluster.local:8181
- name: CRYOSTAT_AGENT_API_WRITES_ENABLED
# Set this to 'true' to turn on the "write" or "mutation" capabilities of the
# Agent's HTTP API. This defaults to 'false', so the Agent HTTP API only exposes
# readonly access to certain low-sensitivity calls. If this is 'true' then the
# Agent will allow Cryostat to dynamically request JFR recordings to be started,
# stopped, deleted, etc. as well as listed and retrieved.
value: true
- name: POD_IP
valueFrom:
fieldRef:
Expand Down Expand Up @@ -411,6 +418,13 @@ spec:

More details about the configuration options for the **Cryostat Agent** [are available here](https://github.com/cryostatio/cryostat-agent/blob/{{site.data.versions.cryostat.release-branch}}/README.md#configuration).

You may also be interested in using the **Cryostat Agent** for application discovery, but using **JMX** for remote management and data
access rather than the **Cryostat Agent** HTTP API. In that case, simply set `CRYOSTAT_AGENT_API_WRITES_ENABLED=false` to turn off as much
of the **Cryostat Agent** HTTP API as possible, then continue to [the next section](#using-jmx) to additionally configure your application
to enable and expose **JMX** for remote management and data access. If the **Cryostat Agent** detects that the application it is attached
to has **JMX** enabled then it will publish itself to the **Cryostat** server with both an **Agent** HTTP URL and a **JMX** URL. If **JMX**
is not detected then it will only publish the HTTP URL.

##### [Using JMX](#using-jmx)
**Cryostat** is also able to use Java Management Extensions (**JMX**) to communicate with target applications. This is a standard JDK feature that can be enabled by passing **JVM**
flags to your application at startup. A basic and insecure setup suitable for testing requires only the following three flags:
Expand Down Expand Up @@ -466,145 +480,11 @@ spec:
...
```

**Cryostat** queries the **Kubernetes** API server and looks for `Service`s with a port either named `jfr-jmx` or with the number `9091`. One or both of these conditions
<span style="color:red">must</span> be met or else **Cryostat** will not automatically detect your application. In this case you may wish to use the [**Cryostat Agent**](#using-the-cryostat-agent-with-jmx)
to enable discovery, while keeping communications over **JMX** rather than HTTP.

##### [Using the Cryostat Agent with JMX](#using-the-cryostat-agent-with-jmx)
The two prior sections have discussed:
- How to use the **Cryostat Agent** to do application discovery and expose data over HTTP.
- How to use **Kubernetes** `Service` configurations for discovery and **JMX** to expose data.

There is a third, hybrid approach: **using the Cryostat Agent to do application discovery, and JMX to expose data**. This may be
useful since the **Agent** HTTP data model is readonly, whereas **JMX** is read-write. This means that using **JMX** to communicate between **Cryostat** and your applications
allows for more dynamic flexibility, for example, the ability to `start` and `stop` Flight Recordings on demand. Using the **Cryostat Agent** for application discovery
is also more flexible than depending on `Service`s with specially-named or specially-numbered ports.

For more context about these concepts, please review
the previous two sections on [using the **Cryostat Agent**](#using-the-cryostat-agent) and [using **JMX**](#using-jmx).

Add dependency configurations to `pom.xml`:
```xml
<project>
...
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/cryostatio/cryostat-agent</url>
</repository>
</repositories>
...
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>io.cryostat</groupId>
<artifactId>cryostat-agent</artifactId>
<version>{{ site.data.versions.agent.version }}</version>
</artifactItem>
</artifactItems>
<stripVersion>true</stripVersion>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
...
</project>
```

Modify the application `Deployment`:
```yaml
apiVersion: apps/v1
kind: Deployment
...
spec:
...
template:
...
spec:
containers:
- name: sample-app
image: docker.io/myorg/myapp:latest
env:
- name: CRYOSTAT_AGENT_APP_NAME
# Replace this with any value you like to use to identify your application.
value: "myapp"
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CRYOSTAT_AGENT_BASEURI
# Update this to correspond to the name of your Cryostat instance
# if it is not 'cryostat'. This assumes that the target application
# and the Cryostat instance are in the same Namespace, but you may
# choose to configure the Agent to communicate with a Cryostat in
# a different Namespace, too.
# (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)
value: https://cryostat.$(NAMESPACE).svc:8181
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: CRYOSTAT_AGENT_HOSTNAME
value: $(POD_IP)
- name: CRYOSTAT_AGENT_CALLBACK
# This infers the Agent Callback directly from the Pod's IP address using the
# Kubernetes Downward API. Use this value directly as provided. The port number
# 9977 can be changed but must match the containerPort below.
value: "http://$(POD_IP):9977"
# Replace "abcd1234" with a base64-encoded authentication token. For example,
# in your terminal, do 'oc whoami -t | base64' to use your user account's
# token as the token that the Agent will pass to authorize itself with
# the Cryostat server.
- name: CRYOSTAT_AGENT_AUTHORIZATION
value: "Bearer abcd1234"
# This environment variable is key to the "hybrid" setup.
# This instructs the Agent to register itself with Cryostat
# as reachable via JMX, rather than reachable via HTTP.
- name: CRYOSTAT_AGENT_REGISTRATION_PREFER_JMX
value: "true"
# Here we configure the application to load the Agent JAR as
# well as to enable JMX, since we want the Agent to register
# itself as reachable via JMX.
- name: JAVA_OPTS
value: >-
-javaagent:/deployments/app/cryostat-agent.jar
-Dcom.sun.management.jmxremote.port=9091
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
ports:
- containerPort: 9977
protocol: TCP
resources: {}
restartPolicy: Always
status: {}
```

Create an application `Service`:
```yaml
apiVersion: v1
kind: Service
...
spec:
ports:
- name: "cryostat-agent"
port: 9977
targetPort: 9977
...
```
**Cryostat** queries the **Kubernetes** API server and looks for `Service`s with a port either named `jfr-jmx` or with the number `9091`.
One or both of these conditions <span style="color:red">must</span> be met or else **Cryostat** will not automatically detect your
application. In this case you may wish to use the [**Cryostat Agent**](#using-the-cryostat-agent) to enable discovery, while keeping
communications over **JMX** rather than HTTP. If you do use the **Cryostat Agent** for discovery and **JMX** for remote management,
you may combine both of the `Service` definitions into a single `Service` with two exposed `ports`.

### [Alternate Setup](#alternate-setup)

Expand Down
11 changes: 6 additions & 5 deletions guides/_subsections/using-the-cryostat-agent.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
## [Using the Cryostat Agent](#using-the-cryostat-agent)

The Cryostat Agent is a new component of Cryostat, implemented as a Java Instrumentation Agent, which acts as a plugin for applications running on the JVM. Prior to the Agent, Cryostat extracted data from the JVM by initiating a connection over JMX. It then fetches the data from JFR and pulls it over the network back toward the Cryostat to be accessible to end users.
The Cryostat Agent is an optional component of Cryostat, implemented as a Java Instrumentation Agent, which acts as a plugin for applications running on the JVM. Prior to the Agent, Cryostat always extracted data from the JVM by initiating a connection over JMX. It then fetched the JFR data from an MBean and pulled it over the network back toward the Cryostat server to make it accessible to end users.

The Agent works differently. It is responsible for fetching data from the JVM and sending it back to Cryostat over HTTP. The Agent works by looking for data within itself and the application it is plugged into. The Cryostat Agent locates Cryostat applications running on the network and it is able to initiate a connection with those applications. It is also able to communicate back to Cryostat about the Cryostat Agent and how to reach it. The Cryostat Agent also pushes its own Java Flight Recorder (JFR) data back to Cryostat by first initiating a network connection with Cryostat, which then analyzes and saves the data to makes it accessible to end users.
The Agent works differently. It is responsible for fetching data from the JVM and sending it back to Cryostat over HTTP. The Agent works by looking for MBean and JFR data within itself and the application it is plugged into. It is also able to communicate back to Cryostat about the application instance the Cryostat Agent is attached to and how to reach it. The Cryostat Agent also pushes its own Java Flight Recorder (JFR) data back to Cryostat by initiating network connections with Cryostat, which may then analyze and save the data to makes it accessible to end users.

The Agent may also be configured, using the property `cryostat.agent.api.writes-enabled` or the corresponding environment variable `CRYOSTAT_AGENT_API_WRITES_ENABLED`, to allow bi-directional read-write access over HTTP. This enables dynamic start/stop/delete of Flight Recordings as well as on-demand JFR pulls much like what Cryostat does over JMX.

The programming interfaces for Cryostat and its Agent are designed to implement Cryostat's specific feature set, rather than being generalized and flexible like JMX. The benefit of this is that the security considerations are easier to understand and model, but choosing to use the Cryostat Agent over JMX may also forego the ability to interoperate with other JMX tooling such as JDK Mission Control, visualvm, jconsole, hawtio, etc.

Expand All @@ -11,9 +13,8 @@ The programming interfaces for Cryostat and its Agent are designed to implement
<li>The Cryostat analyzes these collected data to identify problems that might be affecting the application’s performance.</li>
<li>The Agent is a third-party Java Instrumentation Agent for developers which can be installed on the target JVM program through the command-line arguments or directly attaching to the running JVM instance.</li>
<li>The Agent is foreign code for developers to audit and inspect before including it in their application builds. It is a small amount of code to inspect and likely easier to trust than JMX.</li>
<li>Unlike JMX, JVM doesn’t come with the Agent, so developers are required to add the Cryostat Agent to their application builds, then rebuild and deploy the application.</li>
<li>Developers need to specify the JFR configuration in advance before the attached application starts. The configuration tells the Agent what kind of JFR data to collect and how often to send that data to Cryostat. This is necessary for Cryostat to save the data in storage for the user to analyze. At this point in time, the Agent does not allow for dynamically turning on and off JFR, and the data it collects will always be sent to Cryostat on a fixed consistent schedule.</li>
<li>Once the Agent has been installed or attached to the running JVM instance, it can begin collecting data and sending it to Cryostat for analysis.</li>
<li>Unlike JMX, the JVM doesn’t come with the Agent included, so developers are required to add the Cryostat Agent to their application builds, then rebuild and deploy the application.</li>
<li>Once the Agent has been installed or attached to the running JVM instance, it can begin collecting data and sending it to Cryostat for analysis. If enabled, the Cryostat server that the Cryostat Agent is registered with may also begin to send remote management requests to dynamically start, stop, or delete Flight Recordings as well as to retrieve JFR and MBean data.</li>
</ol>

For instructions on how to install the Cryostat Agent into your applications, [check the Setup section in Getting Started](/get-started/#using-the-cryostat-agent).