初识网络协议:什么是容器
容器(Container)的思想是要变成软件交付的集装箱。而集装箱最重要的就是“打包”和“标准”两大特点。所谓打包,就是将所有的货物打包在一起,然后放到集装箱里面;所谓标准,就是集装箱的尺寸全部都是一样的。
对于容器而言,打包就是将货物封装起来,使货物之间互不干扰,相互隔离。隔离主要使用了两种技术,namespace
和cgroup
。
命名空间(namespace)
命名空间是用来解决在不同的空间里面,类名相同引起的冲突。这在很多编程语言里面都很常见,比如PHP。
在Linux下也是这样的,很多的资源都是全局的。比如进程有全局的进程ID,网络也有全局的路由表。但是,当一台Linux上跑多个进程的时候,如果我们觉得使用不同的路由策略,这些进程可能会冲突,那就需要将这个进程放在一个独立的namespace
里面,这样就可以独立配置网络了。
Linux中网络的namespace
由ip netns
命令操作。它可以创建、删除、查询namespace
。
机制网络(cgroup)
cgroup
全称control groups
,是Linux内核提供的一种可以限制、隔离进程使用的资源机制。
cgroup
能控制哪些资源呢?它有很多子系统:
-
CPU子系统使用调度程序为进程控制CPU的访问;
-
cpuset,如果是多核心的CPU,这个子系统会为进程分配单独的CPU和内存;
-
memory子系统,设置进程的内存限制以及产生内存资源报告;
-
blkio子系统,设置限制每个块设备的输入输出控制;
-
net_cls,这个子系统使用等级识别符(classid)标记网络数据包,可允许Linux流量控制程序(tc)识别从具体
cgroup
中生成的数据包。
Docker
谈及容器,必然会说到docker。docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。
当我们使用docker run
运行一个容器的时候,能看到这样一个拓扑结构。
这张图和之前虚拟机的图有点像,容器里面有张网卡,容器外面有张网卡。容器外的网卡连到docker0网桥,通过这个网桥,容器能够直接实现相互访问。
不过虚拟机的虚拟网卡是通过TUN/TAP
设备虚拟出来的,而容器场景下并没有虚拟化软件,是通过创建一对veth pair
的网卡实现的。从一边发包,另一边就能收到。使用ip link add
创建好一对网卡后,一端打通到docker0网桥上,一端打通到容器里。
如何打通到容器里面?
每一个容器的启动都会对应一个namespace
,在docker中,pid
就是namespace
的名字,可以通过如下命令获取。
此时创建的namespace
不在默认路径下,所以ip netns
看不到,需要ln
软链接一下。这样就可以将另一端打通到容器里面了。
然后重命名容器内的网卡,并给容器内网卡设置IP地址,这样一台机器内部的容器就可以相互访问了。
访问外网的话有之前说过的桥接模式和NAT模式,docker默认使用NAT模式。NAT模式分为SNAT
和DNAT
,如果是容器内部访问外部,需要通过SNAT
,反过来外部访问容器内部,需要通过DNAT
。
那端口是如何映射的呢?
docker有两种方式,一种是通过一个进程docker-proxy
的方式,监听18080,转换为80端口。
另一种方式是通过DNAT
方式,在-A PREROUTING
阶段加一个规则,