「容器管理系统」开发篇:8. docker 的应用

回顾

项目已开源:基于 Golang 的 容器管理系统

什么是 docker ?

Docker 是一种轻量级的虚拟化技术,同时是一个开源的应用容器运行环境搭建平台,可以让开发者以便捷方式打包应用到一个可移植的容器中,然后安装至任何运行LinuxWindows等系统的服务器上。相较于传统虚拟机,Docker容器提供轻量化的虚拟化方式、安装便捷、启停速度快。

docker 的特点

Docker容器具有以下三大特点:

  • 轻量化:一台主机上运行的多个Docker容器可以共享主机操作系统内核;启动迅速,只需占用很少的计算和内存资源。
  • 标准开放:Docker容器基于开放式标准,能够在所有主流Linux版本、Microsoft Windows以及包括VM裸机服务器在内的任何基础设施上运行。
  • 安全可靠:Docker赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker默认提供最强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台主机。

为什么用 docker ?

虚拟机(Virtual Machine,简称VM)的发展,减轻了企业对硬件资源的依赖,它将一台物理设备虚拟为多个逻辑设备,每个逻辑设备可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高设备的工作效率。然而,传统VM需要安装操作系统才能执行应用程序,占用系统资源过多。多数情况下,用户只需要运行简单的应用程序,采用VM技术操作繁琐且造成资源浪费。倘若需要迁移应用服务程序,则需迁移整个VM,因此企业迫切需要轻量级的虚拟化技术。

使用 docker 有什么好处?

容器,就是一种轻量级的虚拟化技术,目的和虚拟机一样,都是为了创造"隔离环境"。但是它不像VM采用操作系统级的资源隔离,容器采用的是进程级的系统隔离。Docker作为创建容器的主流工具,近年来迅速发展,它的优势在于:

可以让开发者将企业需要的各种应用及应用依赖文件封装在Docker镜像文件中,然后在任何物理设备(Linux设备或Window设备等)上安装运行实现虚拟化,让应用程序彻底脱离底层设备,可以在物理机之间灵活迁移部署,使运维工程师摆脱了繁琐的环境部署,极大的提高了工作效率,同时减少了部署过程中的潜在风险

golang 如何操作 docker ?

golang 可以通过 docker sdk 来操作 docker

  • 安装 docker sdk
go 复制代码
go get github.com/docker/docker

封装 docker sdk

  • 初始化 docker client
go 复制代码
import (
    "context"
    "github.com/docker/docker/client"
)

type DockerClient struct {
    Client *client.Client
    Ctx    context.Context
}
  • 获取容器列表
go 复制代码
// ContainerList 获取全部容器列表(含未运行)
func (d *DockerClient) ContainerList() (containerList []types.Container, err error) {
    containerList, err = d.Client.ContainerList(d.Ctx, types.ContainerListOptions{
       All: true,
    })
    return
}

这里面有个需要注意的地方: ContainerListOptions 中的 All 参数,如果 All 参数设置以为 true,则是获取运行中和未运行的全部容器数据,默认不设置,则是获取运行中的容器数据

  • 停止指定容器
go 复制代码
// ContainerStop 停止指定容器
func (d *DockerClient) ContainerStop(ID string) error {
    err := d.Client.ContainerStop(d.Ctx, ID, container.StopOptions{})
    return err
}
  • 获取指定容器日志
go 复制代码
// ContainerLogs 获取指定容器日志
func (d *DockerClient) ContainerLogs(ID string) (string, error) {
    options := types.ContainerLogsOptions{ShowStdout: true}
    out, err := d.Client.ContainerLogs(d.Ctx, ID, options)
    defer func(out io.ReadCloser) {
       err := out.Close()
       if err != nil {
          panic(err)
       }
    }(out)
    // 从 io.ReadCloser 解析数据
    buf := new(bytes.Buffer)
    buf.ReadFrom(out)
    return buf.String(), err
}

这里 sdk 返回是一个 io.ReadCloser 接口,如果需要得到数据,需要进行解析

  • 创建容器
go 复制代码
// ContainerCreate 创建容器
func (d *DockerClient) ContainerCreate(
    options *container.Config,
    hostOptions *container.HostConfig,
    networkingOptions *network.NetworkingConfig,
    platform *specs.Platform,
    name string,
) (cont string, err error) {
    resp, err := d.Client.ContainerCreate(d.Ctx, options, hostOptions, networkingOptions, platform, name)
    if err != nil {
       return "", err
    }

    return resp.ID, nil
}

这里主要注意的是:optionshostOptions 这两个结构体,常用的创建参数,这两个里面基本上可以满足,封装阶段咱们这里不详细讲述

  • 运行指定容器
go 复制代码
// ContainerStart 启动指定容器
func (d *DockerClient) ContainerStart(ID string) error {
    err := d.Client.ContainerStart(d.Ctx, ID, types.ContainerStartOptions{})
    if err != nil {
       return err
    }
    return nil
}

接口调用封装的 sdk

这里着重讲解创建容器,其他方法 sdk 直接的调用即可

  • 创建容器
go 复制代码
func ContainerCreate(c *gin.Context) {
    var params common.ContainerCreateRequest
    if err := c.ShouldBindJSON(&params); err != nil {
       response.Error(c, constant.ErrorParams, err, constant.ErrorMsg[constant.ErrorParams])
       return
    }
    // 配置要启动的容器
    containerOptions := &container.Config{
       Image: params.Image,
       Cmd:   params.Cmd,
    }

    // 主机配置
    hostOptions := &container.HostConfig{
       // 将容器的80端口映射到宿主机的8080端口
       PortBindings: nat.PortMap{
          nat.Port(params.LocalProt + "/tcp"): []nat.PortBinding{
             {
                HostIP:   params.HostIP,
                HostPort: params.HostPort,
             },
          },
       },
       RestartPolicy: container.RestartPolicy{
          Name: params.PolicyName,
       },
    }
    id, err := dockerClient.ContainerCreate(containerOptions, hostOptions, nil, nil, params.ContainerName)
    if err != nil {
       response.Error(c, constant.ErrorCreateContainer, err, constant.ErrorMsg[constant.ErrorCreateContainer])
       return
    }
    response.OK(c, id, constant.ErrorMsg[constant.Success])
}

container.Config 容器的配置

go 复制代码
// Config 包含有关容器的配置数据。
// 它应该只包含有关容器的可移植信息。
// 在这里,"可移植"的意思是"独立于我们运行的主机"。
// 不可移植的信息 *should* 出现在 HostConfig 中。
// 添加到此结构的所有字段都必须标记为 "omiteempty" 才能继续获取
// 来自旧的 "v1Compatibility" 配置的可预测散列。

type Config struct {
    Hostname        string              // 主机名
    Domainname      string              // 域名
    User            string              // 将在容器内运行命令的用户,还支持User:group
    AttachStdin     bool                // 附加标准输入,实现用户交互
    AttachStdout    bool                // 附加标准输出
    AttachStderr    bool                // 附加标准误差
    ExposedPorts    nat.PortSet         `json:",omitempty"` //暴露端口列表
    Tty             bool                // 将标准流附加到Tty,如果Tty未关闭,则包括stdin。
    OpenStdin       bool                // Open stdin
    StdinOnce       bool                // 如果为true,请在连接的1个客户端断开连接后关闭stdin。
    Env             []string            // 要在容器中设置的环境变量列表
    Cmd             strslice.StrSlice   // 启动容器时要运行的命令
    // Healthcheck 描述了如何检查容器是否健康
    Healthcheck     *HealthConfig       `json:",omitempty"` 
    // ArgsEscaped True,如果命令已转义(意味着将其视为命令行)(特定于Windows)。
    ArgsEscaped     bool                `json:",omitempty"` 
    Image           string              // 操作传递的镜像的名称(例如,可以是符号)
    Volumes         map[string]struct{} // 用于容器的卷(装载)列表
    WorkingDir      string              // 将启动命令中的当前目录(PWD)
    Entrypoint      strslice.StrSlice   // 启动容器时要运行的入口点
    NetworkDisabled bool                `json:",omitempty"` // 已禁用网络
    MacAddress      string              `json:",omitempty"` // 容器的Mac地址
    OnBuild         []string            // image Dockerfile上定义的OnBuild元数据
    Labels          map[string]string   // 设置到此容器的标签列表
    StopSignal      string              `json:",omitempty"` // 停止容器的信号
    StopTimeout     *int                `json:",omitempty"` // 停止容器的超时(秒)
    // Shell 表示RUN的Shell形式,CMD,ENTRYPOINT
    Shell           strslice.StrSlice   `json:",omitempty"` 
}

container.HostConfig 主机配置

go 复制代码
// HostConfig 容器的不可移植的 Config 结构。
// 在这里,"不可移植"的意思是"依赖于我们运行的主机"。
// 可移植信息 *should* 出现在配置中。
type HostConfig struct {
    // 适用于所有平台
    Binds           []string      // 此容器的卷绑定列表
    ContainerIDFile string        // 写入containerId的文件(路径)
    LogConfig       LogConfig     // 此容器的日志配置
    NetworkMode     NetworkMode   // 用于容器的网络模式
    PortBindings    nat.PortMap   // 暴露端口(容器)和主机之间的端口映射
    RestartPolicy   RestartPolicy // 用于容器的重新启动策略
    AutoRemove      bool          // 退出时自动删除容器
    VolumeDriver    string        // 用于装载卷的卷驱动程序的名称
    VolumesFrom     []string      // 从其他容器获取的卷列表
    ConsoleSize     [2]uint       // 初始控制台尺寸(高度、宽度)

    // 适用于 UNIX 平台
    CapAdd          strslice.StrSlice // 要添加到容器的内核功能列表
    CapDrop         strslice.StrSlice // 要从容器中删除的内核功能列表
    CgroupnsMode    CgroupnsMode      // 用于容器的Cgroup命名空间模式
    DNS             []string          `json:"Dns"`        // 要查找的DNS服务器列表
    DNSOptions      []string          `json:"DnsOptions"` // 要查找的DNS选项列表
    DNSSearch       []string          `json:"DnsSearch"`  // 要查找的DNS搜索列表
    ExtraHosts      []string          // 额外主机列表
    GroupAdd        []string          // 容器进程将作为其运行的其他组的列表
    IpcMode         IpcMode           // 用于容器的IPC命名空间
    Cgroup          CgroupSpec        // 用于容器的Cgroup
    Links           []string          // 链接列表(名称:alias form)
    OomScoreAdj     int               // OOM-killing的容器偏好
    PidMode         PidMode           // 用于容器的PID命名空间
    Privileged      bool              // 容器是否处于特权模式
    PublishAllPorts bool              // docker是否应该发布容器的所有暴露端口
    ReadonlyRootfs  bool              // 容器根文件系统是只读的吗
    SecurityOpt     []string          // 用于自定义MLS系统(如SELinux)标签的字符串值列表。
    StorageOpt      map[string]string `json:",omitempty"` // 每个容器的存储驱动程序选项。
    Tmpfs           map[string]string `json:",omitempty"` // 用于集装箱的tmpfs(支架)列表
    UTSMode         UTSMode           // 用于容器的UTS命名空间
    UsernsMode      UsernsMode        // 用于容器的用户命名空间
    ShmSize         int64             // shm内存使用总量
    Sysctls         map[string]string `json:",omitempty"` // 用于容器的命名空间sysctl列表
    Runtime         string            `json:",omitempty"` // 与此容器一起使用的运行时

    // 适用于 Windows 平台
    Isolation   Isolation // 容器的隔离技术(例如默认、hyperv)

    // 包含容器的资源(cgroup、ulimit)
    Resources

    // 安装容器使用的规格
    Mounts []mount.Mount `json:",omitempty"`

    // MaskedPaths是容器内要屏蔽的路径列表(这将覆盖默认路径集)
    MaskedPaths []string

    // ReadonlyPaths是要在容器内设置为只读的路径列表(这将覆盖默认路径集)
    ReadonlyPaths []string

    // 在容器内运行自定义init,如果为null,则使用守护进程的配置设置
    Init *bool `json:",omitempty"`
}

network.NetworkingConfig 网络配置

go 复制代码
// NetworkingConfig 
// 表示容器每个接口的网络配置携带"docker run"和"docker network connect"命令中指定的网络配置
type NetworkingConfig struct {
    EndpointsConfig map[string]*EndpointSettings // 每个连接网络的端点配置
}
go 复制代码
// EndpointSettings 存储网络端点详细信息
type EndpointSettings struct {
    // Configurations
    IPAMConfig *EndpointIPAMConfig
    Links      []string
    Aliases    []string
    // 操作数据
    NetworkID           string
    EndpointID          string
    Gateway             string
    IPAddress           string
    IPPrefixLen         int
    IPv6Gateway         string
    GlobalIPv6Address   string
    GlobalIPv6PrefixLen int
    MacAddress          string
    DriverOpts          map[string]string
}
go 复制代码
// EndpointIPAMConfig 表示终结点的IPAM配置
type EndpointIPAMConfig struct {
    IPv4Address  string   `json:",omitempty"`
    IPv6Address  string   `json:",omitempty"`
    LinkLocalIPs []string `json:",omitempty"`
}

specs.Platform 镜像配置

go 复制代码
// Platform 配置镜像运行参数
type Platform struct {
    // Architecture 指定CPU体系结构
    // `amd64` or `ppc64`.
    Architecture string `json:"architecture"`

    // OS 指定操作系统,例如"linux"或"windows"。
    OS string `json:"os"`

    // OSVersion 指定操作系统版本的可选字段,例如在Windows"10.0.14393.1066"上。
    OSVersion string `json:"os.version,omitempty"`

    // OSFeatures 是一个可选字段,用于指定字符串数组,每个字符串都列出所需的操作系统功能
    // (例如,在Windows"win32k"上)。
    OSFeatures []string `json:"os.features,omitempty"`

    // Variant 是指定CPU变体的可选字段,例如"v7",用于在体系结构为"arm"时指定 ARMv7。
    Variant string `json:"variant,omitempty"`
}

golang 操作 docker 的前置条件

注意: docker sdkgolang 是有版本要求的

就项目而言:

  • docker sdk 使用的 tag 版本为:v23.0.3+incompatible
  • docker engine: 24.0.6
  • docker client: 4.24.0 (122432)
  • golang 版本: 1.21.3

golang 1.18 以下使用遇到问题的请看:「容器管理系统」 2. 版本问题记录

结束语

  • 简述了 docker 的发展史
  • docker 的特点
  • docker 的好处
  • golang 如何操作 docker
  • docker sdk 的使用
  • docker 的前置使用条件
相关推荐
凡人的AI工具箱10 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
java亮小白199718 分钟前
Spring循环依赖如何解决的?
java·后端·spring
2301_8112743135 分钟前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
草莓base1 小时前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Ljw...1 小时前
表的增删改查(MySQL)
数据库·后端·mysql·表的增删查改
编程重生之路2 小时前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端
薯条不要番茄酱2 小时前
数据结构-8.Java. 七大排序算法(中篇)
java·开发语言·数据结构·后端·算法·排序算法·intellij-idea
qq_174482857510 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
锅包肉的九珍10 小时前
Scala的Array数组
开发语言·后端·scala
心仪悦悦10 小时前
Scala的Array(2)
开发语言·后端·scala