实验环境

完成初始化集群的环境:
(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这张网卡,即容器里的网卡
![在这里插入图片描述](https://img-blog.csdnimg.cn/7927c37eba114e3aadfcf3a628f6ccc3.png
回到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:
  - {}
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐