我们的oVirt
虚拟化平台上有一个需求,需要对同一网桥上的虚拟机之间进行网络隔离。
参考Docker
实现中对于不同网桥的网络隔离,可以简单的采用iptables
规则来实现。
Docker
在iptables
的filter
表的FORWARD
链的规则如下:
1 | [root@localhost ~]# iptables -nL -v |
Keep learning, keep living...
我们的oVirt
虚拟化平台上有一个需求,需要对同一网桥上的虚拟机之间进行网络隔离。
参考Docker
实现中对于不同网桥的网络隔离,可以简单的采用iptables
规则来实现。
Docker
在iptables
的filter
表的FORWARD
链的规则如下:
1 | [root@localhost ~]# iptables -nL -v |
Linux内核中conntrack
模块使用哈希表来存储连接跟踪条目,当哈希表条目达到上限时,系统会将新分配conntrack
条目的数据包DROP
掉,从而导致网络受到影响。此时,日志中会记录:
1 | nf_conntrack: table full, dropping packet |
哈希表条目上限由参数net.netfilter.nf_conntrack_max
设置。
网上文章对这个问题的解决方法往往是调大该参数。但在涉及多个network namespace
的场景下,不能简单的这样做,还是要根据自身场景分析清楚具体原因。
根据CentOS7 3.10.0-957
版本内核源码,实际上每个network namespace
的conntrack
哈希表是独立的。在表示network namespace
的结构体net
中的成员ct
表示conntrack
相关信息:
1 | struct net { |
netns_ct
结构中保存有独立的哈希表相关信息:
1 | struct netns_ct { |
Linux内核提供了sysctl
机制用于动态配置内核及内核模块的参数, 每个参数对应/proc/sys/
下的一个文件,可以通过sysctl
命令或直接操作/proc/sys/
下的文件对参数进行读写。比如,net.ipv4.ip_forward
对应文件/proc/sys/net/ipv4/ip_forward
。sysctl
命令实际是是对/proc/sys/
操作的封装。
如果需要将sysctl
参数持久化,可以将参数写入文件/etc/sysctl.conf
文件中,这样参数在系统重启后依然生效。这是如何实现的呢?实际是由systemd-sysctl
(或其他类似功能的服务)在系统完成内核模块的加载后,再来加载/etc/sysctl.conf
里的参数。
如果需要立即生效/etc/sysctl.conf
中的参数,可以执行sysctl -p
。但如果此时内核模块未并加载,由于/proc/sys/
目录下并不存在对应的参数文件,因而执行会失败。尽管这种场景下sysctl -p
执行失败,但通过modprobe
命令加载内核模块完成后,查看对应的sysctl
参数,却发现sysctl
参数已经生效。那这种场景下是如何令/etc/sysctl.conf
中的参数生效的呢?
近期遇到一个C++程序退出的问题,经过调查发现,程序是由于接收到SIGPIPE
信号而退出。该程序是多线程程序,使用libcurl
进行HTTPS
访问,同时设置了CURLOPT_NOSIGNAL
选项,但没有自己处理SIGPIPE
信号。在一些情况下,连接已经处理关闭状态,但应用程序不知道,依然向连接发送数据,就会导致SIGPIPE
信号产生,进而导致程序退出。修复方法比较简单,只要应用程序设置SIGPIPE
信号的处理程序即可。
上述问题要发生的一个前提是CURLOPT_NOSIGNAL
选项需要被设置,什么情况下需要设置它呢?libcurl
的作者的博文上写到过这个问题:
1 | 12. Understanding CURLOPT_NOSIGNAL |
业务场景中,需要创建指定的network namespace
, 并且内核模块中的netfilter
逻辑只应生效在该network namespace
中。这就需要我们创建network namespace
之后,将指定的namespace
传递给内核模块。
用户态创建network namespace
可以使用ip
命令指定名称,如:
1 | ip netns add ns1 |
但实际上在内核中,network namespace
并不具备名称信息,名称信息只存在于用户态。可以参考man ip-netns
:
1 | By convention a named network namespace is an object at /var/run/netns/NAME that can be opened. The file descriptor resulting |
最近发现kolla
安装的openstack(Ocata版本)
环境中,某虚拟网络上的虚拟机对外访问异常。经过调查,发现虚拟机外发数据包经过安全组
的网桥后源地址被修改为宿主机的IP
。
简化的网络拓扑如图:
我们的系统使用ansible
执行部署过程,然而部署的目标操作系统和CPU架构环境多种多样,存在大量适配工作。之前调研过是否有Golang
开发的ansible
替代物,但没有找到合适的方案。这种到处运行的场景很适合使用容器,但如果使用docker
, 在不同操作系统上也需要准备不同的docker
安装包,依然需要适配,因而也不是很满足。今天想到runc
本身就是Golang
开发的纯静态二进制,非常适合到处分发,因而尝试调研直接使用runc
来做为运行时运行ansible
容器。
在容器领域,OCI:Open Container Initiative
制定了两个规范,镜像规范: image-spec
和运行时规范:runtime-spec
。镜像规范规定了镜像文件的格式,而运行时规范规定了如何根据配置运行容器。它们之间通过filesystem bundle
联系在一起,镜像可以通过工具转换成filesystem bundle
, 运行时根据filesystem bundle
运行容器进程。
我们的主机网络防护是基于netfilter
实现。最近遇到需要对访问主机上Docker
容器的流量进行防护。几年前其实就处理过这个场景,时间久远忘记了,重新梳理一下记录下来。
我们的主机网络防护模块的hooknum
为LOCAL_IN
和POST_ROUTING
, 并且hook
的优先级为NF_IP_PRI_FIRST
, 也就是在hooknum
位置最先运行。
之前的文章<<从外部访问Docker桥接网络容器路径分析>>分析了从外部访问Docker
桥接网络的网络路径。
PRE_ROUTING
, FORWARD
和 POST_ROUTING
阶段,在PRE_ROUTING
阶段会进行DNAT
, 将目的IP/PORT
, 修改为容器的IP/PORT
。 PRE_ROUTING
, FORWARD
和POST_ROUTING
阶段,在POST_ROUTING
阶段会进行SNAT
, 将源IP/PORT
修改为外部宿主机的IP/PORT
。从网络路径来看,在POST_ROUTING
阶段数据包上的地址是容器本身的地址, 因而我们可以简单的将容器IP/PORT
端口在规则中配置,就可以实现对于访问容器内部流量的防护。
我们的网络防护功能是基于netfilter
框架实现,依赖于nf_conntrack
模块用于跟踪网络连接。在网络连接的维度,我们需要存储一些业务相关的数据。最简单直接的方法就是将这些内容存储在nf_conn
结构中。
查看nf_conn
结构,发现nf_conn
结构中有一个指针ext
可以支持扩展:
1 | struct nf_conn { |
nf_ct_ext
结构:
1 | /* Extensions: optional stuff which isn't permanently in struct. */ |
ctr和crictl都是Kubernetes
环境中管理容器的命令行工具。但它们的目的和使用方法有所不同。
crictl
是基于Kubernetes
的CRI: Container Runtime Interface
接口规范来管理容器, ctr
是containerd自带的容器管理工具, 本身和Kubernetes
无关。
Kubernetes
使用crictl
来管理任意兼容CRI
接口的容器运行时。
containerd
相比于docker
,增加了namespace
的概念,每个image
和container
都在各自的namespace
下可见。目前kubernetes
使用k8s.io
作为namespace
名称。