k8s入门到实战--跨服务调用

背景

在做传统业务开发的时候,当我们的服务提供方有多个实例时,往往我们需要将对方的服务列表保存在本地,然后采用一定的算法进行调用;当服务提供方的列表变化时还得及时通知调用方。

yaml 复制代码
student:  
   url:     
   - 192.168.1.1:8081     
   - 192.168.1.2:8081

这样自然是对双方都带来不少的负担,所以后续推出的服务调用框架都会想办法解决这个问题。

spring cloud 为例:

服务提供方会向一个服务注册中心注册自己的服务(名称、IP等信息),客户端每次调用的时候会向服务注册中心获取一个节点信息,然后发起调用。

但当我们切换到 k8s 后,这些基础设施都交给了 k8s 处理了,所以 k8s 自然得有一个组件来解决服务注册和调用的问题。

也就是我们今天重点介绍的 service

service

在介绍 service 之前我先调整了源码:

go 复制代码
func main() {  
   http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {  
      name, _ := os.Hostname()  
      log.Printf("%s ping", name)  
      fmt.Fprint(w, "pong")  
   })  
   http.HandleFunc("/service", func(w http.ResponseWriter, r *http.Request) {  
      resp, err := http.Get("http://k8s-combat-service:8081/ping")  
      if err != nil {  
         log.Println(err)  
         fmt.Fprint(w, err)  
         return  
      }  
      fmt.Fprint(w, resp.Status)  
   })  
  
   http.ListenAndServe(":8081", nil)  
}

新增了一个 /service 的接口,这个接口会通过 service 的方式调用服务提供者的服务,然后重新打包。

shell 复制代码
make docker

同时也新增了一个 deployment-service.yaml:

yaml 复制代码
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:  
    app: k8s-combat-service # 通过标签选择关联  
  name: k8s-combat-service  
spec:  
  replicas: 1  
  selector:  
    matchLabels:  
      app: k8s-combat-service  
  template:  
    metadata:  
      labels:  
        app: k8s-combat-service  
    spec:  
      containers:  
        - name: k8s-combat-service  
          image: crossoverjie/k8s-combat:v1  
          imagePullPolicy: Always  
          resources:  
            limits:  
              cpu: "1"  
              memory: 100Mi  
            requests:  
              cpu: "0.1"  
              memory: 10Mi  
---  
apiVersion: v1  
kind: Service  
metadata:  
  name: k8s-combat-service  
spec:  
  selector:  
    app: k8s-combat-service # 通过标签选择关联  
  type: ClusterIP  
  ports:  
    - port: 8081        # 本 Service 的端口  
      targetPort: 8081  # 容器端口  
      name: app

使用相同的镜像部署一个新的 deployment,名称为 k8s-combat-service,重点是新增了一个kind: Service 的对象。

这个就是用于声明 service 的组件,在这个组件中也是使用 selector 标签和 deployment 进行了关联。

也就是说这个 service 用于服务于名称等于 k8s-combat-servicedeployment

下面的两个端口也很好理解,一个是代理的端口, 另一个是 service 自身提供出去的端口。

至于 type: ClusterIP 是用于声明不同类型的 service,除此之外的类型还有:

  • NodePort
  • LoadBalancer
  • ExternalName 等类型,默认是 ClusterIP,现在不用纠结这几种类型的作用,后续我们在讲到 Ingress 的时候会具体介绍。

负载测试

我们先分别将这两个 deployment 部署好:

shell 复制代码
k apply -f deployment/deployment.yaml
k apply -f deployment/deployment-service.yaml

❯ k get pod
NAME                                  READY   STATUS    RESTARTS   AGE
k8s-combat-7867bfb596-67p5m           1/1     Running   0          3h22m
k8s-combat-service-5b77f59bf7-zpqwt   1/1     Running   0          3h22m

由于我新增了一个 /service 的接口,用于在 k8s-combat 中通过 service 调用 k8s-combat-service 的接口。

go 复制代码
resp, err := http.Get("http://k8s-combat-service:8081/ping")

其中 k8s-combat-service 服务的域名就是他的服务名称。

如果是跨 namespace 调用时,需要指定一个完整名称,在后续的章节会演示。

我们整个的调用流程如下:

相信大家也看得出来相对于 spring cloud 这类微服务框架提供的客户端负载方式,service 是一种服务端负载,有点类似于 Nginx 的反向代理。

为了更直观的验证这个流程,此时我将 k8s-combat-service 的副本数增加到 2:

yaml 复制代码
spec:  
  replicas: 2

只需要再次执行:

shell 复制代码
❯ k apply -f deployment/deployment-service.yaml
deployment.apps/k8s-combat-service configured
service/k8s-combat-service unchanged

不管我们对 deployment 的做了什么变更,都只需要 apply 这个 yaml 文件即可, k8s 会自动将当前的 deployment 调整为我们预期的状态(比如这里的副本数量增加为 2);这也就是 k8s 中常说的声明式 API

可以看到此时 k8s-combat-service 的副本数已经变为两个了。 如果我们此时查看这个 service 的描述时:

shell 复制代码
❯ k describe svc k8s-combat-service |grep Endpoints
Endpoints:         192.168.130.133:8081,192.168.130.29:8081

会发现它已经代理了这两个 Pod 的 IP。

此时我进入了 k8s-combat-7867bfb596-67p5m 的容器:

shell 复制代码
k exec -it k8s-combat-7867bfb596-67p5m bash
curl http://127.0.0.1:8081/service

并执行两次 /service 接口,发现请求会轮训进入 k8s-combat-service 的代理的 IP 中。

由于 k8s service 是基于 TCP/UDP 的四层负载,所以在 http1.1 中是可以做到请求级的负载均衡,但如果是类似于 gRPC 这类长链接就无法做到请求级的负载均衡。

换句话说 service 只支持连接级别的负载。

如果要支持 gRPC,就得使用 Istio 这类服务网格,相关内容会在后续章节详解。

总结

总的来说 k8s service 提供了简易的服务注册发现和负载均衡功能,当我们只提供 http 服务时是完全够用的。

相关的源码和 yaml 资源文件都存在这里: github.com/crossoverJi...

相关推荐
想打游戏的程序猿4 小时前
核心概念层——深入理解 Agent 是什么
后端·ai编程
woniu_maggie4 小时前
SAP Web Service日志监控:如何用SRT_UTIL快速定位接口问题
后端
一线大码4 小时前
Java 使用国密算法实现数据加密传输
java·spring boot·后端
Rust语言中文社区5 小时前
【Rust日报】用 Rust 重写的 Turso 是一个更好的 SQLite 吗?
开发语言·数据库·后端·rust·sqlite
在屏幕前出油6 小时前
06. FastAPI——中间件
后端·python·中间件·pycharm·fastapi
wuqingshun3141596 小时前
说一下spring的bean的作用域
java·后端·spring
.柒宇.7 小时前
基于 RHEL 9.7 搭建 Kubernetes v1.34 集群实战:Docker 运行时 (cri-dockerd) 与国内源配置详解
docker·云原生·容器·kubernetes·kubelet
钟智强7 小时前
从2.7GB到481MB:我的Docker Compose优化实战,以及为什么不能全信AI
后端·docker
华科易迅7 小时前
Spring JDBC
java·后端·spring
小村儿8 小时前
一起吃透 Claude Code,告别 AI 编程迷茫
前端·后端·ai编程