K8S(十一)—Service详解

Service

官网地址:https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/

Kubernetes 中 Service 是 将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。

Kubernetes 中 Service 的一个关键目标是让你无需修改现有应用程序就能使用不熟悉的服务发现机制。 你可以在 Pod 中运行代码,无需顾虑这是为云原生世界设计的代码,还是为已容器化的老应用程序设计的代码。 你可以使用 Service 让一组 Pod 在网络上可用,让客户端能够与其交互。

如果你使用 Deployment 来运行你的应用, Deployment 可以动态地创建和销毁 Pod。不管是这一刻还是下一刻, 你不知道有多少个这样的 Pod 正在工作以及健康与否;你可能甚至不知道那些健康的 Pod 是如何命名的。 Kubernetes Pod 被创建和销毁以匹配集群的预期状态。 Pod 是临时资源(你不应该期待单个 Pod 既可靠又耐用)。

每个 Pod 获取其自己的 IP 地址(Kubernetes 期待网络插件确保 IP 地址分配)。 对于集群中给定的 Deployment,这一刻运行的这组 Pod 可能不同于下一刻运行应用程序的那组 Pod。

这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用提供工作负载的后端部分?

发布服务(服务类型)

对一些应用的某些部分(如前端),可能希望将其暴露给 Kubernetes 集群外部的 IP 地址。

Kubernetes ServiceTypes 允许指定你所需要的 Service 类型。

可用的 type 值及其行为有:

  • ClusterIP

    通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是你没有为服务显式指定 type 时使用的默认值。 你可以使用 Ingress 或者 Gateway API 向公众暴露服务。

  • NodePort

    通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 为了让节点端口可用,Kubernetes 设置了集群 IP 地址,这等同于你请求 type: ClusterIP 的服务。

  • LoadBalancer

    使用云提供商的负载均衡器向外部暴露服务。 Kubernetes 不直接提供负载均衡组件;你必须提供一个,或者将你的 Kubernetes 集群与云提供商集成。

  • ExternalName

    将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME 记录。 无需创建任何类型代理。

服务 API 中的 type 字段被设计为层层递进的形式 - 每个级别都建立在前一个级别基础上。 并不是所有云提供商都如此严格要求的,但 Kubernetes 的 Service API 设计要求满足这一逻辑。

type: ClusterIP

此默认服务类型从你的集群中有意预留的 IP 地址池中分配一个 IP 地址。

其他几种服务类型在 ClusterIP 类型的基础上进行构建。

如果你定义的服务将 .spec.clusterIP 设置为 "None",则 Kubernetes 不会分配 IP 地址。有关详细信息,请参阅 headless 服务

选择自己的 IP 地址

Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。

用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range CIDR 范围内, 这对 API 服务器来说是通过一个标识来指定的。 如果 IP 地址不合法,API 服务器会返回 HTTP 状态码 422,表示值不合法。

阅读避免冲突, 了解 Kubernetes 如何协助降低两种不同服务试图使用相同 IP 地址的风险和影响。

例子
[root@k8smaster service]# cat nginx.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

[root@k8smaster service]# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE     IP               NODE       NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          4m44s   10.244.185.193   k8snode2   <none>           <none>

[root@k8smaster service]#  kubectl get service
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes      ClusterIP   10.96.0.1      <none>        443/TCP   172m
nginx-service   ClusterIP   10.98.127.82   <none>        80/TCP    4m55s
[root@k8smaster service]# 

[root@k8smaster service]# curl 10.98.127.82
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

type: NodePort

如果你将 type 字段设置为 NodePort,则 Kubernetes 控制平面将在 --service-node-port-range 标志指定的范围内分配端口(默认值:30000-32767)。 每个节点将那个端口(每个节点上的相同端口号)代理到你的服务中。 你的服务在其 .spec.ports[*].nodePort 字段中报告已分配的端口。

使用 NodePort 可以让你自由设置自己的负载均衡解决方案, 配置 Kubernetes 不完全支持的环境, 甚至直接暴露一个或多个节点的 IP 地址。

对于 NodePort 服务,Kubernetes 额外分配一个端口(TCP、UDP 或 SCTP 以匹配服务的协议)。 集群中的每个节点都将自己配置为监听分配的端口并将流量转发到与该服务关联的某个就绪端点。 通过使用适当的协议(例如 TCP)和适当的端口(分配给该服务)连接到所有节点, 你将能够从集群外部使用 type: NodePort 服务。

选择你自己的端口

如果需要特定的端口号,你可以在 nodePort 字段中指定一个值。 控制平面将为你分配该端口或报告 API 事务失败。 这意味着你需要自己注意可能发生的端口冲突。 你还必须使用有效的端口号,该端口号在配置用于 NodePort 的范围内。

以下是 type: NodePort 服务的一个示例清单,它指定了一个 NodePort 值(在本例中为 30007):

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
    - port: 80
      targetPort: 80
      # 可选字段
      # 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767)
      nodePort: 30007
type: NodePort 服务自定义 IP 地址配置

你可以在集群中设置节点以使用特定 IP 地址来提供 NodePort 服务。 如果每个节点都连接到多个网络(例如:一个网络用于应用程序流量,另一个网络用于节点和控制平面之间的流量), 你可能需要执行此操作。

如果你要指定特定的 IP 地址来代理端口,可以将 kube-proxy 的 --nodeport-addresses 标志或 kube-proxy 配置文件的等效 nodePortAddresses 字段设置为特定的 IP 段。

此标志采用逗号分隔的 IP 段列表(例如 10.0.0.0/8192.0.2.0/25)来指定 kube-proxy 应视为该节点本地的 IP 地址范围。

例如,如果你使用 --nodeport-addresses=127.0.0.0/8 标志启动 kube-proxy, 则 kube-proxy 仅选择 NodePort 服务的环回接口。 --nodeport-addresses 的默认值是一个空列表。 这意味着 kube-proxy 应考虑 NodePort 的所有可用网络接口。 (这也与早期的 Kubernetes 版本兼容。)

说明:

此服务呈现为 <NodeIP>:spec.ports[*].nodePort.spec.clusterIP:spec.ports[*].port。 如果设置了 kube-proxy 的 --nodeport-addresses 标志或 kube-proxy 配置文件中的等效字段, 则 <NodeIP> 将是过滤的节点 IP 地址(或可能的 IP 地址)。

例子
[root@k8smaster service]# cat nginx2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-2
  labels:
    app.kubernetes.io/name: proxy2
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service2
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: proxy2
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc
    nodePort: 30007

nginx

type: LoadBalancer

在使用支持外部负载均衡器的云提供商的服务时,设置 type 的值为 "LoadBalancer", 将为 Service 提供负载均衡器。 负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过 Service 的 status.loadBalancer 字段发布出去。

实例:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

来自外部负载均衡器的流量将直接重定向到后端 Pod 上,由云提供商决定如何进行负载平衡。

要实现 type: LoadBalancer 的服务,Kubernetes 通常首先进行与请求 type: NodePort 服务等效的更改。 cloud-controller-manager 组件随后配置外部负载均衡器以将流量转发到已分配的节点端口。

你可以将负载均衡服务配置为忽略分配节点端口, 前提是云提供商实现支持这点。

某些云提供商允许设置 loadBalancerIP。 在这些情况下,将根据用户设置的 loadBalancerIP 来创建负载均衡器。 如果没有设置 loadBalancerIP 字段,将会给负载均衡器指派一个临时 IP。 如果设置了 loadBalancerIP,但云提供商并不支持这种特性,那么设置的 loadBalancerIP 值将会被忽略掉。

说明:

针对 Service 的 .spec.loadBalancerIP 字段已在 Kubernetes v1.24 中被弃用。

此字段的定义模糊,其含义因实现而异。它也不支持双协议栈联网。 此字段可能会在未来的 API 版本中被移除。

如果你正在集成某云平台,该平台通过(特定于提供商的)注解为 Service 指定负载均衡器 IP 地址, 你应该切换到这样做。

如果你正在为集成到 Kubernetes 的负载均衡器编写代码,请避免使用此字段。 你可以与 Gateway 而不是 Service 集成, 或者你可以在 Service 上定义自己的(特定于提供商的)注解,以指定等效的细节。

混合协议类型的负载均衡器

特性状态: Kubernetes v1.26 [stable]

默认情况下,对于 LoadBalancer 类型的服务,当定义了多个端口时, 所有端口必须具有相同的协议,并且该协议必须是受云提供商支持的协议。

当服务中定义了多个端口时,特性门控 MixedProtocolLBService (在 kube-apiserver 1.24 版本默认为启用) 允许 LoadBalancer 类型的服务使用不同的协议。

说明:

可用于负载均衡服务的协议集由你的云提供商决定,他们可能在 Kubernetes API 强制执行的限制之外另加一些约束。

禁用负载均衡器节点端口分配

特性状态: Kubernetes v1.24 [stable]

你可以通过设置 spec.allocateLoadBalancerNodePortsfalse 对类型为 LoadBalancer 的服务禁用节点端口分配。 这仅适用于直接将流量路由到 Pod 而不是使用节点端口的负载均衡器实现。 默认情况下,spec.allocateLoadBalancerNodePortstrue, LoadBalancer 类型的服务继续分配节点端口。 如果现有服务已被分配节点端口,将参数 spec.allocateLoadBalancerNodePorts 设置为 false 时,这些服务上已分配置的节点端口不会被自动释放。 你必须显式地在每个服务端口中删除 nodePorts 项以释放对应端口。

设置负载均衡器实现的类别

特性状态: Kubernetes v1.24 [stable]

对于 type 设置为 LoadBalancer 的 Service, spec.loadBalancerClass 字段允许你不使用云提供商的默认负载均衡器实现, 转而使用指定的负载均衡器实现。

默认情况下,.spec.loadBalancerClass 未设置,如果集群使用 --cloud-provider 配置了云提供商, LoadBalancer 类型服务会使用云提供商的默认负载均衡器实现。

如果设置了 .spec.loadBalancerClass,则假定存在某个与所指定的类相匹配的负载均衡器实现在监视服务变化。 所有默认的负载均衡器实现(例如,由云提供商所提供的)都会忽略设置了此字段的服务。.spec.loadBalancerClass 只能设置到类型为 LoadBalancer 的 Service 之上,而且一旦设置之后不可变更。

.spec.loadBalancerClass 的值必须是一个标签风格的标识符, 可以有选择地带有类似 “internal-vip” 或 “example.com/internal-vip” 这类前缀。 没有前缀的名字是保留给最终用户的。

内部负载均衡器

在混合环境中,有时有必要在同一(虚拟)网络地址块内路由来自服务的流量。

在水平分割 DNS 环境中,你需要两个服务才能将内部和外部流量都路由到你的端点(Endpoints)。

如要设置内部负载均衡器,请根据你所使用的云运营商,为服务添加以下注解之一:

选择一个标签。

练习

  • 编写一个yaml文件mysql.yaml,启动一个MySQL的pod,一个副本,然后创建一个service-mysql,发布Mysql标准的方式,启动pod
apiVersion: v1
kind: Pod
metadata:
  name: mysql
  labels:
    app.kubernetes.io/name: mysqlproxy
spec:
  containers:
  - name: mysql
    image: mysql
    ports:
      - containerPort: 3306
        name: http-mysql-svc
    env:
       -name: MYSQL_ROOT_PASSWORD
        value: "123456"

---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: mysqlproxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 3360
    targetPort: http-mysql-svc
    nodePort: 30008

[root@k8smaster service]# kubectl get pod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP               NODE       NOMINATED NODE   READINESS GATES
mysql     1/1     Running   0          71s   10.244.185.194   k8snode2   <none>           <none>
nginx     1/1     Running   0          33m   10.244.185.193   k8snode2   <none>           <none>
nginx-2   1/1     Running   0          22m   10.244.249.1     k8snode1   <none>           <none>



[root@k8smaster service]# kubectl exec -it mysql -- bash
root@mysql:/# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.27 MySQL Community Server - GPL

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

在Window的sqlyog进行连接

  • 编写一个yaml文件nginx.yaml,使用deployment控制器,启动一个nginx的pod,三个副本,然后创建一个服务service-nginx,发布nginx集群
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: mynginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mynginx
  template:
    metadata:
      labels:
        app: mynginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: mynginx
spec:
  type: NodePort
  selector:
    app: mynginx
  ports:
    - name: name-of-service-port
      protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30009

[root@k8smaster service]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP               NODE       NOMINATED NODE   READINESS GATES
mysql                               1/1     Running   0          14m   10.244.185.194   k8snode2   <none>           <none>
nginx                               1/1     Running   0          46m   10.244.185.193   k8snode2   <none>           <none>
nginx-2                             1/1     Running   0          36m   10.244.249.1     k8snode1   <none>           <none>
nginx-deployment-6dcb8ffbc7-bjdth   1/1     Running   0          27s   10.244.249.3     k8snode1   <none>           <none>
nginx-deployment-6dcb8ffbc7-gg4v9   1/1     Running   0          27s   10.244.185.195   k8snode2   <none>           <none>
nginx-deployment-6dcb8ffbc7-wtkhq   1/1     Running   0          27s   10.244.249.2     k8snode1   <none>           <none>

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈密猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值