本文为从零开始写 Docker 系列第七篇,实现类似 docker commit 的功能,把运行状态的容器存储成镜像保存下来。


完整代码见:https://github.com/lixd/mydocker

欢迎 Star

推荐阅读以下文章对 docker 基本实现有一个大致认识:


开发环境如下:

root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal
root@mydocker:~# uname -r
5.4.0-74-generic

注意:需要使用 root 用户

1. 概述

由于之前使用 pivotRoot + overlayfs 技术 将 /root/merged 目录作为容器的 rootfs,因此容器中的所有改动都发生在该目录下。

这里我们的 mydocker commit 命令只需要把该目录保存下来即可,因此简单实现为 使用 tar 命令将/root/merged 目录打成 tar 包

2. 实现

具体流程

整个打包流程如下图所示:

commitCommand

在 main_ command.go 文件中实现 commitCommand 命令,从用户的输入获取image name。

var commitCommand = cli.Command{
Name: "commit",
Usage: "commit container to image",
Action: func(context *cli.Context) error {
if len(context.Args()) < 1 {
return fmt.Errorf("missing image name")
}
imageName := context.Args().Get(0)
commitContainer(imageName)
return nil
},
}

然后在 main 方法中添加 commit 命令:

func main() {
app := cli.NewApp()
app.Name = "mydocker"
app.Usage = usage app.Commands = []cli.Command{
initCommand,
runCommand,
commitCommand,
}
// 省略其他
}

commitContainer

添加 commit.go文件,通过 commitContainer 函数实现将容器文件系统打包成$ {imagename}.tar 文件。

func commitContainer(imageName string) {
mntPath := "/root/merged"
imageTar := "/root/" + imageName + ".tar"
fmt.Println("commitContainer imageTar:", imageTar)
if _, err := exec.Command("tar", "-czf", imageTar, "-C", mntPath, ".").CombinedOutput(); err != nil {
log.Errorf("tar folder %s error %v", mntPath, err)
}
}

如果你对云原生技术充满好奇,想要深入了解更多相关的文章和资讯,欢迎关注微信公众号。

搜索公众号【探索云原生】即可订阅


3. 测试

测试流程如下:

  • 1)启动容器
  • 2)创建新文件
  • 3)新终端中将容器打包为镜像
  • 4)解压该镜像,查看 2 中的内容是否存在

首先,启动容器

root@mydocker:~/feat-commit/mydocker# ./mydocker run -it /bin/sh
{"level":"info","msg":"resConf:\u0026{ 0 }","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/root/busybox,upperdir=/root/upper,workdir=/root/work /root/merged]","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"command all is /bin/sh","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"init come on","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"Current location is /root/merged","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"Find path /bin/sh","time":"2024-01-19T16:18:24+08:00"}

创建一个文件

/ # echo KubeExplorer > tmp/hello.txt
/ # cat /tmp/hello.txt
KubeExplorer

接着另外打开 terminal 执行 mydocker commit 命令,将当前容器提交为镜像

root@mydocker:~/feat-commit/mydocker# ./mydocker commit myimage
commitContainer imageTar: /root/myimage.tar

再次查看/root 目录的内容,多了 myimage.tar 文件,这个就是我们的镜像文件了

root@mydocker:~# ls
busybox busybox.tar merged myimage.tar upper volume work

查看 myimage.tar 中内容:

root@mydocker:~# tar -tf myimage.tar |grep hello.txt
./tmp/hello.txt

可以看到,前面在容器中创建的 hello.txt 是存在的。

4. 总结

本篇 mydocker commit 比较简单,就是使用 tar 命令将 rootfs 直接进行打包,没有太多需要注意的地方。

镜像构造部分到此就基本完成了,总结一下:

  • 1)首先使用 busybox 作为基础镜像创建了一个容器,理解了什么是 rootfs,以及如何使用 rootfs 来打造容器的基本运行环境。

  • 2)然后,使用 overlayfs 来构建了一个拥有二层模式的镜像,对于最上层可写层的修改不会影响到基础层。这里就基本解释了镜像分层存储的原理。

  • 3)之后使用 -v 参数做了一个 volume 挂载的例子,介绍了如何将容器外部的文件系统挂载到容器中,并且让它可以访问。

  • 4)最后(本文)实现了一个简单版本的容器镜像打包。

这一章主要针对镜像的存储及文件系统做了基本的原理性介绍,通过这几个例子,可以很好地理解镜像是如何构建的,后续会基于这些基础做更多的扩展。


【从零开始写 Docker 系列】持续更新中,搜索公众号【探索云原生】订阅,阅读更多文章。



完整代码见:https://github.com/lixd/mydocker

欢迎 Star

相关代码见 feat-volume 分支,测试脚本如下:

需要提前在 /root 目录准备好 busybox.tar 文件,具体见第四篇第二节。

# 克隆代码
git clone -b feat-commit https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试
./mydocker run -it /bin/ls
./mydocker commit

从零开始写 Docker(七)---实现 mydocker commit 打包容器成镜像的更多相关文章

  1. Docker创建支持ssh服务的容器和镜像

    原文链接:Docker创建支持ssh服务的容器和镜像 1. 这里使用的centos作为容器,所以首先下载centos的images # sudo docker pull centos 2. 下载后执行 ...

  2. docker容器和镜像区别

    这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docker技术还是一知半解的时候,我 ...

  3. docker入门(二)容器与镜像的理解

    10张图带你深入理解Docker容器和镜像 申明:此篇文章是转载的(原文地址http://dockone.io/article/783),今天意外发现已经有人转载了(复制了),希望大家关注原创 原本打 ...

  4. docker入门(二)容器与镜像的关系

    [编者的话]本文用图文并茂的方式介绍了容器.镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker. 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(co ...

  5. [转]图解Docker容器和镜像

    本文转自:https://www.cnblogs.com/wangqiaomei/p/5818636.html 图解Docker容器和镜像 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器 ...

  6. 10张图带你深入理解Docker容器和镜像

    http://dockone.io/article/783 [编者的话]本文用图文并茂的方式介绍了容器.镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker. Doc ...

  7. 图解Docker容器和镜像

    图解Docker容器和镜像 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docke ...

  8. docker容器和镜像

    这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docker技术还是一知半解的时候,我 ...

  9. docker中容器和镜像的区别

    自学docker过程中一直搞不明白 镜像容器的关系,网上查阅看到一篇文章觉得讲的很好,转载记录. 转自 http://blog.csdn.net/chszs/article/details/48252 ...

  10. 10 张图带你深入理解 Docker 容器和镜像

    这篇文章希望能够帮助读者深入理解 Docker 的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对 Docker 技术还是一知半解的 ...

随机推荐

  1. Linux 将命令的输出保存到文件

    当你在 Linux 终端中运行命令或脚本时,它会在终端中打印输出方便你立即查看.方法 1:使用重定向将命令输出保存到文件中你可以在 Linux 中使用重定向来达成目的.使用重定向操作符,它会将输出保存 ...

  2. NC22494 选点

    题目链接 题目 题目描述 有一棵n个节点的二叉树,1为根节点,每个节点有一个值wi.现在要选出尽量多的点. 对于任意一棵子树,都要满足: 如果选了根节点的话,在这棵子树内选的其他的点都要比根节点的值大 ...

  3. NC50940 Running Median

    题目链接 题目 题目描述 For this problem, you will write a program that reads in a sequence of 32-bit signed in ...

  4. NC16664 [NOIP2004]合唱队形

    题目链接 题目 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2-,K,他们的身高分 ...

  5. FTP命令详解(含操作实例)

    以下是微软命令行FTP客户端命令大全,如果你想使用"未加工(RAW)"FTP命令而非下面翻译过的请参考:http://www.nsftools.com/tips/RawFTP.ht ...

  6. 《深入理解JAVA虚拟机》(一) JVM 结构 + 栈帧 详解

    ​ 1.程序计数器(Program Counter Register)         线程独有,每个线程都有自己的计数器:由于CPU的任意时刻只能执行所有线程中的一条,所以需要使用程序计数器来支持J ...

  7. 【Android逆向】破解看雪9月算法破解第二题

    1. apk安装到手机,一样的界面,随便输入一样的报错 2. apk拖入到jadx重看看 public native String sha1(String str); static { System. ...

  8. 【Android逆向】frida 破解 滚动的天空

    1. apk 安装到手机中 2. 玩十次之后,会提示 充值 3. adb shell dumpsys window | grep mCurrentFocus 查看一些当前activity是哪一个 是 ...

  9. django中信号

    # 信号的理解 在某个行为进行的某个阶段给这个行为添加一个附带的行为 # 相关api ## 数据表 pre_init # django的model执行其构造方法前,自动触发 post_init # d ...

  10. 【Azure 应用服务】Azure App Service多实例中,出现某一个实例CPU居高不下的情况,如何重启单个实例呢?

    问题描述 在使用App Service服务中,当多实例中,其中一个实例出现高CPU,高Memory的情况,为了尽可能少的影响正在运行的应用,需要单独重启某一个实例的情况下,如何手动操作呢? 问题解答 ...