在容器内自动注入 OpenTelemetry Java agent

解读

面试官想验证你是否能在 Docker 镜像构建阶段容器运行阶段 两条链路中,零侵入业务代码 地把 OpenTelemetry Java agent 注入到 JVM 进程,并保证:

  1. 镜像体积可控、构建缓存友好
  2. 不同环境(dev/test/prod)可动态开关或替换版本
  3. 符合国内企业对 镜像安全扫描等保测评 的合规要求(如禁止使用 root、敏感信息不落盘)
  4. 与现有 CI/CD(Jenkins、GitLab CI、KubeSphere、龙芯 DevOps 等)无缝集成

知识点

  • 多阶段构建(Multi-stage build)与 BuildKit 缓存挂载
  • OTEL_JAVAAGENT_PATHJAVA_TOOL_OPTIONSJDK_JAVA_OPTIONS 三种注入方式的差异
  • ENTRYPOINT 脚本钩子(shell 与 exec 模式)
  • Kubernetes Downward API / Docker Compose .env 动态传参
  • 非 root 用户(USER 65534)与 只读文件系统(read-only rootfs)场景下的 agent 权限问题
  • 国内网络加速:使用 阿里云 ACR 缓存代理自建 Nexus 缓存 下载 agent 包
  • 镜像安全:agent 的 SHA256 校验、Sigstore Cosign 验签、Trivy 扫描
  • Sidecar vs. 胖镜像 权衡:国内金融客户更倾向 胖镜像 以降低 Pod 启动复杂度
  • Swarm SecretK8s Secret 在 JVM 参数中的引用方式

答案

步骤 1:多阶段下载并校验

# syntax=docker/dockerfile:1.7
FROM alpine:3.19 AS otel-downloader
ARG OTEL_VERSION=2.4.0
ARG APACHE_MIRROR=https://mirrors.aliyun.com/apache
ADD ${APACHE_MIRROR}/opentelemetry/java-agent/${OTEL_VERSION}/opentelemetry-javaagent-${OTEL_VERSION}.jar /tmp/otel.jar
RUN echo "a1b2c3d4...  /tmp/otel.jar" | sha256sum -c -

步骤 2:复制到最终镜像并赋权

FROM eclipse-temurin:17-jre-alpine
COPY --from=otel-downloader --chown=65534:65534 /tmp/otel.jar /opt/agent/otel.jar
USER 65534

步骤 3:ENTRYPOINT 钩子动态注入

ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/agent/otel.jar"
ENV OTEL_SERVICE_NAME=my-service
ENV OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
COPY docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]

docker-entrypoint.sh 核心逻辑:

#!/bin/sh
set -e
# 国内客户常见需求:通过环境变量开关
if [ "${OTEL_ENABLE}" = "false" ]; then
  unset JAVA_TOOL_OPTIONS
fi
exec "$@"

步骤 4:CI/CD 集成
GitLab CI 中:

variables:
  OTEL_VERSION: "2.4.0"
build:
  script:
    - docker build --build-arg OTEL_VERSION=${OTEL_VERSION} -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - trivy image --severity HIGH,CRITICAL --exit-code 0 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

步骤 5:运行时覆盖
Docker Compose 片段:

services:
  app:
    image: my-app:1.0.0
    environment:
      OTEL_SERVICE_NAME: "order-service"
      OTEL_EXPORTER_OTLP_HEADERS: "authorization=Bearer ${TOKEN}"
    secrets:
      - otel-token
secrets:
  otel-token:
    external: true

通过以上 5 步,镜像构建零侵入运行时可开关安全合规国内网络友好,满足面试官对“自动注入”完整链路的期待。

拓展思考

  1. 多架构镜像:国内信创环境需同时支持 x86_64 与 arm64(鲲鹏、飞腾),可在 CI 中引入 docker buildx --platform linux/amd64,linux/arm64 并验证 agent 在不同 JIT 下的性能差异。
  2. Sidecar 模式:当业务方坚持“镜像不胖”时,可用 InitContainer 把 agent 挂到 emptyDir 共享卷,再通过 JAVA_TOOL_OPTIONS 指向 /agents/otel.jar,但需考虑 SeccompAppArmor 策略是否允许共享卷执行 jar。
  3. 版本热更新:利用 Kubernetes Reloader 监听 ConfigMap 中的 OTEL_VERSION 字段,触发滚动升级,实现 白天业务高峰不停机 升级 agent。
  4. 成本治理:国内公有云按 镜像拉取流量计费,可把 agent 层抽成 独立镜像 并在 DaemonSet 中预拉取,业务镜像通过 imagePullPolicy: IfNotPresent 复用,降低 跨可用区拉取费用