«

JDK1.8之前版本使用K8S对Java应用进行资源限制的注意事项

myluzh 发布于 阅读:809 Kubernetes


0x01 前言

flowchart TD
    %% 定义最外层的 Kubernetes 限制
    K8s["🔴 Kubernetes 容器 Limit (例如: 2048Mi)
    (操作系统 Cgroups 物理内存硬限制)
    触碰此线 -> 容器暴毙: OOMKilled (Exit Code 137)"]

    %% 第一层拆分:JVM 和 其他系统进程
    JVM["🔵 JVM 进程 (Java Process)"]
    SysOut["⚙️ 其他容器系统开销
    (如 shell 进程, 监控 Agent, Page Cache 等)"]

    %% 第二层拆分:堆与堆外 (75% vs 25%)
    Heap["🟢 Heap (堆内存)
    由 MaxRAMPercentage=75.0 控制 (约 1536Mi)
    存储业务对象 (Eden, Survivor, Old Gen)
    触碰此线 -> 抛出异常: OutOfMemoryError"]

    NonHeap["🟡 Non-Heap (堆外内存 / 本地内存)
    剩余的 25% 空间 (约 512Mi)
    这是为了防止被 K8s 强杀的『安全缓冲区』"]

    %% 第三层拆分:堆外内存的具体组成
    Metaspace["1️⃣ Metaspace (元空间)
    存放类元数据
    建议限制: -XX:MaxMetaspaceSize=256m"]

    Threads["2️⃣ 线程栈 (Thread Stacks)
    每个线程占用内存
    默认 -Xss1m (如 300个线程=300Mi)"]

    Direct["3️⃣ 直接内存 (Direct Buffers)
    NIO, Netty, 堆外缓存等申请的系统物理内存"]

    Code["4️⃣ Code Cache (代码缓存)
    JIT 即时编译器生成的机器码"]

    Overhead["5️⃣ JVM 自身开销
    GC 维护数据 (如 Card Table) 等底层 C++ 开销"]

    %% 建立连接关系
    K8s --> JVM
    K8s --> SysOut

    JVM --> Heap
    JVM --> NonHeap

    NonHeap --> Metaspace
    NonHeap --> Threads
    NonHeap --> Direct
    NonHeap --> Code
    NonHeap --> Overhead

    %% 样式美化
    style K8s fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#b71c1c
    style JVM fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    style SysOut fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,stroke-dasharray: 5 5
    style Heap fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
    style NonHeap fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
    style Metaspace fill:#fffde7,stroke:#fbc02d,stroke-width:1px
    style Threads fill:#fffde7,stroke:#fbc02d,stroke-width:1px
    style Direct fill:#fffde7,stroke:#fbc02d,stroke-width:1px
    style Code fill:#fffde7,stroke:#fbc02d,stroke-width:1px
    style Overhead fill:#fffde7,stroke:#fbc02d,stroke-width:1px

在处理容器化环境的 OOM 问题时,厘清 Cgroups 资源边界和 JVM 内存划分是最关键的一步。
自上而下的容器内 Java 进程内存结构图。你可以把它想象成一个俄罗斯套娃,最外层是 Kubernetes 设下的“死线”,里面才是 JVM 的各个地盘:

+-------------------------------------------------------------------------+
|                      Kubernetes Pod / Container Limits                  |
|               (Linux Cgroups 物理内存上限 - 比如设为 2048Mi)            |
+=========================================================================+
|                                  |                                      |
|                                  |   OS & Container Overhead (系统开销) |
|         JVM 进程总内存占用       |   (如 sh/bash 进程、监控 Agent、     |
|         (Java Process)           |    Linux Page Cache 页面缓存等)      |
|                                  |                                      |
+----------------------------------+--------------------------------------+
|                                                                         |
| +---------------------------------------------------------------------+ |
| |                            JVM 内部空间                             | |
| +------------------------------------+--------------------------------+ |
| |                                    |                                | |
| |          Heap (堆内存)             |     Non-Heap (堆外/本地内存)   | |
| |       (我们设置的 75% 比例)        |    (剩下的 25% "安全缓冲区")   | |
| |                                    |                                | |
| +------------------------------------+--------------------------------+ |
| | - Young Gen (新生代):              | - Metaspace (元空间/类元数据)  | |
| |     - Eden 区                      | - Thread Stacks (线程栈)       | |
| |     - Survivor 区 (S0/S1)          | - Direct Buffers (NIO直接内存) | |
| |                                    | - Code Cache (JIT 编译机器码)  | |
| | - Old Gen (老年代)                 | - JVM Overhead (GC内部数据结构)| |
| +------------------------------------+--------------------------------+ |
+-------------------------------------------------------------------------+

在JDK 1.8之前的版本中,Java本身并不了解Kubernetes中的资源限制,因此如果Java应用程序申请的资源超出了Pod的限制,Kubernetes将直接kill该Pod。

所以需要在Dockerfile中打包镜像时传递环境变量并将其传递给Java命令,可以按照以下步骤进行操作:

第一步:在Dockerfile中设置环境变量

ENV JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+08"
CMD java -jar $JAVA_OPTS xxx.jar

上述代码将环境变量JAVA_OPTS设置为$JAVA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+08,并将其传递给Java命令。

第二步:在Kubernetes的YAML文件中设置环境变量和资源限制

apiVersion: v1
kind: Pod
metadata:
  name: your-pod
spec:
  containers:
    - name: your-container
      image: your-image
      env:
        - name: JAVA_OPTS
          value: "-Xmx1g"
      resources:
        limits:
          memory: 2Gi
          cpu: 1
        requests:
          memory: 1Gi
          cpu: 0.5

在上述示例中,我们在YAML文件中定义了一个名为your-pod的Pod,并在其中的容器定义中设置了环境变量JAVA_OPTS的值为-Xmx1g。此外,我们还设置了资源限制,包括内存和CPU的限制。

k8s java jdk jdk1.8


正文到此结束
版权声明:若无特殊注明,本文皆为 Myluzh Blog 原创,转载请保留文章出处。
文章内容:https://itho.cn/k8s/363.html
文章标题:《JDK1.8之前版本使用K8S对Java应用进行资源限制的注意事项