前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >说说linux容器的隔离

说说linux容器的隔离

原创
作者头像
mariolu
修改2020-02-13 12:00:30
3K0
修改2020-02-13 12:00:30
举报

前言:

说到docker,大家都懂。但是LXC可能就比较陌生。Docker的起源于LXC。LXC的英文全称是Linux Container,相比较其他虚拟机而言,是一种轻量级虚拟化技术,它介于Chroot(linux的一个改变根目录挂载点的机制)和完整开发的虚拟机之间。LXC不使用单独的内核资源,但是可以创建一个类似的Linux操作系统环境。

Linux Daemon(LXD)是一个轻量级容器管理程序,他是凌驾于LXC之上而衍生的一套外部管理工具。Docker也使用类似技术。LXD使用了LXC API来管理LXC,而且新增RESTful API。

这边文章通过研究LXC的隔离特征来说明容器的一些原理。

一、独立的命名空间

每个容器都有一套独立的Linux环境命名空间。命名空间的作用是对每一个环境做隔离,使用环境的用户来看,好像是一个新的机器环境。命名空间是linux内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。

一套独立的环境需要做到有用户id独立、进程id独立、root根目录独立、网络独立、UTS(UNIX Time-sharing System 的缩写,主机名和NIS域名)独立。

图1、查看lxc隔离依赖的要点
图1、查看lxc隔离依赖的要点

二、用户和用户组ID独立

2.1 uid的映射

不同的 namespace 中用户可以有相同的 UID 和 GID,它们之间互相不影响。父子 namespace 之间可以进行用户映射,如父 namespace (宿主机)的普通用户映射到子 namespace (容器)的 root 用户,以减少子 namespace 的 root 用户操作父 namespace 的风险。user namespace 功能虽然在很早就出现了,但是直到 Linux kernel 3.8之后这个功能才趋于完善。

比如/etc/subuid文件描述了容器内的id和容器外id的对应关系。root:300000:65536的意思

是:root启动的容器,那么容器外是从300000开始到365536的范围对应着容器内1-65536的范围

图2、uid的映射
图2、uid的映射

2.2 uid的取值范围

每个Linux系统要求的uid可选范围都不一样,但是uid通常是由32位,也就是最大值可以是2^31-2(范围:1~4294967295)。路径/etc/login.defs文件的UID_MIN、UID_MAX限定了用户useradd新用户自己设置uid的最小值和最大值,2^31-1是个无效id,实验测试以下命令不能成功。

代码语言:shell
复制
useradd -u 4294967296 test

Uid的取值区间范围作了划分,不同发行商的Linux系统有不一样的划分,但是一般是这么约定:

  • 0-99系统用户uid:
  • 100-500:系统管理管理员程序或者安装脚本产生的用户
  • 1000-x:用户uid,系统登录uid限制于了登录uid的最小值和最大值
  • 网络uid:更偏向于高值
  • 6553x:nobody

三、进程ID独立

3.1进程id的映射

图3、容器内外进程的映射关系
图3、容器内外进程的映射关系

3.2 进程id的取值范围

每个Linux系统要求的pid可选范围都不一样,最大值可以设置到 4194304 (= 222) 。

在64位可以设置到222差不多400w左右。Linux代码设计者认为这个数已经足够了。如果是32位,pid最大值是32768.进程id一旦赋予就不会改变。

图4、进程id的最大值
图4、进程id的最大值

每个系统的设置查看cat /proc/sys/kernel/pid_max。

我在ubuntu 16和centos 7分别得到了131072(= 217)和32768(= 215)。

3.1 那如果到达了最大值,会怎么样?

Pid是顺序产生的。当pid到达最大值,它会从0继续开始找最近可用的pid,如果都没有pid,会报错。

四、UTS(hostname独立)

以下测试了hostname在每个命名空间是独立存在的。

代码语言:javascript
复制
unshare --uts --fork bash
hostname//查看继承的原来hostname
hostname modified //修改成新的hostname
hostname //确认修改成功
eixt //退出
hostname //此时hostname还是原来的hostname
图5、hostname独立
图5、hostname独立

五、网络隔离

在每个容器所需要的最少网卡接口包括默认的lo回环link(这个有什么用):

除开lo,一般隔离的网络还有一对veth-pair。除了veth-pair,linux网卡方式还有其他

这个可以使用ip link help查看到。这里提一下,ip是个很强大的命令,包含但不止于ifconfig和route的功能,是个很强大的工具,link这里指的是链路层。对应的ip addr是网络层。

图6、网络连接type
图6、网络连接type

5.1 veth网卡

那么veth网卡是什么:veth:一对网络设备,包含两个设备,一个设备通过lxc.network.link网桥连接另一个网卡设备。

容器通过veth网卡对,veth 总是成对出现,两个veth网卡分别位于两个命名空间,实现容器内外的通信。在 veth 设备任意一端接收到的数据,都会从另一端发送出去。

图7、veth网卡对
图7、veth网卡对

还比如下面这个宿主机有两个容器,veth0和veth1是一对,veth2和veth3是一对。并且在宿主机的veth0和veth2设备通过NAT连接到外网。这个NAT是通过在iptables追加一个NAT的masquerade规则实现的。当然在发送数据前,需要经过iptables MASQUERADE规则将源地址改成宿主机 ip,这样才能接收到响应数据包。而宿主机网卡接收到的数据会通过iptables DNAT根据端口号修改目的地址和端口为容器的ip 和端口,然后根据路由规则发送到网桥 docker0 中,并最终由网桥 docker0 发送到对应的容器中。

图8、容器网卡全局图
图8、容器网卡全局图

5.2、lxc网络观察

一个veth网卡对和NAT桥接例子,左半部分是容器内,右半部分是宿主机器。eth0和vethK57NDU是一个veth对。发送或接收给一个veth的数据包相当于丢给另一个veth网卡。然后vethK57NDU是通过NAT桥接网络到lxcbr0.

图9、宿主机和容器的网卡配置
图9、宿主机和容器的网卡配置

进一步查看vethK57NDU和lxcbr0的桥接关系:

图10、宿主机的网桥设置
图10、宿主机的网桥设置

所以在容器内丢给eth0的数据包就是相当于丢给宿主机的lxdbr0。

还有个问题是,那么容器内他可能是访问8.8.8.8的因特网,那么这个lxdbr0并没有连接外网,那么数据包是怎么路由出去的。这里其实还有个NAT的MASQUERADE,通过这个规则,外网的数据包最终会通过有联网的ens33外网网卡发出去。

图11、nat的ip路由规则
图11、nat的ip路由规则

容器内的网络配置可以通过查看到,容器内执行lxc profile edit default

图12、容器的网卡设置
图12、容器的网卡设置

5.3做个试验验证下

代码语言:shell
复制
sudo ip netns add mynamespace //创建一个名字叫mynamespace的网络命名空间
sudo ip netns list mynamespace //列出这个命名空间
sudo ls -l /var/run/netns/ //在var目录下生成一个文件夹
total 0     -r-------- 1 root root 0 Nov 10 22:24 mynamespace
sudo ip netns exec mynamespace bash //进入这个命名空间
ip link add vethMYTEST type veth peer name eth0 //新增一个veth网卡对,两个名字分别叫vethMYTEST和eth0
ip link list //查看所有的网卡链路层,可以看到刚添加的两个设备
ip address add 10.0.3.78/24 dev eth0 //为eth0添加ip地址
ip link set eth0 up //启动eth0网络层
ip address list //查看所有的网卡ip层
ip link set vethMYTEST netns 1 //将vethMYTEST设备移出去,移到宿主机
exit //退出去到宿主机
ping -c 2 10.0.3.78 //宿主机ping mynamespace命名空间的eth0网卡,发现还不可达
sudo brctl addif lxcbr0 vethMYTEST //做网桥设置,把vethMYTEST连接到lxcbr0,此时ping到lxcbr0的数据包就可以到命名空间里的eth0
ping -c 2 10.0.3.78 //这时已经可达
sudo ip netns exec mynamespce bash//再次进入这个命名空间
ping 8.8.8.8 //内部发送给外部的包不可达
traceroute 8.8.8.8//并没有找到eth0网卡
ip route//观察没有到网关的路由默认规则
sudo route add default gw 10.0.3.1 eth0//新增一条到网关的路由默认规则
ping 8.8.8.8//ping测试成功



原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 一、独立的命名空间
  • 二、用户和用户组ID独立
    • 2.1 uid的映射
      • 2.2 uid的取值范围
      • 三、进程ID独立
        • 3.1进程id的映射
          • 3.2 进程id的取值范围
            • 3.1 那如果到达了最大值,会怎么样?
            • 四、UTS(hostname独立)
            • 五、网络隔离
              • 5.1 veth网卡
                • 5.2、lxc网络观察
                  • 5.3做个试验验证下
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档