Java Web应用+资源限制

问题描述

构建了一个简单的使用springboot的web应用,应用使用redis作为数据库,意图测试同一个pod中的容器可以使用localhost进行通讯。

但使用过程中,总是会出现莫名奇妙的失败,Pod先进入running状态,之后崩掉。

出现问题的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: hello
  labels:
    app: helloworld
spec:
  imagePullSecrets:
  - name: my-docker-secret
  containers:
  - name: redis
    image: redis:7.0.12
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    ports:
      - containerPort: 6379
  - name: web
    image: registry.cn-hangzhou.aliyuncs.com/bohan838/web-redis:1.0
    env:
      - name: spring.redis.host
        value: localhost
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    ports:
      - containerPort: 8890
    livenessProbe:
      tcpSocket:
        port: 8890
      initialDelaySeconds: 30
      timeoutSeconds: 1
  restartPolicy: Always
# kubectl get pod hello
NAME    READY   STATUS             RESTARTS      AGE
hello   1/2     CrashLoopBackOff   4 (23s ago)   4m5s

问题解决过程

上述问题最大的困难在于无法确定问题原因,因此首先需要定位问题发生地点,因此先需要确定究竟是web应用还是redis的责任。根据READY一列的显示,可以得出两个容器中有一个挂掉了,首先需要确定是哪一个挂掉了

# kubectl exec -it hello -c redis -- ls /
bin   data  etc   lib	 lib64	 media	opt   root  sbin  sys  usr
boot  dev   home  lib32  libx32  mnt	proc  run   srv   tmp  var

# kubectl exec -it hello -c web -- ls /
error: unable to upgrade connection: container not found ("web")

可见,挂掉的是web服务。因此查看web容器的日志信息

kubectl logs hello -c web
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.17)

2023-11-30 07:39:41.808  INFO 1 --- [           main] c.e.helloworld.HelloWorldApplication     : Starting HelloWorldApplication v0.0.1-SNAPSHOT using Java 11.0.13 on hello with PID 1 (/app.jar started by root in /)
2023-11-30 07:39:41.813  INFO 1 --- [           main] c.e.helloworld.HelloWorldApplication     : No active profile set, falling back to 1 default profile: "default"
2023-11-30 07:39:48.400  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2023-11-30 07:39:48.404  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2023-11-30 07:39:48.512  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 85 ms. Found 0 Redis repository interfaces.
2023-11-30 07:39:52.009  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8890 (http)
2023-11-30 07:39:52.024  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-30 07:39:52.025  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.82]
2023-11-30 07:39:52.609  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-11-30 07:39:52.609  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 10390 ms

未见到任何错误信息!!(这便是最困难的点)

虽然未见错误信息,但根据本地的代码测试,怀疑是因为web应用依赖于redis,而web启动比redis要快,因此导致环境准备未完成,所以错误(但在本地测试的情况是,这并不会导致服务宕机,并且具有明确的异常信息,但怀疑异常信息进入了标准错误流中因此不可见,所以最终还是沿着这个方向进行)。为了验证是否是因此而导致的宕机,采取了两种方案:

  • 初始化容器:将redis作为web容器的init container,在这种情况下,因为redis是一个前台执行的应用,最终导致init过程无法结束,web容器无法启动(无效方案)

  • 将redis作为service提前启动(将redis作为独立的pod,并用一个service使其可以被k8s集群内的容器使用)

使用上述方案后,问题依然存在!!

最后,只能怀疑是否是镜像出现了问题,因此使用docker运行该镜像,发现容器能够正常运行,因此排除镜像的问题。为了排除是镜像仓库的问题,在docker上测试的镜像是直接从镜像仓库中获取的。

整个尝试到最后,能够确认的便是:

  1. 问题在于java web应用

  2. docker上能够正常使用,原因在于k8s

解决方案

最后求助于网络,然后浏览到了oom相关的异常,因此怀疑可能是资源限定的问题,因此最后去掉了web容器的资源限制,问题果然得到了解决!

去除资源限制后的yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello
  labels:
    app: helloworld
spec:
  imagePullSecrets:
  - name: my-docker-secret
  containers:
  - name: redis
    image: redis:7.0.12
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    ports:
      - containerPort: 6379
  - name: web
    image: registry.cn-hangzhou.aliyuncs.com/bohan838/web-redis:1.0
    env:
      - name: spring.redis.host
        value: localhost
    ports:
      - containerPort: 8890
    livenessProbe:
      tcpSocket:
        port: 8890
      initialDelaySeconds: 30
      timeoutSeconds: 1
  restartPolicy: Always
# kubectl exec hello -c web -- java -version
openjdk version "11.0.13" 2021-10-19
OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)

最后更新于