jvmkill не может убить JVM в образе, созданном Paketo Java Buildpack

У меня есть приложение для весенней загрузки v2.4.3 в контейнере, созданном с использованием официального плагина spring boot gradle, который для этого использует buildpakcs.

Один из слоев - jvmkill:

      [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'

что совершенно нормально, и он правильно добавляет jvm arg для jvmkill

      Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx387804K -XX:MaxMetaspaceSize=148771K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 23852, Headroom: 0%)                                                                                                                                           
Adding 129 container CA certificates to JVM truststore                                                                                                                               
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=2 -XX:MaxDirectMemorySize=10M -Xmx387804K -XX:MaxMetaspaceSize=148771K -XX:ReservedCodeCacheSize=240M -Xss1M                                                                                                                         
   .   ____          _            __ _ _                                                                                                                                              
  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \                                                                                                                                             
 ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \                                                                                                                                            
  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )                                                                                                                                           
   '  |____| .__|_| |_|_| |_\__, | / / / /                                                                                                                                            
  =========|_|==============|___/=/_/_/_/                                                                                                                                             
  :: Spring Boot ::                (v2.4.3)

Приложение работает в Kubernetes (AWS EKS), но когда я получил OOM

      java.lang.OutOfMemoryError: Java heap space

jvmkill запускается, печатает дамп кучи и отправляет сигнал уничтожения

      Heap                                                                                                                                                                                 
  def new generation   total 116736K, used 90606K [0x00000000e8400000, 0x00000000f02a0000, 0x00000000f02a0000)                                                                        
   eden space 103808K,  87% used [0x00000000e8400000, 0x00000000edc7bae0, 0x00000000ee960000)                                                                                         
   from space 12928K,   0% used [0x00000000ef600000, 0x00000000ef600000, 0x00000000f02a0000)                                                                                          
   to   space 12928K,   0% used [0x00000000ee960000, 0x00000000ee960000, 0x00000000ef600000)                                                                                          
  tenured generation   total 259456K, used 249951K [0x00000000f02a0000, 0x0000000100000000, 0x0000000100000000)                                                                       
    the space 259456K,  96% used [0x00000000f02a0000, 0x00000000ff6b7f18, 0x00000000ff6b8000, 0x0000000100000000)                                                                     
  Metaspace       used 74292K, capacity 76000K, committed 76824K, reserved 208160K                                                                                                    
   class space    used 8689K, capacity 9350K, committed 9600K, reserved 140576K                                                                                                       
 jvmkill killing current process                          ```

но jvm никогда не убивается. Осматривая контейнер, я замечаю, что приложение работает с PID 1, который не может быть уничтожен (или сигнализирован) изнутри контейнера.

      cnb@myhost-6968d47f4b-2cnlj:/$ ps -fea
UID        PID  PPID  C STIME TTY          TIME CMD
cnb          1     0  6 19:49 ?        00:00:54 java org.springframework.boot.loader.JarLauncher
cnb        121     0  0 20:02 pts/0    00:00:00 bash
cnb        131   121  0 20:02 pts/0    00:00:00 ps -fea

поскольку все это было создано самим пакетом сборки java, я ожидал, что он знает, что PID 1 не может быть убит, и запускает приложение по-другому, чтобы оно работало должным образом.

Есть ли что-то, чего мне не хватает или что мне нужно настроить, чтобы buildpackjvmkill работал из коробки?

Обходные пути:

  1. Если бы я запускал образ напрямую с помощью докера, я мог бы использовать docker run --initчтобы иметь возможность сигнализировать процессу jvm (мое приложение работает с PID 7). Не действует для Kubernetes.
      cnb          1  4.0  0.0   1120     4 ?        Ss   20:10   0:00 /sbin/docker-init -- java etc...
cnb          7  4.0  0.0  18372  1580 ?        S    20:10   0:00 java etc...
  1. Я могу shareProcessNamespace: true в моей спецификации k8s, чтобы он работал с PID, отличным от 1, но это было бы трудно оправдать из-за требований соответствия безопасности.

  2. Я мог бы просто добавить флаг jvm -XX:+ExitOnOutOfMemoryError но тогда нет красивого дампа кучи, который показывает jvmkill, плюс покрытие ошибок создания потока jvmkill.

1 ответ

Учитывая обходной путь, упомянутый в https://github.com/airlift/jvmkill#using-inside-docker-containers, вам нужно что-то еще для pid 1. Kubernetes не имеет эквивалента --init но если вы включите tiniв вашем изображении вы можете использовать его вручную с помощью команды контейнера. Вы также можете использовать sh по аналогии.

Также имейте в виду, что это защитит вас только от превышения лимита размера кучи на уровне Java. Если вы достигнете фактического ограничения памяти контейнера, ваш процесс немедленно завершится без возможности его поймать и что-либо сделать.

Другие вопросы по тегам