Kuberrnetes 服务发布

发布于:2025-07-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、Service 的定义

1. Service 的本质

  • 抽象层:将一组 Pod 封装为虚拟服务,提供统一访问入口
  • 核心功能:负载均衡、服务发现、服务暴露
  • 访问方式:通过 DNS 域名格式的服务名称或 IP 地址访问,屏蔽后端 Pod 的动态变化

 

2. Service 的作用

  • 为客户端提供稳定的访问地址(域名 / IP)
  • 实现 4 层负载均衡(TCP/UDP)
  • 解耦客户端与后端 Pod,后端 Pod 变更不影响访问

二、Service 工作原理

1. 基本原理

  • Pod IP 动态性:Pod IP 随创建 / 销毁动态变化,无法直接访问
  • 虚拟 IP 解决方案:Service 为 Pod 创建虚拟 IP(ClusterIP),请求通过该 IP 转发到后端 Pod
  • 关键组件
    • kube-proxy:监听 Service 变化,更新节点 iptables/ipvs 规则,实现流量转发
    • CoreDNS:将 Service 虚拟 IP 注册到 DNS,支持通过服务名访问

2. 负载均衡机制:kube-proxy 代理模式

(1)userspace 模式(已废弃)

  • 原理:kube-proxy 作为 TCP/UDP 代理,流量经 iptables 转发到代理端口,再转发到后端 Pod
  • 缺点:内核态与用户态切换开销大,性能低

(2)iptables 模式(1.2 版本起默认)

  • 原理:kube-proxy 动态更新 iptables 规则,流量通过 NAT 直接路由到目标 Pod
  • 优点:无用户态切换,效率高
  • 缺点:Service 增多时规则膨胀,内核匹配效率下降(O (n) 复杂度)

(3)ipvs 模式(1.8 版本引入)

  • 原理:基于 netfilter,使用 Hash 表存储规则(O (1) 查询复杂度)
  • 优势:专门为负载均衡设计,支持更多算法

rr:轮询

lc:最小连接数

df:目标哈希

sh:源哈希

sed:预计延迟最短

nq:从不排队

  • 依赖:需操作系统启用 IPVS 内核模块(lsmod | grep ip_vs检查)

(4)kernelspace 模式(Windows 专用,暂不介绍)

三、生成用于测试service的Deployment

编写 Deployment,用于各种 service 的验证。

在应用 Service 概念之前,先创建一个提供 web 服务的 Pod 集合,有两个 Tomcat 容器副本组成,每个容器提供的服务端口都为 8080。

编辑deployment

vim webapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: kubeguide/tomcat-app:v1
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP 

selector(选择器)主要用于资源的匹配,只有符合条件的资源才会被调用或使用,可以使用该方式对集群中的各类资源进行分配。Kubernetes 和核心资源 Deployment、statefulset 管理的 Pod 是通过选择器字段决定的,通过 Service 访问这些后端 Pod 也是通过选择器字段决定的。

label(标签)可以对 Kubernetes 的其他资源进行配置,当 Kubernetes 对系统的任何 API 对象如Pod 进行分组时,会对其添加标签,用以精准的选择对应的API对象。

创建该Deployment

kubectl create -f webapp-deployment.yaml

查看pod创建情况

kubectl get pod

查看各个pod的IP地址

kubectl get pods -l app=webapp -o wide

访问这两个地址

curl 10.244.58.202:8080
curl 10.244.85.96:8080

四、service的创建

1、创建一个ClusterIP类型的service

描述:ClusterIP 是默认的 Service 类型。它将创建一个虚拟的 ClusterIP 地址,用于在集群内部访问 Service。

使用场景:适用于集群内部的服务通信,例如将前端服务和后端服务连接起来,供内部其他服务
使用。

编辑service文件

vim webapp-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080
  selector:
    app: webapp

本案例中的 ports 定义的是 Service 本身的端口号8080(用于提供给外部用户访问的端口),targetPort 则用来指定后端 Pod 的容器端口号,selector 定义的是后端 Pod 所拥有的 labe1l,

利用yaml创建此服务

kubectl create -f webapp-service.yaml

查看创建的Service,并访问测试

kubectl get svc

curl 10.108.79.50:8080

查看EndPoint列表

Service 对应的后端由 Pod 的 IP 地址何容器的端口号组成,即一个完成的 IP:Port 访问地址,这在 k8s 中叫做 EndPoint。通过査看 Service 的详细信息,可以看到其后端的 EndPoint 列表

kubectl describe svc webapp

查看EndPoint资源对象

kubectl get endpoints

删除这个service

kubectl delete -f webapp-service.yaml
或
kubectl delete service webapp

2、创建NodePort类型的service

NodePort 将在每个节点上公开一个端口,并将流量转发到 Service。它会创建一个 clusterIP,并将指定的端口映射到每个节点上的相同端口。

这种 service 适用于需要从外部访问集群中的服务时,可以通过节点的 IP 地址和映射的端口进行访问。这对于开发和测试环境非常有用。

创建service文件

设置 Service 类型为 NodePort,并设置具体的 nodePort 端口号为 30008

vim webapp-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  type: NodePort
  ports: 
  - port: 8080
    targetPort: 8080
    nodePort: 30008 
  selector:
    app: webapp

port:8080:这是 Service 的内部端口,即在 Service 内部暴露的端口。客户端请求会发送到这个端口,并由 Kubernetes 负载均衡到后端的 Pods 上。

targetPort:8088:这是指向后端 Pods 上的实际容器端口。当流量通过 Service 到达后端 Pods时,将被转发到这个端口。

nodePort:30008:这是在每个节点上打开的端口,用于外部流量进入集群。外部客户端可以通过<node-ip>:<node-port>访问该 Service。

nodePort 端口的范围在1.23.6 的版本中为 30000-32767

创建此service

继续使用刚才的 webapp 的 Deployment,为此 Deployment 创建一个 NodePort 类型的 service。

kubectl create -f webapp-svc-nodeport.yaml

查看创建的service

kubectl get svc

在windows宿主机上用浏览器访问测试

http://192.168.10.101:30008

因为 NodePort 的方式可以在每个 Node 节点创建出这个端口,所以在任何一个节点使用 netstat-anpt| grep 30008 命令查询,都可以看到此端口,在访问的时候,IP 地址可以使用任何一个主机的IP 地址。

删除该service

kubectl delete -f webapp-svc-nodeport.yaml

3、创建LoadBalancer类型的service

通常在公有云的环境中会使用 LoadBalancer 的类型,可以将 Service 映射到公有云提供的某个负载均衡器的 IP 地址上,客户端通过负载均衡器的 IP 和 Service 的端口号就可以访问到具体的服务。

描述:LoadBalancer 为 Service 创建一个外部负载均衡器,并分配一个外部 IP 地址。它通常由云提供商的负载均衡服务实现。

使用场景:适用于需要将流量从外部负载均衡器分发到集群内部的服务,例如在生产环境中暴露 web应用程序。

编写LoadBalancer类型的service文件

vim webapp-svc-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp
  namespace: default
  labels:
    app: webapp
spec:
  type: LoadBalancer
  ports:
    - port: 8080
      targetPort: http
      protocol: TCP
      name: http
      nodePort: 31771    #没有此参数,系统会自动指定一个随机端口
  selector:
    app: webapp

这个 Service 创建好后,云服务商会在 Service 定义中补充 LoadBalancer 的 IP 地址,之后就可以
在客户端访问该 Service 了。nodePort的端口范围在 30000-32767 之间。

kubectl create -f webapp-svc-loadbalancer.yaml

查看创建结果

kubectl get svc

在windows宿主机上访问

http://192.168.10.101:31771

删除此service

kubectl delete -f webapp-svc-loadbalancer.yaml

4、创建ExternalName类型的service

ExternalName 允许 Service 通过返回 CNAME 记录来引用集群外部的服务。它没有 ClusterIP,NodePort 或LoadBalancer。

适用于需要将 K8s内部的服务与集群外的现有服务进行关联,例如连接到外部的数据库或其他资源。

ExternalName 类型是 Service 的特例,用于将集群外的服务定义为 Kubernetes 的集群 Service,并通过 Externa1Name 字段指定外部服务的地址,可以使用域名或 IP 格式。集群内客户端应用通过访问这个 Service 就能访问外部服务了。他没有选择器,没有定义任何端口和 EndPoint,他通过返回该外部服务的别名来提供服务。

也可以用于两个不同的 namespace 之间的不同 pod 可以通过 name 的形式访问。

两个不同的命名空间中的 pod,可以直接使用他们的 IP 地址进行通信,但是 pod 的 IP 地址是随机分配的,重建 pod 后其 IP 地址就改变了,因此我们要用 pod 对应的 service 名称进行通信。但是跨命名空间是不能解析其他命名空间中创建的 service 名称的。这个时候就可以使用 ExternalName 实现两个不同命名空间中的各个 pod 间的通信。接下来,我们一两个命名空间内 Pod 之间的访问为例,讲解ExternalName 的使用方法。

(1)创建案例所需的两个命名空间

kubectl create namespace test01
kubectl create namespace test02

(2)第一个pod

1.创建命名空间为test01 的pod
vim myapp01.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp01
  namespace: test01
spec:
  replicas: 1
  selector: #标签选择器
    matchLabels: #匹配的标签为
      app: myapp01
      release: canary
  template:
    metadata:
      labels:
        app: myapp01 #和上面的myapp要匹配
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http01
          containerPort: 80
kubectl create -f myapp01.yaml
2.创建一个无头(headless)service
vim myapp-svc-headless01.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc01
  namespace: test01
spec:
  selector:
    app: myapp01 #挑选的pod还是myapp01。一个pod可以有多个service
    release: canary
  clusterIP: None #None表示是无头service
  ports:
  - port: 39320 #service ip中的端口
    targetPort: 80 #容器ip中的端口

(Headless Service)无头 Service:这种服务没有入口地址(没有clusterIP),kube-proxy也不会为其创建负载转发的规则,服务名的解析机制取决于这个无头 Service 是否设置了标签选择器。

无头服务创建出来之后,对于其他外部的 pod 来讲,该 pod 对应的名字就确定了下来,其固定的格式是:

无头服务名.命名空间.svc.cluster.local

kubectl create -f myapp-svc-headless01.yaml
3.创建extername
vim myapp-svc-extername01.yaml
kind: Service
apiVersion: v1
metadata:
  name: myapp-svcname02
  namespace: test01
spec:
  type: ExternalName
  externalName: myapp-svc02.test02.svc.cluster.local
  • name:myapp-svcname02 指的是外部的服务名称
  • myapp-svc02.test02.svc.cluster.local 对方无头服务的名字.对方命名空间的名字.svc.cluster.local
kubectl create -f myapp-svc-extername01.yaml
kubectl get svc -n test01

(3)创建第二个pod

1.创建命名空间为test02的pod
vim myapp02.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp02
  namespace: test02
spec:
  replicas: 1
  selector: #标签选择器
    matchLabels: #匹配的标签为
      app: myapp02
      release: canary
  template:
    metadata:
      labels:
        app: myapp02 #和上面的myapp要匹配
        release: canary
    spec:
      containers:
      - name: myapp02
        image: ikubernetes/myapp:v1
        ports:
        - name: http02
          containerPort: 80
kubectl create -f myapp022.yaml
2.创建一个无头(headless)service
vim myapp-svc-headless02.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc02
  namespace: test02
spec:
  selector:
    app: myapp02 #挑选的pod还是myapp。一个pod可以有多个service
    release: canary
  clusterIP: None #None表示是无头service
  ports:
  - port: 39320 #service ip中的端口
    targetPort: 80 #容器ip中的端口
kubectl create -f myapp-svc-headless02.yaml
3.创建extername
vim myapp-svc-extername02.yaml
kind: Service
apiVersion: v1
metadata:
  name: myapp-svcname01
  namespace: test02
spec:
  type: ExternalName
  externalName: myapp-svc01.test01.svc.cluster.local

name:myapp-svcname01 指的是外部的服务名称

kubectl create -f myapp-svc-extername02.yaml

(4)验证pod间的通信

查看命名空间为test01 的pod

##查看test01容器的ip地址
[root@k8s-master opt]# ku get pods -n test01 -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
myapp01-696c886d6b-bbvrn   1/1     Running   0          30m   10.244.36.80   k8s-node1   <none>           <none>
 
 
##登陆到test02命名空间的容器,访问test01容器的ip
[root@k8s-master opt]# ku exec -it myapp02-55ffcd5f64-xmgq8 -n test02 -- sh
/ # ping 10.244.36.80
PING 10.244.36.80 (10.244.36.80): 56 data bytes
64 bytes from 10.244.36.80: seq=0 ttl=63 time=0.059 ms
64 bytes from 10.244.36.80: seq=1 ttl=63 time=0.121 ms