Service是什么与Service存在的意义

Service引入主要是解决Pod的动态变化,提供统一访问入口:

  • 防止Pod失联,准备找到提供同一个服务的Pod(服务发现)

  • 定义一组Pod的访问策略(负载均衡)

Pod与Service的关系

  • Service通过标签关联一组Pod

  • Service使用iptables或者ipvs为一组Pod提供负载均衡能力

Service定义与创建

vim service.yaml

apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
type: ClusterIP # 服务类型
ports:
- port: 80 # Service端口
protocol: TCP # 协议
targetPort: 80 # 容器端口
selector:
app: web # 指定关联Pod的标签

#创建service: 
kubectl apply -f service.yaml
#查看service:
kubectl get service
#查看service关联一组pod的IP
kubectl get endpoints

Service三种类型

  • ClusterIP:集群内部使用

  • NodePort:对外暴露应用(集群外)

  • LoadBalancer:对外暴露应用,适用公有云

ClusterIP:默认,分配一个稳定的IP地址,即VIP,只能在集群内部访问。

vim service.yaml

apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
type: ClusterIP # 服务类型
ports:
- port: 80 # Service端口
protocol: TCP # 协议
targetPort: 80 # 容器端口
selector:
app: web # 指定关联Pod的标签

访问

kubectl apply -f service.yaml 
kubectl get svc
curl 10.107.214.48 #访问集群内部暴露的service的IP及端口

NodePort:在每个节点上启用一个端口来暴露服务,可以在集群 外部访问。也会分配一个稳定内部集群IP地址。 访问地址:<任意NodeIP>: 端口范围:30000-32767

vim service-node.yaml

apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
type: NodePort # 服务类型
ports:
- port: 80 # Service端口
protocol: TCP # 协议
targetPort: 80 # 容器端口
nodePort: 30009 #nodeport暴露的端口
selector:
app: web # 指定关联Pod的标签

访问

kubectl apply -f service-node.yaml 
kubectl get svc
http://192.168.1.12:30009/ #访问集群外部节点IP以及暴露的端口

NodePort:会在每台Node上监听端口接收用户流量,在实际情 况下,对用户暴露的只会有一个IP和端口,那这么多台Node该使 用哪台让用户访问呢?

这时就需要前面加一个公网负载均衡器为项目提供统一访问入口了。

负载均衡器:

  • 开源:nginx、lvs、haproxy

  • 公有云:SLB

upstream demo {
server 192.168.0.11:30008;
server 192.168.0.12:30008;
}
upstream demo2 {
server 192.168.0.13:30009;
server 192.168.0.14:30009;
}
upstream demo3 {
server 192.168.0.15:30010;
server 192.168.0.16:30010;
}
server {
server_name a.xxx.com;
location / {
proxy_pass http://demo;
}
}
server {
server_name b.xxx.com;
location / {
proxy_pass http://demo2;
}
}
server {
server_name c.xxx.com;
location / {
proxy_pass http://demo3;
}
}

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

Service代理模式

Service的底层实现主要有iptables和ipvs二种网络模式,决定了如何转发流量

基于iptables实现负载均衡的一个过程

1、在浏览器访问 http://192.168.0.11:30009/

2.数据包经过iptables规则匹配,重定向到另一个链KUBE-SVC-LOLE4ISW44XBNF3G

-A KUBE-NODEPORTS -p tcp -m comment --comment “default/web” -m tcp --dport 30009 -j KUBE-SVC-LOLE4ISW44XBNF3G

3.一组规则,有几个pod就会创建几条规则,这里实现了负载均衡 (概率1/3,1/2,1)

-A KUBE-SVC-LOLE4ISW44XBNF3G -m comment --comment “default/web” -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-PXRBKXV7I65SLLDB

-A KUBE-SVC-LOLE4ISW44XBNF3G -m comment --comment “default/web” -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-4MXWCRSI3HRHILKZ

-A KUBE-SVC-LOLE4ISW44XBNF3G -m comment --comment “default/web” -j KUBE-SEP-ODUGDMBPYLOH457E

4.使用DNAT转发到具体的pod

-A KUBE-SEP-PXRBKXV7I65SLLDB -p tcp -m comment --comment “default/web” -m tcp -j DNAT --to-destination 10.244.169.133:80

-A KUBE-SEP-4MXWCRSI3HRHILKZ -p tcp -m comment --comment “default/web” -m tcp -j DNAT --to-destination 10.244.36.67:80

-A KUBE-SEP-ODUGDMBPYLOH457E -p tcp -m comment --comment “default/web” -m tcp -j DNAT --to-destination 10.244.36.68:80

针对ClusterIP实现的转发,后面与nodeport一样,回到了上面的第三步

-A KUBE-SERVICES -d 10.109.90.58/32 -p tcp -m comment --comment “default/web cluster IP” -m tcp --dport 80 -j KUBE-SVC-LOLE4ISW44XBNF3G

kubeadm方式修改ipvs模式:

kubectl edit configmaps kube-proxy -n kube-system 
#搜索mode,添加ipvs,修改完保存
mode: "ipvs"
#删除node1节点proxy的pod,重新生成新的pod
kubectl delete pod kube-proxy-lzjgg -n kube-system
kubectl get pod -o wide -n kube-system
kubectl logs kube-proxy-hnw5p -n kube-system

注: 1、kube-proxy配置文件以configmap方式存储 2、如果让所有节点生效,需要重建所有节点kube-proxy pod

在node1节点上安装ipvsadm工具

yum -y install ipvsadm
ipvsadm -L -n

ip a

二进制方式修改ipvs模式:

vi kube-proxy-config.yml

mode: ipvs

ipvs:

scheduler: "rr“

systemctl restart kube-proxy

注:参考不同资料,文件名可能不同

流程包流程:客户端 ->NodePort/ClusterIP(iptables/Ipvs负载均衡规则) -> 分布在各节点Pod

查看负载均衡规则:

  • iptables模式
iptables-save |grep 
  • ipvs模式
ipvsadm -L -n

当一个客户端访问service的时候,经过iptables/ipvs进行负载均衡,负载到后端的pod上,iptables/ipvs的规则是由kube-proxy去创建的。

当出现问题的时候,应该先检查的service的配置的是不是对的(标签端口等等),再检查kube-proxy是不是正常的,有没有创建对应的iptables/ipvs规则。

Service DNS名称

CoreDNS:是一个DNS服务器,Kubernetes默认采用,以Pod部署在集群中,CoreDNS服 务监视Kubernetes API,为每一个Service创建DNS记录用于域名解析。

CoreDNS YAML文件:

https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns/coredns

ClusterIP A记录格式:..svc.cluster.local

示例:my-svc.my-namespace.svc.cluster.local

当我们在pod内做nslookup(dns)解析时,它会请求coredns pod,coredns里面存放了从k8smaster中获取的service对应的dns记录,就会帮你解析成对应service的IP。

Iptables VS IPVS

Iptables:

  • 灵活,功能强大

  • 规则遍历匹配和更新,呈线性时延

IPVS:

  • 工作在内核态,有更好的性能

  • 调度算法丰富:rr,wrr,lc,wlc,ip hash…

生产环境建议使用IPVS

1、创建一个deployment 副本数 3,然后滚动更新镜像版本,并记录这个更新记录,最后再回滚到上一个版本

vim web-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
namespace: default
spec:
replicas: 3 # Pod副本预期数量
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web # Pod副本的标签
spec:
containers:
- name: web
image: nginx:1.15
kubectl apply -f web-deployment.yaml 
#通过命令更新镜像,指定--record参数会将这条命令记录到历史版本记录中,方便回滚到对应的版本
kubectl set image deploy web-deployment web=nginx:1.16 --record
kubectl get pod -o wide
curl -I10.244.169.140 #验证是否是nginx1.16版本
kubectl rollout history deployment web-deployment #查看历史版本记录
kubectl rollout undo deployment web-deployment #默认回滚到上一个版本
kubectl get pod -o wide #查看pod的IP
curl -I 10.244.36.76 #验证是否回滚到nginx1.15版本

2、给一个应用扩容副本数为3

kubectl scale deployment web-deployment --replicas=6
kubectl get pod

3、创建一个pod,其中运行着nginx、redis、memcached 3个容器

vim nginx-redis-memcached.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-redis-memcached
namespace: default
spec:
replicas: 1 # Pod副本预期数量
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web # Pod副本的标签
spec:
containers:
- name: nginx
image: nginx
containers:
- name: redis
image: redis
containers:
- name: memcached
image: memcached

验证查看

kubectl apply -f nginx-redis-memcached.yaml 
kubectl get pod
kubectl exec -it web-deployment-7c6bf5fdf8-8m2gm nginx -- bash
kubectl exec -it web-deployment-7c6bf5fdf8-8m2gm redis -- bash
kubectl exec -it web-deployment-7c6bf5fdf8-8m2gm memcached -- bash

4、给一个pod创建service,并可以通过ClusterIP/NodePort访问

vim service-node.yaml

apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
type: NodePort # 服务类型
ports:
- port: 80 # Service端口
protocol: TCP # 协议
targetPort: 80 # 容器端口
nodePort: 30009 #nodeport暴露的端口
selector:
app: web # 指定关联Pod的标签

验证查看

kubectl apply -f service-node.yaml 
kubectl get svc
curl -I 10.105.40.240 #访问集群内部service IP
http://192.168.0.12:30009/ #访问任意node节点IP加30009端口

5、创建deployment和service,使用busybox容器nslookup解析service

kubectl run -it dns-test --image=busybox -- sh

注:自由发挥,实现需求即可