├── .env ├── .gitignore ├── Makefile ├── Pipfile ├── README.md ├── mini_docker ├── __init__.py ├── cli.py └── libc.py ├── screenshot └── v01.gif ├── setup.cfg └── setup.py /.env: -------------------------------------------------------------------------------- 1 | PYTHONPATH=. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | 26 | # Installer logs 27 | pip-log.txt 28 | pip-delete-this-directory.txt 29 | 30 | # Unit test / coverage reports 31 | htmlcov/ 32 | .tox/ 33 | .coverage 34 | .coverage.* 35 | .cache 36 | nosetests.xml 37 | coverage.xml 38 | *,cover 39 | 40 | # Translations 41 | *.mo 42 | *.pot 43 | 44 | # idea 45 | .idea 46 | *.iml 47 | 48 | .vscode 49 | .vagrant 50 | .DS_Store 51 | *.log -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON=python 2 | PIPENV=pipenv 3 | 4 | all: help 5 | 6 | help: 7 | @echo "dev -开发环境" 8 | @echo "dist -发布" 9 | @echo "build - 构建" 10 | @echo "clean - 清理" 11 | 12 | dev: 13 | $(PIPENV) shell 14 | 15 | clean: 16 | find . -name "*.pyc" -print0 | xargs -0 rm -rf 17 | -rm -rf .coverage 18 | -rm -rf build 19 | -rm -rf dist 20 | -rm -rf *.egg-info 21 | 22 | build: 23 | $(PYTHON) setup.py sdist 24 | 25 | dist: clean build -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://mirrors.ustc.edu.cn/pypi/web/simple" 3 | verify_ssl = false 4 | name = "pypi" 5 | 6 | 7 | [requires] 8 | python_version = '2.7' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###A mini container 2 | 一个模拟Docker的container系统 3 | 4 | ![截图](./screenshot/v01.gif) 5 | 6 | 7 | ```mkdir /tmp/rootfs && tar -Jxf centos-7-docker.tar.xz -C /tmp/rootfs && git clone https://github.com/fireflyc/mini-docker.git 8 | cd mini-docker/ && pipenv shell && python setup.py develop 9 | ``` -------------------------------------------------------------------------------- /mini_docker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireflyc/mini-docker/0ecf8a2708e65d9495a8eb334e67cecc0403ee0b/mini_docker/__init__.py -------------------------------------------------------------------------------- /mini_docker/cli.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | from ctypes import CFUNCTYPE, c_int, create_string_buffer, c_void_p, cast 4 | from signal import SIGCHLD 5 | from mini_docker import libc 6 | 7 | STACK_SIZE = 1024 8 | 9 | 10 | def child_func(): 11 | print("child pid=%d ppid=%d" % (os.getpid(), os.getppid())) 12 | libc.sethostname("mytest") 13 | new_root_path = "/tmp/rootfs" 14 | target = os.path.join(new_root_path, "proc") 15 | 16 | if not os.path.exists(target): 17 | os.mkdir(target, 0x755) 18 | libc.mount("proc", target, "proc") 19 | 20 | libc.mount(new_root_path, new_root_path, "", libc.MS_BIND | libc.MS_REC) 21 | putold = os.path.join(new_root_path, ".pivot_root") 22 | 23 | if not os.path.exists(putold): 24 | os.mkdir(putold, 0x700) 25 | libc.pivot_root(new_root_path, putold) 26 | os.chdir("/") 27 | 28 | putold = "/.pivot_root" 29 | libc.umount(putold, libc.MNT_DETACH) 30 | os.removedirs(putold) 31 | # 这里会使用新的文件系统 32 | os.execle("/bin/bash", {"PS1": "[mini-docker]"}) 33 | return 0 34 | 35 | 36 | def main(): 37 | child = CFUNCTYPE(c_int)(child_func) 38 | child_stack = create_string_buffer(STACK_SIZE) 39 | child_stack_pointer = c_void_p(cast(child_stack, c_void_p).value + STACK_SIZE) 40 | flags = libc.CLONE_NEWUTS | libc.CLONE_NEWPID | libc.CLONE_NEWIPC \ 41 | | libc.CLONE_NEWNS | libc.CLONE_NEWNET | libc.CLONE_NEWUSER 42 | pid = libc.clone(child, child_stack_pointer, flags | SIGCHLD, ) 43 | print("parent pid=%d ppid=%d child_pid=%d" % (os.getpid(), os.getppid(), pid)) 44 | libc.uid_gid_mapping(pid, "0 %d 1" % os.getuid(), "0 %d 1" % os.getgid()) 45 | os.waitpid(pid, 0) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() 50 | -------------------------------------------------------------------------------- /mini_docker/libc.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | import os 4 | from signal import SIGKILL 5 | 6 | libc = ctypes.CDLL("libc.so.6", use_errno=True) 7 | 8 | # namespace flag 9 | CLONE_NEWNS = 0x00020000 # /* New mount namespace group */ 10 | CLONE_NEWUTS = 0x04000000 # /* New utsname namespace */ 11 | CLONE_NEWIPC = 0x08000000 # /* New ipc namespace */ 12 | CLONE_NEWUSER = 0x10000000 # /* New user namespace */ 13 | CLONE_NEWPID = 0x20000000 # /* New pid namespace */ 14 | CLONE_NEWNET = 0x40000000 # /* New network namespace */ 15 | 16 | # mount flag 17 | MS_RDONLY = 1 # /* Mount read-only */ 18 | MS_NOSUID = 2 # /* Ignore suid and sgid bits */ 19 | MS_NODEV = 4 # /* Disallow access to device special files */ 20 | MS_NOEXEC = 8 # /* Disallow program execution */ 21 | MS_SYNCHRONOUS = 16 # /* Writes are synced at once */ 22 | MS_REMOUNT = 32 # /* Alter flags of a mounted FS */ 23 | MS_MANDLOCK = 64 # /* Allow mandatory locks on an FS */ 24 | MS_DIRSYNC = 128 # /* Directory modifications are synchronous */ 25 | MS_NOATIME = 1024 # /* Do not update access times. */ 26 | MS_NODIRATIME = 2048 # /* Do not update directory access times */ 27 | MS_BIND = 4096 28 | MS_MOVE = 8192 29 | MS_REC = 16384 30 | MS_VERBOSE = 32768 # /* War is peace. Verbosity is silence. MS_VERBOSE is deprecated. */ 31 | MS_SILENT = 32768 32 | MS_POSIXACL = (1 << 16) # /* VFS does not apply the umask */ 33 | MS_UNBINDABLE = (1 << 17) # /* change to unbindable */ 34 | MS_PRIVATE = (1 << 18) # /* change to private */ 35 | MS_SLAVE = (1 << 19) # /* change to slave */ 36 | MS_SHARED = (1 << 20) # /* change to shared */ 37 | MS_RELATIME = (1 << 21) # /* Update atime relative to mtime/ctime. */ 38 | MS_KERNMOUNT = (1 << 22) # /* this is a kern_mount call */ 39 | MS_I_VERSION = (1 << 23) # /* Update inode I_version field */ 40 | MS_STRICTATIME = (1 << 24) # /* Always perform atime updates */ 41 | MS_LAZYTIME = (1 << 25) # /* Update the on-disk [acm]times lazily */ 42 | 43 | # umount flag 44 | MNT_FORCE = 0x00000001 # /* Attempt to forcibily umount */ 45 | MNT_DETACH = 0x00000002 # /* Just detach from the tree */ 46 | MNT_EXPIRE = 0x00000004 # /* Mark for expiry */ 47 | UMOUNT_NOFOLLOW = 0x00000008 # /* Don't follow symlink on umount */ 48 | UMOUNT_UNUSED = 0x80000000 # /* Flag guaranteed to be unused */ 49 | 50 | 51 | def clone(func, stack, flags): 52 | result = libc.clone(func, stack, flags) 53 | if result < 0: 54 | err = ctypes.get_errno() 55 | raise OSError(err, os.strerror(err)) 56 | return result 57 | 58 | 59 | def sethostname(hostname): 60 | result = libc.sethostname(hostname, len(hostname)) 61 | if result < 0: 62 | err = ctypes.get_errno() 63 | raise OSError(err, os.strerror(err)) 64 | return result 65 | 66 | 67 | def mount(src, target, fs, flags=0, data=""): 68 | result = libc.mount(src, target, fs, flags, data) 69 | if result < 0: 70 | err = ctypes.get_errno() 71 | raise OSError(err, os.strerror(err)) 72 | return result 73 | 74 | 75 | def pivot_root(new_root, old_root): 76 | print(new_root) 77 | print(old_root) 78 | result = libc.pivot_root(new_root, old_root) 79 | if result < 0: 80 | err = ctypes.get_errno() 81 | raise OSError(err, os.strerror(err)) 82 | return result 83 | 84 | 85 | def umount(target, flags=0): 86 | result = libc.umount2(target, flags) 87 | if result < 0: 88 | err = ctypes.get_errno() 89 | raise OSError(err, os.strerror(err)) 90 | return result 91 | 92 | 93 | def uid_gid_mapping(pid, uid_map, gid_map): 94 | proc = "/proc" 95 | 96 | def write_map_file(mapping, map_file): 97 | try: 98 | with open(map_file, 'w') as f: 99 | f.write(mapping) 100 | except IOError as e: 101 | os.kill(pid, SIGKILL) 102 | raise IOError( 103 | "Can not write %s: %s\nAborting!" % (map_file, e) 104 | ) 105 | 106 | if uid_map: 107 | write_map_file(uid_map, "%s/%s/uid_map" % (proc, pid)) 108 | 109 | if gid_map: 110 | write_map_file(gid_map, "%s/%s/gid_map" % (proc, pid)) 111 | -------------------------------------------------------------------------------- /screenshot/v01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireflyc/mini-docker/0ecf8a2708e65d9495a8eb334e67cecc0403ee0b/screenshot/v01.gif -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = mini_docker 3 | description-file = 4 | README.md 5 | version = 0.0.1 6 | description = mini docker 7 | author = fireflyc, 8 | author_email = fireflyc@126.com, 9 | 10 | [files] 11 | packages = 12 | mini_docker 13 | 14 | [entry_points] 15 | console_scripts = 16 | mini-docker = mini_docker.cli:main 17 | 18 | [wheel] 19 | universal = 1 20 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | setup_requires=['pbr>=1.8'], 5 | pbr=True) 6 | --------------------------------------------------------------------------------