记一次服务注册&服务发现的问题和解决(etcd和k8s同时结合使用情况下)

探讨Kubernetes与Etcd结合时服务注册与发现的复杂性,特别是在Pod弹性伸缩时出现的gRPC客户端调用超时问题。分析根本原因并提出解决方案,包括改进服务注册策略和利用元数据区分相同地址。

目录

 

背景

分析

原因

总结


背景

  1. 早先的都采用基于grpc+etcd做服务注册和服务发现,都是正常的
  2. 后来有部分服务采用k8s部署,为了新旧兼容,服务会按照老方式把宿主机的ip注册到etcd上,k8s体系类使用体系类的服务发现,k8s体系外的依然使用原来的方式,互不影响
  3. 但是有服务基于k8s部署之后,发现client调用接口超时,而直接通过ip调用却是正常
  4. 且多数是发生在k8s的出现pod弹性伸缩之后

分析

  1. 服务端注册地址,从etcd来看,是正常的
  2. 服务端直接通过ip调用也正常的,所以排除服务端本身的问题
  3. etcd里面显示的地址的确会出现没有的情况,但是通过日志发现服务端lease维持的心跳却是正常的,一度觉得怀疑人生~~
  4. 后来发现是下面的原因A导致的,
  5. 解决后,发现依然有问题,O(∩_∩)O,很奇怪~
  6. 通过打开grpc日志,发现grpc的client端会收到地址列表为空的问题:balancerWrapper: got update addr from Notify:[]
  7. 但是etcd里面显示的注册地址却都是存在的,一度觉得怀疑人生~~
  8. 后来通过阅读grcp源码分析,增加日志排查 :google.golang.org/grpc/balancer.go  文件中 函数 (rr *roundRobin) watchAddrUpdates()  里面,增加 etcd watch的数据变动行为,发现了原因
  9. 通过下面的原因B的处理方式解决

原因

  1. 问题存在两个
  2. 原因A
    1. 早先服务注册:类似于

      key:service/go_server_xx/10.1.1.1:9999
      val:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}

    2. 并且通过etcd的lease机制维持心跳

    3. 假设一个宿主机起了两个pod,也就两个服务,ser1和ser2
    4. ser1启动的时候会注册 service/go_server_xx/10.1.1.1:9999 并且绑定 lease1,并且维持lease1心跳
    5. 之后,ser2启动的时候也会注册 service/go_server_xx/10.1.1.1:9999 并且绑定 lease2,并且维持lease2心跳,住以此时这个key和lease1解绑了
    6. 当发生弹性伸缩的时候,比如ser2关闭了,此时 lease2过期,同时与其绑定的key也被删除,所以client会发生找不到地址列表的问题
    7. 但是从服务端ser1来看,依然提供服务,并且与etcd的lease1心跳也正常~~,哈哈
  3. 解决方法A
    1. 通过在key后边增加后缀解决,类似于:service/go_server_xx/lease1/10.1.1.1:9999, 这样即使一个宿主机上有多个服务,各自也会注册各自的
  4. 原因B
    1. 上述问题解决之后,还有个问题
    2. 比如同一个宿主机注册的两个服务地址

      key1:service/go_server_xx/lease1/10.1.1.1:9999
      val1:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}
      和
      key2:service/go_server_xx/lease2/10.1.1.1:9999
      val2:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}
      

       

    3. 在client端watch etcd变化的时候会发现两个add行为,但是 由于val中的地址是一样的,可用addrs列表里面只会保存一个 10.1.1.1:9999,另一个会跳过:
      grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr)
    4. 当发生弹性伸缩的时候,比如ser2关闭了
      1. watch etcd变化的时候会收到一个del行为,这个时候会删除10.1.1.1:9999
      2. 这样client可用地址列表里面就是空的了
      3. grpc的client对象收到 balancerWrapper: got update addr from Notify:[]
  5. 解决方法B:
    1. 注册的时候增加metadata做区分就可以了,类似

      key1:service/go_server_xx/lease1/10.1.1.1:9999
      val1:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":lease1}
      和
      key2:service/go_server_xx/lease2/10.1.1.1:9999
      val2:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":lease2}
    2. 这样增加的时候,可用地址列表里面就会增加两个元素

总结

etcd+grpc的一整套机制(服务注册&服务发现等)是好的

k8s的那一套机制也是好的

但是两者结合使用,就得注意很多地方,要不然会出现很多蛋疼的问题提

<think> 嗯,用户询问在单台服务器上使用DockerKubernetes的方法注意事项。我需要结合之前提供的引用材料Kubernetes单节点部署的特性来解答。 用户提到的引用材料很有参考价值:引用1讨论了Docker作为容器运行时的性能开销问题,引用2提到kubectl版本冲突的解决方法(特别是macOS用户),引用3是Metrics Server安装命令,引用4强调docker-compose.yml文件的重要性,引用5则是关于Java微服务的CI/CD集成。这些内容都要融入回答中。 对于单服务器场景,首先需要明确Kubernetes单节点集群的定位——它主要用于开发测试而非生产环境。常见的实现方案有两种:Minikube适合本地开发,Kubeadm的单节点配置更接近真实环境。安装过程中要特别注意容器运行时的选择,根据引用1的建议,Containerd比Docker更轻量高效。 关于注意事项,需要重点强调几点:性能方面要预留足够资源(参考引用1的链路复杂性分析);网络方案推荐Flannel这类轻量级插件;存储卷使用hostPath时要考虑数据持久性问题;日志监控必须部署(引用3的Metrics Server正好解决这点);版本管理则呼应引用2提到的冲突问题。 用户可能忽略的是本地开发流程的优化,这里可以引入引用4的观点:用docker-compose简化本地调试,保持开发环境与Kubernetes部署的一致性。最后补充安全建议,比如关闭ServiceAccount自动挂载——虽然单节点风险较低但仍需防范。 相关问题设计要覆盖延伸场景:单节点转生产集群的步骤、轻量运行时对比(呼应引用1)、开发最佳实践(结合引用4)、CI/CD集成(关联引用5)。这样既解决当前问题,又提供可拓展的知识路径。 </think> 在单台服务器上运行 Kubernetes(通常称为**单节点集群**)是可行的,特别适合开发、测试、学习或资源受限的场景。结合 Docker Kubernetes 的主要方法及关键注意事项如下: --- ### **核心方法与工具** 1. **选择单节点 Kubernetes 发行版**: * **Minikube**:专为本地开发设计,自动创建单节点集群(包含控制平面节点),内置 Docker、Containerd 等运行时支持。启动命令: ```bash minikube start --driver=docker ``` * **Kubeadm + `--control-plane-endpoint` 指向本机**: 使用 Kubeadm 初始化控制平面,并指定本机 IP 作为 endpoint,同时取消默认的污点限制,允许 Pod 调度到主节点: ```bash sudo kubeadm init --control-plane-endpoint=<本机IP> --pod-network-cidr=10.244.0.0/16 kubectl taint nodes --all node-role.kubernetes.io/control-plane- # 允许调度Pod到主节点 ``` * **MicroK8s / K3s**:轻量级 Kubernetes 发行版,针对单节点优化: * MicroK8s (Ubuntu): `sudo snap install microk8s --classic` * K3s: `curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik" sh -` 2. **容器运行时选择**: * **Docker**:传统方案,但引用[1]指出其调用链路长、性能开销较高。 * **Containerd** (推荐):Kubernetes 原生支持的轻量级运行时,直接通过 CRI 接口通信,性能更优(参考引用[1])。Minikube/Kubeadm 可直接配置使用。 3. **网络插件**:必须安装轻量 CNI 插件(如 Flannel、Calico): ```bash kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml ``` 4. **持久化存储**:使用 `hostPath` 卷或轻量 CSI 驱动(如 `local-path-provisioner`): ```bash kubectl apply -f https://github.com/rancher/local-path-provisioner/raw/v0.0.24/deploy/local-path-storage.yaml ``` --- ### **关键注意事项** 1. **资源限制**: * 单节点需承载所有组件(API Server、etcd、kubelet、Pod 等),**确保服务器内存≥4GB,CPU≥2核**。 * 使用资源配额避免 Pod 耗尽资源: ```yaml apiVersion: v1 kind: ResourceQuota metadata: name: mem-cpu-quota spec: hard: requests.cpu: "2" requests.memory: 4Gi limits.cpu: "4" limits.memory: 8Gi ``` 2. **高可用性缺失**: * **单点故障风险**:服务器宕机将导致整个集群不可用。**仅适用于非生产环境**。 * **定期备份**:使用 `etcdctl` 备份集群状态: ```bash sudo ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 snapshot save snapshot.db ``` 3. **网络与存储**: * **避免端口冲突**:NodePort 服务需映射主机端口,确保端口不冲突。 * **存储持久化**:`hostPath` 卷数据与主机绑定,需手动管理备份;避免使用分布式存储(如 Ceph)。 4. **安全加固**: * 禁用 ServiceAccount 自动挂载(非必要场景): ```yaml apiVersion: v1 kind: ServiceAccount automountServiceAccountToken: false ``` * 限制 API 访问:启用 `kube-apiserver` 的 `--authorization-mode=RBAC`。 5. **版本管理**(呼应引用[2]): * **统一 CLI 工具版本**:确保 `kubectl`、`kubeadm` 版本与集群一致,避免冲突(尤其 macOS 用户需清理 Homebrew 安装的旧版本)。 6. **监控与日志**: * 安装 Metrics Server(参考引用[3])获取资源指标: ```bash kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml ``` * 使用 `Loki` + `Promtail` 或 `Elasticsearch` + `Fluentd` 收集日志。 --- ### **开发优化建议** 1. **本地环境一致性**(参考引用[4]): * 使用 `docker-compose.yml` 模拟依赖服务(如数据库),保持开发环境与 K8s 部署一致。 * 示例片段: ```yaml services: postgres: image: postgres:14 volumes: - ./data:/var/lib/postgresql/data ``` 2. **CI/CD 集成**(参考引用[5]): * 在单节点集群中部署 Jenkins 或 GitLab Runner,通过 CI/CD 流水线构建镜像并部署应用。 * 示例步骤: ```bash kubectl create deployment my-app --image=my-registry/app:v1 kubectl expose deployment my-app --port=80 --type=NodePort ``` --- ### **总结** **适用场景**:个人开发、功能验证、CI测试环境。 **推荐工具**:Minikube(快速启动)、K3s(极致轻量)。 **关键实践**: 1. 优先使用 **Containerd** 而非 Docker 作为运行时 2. 严格配置 **资源配额** **持久化存储** 3. 统一管理 **工具版本** 避免冲突 4. 通过 **docker-compose + CI/CD** 提升开发效率 > ⚠️ 注意:生产环境必须使用多节点集群实现高可用! --- ### 相关问题 1. **单节点 Kubernetes 集群如何实现零停机升级?** 2. **在资源受限的单服务器上,K3s 与 MicroK8s 如何选择?** 3. **如何将单节点开发环境平滑迁移到生产级多节点集群?** 4. **Containerd 相比 Docker 在 Kubernetes 中的具体性能优势有哪些?**(结合引用[1]深入分析) 5. **单机环境下如何实现 Kubernetes 与 Docker Compose 的混合编排?**(参考引用[4]扩展)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值