有状态扩展:Kubernetes 集群中的 Laravel 会话管理
最后更新于 由 Sarah Morgan
Kubernetes 是扩展 Laravel 应用程序 的绝佳选择。Kubernetes 提供可扩展性、高可用性、服务发现和负载均衡。特别是在 Laravel 部署方面,Kubernetes 可以帮助实现无状态水平扩展、轻松管理后台处理以及存储灵活性。
但它也会带来成本。Kubernetes Pod 的短暂性以及它们的扩展方式使得在 Laravel 中进行会话管理变得很困难。使用默认的文件会话驱动程序会导致在 Pod 重启或被替换时丢失数据,因此开发人员必须转向不同的机制。
在这里,我们想带您了解一些在迁移到 Kubernetes 部署时,在 Laravel 会话管理中会遇到的陷阱。好消息是,只要了解容器化的内部机制,这些问题都是可以解决的。
将 Kubernetes 与 Laravel 一起使用的好处
让我们从为什么你可能想这样做开始。您可以以多种不同的方式部署 Laravel 应用程序,那么为什么要选择一个会立即给你带来这种会话问题的方法呢?嗯,在 Kubernetes 上部署 Laravel 应用程序的核心优势是
- **无状态水平扩展。** Laravel 本质上被设计为对大多数组件(如果本地存储会话和缓存除外)都是无状态的。这种设计与 Kubernetes 的扩展能力相得益彰。当您 Laravel 应用程序的流量增加时,Kubernetes 可以快速生成更多 Laravel 应用程序的副本以处理负载,反之亦然,当流量减少时,Kubernetes 可以快速生成更多 Laravel 应用程序的副本以处理负载,反之亦然,当流量减少时,Kubernetes 可以快速生成更多 Laravel 应用程序的副本以处理负载,反之亦然。
- **后台处理。** Laravel 的作业和队列系统基于 Laravel Horizon 等工具构建,可以从 Kubernetes 的部署策略中获益。例如,工作器容器可以独立于主应用程序进行扩展,从而有效地处理排队的作业。
- **配置管理。** Laravel 的配置系统很大程度上依赖于环境变量。Kubernetes 的 ConfigMaps 和 Secrets 使得在不同环境(开发、登台、生产)中一致且安全地管理和注入这些环境变量变得容易。
- **存储灵活性。** 如果您的 Laravel 应用程序需要文件存储(例如,用于用户上传),Kubernetes 提供了持久卷声明 (PVC),它们抽象了底层存储后端。这意味着您的 Laravel 应用程序可以无缝地存储文件,无论您是在云对象存储、块存储设备还是网络文件系统上。
- **服务网格集成。** 如果您在 Kubernetes 上的 Laravel 应用程序中采用 Istio 等服务网格,您将获得增强的可观察性、流量管理和安全功能,而无需修改 Laravel 应用程序本身。
但是,虽然 Kubernetes 提供了这些好处,但也引入了复杂性,会话管理成为了这种复杂性的牺牲品。上面提到的好处之一是导致会话管理问题的罪魁祸首——无状态水平扩展。这会通过引入 Laravel 应用程序的多个副本,而这些副本可能不会天生共享会话状态,从而导致会话管理问题。
在传统的单服务器设置中,用户的会话数据通常存储在本地,使其可以立即用于来自该用户的后续请求。在 Kubernetes 中,由于多个副本处理请求,用户每次请求都可能由不同的副本提供服务。
Laravel 和 Kubernetes 的会话管理挑战
因此,会话管理的挑战来自三个对 Kubernetes 工作方式至关重要的因素。
首先,需要会话持久性。当会话存储在 Pod 中的本地时,转到不同副本的请求将无法访问之前的会话数据,从而导致用户体验破损。这种不一致会导致用户随机注销或需要帮助保留应用程序状态等问题。
其次是 Pod 的短暂性。Kubernetes 中的 Pod 可以随时终止和替换,尤其是在它们变得不健康或在更新期间。如果会话存储在 Pod 内的本地,并且该 Pod 被终止,则会话数据将丢失,从而影响用户体验。
第三,虽然 Kubernetes 确实提供了跨 Pod 的负载均衡,但在没有会话亲和性配置的情况下,来自同一用户的后续请求可能会落在不同的副本上。如果没有集中式会话存储,这会导致不可预测的行为。
所有这些都会导致数据同步开销和延迟增加,因为尝试通过同步或其他非集中式方法管理会话可能会在请求中引入延迟。这会损害用户体验,尤其是在响应时间至关重要的应用程序中。
为了应对这些挑战,在 Kubernetes 上部署 Laravel 的开发人员有两个选择
- 利用 Kubernetes 的会话亲和性功能,确保来自用户的请求始终落在同一个副本上。
- 将会话存储在 Redis 或数据库等分布式系统中,确保会话数据集中化并可供所有副本访问。
让我们分别介绍每个选项以及它们在 Laravel 中的工作原理。
粘性会话
会话亲和性,也称为“粘性会话”,在部署有状态应用程序或工作负载的场景中至关重要,您希望来自特定客户端的所有请求始终转到同一个 Pod。在 Kubernetes 中,您可以使用服务会话亲和性来实现这一点。
在传统的单体架构中,应用程序通常运行在单个服务器上,会话数据存储在本地。此模型确保来自特定用户的后续请求始终可以访问该用户的会话数据。但是,在 Kubernetes 等容器编排环境中,您的应用程序可能运行在分布在不同节点上的多个 Pod 中。在这种情况下,没有内在的保证,用户后续的请求每次都会命中同一个 Pod。
这就是粘性会话发挥作用的地方。对于特定的应用程序,用户的请求必须始终落在同一个 Pod 上,才能维护应用程序状态、提供一致的用户体验或有效地利用本地缓存。
Kubernetes 提供了会话亲和性的概念,使您能够指定来自特定客户端的所有请求都应路由到同一个 Pod。这是通过使用 Cookie 或 IP 来实现的。
当您根据客户端的 IP 地址配置会话亲和性时,Kubernetes 会检查传入请求的源 IP,并将其定向到与来自该 IP 的先前请求相同的 Pod。如果会话亲和性基于 Cookie,Kubernetes 将确保所有具有特定 Cookie 的请求都定向到同一个 Pod。
在您服务的 YAML 定义中,您必须指定会话亲和性类型。例如
apiVersion: v1kind: Servicemetadata: name: my-servicespec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 9376 sessionAffinity: ClientIP
在上面的配置中,sessionAffinity
字段设置为 ClientIP,这意味着 Kubernetes 服务将使用客户端的 IP 地址来确保来自该 IP 的所有请求都将定向到同一个 Pod。
如果您想对会话亲和性进行更多控制,可以使用 sessionAffinityConfig
。例如,要为会话亲和性设置超时时间
apiVersion: v1kind: Servicemetadata: name: my-servicespec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 9376 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 3600
虽然粘性会话可以解决一些问题,但它们并不总是合适的。如果处理粘性会话的 Pod 不堪重负或关闭,用户可能会遇到性能问题或中断。
仅依赖粘性会话也会导致 Pod 之间的流量分配不均匀,尤其是在某些客户端进行更频繁或资源密集型请求的情况下。对于许多用例而言,与粘性会话相比,Redis 等集中式会话存储可能更具可扩展性和容错性。因此,让我们看看这个选项。
使用 Redis 的分布式会话存储
Laravel 中的 Redis 等分布式会话存储允许您在多个应用程序实例之间共享会话数据。这在 Kubernetes 等可扩展环境中尤其有用,在这些环境中,您的应用程序可能运行在多个 Pod 中,并且这些 Pod 中的任何一个都可以为用户请求提供服务。
Redis 由于以下原因是理想的选择:
- 性能。Redis 是一个内存中的数据结构存储,提供快速的数据访问。这使其非常适合会话管理,在会话管理中,快速读写操作对于维护无缝的用户体验至关重要。
- 可扩展性。Redis 支持水平分区或分片,使其能够跨多台机器进行扩展。在高流量场景中,单个 Redis 实例可能会成为瓶颈,此时您可以将数据分布在多个 Redis 实例中。
- 持久性。虽然 Redis 主要是一个内存存储,但它提供了将数据持久保存到磁盘的机制,而不会影响太多性能。这确保了如果 Redis 服务重新启动,会话数据不会丢失。
- 数据结构。Redis 不仅仅是一个简单的键值存储。它支持各种数据结构,如字符串、哈希、列表和集合。这种灵活性对于更复杂的与会话相关的操作非常有利。
Redis 在粘性会话方面也有一些优势。由于任何实例都可以处理请求,因此流量在所有 Pod 之间更均匀地分布。如果一个 Pod 出现故障,用户会话不会丢失。另一个 Pod 可以接管请求并从 Redis 中获取会话数据。随着流量的增长,您可以根据需要独立地扩展应用程序和 Redis 实例。
让我们逐步了解如何在 Laravel 中将 Redis 设置为会话驱动程序。首先,您需要安装所需的包
composer require predis/predis
然后,您可以更新 Laravel 的 .env 文件以指向 Redis 进行会话管理
SESSION_DRIVER=redisREDIS_HOST=your_redis_serverREDIS_PASSWORD=your_redis_passwordREDIS_PORT=6379
在 Laravel 中,您需要更新您的 config/session.php 以使用 .env 文件中的会话驱动程序
'driver' => env('SESSION_DRIVER', 'file'),
然后,确保 config/database.php 中的 Redis 配置如下所示
'redis' => [ 'client' => 'predis', 'default' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'password' => env('REDIS_PASSWORD', null), 'port' => env('REDIS_PORT', 6379), 'database' => env('REDIS_DB', 0), ],],
接下来,您需要一个表来存储会话。Laravel 提供了一个命令来生成此表
php artisan session:table
这将创建一个迁移文件。运行迁移以创建 sessions 表
php artisan migrate
完成上述配置后,Laravel 将自动使用 Redis 处理会话,而无需在您的应用程序中编写任何特定代码。例如,当您设置一个会话变量时
session(['key' => 'value']);
此值将存储在 Redis 中。要检索它
$value = session('key');
当您调用这些函数时,Laravel 会自动使用 Redis 作为后端来管理会话数据。
要在 Kubernetes 中部署 Redis,您需要使用 Helm 在您的 Kubernetes 集群中部署 Redis 实例
# Add the Bitnami repository which has the Redis charthelm repo add bitnami <https://charts.bitnami.com/bitnami> # Install Redis using Helmhelm install my-redis bitnami/redis
请务必记下安装过程中 Helm 提供的 Redis 实例的密码。然后,创建一个 Kubernetes 服务清单文件。我们将其命名为 redis-service.yaml
apiVersion: v1kind: Servicemetadata: name: redis-servicespec: selector: app: redis ports: - protocol: TCP port: 6379 targetPort: 6379
您可以应用 Kubernetes 服务清单以创建服务
kubectl apply -f redis-service.yaml
然后验证服务是否正在运行
kubectl get svc redis-service
最后,通过更新您的 Laravel .env 文件或配置以使用在 Kubernetes 中创建的 Redis 服务,将 Laravel 指向 Kubernetes Redis 服务
REDIS_HOST=redis-serviceREDIS_PORT=6379
redis-service 是主机,因为它是在 Kubernetes 服务的名称,Kubernetes 内部 DNS 将解析此名称到服务的适当 IP 地址。
恭喜,您已将 Redis 与 Laravel 集成,并确保 Laravel 应用程序与部署在 Kubernetes 中的 Redis 实例通信。这确保会话数据在分布式环境中得到有效存储和管理。
使用 Laravel 和 Kubernetes 实现可扩展的会话
使用会话亲和性和分布式会话存储,您的 Laravel 应用程序可以存储和持久保存所有应用程序实例都可以访问的会话数据。然后,您可以扩展您的应用程序,确保所有用户都保留其会话数据,而无论应用程序的哪个实例处理其请求。在 Kubernetes 环境中,Pod 可能短暂存在,用户请求可以路由到任何 Pod,这些选项对于维护用户会话一致性至关重要。
随着技术的不断发展,确保无缝的用户体验变得至关重要,尤其是在 Kubernetes 等分布式和可扩展的环境中。通过利用 Laravel 的固有适应能力和 Kubernetes 的强大功能,开发人员可以解决会话管理的挑战。这种组合不仅增强了应用程序的弹性,而且还为未来的可扩展性铺平了道路,而不会影响用户体验。
立即尝试 Scout APM!
Sarah 在软件行业拥有超过 18 年的经验。除了她目前担任 Scout 的高级产品经理一职外,她还是 Product School 的特邀演讲者,也是 Tapple.io 的产品战略顾问。