网络空间

网络空间

在 K8s 网络中存在两种 IP(Pod IP 和 Service Cluster IP),Pod IP 地址是实际存在于某个网卡(可以是虚拟设备)上的,Service Cluster IP 它是一个虚拟 IP,是由 kube-proxy 使用 Iptables 规则重新定向到其本地端口,再均衡到后端 Pod 的。每个 Pod 都拥有一个独立的 IP 地址(IPper Pod),而且假定所有的 pod 都在一个可以直接连通的、扁平的网络空间中。用户不需要额外考虑如何建立 Pod 之间的连接,也不需要考虑将容器端口映射到主机端口等问题。

同一个 Pod 的容器共享同一个网络命名空间,它们之间的访问可以用 localhost 地址 + 容器端口就可以访问。同一 Node 中 Pod 的默认路由都是 docker0 的地址,由于它们关联在同一个 docker0 网桥上,地址网段相同,所有它们之间应当是能直接通信的。不同 Node 中 Pod 间通信要满足 2 个条件:Pod 的 IP 不能冲突;将 Pod 的 IP 和所在的 Node 的 IP 关联起来,通过这个关联让 Pod 可以互相访问。

image

共享网络

可以通过本地主机访问 Pod 中的容器,它们使用相同的网络名称空间。对于容器,可观察的主机名是 Pod 的名称。由于容器共享相同的 IP 地址和端口空间,因此应在容器中使用不同的端口进行传入连接。因此,Pod 中的应用程序必须协调其端口使用情况。

在以下示例中,我们将创建一个多容器 Pod,其中一个容器中的 nginx 充当在第二个容器中运行的简单 Web 应用程序的反向代理。

共享网络

我们使用 Nginx 配置文件创建 ConfigMap。到端口 80 的传入 HTTP 请求将转发到本地主机上的端口 5000:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mc3-nginx-conf
data:
  nginx.conf: |-
    user  nginx;
    worker_processes  1;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;

    events {
        worker_connections  1024;
    }

    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;
        keepalive_timeout  65;

        upstream webapp {
            server 127.0.0.1:5000;
        }

        server {
            listen 80;

            location / {
                proxy_pass         http://webapp;
                proxy_redirect     off;
            }
        }
    }    

使用简单的 Web 应用程序和 Nginx 在单独的容器中创建一个多容器 Pod。请注意,对于 Pod,我们仅定义了 nginx 端口 80,在 Pod 外部将无法访问端口 5000。

apiVersion: v1
kind: Pod
metadata:
  name: mc3
  labels:
    app: mc3
spec:
  containers:
    - name: webapp
      image: training/webapp
    - name: nginx
      image: nginx:alpine
      ports:
        - containerPort: 80
      volumeMounts:
        - name: nginx-proxy-config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
  volumes:
    - name: nginx-proxy-config
      configMap:
        name: mc3-nginx-conf

然后通过 NodePort 服务暴露端口:

$ kubectl expose pod mc3 --type=NodePort --port=80
service "mc3" exposed

$ kubectl describe service mc3
...
NodePort:		<unset>	31418/TCP
...

Pause 镜像的作用

比如说现在有一个 Pod,其中包含了一个容器 A 和一个容器 B,它们两个就要共享 Network Namespace。在 K8s 里的解法是这样的:它会在每个 Pod 里,额外起一个 pause container 小容器来共享整个 Pod 的 Network Namespace。pause container 是一个非常小的镜像,大概 100~200KB 左右,是一个汇编语言写的、永远处于“暂停”状态的容器。由于有了这样一个 pause container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 pause container 的 Network Namespace 中。

所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 pause container。这就是 Pod 解决网络共享的一个解法。在 Pod 里面,一定有一个 IP 地址,是这个 Pod 的 Network Namespace 对应的地址,也是这个 pause container 的 IP 地址。所以大家看到的都是一份,而其他所有网络资源,都是一个 Pod 一份,并且被 Pod 中的所有容器共享。这就是 Pod 的网络实现方式。

由于需要有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 pause container 第一个启动。并且整个 Pod 的生命周期是等同于 pause container 的生命周期的,与容器 A 和 B 是无关的。这也是为什么在 K8s 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。

上一页