Kubernetes 要求集群中所有 Pod,无论是节点内还是跨节点,都可以直接通信,或者说所有 Pod 工作在同一跨节点网络,此网络一般是二层虚拟网络,称为 Pod 网络。在安装引导kubernetes时,由选择并安装的network plugin实现。默认情况下,集群中所有 Pod 之间、 Pod 与节点之间可以互通。

网络主要解决两个问题,一个是连通性,实体之间能够通过网络互通。另一个是隔离性,出于安全、限制网络流量的目的,又要控制实体之间的连通性。Network Policy用来实现隔离性,只有匹配规则的流量才能进入 Pod ,同理只有匹配规则的流量才可以离开 Pod 。

但请注意,kubernetes支持的用以实现 Pod 网络的network plugin有很多种,并不是全部都支持Network Policy,为kubernetes选择network plugin时需要考虑到这点,是否需要隔离?可用network plugin及是否支持Network Policy请参考 Networking and Network Policy1

基本原理

Network Policy是kubernetes中的一种资源类型,它从属于某个namespace。其内容从逻辑上看包含两个关键部分,一是 Pod 选择器,基于标签选择相同namespace下的 Pod ,将其中定义的规则作用于选中的 Pod 。另一个就是规则了,就是网络流量进出 Pod 的规则,其采用的是白名单模式,符合规则的通过,不符合规则的拒绝。

Network Policy对象Spec说明

首先给出示例Spec,结合示例说明Spec中的关键字段与逻辑,关于Spec完全说明参考。示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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

对象创建方法与其它如ReplicaSet相同。apiVersion、kind、metadata与其它类型对象含义相同,不详细描述。

.spec.PodSelector

顾名思义,它是 Pod 选择器,基于标签选择与Network Policy处于同一namespace下的 Pod ,如果 Pod 被选中,则对其应用Network Policy中定义的规则。此为可选字段,当没有此字段时,表示选中所有 Pod 。

.spec.PolicyTypes

Network Policy定义的规则可以分成两种,一种是入 Pod 的Ingress规则,一种是出 Pod 的Egress规则。本字段可以看作是一个开关,如果其中包含Ingress,则Ingress部分定义的规则生效,如果是Egress则Egress部分定义的规则生效,如果都包含则全部生效。当然此字段也可选,如果没有指定的话,则默认Ingress生效,如果Egress部分有定义的话,Egress才生效。怎么理解这句话,下文会提到,没有明确定义Ingress、Egress部分,它也是一种规则,默认规则而非没有规则。

.spec.ingress与.spec.egress

前者定义入 Pod 规则,后者定义出 Pod 规则,详细参考这里2,这里只讲一下重点。上例中ingress与egress都只包含一条规则,两者都是数组,可以包含多条规则。当包含多条时,条目之间的逻辑关系是“或”,只要匹配其中一条就可以。

.spec.ingress[].from

也是数组,数组成员对访问 Pod 的外部source进行描述,符合条件的source才可以访问 Pod ,有多种方法,如示例中的ip地址块、名称空间、 Pod 标签等,数组中的成员也是逻辑或的关系。spec.ingress[].from.prots表示允许通过的协议及端口号。

.spec.egress.to

定义的是 Pod 想要访问的外部destination,其它与ingress相同。

默认规则

不定义规则并非没有规则,此时默认规则生效,以下展示默认规则用法。

默认禁止所有入 Pod 流量(Default deny all ingress traffic)

1
2
3
4
5
6
7
8
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress

上例中没有定义 Pod 选择器,表示如果namespace下的某个 Pod 没有被任何Network Policy对象选中,则应用此对象,如果被其它Network Policy选中则不应用此对象。

policyTypes的值为Ingress,表示本例启用Ingress规则。但是本例没有定义具体的Ingress,那就应用默认规则。默认规则禁止所有入 Pod 流量,但例外情况是如果source就是 Pod 运行的节点,则允许通过。

默认允许所有入 Pod 流量(Default allow all ingress traffic)

1
2
3
4
5
6
7
8
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  ingress:
  - {}

同样没有定义 Pod 选择器,意义与上例同。注意ingress的定义,这个是有规则的,只是规则中的条目为空,与默认规则不同,表示全部允许通过。

默认禁止所有出 Pod 流量(Default deny all egress traffic)

1
2
3
4
5
6
7
8
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Egress

与默认禁止所有入 Pod 流量(Default deny all ingress traffic)同,只是流量由入变成出

默认允许所有出 Pod 流量(Default allow all egress traffic)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

与默认允许所有入 Pod 流量(Default allow all ingress traffic)同,只是流量由入变成出。

默认禁止所有入出 Pod 流量(Default deny all ingress and all egress traffic)

1
2
3
4
5
6
7
8
9
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

无需详解,但请注意, Pod 与所运行节点之间流量不受Network Policy限制。

In Action

下面通过一个真实示例展示Network Policy普通用法

用Deployment创建nginx Pod 实例并用service暴露

1
2
3
4
$ kubectl run nginx --image=nginx --replicas=2
deployment "nginx" created
$ kubectl expose deployment nginx --port=80
service "nginx" exposed

确认创建结果

1
2
3
4
5
6
7
8
$ kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
svc/kubernetes              10.100.0.1    <none>        443/TCP    46m
svc/nginx                   10.100.0.16   <none>        80/TCP     33s
 
NAME                        READY         STATUS        RESTARTS   AGE
po/nginx-701339712-e0qfq    1/1           Running       0          35s
po/nginx-701339712-o00ef    1/1           Running       0          35s

测试nginx服务连通性

1
2
3
4
5
6
7
8
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
 
Hit enter for command prompt
 
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
/ #

通过创建Network Policy对象添加隔离性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      run: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

只允许包含access: “true"标签的 Pod 访问nginx服务。

创建Network Policy

1
2
$ kubectl create -f nginx-policy.yaml
networkpolicy "access-nginx" created

测试隔离性

1
2
3
4
5
6
7
8
9
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
 
Hit enter for command prompt
 
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out
/ #

为 Pod 添加access: “true"标签测试连通性

1
2
3
4
5
6
7
8
$ kubectl run busybox --rm -ti --labels="access=true" --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
 
Hit enter for command prompt
 
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
/ #