k8s网络通信
- k8s网络通信
- 1.容器间通信
- 2.pod之间的通信
- 2.1同一节点的pod
- 2.2不同节点的pod之间的通信
- flannel网络原理
- flannel支持多种后端:
- 3.pod和service通信
- 4.pod和外网通信
- 5.Service与集群外部客户端的通信
- ingress
- 创建ingress服务:
- Ingress TLS 配置
- Ingress 认证配置(认证之前做好加密)
- Ingress地址重写
- 6.calico网络插件:能够解决策略
- 限制访问指定服务
- NetworkPolicy策略模型
- 允许指定pod访问服务
- 禁止 namespace 中所有 Pod 之间的相互访问
- 禁止其他 namespace 访问服务
- 只允许指定namespace访问服务
- 允许外网访问服务
k8s网络通信
k8s通过CNI接口接入其他插件来实现网络通讯。目前比较流行的插件有flannel(只解决网络通信,不能解决网络策略),calico
等。
CNI插件存放位置:# cat /etc/cni/net.d/10-flannel.conflist
插件使用的解决方案如下:
虚拟网桥,虚拟网卡,多个容器共用一个虚拟网卡进行通信。
多路复用:MacVLAN,多个容器共用一个物理网卡进行通信。
硬件交换:SR-LOV,一个物理网卡可以虚拟出多个接口,这个性能最好。
1.容器间通信
同一个pod内的多个容器间的通信,通过lo即可实现;
2.pod之间的通信
2.1同一节点的pod
之间通过cni网桥转发数据包。
2.2不同节点的pod之间的通信
需要网络插件支持。
Flannel vxlan模式跨主机通信原理
注:图片转载
通过ip n 看缓存
10.244.0.2—>cni0数据包直接路由–>flannel.1(封装数据包,需要知道对端设备mac:ip n 可以获得并且添加对应的vni)---->再次路由到1上的eth0(fdb获取对端主机ip,arp获取mac)----->根据vni进行相应的vtep设备解包,拿到内部源地址(目标地址)如果目标地址是访问cni的会直接从node2上的flannel.1直接路由到cni,进到对应的容器内部,
如图
src: 10.244.1.1 dst: 10.244.0.1
数据包发送到对端后进行解封,拆成10.244.0.1,数据包到达node1之后,从etho口进去,转发到flannel.1接口上进行解包,10.244.0.1就是自己的段,即成为本地的通信
在node1 flannel.1上封装,看到的地址是172地址的传送,到node2上的flannel.1解封,成目标地址
VXLAN, | 即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VXLAN可以完全在内核态实现封装和解封装工作,从而通过“隧道”机制,构建出覆盖网络(Overlay Network)。 |
VTEP | :VXLAN Tunnel End Point(虚拟隧道端点),在Flannel中 VNI的默认值是1,这也是为什么宿主机的VTEP设备都叫flannel.1的原因。 |
Cni0 | 网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡)。 |
Flannel.1 | : TUN设备(虚拟网卡),用来进行 vxlan 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。 |
Flanneld | :flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac、ip等网络数据信息。 |
flannel网络原理
当容器发送IP包,通过veth pair 发往cni网桥,再路由到本机的flannel.1设备(隧道)进行处理。
VTEP设备之间通过二层数据帧进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址
(ip n),封装
成一个内部数据帧(不能正常进行通信),发送给目的VTEP设备。
内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,承载着内部数据帧通过宿主机的eth0进行传输。
Linux会在内部数据帧前面,加上一个VXLAN头
,VXLAN头里有一个重要的标志叫VNI
,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。
flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,
这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的。获得etho地址
linux内核在IP包前面再加上二层数据帧头,把目标节点的MAC地址填进去,MAC地址从宿主机的ARP表获取。
此时flannel.1设备就可以把这个数据帧从eth0发出去,再经过宿主机网络来到目标节点的eth0设备。目标主机内核网络栈会发现这个数据帧有VXLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据帧,根据VNI的值,交给本机flannel.1设备处理,flannel.1拆包,根据路由表发往cni网桥,最后到达目标容器。bridge fdb
转发数据库
每个节点对应不同网络端ip,
arp -n 直接看见缓存
flannel支持多种后端:
Vxlan
vxlan //报文封装,默认
Directrouting //直接路由,跨网段使用vxlan,同网段使用host-gw模式。
host-gw: //主机网关,性能好,但只能在二层网络中,不支持跨网络如果有成千上万的Pod,容易产生广播风暴,不推荐
UDP: //性能差,不推荐
主机网关模式 直接通过eth0进行通信,不需要flannel.1
# kubectl -n kube-system edit cm kube-flannel-cfg
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw" 模式直接在此处修改
}
}
修改后需更新
kubectl get pod -n kube-system |grep kube-proxy | awk ‘{system(“kubectl delete pod “$1” -n kube-system”)}’ //更新kube-proxy pod
2
配置flannel:如下,同网段使用直接路由方式,不同网段自己直接使用vxlan
# kubectl -n kube-system edit cm kube-flannel-cfg
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true #直连路由
}
}
重载
demo可以直接与另一网段直接通信
3.pod和service通信
*: 通过iptables或ipvs实现通信,ipvs取代不了iptables,因为ipvs只能做负载均衡,而做不了nat转换。
4.pod和外网通信
*iptables的MASQUERADE。
5.Service与集群外部客户端的通信
(ingress、nodeport、loadbalancer)
ingress
一种全局的、为了代理不同后端 Service 而设置的负载均衡服务,就是 Kubernetes 里的Ingress 服务。
Ingress由两部分组成:Ingress controller和Ingress服务。
Ingress Controller 会根据你定义的 Ingress 对象,提供对应的代理能力。
业界常用的各种反向代理
项目,比如 Nginx、HAProxy、Envoy、Traefik 等,都已经为Kubernetes 专门维护了对应的 Ingress Controller。
注:图片转载
用户访问ingress controller ,访问内部的svc,
官网:https://kubernetes.github.io/ingress-nginx/
docker pull pollyduan/ingress-nginx-controller:v0.44.0
docker tag pollyduan/ingress-nginx-controller:v0.44.0 reg.westos.org/library/ingress-nginx-controller:v0.44.0
docker push reg.westos.org/library/ingress-nginx-controller:v0.44.0
docker pull jettech/kube-webhook-certgen:v1.2.0
docker tag jettech/kube-webhook-certgen:v1.2.0 reg.westos.org/library/kube-webhook-certgen:v1.2.0
docker push reg.westos.org/library/kube-webhook-certgen:v1.2.0
用DaemonSet结合nodeselector来部署ingress-controller到特定的node上,然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/443端口就能访问服务。
优点是整个请求链路最简单,性能相对NodePort模式更好。
缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。
比较适合大并发的生产环境使用。
应用ingress controller定义文件
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
需要对文件进行修改
将全文的0.33.0改为0.44.0
在创建的时候,因为之前创建service的时候,端口冲突,将其关闭
创建ingress服务:
$ vim ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo
spec:
rules:
- host: www1.westos.org
http:
paths:
- path: /
backend:
serviceName: myservice
servicePort: 80
应用
[root@server2 ~]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-demo <none> www1.westos.org 172.25.10.3 80 13m
[root@server2 ~]#
做好解析之后可以直接通过域名访问
针对域名的虚拟主机,实现负载均衡
创建新的service
[root@server2 ~]# cat service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: myapp
image: myapp:v1
创建新的域名的ingress
[root@server2 ~]# cat ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo
spec:
rules:
- host: www1.westos.org
http:
paths:
- path: /
backend:
serviceName: myservice
servicePort: 80
- host: www2.westos.org
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
[root@server2 ~]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-demo <none> www1.westos.org,www2.westos.org 172.25.10.3 80 20m
[root@localhost ~]# curl www2.westos.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
Ingress TLS 配置
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
生成key证书
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
将生成的key和证书存储到k8s中
启用tls、
上面的Ingress.yaml文件
[root@server2 ~]# cat ingress.yaml
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo
spec:
tls:
- hosts:
- www1.westos.org
secretName: tls-secret
rules:
- host: www1.westos.org
http:
paths:
- path: /
backend:
serviceName: myservice
servicePort: 80
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo2
spec:
rules:
- host: www2.westos.org
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
kubectl apply -f ingress.yaml
激活tls,会被重定向
Ingress 认证配置(认证之前做好加密)
生成认证文件,把用户密码文件存储到secret中
yum install -y httpd-tools
[root@server2 ~]# htpasswd -c auth lzy
New password:
Re-type new password:
Adding password for user lzy
[root@server2 ~]# htpasswd auth admin
New password:
Re-type new password:
Adding password for user admin
[root@server2 ~]#
kubectl create secret generic basic-auth --from-file=auth secret/basic-auth created
[root@server2 ~]# cat ingress.yaml
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - lzy'
spec:
tls:
- hosts:
- www1.westos.org
secretName: tls-secret
rules:
- host: www1.westos.org
http:
paths:
- path: /
backend:
serviceName: myservice
servicePort: 80
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo2
spec:
rules:
- host: www2.westos.org
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
参考: kubernets.github.io/ingress-nginx/examples/rewite/
Ingress地址重写
重写在一个目录中
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo2
annotations:
nginx.ingress.kubernetes.io/app-root: /hostname.html
spec:
rules:
- host: www2.westos.org
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
www2.westos.org------>直接转到 www2.westos.org/hostname.html
http://www2.westos.org/westos------> www2.westos.org
重定向—>westos关键字
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-demo2
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: www2.westos.org
http:
paths:
- backend:
serviceName: nginx-svc
servicePort: 80
path: /westos(/|$)(.*)
annotations参数
6.calico网络插件:能够解决策略
官网:https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises
calico简介:
flannel实现的是网络通信,calico的特性是在pod之间的隔离。
通过BGP路由,但大规模端点的拓扑计算和收敛往往需要一定的时间和计算资源。
纯三层的转发,中间没有任何的NAT和overlay,转发效率最好。
Calico 仅依赖三层路由可达。Calico 较少的依赖性使它能适配所有 VM、Container、白盒或者混合环境场景。
安装calico:
# wget https://docs.projectcalico.org/manifests/calico.yaml
# vim calico.yml
- name: CALICO_IPV4POOL_IPIP
value: "off"
- name: CALICO_IPV4POOL_VXLAN
value: "Never
- name: CALICO_IPV4POOL_CIDR
value: "10.244.0.0/16" #与初始化时保持一致
# kubectl apply -f calico.yaml
Felix:监听ECTD中心的存储获取事件,用户创建pod后,Felix负责将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。同样如果用户制定了隔离策略,Felix同样会将该策略创建到ACL中,以实现隔离。
BIRD:一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,路由的时候到这里来。
IPIP工作模式:适用于互相访问的pod不在同一个网段中,跨网段访问的场景。
GP工作模式:适用于互相访问的pod在同一个网段,适用于大型网络
calico.yaml 文件中所需镜像
下载所需镜像,上传至私有仓库
docker push reg.westos.org/calico/node:v3.16.1
docker push reg.westos.org/calico/pod2daemon-flexvol:v3.16.1
docker push reg.westos.org/calico/cni:v3.16.1
docker push reg.westos.org/calico/kube-controllers:v3.16.1
删除,
kubectl delete -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
删除之前应用的flannel插件
[root@server2 ~]# kubectl -n kube-system get pod
kube-flannel-ds-9r5x7 1/1 Running 5 13d
kube-flannel-ds-mt6m9 1/1 Running 8 13d
[root@server2 ~]# kubectl delete pod kube-flannel-ds-9r5x7 -n kube-system
pod “kube-flannel-ds-9r5x7” deleted
删除多余的svc pod
在所有节点(server2,3)上移走文件
[root@server2 ~]# cd /etc/cni/net.d/
[root@server2 net.d]# ls
10-flannel.conflist
mv 10-flannel.conflist /mnt/
[root@server2 ~]# ps ax | grep flannel
15915 pts/0 S+ 0:00 grep --color=auto flannel
[root@server2 ~]# cat nginx-service.yml
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: myapp
image: myapp:v1
[root@server2 net.d]# ls
10-calico.conflist calico-kubeconfig
[root@server2 net.d]# pwd
/etc/cni/net.d
限制访问指定服务
pod标签app=nginx的都不能访问
[root@server2 calico]# cat deny-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-nginx
spec:
podSelector:
matchLabels:
app: nginx
看日志 kubectl -n kube-system logs calico-node-fp9bn
NetworkPolicy策略模型
:控制某个namespace下的pod的网络出入站规
允许指定pod访问服务
[root@server2 calico]# cat access-demo.yml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-nginx
spec:
podSelector:
matchLabels:
app: nginx
ingress:
- from:
- podSelector:
matchLabels:
app: demo
允许demo标签的进行访问
禁止 namespace 中所有 Pod 之间的相互访问
[root@server2 calico]# cat deny-pod.yml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: demo
spec:
podSelector: {} #任何标签不能访问
kubectl create namespace demo 创建namespace
在demo namespace上运行两个pod
kubectl run demo1 --image=busyboxplus -it -n demo
kubectl run demo2 --image=busyboxplus -it -n demo
pod之间无法访问
删除pod
kubectl delete pod demo1 --force
禁止其他 namespace 访问服务
实验环境变化
[root@server2 calico]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busyboxplus 1/1 Running 4 4d14h 10.244.1.119 server3 <none> <none>
demo 1/1 Running 17 5d17h 10.244.1.120 server3 <none> <none>
deployment-67f9d9c97f-7rcjq 1/1 Running 0 156m 10.244.141.192 server3 <none> <none>
deployment-67f9d9c97f-ljndc 1/1 Running 0 156m 10.244.141.193 server3 <none> <none>
nginx 1/1 Running 0 32m 10.244.141.194 server3 <none> <none>
[root@server2 calico]# kubectl get pod -n demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo1 1/1 Running 1 25s 10.244.141.198 server3 <none> <none>
demo2 1/1 Running 1 15m 10.244.141.197 server3 <none> <none>
如何禁止访问
nginx是defalut namespace
demo2 是 demo namespace
[root@server2 calico]# cat deny-ns.yml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: deny-namespace
spec:
podSelector:
matchLabels:
ingress:
- from:
- podSelector: {}
只允许指定namespace访问服务
[root@server2 calico]# cat access-ns.yml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-namespace
spec:
podSelector:
matchLabels:
run: nginx ####defalt 的pod的标签是run:nginx
ingress:
- from:
- namespaceSelector:
matchLabels:
role: prod ###namespace test的标签
[root@server2 calico]# kubectl create namespace test
namespace/test created
[root@server2 calico]# kubectl run demo3 --image=busyboxplus -it -n test
If you don't see a command prompt, try pressing enter.
/ # curl 10.244.141.194
^C
/ # Session ended, resume using 'kubectl attach demo3 -c demo3 -i -t' command when the pod is running
更改ns的标签
访问成功
允许外网访问服务
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-external
spec:
podSelector:
matchLabels:
app: web
ingress:
- ports:
- port: 80
from: []
[root@server2 ~]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-demo <none> www1.westos.org 80 105s
[root@server2 ~]# kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.102.82.233 172.25.10.100 80:31793/TCP,443:30584/TCP 15h
ingress-nginx-controller-admission ClusterIP 10.106.2.143 <none> 443/TCP 15h
ingress(见博客loadblancer
) 获得的172.25.10.100
在外网: curl www1.westos.org 就可以访问
/etc/hosts -----> 172.25.10,100 www1.westos.org
[root@localhost file_recv]# curl www1.westos.org
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>