使用 kubernetes 集群运行工作负载时,由于Pod经常处于用后即焚状态,Pod经常被重新生成,因此Pod对应的IP地址也会经常变化,导致无法直接访问Pod提供的服务,Kubernetes中使用了Service来解决这一问题,即在Pod前面使用Service对Pod进行代理,无论Pod怎样变化 ,只要有Label,就可以让Service能够联系上Pod,把PodIP地址添加到Service对应的端点列表(Endpoints)实现对Pod IP跟踪,进而实现通过Service访问Pod。
kubernetes集群中有三层网络,一类是真实存在的,例如Node Network、Pod Network,提供真实IP地址;一类是虚拟的,例如Cluster Network或Service Network,提供虚拟IP地址,不会出现在接口上,仅会出现在Service当中
kube-proxy始终watch(监控)kube-apiserver上关于Service相关的资源变动状态,一旦获取相关信息kube-proxy都要把相关信息转化为当前节点之上的,能够实现Service资源调度到特定Pod之上的规则,进而实现访问Service就能够获取Pod所提供的服务
kube-proxy三种代理模式:UserSpace模式、iptables模式、ipvs模式
userspace 模式是 kube-proxy 使用的第一代模式,该模式在 kubernetes v1.0 版本开始支持使用。
userspace 模式的实现原理:
kube-proxy 会为每个 Service 随机监听一个端口(proxy port),并增加一条 iptables 规则。所以通过 ClusterIP:Port 访问 Service 的报文都 redirect 到 proxy port,kube-proxy 从它监听的 proxy port 收到报文以后,走 round robin(默认) 或是 session affinity(会话亲和力,即同一 client IP 都走同一链路给同一 pod 服务),分发给对应的 pod。
由于 userspace 模式会造成所有报文都走一遍用户态(也就是 Service 请求会先从用户空间进入内核 iptables,然后再回到用户空间,由 kube-proxy 完成后端 Endpoints 的选择和代理工作),需要在内核空间和用户空间转换,流量从用户空间进出内核会带来性能损耗,所以这种模式效率低、性能不高,不推荐使用。
iptables 模式是 kube-proxy 使用的第二代模式,该模式在 kubernetes v1.1 版本开始支持,从 v1.2 版本开始成为 kube-proxy 的默认模式。
iptables 模式的负载均衡模式是通过底层 netfilter/iptables 规则来实现的,通过 Informer 机制 Watch 接口实时跟踪 Service 和 Endpoint 的变更事件,并触发对 iptables 规则的同步更新。
iptables 模式的实现原理图示如下:
通过图示我们可以发现在 iptables 模式下,kube-proxy 只是作为 controller,而不是 server,真正服务的是内核的 netfilter,体现在用户态的是 iptables。所以整体的效率会比 userspace 模式高。
ipvs 模式被 kube-proxy 采纳为第三代模式,模式在 kubernetes v1.8 版本开始引入,在 v1.9 版本中处于 beta 阶段,在 v1.11 版本中正式开始使用。
ipvs(IP Virtual Server) 实现了传输层负载均衡,也就是 4 层交换,作为 Linux 内核的一部分。ipvs
运行在主机上,在真实服务器前充当负载均衡器。ipvs 可以将基于 TCP 和 UDP 的服务请求转发到真实服务器上,并使真实服务器上的服务在单个 IP 地址上显示为虚拟服务。
ipvs 模式的实现原理图示如下:
ipvs 和 iptables 都是基于 netfilter 的,那么 ipvs 模式有哪些更好的性能呢?
ipvs 依赖于 iptables。ipvs 会使用 iptables 进行包过滤、airpin-masquerade tricks(地址伪装)、SNAT 等功能,但是使用的是 iptables 的扩展 ipset,并不是直接调用 iptables 来生成规则链。通过 ipset 来存储需要 DROP 或 masquerade 的流量的源或目标地址,用于确保 iptables 规则的数量是恒定的,这样我们就不需要关心有多少 Service 或是 Pod 了。
使用 ipset 相较于 iptables 有什么优点呢?iptables 是线性的数据结构,而 ipset 引入了带索引的数据结构,当规则很多的时候,ipset 依然可以很高效的查找和匹配。我们可以将 ipset 简单理解为一个 IP(段) 的集合,这个集合的内容可以是 IP 地址、IP 网段、端口等,iptables 可以直接添加规则对这个“可变的集合进行操作”,这样就可以大大减少 iptables 规则的数量,从而减少性能损耗。
举一个例子,如果我们要禁止成千上万个 IP 访问我们的服务器,如果使用 iptables 就需要一条一条的添加规则,这样会在 iptables 中生成大量的规则;如果用 ipset 就只需要将相关的 IP 地址(网段)加入到 ipset 集合中,然后只需要设置少量的 iptables 规则就可以实现这个目标。
下面的表格是 ipvs 模式下维护的 ipset 表集合:
设置名称 | 成员 | 用法 |
---|---|---|
KUBE-CLUSTER-IP | 所有服务 IP + 端口 | 在 masquerade-all=true 或 clusterCIDR 指定的情况下对 Service Cluster IP 地址进行伪装,解决数据包欺骗问题 |
KUBE-LOOP-BACK | 所有服务 IP + 端口 + IP | 解决数据包欺骗问题 |
KUBE-EXTERNAL-IP | 服务外部 IP + 端口 | 将数据包伪装成 Service 的外部 IP 地址 |
KUBE-LOAD-BALANCER | 负载均衡器入口 IP + 端口 | 将数据包伪装成 Load Balancer 类型的 Service |
KUBE-LOAD-BALANCER-LOCAL | 负载均衡器入口 IP + 端口 以及externalTrafficPolicy=local |
接受数据包到 Load Balancer externalTrafficPolicy=local |
KUBE-LOAD-BALANCER-FW | 负载均衡器入口 IP + 端口 以及loadBalancerSourceRanges |
使用指定的 loadBalancerSourceRanges 丢弃 Load Balancer 类型 Service 的数据包 |
KUBE-LOAD-BALANCER-SOURCE-CIDR | 负载均衡器入口 IP + 端口 + 源 CIDR | 接受 Load Balancer 类型 Service 的数据包,并指定 loadBalancerSourceRanges |
KUBE-NODE-PORT-TCP | NodePort 类型服务 TCP 端口 | 将数据包伪装成 NodePort(TCP) |
KUBE-NODE-PORT-LOCAL-TCP | NodePort 类型服务 TCP 端口,带有externalTrafficPolicy=local |
接受数据包到 NodePort 服务,使用 externalTrafficPolicy=local |
KUBE-NODE-PORT-UDP | NodePort 类型服务 UDP 端口 | 将数据包伪装成 NodePort(UDP) |
KUBE-NODE-PORT-LOCAL-UDP | NodePort 类型服务 UDP 端口,使用externalTrafficPolicy=local |
接受数据包到 NodePort 服务,使用 externalTrafficPolicy=local |
iptables
ipvs
使用iptables与ipvs时机
Service类型决定了访问Service的方法
ClusterIP
NodePort
LoadBalancer
ExternalName
port 访问service使用的端口
targetPort Pod中容器端口
nodePort 通过Node实现外网用户访问k8s集群内service (30000-32767)
Service的创建在工作中有两种方式,一是命令行创建,二是通过资源清单文件YAML文件创建。
ClusterIP根据是否生成ClusterIP又可分为普通Service和Headless Service
Service两类:
为Kubernetes的Service分配一个集群内部可访问的固定虚拟IP(Cluster IP), 实现集群内的访问。
该服务不会分配Cluster IP, 也不通过kube-proxy做反向代理和负载均衡。而是通过DNS提供稳定的网络ID来访问,DNS会将headless service的后端直接解析为pod IP列表。
[root@k8s-master01 ~]# cat 01_create_deployment_app_nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-server1
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: c1
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
[root@k8s-master01 ~]# kubectl apply -f 01_create_deployment_app_nginx.yaml
[root@k8s-master01 ~]# kubectl get deployment.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-server1 2/2 2 2 13s
命令创建service
[root@k8s-master01 ~]# kubectl expose deployment.apps nginx-server1 --type=ClusterIP --target-port=80 --port=80
service/nginx-server1 exposed
说明
expose 创建service
deployment.apps 控制器类型
nginx-server1 应用名称,也是service名称
--type=ClusterIP 指定service类型
--target-port=80 指定Pod中容器端口
--port=80 指定service端口
[root@k8s-master01 ~]# cat 02_create_deployment_app_nginx_with_service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-server1
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-smart
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: nginx
[root@k8s-master01 ~]# kubectl apply -f 02_create_deployment_app_nginx_with_service.yaml
查看service
[root@k8s-master01 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 4d15h
nginx-svc ClusterIP 10.101.153.50 80/TCP 3s
查看endpoints
[root@k8s-master01 ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.122.30:6443 4d15h
nginx-svc 172.16.189.74:80,172.16.235.150:80 8s
查看Pod
[root@k8s-master01 ~]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-server1-77d4c485d8-gsrmq 1/1 Running 0 12s
nginx-server1-77d4c485d8-mmc52 1/1 Running 0 12s
[root@k8s-master01 ~]# curl http://10.101.153.50:80
<!DOCTYPE html>
Welcome to nginx!</title>
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
[ root@bbp:/ ]$ exit
Session ended, resume using 'kubectl attach bbp -c bbp -i -t' command when the pod is running
[root@k8s-master01 ~]# cat 05_create_nodeport_service_app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-app
labels:
app: nginx-app
spec:
replicas: 2
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- name: c1
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-app
spec:
type: NodePort
selector:
app: nginx-app
ports:
- protocol: TCP
nodePort: 30001
port: 8060
targetPort: 80
[root@k8s-master01 ~]# kubectl apply -f 05_create_nodeport_service_app.yaml
deployment.apps/nginx-app created
service/nginx-app created
[root@k8s-master01 ~]# kubectl get deployment.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-app 2/2 2 2 26s
[root@k8s-master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 2d22h
nginx-app NodePort 10.104.157.20 8060:30001/TCP 36s
[root@k8s-master01 ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.122.10:6443 2d22h
nginx-app 172.16.1.24:80,172.16.2.20:80 2m10s
[root@master01 ~]# ss -anput | grep ":30001"
tcp LISTEN 0 128 :::30001 :::* users:(("kube-proxy",pid=5826,fd=9))
[root@worker01 ~]# ss -anput | grep ":30001"
tcp LISTEN 0 128 :::30001 :::* users:(("kube-proxy",pid=4937,fd=11))
[root@worker02 ~]# ss -anput | grep ":30001"
tcp LISTEN 0 128 :::30001 :::* users:(("kube-proxy",pid=5253,fd=11))
[root@k8s-master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-app-ffd5ccc78-cnwbx 1/1 Running 0 8m59s
nginx-app-ffd5ccc78-mz77g 1/1 Running 0 8m59s
[root@k8s-master01 ~]# kubectl exec -it nginx-app-ffd5ccc78-cnwbx -- bash
root@nginx-app-ffd5ccc78-cnwbx:/# echo "nginx-app-1" > /usr/share/nginx/html/index.html
root@nginx-app-ffd5ccc78-cnwbx:/# exit
[root@k8s-master01 ~]# kubectl exec -it nginx-app-ffd5ccc78-mz77g -- bash
root@nginx-app-ffd5ccc78-mz77g:/# echo "nginx-app-2" > /usr/share/nginx/html/index.html
root@nginx-app-ffd5ccc78-mz77g:/# exit
[root@ha1 ~]# curl http://192.168.10.101:30001
nginx-app-2
[root@ha1 ~]# curl http://192.168.10.102:30001
nginx-app-1
[root@ha1 ~]# curl http://192.168.10.103:30001
nginx-app-1
[root@ha1 ~]# curl http://192.168.10.104:30001
nginx-app-2
MetalLB可以为kubernetes集群中的Service提供网络负载均衡功能。
MetalLB两大功能为:
参考网址: https://metallb.universe.tf/installation/
资源清单文件下载:
# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml
[root@k8s-master01 metallb]# cat metallb-conf.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.10.100-192.168.10.120
集群节点服务器IP同一段。
在master01节点应用资源清单文件
[root@k8s-master01 ~]# kubectl apply -f metallb-conf.yaml
验证配置
# kubectl describe configmap config -n metallb-system
Name: config
Namespace: metallb-system
Labels:
Annotations:
Data
====
config:
----
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.10.100-192.168.10.120
Events:
创建Deployment控制器类型应用nginx-metallb及service,service类型为LoadBalancer
[root@k8s-master01 ~]# vim 02_nginx-metabllb.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-metallb
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-metallb1
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-metallb
spec:
ports:
- port: 8090
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
[root@k8s-master01 ~]# kubectl apply -f nginx.yaml
[root@k8s-master01 ~]# kubectl get ns
NAME STATUS AGE
default Active 16d
kube-node-lease Active 16d
kube-public Active 16d
kube-system Active 16d
kubernetes-dashboard Active 13d
metallb-system Active 130m
test1 Active 12d
[root@k8s-master01 ~]# kubectl get pods -n metallb-system
NAME READY STATUS RESTARTS AGE
controller-64f8f944d-qdf8m 1/1 Running 0 110m
speaker-cwzq7 1/1 Running 0 110m
speaker-qk5fb 1/1 Running 0 110m
speaker-wsllb 1/1 Running 0 110m
speaker-x4bwt 1/1 Running 0 110m
[root@k8s-master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 16d
nginx-metallb LoadBalancer 10.105.239.69 192.168.10.115 8090:31372/TCP 106m
[root@k8s-master01 ~]# ping 192.168.10.115
PING 192.168.10.90 (192.168.10.90) 56(84) bytes of data.
64 bytes from 192.168.10.90: icmp_seq=1 ttl=64 time=3.45 ms
64 bytes from 192.168.10.90: icmp_seq=2 ttl=64 time=0.040 ms
[root@k8s-master01 ~]# curl http://192.168.122.115:8090
<!DOCTYPE html>
Welcome to nginx!</title>