使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟

使用,kubernetes,nginx,改善,第三方,服务,可靠性,延迟 · 浏览次数 : 290

小编点评

**Nginx cache configuration:** ``` proxy_cache_path /mnt/cache/complete; max_size=1g; inactive=1w; proxy_ignore_headers Cache-Control Expires Set-Cookie; proxy_cache_valid 1m; proxy_cache_use_stale error timeout updating; ``` **Nginx deployment in Kubernetes:** * Deploy the Nginx container image to a Kubernetes service. * Use a Kubernetes Service to manage the number of pods. * The Nginx pods are deployed in a non-privileged container image. * The `proxy_cache_path` directive is configured to point to an S3 bucket. * The S3 bucket is mounted on the `/mnt/cache` path on each pod. **Persistent cache storage:** * To share the cache across all Nginx instances, a shared storage solution is required. * Two containers are deployed on each pod: * One container uses AWS CLI to sync the cache from S3 to the local `/mnt/cache/complete` directory. * Another container uses AWS CLI to sync the cache from S3 to the local `/mnt/cache/tmp` directory. * `proxy_temp_path` is used to specify the location of the temporary cache directory. * `use_temp_path` is set to `on` to enable the use of the temporary directory. **Security considerations:** * The S3 bucket must be private and only accessible by authorized personnel. * The `proxy_cache_path` should be configured with appropriate permissions. * Access to the S3 bucket should be restricted to only authorized services. **Additional notes:** * The `keepalive_timeout` and `client_max_body_size` settings are not set in this configuration, as they are not relevant to the shared cache. * The `aws s3 sync` commands assume that the S3 bucket is already configured and contains the necessary data. * The `lifecycle_configuration` is used to define the lifecycle of the S3 bucket entries.

正文

使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟

译自:How we improved third-party availability and latency with Nginx in Kubernetes

本文讨论了如何在Kubernetes中通过配置Nginx缓存来提升第三方服务访问的性能和稳定性。这种方式基于Nginx来实现,优点是不需要进行代码开发即可实现缓存第三方服务的访问,但同时也缺少一些定制化扩展。不支持缓存写操作,多个pod之间由于使用了集中式共享方式,因而缓存缺乏高可用。

image

使用Nginx作为网关来缓存到第三方服务的访问

第三方依赖

技术公司越来越依赖第三方服务作为其应用栈的一部分。对外部服务的依赖是一种快速拓展并让内部开发者将精力集中在业务上的一种方式,但部分软件的失控可能会导致可靠性和延迟降级。

在Back Market,我们已经将部分产品目录划给了一个第三方服务,我们的团队需要确保能够在自己的Kubernetes集群中快速可靠地访问该产品目录数据。为此,我们使用Nginx作为网关代理来缓存所有第三方API的内部访问。

image

多集群环境中使用Nginx作为网关来缓存第三方API的访问

使用结果

在我们的场景下,使用网关来缓存第三方的效果很好。在运行几天之后,发现内部服务只有1%的读请求才需要等待第三方的响应。下面是使用网关一周以上的服务请求响应缓存状态分布图:

image

HIT:缓存中的有效响应 ->使用缓存

STALE:缓存中过期的响应 ->使用缓存,后台调用第三方

UPDATING:缓存中过期的响应(后台已经更新) ->使用缓存

MISS:缓存中没有响应 ->同步调用第三方

即使在第三方下线12小时的情况下,也能够通过缓存保证96%的请求能够得到响应,即保证大部分终端用户不受影响。

内部网关的响应要远快于直接调用第三方API的方式(第三方位于Europe,调用方位于US)。

image

以 ms 为单位的缓存路径的请求持续时间的 P90(1e3为1秒)

下面看下如何配置和部署Nginx。

Nginx 缓存配置

可以参见官方文档Nginx缓存配置指南以及完整的配置示例

proxy_cache_path ... max_size=1g inactive=1w;
proxy_ignore_headers Cache-Control Expires Set-Cookie;
proxy_cache_valid 1m;
proxy_cache_use_stale error timeout updating
                      http_500 http_502 http_503 http_504;                                     proxy_cache_background_update on;

配置的目标是最小化第三方服务的请求(如HTTP GET)。

如果缓存中不存在响应,则需要等待第三方响应,这也是我们需要尽可能避免的情况,这种现象可能发生在从未请求一个给定的URL或由于响应过期一周而被清除(inactive=1w),或由于该响应是最新最少使用的,且达到了全局的缓存大小的上限(max_size=1g)而被清除。

如果响应位于缓存中,当设置proxy_cache_background_update on时,即使缓存的响应超过1分钟,也会将其直接返回给客户端。如果缓存的响应超过1分钟(proxy_cache_valid 1m),则后台会调用第三方来刷新缓存。这意味着缓存内容可能并不与第三方同步,但最终是一致的。

当第三方在线且经常使用URLs时,可以认为缓存的TTL是1分钟(加上后台缓存刷新时间)。这种方式非常适用于不经常变更的产品数据。

假设全局缓存大小没有达到上限,如果一周内第三方不可达或出现错误,此时就可以使用缓存的响应。当一周内某个URL完全没有被调用时也会发生这种情况。

为了进一步降低第三方的负载,取消了URL的后台并行刷新功能:

proxy_cache_lock on;

第三方API可能会在其响应中返回自引用绝对链接(如分页链接),因此必须重写URLs来保证这些链接指向正确的网关:

sub_filter 'https:\/\/$proxy_host' '$scheme://$http_host';                                                                   sub_filter_last_modified on;                                     
sub_filter_once off;                                     
sub_filter_types application/json;

由于sub_filter不支持gzip响应,因此在重写URLs的时候需要禁用gzip响应:

# Required because sub_filter is incompatible with gzip reponses:                                     proxy_set_header Accept-Encoding "";

回到一开始的配置,可以看到启用了proxy_cache_background_update,该标志会启用后台更新缓存功能,这种方式听起来不错,但也存在一些限制。

当一个客户端请求触发后台缓存更新(由于缓存状态为STALE)时,无需等待后台更新响应就会返回缓存的响应(设置proxy_cache_use_stale updating),但当Nginx后续接收到来自相同客户端连接上的请求时,需要在后台更新响应之后才会处理这些请求(参见ticket)。下面配置可以保证为每个请求都创建一条客户端连接,以此保证所有的请求都可以接收到过期缓存中的响应,不必再等待后台完成缓存更新。

# Required to ensure no request waits for background cache updates:
keepalive_timeout 0;

缺电是客户端需要为每个请求创建一个新的连接。在我们的场景中,成本要低得多,而且这种行为也比让一些客户端随机等待缓存刷新要可预测得多。

Kubernetes部署

上述Nginx配置被打包在了Nginx的非特权容器镜像中,并跟其他web应用一样部署在了Kubernetes集群中。Nginx配置中硬编码的值会通过Nginx容器镜像中的环境变量进行替换(参见Nginx容器镜像文档)。

集群中的网关通过Kubernetes Service进行访问,网关pod的数量是可变的。由于Nginx 缓存依赖本地文件系统,这给缓存持久化带来了问题。

非固定pod的缓存持久化

正如上面的配置中看到的,我们使用了一个非常长的缓存保留时间和一个非常短的缓存有效期来刷新数据(第三方可用的情况下),同时能够在第三方关闭或返回错误时继续使用旧数据提供服务。

我们需要不丢失缓存数据,并在Kubernetes pod扩容启动时能够使用缓存的数据。下面介绍了一种在所有Nginx实例之间共享持久化缓存的方式--通过在pod的本地缓存目录和S3 bucket之间进行同步来实现该功能。每个Nginx pod上除Nginx容器外还部署了两个容器,这两个容器共享了挂载在/mnt/cache路径下的本地卷emptyDir,两个容器都使用了AWS CLI容器镜像,并依赖内部Vault来获得与AWS通信的凭据。

image

init容器会在Nginx启动前启动,负责在启动时将S3 bucket中保存的缓存拉取到本地。

aws s3 sync s3://thirdparty-gateway-cache /mnt/cache/complete

除此之外还会启动一个sidecar容器,用于将本地存储中的缓存数据保存到S3 bucket:

while true
do
  sleep 600
  aws s3 sync /mnt/cache/complete s3://thirdparty-gateway-cache 
done

为了避免上传部分写缓存条目到bucket,使用了Nginx的use_temp_path 选项(使用该选项可以将):

proxy_cache_path /mnt/cache/complete ... use_temp_path=on;
proxy_temp_path /mnt/cache/tmp;

默认的aws s3 sync不会清理bucket中的数据,可以配置bucket回收策略:

<LifecycleConfiguration>
  <Rule>
    <ID>delete-old-entries</ID>
    <Status>Enabled</Status>
    <Expiration>
      <Days>8</Days>
    </Expiration>
  </Rule>
</LifecycleConfiguration>

限制

如果可以接受最终一致性且请求是读密集的,那么这种解决方式是一个不错的选项。但它无法为很少访问的后端提供同等的价值,也不支持写请求(POST、DELETE等)。

鉴于使用了纯代理方式,因此它不支持在第三方的基础上提供抽象或自定义。

除非某种类型的客户端服务认证(如通过服务网格头)作为缓存密钥的一部分,否则会在所有客户端服务之间共享缓存结果。这种方式可以提高性能,但也会给需要多级认证来访问第三方数据的内部服务带来问题。我们的场景中不存在这种问题,因为生产数据对内部服务是公开的,且缓存带来的"认证共享"只会影响读请求。

在安全方面,还需要注意,任何可以访问bucket的人都可以读取甚至修改网关的响应。因此需要确保bucket是私有的,只有特定人员才能访问。

集中式的缓存存储会导致缓存共享(即所有pod会共享S3 bucket中的缓存,并在网关扩展时将缓存复制到pod中),因此这不是Nginx推荐的高可用共享缓存。未来我们会尝试实现Nginx缓存的主/备架构

与使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟相似的内容:

使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟

使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟 译自:How we improved third-party availability and latency with Nginx in Kubernetes 本文讨论了如何在Kubernetes中通过配置Nginx缓存来提升第

[转帖]Traefik中诡异的502和504问题

https://zhuanlan.zhihu.com/p/156138704 我们都知道在 Kubernetes 集群中通常会使用 Ingress 方案来统一代理集群内部的流量,而常用的 Ingress 方案为 traefik 和 nginx,和传统的 Nginx 作为企业内部的反向代理以及负载设备

使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- Ingress-Ngnix

前置条件:使用 Kubeadm 部署 Kubernetes(K8S) 安装 安装ingress-nginx组件(在master节点执行) 通过 ip+port 号进行访问,使用 Service 里的 NodePort 实现,把端口对外暴露 缺陷:一个端口只能使用一次,一个端口对应一个应用,实际使用中

使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- 持久化存储(NFS网络存储)

使用 Kubeadm 部署 Kubernetes(K8S) 安装 使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- Ingress-Ngnix Volume 是 Pod 中能够被多个容器访问的共享目录。 Kubernetes 的 Volume 定义在 Pod 上,它被一个 Po

使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- 持久化存储(PV&PVC)

使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- Ingress-Ngnix 使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- 持久化存储(NFS网络存储) NFS 存在一个弊端,需要知道NFS服务器的地址,配在 yaml 中 PV:持久化存储,对存储资源

[转帖]Kubernetes的Nginx Ingress 0.20之前的版本,upstream的keep-alive不生效

https://www.cnblogs.com/lizexiong/p/15358923.html 1说明 Kubernetes使用nginx-ingress-controller代理到集群内服务的请求,nginx所在的机器上上有大量的time-wait连接。 抓包发现nginx发起的到upstre

[转帖]使用 nginx 作反向代理,启用 keepalive 时,遇到 502 错误的调查过程

https://www.cnblogs.com/lizexiong/p/15358894.html 1.现象 结论见 《kubernetes ingress-nginx 启用 upstream 长连接,需要注意,否则容易 502》。nginx 的访问日志间歇性出现 502 响应,查看 nginx 的

在kubernetes里使用AppArmor限制容器对资源的访问

在kubernetes里使用AppArmor限制容器对资源的访问,AppArmor,强制访问控制(MAC),SELinux,使用AppArmor限制nginx程序访问目录,complain模式,enforce模式,aa-autodep,aa-logprof,aa-complain,aa-enforc...

[转帖]Kubernetes 领进门 | 使用 Ingress-nginx 反向代理外部站点

https://cloud.tencent.com/developer/article/2187041 warning: 这篇文章距离上次修改已过204天,其中的内容可能已经有所变动。 本文旨在展示如何使用 ingress-nginx 作为反向代理加速集群外部服务原理。下文以反向代理 github

使用 Helm 管理应用的一些 Tips

背景 Helm 是一个 Kubernetes 的包管理工具,有点类似于 Mac 上的 brew,Python 中的 PIP;可以很方便的帮我们直接在 kubernetes 中安装某个应用。 比如我们可以直接使用以下命令方便的在 k8s 集群安装和卸载 MySQL: helm install my-s