Kubernetes Network

学习参考:网络策略

摘要:本文全面介绍了 Kubernetes 网络的核心概念与实践。首先从单主机网络通信入手,分析了 Docker 和 Containerd 的网络实现原理;接着深入探讨了跨主机网络通信的多种方案,包括 Flannel、Calico、Weave 和 Macvlan 等 CNI 插件的网络模型、性能特点及适用场景;然后对比了 CNM 和 CNI 两种容器网络模型;最后通过 Calico 网络的实战部署和 Kubernetes 网络策略的详细示例,展示了如何实现 Pod 间的精细访问控制。文章涵盖了从基础原理到高级实践的完整知识体系,为读者构建 Kubernetes 网络知识框架提供了系统指导。

环境准备

root@master30:~# kubectl create ns network
root@master30:~# kubectl config set-context --current --namespace network

单主机网络通信

Docker 单机网络

Docker中的网络接口默认都是虚拟的接口。虚拟接口的最大优势就是转发效率极高。这是因为Linux在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换。

Docker 服务默认会创建一个名称为docker0的Linux网桥(其上有一个docker0内部接口),利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做vethpair)。Docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

在这里插入图片描述

说明:brctl工具由bridge-utils提供。

外部主机要想访问容器,需要通过端口映射实现。

Containerd 单机网络

Containerd 中的网络 与Docker类似,所有网络接口默认都是虚拟接口。

创建容器时,Containerd 会在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通。

跨主机网络通信

跨主机网络通信架构

在这里插入图片描述

以上图片来源于《基于 kubernetes 的容器云平台实战 》。

跨主机通信方法比较多,可以使用:

  1. docker 原生的方案:overlay 和 macvlan。
  2. 第三方提供的方案:flannel 、calico、weave等。

重点关注两点:配置难易程度是否支持网络策略

以下数据来源于《kubernetes 权威指南》。

方案 特性 Flannel Calico macvlan OpenVswitch 直接路由
方案特性 通过虚拟设备flannel0实现对docker0的管理 基于BGP协议的纯三层的网络方案 基于Linux Kernel 的macvlan技术 基于隧道的虚拟路由器技术 基于Linux Kernel 的 vRouter 技术
对底层网络的要求 三层互通 三层互通 二层互通 三层互通 二层互通
配置难易程度 简单-基于etcd 简单-基于etcd 简单-直接使用宿主机网络,需要仔细规划IP地址 复杂-需手工配置个节点的bridge 简单-使用宿主机vRoute功能,需要仔细规划每个Node的IP地址
网络性能 host-gw > VxLAN BGP 模式性能损耗小 性能损耗可忽略 性能损耗较小 性能损耗较小
网络连通性限制 在不支持BGP的网络环境下无法使用 基于macvlan的容器无法与宿主机网络通信 在无法实现大二层互通的网络环境下无法使用

跨主机通信方案

我们将从如下几个方面比较,大家可以根据不同场景选择最合适的方案。

  • 网络模型,采用何种网络模型支持 multi-host 网络?
  • Distributed Store,是否需要 etcd 或 consul 这类分布式 key-value 数据库存储网络信息?
  • IPMA,如何管理容器网络的 IP?
  • 连通与隔离,提供怎样的网络连通性?支持容器间哪个级别和哪个类型的隔离?
  • 性能,性能比较。
网络模型

跨主机网络意味着将不同主机上的容器用同一个虚拟网络连接起来。这个虚拟网络的拓扑结构和实现技术就是网络模型。

  • Underlay:直接使用底层物理网络转发 Pod 流量,无隧道封装;依靠路由 / 二层交换通信。

  • Overlay:在宿主机网络之上构建虚拟隧道,数据包额外封装外层包头;底层网络只负责转发宿主机 IP,不需要感知 Pod 网段。

1. Flannel

Flannel 提供两种主流工作模式

  1. host-gw(Underlay)
    • 原理:节点上添加静态路由,目标Pod网段下一跳指向目标宿主机IP;无任何报文封装
    • 流量:Pod原始IP包直接在底层网络传输。
    • 约束:底层网络能够转发Pod网段路由;公有云大多不支持。
    • 性能:高。
  2. VXLAN(Overlay)
    • 原理:跨节点Pod报文封装UDP VXLAN包头,通过宿主机网络传输,到达对端解封装。
    • 约束:仅要求宿主机之间IP连通,公有云通用。
    • 性能:存在封包解包开销,低于host-gw。

Flannel仅负责连通,不提供网络策略NetworkPolicy

2. Calico
  1. BGP模式(Underlay思想)
    • 节点间通过BGP交换Pod路由,无隧道封装,原生IP转发。
    • 性能最优;前提:网络允许BGP协议发布路由。
  2. IPIP / VXLAN模式(Overlay隧道)
    • 底层不支持BGP时启用IP-in-IP隧道封装;数据包外层增加宿主机IP头。
    • 适配公有云;封装带来性能损耗。

Calico最大亮点:原生支持 NetworkPolicy(Pod防火墙策略)

3. Weave(Weave Net)

默认采用 Overlay(UDP隧道)

  • 工作方式:节点之间建立UDP隧道,封装Pod流量;自动发现集群节点。
  • 特色:
    1. 内置DNS;
    2. 支持NetworkPolicy;
    3. 可以混合运行加密隧道;
  • 短板:
    隧道封装持续占用CPU;大规模集群性能弱于Flannel VXLAN、Calico。

Weave也支持快速MAC路由优化,但主体模型仍然归类Overlay。

4. Macvlan

Macvlan属于Underlay方案

  • 原理:在宿主机物理网卡上虚拟多张独立网卡,每个Pod拥有独立MAC地址;Pod直接接入底层物理局域网。
  • 流量:Pod报文直接在二层物理网络转发,不存在隧道封装
    ✅ 优点:性能极高,Pod和物理机器同等地位,网络延迟极低。
    ❌ 限制:
  1. 宿主机网卡不能开启桥接;
  2. 同一父网卡Macvlan子接口无法直接互通(同宿主机Pod互通缺陷);
  3. 网络规划复杂,传统虚拟化环境容易冲突。

典型场景:需要Pod分配局域网独立IP、需要被局域网其他物理设备直接访问。

CNI方案 可用网络模型 是否隧道封装 支持NetworkPolicy 适用场景
Flannel host-gw(Underlay)、VXLAN(Overlay) VXLAN封装;host-gw无封装 ❌ 不支持 追求简单、不需要Pod访问控制;中小型集群
Calico BGP(Underlay)、IPIP/VXLAN(Overlay) IPIP/VXLAN封装;BGP无封装 ✅ 原生支持 需要网络策略、自建机房/公有云通用
Weave Net UDP隧道Overlay UDP封装 ✅ 支持 测试环境、小规模集群;部署简单,无需etcd
Macvlan Underlay(二层直连物理网络) ❌ 无任何封装 取决于配套CNI Pod需要占用局域网独立IP、低延迟业务
Distributed Store

**Docker Overlay、Flannel 和 Calico 都需要 etcd 或 consul。**Macvlan 是简单的 local 网络,不需要保存和共享网络信息。Weave 自己负责在主机间交换网络配置信息,也不需要 Distributed Store。

IPAM
  • Docker Overlay 网络中所有主机共享同一个 subnet,容器启动时会顺序分配 IP,可以通过 --subnet 定制此 IP 空间。

  • Macvlan 需要用户自己管理 subnet,为容器分配 IP,不同 subnet 通信依赖外部网关。

  • Flannel 为每个主机自动分配独立的 subnet,用户只需要指定一个大的 IP 池。不同 subnet 之间的路由信息也由 Flannel 自动生成和配置。

  • Weave 的默认配置下所有容器使用 10.32.0.0/12 subnet,如果此地址空间与现有 IP 冲突,可以通过 --ipalloc-range 分配特定的 subnet。

  • Calico 从 IP Pool(可定制)中为每个主机分配自己的 subnet。

连通与隔离
  • 同一 Docker Overlay 网络中的容器可以通信,但不同网络之间无法通信,要实现跨网络访问,只有将容器加入多个网络。与外网通信可以通过 docker_gwbridge 网络。

  • Macvlan 网络的连通或隔离完全取决于二层 VLAN 和三层路由。

  • 不同 Flannel 网络中的容器直接就可以通信,没有提供隔离。与外网通信可以通过 bridge 网络。

  • Weave 网络默认配置下所有容器在一个大的 subnet 中,可以自由通信,如果要实现隔离,需要为容器指定不同的 subnet 或 IP。与外网通信的方案是将主机加入到 weave 网络,并把主机当作网关。

  • Calico 默认配置下只允许位于同一网络中的容器之间通信,但通过其强大的 Policy 能够实现几乎任意场景的访问控制。

性能

性能测试是一个非常严谨和复杂的工程,这里我们只尝试从技术方案的原理上比较各方案的性能。

最朴素的判断是:Underlay 网络性能优于 Overlay 网络

  • **Overlay 网络利用隧道技术,将数据包封装到 UDP 中进行传输。**因为涉及数据包的封装和解封,存在额外的 CPU 和网络开销。虽然几乎所有 Overlay 网络方案底层都采用 Linux kernel 的 vxlan 模块,这样可以尽量减少开销,但这个开销与 Underlay 网络相比还是存在的。所以 Macvlan、Flannel host-gw、Calico 的性能会优于 Docker overlay、Flannel vxlan 和 Weave。

  • Overlay 较 Underlay 可以支持更多的二层网段,能更好地利用已有网络,以及有避免物理交换机 MAC 表耗尽等优势,所以在方案选型的时候需要综合考虑。

跨主机网络模型

容器网络的配置是一个复杂的过程,为了应对各式各样的需求:

  • 容器网络的解决方案也多种多样,例如有flannel,calico,kube-ovn,weave等。
  • 容器平台/运行时也是多样的,例如有Kubernetes,Openshift,rkt等。

想要解决这个问题,我们需要一个抽象的接口层,将容器网络配置方案与容器平台方案解耦。

CNM

CNM( Container Network Model,容器网络模型),由 Docker 公司提出,在 docker 项目下的 libnetwork 项目中被采用。按照该模型开发出的 driver 就能与 docker daemon 协同工作,实现容器网络。

  • docker 原生的 driver 包括 none、bridge、overlay 和 macvlan。
  • 第三方 driver 包括 flannel、weave、calico 等。

在这里插入图片描述

容器网络模型对容器网络进行了抽象,由以下三类组件组成:

  • Sandbox 是容器的网络栈,包含容器的 interface、路由表和 DNS 设置。 Linux Network Namespace 是 Sandbox 的标准实现。Sandbox 可以包含来自不同 Network 的 Endpoint。
  • Endpoint 的作用是将 Sandbox 接入 Network。Endpoint 的典型实现是 veth pair,后面我们会举例。一个 Endpoint 只能属于一个网络,也只能属于一个 Sandbox。
  • Network 包含一组 Endpoint,同一 Network 的 Endpoint 可以直接通信。Network 的实现可以是 Linux Bridge、VLAN 等。

如图所示两个容器,一个容器一个 Sandbox,每个 Sandbox 都有一个 Endpoint 连接到 Network 1,第二个 Sandbox 还有一个 Endpoint 将其接入 Network 2.

在这里插入图片描述

下面我们以 docker bridge driver 为例讨论 libnetwork CNM 是如何被实现的。

  1. 两个 Network:默认网络 “bridge” 和自定义网络 “my_net2”。实现方式是 Linux Bridge:“docker0” 和 “br-5d863e9f78b6”。
  2. 三个 Enpoint,由 veth pair 实现,一端(vethxxx)挂在 Linux Bridge 上,另一端(eth0)挂在容器内。
  3. 三个 Sandbox,由 Network Namespace 实现,每个容器有自己的 Sanbox。

在这里插入图片描述

CNI

CNI,全称 Container Network Interface,是 Google 和 **CoreOS **联合定制的网络标准,CNI 规范定义了容器和网络插件之间通信规范,实现多容器通信。各个网络厂商通过该接口实现互相通信。

在这里插入图片描述

一个容器可以被加入到被不同插件所驱动的多个网络之中。一个网络有自己对应的插件和唯一的名称。

CNI 模型包含2个概念:

  • 容器: 独立的 linux 网络命名空间。

  • 网络: 用于互联实体, 这些实体拥有各自独立且唯一的 IP 地址, 可以是容器, 物理机, 或者其他网络设备。

    通过插件设置网络, 包括 CNI Plugin 和 IPAM ( IP address management) Plugin 两类插件:

    • CNI Plugin 负责配置容器网络。
    • IPAM plugin 负责分配容器的IP 地址。

    IPAM Plugin 作为 CNI plugin 的一部分, 与CNI plugin 一起工作。

CNM 和 CNI 比较

特点 CNM CNI
标准规范 Libnetwork cni
最小单元 容器 POD
对守护进程的依赖 依赖 dockerd 不依赖任何守护进程
扩主机通信 依赖外部 KV 数据库 用本身的 KV 数据库
灵活程度 被 docker 绑定 插件可随意替换

Calico 网络(自学)

我们的k8s环境使用Calico网络,所以这里讨论calico网络。

Calico 是一个纯三层的虚拟网络方案,为每个容器分配一个 IP,每个 host 都是 router,把不同 host 的容器连接起来。Calico 不对数据包做额外封装,不需要 NAT 和端口映射,扩展性和性能都很好。

与其他容器网络方案相比,Calico 还有一大优势:network policy。用户可以动态定义 ACL 规则,控制进出容器的数据包,实现业务需求。

准备实验环境

centos7 和 docker

Calico使用etcd存储 Calico 网络状态,在不同主机间共享和交换信息。我们将在10.1.8.30上运行 etcd。

Calico 网络中的每个主机都需要运行 Calico 组件,提供容器 interface 管理、动态路由、动态 ACL、报告状态等功能。

IP 长主机名 短主机名
10.1.8.30 etcd.laoma.cloud etcd
10.1.8.31 docker1.laoma.cloud docker1
10.1.8.32 docker2.laoma.cloud docker2

在这里插入图片描述

安装配置 etcd
# 所有节点配置
[root@etcd ~]# vim /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.1.8.30 etcd.laoma.cloud etcd
10.1.8.31 docker1.laoma.cloud docker1
10.1.8.32 docker2.laoma.cloud docker2

# 安装
[root@etcd ~]# yum install -y etcd

# 配置
[root@etcd ~]# vim /etc/etcd/etcd.conf
# 定义节点名称,默认为default
ETCD_NAME="default"

# 定义节点数据存储目录,默认/var/lib/etcd/default.etcd
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"

# 定义对外提供服务的监听地址,服务端使用
ETCD_LISTEN_CLIENT_URLS="http://localhost:2379,http://10.1.8.30:2379"

# 定义对外提供服务的监听地址,客户端使用
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379,http://10.1.8.30:2379"

# 启用并启动服务
[root@etcd ~]# systemctl enable etcd --now

# 验证状态
[root@etcd ~]# etcdctl cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://10.1.8.30:2379
cluster is healthy

# 查询键值对列表
[root@etcd ~]# etcdctl --endpoints=http://10.1.8.30:2379 ls
配置 docker
# 两个docker节点操作
[root@dockerN ~]# yum install -y docker
[root@dockerN ~]# tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://7i6f7pzu.mirror.aliyuncs.com"]
}
EOF

# 配置docker连接etcd
[root@dockerN ~]# vim /etc/sysconfig/docker
# 在OPTIONS选项最后添加--cluster-store=etcd://10.1.8.30:2379
OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false --cluster-store=etcd://10.1.8.30:2379'

[root@dockerN ~]# systemctl enable docker --now
部署 calico

calico项目地址:https://github.com/projectcalico/calicoctl/releases/

截止2020.12.14,最新版本为v3.17.1,我们这里使用v1.0.2版本。

# 下载 calicoctl
[root@dockerN ~]# wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v1.0.2/calicoctl && chmod +x /usr/local/bin/calicoctl

# 启动 calico
[root@dockerN ~]# mkdir /etc/calico 
[root@dockerN ~]# cat << EOF > /etc/calico/calicoctl.cfg
apiVersion: v1
kind: calicoApiConfig
metadata:
spec:
  etcdEndpoints: "http://10.1.8.30:2379"
EOF

[root@dockerN ~]# calicoctl node run
Running command to load modules: modprobe -a xt_set ip6_tables
Enabling IPv4 forwarding
Enabling IPv6 forwarding
Increasing conntrack limit
Removing old calico-node container (if running).
Running the following command to start calico-node:

docker run --net=host --privileged --name=calico-node -d --restart=always -e ETCD_ENDPOINTS=http://10.1.8.30:2379 -e ETCD_AUTHORITY= -e ETCD_SCHEME= -e NODENAME=docker1.laoma.cloud -e CALICO_NETWORKING_BACKEND=bird -e NO_DEFAULT_POOLS= -e CALICO_LIBNETWORK_ENABLED=true -e CALICO_LIBNETWORK_IFPREFIX=cali -v /var/log/calico:/var/log/calico -v /run/docker/plugins:/run/docker/plugins -v /var/run/docker.sock:/var/run/docker.sock -v /var/run/calico:/var/run/calico -v /lib/modules:/lib/modules calico/node:v1.0.2

Image may take a short time to download if it is not available locally.
Container started, checking progress logs.
Waiting for etcd connection...
Using auto-detected IPv4 address: 10.1.8.31
No IPv6 address configured
Using global AS number
Calico node name:  docker1.laoma.cloud
CALICO_LIBNETWORK_ENABLED is true - start libnetwork service
Calico node started successfully

查看对端信息

[root@docker1 ~]# calicoctl node status
Calico process is running.

IPv4 BGP status
+--------------+-------------------+-------+----------+-------------+
| PEER ADDRESS |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+--------------+-------------------+-------+----------+-------------+
| 10.1.8.32    | node-to-node mesh | up    | 08:11:30 | Established |
+--------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

[root@docker2 ~]# calicoctl node status
Calico process is running.

IPv4 BGP status
+--------------+-------------------+-------+----------+-------------+
| PEER ADDRESS |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+--------------+-------------------+-------+----------+-------------+
| 10.1.8.31    | node-to-node mesh | up    | 08:11:30 | Established |
+--------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.
创建 calico 网络

在 docker1 或 docker2 上执行如下命令创建 calico 网络 cal_ent1:

[root@docker1 ~]# docker network create --driver calico --ipam-driver calico-ipam cal_net1
ea03490f50256dd6c8c7147377a7f9abbdb03ff95e4df815f4532e43f3963937
[root@docker1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
b8e6729060b8        bridge              bridge              local
ea03490f5025        cal_net1            calico              global
1ddf491103ca        host                host                local
5d50b10fef5f        none                null                local

说明:

--driver calico 指定使用 calico 的 libnetwork CNM driver。

--ipam-driver calico-ipam 指定使用 calico 的 IPAM driver 管理 IP。

Calico 网络结构

在 docker1 中运行容器 bbox1 并连接到 cal_net1:

[root@docker1 ~]# docker run --net cal_net1 --name bbox1 -tid busybox
a4e25e5a0a5f1dac3f1e9397bb5223bb13b3e1182aec20bae7d6267ce05055d9
[root@docker1 ~]# docker exec bbox1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
5: cali0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
    inet 192.168.116.192/32 brd 192.168.116.192 scope global cali0
       valid_lft forever preferred_lft forever

cali0 是 calico interface,分配的 IP 为 192.168.116.192。cali0 对应 docker1 编号 6 的 interface cali043af24fe74

[root@docker1 ~]# ip link |grep ^6
6: cali043af24fe74@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default

docker1 将作为 router 负责转发目的地址为 bbox1 的数据包。

[root@docker1 ~]# ip r
default via 10.1.8.2 dev eth0 proto static metric 100 
10.1.8.0/24 dev eth0 proto kernel scope link src 10.1.8.31 metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.116.192 dev cali043af24fe74 scope link 
blackhole 192.168.116.192/26 proto bird 

所有发送到 bbox1 的数据都会发给 cali043af24fe74,因为 cali043af24fe74cali0 是一对 veth pair,bbox1 能够接收到数据。

网络结构如图所示:

在这里插入图片描述

接下来我们在 docker2 中运行容器 bbox2,也连接到 cal_net1:

[root@docker2 ~]# docker run --net cal_net1 --name bbox2 -tid busybox
e942b16a6e1a7d99c012c49e46ca4d94dadedc8a7f343730f9f668b00978eecd
[root@docker2 ~]# docker exec -it bbox2 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
7: cali0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
    inet 192.168.16.129/32 brd 192.168.16.129 scope global cali0
       valid_lft forever preferred_lft forever

docker2的路由:

[root@docker2 ~]# ip r
default via 10.1.8.2 dev eth0 proto static metric 100 
10.1.8.0/24 dev eth0 proto kernel scope link src 10.1.8.132 metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
blackhole 192.168.16.128/26 proto bird 
192.168.16.129 dev cali930d6ab3b71 scope link 
192.168.116.192/26 via 10.1.8.131 dev eth0 proto bird 
  1. 目的地址为 docker1 容器 subnet 192.168.16.128/26 的路由。
  2. 目的地址为本地 bbox2 容器 192.168.16.129 的路由。

此时docker1会增加1条路由:192.168.16.128/26 via 10.1.8.32 dev eth0 proto bird ,因为该网络是docker2上容器使用的子网。

[root@docker1 ~]# ip r
default via 10.1.8.2 dev eth0 proto static metric 100 
10.1.8.0/24 dev eth0 proto kernel scope link src 10.1.8.131 metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.16.128/26 via 10.1.8.132 dev eth0 proto bird 
192.168.116.192 dev cali043af24fe74 scope link 
blackhole 192.168.116.192/26 proto bird 

Calico 网络连通性

bbox1 与 bbox2 的连通性:

[root@docker1 ~]# docker exec bbox1 ping -c 2 192.168.16.129
PING 192.168.16.129 (192.168.16.129): 56 data bytes
64 bytes from 192.168.16.129: seq=0 ttl=62 time=2.293 ms
64 bytes from 192.168.16.129: seq=1 ttl=62 time=0.553 ms

--- 192.168.16.129 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.553/1.423/2.293 ms

DNS解析失败:

[root@docker1 ~]# docker exec bbox1 ping -c 2 bbox2
ping: bad address 'bbox2'

ping 成功,数据包流向如下:

① 根据 bbox1 的路由表,将数据包从 cal0 发出。

[root@docker1 ~]# docker exec bbox1 ip r
default via 169.254.1.1 dev cali0 
169.254.1.1 dev cali0 scope link 

② 数据经过 veth pair 到达 docker1,查看路由表,数据由 eth0 发给 docker2(10.1.8.32)。

路由规则:192.168.16.128/26 via 10.1.8.32 dev eth0 proto bird

③ docker2 收到数据包,根据路由表发送给 cali930d6ab3b71,进而通过 veth pair cali0 到达 bbox2。

路由规则:192.168.16.129 dev cali930d6ab3b71 scope link

Calico 网络隔离性

创建 cal_net2

[root@docker1 ~]# docker network create --driver calico --ipam-driver calico-ipam cal_net2
5e518a8e89df7ab3f8490db1f05afa28f3559f5bff89b3cbec596b076f006d4d

在 docker1 中运行容器 bbox3,连接到 cal_net2:

[root@docker1 ~]# docker run --net cal_net2 --name bbox3 -tid busybox
c2d9672e038dec90bc675a105673e98ffc55095816edac2e98ca52a9d141844c
[root@docker1 ~]# docker exec bbox3 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
7: cali0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
    inet 192.168.116.193/32 brd 192.168.116.193 scope global cali0
       valid_lft forever preferred_lft forever

验证 bbox1 与 bbox3 的连通性。

[root@docker1 ~]# docker exec bbox1 ping -c 2 192.168.116.193
PING 192.168.116.193 (192.168.116.193): 56 data bytes

--- 192.168.116.193 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

虽然 bbox1 和 bbox3 都位于 docker1,但它们属于不同的 calico 网络,默认不能通行。

定制 Calico Policy

**calico 默认的 policy 规则是:**容器只能与同一个 calico 网络中的容器通信。

calico 的每个网络都有一个同名的 profile,profile 中定义了该网络的 policy。

cal_net1 的 profile如下:

[root@docker1 ~]# calicoctl get profile cal_net1 -o yaml
- apiVersion: v1
  kind: profile
  metadata:
    name: cal_net1
    tags:
    - cal_net1
  spec:
    egress:
    - action: allow
      destination: {}
      source: {}
    ingress:
    - action: allow
      destination: {}
      source:
        tag: cal_net1

说明:

  • 命名为 cal_net1,这就是 calico 网络 cal_net1 的 profile。
  • 为 profile 添加一个 tag cal_net1。注意,这个 tag 虽然也叫 cal_net1,其实可以随便设置,这跟上面的 name: cal_net1 没有任何关系。此 tag 后面会用到。
  • egress 对从容器发出的数据包进行控制,当前没有任何限制。
  • ingress 对进入容器的数据包进行限制,当前设置是接收来自 tag cal_net1 的容器,根据第 ① 步设置我们知道,实际上就是只接收本网络的数据包,这也进一步解释了前面的实验结果。

Calico 能够让用户定义灵活的 policy 规则,精细化控制进出容器的流量,下面我们就来实践一个场景:

  1. 创建一个新的 calico 网络 cal_web 并部署一个 httpd 容器 web1
  2. 定义 policy 允许 cal_net2 中的容器访问 web1 的 80 端口。

首先创建 cal_web

[root@docker1 ~]# docker network create --driver calico --ipam-driver calico-ipam cal_web
bf0cae07198db958da1fa5dffb33d98085aa2f4a41282c76dd4b09f2378b36f9

在 docker1中运行容器 web1,连接到 cal_web:

[root@docker1 ~]# docker run --net cal_web --name web1 -d httpd
bceaa77de60e54071e232e9e5634096c171a4ee0b7ba753a6d2d3d90b9a12cb5
[root@docker1 ~]# docker inspect web1 |grep 192
                    "IPAddress": "192.168.116.194",

目前 bbox3 还无法访问 web1 的 80 端口。

[root@docker1 ~]# docker exec bbox3 wget 192.168.116.194
Connecting to 192.168.116.194 (192.168.116.194:80)
wget: can't connect to remote host (192.168.116.194): Connection timed out

创建 policy 文件 web.yml:

[root@docker1 ~]# calicoctl get profile cal_web -o yaml > web.yml
[root@docker1 ~]# vim web.yml
- apiVersion: v1
  kind: profile
  metadata:
    name: cal_web
  spec:
    ingress:
    - action: allow
      protocol: tcp
      destination:
        ports:
        - 80
      source:
        tag: cal_net2

应用policy

[root@docker1 ~]# calicoctl apply -f web.yml
Successfully applied 1 'profile' resource(s)

现在 bbox3 已经能够访问 web1 的 http 服务了。

[root@docker1 ~]# docker exec bbox3 wget 192.168.116.194
Connecting to 192.168.116.194 (192.168.116.194:80)
saving to 'index.html'
index.html           100% |********************************|    45  0:00:00 ETA
'index.html' saved

不过 ping 还是不行,因为只放开了 80 端口。

上面这个例子比较简单,不过已经向我们展示了 calico 强大的 policy 功能。通过 policy,可以动态实现非常复杂的容器访问控制。有关 calico policy 更多的配置,可参看官网文档 http://docs.projectcalico.org/v2.0/reference/calicoctl/resources/policy。

定制 Calico IP 池

calico 会为自动为网络分配 subnet,当然我们也可以定制。

定义一个 IP Pool,并创建calico网络:

[root@docker1 ~]# cat << EOF >ipPool.yml
- apiVersion: v1
  kind: ipPool
  metadata:
    cidr: 17.2.0.0/16
EOF
[root@docker1 ~]# calicoctl create -f ipPool.yml
Successfully created 1 'ipPool' resource(s)
[root@docker1 ~]# docker network create --driver calico --ipam-driver calico-ipam --subnet=17.2.0.0/16 my_net
3a3ef767f259ad7f3688c5aa4905a53b03992da3eebd15244cd36e9c269cc0ea

此时运行容器将分配到指定 subnet 中的 IP。

[root@docker1 ~]# docker run --net my_net -it --rm busybox
/ # ip a show cali0
11: cali0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
    inet 17.2.116.192/32 brd 17.2.116.192 scope global cali0
       valid_lft forever preferred_lft forever
/ # exit

当然也可以通过 --ip 为容器指定 IP,但必须在 subnet 范围之内。

网络策略

网络策略介绍

默认情况,集群网络连通性如下:

  • 集群外部主机可以访问集群内部应用
  • 集群内部应用也可以访问集群外部主机
  • 各个namespace之间没有做任何的隔离策略

如果希望在 IP 地址或端口层面控制网络流量, 考虑使用 Kubernetes 网络策略(NetworkPolicy)。

  • NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” 通信。
  • NetworkPolicy 适用于一端或两端与 Pod 的连接,与其他连接无关。

**提示:**网络策略通过网络插件来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案,例如 calico。

网络策略规约

Pod 有两种隔离: 出口的隔离入口的隔离

  • 默认情况下,**一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。**如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Egress”,则该 Pod 是出口隔离的, 我们称这样的策略适用于该 Pod 的出口。当一个 Pod 的出口被隔离时, 唯一允许的来自 Pod 的连接是适用于出口的 Pod 的某个 NetworkPolicy 的 egress 列表所允许的连接。 这些 egress 列表的效果是相加的。

  • 默认情况下,**一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。**如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Ingress”,则该 Pod 被隔离入口, 我们称这种策略适用于该 Pod 的入口。当一个 Pod 的入口被隔离时,唯一允许进入该 Pod 的连接是来自该 Pod 节点的连接和适用于入口的 Pod 的某个 NetworkPolicy 的 ingress 列表所允许的连接。这些 ingress 列表的效果是相加的。

**网络策略是相加的,所以不会产生冲突。**如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。

**要允许从源 Pod 到目的 Pod 的连接,则源 Pod 的出口策略和目的 Pod 的入口策略都需要允许连接。**如果任何一方不允许连接,建立连接将会失败。

示例:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  # 使用标签过滤限制哪些pod
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  # Ingress控制外部访问内部
  - Ingress
  # Egress控制pod访问外部
  - Egress
  ingress:
  # from限制允许哪些主机可以访问pod
  - from:
    # 通过ip限制
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    # 限制namespace中pod
    - namespaceSelector:
        matchLabels:
          project: myproject
    # 限制同一namespace中pod
    - podSelector:
        matchLabels:
          role: frontend
    # 限制pod上哪些端口可以访问
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978
  • 必需字段:与所有其他的 Kubernetes 配置一样,NetworkPolicy 需要 apiVersionkindmetadata 字段。

  • spec:NetworkPolicy 规约 中包含了在一个命名空间中定义特定网络策略所需的所有信息。

  • spec.podSelector:每个 NetworkPolicy 都包括一个 podSelector, 它对该策略所适用的一组 Pod 进行选择。示例中的策略选择带有 “role=db” 标签的 Pod。 空的 podSelector 选择命名空间下的所有 Pod。

  • spec.policyTypes:每个 NetworkPolicy 都包含一个 policyTypes 列表,其中包含 IngressEgress 或两者兼具。policyTypes 字段表示给定的策略是应用于进入所选 Pod 的入站流量还是来自所选 Pod 的出站流量,或两者兼有。 如果 NetworkPolicy 未指定 policyTypes 则默认情况下始终设置 Ingress; 如果 NetworkPolicy 有任何出口规则的话则设置 Egress

  • spec.ingress:每个 NetworkPolicy 可包含一个 ingress 规则的白名单列表。 每个规则都允许同时匹配 fromports 部分的流量。示例策略中包含一条简单的规则: 它匹配某个特定端口,来自三个来源中的一个:

    • 第一个通过 ipBlock 指定
    • 第二个通过 namespaceSelector 指定
    • 第三个通过 podSelector 指定。
  • spec.egress:每个 NetworkPolicy 可包含一个 egress 规则的白名单列表。 每个规则都允许匹配 toport 部分的流量。该示例策略包含一条规则, 该规则将指定端口上的流量匹配到 10.0.0.0/24 中的任何目的地。

to 和 from 选择器

可以在 ingressfrom 部分或 egressto 部分中指定四种选择器:

  • podSelector:此选择器将在与 NetworkPolicy 相同的命名空间中选择特定的 Pod,应将其允许作为入站流量来源或出站流量目的地。

  • namespaceSelector:此选择器将选择特定的命名空间,应将所有 Pod 用作其入站流量来源或出站流量目的地。

  • namespaceSelector 和 podSelector:指定 namespaceSelectorpodSelectorto/from 条目选择特定命名空间中的特定 Pod。

    示例:

      ...
      ingress:
      - from:
        - namespaceSelector:
            matchLabels:
              user: alice
          podSelector:
            matchLabels:
              role: client
      ...
    

    from 数组中包含两个元素,来自标有 user=alice 的命名空间中标有 role=client 的 Pod 连接。

  • ipBlock:此选择器将选择特定的 IP CIDR 范围以用作入站流量来源或出站流量目的地。 这些应该是集群外部 IP,因为 Pod IP 存在时间短暂的且随机产生。

    集群的入站和出站机制通常需要重写数据包的源 IP 或目标 IP。 在发生这种情况时,不确定在 NetworkPolicy 处理之前还是之后发生, 并且对于网络插件、云提供商、Service 实现等的不同组合,其行为可能会有所不同。

    • 对入站流量而言,这意味着在某些情况下,你可以根据实际的原始源 IP 过滤传入的数据包, 而在其他情况下,NetworkPolicy 所作用的 源IP 则可能是 LoadBalancer 或 Pod 的节点等。

    • 对于出站流量而言,这意味着从 Pod 到被重写为集群外部 IP 的 Service IP 的连接可能会或可能不会受到基于 ipBlock 的策略的约束。

实施网络策略

准备实验环境

环境说明:

  • namespace-web 中有3个 pod:web1、web2、test
  • namespace-laoma 中有1个pod:test
  • 如没有特别说明,默认namespace是web
root@master30:~# kubectl create ns web
root@master30:~# kubens web

# 创建 web1
root@master30:~# kubectl run web1 --image=hub.laoma.cloud/library/nginx --image-pull-policy=IfNotPresent
root@master30:~# kubectl exec -it web1 -- bash -c 'echo Hello web1 > /usr/share/nginx/html/index.html'

# 创建 web2
root@master30:~# kubectl run web2 --image=hub.laoma.cloud/library/nginx --image-pull-policy=IfNotPresent
root@master30:~# kubectl exec -it web2 -- bash -c 'echo Hello web2 > /usr/share/nginx/html/index.html'

# 创建service web1和web2
root@master30:~# kubectl expose pod web1 --port=80 --target-port=80 --type=NodePort
root@master30:~# kubectl expose pod web2 --port=80 --target-port=80 --type=NodePort
root@master30:~# kubectl get service
NAME   TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
web1   NodePort   10.103.36.143    <none>        80:30790/TCP   11s
web2   NodePort   10.111.250.224   <none>        80:32686/TCP   7s

# 同一namespace中web1和web2互相访问
root@master30:~/network# kubectl run test --image=hub.laoma.cloud/library/nginx
root@master30:~/network# kubectl exec -it test -- curl -s web1
Hello web1
root@master30:~/network# kubectl exec -it test -- curl -s web2
Hello web2

# 在不同namespace-laoma中创建测试pod-test,访问web1和web2
root@master30:~# kubectl create ns laoma
root@master30:~# kubectl run test -n laoma --image=hub.laoma.cloud/library/nginx
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web
Hello web1
root@master30:~# kubectl exec -n laoma test -- curl -s web2.web
Hello web2

# 集群内访问service web1和web2
root@master30:~# curl 10.103.36.143
Hello web1
root@master30:~# curl 10.111.250.224
Hello web2

# 集群外节点访问web1和web2
root@client:~# curl http://10.1.8.30:30790
Hello web1
root@client:~# curl http://10.1.8.30:32686
Hello web2
根据 pod 标签限定

根据pod标签限定:

  • 限定同一ns中pod之间访问
  • 所有其他ns中pod或者集群外主机都无法访问ns中pod

示例1:允许同一ns中具有标签run: test的pod,访问具有标签run: web1的pod 80端口。

root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 80
# 创建network policy
root@master30:~# kubectl apply -f netpol.yaml
root@master30:~# kubectl get netpol
NAME                POD-SELECTOR   AGE
my-network-policy   run=web1       4s

# 同一namespace中test可以访问web1,web2不可以访问web1
root@master30:~# kubectl exec test -- curl -s web1
Hello web1

# 不同 namespace 中pod,标签满足不可以访问
root@master30:~# kubectl exec test -n laoma -- curl -s --connect-timeout 3 web1.web
command terminated with exit code 28

# 修改现有标签,同一命名空间的test pod也无法访问了。
root@master30:~/network# kubectl label pod test run=web2 --overwrite
root@master30:~/network# kubectl get pods --show-labels 
NAME   READY   STATUS    RESTARTS   AGE    LABELS
test   1/1     Running   0          9m9s   run=web2
web1   1/1     Running   0          13m    run=web1
web2   1/1     Running   0          13m    run=web2

# 超时退出
root@master30:~/network# kubectl exec test -- curl -s --connect-timeout 3 web1
command terminated with exit code 28

示例2:允许同一namespace中所有pod访问具有标签run: web1的pod 80端口。

设置podSelector: {},则针对namespace中所有pod。

root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}
    ports:
    - protocol: TCP
      port: 80
# 应用策略
root@master30:~# kubectl apply -f netpol.yaml

# 同一namespace中所有pod都可以访问web1
root@master30:~# kubectl exec test -- curl -s web1
Hello web1
root@master30:~# kubectl exec web2 -- curl -s web1
Hello web1

# 不同namespace中pod不可以访问web1
root@master30:~# kubectl exec test -n laoma -- curl -s --connect-timeout 3 web1.web
command terminated with exit code 28

示例3:允许同一ns中具有标签run: test的pod,访问所有pod 80端口。

设置podSelector: {},则针对namespace中所有pod。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 80
根据 ns 标签限定

用于限定,来源于其他namespace中pod。

示例1:允许具有标签project: myproject的namespace中所有pod,访问当前ns中具有标签run: web1的pod 80端口。

root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          project: myproject
    ports:
    - protocol: TCP
      port: 80
# 应用策略
root@master30:~# kubectl apply -f netpol.yaml

# 此时namespace-web中pod和namespace-laoma中pod都无法访问web1
root@master30:~# kubectl exec test -- curl -s --connect-timeout 3 web1
root@master30:~# kubectl exec -n laoma test -- curl -s --connect-timeout 3 web1.web

# 给namespace-laoma添加标签project=myproject,此时namespace-laoma中pod可以访问web1.web
root@master30:~# kubectl label namespaces laoma project=myproject
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web
Hello web1

示例2:允许所有namespace中所有pod,访问当前ns中具有标签run: web1的pod 80端口。

root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 80
# 创建network policy
root@master30:~# kubectl apply -f netpol.yaml

# 所有namespace中所有pod都可以访问web1
root@master30:~# kubectl exec test -- curl -s web1
Hello web1
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web
根据 pod IP 限定

示例1:允许网段 10.1.8.0/24 但不包括子网 10.1.8.128/26 中主机,访问具有标签run: web1的pod 80端口。

root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        # 放行 网段10.1.8.0/24
        cidr: 10.1.8.0/24
        # 不放行网段
        except:
        - 10.1.8.128/26
    ports:
    - protocol: TCP
      port: 80
# 应用策略
root@master30:~# kubectl apply -f netpol.yaml

# 访问失败
root@master30:~# kubectl exec test -- curl -s web1
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web

root@client:~# curl http://10.1.8.30:30251
root@client:~# curl http://10.1.8.31:30251
# 访问成功
root@client:~# curl http://10.1.8.32:30251
Hello web1

# 理论上:10.1.8.0/24网段中主机都可以通过集群任意节点访问web1
# 实际测试:只可以通过pod所在主机的IP访问web1 (10.1.8.32:30790),不可以访问10.1.8.30:30790或10.1.8.31:30790

# 在worker31上也可以通过10.103.36.143访问pod-web1
root@master30:~# kubectl describe pod web1|grep Node:
Node:         worker32.laoma.cloud/10.1.8.32

从实验测试结果来看,根据网段控制来源还不完善。

如果想放行所有主机,阻止部分主机,规则如下:

    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.1.8.0/26
限定所有端口

port参数不设置,代表限定所有端口。

root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
# 应用策略
root@master30:~# kubectl apply -f netpol.yaml

root@master30:~# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE                  NOMINATED NODE   READINESS GATES
test   1/1     Running   0          64m   10.224.193.70   worker32.laoma.cloud   <none>           <none>
web1   1/1     Running   0          82m   10.224.193.67   worker32.laoma.cloud   <none>           <none>
web2   1/1     Running   0          81m   10.224.41.132   worker31.laoma.cloud   <none>           <none>

# 此时满足条件的pod可以ping通pod地址
root@master30:~# kubectl run --rm -it ubuntu -l run=test --image ubuntu -- bash
root@ubuntu:/# apt update
root@ubuntu:/# apt install -y iputils-ping
root@ubuntu:/# ping -c1 10.224.193.67
PING 10.224.193.67 (10.224.193.67) 56(84) bytes of data.
64 bytes from 10.224.193.67: icmp_seq=1 ttl=62 time=0.831 ms

--- 10.224.193.67 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.831/0.831/0.831/0.000 ms

root@ubuntu:/# ping web1 -c1
PING web1.web.svc.cluster.local (10.96.144.145) 56(84) bytes of data.

--- web1.web.svc.cluster.local ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

root@ubuntu:/# exit

# 仍然 ping不通 service,因为service只开放80端口
限定端口范围
root@master30:~# vim netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 32000
      endPort: 32768
多条件规则

只要有一个满足条件就可以访问pod。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.1.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 80
# 创建network policy
root@master30:~# kubectl apply -f netpol.yaml

# namespace-web中pod-test可以访问web1
root@master30:~# kubectl exec test -- curl -s web1
Hello web1

默认策略

默认允许所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - {}
默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

此策略可以确保即使容器没有选择其他任何 NetworkPolicy,也仍然可以被隔离。 此策略不会更改默认的出口隔离行为。

默认允许所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - {}
默认拒绝所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改默认的入站流量隔离行为。

默认拒绝所有入口和所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被 允许入站或出站流量。

无法完成的工作

  • 特定于节点的策略

  • 基于名字的策略。

  • 实现适用于所有命名空间或 Pods 的默认策略。

  • 显式地拒绝策略的能力。NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略。

  • **禁止本地回路或指向宿主的网络流量。**Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求)。

环境清理

root@master30:~# kubectl delete ns network web laoma
root@master30:~# kubectl delete ns web
root@master30:~# kubectl delete ns laoma
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐