赞
踩
随着微服务架构的普及,反向代理服务器在提高Web应用程序性能方面发挥着越来越重要的作用。而Varnish作为一款高性能的HTTP反向代理服务器,凭借其强大的缓存功能和灵活的配置,成为了许多企业和开发者的首选。
然而,仅仅部署Varnish还不足以实现最佳的性能效果。为了最大化Varnish的性能潜力,我们需要将其与Kubernetes这样的容器编排平台相结合。在本文中,我们将深入探讨如何在Kubernetes上部署Varnish反向代理缓存,以实现应用性能的狂飙突进。
Varnish Cache是一款开源、高性能的HTTP加速器,适用于大规模Web应用和服务。其基于内存操作,具备极高的读写速度,并能通过灵活且强大的Varnish Configuration Language(VCL)允许开发者细粒度地控制缓存策略,包括缓存哪些内容、何时缓存、如何刷新等。Varnish可以轻松应对高并发访问和大流量场景,减少对后端服务器的压力,尤其是对于大量静态内容和一些可缓存的动态内容,效果尤为显著。
内存缓存:Varnish采用纯内存存储方式来缓存响应内容,这种设计使其在处理大量并发请求时能够提供极低的延迟响应,尤其适合高负载网站及API服务场景。
VCL配置语言:Varnish Configuration Language (VCL)是一种专门用于定义缓存策略的语言。通过VCL,开发者可以精细控制缓存何种请求、如何缓存以及何时过期等行为,实现高度定制化的缓存逻辑。
事件驱动架构:Varnish基于事件驱动模型构建,能高效地处理网络I/O,充分利用现代硬件资源,以满足大规模、高并发环境下的性能需求。
健康检查与故障转移:Varnish能够对后端服务器进行健康检查,并根据结果自动调整流量分配,确保服务的稳定性和可用性。
缓存命中与未命中:当请求到达Varnish时,它首先会检查缓存中是否存在匹配的响应。如果存在,则直接从缓存返回响应,称为“缓存命中”;反之则需要向后端服务器发起请求,获取响应后再缓存并返回给客户端,此过程为“缓存未命中”。
缓存对象管理:Varnish使用LRU(Least Recently Used)算法管理缓存空间,当缓存满载时,最久未被访问的对象会被替换出去。同时,可以通过设置不同的缓存期限(TTL)或依据HTTP头部信息自定义缓存有效期。
EVM(Varnish内核):Varnish使用一种名为EVM(Varnish Executive VM)的虚拟机来执行VCL脚本,允许开发者在运行时动态改变缓存策略,这使得Varnish具备了强大的灵活性和适应能力。
Grace模式与Keep模式:在缓存项即将过期但尚未刷新的情况下,Varnish支持Grace模式(继续提供已过期但仍可用的缓存)和Keep模式(延长缓存项的生存时间),这些策略有助于降低后端服务器压力,提高整体性能。
部署Kubernetes上,需要生成项目镜像放在仓库中,以下构建一个基于Varnish的Docker镜像的Dockerfile文件示例:
- FROM varnish:7.3.0-alpine
-
- # 添加 VCL 文件
- # COPY deploy/docker/varnish/etc/default.vcl /etc/varnish/
-
- # 暴露容器内的 Varnish 服务端口和的管理端口
- EXPOSE 8080 6000
-
- # 设置用户用户组
- #USER root:root
-
- # 安装调试工具
- #RUN apk update && apk add vim curl
-
- # 设置容器启动时的默认命令
- 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"]
注意:
在Kubernetes中,我们可以借助Deployment
资源来实现Varnish实例的自动化部署和管理。以下是一个详尽的YAML配置示例:
- apiVersion: apps/v1 # API 版本声明,使用apps/v1版本来定义StatefulSet资源
- kind: StatefulSet # 资源类型声明,这里是创建一个StatefulSet资源
- metadata: # 定义StatefulSet的元数据信息
- labels: # 给StatefulSet打上'app: varnish'标签
- app: varnish
- name: varnish # StatefulSet的名称为varnish
- namespace: demo # 运行在demo这个命名空间中
- spec: # 定义StatefulSet的具体规格
- replicas: 2 # 指定副本数量,这里设置为2个Pod副本,更具你的业务调整
- selector: # 通过标签选择器关联目标Pod
- matchLabels:
- app: varnish # 必须与spec.template.metadata.labels相同
- serviceName: varnish-svc-headless # 关联的Headless Service名称
- template: # 定义Pod模板
- metadata: # Pod模板的元数据
- labels: # 给Pod打上'app: varnish'标签
- app: varnish # 必须与spec.selector.matchLabels相同
- spec: # Pod模板的具体规格
- containers: # 容器列表
- image: harbor.xxx.com/demo/varnish:1.0.0 # 使用特定镜像
- imagePullPolicy: Always # 总是尝试拉取最新镜像
- name: varnish # 容器名称为varnish
- resources: # 容器资源限制与请求
- limits:
- cpu: '1' # 最大CPU为1核
- memory: 1Gi # 最大内存为1Gi
- requests:
- cpu: 500m # 请求最小CPU为500m核
- memory: 512Mi # 请求最小内存为512Mi
- volumeMounts: # 容器卷挂载
- - mountPath: /etc/varnish/default.vcl # Varnish配置文件挂载路径
- name: volume-varnish
- subPath: default.vcl # 挂载ConfigMap中的default.vcl子路径
- - mountPath: /etc/localtime # 主机时间文件挂载路径
- name: volume-localtime
- dnsPolicy: ClusterFirst # DNS策略,使用集群默认的DNS策略
- restartPolicy: Always # Pod重启策略,任何情况下都应重启容器
- terminationGracePeriodSeconds: 30 # 容器优雅退出等待时间
- volumes: # 定义使用的持久化卷
- - configMap: # 使用ConfigMap挂载Varnish配置文件
- defaultMode: 420 # 设置文件权限模式
- name: varnish-vcl # 引用ConfigMap资源名称
- name: volume-varnish
- - hostPath: # 使用HostPath方式将主机的/etc/localtime文件挂载到Pod中
- path: /etc/localtime # 主机路径
- type: '' # 不指定类型,表示任何类型
- name: volume-localtime
- updateStrategy: # 更新策略
- type: RollingUpdate # 使用滚动更新的方式升级StatefulSet中的Pod
注意:
创建ConfigMap用于存储Varnish的VCL配置文件,下面是一个包含后端定义、缓存策略和健康检查的示例:
- vcl 4.1;
-
- # 默认后端服务器定义。将其指向您的内容服务器。
- backend default {
- .host = "XXX.XXX.XXX.XXX"; # 后端服务器IP地址
- .port = "80"; # 后端服务器端口
- .probe = { # 健康检查配置
- .url = "/"; # 健康检查URL
- .interval = 5s; # 健康检查间隔
- .timeout = 30s; # 健康检查超时时间
- .window = 5; # 健康检查窗口大小
- .threshold = 3; # 健康检查阈值
- }
- }
-
- # 定义ACL以限制哪些IP可以执行清除操作。
- acl purge {
- "localhost"; # 允许本地主机
- "XX.XX.XX.0"/24; # 允许指定Ip范围
- }
-
- # 当请求到达时调用。
- sub vcl_recv {
- # 如果是BAN请求方法,执行清除操作。
- if (req.method == "BAN") {
- # 如果客户端IP不在purge ACL中,返回403。
- if (!client.ip ~ purge) {
- return(synth(403, "Not allowed."));
- }
- # 执行清除操作。
- ban("req.url ~ " + req.url);
- # 返回200状态码表示清除成功。
- return(synth(200, "Ban added"));
- }
-
- # 可以选择为发往后端的请求添加X-Forwarded-For首部。
- # if (req.http.X-Forwarded-For) {
- # set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
- # } else {
- # set req.http.X-Forwarded-For = client.ip;
- # }
-
- # 对首页和资讯页面进行缓存。
- if (req.url ~ "^/$" || req.url ~ "^/news/(.*)_(.*)_(.*)$") {
- # 标记请求进行哈希处理,以便根据URL进行缓存。
- return (hash);
- }
-
- # 对于其他请求,不进行缓存处理。
- return (pass);
- }
-
- # 当从后端服务器获取响应时调用。
- sub vcl_backend_response {
- # 设置grace模式以缓存未过期的页面。
- set beresp.grace = 30m; # 在后端服务器不可用时保持缓存30分钟。
-
- # 将请求的URL和主机名复制到响应中。
- set beresp.http.url = bereq.url;
- set beresp.http.host = bereq.http.host;
-
- # 对首页和资讯页面设置30天的TTL。
- if (bereq.url ~ "^/$" || bereq.url ~ "^/news/(.*)_(.*)_(.*)$") {
- set beresp.ttl = 30d;
- }
- }
-
- # 当生成合成响应时调用。
- sub vcl_synth {
-
- }
-
- # 当准备向客户端发送响应时调用。
- sub vcl_deliver {
- # 根据是否命中缓存设置Via-Cache响应头。
- if (obj.hits > 0) {
- set resp.http.Via-Cache = "hit";
- } else {
- set resp.http.Via-Cache = "miss";
- }
-
- # 移除Age响应头。
- unset resp.http.Age;
- }
上述VCL配置展示了以下内容:
通过这些配置,可以实现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"
- apiVersion: v1 # 定义Kubernetes API的版本,这里是版本1。
- kind: Service # 定义要创建的Kubernetes资源的类型,这里是Service。
- metadata: # 定义元数据,与资源实例关联的信息。
- labels: # 定义资源的标签。
- app: varnish-svc # 定义标签,这里是app标签,值为varnish-svc。
- name: varnish-svc # 定义资源的名称,这里是varnish-svc。
- namespace: demo # 定义资源的命名空间,这里是demo。
- spec: # 定义资源的规格或配置。
- selector: # 定义选择器,用于匹配哪些Pods应该被这个Service暴露。
- app: varnish # 定义选择器的条件,这里是app标签值为varnish的Pods。
- ports: # 定义Service的端口配置。
- - port: 80 # 定义端口号是80。
- protocol: TCP # 定义协议是TCP。
- targetPort: 8080 # 定义目标端口是8080,这意味着流量将通过80端口进入,然后被路由到端口8080的Pods上。
- type: LoadBalancer # 定义Service的类型,这里是LoadBalancer。LoadBalancer类型的Service会在每个节点上创建一个外部负载均衡器,用于暴露服务给外部客户端。或者选择NodePort、ClusterIP等类型,依据具体应用场景
- apiVersion: v1 # 定义Kubernetes API的版本,这里是版本1。
- kind: Service # 定义要创建的Kubernetes资源的类型,这里是Service。
- metadata: # 定义元数据,与资源实例关联的信息。
- labels: # 定义资源的标签。
- app: varnish-svc-headless # 定义标签,这里是app标签,值为varnish-svc-headless。
- name: varnish-svc-headless # 定义资源的名称,这里是varnish-svc-headless。
- namespace: demo # 定义资源的命名空间,这里是demo。
- spec: # 定义资源的规格或配置。
- clusterIP: None # 定义Service的集群IP为None,表示这是一个Headless Service。
- clusterIPs: # 定义Service的集群IPs。
- - None # 只有一个集群IP,值为None。无头服务,不会进行负载均衡,也不会为该服务分配集群IP,自动配置DNS
- internalTrafficPolicy: Cluster # 定义内部流量策略为Cluster。
- ports: # 定义Service的端口配置。
- - name: varnish-svc-hs # 定义端口的名字为varnish-svc-hs。
- port: 80 # 定义端口号是80。
- protocol: TCP # 定义协议是TCP。
- targetPort: 8080 # 定义目标端口是8080,这意味着流量将通过80端口进入,然后被路由到端口8080的Pods上。
- selector: # 定义选择器,用于匹配哪些Pods应该被这个Service暴露。
- app: varnish # 定义选择器的条件,这里是app标签值为varnish的Pods。
- 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条目将会是这样的格式:
- varnish-0.varnish-svc-headless.demo.svc.cluster.local:8080
- varnish-1.varnish-svc-headless.demo.svc.cluster.local:8080
- ...
- varnish-N.varnish-svc-headless.demo.svc.cluster.local:8080
由于这个老项目是PHP项目,顺便给一个PHP清除缓存的方法:
- function curlVarnishPurge(string $path = ''): bool
- {
- // 本地无 Varnish 服务,直接跳过
- if (SGS::$app_config['env'] == 'local') {
- return true;
- }
-
- // 循环清除指定路由缓存
- try {
- // 获取环境变量中配置的 VARNISH 服务器地址
- $varnishServerIps = explode(' ', getenv("VARNISH_SERVICES"));
-
- foreach ($varnishServerIps as $varnishServerIp) {
- $url = $varnishServerIp . '/' . $path;
-
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'BAN');
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($ch, CURLOPT_HTTPGET, true);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); # 禁止回显返回数据
- $response = curl_exec($ch);
- if ($response === false) {
- throw new Exception(curl_error($ch));
- }
- curl_close($ch);
- }
- } catch (Exception $e) {
- throw new Exception($e);
- }
-
- return true;
- }
自动伸缩与弹性:除了上述的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缓存策略,不仅可以大幅度减轻后端系统的负担,还能极大地提升用户的访问体验,助力企业打造快速、稳定、高效的应用服务体系。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。