使用 Redis 和 PHP 创建留言板

本教程演示了如何使用 Google Kubernetes Engine (GKE) 构建多层 Web 应用。本教程中的应用是一个留言板,访问者可在一份日志中输入文字,并查看其中最新的几个日志条目。

本教程介绍如何使用负载平衡器在外部 IP 上设置留言板 Web 服务,以及如何使用单个主实例 (Leader) 和多个副本(关注者)运行 Redis 集群。

该示例强调了一些重要的 GKE 概念:

  • 使用 YAML 清单文件的声明式配置
  • Deployment,这是 Kubernetes 资源,用于确定一组副本 Pod 的配置
  • Service,用于为一组 Pod 创建内部和外部负载平衡器

目标

如需在 GKE 上部署和运行留言板应用,请执行以下操作:

  1. 设置 Redis Leader
  2. 设置两个 Redis 关注者
  3. 设置留言板 Web 前端
  4. 访问留言板网站
  5. 扩展留言板网络前端

下图展示了完成上述目标后创建的集群架构概览:留言板 GKE 图示

准备工作

请按照以下步骤启用 Kubernetes Engine API:
  1. 访问 Google Cloud Console 中的 Kubernetes Engine 页面
  2. 创建或选择项目。
  3. 稍作等待,让 API 和相关服务完成启用过程。 此过程可能耗时几分钟。
  4. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

安装本教程中使用的以下命令行工具:

  • gcloud 用于创建和删除 Kubernetes Engine 集群。gcloud 包含在 Google Cloud SDK 中。
  • kubectl 用于管理 Kubernetes(即 Kubernetes Engine 使用的集群编排系统)。您可以使用 gcloud 安装 kubectl
    gcloud components install kubectl

gcloud 命令行工具设置默认值

为了节省您在 gcloud 工具中输入项目 IDCompute Engine 可用区或区域选项的时间,您可以设置默认值:
gcloud config set project project-id

根据您在 GKE 中选择使用的操作模式,然后指定默认的可用区或区域。如果您使用标准模式,您的集群是可用区级(对于本教程),因此请设置您的默认计算可用区。如果您使用 Autopilot 模式,您的集群是区域级,因此请设置您的默认计算区域

标准

运行以下命令,将 compute-zone 替换为您的计算可用区,例如 us-west1-a

gcloud config set compute/zone compute-zone

Autopilot

运行以下命令,将 compute-region 替换为您的计算区域,例如 us-west1

gcloud config set compute/region compute-region

创建 GKE 集群

第一步是创建用于运行留言板应用和 Redis 服务的 GKE 集群

创建名为 guestbook 的 GKE 集群:

标准

gcloud container clusters create guestbook --num-nodes=4

Autopilot

 gcloud container clusters create-auto guestbook

您可以使用以下命令列出项目中运行的集群:

gcloud container clusters list
gcloud container clusters describe guestbook

设置 Redis Leader

留言板应用使用 Redis 来存储其数据。该应用将数据写入 Redis Leader 实例,并从多个 Redis 关注者实例中读取数据。第一步是部署 Redis Leader。

首先,克隆示例清单:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
cd kubernetes-engine-samples/guestbook
git checkout abbb383

使用名为 redis-leader-deployment 的清单文件部署 Redis Leader。此清单文件指定运行单个副本 Redis Leader Pod 的 Kubernetes Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-leader
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: leader
        tier: backend
    spec:
      containers:
      - name: leader
        image: "docker.io/redis:6.0.5"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379

运行以下命令以部署 Redis Leader:

kubectl apply -f redis-leader-deployment.yaml

验证 Redis Leader Pod 是否正在运行:

kubectl get pods
输出:
NAME                           READY     STATUS    RESTARTS   AGE
redis-leader-343230949-qfvrq   1/1       Running   0          43s

运行以下命令以查看 Redis Leader Pod 中的日志:

kubectl logs deployment/redis-leader

输出:

1:M 24 Jun 2020 14:48:20.917 * Ready to accept connections

创建 Redis Leader 服务

留言板应用需要与 Redis Leader 通信才能写入其数据。您可以创建一个 Service 来代理传输到 Redis Leader Pod 的流量。

Service 是 Kubernetes 抽象,定义了一个逻辑 Pod 集及其访问政策。Service 实际上是一个命名的负载平衡器,可将流量代理传输到一个或多个 Pod。 设置 Service 时,您需要根据 Pod 标签来说明要代理哪些 Pod。

查看 redis-leader-service.yaml 清单文件,该文件描述了 Redis Leader 的 Service 资源:

apiVersion: v1
kind: Service
metadata:
  name: redis-leader
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: leader
    tier: backend

此清单文件创建了一项名为 redis-leader 的 Service,该 Service 带有一组标签选择器。这些标签与上一步中部署的标签集匹配。因此,该 Service 将网络流量路由到上一步中创建的 Redis Leader Pod。

清单的 ports 部分声明了单个端口映射。在本例中,此 Service 将 port: 6379 上的流量路由到匹配指定 selector 标签的容器的 targetPort: 6379。请注意,Deployment 中使用的 containerPort 必须与 targetPort 匹配才能将流量路由到 Deployment。

通过运行以下命令来启动 Redis Leader Service:

kubectl apply -f redis-leader-service.yaml

验证 Service 已成功创建:

kubectl get service
输出:
NAME           CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes     10.51.240.1     <none>        443/TCP    42s
redis-leader   10.51.242.233   <none>        6379/TCP   12s

设置 Redis 关注者

虽然 Redis Leader 属于单个 Pod,但您可以通过添加一些 Redis 关注者副本来为其实现高可用性,并满足流量需求。

查看 redis-follower-deployment.yaml 清单文件,该文件描述了 Redis 关注者 Pod 的 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-follower
  labels:
    app: redis
    role: follower
    tier: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: follower
        tier: backend
    spec:
      containers:
      - name: follower
        image: gcr.io/google_samples/gb-redis-follower:v2
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379

如需创建 Redis 关注者 Deployment,请运行以下命令:

kubectl apply -f redis-follower-deployment.yaml

通过查询 Pod 列表,验证这两个 Redis 关注者副本是否正在运行:

kubectl get pods
输出:
NAME                              READY   STATUS    RESTARTS   AGE
redis-follower-76588f55b7-bnsq6   1/1     Running   0          27s
redis-follower-76588f55b7-qvtws   1/1     Running   0          27s
redis-leader-dd446dc55-kl7nl      1/1     Running   0          119s

获取其中一个 Redis 关注者的 Pod 日志。

kubectl logs deployment/redis-follower
输出:
1:M 24 Jun 2020 14:50:43.617 * Background saving terminated with success
1:M 24 Jun 2020 14:50:43.617 * Synchronization with replica 10.12.3.4:6379 succeeded

创建 Redis 关注者服务

留言板应用需要与 Redis 关注者通信以读取数据。如需让 Redis 关注者更容易被发现,您必须设置其他 Service。

redis-follower-service.yaml 定义了 Redis 关注者的 Service 配置:

apiVersion: v1
kind: Service
metadata:
  name: redis-follower
  labels:
    app: redis
    role: follower
    tier: backend
spec:
  ports:
    # the port that this service should serve on
  - port: 6379
  selector:
    app: redis
    role: follower
    tier: backend

此文件定义一项在端口 6379 上运行的名为 redis-follower 的 Service。请注意,Service 的 selector 字段与上一步中创建的 Redis 关注者 Pod 匹配。

运行以下命令来创建 redis-follower Service:

kubectl apply -f redis-follower-service.yaml

验证 Service 已成功创建:

kubectl get service
输出:
NAME           CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes     10.51.240.1     <none>        443/TCP    1m
redis-leader   10.51.242.233   <none>        6379/TCP   49s
redis-follower 10.51.247.238   <none>        6379/TCP   3s

设置留言板 Web 前端

在启动并运行了留言板的 Redis 存储后,可以启动留言板 Web 服务器。与 Redis 关注者一样,该前端将使用 Kubernetes Deployment 进行部署。

留言板应用使用 PHP 前端。该前端配置为根据请求属于读取请求还是写入请求,与 Redis 关注者 Service 或 Redis Leader Service 进行通信。该前端公开了一个 JSON 接口,并提供基于 jQuery-Ajax 的用户体验。

查看 frontend-deployment.yaml 清单文件,该文件描述了留言板 Web 服务器的 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
        app: guestbook
        tier: frontend
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v5
        env:
        - name: GET_HOSTS_FROM
          value: "dns"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 80

如需创建留言板 Web 前端 Deployment,请运行以下命令:

kubectl apply -f frontend-deployment.yaml

如需验证这三个副本正在运行,请查询用于标识 Web 前端的标签列表:

kubectl get pods -l app=guestbook -l tier=frontend
输出:
NAME                        READY   STATUS    RESTARTS   AGE
frontend-7b78458576-8kp8s   1/1     Running   0          37s
frontend-7b78458576-gg86q   1/1     Running   0          37s
frontend-7b78458576-hz87g   1/1     Running   0          37s

清单文件指定了环境变量 GET_HOSTS_FROM=dns。当您将配置提供给留言板 Web 前端应用时,前端应用会使用主机名 redis-followerredis-leader 来执行 DNS 查找。DNS 查找将查找您在先前步骤中创建的适当 Service 的 IP 地址。此概念称为 DNS 服务发现。

在外部 IP 地址公开前端

您在先前步骤中创建的 redis-followerredis-leader Service 只能在 GKE 集群中访问,因为 Service 的默认类型是 ClusterIPClusterIP 为 Service 指向的 Pod 集提供单个 IP 地址。此 IP 地址只能在集群内访问。

但是,您需要让留言板 Web 前端 Service 可供外部用户查看。 也就是说,您希望客户端能够从 GKE 集群外部请求 Service。如需完成此请求,您可以根据需求在 Service 配置中指定 type: LoadBalancertype: NodePort。在此示例中,您将使用 type: LoadBalancer。指定此配置的 frontend-service.yaml 清单文件如下所示:

apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  type: LoadBalancer
  ports:
    # the port that this service should serve on
  - port: 80
  selector:
    app: guestbook
    tier: frontend

创建 frontend Service 时,GKE 会创建一个负载平衡器和一个外部 IP 地址。请注意,这些资源需要付费ports 部分下方的端口声明指定了 port: 80,未指定 targetPort。如果忽略 targetPort 属性,则默认为 port 字段的值。在本例中,此 Service 将端口 80 上的外部流量路由到 frontend Deployment 中容器的端口 80。

要创建 Service,请运行以下命令:

kubectl apply -f frontend-service.yaml

访问留言板网站

如需访问留言板 Service,请通过运行以下命令找到您设置的 Service 的外部 IP 地址:

kubectl get service frontend
输出:
NAME       CLUSTER-IP      EXTERNAL-IP        PORT(S)        AGE
frontend   10.51.242.136   109.197.92.229     80:32372/TCP   1m

复制 EXTERNAL-IP 列中的 IP 地址,然后在浏览器中加载页面。

在 GKE 上运行的留言板

如需尝试添加一些留言板条目,请尝试输入信息帖,然后点击提交。您输入的消息会显示在前端。此消息表示数据已通过您之前创建的 Service 成功添加到 Redis。

扩充 Web 前端

假设您的留言板应用已经运行了一段时间,公共宣传突然激增。您决定为前端添加更多 Web 服务器。由于您的服务器被定义为使用 Deployment 控制器的服务,因此您可以轻松进行扩展。

通过运行以下命令,将 frontend Pod 数量扩展到 5 个:

kubectl scale deployment frontend --replicas=5

输出:

deployment.extensions/frontend scaled

系统会更新 Deployment 的配置,以指定现在应该运行 5 个副本:Deployment 会调整正在运行的 Pod 数量,以匹配配置。如需验证副本数量,请运行以下命令:

kubectl get pods
输出:
NAME                             READY     STATUS    RESTARTS   AGE
frontend-88237173-3s3sc          1/1       Running   0          1s
frontend-88237173-twgvn          1/1       Running   0          1s
frontend-88237173-5p257          1/1       Running   0          23m
frontend-88237173-84036          1/1       Running   0          23m
frontend-88237173-j3rvr          1/1       Running   0          23m
redis-leader-343230949-qfvrq     1/1       Running   0          54m
redis-follower-132015689-dp23k   1/1       Running   0          37m
redis-follower-132015689-xq9v0   1/1       Running   0          37m

您可以使用同一命令缩减前端 Pod 的数量。

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

  1. 删除 Service:此步骤将取消并释放为 frontend Service 创建的 Cloud Load Balancer:

    kubectl delete service frontend

  2. 等待系统完成删除为 frontend Service 预配的负载平衡器:当您运行 kubectl delete 时,系统会在后台异步删除负载平衡器。观察以下命令的输出,直到负载平衡器被删除:

    gcloud compute forwarding-rules list

  3. 删除 GKE 集群:此步骤将删除构成 GKE 集群的资源,如计算实例、磁盘和网络资源。

    gcloud container clusters delete guestbook

后续步骤