本文档适用于想在 Google Kubernetes Engine 上部署高可用性 MySQL 拓扑的数据库管理员、云架构师和专业运营人员。
在本教程中,您将了解如何部署 MySQL InnoDB 集群和 MySQL InnoDB ClusterSet、如何在 GKE 集群上部署 MySQL 路由器中间件,以及如何执行升级。
目标
在本教程中,您将学习如何完成以下操作:- 创建和部署有状态 Kubernetes Service。
- 部署 MySQL InnoDB 集群以实现高可用性。
- 部署路由器中间件以进行数据库操作路由。
- 部署 MySQL InnoDB ClusterSet 以实现灾难容忍。
- 模拟 MySQL 集群故障切换。
- 执行 MySQL 版本升级。
以下各部分介绍您将在本教程中构建的解决方案的架构。
MySQL InnoDB 集群
在您的区域 GKE 集群中,使用 StatefulSet 部署具有必要命名和配置的 MySQL 数据库实例,以创建 MySQL InnoDB 集群。如需提供容错和高可用性,您需要部署三个数据库实例 Pod。这样可以确保在任何给定时间不同可用区中的大多数 Pod 可用,从而能够使用共识协议成功选举主实例,并且使 MySQL InnoDB 集群能够容忍单可用区故障。
部署后,您可以将一个 Pod 指定为主实例来响应读取和写入操作。另外两个 Pod 是辅助只读副本。如果主实例遇到基础架构故障,您可以将这两个副本 Pod 中的一个提升为主实例。
在另一个命名空间中,您需要部署三个 MySQL 路由器 Pod,以提供连接路由来提高弹性。您的应用不会连接到数据库服务,而是连接到 MySQL 路由器 Pod。每个路由器 Pod 了解每个 MySQL InnoDB 集群 Pod 的状态和用途,并将应用操作路由到相应的健康 Pod。路由状态会缓存在路由器 Pod 中,并根据存储在 MySQL InnoDB 集群的每个节点上的集群元数据更新。如果某个实例发生故障,路由器会将连接路由调整到活动实例。
MySQL InnoDB ClusterSet
您可以从初始 MySQL InnoDB 集群创建 MySQL InnoDB ClusterSet。这样,如果主集群不再可用,您可以提高灾难容忍度。
如果 MySQL InnoDB 集群主实例不再可用,您可以将 ClusterSet 中的副本集群提升为主实例。使用 MySQL 路由器中间件时,您的应用不需要跟踪主数据库实例的健康状况。路由器会调整路由,以在选举完成后将连接发送到新的主实例。但是,您需要负责确保连接到 MySQL 路由器中间件的应用遵循弹性最佳实践,以便在集群故障切换期间发生错误时可以重试连接。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
设置项目
- 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
-
In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.
-
Enable the GKE API.
-
In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.
-
Enable the GKE API.
设置角色
-
Grant roles to your user account. Run the following command once for each of the following IAM roles:
role/storage.objectViewer, role/logging.logWriter, role/artifactregistry.Admin, roles/container.clusterAdmin, role/container.serviceAgent, roles/serviceusage.serviceUsageAdmin, roles/iam.serviceAccountAdmin
$ gcloud projects add-iam-policy-binding PROJECT_ID --member="USER_IDENTIFIER" --role=ROLE
- Replace
PROJECT_ID
with your project ID. -
Replace
USER_IDENTIFIER
with the identifier for your user account. For example,user:myemail@example.com
. - Replace
ROLE
with each individual role.
- Replace
设置您的环境
在本教程中,您将使用 Cloud Shell 来管理 Google Cloud 上托管的资源。Cloud Shell 预安装有 Docker、kubectl
和 gcloud CLI。
为了使用 Cloud Shell 设置您的环境,请执行以下操作:
设置环境变量。
export PROJECT_ID=PROJECT_ID export CLUSTER_NAME=gkemulti-west export REGION=COMPUTE_REGION
替换以下值:
- PROJECT_ID:您的 Google Cloud 项目 ID。
- COMPUTE_REGION:您的 Compute Engine 区域。在本教程中,区域为
us-west1
。通常,建议使用您附近的区域。
设置默认环境变量。
gcloud config set project PROJECT_ID gcloud config set compute/region COMPUTE_REGION
克隆代码库。
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
切换到工作目录。
cd kubernetes-engine-samples/databases/gke-stateful-mysql/kubernetes
创建 GKE 集群
在本部分中,您将创建一个区域级 GKE 集群。与可用区级集群不同,区域级集群的控制平面会被复制到多个可用区,因此单个可用区中的服务中断不会导致该控制平面不可用。
如需创建 GKE 集群,请按照以下步骤操作:
Autopilot
在 Cloud Shell 中,在
us-west1
区域中创建 GKE Autopilot 集群。gcloud container clusters create-auto $CLUSTER_NAME \ --region=$REGION
获取 GKE 集群凭据。
gcloud container clusters get-credentials $CLUSTER_NAME \ --region=$REGION
跨三个区域部署 Service。
kubectl apply -f prepare-for-ha.yaml
默认情况下,Autopilot 会在两个可用区中预配资源。
prepare-for-ha.yaml
中定义的 Deployment 会设置replicas:3
、具有requiredDuringSchedulingIgnoredDuringExecution
的podAntiAffinity
,以及topologyKey: "topology.kubernetes.io/zone"
,以确保 Autopilot 在集群的三个可用区中预配节点。检查 Deployment 的状态。
kubectl get deployment prepare-three-zone-ha --watch
当您看到三个 Pod 处于就绪状态时,请使用
CTRL+C
取消此命令。输出类似于以下内容:NAME READY UP-TO-DATE AVAILABLE AGE prepare-three-zone-ha 0/3 3 0 9s prepare-three-zone-ha 1/3 3 1 116s prepare-three-zone-ha 2/3 3 2 119s prepare-three-zone-ha 3/3 3 3 2m16s
运行此脚本以验证 Pod 已在三个可用区中部署。
bash ../scripts/inspect_pod_node.sh default
输出的每一行对应一个 Pod,第二列表示 Cloud 可用区。输出类似于以下内容:
gk3-gkemulti-west1-default-pool-eb354e2d-z6mv us-west1-b prepare-three-zone-ha-7885d77d9c-8f7qb gk3-gkemulti-west1-nap-25b73chq-739a9d40-4csr us-west1-c prepare-three-zone-ha-7885d77d9c-98fpn gk3-gkemulti-west1-default-pool-160c3578-bmm2 us-west1-a prepare-three-zone-ha-7885d77d9c-phmhj
Standard
在 Cloud Shell 中,在
us-west1
区域中创建 GKE Standard 集群。gcloud container clusters create $CLUSTER_NAME \ --region=$REGION \ --machine-type="e2-standard-2" \ --disk-type="pd-standard" \ --num-nodes="5"
获取 GKE 集群凭据。
gcloud container clusters get-credentials $CLUSTER_NAME \ --region=$REGION
部署 MySQL StatefulSet
在本部分中,您将部署一个 MySQL StatefulSet。每个 StatefulSet 由三个 MySQL 副本组成。
如需部署 MySQL StatefulSet,请按照以下步骤操作:
为 StatefulSet 创建命名空间。
kubectl create namespace mysql1
创建 MySQL 密钥。
kubectl apply -n mysql1 -f secret.yaml
密码随每个 Pod 一起部署,并供本教程中的 MySQL InnoDB 集群和 ClusterSet 部署的管理脚本和命令使用。
创建 StorageClass。
kubectl apply -n mysql1 -f storageclass.yaml
此存储类别使用
pd-balanced
永久性磁盘类型,该类型可平衡性能和费用。volumeBindingMode
字段设置为WaitForFirstConsumer
,表示 GKE 将推迟预配 PersistentVolume,直到 Pod 创建完成为止。此设置可确保在安排 Pod 的可用区中预配磁盘。部署 MySQL 实例 Pod 的 StatefulSet。
kubectl apply -n mysql1 -f c1-mysql.yaml
此命令会部署由三个副本组成的 StatefulSet。在本教程中,MySQL 主集群部署在
us-west1
中的三个可用区中。输出类似于以下内容:service/mysql created statefulset.apps/dbc1 created
在本教程中,资源限制和请求设置为最小值以节省费用。在规划生产工作负载时,请务必根据贵组织的需求适当设置这些值。
验证 StatefulSet 已成功创建。
kubectl get statefulset -n mysql1 --watch
StatefulSet 可能需要大约 10 分钟才能准备就绪。
当所有三个 Pod 都处于就绪状态时,使用
Ctrl+C
退出该命令。如果您看到 CPU 或内存不足导致的PodUnscheduleable
错误,请等待几分钟,以便控制平面调整大小以适应大型工作负载。输出类似于以下内容:
NAME READY AGE dbc1 1/3 39s dbc1 2/3 50s dbc1 3/3 73s
如需检查 Pod 在 GKE 集群节点上的放置位置,请运行此脚本:
bash ../scripts/inspect_pod_node.sh mysql1 mysql
输出结果显示 Pod 名称、GKE 节点名称和预配该节点的可用区,如下所示:
gke-gkemulti-west-5-default-pool-4bcaca65-jch0 us-west1-b dbc1-0 gke-gkemulti-west-5-default-pool-1ac6e8b5-ddjx us-west1-c dbc1-1 gke-gkemulti-west-5-default-pool-1f5baa66-bf8t us-west1-a dbc1-2
输出中的列分别表示主机名、Cloud 可用区和 Pod 名称。
StatefulSet 规范 (
c1-mysql.yaml
) 中的topologySpreadConstraints
政策指示调度器将 Pod 均匀地分布在故障域 (topology.kubernetes.io/zone
) 中。podAntiAffinity
政策强制执行限制条件,规定 Pod 不能位于同一 GKE 集群节点 (kubernetes.io/hostname
) 中。对于 MySQL 实例 Pod,此政策会使 Pod 均匀分布在 Google Cloud 区域的三个可用区中。此放置策略将每个数据库实例放置在单独的故障域中,从而实现 MySQL InnoDB 集群的高可用性。
准备 MySQL InnoDB 主集群
如需配置 MySQL InnoDB 集群,请遵循以下步骤:
在 Cloud Shell 终端中,为要添加到集群的 MySQL 实例设置群组复制配置。
bash ../scripts/c1-clustersetup.sh
该脚本将远程连接到三个 MySQL 实例中的每一个,以设置和保留以下环境变量:
group_replication_ip_allowlist
:允许集群中的实例连接到组中的任意实例。binlog_transaction_dependency_tracking='WRITESET'
:允许不发生冲突的并行事务。
在 8.0.22 之前的 MySQL 版本中,使用
group_replication_ip_whitelist
而不是group_replication_ip_allowlist
。打开第二个终端,这样您就不需要为每个 Pod 创建一个 shell。
在 Pod
dbc1-0
上连接到 MySQL Shell。kubectl -n mysql1 exec -it dbc1-0 -- \ /bin/bash \ -c 'mysqlsh --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql.mysql1.svc.cluster.local"'
验证 MySQL 组复制许可名单以连接到其他实例。
\sql SELECT @@group_replication_ip_allowlist;
输出类似于以下内容:
+----------------------------------+ | @@group_replication_ip_allowlist | +----------------------------------+ | mysql.mysql1.svc.cluster.local | +----------------------------------+
验证各个实例上的
server-id
都是唯一的。\sql SELECT @@server_id;
输出类似于以下内容:
+-------------+ | @@server_id | +-------------+ | 21 | +-------------+
配置每个实例以在 MySQL InnoDB 集群中使用,并在每个实例上创建管理员账号。
\js dba.configureInstance('root@dbc1-0.mysql.mysql1.svc.cluster.local', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")}); dba.configureInstance('root@dbc1-1.mysql.mysql1.svc.cluster.local', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")}); dba.configureInstance('root@dbc1-2.mysql.mysql1.svc.cluster.local', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
所有实例必须具有相同的用户名和密码,才能使 MySQL InnoDB 集群正常运行。每个命令都会生成类似以下内容的输出:
... The instance 'dbc1-2.mysql:3306' is valid to be used in an InnoDB cluster. Cluster admin user 'icadmin'@'%' created. The instance 'dbc1-2.mysql.mysql1.svc.cluster.local:3306' is already ready to be used in an InnoDB cluster. Successfully enabled parallel appliers.
验证实例已准备好在 MySQL InnoDB 集群中使用。
dba.checkInstanceConfiguration()
输出类似于以下内容:
... The instance 'dbc1-0.mysql.mysql1.svc.cluster.local:3306' is valid to be used in an InnoDB cluster. { "status": "ok" }
(可选)您可以连接到每个 MySQL 实例并重复此命令。例如,运行以下命令以检查
dbc1-1
实例上的状态:kubectl -n mysql1 exec -it dbc1-0 -- \ /bin/bash \ -c 'mysqlsh --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-1.mysql.mysql1.svc.cluster.local" \ --js --execute "dba.checkInstanceConfiguration()"'
创建 MySQL InnoDB 主集群
接下来,使用 MySQL Admin createCluster
命令创建 MySQL InnoDB 集群。从 dbc1-0
实例(它将是集群的主实例)开始,然后向集群添加两个副本实例。
如需初始化 MySQL InnoDB 集群,请遵循以下步骤:
创建 MySQL InnoDB 集群。
var cluster=dba.createCluster('mycluster');
运行
createCluster
命令会触发以下操作:- 部署元数据架构。
- 验证组复制的配置正确无误。
- 将其注册为新集群的种子实例。
- 创建必要的内部账号,例如复制用户账号。
- 开始组复制。
此命令会初始化 MySQL InnoDB 集群,并使用主机
dbc1-0
作为主实例。集群引用存储在集群变量中。输出类似于以下内容:
A new InnoDB cluster will be created on instance 'dbc1-0.mysql:3306'. Validating instance configuration at dbc1-0.mysql:3306... This instance reports its own address as dbc1-0.mysql.mysql1.svc.cluster.local:3306 Instance configuration is suitable. NOTE: Group Replication will communicate with other instances using 'dbc1-0.mysql:33061'. Use the localAddress option to override. Creating InnoDB cluster 'mycluster' on 'dbc1-0.mysql.mysql1.svc.cluster.local:3306'... Adding Seed Instance... Cluster successfully created. Use Cluster.addInstance() to add MySQL instances. At least 3 instances are needed for the cluster to be able to withstand up to one server failure.
将第二个实例添加到集群。
cluster.addInstance('icadmin@dbc1-1.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
将剩余的实例添加到集群。
cluster.addInstance('icadmin@dbc1-2.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
输出类似于以下内容:
... The instance 'dbc1-2.mysql:3306' was successfully added to the cluster.
验证集群的状态。
cluster.status()
此命令会显示集群的状态。该拓扑由三个主机组成,即一个主实例和两个辅助实例。或者,您也可以调用
cluster.status({extended:1})
。输出类似于以下内容:
{ "clusterName": "mysql1", "defaultReplicaSet": { "name": "default", "primary": "dbc1-0.mysql:3306", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "dbc1-0.mysql:3306": { "address": "dbc1-0.mysql:3306", "memberRole": "PRIMARY", "mode": "R/W", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.28" }, "dbc1-1.mysql:3306": { "address": "dbc1-1.mysql:3306", "memberRole": "SECONDARY", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.28" }, "dbc1-2.mysql:3306": { "address": "dbc1-2.mysql:3306", "memberRole": "SECONDARY", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.28" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "dbc1-0.mysql:3306" }
您还可以调用
cluster.status({extended:1})
来获取其他状态详细信息。
创建示例数据库
如需创建示例数据库,请按照以下步骤操作:
创建数据库并将数据加载到数据库中。
\sql create database loanapplication; use loanapplication CREATE TABLE loan (loan_id INT unsigned AUTO_INCREMENT PRIMARY KEY, firstname VARCHAR(30) NOT NULL, lastname VARCHAR(30) NOT NULL , status VARCHAR(30) );
将示例数据插入数据库。如要插入数据,您必须连接到集群的主实例。
INSERT INTO loan (firstname, lastname, status) VALUES ( 'Fred','Flintstone','pending'); INSERT INTO loan (firstname, lastname, status) VALUES ( 'Betty','Rubble','approved');
验证该表包含上一步中插入的三行。
SELECT * FROM loan;
输出类似于以下内容:
+---------+-----------+------------+----------+ | loan_id | firstname | lastname | status | +---------+-----------+------------+----------+ | 1 | Fred | Flintstone | pending | | 2 | Betty | Rubble | approved | +---------+-----------+------------+----------+ 2 rows in set (0.0010 sec)
创建 MySQL InnoDB ClusterSet
您可以使用专用的 ClusterSet 复制渠道创建 MySQL InnoDB ClusterSet,以管理从主集群到副本集群的复制。
MySQL InnoDB ClusterSet 可将 MySQL InnoDB 主集群与它自己在其他位置(例如多个可用区和多个区域)的一个或多个副本相连,从而为 MySQL InnoDB 集群部署提供灾难容忍。
如果您关闭了 MySQL Shell,请在新的 Cloud Shell 终端中运行以下命令来创建新的 shell:
kubectl -n mysql1 exec -it dbc1-0 -- \
/bin/bash -c 'mysqlsh \
--uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql.mysql1.svc.cluster.local"'
如需创建 MySQL InnoDB ClusterSet,请按照以下步骤操作:
在 MySQL Shell 终端中,获取集群对象。
\js cluster=dba.getCluster()
输出类似于以下内容:
<Cluster:mycluster>
使用存储在集群对象中的现有 MySQL InnoDB 主集群初始化 MySQL InnoDB ClusterSet。
clusterset=cluster.createClusterSet('clusterset')
输出类似于以下内容:
A new ClusterSet will be created based on the Cluster 'mycluster'. * Validating Cluster 'mycluster' for ClusterSet compliance. * Creating InnoDB ClusterSet 'clusterset' on 'mycluster'... * Updating metadata... ClusterSet successfully created. Use ClusterSet.createReplicaCluster() to add Replica Clusters to it. <ClusterSet:clusterset>
检查 MySQL InnoDB ClusterSet 的状态。
clusterset.status()
输出类似于以下内容:
{ "clusters": { "mycluster": { "clusterRole": "PRIMARY", "globalStatus": "OK", "primary": "dbc1-0.mysql:3306" } }, "domainName": "clusterset", "globalPrimaryInstance": "dbc1-0.mysql:3306", "primaryCluster": "mycluster", "status": "HEALTHY", "statusText": "All Clusters available." }
(可选)您可以调用
clusterset.status({extended:1})
来获取其他状态详细信息,包括有关集群的信息。退出 MySQL Shell。
\q
部署 MySQL 路由器
您可以部署 MySQL 路由器,以将客户端应用流量定向到正确的集群。路由基于发出数据库操作的应用的连接端口:
- 写入操作会路由到主 ClusterSet 中的主集群实例。
- 读取操作可以路由到主集群中的任何实例。
当您启动 MySQL 路由器时,它会针对 MySQL InnoDB ClusterSet 部署引导。与 MySQL InnoDB ClusterSet 连接的 MySQL 路由器实例可识别任何受控制的切换或紧急故障切换,并将流量定向到新的主集群。
如需部署 MySQL 路由器,请按照以下步骤操作:
在 Cloud Shell 终端中,部署 MySQL 路由器。
kubectl apply -n mysql1 -f c1-router.yaml
输出类似于以下内容:
configmap/mysql-router-config created service/mysql-router created deployment.apps/mysql-router created
检查 MySQL 路由器部署的就绪情况。
kubectl -n mysql1 get deployment mysql-router --watch
三个 Pod 都准备就绪后,输出类似于以下内容:
NAME READY UP-TO-DATE AVAILABLE AGE mysql-router 3/3 3 0 3m36s
如果您在控制台中看到
PodUnschedulable
错误,请等待一两分钟,此时 GKE 会预配更多节点。刷新后,您应该会看到3/3 OK
。在现有集群的任何成员上启动 MySQL Shell。
kubectl -n mysql1 exec -it dbc1-0 -- \ /bin/bash -c 'mysqlsh --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql"'
此命令会连接到
dbc1-0
Pod,然后启动连接到dbc1-0
MySQL 实例的 shell。验证路由器配置。
clusterset=dba.getClusterSet() clusterset.listRouters()
输出类似于以下内容:
{ "domainName": "clusterset", "routers": { "mysql-router-7cd8585fbc-74pkm::": { "hostname": "mysql-router-7cd8585fbc-74pkm", "lastCheckIn": "2022-09-22 23:26:26", "roPort": 6447, "roXPort": 6449, "rwPort": 6446, "rwXPort": 6448, "targetCluster": null, "version": "8.0.27" }, "mysql-router-7cd8585fbc-824d4::": { ... }, "mysql-router-7cd8585fbc-v2qxz::": { ... } } }
退出 MySQL Shell。
\q
运行此脚本以检查 MySQL 路由器 Pod 的放置位置。
bash ../scripts/inspect_pod_node.sh mysql1 | sort
该脚本会显示
mysql1
命名空间中所有 Pod 的节点和 Cloud 可用区位置,输出类似于以下内容:gke-gkemulti-west-5-default-pool-1ac6e8b5-0h9v us-west1-c mysql-router-6654f985f5-df97q gke-gkemulti-west-5-default-pool-1ac6e8b5-ddjx us-west1-c dbc1-1 gke-gkemulti-west-5-default-pool-1f5baa66-bf8t us-west1-a dbc1-2 gke-gkemulti-west-5-default-pool-1f5baa66-kt03 us-west1-a mysql-router-6654f985f5-qlfj9 gke-gkemulti-west-5-default-pool-4bcaca65-2l6s us-west1-b mysql-router-6654f985f5-5967d gke-gkemulti-west-5-default-pool-4bcaca65-jch0 us-west1-b dbc1-0
您可以观察到 MySQL 路由器 Pod 在各个可用区中均匀分布;也就是说,它不与 MySQL Pod 位于同一节点上,也不与另一个 MySQL 路由器 Pod 位于同一节点上。
管理 GKE 和 MySQL InnoDB 集群升级
MySQL 和 Kubernetes 的更新会定期发布。请遵循运营最佳实践定期更新您的软件环境。默认情况下,GKE 会为您管理集群和节点池升级。Kubernetes 和 GKE 还提供了其他功能来辅助 MySQL 软件升级。
规划 GKE 升级
您可以执行主动步骤并设置配置,以在运行有状态服务时缓解风险并使集群升级更加顺畅,包括:
Standard 集群:遵循 GKE 升级集群的最佳实践。选择适合的升级策略,以确保在维护窗口内进行升级:
如需了解详情,请参阅升级运行有状态工作负载的集群。根据您选择的发布渠道,Autopilot 集群会自动升级。
请使用维护窗口来确保升级在预期时间进行。在维护窗口之前,请确保数据库备份成功。
在允许流量进入升级后的 MySQL 节点之前,请使用就绪性探测和活跃性探测来确保节点已准备好接受流量。
创建探测,以在接受流量之前评估复制是否同步。此任务可以通过自定义脚本完成,具体取决于数据库的复杂程度和规模。
设置 Pod 中断预算 (PDB) 政策
当 MySQL InnoDB 集群在 GKE 上运行时,任意时刻都必须有足够的实例在运行,以满足仲裁要求。
在本教程中,MySQL 集群包含三个实例,则必须有两个实例可用才能形成仲裁。PodDisruptionBudget
政策使您可以限制在任何给定时刻可终止的 Pod 数量。这对于有状态服务的稳定状态操作和集群升级非常有用。
为确保仅有限数量的 Pod 可同时中断,请将工作负载的 PDB 设置为 maxUnavailable: 1
。这样可以确保在服务运行过程中的任何时刻,最多只有一个 Pod 没有在运行。
以下 PodDisruptionBudget
政策清单将 MySQL 应用的最大不可用 Pod 数设置为一。
如需将 PDB 政策应用于集群,请按照以下步骤操作:
使用
kubectl
应用 PDB 政策。kubectl apply -n mysql1 -f mysql-pdb-maxunavailable.yaml
查看 PDB 的状态。
kubectl get poddisruptionbudgets -n mysql1 mysql-pdb -o yaml
在输出的
status
部分中,查看currentHealthy
和desiredHealthy
Pod 计数。输出类似于以下内容:status: ... currentHealthy: 3 desiredHealthy: 2 disruptionsAllowed: 1 expectedPods: 3 ...
规划 MySQL 二进制文件升级
Kubernetes 和 GKE 提供可协助进行 MySQL 二进制文件升级的功能。但是,您需要执行一些操作来为升级做准备。
在开始升级过程之前,请牢记以下注意事项:
- 升级应先在测试环境中执行。对于生产系统,您应该在测试环境中执行进一步测试。
- 对于某些二进制文件版本,执行升级后便无法降级版本。请花些时间了解升级的影响。
- 复制来源可以复制到较新版本。但是,通常不支持从新版本复制到旧版本。
- 在部署升级后的版本之前,请确保您拥有完整的数据库备份。
- 请记住,Kubernetes Pod 具有临时性。重新部署 Pod 时,Pod 存储在非永久性卷中的所有配置状态都将丢失。
- 对于 MySQL 二进制文件升级,请使用上文介绍的相同 PDB、节点池更新策略和探测。
在生产环境中,您应遵循以下最佳实践:
- 使用新版 MySQL 创建容器映像。
- 在源代码控制代码库中保留映像构建说明。
- 使用自动映像构建和测试流水线(如 Cloud Build),并将映像二进制文件存储在 Artifact Registry 等映像存储库中。
为简化本教程,您将不会构建和保留容器映像,而将使用公共 MySQL 映像。
部署升级后的 MySQL 二进制文件
如需执行 MySQL 二进制文件升级,请发出一个修改 StatefulSet 资源映像版本的声明式命令。GKE 会执行必要的步骤来停止当前 Pod,使用升级后的二进制文件部署新的 Pod,并将永久性磁盘挂接到新的 Pod。
验证 PDB 已创建。
kubectl get poddisruptionbudgets -n mysql1
获取有状态集的列表。
kubectl get statefulsets -n mysql1
使用
app
标签获取正在运行的 Pod 列表。kubectl get pods --selector=app=mysql -n mysql1
更新有状态集中的 MySQL 映像。
kubectl -n mysql1 \ set image statefulset/dbc1 \ mysql=mysql/mysql-server:8.0.30
输出类似于以下内容:
statefulset.apps/mysql image updated
检查终止 Pod 和新 Pod 的状态。
kubectl get pods --selector=app=mysql -n mysql1
验证 MySQL 二进制文件升级
在升级期间,您可以验证发布、新 Pod 和现有 Service 的状态。
运行
rollout status
命令来确认升级。kubectl rollout status statefulset/dbc1 -n mysql1
输出类似于以下内容:
partitioned roll out complete: 3 new pods have been updated...
检查有状态集以确认映像版本。
kubectl get statefulsets -o wide -n mysql1
输出类似于以下内容:
NAME READY AGE CONTAINERS IMAGES dbc1 3/3 37m mysql mysql/mysql-server:8.0.30
检查集群状态。
kubectl -n mysql1 \ exec -it dbc1-0 -- \ /bin/bash \ -c 'mysqlsh \ --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-1.mysql.mysql1.svc.cluster.local" \ --js \ --execute "print(dba.getClusterSet().status({extended:1})); print(\"\\n\")"'
对于每个集群实例,查找输出中的状态和版本值。输出类似于以下内容:
... "status": "ONLINE", "version": "8.0.30" ...
回滚上一次的应用部署发布
还原已升级的二进制文件版本的部署时,发布过程会逆转,并会使用先前的映像版本部署一组新的 Pod。
如需将部署还原到先前的工作版本,请使用 rollout undo
命令:
kubectl rollout undo statefulset/dbc1 -n mysql1
输出类似于以下内容:
statefulset.apps/dbc1 rolled back
横向扩容数据库集群
如需横向扩容 MySQL InnoDB 集群,请向 GKE 集群节点池添加更多节点(仅在使用 Standard 集群时才需要此操作),部署更多 MySQL 实例,然后将每个实例添加到现有 MySQL InnoDB 集群。
向 Standard 集群添加节点
如果您使用的是 Autopilot 集群,则无需执行此操作。
如需将节点添加到 Standard 集群,请遵循适用于 Cloud Shell 或 Google Cloud 控制台的以下说明。如需了解详细步骤,请参阅调整节点池的大小。
gcloud
在 Cloud Shell 中,将每个代管式实例组中的默认节点池调整为 8 个实例。
gcloud container clusters resize ${CLUSTER_NAME} \
--node-pool default-pool \
--num-nodes=8
控制台
如需向 Standard 集群添加节点,请执行以下操作:
- 在 Google Cloud 控制台中打开
gkemulti-west1
集群页面。 - 选择节点,然后点击默认池。
- 向下滚动到实例组。
- 对于每个实例组,将
Number of nodes
值从 5 个节点调整为 8 个节点。
向主集群添加 MySQL Pod
如需部署更多 MySQL Pod 以横向扩容集群,请按照以下步骤操作:
在 Cloud Shell 中,将 MySQL 部署中的副本数量从三个副本更新为五个副本。
kubectl scale -n mysql1 --replicas=5 -f c1-mysql.yaml
验证部署的进度。
kubectl -n mysql1 get pods --selector=app=mysql -o wide
如需确定 Pod 是否已准备就绪,请使用
--watch
标志来监视部署。如果您使用的是 Autopilot 集群并看到Pod Unschedulable
错误,则可能表示 GKE 正在预配节点以容纳额外的 Pod。为要添加到集群的新 MySQL 实例配置组复制设置
bash ../scripts/c1-clustersetup.sh 3 4
该脚本会将命令提交到在序数为 3 到 4 的 Pod 上运行的实例。
打开 MySQL Shell。
kubectl -n mysql1 \ exec -it dbc1-0 -- \ /bin/bash \ -c 'mysqlsh \ --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql"'
配置两个新的 MySQL 实例。
dba.configureInstance('root:$MYSQL_ROOT_PASSWORD@dbc1-3.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")}); dba.configureInstance('root:$MYSQL_ROOT_PASSWORD@dbc1-4.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
这些命令会检查实例是否已正确配置以在 MySQL InnoDB 集群中使用,并执行必要的配置更改。
将其中一个新实例添加到主集群。
cluster = dba.getCluster() cluster.addInstance('icadmin@dbc1-3.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
将第二个新实例添加到主集群。
cluster.addInstance('icadmin@dbc1-4.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
获取 ClusterSet 状态,其中也包括集群状态。
clusterset = dba.getClusterSet() clusterset.status({extended: 1})
输出类似于以下内容:
"domainName": "clusterset", "globalPrimaryInstance": "dbc1-0.mysql:3306", "metadataServer": "dbc1-0.mysql:3306", "primaryCluster": "mycluster", "status": "HEALTHY", "statusText": "All Clusters available."
退出 MySQL Shell。
\q
清理
为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。
删除项目
为避免支付费用,最简单的方法是删除您为本教程创建的项目。
Delete a Google Cloud project:
gcloud projects delete PROJECT_ID
后续步骤
- 详细了解 Google Cloud 的运维套件 MySQL 集成如何收集与 InnoDB 相关的性能指标。
- 详细了解 Backup for GKE,该服务用于在 GKE 中备份和恢复工作负载。
- 详细探索永久性卷。