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
相关产品推荐
相关产品推荐

