Service 是 某种抽象封装 的 Pod ,因此Service也有其IP

Service 也是 Pod ,因此他们只能在k8s集群内部被访问到,如果想要从外部访问

  1. 用 Ingress
  2. 用 NodePort

服务发现:在微服务架构中,服务可能会动态地注册和注销,服务的端口可能会变化。服务发现机制允许服务实例在启动时注册自己的endpoint信息,包括IP和端口,客户端通过服务发现机制来获取最新的服务endpoint信息。

原理#

看下面解析配合上面的图片

  • 各个部分

    • Service

      每个Service都有其IP

      1. 创建 Service 时,会创建一个服务名即 nginx-svc , 同时他会绑定一个端口号 :80:32057

      :80 是pod暴露出来的端口号,:32057 是Service监听的端口号

      1. 当使用 NodePort 类型的 Service 时,外部流量通过 Node IP(也就是192.168.113.120) 和指定的端口(NodePort:32057)访问服务。这允许从集群外部访问集群内的服务。

      2. Service 通过选择器(selector)与一组 Pod 相关联,它将流量路由到这些 Pod,即

        selector: app=nginx-deploy

      3. Service 实际上是连接这整个集群的所有Node的,因此但我访问192.168.113.120:32057

        192.168.113.121:32057192.168.113.122:32057 效果是一样的

    • Endpoint

      1. 这个控制器是在Service被创建的时候自动会被创建,主要管理的是对 PodIP 和 ServiceIP 的映射,

      address:10.244.36.107就表示对于 selector: app=nginx-deploy 这个服务对应的 PodIP 是这个address

      1. Endpoint中还会显示出Pod的targetPort:80,表示Pod暴露出来的端口是80端口
      2. Endpoint的服务名称通常与Service一致为nginx-svc
    • kube-proxy

      代理,为什么Endpoint会知道Pod的IP,就是靠这个ProxyIP来访问的

      访问任何节点的 80 端口将通过 kube-proxy 转发到后端的 Pod — 任何要访问到Pod都需要 kube-proxy的代理

    • 各个IP

      1. NodeIP : 也就是各个节点的IP,可以简单理解为主机IP
      2. PodIP : 这个是PodIP,这个Pod在创建销毁过程中会不断变化
      3. ServiceIP: 这个是Service的IP,通过访问ServiceIP来转化到Pod的IP
      4. ClusterIP: 这是一个虚拟的IP地址,用于在集群内部访问 Service。
    • NodePort

      上面讲的东西都是k8s内部的网络访问,但是这个NodePort是提供外部访问的,他相当于是在Node上面开了一个Port,然后我们可以通过这个NodePort来访问这个Pod

  • 访问流程

    如果 Node1 -> Node2

    也就是 Node1 -> service -> endpoint -> kube-proxy -> Node2

配置文件#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels: nginx # service 自己本身的标签
spec:
selector:
app: nginx-deploy # 匹配哪些pod会被代理
ports:
- port: 80 # service 监听的端口,在内网访问时使用
targetPort: 80 # pod 暴露的端口
name: web # 端口的名字
type: NodePort # type 随机启动一个集群上的端口,映射到ports的端口 30000~3276# 7之间,并且集群上的每一个 node 都会绑定这个端口,能够让我们直接用NodeIP去访问
# 实际生产环境中 不推荐;一般测试环境中使用,这个端口会直接绑定到Node上面,# 通过Node:port 来表示,这个相当于是吧端口暴露给外界,一般用Ingress实现 ~

如果要查看:

1
2
3
kubectl get svc  #service

kubectl get ep # endpoint

代理外部服务(可以不看,yzy说一般不干这事)#

image-20240710165210595

实现service外部服务的一个访问

即请求pod的时候能够通过service访问到外部的服务

                      Pod  ------>    Service  ------>   外部服务
  • 第一种需求:

    我们应用环境有开发环境和测试环境,每个环境有不同的IP,我想要的就是不改变Pod应用程序内部的代码结构,只改变Service中的内容从而改变选择不同的环境

  • 第二种需求:

    项目迁移到k8s时,mysql的迁移不好进行,不能直接实现迁移

    就导致我们有一部分的mysql的业务还得在k8s集权之外实现,所以需要代理外部服务

    现在就有两种访问形式

    1. 直接通过Pod来访问外部服务 (不可取)
    2. Pod->service->外部服务 这个形式来访问 (可取)

    why: 第一种形式需要在Pod内部写上mysql的Pod不方便,第二种只需要改变Service内部的mysql配置,因此第二种更方便,同时如果我有几十个mysql,你就会发现我动一发而改全身,非常麻烦。

    究其原因还是关于解耦的问题 ,核心都是通过Service这个中间人的方式灵活设置转换这些配置

实现方式:

当我编写 Service 的时候,不编写 selector , 也就不会自动创建 Endpoint ,然后我们自己创建 Endpoint

  • 首先我们进入/home/echin/services 创建相应的services-svc-external.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    apiVersion: v1
    kind: Service
    metadata:
    name: nginx-svc-external
    spec:
    type: ClusterIP # 将 type 设置在 spec 层级下
    ports:
    - port: 80 # Service 监听的端口,在内网访问时使用
    targetPort: 80 # Pod 暴露的端口
    name: web # 端口的名字
  • hhhyc@hhhyc:~/services$ sudo kubectl create -f services-svc-external.yaml
    \service/nginx-svc-external created
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    * ````sh
    hhhyc@hhhyc:~/services$ sudo kubectl get svc
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 3d16h
    nginx ClusterIP None <none> 80/TCP 19h
    nginx-svc-external ClusterIP 10.43.204.225 <none> 80/TCP 32s

    hhhyc@hhhyc:~/services$ sudo kubectl get ep
    NAME ENDPOINTS AGE
    kubernetes 172.21.133.183:6443 3d16h
    nginx <none> 19h
    可以发现我们创建了 Service 但是并没有创建 endpoint
  • 配置一下 endpoint.yaml

    vim nginx-ep-external.yaml 直接创建,配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    apiVersion: v1
    kind: Endpoints
    metadata:
    name: nginx-svc-external # 与 Service 名称对应
    subsets:
    - addresses:
    - ip: 120.78.159.117 # 这里写上我们要代理的外部服务的Ip地址,一般来说这里>时k8s内部的地址
    ports:
    - name: web
    port: 80
    protocol: TCP

    注意

    1. 这里的addresses是目标ip,就是要访问的外部服务ip
    2. 下面这个ports:name 需要和对应的 Service中的Port:name相对应
    3. metadata:name也要和 Service 中的相对应
  • 创建 endpoint

    1
    2
    hhhyc@hhhyc:~/services$ sudo kubectl create -f nginx-ep-external.yaml
    endpoints/nginx-svc-external created

    查看是否创建成功

    1
    2
    3
    4
    5
    hhhyc@hhhyc:~/services$ sudo kubectl get ep
    NAME ENDPOINTS AGE
    kubernetes 172.21.133.183:6443 3d16h
    nginx <none> 20h
    nginx-svc-external 120.78.159.117:80 13s

    查看 Service 服务发现目标ip已经修改完毕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    hhhyc@hhhyc:~/services$ sudo kubectl describe ep nginx-svc-external
    Name: nginx-svc-external
    Namespace: default
    Labels: <none>
    Annotations: <none>
    Subsets:
    Addresses: 120.78.159.117
    NotReadyAddresses: <none>
    Ports:
    Name Port Protocol
    ---- ---- --------
    web 80 TCP

    Events: <none>
  • 最后访问

    但是视频中教学的是有个脚本,我跳了一些,就访问不了哈哈哈

Type#

  • ClusterIP

    只能在集群内部使用,不配置类型的话默认是 ClulsterIP

  • ExternalName

    这个就是刚才的哪个访问外部服务的例子,我们给他一个域名,他就能自动跳转到那个域名

  • NodePort(不建议)

    在所有的Node上开个端口,所有节点上都可以访问端口,

    如果要用一般这个也不写死,随机一个Port,不会重复,但还是不建议访问

  • LoadBlancer(不建议)

    使用云服务商来访问Pod

感觉有必要总结一下我的Service#

  1. Service 是 Pod ,因此有IP,pod
  2. Service 是用来服务发现的,所以相当于他是个中间人,Pod要找其他的Pod就通过Service来实现
  3. Pod,service 都是在 k8s集群 内部的实现,这里的访问只有东西流量的访问,没有外部参与

这样子理解会好很多–建议多看看配置文件和敲命令行理解