注:

在 Oracle Linux 上运行控制组版本 2

简介

控制组 (Control Groups, cgroups) 是一项 Linux 内核功能,用于限制、确定优先级并为正在运行的进程分配 CPU 时间、内存和网络带宽等资源。

本教程指导您限制使用 cgroups v2 的用户进程的 CPU 时间。

目标

在此实验室中,您将学习:

先决条件

设置实验室环境

注意:使用免费实验室环境时,请参见 Oracle Linux Lab Basics 了解连接和其他使用情况说明。

在开始实验室之前,我们需要完成一些清洁用品。创建的项目用于演示 cgroup 的限制功能。

创建加载生成脚本

  1. 如果尚未连接,请打开终端并通过 ssh 连接到 ol8-server 系统。

    ssh oracle@<ip_address_of_ol8-server>
    
  2. 创建 foo.exe 脚本。

    echo '#!/bin/bash
    
    /usr/bin/sha1sum /dev/zero' > foo.exe
    
  3. foo.exe 脚本复制到 $PATH 中的某个位置并设置正确的权限。

    sudo mv foo.exe /usr/local/bin/foo.exe
    sudo chown root:root /usr/local/bin/foo.exe
    sudo chmod 755 /usr/local/bin/foo.exe
    

    注意:(可选)如果使用 SELinux enforcing 运行:

    sudo sestatus
    

    通过运行以下命令在复制和更改权限后修复 SELinux 标签:

    sudo /sbin/restorecon -v /usr/local/bin/foo.exe
    

创建加载生成服务

  1. 创建 foo.service 文件。

    echo '[Unit]
    Description=the foo service
    After=network.target
    
    [Service]
    ExecStart=/usr/local/bin/foo.exe
    
    [Install]
    WantedBy=multi-user.target' > foo.service
    
  2. foo.service 脚本复制到 systemd 脚本所在的位置并设置正确的权限。

    sudo mv foo.service /etc/systemd/system/foo.service
    sudo chown root:root /etc/systemd/system/foo.service
    sudo chmod 644 /etc/systemd/system/foo.service
    

    注意:(可选)如果使用 SELinux enforcing 运行,请通过运行以下命令在复制和更改权限后修复 SELinux 标签:

    sudo /sbin/restorecon -v /etc/systemd/system/foo.service
    
  3. 重新加载守护进程,以便 systemd 识别新服务。

    sudo systemctl daemon-reload
    
  4. 启动 foo.service 并检查其状态。

    sudo systemctl start foo.service
    sudo systemctl status foo.service
    

创建用户

其他用户将允许在这些不同的帐户下运行负载生成脚本以及不同的 CPU 权重。

  1. 创建用户和设置密码。

    sudo useradd -u 8000 ralph
    sudo useradd -u 8001 alice
    echo "ralph:oracle" | sudo chpasswd
    echo "alice:oracle" | sudo chpasswd
    
  2. 允许 SSH 连接。

    oracle 用户帐户复制 SSH 密钥。

    sudo mkdir /home/ralph/.ssh
    sudo cp /home/oracle/.ssh/authorized_keys /home/ralph/.ssh/authorized_keys
    sudo chown -R ralph:ralph /home/ralph/.ssh
    sudo chmod 700 /home/ralph/.ssh
    sudo chmod 600 /home/ralph/.ssh/authorized_keys
    
  3. alice 用户重复此步骤。

    sudo mkdir /home/alice/.ssh
    sudo cp /home/oracle/.ssh/authorized_keys /home/alice/.ssh/authorized_keys
    sudo chown -R alice:alice /home/alice/.ssh
    sudo chmod 700 /home/alice/.ssh
    sudo chmod 600 /home/alice/.ssh/authorized_keys
    
  4. 打开新终端并验证两个 SSH 连接是否正常工作。

    ssh ralph@<ip_address_of_ol8-server>
    

    然后,对会话执行 exit,然后对以下用户重复此步骤。

    ssh alice@<ip_address_of_ol8-server>
    

    退出会话,然后关闭终端窗口。

挂载 cgroups v2

默认情况下,Oracle Linux 在引导时挂载 cgroups v1。要使用 cgroups v2,必须手动配置引导内核参数。

  1. 返回到您以 oracle 身份登录的终端。

  2. 添加 cgroups v2 systemd 内核参数。

    sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"
    

    而只能通过运行 sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="systemd.unified_cgroup_hierarchy=1" 来指定当前引导项。

  3. 重新引导。

    sudo reboot
    

    重新引导需要几分钟才能完成。

    注意:在重新引导完成并且 sshd 守护进程正在运行之前,您将无法对系统执行 ssh 操作。

  4. 通过 ssh 重新连接到 ol8-server 系统。

    ssh oracle@<ip_address_of_ol8-server>
    
  5. 验证是否已挂载 cgroups v2。

    sudo mount -l | grep cgroup
    

    输出示例:

    cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate)
    
  6. 检查 cgroups 已挂载目录的内容。

    ll /sys/fs/cgroup
    

    输出示例:

    total 0
    -r--r--r--.  1 root root 0 Mar 13 21:20 cgroup.controllers
    -rw-r--r--.  1 root root 0 Mar 13 21:20 cgroup.max.depth
    -rw-r--r--.  1 root root 0 Mar 13 21:20 cgroup.max.descendants
    -rw-r--r--.  1 root root 0 Mar 13 21:20 cgroup.procs
    -r--r--r--.  1 root root 0 Mar 13 21:20 cgroup.stat
    -rw-r--r--.  1 root root 0 Mar 13 21:20 cgroup.subtree_control
    -rw-r--r--.  1 root root 0 Mar 13 21:20 cgroup.threads
    -rw-r--r--.  1 root root 0 Mar 13 21:20 cpu.pressure
    -r--r--r--.  1 root root 0 Mar 13 21:20 cpuset.cpus.effective
    -r--r--r--.  1 root root 0 Mar 13 21:20 cpuset.mems.effective
    drwxr-xr-x.  2 root root 0 Mar 13 21:20 init.scope
    -rw-r--r--.  1 root root 0 Mar 13 21:20 io.pressure
    -rw-r--r--.  1 root root 0 Mar 13 21:20 memory.pressure
    drwxr-xr-x. 87 root root 0 Mar 13 21:20 system.slice
    drwxr-xr-x.  4 root root 0 Mar 13 21:24 user.slice
    

    输出显示 root control group 的缺省位置。该目录包含所有以 cgroup 为前缀的接口文件以及与以 .scope.slice 结尾的 systemd 相关的目录。

使用虚拟文件系统

在开始之前,我们需要了解挂载在 /sys/fs/cgroup 上的 cgroups 虚拟文件系统。

  1. 显示每个人都参与 CPU 的 CPU。

    cat /sys/fs/cgroup/cpuset.cpus.effective
    

    输出示例:

    [oracle@ol8-server ~]$ cat /sys/fs/cgroup/cpuset.cpus.effective
    0-1
    

    我们的测试箱是部署在 VM.Standard2.1 配置上的 Oracle Linux 8 实例,这是双核系统。

  2. 显示哪些控制器处于活动状态。

    cat /sys/fs/cgroup/cgroup.controllers
    

    输出示例:

    [oracle@ol8-server ~]$ cat /sys/fs/cgroup/cgroup.controllers
    cpuset cpu io memory pids rdma
    

    最好查看显示 CPU 控制器,因为我们稍后将在此实验室中使用它。

  3. 显示 oracle 产生的进程。

    首先,我们需要确定 oracle 的用户 ID (UID)。

    who
    id
    

    输出示例:

    [oracle@ol8-server ~]$ who
    oracle   pts/0        2022-03-13 21:23 (10.39.209.157)
    [oracle@ol8-server ~]$ id
    uid=1001(oracle) gid=1001(oracle) groups=1001(oracle),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    

    使用 UID,可以找到 oracle 用户分片。

    cd /sys/fs/cgroup/user.slice
    ls
    

    输出示例:

    [oracle@ol8-server ~]$ cd /sys/fs/cgroup/user.slice
    [oracle@ol8-server user.slice]$ ls
    cgroup.controllers      cgroup.subtree_control  memory.events        memory.pressure      pids.max
    cgroup.events           cgroup.threads          memory.events.local  memory.stat          user-0.slice
    cgroup.freeze           cgroup.type             memory.high          memory.swap.current  user-1001.slice
    cgroup.max.depth        cpu.pressure            memory.low           memory.swap.events   user-989.slice
    cgroup.max.descendants  cpu.stat                memory.max           memory.swap.max
    cgroup.procs            io.pressure             memory.min           pids.current
    cgroup.stat             memory.current          memory.oom.group     pids.events
    

    Systemd 为每个用户分配一个名为 user-<UID>.slice 的分片。那么该目录下是什么?

    cd user-1001.slice
    ls
    

    输出示例:

    [oracle@ol8-server user.slice]$ cd user-1001.slice/
    [oracle@ol8-server user-1001.slice]$ ls
    cgroup.controllers  cgroup.max.descendants  cgroup.threads  io.pressure        user-runtime-dir@1001.service
    cgroup.events       cgroup.procs            cgroup.type     memory.pressure
    cgroup.freeze       cgroup.stat             cpu.pressure    session-3.scope
    cgroup.max.depth    cgroup.subtree_control  cpu.stat        user@1001.service
    

    这些是 oracle 用户的顶层 cgroup。但是,cgroup.procs 中未列出进程。那么,用户进程列表在哪里?

    cat cgroup.procs
    

    输出示例:

    [oracle@ol8-server user-1001.slice]$ cat cgroup.procs
    [oracle@ol8-server user-1001.slice]$
    

    oracle 在此实验开始时打开 SSH 会话时,用户会话将创建一个范围子单元。在此范围内,可以检查 cgroup.procs 以获取该会话下产生的进程列表。

    注意:根据与系统的连接数,用户可能有多个会话;因此,根据需要替换下面示例中的 3。

    cd session-3.scope
    ls
    cat cgroup.procs
    

    输出示例:

    [oracle@ol8-server user-1001.slice]$ cd session-3.scope/
    [oracle@ol8-server session-3.scope]$ ls
    cgroup.controllers  cgroup.max.depth        cgroup.stat             cgroup.type   io.pressure
    cgroup.events       cgroup.max.descendants  cgroup.subtree_control  cpu.pressure  memory.pressure
    cgroup.freeze       cgroup.procs            cgroup.threads          cpu.stat
    [oracle@ol8-server session-3.scope]$ cat cgroup.procs
    3189
    3200
    3201
    54217
    

    现在,我们发现这些进程非常困难,因此可以使用 systemd-cgls 在树形视图中显示相同的信息。

    注意:在虚拟文件系统中运行时,systemd-cgls 将 cgroup 输出限制为当前工作目录。

    cd /sys/fs/cgroup/user.slice/user-1001.slice
    systemd-cgls
    

    输出示例:

    [oracle@ol8-server user-1001.slice]$ systemd-cgls
    Working directory /sys/fs/cgroup/user.slice/user-1001.slice:
    ├─session-3.scope
    │ ├─ 3189 sshd: oracle [priv]
    │ ├─ 3200 sshd: oracle@pts/0
    │ ├─ 3201 -bash
    │ ├─55486 systemd-cgls
    │ └─55487 less
    └─user@1001.service
      └─init.scope
        ├─3193 /usr/lib/systemd/systemd --user
        └─3195 (sd-pam)
    

限制使用的 CPU 核心

对于 cgroups v2,systemd 对 cpuset 控制器具有完全控制权。通过此控制级别,管理员可以仅调度特定 CPU 核心上的工作。

  1. 检查 CPU user.slice

    cd /sys/fs/cgroup/user.slice
    ls
    cat ../cpuset.cpus.effective
    

    输出示例:

    [oracle@ol8-server cgroup]$ cd /sys/fs/cgroup/user.slice/
    [oracle@ol8-server user.slice]$ ls
    cgroup.controllers      cgroup.subtree_control  memory.events        memory.pressure      pids.max
    cgroup.events           cgroup.threads          memory.events.local  memory.stat          user-0.slice
    cgroup.freeze           cgroup.type             memory.high          memory.swap.current  user-1001.slice
    cgroup.max.depth        cpu.pressure            memory.low           memory.swap.events   user-989.slice
    cgroup.max.descendants  cpu.stat                memory.max           memory.swap.max
    cgroup.procs            io.pressure             memory.min           pids.current
    cgroup.stat             memory.current          memory.oom.group     pids.events
    [oracle@ol8-server user.slice]$ cat ../cpuset.cpus.effective
    0-1
    

    cpuset.cpus.effective 显示了 user.slice 使用的实际核心。如果特定 cgroup 目录中不存在参数,或者未设置参数,则从父级继承值,这恰好是此情况的顶级 cgroup 根目录。

  2. system 和用户 0、1001 和 989 分片限制为 CPU 核心 0。

    cat /sys/fs/cgroup/system.slice/cpuset.cpus.effective
    sudo systemctl set-property system.slice AllowedCPUs=0
    cat /sys/fs/cgroup/system.slice/cpuset.cpus.effective
    

    输出示例:

    [oracle@ol8-server user.slice]$ cat /sys/fs/cgroup/system.slice/cpuset.cpus.effective
    cat: /sys/fs/cgroup/system.slice/cpuset.cpus.effective: No such file or directory
    [oracle@ol8-server user.slice]$ sudo systemctl set-property system.slice AllowedCPUs=0
    [oracle@ol8-server user.slice]$ cat /sys/fs/cgroup/system.slice/cpuset.cpus.effective
    0
    

    注意:No such file or directory 指示默认情况下,system 分片从父级继承其 cpuset.cpus.effective 值。

    sudo systemctl set-property user-0.slice AllowedCPUs=0
    sudo systemctl set-property user-1001.slice AllowedCPUs=0
    sudo systemctl set-property user-989.slice AllowedCPUs=0
    
  3. ralph 用户限制为 CPU 核心 1。

    sudo systemctl set-property user-8000.slice AllowedCPUs=1
    cat /sys/fs/cgroup/user.slice/user-8000.slice/cpuset.cpus.effective
    

    输出示例:

    [oracle@ol8-server ~]$ sudo systemctl set-property user-8000.slice AllowedCPUs=1
    [oracle@ol8-server ~]$ cat /sys/fs/cgroup/user.slice/user-8000.slice/cpuset.cpus.effective
    1
    
  4. 打开新终端,并通过 ssh 作为 ralph 连接到 ol8-server 系统。

    ssh ralph@<ip_address_of_ol8-server>
    
  5. 使用 foo.exe 脚本进行测试。

    foo.exe &
    

    检验结果。

    top
    

    运行 top 后,命中 1 key 以分别显示 CPU。

    输出示例:

    top - 18:23:55 up 21:03,  2 users,  load average: 1.03, 1.07, 1.02
    Tasks: 155 total,   2 running, 153 sleeping,   0 stopped,   0 zombie
    %Cpu0  :  6.6 us,  7.0 sy,  0.0 ni, 84.8 id,  0.0 wa,  0.3 hi,  0.3 si,  1.0 st
    %Cpu1  : 93.0 us,  6.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.7 hi,  0.3 si,  0.0 st
    MiB Mem :  14707.8 total,  13649.1 free,    412.1 used,    646.6 buff/cache
    MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  13993.0 avail Mem
    
       PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
    226888 ralph     20   0  228492   1808   1520 R  99.7   0.0 199:34.27 sha1sum
    269233 root      20   0  223724   6388   1952 S   1.3   0.0   0:00.04 pidstat
       1407 root      20   0  439016  41116  39196 S   0.3   0.3   0:17.81 sssd_nss
       1935 root      20   0  236032   3656   3156 S   0.3   0.0   0:34.34 OSWatcher
       2544 root      20   0  401900  40292   9736 S   0.3   0.3   0:10.62 ruby
          1 root      20   0  388548  14716   9508 S   0.0   0.1   0:21.21 systemd
    ...
    

    键入 q 退出顶部。

  6. 检查运行进程的处理器的替代方式。

    ps -eo pid,psr,user,cmd | grep ralph
    

    输出示例:

    [ralph@ol8-server ~]$ ps -eo pid,psr,user,cmd | grep ralph
     226715   1 root     sshd: ralph [priv]
     226719   1 ralph    /usr/lib/systemd/systemd --user
     226722   1 ralph    (sd-pam)
     226727   1 ralph    sshd: ralph@pts/2
     226728   1 ralph    -bash
     226887   1 ralph    /bin/bash /usr/local/bin/foo.exe
     226888   1 ralph    /usr/bin/sha1sum /dev/zero
     269732   1 ralph    ps -eo pid,psr,user,cmd
     269733   1 ralph    grep --color=auto ralph
    

    psr 列是 cmd 或实际进程的 CPU 编号。

  7. 退出并关闭用于以 ralph 身份登录的终端窗口。

  8. 中止 foo.exe 作业。

    切换回您以 oracle 身份登录的终端,然后运行以下命令。

    sudo pkill sha1sum
    

调整用户的 CPU 权重

现在可以让 alice 联接有趣的内容。她需要完成一些关键工作,因此,我们会将 CPU 的正常优先级提高一倍。

  1. alice 分配给与 ralph 相同的 CPU。

    sudo systemctl set-property user-8001.slice AllowedCPUs=1
    cat /sys/fs/cgroup/user.slice/user-8001.slice/cpuset.cpus.effective
    
  2. 设置 CPUWeight

    sudo systemctl set-property user-8001.slice CPUWeight=200
    cat /sys/fs/cgroup/user.slice/user-8001.slice/cpu.weight
    

    默认权重是 100,因此 200 是该数字的两倍。

  3. 打开新终端,并通过 ssh 作为 ralph 连接到 ol8-server 系统。

    ssh ralph@<ip_address_of_ol8-server>
    
  4. ralph 身份运行 foo.exe

    foo.exe &
    
  5. 打开另一个新终端,并通过 ssh 作为 alice 连接到 ol8-server 系统。

    ssh alice@<ip_address_of_ol8-server>
    
  6. alice 身份运行 foo.exe

    foo.exe &
    
  7. 通过 top 验证 alice 是否获得更高优先级。

    top
    

    顶部运行后,命中 1 key 以分别显示 CPU。

    输出示例:

    top - 20:10:55 up 25 min,  3 users,  load average: 1.29, 0.46, 0.20
    Tasks: 164 total,   3 running, 161 sleeping,   0 stopped,   0 zombie
    %Cpu0  :  0.0 us,  0.0 sy,  0.0 ni, 96.5 id,  0.0 wa,  0.0 hi,  3.2 si,  0.3 st
    %Cpu1  : 92.4 us,  7.6 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    MiB Mem :  15715.8 total,  14744.6 free,    438.5 used,    532.7 buff/cache
    MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  15001.1 avail Mem 
    
        PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND  
       7934 alice     20   0   15800   1768   1476 R  67.0   0.0   0:36.15 sha1sum  
       7814 ralph     20   0   15800   1880   1592 R  33.3   0.0   0:34.60 sha1sum  
          1 root      20   0  388476  14440   9296 S   0.0   0.1   0:02.22 systemd  
          2 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kthreadd
    ...
    
  8. 返回以 oracle 用户身份登录的终端。

  9. 使用 foo.service 加载 system.slice

    sudo systemctl start foo.service
    

    现在,查看仍在 alice 终端窗口中运行的顶部输出。查看 foo.service 正在占用 CPU 0,而用户根据其权重拆分 CPU 1。

    输出示例:

    top - 19:18:15 up 21:57,  3 users,  load average: 2.15, 2.32, 2.25
    Tasks: 159 total,   4 running, 155 sleeping,   0 stopped,   0 zombie
    %Cpu0  : 89.1 us,  7.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.7 hi,  0.3 si,  2.6 st
    %Cpu1  : 93.7 us,  5.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.7 hi,  0.3 si,  0.0 st
    MiB Mem :  14707.8 total,  13640.1 free,    420.5 used,    647.2 buff/cache
    MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  13984.3 avail Mem
    
        PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
     280921 root      20   0  228492   1776   1488 R  93.4   0.0   0:07.74 sha1sum
     279185 alice     20   0  228492   1816   1524 R  65.6   0.0   7:35.18 sha1sum
     279291 ralph     20   0  228492   1840   1552 R  32.8   0.0   7:00.30 sha1sum
       2026 oracle-+  20   0  935920  29280  15008 S   0.3   0.2   1:03.31 gomon
          1 root      20   0  388548  14716   9508 S   0.0   0.1   0:22.30 systemd
          2 root      20   0       0      0      0 S   0.0   0.0   0:00.10 kthreadd
    ...
    

分配 CPU 限额

最后,我们将限制 ralph 的 CPU 时间。

  1. 返回以 oracle 用户身份登录的终端。

  2. 将定额设置为 5%

    sudo systemctl set-property user-8000.slice CPUQuota=5%
    

    更改将立即生效,如在仍在 alice 用户终端中运行的顶部输出中所示。

    输出示例:

    top - 19:24:53 up 22:04,  3 users,  load average: 2.21, 2.61, 2.45
    Tasks: 162 total,   4 running, 158 sleeping,   0 stopped,   0 zombie
    %Cpu0  : 93.0 us,  4.7 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.7 hi,  0.0 si,  1.7 st
    %Cpu1  : 91.7 us,  5.6 sy,  0.0 ni,  0.0 id,  0.0 wa,  1.0 hi,  1.0 si,  0.7 st
    MiB Mem :  14707.8 total,  13639.4 free,    420.0 used,    648.4 buff/cache
    MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  13984.7 avail Mem
    
        PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
     280921 root      20   0  228492   1776   1488 R  97.4   0.0   6:26.75 sha1sum
     279185 alice     20   0  228492   1816   1524 R  92.1   0.0  12:21.12 sha1sum
     279291 ralph     20   0  228492   1840   1552 R   5.3   0.0   8:44.84 sha1sum
          1 root      20   0  388548  14716   9508 S   0.0   0.1   0:22.48 systemd
          2 root      20   0       0      0      0 S   0.0   0.0   0:00.10 kthreadd
    ...
    
  3. 使用 oracle 终端窗口恢复 ralph 用户的上限。

echo "max 100000" | sudo tee -a user-8000.slice/cpu.max

配额将写入 cpu.max 文件,缺省值为 max 100000

输出示例:

[oracle@ol8-server user.slice]$ echo "max 100000" | sudo tee -a user-8000.slice/cpu.max
max 100000

您可以启用 cgroups v2,将用户限制在系统负载不足时使用的特定 CPU,并将其锁定为仅使用该 CPU 的百分比。查看其他资源,了解有关 Oracle Linux 的更多信息。

详细信息

请参阅其他相关资源:

更多学习资源

docs.oracle.com/learn 上浏览其他实验室,或者在 Oracle Learning YouTube 渠道上访问更多免费学习内容。此外,访问 education.oracle.com/learning-explorer 以成为 Oracle Learning Explorer。

有关产品文档,请访问 Oracle 帮助中心