一、service的简介
1.1Service可以看作是一组提供相同服务的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 设定)。
1.2外部主机访问节点
[root@server2 manifest]# kubectl apply -f service.yml
[root@server2 manifest]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d18h
myservice ClusterIP 10.102.194.46 <none> 80/TCP 3h8m
[root@server2 manifest]# kubectl apply -f pod2.yml
deployment.apps/deployment-example created
[root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
deployment-example-67764dd8bd-6bkml 1/1 Running 0 3s
deployment-example-67764dd8bd-ffpxw 1/1 Running 0 3s
deployment-example-67764dd8bd-nprg7 1/1 Running 0 3s
更改myservice文件ClusterIP改为NodePort:将Service通过指定的Node上的端口暴露给外部,访问任意一个
[root@server2 manifest]# kubectl edit svc myservice
service/myservice edited
暴露端口30580
[root@server2 manifest]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d18h
myservice NodePort 10.102.194.46 <none> 80:30580/TCP 3h20m
在其他节点上查看
[root@server4 ~]# netstat -tnpl | grep 30580
tcp 0 0 0.0.0.0:30580 0.0.0.0:* LISTEN 17972/kube-proxy
[root@server3 ~]# netstat -tnpl | grep 30580
tcp 0 0 0.0.0.0:30580 0.0.0.0:* LISTEN 18045/kube-proxy
在不属于集群的主机上访问
**二、Service 是由 kube-proxy 组件,加上 iptables 来共同实现的.
kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源。
IPVS模式的service,可以使K8s集群支持更多量级的Pod。
开启kube-proxy的ipvs模式:
yum install -y ipvsadm //所有节点安装
root@server2 manifest]# kubectl edit cm kube-proxy -n kube-system //修改IPVS模式
更新kube-proxy pod。
mode: "ipvs" • $ kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'
在其他节点上创建这个VIP
kube-proxy通过linux的IPVS模块,以rr轮询方式调度service中的Pod
IPVS模式下,kube-proxy会在service创建后,在宿主机上添加一个虚拟网卡:
kube-ipvs0,并分配service IP
查卡每个节点都有:10.98.46.168 IP
[root@server2 manifest]# ip a | grep kube-ipvs0
9: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0
inet 10.98.46.168/32 brd 10.98.46.168 scope global kube-ipvs0
[root@server3 ~]# ip a | grep kube-ipvs0
58: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0
inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
inet 10.98.46.168/32 brd 10.98.46.168 scope global kube-ipvs0
[root@server4 ~]# ip a | grep kube-ipvs0
41: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0
inet 10.98.46.168/32 brd 10.98.46.168 scope global kube-ipvs0
在server3上查看
三、Kubernetes 提供了一个 DNS 插件 Service
通过域名解析IP;也可以访问域名实现负载均衡(IP地址会变化,但是域名不会改变)
当重新创建pod后,VIP在其他节点都是实时同步的
四、Headless Service “无头服务”
• Headless Service不需要分配一个VIP,而是直接以DNS记录的方式解析出被代理
Pod的IP地址。
• 域名格式:$(servicename).$(namespace).svc.cluster.local
[root@server2 manifest]# yum install -y bind-utils
[root@server2 manifest]# dig myservice.default.svc.cluster.local @10.96.0.10
[root@server2 manifest]# cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
clusterIP: None 不显示IP
[root@server2 manifest]# 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 5d22h
10.96.0.10此地址是集群自动分配的servicer地址,此地址后面有两个pod如下图所示
使用dig命名也可以直接访问下面两个地址解析到容器地址——实现负载均衡
查看service后端的两个pod地址
[root@server2 manifest]# kubectl describe svc myservice
实现负载均衡
Pod滚动更新后,依然可以解析:
[root@server2 manifest]# vim pod2.yml
五、外部访问service的另外两种方式
5.1、 LoadBalancer 类型的 Service
从外部访问 Service 的第二种方式,适用于公有云上的 Kubernetes 服务。这
时候,你可以指定一个 LoadBalancer 类型的 Service。
[root@server2 manifest]# kubectl delete -f service.yml
service "myservice" deleted
[root@server2 manifest]# cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
type: LoadBalancer 指定一个 LoadBalancer 类型的 Service
[root@server2 manifest]# kubectl apply -f service.yml
service/myservice created
[root@server2 manifest]# kubectl get svc myservice
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myservice LoadBalancer 10.104.101.0 <pending> 80:30509/TCP 16s
流程:用户——访问公有云上的slb——访问后端pod——访问(172.25.254.2/3/4:30509)都可以。
在service提交后,Kubernetes就会调用 CloudProvider 在公有云上为你创建一个负载均衡服务,并且把被代理的 Pod 的 IP地址配置给负载均衡服务做后端。
5.2从外部访问的第三种方式叫做ExternalName
集群内部的pod访问集群之外的资源
[root@server2 manifest]# 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 公司域名
使用dns地址解析
也可以使用后端pod的地址
更改公司名称
[root@server2 manifest]# 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.taobao.com 淘宝
5.3service允许为其分配一个公有IP。
外部主机访问pod
[root@server2 manifest]# vim service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
externalIPs:
- 172.25.254.100 自定义一个外网IP
[root@server2 manifest]# kubectl delete -f service.yml
service "myservice" deleted
[root@server2 manifest]# kubectl apply -f service.yml
service/myservice created
[root@server2 manifest]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d
myservice ClusterIP 10.100.32.158 172.25.254.100 80/TCP 9s
外部主机访问
[root@server1 ~]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
六、不同网段的pod容器跨主机访问
6.1Flannel vxlan模式跨主机通信原理
server2跨主机访问server3
子网分配信息存放位置
[root@server2 manifest]# cd /run/flannel/
[root@server2 flannel]# cat subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
[root@server4 ~]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.2.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
[root@server3 ~]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
每个节点也都会启动一个服务
ps ax | grep flnneld
55573 pts/0 R+ 0:00 grep --color=auto flnneld
查看各个节点的MAC地址
网关10.244.1.0的MAC地址就是server2上flannel1的MAC地址
网关10.244.2.0的MAC地址就是server3上flannel1的MAC地址
flannel包装: 搭载udp数据包到达server4
源地址:server3:10.244.1.86-mac:f6:73:ec:b5:07:9a
目的地:server4:10.244.2.61-mac:1e:0f:e8:fc:db:2a
eth0包装:
源地址:172.25.254.3:00:0c:29:ba:53:cb
目的地:172.25.254.4:00:0c:29:c6:f7:50
到达server4之后解封到达flannel1之后解封再到达cni0。
这样会影响性能导致访问速度不一致
6.2host-gw模式跨主机通信
[root@server2 manifest]# cat service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: myapp
type: NodePort 更改类型
[root@server2 manifest]# cat pod2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
# Unique key of the Deployment instance
name: deployment-example
spec:
# 3 Pods should exist at all times.
replicas: 4
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
# Apply this label to pods and default
# the Deployment label selector to this value
app: myapp
spec:
containers:
- name: myapp
# Run this image
image: myapp:v1
[root@server2 manifest]# kubectl get svc 改为NodePort之后会有一个端口31891
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d2h
myservice NodePort 10.110.8.176 <none> 80:31891/TCP 8m11s
更改通信模式为host-gw模式
查看通信原理:
10.244.1.0访问10.244.2.0:
通过ens33直接转发定向到server4主机IP上没有经过flannel封装
[root@server3 ~]# ip route
default via 192.168.1.1 dev ens33 proto static metric 100
10.244.0.0/24 via 172.25.254.2 dev ens33
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
10.244.2.0/24 via 172.25.254.4 dev ens33
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.25.254.0/24 dev ens33 proto kernel scope link src 172.25.254.3 metric 100
192.168.1.0/24 dev ens33 proto kernel scope link src 192.168.1.33 metric 100
[root@server4 ~]# ip route 同样的原理也是经过ens33网卡出去直接访问主机IP
default via 192.168.1.1 dev ens33 proto static metric 100
10.244.0.0/24 via 172.25.254.2 dev ens33
10.244.1.0/24 via 172.25.254.3 dev ens33
10.244.2.0/24 dev cni0 proto kernel scope link src 10.244.2.1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.25.254.0/24 dev ens33 proto kernel scope link src 172.25.254.4 metric 100
192.168.1.0/24 dev ens33 proto kernel scope link src 192.168.1.44 metric 100
注意:必须二层网络互相连通。大多数情况下二层网络是不通的,都是三层通信
外部主机访问测试
知识点补充:flannel不支持网络策略,只是支持通信。
6.3.1默认情况下vxlan没有打开直连路由,打开Vxlan的直连路由功能
当在同一个网段内使用直连路由,不同网段的使用隧道模式
编辑文件flannel
查看已经更改
[root@server2 manifest]# kubectl describe -n kube-system cm kube-flannel-cfg
[root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
deployment-example-7449b5b68f-55f7q 1/1 Running 0 5m51s
deployment-example-7449b5b68f-6g4fz 1/1 Running 0 5m51s
deployment-example-7449b5b68f-fszzl 1/1 Running 0 5m51s
deployment-example-7449b5b68f-vzrqq 1/1 Running 0 5m51s
[root@server2 manifest]# kubectl apply -f service.yml
service/myservice created
[root@server2 manifest]# kubectl get svc myservice
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myservice NodePort 10.99.43.52 <none> 80:32685/TCP 3s
外部主机测试访问
6.3.2使用caclio模式增加网略策略
获取文件:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#
vim calico.yaml
在文件中查找;下载相应软件包(一共四个),并上传至harbor仓库
默认情况下激活IPIP是基于IP的隧道,隧道可以忽略不同网段的限制。当前实验环境在同一个网段下可以不开启IPIP。
vim calico.yaml
所有节点移走flannel配置文件
cni调用的指令文件
cni主要工作:把新建的容器添加到网络站上和删除。
运行caclio网络插件
使用的是daemonsets方式
[root@server2 ~]# kubectl get daemonsets.apps -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 3 3 0 3 0 kubernetes.io/os=linux 3m12s
kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 18h
查看IPIP隧道模式被禁用
[root@server2 ~]# kubectl describe ippool 使用的是边界网关协议
创建service和pod
外部主机测试访问
6.3.2.1calico自定义IP及开启隧道模式
[root@server2 ~]# vim calico.yaml
[root@server2 ~]# kubectl delete -f calico.yaml 删除
[root@server2 ~]# kubectl apply -f calico.yaml 新建
在节点上查看隧道模式已经开启
7.更改网络插件模式为host-gw模式
移走所有节点的calico文件
cd /etc/cni/net.d/
ls
10-calico.conflist calico-kubeconfig
mv * /mnt/
vim kube-flannel.yml
kubectl delete -f calico.yaml
kubectl apply -f kube-flannel.yml
可以重启所有主机