首先,向大家科普下Kubernetes所选择的CNI网络接口,简单介绍下网络实现的背景。

CNI即Container Network Interface,是一套容器网络的定义规范,包括方法规范、参数规范、响应规范等等。CNI只要求在容器创建时为容器分配网络资源、删除容器时释放网络资源。CNI与调用者之间的整个交互过程如下图所示:

CNI实现与外界的交互都通过进程参数和环境变量传递,也只要求输出结果符合CNI规范即可,与实现语言也没什么特殊要求。比如Calico早期版本就使用Python实现了CNI规范,为Kubernetes提供了网络实现。常见的环境变量设置如下:

  • CNI_COMMAND:调用指定CNI动作,ADD表示增加网卡,DEL表示释放网卡

  • CNI_CONTAINERID:容器ID

  • CNI_NETNS:容器网络命名空间文件位置

  • CNI_ARGS:额外传递的参数

  • CNI_IFNAME:设置的容器网卡名称,如eth0

正因如此,CNI规范实现起来非常容易扩展,除了CNI自带的Bridge、Macvlan等基本实现以外,还有大量的第三方实现可供选择,包括Calico、Romana、Flannel等常用实现。同时CNI支持多种容器运行时,包括Docker、rkt、Mesos、Hyper等容器引擎都可以使用。这也是Kubernetes选择使用CNI的一大重要原因。

相对的,Docker提出的CNM(Cotainer Network Model)模型实现就比较复杂,但更为完善,比较接近传统的网络概念。如下图所示:

Sandbox就是容器的网络命名空间,Endpoint为容器连接到网络中的一张网卡,而网络则是一组相互通信的Endpoint的集合,比较接近Neutron中的网络定义。

在CNM中,docker engine通过HTTP REST API调用网络实现,为容器配置网络。这些API接口涵盖网络管理、容器管理、创建endpoint等十几个接口。同时CNM模型还隐含在docker自身附带的service机制、dns机制等附加约束,因此可以在一定程度上说,CNM模型只是专为docker容器实现的,对别的容器运行时并不友好。

由于上面这些技术上的原因以及一些商业上的原因,Kubernetes最终选择了CNI作为自己的网络接口。

当然,Kubernetes也提供一些取巧的方法,将CNI接口转化为对CNM模型的调用,从而实现两种模型的通用。例如to_docker,这个脚本就将Kubernetes对CNI的调用转换为Docker CNM网络的对应操作,从而实现CNI到CNM的转换。

接下来,给大家介绍下Kubernetes中网络概念和通信原理。

在Kubernetes的网络模型中,约定了三个基本约束:

  1. 所有容器之间都可以无须SNAT即可相互直接以IP通信。

  2. 所有主机与容器之间都可以无须SNAT即可相互直接以IP通信。

  3. 容器看到的自身IP与其他容器看到的容器IP相同。

在满足约束的基础上,Kubernetes不关心具体的网络通信原理,只以三个约束为既定事实,在此基础上,根据Kubernetes自身逻辑处理网络通信,从而避免Kubernetes功能纠结在纷繁复杂的网络实现中。

而在网络概念上,Kubernetes中有两种核心IP:

  • POD IP:有CNI实现提供,Kubernetes不管这个IP是否可达,只负责使用这个IP实现配置iptables、做健康检查等功能。默认情况下,这个IP在Kubernetes集群范围内都是可达的,并且可以进行ping等操作。

  • cluster IP:即服务IP,这个IP在Kubernetes中只是用于实现服务交互通信,本质上只是iptables上的几条DNAT规则。默认情况下,这个IP上只能提供服务端口的访问,且不可ping。

以集群的DNS服务为例,相关的核心iptables如下图所示:

这些iptables都是由kube-proxy生成的,而且kube-proxy并不实际负责进行转发,因此即使kube-proxy服务异常,已经产生的iptables依然可以使流量能够正确的在服务IP和POD IP之间流转。其网络流量路径可以参考下图:

当访问DNS服务的端口10.254.0.3时,kube-proxy生成的iptables DNAT规则,将流量转发到后端POD IP及对应端口上,将流量按后端POD的IP个数实行随机均等分配。

而kube-proxy可以从kube-apiserver获取服务和POD的状态更新,随时根据其状态更新iptables,从而实现服务的高可用与动态扩展。

在基础的IP通信机制上,Kubernetes还通过Network Policy和Ingress提高网络安全性和响应性能。

Network Policy提供了网络隔离能力,它基于SIG-Network group演进而来,Kubernetes只提供内置的labelSelector和label以及Network Policy API定义,本身并不负责实现如何隔离。在Kubernetes使用的CNI网络实现中,目前只有Calico、Romana、Contiv等少少几个实现了Network Policy集成。一个典型的Network Policy定义如下所示:

apiVersion: extensions/v1beta1

kind: NetworkPolicy

metadata:

name: test-network-policy

namespace: default

spec:

podSelector:

matchLabels:

role: db

ingress:

- from:

- podSelector:

matchLabels:

role: frontend

ports:

- protocol: tcp

port: 6379

它指定约束,具有role:db标签的POD只能被具有role:frontend标签的POD访问,除此之外拒绝所有流量。从功能上来讲,Network Policy可以等价于Neutron的安全组。

Ingress是负责对外提供服务的,通过Nginx对外提供一个单独接口,实现集群中的所有服务的对外提供,从而取代使用NodePort暴露每个服务的现有实现。目前,Kubernetes的Ingress提供了Nginx和GCE两种实现,感兴趣的同学可以直接参考官文档,https://github.com/kubernetes/ingress/tree/master/controllers。

Kubernetes社区中,比较常见的几种网络实现主要是以下两种:

  1. 基于Overlay网络:以Flannel、Weave为代表。Flannel是CoreOS为Kubernetes专门定制实现的Overlay网络方案,也是Kubernetes默认的网络实现。它基于VXLAN或者UDP整个集群的Overlay网络,从而实现容器在集群上的通信,满足Kubernetes网络模型的三大基本约束。由于在通信过程中存在数据包的封包解包等额外损耗,性能较差,但已经基本满足使用。

  2. 以L3路由为基础实现网络:以Calico、Romana为代表。其中,Calico是广泛流传的性能最好的Kubernetes网络实现,基于纯三层的路由实现网络通信,结合iptables实现的安全控制,可以满足大多数云的性能需求。但是由于它要求主机上必须打开BGP形成路由拓扑,在一些数据中心上可能不会被允许。同时,Calico还比较早地支持了Network Policy,并且可以将Calico自身的数据直接托管在Kubernetes中,从而实现与Kubernetes的深度集成。

从上面这些网络实现来看,目前Kubernetes的网络实现都还谈不上是比较成熟的SDN,因此我们公司在考察Kubernetes后,决定基于Neutron,为Kubernetes提供一个可用的SDN实现,这就是Skynet项目的由来。

下面我来跟大家分享下,Skynet在实践过程中的一些经验。

在实践中,首先要解决的就是Kubernetes中的网络概念,怎么翻译到Neutron中,才能比较合适地实现功能。

在第一个版本中,Kubernetes网络中概念翻译对应如下表所示:

  • POD ----> 虚拟机

  • Service -------> loadbalancer

  • Endpoints -------> pool

  • Service后端POD ----> member

但是,由于Kubernetes中支持同一服务上设置多个服务端口,而Neutron的每个Load Balancer仅支持一个对外端口。好在,去年OpenStack的Mitaka版本后,Neutron LBaaS V2正式发布,因此有了第二个版本的概念翻译。

  • POD ----> 虚拟机

  • Service -----> lbaasv2 loadbalancer

  • Service port ----->lbaasv2 listener

  • Endpoints -----> lbaasv2 pool

  • Service后端POD ------>lbaasv2 member

  • POD livenessProbe ----->health monitor

LBaaS V2的基本术语图解如下所示:

  • Load Balancer:负载均衡器,对应一个HAProxy进程,占据一个子网IP。可以逻辑上映射为Kubernetes中的Service。

  • Listener:监听器,表示负载均衡器本身提供的一个前端监听端口。对应service定义中的ports中port。

  • pool:监听器后端的成员集合记录。

  • member:监听器后端的成员。对应service使用的Endpoints的addresses列表,每个地址可以对应service声明中的targetPort的映射。

  • health monitor:pool中的成员健康检查器,类似Kubernetes中的livenessProbe,目前不映射。

就资源数量的映射来说:Kubernetes的一个service,对应一个Load Balancer。service中的每个port对应监听这个Load Balancer的一个Listener。每个Listener后端都对接一个pool包含其后端资源。而Kubernetes中的每个Service都有一个对应的Endpoints来包含其后端POD。Endpoints中的每个IP+Service声明port的targetPort就对应pool中的一个member。

初步完成了概念的映射后,我们简单介绍下开发中的思路。

在整体结构上,Skynet居于Kubernetes和Neutron之间,实现了CNI规范,基于Neutron为容器配置网络。service-watcher负责监听Kubernetes的资源,对服务等概念翻译为Neutron实现,从而实现完整的网络功能。如下所示:

kubelet是创建POD的直接操作者,在为POD设置网络时,通过CNI接口规范,调用Skynet实现。Skynet通过调用Neutron为容器分配IP,并通过在POD容器网络命令空间中操作,实现IP、路由等通信规则的设置。

而Neutron原生的DHCP、LBaaS v2等机制可以基本保持不变。从而实现完整的集成,可以使Kubernetes集群获得完整的Neutron SDN功能。而当容器内需要进行DNS时,则可以通过Neutron自带的DHCP Agent负责实现解析,在集群网络中正常工作。

如前文所述,Skynet实现了CNI规范,kubelet与Skynet之间的交互过程如下所示:

简要介绍下每个步骤:

kubelet通过CNI机制调用skynet,主要传递的参数如下:

  • CNI_COMMAND:调用指定CNI动作,ADD表示增加网卡,DEL表示释放网卡

  • CNI_CONTAINERID:容器ID

  • CNI_NETNS:容器网络命名空间文件位置

  • CNI_ARGS:额外传递的参数

  • CNI_IFNAME:设置的容器网卡名称,如eth0

执行ADD操作时,Skynet根据传入的参数和POD的配置,通过neutron-server为POD创建port。

执行ADD操作时,Skynet根据port和网络配置,为容器创建网络设备,并挂载到容器命名空间中。

neutron-linuxbridge-agent,根据容器的网络和安全组规则生成iptables。从而利用Neutron原生的安全组功能,同时也可以直接利用Neutron的一整套SDN实现,包括vRouter、FWaaS、VPNaaS等服务。

service-watcher将Kubernetes服务映射为Neutron LBaaS v2实现后,以VLAN网络为例,POD与服务之间的流量通信过程如下图:

当集群内容器访问服务时,Kubernetes默认都是通过服务名称访问,服务名通过Neutron的DHCP机制,可以由每个网络的Dnsmasq进程负责解析,获得service对应负载均衡的IP地址后,即可用于网络通信,由物理交换机负责流量的中转。

在实际实现中,以Kubernetes中一个服务的定义映射到Neutron的loadbalancer为例演示下。

例如对下面的service实现:

kind: Service

apiVersion: v1

metadata:

name: neutron-service

namespace: default

labels:

app: neutron-service

annotations:

skynet/subnet_id: a980172e-638d-474a-89a2-52b967803d6c

spec:

ports:

- name: port1

protocol: TCP

port: 8888

targetPort: 8000

- name: port2

protocol: TCP

port: 9999

targetPort: 9000

selector:

app: neutron-service

type: NodePort

kind: Endpoints

apiVersion: v1

metadata:

name: neutron-service

namespace: default

labels:

app: neutron-service

subsets:

- addresses:

- ip: 192.168.119.187

targetRef:

kind: Pod

namespace: default

name: neutron-service-puds0

uid: eede8e24-85f5-11e6-ab34-000c29fad731

resourceVersion: '2381789'

- ip: 192.168.119.188

targetRef:

kind: Pod

namespace: default

name: neutron-service-u9nnw

uid: eede9b70-85f5-11e6-ab34-000c29fad731

resourceVersion: '2381787'

ports:

- name: port1

port: 8000

protocol: TCP

- name: port2

port: 9000

protocol: TCP

POD和Service通过特定注解来指定使用的Neutron网络、IP等配置,与Kubernetes尽量解耦。

上面的Service映射成Load Balancer后,其定义如下所示:

{

"statuses": {

"loadbalancer": {

"name": "neutron-service",

"provisioning_status": "ACTIVE",

"listeners": [

{

"name": "neutron-service-8888",

"provisioning_status": "ACTIVE",

"pools": [

{

"name": "neutron-service-8888",

"provisioning_status": "ACTIVE",

"healthmonitor": {},

"members": [

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.188",

"protocol_port": 8000,

"id": "461a0856-5c97-417e-94b4-c3486d8e2160",

"operating_status": "ONLINE"

},

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.187",

"protocol_port": 8000,

"id": "1d1b3da6-b1a1-485b-a25a-243e904fcedb",

"operating_status": "ONLINE"

}

],

"id": "95f42465-0cab-477e-a7de-008621235d52",

"operating_status": "ONLINE"

}

],

"l7policies": [],

"id": "6cf0c3dd-3aec-4b35-b2a5-3c0a314834e8",

"operating_status": "ONLINE"

},

{

"name": "neutron-service-9999",

"provisioning_status": "ACTIVE",

"pools": [

{

"name": "neutron-service-9999",

"provisioning_status": "ACTIVE",

"healthmonitor": {},

"members": [

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.188",

"protocol_port": 9000,

"id": "2faa9f42-2734-416a-a6b2-ed922d01ca50",

"operating_status": "ONLINE"

},

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.187",

"protocol_port": 9000,

"id": "81f777b1-d999-48b0-be79-6dbdedca5e97",

"operating_status": "ONLINE"

}

],

"id": "476952ac-64a8-4594-8972-699e87ae5b9b",

"operating_status": "ONLINE"

}

],

"l7policies": [],

"id": "c6506b43-2453-4f04-ba87-f5ba4ee19b17",

"operating_status": "ONLINE"

}

],

"pools": [

{

"name": "neutron-service-8888",

"provisioning_status": "ACTIVE",

"healthmonitor": {},

"members": [

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.188",

"protocol_port": 8000,

"id": "461a0856-5c97-417e-94b4-c3486d8e2160",

"operating_status": "ONLINE"

},

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.187",

"protocol_port": 8000,

"id": "1d1b3da6-b1a1-485b-a25a-243e904fcedb",

"operating_status": "ONLINE"

}

],

"id": "95f42465-0cab-477e-a7de-008621235d52",

"operating_status": "ONLINE"

},

{

"name": "neutron-service-9999",

"provisioning_status": "ACTIVE",

"healthmonitor": {},

"members": [

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.188",

"protocol_port": 9000,

"id": "2faa9f42-2734-416a-a6b2-ed922d01ca50",

"operating_status": "ONLINE"

},

{

"name": "",

"provisioning_status": "ACTIVE",

"address": "192.168.119.187",

"protocol_port": 9000,

"id": "81f777b1-d999-48b0-be79-6dbdedca5e97",

"operating_status": "ONLINE"

}

],

"id": "476952ac-64a8-4594-8972-699e87ae5b9b",

"operating_status": "ONLINE"

}

],

"id": "31b61658-4708-4a48-a3c4-0d61a127cd09",

"operating_status": "ONLINE"

}

}

}

其对应的HAProxy进程配置如下所示:

# Configuration for neutron-service

global

daemon

user nobody

group nogroup

log /dev/log local0

log /dev/log local1 notice

stats socket /var/lib/neutron/lbaas/v2/31b61658-4708-4a48-a3c4-0d61a127cd09/haproxy_stats.sock mode 0666 level user

defaults

log global

retries 3

option redispatch

timeout connect 5000

timeout client 50000

timeout server 50000

frontend 6cf0c3dd-3aec-4b35-b2a5-3c0a314834e8

option tcplog

bind 192.168.119.178:8888

mode tcp

default_backend 95f42465-0cab-477e-a7de-008621235d52

frontend c6506b43-2453-4f04-ba87-f5ba4ee19b17

option tcplog

bind 192.168.119.178:9999

mode tcp

default_backend 476952ac-64a8-4594-8972-699e87ae5b9b

backend 476952ac-64a8-4594-8972-699e87ae5b9b

mode tcp

balance roundrobin

server 81f777b1-d999-48b0-be79-6dbdedca5e97 192.168.119.187:9000 weight 1

server 2faa9f42-2734-416a-a6b2-ed922d01ca50 192.168.119.188:9000 weight 1

backend 95f42465-0cab-477e-a7de-008621235d52

mode tcp

balance roundrobin

server 1d1b3da6-b1a1-485b-a25a-243e904fcedb 192.168.119.187:8000 weight 1

server 461a0856-5c97-417e-94b4-c3486d8e2160 192.168.119.188:8000 weight 1

综上所述,通过基于Neutron的Skynet,我们为Kubernetes初步实现了SDN的功能,同时提供了如下网络功能增强:

  1. POD的IP、Mac、主机名等网络配置的保持;

  2. 基于Neutron安全组,实现了POD之间的网络隔离功能,更加通用;

  3. 支持通过HAProxy直接对外提供服务,性能上会比原生的iptables好很多。

当然,目前有一些Kubernetes特性在Skynet网络方案中还不支持,需要在后面进行增强或实现:

  1. Headless services这一类没有集群IP的Service无法处理。

  2. 由于neutron-server与neutron-plugin之间的消息都是通过RabbitMQ进行,不是特别适合容器环境下网络快速变更的现状,会是整个方案的一大瓶颈。

基于Neutron的Kubernetes SDN实践经验之谈的更多相关文章

  1. QCon技术干货:个推基于Docker和Kubernetes的微服务实践

    2016年伊始,Docker无比兴盛,如今Kubernetes万人瞩目.在这个无比需要创新与速度的时代,由容器.微服务.DevOps构成的云原生席卷整个IT界.在近期举办的QCon全球软件开发大会上, ...

  2. 灵雀云Kube-OVN:基于OVN的开源Kubernetes网络实践

    近日,灵雀云发布了基于OVN的Kubernetes网络组件Kube-OVN,并正式将其在Github上开源.Kube-OVN提供了大量目前Kubernetes不具备的网络功能,并在原有基础上进行增强. ...

  3. 【译】Kubernetes监控实践(2):可行监控方案之Prometheus和Sensu

    本文介绍两个可行的K8s监控方案:Prometheus和Sensu.两个方案都能全面提供系统级的监控数据,帮助开发人员跟踪K8s关键组件的性能.定位故障.接收预警. 拓展阅读:Kubernetes监控 ...

  4. 026.[转] 基于Docker及Kubernetes技术构建容器云平台 (PaaS)

    [编者的话] 目前很多的容器云平台通过Docker及Kubernetes等技术提供应用运行平台,从而实现运维自动化,快速部署应用.弹性伸缩和动态调整应用环境资源,提高研发运营效率. 本文简要介绍了与容 ...

  5. 洪强宁:宜信PaaS平台基于Calico的容器网络实践

    洪强宁:宜信PaaS平台基于Calico的容器网络实践   本文内容来自由七牛云主办的ECUG Con,独家授权InfoQ整理完成 容器云面临的网络挑战 在传统的IDC的架构里面网络是很重要的事情,在 ...

  6. 基于Rust-vmm实现Kubernetes运行时

    随着容器及K8s的广泛使用,越来越多的容器安全与隔离问题被暴露出来,如:容器逃逸.水平攻击.DDos攻击等严重威胁了办公和生产环境的安全与稳定,影响了业务的正常运行.安全容器技术孕育而生,产生了kat ...

  7. TopoLVM: 基于LVM的Kubernetes本地持久化方案,容量感知,动态创建PV,轻松使用本地磁盘

    正文 研发测试场景下,一般追求的是一键快速起环境,横向动态复制,一人一套,随起随用,用完即走.作为使用方,其不用关心实际的物理资源是怎样的,环境起在哪里,只要声明自己的使用需求即可.但作为方案构建者以 ...

  8. 学习笔记——Maven实战(四)基于Maven的持续集成实践

    Martin的<持续集成> 相信很多读者和我一样,最早接触到持续集成的概念是来自Martin的著名文章<持续集成>,该文最早发布于2000年9月,之后在2006年进行了一次修订 ...

  9. 基于Open vSwitch的OpenFlow实践

    Open vSwitch(下面简称为 OVS)是由 Nicira Networks 主导的,运行在虚拟化平台(例如 KVM,Xen)上的虚拟交换机.在虚拟化平台上,OVS 可以为动态变化的端点提供 2 ...

随机推荐

  1. Apache HttpComponents中的cookie匹配策略

    Apache HttpComponents中的cookie匹配策略 */--> pre.src {background-color: #292b2e; color: #b2b2b2;} pre. ...

  2. koa2怎么自定义一个中间件

    首先定义一个方法 function test(ctx){ global.console.log('m1') } 把这个中间件导出去 module.exports=function(){ return ...

  3. 优化升级logging封装RotatingFileHandler

    1.升级优化,提供用户自定义日志level文件夹生成控制,提供日志错误显示到日志打印异常补获到日志 # coding=utf-8 import logging import time import o ...

  4. 1349: Taking Pebbles (博弈 打表找规律)

    1349: Taking Pebbles Submit Page    Summary    Time Limit: 1 Sec     Memory Limit: 128 Mb     Submit ...

  5. RHEL 7.6 安装 Oracle 18c RAC

    RHEL 7.6 安装 Oracle 18c RAC 第一部分 安装规划 虚拟环境 VirtualBox 6.0 OS 版本 Red Hat Enterprise Linux Server relea ...

  6. linux 学习第十八天学习(DNS分离解析、DHCP配置、邮件服务配置)

    DNS分离解析技术 yum install bind-chroot systemctl restart named systemctl enable named vim /etc/named.conf ...

  7. PHP操作xml学习笔记之增删改查(1)—增加

    xml文件 <?xml version="1.0" encoding="utf-8"?><班级>    <学生>       ...

  8. STM32 HAL库学习系列第1篇 ADC配置 及 DAC配置

    ADC工作均为非阻塞状态 轮询模式 中断模式 DMA模式 库函数: HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);//轮询模式,需放 ...

  9. python3爬虫-通过selenium登陆拉钩,爬取职位信息

    from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from se ...

  10. PCIE_DMA实例一:xapp1052详细使用说明

    一:前言 很多和我一样初学pcie的硬件工程师都会遇到这样一个问题,看了不少pcie相关的资料,还是搞不清这玩意儿到底该怎么用.于是我们打开ISE的core_generator工具,生成了一个pcie ...