PaaS 平台的网络需求

在使用 Docker 构建 PaaS 平台的过程中,我们首先遇到的问题是需要选择一个满足需求的网络模型:

  • 让每个容器拥有自己的网络栈,特别是独立的 IP 地址
  • 能够进行跨服务器的容器间通讯,同时不依赖特定的网络设备
  • 有访问控制机制,不同应用之间互相隔离,有调用关系的能够通讯

调研了几个主流的网络模型:

  • Docker 原生的 Bridge 模型:NAT 机制导致无法使用容器 IP 进行跨服务器通讯(后来发现自定义网桥可以解决通讯问题,但是觉得方案比较复杂)
  • Docker 原生的 Host 模型:大家都使用和服务器相同的 IP,端口冲突问题很麻烦
  • Weave OVS 等基于隧道的模型:由于是基于隧道的技术,在用户态进行封包解包,性能折损比较大,同时出现问题时网络抓包调试会很蛋疼

在对上述模型都不怎么满意的情况下,发现了一个还不怎么被大家关注的新项目:Project Calico 。

Project Calico 是纯三层的 SDN 实现,它基于 BPG 协议和 Linux 自己的路由转发机制,不依赖特殊硬件,没有使用 NAT 或 Tunnel 等技术。能够方便的部署在物理服务器,虚拟机(如 OpenStack)或者容器环境下。同时它自带的基于 Iptables 的 ACL 管理组件非常灵活,能够满足比较复杂的安全隔离需求。

使用 Calico 来实现 Docker 的跨服务器通讯

环境准备

  • 两个 Linux 环境 node1|2(物理机,VM 均可),假定 IP 为:192.168.78.21|22
  • 为了简单,请将 node1|2 上的 Iptables INPUT 策略设为 ACCEPT,同时安装 Docker
  • 一个可访问的 Etcd 集群(192.168.78.21:2379),Calico 使用其进行数据存放和节点发现

启动 Calico

在 node1|2 上面下载控制脚本:

# wget https://github.com/projectcalico/calico-docker/releases/download/v0.4.9/calicoctl

启动

# export ETCD_AUTHORITY=192.168.78.21:2379
# ./calicoctl node --ip=192.168.78.21|22

docker ps 能看到:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74cc20b90b0f calico/node:v0.4.9 "/sbin/my_init" 24 seconds ago Up 23 seconds calico-node 

部署测试实例

在 Calico 中,有一个 Profile 的概念(类似 AWS 的 Security Group),位于同一个 Profile 中的实例才能互相通讯,所以我们先创建一个名为 db 的 Profile:

在 node1 上执行:

[node1]# ./calicoctl profile add db

然后启动测试实例:

[node1]# export DOCKER_HOST=localhost:2377
[node1]# docker run -n container1 -e CALICO_IP=auto -e CALICO_PROFILE=db -td ubuntu

这里大家注意,我们注入了两个环境变量:CALICO_IP 和 CALICO_PROFILE 。

前者告诉 CALICO 自动进行 IP 分配,后者将此容器加入到 Profile db 中。

那么 Calico 是怎么做到在容器启动的时候分配 IP 的呢?

大家注意我们在 run 一个容器前,先执行了一个 export,这里其实就是将 Docker API 的入口劫持到了 Calico 那里。Calico 内部是一个 twistd 实现的 Python Daemon,转发所有 Docker 的 API 请求给真正的 Docker 服务,如果发现是start 则插入自己的逻辑创建容器的网络栈。

容器启动后我们查看 container1 获取的 IP 地址:

[container1]# ip addr
...
8: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 1e:48:3e:ec:71:52 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/32 scope global eth1
valid_lft forever preferred_lft forever

我们会看到 eth1 这个网络接口被设置了 IP 192.168.0.1

同样在 node2 上面部署 container2。

默认设置下 IP 会在 192.168.0.0/16 中按顺序分配,所以 container2 会是192.168.0.2

然后我们就会发现 container1|2 能够互相 ping 通了!

路由实现

接下来让我们看一下在上面的 demo 中,Calico 是如何让不在一个节点上的两个容器互相通讯的:

  • Calico 节点启动后会查询 Etcd,和其他 Calico 节点使用 BGP 协议建立连接
[node1]# netstat -anpt | grep 179
tcp 0 0 0.0.0.0:179 0.0.0.0:* LISTEN 21887/bird
tcp 0 0 192.168.78.21:46427 192.168.78.22:179 ESTABLISHED 21887/bird
  • 容器启动时,劫持相关 Docker API,进行网络初始化

    • 如果没有指定 IP,则查询 Etcd 自动分配一个可用 IP
    • 创建一对 veth 接口用于容器和主机间通讯,设置好容器内的 IP 后,打开 IP 转发
    • 在主机路由表添加指向此接口的路由
主机上:
[node1]# ip link show
...
7: cali2466cece7bc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
link/ether 96:c4:86:4d:d7:2c brd ff:ff:ff:ff:ff:ff
容器内:
[container1]# ip addr
...
8: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 1e:48:3e:ec:71:52 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/32 scope global eth1
valid_lft forever preferred_lft forever
主机路由表:
[node1]# ip route
...
192.168.0.1 dev cali2466cece7bc scope link
  • 然后将此路由通过 BGP 协议广播给其他所有节点,在两个节点上的路由表最终是这样的:
[node1]# ip route
...
192.168.0.1 dev cali2466cece7bc scope link
192.168.0.2 via 192.168.78.22 dev enp0s8 proto bird
[node2]# ip route
...
192.168.0.1 via 192.168.78.21 dev enp0s8 proto bird
192.168.0.2 dev caliea3aaf5a7be scope link

大家看这个路由,node2 上面的 container2 要访问 container1(192.168.0.1),通过查路由表得知需要将包转给 192.168.78.21,也就是 node1。形象的展示数据流向是这样的:

container2[eth1] -> node2[caliea3aaf5a7be] -> route -> node1[cali2466cece7bc] -> container1[eth1]

至此,跨节点通讯打通,整个流程没有任何 NAT,Tunnel 封包。所以只要三层可达的环境,就可以应用 Calico。

利用 Profile 实现 ACL

在之前的 demo 中我们提到了 Profile,Calico 每个 Profile 都自带一个规则集,用于对 ACL 进行精细控制,如刚刚的db 的默认规则集是:

[node1]# ./calicoctl profile db rule json
{
"id": "db",
"inbound_rules": [
{
"action": "allow",
"src_tag": "db"
}
],
"outbound_rules": [
{
"action": "allow"
}
]
}

这个规则集表示入连接只允许来自 Profile 名字是 db 的实例,出连接不限制,最后隐含了一条默认策略是不匹配的全部 drop,所以同时位于不同 Profile 的实例互相是不能通讯的,这就解决了隔离的需求。

下面是一个更复杂的例子:

在常见的网站架构中,一般是前端 WebServer 将请求反向代理给后端的 APP 服务,服务调用后端的 DB:

WEB -> APP -> DB

所以我们要实现:

  • WEB 暴露 80 和 443 端口
  • APP 允许 WEB 访问
  • DB 允许 APP 访问 3306 端口
  • 除此之外,禁止所有跨服务访问

那么我们就可以如此构建 json:

对于 WEB

[node1]# cat web-rule.json
{
"id": "web",
"inbound_rules": [
{
"action": "allow",
"src_tag": "web"
},
{
"action": "allow",
"protocol": "tcp",
"dst_ports": [
80,
443
]
}
],
"outbound_rules": [
{
"action": "allow"
}
]
}
[node1]# ./calicoctl profile web rule update < web-rule.json

入站规则我们增加了一条允许 80 443

对于 APP

[node1]# cat app-rule.json
{
"id": "app",
"inbound_rules": [
{
"action": "allow",
"src_tag": "app"
},
{
"action": "allow",
"src_tag": "web"
}
],
"outbound_rules": [
{
"action": "allow"
}
]
}
[node1]# ./calicoctl profile app rule update < app-rule.json

对于后端服务,我们只允许来自 web 的连接。

对于 DB,我们在只允许 APP 访问的基础上还限制了只能连接 3306。

[node1]# cat db-rule.json
{
"id": "db",
"inbound_rules": [
{
"action": "allow",
"src_tag": "db"
},
{
"action": "allow",
"src_tag": "APP",
"protocol": "tcp",
"dst_ports": [
3306
]
}
],
"outbound_rules": [
{
"action": "allow"
}
]
}
[node1]# ./calicoctl profile db rule update < db-rule.json

很简单的几条规则,我们就实现了上述需求。

Profile 高级特性:Tag

有同学可能说,在现实环境中,会有多组不同的 APP 都需要访问 DB,如果每个 APP 都在 db 中增加一条规则也很麻烦同时还容易出错。

这里我们可以利用Profile 的高级特性 Tag 来简化操作:

  • 每个 Profile 默认拥有一个和 Profile 名字相同的 Tag
  • 每个 Profile 可以有多个 Tag,以 List 形式保存

利用 Tag 我们可以将一条规则适配到指定的一组 Profile 上。

参照上面的例子,我们给所有需要访问 DB 的 APP 的 Profile 都加上 db-users这个 Tag:

[node1]# ./calicoctl profile app1 tag add db-users
[node1]# ./calicoctl profile app2 tag add db-users
[node1]# ./calicoctl profile app3 tag add db-users
...

然后修改 db-rule.json 为:

{
"id": "db",
"inbound_rules": [
{
"action": "allow",
"src_tag": "db"
},
{
"action": "allow",
"src_tag": "db-users",
"protocol": "tcp",
"dst_ports": [
3306
]
}
],
"outbound_rules": [
{
"action": "allow"
}
]
}

将之前的 src_tag: app 替换为 src_tag: db-users。这样所有打了 db-user 这个 Tag 的实例就都能访问数据库了。

Profile 的实现

Profile 的实现基于 Iptables 和 IPSet。我们以刚刚的 db 规则集中 inbound 部分为例:

Calico 在启动后会在 Iptables 中新建一些 Chain,数据包会在不同的 Chain 之间跳转,下面我截取了一些关键的规则列表:

[node1]# iptables -n -L -v
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
target prot in out source destination
felix-FORWARD all * * 0.0.0.0/0 0.0.0.0/0
Chain felix-FORWARD (1 references)
target prot in out source destination
felix-TO-ENDPOINT all * cali+ 0.0.0.0/0 0.0.0.0/0
Chain felix-TO-ENDPOINT (1 references)
target prot in out source destination
felix-to-2466cece7bc all * cali2466cece7bc 0.0.0.0/0 0.0.0.0/0 [goto]
Chain felix-to-2466cece7bc (1 references)
target prot in out source destination
felix-p-db-i all * * 0.0.0.0/0 0.0.0.0/0
Chain felix-p-db-i (2 references)
target prot in out source destination
RETURN all * * 0.0.0.0/0 0.0.0.0/0 match-set felix-v4-db src
RETURN tcp * * 0.0.0.0/0 0.0.0.0/0 match-set felix-v4-db-users src multiport dports 3306

这个略复杂,我们慢慢看。基本上数据包是从上到下一步步跳转的。

当发给 container1 的数据包到达 node1 后,由于目标 IP 192.168.0.1和 node1 自身 IP 不同,会被放入FORWARD 链,然后跳转到felix-FORWARD,通过查询路由表:

192.168.0.1 dev cali2466cece7bc scope link

得知下一跳接口为 cali2466cece7bc,于是先跳转到felix-TO-ENDPOINT,再跳转到 felix-to-2466cece7bc

在这里,定义了具体的 ACL 列表,felix-p-db-i,这个 db 是不是很眼熟?

对,就是这个 container 所属 Profile 的名字,而 felix-p-db-i 中就是 Profiledb 的 inbound 规则集。而 felix-p-db-i 的内容:

match-set felix-v4-db src
match-set felix-v4-db-users src multiport dports 3306

felix-v4-db 和 felix-v4-db-users 是不是也很熟悉?

在 db 规则集中的两个 Tag 在这里加了个前缀变成了 IPSet,它包括了所有打了这个 Tag 的 IP 列表:

[node1]# ipset list
...
Name: felix-v4-db
Type: hash:ip
Revision: 1
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16576
References: 1
Members:
192.168.0.1
192.168.0.2

至此,ACL 部分分析完毕。可见 Calico 灵活运用了 Iptables 的种种高级特性。

性能测试

来自官方测试结果

测试环境

  • 8 core CPU
  • 64GiB RAM
  • 10Gb 网卡直连
  • Ubuntu 14.04.2 with 3.13 Kernel(3.10 版本的 Kernel 修复了一些 veth 性能的问题)
  • 没有额外的内核参数调优

测试了四种场景:

  • 物理服务器(基准)
  • 部署了 Calico 的容器之间
  • 部署了 Calico 的 OpenStack VM 之间
  • 部署了 OVS with VxLAN 的 OpenStack VM 之间

测试了两种数据大小:

  • 20000 byte
  • 500 byte

吞吐量 & CPU 使用率测试

吞吐量极限

同一时刻 CPU 的使用率

把它们合并成一张图就是每 Gb/s 的 CPU 使用率

可以看到,部署了 Calico 的两个场景都非常贴近物理服务器的性能。

延迟测试

测试方法是:节点间交换 1 byte 数据包

这个结果显示,Calico 容器非常接近物理服务器,而 OpenStack 场景由于网络虚拟化的缘故延迟稍大。

结论

测试结果表明,Calico 的性能非常接近物理服务器,比基于隧道的 OVS 性能好很多。

Calico 的发展

Calico 和 Docker 一样是很年轻的项目,但是坑比后者少多了,我遇到了一些,如docker inspect 没有显示 Calico 分配的 IP,BGP 客户端重启姿势不正确导致路由周期性消失重建等等。但是他们的开发进度非常快,一个 issue 提出来到修复可能就一两天时间(再次鄙视 Docker)。

目前唯一一个比较麻烦的问题是,Calico 这种劫持 Docker API 的方式,容器的网络栈是在容器启动后才进行初始化,所以在头几秒其实是没有网络可用的,这会导致那些启动就要访问网络的容器挂掉。解决方案有两个:

  • 升级 Docker 到支持 libnetwork 的版本,Calico 在新版本(>0.5)中支持了 libnetwork,理论上能够解决这个问题。但是代价要踩新版本 Docker 带来的更多的坑。
  • 自定义容器的 CMD,实现一个 entry 脚本,待网络可用后再 exec 载入真正的进程。

Q&A

Q:自定义容器的 CMD,实现一个 entry 脚本,待网络可用后再 exec 载入真正的进程。 有没有具体的?

主要实现方式就是 Dockerfile 中的 CMD 可以这个样子: /entry.sh your-cmd 。这个 entry.sh 中判断 IP 是否已经分配好,如果没有就 sleep 重试。分配好后再用 exec 载入后面的 your-cmd 。

Q:是否每增加一个容器宿主机就需要增加一条路由?如果容器数量很多会有问题吗?

是的。关于这个问题我咨询过开发团队,他们表示压测过单机 10 万条路由,没有问题。同时将来会推出路由段的广播机制,即:每台服务器使用一小段,之间只需要广播此段即可。

Q:如果要做容器间通讯限速,Calico能做吗?

由于每个由 Calico 管理的容器在宿主机上面都有一个唯一的网络接口(veth 的一端),通过限制此接口流量即可进行限速。Calico 官方没有提供这个功能,我们可以用常规的其他手段解决。同时这个接口还有一个好处就是非常容易做容器的流量监控,只要看接口计数器即可。

Q:Calico 和 Kubernetes 的整合你们尝试过吗?Calico只是接管了容器间的通信,和 Kubernetes 的 Service Cluster IP 没有关系吧?

我们没有使用 Kubernetes 的解决方案,而是自行开发的调度编排等组件。Cailco 官方是支持的并且有相关文档可以参考。具体请参考:https://github.com/projectcalico/calico-docker/blob/master/docs/kubernetes/README.md

原文链接

时间: 2024-08-29 03:39:39

PaaS 平台的网络需求的相关文章

办公管理支撑流程能力PaaS平台运维开发软件需求设计方案

1.概述 办公管理支撑流程能力PaaS平台(以下文中简称"能力平台")运维开发是指基于Cordys BOP 4 PaaS平台,通过二次开发,为运维开发人员提供快速支撑办公管理流程开发.实施.再造的能力,也为管理流程全生命周期管理提供数据支撑能力.管理目标及软件需求如下: (1)开发/运维人员 开发/运维人员是指系统平台管理人员.运维人员,例如:省公司与地市公司信息化系统管理员.开发/运维人员为专业信息化管理员,都是受过专业计算机教育,懂计算机软件维护.开发,但是,不是专业程序员,也就是

有容云:容器驱动的PaaS平台实现方案(上)

编者注: 本文基于上海容器大会现场演讲内容,立足于实战跟大家分享了新一代PaaS平台构建中遇到的问题.当下主流PaaS平台解析.企业交付经验及心得体会等.文章较长,分为上.下两个部分,本文为上篇. 嘉宾介绍: 马洪喜,有容云联合创始人兼首席架构师.此前担任Rancher Labs中国区技术负责人.Citrix公司资深架构师.Oracle公司虚拟化产品开发经理等职务,在容器云.IaaS云.桌面云建设方面拥有较为丰富的经验. 本次大会的大部分朋友都是以用户身份分享了自己家的故事和经验,我作为厂商代表

PAAS平台的web应用性能測试与分析

引言 为什么我会写这一篇博客,由于近期非常多京东云擎jae的用户反应一个问题就是他们部署在jae上面的应用訪问非常慢,有极少数应用甚至常常出现504超时现象.当然大家首先想到的是jae性能太差,这也是人之常情,往往出现什么错误的时候首先想到是别人的不好.工作中非常多同事也是这样,假设软件系统出现一个bug首先怀疑的肯定不是自己写的代码.今天花时间写这一篇博客主要就是告诉大家如何确定我们部署在PAAS平台(不不过JAE哦)web应用为什么慢?慢在哪儿了?有什么方法能够解决? 原因分析 出现訪问自己

同时面向运维和开发的企业级PaaS平台--OpenShift

大卫说:笔者在年初分享过一篇文章<大卫看Docker-第一篇>.文中介绍了Docker一些基本概念.本文同时作为<大卫看Docker-第二篇>而存在.     随着容器技术的兴起,越来越多的人都在关注这项技术.既然Docker是一项很不错的技术,如何将它应用到企业中呢?对此,红帽的提供了基于容器的.同时面向运维和开发的企业级开源PaaS解决方案. 此前文章已经提到过,红帽作为开源界的领导者,其所有企业级解决方案在社区都有对应的开源项目,openshift也不例外.2011年,Red

PAAS平台7&amp;#215;24小时可用性应用设计

如今非常多企业都在搭建自己的私有PAAS平台,当然也有非常多大型互联网公司搭建共同拥有PAAS平台(比如SAE/BAE/JAE(jae.jd.com)).那么使用PAAS平台来部署SAAS应用有哪些优点呢?除了大家都知道方便部署管理,节约资源和成本,今天我主要给大家介绍还有一个优点就是让部署在PAAS平台上的应用非常easy做到7×24小时不server执行(哪怕须要又一次部署和更新应用),这个对于一般的企业和普通开发人员来说是非常难办到的.当然假设要在PAAS平台做到事实上也不是那么简单的.须

移动App开发需要更多的PaaS平台而不是IaaS

时代的变迁,创业的大潮,越来越多的人关注了有点开发,越来越多的人了解了互联网服务术语:PaaS.IaaS.SaaS.BaaS等.今天大家在开发App的时候这么多复杂的云服务如何来选择呢? IaaS服务商 大家提起马上能想到的一定就是“阿里云”.“腾讯云”.“微软Azure”.“AWS”.这些都是IaaS基础云服务.这些基础云服务对你的移动App项目落地很重要,但是选择并不困难,因为选择IaaS服务商的过程更像在服务器时代的服务器,你会选择购买某一个品牌的服务器硬件. PaaS服务商,就像以前找牛

基于PaaS平台的人员跨部门多重身份技术解决方案

1.系统现状 系统使用范围为全省,包括省公司本部及各个中心.十三个地市分公司.其中,地市分公司区县按地市分公司部门管理:中心按省公司本部部门管理,只包括省级本部主要人员,其他不在系统内. 系统业务包括:公文管理.部室专业垂直办公.通用办公及专业系统,其中,部室专业垂直管理为9个独立系统,专业系统为9个独立系统,公文管理与通用办公.业务流程(370个流程).综合信息(含20个信息专栏)组成全省集中办公系统. 2.关于人员跨部门多重身份解决措施 由于原系统为分散独立系统,人员跨部门多重身份的情况较少

PaaS平台未来前瞻——API接口渠道之争

顺应互联网大开放时代的契机,提供API接口,输出某些定向能力的商用PaaS平台正在迅速抢占云计算市场,API作为一种"产品"趋势正在逐渐兴起,如果要与成熟标准的软件产品运作模式找差距,企业级API接口的渠道尚处于雏形状态. PaaS平台的定位和基因决定了其只能为用户提供API接口,最多是辅助说明性的DEMO产品,无法像标准的软件产品一样满足用户需求,这样一来,无论对能力平台使用者. 还是想要做API接口代理者而言,都有一定的技术门槛,即便是开放API领域的开拓者和领导者Google也只

有容云:容器驱动的PaaS平台实现方案(下)

编者注: 本文基于上海容器大会现场演讲内容,立足于实战跟大家分享了新一代PaaS平台构建中遇到的问题.当下主流PaaS平台解析.企业交付经验及心得体会等.文章较长,分为上.下两个部分,本文为下篇. 前文阅读请点击:容器驱动的PaaS平台实现方案(上) 下面我花一点时间跟大家分享下比较干货的东西.比如说容器的网络,之前我们也听了一些专家谈到Flannel,Calico 等等,但是我不知道大家有没有注意到,国内外在谈容器网络的时候更多的时候会再谈Overlay网的构建,比如IPSec.VXLAN等等