Service可以看作是一组提供相同服务的Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。 service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)
service的类型:
• ClusterIP:默认值,k8s系统给service自动分配的虚拟IP,只能在集群内部访问。
• NodePort:将Service通过指定的Node上的端口暴露给外部,访问任意一个 NodeIP:nodePort都将路由到ClusterIP。
• LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负 载均衡器,并将请求转发到 :NodePort,此模式只能在云服务器上使用。
• ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。
Service 是由 kube-proxy 组件,加上 iptables 来共同实现的.
• kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的 CPU资源。
• IPVS模式的service,可以使K8s集群支持更多量级的Pod。
开启kube-proxy的ipvs模式:
yum install -y ipvsadm
所有节点安装
kubectl edit cm kube-proxy -n kube-system
修改IPVS模式 mode: “ipvs”
kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'
更新kube-proxy pod
IPVS模式下,kube-proxy会在service创建后,在宿主机上添加一个虚拟网卡: kube-ipvs0,并分配service IP。
kube-proxy通过linux的IPVS模块,以rr轮询方式调度service中的Pod。
Kubernetes 提供了一个 DNS 插件 Service
[kubeadm@server1 mainfest]$ vim service.yml
[kubeadm@server1 mainfest]$ cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
[kubeadm@server1 mainfest]$ kubectl apply -f service.yml
service/myservice created
[kubeadm@server1 mainfest]$ vim pod2.yml
[kubeadm@server1 mainfest]$ cat pod2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-example
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:v2
[kubeadm@server1 mainfest]$ kubectl apply -f pod2.yml
deployment.apps/deployment-example created
[kubeadm@server1 mainfest]$ kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: ClusterIP
IP: 10.107.6.65
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.47:80,10.244.2.70:80
Session Affinity: None
Events: <none>
[kubeadm@server1 mainfest]$ kubectl run demo --image=busyboxplus -it --restart=Never
If you don't see a command prompt, try pressing enter.
[ root@demo:/ ]$ curl 10.107.6.65
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[ root@demo:/ ]$ curl 10.107.6.65
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[ root@demo:/ ]$ curl 10.107.6.65/hostname.html
deployment-example-67764dd8bd-pvkc6
[ root@demo:/ ]$ curl 10.107.6.65/hostname.html
deployment-example-67764dd8bd-p5qnr
[ root@demo:/ ]$ curl 10.107.6.65/hostname.html
deployment-example-67764dd8bd-pvkc6
[ root@demo:/ ]$ curl 10.107.6.65/hostname.html
deployment-example-67764dd8bd-p5qnr
[ root@demo:/ ]$ curl myservice/hostname.html
deployment-example-67764dd8bd-pvkc6
[ root@demo:/ ]$ curl myservice/hostname.html
deployment-example-67764dd8bd-p5qnr
[ root@demo:/ ]$ curl myservice/hostname.html
deployment-example-67764dd8bd-pvkc6
[ root@demo:/ ]$ nslookup myservice
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: myservice
Address 1: 10.107.6.65 myservice.default.svc.cluster.local
[ root@demo:/ ]$ [kubeadm@server1 mainfest]$
[kubeadm@server1 mainfest]$ kubectl get services kube-dns --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 7d3h
[kubeadm@server1 mainfest]$ cat pod2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-example
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:v2
[kubeadm@server1 mainfest]$ kubectl apply -f pod2.yml
deployment.apps/deployment-example unchanged
[kubeadm@server1 mainfest]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
demo 0/1 Completed 0 7h16m
deployment-example-67764dd8bd-p5qnr 1/1 Running 0 7h17m
deployment-example-67764dd8bd-pvkc6 1/1 Running 0 7h17m
[kubeadm@server1 mainfest]$ vim service.yml
[kubeadm@server1 mainfest]$ cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
[kubeadm@server1 mainfest]$ kubectl apply -f service.yml
service/myservice created
[kubeadm@server1 mainfest]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d10h
myservice ClusterIP 10.100.83.68 <none> 80/TCP 5s
[kubeadm@server1 mainfest]$ kubectl edit svc myservice
service/myservice edited
[kubeadm@server1 mainfest]$ kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: NodePort
IP: 10.100.83.68
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 31059/TCP
Endpoints: 10.244.1.47:80,10.244.2.70:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
[kubeadm@server1 mainfest]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d10h
myservice NodePort 10.100.83.68 <none> 80:31059/TCP 5m30s
[kubeadm@server1 mainfest]$ kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 7d10h
[kubeadm@server1 mainfest]$ dig myservice.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-72.el7 <<>> myservice.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 541
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myservice.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myservice.default.svc.cluster.local. 30 IN A 10.100.83.68
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Fri Jun 26 14:31:56 CST 2020
;; MSG SIZE rcvd: 115
[kubeadm@server1 mainfest]$ kubectl run demo --image=busyboxplus -it --restart=Never
If you don't see a command prompt, try pressing enter.
[ root@demo:/ ]$ cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
[ root@demo:/ ]$ ping myservice
PING myservice (10.100.83.68): 56 data bytes
64 bytes from 10.100.83.68: seq=0 ttl=64 time=0.034 ms
^C
--- myservice ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.034/0.034/0.034 ms
Headless Service “无头服务” 。 Headless Service不需要分配一个VIP,而是直接以DNS记录的方式解析出被代理 Pod的IP地址。
域名格式:$(servicename).$(namespace).svc.cluster.local
yum install -y bind-utils.x86_64
安装解析工具
[kubeadm@server1 mainfest]$ vim service.yml
[kubeadm@server1 mainfest]$ cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
clusterIP: None
[kubeadm@server1 mainfest]$ kubectl apply -f service.yml
service/myservice created
[kubeadm@server1 mainfest]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d11h
myservice ClusterIP None <none> 80/TCP 10s
[kubeadm@server1 mainfest]$ kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: ClusterIP
IP: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.47:80,10.244.1.48:80,10.244.2.70:80
Session Affinity: None
Events: <none>
[kubeadm@server1 mainfest]$ 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: prometheus.io/port: 9153
prometheus.io/scrape: true
Selector: k8s-app=kube-dns
Type: ClusterIP
IP: 10.96.0.10
Port: dns 53/UDP
TargetPort: 53/UDP
Endpoints: 10.244.0.10:53,10.244.0.11:53
Port: dns-tcp 53/TCP
TargetPort: 53/TCP
Endpoints: 10.244.0.10:53,10.244.0.11:53
Port: metrics 9153/TCP
TargetPort: 9153/TCP
Endpoints: 10.244.0.10:9153,10.244.0.11:9153
Session Affinity: None
Events: <none>
[kubeadm@server1 mainfest]$ dig myservice.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-72.el7 <<>> myservice.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19011
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myservice.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myservice.default.svc.cluster.local. 30 IN A 10.244.2.70
myservice.default.svc.cluster.local. 30 IN A 10.244.1.48
myservice.default.svc.cluster.local. 30 IN A 10.244.1.47
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Fri Jun 26 15:01:23 CST 2020
;; MSG SIZE rcvd: 217
[kubeadm@server1 mainfest]$ dig myservice.default.svc.cluster.local @10.244.0.11
; <<>> DiG 9.9.4-RedHat-9.9.4-72.el7 <<>> myservice.default.svc.cluster.local @10.244.0.11
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16525
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myservice.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myservice.default.svc.cluster.local. 30 IN A 10.244.1.48
myservice.default.svc.cluster.local. 30 IN A 10.244.2.70
myservice.default.svc.cluster.local. 30 IN A 10.244.1.47
;; Query time: 0 msec
;; SERVER: 10.244.0.11#53(10.244.0.11)
;; WHEN: Fri Jun 26 15:02:57 CST 2020
;; MSG SIZE rcvd: 217
[kubeadm@server1 mainfest]$ kubectl delete pod --all
pod "deployment-example-67764dd8bd-p5qnr" deleted
pod "deployment-example-67764dd8bd-pvkc6" deleted
pod "deployment-example-67764dd8bd-smr7c" deleted
[kubeadm@server1 mainfest]$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-example-67764dd8bd-6pwdw 1/1 Running 0 7s 10.244.2.74 server3 <none> <none>
deployment-example-67764dd8bd-jl7nl 1/1 Running 0 7s 10.244.1.49 server2 <none> <none>
deployment-example-67764dd8bd-zvd28 1/1 Running 0 7s 10.244.2.73 server3 <none> <none>
[kubeadm@server1 mainfest]$ dig myservice.default.svc.cluster.local @10.244.0.11
; <<>> DiG 9.9.4-RedHat-9.9.4-72.el7 <<>> myservice.default.svc.cluster.local @10.244.0.11
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48989
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myservice.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myservice.default.svc.cluster.local. 30 IN A 10.244.1.49
myservice.default.svc.cluster.local. 30 IN A 10.244.2.73
myservice.default.svc.cluster.local. 30 IN A 10.244.2.74
;; Query time: 0 msec
;; SERVER: 10.244.0.11#53(10.244.0.11)
;; WHEN: Fri Jun 26 15:14:37 CST 2020
;; MSG SIZE rcvd: 217
从外部访问 Service 的第二种方式,适用于公有云上的 Kubernetes 服务。这时候,你可以指定一个 LoadBalancer 类型的 Service。
在service提交后,Kubernetes就会调用 CloudProvider 在公有云上创建一个负载均衡服务,并且把被代理的 Pod 的 IP地址配置给负载均衡服务做后端。
[kubeadm@server1 mainfest]$ vim service.yml
[kubeadm@server1 mainfest]$ cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
type: LoadBalancer
[kubeadm@server1 mainfest]$ kubectl apply -f service.yml
service/myservice created
[kubeadm@server1 mainfest]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d11h
myservice LoadBalancer 10.104.173.144 <pending> 80:32646/TCP 5s
从外部访问的第三种方式叫做ExternalName。适用于集群内部容器访问外部资源
[kubeadm@server1 mainfest]$ vim service.yml
[kubeadm@server1 mainfest]$ cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
type: ExternalName
externalName: www.baidu.com
[kubeadm@server1 mainfest]$ kubectl apply -f service.yml
service/myservice created
[kubeadm@server1 mainfest]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d11h
myservice ExternalName <none> www.baidu.com 80/TCP 4s
[kubeadm@server1 mainfest]$ dig myservice.default.svc.cluster.local @10.244.0.11
; <<>> DiG 9.9.4-RedHat-9.9.4-72.el7 <<>> myservice.default.svc.cluster.local @10.244.0.11
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44555
;; flags: qr aa rd; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myservice.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myservice.default.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 61.135.169.121
www.a.shifen.com. 30 IN A 61.135.169.125
;; Query time: 75 msec
;; SERVER: 10.244.0.11#53(10.244.0.11)
;; WHEN: Fri Jun 26 15:28:43 CST 2020
;; MSG SIZE rcvd: 233
[kubeadm@server1 mainfest]$ vim service.yml
[kubeadm@server1 mainfest]$ cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
externalIPs:
- 172.25.1.100
[kubeadm@server1 mainfest]$ kubectl apply -f service.yml
service/myservice created
[kubeadm@server1 mainfest]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d11h
myservice ClusterIP 10.105.122.218 172.25.1.100 80/TCP 4s
[kubeadm@server1 mainfest]$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:bb:3e:1d brd ff:ff:ff:ff:ff:ff
inet 192.168.43.11/24 brd 192.168.43.255 scope global ens33
valid_lft forever preferred_lft forever
inet 172.25.1.1/24 brd 172.25.1.255 scope global ens33
valid_lft forever preferred_lft forever
inet 192.168.3.201/24 brd 192.168.3.255 scope global ens33
valid_lft forever preferred_lft forever
inet6 2408:84fb:1:1209:20c:29ff:febb:3e1d/64 scope global mngtmpaddr dynamic
valid_lft 3317sec preferred_lft 3317sec
inet6 fe80::20c:29ff:febb:3e1d/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:da:68:ae:81 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether d2:fd:d6:3b:73:d3 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::d0fd:d6ff:fe3b:73d3/64 scope link
valid_lft forever preferred_lft forever
5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 4a:a1:c4:bb:75:78 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::48a1:c4ff:febb:7578/64 scope link
valid_lft forever preferred_lft forever
6: veth3fc18a62@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
link/ether a2:7d:16:36:60:6a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::a07d:16ff:fe36:606a/64 scope link
valid_lft forever preferred_lft forever
7: veth99a251bf@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
link/ether 86:20:80:1b:26:22 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::8420:80ff:fe1b:2622/64 scope link
valid_lft forever preferred_lft forever
8: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether d2:d9:2f:45:a0:05 brd ff:ff:ff:ff:ff:ff
9: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether 9a:26:f3:9b:57:38 brd ff:ff:ff:ff:ff:ff
inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.105.122.218/32 brd 10.105.122.218 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 172.25.1.100/32 brd 172.25.1.100 scope global kube-ipvs0
valid_lft forever preferred_lft forever
[kubeadm@server1 mainfest]$ logout
[root@server1 ~]# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.25.1.100:80 rr
-> 10.244.1.49:80 Masq 1 0 0
-> 10.244.2.73:80 Masq 1 0 0
-> 10.244.2.74:80 Masq 1 0 0
TCP 10.96.0.1:443 rr
-> 192.168.43.11:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 10.244.0.10:53 Masq 1 0 0
-> 10.244.0.11:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 10.244.0.10:9153 Masq 1 0 0
-> 10.244.0.11:9153 Masq 1 0 0
TCP 10.105.122.218:80 rr
-> 10.244.1.49:80 Masq 1 0 0
-> 10.244.2.73:80 Masq 1 0 0
-> 10.244.2.74:80 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 10.244.0.10:53 Masq 1 0 0
-> 10.244.0.11:53 Masq 1 0 0