Service

Service 概念以及作用

Kubernetes 中 Pod 是随时可以消亡的(节点故障、容器内应用程序错误等原因)。如果使用 Deployment 运行您的应用程序,Deployment 将会在 Pod 消亡后再创建一个新的 Pod 以维持所需要的副本数。每一个 Pod 有自己的 IP 地址,然而,对于 Deployment 而言,对应 Pod 集合是动态变化的。

这个现象导致了如下问题:

  • 如果某些 Pod(假设是 'backends')为另外一些 Pod(假设是 'frontends')提供接口,在 'backends' 中的 Pod 集合不断变化(IP 地址也跟着变化)的情况下,'frontends' 中的 Pod 如何才能知道应该将请求发送到哪个 IP 地址?

Service 存在的意义,就是为了解决这个问题。

Kubernetes 中 Service 是一个 API 对象,通过 kubectl + YAML 或者 Kuboard,定义一个 Service,可以将符合 Service 指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者。

Service 是 Kubernetes 中的一种服务发现机制:

  • Pod 有自己的 IP 地址
  • Service 被赋予一个唯一的 dns name
  • Service 通过 label selector 选定一组 Pod
  • Service 实现负载均衡,可将请求均衡分发到选定这一组 Pod 中

例如,假设有一个无状态的图像处理后端程序运行了 3 个 Pod 副本。这些副本是相互可替代的(前端程序调用其中任何一个都可以)。在后端程序的副本集中的 Pod 经常变化(销毁、重建、扩容、缩容等)的情况下,前端程序不应该关注这些变化。

Kubernetes 通过引入 Service 的概念,将前端与后端解耦。

创建Service

以下yaml创建一组pod

  • 每个pod都监听9376 tcp端口
  • 每个pod都有标签 app=Myapp
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
  • k8s将为该service分配一个ip地址(clusterIP或者集群内ip)
  • 不断扫描符合该selector的pod,并将最新的结果更新到与service同名的my-service的endpoint对象中。
  • Pod 的定义中,Port 可能被赋予了一个名字,您可以在 Service 的targetPort 字段引用这些名字,而不是直接写端口号。这种做法可以使得您在将来修改后端程序监听的端口号,而无需影响到前端程序。
  • Service 的默认传输协议是 TCP,您也可以使用其他支持的传输协议。
  • Kubernetes Service 中,可以定义多个端口,不同的端口可以使用相同或不同的传输协议

虚拟IP和服务代理

Kubernetes 集群中的每个节点都运行了一个 kube-proxy,负责为 Service(ExternalName 类型的除外)提供虚拟 IP 访问。

k8s支持三种proxy mode,分别是

  • User space proxy mode
  • Iptables proxy mode
  • Ipvs proxy mode

User space 代理模式

  • kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
  • kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 打开一个随机端口
  • kube-proxy 安装 iptables 规则,将发送到该 Service 的 ClusterIP(虚拟 IP)/ Port 的请求重定向到该随机端口
  • 任何发送到该随机端口的请求将被代理转发到该 Service 的后端 Pod 上(kube-proxy 从 Endpoint 信息中获得可用 Pod)
  • kube-proxy 在决定将请求转发到后端哪一个 Pod 时,默认使用 round-robin(轮询)算法,并会考虑到 Service 中的SessionAffinity 的设定

k8s学习-服务发现以及网络1_ico


Iptables代理模式

iptables代理模式为默认的代理模式

在 iptables proxy mode 下:

  • kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
  • kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 安装 iptable 规则
  • iptables 将发送到 Service 的 ClusterIP / Port 的请求重定向到 Service 的后端 Pod 上
  • 对于 Service 中的每一个 Endpoint,kube-proxy 安装一个 iptable 规则
  • 默认情况下,kube-proxy 随机选择一个 Service 的后端 Pod

k8s学习-服务发现以及网络1_nginx_02

iptables proxy mode 的优点:

  • 更低的系统开销:在 linux netfilter 处理请求,无需在 userspace 和 kernel space 之间切换
  • 更稳定

与 user space mode 的差异:

  • 使用 iptables mode 时,如果第一个 Pod 没有响应,则创建连接失败
  • 使用 user space mode 时,如果第一个 Pod 没有响应,kube-proxy 会自动尝试连接另外一个后端 Pod

IPVS代理模式

  • kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
  • kube-proxy 根据监听到的事件,调用 netlink 接口,创建 IPVS 规则;并且将 Service/Endpoint 的变化同步到 IPVS 规则中
  • 当访问一个 Service 时,IPVS 将请求重定向到后端 Pod

k8s学习-服务发现以及网络1_ico_03

IPVS 模式的优点

IPVS proxy mode 基于 netfilter 的 hook 功能,与 iptables 代理模式相似,但是 IPVS 代理模式使用 hash table 作为底层的数据结构,并在 kernel space 运作。这就意味着

  • IPVS 代理模式可以比 iptables 代理模式有更低的网络延迟,在同步代理规则时,也有更高的效率
  • 与 user space 代理模式 / iptables 代理模式相比,IPVS 模式可以支持更大的网络流量

IPVS 提供更多的负载均衡选项:

  • rr: round-robin
  • lc: least connection (最小打开的连接数)
  • dh: destination hashing
  • sh: source hashing
  • sed: shortest expected delay
  • nq: never queue

代理模式总结

在所有的代理模式中,发送到 Service 的 IP:Port 的请求将被转发到一个合适的后端 Pod,而无需调用者知道任何关于 Kubernetes/Service/Pods 的细节。

Service 中额外字段的作用:

  • service.spec.sessionAffinity
  • 默认值为 "None"
  • 如果设定为 "ClientIP",则同一个客户端的连接将始终被转发到同一个 Pod
  • service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
  • 默认值为 10800 (3 小时)
  • 设定会话保持的持续时间

多端口的service

可以在一个service对象中定义多个端口,此时必须每个端口定义一个名字

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377


使用自定义的ip地址

创建 Service 时,如果指定 .spec.clusterIP​ 字段,可以使用自定义的 Cluster IP 地址。该 IP 地址必须是 APIServer 中配置字段 service-cluster-ip-range CIDR 范围内的合法 IPv4 或 IPv6 地址,否则不能创建成功。

可能用到自定义 IP 地址的场景:

  • 想要重用某个已经存在的 DNS 条目
  • 遗留系统是通过 IP 地址寻址,且很难改造

服务发现


Kubernetes 支持两种主要的服务发现模式:

  • 环境变量
  • DNS

环境变量

kubelet 查找有效的 Service,并针对每一个 Service,向其所在节点上的 Pod 注入一组环境变量。支持的环境变量有:

  • Docker links 兼容 (opens new window)的环境变量
  • {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT
  • Service name 被转换为大写
  • 小数点. 被转换为下划线_

例如,Service redis-master 暴露 TCP 端口 6379,其 Cluster IP 为 10.0.0.11,对应的环境变量如下所示:

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

如果要在 Pod 中使用基于环境变量的服务发现方式,必须先创建 Service,再创建调用 Service 的 Pod。否则,Pod 中不会有该 Service 对应的环境变量。

DNS

CoreDNS 监听 Kubernetes API 上创建和删除 Service 的事件,并为每一个 Service 创建一条 DNS 记录。集群中所有的 Pod 都可以使用 DNS Name 解析到 Service 的 IP 地址。

例如,名称空间 my-ns 中的 Service my-service,将对应一条 DNS 记录 my-service.my-ns。 名称空间 my-ns 中的Pod可以直接 nslookup my-service (my-service.my-ns 也可以)。其他名称空间的 Pod 必须使用 my-service.my-ns。my-service 和 my-service.my-ns 都将被解析到 Service 的 Cluster IP。

Kubernetes 同样支持 DNS SRV(Service)记录,用于查找一个命名的端口。假设 my-service.my-ns Service 有一个 TCP 端口名为 http,则,您可以 nslookup _http._tcp.my-service.my-ns 以发现该Service 的 IP 地址及端口 http

对于 ExternalName 类型的 Service,只能通过 DNS 的方式进行服务发现。

Headless Services

Headless” Service 不提供负载均衡的特性,也没有自己的 IP 地址。创建 “headless” Service 时,只需要指定 .spec.clusterIP 为 "None"。

“Headless” Service 可以用于对接其他形式的服务发现机制,而无需与 Kubernetes 的实现绑定。

对于 “Headless” Service 而言:

  • 没有 Cluster IP
  • kube-proxy 不处理这类 Service
  • Kubernetes不提供负载均衡或代理支持

DNS 的配置方式取决于该 Service 是否配置了 selector:

  • 配置了 SelectorEndpoints Controller 创建Endpoints 记录,并修改 DNS 配置,使其直接返回指向 selector 选取的 Pod 的 IP 地址
  • 没有配置 SelectorEndpoints Controller 不创建Endpoints 记录。DNS服务返回如下结果中的一种:
  • 对 ExternalName 类型的 Service,返回 CNAME 记录
  • 对于其他类型的 Service,返回与 Service 同名的Endpoints 的 A 记录

发布service

service类型

Kubernetes 中可以通过不同方式发布 Service,通过 ServiceType​ 字段指定,该字段的默认值是 ClusterIP,可选值有:

  • ClusterIP:  默认值。通过集群内部的一个 IP 地址暴露 Service,只在集群内部可以访问
  • NodePort: 通过每一个节点上的的静态端口(NodePort)暴露 Service,同时自动创建 ClusterIP 类型的访问方式
  • 在集群内部通过 $(ClusterIP): $(Port) 访问
  • 在集群外部通过 $(NodeIP): $(NodePort) 访问
  • LoadBalancer:  通过云服务供应商(AWS、Azure、GCE 等)的负载均衡器在集群外部暴露 Service,同时自动创建 NodePort 和 ClusterIP 类型的访问方式
  • 在集群内部通过 $(ClusterIP): $(Port) 访问
  • 在集群外部通过 $(NodeIP): $(NodePort) 访问
  • 在集群外部通过 $(LoadBalancerIP): $(Port) 访问
  • ExternalName:  将 Service 映射到 externalName 指定的地址(例如:foo.bar.example.com),返回值是一个 CNAME 记录。不使用任何代理机制。

ClusterIp

ClusterIP 是 ServiceType 的默认值

NodePort

对于 NodePort​ 类型的 Service,Kubernetes 为其分配一个节点端口(对于同一 Service,在每个节点上的节点端口都相同),该端口的范围在初始化 apiserver 时可通过参数 --service-node-port-range​ 指定(默认是:30000-32767),参考 修改NodePort的范围​。节点将该端口上的网络请求转发到对应的 Service 上。可通过 Service 的 .spec.ports[*].nodePort 字段查看该 Service 分配到的节点端口号。

在启动 kube-proxy 时使用参数 --nodeport-address 可指定阶段端口可以绑定的 IP 地址段。该参数接收以逗号分隔的 CIDR 作为参数值(例如:10.0.0.0/8,192.0.2.0/25),kube-proxy 将查找本机符合该 CIDR 的 IP 地址,并将节点端口绑定到符合的 IP 地址上。

例如,

  • 如果启动 kube-proxy 时指定了参数--nodeport-address=127.0.0.0/8,则 kube-proxy 只将阶段端口绑定到 loopback 地址上。
  • --nodeport-address 的默认值是一个空列表。则 kube-proxy 将节点端口绑定到该节点所有的网络 IP 地址上。

您可以通过 nodePort 字段指定节点端口号(必须在 --service-node-port-range 指定的范围内)。Kubernetes 在创建 Service 时将使用该节点端口,如果该端口已被占用,则创建 Service 将不能成功。在这种情况下,您必须自己规划好端口使用,以避免端口冲突。

使用 NodePort,您可以:

  • 根据自己的需要配置负载均衡器
  • 配置 Kubernetes / 非 Kubernetes 的混合环境
  • 直接暴露一到多个节点的 IP 地址,以便客户端可访问 Kubernetes 中的 Service

NodePort 类型的 Service 可通过如下方式访问:

  • 在集群内部通过 $(ClusterIP): $(Port) 访问
  • 在集群外部通过 $(NodeIP): $(NodePort) 访问

LoadBalancer

在支持外部负载均衡器的云环境中(例如 GCE、AWS、Azure 等),将 .spec.type​ 字段设置为 LoadBalancer​,Kubernetes 将为该Service 自动创建一个负载均衡器。负载均衡器的创建操作异步完成,您可能要稍等片刻才能真正完成创建,负载均衡器的信息将被回写到 Service 的 .status.loadBalancer 字段

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155

ExternalName

ExternalName 类型的 Service 映射到一个外部的 DNS name,而不是一个 pod label selector。可通过 spec.externalName 字段指定外部 DNS name。

下面的例子中,名称空间 prod 中的 Service my-service 将映射到 my.database.example.com

apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com

执行 nslookup my-service.prod.svc.cluster.local​ 指令时,集群的 DNS 服务将返回一个 CNAME​ 记录,其对应的值为 my.database.example.com​。访问 my-service​ 与访问其他类型的 Service 相比,网络请求的转发发生在 DNS level,而不是使用 proxy。如果您在后续想要将 my.database.example.com 对应的数据库迁移到集群内部来,您可以按如下步骤进行:

  1. 在 Kubernetes 中部署数据库(并启动数据库的 Pod)
  2. 为 Service 添加合适的 selector 和 endpoint
  3. 修改 Service 的类型

External Ip

如果有外部 IP 路由到 Kubernetes 集群的一个或多个节点,Kubernetes Service 可以通过这些 externalIPs​ 进行访问。externalIP 需要由集群管理员在 Kubernetes 之外配置。

在 Service 的定义中, externalIPs 可以和任何类型的 .spec.type 一通使用。在下面的例子中,客户端可通过 80.11.12.10:80 (externalIP:port) 访问my-service

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10

Service/POD的DNS

Kubernetes 集群中运行了一组 DNS Pod,配置了对应的 Service,并由 kubelete 将 DNS Service 的 IP 地址配置到节点上的容器中以便解析 DNS names。

集群中的每一个 Service(包括 DNS 服务本身)都将被分配一个 DNS name。默认情况下,客户端 Pod 的 DNS 搜索列表包括 Pod 所在的名称空间以及集群的默认域。例如:

假设名称空间 bar 中有一个 Service 名为 foo:

  • 名称空间bar 中的 Pod 可以通过nslookup foo 查找到该 Service
  • 名称空间quux 中的 Pod 可以通过nslookup foo.bar 查找到该 Service

Services

A记录

Kubernetes 集群中运行了一组 DNS Pod,配置了对应的 Service,并由 kubelete 将 DNS Service 的 IP 地址配置到节点上的容器中以便解析 DNS names。

集群中的每一个 Service(包括 DNS 服务本身)都将被分配一个 DNS name。默认情况下,客户端 Pod 的 DNS 搜索列表包括 Pod 所在的名称空间以及集群的默认域。例如:

假设名称空间 bar 中有一个 Service 名为 foo:

  • 名称空间bar 中的 Pod 可以通过nslookup foo 查找到该 Service
  • 名称空间quux 中的 Pod 可以通过nslookup foo.bar 查找到该 Service

SRV 记录

Service(含 headless Service)的命名端口(有 name 的端口)将被分配一个 SRV 记录,其格式为 _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster-domain.example:

  • 对于一个普通 Service(非 headless Service),该 SRV 记录解析到其端口号和域名my-svc.my-namespace.svc.cluster-domain.example
  • 对于一个 Headless Service,该 SRV 记录解析到多个结果:每一个结果都对应该 Service 的一个后端 Pod,包含其端口号和 Pod 的域名auto-generated-pod-name.my-svc.my-namespace.svc.cluster-domain.example

Pods

Pod 的 hostname / subdomain

Kubernetes 在创建 Pod 时,将 Pod 定义中的 metadata.name 的值作为 Pod 实例的 hostname。

Pod 定义中有一个可选字段 spec.hostname 可用来直接指定 Pod 的 hostname。例如,某 Pod 的 spec.hostname 字段被设置为 my-host,则该 Pod 创建后 hostname 将被设为 my-host

Pod 定义中还有一个可选字段 spec.subdomain 可用来指定 Pod 的 subdomain。例如,名称空间 my-namespace 中,某 Pod 的 hostname 为 foo,并且 subdomain 为 bar,则该 Pod 的完整域名(FQDN)为 foo.bar.my-namespace.svc.cluster-domain.example。

Pod的DNS Policy

可以为每一个 Pod 设置其自己的 DNS Policy。Kubernetes 通过 Pod 定义中的 spec.dnsPolicy 字段设置 DNS Policy,可选的值有:

  • Default: Pod 从其所在的节点继承域名解析配置。更多细节请参考 Customizing DNS Service (opens new window)
  • ClusterFirst:任何与集群域名后缀(例如 www.kubernetes.io)不匹配的 DNS 查询,都将被转发到 Pod 所在节点的上游 DNS 服务。集群管理员可能配置了额外的 stub-domain 及上游 DNS 服务,更多细节请参考 Customizing DNS Service (opens new window)
  • ClusterFirstWithHostNet: 对于运行在节点网络上的 Pod,其 dnsPolicy 必须指定为 ClusterFirstWithHostNet
  • None: 允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。此时,该 Pod 的 DNS 的所有设置必须通过 spce.dnsConfig 指定。 参考 ​Pod 的 DNS 配置

Pod的dns配置

Pod 定义中的 spec.dnsConfig​ 是可选字段,且可以与任何类型的 spec.dnsPolicy​ 配合使用。如果 spec.dnsPolicy​ 被设置为 “None”,则 spec.dnsConfig 必须被指定。

spec.dnsConfig 中有如下字段可以配置:

  • nameservers: Pod 的 DNS Server IP 地址列表。最多可以执行 3 个 IP 地址。当 spec.dnsPolicy 为 “None”,至少需要指定一个 IP 地址,其他情况下该字段是可选的。DNS Server 的 IP 地址列表将会与 DNS Policy 所产生的 DNS Server 地址列表合并(重复的条目被去除)。
  • searches:Pod 中执行域名查询时搜索域的列表。该字段是可选的。如果指定了该字段,则指定的搜索域列表将与 DNS Policy 所产生的搜索域列表合并(重复的条目被去除)。合并后的列表最多不超过 6 个域。
  • options:可选数组,其中每个元素由 name 字段(必填)和 value 字段(选填)组成。该列表中的内容将与 DNS Policy 所产生的 DNS 选项合并(重复的条目被去除)

Service 连接应用程序

Kubernetes 的网络模型

通常,Docker 使用一种 ​​host-private​​ 的联网方式,在此情况下,只有两个容器都在同一个节点(主机)上时,一个容器才可以通过网络连接另一个容器。为了使 Docker 容器可以跨节点通信,必须在宿主节点(主机)的 IP 地址上分配端口,并将该端口接收到的网络请求转发(或代理)到容器中。这意味着,用户必须非常小心地为容器分配宿主节点(主机)的端口号,或者端口号可以自动分配。

在一个集群中,多个开发者之间协调分配端口号是非常困难的。Kubernetes 认为集群中的两个 Pod 应该能够互相通信,无论他们各自在哪个节点上。每一个 Pod 都被分配自己的 “cluster-private-IP”,因此,您无需在 Pod 间建立连接,或者将容器的端口映射到宿主机的端口。因此:

  • Pod 中的任意容器可以使用 localhost 直连同 Pod 中另一个容器的端口
  • 集群中的任意 Pod 可以使用另一的 Pod 的cluster-private-IP直连对方的端口,(无需 NAT 映射)

在集群中部署pod

以下为部署nginx示例,重点关注网络连接方面

#创建文件 run-my-nginx.yaml,文件内容如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
#执行以下命令,部署pod并检查运行情况
kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
##################输出结果#################
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-3800858182-jr4a2 1/1 Running 0 13s 10.244.3.4 kubernetes-minion-905m
my-nginx-3800858182-kna2y 1/1 Running 0 13s 10.244.2.5 kubernetes-minion-ljyd
#执行命令 kubectl get pods -l run=my-nginx -o yaml | grep podIP, 检查 Pod 的 IP 地址,输出结果如下:
podIP: 10.244.3.4
podIP: 10.244.2.5

在集群中的任意节点上,您可以执行 ​​curl 10.244.3.4​​​ 或 ​​curl 10.244.2.5​​ 获得 nginx 的响应。此时:

  • 容器并没有使用节点上的 80 端口
  • 没有使用 NAT 规则对容器端口进行映射

这意味着,您可以

  • 在同一节点上使用 80 端口运行多个 nginx Pod
  • 在集群的任意节点/Pod 上使用 nginx Pod 的 clusterIP 访问 nginx 的 80 端口

同 Docker 一样,Kubernets 中,仍然可以将 Pod 的端口映射到宿主节点的网络地址上(使用 nodePort),但是使用 Kubernetes 的网络模型时,这类需求已经大大减少了。

创建service

上面的步骤中,我们已经创建了 nginx Pod,运行在集群的 IP 地址空间。您可以直接通过 Pod 的地址访问其端口,但是如果某一个 Pod 终止了该怎么办?Pod 因为故障或其他原因终止后,Deployment Controller 将创建一个新的 Pod 以替代该 Pod,但是 IP 地址将发生变化。Kubernetes Service 解决了这样的问题。

Kubernetes Service:

  • 定义了集群中一组 Pod 的逻辑集合,该集合中的 Pod 提供了相同的功能
  • 被创建后,获得一个唯一的 IP 地址(ClusterIP)。直到该 Service 被删除,此地址不会发生改变
  • Pod 可以直接连接 Service IP 地址上的端口,且发送到该 IP 地址的网络请求被自动负载均衡分发到 Service 所选取的 Pod 集合中
#执行以下命令创建service
kubectl expose deployment/my-nginx
#等价于kubectl apply -f nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
run: my-nginx

该 yaml 文件将创建一个 Service:

  • 该 Service 通过 label selector 选取包含run: my-nginx 标签的 Pod 作为后端 Pod
  • 该 Service 暴露一个端口 80(spec.ports[*].port)
  • 该 Service 将 80 端口上接收到的网络请求转发到后端 Pod 的 80 (spec.ports[*].targetPort)端口上,支持负载均衡
#执行命令kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.0.162.149 <none> 80/TCP 21s

Service 的后端 Pod 实际上通过 Endpoints​ 来暴露。Kubernetes 会持续检查 Service 的 label selector spec.selector,并将符合条件的 Pod 更新到与 Service 同名(my-nginx)的 Endpoints 对象。如果 Pod 终止了,该 Pod 将被自动从 Endpoints 中移除,新建的 Pod 将自动被添加到该 Endpoint。

#执行命令kubectl describe svc my-nginx
#######################显示结果#########################
Name: my-nginx
Namespace: default
Labels: run=my-nginx
Annotations: <none>
Selector: run=my-nginx
Type: ClusterIP
IP: 10.0.162.149
Port: <unset> 80/TCP
Endpoints: 10.244.2.5:80,10.244.3.4:80
Session Affinity: None
Events: <none>

访问service

环境变量

针对每一个有效的 Service,kubelet 在创建 Pod 时,向 Pod 添加一组环境变量

DNS

Kubernetes 提供了一个 DNS cluster addon,可自动为 Service 分配 DNS name。

保护service的安全

到目前为止,我们只是从集群内部访问了 nginx server。在将该 Service 公布到互联网时,您可能需要确保该通信渠道是安全的。为此,您必须:

  • 准备 https 证书(购买,或者自签名)
  • 将该 nginx 服务配置好,并使用该 https 证书
  • 配置 Secret,以使得其他 Pod 可以使用该证书

以下是配置nginx使用自签名证书

#创建密钥对
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
#将密钥转换为base64编码
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64
#创建nginxsecrets.yaml文件,使用前面命令输出的 base64 编码替换其中的内容
apiVersion: "v1"
kind: "Secret"
metadata:
name: "nginxsecret"
namespace: "default"
data:
nginx.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
nginx.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

# 创建 Secrets
kubectl apply -f nginxsecrets.yaml
# 查看 Secrets
kubectl get secrets
#####################输出结果##########
NAME TYPE DATA AGE
default-token-il9rc kubernetes.io/service-account-token 1 1d
nginxsecret Opaque 2 1m
#修改 nginx 部署,使 nginx 使用 Secrets 中的 https 证书,修改 Service,使其暴露 80 端口和 443端口。
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
- port: 443
protocol: TCP
name: https
selector:
run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
volumes:
- name: secret-volume
secret:
secretName: nginxsecret
containers:
- name: nginxhttps
image: bprashanth/nginxhttps:1.0
ports:
- containerPort: 443
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx/ssl
name: secret-volume

#执行命令,生效
kubectl delete deployments,svc my-nginx
kubectl create -f ./nginx-secure-app.yaml
#此时可以从任何节点访问nginx server
kubectl get pods -o yaml | grep -i podip
podIP: 10.244.3.5
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

暴露Service

可能有一部分功能需要通过 Service 发布到一个外部的 IP 地址上。Kubernetes 支持如下两种方式:

  • NodePort
  • LoadBalancer

此时,如果您的节点有公网地址,则 nginx HTTPS 部署已经可以接受来自于互联网的请求了。

通过ingress访问应用

ingress

ingress 是 Kubernetes 的一种 API 对象,将集群内部的 Service 通过 HTTP/HTTPS 方式暴露到集群外部,并通过规则定义 HTTP/HTTPS 的路由。Ingress 具备如下特性:集群外部可访问的 URL、负载均衡、SSL Termination、按域名路由(name-based virtual hosting)

示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress-for-nginx # Ingress 的名字,仅用于标识
spec:
rules: # Ingress 中定义 L7 路由规则
- host: demo.my-company.com # 根据 virtual hostname 进行路由(请使用您自己的域名)
http:
paths: # 按路径进行路由
- path: /
backend:
serviceName: nginx-service # 指定后端的 Service 为之前创建的 nginx-service
servicePort: 80

Ingress Controller 通常是部署在集群中的一个 Deployment,并且通过 NodePort Service 暴露自己的端口,使得用户可以在集群外通过其 NodePort 访问到 Ingress Controller,假设该端口为 32351​,并且 demo.my-company.com​ 这个域名被解析到集群中某一个节点的 IP(或者被配置到浏览器所在机器的 hosts 文件),则当用户在浏览器中输入地址 http://demo.my-company.com:32351 时:

  1. 请求被转发到集群某节点的32351 节点端口;
  2. 根据 Service 的定义,请求被转发到 Ingress Controller 的 Web 端口;
  3. Ingress Controller 根据请求域名demo.my-company.com 以及请求路径,匹配到 Ingress 定义中该请求应该被路由到nginx-service 的80 端口;
  4. Ingress Controller 执行 L7 路由转发,将请求发送到nginx-service 的80 端口。

配置pod的etc/hosts

某些情况下,DNS 或者其他的域名解析方法可能不太适用,您需要配置 /etc/hosts 文件,在Linux下是比较容易做到的,在 Kubernetes 中,可以通过 Pod 定义中的 hostAliases 字段向 Pod 的 /etc/hosts 添加条目。

适用其他方法修改 Pod 的 /etc/hosts 文件是不被推荐的,因为 kubelet 可能在重新创建 Pod 时,就会覆盖这些修改。

默认hosts文件内容

#创建pod后查看/etc/hosts文件默认内容
kubectl run nginx --image nginx --generator=run-pod/v1
#查看pod的ip
kubectl get pods -o wide
########输出结果####
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
#查看hosts文件内容
kubectl exec nginx -- cat /etc/hosts
######输出结果#####
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.200.0.4 nginx

使用hostAliases添加额外的条目

​通过 Pod 定义中的 .spec.hostAliases​ 字段,我们可以向 Pod 的 /etc/hosts​ 文件中添加额外的条目,用来解析 foo.local、bar.local​ 到 127.0.0.1 和 foo.remote、bar.remote​ 到 10.1.2.3

apiVersion: v1
kind: Pod
metadata:
name: hostaliases-pod
spec:
restartPolicy: Never
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "foo.local"
- "bar.local"
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
containers:
- name: cat-hosts
image: busybox
command:
- cat
args:
- "/etc/hosts"
#执行命令
kubectl apply -f https://kuboard.cn/statics/learning/svc/host-aliases-pod.yaml
#查看pod的ip和状态
kubectl get pod --output=wide
#输出结果
NAME READY STATUS RESTARTS AGE IP NODE
hostaliases-pod 0/1 Completed 0 6s 10.200.0.5 worker0
#查看hosts文件内容
kubectl logs hostaliases-pod
################输出结###########
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.200.0.5 hostaliases-pod

# Entries added by HostAliases.
127.0.0.1 foo.local bar.local
10.1.2.3 foo.remote bar.remote

网络插件

目前主流的三个网络CNI插件

  • Flannel
  • Calico
  • Weave

网络架构是Kubernetes中较为复杂、让很多用户头疼的方面之一。Kubernetes网络模型本身对某些特定的网络功能有一定要求,但在实现方面也具有一定的灵活性。因此,业界已有不少不同的网络方案,来满足特定的环境和要求。

CNI意为容器网络接口,它是一种标准的设计,为了让用户在容器创建或销毁时都能够更容易地配置容器网络。在本文中,我们将集中探索与对比目前最流行的CNI插件:Flannel、Calico、Weave和Canal(技术上是多个插件的组合)。这些插件既可以确保满足Kubernetes的网络要求,又能为Kubernetes集群管理员提供他们所需的某些特定的网络功能。

背景

容器网络是容器选择连接到其他容器、主机和外部网络(如Internet)的机制。容器的Runtime提供了各种网络模式,每种模式都会产生不同的体验。

例如,Docker默认情况下可以为容器配置以下网络:

  • none:将容器添加到一个容器专门的网络堆栈中,没有对外连接。
  • host:将容器添加到主机的网络堆栈中,没有隔离。
  • default bridge:默认网络模式。每个容器可以通过IP地址相互连接。
  • 自定义网桥:用户定义的网桥,具有更多的灵活性、隔离性和其他便利功能。

Docker还可以让用户通过其他驱动程序和插件,来配置更高级的网络(包括多主机覆盖网络)。

CNI的初衷是创建一个框架,用于在配置或销毁容器时动态配置适当的网络配置和资源。下面链接中的CNI规范概括了用于配制网络的插件接口,这个接口可以让容器运行时与插件进行协调:

插件负责为接口配置和管理IP地址,并且通常提供与IP管理、每个容器的IP分配、以及多主机连接相关的功能。容器运行时会调用网络插件,从而在容器启动时分配IP地址并配置网络,并在删除容器时再次调用它以清理这些资源。

运行时或协调器决定了容器应该加入哪个网络以及它需要调用哪个插件。然后,插件会将接口添加到容器网络命名空间中,作为一个veth对的一侧。接着,它会在主机上进行更改,包括将veth的其他部分连接到网桥。再之后,它会通过调用单独的IPAM(IP地址管理)插件来分配IP地址并设置路由。

在Kubernetes中,kubelet可以在适当的时间调用它找到的插件,来为通过kubelet启动的pod进行自动的网络配置。

相关概念

  • 第2层网络:OSI(Open Systems Interconnections,开放系统互连)网络模型的“数据链路”层。第2层网络会处理网络上两个相邻节点之间的帧传递。第2层网络的一个值得注意的示例是以太网,其中MAC表示为子层。
  • 第3层网络:OSI网络模型的“网络”层。第3层网络的主要关注点,是在第2层连接之上的主机之间路由数据包。IPv4、IPv6和ICMP是第3层网络协议的示例。
  • VXLAN:代表“虚拟可扩展LAN”。首先,VXLAN用于通过在UDP数据报中封装第2层以太网帧来帮助实现大型云部署。VXLAN虚拟化与VLAN类似,但提供更大的灵活性和功能(VLAN仅限于4096个网络ID)。VXLAN是一种封装和覆盖协议,可在现有网络上运行。
  • Overlay网络:Overlay网络是建立在现有网络之上的虚拟逻辑网络。Overlay网络通常用于在现有网络之上提供有用的抽象,并分离和保护不同的逻辑网络。
  • 封装:封装是指在附加层中封装网络数据包以提供其他上下文和信息的过程。在overlay网络中,封装被用于从虚拟网络转换到底层地址空间,从而能路由到不同的位置(数据包可以被解封装,并继续到其目的地)。
  • 网状网络:网状网络(Mesh network)是指每个节点连接到许多其他节点以协作路由、并实现更大连接的网络。网状网络允许通过多个路径进行路由,从而提供更可靠的网络。网状网格的缺点是每个附加节点都会增加大量开销。
  • BGP:代表“边界网关协议”,用于管理边缘路由器之间数据包的路由方式。BGP通过考虑可用路径,路由规则和特定网络策略,帮助弄清楚如何将数据包从一个网络发送到另一个网络。BGP有时被用作CNI插件中的路由机制,而不是封装的覆盖网络。

网络插件比较

flannel

k8s学习-服务发现以及网络1_nginx_04

由CoreOS开发的项目Flannel,可能是最直接和最受欢迎的CNI插件。它是容器编排系统中最成熟的网络结构示例之一,旨在实现更好的容器间和主机间网络。随着CNI概念的兴起,Flannel CNI插件算是早期的入门。

与其他方案相比,Flannel相对容易安装和配置。它被打包为单个二进制文件FlannelD,许多常见的Kubernetes集群部署工具和许多Kubernetes发行版都可以默认安装Flannel。Flannel可以使用Kubernetes集群的现有etcd集群来使用API存储其状态信息,因此不需要专用的数据存储。

Flannel配置第3层IPv4 Overlay网络。它会创建一个大型内部网络,跨越集群中每个节点。在此Overlay网络中,每个节点都有一个子网,用于在内部分配IP地址。在配置Pod时,每个节点上的Docker桥接口都会为每个新容器分配一个地址。同一主机中的Pod可以使用Docker桥接进行通信,而不同主机上的pod会使用flanneld将其流量封装在UDP数据包中,以便路由到适当的目标。

Flannel有几种不同类型的后端可用于封装和路由。默认和推荐的方法是使用VXLAN,因为VXLAN性能更良好并且需要的手动干预更少。

总的来说,Flannel是大多数用户的不错选择。从管理角度来看,它提供了一个简单的网络模型,用户只需要一些基础知识,就可以设置适合大多数用例的环境。一般来说,在初期使用Flannel是一个稳妥安全的选择,直到你开始需要一些它无法提供的东西。

calico

k8s学习-服务发现以及网络1_ico_05

Calico是Kubernetes生态系统中另一种流行的网络选择。虽然Flannel被公认为是最简单的选择,但Calico以其性能、灵活性而闻名。Calico的功能更为全面,不仅提供主机和pod之间的网络连接,还涉及网络安全和管理。Calico CNI插件在CNI框架内封装了Calico的功能。

在满足系统要求的新配置的Kubernetes集群上,用户可以通过应用单个manifest文件快速部署Calico。如果您对Calico的可选网络策略功能感兴趣,可以向集群应用其他manifest,来启用这些功能。

尽管部署Calico所需的操作看起来相当简单,但它创建的网络环境同时具有简单和复杂的属性。与Flannel不同,Calico不使用overlay网络。相反,Calico配置第3层网络,该网络使用BGP路由协议在主机之间路由数据包。这意味着在主机之间移动时,不需要将数据包包装在额外的封装层中。BGP路由机制可以本地引导数据包,而无需额外在流量层中打包流量。

除了性能优势之外,在出现网络问题时,用户还可以用更常规的方法进行故障排除。虽然使用VXLAN等技术进行封装也是一个不错的解决方案,但该过程处理数据包的方式同场难以追踪。使用Calico,标准调试工具可以访问与简单环境中相同的信息,从而使更多开发人员和管理员更容易理解行为。

除了网络连接外,Calico还以其先进的网络功能而闻名。 网络策略是其最受追捧的功能之一。此外,Calico还可以与服务网格Istio集成,以便在服务网格层和网络基础架构层中解释和实施集群内工作负载的策略。这意味着用户可以配置强大的规则,描述Pod应如何发送和接受流量,提高安全性并控制网络环境。

如果对你的环境而言,支持网络策略是非常重要的一点,而且你对其他性能和功能也有需求,那么Calico会是一个理想的选择。此外,如果您现在或未来有可能希望得到技术支持,那么Calico是提供商业支持的。一般来说,当您希望能够长期控制网络,而不是仅仅配置一次并忘记它时,Calico是一个很好的选择。

weave 

Weave是由Weaveworks提供的一种Kubernetes CNI网络选项,它提供的模式和我们目前为止讨论的所有网络方案都不同。Weave在集群中的每个节点之间创建网状Overlay网络,参与者之间可以灵活路由。这一特性再结合其他一些独特的功能,在某些可能导致问题的情况下,Weave可以智能地路由。

为了创建网络,Weave依赖于网络中每台主机上安装的路由组件。然后,这些路由器交换拓扑信息,以维护可用网络环境的最新视图。当需要将流量发送到位于不同节点上的Pod时,Weave路由组件会自动决定是通过“快速数据路径”发送,还是回退到“sleeve”分组转发的方法。

快速数据路径依靠内核的本机Open vSwitch数据路径模块,将数据包转发到适当的Pod,而无需多次移入和移出用户空间。Weave路由器会更新Open vSwitch配置,以确保内核层具有有关如何路由传入数据包的准确信息。相反,当网络拓扑不适合快速数据路径路由时,sleeve模式可用作备份。它是一种较慢的封装模式,在快速数据路径缺少必要的路由信息或连接的情况下,它可以来路由数据包。当流量通过路由器时,它们会了解哪些对等体与哪些MAC地址相关联,从而允许它们以更少的跳数、更智能地路由后续流量。当网络更改导致可用路由改变时,这一相同的机制可以帮助每个节点进行自行更正。

与Calico一样,Weave也为Kubernetes集群提供网络策略功能。设置Weave时,网络策略会自动安装和配置,因此除了添加网络规则之外,用户无需进行其他配置。一个其他网络方案都没有、Weave独有的功能,是对整个网络的简单加密。虽然这会增加相当多的网络开销,但Weave可以使用NaCl加密来为sleeve流量自动加密所有路由流量,而对于快速数据路径流量,因为它需要加密内核中的VXLAN流量,Weave会使用IPsec ESP来加密快速数据路径流量。

对于那些寻求功能丰富的网络、同时希望不要增加大量复杂性或管理难度的人来说,Weave是一个很好的选择。它设置起来相对容易,提供了许多内置和自动配置的功能,并且可以在其他解决方案可能出现故障的场景下提供智能路由。网状拓扑结构确实会限制可以合理容纳的网络的大小,不过对于大多数用户来说,这也不是一个大问题。此外,Weave也提供收费的技术支持,可以为企业用户提供故障排除等等技术服务。