docker數據卷,自己動手寫Docker系列 -- 4.3實現volume數據卷

 2023-10-12 阅读 24 评论 0

摘要:簡介 在上篇中對容器和鏡像實現了進一步的文件隔離,是容器內的修改不影響到宿主機。本篇中將實現docker中的volume,提供持久化存儲能力 源碼說明 同時放到了Gitee和Github上,都可進行獲取 Gitee: https://gitee.com/free-love/docker-demoGitHub: https:

簡介

在上篇中對容器和鏡像實現了進一步的文件隔離,是容器內的修改不影響到宿主機。本篇中將實現docker中的volume,提供持久化存儲能力

源碼說明

同時放到了Gitee和Github上,都可進行獲取

  • Gitee: https://gitee.com/free-love/docker-demo
  • GitHub: https://github.com/lw1243925457/dockerDemo

本章節對應的版本標簽是:4.3,防止后面代碼過多,不好查看,可切換到標簽版本進行查看

代碼實現

在上篇中,我們實現了容器和鏡像的文件隔離,在容器內的修改不會影響到宿主機內

docker數據卷,但我們也會有一些持久化的存儲,在容器中操作后,想要保存下來,便于后序查看或者重啟后進行加載,對應docker中的 -v 參數

這里的原理還是同上篇中的一樣,也是使用文件掛載的方式,不同于上篇的是,這個-v的掛載只卸載卷,但不刪除文件,這樣文件就保留了下來

代碼也不是太復雜,直接上代碼,相比較書中的代碼,稍微做了一些結構上的調整和優化

新增 -v 命令參數

我們在RunCommand中增加-v命令參數,和docker中的-v一樣

需要注意的是,目前暫時單數據卷掛載,還不能像docker一樣提供多個-v,但影響不大

var RunCommand = cli.Command{Name:  "run",Usage: `Create a container with namespace and cgroups limit mydocker run -ti [command]`,Flags: []cli.Flag{......// 添加-v標簽cli.StringFlag{Name:  "v",Usage: "volume",},},/*這里是run命令執行的真正函數1.判斷參數是否包含command2.獲取用戶指定的command3.調用Run function 去準備啟動容器*/Action: func(context *cli.Context) error {......volume := context.String("v")run.Run(tty, cmdArray, resConfig, volume)return nil},
}

dockerfile copy。上面加參數傳入了Run函數中,在Run函數中,我們將其繼續傳遞到進程啟動的初始化進程和退出時的清理函數中

func Run(tty bool, cmdArray []string, config *subsystem.ResourceConfig, volume string) {......mntUrl := pwd + "/mnt/"rootUrl := pwd + "/"// 傳入初始化進程中parent, writePipe := container.NewParentProcess(tty, rootUrl, mntUrl, volume)if err := parent.Start(); err != nil {log.Error(err)// 如果fork進程出現異常,但有相關的文件已經進行了掛載,需要進行清理,避免后面運行報錯時,需要手工清理deleteWorkSpace(rootUrl, mntUrl, volume)return}......log.Infof("parent process run")_ = parent.Wait()// 傳入退出時的清理函數中deleteWorkSpace(rootUrl, mntUrl, volume)os.Exit(-1)
}

創建容器文件系統

在進程初始化函數中,會創建容器文件系統,和上篇文件中一樣,我們只是在newWorkSpace函數中新增一個函數,來掛載持久化數據卷即可

回顧下,這個核心函數是功能大致如下:

1.創建只讀層
2.創建容器讀寫層
3.創建掛載點并將只讀層和讀寫層掛載到掛載點上

下面我們要增加的是:

自己動手寫docker?4.在容器內創建對應的數據卷,并將其掛載到掛載點上

我們這步新增的需要在第三步后面,因為需要掛載點已經準備就緒

func newWorkSpace(rootUrl, mntUrl, volume string) error {if err := createReadOnlyLayer(rootUrl); err != nil {return err}if err := createWriteLayer(rootUrl); err != nil {return err}if err := createMountPoint(rootUrl, mntUrl); err != nil {return err}// 在容器內創建對應的數據卷,并將其掛載到掛載點上if err := mountExtractVolume(mntUrl, volume); err != nil {return err}return nil
}

掛載數據卷的具體處理如下:

1.如果參數有效才進行掛載操作:空直接返回;參數錯誤則報錯
2.如果宿主機中的文件路徑不存在,需要進行創建(書中是使用mkdir,這樣如果多級目錄時,上級目錄沒有時會報錯,這里mkdirall遞歸創建)
3.在容器讀寫層創建對應到容器內的文件
4.將宿主機文件進行掛載

具體實現如下:


func mountVolume(mntUrl string, volumeUrls []string) error {// 如果宿主機文件目錄不存在則創建parentUrl := volumeUrls[0]exist, err := pathExist(parentUrl)if err != nil && !os.IsNotExist(err) {return err}if !exist {// 使用mkdir all 遞歸創建文件夾if err := os.MkdirAll(parentUrl, 0777); err != nil {return fmt.Errorf("mkdir parent dir err: %v", err)}}// 在容器文件系統內創建掛載點containerUrl := mntUrl + volumeUrls[1]if err := os.Mkdir(containerUrl, 0777); err != nil {return fmt.Errorf("mkdir container volume err: %v", err)}// 把宿主機文件目錄掛載到容器掛載點dirs := "dirs=" + parentUrlcmd := exec.Command("mount", "-t", "aufs", "-o", dirs, "none", containerUrl)cmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {return fmt.Errorf("mount volume err: %v", err)}return nil
}func pathExist(path string) (bool, error) {_, err := os.Stat(path)if err == nil {return true, err}return false, err
}

容器退出時的清理

dockerfile volume,在上篇中清理動作是直接卸載了掛載點,并刪除了讀寫層

我們這次的數據卷是需要持久化保存的,只需要進行將掛載點卸載即可

具體實現如下:

func deleteWorkSpace(rootUrl, mntUrl, volume string) {// 這里在刪除掛載點之前,把數據卷卸載即可// 后面的刪除掛載點和刪除讀寫層后,不會影響宿主機的文件unmountVolume(mntUrl, volume)deleteMountPoint(mntUrl)deleteWriteLayer(rootUrl)
}

unmountVolume 具體實現如下:

func unmountVolume(mntUrl string, volume string) {if volume == "" {return}volumeUrls := strings.Split(volume, ":")if len(volumeUrls) != 2 || volumeUrls[0] == "" || volumeUrls[1] == "" {return}// 卸載容器內的 volume 掛載點的文件系統containerUrl := mntUrl + volumeUrls[1]cmd := exec.Command("umount", containerUrl)cmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Errorf("ummount volume failed: %v", err)}
}

運行測試

編譯一波代碼,在容器內創建一個文件

?  dockerDemo git:(main) ? go build mydocker/main.go
?  dockerDemo git:(main) ? ./main run -ti -v /root/volumn/test:/test sh
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"all command is : sh","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"parent process run","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"init come on","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"current location: /home/lw/code/go/dockerDemo/mnt","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"find path: /bin/sh","time":"2022-03-20T10:15:04+08:00"}
/ # ls
bin   dev   etc   home  main  proc  root  sys   test  tmp   usr   var
/ # touch /test/test.txt
/ # ls /test/
test.txt

docker 開發,我們新開一個sh看看宿主機的情況,可以看到文件在宿主機中也有

?  ~ ls /root/volumn/test
test.txt

然后我們退出容器,可以看到當前運行目錄下的文件已經被清理掉了

/ # exit
?  dockerDemo git:(main) ? ll
總用量 4.6M
drwxr-xr-x 12 root root 4.0K 3月  17 06:17 busybox
drwxrwxr-x  2 lw   lw   4.0K 3月  18 20:45 docs
drwxrwxr-x  3 lw   lw   4.0K 3月   7 04:55 example
-rw-rw-r--  1 lw   lw    382 3月  12 10:18 go.mod
-rw-rw-r--  1 lw   lw   2.0K 3月  12 10:18 go.sum
-rw-rw-r--  1 lw   lw    12K 3月  12 10:18 LICENSE
-rwxr-xr-x  1 root root 4.6M 3月  20 10:15 main
drwxrwxr-x  6 lw   lw   4.0K 3月  12 10:20 mydocker
-rw-rw-r--  1 lw   lw    473 3月  12 10:18 README.md

我們再次去查看宿主機的文件,發現依舊存在,目的達成

?  ~ ls /root/volumn/test
test.txt

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/135520.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息