手搓docker - 实现篇

目录

目录结构

Main

Command

Namespace

UFS

Cgroups


目录结构

手搓docker - 实现篇_第1张图片

其中, ubuntu的基础包来源于清华源

Index of /ubuntu-cdimage/ubuntu-base/releases/23.04/release/ | 清华大学开源软件镜像站 | Tsinghua Open Source MirrorIndex of /ubuntu-cdimage/ubuntu-base/releases/23.04/release/ | 清华大学开源软件镜像站,致力于为国内和校内用户提供高质量的开源软件镜像、Linux 镜像源服务,帮助用户更方便地获取开源软件。本镜像站由清华大学 TUNA 协会负责运行维护。https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/ubuntu-base/releases/23.04/release/

Main

main.go

package main

import (
	"gocker/command"
	"os"
)

func main() {
	switch os.Args[1] {
	case "run":
		err := command.Run()
		if err != nil {
			panic(err)
		}

	case "init":
		err := command.Init(os.Args[2], os.Args[3], os.Args[3:])
		if err != nil {
			panic(err)
		}
	}
}

Command

command/init.go

package command

import (
	"fmt"
	"gocker/ufs"
	"syscall"
	"os"
)

func Init(containerName string, cmd string, args []string) (err error) {
	err = ufs.SetMntNamespace(containerName)
	if err != nil {
		return fmt.Errorf("set mnt namespace fail %s", err)
	}
	
	// 改变当前工作目录
	syscall.Chdir("/")

	// 设置挂载
	defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
	err = syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")
	if err != nil {
		return fmt.Errorf("mount proc fail %s", err)
	}

	// 执行命令
	err = syscall.Exec(cmd, args, os.Environ())
	if err != nil {
		return fmt.Errorf("exec proc fail %s", err)
	}

	return nil
}

command/run.go

package command

import (
	"fmt"
	"gocker/cgroups"
	"gocker/namespace"
	"gocker/ufs"
	"os"
	"os/exec"
	"time"
)

func Run() error {
	os.Args[1] = "init"

	cmd := exec.Command("/proc/self/exe", os.Args[1:]...)

	namespace.SetNamespace(cmd)
	err := cmd.Start()
	if err != nil {
		return fmt.Errorf("cmd start fail %s", err)
	}

	containerName := os.Args[2]

	time.Sleep(time.Second)

	err = cgroups.ConfigDefaultCgroups(cmd.Process.Pid, containerName)
	if err != nil {
		fmt.Printf("config cgroup fail %s\n", err)
	}

	cmd.Wait()
	cgroups.CleanCgroupsPath(containerName)
	ufs.DelMntNamespace(containerName)
	return nil
}

Namespace

Unamespace/namespace.go

package namespace

import (
	"os"
	"os/exec"
	"syscall"
)

func SetNamespace(cmd *exec.Cmd) {
	// 设置容器进程的命名空间
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
			syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
	}

	// 设置容器进程的环境
	cmd.Env = os.Environ()
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
}

UFS

ufs/ufs.go

package ufs

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
)

const (
	// 合并后逻辑上的完整文件系统
	mntLayerPath = "/root/gocker/mnt"
	// 工作目录
	workLayerPath = "/root/gocker/work"
	// 叠加层
	overLayerPath = "/root/gocker/over"
	// 只读的根基础文件系统
	imagePath = "ubuntu-base-23.04-base-amd64"
	oldPath   = ".old"
)

// 获取容器中work目录路径
func workerLayer(containerName string) string {
	return fmt.Sprintf("%s/%s", workLayerPath, containerName)
}

// 获取容器中逻辑根文件系统路径
func mntLayer(containerName string) string {
	return fmt.Sprintf("%s/%s", mntLayerPath, containerName)
}

// 获取容器中over目录路径
func overLayer(containerName string) string {
	return fmt.Sprintf("%s/%s", overLayerPath, containerName)
}

func mntOldLayer(containerName string) string {
	return fmt.Sprintf("%s/%s", mntLayer(containerName), oldPath)
}

func delMntNamespace(path string) error {
	_, err := exec.Command("umount", path).CombinedOutput()
	if err != nil {
		return fmt.Errorf("umount fail path=%s err=%s", path, err)
	}

	err = os.RemoveAll(path)

	if err != nil {
		return fmt.Errorf("remove dir fail path=%s err=%s", path, err)
	}
	return err
}

func DelMntNamespace(containerName string) error {
	err := delMntNamespace(mntLayer(containerName))
	if err != nil {
		return err
	}

	err = delMntNamespace(workerLayer(containerName))
	if err != nil {
		return err
	}

	err = delMntNamespace(overLayer(containerName))
	if err != nil {
		return err
	}

	return nil
}

func SetMntNamespace(containerName string) error {

	containerMntPath := mntLayer(containerName)

	containerWorkPath := workerLayer(containerName)
	
	containerOverPath := overLayer(containerName)

	containerMntOldPath := mntOldLayer(containerName)

	// 创建挂载点
	err := os.MkdirAll(containerMntPath, 0700)
	if err != nil {
		return err
	}

	// 创建工作目录
	err = os.MkdirAll(containerWorkPath, 0700)
	if err != nil {
		return err
	}

	// 创建叠加目录
	err = os.MkdirAll(containerOverPath, 0700)
	if err != nil {
		return err
	}

	// 挂载overlay的联合文件系统
	err = syscall.Mount("overlay", containerMntPath, "overlay", 0, 
						fmt.Sprintf("upperdir=%s,lowerdir=%s,workdir=%s", containerOverPath, imagePath, containerWorkPath))
	if err != nil {
		return fmt.Errorf("mount overlay fail err=%s", err)
	}

	// 设置为私有文件系统
	err = syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")
	if err != nil {
		return fmt.Errorf("reclare rootfs private fail err=%s", err)
	}

	// 挂载
	err = syscall.Mount(containerMntPath, containerMntPath, "bind", syscall.MS_BIND|syscall.MS_REC, "")
	if err != nil {
		return fmt.Errorf("mount rootfs in new mnt space fail err=%s", err)
	}

	
	err = os.MkdirAll(containerMntOldPath, 0700)
	if err != nil {
		return fmt.Errorf("mkdir mnt old layer fail err=%s", err)
	}

	// 设置私有的根文件系统
	err = syscall.PivotRoot(containerMntPath, containerMntOldPath)
	if err != nil {
		return fmt.Errorf("pivot root  fail err=%s", err)
	}
	
	return nil
}

Cgroups

cgroups/cgroups.go

package cgroups

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
)

const (
	cgroupsPath = "/sys/fs/cgroup"
	dockerName  = "gocker"
)

func ConfigDefaultCgroups(pid int, containerName string) error {

	var (
		cpuPath    = path.Join(cgroupsPath, "cpu", dockerName, containerName)
		memoryPath = path.Join(cgroupsPath, "memory", dockerName, containerName)
	)

	// 创建容器的控制目录
	if err := os.MkdirAll(cpuPath, 0700); err != nil {
		return fmt.Errorf("create cgroup path fail err=%s", err)
	}
	if err := os.MkdirAll(memoryPath, 0700); err != nil {
		return fmt.Errorf("create cgroup path fail err=%s", err)
	}
	// 设置cpu
	if err := os.WriteFile(path.Join(cpuPath, "cpu.cfs_quota_us"), []byte("50000"), 0700); err != nil {
		return fmt.Errorf("write cpu quota us fail err=%s", err)
	}
	if err := os.WriteFile(path.Join(cpuPath, "tasks"), []byte(strconv.Itoa(pid)), 0644); err != nil {
		return fmt.Errorf("write cpu tasks  fail err=%s", err)
	}

	// 设置内存
	if err := os.WriteFile(path.Join(memoryPath, "memory.limit_in_bytes"), []byte("200m"), 0700); err != nil {
		return fmt.Errorf("write memory limit bytes fail err=%s", err)
	}
	if err := os.WriteFile(path.Join(memoryPath, "tasks"), []byte(strconv.Itoa(pid)), 0644); err != nil {
		return fmt.Errorf("write memory tasks  fail err=%s", err)
	}
	return nil
}

func CleanCgroupsPath(containerName string) error {
	var err error
	output, e := exec.Command("cgdelete", "-r", fmt.Sprintf("memory:%s/%s", dockerName, containerName)).Output()
	if e != nil {
		err = fmt.Errorf("cgdelete fail err=%s output=%s", e, string(output))
	}

	output, err = exec.Command("cgdelete", "-r", fmt.Sprintf("cpu:%s/%s", dockerName, containerName)).Output()
	if e != nil {
		err = fmt.Errorf("%s, cgdelete fail err=%s output=%s", err, e, string(output))
	}
	
	return err
}

你可能感兴趣的:(实现框架,容器,docker,容器,运维)