美文网首页
通过easyctl为你的程序添加配额限制

通过easyctl为你的程序添加配额限制

作者: 微凉哇 | 来源:发表于2021-10-26 17:40 被阅读0次

以下内容适用于CenOS7平台

缘起

试想下,你是否遇到以下场景:

  1. 同一主机运行多个程序,由于某个程序异常(循环引用等)导致系统CPU占用300%+(可能更多),从而影响其他服务正常运行(具体体现为:调用超时或
  2. 同一主机运行多个程序,由于某个程序异常(未分页查询大表)导致操作系统OOM,操作系统无法正常运行,从而影响其他服务正常运行。此时只能通过重启宿主机的方式修复。
  3. 同一主机运行多个程序,由于某个程序异常(批量下载/上传文件)导致系统带宽耗尽,从而影响其他服务正常运行(具体体现为:网络传输慢)
  4. ...

以上几个场景,有个共同点:未对程序的运行时资源进行配额限制。

那么如何解决呢?

针对java程序,可以配置jvm参数,指定内存大小,从而达到限额的作用。

那怎么限制程序可使用的CPUIO资源呢?

docker 或许是个不错的选择

docker方式的弊

由于docker本身存在一定的使用成本,比如你不得不做以下事情:

  1. 安装docker运行时
  2. 把你的程序做成镜像

通过docker实现程序配额限制的话,事情变得复杂起来了。

那么还有没有其他的方式呢?

有,我们可以利用容器的的核心技术--cgroups(控制组),对资源进行限制。

cgroup 使用起来又很麻烦。

基于上述痛点,easyctl 引入了boot app-with-cgroups指令来处理程序配额问题。

easyctl 对程序计算资源限额的利:

  1. easyctl 精简易用,4M左右的二进制安装包,对环境零依赖。
  2. 足够简单地进行配额限制
  3. 对原有程序无侵入

接下来我们聊聊具体实现。

easyctl配额限制实现原理

核心原理基于linux cgroupscontainerd cgroups 封装实现),流程如下:

  1. 通过boot-app.app-name(服务名称)字段创建控制组
  2. 通过boot-app.resources.limits(资源限制)字段创建控制组子系统(内存、cpu、io等)
  3. 通过boot-app.boot-cmd(启动命令)字段执行启动程序指令,并获取进程id
  4. 将进程id添加至控制组内

使用说明

  • 前置条件: 安装easyctl

  • 版本支持:v0.7.14-alpha以上

  • 验证性内容参考-测试说明部分

  • 适用平台:

    • CentOS7
    • CentOS6 由于内核版本原因,不建议使用
  • 可限制配额资源:

    • 内存
    • cpu
    • 网络
    • 磁盘

参数说明

  • boot-app.app-name: 服务名称,用于关联控制组名称。同一主机上该字段不可重复。
  • boot-app.boot-cmd: 程序启动命令
  • boot-app.resources.limits.cpu: CPU限额,可申请vCore数量,正整数字段。(设置为0或不设置,表示不限制)
  • boot-app.resources.limits.memory: CPU限额,可申请vCore数量,可选单位:GBMB。(设置为0或不设置,表示不限制)

开始使用

1.生成配置文件

$ easyctl boot app-with-cgroups
INFO[0000] 生成配置文件样例, 请携带 -c 参数重新执行 -> config.yaml

2.调整配置

vi config.yaml,调整以下参数

  • boot-app.app-name: 服务名称,用于关联控制组名称。同一主机上该字段不可重复。
  • boot-app.boot-cmd: 程序启动命令
  • boot-app.resources.limits.cpu: CPU限额,可申请vCore数量,正整数字段。(设置为0或不设置,表示不限制)
  • boot-app.resources.limits.memory: CPU限额,可申请vCore数量,可选单位:GBMB。(设置为0或不设置,表示不限制)
boot-app:
  - app-name: eureka-app
    boot-cmd: nohup /usr/bin/java -jar eureka.jar &> /dev/null &
    resources:
      limits:
        cpu: 2
        memory: 3GB

3.执行启动

$ easyctl boot app-with-cgroups -c config.yaml --debug

4.确认配额是否合法(OOM等会有Kill信息)

$ sudo journalctl -xef

测试说明

针对配额内容进行验证性测试

测试CPU限额

实现原理:基于控制组cpu.cfs_period_uscpu.cfs_quota_us实现对CPU的强限制

注: 高级/自定义设置建议使用原生cgroups

A.测试用例1: 配额1vCore申请1vCore

boot-app:
  - app-name: app1
    boot-cmd: stress --cpu 1 --vm 1 --vm-bytes 2G --vm-hang 120 --timeout 120s
    resources:
      limits:
        cpu: 1
        memory: 3GB

执行

easyctl boot app-with-cgroups -c config.yaml

top观测

PID    USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
221208 root      20   0    7312    100      0 R  99.7  0.0   0:29.00 stress
   290 root      20   0       0      0      0 S   0.3  0.0   0:00.01 ksoftirqd/56
...

证明限制生效

B.测试用例2: 配额1vCore申请2vCore

boot-app:
  - app-name: app1
    boot-cmd: stress --cpu 2 --vm 2 --vm-bytes 2G --vm-hang 120 --timeout 120s
    resources:
      limits:
        cpu: 1
        memory: 3GB

执行

easyctl boot app-with-cgroups -c config.yaml

top观测

PID    USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
222430 root      20   0    7312    100      0 R  49.5  0.0   0:03.34 stress
222428 root      20   0    7312    100      0 R  49.2  0.0   0:03.29 stress
...

配额为1vCore的情况下,若申请2vCore,两个线程将均分1vCore使用时间(50%

C.测试用例3: 配额2vCore申请1vCore

boot-app:
  - app-name: app1
    boot-cmd: stress --cpu 1 --vm 2 --vm-bytes 2G --vm-hang 120 --timeout 120s
    resources:
      limits:
        cpu: 2
        memory: 3GB

执行

easyctl boot app-with-cgroups -c config.yaml

top观测

PID    USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
223488 root      20   0    7312    100      0 R  99.7  0.0   0:07.42 stress
...

D.测试用例4: 配额4vCore申请4vCore

boot-app:
  - app-name: app1
    boot-cmd: stress --cpu 4 --vm 2 --vm-bytes 2G --vm-hang 120 --timeout 120s
    resources:
      limits:
        cpu: 4
        memory: 3GB

执行

easyctl boot app-with-cgroups -c config.yaml

top观测

PID    USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
224362 root      20   0    7312    100      0 R  99.7  0.0   0:09.68 stress
224364 root      20   0    7312    100      0 R  99.7  0.0   0:09.69 stress
224365 root      20   0    7312    100      0 R  99.7  0.0   0:09.69 stress
224366 root      20   0    7312    100      0 R  99.7  0.0   0:09.69 stress
...

E.测试用例5: 不限制cpu配额

boot-app:
  - app-name: app1
    boot-cmd: stress --cpu 4 --vm 2 --vm-bytes 2G --vm-hang 120 --timeout 120s
    resources:
      limits:
        memory: 3GB

执行

easyctl boot app-with-cgroups -c config.yaml

top观测

PID    USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
239180 root      20   0    7312    100      0 R 100.0  0.0   0:11.01 stress
239177 root      20   0    7312    100      0 R  99.7  0.0   0:11.00 stress
239181 root      20   0    7312    100      0 R  99.7  0.0   0:11.01 stress
239182 root      20   0    7312    100      0 R  99.7  0.0   0:11.01 stress
...

测试内存限额

实现原理:基于控制组memory.limit_in_bytes内存配置字段实现

注: 高级/自定义设置建议使用原生cgroups

A.测试用例1: 配额1GB申请512M

boot-app:
  - app-name: app1
    boot-cmd: stress --vm 1 --vm-bytes 512M --vm-hang 120 --timeout 120s
    resources:
      limits:
        memory: 1GB

执行

$ easyctl boot app-with-cgroups -c config.yaml --debug
[easyctl] localhost.localdomain | 2021-10-25T04:51:21-04:00 | info | 启动命令: stress --vm 1 --vm-bytes 512M --vm-hang 120 --timeout 120s, 进程id: 244051
[easyctl] localhost.localdomain | 2021-10-25T04:51:21-04:00 | info | 限制程序配额 -> CPU: 0核, 内存: 1GB
[easyctl] localhost.localdomain | 2021-10-25T04:51:21-04:00 | info | 创建cpu子系统: /sys/fs/cgroup/cpu/app1 memory子系统: /sys/fs/cgroup/memory/app1
[easyctl] localhost.localdomain | 2021-10-25T04:51:21-04:00 | debug | 0
[easyctl] localhost.localdomain | 2021-10-25T04:51:21-04:00 | debug | Quota: 0 Period: 100000
[weiliang@localhost ~]$ ps -ef|grep 244051
root     244051      1  0 04:51 pts/0    00:00:00 stress --vm 1 --vm-bytes 512M --vm-hang 120 --timeout 120s
root     244052 244051 99 04:51 pts/0    00:00:11 stress --vm 1 --vm-bytes 512M --vm-hang 120 --timeout 120s
root     244053 244051  1 04:51 pts/0    00:00:00 stress --vm 1 --vm-bytes 512M --vm-hang 120 --timeout 120s
...

运行正常 ,证明限制生效

B.测试用例2: 配额1GB申请2GB

boot-app:
  - app-name: app1
    boot-cmd: stress --vm 1 --vm-bytes 2GB --vm-hang 120 --timeout 120s
    resources:
      limits:
        cpu: 1
        memory: 1GB

执行,并查询进程

$ easyctl boot app-with-cgroups -c config.yaml --debug
[easyctl] localhost.localdomain | 2021-10-25T05:01:52-04:00 | info | 启动命令: stress --vm 1 --vm-bytes 2G --vm-hang 120 --vm-stride 64 --timeout 120s, 进程id: 244246
[easyctl] localhost.localdomain | 2021-10-25T05:01:52-04:00 | info | 限制程序配额 -> CPU: 0核, 内存: 1GB
[easyctl] localhost.localdomain | 2021-10-25T05:01:52-04:00 | info | 创建cpu子系统: /sys/fs/cgroup/cpu/app1 memory子系统: /sys/fs/cgroup/memory/app1
[easyctl] localhost.localdomain | 2021-10-25T05:01:52-04:00 | debug | 0
[easyctl] localhost.localdomain | 2021-10-25T05:01:52-04:00 | debug | Quota: 0 Period: 100000
$ ps -ef|grep 244246
weiliang  244250 244014  0 05:02 pts/0    00:00:00 grep --color=auto 244246

进程启动失败。此时查看系统日志,由于OOM已被kill掉了

$ journalctl -xef
...
Oct 25 05:01:52 localhost.localdomain kernel: [ pid ]   uid  tgid total_vm      rss nr_ptes swapents oom_score_adj name
Oct 25 05:01:52 localhost.localdomain kernel: [244246]     0 244246     1828      107       8        0             0 stress
Oct 25 05:01:52 localhost.localdomain kernel: [244247]     0 244247   526117   262128     521        0             0 stress
Oct 25 05:01:52 localhost.localdomain kernel: Memory cgroup out of memory: Kill process 244247 (stress) score 971 or sacrifice child
Oct 25 05:01:52 localhost.localdomain kernel: Killed process 244247 (stress) total-vm:2104468kB, anon-rss:1048388kB, file-rss:124kB, shmem-rss:0kB
...

C.测试用例3: 配额1GB申请1GB

boot-app:
  - app-name: app1
    boot-cmd: stress --vm 1 --vm-bytes 1G --vm-hang 120 --timeout 120s
    resources:
      limits:
        cpu: 1
        memory: 1GB

执行,并查询进程

$ easyctl] localhost.localdomain | 2021-10-25T05:28:34-04:00 | info | 启动命令: stress --vm 1 --vm-bytes 1G --vm-hang 120 --vm-stride 64 --timeout 120s, 进程id: 244609
[easyctl] localhost.localdomain | 2021-10-25T05:28:34-04:00 | info | 限制程序配额 -> CPU: 0核, 内存: 1GB
[easyctl] localhost.localdomain | 2021-10-25T05:28:34-04:00 | info | 创建cpu子系统: /sys/fs/cgroup/cpu/app1 memory子系统: /sys/fs/cgroup/memory/app1
[easyctl] localhost.localdomain | 2021-10-25T05:28:34-04:00 | debug | 0
[easyctl] localhost.localdomain | 2021-10-25T05:28:34-04:00 | debug | Quota: 0 Period: 100000
$ ps -ef|grep 244609
root     244609      1  0 05:28 pts/0    00:00:00 stress --vm 1 --vm-bytes 1G --vm-hang 120 --vm-stride 64 --timeout 120s
root     244612 244609  5 05:28 pts/0    00:00:00 stress --vm 1 --vm-bytes 1G --vm-hang 120 --vm-stride 64 --timeout 120s

最佳实践

不限制cpu,限制内存最大8GB

boot-app:
  - app-name: replace_to_your_program_name
    boot-cmd: replace_to_your_program_boot_command
    resources:
      limits:
        memory: 8GB

不限制内存,限制cpu最多使用2vCorecpu线程)

boot-app:
  - app-name: replace_to_your_program_name
    boot-cmd: replace_to_your_program_boot_command
    resources:
      limits:
        cpu: 2

限制内存最大8GBcpu最多使用2vCorecpu线程)

boot-app:
  - app-name: replace_to_your_program_name
    boot-cmd: replace_to_your_program_boot_command
    resources:
      limits:
        cpu: 2
        memory: 8GB

后记

本功能实现初衷: 以实际需求驱动学习容器原理,并通过简单的应用程序落地实践。

相关文章

  • 通过easyctl为你的程序添加配额限制

    以下内容适用于CenOS7平台 缘起 试想下,你是否遇到以下场景: 同一主机运行多个程序,由于某个程序异常(循环引...

  • 磁盘配额quota

    磁盘配额 磁盘配额就是管理员可以为用户所能使用的磁盘空间进行配额限制的一个功能。他可以通过用户和组进行配额管理的,...

  • 12、磁盘配额quota

    磁盘配额 配额:quota 什么是磁盘配额? 限制磁盘资源的使用的。 限制原因就是因为资源不是无限的。 应用...

  • linux磁盘配额

    磁盘配额定义:针对某个用户或者某个组限制使用磁盘的使用空间。软限制:警告硬限制:禁用分配磁盘配额(对配置文件修改)...

  • etcd磁盘清理步骤

    etcd默认的空间配额限制为2G,超出空间配额限制就会影响服务,所以需要定期清理 以下是etcd磁盘清理的步骤: ...

  • 14-1高级文件系统管理之磁盘配额

    配置磁盘配额 一、开启磁盘配额功能 在/etc/fstab磁盘配额的挂载设备那行,挂载选项添加:分区挂载选项:us...

  • 6. kubernetes 资源和调度

    6. kubernetes 资源和调度 一、资源配额与限制 资源配额用于管理命名空间(NameSpace)中对象...

  • 12. ZooKeeper配额和认证

    ZooKeeper具有与其数据模型相关的可配置配额(quota)。 可以设置znode上的配额限制和存储的数据量。...

  • kubernetes资源对象之resourcequota

    Resource Quotas,资源配额,是用来限制用户资源用量的一种机制。 使用资源配额前提条件 在 API S...

  • 【CentOS基础篇】之磁盘分区

    分区的作用 优化I/O 性能实现磁盘空间配额限制高修复速度隔离系统和程序安装多个OS(操作系统)采用不同文件系统 ...

网友评论

      本文标题:通过easyctl为你的程序添加配额限制

      本文链接:https://www.haomeiwen.com/subject/yulyaltx.html