ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PolarDB-X 三种部署方式详解:从二进制到Docker与K8s的完整实践

PolarDB-X 三种部署方式详解:从二进制到Docker与K8s的完整实践 开头先交代一下背景。之前一直在用单机MySQL做业务后来数据量上来分库分表的诉求越来越强烈就盯上了PolarDB-X。真正上手才发现这套分布式数据库的部署路径比想象中多docker、k8s、二进制部署这三种方式我前后都折腾了一遍踩了不少坑但也因此把它内部那几个核心角色之间的关系彻底搞明白了。这篇文章就把我完整的实操过程、关键原理和踩坑记录整理出来给准备部署PolarDB-X的DBA、后端开发和SRE做个参考。1. 部署PolarDB-X前先搞懂它由哪些角色组成1.1 GMS、CN、DN、CDC各自在集群里干什么很多人拿到PolarDB-X第一反应是这不就是 MySQL 换个壳吗装一个实例然后用 MySQL 客户端连上去不就行了。这个想法对了一半PolarDB-X 的通信协议确实兼容 MySQL但它的底层架构完全不是单实例而是由多个分布式角色协作组成的。一个最小的 PolarDB-X 集群通常包含这几个角色GMSGlobal Meta Service全局元数据服务负责管理整个集群的拓扑结构、库表元数据、权限信息、全局事务状态。它相当于整个集群的大脑所有关于这个表在哪些 DN 上有分片的信息都存在这里。CNCompute Node计算节点用户接入的入口负责接收 SQL、做语法解析、生成执行计划、把请求分发到对应的 DN 上最后汇总结果返回给客户端。DNData Node数据节点真正存储数据的组件底层是一个兼容 MySQL 协议的存储引擎支持多副本数据按分片规则散落在多个 DN 上。CDCChange Data Capture变更数据捕获服务负责把事务日志解析成标准的 Binlog 流供下游做数据同步或者增量订阅。如果把 PolarDB-X 比作一个公司GMS 是行政中枢CN 是前台接待DN 是各个业务部门CDC 是给外部合作伙伴定期同步工作简报的接口人。任何一个角色挂了集群的健康状态都会受影响只不过影响面有大有小。1.2 三种部署路径的本质区别谁在管理这些角色docker、k8s、二进制部署说到底解决的是同一个问题怎么把这个几个角色跑起来并让它们互相发现、协同工作。区别在于管理的尺度和出问题之后由谁来自动处理。二进制部署是进程级的所有步骤都要自己来。下载压缩包、解压、改配置、启动等连组件之间怎么注册、怎么发现都得按顺序手动搞定。好处是你能看到每个进程的真实行为对理解 PolarDB-X 内部机制帮助很大。Docker 部署是容器级的镜像里已经把所有角色的可执行文件、依赖库、运行环境都打包好了。用 docker-compose 定义好服务拓扑一条命令就能拉起来。它对宿主机的依赖只剩下一个 Docker 引擎隔离性和可移植性都更好。K8s 部署是声明式资源级的你只需要告诉 Kubernetes我想要一个什么样的 PolarDB-X 集群剩下的副本管理、故障转移、存储分配、网络暴露都由 Operator 自动完成。这是生产环境最推荐的方式因为它把分布式数据库的运维复杂度压缩到了一次 apply。1.3 为什么 etcd 在这套体系里这么重要PolarDB-X 的组件之间不是靠静态 IP 配置文件互相连接的而是通过 etcd 做服务发现。GMS、CN、DN 启动之后都会向 etcd 注册自己的地址和状态CN 在处理用户请求时需要实时从 etcd 查询当前集群里有哪几个 DN、路由信息是什么。这也是为什么无论用哪种方式部署etcd 都是绕不开的前置组件。理解这一点后面排查各种连不上找不到节点的问题会轻松很多。2. 二进制部署手动拉起每个进程的实操记录2.1 下载二进制包与前置依赖准备二进制部署花的时间最长但也是我收获最大的部分。先说环境我用的是一台 4C8G 的 Linux 虚拟机操作系统是 CentOS 7.9。这个配置跑一个最小集群刚刚好如果内存只有 4G 会比较紧张因为 GMS 和 CN 都是 Java 服务光 JVM 堆就可能吃掉 4G 以上。前置依赖有三个JDK 8、etcd、MySQL 客户端。# 安装 JDK 8 yum install -y java-1.8.0-openjdk # 下载 etcd 单节点版本用于测试 wget https://github.com/etcd-io/etcd/releases/download/v3.5.0/etcd-v3.5.0-linux-amd64.tar.gz tar zxvf etcd-v3.5.0-linux-amd64.tar.gz mv etcd-v3.5.0-linux-amd64 /opt/etcdPolarDB-X 的二进制发布包主要分两部分。一部分是 polardbx-engine也就是 DN 存储引擎本质上是 MySQL 的一个分布式分支另一部分是 polardbx-sql里面同时包含 GMS 和 CN 这两个 Java 服务的启动脚本。到 GitHub Releases 页面下载对应版本即可需要注意 release note 里会标明这个版本对应的 polardbx-engine 和 polardbx-sql 版本要配套使用不要混搭。2.2 启动 etcd 并验证服务发现先启动 etcd因为后面所有组件都要往这里注册。测试环境用单节点就行生产环境至少三个节点起步。/opt/etcd/etcd \ --name pxc-etcd-1 \ --listen-client-urls http://0.0.0.0:2379 \ --advertise-client-urls http://192.168.1.10:2379 \ --listen-peer-urls http://0.0.0.0:2380 \ --initial-advertise-peer-urls http://192.168.1.10:2380 \ --initial-cluster pxc-etcd-1http://192.168.1.10:2380 \ --initial-cluster-state new这里有一个非常关键的细节advertise-client-urls 不能写 127.0.0.1。如果写了回环地址etcd 自己在本机注册没问题但其他机器上的 GMS、DN、CN 来查询时会拿到一个谁也访问不到的地址导致集群之间互相发现不了。我第一次踩坑就栽在这里CN 一直报unable to fetch topology from meta storage排查了很久才发现是 etcd 的 advertise 地址写错了。启动后可以用 etcdctl 验证一下/opt/etcd/etcdctl --endpointshttp://192.168.1.10:2379 endpoint health返回 healthy 就说明服务发现的基础设施已经就绪。2.3 按顺序启动 GMS、DN、CN启动顺序是有讲究的大致是 etcd - GMS - DN - CN。GMS 要先起来因为 DN 注册时需要向 GMS 获取一些元数据信息CN 最后启动因为它启动时会去发现集群中已有的 GMS 和 DN。GMS 启动命令大致如下cd /opt/polardbx-sql bin/start_gms.sh --port 3306 --etcd http://192.168.1.10:2379DN 启动命令cd /opt/polardbx-engine bin/start_dn.sh --port 3307 --etcd http://192.168.1.10:2379CN 启动命令cd /opt/polardbx-sql bin/start_cn.sh --port 8522 --etcd http://192.168.1.10:2379每个组件启动后都会向 etcd 注册。等三个角色都起来后用 MySQL 客户端连 CN 的 8522 端口验证mysql -h127.0.0.1 -P8522 -upolardbx_root -p123456能连上之后可以先建一个带分片键的测试表验证分布式能力是否真的生效create database test_db; create table test_order ( id bigint not null auto_increment, user_id bigint not null, amount decimal(10,2), primary key(id), key idx_user(user_id) ) partition by hash(user_id) partitions 4;如果这条建表语句能成功执行说明 CN 已经把分片规则同步给各个 DN 了分布式表是真的建出来了不是单机模拟。2.4 二进制部署常见的三个坑第一个坑是 JVM 内存参数。GMS 和 CN 默认的 -Xmx 可能设得比较大在 8G 内存的机器上同时跑三个角色很容易因为内存不足导致进程被操作系统 OOM Kill。建议在启动脚本里显式指定 -Xmx2g 之类的小堆配置。第二个坑是端口冲突。8522 是 CN 对外服务的默认端口3306 是 GMS 的默认端口3307 是 DN 的默认端口。如果你的机器上装了 MySQL 或者别的服务占了这些端口启动会直接失败。部署前先用 ss -lntp 检查一下端口占用情况。第三个坑是日志不落盘。很多启动脚本默认把日志输出到 nohup.out 或者某个固定的 logs 目录但如果目录权限不对进程可能启动了但日志写不进去看起来像卡住了一样。遇到这种情况先确认运行用户对 logs 目录有写权限然后去看实际日志文件而不是看终端输出。3. Docker Compose 部署一台机器快速跑起完整集群3.1 all-in-one 镜像与 compose 编排二进制部署太折腾每次想快速验证一个功能都得先花半小时把环境搭起来。后来我发现官方提供了 all-in-one 的 Docker 镜像polardb/polardb-x它把 GMS、CN、DN、CDC 全部封装在了同一个容器里容器启动时会自动拉起这几个内部进程。这对本地开发来说非常友好。我用的 docker-compose.yml 是这样的version: 3 services: polardb-x: image: polardb/polardb-x:latest container_name: polardb-x ports: - 8522:8522 - 8080:8080 environment: - MODEdev volumes: - pxc-data:/data restart: unless-stopped volumes: pxc-data:启动命令就一行docker compose up -d然后看日志docker compose logs -f polardb-x看到类似 PolarDB-X is ready 的日志输出就可以连接了。3.2 初始化与连接验证连接方式跟二进制部署完全一样还是通过 8522 端口mysql -h127.0.0.1 -P8522 -upolardbx_root -p123456all-in-one 镜像的默认账号密码一般是polardbx_root/123456具体以镜像文档的说明为准。这个方案最方便的地方在于Docker 镜像把二进制部署过程中所有容易出错的环节都屏蔽掉了。etcd 不需要自己装JVM 参数不需要自己调角色之间的启动顺序也不需要关心。对只是想体验 PolarDB-X 功能、验证业务代码兼容性的人来说这是性价比最高的选择。3.3 镜像下载慢和 Docker Desktop 虚拟化问题的处理用 Docker 部署绕不开两个问题。第一个是镜像下载慢。polardb/polardb-x这个镜像体积不算小如果是首次在本地拉取可能会等很久。解决办法是配置镜像加速器Docker Desktop、国内各大云厂商都提供加速地址在 Docker 引擎配置里加上 registry-mirrors 就行。另外一个土办法是先在一台网络好的服务器上 docker pull 完再 save 成 tar 包导到目标机器上。第二个问题在 Windows 上比较常见。很多人在 Windows 用 Docker Desktop 启动时报错提示 virtualization support not detected 或者 Docker Desktop failed to start because virtualisation support wasnt detected。这个问题的根源是 Hyper-V 或者 WSL2 没开启进 BIOS 把 CPU 虚拟化打开然后在 Windows 功能里启用适用于 Linux 的 Windows 子系统和虚拟机平台重启之后再启动 Docker Desktop 就正常了。3.4 挂载与数据持久化建议我特别想强调数据持久化这一点。docker-compose 里如果在 volumes 中只写了命名卷但容器内数据库目录没挂准容器一重建数据就没了。我在测试时就干过这种事辛辛苦苦建的表和数据一条 docker compose down 再 up 全部清空只能从头再来。建议是把容器内的数据目录显式挂载到宿主机的一个固定目录比如volumes: - /data/polardb-x:/data这样即使容器被删了宿主机上/data/polardb-x目录里的数据还在。实际操作时先启动一次容器用docker exec进去看看数据实际写在哪个路径再修改挂载关系这样最稳妥。4. K8s 部署用 Operator 管理有状态集群4.1 为什么原生 Deployment 不适合 PolarDB-X 这类有状态应用Docker 方式虽然省心但它是单体容器所有角色堆在一个容器里没法独立扩缩容。比如业务增长需要把 CN 从 1 个扩到 3 个Docker 方式没法优雅地做到。K8s 部署就能解决这个问题。但直接用原生 Deployment 去跑 PolarDB-X 也不太合适。关键原因在于 PolarDB-X 的每个角色都有状态DN 的数据要持久化到 PV 上多副本之间要保证顺序启动故障后要重新调度到可用节点。这些逻辑如果都自己写 K8s yaml工作量巨大且维护成本极高。官方提供的方案是 PolardbX Operator。它本质上是一个运行在 K8s 里的控制器监听用户提交的PolarDBXCluster自定义资源然后自动创建和管理对应的 StatefulSet、Service、PVC 等底层资源。4.2 Helm 安装 Operator前提是你已经有一套可用的 K8s 集群并且安装了 Helm。用 Helm 安装 Operator 比较干净helm repo add polardbx https://polardb.github.io/polardb-operator helm repo update helm install polardbx-operator polardbx/polardb-operator -n polardbx-system --create-namespace安装完成后确认一下kubectl get pods -n polardbx-system看到 polardbx-operator-controller-manager 处于 Running 状态说明 Operator 已经就绪。这里提醒一下K8s 版本不要太老我测试时用的是 1.24 版本Operator 对太老的 K8s 版本兼容性不太好有可能出现 CRD 注册失败的情况。4.3 编写 PolarDBXCluster YAMLOperator 装好之后创建一个 PolarDB-X 集群实例。下面是我测试用的最小 YAMLapiVersion: polardbx.aliyun.com/v1 kind: PolarDBXCluster metadata: name: pxc-demo namespace: polardbx spec: topology: cn: replicas: 2 resources: limits: cpu: 2 memory: 4Gi dn: replicas: 2 resources: limits: cpu: 2 memory: 4Gi storage: class: local-path注意spec.storage.class这个字段它指定的是 K8s 集群里的 StorageClass。如果集群里没有可用的默认 StorageClassPVC 会一直卡在 Pending 状态Pod 也起不来。我自己就踩过这个坑后来在 Kind 集群里装了一个 local-path-provisioner并在 YAML 里显式指定Pod 才正常调度。应用 YAMLkubectl apply -f pxc-demo.yaml然后观察状态kubectl get polardbxcluster -n polardbx kubectl get pods -n polardbx等 Pod 全部 Running集群就创建成功了。整个过程中 Operator 会自动完成 GMS、CN、DN 的创建和初始化不需要人工介入。4.4 访问集群与常见故障排查K8s 集群内部的 Pod 不是直接暴露给外部的需要把 CN 的服务端口转发出来kubectl port-forward svc/pxc-demo-cn -n polardbx 8522:8522然后在本机执行mysql -h127.0.0.1 -P8522 -upolardbx_root -p123456排查问题最常用的三个命令# 看集群自定义资源状态 kubectl describe polardbxcluster pxc-demo -n polardbx # 看具体 Pod 日志 kubectl logs -f pxc-demo-cn-0 -n polardbx # 看 PVC 状态 kubectl get pvc -n polardbx我遇到最多的故障场景是镜像拉取超时也就是 Pod 状态显示 ImagePullBackOff。这种情况一般不是因为镜像不存在而是 K8s 集群无法访问外网镜像仓库或者没有配置 imagePullSecret。在离线环境部署时需要先把镜像推到内网仓库然后在 YAML 里把 image 字段改成内网地址或者给 namespace 配置 imagePullSecret。4.5 生产环境要额外关注的资源与调度设置如果只是测试上面的配置完全够用。但生产环境部署时有几个细节必须处理。一个是资源请求和限制。不要把 request 和 limit 写成一样的值否则节点资源稍微紧张时Pod 不会被合理挤占可能导致调度失败。建议 request 设成实际使用量的 80%limit 设成峰值。另一个是节点亲和性。DN 是有状态节点扩容和故障迁移时尽量让副本分布在不同节点上避免同一个物理机挂掉导致多个副本同时不可用。可以在 YAML 里通过 nodeAffinity 或者 podAntiAffinity 控制让同一个集群的 DN 副本分散部署。还有一个是备份。Operator 只管集群的生命周期不管数据备份。生产环境一定要在 K8s 外面配置周期性的数据备份和恢复演练不要以为集群跑在 K8s 上就万事大吉了。5. 三种部署方式怎么选对比与建议5.1 从安装速度、运维成本、故障恢复能力看差异把三种方式放到一起对比差异非常明显对比项二进制部署Docker ComposeK8s Operator前置依赖JDK、etcd、各类系统库Docker 引擎K8s 集群 Helm安装速度慢手动操作多快一条命令拉起中等取决于集群是否就绪故障恢复手动查看日志、手动重启手动重启容器Operator 自动调度恢复扩缩容能力难需要手动加节点不可行单容器固定角色支持 CN/DN 独立扩缩容数据持久化本地目录自己管理挂载卷自己管理PVC 自动分配适合场景学习原理、二次开发本地开发、功能验证生产环境、长期运行从表格能看出来三者的定位其实是完全不同的。二进制部署适合钻研原理Docker 适合快速起步K8s 才是真正面向生产环境的方案。5.2 我推荐的场景选择如果是想弄懂CN 启动时是怎么发现 DN 的GMS 挂了对集群有什么影响这类问题用二进制部署过一遍是最值得的。虽然过程繁琐但你会对每个角色的职责有非常具象的认知。这个认知在以后排查任何分布式数据库问题时都会帮到你。如果是日常写代码联调需要一整套 MySQL 兼容的分布式环境直接选 Docker Compose。不要在生产环境用这个方案不是因为性能差而是它把所有角色塞进一个容器失去了分布式部署的意义出了问题也不好隔离。如果是要给业务提供长期稳定的数据库服务生产方式选 K8s Operator。它把副本管理、故障恢复、存储分配都标准化了后续扩容缩容、版本升级都有现成的路径。前提是你的团队有基本的 K8s 运维能力。5.3 部署前通用检查清单几个容易被忽略的细节最后分享几个在三种部署方式下都适用的小细节都是我实际踩过的第一NTP 时间同步。分布式数据库对节点间的时间偏差很敏感尤其是涉及事务、日志时间戳的场景。时间不一致会导致一些看起来毫无规律的问题比如事务提交报错、binlog 位点错乱。部署前确保所有节点都配置了 NTP 同步。第二文件句柄限制。PolarDB-X 的数据节点和计算节点在并发高的时候会打开大量文件系统默认的 ulimit 1024 肯定不够。在启动前检查一下 ulimit -n如果是 1024改到 65535 以上再部署。第三swap 的问题。Java 服务最怕内存被 swap 换出GC 停顿会明显变大。如果条件允许在部署节点上关闭 swap 或者把 swappiness 调得很低。很多诡异的性能问题排查到最后都是 swap 导致的。部署这套东西一开始会觉得步骤多、概念多但当你真正把三种方式都走一遍你会发现 PolarDB-X 的架构设计其实非常清晰。我自己的体会是第一次部署时踩的那些坑——端口冲突、etcd 地址写错、PVC 卡 Pending——才是最宝贵的经验。如果你现在正准备部署建议从二进制或者 Docker 开始跑通一台机器再考虑往 K8s 上迁移这样每一步都有底。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进