1. 有一个项目,它有自己的MySQL数据库核Redis缓存中间件,我们只希望这个项目的应用能够访问该中间件。
  2. 假如有一个项目需要通过Ingress进行对外发布,我们想要除了Ingress外,其他的Namespace下的Pod都不能访问该项目。

假设有一个项目叫nw-demo,里面部署了三个微服务,分别是MySQL、Redis和Nginx。现需要对MySQL、Redis、Nginx进行隔离,分别实现如下效果:

  • MySQL、Redis只能被该Namespace下的Pod访问
  • Nginx可以被Ingress-nginx命名空间下的Pod和该Namespace下的Pod访问

首先创建该项目所用的Namespace(如果已经存在,或者用其他Namespace测试,也可以不创建):

kubectl create namespace nw-demo

创建MySQL服务,MySQL以容器启动时,必须root的密码,或者设置密码为空,所以需要设置一个MYSQL_ROOT_PASSWORD的变量:

kubectl create deployment mysql --image=mysql:5.7.23 -n nw-demo
kubectl set env deployment mysql MYSQL_ROOT_PASSWORD=mysql -n nw-demo

创建Redis服务:

kubectl create deployment redis --image=redis:5.0.9-alpine3.11 -n nw-demo

确认容器是否启动:

# kubectl get pods -n nw-demo -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP             NODE         NOMINATED NODE   READINESS GATES
mysql-69bbd89b95-757dn   1/1     Running   0          2m38s   10.33.125.62   k8s-node01   <none>           <none>
redis-64c566c6bf-tv5zz   1/1     Running   0          24s     10.33.125.63   k8s-node01   <none>           <none>

在没有配置任何网络策略时,测试网络的连通性,可以在任意Kubernetes节点上执行telnet命令:

# telnet 10.33.125.62 3306
Trying 10.33.125.62...
Connected to 10.33.125.62.
Escape character is '^]'.
J
5.7.23T\:E`:;\x.oy	H'Mmysql_native_password^CConnection closed by foreign host.
# telnet 10.33.125.63 6379
Trying 10.33.125.63...
Connected to 10.33.125.63.
Escape character is '^]'.
^]
telnet> quit
Connection closed.

可以看到此时的网络都是可以通信的。接下来可以根据Pod的标签进行网络隔离,首先查看Pod的标签:

# kubectl get pods -n nw-demo --show-labels
NAME                     READY   STATUS    RESTARTS   AGE     LABELS
mysql-69bbd89b95-757dn   1/1     Running   0          8m5s    app=mysql,pod-template-hash=69bbd89b95
redis-64c566c6bf-tv5zz   1/1     Running   0          5m51s   app=redis,pod-template-hash=64c566c6bf

然后根据标签配置网络策略,本示例的配置将MySQL和Redis进行了拆分,配置了两个网络策略,当然也可以给两个Pod配置一个相同的标签,这样就可以使用同一个网络策略进行限制。但是在生产环境中,并不推荐使用同一个网络策略,因为有时候需要更细粒度的策略,同一个网络策略可能会有局限性,也会导致配置失败,所以本示例采用分开的网络策略进行配置:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: mysql-np
  namespace: nw-demo
spec:
  podSelector:
    matchLabels:
      app: mysql
  policyTypes:
  - "Ingress"
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          access-nw-mysql-redis: "true"
    ports:
    - protocol: TCP
      port: 3306

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: redis-np
  namespace: nw-demo
spec:
  podSelector:
    matchLabels:
      app: redis
  policyTypes:
  - "Ingress"
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          access-nw-mysql-redis: "true"
    ports:
    - protocol: TCP
      port: 6379

该yaml含有两个NetworkPolicy,其中mysql-np是对具有app=mysql标签的Pod进行管理,redis-np是对具有app=redis标签的Pod进行管理。但是需要注意的是该网络策略的ingress from是以namespaceSelector的标签进行匹配的,并非podSelector,或者是两者的结合。因为在生产环境中,同一个Namespace下可能会有很多不同类型、不同标签的Pod,并且它们可能并不具有一个相同的标签,所以如果通过podSelector进行选择,可能会比较麻烦,因为Pod一旦创建,对其标签的修改是很不方便的(apps/v1一旦创建就不可修改)。而使用namespaceSelector另一个好处是,可以很方便的对某个Namespace下的Pod进行管控,直接给指定Namespace添加标签即可,当然,如果需要更细粒度的管控,也可以结合podSelector使用。

接下来创建该NetworkPolicy:

# kubectl create -f mysql-redis-nw.yaml
networkpolicy.networking.k8s.io/mysql-np created
networkpolicy.networking.k8s.io/redis-np created
# kubectl get networkpolicy -n nw-demo
NAME       POD-SELECTOR   AGE
mysql-np   app=mysql      16m
redis-np   app=redis      16m

创建后宿主机和任何Pod都不能访问该Namespace下的MySQL和Redis:

# telnet 10.33.125.63 6379
Trying 10.33.125.63...
^C
# telnet 10.33.125.62 3306
Trying 10.33.125.62...
^C

在nw-demo命名空间下创建一个用于测试连通性的工具,然后进行测试,也是不能访问该服务的:

# kubectl run -it busybox --image=busybox:1.28.3 -n nw-demo
If you don't see a command prompt, try pressing enter.
/ # telnet 10.33.125.62 3306
^C
/ # 

由于之前的from配置的是namespaceSelector,所以如果想要某一个Namespace下的Pod能够访问,直接给该Namespace添加一个NetworkPolicy中配置的标签即可,比如允许nw-demo命名空间下的所有Pod访问该NetworkPolicy隔离的服务:

# kubectl label namespaces nw-demo access-nw-mysql-redis=true
namespace/nw-demo labeled

使用nw-demo命名空间下的busybox再次测试:

# kubectl exec -it busybox  -n nw-demo -- sh
/ # telnet 10.33.125.62 3306
J
5.7.23/(TXs^%bx0
                kREn	Yqmysql_native_password^C
Console escape. Commands are:

 l	go to line mode
 c	go to character mode
 z	suspend telnet
 e	exit telnet
/ #

此时nw-demo下的Pod已经可以访问MySQL和Redis,可以对其他Namespace下的Pod进行测试,比如在default命名空间进行测试:

# kubectl run -it busybox --image=busybox:1.28.3 -n default
If you don't see a command prompt, try pressing enter.
/ # telnet 10.33.125.62 3306
^C
/ #

可以看到此时default命名空间下的Pod并不能访问nw-demo的服务,如果想要MySQL和Redis对default命名空间开放,只需添加一个access-nw-mysql-redis=true的标签即可。
相对于传统架构,对中间件的访问限制,在Kubernetes中实现同样的效果,可能配置更加方便且易于管理。