在 Kubernetes 中手动录制 Java Flight Recorder

本文档介绍了如何在没有额外工具的情况下,为容器化环境中的 Keycloak 创建 Java Flight Recorder 录制。

概述

Java Flight Recorder (JFR) 记录 Java 虚拟机的事件,包括线程转储,这些转储可以组装成火焰图,并用于分析性能。在完整的设置中,使用 Cryostat 捕获性能指标 提供了一种自动化的方式,并且不需要自定义 Keycloak 镜像。如果没有这样的设置,请按照以下说明捕获 JFR。

此方法不使用异步分析,因为据我所知,OpenShift 内部无法使用异步分析。因此,录制将存在安全点偏差问题。请参阅 在容器中分析 Java

准备 Keycloak 镜像

以下步骤需要容器中存在 jcmd 来启动 Java Flight Recording,以及 tar 来使用 kubectl cp 从容器中检索录制。

虽然旧版本的 Keycloak 包含这些工具,但较新的 Keycloak 镜像版本不包含它们,以使镜像更小更安全。因此,第一步是创建一个包含这些工具的自定义 Keycloak 镜像。有两种方法可以做到:从头开始创建 Keycloak 镜像,或更新 Keycloak 镜像并添加必要的包。

从头开始构建 Keycloak

如果您是从 Keycloak 的主存储库构建自定义 Keycloak 发行版,请更改文件 quarkus/container/Dockerfile,并将以下行

RUN bash /tmp/ubi-null.sh java-17-openjdk-headless glibc-langpack-en

替换为

RUN bash /tmp/ubi-null.sh java-17-openjdk-devel tar glibc-langpack-en

然后按照 使用自定义 Keycloak 镜像在 Kubernetes 中进行部署 中的描述构建镜像。

向镜像添加其他 RPM 包

Keycloak 关于容器的文档 包含一个关于如何添加包的章节。要添加两个包 java-17-openjdk-devel tar,请使用以下 Dockerfile:

FROM registry.access.redhat.com/ubi9 AS ubi-micro-build
RUN mkdir -p /mnt/rootfs
RUN dnf install --installroot /mnt/rootfs java-17-openjdk-devel tar --releasever 9 --setopt install_weak_deps=false --nodocs -y; dnf --installroot /mnt/rootfs clean all

FROM quay.io/keycloak/keycloak
COPY --from=ubi-micro-build /mnt/rootfs /

然后按照 使用自定义 Keycloak 镜像在 Kubernetes 中进行部署 中的描述使用镜像。

更新 JVM 选项

从 Keycloak 23 开始,不再需要覆盖 -XX:FlightRecorderOptions=stackdepth JVM 选项,因为 Keycloak 默认使用 512。

Keycloak 使用非常深的堆栈跟踪来进行调用。为了能够使用火焰图,请增加堆栈帧的数量,并添加以下 JVM 选项:

-XX:FlightRecorderOptions=stackdepth=512

当使用 Keycloak 运算符时,可以通过 Keycloak 的自定义资源将其传递给 Keycloak 镜像,如下所示

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
spec:
  unsupported:
    podTemplate:
      spec:
        containers:
          - env:
              - name: JAVA_OPTS_APPEND
                value: >
                  -XX:FlightRecorderOptions=stackdepth=512

如果您正在运行由运算符管理的 StatefulSet 或 Deployment,请考虑停止运算符并手动更新 StatefulSet 或 Deployment 以添加或扩展 Java 选项。

开始录制

要开始录制,请在容器中执行以下命令:

kubectl exec -n namespace pod -- jcmd 1 JFR.start duration=60s filename=/tmp/recording.jfr settings=/usr/lib/jvm/java/lib/jfr/profile.jfc

1 是所有基于 Quarkus 的 Keycloak 容器的 Java 进程的默认进程 ID。对于基于 Wildfly 的发行版,这可能是不同的进程 ID。使用不带参数的 jcmd 列出所有 Java 进程 ID 以找到您要查找的进程 ID。

如果 Pod 中运行了多个容器,请添加 CLI 选项 -c container

profile.jfc 包含有关要捕获的内容的说明。profile.jfc 是 JVM 附带的标准配置文件之一,代表“分析”:它收集大量信息,并且应该收集数分钟的信息。一分钟的录制大约会收集 5 兆字节的数据,因此请将时间跨度设置得短一些。根据需要调整它以收集所需的信息。

检索录制

要检索录制,请执行以下命令:

kubectl cp -n namespace keycloak pod:/tmp/recording.jfr recording.jfr --retries 999

如果 Pod 中运行了多个容器,请添加 CLI 选项 -c container

CLI 选项 --retries 999 有助于恢复大型文件的下载 否则可能会失败

分析录制

有关详细信息,请参阅 分析 Java Flight Recorder 录制