在 GCP GKE 上部署 TiDB 集群

    部署前,确认已安装以下软件:

    配置

    为保证部署顺利,需要提前进行一些配置。在开始配置 Google Cloud SDK、API、Terraform 前,先下载以下资源:

    安装 Google Cloud SDK 后,需要执行 进行初始化

    配置 API

    如果使用的 GCP 项目是新项目,需确保以下 API 已启用:

    1. gcloud services enable cloudresourcemanager.googleapis.com \
    2. cloudbilling.googleapis.com iam.googleapis.com \
    3. compute.googleapis.com container.googleapis.com

    配置 Terraform

    要执行 Terraform 脚本,需要设置以下 3 个环境变量。你可以等 Terraform 提示再输入,也可以提前在 .tfvars 文件中定义变量。

    • GCP_CREDENTIALS_PATH:GCP 证书文件路径。

      • 建议另建一个服务账号给 Terraform 使用,参考。./create-service-account.sh 会创建最低权限的服务账号。

      • 参考服务账号密钥文档来创建服务账号密钥。下面脚本中的步骤详细说明了如何使用 deploy/gcp 目录中提供的脚本执行此操作。或者,如果自己创建服务账号和密钥,可以在创建时选择 JSON 类型的密钥。下载的包含私钥的 JSON 文件即所需的证书文件。

    • GCP_REGION:创建资源所在的区域,例如:us-west1

    • GCP_PROJECT:GCP 项目的名称。

    要使用以上 3 个环境变量来配置 Terraform,可执行以下步骤:

    1. GCP_REGION 替换为你的 GCP Region。

      1. echo GCP_REGION=\"us-west1\" >> terraform.tfvars
    2. GCP_PROJECT 替换为你的 GCP 项目名称,确保连接的是正确的 GCP 项目。

      1. echo "GCP_PROJECT=\"$(gcloud config get-value project)\"" >> terraform.tfvars
    3. 初始化 Terraform。

      1. terraform init
    4. 为 Terraform 创建一个有限权限的服务账号,并设置证书路径。

      1. ./create-service-account.sh

    Terraform 自动加载和填充匹配 terraform.tfvars*.auto.tfvars 文件的变量。相关详细信息,请参阅 。上述步骤会使用 GCP_REGIONGCP_PROJECT 填充 terraform.tfvars 文件,使用 GCP_CREDENTIALS_PATH 填充 credentials.auto.tfvars 文件。

    部署 TiDB 集群

    本小节介绍如何部署 TiDB 集群。

    1. 确定实例类型。

      • 如果只是想试一下 TiDB,又不想花费太高成本,可以采用轻量级的配置:

        1. cat small.tfvars >> terraform.tfvars
      • 如果要对生产环境的部署进行 benchmark 测试,则建议采用生产级的配置:

        1. cat prod.tfvars >> terraform.tfvars

        prod.tfvars 会默认创建一个新的 VPC,两个子网和一个 f1-micro 实例作为堡垒机,以及使用以下实例类型作为工作节点的 GKE 集群:

        • 3 台 n1-standard-4 实例:部署 PD
        • 3 台 n1-highmem-8 实例:部署 TiKV
        • 3 台 n1-standard-16 实例:部署 TiDB
        • 3 台 n1-standard-2 实例:部署监控组件

          如上所述,生产环境的部署需要 91 个 CPU,超过了 GCP 项目的默认配额。可以参考来增加项目配额。扩容同样需要更多 CPU。

        注意:

        • 请通过 variables.tf 文件中的 tidb_operator_version 确认当前版本脚本中默认的 TiDB Operator 版本,如果默认版本不是想要使用的版本,请在 terraform.tfvars 中配置 tidb_operator_version
        • 默认创建的是 Regional 集群,会在 3 个可用区里都创建节点数量。比如配置 pd_count = 1,实际为 PD 创建的节点数为 3 个。可以通过配置 node_locations 来限定可用区,或者 location 来创建 Zonal 集群,具体可参见 examples/ 下例子。
        • 工作节点的数量取决于指定 Region 中可用区的数量。大部分 Region 有 3 个可用区,us-central1 有 4 个可用区。参考 Regions and Zones 查看更多信息。参考部分来自定义区域集群的节点池。
    2. 启动脚本来部署 TiDB 集群:

      1. terraform apply

      注意:

      如果未提前设置上文所述的 3 个环境变量,执行 terraform apply 过程中会有提示出现,要求对 3 个变量进行设置。详情请参考配置 Terraform

      整个过程可能至少需要 10 分钟。terraform apply 执行成功后,会输出类似如下的信息:

      1. Apply complete! Resources: 23 added, 0 changed, 0 destroyed.
      2. Outputs:
      3. how_to_ssh_to_bastion = gcloud compute ssh tidb-cluster-bastion --zone us-west1-b
      4. kubeconfig_file = ./credentials/kubeconfig_tidb-cluster
    1. 准备 TidbCluster 和 TidbMonitor CR 文件:

      1. cp manifests/{db,db-monitor}.yaml.example .

      使用 GKE 部署过程中配置的 default_tidb_cluster_name(默认为 tidb-cluster)替换 db.yamldb-monitor.yaml 文件中所有的 CLUSTER_NAME

      1. sed 's/CLUSTER_NAME/${cluster_name}/g' db.yaml.example > db.yaml
      2. sed 's/CLUSTER_NAME/${cluster_name}/g' db-monitor.yaml.example > db-monitor.yaml

      参考 和集群配置文档完成 CR 文件配置。

      注意:

      • 请确保 GKE 部署过程中 PD、TiKV 或者 TiDB 节点的数量的值,与 db.yaml 中对应组件的 replicas 字段值一致。注意 Regional 集群下,实际创建的节点数为 pd_count/tikv_count/tidb_count 的 3 倍。
      • 请确保 db-monitor.yamlspec.initializer.versiondb.yamlspec.version 一致,以保证监控显示正常。
      1. kubectl --kubeconfig credentials/kubeconfig_${gke_name} create namespace ${namespace}

      注意:

      namespace 是,可以起一个方便记忆的名字,比如和 default_tidb_cluster_name 相同的名称。

    2. 部署 TiDB 集群:

      1. kubectl --kubeconfig credentials/kubeconfig_${gke_name} create -f db.yaml -n ${namespace}
      2. kubectl --kubeconfig credentials/kubeconfig_${gke_name} create -f db-monitor.yaml -n ${namespace}

    访问 TiDB 数据库

    terraform apply 运行完成后,可执行以下步骤来访问 TiDB 数据库。注意用小节的输出信息替换 ${} 部分的内容。

    1. 获取 TiDB Internal LoadBalancer IP 地址:

      其中 EXTERNAL-IP 为 Internal LoadBalancer IP 地址。

    2. 通过 ssh 远程连接到堡垒机。

      1. gcloud compute ssh ${gke_cluster_name}-bastion --zone ${zone}
    3. 通过 MySQL 客户端来访问 TiDB 集群。

      1. mysql -h ${tidb_ilb_ip} -P 4000 -u root

    与 GKE 集群交互

    你可以通过 kubectlhelm 使用 kubeconfig 文件 credentials/kubeconfig_${gke_cluster_name} 和 GKE 集群交互。交互方式主要有以下两种。

    注意:

    gke_cluster_name 默认为 tidb-cluster,可以通过 variables.tfgke_name 修改。

    • 指定 --kubeconfig 参数:

      1. kubectl --kubeconfig credentials/kubeconfig_${gke_cluster_name} get po -n ${tidb_cluster_name}

      注意:

      下面这条命令使用的 --kubeconfig 参数至少需要 Helm 2.10.0 版本以上。

      1. helm --kubeconfig credentials/kubeconfig_${gke_cluster_name} ls
    • 设置 KUBECONFIG 环境变量:

      1. export KUBECONFIG=$PWD/credentials/kubeconfig_${gke_cluster_name}
      1. kubectl get po -n ${tidb_cluster_name}
      1. helm ls

    一个 tidb-cluster 模块的实例对应一个 GKE 集群中的 TiDB 集群。要添加一个新的 TiDB 集群,可执行以下步骤:

    1. 编辑 tidbclusters.tf 文件来添加一个 tidb-cluster 模块。

      例如:

      1. module "example-tidb-cluster" {
      2. providers = {
      3. helm = "helm.gke"
      4. }
      5. source = "../modules/gcp/tidb-cluster"
      6. cluster_id = module.tidb-operator.cluster_id
      7. tidb_operator_id = module.tidb-operator.tidb_operator_id
      8. gcp_project = var.GCP_PROJECT
      9. gke_cluster_name = ${gke_cluster_name}
      10. cluster_name = "example-tidb-cluster"
      11. cluster_version = "v3.0.1"
      12. kubeconfig_path = local.kubeconfig
      13. tidb_cluster_chart_version = "v1.0.0"
      14. pd_instance_type = "n1-standard-1"
      15. tikv_instance_type = "n1-standard-4"
      16. monitor_instance_type = "n1-standard-1"
      17. pd_node_count = 1
      18. tikv_node_count = 2
      19. tidb_node_count = 1
      20. monitor_node_count = 1
      21. }

      注意:

      • 每个集群的 cluster_name 必须是唯一的。
      • 为任一组件实际创建的总节点数 = 配置文件中的节点数 * 配置的可用区的个数(Regional 集群默认为 3)。
    2. 修改完成后,执行以下命令来创建集群。

      1. terraform init
      2. terraform apply

    扩容

    如果需要扩容 TiDB 集群,可执行以下步骤:

    1. 增大 .tfvars 文件中 pd_counttikv_counttidb_count 变量。
    2. 运行 terraform apply

    警告:

    由于缩容过程中无法确定哪个节点会被删除,因此目前不支持集群缩容。通过修改 tikv_count 来进行缩容可能会导致数据丢失。

    扩容过程会持续几分钟,你可以通过以下命令来持续观察进度:

    1. kubectl --kubeconfig credentials/kubeconfig_${gke_cluster_name} get po -n ${tidb_cluster_name} --watch

    例如,可以将 tidb_count 从 1 改为 2 来扩容 TiDB:

    1. tidb_count = 2

    自定义

    你可以更改 variables.tf 中的默认值,例如集群名称和镜像版本等,但更建议在 terraform.tfvars 文件或其它相关文件中来指定值。

    GCP 允许 n1-standard-1 或者更大的实例类型挂载本地 SSD,这提供了更好的自定义特性。

    自定义 TiDB 参数配置

    Terraform 脚本为 GKE 中的 TiDB 集群提供了默认设置。你也可以在 tidbclusters.tf 中为每个 TiDB 集群指定一个覆盖配置 override_values 或者覆盖配置文件 override_values_file。如果同时配置两个变量,override_values 配置将生效,该自定义配置会覆盖默认设置,示例如下:

    1. override_values = <<EOF
    2. discovery:
    3. image: pingcap/tidb-operator:v1.0.1
    4. imagePullPolicy: IfNotPresent
    5. resources:
    6. limits:
    7. cpu: 250m
    8. memory: 150Mi
    9. requests:
    10. cpu: 30m
    11. memory: 30Mi
    12. EOF
    1. override_values_file = "./test-cluster.yaml"

    集群默认使用 deploy/modules/gcp/tidb-cluster 模块中的 values/default.yaml 作为覆盖配置文件。

    如果需要自定义集群版本和副本数,可以修改 tidbclusters.tf 文件中每个 tidb-cluster module 的参数。

    注意:

    自定义配置中,不建议在 values.yaml 中包含以下配置(tidb-cluster module 默认固定配置):

    自定义 TiDB Operator

    如果要自定义 TiDB Operator,可以使用 operator_helm_values 变量来指定覆盖配置或者使用 operator_helm_values_file 变量来指定覆盖配置文件。如果同时配置两个变量,operator_helm_values 配置将生效,该自定义配置会传递给 tidb-operator 模块,示例如下:

    1. operator_helm_values = <<EOF
    2. controllerManager:
    3. resources:
    4. limits:
    5. cpu: 250m
    6. memory: 150Mi
    7. requests:
    8. cpu: 30m
    9. memory: 30Mi
    10. EOF
    1. operator_helm_values_file = "./test-operator.yaml"

    GKE 使用 作为其默认的日志收集工具,然后将日志转发到 Stackdriver。Fluentd 进程可能会占用大量资源,消耗大量的 CPU 和 RAM。Fluent Bit 是一种性能更高,资源占用更少的替代方案。与 Fluentd 相比,更建议在生产环境中使用 Fluent Bit。可参考。

    自定义节点池

    集群是按区域 (regional) 而非按可用区 (zonal) 来创建的。也就是说,GKE 向每个可用区复制相同的节点池,以实现更高的可用性。但对于 Grafana 这样的监控服务来说,通常没有必要维护相同的可用性。你可以通过 gcloud 手动删除节点。

    注意:

    GKE 节点池通过实例组管理。如果你使用 gcloud compute instances delete 命令删除某个节点,GKE 会自动重新创建节点并将其添加到集群。

    如果你需要从监控节点池中删掉一个节点,可采用如下步骤:

    1. 获取托管的实例组和所在可用区。

      1. gcloud compute instance-groups managed list | grep monitor

      输出结果类似:

      1. gke-tidb-monitor-pool-08578e18-grp us-west1-b zone gke-tidb-monitor-pool-08578e18 0 0 gke-tidb-monitor-pool-08578e18 no
      2. gke-tidb-monitor-pool-7e31100f-grp us-west1-c zone gke-tidb-monitor-pool-7e31100f 1 1 gke-tidb-monitor-pool-7e31100f no
      3. gke-tidb-monitor-pool-78a961e5-grp us-west1-a zone gke-tidb-monitor-pool-78a961e5 1 1 gke-tidb-monitor-pool-78a961e5 no

      第一列是托管的实例组,第二列是所在的可用区。

    2. 获取实例组中的实例名字。

      1. gcloud compute instance-groups managed list-instances ${instance_group} --zone ${zone}

      示例:

      1. gcloud compute instance-groups managed list-instances gke-tidb-monitor-pool-08578e18-grp --zone us-west1-b

      输出结果类似:

      1. NAME ZONE STATUS ACTION INSTANCE_TEMPLATE VERSION_NAME LAST_ERROR
      2. gke-tidb-monitor-pool-08578e18-c7vd us-west1-b RUNNING NONE gke-tidb-monitor-pool-08578e18
    3. 通过指定托管的实例组和实例的名称来删掉该实例。

      例如:

      1. gcloud compute instance-groups managed delete-instances gke-tidb-monitor-pool-08578e18-grp --instances=gke-tidb-monitor-pool-08578e18-c7vd --zone us-west1-b

    如果你不想再继续使用 TiDB 集群,可以通过如下命令进行销毁:

    1. terraform destroy

    注意:

    在执行 terraform destroy 过程中,可能会发生错误:Error reading Container Cluster "tidb": Cluster "tidb" has status "RECONCILING" with message""。当 GCP 升级 Kubernetes master 节点时会出现该问题。一旦问题出现,就无法删除集群,需要等待 GCP 升级结束,再次执行 terraform destroy

    删除磁盘

    如果你不再需要之前的数据,并且想要删除正在使用的磁盘,有以下两种方法可以完成此操作:

    • 手动删除:在 Google Cloud Console 中删除磁盘,或使用 gcloud 命令行工具执行删除操作。

    • 自动删除:在执行 terraform destroy 之前将 Kubernetes 的 PV (Persistent Volume) 回收策略设置为 Delete,具体操作为在 terraform destroy 之前运行以下 kubectl 命令:

      1. kubectl --kubeconfig /path/to/kubeconfig/file get pvc -n namespace-of-tidb-cluster -o jsonpath='{.items[*].spec.volumeName}'|fmt -1 | xargs -I {} kubectl --kubeconfig /path/to/kubeconfig/file patch pv {} -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'

      上述命令将获取 TiDB 集群命名空间中的 PVC (Persistent Volume Claim),并将绑定的 PV 的回收策略设置为 Delete。在执行 terraform destroy 过程中删除 PVC 时,也会将磁盘删除。

      下面是一个名为 change-pv-reclaimpolicy.sh 的脚本。相对于仓库根目录来说,它在 deploy/gcp 目录,简化了上述过程。

      1. ./change-pv-reclaimpolicy.sh /path/to/kubeconfig/file ${tidb_cluster_namespace}

    管理多个 Kubernetes 集群

    本节介绍管理多个 Kubernetes 集群的最佳实践,其中每个 Kubernetes 集群都可以部署一个或多个 TiDB 集群。

    在 TiDB 的案例中,Terraform 模块通常结合了几个子模块:

    • tidb-operator:为 TiDB 集群提供 并部署 TiDB Operator。
    • tidb-cluster:在目标 Kubernetes 集群中创建资源池。
    • 一个 vpc 模块,一个 bastion 模块和一个 project-credentials 模块:专门用于 GKE 上的 TiDB 集群。

    管理多个 Kubernetes 集群的最佳实践有以下两点:

    1. 为每个 Kubernetes 集群创建一个新目录。
    2. 根据具体需求,使用 Terraform 脚本将上述模块进行组合。

    如果采用了最佳实践,集群中的 Terraform 状态不会相互干扰,并且可以很方便地管理多个 Kubernetes 集群。示例如下(假设已在项目根目录):

    1. mkdir -p deploy/gcp-staging && \
    2. vim deploy/gcp-staging/main.tf

    deploy/gcp-staging/main.tf 中的内容类似:

    1. provider "google" {
    2. credentials = file(var.GCP_CREDENTIALS_PATH)
    3. region = var.GCP_REGION
    4. project = var.GCP_PROJECT
    5. }
    6. // required for taints on node pools
    7. provider "google-beta" {
    8. credentials = file(var.GCP_CREDENTIALS_PATH)
    9. region = var.GCP_REGION
    10. }
    11. locals {
    12. gke_name = "another-gke-name"
    13. credential_path = "${path.cwd}/credentials"
    14. kubeconfig = "${local.credential_path}/kubeconfig_${var.gke_name}"
    15. }
    16. module "project-credentials" {
    17. source = "../modules/gcp/project-credentials"
    18. }
    19. module "vpc" {
    20. source = "../modules/gcp/vpc"
    21. create_vpc = true
    22. gcp_project = var.GCP_PROJECT
    23. gcp_region = var.GCP_REGION
    24. vpc_name = "${locals.gke_name}-vpc-network"
    25. private_subnet_name = "${locals.gke_name}-private-subnet"
    26. public_subnet_name = "${locals.gke_name}-public-subnet"
    27. }
    28. module "tidb-operator" {
    29. source = "../modules/gcp/tidb-operator"
    30. gke_name = locals.gke_name
    31. vpc_name = module.vpc.vpc_name
    32. subnetwork_name = module.vpc.private_subnetwork_name
    33. gcp_project = var.GCP_PROJECT
    34. gcp_region = var.GCP_REGION
    35. kubeconfig_path = local.kubeconfig
    36. tidb_operator_version = "v1.0.0"
    37. }
    38. module "bastion" {
    39. source = "../modules/gcp/bastion"
    40. vpc_name = module.vpc.vpc_name
    41. public_subnet_name = module.vpc.public_subnetwork_name
    42. gcp_project = var.GCP_PROJECT
    43. bastion_name = "${locals.gke_name}-tidb-bastion"
    44. }
    45. # HACK: 强制使 Helm 依赖 GKE 集群
    46. data "local_file" "kubeconfig" {
    47. depends_on = [module.tidb-operator.cluster_id]
    48. filename = module.tidb-operator.kubeconfig_path
    49. }
    50. resource "local_file" "kubeconfig" {
    51. depends_on = [module.tidb-operator.cluster_id]
    52. content = data.local_file.kubeconfig.content
    53. filename = module.tidb-operator.kubeconfig_path
    54. }
    55. provider "helm" {
    56. alias = "gke"
    57. insecure = true
    58. install_tiller = false
    59. kubernetes {
    60. config_path = local_file.kubeconfig.filename
    61. }
    62. }
    63. module "tidb-cluster-a" {
    64. providers = {
    65. helm = "helm.gke"
    66. }
    67. source = "../modules/gcp/tidb-cluster"
    68. gcp_project = var.GCP_PROJECT
    69. gke_cluster_location = var.GCP_REGION
    70. gke_cluster_name = locals.gke_name
    71. cluster_name = "tidb-cluster-a"
    72. cluster_version = "v3.0.1"
    73. kubeconfig_path = module.tidb-operator.kubeconfig_path
    74. tidb_cluster_chart_version = "v1.0.0"
    75. pd_instance_type = "n1-standard-1"
    76. tikv_instance_type = "n1-standard-4"
    77. tidb_instance_type = "n1-standard-2"
    78. monitor_instance_type = "n1-standard-1"
    79. }
    80. module "tidb-cluster-b" {
    81. providers = {
    82. helm = "helm.gke"
    83. }
    84. source = "../modules/gcp/tidb-cluster"
    85. gcp_project = var.GCP_PROJECT
    86. gke_cluster_location = var.GCP_REGION
    87. gke_cluster_name = locals.gke_name
    88. cluster_name = "tidb-cluster-b"
    89. cluster_version = "v3.0.1"
    90. kubeconfig_path = module.tidb-operator.kubeconfig_path
    91. tidb_cluster_chart_version = "v1.0.0"
    92. pd_instance_type = "n1-standard-1"
    93. tikv_instance_type = "n1-standard-4"
    94. tidb_instance_type = "n1-standard-2"
    95. monitor_instance_type = "n1-standard-1"
    96. }
    97. output "how_to_ssh_to_bastion" {
    98. value= module.bastion.how_to_ssh_to_bastion
    99. }

    如上述代码所示,你可以在每个模块调用中省略几个参数,因为有合理的默认值,并且可以轻松地自定义配置。例如,如果你不需要调用堡垒机模块,将其删除即可。

    如果要自定义每个字段,可使用以下两种方法中的一种:

    • 直接修改 *.tf 文件中 module 的参数配置。
    • 参考每个模块的 variables.tf 文件,了解所有可修改的参数,并在 terraform.tfvars 中设置自定义值。

    注意:

    • 创建新目录时,请注意其与 Terraform 模块的相对路径,这会影响模块调用期间的 source 参数。
    • 如果要在 tidb-operator 项目之外使用这些模块,务必确保复制整个 modules 目录并保持目录中每个模块的相对路径不变。
    • 由于 Terraform 的限制(参见 hashicorp/terraform#2430),上面示例中添加了 HACK: 强制使 Helm 依赖 GKE 集群部分对 Helm provider 进行处理。如果自己编写 tf 文件,需要包含这部分内容。