为什么需要 Service:
每个Pod被创建后,都会有它自己的IP,但由于Pod存在生命周期,经常会被创建与销毁,一旦重新创建POD,该POD的IP就会发生变化。
所以这些POD应用需要对外提供一个统一的访问入口,即固定IP,外部才能正常访问,这个统一的访问入口就是service。
Service 定义:
我们可以将多个POD划分到同一个逻辑组中,并统一向外提供服务,这个逻辑组就是一个service。POD是通过Label Selector加入到指定的service中。
Service相当于是一个负载均衡器,用户请求会先到达service,再由service转发到它内部的某个POD上(默认以round-robin方式转发)。
请求(集群内部或外部) -> Service -> POD1,POD2,...
Service有四种类型:
不同service类型通过 services.spec.type 字段来指定。
- ClusterIP:用于集群内部访问。该类型会为service分配一个IP,集群内部请求先到达service,再由service转发到其内部的某个POD上。
- NodePort:用于集群外部访问。该类型会将Service的Port映射到集群的每个Node节点上,然后在集群之外,就能通过Node节点上的映射端口访问到这个Service。
- LoadBalancer:用于集群外部访问
- ExternalName:
Headless Service:
Service作为访问入口,它可以用 IP 或 service_name(要借助于内部DNS解析)这两种方式来访问。当创建好service时,系统会自动为Service分配一个IP,我们也可以通过services.spec.clusterIP 字段手动指定一个IP,但若这个字段设置为None,系统就不会再为这个service分配IP,后面就只能通过service_name来访问,而这种没有IP的service,就被称为Headless Service。
也就是,Headless表示这个service没有分配IP信息,只能通过service_name来访问。
service字段说明:
services.spec.
clusterIP: 设置service的IP。若省略该字段,默认由系统随机分配
ports:
port: 设置service的访问端口
targetPort: 设置service下的后端POD端口
nodePort: 设置映射到node节点上的端口。当type为NodePort或LoadBalancer时,需要为service指定一个映射在Node节点上的端口
selector: 设置标签选择器。POD根据匹配此标签来加入到此service下
type: 定义service类型,可选值有ClusterIP(默认), NodePort, LoadBalancer, ExternalName
sessionAffinity: 可选值是ClientIP或None。默认值为None,表示以round-robin方式将请求转发到后端Pod,若值为ClientIP,表示保持连接,请求始终会被调度到同一个Pod上
ClusterIP类型
需要将 services.spec.type 设置为 ClusterIP,该类型会为service分配一个IP,这个IP只能在集群内部访问,用户请求先到达service,再由service转发到其内部的某个POD上。
默认service是以round-robin方式将请求转发到后端Pod,若始终要转发到同一个POD上,需要设置services.spec.sessionAffinity。
请求(集群内部)---> service_ip/service_name:8080 -> POD1:80,POD2:80,POD3:80示例1:带有IP的Service
第一步:创建一个ClusterIP类型的Service(手动指定IP或省略ClusterIP字段)
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector: #设置标签选择器。POD通过匹配此label来选择是否要加入到这个Service下
app: myweb01
type: ClusterIP #设置service类型为ClusterIP
ClusterIP: 172.17.202.174 #为service指定一个IP,这个IP在集群内部可访问。若省略该字段,系统会自动分配一个IP
ports:
- name: nginx
port: 8080
protocol: TCP
targetPort: 80
sessionAffinity: None
第二步:创建POD(加入到此service中)
apiVersion: apps/v1
kind: Deployment
metadata:
name: svc01-pod-demo
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myweb01
template:
metadata:
labels:
app: myweb01
spec:
containers:
- name: myweb01
image: nginx:1.7.9
ports:
- name: http
containerPort: 80
第三步:查看Service
$ kubectl get svc my-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-service ClusterIP 172.17.202.1748080/TCP 4s $ kubectl describe svc my-service Name: my-service Namespace: default Labels: Annotations: ... Selector: app=myweb01 #service会通过匹配此label,选择哪些POD加入到这个service下 Type: ClusterIP IP: 172.17.2.14 Port: nginx 8080/TCP TargetPort: 80/TCP Endpoints: 172.16.1.12:80,172.16.2.17:80,172.16.2.45:80 #表示当前已加入到此service下的POD Session Affinity: None Events:
第四步:集群内部访问Service
- 直接访问Service的IP;
- 直接访问Service的Name(依靠DNS解析):同一个Namespace下直接通过servicename访问,跨Namespace访问需要加上namespace,如{servicename}.{namespace};
- 通过环境变量访问:将service的一些IP、端口等信息,通过环境变量方式传到Pod里;
集群内的Pod和Node
|
| curl 172.17.2.14:8080
| curl my-service:8080
| curl $service_name:$service_port
|
——————↓———————
| my-service |
| 172.17.2.14 |
'——————↓———————'
|
____________________|_________________________
| / | |
| / | |
| POD1 | POD3 |
| 172.16.1.12:80 | 172.16.2.17:80 |
| POD2 |
| 172.16.2.45:80 |
| |
| Deployment |
| Pod Label: app=myweb01 |
|______________________________________________|
补充说明:关于用service_name访问服务
域名解析操作是通过集群内部的DNS服务器来完成的,DNS服务器IP 就是名为kube-dns的service的IP,如下:
$ kubectl -n kube-system get svc kube-dns NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 172.17.0.1053/UDP,53/TCP 478d $ kubectl -n kube-system describe svc kube-dns Name: kube-dns Namespace: kube-system Labels: k8s-app=kube-dns kubernetes.io/cluster-service=true kubernetes.io/name=KubeDNS Annotations: ..... Selector: k8s-app=kube-dns Type: ClusterIP IP: 172.17.0.10 Port: dns 53/UDP TargetPort: 53/UDP Endpoints: 172.16.0.5:53,172.16.1.2:53 Port: dns-tcp 53/TCP TargetPort: 53/TCP Endpoints: 172.16.0.5:53,172.16.1.2:53 Session Affinity: ClientIP Events:
kube-dns这个service下还有两个名为coredns的POD,也就是说,域名解析功能实际上是通过这两个POD来实现的,所以要确保集群内有coredns容器处于运行状态。
$ kubectl -n kube-system get pods | grep coredns NAME READY STATUS RESTARTS AGE coredns-86f77d55bb-2j2wc 1/1 Running 14 478d coredns-86f77d55bb-gbjpg 1/1 Running 15 478d
可以进入到某个POD容器中查看/etc/resolv.conf文件中的nameserver,默认是指向kube-dns这个servcie的IP。
$ kubectl exec my-pod01-55d7dbfdfd-2w7sv cat /etc/resolv.conf nameserver 172.17.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
集群中默认的域名后缀为:svc.cluster.local.
若两个service在不同的namespace中,它们的POD之间要通过域名互相访问时,就需要在默认域名后缀前加多一级,即加上对应的namespace名称,也就是【NAMESPACE.svc.cluster.local.】。
比如,对于在同一个namespace下,若service1的POD访问service2的POD,则直接【ping service2】就能解析域名;对于跨Namespace情况(service2在名为testns的namespace下),则需要【ping service2.testns】才能解析域名。
一个完整service名称为:SERVICE_NAME.NAMESPACE.svc.cluster.local.
示例2:Headless Service(无IP的Service)第一步:创建一个ClusterIP类型的Headless Service(ClusterIP字段设置为None)
apiVersion: v1
kind: Service
metadata:
name: my-svc01
namespace: mytest
spec:
selector:
app: my-test01-pod
type: ClusterIP
clusterIP: None #指定为None,创建的service就不会分配到IP信息
ports:
- name: nginx
port: 80
targetPort: 8082
第二步:创建POD(加入到此service中)
apiVersion: apps/v1
kind: Deployment
metadata:
name: test01-pod
namespace: mytest
spec:
replicas: 3
selector:
matchLabels:
app: my-test01-pod
template:
metadata:
labels:
app: my-test01-pod
spec:
containers:
- name: my-nginx
image: nginx:1.7.9
ports:
- name: http
containerPort: 8082
第三步:查看service
$ kubectl -n mytest get svc my-svc01 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-svc01 ClusterIP None80/TCP 11m $ kubectl -n mytest describe svc my-svc01 Name: my-svc01 Namespace: mytest Labels: Annotations: ... Selector: app=my-test01-pod Type: ClusterIP IP: None Port: nginx 80/TCP TargetPort: 8082/TCP Endpoints: 172.16.1.169:8082,172.16.2.52:8082,172.16.1.143:8082 Session Affinity: None Events:
第四步:通过域名(即service_name)访问服务
访问流程:
集群内的Pod
|
| curl my-svc01:8082 (直接解析到后端POD的IP上,且指定的是Pod容器里的服务实际监听的Port)
|
____________________|_________________________
| / | |
| / | |
| POD1 | POD3 |
| 172.16.2.52:8082 | 172.16.1.143:8082 |
| POD2 |
| 172.16.1.169:8082 |
| |
| Deployment |
| Pod Label: app=myweb01 |
|______________________________________________|
查看一下POD容器里面服务监听的端口,如下:
$ kubectl -n mytest get pods
NAME READY STATUS RESTARTS AGE IP
test01-pod-5cfd78675c-wcrgx 1/1 Running 0 20m 172.16.2.52
test01-pod-5cfd78675c-abxyd 1/1 Running 0 20m 172.16.1.143
test01-pod-5cfd78675c-xxsjz 1/1 Running 0 20m 172.16.1.169
#因为在打包nginx镜像时,就已经将nginx服务的监听端口修改为8082了
$ kubectl -n mytest exec -it test01-pod-5cfd78675c-wcrgx bash
root@test01-pod-5cfd78675c-wcrgx:/# ss -tnlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:8082 *:* users:(("nginx",1,10))
跨namespace访问,在default命名空间下的Pod容器里面,访问mytest命名空间下的my-svc01服务,如下:
$ kubectl -n default exec testpod bash root@testpod:/# ping my-svc01.mytest PING my-svc01.mytest.svc.cluster.local (172.16.2.52) 56(84) bytes of data. 64 bytes from 172.16.2.52: icmp_seq=1 ttl=63 time=0.266 ms ... root@testpod:/# curl my-svc01.mytest:8082 test01-pod-5cfd78675c-wcrgx root@testpod:/# curl my-svc01.mytest:8082 test01-pod-5cfd78675c-xxsjz
不跨namespace访问,在mytest命名空间下的Pod容器里面访问服务,如下:
$ kubectl -n zjmtest exec testpod2 bash root@testpod:/# ping my-svc01 PING my-svc01.mytest.svc.cluster.local (172.16.2.52): 48 data bytes 64 bytes from 172.16.2.52: icmp_seq=1 ttl=63 time=0.266 ms ... root@testpod2:/# curl my-svc01:8082 test01-pod-5cfd78675c-wcrgx root@testpod2:/# curl my-svc01:8082 test01-pod-5cfd78675c-xxsjzNodePort类型
需要将 services.spec.type 设置为 NodePort,该类型是建立在ClusterIP基础上的,所以必须要为service分配一个IP,所以不能将 services.spec.clusterIP 字段设置为None。
设置为NodePort类型后,系统会在集群中的每个节点上监听一个nodePort端口。当在集群外部发起一个请求时,请求会先到达节点上监听的端口,然后再路由到service的IP,最后由service转发到内部POD上;
请求(集群外部)---> node_ip:31180 ---> service_ip:8080 ---> POD1:80,POD2:80,POD3:80示例:
第一步:创建一个NodePort类型的service(必须指定一个IP)
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: myweb01
type: NodePort
clusterIP: 172.17.99.99
ports:
- name: nginx
port: 8080 #service的端口
targetPort: 80 #service下的Pod端口
nodePort: 31180 #当type为NodePort时,需要指定一个端口,该端口会监听在集群内的每个节点上
sessionAffinity: None
第二步:创建POD(加入到此service中)
apiVersion: apps/v1
kind: Deployment
metadata:
name: svc01-pod-demo
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myweb01
template:
metadata:
labels:
app: myweb01
spec:
containers:
- name: myweb01
image: nginx:1.7.9
ports:
- name: http
containerPort: 80
第三步:查看Service
$ kubectl get svc my-service -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR my-service NodePort 172.17.99.998080:31180/TCP 21m app=myweb01 $ kubectl describe svc my-service Name: my-service Namespace: default Labels: Annotations: Selector: app=myweb01 Type: NodePort IP: 172.17.99.99 Port: nginx 8080/TCP TargetPort: 80/TCP NodePort: nginx 31180/TCP Endpoints: 172.16.1.164:80,172.16.2.169:80,172.16.2.46:80 Session Affinity: None External Traffic Policy: Cluster Events:
第四步:查看集群node节点上的监听端口(会在每个node节点监听上31180端口)
$ netstat -tunlp | grep 31180 tcp6 0 0 :::31180 :::* LISTEN 4164/kube-proxy
第五步:集群外部访问Service
$ kubectl get nodes -owide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME master1 Ready master 478d v1.15.1 172.24.11.208CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://18.9.2 master2 Ready master 478d v1.15.1 172.24.11.209 CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://18.9.2 master3 Ready master 478d v1.15.1 172.24.11.207 CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://18.9.2 node1 Ready 478d v1.15.1 172.24.11.210 CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://18.9.2 node2 Ready 478d v1.15.1 172.24.11.212 CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://18.9.2 node3 Ready 478d v1.15.1 172.24.11.211 CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://18.9.2 $ curl 172.24.11.210:31180 $ curl 172.24.11.211:31180 $ curl 172.24.11.212:31180
访问流程:
集群外部
|
| curl node_ip:node_port
|
_______________________|_________________________
| | |
| 集群节点 | |
| .—————————↓———————————. |
| | nodes_ip:node_port | |
| | 172.24.11.210:31180 | |
| | 172.24.11.211:31180 | |
| | 172.24.11.212:31180 | |
| '—————————↓———————————' |
| | |
| .—————————↓——————————. |
| | my-service | |
| | 172.17.99.99:8080 | |
| '—————————↓——————————' |
| | |
| ____________________|______________________ |
| | / | | |
| | / | | |
| | POD1 | POD3 | |
| | 172.16.1.164:80 | 172.16.2.46:80 | |
| | POD2 | |
| | 172.16.2.169:80 | |
| | | |
| | Deployment | |
| | Pod Label: app=myweb01 | |
| |___________________________________________| |
|_________________________________________________|



