Kubernetes Service是一种为一组功能相同的 pod 提供单一不变的接入点的资源。
当服务存在时,它的 IP 地址和端口不会改变。 客户端通过 IP 地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个 pod 上。

结合实例解释

前端 web 服务器和后端数据库服务器的例子: 通过为前端 pod 创建服务, 并且将其配置成可以在集群外部访问, 可以暴露一个单一不变的 IP 地址让外部的客户端连接 pod。 同理, 可以为后台数据库 pod 创建服务, 并为其分配一个固定的 IP 地址。 尽管 pod 的 IP 地址会改变, 但是服务的 IP地址固定不变。

image.png

5.1.1 创建服务

实验环境准备:在前面的章节中, 通过创建 ReplicationController 运行了三个包含 Node.js 应用的 pod。 再次创建 ReplicationController 并且确认 pod 启动运行, 在这之后将会为这三个 pod 创建一个服务。
https://www.yuque.com/wayneking-erlxv/op6shg/wz4iqx

  1. cd /root/k8s/
  2. kubectl create -f kubia-rc.yaml

image.png

通过 kubectI expose 创建服务

先创建RC等资源,再利用 kubectl expose命令和标签选择器来创建Service资源。
kubectl expose rc kubia --type=ClusterIP --name kubia-http


通过 YAML 描述文件来创建

先创建RC等资源,然后使用YAML文件创建Service。
image.png

cd /root/k8s/

cat >kubia-svc.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia
EOF

kubectl create -f kubia-svc.yaml

spec.Type默认为clusterIP

检测新的服务

image.png
CLUSTER-IP只能在集群内部被访问。这里Service的主要目标就是使集群内部的其他pod可以访问当前这组pod。

从集群内部测试服务

可以通过以下几种方法向Service发送请求:

  • 创建一个pod,它将发到服务的CLUSTER-IP并记录响应。可以通过查看pod日志检查服务的响应;
  • 使用ssh远程登录到其中一个Kubernetes节点上,然后使用curl命令;
  • 使用命令kubectl exec 在一个已经存在的pod中执行curl。

在运行的容器中远程执行命令

可以使用kubectl exec命令远程地在一个已经存在的pod容器上执行任何
命令。
image.png

为什么使用双横杠(—)?

双横杠(—)代表着kubectl命令选项的结束。在双横杠之后的内容是指 在pod内部需要执行的命令。如果需要执行的命令并没有以横杠开始的参数,横杠也不是必需的。否则,应该加上双横杠。

image.png

配置服务上的会话亲和性(SESSION AFFINITY)

Service proxy通常将每个连接随机选中的后端pod中的一个,所以每次处理请求的pod可能不同。
如果希望特定客户端产生的所有请求每次都指向同一个pod可以设置服务的sessionAffinity属性为ClientIP(默认值是None)。这种方式将会使服务代理将来自同 一个 client IP 的所有请求转发至同一个 pod上。
image.png

为什么不支持基于cookie的会话亲和性选项?

Kubernetes Service不在 HTTP 层面上工作。Service处理 TCP 和 UDP 包,并不关心其中的载荷内容。因为 cookie 是 HTTP 协议中的一部分,服务并不知道它们。

同—个服务暴露多个端口

无须创建两个不同的服务。通过一个集群 IP, 使用一个服务就可以将多个端口全部暴露出来。

注意:在创建一个有多个端口的服务的时候,必须给每个端口指定名字。image.png

注意:标签选择器应用于整个服务,不能对每个端口做单独的配置。如果不同的pod 有不同的端口映射关系,需要创建两个服务。

使用命名的端口

你也可以给pod的端口号命名,然后在Service spec 中通过该名称来指定端口。这样对于一些不是众所周知的端口号,使得service spec更加清晰。
image.png
#也可以给pod template中定义的容器端口命名。
image.png

为什么要采用命名端口的方式?

最大好处就是即使更换端口号也无须更改服务spec,而只需要改变pod sepc中的端口号,因为端口号的名称不变。

5.1.2 发现服务

Kubernetes 为客户端pod提供了发现service的 IP 和端口的方式。

通过环境变量发现服务

在pod开始运行的时候,Kubernetes会初始化一系列的环境变量指向现在存在的service。如果你创建的service早于客户端 pod 创建,pod上的进程可以根据环境变量获取service的IP地址和端口号。否则,pod上的进程看不到service上关于其IP和端口号的环境变量(服务名_SERVICE_HOST,服务名_SERVICE_PORT)。

因service在pod之后创建,现在我们让它重新创建pod,这样就能保证新创建的pod可以看到service相关的变量了

kubectl delete po --all

RC重新创建pod之后,我们在pod上执行env,观察它的环境变量
image.png
注意:这些是kubernetes和kubia两个service的相关环境变量。KUBIA_SERVICE_HOST 和 KUBIA_SERVICE_PORT,分别代表了kubia服务的IP地址和端口号。

现在回顾本章开始部分的前后端的例子,当前端 pod 需要连接后端数据库服务 pod 时,可以通过名为 backend_database 的服务将后端 pod 暴露出来,然后前端 pod通过自己的环境变量BACKEND_DATABASE_SERVICE_HOST 和 BACKEND_DATABASE_SERVICE_PORT 去获得 IP 地址和端口信息。

注意:在环境变量中,服务名中的横杠(-)会被转换为下划线(_),并且名称都会转换为大写字母。

通过 DNS 发现服务

有kube-system命名空间有,有一个或多个pod就是运行DNS服务的。在集群中的其他pod都被配置成使用其作为DNS服务器(Kubernetes 通过修改每个容器的 /etc/resolv.conf 实现)。运行在pod上的进程的DNS查询都会被Kubernetes自身的DNS服务器响应,该服务器知道系统中运行的所有服务。

每个服务在内部DNS服务器中获得一个DNS条目,客户端的pod在知道服务名称的情况下可以通过FQDN(全限定域名)来访问,而不是诉诸于环境变量。

注意:pod 是否使用内部的 DNS 服务器是根据 pod 中spec的 dnsPolicy 属性来决定的。

实验:观察集群中的内部DNS服务器。(pod -> rs -> deploy -> svc)
image.png

通过 FQDN 连接服务

再次回顾前端-后端的例子,前端可以通过打开以下 FQDN 的连接来访后端数据库服务:
backend-database.default.svc.cluster.local

  • backend-database 表示service name;
  • default 表示服务所在的namespace;
  • svc.cluster.local 表示所有的集群本地服务名称中使用的集群域后缀。

注意:客户端必须知道服务的端口号。如果不是使用标准端口(如HTTP的80),客户端可以从环境变量中获取端口号。

如果前端 pod 和数据库 pod 在同一个命名空间下,可以省略pod中resolv.conf文件search指定的各个search path,直接使用backend-database来指代服务。
image.png

在 pod 容器中运行 shell

$ kubectl exec -it kubia-3inly -- bash
root@kubia-3inly:/# curl http://kubia.default.svc.cluster.local
You’ve hit kubia-5asi2
root@kubia-3inly:/# curl http://kubia.default
You’ve hit kubia-3inly
root@kubia-3inly:/# curl http://kubia
You’ve hit kubia-8awf3

无法 ping 通服务 IP 的原因

$ kubectl exec -it kubia-3inly bash
root@kubia-3inly:/# ping kubia
PING kubia.default.svc.cluster.local (10.111.249.153): 56 data bytes
^C—- kubia.default.svc.cluster.local ping statistics —-
54 packets transmitted, 0 packets received, 100% packet loss

curl 这个服务是工作的,但是却 ping 不通。这是因为服务的CLUSTER IP是一个虚拟IP,并且只有与服务端口结合时才有意义。

kubectl explain svc.spec.ports

port -required-
The port that will be exposed by this service.

targetPort
Number or name of the port to access on the pods targeted by the service.
If this is not specified, the value of the ‘port’ field is used (an identity map).
This field is ignored for services with clusterIP=None, and should be omitted or set equal to the ‘port’ field.