首页 > 其他分享 >从零开始写 Docker(七)---实现 mydocker commit 打包容器成镜像

从零开始写 Docker(七)---实现 mydocker commit 打包容器成镜像

时间:2024-03-19 10:15:58浏览次数:18  
标签:容器 tar --- mydocker commit Docker root

mydocker-commit.png

本文为从零开始写 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. 实现

具体流程

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

mydocker-commit-process.png

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

标签:容器,tar,---,mydocker,commit,Docker,root
From: https://www.cnblogs.com/KubeExplorer/p/18082109

相关文章

  • ElasticSearch - 基础概念和映射
    前言写这篇东西,是因为官方文档看着太痛苦,于是乎想用大白话来聊聊ElasticSearc(下面都简称ES)。所以下文对于ES一些概念的表述可能会与官方有出入,所以需要准确的表述和详细定义的,请跳转官方文档。我也尽量贴上官方的链接。前置知识:因为下文会使用mysql的一些概念来描述,所以......
  • 十大排序算法--Java实现
    冒泡排序(BubbleSort)基本思想:冒泡排序是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。算法步骤:从第一个元素开始,比较相邻的元素。......
  • GPT实战系列-LangChain的Prompt提示模版构建
    GPT实战系列-LangChain的Prompt提示模版构建LangChainGPT实战系列-LangChain如何构建基通义千问的多工具链GPT实战系列-构建多参数的自定义LangChain工具GPT实战系列-通过Basetool构建自定义LangChain工具方法GPT实战系列-一种构建LangChain自定义Tool工具的简单方法G......
  • 303. 区域和检索 - 数组不可变
    目录题目前缀和题目给定一个整数数组nums,处理以下类型的多个查询:计算索引left和right(包含left和right)之间的nums元素的和,其中left<=right实现NumArray类:NumArray(int[]nums)使用数组nums初始化对象intsumRange(inti,intj)返回数组nums中索引......
  • 【2024-03-18】稳也是势
    20:00我依然故我,我在每个清晨新生。                                                 ——阿多尼斯今天公司高层会议期间,领导再一次强调了公司的战略方向,以及战术上的......
  • 界面组件DevExpress WinForms v23.2 - 数据可视化功能升级
    DevExpressWinForms拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任!DevExpressWinForms控件日前正式发布了v23.2,此版......
  • 在16G的GPU上微调Mixtral-8x7B
    Mixtral-8x7B是最好的开源llm之一。但是消费级硬件上对其进行微调也是非常具有挑战性的。因为模型需要96.8GB内存。而微调则需要更多的内存来存储状态和训练数据。比如说80gbRAM的H100GPU是不够的。这时我们就想到了QLoRA,它将模型大小除以4,同时通过仅调整LoRA适配器进行微调来......
  • K8s(Kubernetes)-Tips
    K8S中文官网https://www.kubernetes.org.cn/Kubernetes集群组件结构一个kubernetes集群主要是由控制节点(master)、**工作节点(node)**构成,每个节点上都会安装不同的组件。master:集群的控制平面,负责集群的决策(管理)ApiServer:资源操作的唯一入口,接收用户输......
  • Django-域名管理项目
    1,相关版本vscoe:1.82.12,cmd命令行创建并激活虚拟机环境创建虚拟环境python.exe-mvenvdjango-env激活并进入虚拟环境django-env/Script/active3,vscode终端创建并激活虚拟环境执行激活命令active报错时的处理办法https://blog.csdn.net/weixin_44548098/ar......
  • L2-024 部落
    注意merge的时候如果p1和p2相等及时返回否则死循环了,代码有问题而不是算法超时。#define_CRT_SECURE_NO_WARNINGS#include<bits/stdc++.h>usingnamespacestd;#definelllonglongintparent[10010],deep[10010];intgetf(intx){ inty=x; while(parent[y]!=-......