一、背景

随着业务快速发展,目前离线分析数仓Doris集群,承载着多样化的计算需求,比如数据科学日常Job、业务离线跑批、客户侧报表查询等,会出现资源抢占情况,需要对不同账户资源请求进行资源限制,经过调研采用Doris官方的Workload Group方案。

Doris版本:doris-2.1.8-1-834d802457

二、Workload Group介绍:

Workload Group是Apache Doris提供的进程内资源隔离机制,通过对 BE进程内的CPU、内存、IO资源进行细粒度划分,实现不同业务负载之间的资源隔离。

原理如下图所示:

workload_group

目前支持的隔离能力包括:

资源类型 隔离方式 说明
CPU 软限 / 硬限 软限按权重分配,硬限为绝对上限
内存 软限 / 硬限 硬限超限时自动 kill 查询释放内存
IO 限速 限制读本地/远程文件的 IO 吞吐
并发 排队机制 超并发查询进入队列等待

更多参考: https://doris.apache.org/zh-CN/docs/2.1/admin-manual/workload-management/workload-group

三、Doris集群调整,支持Cgroup(手动部署)

3.1 Cgroup环境配置

查看系统支持的 CGroup版本

root@doristest:~# cat /proc/filesystems | grep cgroup
nodev   cgroup
nodev   cgroup2

确认Cgroup生效版本:

root@doristest:~# ls /sys/fs/cgroup/cpu/
ls: cannot access '/sys/fs/cgroup/cpu/': No such file or directory 
root@doristest:~# ls /sys/fs/cgroup/cgroup.controllers
/sys/fs/cgroup/cgroup.controllers # 说明生效版本为cgroup v2

3.2 创建Doris CGroup 目录

# ========== CGroup v1 操作 ==========
mkdir /sys/fs/cgroup/cpu/doris

# 设置权限(root 为运行 BE 的用户)
chmod 770 /sys/fs/cgroup/cpu/doris
chown -R root:root /sys/fs/cgroup/cpu/doris


# ========== CGroup v2 操作 ==========
mkdir /sys/fs/cgroup/doris

# 设置权限
chmod 770 /sys/fs/cgroup/doris
chown -R root:root /sys/fs/cgroup/doris  # 视情况而定,目前为root

3.3 CGroup v2 额外配置

CGroup v2 权限管控更严格,需要额外配置:

# 修改根目录 cgroup.procs 文件权限
chmod a+w /sys/fs/cgroup/cgroup.procs

# 启用 cpu 控制器
# 进入 doris 目录
cd /sys/fs/cgroup/doris

# 启用 cpu 控制器(通过修改父级 subtree_control)
# 默认已经设置,可忽略
echo +cpu > ../cgroup.subtree_control

# 验证:查看 doris 目录下是否出现 cpu.max 文件
/sys/fs/cgroup/doris/cpu.max
# 且 cgroup.controllers 应包含 cpu
# cat /sys/fs/cgroup/cgroup.controllers 
cpuset cpu io memory hugetlb pids rdma misc

3.4 配置 BE 并重启

编辑 be.conf 文件:

# ========== CGroup v1 配置 ==========
doris_cgroup_cpu_path = /sys/fs/cgroup/cpu/doris

# ========== CGroup v2 配置 ==========
doris_cgroup_cpu_path = /sys/fs/cgroup/doris

重启 BE 服务:

# 重启 BE
./bin/stop_be.sh
./bin/start_be.sh

验证:查看 be.INFO 日志

grep "add thread" log/be.INFO
# 出现 "add thread xxx to group" 字样表示配置成功

3.5 持久化配置(可选)

机器重启后 CGroup 配置会清空,建议使用 systemd 创建开机自动配置服务:

# /etc/systemd/system/doris-cgroup.service
[Unit]
Description=Doris CGroup Setup
Before=doris-be.service

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'mkdir -p /sys/fs/cgroup/cpu/doris && chmod 770 /sys/fs/cgroup/cpu/doris && chown -R doris:doris /sys/fs/cgroup/cpu/doris'

[Install]
WantedBy=multi-user.target

设置开机自动启动:

systemctl enable doris-cgroup.service

四、通过Ansible批量启用Doris节点Cgroup

4.1 准备工作

在执行批量部署前,需要确认以下信息:

  1. 确认Cgroup版本:所有Doris BE节点的Cgroup版本(v1或v2)
  2. 准备主机清单:编辑Ansible的hosts文件,添加所有BE节点
  3. 确认BE用户:运行Doris BE服务的用户(当前为root)

4.2 配置主机清单

编辑 hosts 文件:

1
2
3
[doris_dabe_nodes]
# 如果有不同Cgroup版本的节点,可以分组,这里仅指定离线分析数仓BE节点
10.18.7.160

4.3 创建Ansible Playbook

创建文件 setup_doris_cgroup.yaml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
---
- name: Setup Doris CGroup Configuration
  hosts: doris_dabe_nodes
  become: true
  vars:
    # CGroup配置路径(根据版本自动选择)
    cgroup_v1_path: "/sys/fs/cgroup/cpu/doris"
    cgroup_v2_path: "/sys/fs/cgroup/doris"
    doris_user: "root"
    doris_group: "root"
    
  tasks:
    - name: Detect CGroup version
      stat:
        path: /sys/fs/cgroup/cpu/
      register: cgroup_v1_check
      
    - name: Set CGroup path based on version
      set_fact:
        cgroup_path: "{{ cgroup_v1_path if cgroup_v1_check.stat.exists else cgroup_v2_path }}"
        cgroup_version: "{{ 'v1' if cgroup_v1_check.stat.exists else 'v2' }}"
        
    - name: Display detected CGroup version
      debug:
        msg: "Node {{ inventory_hostname }} uses CGroup {{ cgroup_version }}, path: {{ cgroup_path }}"

    - name: Create CGroup directory
      file:
        path: "{{ cgroup_path }}"
        state: directory
        mode: '0770'
        owner: "{{ doris_user }}"
        group: "{{ doris_group }}"
      register: cgroup_dir_result
      
    - name: Configure CGroup v2 specific settings
      block:
        - name: Enable write permission on cgroup.procs
          file:
            path: /sys/fs/cgroup/cgroup.procs
            mode: 'a+w'
            
        - name: Enable CPU controller for CGroup v2
          shell: |
            if ! grep -q '+cpu' /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null; then
              echo '+cpu' > /sys/fs/cgroup/cgroup.subtree_control
            fi
          args:
            executable: /bin/bash
          ignore_errors: yes
          
        - name: Verify CPU controller is enabled
          stat:
            path: "{{ cgroup_path }}/cpu.max"
          register: cpu_controller_check
          
        - name: Display CPU controller status
          debug:
            msg: "CPU controller is {{ 'enabled' if cpu_controller_check.stat.exists else 'NOT enabled' }}"
      when: cgroup_version == 'v2'

    - name: Verify CGroup directory structure
      shell: |
        echo "=== CGroup Version: {{ cgroup_version }} ==="
        echo "CGroup Path: {{ cgroup_path }}"
        ls -la {{ cgroup_path }} 2>/dev/null || echo "Directory verified"
        echo ""
        if [ "{{ cgroup_version }}" = "v2" ]; then
          echo "=== CPU Controller Check ==="
          cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null
          test -f {{ cgroup_path }}/cpu.max && echo "cpu.max: EXISTS" || echo "cpu.max: NOT FOUND"
        fi
      args:
        executable: /bin/bash
      register: verify_result
      
    - name: Display verification results
      debug:
        var: verify_result.stdout_lines

    - name: Create systemd service for CGroup persistence (optional)
      copy:
        dest: /etc/systemd/system/doris-cgroup.service
        content: |
          [Unit]
          Description=Doris CGroup Setup
          Before=doris-be.service

          [Service]
          Type=oneshot
          ExecStart=/bin/bash -c 'mkdir -p {{ cgroup_path }} && chmod 770 {{ cgroup_path }} && chown -R {{ doris_user }}:{{ doris_group }} {{ cgroup_path }}'
          {% if cgroup_version == 'v2' %}
          ExecStartPost=/bin/bash -c 'chmod a+w /sys/fs/cgroup/cgroup.procs'
          ExecStartPost=/bin/bash -c 'echo "+cpu" > /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null || true'
          {% endif %}

          [Install]
          WantedBy=multi-user.target
        mode: '0644'
      when: cgroup_dir_result is changed
      notify: Enable systemd service

  handlers:   
    - name: Enable systemd service
      systemd:
        name: doris-cgroup
        enabled: yes
        daemon_reload: yes

4.4 执行批量部署

语法检查和试运行

1
2
3
4
5
6
7
8
# 1. 语法检查
ansible-playbook -i hosts setup_doris_cgroup.yaml --syntax-check

# 2. 列出所有将要执行的任务
ansible-playbook -i hosts setup_doris_cgroup.yaml --list-tasks

# 3. 试运行(不实际执行,只显示会做的更改)
ansible-playbook -i hosts setup_doris_cgroup.yaml --check --diff

4.5 正式执行并验证部署结果

正式执行

1
2
3
4
5
6
7
8
# 方式1: 在所有BE节点执行
ansible-playbook -i hosts setup_doris_cgroup.yaml

# 方式2: 只在特定节点执行(如先在单台测试)
ansible-playbook -i hosts setup_doris_cgroup.yaml --limit 10.18.7.101

# 方式3: 逐步执行,每个任务需确认
ansible-playbook -i hosts setup_doris_cgroup.yaml --step

通过Doris Manager管理节点调整Doris BE配置(滚动重启):

# ========== CGroup v1 配置 ==========
doris_cgroup_cpu_path = /sys/fs/cgroup/cpu/doris

# ========== CGroup v2 配置 ==========
doris_cgroup_cpu_path = /sys/fs/cgroup/doris

doris be增加Cgroup配置

验证:

1
2
3
4
5
# 1. 检查CGroup目录是否创建
ansible doris_dabe_nodes -i hosts -m shell -a "ls -la /sys/fs/cgroup/doris 2>/dev/null || ls -la /sys/fs/cgroup/cpu/doris 2>/dev/null"

# 2. 检查BE日志中CGroup初始化情况(根据实际BE日志路径调整)
ansible doris_dabe_nodes -i hosts -m shell -a "tail -100 /opt/doris/doris/be/log/be.INFO | grep -i 'cgroup\|add thread' | tail -5"

至此,Doris BE节点已经开启Cgroup资源限制.

五、Workload Group管理

5.1 创建 Workload Group

基础示例(CPU 软限)

1
2
3
4
CREATE WORKLOAD GROUP IF NOT EXISTS g1
PROPERTIES (
    "cpu_share" = "1024"
);

完整配置示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
CREATE WORKLOAD GROUP IF NOT EXISTS etl_group
PROPERTIES (
    -- CPU 配置(软限模式)
    "cpu_share" = "2048",
    "cpu_hard_limit" = "40%",
    
    -- 内存配置(硬限模式)
    "memory_limit" = "40%",
    "enable_memory_overcommit" = "false",
    
    -- 并发控制
    "max_concurrency" = "1",
    "max_queue_size" = "100",
    "queue_timeout" = "10000",
    
    -- IO 限速
    "read_bytes_per_second" = "104857600",        -- 100MB/s
    "remote_read_bytes_per_second" = "52428800"   -- 50MB/s
);

5.2 属性详解

属性名 类型 默认值 取值范围 说明
cpu_share INT -1 [1, 10000] CPU 软限权重,值越大优先级越高
cpu_hard_limit INT -1 [1%, 100%] CPU 硬限百分比(2.1 版本新增)
memory_limit FLOAT -1 (0%, 100%] 内存限制百分比
enable_memory_overcommit BOOL true true/false true=软限(可超额),false=硬限
max_concurrency INT 2147483647 [0, 2147483647] 最大并发查询数
max_queue_size INT 0 [0, 2147483647] 排队队列长度(0=不排队)
queue_timeout INT 0 [0, 2147483647] 排队超时时间(毫秒)
scan_thread_num INT -1 [1, 2147483647] Scan 线程数(-1=使用 BE 配置)
max_remote_scan_thread_num INT -1 [1, 2147483647] 外表 Scan 最大线程数
min_remote_scan_thread_num INT -1 [1, 2147483647] 外表 Scan 最小线程数
read_bytes_per_second INT -1 [1, 9223372036854775807] 内表读 IO 限速(字节/秒)
remote_read_bytes_per_second INT -1 [1, 9223372036854775807] 外表读 IO 限速(字节/秒)

注意事项

  1. 创建时必须至少指定一个属性
  2. CGroup v1 的 cpu_share 默认值为 1024,取值范围 2-262144
  3. CGroup v2 的 cpu_share 默认值为 100,取值范围 1-10000
  4. 所有 Workload Group 的 memory_limit 累加不能超过 100%
  5. 所有 Workload Group 的 cpu_hard_limit 累加不能超过 100%

5.3 修改 Workload Group

ALTER WORKLOAD GROUP g1 PROPERTIES('cpu_share' = '4096');

-- 修改内存限制为硬限
ALTER WORKLOAD GROUP g1 PROPERTIES(
    'memory_limit' = '30%',
    'enable_memory_overcommit' = 'false'
);

-- 修改并发和排队参数
ALTER WORKLOAD GROUP g1 PROPERTIES(
    'max_concurrency' = '100',
    'max_queue_size' = '200',
    'queue_timeout' = '30000'
);

-- 添加 CPU 硬限(需先开启硬限模式)
ALTER WORKLOAD GROUP g1 PROPERTIES('cpu_hard_limit' = '20%');

5.4 删除 Workload Group

- 删除指定 Workload Group
DROP WORKLOAD GROUP g1;

– 注意:系统默认的 normal 组不可删除

六、用户绑定与授权

6.1 查看可用 Workload Group

-- 查看当前用户有权限使用的 Workload Group
SELECT name FROM information_schema.workload_groups;

6.2 授权

-- 授予用户使用指定 Workload Group 的权限
GRANT USAGE_PRIV ON WORKLOAD GROUP 'g1' TO 'user_1'@'%';

-- 授予所有 Workload Group 权限
GRANT USAGE_PRIV ON WORKLOAD GROUP '*' TO 'user_1'@'%';

-- 撤销权限
REVOKE USAGE_PRIV ON WORKLOAD GROUP 'g1' FROM 'user_1'@'%';

6.3 绑定方式一:用户属性(推荐)

为用户设置默认 Workload Group,持久生效:

-- 设置用户默认 Workload Group
SET PROPERTY FOR 'user_1' = 'default_workload_group' = 'g1';

-- 查看用户属性
SHOW PROPERTY FOR 'user_1';

七、监控与查看

7.1 查看 Workload Group 列表

-- Show 语句
SHOW WORKLOAD GROUPS;

-- 系统表查询
SELECT * FROM information_schema.workload_groups;

7.2 查看资源使用情况

-- 查看各 Workload Group 内存使用(单位:MB)
SELECT 
    workload_group_id,
    name,
    MEMORY_USAGE_BYTES / 1024 / 1024 AS mem_used_mb,
    CPU_USAGE_PERCENT AS cpu_percent
FROM workload_group_resource_usage;

-- 查看特定 Workload Group 详情
SELECT * FROM information_schema.workload_groups WHERE name = 'g1';

7.3 系统表字段说明

workload_groups 表

字段 说明
ID Workload Group ID
NAME 名称
CPU_SHARE CPU 软限权重
MEMORY_LIMIT 内存限制百分比
ENABLE_MEMORY_OVERCOMMIT 是否允许内存超配
MAX_CONCURRENCY 最大并发数
MAX_QUEUE_SIZE 队列长度
QUEUE_TIMEOUT 排队超时时间
CPU_HARD_LIMIT CPU 硬限百分比
SCAN_THREAD_NUM Scan 线程数
READ_BYTES_PER_SECOND 内表 IO 限速
REMOTE_READ_BYTES_PER_SECOND 外表 IO 限速

八、CPU 软硬限模式切换

8.1 模式说明

模式 特点 适用场景
CPU 软限 按权重分配 CPU 时间,空闲时可使用全部 CPU 负载波动大,希望充分利用资源
CPU 硬限 绝对上限,无论 CPU 是否空闲都不能超过 需要严格资源隔离,保障 SLA

注意:一个集群同一时刻只能使用一种模式,不能混用。

8.2 从软限切换到硬限

步骤 1:为所有 Workload Group 设置硬限值

-- 必须为所有 Group 设置 cpu_hard_limit
ALTER WORKLOAD GROUP g1 PROPERTIES('cpu_hard_limit' = '30%');
ALTER WORKLOAD GROUP g2 PROPERTIES('cpu_hard_limit' = '20%');
ALTER WORKLOAD GROUP normal PROPERTIES('cpu_hard_limit' = '50%');

-- 验证:所有 Group 的 cpu_hard_limit 累加 <= 100%
SELECT SUM(cpu_hard_limit) FROM information_schema.workload_groups 
WHERE cpu_hard_limit > 0;

步骤 2:开启集群硬限开关

-- 内存中立即生效(重启后失效)
ADMIN SET FRONTEND CONFIG ("enable_cpu_hard_limit" = "true");

-- 持久化配置:修改所有 FE 的 fe.conf
echo "experimental_enable_cpu_hard_limit = true" >> fe/conf/fe.conf

8.3 从硬限切换回软限

-- 关闭硬限开关(自动恢复为软限模式)
ADMIN SET FRONTEND CONFIG ("enable_cpu_hard_limit" = "false");

– 如需持久化,修改 fe.conf 为 false 或删除该配置

以上,为Doris离线分析数仓增加的资源限制的整体方案。

参考文档:https://doris.apache.org/zh-CN/docs/2.1/admin-manual/workload-management/workload-group