Skip to content

微服务Microservices

历史与背景

从2016 年和 2017 开始,微服务技术迅猛普及,Spring Cloud 为代表的传统侵入式开发框架,占据着微服务市场的主流地位,在golang领域,微服务框架也蓬勃发展,产生了众多成熟的微服务框架,包括go-zero, go-kratos,go-micro,go-kit

描述

  • 微服务(Microservices)是一种架构风格

  • 一个大型复杂软件应用由一个或多个微服务组成

  • 系统中的各个微服务可被独立部署,各个微服务之间是松耦合的

  • 每个微服务仅关注于完成一件任务并很好地完成该任务

  • 在所有情况下,每个任务代表着一个小的业务能力

组件与组成概念

  • 服务注册:服务提供方将自己调用地址注册到服务注册中心(采用consul, etcd等),让服务调用方能够方便地找到自己
  • 服务发现:服务调用方从服务注册中心找到自己需要调用的服务的地址
  • 负载均衡:服务提供方一般以多实例的形式提供服务,负载均衡功能能够让服务调用方连接到合适的服务节点
  • 服务网关:服务网关是服务调用的唯一入口,可以在这个组件是实现用户鉴权、动态路由、灰度发布、A/B 测试、负载限流等功能
  • 配置中心:将本地化的配置信息(properties, xml, yaml 等)注册到配置中心,实现程序包在开发、测试、生产环境的无差别性,方便程序包的迁移
  • API 管理:以方便的形式编写及更新 API 文档,并以方便的形式供调用者查看和测试
  • 集成框架:微服务组件都以职责单一的程序包对外提供服务,集成框架以配置的形式将所有微服务组件(特别是管理端组件)集成到统一的界面框架下,让用户能够在统一的界面中使用系统
  • 分布式事务:对于重要的业务,需要通过分布式事务技术(TCC、高可用消息服务、最大努力通知)保证数据的一致性
  • 调用链:记录完成一个业务逻辑时调用到的微服务,并将这种串行或并行的调用关系展示出来
  • 支撑平台:系统微服务化后,系统变得更加碎片化,系统的部署、运维、监控等都比单体架构更加复杂,那么,就需要将大部分的工作自动化

优点

  • 降低系统复杂度:每个服务都比较简单,只关注于一个业务功能
  • 松耦合:微服务架构方式是松耦合的,每个微服务可由不同团队独立开发,互不影响
  • 跨语言:只要符合服务 API 契约,开发人员可以自由选择开发技术,这就意味着开发人员可以采用新技术编写或重构服务,由于服务相对较小,所以这并不会对整体应用造成太大影响
  • 独立部署:微服务架构可以使每个微服务独立部署。开发人员无需协调对服务升级或更改的部署。这些更改可以在测试通过后立即部署。所以微服务架构也使得 CI/CD 成为可能

缺点

  • 缺乏统一的标准:业务逻辑应该按照什么规则划分为微服务,这本身就是一个经验工程,微服务的目标是充分分解应用程序,以促进敏捷开发和持续集成部署
  • 微服务的分布式特点带来的复杂性:开发人员需要基于 RPC 或者消息实现微服务之间的调用和通信,而这就使得服务之间的发现、服务调用链的跟踪和质量问题变得的相当棘手
  • 分区的数据库体系和分布式事务:更新多个业务实体的业务交易相当普遍,不同服务可能拥有不同的数据库,CAP 原理的约束,使得我们不得不放弃传统的强一致性,而转而追求最终一致性,这个对开发人员来说是一个挑战
  • 测试挑战:传统的单体WEB应用只需测试单一的 REST API 即可,而对微服务进行测试,需要启动它依赖的所有其他服务,这种复杂性不可低估
  • 跨多个服务的更改:比如在传统单体应用中,若有 A、B、C 三个服务需要更改,A 依赖 BB 依赖 C,我们只需更改相应的模块,然后一次性部署即可,但是在微服务架构中,我们需要仔细规划和协调每个服务的变更部署。我们需要先更新 C,然后更新 B,最后更新 A
  • 部署复杂:微服务由不同的大量服务构成,每种服务可能拥有自己的配置、应用实例数量以及基础服务地址,这里就需要不同的配置、部署、扩展和监控组件,此外,我们还需要服务发现机制,以便服务可以发现与其通信的其他服务的地址,因此,成功部署微服务应用需要开发人员有更好地部署策略和高度自动化的水平

服务网格Service Mesh

背景

2017 年底,非侵入式的 Service Mesh 技术从萌芽到走向了成熟

服务网格Service Mesh是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求

在实践中,Service Mesh 通常以轻量级网络代理阵列的形式实现,这些代理与应用程序代码部署在一起,对应用程序来说无需感知代理的存在

Istio是第二代Service Mesh的明星项目,由Google、IBM、Lyft 联合发布

描述

是微服务之间位于应用层下面的一层,负责微服务之间的注册发现,网络调用,限流,熔断等

特点

  • 应用程序间通讯的中间层
  • 轻量级网络代理
  • 应用程序无感知
  • 解耦应用程序的重试/超时、监控、追踪和服务发现

特性

  • 流量管理:控制服务间的流量和API调用流,使调用更可靠,增强不同环境下的网络鲁棒性
  • 可观测性:了解服务之间的依赖关系和它们之间的性质和流量,提供快速识别定位问题的能力
  • 策略实施:通过配置mesh而不是以改变代码的方式来控制服务之间的访问策略
  • 服务识别和安全:提供在mesh里的服务可识别性和安全性保护

Istio架构

分为控制平面和数据平面

  • 数据平面:由一组智能代理Envoysidecar模式部署,协调和控制所有服务之间的网络通信

    • 通常是按照无状态目标设计的,但实际上为了提高流量转发性能,需要缓存一些数据,因此无状态也是有争议的
    • 直接处理入站和出站数据包,转发、路由、健康检查、负载均衡、认证、鉴权、产生监控数据等
    • 对应用来说透明,即可以做到无感知部署
  • 控制平面:负责管理和配置代理路由流量,以及在运行时执行的政策

    • 不直接解析数据包
    • 与控制平面中的代理通信,下发策略和配置
    • 负责网络行为的可视化
    • 通常提供 API 或者命令行工具可用于配置版本化管理,便于持续集成和部署

    istio-mesh-arch

Envoy

sidecar模式: 将应用程序的功能划分为单独的进程运行在同一个最小调度单元中(例如k8s中的 Pod)

Istio 使用 Envoy 代理的扩展版本,该代理是以 C++ 开发的高性能代理,用于调解服务网格中所有服务的所有入站和出站流量

Envoy 代理被部署为服务的 sidecar(即架构中Service旁边的Proxy),在逻辑上为服务增加了 Envoy 的许多内置特性,例如

  • 动态服务发现
  • 负载均衡
  • TLS 终端
  • HTTP/2 gRPC 代理
  • 熔断器
  • 健康检查
  • 基于百分比流量分割的分阶段发布
  • 故障注入
  • 丰富的指标

sidecar模式部署,这允许 Istio 将大量关于流量行为的信号作为属性提取出来,并发送给监控系统以提供有关整个服务网格的行为信息,Sidecar代理模型还允许你将 Istio功能添加到现有部署中,无需重新构建或重写代码

Pilot

PilotEnvoy sidecar提供服务发现、用于智能路由的流量管理功能(例如,A/B 测试、金丝雀发布等)以及弹性功能(超时、重试、熔断器等),将控制流量行为的高级路由规则转换为特定于环境的配置

Citadel

通过内置的身份和证书管理,可以支持强大的服务间以及最终用户的身份验证,可以使用 Citadel 来升级服务网格中的未加密流量,使用 Citadeloperator可以执行基于服务身份的策略,而不是相对不稳定的 3 层或 4 层网络标识

Galley

配置验证、提取、处理和分发组件,负责将其余的 Istio 组件与从底层平台(例如 Kubernetes)获取用户配置的细节隔离开来

Istio Service Mesh的实践

首先,需要有一套k8s系统,可以采用rke2或者k3s搭建

下载安装Istio(此处需要考虑国内网络访问问题)

$ curl -L https://istio.io/downloadIstio | sh -
$ curl -L https://istio.io/downloadIstio | sh -

转到 Istio 包目录,例如,如果包是 istio-1.19.3

$ cd istio-1.19.3
$ cd istio-1.19.3

安装目录包含

  • samples/ 目录下的示例应用程序
  • bin/ 目录下的 istioctl 客户端二进制文件

istioctl 客户端添加到路径(LinuxmacOS

$ export PATH=$PWD/bin:$PATH
$ export PATH=$PWD/bin:$PATH

对于本次安装,我们采用 demo 配置组合。 选择它是因为它包含了一组专为测试准备的功能集合,另外还有用于生产或性能测试的配置组合。

$ istioctl install --set profile=demo -y
$ istioctl install --set profile=demo -y

给命名空间添加标签,指示 Istio 在部署应用的时候,自动注入 Envoy 边车代理

$ kubectl label namespace default istio-injection=enabled
namespace/default labeled
$ kubectl label namespace default istio-injection=enabled
namespace/default labeled

运行istio自带的服务,快速验证集群配置与网络是否正常,如果后面一条命令输出<title>标签,则表示集群正常

$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"

<title>Simple Bookstore App</title>
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"

<title>Simple Bookstore App</title>

使用gin开发微服务

环境说明

实验设备IP: 172.16.67.147

实验系统: Ubuntu22.04 amd64

k8s可以默认使用containerd作为容器运行时,这个和使用docker不冲突,docker也是使用containerd作为容器运行时的

k8s仍然可以直接拉取docker.io的镜像

使用本地Dockerfile构建镜像,推送到dockerhub(或者企业自己搭建的harbor仓库)之后,可以直接被k8s拉取并使用

gin代码

此处使用gin开发一个web服务,并构建镜像,并部署到k8s

创建一个文件夹gin-demo,并创建一个gin-demo/main.go文件写入如下,该服务会监听8080端口,并返回打印当前时间和本机hostname的返回

go
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"os/exec"
)

func main() {
	r := gin.New()
	r.Use(gin.Recovery())
	r.GET("/ping", func(c *gin.Context) {
		hostname, _ := exec.Command("hostname").CombinedOutput()
		date, _ := exec.Command("date").CombinedOutput()
		c.JSON(200, gin.H{
			"message": fmt.Sprintf("%s pong at %s", string(date), string(hostname)),
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"os/exec"
)

func main() {
	r := gin.New()
	r.Use(gin.Recovery())
	r.GET("/ping", func(c *gin.Context) {
		hostname, _ := exec.Command("hostname").CombinedOutput()
		date, _ := exec.Command("date").CombinedOutput()
		c.JSON(200, gin.H{
			"message": fmt.Sprintf("%s pong at %s", string(date), string(hostname)),
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

更新mod依赖

shell
$ cd gin-demo
$ go mod init
$ go mod tidy
$ cd gin-demo
$ go mod init
$ go mod tidy

阶段式构建Dockerfile

go
FROM golang:alpine as builder

WORKDIR /work

ADD go.mod .
ADD go.sum .
RUN go env -w GOPROXY=https://goproxy.cn,direct && go mod download

ADD main.go .
RUN go build -o gin-demo .

FROM alpine
WORKDIR /work
COPY --from=builder /work/gin-demo .
RUN chmod +x gin-demo
ENTRYPOINT ["./gin-demo"]
FROM golang:alpine as builder

WORKDIR /work

ADD go.mod .
ADD go.sum .
RUN go env -w GOPROXY=https://goproxy.cn,direct && go mod download

ADD main.go .
RUN go build -o gin-demo .

FROM alpine
WORKDIR /work
COPY --from=builder /work/gin-demo .
RUN chmod +x gin-demo
ENTRYPOINT ["./gin-demo"]

开始构建镜像gin-demo

下面采用的dockerhub账号是2976560783,所以构建的镜像也需要包含该信息

shell
$ docker build -t 2976560783/gin-demo .
$ docker build -t 2976560783/gin-demo .

登录dockerhub并推送该镜像

shell
$ docker login
$ docker push 2976560783/gin-demo
$ docker login
$ docker push 2976560783/gin-demo

gin web服务

构建k8sDeployment资源yaml配置文件gin-demo.yaml

需要注意修改下面image: 参数的值,配置3实例

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gin-demo
  labels:
    app: gin-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: gin-demo
  template:
    metadata:
      labels:
        app: gin-demo
    spec:
      containers:
        - name: gin-demo
          image: 2976560783/gin-demo:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gin-demo
  labels:
    app: gin-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: gin-demo
  template:
    metadata:
      labels:
        app: gin-demo
    spec:
      containers:
        - name: gin-demo
          image: 2976560783/gin-demo:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP

应用该配置文件

shell
$ kubectl apply -f gin-demo.yaml
$ kubectl apply -f gin-demo.yaml

查看部署状态

查看到READY栏下面是2/2,这个就是因为我们安装了istio的原因,istio会在每个服务pod中安装一个Proxy

shell
$ kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS       AGE   IP           NODE               NOMINATED NODE   READINESS GATES
gin-demo-d4659f67c-4vgjc   2/2     Running   2 (5m4s ago)   15h   10.42.0.36   demo-server-node   <none>           <none>
gin-demo-d4659f67c-7zcn6   2/2     Running   2 (5m4s ago)   15h   10.42.0.40   demo-server-node   <none>           <none>
gin-demo-d4659f67c-j8xvn   2/2     Running   2 (5m4s ago)   15h   10.42.0.41   demo-server-node   <none>           <none>
$ kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS       AGE   IP           NODE               NOMINATED NODE   READINESS GATES
gin-demo-d4659f67c-4vgjc   2/2     Running   2 (5m4s ago)   15h   10.42.0.36   demo-server-node   <none>           <none>
gin-demo-d4659f67c-7zcn6   2/2     Running   2 (5m4s ago)   15h   10.42.0.40   demo-server-node   <none>           <none>
gin-demo-d4659f67c-j8xvn   2/2     Running   2 (5m4s ago)   15h   10.42.0.41   demo-server-node   <none>           <none>

可以看到部署的pod会产生几个IP,是由虚拟网卡产生的设备内部的NAT IP地址

测试k8s设备机器访问服务(从其他设备是无法远程访问到10.42.0.24的)

$ curl http://10.42.0.36:8080/ping
{"message":"Fri Oct 20 01:24:56 UTC 2023\n pong at gin-demo-d4659f67c-4vgjc\n"}
$ curl http://10.42.0.36:8080/ping
{"message":"Fri Oct 20 01:24:56 UTC 2023\n pong at gin-demo-d4659f67c-4vgjc\n"}

但是我们目前的服务是不具备负载均衡的,同时我们的Pod也可能随时会被k8s关闭重启

Service负载均衡

创建一个gin-demo-service.yaml

yaml
apiVersion: v1
kind: Service
metadata:
  name: gin-demo
  labels:
    app: gin-demo
spec:
  ports:
    - port: 8080
      name: http
      protocol: TCP
      targetPort: 8080
  selector:
    app: gin-demo
apiVersion: v1
kind: Service
metadata:
  name: gin-demo
  labels:
    app: gin-demo
spec:
  ports:
    - port: 8080
      name: http
      protocol: TCP
      targetPort: 8080
  selector:
    app: gin-demo

应用kubectl apply -f gin-demo-service.yaml

Service会依据spec.selector:参数定义的匹配规则,把流量定向到Deployment资源的metadata.labels参数相同的资源上

$ kubectl get service -o wide
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
gin-demo     ClusterIP   10.43.45.69   <none>        8080/TCP   15h
kubernetes   ClusterIP   10.43.0.1     <none>        443/TCP    22h
$ kubectl get service -o wide
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
gin-demo     ClusterIP   10.43.45.69   <none>        8080/TCP   15h
kubernetes   ClusterIP   10.43.0.1     <none>        443/TCP    22h

现在可以多次执行如下命令,会发现获取的返回值会随机来源于某个Pod gin-demoip的值与gin demo service的值保持一致

$ curl http://10.43.45.69:8080/ping
$ curl http://10.43.45.69:8080/ping

现在进入到任意一个节点的pod内部执行命令

$ kubectl exec -it gin-demo-d4659f67c-4vgjc -- sh
$ kubectl exec -it gin-demo-d4659f67c-4vgjc -- sh

访问域名gin-demo,输出如下,运行多次,发现域名被映射为服务gin-demo,访问多次可以看到负载均衡的效果

$ wget http://gin-demo:8080/ping -O - |cat

Connecting to gin-demo:8080 (10.43.50.108:8080)
writing to stdout
-                    100% |**************************************************************************************************************************************|    80  0:00:00 ETA
written to stdout
{"message":"Wed Oct 18 09:46:42 UTC 2023\n pong at gin-demo-69d5b4d649-j8xvn\n"}
$ wget http://gin-demo:8080/ping -O - |cat

Connecting to gin-demo:8080 (10.43.50.108:8080)
writing to stdout
-                    100% |**************************************************************************************************************************************|    80  0:00:00 ETA
written to stdout
{"message":"Wed Oct 18 09:46:42 UTC 2023\n pong at gin-demo-69d5b4d649-j8xvn\n"}

现在确定可以从设备内部网络访问service可以实现动态的负载均衡

Ingress外部设备访问

目前servicepod都不能被外部网络设备访问,所以就需要配置一个ingress

整体架构

istio-k8s-arch

创建一个gin-demo-ingress.yaml

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
    - http:
        paths:
          - path: /api/v1(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: gin-demo
                port:
                  name: http
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
    - http:
        paths:
          - path: /api/v1(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: gin-demo
                port:
                  name: http

应用ingress

$ kubectl apply -f gin-demo-ingress.yaml
$ kubectl apply -f gin-demo-ingress.yaml

查看ingress的状态

$ kubectl get ingresses
NAME      CLASS    HOSTS   ADDRESS         PORTS   AGE
gateway   <none>   *       172.16.67.147   80      15h
$ kubectl get ingresses
NAME      CLASS    HOSTS   ADDRESS         PORTS   AGE
gateway   <none>   *       172.16.67.147   80      15h
$ kubectl describe ingresses
Name:             gateway
Labels:           <none>
Namespace:        default
Address:          172.16.67.147
Ingress Class:    <none>
Default backend:  <default>
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /api/v1(/|$)(.*)   gin-demo:http (10.42.0.36:8080,10.42.0.40:8080,10.42.0.41:8080)
Annotations:  kubernetes.io/ingress.class: nginx
              nginx.ingress.kubernetes.io/rewrite-target: /$2
Events:
  Type    Reason  Age                From                      Message
  ----    ------  ----               ----                      -------
  Normal  Sync    15h (x2 over 15h)  nginx-ingress-controller  Scheduled for sync
  Normal  Sync    19m                nginx-ingress-controller  Scheduled for sync
$ kubectl describe ingresses
Name:             gateway
Labels:           <none>
Namespace:        default
Address:          172.16.67.147
Ingress Class:    <none>
Default backend:  <default>
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /api/v1(/|$)(.*)   gin-demo:http (10.42.0.36:8080,10.42.0.40:8080,10.42.0.41:8080)
Annotations:  kubernetes.io/ingress.class: nginx
              nginx.ingress.kubernetes.io/rewrite-target: /$2
Events:
  Type    Reason  Age                From                      Message
  ----    ------  ----               ----                      -------
  Normal  Sync    15h (x2 over 15h)  nginx-ingress-controller  Scheduled for sync
  Normal  Sync    19m                nginx-ingress-controller  Scheduled for sync

表明在访问http路径有前缀/api/v1的时候,请求会被转发到gin-demoservice,之后再被负载转发到deployment

接下去从另外一台设备进行访问,多次访问的结果pong at后面的数据是会变化的

$ curl http://172.16.67.147/api/v1/ping
{"message":"Fri Oct 20 01:42:22 UTC 2023\n pong at gin-demo-d4659f67c-4vgjc\n"}
$ curl http://172.16.67.147/api/v1/ping
{"message":"Fri Oct 20 01:42:22 UTC 2023\n pong at gin-demo-d4659f67c-4vgjc\n"}

Service NodeType类型外部访问

有些时候我们不希望使用ingress,比如想换一个外部网络访问端口

创建一个gin-demo-nodeport.yaml

yaml
apiVersion: v1
kind: Service
metadata:
  name: gin-demo-node-port
  labels:
    app: gin-demo
spec:
  type: NodePort
  ports:
    - port: 8080
      name: http
      protocol: TCP
      targetPort: 8080
      nodePort: 31001
  selector:
    app: gin-demo
apiVersion: v1
kind: Service
metadata:
  name: gin-demo-node-port
  labels:
    app: gin-demo
spec:
  type: NodePort
  ports:
    - port: 8080
      name: http
      protocol: TCP
      targetPort: 8080
      nodePort: 31001
  selector:
    app: gin-demo

上述配置文件指定type: NodePortspec.ports[0].nodePort

其中nodePort可选值是30000-32767,上面的配置参数表示在宿主机上开放一个31001端口,可以被远程设备访问

应用该配置文件

$ kubectl apply -f gin-demo-nodeport.yaml
$ kubectl apply -f gin-demo-nodeport.yaml

查看service状态

$ kubectl describe svc gin
Name:              gin-demo
Namespace:         default
Labels:            app=gin-demo
Annotations:       <none>
Selector:          app=gin-demo
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.43.86.114
IPs:               10.43.86.114
Port:              http  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.42.0.36:8080,10.42.0.40:8080,10.42.0.41:8080
Session Affinity:  None
Events:            <none>

Name:                     gin-demo-node-port
Namespace:                default
Labels:                   app=gin-demo
Annotations:              <none>
Selector:                 app=gin-demo
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.43.4.171
IPs:                      10.43.4.171
Port:                     http  8080/TCP
TargetPort:               8080/TCP
NodePort:                 http  31001/TCP
Endpoints:                10.42.0.36:8080,10.42.0.40:8080,10.42.0.41:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
$ kubectl describe svc gin
Name:              gin-demo
Namespace:         default
Labels:            app=gin-demo
Annotations:       <none>
Selector:          app=gin-demo
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.43.86.114
IPs:               10.43.86.114
Port:              http  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.42.0.36:8080,10.42.0.40:8080,10.42.0.41:8080
Session Affinity:  None
Events:            <none>

Name:                     gin-demo-node-port
Namespace:                default
Labels:                   app=gin-demo
Annotations:              <none>
Selector:                 app=gin-demo
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.43.4.171
IPs:                      10.43.4.171
Port:                     http  8080/TCP
TargetPort:               8080/TCP
NodePort:                 http  31001/TCP
Endpoints:                10.42.0.36:8080,10.42.0.40:8080,10.42.0.41:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

可以看到,两个不同的serviceEndpoints是一样的,gin-demo-node-port还可以远程访问

在另外一台设备访问

$ curl http://172.16.67.147:31001/ping 
{"message":"Fri Oct 20 03:22:53 UTC 2023\n pong at gin-demo-d4659f67c-4vgjc\n"}
$ curl http://172.16.67.147:31001/ping 
{"message":"Fri Oct 20 03:22:53 UTC 2023\n pong at gin-demo-d4659f67c-4vgjc\n"}

参考阅读

服务(Microservices)和服务网格(Service Mesh)架构概念整理

jimmysong Istio概述

k8s ingress官方文档

k8s service官方文档

Last updated:

Released under the MIT License.