「容器管理系统」开发篇: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 的前置使用条件
相关推荐
跟着珅聪学java22 分钟前
spring boot +Elment UI 上传文件教程
java·spring boot·后端·ui·elementui·vue
海鸥8124 分钟前
podman和与docker的比较 及podman使用
docker·容器·podman
徐小黑ACG1 小时前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
战族狼魂4 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
杉之5 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch6 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
bobz9657 小时前
k8s 怎么提供虚拟机更好
后端
bobz9657 小时前
nova compute 如何创建 ovs 端口
后端
用键盘当武器的秋刀鱼7 小时前
springBoot统一响应类型3.5.1版本
java·spring boot·后端
zyk_5207 小时前
Docker desktop如何汉化
运维·docker·容器