【CKA考试笔记】十三、k8s中的网络
calico的通信过程、k8s中的网络策略
文章目录
实验环境
完成初始化集群的环境:
(vms21)192.168.26.21——master1
(vms22)192.168.26.22——worker1
(vms23)192.168.26.23——worker2
两台非k8s集群环境的虚拟机:
(vms31)192.168.26.31
(vms32)192.168.26.32
一、calico通信的过程
calico有两种通信模式:ipip、bgp
ipip模式:
1.如下图有两个节点,当我们装好calico之后,会在各节点里建立一个接口、隧道(各节点中都有一个calico-node容器,它负责来创建tunl0这个接口、隧道),如下图节点A里的tunl0和节点B里的tunl0,这两个tunl0即隧道的两端,它们之间可以看成一个直接相通的隧道(一个虚拟的线路,不是真实的物理线路),多个节点之间就会建立起多个隧道
2.这个隧道能连通的前提是这个两个节点之间物理上能通信,因此,各节点中的tunl0必须在节点中选择一张物理网卡来连接,如下图的ens32
3.每当在节点中创建一个pod或容器时,就会动态地新建一张虚拟网卡来将pod或容器与tunl0接口连接起来,pod或容器里会有一张网卡与这张虚拟网卡连接,他们之间是vethpair的关系(即直接相连),这张虚拟网卡是动态创建的,一旦pod或容器被删除,这张虚拟网卡也会被删除
4.pod或容器向外发送数据包时,就直接发给这张虚拟网卡,虚拟网卡又将数据包发送给tunl0,然后通过隧道将数据发送给接收方
bgp模式
bgp模式与ipip不同的地方在于,ipip模式是通过隧道来传输数据,bgp则是通过bgp协议、以路由的方式传输数据
实验:在非k8s环境里,准备两台机器,通过calico实现容器跨主机通信
calico的信息,是存储在etcd数据库里面的,假设两个使用calico通信节点不是在k8s集群环境里,则需要对每个节点都对应建立一个etcd来保存calico信息
搭建etcd集群
这里我们使用docker来管理镜像
一、两台机器上都安装docker和etcd
yum install docker-ce etcd -y
二、配置etcd
vim /etc/etcd/etcd.conf
修改vms31、vms32上etcd.conf配置文件中以下内容:
ETCD_DATA_DIR="/var/lib/etcd/cluster.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.26.31:2380,http://localhost:2380"
ETCD_LISTEN_CLIENT_URLS="http://192.168.26.31:2379,http://localhost:2379"
ETCD_NAME="etcd-31"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.26.31:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379,http://192.168.26.31:2379"
ETCD_INITIAL_CLUSTER="etcd-31=http://192.168.26.31:2380,etcd-32=http://192.168.26.32:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
将配置文件拷贝到vms32上
scp /etc/etcd/etcd.conf 192.168.26.32:/etc/etcd/
并作相应修改,将前6行中的31改为32
ETCD_DATA_DIR="/var/lib/etcd/cluster.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.26.32:2380,http://localhost:2380"
ETCD_LISTEN_CLIENT_URLS="http://192.168.26.32:2379,http://localhost:2379"
ETCD_NAME="etcd-32"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.26.32:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379,http://192.168.26.32:2379"
ETCD_INITIAL_CLUSTER="etcd-31=http://192.168.26.31:2380,etcd-32=http://192.168.26.32:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
三、开启并设置开机自启etcd(两台机器上)
systemctl enable etcd --now
四、查看docker的启动脚本
systemctl status docker
#输出:
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: inactive (dead)
Docs: https://docs.docker.com
得知docker的启动脚本为:/usr/lib/systemd/system/docker.service
五、编辑docker的启动脚本,使docker能够去读取etcd里的数据
vim /usr/lib/systemd/system/docker.service
编辑ExecStart后的值,增加访问etcd的地址
修改后为:
#vms31中的修改为:
ExecStart=/usr/bin/dockerd --cluster-store=etcd://192.168.26.31:2379 -H fd:// --containerd=/run/containerd/containerd.sock
#vms32中的修改为:
ExecStart=/usr/bin/dockerd --cluster-store=etcd://192.168.26.32:2379 -H fd:// --containerd=/run/containerd/containerd.sock
六、重启etcd、启动docker
systemctl daemon-reload ; systemctl start etcd
systemctl daemon-reload ; systemctl start docker
七、在两台机器上准备calico所需的配置文件
使用calico网络时,在每个节点都会有一个calico-node容器,它会去创建tunl0隧道接口
因此需要在两台机器上都创建一个calico配置文件
#1.两个节点上创建文件夹
mkdir /etc/calico
#2.vms31上创建配置文件
cat > /etc/calico/calicoctl.cfg <<EOF
apiVersion: v1
kind: calicoApiConfig
metadata:
spec:
datastoreType: "etcdv2"
etcdEndpoints: "http://192.168.26.31:2379"
EOF
#3.vms32上创建配置文件
cat > /etc/calico/calicoctl.cfg <<EOF
apiVersion: v1
kind: calicoApiConfig
metadata:
spec:
datastoreType: "etcdv2"
etcdEndpoints: "http://192.168.26.32:2379"
EOF
八、两个节点上都准备好了calico配置文件,若欲让两个节点通信,建立隧道,则各个节点上都需要建立并运行一个calico-node(calico-node会帮我们建立隧道接口,不需要我们手动建立)
两个节点上安装calico客户端工具calicoctl及拉取calico-node镜像
二进制形式安装calicoctl
wget https://github.com/projectcalico/calicoctl/releases/download/v1.5.0/calicoctl -O calicoctl
授予可执行权限,并将文件移动到/bin目录下
chmod +x calicoctl ; mv calicoctl /bin/
拉取calico-node镜像
docker pull docker.io/calico/node:v2.6.12
九、在每个节点上都建立calico-node的pod
#建立calico-node的pod,并加载calicoctl.cfg配置文件
calicoctl node run --node-image=docker.io/calico/node:v2.6.12 -c /etc/calico/calicoctl.cfg
建立成功
十、测试,可以查看对方节点的信息
calicoctl node status
十一、建立两个节点之间的隧道
在任意节点上创建网络,会同步给远端
#--driver calico:指定使用calico的libnetwork CNM driver
#--ipam-driver calico-ipam:指定使用calico的IPAM driver管理IP
docker network create --driver calico --ipam-driver calico-ipam calnet1
(calico为global网络,etcd会将calnet1同步到所有主机,只要使用calico网络创建出来的,都可以识别出来)
创建好后,在另一个节点上可以查看到网络
docker network list
十二、各节点上拉取busybox镜像
docker pull busybox
十三、另打开一个vms31的控制台,创建一个名为c31,使用calnet1网络的busybox容器,创建后即进入容器
docker run -it --name=c31 --network=calnet1 busybox
此时在vms32上就无法创建name为c31的容器了(冲突了),因为已经存在这个通道了
在vms32上创建名为c32,使用calnet1网络的busybox容器
docker run -it --name=c32 --network=calnet1 busybox
回到vms31的busybox容器中,通过ip address
查看网络信息
ip a
可以看到cali0@if9这张网卡,即容器里的网卡
回到vms原先的控制台,通过ip address
查看该节点(宿主机)的网络信息
ip a
可以看到cali9d25e873a83@if8这张网卡,即对应c31容器的虚拟网卡
“8: cali0@if9” 中的 “if9” 即对应虚拟网卡 “9: cali9d25e873a83@if8” 中的 “9”
虚拟网卡 “cali9d25e873a83@if8” 中的 “if8” 即对应 “8: cali0@if9” 中的 “8”
这就是它们之间形成的vethpair关系
同理,vms32中的c32容器中的网卡与vms32中虚拟网卡也是这样的vethpair关系
查看c32的网络信息
另打开一个vms32的控制台,查看网络信息
即如下图所示:
十四、测试跨主机通信
c31容器中的网卡cali0@if9的IP地址为:192.168.18.2/32
c32容器中的网卡cali0@if5的IP地址为:192.168.3.64/32
测试在c31容器中ping c32的IP
ping 192.168.3.64
可以ping通,验证了可以跨主机通信
回到vms31宿主机上, 查看路由信息,可以看到数据的转发过程
route -n
ip route
二、网络策略
使用calico的一个优点即它支持网络策略(两个pod跨主机通信时,可以实现类似防火墙的策略)
网络策略本质上就是一个防火墙,在没有网络策略的情况下,所有数据包都是能通信的,但是,一旦配置了网络策略,则除了允许的那部分之外,其他都是被拒绝的
创建网络策略时有几个要考虑的地方:
1.策略应用到谁?——保护哪个pod
2.是针对流量入(限制流量流入pod)、还是流量出(限制pod往外连接)
3.网络策略具体的规则
创建网络策略
1.在k8s文档中搜索networkpolicy
2.找到yaml文件模板,如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
spec.podSelector——配置该网络策略应用到谁(保护哪些pod),通过pod的标签来匹配,配置多个pod的共同标签即保护多个pod
若想应用到当前命名空间下的所有pod,保护所有pod,则直接写为:
podSelector:{}
#或
podSelector:
matchLabels:
spec.policyTypes——配置是入策略还是出策略,Ingress为入策略,Egress为出策略,两个都写则为都启用
spec.ingress.from——配置哪些允许哪些客户端可以访问进来(流量入)
— ipBlock:通过ip限制,cidr下配置允许访问的ip,except表示除了xx之外,如cidr:192.168.26.0 except:192.168.26.31,表示只允许192.168.26.0网段下的网络,除了192.168.26.31之外,都可以访问进来
— namespaceSelector:通过命名空间限制,只有在指定命名空间下的pod可以访问进来,若想所有命名空间下的pod都可以访问进来,则将matchLabels下清空matchLabels:
,例:只允许default命名空间下的pod可以访问进来,则先获取default命名空间的标签kubectl get ns --show-labels default
,查得为“kubernetes.io/metadata.name=default”,然后将matchLabels下写为“kubernetes.io/metadata.name: default”
— podSelector:通过标签限制,注意,前提是在当前命名空间下,即和这个网络策略是同一个命名空间下的pod,并且只有匹配matchLabels下配置的标签的pod才可以访问进来(只有在k8s集群中,才有标签的概念),若想在当前命名空间下所有pod都可以访问,则直接将matchLabels下清空matchLabels:
spec.ports——配置允许哪些端口可以访问进来,若没指定则表示允许所有端口访问
spec.egress.to——配置允许pod访问哪些地址(流量出),选择器的配置同ingress
查看网络策略
kubectl get networkpolicy
实验:流量入的策略
创建命名空间net,在命名空间net下,创建两个pod:pod1、pod2,针对这两个pod分别创建两个LoadBalancer类型的svc:svc1关联pod1、svc2关联pod2
在net命名空间下再创建一个pod,名为testpod
在default命名空间下创建一个pod,名为default-testpod
(1)创建命名空间net,并使用kubens工具切换net为当前命名空间
kubectl create ns net
kubens net
(2)创建pod1,yaml文件pod11.yaml内容如下:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
(3)创建pod2,除名字改为pod2外,其他都与pod1相同
用sed替换关键字"pod1"为"pod2",然后创建
sed 's/pod1/pod2/' pod11.yaml | kubectl apply -f -
(4)修改pod1、pod2的nginx容器内主页内容
kubectl exec -it pod1 -- sh -c "echo 111 > /usr/share/nginx/html/index.html"
kubectl exec -it pod2 -- sh -c "echo 222 > /usr/share/nginx/html/index.html"
(5)针对pod1、pod2创建LoadBalancer类型的svc
kubectl expose --name=svc1 pod pod1 --port 80 --type LoadBalancer
kubectl expose --name=svc2 pod pod2 --port 80 --type LoadBalancer
(6)另打开两个控制台,创建两个测试用的pod
#net命名空间下的testpod
kubectl run testpod -it --rm --image nginx --image-pull-policy IfNotPresent -- bash
#default命名空间下default-testpod
kubectl run default-testpod -it --image nginx --image-pull-policy IfNotPresent -n default -- bash
(7)此时没有网络策略时,任何数据包都是能通信的
创建完testpod后,进入了testpod内,测试访问svc1、svc2,都是可以访问通的
curl svc1
#输出:
111
curl svc2
#输出:
222
相同的,default命名空间下的default-testpod也可以访问pod1、pod2
curl svc1.net
#输出:
111
curl svc2.net
#输出:
222
(8)创建一个限制流量入的网络策略
先查看pod1、pod2的标签
kubectl get pods --show-labels
#输出:
NAME READY STATUS RESTARTS AGE LABELS
pod1 1/1 Running 0 146m run=pod1
pod2 1/1 Running 0 146m run=pod2
testpod 1/1 Running 0 142m run=testpod
在master上创建针对pod1的流量入的策略,根据k8s文档中的yaml模板,编辑mypolicy1.yaml,如下:
a.配置网络策略应用为pod1,标签选择器podSelector.matchLabels配置为run=pod1
b.配置policyTypes为Ingress
c.删除metadata下namespace的配置,表示默认在当前命名空间下
d.spec下使用ingress及ingress下的from来配置网络策略
e.配置只允许192.168.26.0/24网段下的网络可以访问进来
f.配置只允许端口80的可以访问进来
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mypolicy1
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 192.168.26.0/24
# except:
# - 172.17.1.0/24
# - namespaceSelector:
# matchLabels:
# project: myproject
# - podSelector:
# matchLabels:
# role: frontend
ports:
- protocol: TCP
port: 80
创建该网络策略
kubectl apply -f mypolicy1.yaml
(9)测试,因所有pod的网段都是10.244.x.x,我们通过pod测试访问svc1
testpod测试访问svc1curl svc1
——无法访问
default-testpod测试访问svc1curl svc1.net
——无法访问
而访问svc2可以正常访问(因为我们只对pod1应用了网络策略),并且在master及worker节点上可以正常访问svc1
(10)现在我们改变网络策略规则,要求在default命名空间下,且含有标签xx=xx的pod才可以访问进来,yaml中设置如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mypolicy1
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
# - ipBlock:
# cidr: 192.168.26.0/24
# except:
# - 172.17.1.0/24
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: default
- podSelector:
matchLabels:
xx: xx
ports:
- protocol: TCP
port: 80
(11)刚才两个条件是并且的关系,因为前面带“-”,表示为同级别并列的两个对象,若我们想让条件变为:在default命名空间下的pod,或含有标签xx=xx的pod可以访问进来,则将两个条件都在一个“-”下即可,yaml如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mypolicy1
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
# - ipBlock:
# cidr: 192.168.26.0/24
# except:
# - 172.17.1.0/24
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: default
podSelector:
matchLabels:
xx: xx
ports:
- protocol: TCP
port: 80
(12)现在改变需求,如下:
假设服务有多个端口
a.允许192.168.26.0/24网段下的主机可以访问80,但不能访问808
b.允许所有含有xx=xx标签的pod能访问808,但不能访问80
这种情况下,只需配置多个ingress下的from即可,yaml如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mypolicy1
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 192.168.26.0/24
ports:
- protocol: TCP
port: 80
- from:
- podSelector:
matchLabels:
xx: xx
ports:
- protocol: TCP
port: 808
实验:流量出的策略
沿用上一个实验中的环境
(1)创建一个针对pod1的限制流量出的网络策略,pod1对外访问时,只允许它访问svc2
yaml文件为mypolicy2.yaml,yaml文件内容如下:
a.配置网络策略应用为pod1,标签选择器podSelector.matchLabels配置为run=pod1
b.配置policyTypes为Egress
c.删除metadata下namespace的配置,表示默认在当前命名空间下
d.spec下使用egress及egress下的to来配置网络策略
e.配置只允许对外访问pod2,pod2的标签为run=pod2,配置egress.to.podSelector.matchLabels为:run=pod2
f.配置只允许对外访问端口为80的服务进程
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mypolicy2
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
run: pod2
ports:
- protocol: TCP
port: 80
(2)创建这个网络策略
kubectl apply -f mypolicy2.yaml
(3)查看pod2的ip、svc2的ip
kubectl get pods -o wide
#输出:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 1 (2m48s ago) 5h58m 10.244.7.40 vms22.rhce.cc <none> <none>
pod2 1/1 Running 1 (2m46s ago) 5h58m 10.244.70.84 vms23.rhce.cc <none> <none>
testpod 1/1 Running 1 (2m48s ago) 5h54m 10.244.7.37 vms22.rhce.cc <none> <none>
kubectl get svc
#输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc1 LoadBalancer 10.108.53.108 192.168.26.240 80:31078/TCP 5h55m
svc2 LoadBalancer 10.96.105.161 192.168.26.241 80:30594/TCP 5h55m
(4)进入pod1,测试直接访问pod2、访问svc2,都能访问成功
kubectl exec -it pod1 -- bash
curl 10.244.70.84
#输出:
222
curl 10.96.105.161
#输出:
222
但是若通过svc2的名字访问,则会失败,这是因为没有通过coredns解析(现在只允许访问svc2,不允许去访问kube-system命名空间下的coredns做解析)
(5)现在我们希望可以去解析
获取kube-system命名空间的标签信息
kubectl get ns kube-system --show-labels
#输出:
NAME STATUS AGE LABELS
kube-system Active 26d kubernetes.io/metadata.name=kube-system
查看kube-system下的coredns的pod及标签信息
kubectl get pods -n kube-system --show-labels | grep core
#输出:
coredns-74586cf9b6-rlkhp 1/1 Running 15 (58m ago) 26d k8s-app=kube-dns,pod-template-hash=74586cf9b6
coredns-74586cf9b6-x5vld 1/1 Running 15 (58m ago) 26d k8s-app=kube-dns,pod-template-hash=74586cf9b6
yaml文件中修改:
增加一个to配置项,使用namespaceSelector配置允许访问kube-system命名空间下的pod,并且通过podSelector配置含有标签k8s-app=kube-dns的pod
即允许访问kube-system命名空间下的含有标签k8s-app=kube-dns的pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mypolicy2
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- to:
- podSelector:
matchLabels:
run: pod2
ports:
- protocol: TCP
port: 80
默认策略
前面说过,若是没有配置网络策略的情况下,所有数据包都是能通信的,这是因为默认允许所有入站流量,相反,也可以设置默认拒绝所有入站流量,yaml如下:
默认拒绝所有入站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
默认允许所有入站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {}
更多推荐
所有评论(0)