第六章 Kubernetes进阶之Service与外界连通
1.Pod与Service的关系
Pod出现故障以后Deployment会根据策略重启Pod,但是重启Pod会生成新的IP,需要引入Service概念保证访问正常
Service
- 防止Pod失联
- 定义一组Pod访问策略
- 支持ClusterIP,NodePort以及LoadBanlancer三种类型
- Service的底层主要有Iptables和IPVS两种网络模式
2.Service的定义
Pod与Service的关系
- 通过label-selector相关联
- 通过Service实现Pod的负载均衡(TCP/UDP 4层)
示例service.yaml
#版本 apiVersion: v1 #类型是service kind: Service metadata: name: my-service namespace: default spec: #service默认clusterIP clusterIP: 10.10.10.123 ports: - name: http #service端口 port: 80 protocol: TCP #后端容器端口 targetPort: 80 #标签选择器关联后端的一组Pod selector: run: nginx
创建
kubectl apply -f service.yaml
因为该service标签匹配一个nginx所以如果没有创建nginx的deployment需要创建一个
kubectl run nginx --replicas=3 --image=nginx --port=80
创建以后可以查看有对应标签的pod
# kubectl get pod -l run=nginx NAME READY STATUS RESTARTS AGE nginx-57867cc648-b4rct 1/1 Running 0 9m53s nginx-57867cc648-qgbwl 1/1 Running 0 9m53s nginx-57867cc648-qmbbw 1/1 Running 0 9m53s
查看
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 6d23h my-service ClusterIP 10.10.10.123 <none> 80/TCP 6m25s
其中name为kubernetes的service是k8s安装以后默认的一个service默认IP的service的第一个IP 10.10.10.1端口是443
默认的svc的作用是负载均衡后端master的IP
service需要动态感知后端ip变化可以通过以下命令查看,每个svc管理后端的一组容器
# kubectl get endpoints NAME ENDPOINTS AGE kubernetes 192.168.1.63:6443,192.168.1.64:6443 6d23h my-service 172.17.41.2:80,172.17.55.2:80,172.17.55.4:80 9m59s
在集群内部node例如192.168.1.65可以使用ClusterIP访问
curl 10.10.10.123
PS:这里配置文件指定了clusterIP为10.10.10.123,默认不知道该clusterIP是随机的
apiVersion: v1 kind: Service metadata: labels: run: nginx name: my-service spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx
不能直接应用生效
# kubectl apply -f service.yaml The Service "my-service" is invalid: spec.clusterIP: Invalid value: "": field is immutable
需要删除原svc再应用生效
kubectl delete svc my-service kubectl apply -f service.yaml
查看生成了随机的clusterIP
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 7d5h my-service ClusterIP 10.10.10.196 <none> 80/TCP 29s
3.service类型
service提供四层负载均衡把请求发送到后端的pod,默认规则是轮训
- ClusterIP:默认,分配一个集群内部可以访问的虚拟IP
- NodePort:在每个node上分配一个端口作为外部访问入口
- LoadBanlancer:工作在特定的Cloud Provider上,例如Google,AWS,OpenStack
ClusterIP
分配一个内部集群IP地址,只能在集群内部访问(同Namespace内的Pod),默认ServiceType。 ClusterIP 模式的 Service 为你提供的,就是一个 Pod 的稳定的 IP 地址,即 VIP。
NodePort
分配一个内部集群IP地址,并在每个节点上启用一个端口来暴露服务,可以在集群外部访问。 访问地址:: node端口范围30000-32067
Client→NodeIP:NodePort→ClusterIP:ServicePort→PodIP:containerPort
#版本 apiVersion: v1 #类型是service kind: Service metadata: name: my-service namespace: default spec: type: NodePort ports: - name: http #service端口 port: 80 protocol: TCP #后端容器端口 targetPort: 80 #标签选择器关联后端的一组Pod selector: run: nginx
应用,service定义类型不支持自动更新,需要删除原有的配置再重新应用
kubectl delete -f service.yaml kubectl apply -f service.yaml
查看,类型变成NodePort了并且随机产生一个端口,本次随机端口是46194
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 7d3h my-service NodePort 10.10.10.238 <none> 80:46194/TCP 104s
即使是使NodePort类型也会分配一个ClusterIP在集群内部可以访问,NodePort是供集群外部访问使用的
使用NodePort会在多个node上可以访问,企业应用可以加一台负载均衡负载到一个或多个node
LoadBalancer
分配一个内部集群IP地址,并在每个节点上启用一个端口来暴露服务。 除此之外,Kubernetes会请求底层云平台上的负载均衡器,将每个Node([NodeIP]:[NodePort])作为后端添加进去
4.Service代理模式
底层流量转发与负载均衡实现
- Iptables
- IPVS
service通过部署在node的应用kube-proxy实现
可以通过kube-proxy配置文件查看模式
# cat /opt/kubernetes/cfg/kube-proxy KUBE_PROXY_OPTS="--logtostderr=true \ --v=4 \ --hostname-override=192.168.31.65 \ --cluster-cidr=10.0.0.0/24 \ --proxy-mode=ipvs \ --masquerade-all=true \ --kubeconfig=/opt/kubernetes/cfg/kube-proxy.kubeconfig"
查看Iptables的转发方式
删除其中一台node192.168.1.66的ipvs配置
KUBE_PROXY_OPTS="--logtostderr=true \ --v=4 \ --hostname-override=192.168.31.66 \ --cluster-cidr=10.0.0.0/24 \ --kubeconfig=/opt/kubernetes/cfg/kube-proxy.kubeconfig"
重启kube-proxy组件
systemctl restart kube-proxy
查看service对应的Iptables规则,过滤的IP为上面service的ip通过IP地址找到对应的规则
iptables-save|grep 10.10.10.238
查看对应的规则
iptables-save|grep KUBE-SVC-I37Z43XJW6BD4TLV
继续查看对应规则
iptables-save|grep KUBE-SEP-ETBSYW4QFKX7DP4Z
Iptables的问题
1,创建很多Iptables(更新,非增量式)
2,Iptables规则是从上至下逐条匹配(规则多了,匹配延时大)
无法大规模应用,救世主IPVS
查看IPVS的工作方式
LVS基于IPVS内核调度模块实现的负载均衡
查看ipvs规则,需要kube-proxy设置ipvs工作模式才能查看到
ipvsadm -ln
如果没有这个命令安装即可
yum -y install ipvsadm
会生成虚拟网卡kube-ipvs0,所有的ClusterIP会绑定在该网卡上
默认使用rr轮训负载均衡策略,因为工作在内核态所以速度快
对比
Iptables
- 灵活功能强大(可以在数据包不同阶段对包进行操作)
- 规则遍历匹配和更新,呈线性时延
IPVS
- 工作在内核态,有更好的性能
- 调度算法丰富:rr,wrr,lc,wlc,ip hash
设置调度算法,一般使用默认即可无需配置
Iptables和ipvs示意图
5.DNS
service创建以后在集群内部可以通过cluserIP访问,但是涉及迁移时IP会发生变化,需要部署内部DNS服务
DNS服务监视Kubernetes API,为每一个Service创建DNS记录用于域名解析
DNS配置文件官方下载地址https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns
下载以后需要修改以下几处
修改后
修改镜像
修改后
修改资源限制
修改后
修改集群DNS地址
修改后
应用
kubectl apply -f coredns.yaml
排错:应用以后pod显示pending状态
kubectl get pod -n kube-system kubectl describe pod coredns-65589b968c-s7m28 -n kube-system
错误信息为
nodes are available: 2 node(s) didn't match node selector
标签不匹配
查看系统标签
kubectl get node --show-labels NAME STATUS ROLES AGE VERSION LABELS 192.168.1.65 Ready <none> 10d v1.13.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=192.168.1.65 192.168.1.66 Ready <none> 10d v1.13.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=192.168.1.66
查看配置文件发现确实不匹配实际beta.kubernetes.io/os=linux 配置文件为beta.kubernetes.io/os=linux
修改或者注释这两行即可
部署DNS以后测试
kubectl run -it --image=busybox:1.28.4 --rm --restart=Never sh
PS:镜像需要使用1.28.4版本
解析 nslookup加svc名
nslookup kubernetes
默认的service是在默认的命名空间default下
# kubectl get svc -n default NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 9d my-service NodePort 10.10.10.196 <none> 80:37159/TCP 2d18h
创建一个容器使用命名空间为kube-system
kubectl run -it --image=busybox:1.28.4 --rm --restart=Never sh -n kube-system
因为该容器的命名空间是kube-system所以无法解析默认命名空间default下的service需要在域名后加命名空间名称访问
# nslookup kubernetes Server: 10.10.10.2 Address 1: 10.10.10.2 coredns.kube-system.svc.cluster.local nslookup: can't resolve 'kubernetes' / # nslookup kubernetes.default Server: 10.10.10.2 Address 1: 10.10.10.2 coredns.kube-system.svc.cluster.local Name: kubernetes.default Address 1: 10.10.10.1 kubernetes.default.svc.cluster.local
排错:nslookup可以正确解析外网DNS例如www.baidu.com但是无法解析内网DNS
查看core日志
kubectl logs -n kube-system coredns-7777dd7b8c-7dv26
E0309 03:15:43.061842 1 reflector.go:205] github.com/coredns/coredns/plugin/kubernetes/controller.go:318: Failed to list *v1.Namespace: Get https://10.10.10.1:443/api/v1/namespaces?limit=500&resourceVersion=0: x509: certificate is valid for 10.0.0.1, 127.0.0.1, 192.168.1.60, 192.168.1.61, 192.168.1.62, 192.168.1.63, 192.168.1.64, 192.168.1.65, 192.168.1.66, not 10.10.10.1
原因是因为在创建api证书的时候填写hosts信息错误
修改IP地址以后重新创建证书重启api即可