K8s技术问询:如何仅在部署NodeJS的节点各部署1个MySQL Router
首先直接给结论:Kubernetes默认的原生资源(比如Deployment、DaemonSet、StatefulSet)并不支持这种“动态跟踪特定Pod所在节点并部署对应实例”的逻辑。默认组件要么是按固定副本数部署(Deployment),要么是在所有匹配标签的节点上部署(DaemonSet),无法自动感知其他Pod的分布并调整部署目标。
接下来咱们聊聊具体怎么实现你的需求:
方案一:动态节点标签 + DaemonSet(最易上手)
这个方案的核心思路是:
- 定期扫描命名空间X里的NodeJS Pod,给运行了至少一个NodeJS实例的节点打上专属标签
- 用DaemonSet部署MySQL Router,让它只在带有这个专属标签的节点上运行(DaemonSet天然保证每个节点只跑一个Pod)
步骤1:给NodeJS Pod打上统一标签
首先确保你的NodeJS Deployment/StatefulSet的Pod有统一标签,比如app: nodejs,这样方便后续识别:
# 示例NodeJS Deployment片段 apiVersion: apps/v1 kind: Deployment metadata: name: nodejs-app namespace: X spec: selector: matchLabels: app: nodejs template: metadata: labels: app: nodejs # 统一标签 spec: containers: - name: nodejs image: your-nodejs-image:latest
步骤2:编写节点标签自动更新脚本
写一个简单的脚本,用来识别有NodeJS Pod的节点并打标签,同时移除没有NodeJS Pod的节点的标签:
#!/bin/bash NAMESPACE="X" NODEJS_LABEL="app=nodejs" TARGET_LABEL="has-nodejs-app=true" # 获取当前运行NodeJS Pod的所有节点(去重) nodes_with_nodejs=$(kubectl get pods -n $NAMESPACE -l $NODEJS_LABEL -o json | jq -r '.items[].spec.nodeName' | sort | uniq) # 获取当前带有目标标签的所有节点 nodes_with_label=$(kubectl get nodes -l has-nodejs-app=true -o json | jq -r '.items[].metadata.name') # 给有NodeJS的节点打标签(覆盖已有标签) for node in $nodes_with_nodejs; do kubectl label nodes $node has-nodejs-app=true --overwrite done # 移除没有NodeJS的节点的标签 for node in $nodes_with_label; do if ! echo "$nodes_with_nodejs" | grep -q "$node"; then kubectl label nodes $node has-nodejs-app- fi done
步骤3:用CronJob定期运行脚本
把这个脚本做成Kubernetes CronJob,让它定期执行(比如每分钟一次,根据你的业务调整):
apiVersion: batch/v1 kind: CronJob metadata: name: node-label-updater namespace: X spec: schedule: "* * * * *" # 每分钟执行一次 jobTemplate: spec: template: spec: serviceAccountName: node-labeler-sa # 需要给这个SA分配节点标签权限 containers: - name: label-updater image: bitnami/kubectl:latest # 带kubectl和jq的镜像 command: ["bash", "-c"] args: - | # 把上面的脚本内容粘贴到这里 #!/bin/bash NAMESPACE="X" NODEJS_LABEL="app=nodejs" TARGET_LABEL="has-nodejs-app=true" nodes_with_nodejs=$(kubectl get pods -n $NAMESPACE -l $NODEJS_LABEL -o json | jq -r '.items[].spec.nodeName' | sort | uniq) nodes_with_label=$(kubectl get nodes -l has-nodejs-app=true -o json | jq -r '.items[].metadata.name') for node in $nodes_with_nodejs; do kubectl label nodes $node has-nodejs-app=true --overwrite done for node in $nodes_with_label; do if ! echo "$nodes_with_nodejs" | grep -q "$node"; then kubectl label nodes $node has-nodejs-app- fi done restartPolicy: OnFailure
步骤4:部署MySQL Router的DaemonSet
创建DaemonSet,指定只在带有has-nodejs-app=true标签的节点上运行:
apiVersion: apps/v1 kind: DaemonSet metadata: name: mysql-router namespace: X spec: selector: matchLabels: app: mysql-router template: metadata: labels: app: mysql-router spec: nodeSelector: has-nodejs-app: "true" # 只选择有NodeJS的节点 containers: - name: mysql-router image: mysql/mysql-router:8.0 # 这里添加你的MySQL Router配置,比如环境变量、端口映射等 env: - name: MYSQL_HOST value: "mysql-group-replication-service.X.svc.cluster.local" ports: - containerPort: 6446
方案二:自定义控制器/Operator(更实时高效)
如果你的业务需要更实时的响应(比如NodeJS Pod刚部署到节点,Router立刻启动),可以写一个自定义控制器:
- 监听命名空间X里的NodeJS Pod的
CREATE、DELETE事件 - 当节点上的NodeJS Pod数量从0变为1时,创建一个MySQL Router Pod绑定到该节点
- 当节点上的NodeJS Pod数量从1变为0时,删除该节点上的MySQL Router Pod
你可以用Kubernetes Client SDK(比如Go的client-go、Python的kubernetes-client)来开发这个控制器,或者用Kubebuilder/Operator Framework快速生成脚手架。
额外注意:确保每个节点仅运行一个Router
如果不用DaemonSet(比如用Deployment),可以通过Pod反亲和性来保证同一个节点上只有一个Router:
affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - mysql-router topologyKey: kubernetes.io/hostname
这个规则会强制Kubernetes不会把第二个Router Pod调度到已经有Router的节点上。
内容的提问来源于stack exchange,提问作者Đinh Anh Huy

