我如何将我的Spring Boot应用连接到同一Kubernetes命名空间内的Redis服务器?

huangapple go评论63阅读模式
英文:

How can I connect my springboot application to my redis server within the same kubernetes namespace?

问题

我的Kubernetes命名空间同时托管了我的Spring Boot应用程序和Redis服务。然而,当我运行Spring Boot应用程序时,我收到了以下错误:

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
...
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Failed connecting to localhost:6379

我将发布应用程序和Redis服务器的清单YAML如下:

以下是成功部署我的应用程序到命名空间并可以从URL访问的清单:

这个清单成功地将我的应用程序部署到了命名空间,并且我可以从URL访问它。

以下是我的Spring Boot应用程序的配置文件:

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        return new JedisConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        return redisTemplate;
    }

}

我的Redis服务器是否正确部署?我是否需要以某种方式将Spring Boot应用程序绑定到我的Redis服务器?

您的Redis服务器似乎已正确部署到命名空间中。但是,根据错误消息和您的Spring Boot应用程序配置来看,应用程序正在尝试连接到本地主机(localhost)的6379端口,而这似乎不适用于Kubernetes环境。

在Kubernetes中,容器之间通常无法直接访问本地主机。您应该使用服务名称来引用Redis服务,而不是直接使用本地主机。

在您的Spring Boot应用程序的配置文件中,将以下行:

return new JedisConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));

更改为引用Redis服务的名称,如下所示:

return new JedisConnectionFactory(new RedisStandaloneConfiguration("redis-service", 6379));

这将允许您的Spring Boot应用程序在Kubernetes中正确连接到Redis服务。确保在应用程序部署清单中也正确设置了环境变量或配置,以指向Redis服务的名称。

英文:

I have a kubernetes namespace that hosts both my springboot application and the redis service. However when I run the springboot app I get error

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
...
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Failed connecting to localhost:6379

Ill post both the manifest yaml for the application and redis server below.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-deployment
  namespace: spring-poc
  labels:
    app: spring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring
  template:
    metadata:
      labels:
        app: spring
    spec:
      containers:
        - name: spring
          image: spring-application
          resources:
            requests:
              memory: 1G
              cpu: &quot;500m&quot;
            limits:
              memory: 1G
              cpu: 1
          ports:
            - name: http
              containerPort: 8080
          securityContext:
            runAsUser: 1000
      imagePullSecrets:
        - name: prod.secret
---
apiVersion: v1
kind: Service
metadata:
  name: spring-service
  namespace: spring-poc
  labels:
    app: spring-service
spec:
  selector:
    app: spring
  type: ClusterIP
  ports:
    - name: http
      port: 8080
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-ingress
  namespace: spring-poc
spec:
  rules:
    - host: spring.url.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: spring-service
                port:
                  number: 8080
  tls:
    - hosts:
        - spring.url.com

This manifest successfully deploys my app to the namespace and I can access it from the url.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-deployment
  namespace: spring-poc
  labels:
    app: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis
          resources:
            requests:
              memory: 1G
              cpu: &quot;500m&quot;
            limits:
              memory: 1G
              cpu: 1
          ports:
            - name: redis
              containerPort: 6379
          securityContext:
            runAsUser: 1000
      imagePullSecrets:
        - name: prod.secret
---
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: spring-poc
  labels:
    app: redis
spec:
  selector:
    app: redis
  type: ClusterIP
  ports:
    - name: redis
      port: 6379
      targetPort: 6379

This manifest successfully deploys a redis server to my namespace. If I kubectl exec -it redis-deployment -- redis-cli and run ping it returns pong.

Below is my configuration file for the springboot application

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * The type Cache config.
 */
@Configuration
@EnableCaching
public class CacheConfig {

    /**
     * Redis connection factory jedis connection factory.
     *
     * @return the jedis connection factory
     */
    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        return new JedisConnectionFactory(new RedisStandaloneConfiguration(&quot;127.0.0.1&quot;, 6379));
    }

    /**
     * Redis template.
     *
     * @param jedisConnectionFactory the jedis connection factory
     * @return the redis template
     */
    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate&lt;String, Object&gt; redisTemplate = new RedisTemplate&lt;&gt;();
        redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        return redisTemplate;
    }

}

Is my redis server deployed correctly? Do I need to bind the springboot application to my redis server somehow?

答案1

得分: 1

你正在创建一个使用本地主机(127.0.0.1)的 RedisStandaloneConfiguration。Pods(Redis 和你的 Spring 应用程序)是分开的,无法以这种方式通信。

因此,你需要将 127.0.0.1 替换为 redis-service.spring-poc,如下所示:

[...]
@Bean
public JedisConnectionFactory redisConnectionFactory() {
    return new JedisConnectionFactory(new RedisStandaloneConfiguration("redis-service.spring-poc", 6379));
}
[...]

最佳实践是从环境变量或应用程序配置中获取这些信息,这样你仍然可以在工作站上使用 localhost 进行测试。

解释:

Kubernetes 提供了开箱即用的 DNS。你可以使用 <service-name>.<namespace-name> 查询每个 Pod。你可以将其类比为类似于 google.de 的域名。因此,你可以通过这种方式连接到每个 Pod。在你的示例中,Redis 服务被称为 redis-service,它需要与部署在同一命名空间中,即 spring-poc(尽管你的清单中写着 namespace: 48713-poc,但这不会起作用)。因此,要从 Spring 连接到 Redis,你需要连接到 redis-service.spring-poc:6379

从技术上讲,redis-service:6379 也可以工作,但我建议更加详细。还可以查看有关 Pod 之间通信的 DNS 文档

英文:

You're creating a RedisStandaloneConfiguration with localhost (127.0.0.1). The pods (redis and your spring app) are separated and can't communicate like that.

So you need to replace 127.0.0.1 with redis-service.spring-poc, like this:

[...]
@Bean
    public JedisConnectionFactory redisConnectionFactory() {
        return new JedisConnectionFactory(new RedisStandaloneConfiguration(&quot;redis-service.spring-poc&quot;, 6379));
    }
[...]

Best practice would be to get that from environment variables or application config, so you can still use localhost for testing on your workstation.

Explanation:

Kubernetes provides out-of-the-box dns. You can query every pod with &lt;service-name&gt;.&lt;namespace-name&gt;. You can think of it similar to a domain like google.de. So you can connect to every pod via it's service by that. In your example the redis service is called redis-service and it needs to be in the same namespace as the deployment, which is spring-poc (though your manifest says namespace: 48713-poc which wouldn't work). So to connect from spring to redis you need to connect to redis-service.spring-poc:6379.

Technically redis-service:6379 would also work, but I'd recommend to be verbose. Also check out the docs about dns for pod to pod communication.

huangapple
  • 本文由 发表于 2023年6月15日 04:56:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477482.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定