本文为从零开始写 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. SVC服务的发布

    目录 服务的发布 发布的方式 1.NodePort 方法一:创建的时候直接指定类型 方法二:在线修改(将其他类型改为NodePort) 2.LoadBalance 3.Ingress(推荐/重点) 服 ...

  2. Power BI 13 DAY

    分组依据 在PQ下对维度进行汇总,并对其他字段进行不同的计算方式,就需要使用分组依据 将下表中已"店名"为维度对重复店名作排重处理,对"单号"字段进行计数计算, ...

  3. JOISC 2018 记录

    Day1 T1 Construction of Highway 每一次操作形如查询一条到根的链上的逆序对数,然后将这条链的权值全部修改成同一个权值. 发现这个操作类似于 LCT 的 Access 操作 ...

  4. ES6学习 第七章 函数的扩展

    前言 本章介绍函数的扩展.有些不常用的知识了解即可. 本章原文链接:函数的扩展. 函数参数的默认值 ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面. 当函数形参没有被赋值时,才会将默认值 ...

  5. NC51111 Atlantis

    题目链接 题目 题目描述 There are several ancient Greek texts that contain descriptions of the fabled island At ...

  6. 服务端高性能网络IO编程模型简析

    服务端高性能网络IO编程模型简析 一.客户端与服务器端 多数网络应用可以分为客户端(client)和服务器端(server)模型,然后中间通过各种定义的协议来进行两端的通信. 比如常用的 Nginx ...

  7. virtualapp 应用启动源码分析

    应用启动源码分析 在HomeActvity中的OnCreate方法会调用initLaunchpad private void initLaunchpad() { mLauncherView.setHa ...

  8. Google Chrome 开启多下载下载,提高文件下载速度

    在地址栏输入: chrome://flags/#enable-parallel-downloading Parallel downloading改为Enabled后重启浏览器即可打开多线程下载 (多线 ...

  9. 解析Spring中的循环依赖问题:再探三级缓存(AOP)

    前言 在之前的内容中,我们简要探讨了循环依赖,并指出仅通过引入二级缓存即可解决此问题.然而,你可能会好奇为何在Spring框架中还需要引入三级缓存singletonFactories.在前述总结中,我 ...

  10. Taurus.MVC WebMVC 入门开发教程4:数据列表绑定List<Model>

    前言: 在本篇 Taurus.MVC WebMVC 入门开发教程的第四篇文章中, 我们将学习如何实现数据列表的绑定,通过使用 List<Model> 来展示多个数据项. 我们将继续使用 T ...