Kubernetes 上的 Bitnami Redis Sentinel 和 Sidekiq

Bitnami Redis Sentinel & Sidekiq on Kubernetes

所以现在我们正在尝试让 Bitnami Redis Sentinel 集群与我们的 Rails 应用程序 + Sidekiq 一起工作。

我们尝试了不同的方法,但我们并不清楚我们应该如何为 Sidekiq 指定哨兵(关键部分在这里,哨兵节点是只读的,所以我们不能将它们用于 sidekiq,因为作业状态被写入)。

因为在 Kubernetes 上只有 2 个服务可用:“redis”和“redis-headless”(不确定它们有何不同?)- 我怎样才能像这样指定哨兵:

Sidekiq.configure_server do |config|
  config.redis = {
    url: "redis",
    sentinels: [
      { host: "?", port: 26379 } # why do we have to specify it here seperately, since we should be able to get a unified answer via a service, or?
      { host: "?", port: 26379 }  
      { host: "?", port: 26379 } 
    }
  }
end

如果有人可以阐明这一点,那就太好了。据我了解,bitnami redis sentiel 仅 returns master 的 IP 并且应用程序必须处理对该 master 的相应写入然后 (https://github.com/bitnami/charts/tree/master/bitnami/redis#master-replicas-with-sentinel ) - 但我真的不明白如何用 sidekiq 做到这一点?

Kubernetes 服务和 Headless 服务的区别

让我们从澄清 Headless Service 和 Service 之间的区别开始。

一个 Service 允许一个连接到一个 Pod,而一个 headless Service returns 来自所有可用 IP 地址的列表 pods,允许 auto-discover.

Marco Luksa 在 SO :

上发表了更详细的解释

Each connection to the service is forwarded to one randomly selected backing pod. But what if the client needs to connect to all of those pods? What if the backing pods themselves need to each connect to all the other backing pods. Connecting through the service clearly isn’t the way to do this. What is?

For a client to connect to all pods, it needs to figure out the the IP of each individual pod. One option is to have the client call the Kubernetes API server and get the list of pods and their IP addresses through an API call, but because you should always strive to keep your apps Kubernetes-agnostic, using the API server isn’t ideal

Luckily, Kubernetes allows clients to discover pod IPs through DNS lookups. Usually, when you perform a DNS lookup for a service, the DNS server returns a single IP — the service’s cluster IP. But if you tell Kubernetes you don’t need a cluster IP for your service (you do this by setting the clusterIP field to None in the service specification ), the DNS server will return the pod IPs instead of the single service IP. Instead of returning a single DNS A record, the DNS server will return multiple A records for the service, each pointing to the IP of an individual pod backing the service at that moment. Clients can therefore do a simple DNS A record lookup and get the IPs of all the pods that are part of the service. The client can then use that information to connect to one, many, or all of them.

Setting the clusterIP field in a service spec to None makes the service headless, as Kubernetes won’t assign it a cluster IP through which clients could connect to the pods backing it.

Marco Luksa 的“Kubernetes 实战”

如何指定哨兵

正如 Redis documentation 所说:

When using the Sentinel support you need to specify a list of sentinels to connect to. The list does not need to enumerate all your Sentinel instances, but a few so that if one is down the client will try the next one. The client is able to remember the last Sentinel that was able to reply correctly and will use it for the next requests.

所以这个想法是提供你所拥有的,如果你扩展 redis pods,那么你不需要 re-configure Sidekiq(或者 Rails 如果你正在使用 Redis 进行缓存)。

全部结合起来

现在您只需要一种从 Ruby 中的无头服务获取 IP 地址并配置 Redis 客户端哨兵的方法。

幸运的是,自 Ruby 2.5.0 以来,Resolv class 可用并且可以为您做到这一点。

irb(main):007:0> Resolv.getaddresses "redis-headless"
=> ["172.16.105.95", "172.16.105.194", "172.16.9.197"]

这样你就可以做到:

Sidekiq.configure_server do |config|
  config.redis = {
    # This `host` parameter is used by the Redis gem with the Redis command
    # `get-master-addr-by-name` (See https://redis.io/topics/sentinel#obtaining-the-address-of-the-current-master)
    # in order to retrieve the current Redis master IP address.
    host: "mymaster",
    sentinels: Resolv.getaddresses('redis-headless').map do |address|
      { host: address, port: 26379 }
    end
  }
end

这将创建一个哈希数组,IP 地址为 host:,26379 为 port: