在Cloud Foundry v2版本号中,该平台使用warden技术来实现用户应用实例执行的资源控制与隔离。
简要的介绍下warden,就是dea_ng假设须要执行用户应用实例(本文暂不考虑warden container提供staging打包环境),则发送对应请求给warden server,由warden server来创建warden container,并在warden container内部执行应用实例,而warden container的详细实现中使用cgroups等内核虚拟化技术。保证了container内进程组的资源控制与隔离。warden的架构与实现能够參考我的博文:Cloud
Foundry中warden的架构与实现。
warden网络架构图
warden container的功能能够觉得与传统的虚拟机类似,不过传统的虚拟机通过Hypervisor等技术来实现资源的控制与隔离,而warden container通过虚拟化内核来达到该目的。而在网络方面,warden container技术创建出一块虚拟网卡。专门供warden container内部使用,另外为warden container内部的虚拟网卡在warden server所在的宿主机上也配对了一块虚拟网卡,充当container的外部网关。只创建出两块虚拟网卡。原则上能够保证物理上的“连通”,可是却非常难做到网络间通信的“联通”,故在物理资源以及虚拟物理资源完备的情况。warden
server在宿主机上通过iptables设计并加入了多条规则,保证整个网络的通信能够满足warden container的要求,同一时候又不影响宿主机的通信。
下面是warden server所在宿主机的物理环境、iptables所在的网络层、操作系统、用户应用层之间的关系图:
warden container网络通信方式
- 用户应用实例作为一个web server,提供HTTP訪问服务;
- 用户应用实例使用service服务时,建立与service instance的连接。
warden iptables配置之net.sh文件
- 启动warden server时,调用文件warden/warden/root/linux/net.sh,制定warden server所在宿主机的部分iptables规则。
- 在创建warden container的时候,调用文件warden/warden/root/linux/skeleton/net.sh,制定因为warden container创建后须要加入的iptables规则(包含只创建一个warden
container的iptables设置。以及为一个warden container做port映射时的iptables设置)。
warden server之net.sh
setup_filter
关于warden/warden/root/linux/net.sh,能够看该shell脚本中最为关键的两个方法setup_filter以及setup_nat。
下面是setup_filter的源代码实现:
function setup_filter() {
teardown_filter # Create or flush forward chain
iptables -N ${filter_forward_chain} 2> /dev/null || iptables -F ${filter_forward_chain}
iptables -A ${filter_forward_chain} -j DROP # Create or flush default chain
iptables -N ${filter_default_chain} 2> /dev/null || iptables -F ${filter_default_chain} # Always allow established connections to warden containers
iptables -A ${filter_default_chain} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT for n in ${ALLOW_NETWORKS}; do
if [ "$n" == "" ]
then
break
fi iptables -A ${filter_default_chain} --destination "$n" --jump RETURN
done for n in ${DENY_NETWORKS}; do
if [ "$n" == "" ]
then
break
fi iptables -A ${filter_default_chain} --destination "$n" --jump DROP
done iptables -A ${filter_default_chain} --jump REJECT # Accept packets related to previously established connections
iptables -I INPUT -m state --state ESTABLISHED,RELATED --jump ACCEPT -m comment --comment 'related-traffic' # Reject traffic from containers to host
if [ "$ALLOW_HOST_ACCESS" != "true" ]; then
iptables -A INPUT -s ${POOL_NETWORK} --jump REJECT -m comment --comment 'block-traffic-to-host'
fi # Forward outbound traffic via ${filter_forward_chain}
iptables -A FORWARD -i w-+ --jump ${filter_forward_chain} # Forward inbound traffic immediately
default_interface=$(ip route show | grep default | cut -d' ' -f5 | head -1)
iptables -I ${filter_forward_chain} -i $default_interface --jump ACCEPT
}
下面首先逐步分析每一个部分iptables的制定的意义。随后将各iptables个则串联起来分析其功能。
关于iptables在内核中的简要流程图例如以下图:
随后的即创建两条链:filter_forward_chain以及filter_forward_chain。增加这两条链先前存在的话,清楚该两条链之后再创建。
# Always allow established connections to warden containers
iptables -A ${filter_default_chain} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
由凝视能够发现,该规则加入在filter_default_chain链中,当匹配条件为:该数据包表示为是在已建立的连接上传输时。对该数据包採取ACCEPT操作。
该规则从功能的角度非常好理解,也就是:假如warden container内部发起一个请求,向外建立一个连接,则在该连接上返回数据包时,始终接受这种数据包。
然而。我始终对该规则的底层实现,抱有非常大的疑惑。
待下文分析了很多其它的warden 网络背景知识之后,再进行进一步的阐述。
在此。能够简要说明一下在iptables中DROP与REJECT的差别。DROP:直接採取丢弃数据包的操作;REJECT:对于原来的数据包採取丢弃措施。随后向数据包的发送方,返回一个类似于ICMP错误的信息包。
据包在同一个链中的规则匹配将从第一条開始匹配,遇到匹配成功则运行对应操作,否则继续向下运行,因此该规则表明这条脸中之前都匹配不成的数据包。内核会採取REJECT操作,将数据包丢弃。并返回一个错误数据包。到此,filter_default_chain中的规则设置基本完成。
# Accept packets related to previously established connections
iptables -I INPUT -m state --state ESTABLISHED,RELATED --jump ACCEPT -m comment --comment 'related-traffic' # Reject traffic from containers to host
if [ "$ALLOW_HOST_ACCESS" != "true" ]; then
iptables -A INPUT -s ${POOL_NETWORK} --jump REJECT -m comment --comment 'block-traffic-to-host'
fi
到此。对于INPUT链的规则制定已经完毕。
创建warden container之net.sh链。即当网络发现接收的网络数据包的目的IP,与该网卡的IP不一致时,则觉得须要为该数据包运行转发(FORWARD)操作。将该数据包放入FORWARD链运行规则处理。此处的代码例如以下:
# Forward outbound traffic via ${filter_forward_chain}
iptables -A FORWARD -i w-+ --jump ${filter_forward_chain} # Forward inbound traffic immediately
default_interface=$(ip route show | grep default | cut -d' ' -f5 | head -1)
iptables -I ${filter_forward_chain} -i $default_interface --jump ACCEPT
当中第一条规则表示:对于从网络设备接口名字为‘w-+’(正則表達式。也就是为warden container而虚拟出的虚拟网卡的设备名字)发来。同一时候又须要运行转发工作的数据包。将该数据包跳转至filter_forward_chain链。这样保证了,全部从warden container发出,又不是发往宿主机的网卡的数据包,进入FORWARD链后直接进入filter_forward_chain链。
稍后分析完还有一个net.sh文件之后。再统一阐述。
setup_nat
下面简单分析setup_nat方法:
function setup_nat() {
teardown_nat # Create prerouting chain
iptables -t nat -N ${nat_prerouting_chain} 2> /dev/null || true # Bind chain to PREROUTING
(iptables -t nat -S PREROUTING | grep -q "\-j ${nat_prerouting_chain}\b") ||
iptables -t nat -A PREROUTING \
--jump ${nat_prerouting_chain} # Bind chain to OUTPUT (for traffic originating from same host)
(iptables -t nat -S OUTPUT | grep -q "\-j ${nat_prerouting_chain}\b") ||
iptables -t nat -A OUTPUT \
--out-interface "lo" \
--jump ${nat_prerouting_chain} # Create postrouting chain
iptables -t nat -N ${nat_postrouting_chain} 2> /dev/null || true
# Bind chain to POSTROUTING
(iptables -t nat -S POSTROUTING | grep -q "\-j ${nat_postrouting_chain}\b") ||
iptables -t nat -A POSTROUTING \
--jump ${nat_postrouting_chain} # Enable NAT for traffic coming from containers
(iptables -t nat -S ${nat_postrouting_chain} | grep -q "\-j SNAT\b") ||
iptables -t nat -A ${nat_postrouting_chain} \
--source ${POOL_NETWORK} \
--jump SNAT \
--to $(external_ip)
}
该部分的内容比較easy理解:
1.首先创建nat_prerouting_chain 链,随后将全部的PREROUTING链中的数据包都跳转至nat_prerouting_chain;
2.随后将链绑定在nat_prerouting_chain上,保证通过lo网络设备的数据包,都跳转到该链;
3.创建nat_postrouting_chain链,保证将默认的POSTROUTING链中的全部数据包都跳转至创建的该链。
4.为从warden container中发出的数据包都採取SNAT处理。使得数据包的源IP都替换为warden server宿主机的IP。
事实上在完毕以上这两步之后,还运行了下面代码:
# Enable forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
加以保证转发工作能够有效,详细内容能够查看:/proc/sys/net/ipv4/*的各部分作用。
创建warden container之net.sh
前篇分析的是。warden server启动的时候,对warden server所在的宿主机进行的iptbales设置。在整个过程中,不涉及不论什么的container。而这一章节,将介绍分析warden server在启动某个详细warden container的时候,再对warden server所在的宿主机设置iptables规则。
该文件地址例如以下:warden/warden/root/linux/skeleton/net.sh。
setup_filter
首先分析setup_filter方法,源代码例如以下:
function setup_filter() {
teardown_filter # Create instance chain
iptables -N ${filter_instance_chain}
iptables -A ${filter_instance_chain} \
--goto ${filter_default_chain} # Bind instance chain to forward chain
iptables -I ${filter_forward_chain} 2 \
--in-interface ${network_host_iface} \
--goto ${filter_instance_chain} # Create instance log chain
iptables -N ${filter_instance_log_chain}
iptables -A ${filter_instance_log_chain} \
-p tcp -m conntrack --ctstate NEW,UNTRACKED,INVALID -j LOG --log-prefix "${filter_instance_chain} " iptables -A ${filter_instance_log_chain} \
--jump RETURN
}
1. 该方法创建了关于instance的链。随后,将该warden container instance链内的全部数据包都跳至filter_default_chain,也就是在前一章节中提到的filter_default_chain;
2.绑定该warden container instance的FORWARD链至filter_foward链,也就是说,全部进入filter_foward_chain的数据包,依据数据包从哪个网络接口设备来,进入对应的filter_instance_chain链。也就是说,从network_host_iface网络设备“接收”到的数据包(network_host_iface即为network_container_iface="w-${id}-1"),都转至filter_instance_chain。
3.创建log chain。
setup_nat
在创建warden container的时候,也会设置nat,源代码例如以下:
function setup_nat() {
teardown_nat
# Create instance chain
iptables -t nat -N ${nat_instance_chain}
# Bind instance chain to prerouting chain
iptables -t nat -A ${nat_prerouting_chain} \
--jump ${nat_instance_chain}
}
简要分析即为:创建对应的nat_instance_chain,随后将nat_prerouting_chain中的数据包都跳转至该nat_instance_chain。
net_in之port映射实现
使得Cloud Foundry内warden container的用户应用实例能够为外部提供web服务,那么warden container和warden server所在宿主机进行一个port映射操作,也就是warden server提供的net_in接口,真正的运行部分。即为创建容器的该net.sh文件里的in參数运行。
下面是net_in详细操作的源代码实现:
"in")
if [ -z "${HOST_PORT:-}" ]; then
echo "Please specify HOST_PORT..." 1>&2
exit 1
fi
if [ -z "${CONTAINER_PORT:-}" ]; then
echo "Please specify CONTAINER_PORT..." 1>&2
exit 1
fi
iptables -t nat -A ${nat_instance_chain} \
--protocol tcp \
--destination "${external_ip}" \
--destination-port "${HOST_PORT}" \
--jump DNAT \
--to-destination "${network_container_ip}:${CONTAINER_PORT}"
;;
可见该代码块中最为重要的部分为。创建DNAT规则转换的部分。也就是,在对应的instance规则链中,对于TCP数据包,假设该数据包的目的IP和目的PORT为warden server所在宿主机的IP和映射的PORT。则运行DNAT转换,转换为warden container IP和warden container的port。
总结
以上便是对Cloud Foundry v2中warden的网络设计之iptables规则制定。做了简要的分析。然而,在这之中,有非常多的设计不过留了接口,并未真正实现。
另,感谢浙江大学VLIS实验室的实习生高相林的共同研究。