容器技术,其优势以及应用场景想必大家都不陌生。本文旨在用DIY一个简陋的Linux容器作为例子,让感兴趣的同学大致体会容器是如何建成的。

什么是容器?

提到容器最容易想到的大概就是Docker了, 然而Docker只是容器的一种实现以及提供丰富的周边工具,本质上也是利用了Linux primitives构建Linux container。如果要求我只能用一句话来定义容器,那么我会说:容器是 一个隔离的进程工作室 (an isolated process workspace)。这么定义当然太笼统,展开细节可以轻松讲几篇文章,我们这里聚焦最浅显的概念:隔离 (isolation),普遍的观点是将隔离分为两类:

  1. Namespace isolation: 容器内的进程看到的pid, 文件系统,hostname, IP, 网卡都是独立于host的,在namespace内无法修改其他namespaces的内容。
  2. 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结合在一起搭建出了易用容器。欢迎对容器技术感兴趣的同学拍砖交流。

无需Docker, 5分钟徒手DIY 一个Linux容器相关推荐

  1. 一分钟学一个 Linux 命令 - ls

    前言 大家好,我是 god23bin.今天我给大家带来的是 Linux 命令系列,每天只需一分钟,记住一个 Linux 命令不成问题.今天,我们要介绍的是一个常用而又强大的命令:ls(list). 什 ...

  2. 五分钟,手撸一个Spring容器!

    Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开Spring神秘的面 ...

  3. 五分钟,手撸一个Spring容器

    大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开S ...

  4. Linux 容器 (LXC) 介绍

    自云计算兴起以来,微服务一直在稳步流行.亚马逊和 Netflix 等公司正在使用云平台来提供他们最需要的服务.容器是此类云原生应用程序的核心.容器是一个隔离的环境,可促进微服务并提供卓越的扩展性和快速 ...

  5. Windows上的原生Linux容器(盆盆的Kubecon课程分享)

    您测试过Windows上的Linux容器吗? 这是本周末刚闭幕的Kubecon大会,笔者在课堂上提出的问题. 关于这个问题,大家常有的困惑就是,我们为什么要在Windows上运行Linux容器?您可以 ...

  6. 荔枝派zero运行linux,荔枝派Zero: 5分钟DIY一个迷你笔记本电脑

    本文演示了使用荔枝派Zero的RGB LCD 接口快速DIY一个掌上笔记本电脑的例子,展示了Zero实现掌上人机交互设备的便利性. 除此之外,荔枝派Zero还具有MIPI摄像头接口,可用于机器视觉识别 ...

  7. 使用Docker在5分钟内设置一个私有密码库

    收回隐私 (Taking Privacy Back) Github Repo here! Github回购在这里 ! With everything happening in the world ma ...

  8. 一分钟学一个 Linux 命令 - mkdir 和 touch

    前言 大家好,我是god23bin.欢迎来到<一分钟学一个 Linux 命令>系列,今天需要你花两分钟时间来学习下,因为今天要讲的是两个命令,mkdir 和 touch 命令.前一个命令是 ...

  9. 浅析linux容器--Docker

    最近公司调整微服务架构,有幸开始接触到Docker,刚开始接触到docker的时候我去简单的百度了下docker容器,了解到docker属于linux容器中的一种,通过docker,linux容器发展 ...

最新文章

  1. MapRdeuceYarn的工作机制(YarnChild是什么)
  2. 树,森林与二叉树之间的转换
  3. linux中offsetof与container_of宏定义
  4. tomcat各目录(文件)作用
  5. Mvc 页面缓存 OutputCache VaryByCustom
  6. 微信小程序如何检测接收iBeacon信号
  7. 转》谨防APP明文HTTP传输数据泄露隐私
  8. 微软一站式示例代码库(中文版)2011-07-14版本, 新添加ASP.NET, Azure, Silverlight, WinForm等14个Sample...
  9. 安卓学习笔记38:利用OpenGL ES绘制旋转立方体
  10. 华为云ModelArts 3.0助力行业AI高效落地
  11. WebWork配置文件详解
  12. Android SQL删除表、清空表
  13. c 自定义实现string类 clear_JVM类加载器是否可以加载自定义的String
  14. Yolo3 实现目标检测
  15. win10如何在当前目录打开cmd窗口
  16. MAC 用命令查看IP
  17. 解决各个浏览器文件下载中文乱码问题
  18. 农夫过河算法java,Java农夫过河问题的继承与多态实现详解
  19. Android Telephony纲要
  20. Mobius反演(莫比乌斯反演)

热门文章

  1. Linux编程获取本机IP地址
  2. Spring源码解析之BeanFactory
  3. win10 64位 安装TensorFlow
  4. scala学习--难点
  5. 【OpenCV新手教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放...
  6. 一个对于小数四舍五入的算法C语言版
  7. numpy——zeros(), ones(), empty(), eye()
  8. 分页offset格式_MySQL中limit分页查询性能问题分析
  9. 如何修改vue打包的名字_教你如何修改Mac的电脑名字
  10. go mockweb接口_GitHub - duxiaoman/AnyMock: 通用接口Mock平台