从零自制docker-8-【构建实现run命令的容器】

这篇具有很好参考价值的文章主要介绍了从零自制docker-8-【构建实现run命令的容器】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

log “github.com/sirupsen/logrus”

log “github.com/sirupsen/logrus”: 这是引入了 github.com/sirupsen/logrus 包,并将其重命名为 log

args...

假设我们有一个切片 myArgs := []string{"arg1", "arg2", "arg3"},我们想将这个切片作为参数传递给 exec.Command() 函数。

如果直接传递 myArgs 作为参数,那么 exec.Command() 函数会将整个切片作为一个单独的参数:

cmd := exec.Command("/path/to/executable", myArgs)

这相当于执行命令 /path/to/executable ["arg1" "arg2" "arg3"]

但是如果我们使用 args... 语法,就可以将切片中的每个元素都作为独立的参数传递给 exec.Command() 函数:

cmd := exec.Command("/path/to/executable", myArgs...)

这相当于执行命令 /path/to/executable "arg1" "arg2" "arg3"

args... 语法会将切片 myArgs 中的每个元素都作为独立的参数传递给 exec.Command() 函数。这种方式更加灵活,可以方便地将任意长度的参数列表传递给外部命令。

另一个例子是:

args := []string{"-flag1", "value1", "-flag2", "value2"}
cmd := exec.Command("/path/to/executable", args...)

这会将 args 切片中的 6 个元素全部作为独立的参数传递给 /path/to/executable 命令。

总之,args... 语法可以将一个切片展开为多个独立的参数,传递给需要接受多个参数的函数,在处理命令行参数时非常有用。

go module

三种包管理方式参考连接

go get下载的第三方包的位置

在GOMODULE模式下,go get命令会将依赖包下载到$GOPATH/pkg/mod目录下

go module包管理方式(推荐使用)

GO111MODULE 是一个用来控制 Go Modules 行为的环境变量。在 Go 1.11 版本之后,Go 引入了 Go Modules 来管理依赖关系和版本控制。GO111MODULE 环境变量可以设置为以下几个值:

  1. GO111MODULE=on:这是启用 Go Modules 的模式。当设置为 on 时,Go 将会使用 Go Modules 来管理依赖关系,无需再依赖 GOPATH。

  2. GO111MODULE=off:这是禁用 Go Modules 的模式。当设置为 off 时,Go 将使用传统的 GOPATH 模式来管理依赖关系。

  3. GO111MODULE=auto:这是自动模式。在自动模式下,Go 将根据当前目录是否包含 go.mod 文件来决定是否启用 Go Modules。如果在项目根目录下存在 go.mod 文件,则会启用 Go Modules,否则使用 GOPATH 模式。

因此,当设置 GO111MODULE=''(即空字符串)时,表示未显式设置 GO111MODULE 环境变量,Go 将根据自动模式来决定是否启用 Go Modules,具体行为取决于当前项目的目录结构和是否存在 go.mod 文件。

go mod tidy:检测该文件夹目录下所有引入的依赖,写入 go.mod 文件。删除错误或者不使用的modules,下载没download的package(下载到$GOPATH/pkg/mod)

import第三方包失败

go env -w GOPROXY=https://goproxy.cn,direct

再尝试

package和 go import的导入

go import导入包详解

从零自制docker-8-【构建实现run命令的容器】,从零自制docker,docker,容器,运维

golang使用同目录下的文件,golang中的package使用简介

在golang 里面一个目录为一个package, 一个package级别的func, type, 变量, 常量, 这个package下的所有文件里的代码都可以随意访问, 也不需要首字母大写。
引用当前目录得其它子目录中的文件可以import这个文件所在的文件夹相对位置,也可以import 当前目录/子目录中的package

Go语言中的包Package详解

包分为两种,一种是main函数(可执行文件)一种是库函数的包
可执行程序的包,编译完成后会生成一个可执行文件、静态库的包编译之后会生成一个.a为后缀的文件(在$GOPATH/pkg/里),自己不能执行只能被可执行包引用。

可执行程序的包必须以main作为包名,静态库的包名没有

main包和其他类库通过静态链接,最终形成的可执行文件是没有任何外部依赖的。

go build . 和go run

go build .将当前目录所有的文件都会编译并生成一个可执行程序
go run 编译单个程序并运行,此时如果多个package为main的程序有相互依赖,那么go run 其中一个会报错,因为用了其他package为main的程序

cli库

Go 每日一库之 cli

log.SetFormatter(&log.JSONFormatter{})

假设我们有一个简单的 Go 程序,用于记录一些基本的日志信息:

package main

import (
    "log"
)

func main() {
    // 设置日志输出格式为 JSON 格式
    log.SetFormatter(&log.JSONFormatter{})

    // 记录一些日志信息
    log.Println("This is an informational message")
    log.Printf("User %s logged in", "John Doe")
    log.Fatalf("Failed to connect to database: %s", "connection refused")
}

在这个例子中,我们首先使用 log.SetFormatter(&log.JSONFormatter{}) 将日志输出格式设置为 JSON 格式。

然后,我们分别使用 log.Println()log.Printf()log.Fatalf() 记录了三条不同级别的日志信息。

当我们运行这个程序时,日志输出将会是 JSON 格式,类似于以下内容:

{"level":"info","msg":"This is an informational message","time":"2024-04-07T12:34:56Z"}
{"level":"info","msg":"User John Doe logged in","time":"2024-04-07T12:34:56Z"}
{"level":"error","msg":"Failed to connect to database: connection refused","time":"2024-04-07T12:34:56Z"}

与默认的文本格式相比,JSON 格式的日志输出包含了更多的元信息,如日志级别(level)、消息内容(msg)和时间戳(time)。这种结构化的日志格式更加方便后续的处理和分析,特别是在需要机器解析日志的场景下。

总之,log.SetFormatter(&log.JSONFormatter{}) 是一个非常实用的方法,可以帮助我们更好地管理和处理 Go 程序的日志信息。

error和nil的关系

error类型和nil的关系是这样的:

  • error类型是Go语言中一个接口,用来表示一个错误情况。一个实现了Error()方法的值,即使没有实现任何具体的行为,也被称为一个error值。
  • nil是Go语言中的一个特殊值,它表示的是空值或不存在的值,对于error类型的值来说,nil表示没有错误发生。

在函数返回值中,当函数执行正确,没有遇到错误时,我们通常会用nil来表示。例如:

func someFunction() error {
    // 函数执行成功,没有错误
    return nil
}

当函数执行遇到错误时,我们会返回一个非nilerror值,例如:

func someFunction() error {
    // 函数执行失败,返回一个错误
    return errors.New("Some error occurred.")
}

所以,error类型和nil的关系是:nil表示error类型的值为非错误状态,非nilerror表示存在错误。在函数返回时,如果error值为nil,则表示函数执行成功,否则表示有错误发生。

cmd.Wait()和cmd.Start()

err变量在调用cmd.Wait()方法后得到返回值。当调用cmd.Start()时,该方法会启动命令并在后台执行。如果在启动命令时发生了错误(例如命令不存在、权限不足等),cmd.Start()会立即返回一个非nil的错误值。

而cmd.Wait()方法的作用是等待命令执行完毕。当命令执行结束后,此方法会返回。返回值有两个含义:

若命令执行成功结束,err将为nil。
若在命令执行过程中出现任何错误(比如命令被信号终止、超时退出等),err将包含相应的错误信息。
因此,在你的代码片段中,err = cmd.Wait()这一行会阻塞直到外部命令执行完毕,然后返回一个表示命令执行结果(成功与否及退出状态)的错误值。

arg……

在Go语言中,os/exec包提供了执行外部命令的功能。这里的代码片段是用于创建一个可执行命令的实例。

args := []string{"init", cmd} 是定义了一个字符串切片(slice),它存储了要执行的命令及其参数。在这个例子中:

  • "init":是一个命令参数,表示要执行的子命令为 “init”。
  • cmd:是一个变量,通常代表另一个命令参数。具体的值取决于程序上下文中的赋值情况。

然后,command := exec.Command("/proc/self/exe", args...) 这一行是用来创建一个新的 *Cmd 结构体实例,这个实例代表了一个待执行的命令。

"/proc/self/exe" 是一个特殊路径,在Linux系统中指向当前正在运行的可执行文件。也就是说,这里实际上是打算执行与当前进程相同的一个新进程,并且传入参数 args

args... 是Go语言中的变长参数语法,它会将字符串切片 args 扩展为多个单独的参数传递给 exec.Command 函数。

所以整个语句的作用是,根据给定的参数列表 args,执行与当前进程相同的新的命令行程序,并且第一个参数是 “init”,第二个参数是变量 cmd 的值。

context.Args().Get(0)

在Go语言中,这段代码片段是基于第三方命令行工具库(如urfave/clicobra)来定义一个命令行命令init的结构体。在这些库中,cli.Context对象包含了从命令行输入解析出来的所有信息。

context.Args().Get(0) 这一行代码在用户调用命令 init 并带有一个或多个额外参数时,用于获取命令行中紧随命令名 init 之后的第一个参数。

举个例子,假设用户在命令行中执行了如下命令:

./myapp init mycontainer

在上述代码段中,context.Args().Get(0) 将返回字符串 "mycontainer"。这是因为context.Args() 返回的是一个包含所有非命令名称参数的字符串切片,索引0位置的元素就是第一个参数。

然而,根据这段代码的具体上下文,看起来开发者并未真正使用到 cmd 变量(它保存了第一个参数的值),而是直接调用了 contain.init() 方法初始化容器。如果预期是利用第一个参数来定制初始化行为,那么应该会在 contain.init() 调用中使用到 cmd 变量。

syscall.Exec和os/exec.Command

在Go语言中,syscall.Execos/exec.Command 都是用来执行外部程序的,但它们在用途和机制上有显著的不同:

  1. syscall.Exec

    • syscall.Exec 是一个低级别的系统调用,直接在当前进程中替换当前程序的映像和数据,执行新的程序。
    • syscall.Exec 成功执行后,原始程序的内存空间、文件描述符等资源将被新程序接管,原始程序不会恢复执行,因为它已经被完全替换掉了。
    • 通常用在那些希望彻底转换当前进程上下文而不返回的情况。
  2. os/exec.Command

    • os/exec.Command 提供了一种高级别的、更易于使用的API来执行外部程序,并且它不会替换当前进程的内容。
    • exec.Command 创建并返回一个 *Cmd 类型的结构体,调用其 Run() 方法来执行外部程序,执行完成后,当前Go程序会继续执行后续代码。
    • 你可以通过 Output()CombinedOutput() 等方法获取子进程的输出,并且可以控制子进程的标准输入、输出和错误流。
    • 如果需要在同一进程中连续执行多个外部命令,或者需要处理子进程的输出结果,那么 os/exec 库是非常适合的。

简单来说,syscall.Exec 更像是进程自身的“变身”,而 os/exec.Command 则是在当前进程中启动一个新的子进程来进行任务,并且可以对子进程进行更多的控制和通信。

syscall.Exec

在Go语言中,当程序执行到 syscall.Exec 函数时,会发生以下情况:

  1. 当前进程替换
    syscall.Exec 函数负责执行一个新的程序,同时替换当前进程的映像和内存空间。这意味着当前执行的Go程序的所有代码和数据都会被新的程序所取代,不再有任何机会回到原来的Go程序执行流中。

  2. 资源继承
    新程序将继承当前进程的PID(进程ID)、打开的文件描述符以及其他一些进程相关的资源。这意味着新程序可以从原先的进程中接过控制权,例如继续监听同一套网络端口或读写已打开的文件。

  3. 程序执行
    syscall.Exec 函数接受一系列参数,包括要执行的程序路径、命令行参数和环境变量。新程序将以指定的方式执行,就像它是由shell或其他启动器直接启动的一样。

  4. 永不返回
    一旦 syscall.Exec 成功执行了新程序,原Go程序的代码将不再被执行,并且 syscall.Exec 函数本身也不会返回——因为它已经不再是那个进程的一部分了。如果 syscall.Exec 失败(例如由于找不到要执行的程序或权限问题),它会返回一个错误,但这种情况下的“返回”实际上已经是当前进程内的紧急错误处理逻辑了,而不是原Go程序的逻辑继续执行。

  5. 用途
    syscall.Exec 通常用于服务程序升级、执行子进程并替换自己以进入不同的执行环境等情况,或者是不需要原程序继续运行的场景。相比之下,如果你想在当前进程中启动一个子进程并与其交互或等待子进程结束后继续执行,应该使用 os/exec 包中的 CommandRun 等方法。

  • syscall.Exec(command, argv, os.Environ()) 的作用是:

    • command:这是要执行的新程序的路径或者可执行文件名。
    • argv:这是一个字符串切片,包含传递给新程序的命令行参数。在这个例子中,只有 command 一个参数,但实际上可以有多个,比如 argv := []string{command, arg1, arg2, ...}
    • os.Environ():这个函数返回当前进程的环境变量列表,格式为 key=value 的字符串切片。在 syscall.Exec 中,这个参数用于传递给新程序,使其继承当前进程的环境变量。

mountflags

  1. syscall.MS_NOEXEC
    设置此标志后,挂载的文件系统上的所有文件都无法被执行,即使是可执行文件。这是一项重要的安全措施,可以阻止恶意或误操作尝试执行存储在特定文件系统分区中的程序。例如,在只用于数据存储的分区上启用此选项可以增强安全性,确保即便有潜在恶意脚本或程序也不得运行。

  2. syscall.MS_NOSUID
    当挂载文件系统时带有此标志,系统会忽略文件的Set-User-ID (SUID) 和 Set-Group-ID (SGID) 位的效果。在Linux中,SUID和SGID权限通常允许用户在执行某个文件时暂时获取文件所有者的权限或执行该文件时的组权限。禁用SUID和SGID意味着,无论文件本身有何种特殊权限设定,进程在执行文件时不会继承文件所有者的用户ID或组ID,从而限制了可能因滥用特权而导致的安全风险。

  3. syscall.MS_NODEV
    使用此标志挂载文件系统时,系统将不允许任何设备特殊文件在该文件系统上工作。设备文件是用来访问硬件设备(如硬盘、终端、打印机等)的接口。禁止设备文件意味着无法在该挂载点创建或访问设备节点,这对于防止未经授权的设备访问以及增强隔离环境的安全性非常有用,比如在沙箱环境中或对安全性要求较高的文件系统分区。

syscall.Mount

syscall.Mount 是 Go 语言标准库 syscall 包中提供的一个函数,用于直接调用操作系统的系统调用来完成文件系统的挂载操作。在Linux环境下,这个函数对应的就是Linux内核提供的mount(2)系统调用接口。

函数签名一般如下所示(可能会根据不同Go版本有所变化):

func Mount(source string, target string, fstype string, flags int, data string) error

参数含义:

  • source:待挂载的文件系统源,它可以是设备名(如 /dev/sda1)、网络共享路径或者其他形式,对于虚拟文件系统如 proc,则是一个标识符字符串。

  • target:挂载点,即目标挂载路径,通常是一个已经存在的目录。

  • fstype:文件系统的类型名称,如 ext4ntfs 或者像上面提到的 proc 这样的特殊文件系统类型。

  • flags:挂载标志,是一个整数值,由多个标志位组合而成,例如 syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV,这些标志位用于控制挂载行为的不同属性。

  • data:可选的额外挂载选项,通常是一个字符串,格式取决于所使用的文件系统类型,对于一些文件系统,可能包含挂载选项的列表或其他特定信息,对于不需要额外数据的文件系统,可以传入空字符串 ""

函数执行的结果是成功或失败,若失败则返回一个错误对象。

在给出的例子中:

syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")

这是在挂载Linux的虚拟文件系统 /proc/proc 目录下,并且使用了 defaultMountFlags 中定义的一系列安全相关的挂载标志。

请注意,只有包中首字母大写的函数(public函数)才能被其他包调用。首字母小写的函数(private函数)只能在包内部使用

代码

https://github.com/FULLK/llkdocker/tree/main/run_docker

最终效果

从零自制docker-8-【构建实现run命令的容器】,从零自制docker,docker,容器,运维
从零自制docker-8-【构建实现run命令的容器】,从零自制docker,docker,容器,运维

问题

发现只能run一次,下次就出现从零自制docker-8-【构建实现run命令的容器】,从零自制docker,docker,容器,运维
有待解决文章来源地址https://www.toymoban.com/news/detail-845065.html

到了这里,关于从零自制docker-8-【构建实现run命令的容器】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何使用Docker构建Nacos容器并实现固定公网地址远程访问

    Nacos是阿里开放的一款中间件,也是一款服务注册中心,它主要提供三种功能:持久化节点注册,非持久化节点注册和配置管理。 本例通过结合Cpolar内网穿透实现远程访问Nacos 提供的UI (控制台)界面,帮助管理所有的服务和应用的配置 Cpolar内网穿透是一种安全的内网穿透云服务

    2024年01月17日
    浏览(52)
  • 构建高可用性的 SQL Server:Docker 容器下的主从同步实现

    摘要: 本文将介绍如何在 Docker 环境下搭建 MS SQL Server 的主从同步,帮助读者了解主从同步的原理和实现方式,进而提高数据的可靠性和稳定性。 在当今信息化的时代,数据的安全性和稳定性显得尤为重要。数据库是许多企业和组织存储和管理数据的核心,因此如何保证数据

    2024年02月10日
    浏览(70)
  • docker篇---重启策略 + run 容器常用参数

    container runctime 负责的是容器的生命周期管理 OCI :(open container initiative)开源容器协议, 包含两个规范:运行规范(runctime spec)-对容器状态、创建、删除、查看定义 镜像规范(image-spec)-如何创建一个OCI 运行时的系统包 CRI (container run interface) 容器运行接口 隔开了各个

    2024年02月12日
    浏览(63)
  • [ 云原生 | Docker ] 构建高可用性的 SQL Server:Docker 容器下的主从同步实现指南

    本系列博文还在更新中,收录在专栏:#MS-SQL Server 专栏中。 本系列文章列表如下: 【SQL Server】 Linux 运维下对 SQL Server 进行安装、升级、回滚、卸载操作 【SQL Server】数据库开发指南(一)数据库设计的核心概念和基本步骤 【SQL Server】数据库开发指南(二)MSSQL数据库开发对

    2023年04月15日
    浏览(66)
  • 从零开始自制实现C++ High-Performance WebServer 全流程记录

    这个 从零自制高性能多线程的WebServer 博客系列呢 刚开始我写之初 其实也就是想记录一下 一个linux后端开发者 以此作为 网络编程的起点 以及多线程编程的起点的博客记录而已 因为刚开始 对于很多后台开发的前辈啊 所给出的后端学习的路啊 就比如写一个WebServer 其实这个项

    2024年02月16日
    浏览(51)
  • docker run 之后,docker ps找不到容器的解决办法

    1. 问题: 今天在部署项目过程中,用docker run指令启动容器返回了容器id,但是用 docker ps 指令却不显示刚才启动的容器。 2. 问题查找: 由于 docker ps 指令只能查看正在运行的容器,-a 是显示所有容器,使用 docker ps -a 指令查看容器的状态是否是 Exited 的。如果容器状态是 Exi

    2024年02月11日
    浏览(42)
  • docker run之后 docker ps 不显示运行中的容器

    docker run 启动mysql以后 生成的对应容器直接exited(1) 问题: 今天在部署项目过程中,用docker run指令启动容器返回了容器id,但是用docker ps指令却不显示刚才启动的容器。 问题查找: 由于docker ps指令只能查看正在运行的容器,-a 是显示所有容器,使用docker ps -a指令查看容器的

    2024年01月15日
    浏览(37)
  • 【Docker】docker run 命令详解

    Docker运行命令是在Docker中启动容器的主要方式之一。它是Docker CLI(命令行界面)中的基本命令之一,用于启动容器。 在这个语法中,“OPTIONS”包含了一些参数,用于配置和控制容器的各种方面。“IMAGE”是要使用的Docker镜像。“COMMAND”是在容器内执行的命令,“ARG”是该命

    2024年02月16日
    浏览(39)
  • Docker run命令

    docker run 命令用于创建、启动和运行一个新的 Docker 容器。它的参数如下: 参数 含义 -d 以后台模式运行容器 -p 将容器的端口映射到主机的端口 –name 为容器指定一个名称,可以在后面的命令中引用该容器 –restart 指定容器的重启策略,可以是always(默认值)、on-failure、unl

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包