网络空间
网络空间
在 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 可以互相访问。
共享网络
可以通过本地主机访问 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 不会重建,也不会重启,这是非常重要的一个设计。