发布于 2026-01-06 0 阅读
0

在 Google Cloud 中使用 Kubernetes Engine 和 Cloud SQL 正确扩展像 WordPress 这样的有状态应用程序

在 Google Cloud 中使用 Kubernetes Engine 和 Cloud SQL 正确扩展像 WordPress 这样的有状态应用程序

网上有很多示例展示了如何在 Kubernetes 中运行 WordPress。但这些示例的主要问题在于:它们通常只运行一个 WordPress Pod,无法真正实现扩展。

所以我面临的问题是,我需要一个高度可扩展的 WordPress 设置,这就是我最终想出的办法。

为什么有状态应用程序难以扩展?

这些应用程序直接写入磁盘,而且大多数情况下你无法阻止。这种情况在基于 PHP 且使用某种插件系统的应用程序中尤为常见。因此,文件无法存储在某种存储桶中,而必须存储在应用程序的文件系统中。

你可能会说,像https://de.wordpress.org/plugins/wp-stateless/这样的 Stateless 插件会将数据写入云存储桶。没错,它的确会这样做,但它并不会将插件或某些插件直接写入的文件存储在云存储桶中(这种情况虽然令人遗憾,但却是事实)。

该怎么办?

我们需要一些东西,我们想要一个可扩展的数据库,我们需要某种共享文件库供我们的应用程序使用,以及应用程序本身。

为了简洁起见,我们将直接使用预定义的 WordPress Docker 镜像,但您应该始终尝试根据自身需求对这些 Dockerfile 进行修改。将它们作为基础,然后根据您的需要进行扩展。

所以我们需要一个共享磁盘,而这正是我们遇到的第一个问题。我们需要在 Kubernetes 集群中使用 ReadWriteMany 卷,问题就此开始。云服务提供商并不提供这种卷。
如果您查看Kubernetes 文档,
就会发现 GCEPersistantDisk、AzureDisk 和 AWSElasticBlockStore 都不支持我们的需求。虽然
Google Cloud 的 CloudFileStore 或 AzureFileStore 等服务提供了一些选择,但它们对于我们的需求来说太贵也太大了(我们不需要 1TB 的空间来存储 WordPress 网站,谢谢)。

美国国家森林管理局前来救援

但当我们查看列表时,我们发现了救星:NFS 来帮忙了。让我们创建一个唯一的选项,即连接到 NFS 的 ReadWriteOnce 存储。因此,我们需要一个理想情况下可以在不同区域之间共享的存储类:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
 name: regionalpd-storageclass
provisioner: kubernetes.io/gce-pd
parameters:
 type: pd-standard
 replication-type: regional-pd
 zones: europe-west3-b, europe-west3-c

Enter fullscreen mode Exit fullscreen mode

我们需要创建销量声明

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: nfs
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: ""
  volumeName: nfs
Enter fullscreen mode Exit fullscreen mode

现在让我们创建我们的NFS

apiVersion: v1
kind: Service
metadata:
 name: nfs-server
spec:
 clusterIP: 10.3.240.20
 ports:
   - name: nfs
     port: 2049
   - name: mountd
     port: 20048
   - name: rpcbind
     port: 111
 selector:
   role: nfs-server
Enter fullscreen mode Exit fullscreen mode

现在我们添加 NFS 本身。好处在于,我们可以使用预定义的服务。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: nfs-server
spec:
 replicas: 1
 selector:
   matchLabels:
     role: nfs-server
 template:
   metadata:
     labels:
       role: nfs-server
   spec:
     containers:
       - name: nfs-server
         image: gcr.io/google_containers/volume-nfs:0.8
         ports:
           - name: nfs
             containerPort: 2049
           - name: mountd
             containerPort: 20048
           - name: rpcbind
             containerPort: 111
         securityContext:
           privileged: true
         volumeMounts:
           - mountPath: /exports
             name: nfs
     volumes:
       - name: nfs
         gcePersistentDisk:
           pdName: nfs
           fsType: ext4
Enter fullscreen mode Exit fullscreen mode

CloudSQL 如此安全,如此美好

好了,我们已经为静态数据搭建了一个运行中的 NFS。接下来,关键一步是连接 Cloud SQL。假设你已经设置好了 Cloud SQL MySQL 数据库,那么如何将你的 Pod 连接到它呢?

我们使用容器自带的 SQL 代理。这样做的好处是,我们的 MySQL 服务没有暴露,我们可以使用本地主机。是不是很棒?

首先,您需要激活Cloud SQL 管理 API。

您需要创建一个具有实际访问云 SQL 权限的服务帐户。

在这里,我们创建一个拥有 Cloud SQL > Cloud SQL 客户端权限的新角色。

下载创建的私钥,我们需要用这个私钥来访问 SQL 实例。

如果您尚未创建数据库用户,请立即创建。

gcloud sql users create [DBUSER] --host=% --instance=[INSTANCE_NAME] --password=[PASSWORD]
Enter fullscreen mode Exit fullscreen mode

我们需要实例的名称,很简单:

gcloud sql instances describe [INSTANCE_NAME]
Enter fullscreen mode Exit fullscreen mode

或者您也可以在网页界面中找到它: 现在我们将凭据保存到我们的 Kubernetes 实例:
Google Cloud Web界面

kubectl create secret generic cloudsql-instance-credentials \
    --from-file=credentials.json=[PROXY_KEY_FILE_PATH]
kubectl create secret generic cloudsql-db-credentials \
    --from-literal=username=[DBUSER] --from-literal=password=[PASSWORD]

Enter fullscreen mode Exit fullscreen mode

所以我们准备好搭建WordPress网站了吗?

让我们首先创建这项服务:

apiVersion: v1
kind: Service
metadata:
 name: wlp-service
 labels:
   app: wlp-service
spec:
 type: LoadBalancer
 sessionAffinity: ClientIP
 ports:
   - port: 443
     targetPort: 443
     name: https
   - port: 80
     targetPort: 80
     name: http
 selector:
   app: wordpress
Enter fullscreen mode Exit fullscreen mode

好了,现在服务已经启动并运行,只差 Pod 本身了。
我们把它拆分一下,以便我解释。

apiVersion: apps/v1
kind: Deployment
metadata:
 name: wordpress
 labels:
   app: wordpress
spec:
 replicas: 2
 strategy:
   type: RollingUpdate
 selector:
   matchLabels:
     app: wordpress
 template:
   metadata:
     labels:
       app: wordpress
   spec:
     containers:
       - name: wordpress
         image: wordpress:7.3-apache
         imagePullPolicy: Always
         env:
           - name: DB_USER
             valueFrom:
               secretKeyRef:
                 name: "cloudsql-db-credentials"
                 key: username
           - name: DB_PASSWORD
             valueFrom:
               secretKeyRef:
                 name: "cloudsql-db-credentials"
                 key: password
         ports:
           - containerPort: 80
             name: wordpress
           - containerPort: 443
             name: ssl

Enter fullscreen mode Exit fullscreen mode

这样足以运行 WordPress,但不需要数据库或持久性 NFS。让我们逐一添加 Cloud SQL 代理:

       - name: cloudsql-proxy
         image: gcr.io/cloudsql-docker/gce-proxy:1.11
         command: ["/cloud_sql_proxy",
                   "-instances=[YOUR INSTANCESTRING THAT WE LOOKED UP]=tcp:3306",
                   "-credential_file=/secrets/cloudsql/credentials.json"]
         securityContext:
           runAsUser: 2  # non-root user
           allowPrivilegeEscalation: false
         volumeMounts:
           - name: cloudsql-instance-credentials
             mountPath: /secrets/cloudsql
             readOnly: true
     volumes:
       - name: cloudsql-instance-credentials
         secret:
           secretName: cloudsql-instance-credentials
Enter fullscreen mode Exit fullscreen mode

太好了,现在我们可以通过本地主机访问我们的 Cloud SQL 了 :) 它实际上在你的 pod 中添加了第二个容器,该容器会将所有到达 3306 端口的流量代理到我们的 Cloud SQL 实例,而无需将流量暴露给公共网络。

现在我们要将 wp-content 目录挂载到 NFS。

volumeMounts:
           - name: my-pvc-nfs
             mountPath: "/var/www/html/wp-content"
volumes:
        - name:  my-pvc-nfs
        nfs:
            server: 10.3.240.20
            path: "/"

Enter fullscreen mode Exit fullscreen mode

你可能会问,马里奥,你为什么要给NFS设置固定IP地址?原因很简单。据我所知,这是唯一一个内部DNS无法正常工作的例子。

就这样,现在我们可以通过创建和hpa来扩展我们的pods。

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
 name: wordpress
 namespace: default
spec:
 maxReplicas: 10
 metrics:
   - resource:
       name: cpu
       targetAverageUtilization: 50
     type: Resource
 minReplicas: 3
 scaleTargetRef:
   apiVersion: extensions/v1beta1
   kind: Deployment
   name: wordpress
Enter fullscreen mode Exit fullscreen mode

我们所有的 WordPress 内容文件都存储在 NFS 服务器上,并在各个实例之间共享。没错,NFS 现在确实是我们的单点故障,但 NFS 比只运行一台机器要稳定得多。如果使用 Redis 之类的缓存或者增加 FPM 缓存,还可以进一步缩短加载时间。

很酷吧?

您对 Kubernetes/云基础知识入门感兴趣吗?请告诉我。

文章来源:https://dev.to/mfahlandt/scaling-properly-a-stateful-app-like-wordpress-with-kubernetes-engine-and-cloud-sql-in-google-cloud-27jh