Docker-Java限制cpu和内存及浅析源码解决docker磁盘挂载失效问题

之前工作流的运行都是用的docker-java提供的api拉起的docker容器直接跑服务,但是最新线上的新业务资源消耗较大,单个容器如果不加控制,CPU和内存都会拉满,导致服务器莫名宕机事故的发生,所以Docker限制cpu使用率和内存限制就得安排上

HostConfig构建

自定义HostConfig,设置cpu和内存限制,pipeline配置了就按照配置来,如果没有就走默认配置

public void setUp() {
    this.dockerHostConfig = new HostConfig();
    Double memoryValue = this.pipeline.getMemory() != null
                            ? this.pipeline.getMemory() * 1024 * 1024 * 1024
                            : this.config.getDefaultMemoryLimitInGb() * 1024 * 1024 * 1024;
    this.dockerHostConfig.withMemory(memoryValue.longValue());

    double cpu = StringUtils.isNotBlank(this.pipeline.getCpu())
                ? Double.parseDouble(this.pipeline.getCpu())
                : this.config.getDefaultCpuCoreLimit();
    // 单个 CPU 为 1024,两个为 2048,以此类推
    this.dockerHostConfig.withCpuShares((int)(cpu * 1024));
}

CreateContainerCmd 构建

public String startContainer(String image,
                                String name,
                                List portBinds,
                                List volumeBinds,
                                List extraHosts,
                                List envs,
                                List entrypoints,
                                HostConfig hostConfig,
                                String... cmds) {
    List volumes = new ArrayList<>();
    List volumesBinds = new ArrayList<>();

    ......

    ......

    ......

    CreateContainerCmd cmd = this.client.createContainerCmd(image)
                                            .withName(name)
                                            .withVolumes(volumes)
                                            .withBinds(volumesBinds);

    if (portBinds != null && portBinds.size() > 0) {
        cmd = cmd.withPortBindings(portBindings);
    }

    if (cmds != null && cmds.length > 0) {
        cmd = cmd.withCmd(cmds);
    }

    if (extraHosts != null && extraHosts.size() > 0) {
        cmd.withExtraHosts(extraHosts);
    }

    if (envs != null) {
        cmd.withEnv(envs);
    }

    if (entrypoints != null) {
        cmd.withEntrypoint(entrypoints);
    }

    // 这一句是重点
    cmd.withHostConfig(hostConfig);

    CreateContainerResponse container = cmd.exec();
    this.client.startContainerCmd(container.getId()).exec();
    return container.getId();
}

docker inspect containerId

执行 docker inspect a436678ccb0c 结果如下

"HostConfig": {
    "Binds": [],
    "ContainerIDFile": "",
    "LogConfig": {
        "Type": "json-file",
        "Config": {
            "max-file": "3",
            "max-size": "10m"
        }
    },
    "NetworkMode": "default",
    "PortBindings": null,
    "RestartPolicy": {
        "Name": "",
        "MaximumRetryCount": 0
    }
    "CpuShares": 2048,
    "Memory": 6442450944,
    "NanoCpus": 0,
    "CgroupParent": "",
    "BlkioWeight": 0,
    "BlkioWeightDevice": null
    }

CpuShares和Memory已经是我们设置的默认值,API生效,我们再来看下执行的日志

proc "pipeline_task_4b86c7830e4c4e39a77c454589c9e7e9_1" starting 2021-09-22 17:30:15 logPath:/mnt/xx/xx/logs/2021/09/22/bfbadf65-ac41-459d-a96d-3dc9a0105c25/job.log
+ java -jar /datavolume/xxx/xx.jar  --spring.profiles.active=test
STDERR: Error: Unable to access jarfile /datavolume/xxx/xx.jar
5c494aeacb87af3a46a4fedc6e695ae888d4d2b9d7e603f24ef7fe114956c782 finished!

proc "pipeline_task_4b86c7830e4c4e39a77c454589c9e7e9_1" exited with status 1
proc "&#x65B0;&#x589E;&#x8282;&#x70B9;" error
start to kill all pipeline task
pipeline exit with error

执行文件没有找到,向上看Binds为空,所以挂载丢了,可以为什么了?明明 withVolumes()withBinds() 两个方法逻辑都没有动,还是看下源码分析一下吧

看源码之前我们先了解一下docker的hostConfig,文件路径在:/var/lib/docker/containers/{container-id}/hostconfig.json
其实这个就是容器运行的宿主机配置,磁盘绑定,cpu、内存限制、DNS、网络以及种种配置都在这个文件中,docker-java中HostConfig对象其实就是这个json对应的model,我们自定义了HostConfig对象,问题应当是出在 cmd.withHostConfig(hostConfig); 这一句代码上

以前的绑定逻辑
之前没有限制,所以在实例化CreateContainerCmd时候没有定制HostConfig参数

CreateContainerCmd cmd = this.client.createContainerCmd(image)
                        .withName(name)
                        .withVolumes(volumes)
                        .withBinds(volumesBinds);

CreateContainerCmd withBinds

/**
     *
     * @deprecated see {@link #getHostConfig()}
     */
    @Deprecated
    default CreateContainerCmd withBinds(Bind... binds) {
        Objects.requireNonNull(binds, "binds was not specified");
        getHostConfig().setBinds(binds);
        return this;
    }
@JsonProperty("HostConfig")
private HostConfig hostConfig = new HostConfig();

我们再看下 CreateContainerCmdwithHostConfig() 方法,代码也是在实现类里面

@Override
public CreateContainerCmd withHostConfig(HostConfig hostConfig) {
    this.hostConfig = hostConfig;
    return this;
}

直接覆盖了对象中原来的hostConfig, 我们的withHostConfig又在最后调用的可不就把挂载丢了吗,正好CreateContainerCmd 的 withBinds 方法也被 @Deprecated 修饰了,我们就来调整一下代码

public String startContainer(String image,
                                String name,
                                List portBinds,
                                List volumeBinds,
                                List extraHosts,
                                List envs,
                                List entrypoints,
                                HostConfig hostConfig,
                                String... cmds) {
    List volumes = new ArrayList<>();
    List volumesBinds = new ArrayList<>();

    ......

    //这一行很关键
    hostConfig.withBinds(volumesBinds);

    if (portBinds != null && portBinds.size() > 0) {
        hostConfig.withPortBindings(portBindings);
    }

    if (extraHosts != null && extraHosts.size() > 0) {
        hostConfig.withExtraHosts(extraHosts.toArray(new String[extraHosts.size()]));
    }
    CreateContainerCmd cmd = this.client.createContainerCmd(image).withHostConfig(hostConfig)
        .withName(name)
        .withVolumes(volumes);

    if (cmds != null && cmds.length > 0) {
        cmd = cmd.withCmd(cmds);
    }

    if (envs != null) {
        cmd.withEnv(envs);
    }

    if (entrypoints != null) {
        cmd.withEntrypoint(entrypoints);
    }

    CreateContainerResponse container = cmd.exec();
    this.client.startContainerCmd(container.getId()).exec();
    return container.getId();
};

OK,搞定, docker stats 查看容器的cpu占用,始终不会超过200%

Original: https://www.cnblogs.com/surging-dandelion/p/15354045.html
Author: 蒲公英的狂想
Title: Docker-Java限制cpu和内存及浅析源码解决docker磁盘挂载失效问题

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/579576/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java基础—双层for循环之小星星实操

    在Java基础中,我们会学到循环结构,其中for的双层循环更为常见。比如熟知的9*9乘法表就是用 双层for循环。 双层for循环的特点:1、内层循环一遍,相当于外层循环只执行一次…

    Java 2023年6月7日
    084
  • Vim编辑器以及linux常用命令

    Shift+G tar -xvf *.tar firewall-cmd –list-ports firewall-cmd –reload systemctl…

    Java 2023年6月8日
    059
  • Java如何去除字符串中的HTML标签

    Java如何去除字符串中的HTML标签 使用爬虫爬取网站数据,有时会将HTML相关的标签也一并获取,如何将这些无关的标签去除呢,往下看: 直接写个Test类: @Test void…

    Java 2023年6月15日
    084
  • BAT 基础语法

    命令 //功能 echo //标准输出命令 在CMD窗口中 显示echo 后的内容 @ //关闭当前行的 回显 回显:源代码在 CMD 窗口中再次显示 pasue // 暂停程序 …

    Java 2023年6月8日
    087
  • MySQL基础入门(1)

    为什么学习MySQL 关系数据库管理系统(Relational Database Management System, RDBMS)是一种极为重要的工具,其应用十分广泛,从商业、科…

    Java 2023年6月5日
    066
  • rocketmq实现延迟队列精确到秒级实现方案1-代理实现

    简单的来说,就是rocketmq发送消息到broker的时候,判断是否定时消息, 如果是定时消息,将消息发送到代理服务(这个是一个独立的服务,需要自己开发,定时地把消息发送出去),…

    Java 2023年6月5日
    085
  • MongoDB 常用启动参数

    每日一句 Once you choose your way of life, be brave to stick it out and never return. 生活的道路一旦选…

    Java 2023年6月9日
    0181
  • 加工生产调度 贪心算法 c++

    某工厂收到了 n n 个产品的订单,这 n n 个产品分别在 A,B A ,B 两个车间加工,并且必须先在 A A 车间加工后才可以到 B B 车间加工。某个产品 i i 在 A,…

    Java 2023年6月5日
    065
  • Domain-Driven Design and Spring(自译:领域驱动设计和Spring)

    1. Introduction This script is supposed to give a brief overview about the fundamental bui…

    Java 2023年5月29日
    053
  • 4、多态

    多态概念(一共三点满足就行) 1、 继承 2、程序运行时将子类对象赋值给父类 3、通过父类去调用子类的方法 一、父类类型做方法的参数 <span class=”kwd”&gt…

    Java 2023年6月6日
    091
  • Kafdrop

    Kafdrop 是一个用于查看 Kafka 主题和浏览消费者组的 Web UI docker run -d –rm -p 9000:9000 \ -e KAFKA_BROKERC…

    Java 2023年6月7日
    085
  • Rocket MQ报错No route info of this topic的问题探究

    系统订单创建成功之后需要发送订单创建成功的消息,但是今天突然遇到了如下的报错 出现上面的错误,主要有三种情况 一:Topic不存在,即没有创建出来次Topic(我们项目均为手动创建…

    Java 2023年5月30日
    074
  • Maven 依赖调解源码解析(六):dependencyManagement 版本锁定

    我们在根模块 mavenDependencyDemo 中,用 dependencyManagement 的形式直接指定 X 的版本为 2.0。同时,A 依赖 C,而 C 依赖了 X…

    Java 2023年6月16日
    087
  • <1>Linux简介及基本操作

    一、什么是Unix/linux 下C 开发(uc 开发)和前边学的C 编程有什么区别 ? C 语言学的是 1 )C 的语法 2 )标准C 的库函数: printf malloc f…

    Java 2023年6月15日
    077
  • 0.前言 在学习Java高级之前的一些想说的话

    在学习Java高级之前的一些想说的话 1.将会学到什么? IO流 线程 网络编程 XML解析 设计模式 当然,真正的JAVA高级对于每个人或者每个组织的定义可能都不太一样,这里所讲…

    Java 2023年6月13日
    076
  • spring cloud 服务链路追踪 skywalking 6.1

    随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言…

    Java 2023年6月16日
    0185
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球