Make writing a habit together! This is the fifth day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.
Introduction to the
In the previous article, we implemented the busybox directory as the root directory of the file, but operations on the file in the container still affect the directory of the host. In this article, we implement further container and image isolation
The source code that
It is available on both Gitee and Github
- Gitee: https://gitee.com/free-love/docker-demo
- GitHub: https://github.com/lw1243925457/dockerDemo
The corresponding version label in this section is 4.2. You can switch to the label version to avoid too much code
The code
In this chapter, the code in the book can be directly referenced, and the basic can run
Start by modifying the Run command startup entry, passing in the relevant path parameters and cleaning up the relevant files when the container exits
func Run(tty bool, cmdArray []string, config *subsystem.ResourceConfig) {
pwd, err := os.Getwd()
iferr ! =nil {
log.Errorf("Run get pwd err: %v", err)
return
}
mntUrl := pwd + "/mnt/"
rootUrl := pwd + "/"
// Separate the newly created read-only layer from the writable layer
parent, writePipe := container.NewParentProcess(tty, rootUrl, mntUrl)
iferr := parent.Start(); err ! =nil {
log.Error(err)
return
}
cgroupManager := cgroup.NewCgroupManager("mydocker-cgroup")
defer cgroupManager.Destroy()
iferr := cgroupManager.Apply(parent.Process.Pid); err ! =nil {
log.Errorf("cgroup apply err: %v", err)
return
}
iferr := cgroupManager.Set(config); err ! =nil {
log.Errorf("cgoup set err: %v", err)
return
}
sendInitCommand(cmdArray, writePipe)
log.Infof("parent process run")
_ = parent.Wait()
// Delete the correlation when the container exits
deleteWorkSpace(rootUrl, mntUrl)
os.Exit(- 1)}// Write the running parameters to the pipe
func sendInitCommand(array []string, writePipe *os.File) {
command := strings.Join(array, "")
log.Infof("all command is : %s", command)
if_, err := writePipe.WriteString(command); err ! =nil {
log.Errorf("write pipe write string err: %v", err)
return
}
iferr := writePipe.Close(); err ! =nil {
log.Errorf("write pipe close err: %v", err)
}
}
func deleteWorkSpace(rootUrl, mntUrl string) {
deleteMountPoint(mntUrl)
deleteWriteLayer(rootUrl)
}
func deleteMountPoint(mntUrl string) {
cmd := exec.Command("umount", mntUrl)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
iferr := cmd.Run(); err ! =nil {
log.Errorf("deleteMountPoint umount %s err : %v", mntUrl, err)
}
iferr := os.RemoveAll(mntUrl); err ! =nil {
log.Errorf("deleteMountPoint remove %s err : %v", mntUrl, err)
}
}
func deleteWriteLayer(rootUrl string) {
writeUrl := rootUrl + "writeLayer/"
iferr := os.RemoveAll(writeUrl); err ! =nil {
log.Errorf("deleteMountPoint remove %s err : %v", writeUrl, err)
}
}
Copy the code
Docker removes the Write Layer and container-init Layer of the Container while keeping all the contents of the image. In this section, the Write Layer is removed when the container exits. DeleteWorkSpace functions include DeleteMountPoint and DeleteWriteLayer. First, umount the MNT directory in the DeleteMountPoint function. Then, delete the MNT directory. Finally, delete the writeLayer folder in the DeleteWriteLayer function. The container’s changes to the file system have been erased.
Here is the code to create the read-only and writable layers:
Modify NewParentProcess
func NewParentProcess(tty bool, rootUrl, mntUrl string) (*exec.Cmd, *os.File) {
readPipe, writePipe, err := os.Pipe()
iferr ! =nil {
log.Errorf("create pipe error: %v", err)
return nil.nil
}
cmd := exec.Command("/proc/self/exe"."init")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
}
if tty {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
// Pass one end of the pipe into the fork process
cmd.ExtraFiles = []*os.File{readPipe}
iferr := newWorkSpace(rootUrl, mntUrl); err ! =nil {
log.Errorf("new work space err: %v", err)
return nil.nil
}
cmd.Dir = mntUrl
return cmd, writePipe
}
func newWorkSpace(rootUrl string, mntUrl string) error {
iferr := createReadOnlyLayer(rootUrl); err ! =nil {
return err
}
iferr := createWriteLayer(rootUrl); err ! =nil {
return err
}
iferr := createMountPoint(rootUrl, mntUrl); err ! =nil {
return err
}
return nil
}
// We put busyBox directly in the project directory as the read-only layer of the container
func createReadOnlyLayer(rootUrl string) error {
busyboxUrl := rootUrl + "busybox/"
exist, err := pathExist(busyboxUrl)
iferr ! =nil {
return err
}
if! exist {return fmt.Errorf("busybox dir don't exist: %s", busyboxUrl)
}
return nil
}
// Create a folder called writeLayer as the only writable layer of the container
func createWriteLayer(rootUrl string) error {
writeUrl := rootUrl + "writeLayer/"
if err := os.Mkdir(writeUrl, 0777); err ! =nil {
return fmt.Errorf("create write layer failed: %v", err)
}
return nil
}
func createMountPoint(rootUrl string, mntUrl string) error {
// Create the MNT folder as the mount point
if err := os.Mkdir(mntUrl, 0777); err ! =nil {
return fmt.Errorf("mkdir faild: %v", err)
}
// mount writeLayer and busybox to MNT
dirs := "dirs=" + rootUrl + "writeLayer:" + rootUrl + "busybox"
cmd := exec.Command("mount"."-t"."aufs"."-o", dirs, "none", mntUrl)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
iferr := cmd.Run(); err ! =nil {
return fmt.Errorf("mmt dir err: %v", err)
}
return nil
}
func pathExist(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, err
}
return false, err
}
Copy the code
The NewWorkSpace function is used to create container file systems, including CreateReadOnlyLayer, CreateWriteLayer, and CreateMountPoint. The CreateReadOnlyLayer function creates a busybox folder and decompresses busybox.tar to the busybox folder as the read-only layer of the container. The CreateWriteLayer function creates a folder called writeLayer as the container’s only writable layer. In the CreateMountPoint function, you first create the MNT folder as a mount point, and then mount the writeLayer and busybox directories to the MNT directory. Finally, replace the host directory /root/busybox used by the container with /root/mnt in the NewParentProcess function.
Run the test
After we run the container, we create a file in it:
➜ dockerDemo git:(main) Qualify./main run-ti sh {"level":"info"," MSG ":" Memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-18T20:38:46+08:00"} {"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-18T20:38:46+08:00"} {"level":"info","msg":"all command is : sh","time":"2022-03-18T20:38:46+08:00"} {"level":"info","msg":"parent process run","time":"2022-03-18T20:38:46+08:00"} {"level":"info","msg":"init come on","time":"2022-03-18T20:38:46+08:00"} {"level":"info","msg":"current location: /home/lw/code/go/dockerDemo/mnt","time":"2022-03-18T20:38:46+08:00"} {"level":"info","msg":"find path: /bin/sh","time":"2022-03-18T20:38:46+08:00"} / # touch /tmp/test.txt / # ls /tmp/ test.txtCopy the code
Then we view the contents of the relevant folder on the host:
➜ dockerDemo git:(main) ls busybox/ TMP ➜ dockerDemo git:(main) qualify ls MNT/TMP/test.txtCopy the code
We can see that there are no corresponding changes in BusyBox, just our read-only layer
We then use exit to exit the container. After exiting the container, the read-only layer on the host will be deleted accordingly