在本教程中,您将学习如何使用 Kubernetes、Helm 和 Mcrouter 在 Google Kubernetes Engine (GKE) 上部署分布式 Memcached 服务器集群。 Memcached 是一种流行的开源、多用途缓存系统。 它通常用作常用数据的临时存储,以加速 Web 应用的速度并减轻数据库负载。
Memcached 的特点
Memcached 有两大设计目标:
- 简单性:Memcached 功能类似于一个大型的哈希表,并且会提供一个简单的 API 以按照键来存储和检索任意形状的对象。
- 速度:Memcached 专门在随机存取存储器 (RAM) 上保存缓存数据,从而实现极快速数据访问。
Memcached 是一个分布式系统,可让其哈希表的容量在服务器池中横向扩缩。由于每台 Memcached 服务器都在与池中的其他服务器完全隔离的环境中运行, 因此,服务器之间的路由和负载平衡必须在客户端层级执行。此外,Memcached 客户端使用一致的哈希技术架构相应地选择目标服务器。此架构保证可满足以下条件:
- 始终为同一个键选择同一台服务器。
- 在服务器之间保持内存使用量的平衡。
- 服务器池缩小或扩大时,仅需重定位最少数量的键。
下图概括说明了 Memcached 客户端与分布式 Memcached 服务器池之间的交互。
目标
- 了解 Memcached 的分布式架构的一些特点。
- 使用 Kubernetes 和 Helm 将 Memcached 服务部署至 GKE。
- 部署开源 Memcached 代理 Mcrouter,提高系统的性能。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
- Compute Engine
准备工作
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Compute Engine and GKE APIs.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Compute Engine and GKE APIs.
- 启动 Cloud Shell 实例。
打开 Cloud Shell
部署 Memcached 服务
将 Memcached 服务部署至 GKE 的一个简单方法是使用 Helm 图表。要继续进行部署,请在 Cloud Shell 中执行以下步骤:
创建一个新的有三个节点的 GKE 集群:
gcloud container clusters create demo-cluster --num-nodes 3 --zone us-central1-f
下载
helm
二进制存档文件:HELM_VERSION=3.7.1 cd ~ wget https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz
将存档文件解压缩到本地系统:
mkdir helm-v${HELM_VERSION} tar zxfv helm-v${HELM_VERSION}-linux-amd64.tar.gz -C helm-v${HELM_VERSION}
将
helm
二进制文件的目录添加到PATH
环境变量中:export PATH="$(echo ~)/helm-v${HELM_VERSION}/linux-amd64:$PATH"
使用此命令,可在当前 Cloud Shell 会话期间让
helm
二进制变得可检测到。要使此配置存在于多个会话中,请将该命令添加到 Cloud Shell 用户的~/.bashrc
文件中。安装具有高可用性架构的新 Memcached Helm 图表版本:
helm repo add bitnami https://charts.bitnami.com/bitnami helm install mycache bitnami/memcached --set architecture="high-availability" --set autoscaling.enabled="true"
Memcached Helm 图表使用 StatefulSet 控制器。 这样做的好处之一是 pod 的名称变得有条理、可预测。在这种情况下,名称是
mycache-memcached-{0..2}
。这种排序使 Memcached 客户端引用服务器变得更容易。要查看正在运行的 pod,请运行以下命令:
kubectl get pods
Google Cloud Console 输出如下所示:
NAME READY STATUS RESTARTS AGE mycache-memcached-0 1/1 Running 0 45s mycache-memcached-1 1/1 Running 0 35s mycache-memcached-2 1/1 Running 0 25s
发现 Memcached 服务端点
Memcached Helm 图表使用无头服务。 无头服务公开其所有 pod 的 IP 地址,以便它们可以一个一个地被发现。
验证所部署的服务是否无头:
kubectl get service mycache-memcached -o jsonpath="{.spec.clusterIP}"
输出
None
确认该服务没有clusterIP
,因此是无头的。服务为以下格式的主机名创建 DNS 记录:
[SERVICE_NAME].[NAMESPACE].svc.cluster.local
在本教程中,服务名称为
mycache-memcached
。由于命名空间未明确定义,因此使用默认命名空间,完整的主机名为mycache-memcached.default.svc.cluster.local
。此主机名会解析为服务所公开的三个 pod 的一组 IP 地址和域名。如果将来某些 pod 被添加到池中,或者旧的 pod 被移除,则kube-dns
会自动更新 DNS 记录。客户端会发现 Memcached 服务端点,如后续步骤所述。
检索端点的 IP 地址:
kubectl get endpoints mycache-memcached
输出类似于以下内容:
NAME ENDPOINTS AGE mycache-memcached 10.36.0.32:11211,10.36.0.33:11211,10.36.1.25:11211 3m
请注意,每个 Memcached 的 pod 都有一个独立的 IP 地址,分别为
10.36.0.32
、10.36.0.33
和10.36.1.25
。对于您自己的服务器实例,这些 IP 地址可能不同。每个 pod 都会侦听 Memcached 的默认端口11211
。如果要使用不同的方法完成第 2 步操作,可使用 Python 等编程语言执行 DNS 检查:
启动集群内的 Python 交互式控制台:
kubectl run -it --rm python --image=python:3.10-alpine --restart=Never python
在 Python 控制台中,运行以下命令:
import socket print(socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local')) exit()
输出类似于以下内容:
('mycache-memcached.default.svc.cluster.local', ['mycache-memcached.default.svc.cluster.local'], ['10.36.0.32', '10.36.0.33', '10.36.1.25'])
通过打开与端口
11211
上正在运行的一个 Memcached 服务器建立的telnet
会话,测试部署:kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet mycache-memcached-0.mycache-memcached.default.svc.cluster.local 11211
看到
telnet
提示后,使用 Memcached ASCII 协议运行以下命令:set mykey 0 0 5 hello get mykey quit
生成的输出以粗体显示:
set mykey 0 0 5 hello STORED get mykey VALUE mykey 0 5 hello END quit
实现服务发现逻辑
现在,您可以实现下图中所示的基本服务发现逻辑。
概括来讲,服务发现逻辑包括以下步骤:
- 应用为
mycache-memcached.default.svc.cluster.local
的 DNS 记录查询kube-dns
。 - 应用检索与该记录关联的 IP 地址。
- 应用实例化新的 Memcached 客户端,并为其提供所检索到的 IP 地址。
- Memcached 客户端的集成负载平衡器连接到指定 IP 地址上的 Memcached 服务器。
现在,您可使用 Python 实现此服务发现逻辑:
在您的集群中部署新的已启用 Python 的 pod,并在该 pod 中开启 shell 会话:
kubectl run -it --rm python --image=python:3.10-alpine --restart=Never sh
安装
pymemcache
库:pip install pymemcache
通过运行
python
命令启动 Python 交互式控制台。在 Python 控制台中,运行以下命令:
import socket from pymemcache.client.hash import HashClient _, _, ips = socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local') servers = [(ip, 11211) for ip in ips] client = HashClient(servers, use_pooling=True) client.set('mykey', 'hello') client.get('mykey')
输出如下所示:
b'hello'
b
前缀表示字节字面量,字节字面量是 Memcached 存储数据所采用的格式。退出 Python 控制台:
exit()
按
Control
+D
退出 pod 的 shell 会话。
启用连接池
随着缓存需求的增长,以及池扩展到数十、数百或数千个 Memcached 服务器,您可能会遇到一些限制。特别是,大量来自 Memcached 客户端的打开连接可能会使服务器负载过重,如下图所示。
要减少打开连接数,您必须引入代理来启用连接池,如下图所示。
Mcrouter(发音为“mick router”)是一个功能强大的开源 Memcached 代理,可启用连接池。集成 Mcrouter 是无缝的,因为它使用标准的 Memcached ASCII 协议。对于 Memcached 客户端,Mcrouter 会像一个正常的 Memcached 服务器运行。对于 Memcached 服务器,Mcrouter 会像一个正常的 Memcached 客户端运行。
要部署 Mcrouter,请在 Cloud Shell 中运行以下命令。
删除以前安装的
mycache
Helm 图表版本:helm delete mycache
通过安装新的 Mcrouter Helm 图表版本,部署新的 Memcached pod 和 Mcrouter pod:
helm repo add stable https://charts.helm.sh/stable helm install mycache stable/mcrouter --set memcached.replicaCount=3
现在,代理 pod 可接受来自客户端应用的请求。
通过连接到其中一个代理 pod,测试此设置。在 Mcrouter 的默认端口
5000
上使用telnet
命令。MCROUTER_POD_IP=$(kubectl get pods -l app=mycache-mcrouter -o jsonpath="{.items[0].status.podIP}") kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet $MCROUTER_POD_IP 5000
看到
telnet
提示后,运行以下命令:set anotherkey 0 0 15 Mcrouter is fun get anotherkey quit
这些命令设置并回显您的键值。
现在,您就部署了一个启用连接池的代理。
降低延时
要提高弹性,常见做法是使用带有多个节点的集群。在本教程中,使用了带有三个节点的集群。不过,使用多个节点还会带来因节点之间网络流量较大而引起的延迟时间增加的风险。
并置代理 pod
您可以通过将客户端应用 pod 仅连接到同一节点上的 Memcached 代理 pod 来降低此风险。下图说明了此配置。
执行以下配置:
- 确保每个节点包含一个正在运行的代理 pod。一种常见方法是使用 DaemonSet 控制器部署代理 pod。 节点添加到集群中后,新的代理 pod 会自动添加到节点中。从集群中删除节点后,会对这些 pod 进行垃圾回收。在本教程中,您早些时候部署的 Mcrouter Helm 图表默认使用 DaemonSet 控制器。因此,此步骤已完成。
- 在代理容器的 Kubernetes 参数中设置
hostPort
值,以使节点侦听该端口并将流量重定向到代理。在本教程中,Mcrouter Helm 图表默认将此参数用于端口5000
。因此,此步骤也已完成。 通过使用
spec.env
条目并选择spec.nodeName
fieldRef
值,将节点名称作为应用 pod 中的环境变量公开。如需详细了解此方法,请参阅 Kubernetes 文档。部署一些示例应用 pod:
cat <<EOF | kubectl create -f - apiVersion: apps/v1 kind: Deployment metadata: name: sample-application spec: selector: matchLabels: app: sample-application replicas: 9 template: metadata: labels: app: sample-application spec: containers: - name: busybox image: busybox:1.33 command: [ "sh", "-c"] args: - while true; do sleep 10; done; env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName EOF
通过查看一个示例应用 pod,验证是否已公开节点名称:
POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}") kubectl exec -it $POD -- sh -c 'echo $NODE_NAME'
此命令输出如下格式的节点名称:
gke-demo-cluster-default-pool-XXXXXXXX-XXXX
连接 pod
示例应用 pod 现在可以连接到 Mcrouter 的默认端口 5000
上的各自配对节点中运行的 Mcrouter pod。
通过打开
telnet
会话,启动其中一个 pod 连接:POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}") kubectl exec -it $POD -- sh -c 'telnet $NODE_NAME 5000'
看到
telnet
提示后,运行以下命令:get anotherkey quit
生成的输出如下:
Mcrouter is fun
最后,作为说明,以下 Python 代码是可通过检索环境中的 NODE_NAME
变量并使用 pymemcache
库执行此连接的一个示例程序:
import os
from pymemcache.client.base import Client
NODE_NAME = os.environ['NODE_NAME']
client = Client((NODE_NAME, 5000))
client.set('some_key', 'some_value')
result = client.get('some_key')
清除数据
为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。
执行以下命令,删除 GKE 集群:
gcloud container clusters delete demo-cluster --zone us-central1-f
(可选)删除 Helm 二进制文件:
cd ~ rm -rf helm-v3.7.1 rm helm-v3.7.1-linux-amd64.tar.gz
后续步骤
- 探索除简单连接池之外,Mcrouter 提供的其他多项功能,如故障切换副本、可靠的删除流、冷缓存预热、多集群广播。
- 探索 Memcached 图表和 Mcrouter 图表的源文件,详细了解各自的 Kubernetes 配置。
- 了解在 App Engine 上使用 Memcached 的有效方法。其中一些方法还适用于其他平台,如 GKE。