You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kubernetes中Spring Boot应用只读文件系统Pod部署问题

Spring Boot 3.0.0 在Kubernetes只读文件系统Pod中部署失败的解决方法

问题背景

我需要在Kubernetes Deployment中运行Spring Boot 3.0.0应用的Docker容器,需遵循以下安全加固规则:

  • 不为Pod挂载emptyDir卷
  • 在Pod的securityContext中定义runAsNonRoot
  • 在Pod的securityContext中设置readOnlyRootFilesystem: true以挂载只读文件系统

我的应用仅将数据存储到数据库,无需文件系统写入权限,因此在Deployment中配置了如下securityContext:

securityContext:
  runAsNonRoot: true
  readOnlyRootFilesystem: true

但部署运行失败,报错信息如下:

[2023-02-01 15:12:31.762] ERROR [main] [org.springframework.boot.SpringApplication - 820]: Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:164)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:578)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:95)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
Caused by: org.springframework.boot.web.server.WebServerException: Unable to create tempDir. java.io.tmpdir is set to /tmp
    at org.springframework.boot.web.server.AbstractConfigurableWebServerFactory.createTempDir(AbstractConfigurableWebServerFactory.java:208)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:194)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:183)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:161)
    ... 16 common frames omitted
Caused by: java.nio.file.FileSystemException: /tmp/tomcat.8080.8311954706877871713: Read-only file system
    at java.base/sun.nio.fs.UnixException.translateToIOException(Unknown Source)
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
    at java.base/sun.nio.fs.UnixFileSystemProvider.createDirectory(Unknown Source)
    at java.base/java.nio.file.Files.createDirectory(Unknown Source)
    at java.base/java.nio.file.TempFileHelper.create(Unknown Source)
    at java.base/java.nio.file.TempFileHelper.createTempDirectory(Unknown Source)
    at java.base/java.nio.file.Files.createTempDirectory(Unknown Source)
    at org.springframework.boot.web.server.AbstractConfigurableWebServerFactory.createTempDir(AbstractConfigurableWebServerFactory.java:202)
    ... 19 common frames omitted

问题根源是Spring Boot尝试在只读的/tmp目录下创建Tomcat临时目录,导致启动失败。已知有挂载emptyDir或PVC的方案,但不想仅为临时目录挂载卷,求在readOnlyRootFilesystem: true下成功部署的可行方案?

可行解决方案

1. 指定JVM临时目录到内存文件系统

大部分Linux系统默认提供/dev/shm目录,这是基于内存的tmpfs文件系统,默认可写且无需额外挂载卷。通过JVM参数将java.io.tmpdir指向该目录:

在Deployment的容器配置中添加环境变量:

containers:
- name: your-spring-app
  image: your-spring-boot-image:3.0.0
  env:
  - name: JAVA_OPTS
    value: "-Djava.io.tmpdir=/dev/shm"
  securityContext:
    runAsNonRoot: true
    readOnlyRootFilesystem: true

验证Pod运行用户对/dev/shm的写入权限,通常默认配置下普通用户拥有该权限。

2. 自定义Web服务器临时目录

针对不同的嵌入式Web服务器,通过Spring Boot配置指定临时目录到/dev/shm:

  • Tomcat:添加配置server.tomcat.basedir=/dev/shm/tomcat
  • Jetty:添加配置server.jetty.base-dir=/dev/shm/jetty
  • Undertow:添加配置server.undertow.temp-directory=/dev/shm/undertow

可以将配置写入application.yaml,或通过环境变量传递:

env:
- name: SPRING_APPLICATION_JSON
  value: '{"server":{"tomcat":{"basedir":"/dev/shm/tomcat"}}}'

3. 利用内存型emptyDir(备选方案)

若上述两种方式不可行,可使用内存介质的emptyDir挂载临时目录,相比磁盘型emptyDir性能更好且重启后自动清理,符合安全要求:

containers:
- name: your-spring-app
  image: your-spring-boot-image:3.0.0
  volumeMounts:
  - name: temp-dir
    mountPath: /tmp
  securityContext:
    runAsNonRoot: true
    readOnlyRootFilesystem: true
volumes:
- name: temp-dir
  emptyDir:
    medium: Memory
    sizeLimit: 128Mi

这种方式虽使用了emptyDir,但内存介质的特性使其不会遗留数据,满足安全加固的核心需求。


内容的提问来源于stack exchange,提问作者user1563721

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 07:15:27