0%

Kubernetes Service之ClusterIP Service、NodePort Service

K8s的Service资源是一种为一组功能相同的Pod提供统一不变的流量入口的资源。这里介绍两种典型的Service类型:ClusterIP Service、NodePort Service

abstract.png

ClusterIP Service

ClusterIP Service通过集群的内部IP暴露服务,使得服务只能够在集群内部被访问。这也是默认Service类型。下面我们通过RS创建一组Pod,然后通过ClusterIP Service统一流量入口。使得无论RS的Pod无论怎么变化,都可以被访问到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# API组、版本
apiVersion: apps/v1
# 资源类型
kind: ReplicaSet
metadata:
# RS名称
name: my-kubia-rs-1
spec:
# 副本数量
replicas: 3
# 标签选择器
selector:
matchLabels:
app: kubia
# Pod 模板
template:
metadata:
# 标签信息
labels:
app: kubia
spec:
# 容器信息
containers:
- name: my-kubia-1
image: luksa/kubia
# 仅用于展示容器所使用的端口
ports:
- containerPort: 8080
protocol: TCP

---

# API组、版本
apiVersion: v1
# 资源类型
kind: Service
metadata:
name: kubia-service
spec:
# Service类型
type: ClusterIP
# 标签选择器
selector:
app: kubia
ports:
- port: 9696 # 服务监听端口
targetPort: 8080 # 服务将请求转发到的目标端口

效果如下所示

figure 1.jpeg

由于集群内各Pod之间的网络是互通的,故我们可以先通过Pod的IP、Port来访问验证下Pod是否可以正常工作。效果如下所示

figure 2.jpeg

现在我们尝试通过服务的IP、Port进行访问,效果如下所示。不难看出服务同时具有负载均衡的功能了

figure 3.jpeg

此外,还可以通过sessionAffinity配置服务的会话亲和性。当配置为ClientIP时,其可以保证将来自相同客户端IP的请求总是转发至同一个Pod中

1
2
3
4
5
apiVersion: v1
kind: Service
spec:
# 基于客户端IP的会话亲和性
sessionAffinity: ClientIP

需要注意的是,当Service暴露多个端口时,需要给每个端口指定名字。如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
spec:
ports:
- name: http-port #名为http-port的端口转发
port: 80 # 服务监听端口
targetPort: 3080 # 服务将请求转发到的目标端口
- name: https-port #名为https-port的端口转发
port: 443 # 服务监听端口
targetPort: 3443 # 服务将请求转发到的目标端口
- name: test-port #名为test-port的端口转发
port: 69 # 服务监听端口
targetPort: 3069 # 服务将请求转发到的目标端口

NodePort Service

该类型的服务类似于ClusterIP Service,集群内部的Pod可以直接通过该服务的IP、Port来访问服务。不同之处在于该NodePort Service还会在K8s集群中每个节点打开一个端口,同时将节点上该端口的流量进行转发重定向到该服务当中。这也是该服务类型名称的由来。此时一方面,Pod就可以通过任意节点的IP、NodePort Service在集群该节点打开的端口来访问访问服务;另一方面,如果K8s集群外的客户端可以访问K8s集群节点的话,就可以实现将K8s中的服务暴露给外部客户端了。而不仅仅是集群内的Pod才能访问了。示例配置文件如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# API组、版本
apiVersion: apps/v1
# 资源类型
kind: ReplicaSet
metadata:
# RS名称
name: my-kubia-rs-1
spec:
# 副本数量
replicas: 3
# 标签选择器
selector:
matchLabels:
app: kubia
# Pod 模板
template:
metadata:
# 标签信息
labels:
app: kubia
spec:
# 容器信息
containers:
- name: my-kubia-1
image: luksa/kubia
# 仅用于展示容器所使用的端口
ports:
- containerPort: 8080
protocol: TCP

---

# API组、版本
apiVersion: v1
# 资源类型
kind: Service
metadata:
# 资源名称
name: my-nodeport-service-1
spec:
# Service类型
type: NodePort
selector:
app: kubia
ports:
- port: 80 # 服务监听端口
targetPort: 8080 # 服务将请求转发到Pod的目标端口
# 在集群各节点所打开的端口, 使得可以通过集群中任一节点IP、nodePort端口号访问该服务
nodePort: 30123

效果如下所示,我们可以直接通过服务的集群IP、端口访问服务。这点与ClusterIP Service并无二异

figure 4.jpeg

现在我们尝试通过集群节点IP、NodePort Service在节点上打开的端口来访问服务。效果如下所示

figure 5.jpeg

服务发现

环境变量

当服务先创建、Pod后创建时,在创建Pod过程中会把活跃Service的IP、Port信息写入该Pod的环境变量当中。具体地,服务IP、Port的环境变量名分别为{ServiceName}_SERVICE_HOST、{ServiceName}_SERVICE_PORT。其中,ServiceName是服务名称的大写,服务名称中的横线被转换成下划线

1
2
# 查看指定Pod中的环境变量
kubectl exec <Pod的名称> env

效果如下所示。这样集群内的Pod就可以通过环境变量获取服务的IP、Port。但如果先创建Pod、再创建服务的话,此时Pod当中就不会存在后续所创建服务的环境变量了

figure 6.jpeg

DNS

集群中的Pod还可以通过服务FQDN全限定域名作为服务的IP进行访问。FQDN的名称规则为..svc.cluster.local。其中FQDN名称中的service-name表示服务名称,namespace表示命名空间的名称,svc.cluster.local表示所有集群本地服务名称中使用的可配置集群域后缀。例如,default命名空间下的kubia-service服务,其FQDN名称为kubia-service.default.svc.cluster.local。甚至我们可以直接用FQDN的前半部分——.来作为服务的IP。效果如下所示

figure 7.jpeg

Note

Service的集群IP是一个虚拟IP,故无法ping通是正常的

参考文献

  1. Kubernetes in Action中文版 Marko Luksa著
  2. 深入剖析Kubernetes 张磊著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝