简介

Service在Kubernetes中主要是为POD固定化IP 来访问POD,当Pod发生异常重新创建时,Pod还会与service进行互联,Pod的IP的不固定,但是Service的IP时固定的。

简称:SVC,主要对Pod中容器的负载均衡,并且绑定SVC指定IP地址方便其他容器进行调用;

由于Pod都是内部的私有地址不能正常给客户端提供访问,通过service服务发现暴露给客户端,客户端根据暴露的地址+端口来轮询访问内部的Pod;

Service作用

通过label、selector进行关连实现 服务发现

通过Service实现Pod的 负载均衡

四层转发

Service类型

Cluster IP

Service通过Cluster内部的IP对外提供服务,只有Cluster内的节点和Pod可访问,这是默认的Service类型,适用于前端服务访问后端服务,比如前端nginx集群要访问后端的php来解析动态网页,这个时候php就可以使用内部的Service访问

默认分配一个稳定的IP,只能在集群内部访问(同Namespace内的Pod)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@pool1 k8s_yaml]# vi ClusterIP_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: {svc_name}
labels:
{key}: {value}
spec:
selector:
{key}: {value}
ports:
- port: 80
protocol: TCP
targetPort: 80

[root@pool1 k8s_yaml]# kubectl get svc | grep cluster
clusterip ClusterIP 10.101.34.134 <none> 80/TCP 19s

NodePort

Service通过Cluster节点的静态端口对外提供服务。Cluster外部可以通过集群中的节点IP:prot访问到Service,自动分配的端口号在30000-32767,手动指定也必须是这个范围。

在每个节点上启用一个端口来暴露服务,可以在集群外部访问,也会分配一个稳定内部集群IP地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@pool1 k8s_yaml]# vi NodePort_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: {svc_name}
labels:
{key}: {value}
spec:
selector:
{key}: {value}
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30443 # 如,不指定nodeport IP则随机分配;不得手动指定30000以内得端口
type: NodePort

[root@pool1 k8s_yaml]# kubectl get svc | grep nodeport
nodeport NodePort 10.104.150.108 <none> 80:30443/TCP 6s

LoadBalancer

Service利用cloud provider特有的load balancer对外提供服务,cloud provider负责将load balancer的流量导向Service。

与NodePort类似,在每个节点上启用一个端口来暴露服务。除此之外,kubernetes会请求底层云平台上的负载均衡器,将每个Node([NodeIP]:[NodePort])作为后端添加进去。

Cluster IP访问Service

Kubernetes Service 从逻辑上代表了一组 Pod,具体是哪些 Pod 则是由 label 来挑选。这个label是写在yml中的labels选项,并不是为node设置的label,这个label可以代表运行服务的Pod。

Service 有自己 IP,而且这个 IP 是不变的。客户端只需要访问 Service 的 IP,kube-proxy组件用来建立和维护Service与Pod的映射关系。无论后端的Pod如何变化,对用户访问不会有任何影响,因为Service没有变,相当于集群的代理。

Service的使用依赖于Pod的运行,运行一个httpd的Deployment来创建Pod,进行Service的创建

Clustart内部访问

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
[root@node1 ~]# vim httpd.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd
spec:
selector:
matchLabels:
run: httpd # Service会通过这个label来代理服务Pod
replicas: 3
template:
metadata:
labels:
run: httpd
spec:
containers:
- name: httpd
image: httpd:latest
ports:
- containerPort: 80 # 使用Service映射,需要将服务端口监听

[root@node1 ~]# kubectl apply -f httpd.yml
deployment.apps/httpd created

[root@node1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
httpd-5dd67c6b75-5xjg4 1/1 Running 0 4m10s 10.244.1.3 node2
httpd-5dd67c6b75-89hdk 1/1 Running 0 4m10s 10.244.2.3 node3
httpd-5dd67c6b75-ngnk4 1/1 Running 0 4m10s 10.244.2.4 node3

# cluster IP只适用于在集群内部访问pod的ip 外部是访问不到的
[root@node1 ~]# curl 10.244.1.3
<html><body><h1>It works!</h1></body></html>
[root@node1 ~]# curl 10.244.2.3
<html><body><h1>It works!</h1></body></html>
[root@node1 ~]# curl 10.244.2.4
<html><body><h1>It works!</h1></body></html>

# 创建Service将端口映射到物理机,使得外部可以访问
[root@node1 ~]# vim httpd-svc.yml
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
selector:
run: httpd # 选择集群中有此标签的所有Pod进行端口映射
ports:
- protocol: TCP # 采用tcp协议
port: 8080 # 映射以后的访问端口
targetPort: 80 # Pod内监听端口

[root@node1 ~]# kubectl apply -f httpd-svc.yml
service/httpd-svc created

[root@node1 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-svc ClusterIP 10.102.3.63 <none> 8080/TCP 5m8s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18m

[root@node1 ~]# kubectl describe service httpd-svc
Name: httpd-svc
Namespace: default
Labels: <none>
Annotations: Selector: run=httpd
Type: ClusterIP
IP: 10.102.3.63
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.3:80,10.244.2.3:80,10.244.2.4:80 # 如果此处没有pod IP那么这个service是无法访问的,需检查yaml中selector标签
Session Affinity: None
Events: <none>

# 访问cluster IP
[root@node1 ~]# curl 10.102.3.63:8080
<html><body><h1>It works!</h1></body></html>

Service外部暴露端口

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
[root@node1 ~]# vim httpd-svc.yml 
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
type: NodePort # 只需要添加类型为NodePort即可
selector:
run: httpd
ports:
- protocol: TCP
port: 8080
targetPort: 80

[root@node1 ~]# kubectl apply -f httpd-svc.yml
service/httpd-svc configured

[root@node1 ~]# kubectl get service httpd-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-svc NodePort 10.102.3.63 <none> 8080:32558/TCP 131m
# 以上信息表示,pod容器内的80端口映射为Cluster IP的8080端口,然后再映射到节点ip的随机端口32558

[root@node1 ~]# netstat -anput | grep 32558
tcp 0 0 0.0.0.0:32558 0.0.0.0:* LISTEN 5958/kube-proxy

[root@node1 ~]# curl 10.99.2.15:32558
<html><body><h1>It works!</h1></body></html>
[root@node1 ~]# curl 10.99.2.16:32558
<html><body><h1>It works!</h1></body></html>
[root@node1 ~]# curl 10.99.2.17:32558
<html><body><h1>It works!</h1></body></html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 手动指定映射端口
[root@node1 ~]# vim httpd-svc.yml
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
type: NodePort
selector:
run: httpd
ports:
- protocol: TCP
nodePort: 30000 # 添加了nodePort,指定映射到主机的端口,范围30000-32767
port: 8080
targetPort: 80

[root@node1 ~]# kubectl apply -f httpd-svc.yml
service/httpd-svc configured

[root@node1 ~]# curl 10.99.2.15:30000
<html><body><h1>It works!</h1></body></html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 多端口映射
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
type: NodePort
selector:
run: httpd
ports:
- protocol: TCP
nodePort: 30000
port: 8080
targetPort: 80
- protocol: TCP
nodePort: 30443
port: 8081
targetPort: 443

Service两种负载模式

  • iptables模式
  • ipvs模式(底层lvs)

查看负载均衡规则

iptables模式

特点:

  • 灵活,功能强大(包过滤,地址转换)
  • 规则遍历匹配和更新,呈线性时延

缺点:

  • 逐条匹配规则,如果规则条数很多,性能会很差
1
iptables-save | grep {service_name}

ipvs模式(底层lvs)

特点:

  • 工作在内核态,有更好的性能
  • 调度算法:rr、wrr、lc、wlc、ip、hash
1
ipvsadm -L -n

转发包流程

客户端->NodePort/ClusterIP(IPtables/IPvs负载均衡规则)->分布在各个Node节点得Pod;