写点什么

自己动手写 Docker 系列 -- 4.1 使用 busybox 创建容器

作者:
  • 2022 年 3 月 20 日
  • 本文字数:6202 字

    阅读完需:约 20 分钟

简介

目前 docker demo 中还是使用的系统原有 proc,不怎么纯净,本篇中使用 busybox 来更换 docker demo 的系统挂载点

源码说明

同时放到了 Gitee 和 Github 上,都可进行获取



本章节对应的版本标签是:4.1,防止后面代码过多,不好查看,可切换到标签版本进行查看

效果查看

下面是在没有修改代码前的我们的 docker 的挂载情况,可以看到非常的庞大,不纯净


➜  dockerDemo git:(main) ✗ ./main run -ti /bin/sh{"level":"info","msg":"conmand all is /bin/sh","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"parent process run","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"init come on","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"command: /bin/sh, args: [/bin/sh]","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"RunContainerInitProcess command /bin/sh, args [/bin/sh]","time":"2022-03-17T05:44:58+08:00"}{"level":"info","msg":"find path: /bin/sh","time":"2022-03-17T05:44:58+08:00"}# pwd/home/lw/code/go/dockerDemo# ls -l总用量 4668drwxrwxr-x 2 lw lw    4096 3月  17 05:44 docsdrwxrwxr-x 3 lw lw    4096 3月   7 04:55 example-rw-rw-r-- 1 lw lw     382 3月  12 10:18 go.mod-rw-rw-r-- 1 lw lw    1965 3月  12 10:18 go.sum-rw-rw-r-- 1 lw lw   11558 3月  12 10:18 LICENSE-rwxrwxr-x 1 lw lw 4741951 3月  14 20:58 maindrwxrwxr-x 6 lw lw    4096 3月  12 10:20 mydocker-rw-rw-r-- 1 lw lw     473 3月  12 10:18 README.md# mount/dev/sda2 on / type ext4 (rw,relatime,errors=remount-ro)udev on /dev type devtmpfs (rw,nosuid,noexec,relatime,size=16103592k,nr_inodes=4025898,mode=755,inode64)devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,inode64)hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,pagesize=2M)mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)tmpfs on /run type tmpfs (rw,nosuid,nodev,noexec,relatime,size=3227448k,mode=755,inode64)tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k,inode64)tmpfs on /run/user/125 type tmpfs (rw,nosuid,nodev,relatime,size=3227444k,mode=700,uid=125,gid=130,inode64)gvfsd-fuse on /run/user/125/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=125,group_id=130)tmpfs on /run/user/0 type tmpfs (rw,nosuid,nodev,relatime,size=3227444k,mode=700,inode64)gvfsd-fuse on /run/user/0/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=0,group_id=0)nsfs on /run/docker/netns/d503d5c5cda8 type nsfs (rw)sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755,inode64)cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)cgroup on /sys/fs/cgroup/misc type cgroup (rw,nosuid,nodev,noexec,relatime,misc)cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime)efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)none on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)fusectl on /sys/fs/fuse/connections type fusectl (rw,nosuid,nodev,noexec,relatime)configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime)proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=28,pgrp=0,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=26120)/var/lib/snapd/snaps/core20_1361.snap on /snap/core20/1361 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/bare_5.snap on /snap/bare/5 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/gnome-3-38-2004_99.snap on /snap/gnome-3-38-2004/99 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/gtk-common-themes_1519.snap on /snap/gtk-common-themes/1519 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/snapd_14978.snap on /snap/snapd/14978 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/snap-store_558.snap on /snap/snap-store/558 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/core20_1376.snap on /snap/core20/1376 type squashfs (ro,nodev,relatime,x-gdu.hide)/dev/nvme0n1p1 on /boot/efi type vfat (rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)/var/lib/snapd/snaps/snapd_15177.snap on /snap/snapd/15177 type squashfs (ro,nodev,relatime,x-gdu.hide)overlay on /var/lib/docker/overlay2/d50aa7883be1a9bfc50a2519093575713c356885fd916d5973793492bb8d2d07/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/6DRQTGKE2LPQHTVVITKND2RW3J:/var/lib/docker/overlay2/l/YESCAXPAOBR6LE7TQHTNL3OF4Y,upperdir=/var/lib/docker/overlay2/d50aa7883be1a9bfc50a2519093575713c356885fd916d5973793492bb8d2d07/diff,workdir=/var/lib/docker/overlay2/d50aa7883be1a9bfc50a2519093575713c356885fd916d5973793492bb8d2d07/work)proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
复制代码


下面是跟着书中写的,使用 busybox 作为挂载点,创建容器,可以看到比较纯净了


➜  busybox git:(main) ✗ ./main run -ti /bin/sh{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-17T06:17:48+08:00"}{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-17T06:17:48+08:00"}{"level":"info","msg":"all command is : /bin/sh","time":"2022-03-17T06:17:48+08:00"}{"level":"info","msg":"parent process run","time":"2022-03-17T06:17:48+08:00"}{"level":"info","msg":"init come on","time":"2022-03-17T06:17:48+08:00"}{"level":"info","msg":"current location: /home/lw/code/go/dockerDemo/busybox","time":"2022-03-17T06:17:48+08:00"}{"level":"info","msg":"find path: /bin/sh","time":"2022-03-17T06:17:48+08:00"}/ # lsbin   dev   etc   home  main  proc  root  sys   tmp   usr   var/ # mount/dev/sda2 on / type ext4 (rw,relatime,errors=remount-ro)proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)/ #
复制代码

环境依赖准备

使用 busybox 需要做一些准备工作:


  1. 安装 docker

  2. 需要拉取 busybox 的 docker 镜像,打包后解压以备使用

安装 docker

需要在机器上安装 docker,Ubuntu20 可以参考:https://docs.docker.com/engine/install/ubuntu/


咔咔咔一顿敲命令就行了,这里就不再赘述了

拉取 busybox 的 docker 镜像,打包后解压以备使用

操作过程的命令如下,照着运行即可


docker pull busybox
// 可以看到busybox才2M不到,真小➜ dockerDemo git:(main) ✗ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEbusybox latest 2fb6fc2d97e1 5 days ago 1.24MBhello-world latest feb5d9fea6a5 5 months ago 13.3kB
// 后台运行起来➜ dockerDemo git:(main) ✗ docker run -d busybox top -b29148027fac463baa870831bcbc4dcaea48d5f2253ebd95118b8c65136017021➜ dockerDemo git:(main) ✗ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES29148027fac4 busybox "top -b" 15 seconds ago Up 15 seconds loving_edison
// 打包运行的镜像,创建目录,解压到指定目录中➜ dockerDemo git:(main) ✗ docker export -o /home/lw/Downloads/busybox.tar 29148027fac4➜ dockerDemo git:(main) ✗ mkdir /opt/busybox➜ dockerDemo git:(main) ✗ tar -xvf /home/lw/Downloads/busybox.tar -C /opt/busybox/
// 可以看到和系统根目录还挺像➜ dockerDemo git:(main) ✗ ls /opt/busyboxbin dev etc home proc root sys tmp usr var
复制代码


这样,准备工作就完成了

代码编写

本篇中代码比较少,就修改一个 init 文件即可


pivot_root 是一个重要的概念,可以参考书中描述和后面参考链接的一篇博客


照抄书中的代码跑不动,需要结合博客一起


pivot_root 是一个系统调用,主要功能是去改变当前的 root 文件系统。pivot_root 可以将当前进程的 root 文件系统移动到 put_old 文件夹中,然后使 new_root 成为新的 root 文件系统。new_root 和 put_old 必须不能同时存在当前 root 的同一个文件系统中。pivot_root 和 chroot 的主要区别是,pivot_root 是把整个系统切换到一个新的 root 目录,而移除对之前 root 文件系统的依赖,这样你就能够 umount 原先的 root 文件系统。而 chroot 是针对某个进程,系统的其他部分依旧运行于老的 root 目录中。


实现代码如下:


func RunContainerInitProcess() error {  // 用setupMount代替原来的相关挂载操作  if err := setUpMount(); err != nil {    return err  }
cmdArray := readUserCommand() path, err := exec.LookPath(cmdArray[0]) if err != nil { log.Errorf("can't find exec path: %s %v", cmdArray[0], err) return err } log.Infof("find path: %s", path) if err := syscall.Exec(path, cmdArray, os.Environ()); err != nil { log.Errorf("syscall exec err: %v", err.Error()) } return nil}
// 初始化挂载点func setUpMount() error { // 首先设置根目录为私有模式,防止影响pivot_root,这一步是书中没有的,但没有运行不起来 if err := syscall.Mount("/", "/", "", syscall.MS_REC|syscall.MS_PRIVATE, ""); err != nil { return fmt.Errorf("setUpMount Mount proc err: %v", err) }
// 获取当前路径 pwd, err := os.Getwd() if err != nil { return fmt.Errorf("get current location err: %v", err) } log.Infof("current location: %s", pwd)
err = privotRoot(pwd) if err != nil { return err }
// mount proc defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV err = syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "") if err != nil { log.Errorf("proc挂载 failed: %v", err) return err } syscall.Mount("tmpfs", "/dev", "tempfs", syscall.MS_NOSUID|syscall.MS_STRICTATIME, "mode=755") return nil}
func privotRoot(root string) error { // 为了使当前root的老root和新root不在同一个文件系统下,我们把root重新mount一次 // bind mount 是把相同的内容换了一个挂载点的挂载方法 if err := syscall.Mount(root, root, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mount rootfs to itself error: %v", err) }
// 创建 rootfs、.pivot_root 存储 old_root pivotDir := filepath.Join(root, ".pivot_root") // 判断当前目录是否已有该文件夹 if _, err := os.Stat(pivotDir); err == nil { // 存在则删除 if err := os.Remove(pivotDir); err != nil { return err } } if err := os.Mkdir(pivotDir, 0777); err != nil { return fmt.Errorf("mkdir of pivot_root err: %v", err) }
// pivot_root 到新的rootfs,老的old_root现在挂载在rootfs/.pivot_root上 // 挂载点目前依然可以在mount命令中看到 if err := syscall.PivotRoot(root, pivotDir); err != nil { return fmt.Errorf("pivot_root err: %v", err) }
// 修改当前工作目录到跟目录 if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir root err: %v", err) }
// 取消临时文件.pivot_root的挂载并删除它 // 注意当前已经在根目录下,所以临时文件的目录也改变了 pivotDir = filepath.Join("/", ".pivot_root") if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("unmount pivot_root dir err: %v", err) } return os.Remove(pivotDir)}
复制代码


编写完成后,还有重要的一步:记得运行程序的当前目录要在 busybox 下


因为我们代码中需要挂载 /proc,如果在其他目录下,那是挂载不了这个的,但 busybox 里面有这个目录

参考链接

发布于: 刚刚阅读数: 2
用户头像

关注

还未添加个人签名 2018.09.09 加入

代码是门手艺活,也是门艺术活

评论

发布
暂无评论
自己动手写Docker系列 -- 4.1使用busybox创建容器_Docker_萧_InfoQ写作平台