无需Docker, 5分钟徒手DIY 一个Linux容器
容器技术,其优势以及应用场景想必大家都不陌生。本文旨在用DIY一个简陋的Linux容器作为例子,让感兴趣的同学大致体会容器是如何建成的。
什么是容器?
提到容器最容易想到的大概就是Docker了, 然而Docker只是容器的一种实现以及提供丰富的周边工具,本质上也是利用了Linux primitives构建Linux container。如果要求我只能用一句话来定义容器,那么我会说:容器是 一个隔离的进程工作室 (an isolated process workspace)
。这么定义当然太笼统,展开细节可以轻松讲几篇文章,我们这里聚焦最浅显的概念:隔离 (isolation),普遍的观点是将隔离分为两类:
- Namespace isolation: 容器内的进程看到的pid, 文件系统,hostname, IP, 网卡都是独立于host的,在namespace内无法修改其他namespaces的内容。
- Resource isolation: 容器内的进程系统资源的使用可以限制,例如容器内的进程们CPU/Memory使用不能超过quota等
DIY一个Linux容器
接下来我们将step by step讲解如何用Linux自带的工具将进程 (processes)放入容器 (an isolated workspace), 注: 整个流程参考了Eric Chiang的containers from scratch, 原文中有几个地方我没有直接走通,这里加了一些个人的剪裁和修改。我的环境是Ali Linux 4.4.114-1.al7.x86_64, 接下来的步骤无需安装Docker也不需要安装其他工具
Namespace隔离
1. 准备容器文件系统 (类似Docker image)
container filesystem本质上就是一个tarball, 有兴趣的话可以看一下Redbeard的minimal containers 视频,核心是一句话:A CONTAINER FILESYSTEM ISN'T MAGIC, IT'S JUST A TARBALL
# 下载+解压container filesystem tarball [root@iZ2zea1bcc0lrqq26qw1esZ ~]# wget https://github.com/ericchiang/containers-from-scratch/releases/download/v0.1.0/rootfs.tar.gz [root@iZ2zea1bcc0lrqq26qw1esZ ~]# tar -xzf rootfs.tar.gz [root@iZ2zea1bcc0lrqq26qw1esZ ~]# ls rootfs bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
可以看到 rootfs下面看到的文件结构和Liunx / 下看到的很相似。
2. 创建一个简陋的容器
我们的目的是进入rootfs,并且以rootfs为新的root (/), 这样进程只能看到rootfs中的文件, Linux自带的chroot工具可以很容易达到这种效果
# Mount rootfs/proc,这样在rootfs子文件系统中的进程可以做`ps aux`这样的命令 [root@iZ2zea1bcc0lrqq26qw1esZ ~]# mount -t proc proc rootfs/proc # 用chroot将 /bin/bash 进程看到的root限制在dir rootfs, 恭喜你进入了你自建的隔离非常简陋的容器! [root@iZ2zea1bcc0lrqq26qw1esZ ~]# chroot rootfs /bin/bash # 现在bash shell中的root (/) 已经变成了 host上面的“/root/rootfs” root@iZ2zea1bcc0lrqq26qw1esZ:/# bin/ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys test.py tmp usr var # 这时文件系统隔离有了,但是进程隔离还没有, 下面的命令可以看到机器上所有的进程,我们容器里的shell甚至可以去kill其他进程,不理想! root@iZ2zea1bcc0lrqq26qw1esZ:/# bin/ps aux # 退出该容器 exit
3. 隔离PID
为了限制/bin/bash进程能看到的pids,我们需要为bash进程创建pid namespace。用 Linux unshare command 可以方便地为进程创造不同类型的namespace
# 下面的命令为 /bin/bash进程创建了一个新的process namespace [root@iZ2zea1bcc0lrqq26qw1esZ ~]# unshare -p -f --mount-proc=$PWD/rootfs/proc chroot rootfs /bin/bash # 现在shell里面只能看到本pid namespace的两个进程了,注意shell进程是pid 1, 这里和Docker的主进程为pid 1完全一样 root@iZ2zea1bcc0lrqq26qw1esZ:/# bin/ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 20272 3284 ? S 19:08 0:00 /bin/bash root 2 0.0 0.0 17500 2084 ? R+ 19:08 0:00 bin/ps aux
上面几步之后,我们基本有了一个对filesystm和pid有隔离的容器,这个容器距离严谨的namespace隔离还有很长的路要走,例如创建Network namespace, 修改hostname, username space (不允许容器用户为root), 这里假设我们的隔离的需求很简单只是mount + pid。
Resource 隔离
CGroup (control group) 对于CPU, memory, 文件系统资源的隔离的技术已经很成熟,大多数容器也都选择用cgroup来做资源限制,对cgroup的细节不在这里介绍,下面的步骤会为上面创建好的container加上内存使用的限制
# 下面这些命令在host shell内执行,而不是刚才创建的container shell # 创建一个叫做mygroup的memory cgroup [root@iZ2zea1bcc0lrqq26qw1esZ ~]# mkdir /sys/fs/cgroup/memory/mygroup # 限制在这个cgroup里面所有的进程内存使用和不能超过100MB [root@iZ2zea1bcc0lrqq26qw1esZ ~]# echo "100000000" > /sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes # 找到我们的container在host上面的pid,这里是10028 (这里需要注意有两个bash进程,我们的容器进程是从unshare命令里面fork出来的,因此是10028而不是unshare 10027本身) [root@iZ2zea1bcc0lrqq26qw1esZ ~]# ps aux|grep bin/bash root 10027 0.0 0.0 107904 704 pts/0 S 01:49 0:00 unshare -p -f --mount-proc=/root/rootfs/proc chroot rootfs /bin/bash root 10028 0.0 0.0 20264 3068 pts/0 S+ 01:49 0:00 /bin/bash root 10031 0.0 0.0 112668 2216 pts/1 S+ 01:49 0:00 grep --color=auto bin/bash # 根据你找到pid替换$MY_PID [root@iZ2zea1bcc0lrqq26qw1esZ ~]# export MY_PID=10028 # 将shell pid加入mygroup, 加入之后,容器内所有的进程 (bash以及其子进程)都会受到内存使用100M的 [root@iZ2zea1bcc0lrqq26qw1esZ ~]# echo $MY_PID > /sys/fs/cgroup/memory/mygroup/tasks # 下面这些命令在container shell内执行 # 测试一下效果,下面的python脚本会不断消耗内存 bin/cat <<EOF > ./test.py a = '' i=0 while True: i += 1 print "%dmb" % (i*10,) some_str = ' ' * 10000000 a = a + some_str EOF # 执行脚本,可以看到内存接近100M的时候脚本会被kill, 现在我们再也不用担心这个容器内的进程会用光host的内存啦, nice! root@iZ2zea1bcc0lrqq26qw1esZ:/# /usr/bin/python test.py 10mb 20mb 30mb 40mb 50mb 60mb 70mb 80mb Killed
总结
到这里,我们DIY了一个文件系统,pid隔离,内存限制为100M的容器。通用的容器需要对网络,user namespace, 以及其他系统资源(CPU, disk等)做了更完整的隔离,这篇文章的目的不是鼓励大家去自建容器,而是通过一个简单的动手教程让大家熟悉Linux容器的构成和实现。Docker以及其他容器runtime都提供了强大的功能,但是归根溯源都还是将Linux已有的namespace工具,cgroup结合在一起搭建出了易用容器。欢迎对容器技术感兴趣的同学拍砖交流。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
windows7上使用docker容器
windows7上使用docker容器 1.安装 下载DockerToolbox,并安装。 下载地址:https://dn-dao-github-irror.daocloud.io/docker/toolbox/releases/download/v17.06.2-ce/DockerToolbox-17.06.2-ce.exe。安装过程不多说了,和别的windows软件差别不大。安装完成后桌面有以下图标: 2.开始 双击“Docker Quickstart Terminal”,出现下图: 图中说的意思是,没有找到默认的Boot2Docker ISO文件,正在下载最新的发布包。但实际上,这个文件是在我们的安装路径已经有了。并且从github.com下载在国内几乎是不可能的! 找到刚才的安装路径,复制boot2docker.iso文件到C:\Users\jackie.docker\machine\cache目录下,关掉docker终端,重新打开。 如下图:说明docker启动成功。至此,Windows7上的docker安装完成。 3.部署tomcat 从仓库拉取tomcat镜像: dock...
- 下一篇
捷讯分享阿里云服务器ECS如何升降配置
我们在日常用阿里云产品的时候都知道云服务器有升降配的操作,很大程度适应了大部分客户的需求。今天阿里云湖北授权服务中心的小编就跟大家简单的聊聊如何升降云服务器的配置。所谓升降配就是对实例的规格配置(CPU+内存)、带宽等进行升级或降级。另外,如果用户想使用API进行升级配置,请提交工单申请白名单开通。 一、阿里云的升降配有以下一些应用场景 1、续费配置:即对实例规格进行降级。在续费的同时,选择较低的实例规格,节省费用。 2、升级配置:选择同系列中更高的实例规格配置。 3、带宽临时升级:支持带宽无缝不停机在线升级,升级后无需重启。 4、转换基础带宽的付费方式,即从按固定带宽转换为按使用流量,或相反。 5、首次 0Mbps 带宽升级(需要在控制台重启实例)。 注意: ① 仅支持包年包月的实例。按量付费的实例不能升降配。 ② 降配后的新配置会在新的续费周期内生效。当前剩余服务期限内配置不会发生改变。 ③ 提交续费降配操作后,当前剩余服务期限内将不再支持升级和降配功能。需谨慎操作。 ④ 升降配的时候,实例规格族不能互换,只能选择同一规格族中的相关规格。 ⑤ 对实例规格进行升级,包括 CPU、内存...
相关文章
文章评论
共有0条评论来说两句吧...