当前位置:   article > 正文

【性能革命】揭秘:在Kubernetes上部署Varnish反向代理缓存,让你的应用性能狂飙突进!

【性能革命】揭秘:在Kubernetes上部署Varnish反向代理缓存,让你的应用性能狂飙突进!

引言

随着微服务架构的普及,反向代理服务器在提高Web应用程序性能方面发挥着越来越重要的作用。而Varnish作为一款高性能的HTTP反向代理服务器,凭借其强大的缓存功能和灵活的配置,成为了许多企业和开发者的首选。

然而,仅仅部署Varnish还不足以实现最佳的性能效果。为了最大化Varnish的性能潜力,我们需要将其与Kubernetes这样的容器编排平台相结合。在本文中,我们将深入探讨如何在Kubernetes上部署Varnish反向代理缓存,以实现应用性能的狂飙突进。

一、Varnish Cache:性能加速的利器

Varnish Cache是一款开源、高性能的HTTP加速器,适用于大规模Web应用和服务。其基于内存操作,具备极高的读写速度,并能通过灵活且强大的Varnish Configuration Language(VCL)允许开发者细粒度地控制缓存策略,包括缓存哪些内容、何时缓存、如何刷新等。Varnish可以轻松应对高并发访问和大流量场景,减少对后端服务器的压力,尤其是对于大量静态内容和一些可缓存的动态内容,效果尤为显著。

Varnish核心特性

  1. 内存缓存:Varnish采用纯内存存储方式来缓存响应内容,这种设计使其在处理大量并发请求时能够提供极低的延迟响应,尤其适合高负载网站及API服务场景。

  2. VCL配置语言:Varnish Configuration Language (VCL)是一种专门用于定义缓存策略的语言。通过VCL,开发者可以精细控制缓存何种请求、如何缓存以及何时过期等行为,实现高度定制化的缓存逻辑。

  3. 事件驱动架构:Varnish基于事件驱动模型构建,能高效地处理网络I/O,充分利用现代硬件资源,以满足大规模、高并发环境下的性能需求。

  4. 健康检查与故障转移:Varnish能够对后端服务器进行健康检查,并根据结果自动调整流量分配,确保服务的稳定性和可用性。

Varnish缓存机制

  1. 缓存命中与未命中:当请求到达Varnish时,它首先会检查缓存中是否存在匹配的响应。如果存在,则直接从缓存返回响应,称为“缓存命中”;反之则需要向后端服务器发起请求,获取响应后再缓存并返回给客户端,此过程为“缓存未命中”。

  2. 缓存对象管理:Varnish使用LRU(Least Recently Used)算法管理缓存空间,当缓存满载时,最久未被访问的对象会被替换出去。同时,可以通过设置不同的缓存期限(TTL)或依据HTTP头部信息自定义缓存有效期。

  3. EVM(Varnish内核):Varnish使用一种名为EVM(Varnish Executive VM)的虚拟机来执行VCL脚本,允许开发者在运行时动态改变缓存策略,这使得Varnish具备了强大的灵活性和适应能力。

  4. Grace模式与Keep模式:在缓存项即将过期但尚未刷新的情况下,Varnish支持Grace模式(继续提供已过期但仍可用的缓存)和Keep模式(延长缓存项的生存时间),这些策略有助于降低后端服务器压力,提高整体性能。

二、Kubernetes上的Varnish部署实战

1.创建Varnish Dockerfile 文件

部署Kubernetes上,需要生成项目镜像放在仓库中,以下构建一个基于Varnish的Docker镜像的Dockerfile文件示例:

  1. FROM varnish:7.3.0-alpine
  2. # 添加 VCL 文件
  3. # COPY deploy/docker/varnish/etc/default.vcl /etc/varnish/
  4. # 暴露容器内的 Varnish 服务端口和的管理端口
  5. EXPOSE 8080 6000
  6. # 设置用户用户组
  7. #USER root:root
  8. # 安装调试工具
  9. #RUN apk update && apk add vim curl
  10. # 设置容器启动时的默认命令
  11. CMD ["varnishd", "-F", "-a", "0.0.0.0:8080", "-T", "0.0.0.0:6000", "-f", "/etc/varnish/default.vcl", "-p", "thread_pools=2" ,"-s", "malloc,1g"]

注意:

  • 默认用户:用户组 varnish:varnish ,若想在容器内使用安装软件调试,可使用 root:root ,否则提示无权限
  • 默认缓存时间120s,如何修改默认缓存有效时间,使用 "-t", "1d" (默认缓存一天)
  • default.vcl 配置文件可放在 kubernets ConfigMap

2. 创建Varnish Deployment

在Kubernetes中,我们可以借助Deployment资源来实现Varnish实例的自动化部署和管理。以下是一个详尽的YAML配置示例:

  1. apiVersion: apps/v1 # API 版本声明,使用apps/v1版本来定义StatefulSet资源
  2. kind: StatefulSet # 资源类型声明,这里是创建一个StatefulSet资源
  3. metadata: # 定义StatefulSet的元数据信息
  4. labels: # 给StatefulSet打上'app: varnish'标签
  5. app: varnish
  6. name: varnish # StatefulSet的名称为varnish
  7. namespace: demo # 运行在demo这个命名空间中
  8. spec: # 定义StatefulSet的具体规格
  9. replicas: 2 # 指定副本数量,这里设置为2个Pod副本,更具你的业务调整
  10. selector: # 通过标签选择器关联目标Pod
  11. matchLabels:
  12. app: varnish # 必须与spec.template.metadata.labels相同
  13. serviceName: varnish-svc-headless # 关联的Headless Service名称
  14. template: # 定义Pod模板
  15. metadata: # Pod模板的元数据
  16. labels: # 给Pod打上'app: varnish'标签
  17. app: varnish # 必须与spec.selector.matchLabels相同
  18. spec: # Pod模板的具体规格
  19. containers: # 容器列表
  20. image: harbor.xxx.com/demo/varnish:1.0.0 # 使用特定镜像
  21. imagePullPolicy: Always # 总是尝试拉取最新镜像
  22. name: varnish # 容器名称为varnish
  23. resources: # 容器资源限制与请求
  24. limits:
  25. cpu: '1' # 最大CPU为1
  26. memory: 1Gi # 最大内存为1Gi
  27. requests:
  28. cpu: 500m # 请求最小CPU为500m核
  29. memory: 512Mi # 请求最小内存为512Mi
  30. volumeMounts: # 容器卷挂载
  31. - mountPath: /etc/varnish/default.vcl # Varnish配置文件挂载路径
  32. name: volume-varnish
  33. subPath: default.vcl # 挂载ConfigMap中的default.vcl子路径
  34. - mountPath: /etc/localtime # 主机时间文件挂载路径
  35. name: volume-localtime
  36. dnsPolicy: ClusterFirst # DNS策略,使用集群默认的DNS策略
  37. restartPolicy: Always # Pod重启策略,任何情况下都应重启容器
  38. terminationGracePeriodSeconds: 30 # 容器优雅退出等待时间
  39. volumes: # 定义使用的持久化卷
  40. - configMap: # 使用ConfigMap挂载Varnish配置文件
  41. defaultMode: 420 # 设置文件权限模式
  42. name: varnish-vcl # 引用ConfigMap资源名称
  43. name: volume-varnish
  44. - hostPath: # 使用HostPath方式将主机的/etc/localtime文件挂载到Pod中
  45. path: /etc/localtime # 主机路径
  46. type: '' # 不指定类型,表示任何类型
  47. name: volume-localtime
  48. updateStrategy: # 更新策略
  49. type: RollingUpdate # 使用滚动更新的方式升级StatefulSet中的Pod

注意:

  • 根据实际需求调整CPU和内存限制、请求以及其他参数。

3. 创建Varnish VCL ConfigMap

创建ConfigMap用于存储Varnish的VCL配置文件,下面是一个包含后端定义、缓存策略和健康检查的示例:

  1. vcl 4.1;
  2. # 默认后端服务器定义。将其指向您的内容服务器。
  3. backend default {
  4. .host = "XXX.XXX.XXX.XXX"; # 后端服务器IP地址
  5. .port = "80"; # 后端服务器端口
  6. .probe = { # 健康检查配置
  7. .url = "/"; # 健康检查URL
  8. .interval = 5s; # 健康检查间隔
  9. .timeout = 30s; # 健康检查超时时间
  10. .window = 5; # 健康检查窗口大小
  11. .threshold = 3; # 健康检查阈值
  12. }
  13. }
  14. # 定义ACL以限制哪些IP可以执行清除操作。
  15. acl purge {
  16. "localhost"; # 允许本地主机
  17. "XX.XX.XX.0"/24; # 允许指定Ip范围
  18. }
  19. # 当请求到达时调用。
  20. sub vcl_recv {
  21. # 如果是BAN请求方法,执行清除操作。
  22. if (req.method == "BAN") {
  23. # 如果客户端IP不在purge ACL中,返回403
  24. if (!client.ip ~ purge) {
  25. return(synth(403, "Not allowed."));
  26. }
  27. # 执行清除操作。
  28. ban("req.url ~ " + req.url);
  29. # 返回200状态码表示清除成功。
  30. return(synth(200, "Ban added"));
  31. }
  32. # 可以选择为发往后端的请求添加X-Forwarded-For首部。
  33. # if (req.http.X-Forwarded-For) {
  34. # set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
  35. # } else {
  36. # set req.http.X-Forwarded-For = client.ip;
  37. # }
  38. # 对首页和资讯页面进行缓存。
  39. if (req.url ~ "^/$" || req.url ~ "^/news/(.*)_(.*)_(.*)$") {
  40. # 标记请求进行哈希处理,以便根据URL进行缓存。
  41. return (hash);
  42. }
  43. # 对于其他请求,不进行缓存处理。
  44. return (pass);
  45. }
  46. # 当从后端服务器获取响应时调用。
  47. sub vcl_backend_response {
  48. # 设置grace模式以缓存未过期的页面。
  49. set beresp.grace = 30m; # 在后端服务器不可用时保持缓存30分钟。
  50. # 将请求的URL和主机名复制到响应中。
  51. set beresp.http.url = bereq.url;
  52. set beresp.http.host = bereq.http.host;
  53. # 对首页和资讯页面设置30天的TTL。
  54. if (bereq.url ~ "^/$" || bereq.url ~ "^/news/(.*)_(.*)_(.*)$") {
  55. set beresp.ttl = 30d;
  56. }
  57. }
  58. # 当生成合成响应时调用。
  59. sub vcl_synth {
  60. }
  61. # 当准备向客户端发送响应时调用。
  62. sub vcl_deliver {
  63. # 根据是否命中缓存设置Via-Cache响应头。
  64. if (obj.hits > 0) {
  65. set resp.http.Via-Cache = "hit";
  66. } else {
  67. set resp.http.Via-Cache = "miss";
  68. }
  69. # 移除Age响应头。
  70. unset resp.http.Age;
  71. }

上述VCL配置展示了以下内容:

  1. 后端服务器定义:默认后端服务器的IP地址和端口,以及健康检查配置。
  2. 清除操作:通过BAN请求方法执行清除操作,限制执行清除操作的客户端IP地址。
  3. 请求处理:根据请求的URL进行缓存处理,对首页和资讯页面进行缓存,其他请求不进行缓存处理。
  4. 后端响应处理:将请求的URL和主机名复制到响应中,根据URL设置TTL值,并启用grace模式和keep模式。

通过这些配置,可以实现Varnish缓存的基本功能,包括缓存控制、清除操作、URL哈希处理等。同时,还展示了如何根据需求进行定制化配置,例如设置TTL值、启用grace模式和keep模式等。

如何清除缓存呢?下面提供命令在容器内清除缓存:

# 清除首页缓存

curl -X BAN "varnish-0.varnish-svc-headless.demo.svc.cluster.local:8080/"

curl -X BAN "varnish-1.varnish-svc-headless.demo.svc.cluster.local:8080/"

# 清除指定新闻

curl -X BAN "varnish-0.varnish-svc-headless.demo.svc.cluster.local:8080/news/20230823_2284_2919"

curl -X BAN "varnish-1.varnish-svc-headless.demo.svc.cluster.local:8080/news/20230823_2284_2919"

4. 创建Varnish Service

  • 创建Service资源(供外部访问),对外暴露Varnish集群的服务端点,并确保流量能够正确路由至Varnish实例:
  1. apiVersion: v1 # 定义Kubernetes API的版本,这里是版本1
  2. kind: Service # 定义要创建的Kubernetes资源的类型,这里是Service。
  3. metadata: # 定义元数据,与资源实例关联的信息。
  4. labels: # 定义资源的标签。
  5. app: varnish-svc # 定义标签,这里是app标签,值为varnish-svc。
  6. name: varnish-svc # 定义资源的名称,这里是varnish-svc。
  7. namespace: demo # 定义资源的命名空间,这里是demo。
  8. spec: # 定义资源的规格或配置。
  9. selector: # 定义选择器,用于匹配哪些Pods应该被这个Service暴露。
  10. app: varnish # 定义选择器的条件,这里是app标签值为varnish的Pods。
  11. ports: # 定义Service的端口配置。
  12. - port: 80 # 定义端口号是80
  13. protocol: TCP # 定义协议是TCP。
  14. targetPort: 8080 # 定义目标端口是8080,这意味着流量将通过80端口进入,然后被路由到端口8080的Pods上。
  15. type: LoadBalancer # 定义Service的类型,这里是LoadBalancer。LoadBalancer类型的Service会在每个节点上创建一个外部负载均衡器,用于暴露服务给外部客户端。或者选择NodePort、ClusterIP等类型,依据具体应用场景

  • 创建Headless Service资源(供内部访问),由于Varnish服务中缓存需要手动清除,为了安全只能在集群内部限制访问,达到安全的清除的目的:
  1. apiVersion: v1 # 定义Kubernetes API的版本,这里是版本1
  2. kind: Service # 定义要创建的Kubernetes资源的类型,这里是Service。
  3. metadata: # 定义元数据,与资源实例关联的信息。
  4. labels: # 定义资源的标签。
  5. app: varnish-svc-headless # 定义标签,这里是app标签,值为varnish-svc-headless。
  6. name: varnish-svc-headless # 定义资源的名称,这里是varnish-svc-headless。
  7. namespace: demo # 定义资源的命名空间,这里是demo。
  8. spec: # 定义资源的规格或配置。
  9. clusterIP: None # 定义Service的集群IP为None,表示这是一个Headless Service。
  10. clusterIPs: # 定义Service的集群IPs。
  11. - None # 只有一个集群IP,值为None。无头服务,不会进行负载均衡,也不会为该服务分配集群IP,自动配置DNS
  12. internalTrafficPolicy: Cluster # 定义内部流量策略为Cluster。
  13. ports: # 定义Service的端口配置。
  14. - name: varnish-svc-hs # 定义端口的名字为varnish-svc-hs。
  15. port: 80 # 定义端口号是80
  16. protocol: TCP # 定义协议是TCP。
  17. targetPort: 8080 # 定义目标端口是8080,这意味着流量将通过80端口进入,然后被路由到端口8080的Pods上。
  18. selector: # 定义选择器,用于匹配哪些Pods应该被这个Service暴露。
  19. app: varnish # 定义选择器的条件,这里是app标签值为varnish的Pods。
  20. type: ClusterIP # 定义Service的类型,这里是ClusterIP。ClusterIP类型的Service会为每个选择器在集群中创建一个唯一的IP地址,并路由到后端的Pods。

注意:

在Kubernetes中,当创建一个Headless Service时,系统会为每个Pod生成一个DNS条目,这些DNS条目的格式遵循特定的域名格式:

<service-name>.<namespace>.svc.cluster.local

对于Headless Service而言,它不会被分配Cluster IP,而是为Service中的每一个Pod提供一个独立的DNS A记录。例如,我们有一个名为varnish的Headless Service,并且它位于demo命名空间下,那么对应Pods的DNS条目将会是这样的格式:

  1. varnish-0.varnish-svc-headless.demo.svc.cluster.local:8080
  2. varnish-1.varnish-svc-headless.demo.svc.cluster.local:8080
  3. ...
  4. varnish-N.varnish-svc-headless.demo.svc.cluster.local:8080

由于这个老项目是PHP项目,顺便给一个PHP清除缓存的方法:

  1. function curlVarnishPurge(string $path = ''): bool
  2. {
  3. // 本地无 Varnish 服务,直接跳过
  4. if (SGS::$app_config['env'] == 'local') {
  5. return true;
  6. }
  7. // 循环清除指定路由缓存
  8. try {
  9. // 获取环境变量中配置的 VARNISH 服务器地址
  10. $varnishServerIps = explode(' ', getenv("VARNISH_SERVICES"));
  11. foreach ($varnishServerIps as $varnishServerIp) {
  12. $url = $varnishServerIp . '/' . $path;
  13. $ch = curl_init();
  14. curl_setopt($ch, CURLOPT_URL, $url);
  15. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'BAN');
  16. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  17. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  18. curl_setopt($ch, CURLOPT_HTTPGET, true);
  19. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); # 禁止回显返回数据
  20. $response = curl_exec($ch);
  21. if ($response === false) {
  22. throw new Exception(curl_error($ch));
  23. }
  24. curl_close($ch);
  25. }
  26. } catch (Exception $e) {
  27. throw new Exception($e);
  28. }
  29. return true;
  30. }

三、性能优化与最佳实践

  • 自动伸缩与弹性:除了上述的Deployment配置外,还可以结合Horizontal Pod Autoscaler根据CPU或内存使用率自动扩缩Varnish实例数量。

  • 缓存清理与刷新:利用Varnish内置的purge机制或BAN指令,配合后台任务或事件驱动的方式定期清理或实时刷新缓存。

  • 监控与告警:集成Prometheus Exporter收集Varnish Metrics,并利用Grafana展示丰富的可视化监控图表;同时配置Alertmanager进行异常情况的通知。

  • 安全加固:限制Varnish管理接口的访问权限,仅允许授权IP或内部服务访问;同时考虑启用SSL/TLS加密传输,确保数据的安全性。

  • 跨域资源共享(CORS)支持:若应用涉及跨域访问,可在VCL配置中添加CORS相关头部设置。

四、总结

通过上述架构调整,项目性能得到很大提升,经过压测和实际运行,使用比之前更少的资源,抗压至少提升5倍(压测机器的极限),在压测情况服务访问响应还是非常快,服务器压力显示很小。

在Kubernetes上部署Varnish Cache不仅能实现应用性能的巨大飞跃,还充分体现了云原生架构的优势,即弹性、可扩展性和自动化管理。通过合理配置和不断优化Varnish缓存策略,不仅可以大幅度减轻后端系统的负担,还能极大地提升用户的访问体验,助力企业打造快速、稳定、高效的应用服务体系。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/237534
推荐阅读
相关标签
  

闽ICP备14008679号